diff options
Diffstat (limited to 'unrefactored/msglist.py')
-rw-r--r-- | unrefactored/msglist.py | 312 |
1 files changed, 312 insertions, 0 deletions
diff --git a/unrefactored/msglist.py b/unrefactored/msglist.py new file mode 100644 index 0000000..93a2608 --- /dev/null +++ b/unrefactored/msglist.py @@ -0,0 +1,312 @@ +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import gobject +import gtk +import hippo +import logging +import traceback +import sys + +from gettext import gettext as _ +from sugar import profile +from sugar.graphics import style +from sugar.graphics.icon import CanvasIcon + +import query + +from msgentry import MessageEntry + +UPDATE_INTERVAL = 300000 +NO_MATCHES = _('No matching messages') +NOTHING_TO_SHOW = _('Select a tag') + +class MsgList(gtk.HBox): + + def __init__(self, store): + self._store = store + self._query = {} + self._result_set = None + self._entries = [] + self._page_size = 10 + self._last_value = -1 + self._reflow_sid = 0 + + gtk.HBox.__init__(self) + self.set_flags(gtk.HAS_FOCUS | gtk.CAN_FOCUS) + self.connect('key-press-event', self._key_press_event_cb) + + self._box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, + background_color=style.COLOR_WHITE.get_int()) + self._canvas = hippo.Canvas() + self._canvas.set_root(self._box) + + self.pack_start(self._canvas) + self._canvas.show() + + self._vadjustment = gtk.Adjustment(value=0, lower=0, upper=0, + step_incr=1, page_incr=0, page_size=0) + self._vadjustment.connect('value-changed', self._vadjustment_value_changed_cb) + self._vadjustment.connect('changed', self._vadjustment_changed_cb) + + self._vscrollbar = gtk.VScrollbar(self._vadjustment) + self.pack_end(self._vscrollbar, expand=False, fill=False) + self._vscrollbar.show() + + self.connect('scroll-event', self._scroll_event_cb) + self.connect('destroy', self.__destroy_cb) + + self._fully_obscured = True + self._dirty = False + self._refresh_idle_handler = None + self._update_dates_timer = None + + def __destroy_cb(self, widget): + if self._result_set: + self._result_set.destroy() + + def _vadjustment_changed_cb(self, vadjustment): + logging.debug('_vadjustment_changed_cb:\n \t%r\n \t%r\n \t%r\n \t%r\n \t%r\n' % \ + (vadjustment.props.lower, vadjustment.props.page_increment, + vadjustment.props.page_size, vadjustment.props.step_increment, + vadjustment.props.upper)) + if vadjustment.props.upper > self._page_size: + self._vscrollbar.show() + else: + self._vscrollbar.hide() + + def _vadjustment_value_changed_cb(self, vadjustment): + gobject.idle_add(self._do_scroll) + + def _do_scroll(self, force=False): + import time + t = time.time() + + value = int(self._vadjustment.props.value) + + if value == self._last_value and not force: + return + self._last_value = value + + self._result_set.seek(value) + mobjects = self._result_set.read(self._page_size) + + if self._result_set.length != self._vadjustment.props.upper: + self._vadjustment.props.upper = self._result_set.length + self._vadjustment_changed() + + self._refresh_view(mobjects) + self._dirty = False + + logging.debug('_do_scroll %r %r\n' % (value, (time.time() - t))) + + return False + + def _refresh_view(self, mobjects): + logging.debug('ListView %r' % self) + # no mail to show + if len(mobjects) == 0: + self._show_message(NOTHING_TO_SHOW) + return + # refresh view and create the entries if they don't exist + for i in range(0, self._page_size): + try: + if i < len(mobjects): + if i >= len(self._entries): + entry = self.create_entry() + self._box.append(entry) + self._entries.append(entry) + entry.mobject = mobjects[i] + else: + entry = self._entries[i] + entry.mobject = mobjects[i] + entry.set_visible(True) + elif i < len(self._entries): + entry = self._entries[i] + entry.set_visible(False) + except Exception: + logging.error('Exception while displaying entry:\n' + \ + ''.join(traceback.format_exception(*sys.exc_info()))) + + def create_entry(self): + return MessageEntry() + + def update_with_query(self, query): + logging.debug('update_with_query, page_size = %d' % self._page_size) + self._query = query + if self._page_size > 0: + self.refresh() + + def refresh(self): + if self._result_set: + self._result_set.destroy() + self._result_set = query.find(self._store, self._query) + self._vadjustment.props.upper = self._result_set.length + self._vadjustment.changed() + + self._vadjustment.props.value = min(self._vadjustment.props.value, + self._result_set.length -self._page_size) + + if self._result_set.length == 0: + if self._query.get('query', ''): + self._show_message(NO_MATCH) + else: + self._show_message(NOTHING_TO_SHOW) + else: + self._clear_message() + self._do_scroll(force=True) + + def _scroll_event_cb(self, hbox, event): + if event.direction == gtk.gdk.SCROLL_UP: + if self._vadjustment.props.value > self._vadjustment.props.lower: + self._vadjustment.props.value -= 1 + elif event.direction == gtk.gdk.SCROLL_DOWN: + max_value = self._result_set.length - self._page_size + if self._vadjustment.props.value < max_value: + self._vadjustment.props.value += 1 + + def do_focus(self, direction): + if not self.is_focus(): + self.grab_focus() + return True + return False + + def _key_press_event_cb(self, widget, event): + keyname = gtk.gdk.keyval_name(event.keyval) + + if keyname == 'Up': + if self._vadjustment.props.value > self._vadjustment.props.lower: + self._vadjustment.props.value -= 1 + elif keyname == 'Down': + max_value = self._result_set.length - self._page_size + if self._vadjustment.props.value < max_value: + self._vadjustment.props.value += 1 + elif keyname == 'Page_Up' or keyname == 'KP_Page_Up': + new_position = max(0, self._vadjustment.props.value - self._page_size) + if new_position != self._vadjustment.props.value: + self._vadjustment.props.value = new_position + elif keyname == 'Page_Down' or keyname == 'KP_Page_Down': + new_position = min(self._result_set.length - self._page_size, + self._vadjustment.props.value + self._page_size) + if new_position != self._vadjustment.props.value: + self._vadjustment.props.value = new_position + elif keyname == 'Home' or keyname == 'KP_Home': + new_position = 0 + if new_position != self._vadjustment.props.value: + self._vadjustment.props.value = new_position + elif keyname == 'End' or keyname == 'KP_End': + new_position = max(0, self._result_set.length - self._page_size) + if new_position != self._vadjustment.props.value: + self._vadjustment.props.value = new_position + else: + return False + + return True + + def do_size_allocate(self, allocation): + gtk.HBox.do_size_allocate(self, allocation) + new_page_size = int(allocation.height / style.GRID_CELL_SIZE) + + logging.debug("do_size_allocate: %r" % new_page_size) + + if new_page_size != self._page_size: + self._page_size = new_page_size + self._queue_reflow() + + def _queue_reflow(self): + if not self._reflow_sid: + self._reflow_sid = gobject.idle_add(self._reflow_idle_cb) + + def _reflow_idle_cb(self): + self._box.clear() + self._entries = [] + + self._vadjustment.props.page_size = self._page_size + self._vadjustment.props.page_increment = self._page_size + self._vadjustment_changed() + + if self._result_set is None: + self._result_set = query.find(self._store, self._query) + + max_value = max(0, self._result_set.length - self._page_size) + if self._vadjustment.props.value > max_value: + self._vadjustment.props.value = max_value + else: + self._do_scroll(force=True) + + self._reflow_sid = 0 + + def _show_message(self, message): + box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL, + background_color=style.COLOR_WHITE.get_int(), + yalign=hippo.ALIGNMENT_CENTER) + icon = CanvasIcon(size=style.LARGE_ICON_SIZE, + file_name='activity/activity-mail.svg', + stroke_color=style.COLOR_BUTTON_GREY.get_svg(), + fill_color=style.COLOR_TRANSPARENT.get_svg()) + text = hippo.CanvasText(text=message, + xalign=hippo.ALIGNMENT_CENTER, + font_desc=style.FONT_NORMAL.get_pango_desc(), + color=style.COLOR_BUTTON_GREY.get_int()) + + box.append(icon) + box.append(text) + self._canvas.set_root(box) + + def _clear_message(self): + self._canvas.set_root(self._box) + + def update_dates(self): + logging.debug('MessageList.update_dates') + for entry in self._entries: + entry.update_date() + + def _set_dirty(self): + if self._fully_obscured: + self._dirty = True + else: + self._schedule_refresh() + + def _schedule_refresh(self): + if self._refresh_idle_handler is None: + logging.debug('Add refresh idle callback') + self._refresh_idle_handler = gobject.idle_add(self.__refresh_idle_cb) + + def __refresh_idle_cb(self): + self.refresh() + if self._refresh_idle_handler is not None: + logging.debug('Remove refresh idle callback') + gobject.source_remove(self._refresh_idle_handler) + self._refresh_idle_handler = None + return False + + def set_is_visible(self, visible): + logging.debug('canvas_visibility_notify_event_cb %r' % visible) + if visible: + self._fully_obscured = False + if self._dirty: + self._schedule_refresh() + if self._update_dates_timer is None: + logging.debug('Adding date updating timer') + self._update_dates_timer = gobject.timeout_add(UPDATE_INTERVAL, self.__update_dates_timer_cb) + else: + self._fully_obscured = True + if self._update_dates_timer is not None: + logging.debug('Remove date updating timer') + gobject.source_remove(self._update_dates_timer) + self._update_dates_timer = None + + def __update_dates_timer_cb(self): + self.update_dates() + return True |