view getan/states.py @ 74:057e0fc8a74d

switch to EditEntryState when 'e' is pressed in the EntryList to be able to edit the entry description
author Björn Ricks <bjoern.ricks@intevation.de>
date Wed, 25 May 2011 13:41:02 +0200
parents 745c644d9564
children 07394c6155e2
line wrap: on
line source
#!/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.resources import gettext as _
from getan.utils     import human_time, safe_int

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:
            if not self.controller.entries_view.rows:
                return self
            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': _('Choose a project: '),
    }

    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 'insert' in key:
            return AddProjectNameState(self.controller, self.view)

        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())
                    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):
        self.controller.start_project(self.view.item_in_focus())
        return RunningProjectsState(self.controller, self.view)

class ExitState(ProjectState):
    messages = {
        'quit'  : _(" Really quit? (y/n)"),
        'choose': _(" Choose a project:")
    }

    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': _("Enter a description: "),
        'add_time'   : _("Enter time to add [min]: "),
        'min_time'   : _("Enter time to subtract [min]: "),
        'continue'   : _("Press 'Space' to continue."),
        'running'    : _("Running ( %s ) on '%s'."),
        'paused'     : _(" Break   ( %s )%s."),
    }

    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 proj: return
        if not self.break_start:
            self.controller.view.set_footer_text(self.msg('running') %
                                                 (human_time(self.sec),
                                                  proj.desc),
                                                 'running')
            self.controller.view.loop.draw_screen()
            self.sec = self.sec + 1
        else:
            self.view.set_footer_text(
                self.msg('paused') %
                (human_time((datetime.now()-self.break_start).seconds),
                 self.msg('continue')),
                '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()
            if proj:
                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()
            if proj: 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 enter(self):
        raise Exception("Not implemented")

    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.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         = safe_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         = safe_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': _(" Choose a project."),
    }

    def enter(self):
        text = self.footer.get_edit_text()
        if text == '':
            return self
        self.controller.stop_project(text)
        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()
        if project:
            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:
            if self.view.selection:
                return DeleteEntryState(self.projectlist_state,
                                        self.controller, self.view)
        if 'm' in key:
            if self.view.selection:
                return MoveEntryState(self.projectlist_state,
                                      self.controller, self.view)
        if 'e' in key:
            entry = self.view.item_in_focus()
            if entry:
                return EditEntryState(self.projectlist_state,
                                      self.controller, self.view, entry)
        return self


class DeleteEntryState(EntryListState):
    messages = {
        'delete'  : _("Really delete this entry? (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:
            entries = [x.item for x in self.view.selection]
            if entries:
                self.controller.delete_entries(entries)
                self.renew_focus()
            self.view.set_footer_text("", 'entry_footer')
            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': _(" Into which project do you want to move these entries?"),
        'really':  _(" Are you sure? (> %s) (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.renew_focus()
            self.view.set_footer_text('', 'entry_footer')
            self.proj = None
            return DefaultEntryListState(self.projectlist_state,
                                         self.controller, self.view)

        if 'n' in key and self.proj:
            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

class CreateProjectState(HandleUserInputState):

    messages = {
            'choose_proj': _(' Choose a project.'),
            }

    def __init__(self, controller, view):
        super(CreateProjectState, self).__init__(controller, view, None,
                controller.view.view.get_footer())

    def exit(self):
        self.controller.view.set_footer_text(self.msg('choose_proj'), 'question')
        return PausedProjectsState(self.controller, self.view)

class AddProjectNameState(CreateProjectState):
    messages = {
        'choose_proj': _(' Choose a project.'),
        'proj_name': _('Insert new project name: '),
    }

    def __init__(self, controller, view):
        controller.view.set_footer_text(self.msg('proj_name'),
                                             'question', 1)
        super(AddProjectNameState, self).__init__(controller, view)
        self.view.frame.set_focus('footer')

    def enter(self):
        project = self.footer.get_edit_text()
        if project == '':
            return self
        return AddProjectKeyState(self.controller, self.view, project)


class AddProjectKeyState(CreateProjectState):

    messages = {
        'proj_name': _('Insert new project name: '),
        'proj_key': _('Insert key for new project: '),
    }

    def __init__(self, controller, view, project):
        controller.view.set_footer_text(self.msg('proj_key'),
                                             'question', 1)
        super(AddProjectKeyState, self).__init__(controller, view)
        self.view.frame.set_focus('footer')
        self.project = project

    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 len(key) >= 1 and len(key[0]) == 1:
            self.footer.set_edit_pos(pos)
            self.footer.set_edit_text(key[0])
            return self

    def enter(self):
        key = self.footer.get_edit_text()
        if key == '':
            return self
        return AddProjectDescriptionState(self.controller, self.view,
                self.project, key)

class AddProjectDescriptionState(CreateProjectState):
    messages = {
        'proj_description': _('Insert a description for project: '),
        'choose_proj': _(" Choose a project.")
        }

    def __init__(self, controller, view, project, key):
        controller.view.set_footer_text(self.msg('proj_description'),
                                             'question', 1)
        super(AddProjectDescriptionState, self).__init__(controller, view)
        self.view.frame.set_focus('footer')
        self.project = project
        self.key = key

    def enter(self):
        description = self.footer.get_edit_text()
        if description == '':
            return self
        self.controller.add_project(self.key, description)
        self.controller.view.set_footer_text(self.msg('choose_proj'), 'question')
        return PausedProjectsState(self.controller, self.view)


class EditEntryState(HandleUserInputState):
    messages = {
        'edit_entry': _('Edit entry text: '),
    }

    def __init__(self, state, controller, view, entry):
        controller.view.set_footer_text(self.msg('edit_entry'),
                                             'question', True)
        super(EditEntryState, self).__init__(controller, view,
                None, controller.view.view.get_footer())
        self.footer.set_edit_text(entry.desc)
        self.footer.set_edit_pos(len(self.footer.edit_text))
        self.view.frame.set_focus('footer')
        self.entry = entry
        self.state = state
        logger.debug("EditEntryState: Entry %s" % entry)

    def enter(self):
        entry_desc = self.footer.get_edit_text()
        if entry_desc == '':
            return self
        entry = self.entry
        entry.desc = entry_desc
        self.controller.update_entry(entry)
        return self.exit()

    def exit(self):
        self.controller.view.set_footer_text("", 'question', False)
        return DefaultEntryListState(self.state, self.controller, self.view)

This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)