Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/jarabe/journal/listview.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/jarabe/journal/listview.py')
-rw-r--r--src/jarabe/journal/listview.py634
1 files changed, 31 insertions, 603 deletions
diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index 9e19f70..f4406b2 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2009, Tomeu Vizoso
+# Copyright (C) 2010, Aleksey Lim
#
# 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
@@ -14,628 +14,56 @@
# 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 logging
-from gettext import gettext as _
-import time
-import gobject
-import gtk
import hippo
-import gconf
-import pango
from sugar.graphics import style
-from sugar.graphics.icon import CanvasIcon, Icon, CellRendererIcon
-from sugar.graphics.xocolor import XoColor
-from sugar import util
-
-from jarabe.journal.listmodel import ListModel
-from jarabe.journal.palettes import ObjectPalette, BuddyPalette
-from jarabe.journal import model
-from jarabe.journal import misc
-
-UPDATE_INTERVAL = 300
-
-MESSAGE_EMPTY_JOURNAL = 0
-MESSAGE_NO_MATCH = 1
-
-class TreeView(gtk.TreeView):
- __gtype_name__ = 'JournalTreeView'
-
- def __init__(self):
- gtk.TreeView.__init__(self)
- self.set_headers_visible(False)
- def do_size_request(self, requisition):
- # HACK: We tell the model that the view is just resizing so it can avoid
- # hitting both D-Bus and disk.
- tree_model = self.get_model()
- if tree_model is not None:
- tree_model.view_is_resizing = True
- try:
- gtk.TreeView.do_size_request(self, requisition)
- finally:
- if tree_model is not None:
- tree_model.view_is_resizing = False
+from jarabe.journal.homogeneview import HomogeneView
+from jarabe.journal.homogeneview import Cell
+from jarabe.journal.widgets import *
-class BaseListView(gtk.Bin):
- __gtype_name__ = 'JournalBaseListView'
- __gsignals__ = {
- 'clear-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
- }
+class _Cell(Cell):
def __init__(self):
- self._query = {}
- self._model = None
- self._progress_bar = None
- self._last_progress_bar_pulse = None
- self._scroll_position = 0.
-
- gobject.GObject.__init__(self)
-
- self.connect('map', self.__map_cb)
- self.connect('unrealize', self.__unrealize_cb)
- self.connect('destroy', self.__destroy_cb)
-
- self._scrolled_window = gtk.ScrolledWindow()
- self._scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
- self.add(self._scrolled_window)
- self._scrolled_window.show()
-
- self.tree_view = TreeView()
- selection = self.tree_view.get_selection()
- selection.set_mode(gtk.SELECTION_NONE)
- self.tree_view.props.fixed_height_mode = True
- self.tree_view.modify_base(gtk.STATE_NORMAL,
- style.COLOR_WHITE.get_gdk_color())
- self._scrolled_window.add(self.tree_view)
- self.tree_view.show()
-
- self.cell_title = None
- self.cell_icon = None
- self._title_column = None
- self.date_column = None
- self._add_columns()
-
- self.tree_view.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
- [('text/uri-list', 0, 0),
- ('journal-object-id', 0, 0)],
- gtk.gdk.ACTION_COPY)
-
- # Auto-update stuff
- self._fully_obscured = True
- self._dirty = False
- self._refresh_idle_handler = None
- self._update_dates_timer = None
-
- model.created.connect(self.__model_created_cb)
- model.updated.connect(self.__model_updated_cb)
- model.deleted.connect(self.__model_deleted_cb)
-
- def __model_created_cb(self, sender, **kwargs):
- self._set_dirty()
-
- def __model_updated_cb(self, sender, **kwargs):
- self._set_dirty()
-
- def __model_deleted_cb(self, sender, **kwargs):
- self._set_dirty()
-
- def _add_columns(self):
- cell_favorite = CellRendererFavorite(self.tree_view)
- cell_favorite.connect('clicked', self.__favorite_clicked_cb)
-
- column = gtk.TreeViewColumn()
- column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
- column.props.fixed_width = cell_favorite.props.width
- column.pack_start(cell_favorite)
- column.set_cell_data_func(cell_favorite, self.__favorite_set_data_cb)
- self.tree_view.append_column(column)
-
- self.cell_icon = CellRendererActivityIcon(self.tree_view)
-
- column = gtk.TreeViewColumn()
- column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
- column.props.fixed_width = self.cell_icon.props.width
- column.pack_start(self.cell_icon)
- column.add_attribute(self.cell_icon, 'file-name', ListModel.COLUMN_ICON)
- column.add_attribute(self.cell_icon, 'xo-color',
- ListModel.COLUMN_ICON_COLOR)
- self.tree_view.append_column(column)
-
- self.cell_title = gtk.CellRendererText()
- self.cell_title.props.ellipsize = pango.ELLIPSIZE_MIDDLE
- self.cell_title.props.ellipsize_set = True
-
- self._title_column = gtk.TreeViewColumn()
- self._title_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
- self._title_column.props.expand = True
- self._title_column.props.clickable = True
- self._title_column.pack_start(self.cell_title)
- self._title_column.add_attribute(self.cell_title, 'markup',
- ListModel.COLUMN_TITLE)
- self.tree_view.append_column(self._title_column)
-
- buddies_column = gtk.TreeViewColumn()
- buddies_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
- self.tree_view.append_column(buddies_column)
-
- for column_index in [ListModel.COLUMN_BUDDY_1, ListModel.COLUMN_BUDDY_2,
- ListModel.COLUMN_BUDDY_3]:
- cell_icon = CellRendererBuddy(self.tree_view,
- column_index=column_index)
- buddies_column.pack_start(cell_icon)
- buddies_column.props.fixed_width += cell_icon.props.width
- buddies_column.add_attribute(cell_icon, 'buddy', column_index)
- buddies_column.set_cell_data_func(cell_icon,
- self.__buddies_set_data_cb)
-
- cell_progress = gtk.CellRendererProgress()
- cell_progress.props.ypad = style.GRID_CELL_SIZE / 4
- buddies_column.pack_start(cell_progress)
- buddies_column.add_attribute(cell_progress, 'value',
- ListModel.COLUMN_PROGRESS)
- buddies_column.set_cell_data_func(cell_progress,
- self.__progress_data_cb)
-
- cell_text = gtk.CellRendererText()
- cell_text.props.xalign = 1
-
- # Measure the required width for a date in the form of "10 hours, 10
- # minutes ago"
- timestamp = time.time() - 10 * 60 - 10 * 60 * 60
- date = util.timestamp_to_elapsed_string(timestamp)
- date_width = self._get_width_for_string(date)
-
- self.date_column = gtk.TreeViewColumn()
- self.date_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
- self.date_column.props.fixed_width = date_width
- self.date_column.set_alignment(1)
- self.date_column.props.resizable = True
- self.date_column.props.clickable = True
- self.date_column.pack_start(cell_text)
- self.date_column.add_attribute(cell_text, 'text', ListModel.COLUMN_DATE)
- self.tree_view.append_column(self.date_column)
-
- def _get_width_for_string(self, text):
- # Add some extra margin
- text = text + 'aaaaa'
-
- widget = gtk.Label('')
- context = widget.get_pango_context()
- layout = pango.Layout(context)
- layout.set_text(text)
- width, height_ = layout.get_size()
- return pango.PIXELS(width)
-
- def do_size_allocate(self, allocation):
- self.allocation = allocation
- self.child.size_allocate(allocation)
-
- def do_size_request(self, requisition):
- requisition.width, requisition.height = self.child.size_request()
-
- def __destroy_cb(self, widget):
- if self._model is not None:
- self._model.stop()
-
- def __buddies_set_data_cb(self, column, cell, tree_model, tree_iter):
- progress = tree_model[tree_iter][ListModel.COLUMN_PROGRESS]
- cell.props.visible = progress >= 100
-
- def __progress_data_cb(self, column, cell, tree_model, tree_iter):
- progress = tree_model[tree_iter][ListModel.COLUMN_PROGRESS]
- cell.props.visible = progress < 100
-
- def __favorite_set_data_cb(self, column, cell, tree_model, tree_iter):
- favorite = tree_model[tree_iter][ListModel.COLUMN_FAVORITE]
- if favorite:
- client = gconf.client_get_default()
- color = XoColor(client.get_string('/desktop/sugar/user/color'))
- cell.props.xo_color = color
- else:
- cell.props.xo_color = None
-
- def __favorite_clicked_cb(self, cell, path):
- row = self._model[path]
- metadata = model.get(row[ListModel.COLUMN_UID])
- if not model.is_editable(metadata):
- return
- if metadata.get('keep', 0) == '1':
- metadata['keep'] = '0'
- else:
- metadata['keep'] = '1'
- model.write(metadata, update_mtime=False)
-
- def update_with_query(self, query_dict):
- logging.debug('ListView.update_with_query')
- self._query = query_dict
-
- if 'order_by' not in self._query:
- self._query['order_by'] = ['+timestamp']
-
- self.refresh()
-
- def refresh(self):
- logging.debug('ListView.refresh query %r', self._query)
- self._stop_progress_bar()
-
- if self._model is not None:
- self._model.stop()
- self._dirty = False
-
- self._model = ListModel(self._query)
- self._model.connect('ready', self.__model_ready_cb)
- self._model.connect('progress', self.__model_progress_cb)
- self._model.setup()
-
- def __model_ready_cb(self, tree_model):
- self._stop_progress_bar()
-
- self._scroll_position = self.tree_view.props.vadjustment.props.value
- logging.debug('ListView.__model_ready_cb %r', self._scroll_position)
-
- if self.tree_view.window is not None:
- # prevent glitches while later vadjustment setting, see #1235
- self.tree_view.get_bin_window().hide()
-
- # Cannot set it up earlier because will try to access the model
- # and it needs to be ready.
- self.tree_view.set_model(self._model)
-
- self.tree_view.props.vadjustment.props.value = self._scroll_position
- self.tree_view.props.vadjustment.value_changed()
-
- if self.tree_view.window is not None:
- # prevent glitches while later vadjustment setting, see #1235
- self.tree_view.get_bin_window().show()
-
- if len(tree_model) == 0:
- if self._is_query_empty():
- self._show_message(MESSAGE_EMPTY_JOURNAL)
- else:
- self._show_message(MESSAGE_NO_MATCH)
- else:
- self._clear_message()
-
- def __map_cb(self, widget):
- logging.debug('ListView.__map_cb %r', self._scroll_position)
- self.tree_view.props.vadjustment.props.value = self._scroll_position
- self.tree_view.props.vadjustment.value_changed()
-
- def __unrealize_cb(self, widget):
- self._scroll_position = self.tree_view.props.vadjustment.props.value
- logging.debug('ListView.__map_cb %r', self._scroll_position)
-
- is_editable = self._query.get('mountpoints', '') == '/'
- self.cell_title.props.editable = is_editable
+ Cell.__init__(self)
- def _is_query_empty(self):
- # FIXME: This is a hack, we shouldn't have to update this every time
- # a new search term is added.
- if self._query.get('query', '') or self._query.get('mime_type', '') or \
- self._query.get('keep', '') or self._query.get('mtime', '') or \
- self._query.get('activity', ''):
- return False
- else:
- return True
+ self._row = gtk.HBox()
+ self._row.props.spacing = style.DEFAULT_SPACING
+ self.add(self._row)
- def __model_progress_cb(self, tree_model):
- if self._progress_bar is None:
- self._start_progress_bar()
+ keep = KeepIcon(box_width=style.GRID_CELL_SIZE)
+ self._row.pack_start(keep, expand=False)
- if time.time() - self._last_progress_bar_pulse > 0.05:
- self._progress_bar.pulse()
- self._last_progress_bar_pulse = time.time()
+ icon = ObjectIcon(size=style.STANDARD_ICON_SIZE)
+ self._row.pack_start(icon, expand=False)
- def _start_progress_bar(self):
- alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5)
- self.remove(self.child)
- self.add(alignment)
- alignment.show()
+ title = Title(xalign=0, yalign=0.5, xscale=1, yscale=0)
+ self._row.pack_start(title)
- self._progress_bar = gtk.ProgressBar()
- self._progress_bar.props.pulse_step = 0.01
- self._last_progress_bar_pulse = time.time()
- alignment.add(self._progress_bar)
- self._progress_bar.show()
+ details = DetailsIcon()
+ self._row.pack_end(details, expand=False)
- def _stop_progress_bar(self):
- if self._progress_bar is None:
- return
- self.remove(self.child)
- self.add(self._scrolled_window)
- self._progress_bar = None
+ date = Timestamp()
+ self._row.pack_end(date, expand=False)
- def _show_message(self, message):
- canvas = hippo.Canvas()
- self.remove(self.child)
- self.add(canvas)
- canvas.show()
+ buddies = Buddies(buddies_max=3,
+ xalign=0, yalign=0.5, xscale=1, yscale=0.15)
+ self._row.pack_end(buddies, expand=False)
- box = hippo.CanvasBox(orientation=hippo.ORIENTATION_VERTICAL,
- background_color=style.COLOR_WHITE.get_int(),
- yalign=hippo.ALIGNMENT_CENTER,
- spacing=style.DEFAULT_SPACING,
- padding_bottom=style.GRID_CELL_SIZE)
- canvas.set_root(box)
+ self.show_all()
- icon = CanvasIcon(size=style.LARGE_ICON_SIZE,
- icon_name='activity-journal',
- stroke_color = style.COLOR_BUTTON_GREY.get_svg(),
- fill_color = style.COLOR_TRANSPARENT.get_svg())
- box.append(icon)
+ def do_fill_in_cell_content(self, table, metadata):
+ for i in self._row.get_children():
+ i.check_out(metadata)
- if message == MESSAGE_EMPTY_JOURNAL:
- text = _('Your Journal is empty')
- elif message == MESSAGE_NO_MATCH:
- text = _('No matching entries')
- else:
- raise ValueError('Invalid message')
- text = hippo.CanvasText(text=text,
- xalign=hippo.ALIGNMENT_CENTER,
- font_desc=style.FONT_BOLD.get_pango_desc(),
- color = style.COLOR_BUTTON_GREY.get_int())
- box.append(text)
-
- if message == MESSAGE_NO_MATCH:
- button = gtk.Button(label=_('Clear search'))
- button.connect('clicked', self.__clear_button_clicked_cb)
- button.props.image = Icon(icon_name='dialog-cancel',
- icon_size=gtk.ICON_SIZE_BUTTON)
- canvas_button = hippo.CanvasWidget(widget=button,
- xalign=hippo.ALIGNMENT_CENTER)
- box.append(canvas_button)
-
- def __clear_button_clicked_cb(self, button):
- self.emit('clear-clicked')
-
- def _clear_message(self):
- if self.child == self._scrolled_window:
- return
- self.remove(self.child)
- self.add(self._scrolled_window)
- self._scrolled_window.show()
-
- def update_dates(self):
- if not self.tree_view.flags() & gtk.REALIZED:
- return
- visible_range = self.tree_view.get_visible_range()
- if visible_range is None:
- return
-
- logging.debug('ListView.update_dates')
-
- path, end_path = visible_range
- tree_model = self.tree_view.get_model()
-
- while True:
- x, y, width, height = self.tree_view.get_cell_area(path,
- self.date_column)
- x, y = self.tree_view.convert_tree_to_widget_coords(x, y)
- self.tree_view.queue_draw_area(x, y, width, height)
- if path == end_path:
- break
- else:
- next_iter = tree_model.iter_next(tree_model.get_iter(path))
- path = tree_model.get_path(next_iter)
-
- def _set_dirty(self):
- if self._fully_obscured:
- self._dirty = True
- else:
- self.refresh()
-
- def set_is_visible(self, visible):
- if visible != self._fully_obscured:
- return
-
- logging.debug('canvas_visibility_notify_event_cb %r', visible)
- if visible:
- self._fully_obscured = False
- if self._dirty:
- self.refresh()
- if self._update_dates_timer is None:
- logging.debug('Adding date updating timer')
- self._update_dates_timer = \
- gobject.timeout_add_seconds(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
-
-class ListView(BaseListView):
- __gtype_name__ = 'JournalListView'
-
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object]))
- }
+class ListView(HomogeneView):
def __init__(self):
- BaseListView.__init__(self)
- self._is_dragging = False
-
- self.tree_view.connect('drag-begin', self.__drag_begin_cb)
- self.tree_view.connect('button-release-event',
- self.__button_release_event_cb)
-
- self.cell_title.connect('edited', self.__cell_title_edited_cb)
- self.cell_title.connect('editing-canceled', self.__editing_canceled_cb)
-
- self.cell_icon.connect('clicked', self.__icon_clicked_cb)
- self.cell_icon.connect('detail-clicked', self.__detail_clicked_cb)
-
- cell_detail = CellRendererDetail(self.tree_view)
- cell_detail.connect('clicked', self.__detail_cell_clicked_cb)
-
- column = gtk.TreeViewColumn()
- column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
- column.props.fixed_width = cell_detail.props.width
- column.pack_start(cell_detail)
- self.tree_view.append_column(column)
-
- def __drag_begin_cb(self, widget, drag_context):
- self._is_dragging = True
-
- def __button_release_event_cb(self, tree_view, event):
- try:
- if self._is_dragging:
- return
- finally:
- self._is_dragging = False
-
- pos = tree_view.get_path_at_pos(int(event.x), int(event.y))
- if pos is None:
- return
-
- path, column, x_, y_ = pos
- if column != self._title_column:
- return
-
- row = self.tree_view.get_model()[path]
- metadata = model.get(row[ListModel.COLUMN_UID])
- self.cell_title.props.editable = model.is_editable(metadata)
-
- tree_view.set_cursor_on_cell(path, column, start_editing=True)
-
- def __detail_cell_clicked_cb(self, cell, path):
- row = self.tree_view.get_model()[path]
- self.emit('detail-clicked', row[ListModel.COLUMN_UID])
-
- def __detail_clicked_cb(self, cell, uid):
- self.emit('detail-clicked', uid)
-
- def __icon_clicked_cb(self, cell, path):
- row = self.tree_view.get_model()[path]
- metadata = model.get(row[ListModel.COLUMN_UID])
- misc.resume(metadata)
-
- def __cell_title_edited_cb(self, cell, path, new_text):
- row = self._model[path]
- metadata = model.get(row[ListModel.COLUMN_UID])
- metadata['title'] = new_text
- model.write(metadata, update_mtime=False)
- self.cell_title.props.editable = False
-
- def __editing_canceled_cb(self, cell):
- self.cell_title.props.editable = False
-
-class CellRendererFavorite(CellRendererIcon):
- __gtype_name__ = 'JournalCellRendererFavorite'
-
- def __init__(self, tree_view):
- CellRendererIcon.__init__(self, tree_view)
-
- self.props.width = style.GRID_CELL_SIZE
- self.props.height = style.GRID_CELL_SIZE
- self.props.size = style.SMALL_ICON_SIZE
- self.props.icon_name = 'emblem-favorite'
- self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
- self.props.prelit_stroke_color = style.COLOR_BUTTON_GREY.get_svg()
- self.props.prelit_fill_color = style.COLOR_BUTTON_GREY.get_svg()
-
-class CellRendererDetail(CellRendererIcon):
- __gtype_name__ = 'JournalCellRendererDetail'
-
- def __init__(self, tree_view):
- CellRendererIcon.__init__(self, tree_view)
-
- self.props.width = style.GRID_CELL_SIZE
- self.props.height = style.GRID_CELL_SIZE
- self.props.size = style.SMALL_ICON_SIZE
- self.props.icon_name = 'go-right'
- self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
- self.props.stroke_color = style.COLOR_TRANSPARENT.get_svg()
- self.props.fill_color = style.COLOR_BUTTON_GREY.get_svg()
- self.props.prelit_stroke_color = style.COLOR_TRANSPARENT.get_svg()
- self.props.prelit_fill_color = style.COLOR_BLACK.get_svg()
-
-class CellRendererActivityIcon(CellRendererIcon):
- __gtype_name__ = 'JournalCellRendererActivityIcon'
-
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([str])),
- }
-
- def __init__(self, tree_view):
- self._show_palette = True
-
- CellRendererIcon.__init__(self, tree_view)
-
- self.props.width = style.GRID_CELL_SIZE
- self.props.height = style.GRID_CELL_SIZE
- self.props.size = style.STANDARD_ICON_SIZE
- self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
-
- self.tree_view = tree_view
-
- def create_palette(self):
- if not self._show_palette:
- return None
-
- tree_model = self.tree_view.get_model()
- metadata = tree_model.get_metadata(self.props.palette_invoker.path)
-
- palette = ObjectPalette(metadata, detail=True)
- palette.connect('detail-clicked',
- self.__detail_clicked_cb)
- return palette
-
- def __detail_clicked_cb(self, palette, uid):
- self.emit('detail-clicked', uid)
-
- def set_show_palette(self, show_palette):
- self._show_palette = show_palette
-
- show_palette = gobject.property(type=bool, default=True,
- setter=set_show_palette)
-
-class CellRendererBuddy(CellRendererIcon):
- __gtype_name__ = 'JournalCellRendererBuddy'
-
- def __init__(self, tree_view, column_index):
- CellRendererIcon.__init__(self, tree_view)
-
- self.props.width = style.STANDARD_ICON_SIZE
- self.props.height = style.STANDARD_ICON_SIZE
- self.props.size = style.STANDARD_ICON_SIZE
- self.props.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
-
- self.tree_view = tree_view
- self._model_column_index = column_index
-
- def create_palette(self):
- tree_model = self.tree_view.get_model()
- row = tree_model[self.props.palette_invoker.path]
-
- if row[self._model_column_index] is not None:
- nick, xo_color = row[self._model_column_index]
- return BuddyPalette((nick, xo_color.to_string()))
- else:
- return None
-
- def set_buddy(self, buddy):
- if buddy is None:
- self.props.icon_name = None
- else:
- nick_, xo_color = buddy
- self.props.icon_name = 'computer-xo'
- self.props.xo_color = xo_color
-
- buddy = gobject.property(type=object, setter=set_buddy)
-
+ HomogeneView.__init__(self, _Cell)
+ self.frame_size = (None, 1)
+ self.cell_size = (None, style.GRID_CELL_SIZE)