view getan/view.py @ 215:a16c2c42d998

Use node_in_focus to get item_in_focus Avoid duplicate code
author Björn Ricks <bjoern.ricks@intevation.de>
date Fri, 05 Apr 2013 19:58:21 +0200
parents 6d5c8e205872
children bcaddd0319de
line wrap: on
line source
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# (c) 2010 by Ingo Weinzierl <ingo.weinzierl@intevation.de>
# (c) 2012 by Björn Ricks <bjoern.ricks@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 locale

import urwid
import urwid.raw_display

from getan.nodes import ProjectNode, EntryNode
from getan.resources import gettext as _
from getan.states    import *
from getan.utils     import short_time, format_datetime

logger = logging.getLogger()

class ListWidget(urwid.BoxWidget):
    def _update_view(self):
        logger.debug("ListWidget: update view now.")
        if self.size and len(self.rows) > self.size[1]:
            start_idx = self.top
            end_idx   = start_idx + self.size[1] - 6
        else:
            start_idx = self.top
            end_idx   = start_idx + len(self.rows)
        listbox = urwid.LineBox(urwid.Padding(
            urwid.ListBox(urwid.SimpleListWalker(self.rows[start_idx:
                                                           end_idx])),
            ('fixed left', 1),
            ('fixed right', 1)))

        self.body = urwid.AttrWrap(listbox, 'body')

        self.frame.set_body(self.body)
        self.frame.set_footer(self.footer)
        self.frame.set_header(self.header)

    def update_focus(self, focus, unfocus=-1):
        if self.rows:
            self.focused = focus
            if focus >= 0 and focus <= len(self.rows)-1:
                self.rows[focus].focus = True
                self.rows[focus].update_w()
            if unfocus >= 0:
                self.rows[unfocus].focus = False
                self.rows[unfocus].update_w()
        if self.size:
            self.validate_view()

    def render(self, size, focus=False):
        self.size = size
        maxcol, maxrow = size
        return self.frame.render((maxcol, maxrow), focus)

    def set_footer_text(self, text, attr, edit=False):
        if edit:
            logger.debug("ListWidget: set footer text (edit) = '%s'" % text)
            self.footer = urwid.AttrWrap(urwid.Edit(text), attr)
        else:
            logger.debug("ListWidget: set footer text = '%s'" % text)
            self.footer = urwid.AttrWrap(urwid.Text(text),attr)
        self._update_view()

    def row_count(self):
        if not self.rows:
            return 0
        return len(self.rows)

    def item_in_focus(self):
        node = self.node_in_focus()
        if node:
            return node.get_item()
        return None

    def node_in_focus(self):
        if self.rows:
            return self.rows[self.focused]
        return None

    def up(self):
        if self.focused > 0:
            logger.debug("ListWidget: navigate to upper row.")
            self.update_focus(self.focused-1, self.focused)

    def down(self):
        if self.focused < len(self.rows) - 1:
            logger.debug("ListWidget: navigate to lower row.")
            self.update_focus(self.focused+1, self.focused)

    def validate_view(self):
        if self.focused < self.top:
            self.top = self.focused
            self._update_view()
        if self.focused + 8 >= self.top + self.size[1]:
            self.top = self.focused - (self.size[1]) + 9
        if self.top < 0:
            self.top = 0
        self._update_view()

    def select(self):
        if not self.rows:
            return None
        node = self.rows[self.focused]
        logger.debug("ListWidget: select row '%s'" % self.focused)
        node.select()
        if node.selected:
            self.selection.append(node)
        else:
            if node in self.selection:
                self.selection.pop()
        logger.debug("ListWidget: all selected rows: %r" % self.selection)
        return node

    def clear(self):
        logger.debug("EntryList: clear focus and selection of all entries.")
        for node in self.selection:
            if node.selected:
                node.select()
        self.update_focus(-1, self.focused)
        self.focused = False


class ProjectNode(urwid.WidgetWrap):
    MODES = [
        (0, _('Total')),
        (1, _('Year')),
        (2, _('Month')),
        (3, _('Week')),
        (4, _('Day'))
    ]

    def __init__(self, proj, mode=3, indent=0):
        self.selected   = False
        self.focus      = False
        self.indent     = indent
        self.mode       = self.MODES[mode]
        self.item       = proj
        w               = self.update()
        self.__super.__init__(w)
        self.update_w()

    def update(self):
        logger.debug("Update ProjectNode '%s'" % self.item.key)
        time_str    = self._get_formatted_time()
        proj_desc = self.item.desc
        if proj_desc is None:
            proj_desc = ""

        description = urwid.Text([' ' * self.indent,
                                  ('project_key', self.item.key),
                                  (' '),
                                  (proj_desc)], wrap="clip")
        if self._get_time():
            time = urwid.Text('%s (%s)' % (self.mode[1], time_str))
        else:
            time = urwid.Text('')
        self.widget = urwid.AttrWrap(urwid.Columns([description, time]),None)
        self._w     = self.widget
        return self._w

    def _get_formatted_time(self):
        return human_time(self._get_time())

    def _get_time(self):
        if self.mode == self.MODES[0]:
            return self.item.total
        if self.mode == self.MODES[1]:
            return self.item.year()
        if self.mode == self.MODES[2]:
            return self.item.month()
        if self.mode == self.MODES[3]:
            return self.item.week()
        if self.mode == self.MODES[4]:
            return self.item.day()
        return self.item.week()

    def get_item(self):
        return self.item

    def switch_time_mode(self):
        tmp = self.mode[0] + 1
        if tmp > 4:
            self.mode = self.MODES[0]
        else:
            self.mode = self.MODES[tmp]
        self.update()

    def update_w(self):
        if self.focus:
            if self.selected:
                self._w.focus_attr = 'selected focus'
                self._w.attr       = 'selected focus'
            else:
                self._w.focus_attr = 'focus'
                self._w.attr       = 'focus'
        else:
            if self.selected:
                self._w.focus_attr = 'selected'
                self._w.attr       = 'selected'
            else:
                self._w.focus_attr = 'body'
                self._w.attr       = 'body'

    def select(self):
        self.selected = not self.selected
        self.update_w()


class ProjectList(ListWidget):

    PROJECT_MODES = {
            0: "id",
            1: "key",
            2: "desc",
            3: "tree",
    }

    def __init__(self, controller, rows):
        self.selection    = []
        self.focused      = 0
        self.size         = ()
        self.top          = 0
        self.controller   = controller
        self.project_mode = 0

        self.load_rows(rows)
        self.header    = urwid.LineBox(urwid.AttrWrap(urwid.Text("\n%s\n" %
            _('List of registered projects')),'project_header'))
        self.footer    = urwid.Edit()
        self.body      = urwid.LineBox(urwid.Padding(urwid.AttrWrap(
            self.listbox, 'entries'),('fixed left',1),('fixed right',1)))
        self.frame     = urwid.Frame(self.body, header=self.header,
                                    footer=self.footer)
        self.update_focus(self.focused)
        self._total_time()

    def load_rows(self, rows):
        self.raw_rows = rows
        self.update_rows()

    def update_rows(self):
        if self.project_mode == 3:
            self.rows = self.create_project_tree()
        else:
            self.rows = self.create_project_list()
        self.listbox = urwid.ListBox(urwid.SimpleListWalker(self.rows))

    def create_project_list(self):
            return [ProjectNode(x) for x in sorted(self.raw_rows,
                key=lambda row: self._get_project_sort_key(row))]

    def create_project_tree(self):
        # create a simple one child tree until now
        # this should be extended and improved in future
        nodes = []
        keys = []
        for proj in sorted(self.raw_rows, key=lambda proj: proj.key):
            k = proj.key[0]
            if k in keys:
                nodes.append(ProjectNode(proj, indent=3))
            else:
                keys.append(k)
                nodes.append(ProjectNode(proj))
        return nodes

    def _get_project_sort_key(self, proj):
        return getattr(proj, self.PROJECT_MODES[self.project_mode])

    def _total_time(self):
        if not self.rows:
            return
        logger.debug("ProjectList: update projects total time.")
        total = 0
        for proj in self.rows:
            tmp = proj._get_time()
            if tmp and type(tmp) == int:
                total += tmp
        self.set_footer_info("All projects: %s %s"
                             % (proj.mode[1], human_time(total)),
                             "project_footer")

    def set_footer_info(self, text, attr):
        logger.debug("ProjectList: set_footer_info to '%s'" % text)
        self._footer_info = (text, attr)
        self.set_footer_text(text, attr, False)

    def reset_footer(self):
        logger.debug("ProjectList: reset_footer to '%s'" % self._footer_info[0])
        self.set_footer_text(*self._footer_info)

    def update(self):
        logger.debug("ProjectList: update focused project row now.")
        if len(self.rows) > 0:
            self.rows[self.focused].update()
            self._total_time()
            self.controller.loop.draw_screen()

    def update_all(self):
        logger.debug("ProjectList: update all project rows now.")
        for proj in self.rows:
            proj.update()
        self._total_time()
        self.controller.loop.draw_screen()

    def switch_time_mode(self):
        logger.debug("ProjectList: switch time mode now.")
        for proj in self.rows:
            proj.switch_time_mode()
        self._total_time()
        self.controller.loop.draw_screen()

    def unhandled_keypress(self, key):
        logger.debug("ProjectList: unhandled keypress '%r'" % key)

    def select_project(self, project):
        for proj_node in self.rows:
            if proj_node.item.key == project.key:
                idx = self.rows.index(proj_node)
                self.update_focus(idx, self.focused)
                self.focused = idx
                self.select()
                break

    def switch_project_order(self):
        self.project_mode += 1
        if self.project_mode >= len(self.PROJECT_MODES):
            self.project_mode = 0
        logger.debug("Switching project mode to %s" % self.project_mode)
        self.update_rows()
        self.controller.update_project_list()
        self.update_focus(0)


class EntryNode(urwid.WidgetWrap):

    def __init__(self, entry):
        self.selected = False
        self.focus    = False
        self.item     = entry
        w             = self.update()
        self.__super.__init__(w)
        self.update_w()

    def update(self):
        logger.debug("EntryNode: update entry '%s'." % self.item.desc)
        row = urwid.Text(' %s [%s] %s' \
                         % (format_datetime(self.item.start), 
                            short_time(self.item.duration().seconds),
                            self.item.desc), wrap='clip')
        self.widget = urwid.AttrWrap(row, None)
        self._w     = self.widget
        self.update_w()
        return self._w

    def update_w(self):
        if self.focus:
            if self.selected:
                self._w.focus_attr = 'selected focus entry'
                self._w.attr       = 'selected focus entry'
            else:
                self._w.focus_attr = 'focus entry'
                self._w.attr       = 'focus entry'
        else:
            if self.selected:
                self._w.focus_attr = 'selected entry'
                self._w.attr       = 'selected entry'
            else:
                self._w.focus_attr = 'entry body'
                self._w.attr       = 'entry body'

    def select(self):
        logger.debug("EntryNode: update selection of entry '%s'"
                     % self.item.desc)
        self.selected = not self.selected
        self.update_w()

    def get_item(self):
        return self.item


class EntryList(ListWidget):

    def __init__(self, rows):
        self.selection = []
        self.top       = 0
        self.size      = ()
        self.focused   = 0
        self.rows      = [EntryNode(x) for x in rows]
        listbox        = urwid.ListBox(urwid.SimpleListWalker(self.rows))
        self.body      = urwid.LineBox(urwid.Padding(urwid.AttrWrap(
            listbox, 'entry_body'),
            ('fixed left', 1), ('fixed right', 1)))
        self.header    = urwid.LineBox(urwid.AttrWrap(urwid.Text(
            "\n%s\n"
            % _('Last entries')),'entry_header'))
        self.footer    = urwid.AttrWrap(urwid.Text(""), 'entry_footer')
        self.frame     = urwid.Frame(self.body, header=self.header,
                                     footer=self.footer)

    def set_rows(self, rows):
        if rows:
            self.rows = [EntryNode(x) for x in rows]
        else:
            self.rows = []
        self._update_view()

class GetanView(urwid.WidgetWrap):

    palette = [
        ('header',               'white',  'dark blue'),
        ('project_header',       'white',  'dark cyan'),
        ('entry_header',         'white',  'dark cyan'),
        ('footer',               'yellow', 'dark blue'),
        ('entry_footer',         'white',  'dark blue'),
        ('project_footer',       'white',  'dark blue'),
        ('project_key',          'black',  'dark cyan'),
        ('body',                 'white',  'black'),
        ('entry body',           'white',  'dark blue'),
        ('entries',              'white',  'black'),
        ('entry_body',           'white',  'dark blue'),
        ('focused entry',        'white',  'dark cyan'),
        ('selected entry',       'yellow', 'light cyan'),
        ('selected focus entry', 'yellow', 'dark cyan'),
        ('info',                 'white',  'dark red'),
        ('focus',                'white',  'dark blue'),
        ('selected',             'black',  'light green'),
        ('selected focus',       'yellow', 'dark cyan'),
        ('question',             'white',  'dark red'),
        ('running',              'yellow', 'dark green'),
        ('paused_running',       'white',  'dark red'),
    ]

    def __init__(self, controller, proj_list, entr_list):
        encoding = locale.getpreferredencoding()
        urwid.set_encoding(encoding)
        logger.debug("used encoding: %s" % encoding)
        self.controller = controller
        self.proj_list = proj_list
        self.entr_list = entr_list
        self.columns = urwid.Columns([
            urwid.Padding(self.proj_list, ('fixed left',0),('fixed right',1)),
            self.entr_list], 0)

        self.header = urwid.AttrWrap(urwid.Text('%s\n' % _('.: getan :.')),
                                       'header')
        self.footer = urwid.AttrWrap(urwid.Text(_('Choose a project:')),
                                       'question')
        self.col_list = self.columns.widget_list
        view = urwid.AttrWrap(self.columns, 'body')
        self.view = urwid.Frame(view, header=self.header,footer=self.footer)
        self._w = self.view

    def get_frame(self):
        return self.view

    def set_footer_text(self, text, attr, edit=False):
        if edit:
            logger.debug("GetanView: set footer text (edit): '%s'" % text)
            self.view.set_footer(urwid.AttrWrap(urwid.Edit(text),attr))
        else:
            logger.debug("GetanView: set footer text: '%s'" % text)
            self.view.set_footer(urwid.AttrWrap(urwid.Text(text),attr))

    def get_palette(self):
        return self.palette

    def keypress(self, size, key):
        self.controller.state.keypress(size, key)

    def update_entries(self, entries):
        self.entr_list.set_rows(entries)

    def set_focus(self, column):
        self.columns.set_focus_column(column)
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)