diff getan/states.py @ 23:9c4e8ba3c4fa

Added a new implementation of 'getan' based on urwid, a python console user interface library.
author Ingo Weinzierl <ingo_weinzierl@web.de>
date Sat, 28 Aug 2010 20:16:58 +0200
parents
children 155b23da504b
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/getan/states.py	Sat Aug 28 20:16:58 2010 +0200
@@ -0,0 +1,427 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+#
+# (c) 2010 by Ingo Weinzierl <ingo.weinzierl@intevation.de>
+#
+# This is Free Software licensed under the terms of GPLv3 or later.
+# For details see LICENSE coming with the source of 'getan'.
+#
+
+
+import logging
+import signal
+from   datetime import datetime, timedelta
+
+from getan.utils import human_time
+
+logger = logging.getLogger()
+
+class State(object):
+    messages = {
+    }
+
+    def __init__(self, controller, view):
+        self.controller = controller
+        self.view       = view
+
+    def msg(self, key):
+        return self.messages[key]
+
+
+class ProjectState(State):
+    def keypress(self, key):
+        logger.debug("ProjectState: handle key '%r'" % key)
+        if 'f1' in key:
+            self.view.switch_time_mode()
+            return None
+
+        if 'tab' in key:
+            self.controller.entries_view.focused = 0
+            self.controller.entries_view.update_focus(0)
+            return DefaultEntryListState(self, self.controller,
+                                  self.controller.entries_view)
+
+
+class PausedProjectsState(ProjectState):
+    messages = {
+        'choose_proj': u" Wählen Sie ein Projekt:"
+    }
+
+    def keypress(self, key):
+        logger.debug("PausedProjectsState: handle key '%r'" % key)
+        ret = super(PausedProjectsState, self).keypress(key)
+        if ret:
+            return ret
+
+        if 'up' in key:
+            return self.up()
+
+        if 'down' in key:
+            return self.down()
+
+        if 'enter' in key:
+            return self.select()
+
+        if 'esc' in key:
+            return ExitState(self.controller, self.view)
+
+        else:
+            if len(key) > 0:
+                proj = self.controller.project_by_key(key[0])
+                if proj:
+                    self.view.select_project(proj)
+                    self.controller.start_project(self.view.item_in_focus())
+                    self.controller.update_entries(
+                        self.view.item.in_focus().project)
+                    return RunningProjectsState(self.controller, self.view)
+        return self
+
+    def up(self):
+        self.view.up()
+        self.controller.update_entries(self.view.item_in_focus())
+        return self
+
+    def down(self):
+        self.view.down()
+        self.controller.update_entries(self.view.item_in_focus())
+        return self
+
+    def select(self):
+        proj_node = self.view.select()
+        self.controller.start_project(self.view.item_in_focus())
+        return RunningProjectsState(self.controller, self.view)
+
+
+class ExitState(ProjectState):
+    messages = {
+        'quit'  : u" Wirklich beenden? (y/n)",
+        'choose': u" Wählen Sie ein Projekt:"
+    }
+
+    def __init__(self, controller, view):
+        super(ExitState, self).__init__(controller, view)
+        self.controller.view.set_footer_text(self.msg('quit'), 'question')
+
+    def keypress(self, key):
+        logger.debug("ExitState: handle key '%r'" % key)
+        ret = super(ExitState, self).keypress(key)
+        if ret:
+            return ret
+
+        if 'y' in key or 'Y' in key:
+            self.controller.exit()
+
+        if 'n' in key or 'N' in key:
+            self.controller.view.set_footer_text(self.msg('choose'), 'question')
+            return PausedProjectsState(self.controller, self.view)
+
+        return self
+
+
+class RunningProjectsState(ProjectState):
+    messages = {
+        'description': u" Geben Sie eine Beschreibung ein: ",
+        'add_time'   : u" Geben Sie die zu addierende Zeit ein [min]: ",
+        'min_time'   : u" Geben Sie die zu abzuziehende Zeit ein [min]: ",
+        'paused'     : u" 'Space' zum Fortzusetzen",
+    }
+
+    sec         = 0
+    break_start = None
+
+    def __init__(self, controller, view):
+        super(RunningProjectsState, self).__init__(controller, view)
+        signal.signal(signal.SIGALRM, self.handle_signal)
+        signal.alarm(1)
+
+    def handle_signal(self, signum, frame):
+        proj = self.view.item_in_focus()
+        if not self.break_start:
+            self.controller.view.set_footer_text(" Running ( %s ) on '%s'" %
+                                                 (human_time(self.sec),
+                                                  proj.desc),
+                                                 'running')
+            self.controller.view.loop.draw_screen()
+            self.sec = self.sec + 1
+        else:
+            self.view.set_footer_text(
+                ' Break   ( %s )%s' %
+                (human_time((datetime.now()-self.break_start).seconds),
+                 self.msg('paused')),
+                'paused_running')
+            self.controller.view.loop.draw_screen()
+
+        signal.signal(signal.SIGALRM, self.handle_signal)
+        signal.alarm(1)
+
+    def keypress(self, key):
+        logger.debug("RunningProjectsState: handle key '%r'" % key)
+        ret = super(RunningProjectsState, self).keypress(key)
+        if ret:
+            return ret
+
+        if 'enter' in key:
+            return self.stop()
+        if '+' in key:
+            self.view.set_footer_text(self.msg('add_time'),
+                                                 'question', 1)
+            self.view.frame.set_focus('footer')
+            return AddTimeState(self.controller, self.view, self)
+        if '-' in key:
+            self.view.set_footer_text(self.msg('min_time'),
+                                                 'question', 1)
+            self.view.frame.set_focus('footer')
+            return SubtractTimeState(self.controller, self.view, self)
+        if ' ' in key and not self.break_start:
+            self.break_start = datetime.now()
+            return self
+        if ' ' in key and self.break_start:
+            self.view._total_time()
+            proj             = self.view.item_in_focus()
+            proj.start      += datetime.now() - self.break_start
+            self.break_start = None
+            signal.signal(signal.SIGALRM, self.handle_signal)
+            signal.alarm(1)
+        return self
+
+    def stop(self):
+        signal.alarm(0)
+        self.view.select()
+        if self.break_start:
+            proj = self.view.item_in_focus()
+            proj.start += datetime.now() - self.break_start
+        self.controller.view.set_footer_text(self.msg('description'),'question',1)
+        self.controller.view.get_frame().set_focus('footer')
+        return DescriptionProjectsState(
+            self.controller, self.view, self,
+            self.controller.view.get_frame().get_footer())
+
+
+class HandleUserInputState(State):
+    def __init__(self, controller, view, state, footer):
+        self.controller = controller
+        self.view       = view
+        self.state      = state
+        self.footer     = footer
+
+    def keypress(self, key):
+        logger.debug("HandleUserInputState: handle key '%r'" % key)
+        pos = self.footer.edit_pos
+
+        if 'esc' in key:
+            return self.exit()
+        elif 'enter' in key:
+            return self.enter()
+        elif 'left' in key:
+            self.footer.set_edit_pos(pos-1)
+        elif 'right' in key:
+            self.footer.set_edit_pos(pos+1)
+        elif 'backspace' in key:
+            text = self.footer.edit_text
+            self.footer.set_edit_text(
+                '%s%s' % (text[0:pos-1], text[pos:len(text)]))
+            self.footer.set_edit_pos(pos-1)
+        elif 'delete' in key:
+            text = self.footer.edit_text
+            self.footer.set_edit_text(
+                '%s%s' % (text[0:pos], text[pos+1:len(text)]))
+            self.footer.set_edit_pos(pos)
+        elif len(key) >= 1 and len(key[0]) == 1:
+            return self.insert(key)
+        return self   
+
+    def exit(self):
+        return self.state
+
+    def insert(self, key):
+        logger.debug("Enter key: %r" % key)
+        self.footer.insert_text(key[0])
+        return self
+
+
+class BaseTimeState(HandleUserInputState):
+    def __init__(self, controller, view, running_state):
+        super(BaseTimeState, self).__init__(controller, view, running_state,
+                                           view.frame.get_footer())
+
+    def exit(self):
+        self.view._total_time()
+        return self.running_state
+
+    def insert(self, key):
+        if key[0] in ['0','1','2','3','4','5','6','7','8','9']:
+            self.footer.insert_text(key[0])
+        else:
+            logger.debug("BaseTimeState: invalid character for "\
+                         "adding/subtracting time: '%r'" % key)
+        return self
+
+
+class AddTimeState(BaseTimeState):
+    def enter(self):
+        minutes         = int(self.view.frame.get_footer().get_edit_text())
+        project         = self.view.item_in_focus()
+        project.start  -= timedelta(minutes=minutes)
+        self.state.sec += minutes * 60
+        logger.info("AddTimeState: add %d minutes to project '%s'"
+                    % (minutes, project.desc))
+        self.view._total_time()
+        return self.state
+
+
+class SubtractTimeState(BaseTimeState):
+    def enter(self):
+        minutes         = int(self.view.frame.get_footer().get_edit_text())
+        project         = self.view.item_in_focus()
+        project.start  += timedelta(minutes=minutes)
+        self.state.sec -= minutes * 60
+        logger.info("SubtractTimeState: subtract %d minutes from project '%s'"
+                    % (minutes, project.desc))
+        self.view._total_time()
+        return self.state
+
+
+class DescriptionProjectsState(HandleUserInputState):
+    messages = {
+        'choose_proj': u" Wählen Sie ein Projekt."
+    }
+
+    def enter(self):
+        text = self.footer.get_edit_text()
+        if text == '':
+            return self
+        self.controller.stop_project()
+        self.controller.view.set_footer_text(self.msg('choose_proj'), 'question')
+        return PausedProjectsState(self.controller, self.view)
+
+    def exit(self):
+        project = self.view.item_in_focus()
+        time    = (datetime.now() - project.start).seconds
+        self.state.sec = time
+        signal.signal(signal.SIGALRM, self.state.handle_signal)
+        signal.alarm(1)
+        return self.state
+ 
+
+class EntryListState(State):
+    def __init__(self, state, controller, view):
+        self.projectlist_state = state
+        self.controller        = controller
+        self.view              = view
+
+    def keypress(self, key):
+        logger.debug("EntryListState: pressed key '%r'" % key)
+        if 'tab' in key:
+            self.view.clear()
+            return self.projectlist_state
+        if 'up' in key:
+            return self.up()
+        if 'down' in key:
+            return self.down()
+        if 'enter' in key:
+            return self.select()
+        return None
+        
+    def up(self):
+        self.view.up()
+        return self
+
+    def down(self):
+        self.view.down()
+        return self
+
+    def select(self):
+        self.view.select()
+        return self
+
+    def renew_focus(self):
+        e_len = self.view.row_count()
+        f     = self.view.focused
+        if f >= e_len: f = e_len - 1
+        self.view.focused = f
+        self.view.update_focus(f)
+
+
+class DefaultEntryListState(EntryListState):
+    def keypress(self, key):
+        ret = super(DefaultEntryListState, self).keypress(key)
+        if ret:
+            return ret
+
+        if 'd' in key:
+            return DeleteEntryState(self.projectlist_state,
+                                    self.controller, self.view)
+        if 'm' in key:
+            return MoveEntryState(self.projectlist_state,
+                                  self.controller, self.view)
+        return self
+
+
+class DeleteEntryState(EntryListState):
+    messages = {
+        'delete'  : u" Wirklich löschen? (y/n)",
+    }
+
+    def __init__(self, state, controller, view):
+        super(DeleteEntryState, self).__init__(state, controller, view)
+        self.view.set_footer_text(self.msg('delete'), 'question')
+
+    def keypress(self, key):
+        ret = super(DeleteEntryState, self).keypress(key)
+        if ret:
+            return ret
+
+        if 'y' in key:
+            self.controller.delete_entries(self.view.selection)
+            self.renew_focus()
+            return DefaultEntryListState(self.projectlist_state,
+                                         self.controller, self.view)
+
+        if 'n' in key:
+            self.view.set_footer_text("", 'entry_footer')
+            return DefaultEntryListState(self.projectlist_state,
+                                         self.controller, self.view)
+
+        return self
+
+
+class MoveEntryState(EntryListState):
+    messages = {
+        'project': u" In welches Projekt möchten Sie die Einträge verschieben?",
+        'really':  u" Sind sie sich sicher? (y/n)",
+    }
+
+    proj = None
+
+    def __init__(self, state, controller, view):
+        super(MoveEntryState, self).__init__(state, controller, view)
+        self.view.set_footer_text(self.msg('project'), 'question')
+
+    def keypress(self, key):
+        if 'y' in key and self.proj:
+            logger.debug("MoveEntryState: move selected entries.")
+            self.controller.move_selected_entries(self.proj)
+            self.view.set_footer_text('', 'entry_footer')
+            self.proj = None
+            self.renew_focus()
+            return DefaultEntryListState(self.projectlist_state,
+                                         self.controller, self.view)
+
+        if 'n' in key:
+            self.view.set_footer_text('', 'entry_footer')
+            return DefaultEntryListState(self.projectlist_state,
+                                         self.controller, self.view)
+
+        if 'esc' in key:
+            self.view.set_footer_text('', 'entry_footer')
+            return DefaultEntryListState(self.projectlist_state,
+                                         self.controller, self.view)
+
+        if len(key) > 0 and self.proj is None:
+            self.proj = self.controller.project_by_key(key[0])
+            if self.proj:
+                logger.debug("MoveEntryState: prepared entries to be moved to "\
+                             "project '%s'" % self.proj.desc)
+                self.view.set_footer_text(self.msg('really'), 'question')
+
+        return self
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)