Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@member.fsf.org>2009-08-25 16:28:35 (GMT)
committer Aleksey Lim <alsroot@member.fsf.org>2009-08-25 16:28:35 (GMT)
commitd0b12ac2dc48c066c86eb3b3dbde642b5829f615 (patch)
tree568005434e8f2931fd28a7c778c52583bca2cd29 /src
parent01ca2fe683577c0ff0f2bb101e2f97ddebb75694 (diff)
Revert thumbs merge
Diffstat (limited to 'src')
-rw-r--r--src/jarabe/journal/Makefile.am35
-rw-r--r--src/jarabe/journal/expandedentry.py31
-rw-r--r--src/jarabe/journal/journalactivity.py35
-rw-r--r--src/jarabe/journal/journaltoolbox.py30
-rw-r--r--src/jarabe/journal/lazymodel.py416
-rw-r--r--src/jarabe/journal/listmodel.py21
-rw-r--r--src/jarabe/journal/listview.py359
-rw-r--r--src/jarabe/journal/misc.py22
-rw-r--r--src/jarabe/journal/model.py20
-rw-r--r--src/jarabe/journal/objectchooser.py59
-rw-r--r--src/jarabe/journal/objectmodel.py84
-rw-r--r--src/jarabe/journal/objectview.py301
-rw-r--r--src/jarabe/journal/smoothtable.py346
-rw-r--r--src/jarabe/journal/source.py92
-rw-r--r--src/jarabe/journal/tableview.py169
-rw-r--r--src/jarabe/journal/thumbsview.py287
16 files changed, 397 insertions, 1910 deletions
diff --git a/src/jarabe/journal/Makefile.am b/src/jarabe/journal/Makefile.am
index d99fa1c..f4bf273 100644
--- a/src/jarabe/journal/Makefile.am
+++ b/src/jarabe/journal/Makefile.am
@@ -1,24 +1,17 @@
sugardir = $(pythondir)/jarabe/journal
-sugar_PYTHON = \
- __init__.py \
- detailview.py \
- expandedentry.py \
- journalactivity.py \
- journalentrybundle.py \
+sugar_PYTHON = \
+ __init__.py \
+ detailview.py \
+ expandedentry.py \
+ journalactivity.py \
+ journalentrybundle.py \
journaltoolbox.py \
- keepicon.py \
- lazymodel.py \
- listmodel.py \
- listview.py \
- misc.py \
- modalalert.py \
- model.py \
- objectchooser.py \
- objectmodel.py \
- objectview.py \
- palettes.py \
- smoothtable.py \
- source.py \
- tableview.py \
- thumbsview.py \
+ keepicon.py \
+ listmodel.py \
+ listview.py \
+ misc.py \
+ modalalert.py \
+ model.py \
+ objectchooser.py \
+ palettes.py \
volumestoolbar.py
diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py
index 9864891..88c0b41 100644
--- a/src/jarabe/journal/expandedentry.py
+++ b/src/jarabe/journal/expandedentry.py
@@ -16,8 +16,10 @@
import logging
from gettext import gettext as _
+import StringIO
import hippo
+import cairo
import gobject
import gtk
import cjson
@@ -170,10 +172,29 @@ class ExpandedEntry(hippo.CanvasBox):
height = style.zoom(240)
box = hippo.CanvasBox()
- preview = misc.load_preview(self._metadata)
+ if self._metadata.has_key('preview') and \
+ len(self._metadata['preview']) > 4:
+
+ if self._metadata['preview'][1:4] == 'PNG':
+ preview_data = self._metadata['preview']
+ else:
+ # TODO: We are close to be able to drop this.
+ import base64
+ preview_data = base64.b64decode(
+ self._metadata['preview'])
+
+ png_file = StringIO.StringIO(preview_data)
+ try:
+ surface = cairo.ImageSurface.create_from_png(png_file)
+ has_preview = True
+ except Exception:
+ logging.exception('Error while loading the preview')
+ has_preview = False
+ else:
+ has_preview = False
- if preview is not None:
- preview_box = hippo.CanvasImage(image=preview,
+ if has_preview:
+ preview_box = hippo.CanvasImage(image=surface,
border=style.LINE_WIDTH,
border_color=style.COLOR_BUTTON_GREY.get_int(),
xalign=hippo.ALIGNMENT_CENTER,
@@ -190,18 +211,16 @@ class ExpandedEntry(hippo.CanvasBox):
color=style.COLOR_BUTTON_GREY.get_int(),
box_width=width,
box_height=height)
-
preview_box.connect_after('button-release-event',
self._preview_box_button_release_event_cb)
box.append(preview_box)
-
return box
def _create_buddy_list(self):
vbox = hippo.CanvasBox()
vbox.props.spacing = style.DEFAULT_SPACING
-
+
text = hippo.CanvasText(text=_('Participants:'),
font_desc=style.FONT_NORMAL.get_pango_desc())
text.props.color = style.COLOR_BUTTON_GREY.get_int()
diff --git a/src/jarabe/journal/journalactivity.py b/src/jarabe/journal/journalactivity.py
index 3ec09e8..08a5a0f 100644
--- a/src/jarabe/journal/journalactivity.py
+++ b/src/jarabe/journal/journalactivity.py
@@ -18,7 +18,7 @@
import logging
from gettext import gettext as _
import sys
-import traceback
+import traceback
import uuid
import gtk
@@ -34,7 +34,7 @@ from sugar import wm
from jarabe.model import bundleregistry
from jarabe.journal.journaltoolbox import MainToolbox, DetailToolbox
-from jarabe.journal.objectview import ObjectView
+from jarabe.journal.listview import ListView
from jarabe.journal.detailview import DetailView
from jarabe.journal.volumestoolbar import VolumesToolbar
from jarabe.journal import misc
@@ -105,14 +105,11 @@ class JournalActivity(Window):
logging.debug("STARTUP: Loading the journal")
Window.__init__(self)
- accel_group = gtk.AccelGroup()
- self.set_data('sugar-accel-group', accel_group)
- self.add_accel_group(accel_group)
-
self.set_title(_('Journal'))
self._main_view = None
self._secondary_view = None
+ self._list_view = None
self._detail_view = None
self._main_toolbox = None
self._detail_toolbox = None
@@ -134,10 +131,10 @@ class JournalActivity(Window):
model.updated.connect(self.__model_updated_cb)
model.deleted.connect(self.__model_deleted_cb)
- self._dbus_service = JournalActivityDBusService(self)
+ self._dbus_service = JournalActivityDBusService(self)
self.iconify()
-
+
self._critical_space_alert = None
self._check_available_space()
@@ -155,11 +152,11 @@ class JournalActivity(Window):
self._main_toolbox = MainToolbox()
self._main_view = gtk.VBox()
- self._objects_view = ObjectView()
- self._objects_view.connect('clear-clicked', self.__clear_clicked_cb)
- self._objects_view.connect('detail-clicked', self.__detail_clicked_cb)
- self._main_view.pack_start(self._objects_view)
- self._objects_view.show()
+ self._list_view = ListView()
+ self._list_view.connect('detail-clicked', self.__detail_clicked_cb)
+ self._list_view.connect('clear-clicked', self.__clear_clicked_cb)
+ self._main_view.pack_start(self._list_view)
+ self._list_view.show()
self._volumes_toolbar = VolumesToolbar()
self._volumes_toolbar.connect('volume-changed',
@@ -168,7 +165,6 @@ class JournalActivity(Window):
search_toolbar = self._main_toolbox.search_toolbar
search_toolbar.connect('query-changed', self._query_changed_cb)
- search_toolbar.connect('view-changed', self.__view_changed_cb)
search_toolbar.set_mount_point('/')
def _setup_secondary_view(self):
@@ -199,12 +195,9 @@ class JournalActivity(Window):
self.show_main_view()
def _query_changed_cb(self, toolbar, query):
- self._objects_view.update_with_query(query)
+ self._list_view.update_with_query(query)
self.show_main_view()
- def __view_changed_cb(self, sender, view):
- self._objects_view.change_view(view)
-
def show_main_view(self):
if self.toolbox != self._main_toolbox:
self.set_toolbox(self._main_toolbox)
@@ -268,7 +261,7 @@ class JournalActivity(Window):
def _focus_in_event_cb(self, window, event):
self.search_grab_focus()
- self._objects_view.update_dates()
+ self._list_view.update_dates()
def _check_for_bundle(self, object_id):
registry = bundleregistry.get_registry()
@@ -307,12 +300,12 @@ class JournalActivity(Window):
if event.changed_mask & gtk.gdk.WINDOW_STATE_ICONIFIED:
state = event.new_window_state
visible = not state & gtk.gdk.WINDOW_STATE_ICONIFIED
- self._objects_view.set_is_visible(visible)
+ self._list_view.set_is_visible(visible)
def __visibility_notify_event_cb(self, window, event):
logging.debug('visibility_notify_event_cb %r', self)
visible = event.state != gtk.gdk.VISIBILITY_FULLY_OBSCURED
- self._objects_view.set_is_visible(visible)
+ self._list_view.set_is_visible(visible)
def _check_available_space(self):
''' Check available space on device
diff --git a/src/jarabe/journal/journaltoolbox.py b/src/jarabe/journal/journaltoolbox.py
index a201550..201bf76 100644
--- a/src/jarabe/journal/journaltoolbox.py
+++ b/src/jarabe/journal/journaltoolbox.py
@@ -26,7 +26,6 @@ import gobject
import gio
import gtk
-from sugar.graphics.radiotoolbutton import RadioToolButton
from sugar.graphics.toolbox import Toolbox
from sugar.graphics.toolcombobox import ToolComboBox
from sugar.graphics.toolbutton import ToolButton
@@ -74,10 +73,7 @@ class SearchToolbar(gtk.Toolbar):
__gsignals__ = {
'query-changed': (gobject.SIGNAL_RUN_FIRST,
gobject.TYPE_NONE,
- ([object])),
- 'view-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object]))
+ ([object]))
}
def __init__(self):
@@ -118,34 +114,10 @@ class SearchToolbar(gtk.Toolbar):
#self.insert(tool_item, -1)
#tool_item.show()
- separator = gtk.SeparatorToolItem()
- separator.props.draw = False
- separator.set_expand(True)
- self.insert(separator, -1)
- separator.show()
-
- list_button = RadioToolButton(named_icon='view-list')
- list_button.props.tooltip = _('List view')
- list_button.props.accelerator = _('<Ctrl>1')
- list_button.connect('toggled', self.__view_button_toggled_cb, 0)
- self.insert(list_button, -1)
- list_button.show()
-
- thumb_button = RadioToolButton(named_icon='view-thumbs')
- thumb_button.props.group = list_button
- thumb_button.props.tooltip = _('Thumbs view')
- thumb_button.props.accelerator = _('<Ctrl>2')
- thumb_button.connect('toggled', self.__view_button_toggled_cb, 1)
- self.insert(thumb_button, -1)
- thumb_button.show()
-
self._query = self._build_query()
self.refresh_filters()
- def __view_button_toggled_cb(self, button, view_num):
- self.emit('view-changed', view_num)
-
def give_entry_focus(self):
self._search_entry.grab_focus()
diff --git a/src/jarabe/journal/lazymodel.py b/src/jarabe/journal/lazymodel.py
deleted file mode 100644
index a5938c6..0000000
--- a/src/jarabe/journal/lazymodel.py
+++ /dev/null
@@ -1,416 +0,0 @@
-# Copyright (C) 2009, 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
-# 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 gtk
-import logging
-from gobject import GObject, SIGNAL_RUN_FIRST, TYPE_PYOBJECT
-
-
-class Source(GObject):
-
- __gsignals__ = {
- 'objects-updated': (SIGNAL_RUN_FIRST, None, []),
- 'row-delayed-fetch': (SIGNAL_RUN_FIRST, None, 2 * [TYPE_PYOBJECT]),
- }
-
- def get_count(self):
- """ Returns number of objects """
- pass
-
- def get_row(self, offset):
- """ Get object
-
- Returns:
- objects in dict {field_name: value, ...}
- False can't fint object
- None wait for reply signal
-
- """
- pass
-
- def get_order(self):
- """ Get current order, returns (field_name, gtk.SortType) """
- pass
-
- def set_order(self, field_name, sort_type):
- """ Set current order """
- pass
-
-
-class LazyModel(gtk.GenericTreeModel):
-
- def __init__(self, columns, calc_columns=None):
- """ columns/calc_columns = {field_name: (column_num, column_type)} """
- gtk.GenericTreeModel.__init__(self)
-
- self.columns_by_name = {}
- self.columns_by_num = {}
- self.columns_types = {}
-
- for name, i in columns.items():
- self.columns_by_name[name] = i[0]
- self.columns_by_num[i[0]] = name
- self.columns_types[i[0]] = i[1]
-
- if calc_columns is not None:
- for name, i in calc_columns.items():
- self.columns_types[i[0]] = i[1]
-
- self._n_columns = max(self.columns_types.keys()) + 1
-
- self._source = None
- self._closing = False
- self._view = None
- self._last_count = 0
- self._cache = {}
- self._frame = (0, -1)
- self._in_process = {}
- self._postponed = []
-
- self.set_source(None, force=True)
- self.set_view(None, force=True)
-
- def on_calc_value(self, row, column):
- # stub
- pass
-
- def get_source(self):
- return self._source
-
- def set_source(self, source, force=False):
- if self._source == source and not force:
- return
-
- if self._source is not None:
- self._source.disconnect_by_func(self.refresh)
- self._source.disconnect_by_func(self.__delayed_fetch_cb)
-
- self._source = source
- if self._source is not None:
- self._source.connect('objects-updated', self.refresh)
- self._source.connect('row-delayed-fetch', self.__delayed_fetch_cb)
-
- self.refresh()
-
- source = property(get_source, set_source)
-
- def get_view(self):
- return self._view
-
- def set_view(self, view, force=False):
- if self._view == view and not force:
- return
-
- cursor = None
-
- if self._view is not None:
- cursor = self._view.get_cursor()
- self._unset_view_model()
-
- self._view = view
- self._cache = {}
- self._frame = (0, -1)
- self._in_process = {}
- self._postponed = []
-
- if self._source is None:
- self._last_count = 0
- else:
- self._last_count = self._source.get_count()
-
- if self._source is not None and view is not None:
- self._update_columns()
- view.set_model(self)
- if cursor is not None:
- view.set_cursor(*cursor)
-
- view = property(get_view, set_view)
-
- def get_order(self):
- if self._source is None:
- return None
- order = self._source.get_order()
- if order is None:
- return None
- return (self.columns_by_name[order[0]], order[1])
-
- def set_order(self, column, order):
- if self._source is None:
- return
- self._source.set_order(self.columns_by_num[column], order)
- self._update_columns()
-
- def refresh(self, sender=None):
- if self._source is None or self._view is None:
- return
-
- if self._last_count == 0:
- self.set_view(self._view, force=True)
-
- self._update_columns()
-
- count = self._source.get_count()
- if self._frame[0] >= count:
- self._frame = (0, -1)
- elif self._frame[1] >= count:
- self._frame = (self._frame[0], count-1)
-
- self._cache = {}
-
- if self._last_count != count:
- self._unset_view_model()
- self._view.set_model(self)
- else:
- for i in range(self._frame[0], self._frame[1]+1):
- self.emit('row-changed', (i, ), self.get_iter((i, )))
-
- self._last_count = count
-
- def recalc(self, fields):
- for i, row in self._cache.items():
- for field in fields:
- if field in row:
- del row[field]
- self.emit('row-changed', (i, ), self.get_iter((i, )))
-
- def get_row(self, pos, frame=None):
- if self._source is None:
- return False
- if not isinstance(pos, tuple):
- pos = self.get_path(pos)
- return self._get_row(pos[0], frame or (pos, pos))
-
- def _unset_view_model(self):
- try:
- self._closing = True
- self._view.set_model(None)
- finally:
- self._closing = False
-
- def __delayed_fetch_cb(self, source, offset, metadata):
- if not offset in self._in_process:
- logging.debug('__delayed_fetch_cb: no offset=%s' % offset)
- return
-
- logging.debug('__delayed_fetch_cb: get %s' % offset)
-
- path = (offset, )
- iterator = self.get_iter(path)
- row = Row(self, path, iterator, metadata)
-
- if self.in_frame(offset):
- self._cache[offset] = row
-
- del self._in_process[offset]
- self.emit('row-changed', path, iterator)
- if self._in_process:
- return
-
- while self._postponed:
- offset, force = self._postponed.pop()
- if not force and not self.in_frame(offset):
- continue
- row = self.get_row((offset, ))
- if row is not None and row != False:
- self.emit('row-changed', row.path, row.iterator)
- else:
- break
-
- def _get_row(self, offset, frame):
-
- def fetch():
- row = self._source.get_row(offset)
-
- if row is None or row == False:
- if row is not None:
- logging.debug('_get_row: can not find row for %s' % offset)
- return False
- logging.debug('_get_row: wait for reply for %s' % offset)
- self._in_process[offset] = True
- return None
-
- row = Row(self, (offset, ), self.get_iter(offset), row)
- self._cache[offset] = row
- return row
-
- out = self._cache.get(offset)
- if out is not None:
- return out
-
- if frame[0] >= frame[1]:
- # just return requested single row and do not change cache
- # if requested frame has <= 1 rows
- if self._in_process:
- self._postponed.append((offset, True))
- return None
- else:
- return fetch()
-
- if frame != self._frame:
- # switch to new frame
- intersect_min = max(frame[0], self._frame[0])
- intersect_max = min(frame[1], self._frame[1])
- if intersect_min > intersect_max:
- self._cache = {}
- else:
- for i in range(self._frame[0], intersect_min):
- if i in self._cache:
- del self._cache[i]
- for i in range(intersect_max+1, self._frame[1]+1):
- if i in self._cache:
- del self._cache[i]
- self._frame = frame
-
- if self._in_process:
- self._postponed.append((offset, False))
- return None
-
- return fetch()
-
- def _update_columns(self):
- order = self.get_order()
- if order is None or not hasattr(self._view, 'get_columns'):
- return
-
- for column in self._view.get_columns():
- if column.get_sort_column_id() == order[0]:
- column.props.sort_indicator = True
- column.props.sort_order = order[1]
- else:
- column.props.sort_indicator = False
-
- def in_frame(self, offset):
- return offset >= self._frame[0] and offset <= self._frame[1]
-
- def on_get_n_columns(self):
- return self._n_columns
-
- def on_get_column_type(self, index):
- return self.columns_types.get(index, bool)
-
- def on_iter_n_children(self, iterator):
- if iterator is None and not self._closing:
- return self._source.get_count()
- else:
- return 0
-
- def on_get_value(self, offset, column):
- if self._view is None or offset >= self._source.get_count():
- return None
-
- # return value only if iterator came from visible range
- # (on setting model, gtk.TreeView scans all items)
- vrange = self._view.get_visible_range()
- if vrange and offset >= vrange[0][0] and offset <= vrange[1][0]:
- row = self._get_row(offset, (vrange[0][0], vrange[1][0]))
- return row is not None and row != False and row[column]
-
- return None
-
- def on_iter_nth_child(self, iterator, n):
- return n
-
- def on_get_path(self, iterator):
- return iterator
-
- def on_get_iter(self, path):
- if self._source.get_count() and not self._closing:
- return path[0]
- else:
- return False
-
- def on_iter_next(self, iterator):
- if iterator is not None:
- if iterator >= self._source.get_count() - 1 or self._closing:
- return None
- return iterator + 1
- return None
-
- def on_get_flags(self):
- return gtk.TREE_MODEL_ITERS_PERSIST | gtk.TREE_MODEL_LIST_ONLY
-
- def on_iter_children(self, iterator):
- return None
-
- def on_iter_has_child(self, iterator):
- return False
-
- def on_iter_parent(self, iterator):
- return None
-
-
-class Row(object):
-
- def __init__(self, model, path, iterator, metadata):
- self.model = model
- self.iterator = iterator
- self.path = path
- self.metadata = metadata
- self.row = [None] * len(model.columns_by_name)
- self._calced_row = {}
-
- for name, value in metadata.items():
- column = model.columns_by_name.get(str(name), -1)
- if column != -1:
- self.row[column] = value
-
- def __getitem__(self, key):
- if isinstance(key, int):
- if key < len(self.row):
- return self.row[key]
- else:
- if key in self._calced_row:
- return self._calced_row[key]
- else:
- value = self.model.on_calc_value(self, key)
- if value is not None:
- self._calced_row[key] = value
- return value
- else:
- return self.metadata[key]
-
- def __setitem__(self, key, value):
- if isinstance(key, int):
- if key < len(self.row):
- self.row[key] = value
- else:
- self._calced_row[key] = value
- else:
- self.metadata[key] = value
-
- def __delitem__(self, key):
- if isinstance(key, int):
- if key < len(self.row):
- del self.row[key]
- else:
- del self._calced_row[key]
- else:
- del self.metadata[key]
-
- def __contains__(self, key):
- if isinstance(key, int):
- return key < len(self.row) or key in self._calced_row
- else:
- return self.metadata.__contains__(key)
-
- def has_key(self, key):
- return self.__contains__(key)
-
- def get(self, key, default=None):
- if key in self:
- return self.__getitem__(key)
- else:
- return default
diff --git a/src/jarabe/journal/listmodel.py b/src/jarabe/journal/listmodel.py
index 6f0d4f1..917fbb1 100644
--- a/src/jarabe/journal/listmodel.py
+++ b/src/jarabe/journal/listmodel.py
@@ -65,18 +65,35 @@ class ListModel(gtk.GenericTreeModel, gtk.TreeDragSource):
COLUMN_BUDDY_3: object,
COLUMN_BUDDY_2: object}
- def __init__(self, result_set):
+ _PAGE_SIZE = 10
+
+ def __init__(self, query):
gobject.GObject.__init__(self)
self._last_requested_index = None
self._cached_row = None
- self._result_set = result_set
+ self._result_set = model.find(query, ListModel._PAGE_SIZE)
self._temp_drag_file_path = None
# HACK: The view will tell us that it is resizing so the model can
# avoid hitting D-Bus and disk.
self.view_is_resizing = False
+ self._result_set.ready.connect(self.__result_set_ready_cb)
+ self._result_set.progress.connect(self.__result_set_progress_cb)
+
+ def __result_set_ready_cb(self, **kwargs):
+ self.emit('ready')
+
+ def __result_set_progress_cb(self, **kwargs):
+ self.emit('progress')
+
+ def setup(self):
+ self._result_set.setup()
+
+ def stop(self):
+ self._result_set.stop()
+
def get_metadata(self, path):
return model.get(self[path][ListModel.COLUMN_UID])
diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index 3366def..251388d 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -20,99 +20,106 @@ import time
import gobject
import gtk
+import hippo
import gconf
import pango
from sugar.graphics import style
-from sugar.graphics.icon import CellRendererIcon
+from sugar.graphics.icon import CanvasIcon, Icon, CellRendererIcon
from sugar.graphics.xocolor import XoColor
from sugar import util
-from jarabe.journal.source import Source
from jarabe.journal.listmodel import ListModel
from jarabe.journal.palettes import ObjectPalette, BuddyPalette
from jarabe.journal import model
from jarabe.journal import misc
-class ListView(gtk.TreeView):
- __gtype_name__ = 'JournalListView'
+UPDATE_INTERVAL = 300
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object])),
- 'entry-activated': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([str])),
- }
+MESSAGE_EMPTY_JOURNAL = 0
+MESSAGE_NO_MATCH = 1
+
+class TreeView(gtk.TreeView):
+ __gtype_name__ = 'JournalTreeView'
def __init__(self):
gtk.TreeView.__init__(self)
- self.props.fixed_height_mode = True
- self.cell_title = None
- self.cell_icon = None
- self._title_column = None
- self.date_column = None
- self._add_columns()
+ 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
- self.enable_model_drag_source(gtk.gdk.BUTTON1_MASK,
- [('text/uri-list', 0, 0), ('journal-object-id', 0, 0)],
- gtk.gdk.ACTION_COPY)
+class BaseListView(gtk.Bin):
+ __gtype_name__ = 'JournalBaseListView'
- self.cell_title.props.editable = True
- self.cell_title.connect('edited', self.__cell_title_edited_cb)
+ __gsignals__ = {
+ 'clear-clicked': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([]))
+ }
- self.cell_icon.connect('clicked', self.__icon_clicked_cb)
- self.cell_icon.connect('detail-clicked', self.__detail_clicked_cb)
+ def __init__(self):
+ self._query = {}
+ self._model = None
+ self._progress_bar = None
+ self._last_progress_bar_pulse = None
- cell_detail = CellRendererDetail(self)
- cell_detail.connect('clicked', self.__detail_cell_clicked_cb)
+ gobject.GObject.__init__(self)
- 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.append_column(column)
+ self.connect('destroy', self.__destroy_cb)
- self.connect('notify::hover-selection',
- self.__notify_hover_selection_cb)
- self.connect('button-release-event', self.__button_release_event_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()
- def __button_release_event_cb(self, tree_view, event):
- if not tree_view.props.hover_selection:
- return False
+ self.tree_view = TreeView()
+ 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()
- if event.window != tree_view.get_bin_window():
- return False
+ self.cell_title = None
+ self.cell_icon = None
+ self._title_column = None
+ self.date_column = None
+ self._add_columns()
- pos = tree_view.get_path_at_pos(event.x, event.y)
- if pos is None:
- return False
+ 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)
- path, column_, x_, y_ = pos
- uid = tree_view.get_model()[path][Source.FIELD_UID]
- self.emit('entry-activated', uid)
+ # Auto-update stuff
+ self._fully_obscured = True
+ self._dirty = False
+ self._refresh_idle_handler = None
+ self._update_dates_timer = None
- return False
+ model.created.connect(self.__model_created_cb)
+ model.updated.connect(self.__model_updated_cb)
+ model.deleted.connect(self.__model_deleted_cb)
- def __notify_hover_selection_cb(self, widget, pspec):
- self.cell_icon.props.show_palette = not self.props.hover_selection
+ def __model_created_cb(self, sender, **kwargs):
+ self._set_dirty()
- 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
+ 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)
+ cell_favorite = CellRendererFavorite(self.tree_view)
cell_favorite.connect('clicked', self.__favorite_clicked_cb)
column = gtk.TreeViewColumn('')
@@ -120,9 +127,9 @@ class ListView(gtk.TreeView):
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.append_column(column)
+ self.tree_view.append_column(column)
- self.cell_icon = CellRendererActivityIcon(self)
+ self.cell_icon = CellRendererActivityIcon(self.tree_view)
column = gtk.TreeViewColumn('')
column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
@@ -130,8 +137,8 @@ class ListView(gtk.TreeView):
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.append_column(column)
+ ListModel.COLUMN_ICON_COLOR)
+ self.tree_view.append_column(column)
self.cell_title = gtk.CellRendererText()
self.cell_title.props.ellipsize = pango.ELLIPSIZE_MIDDLE
@@ -145,15 +152,15 @@ class ListView(gtk.TreeView):
self._title_column.add_attribute(self.cell_title, 'markup',
ListModel.COLUMN_TITLE)
self._title_column.connect('clicked', self.__header_clicked_cb)
- self.append_column(self._title_column)
+ self.tree_view.append_column(self._title_column)
buddies_column = gtk.TreeViewColumn('')
buddies_column.props.sizing = gtk.TREE_VIEW_COLUMN_FIXED
- self.append_column(buddies_column)
+ 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,
+ 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
@@ -179,7 +186,7 @@ class ListView(gtk.TreeView):
self.date_column.pack_start(cell_text)
self.date_column.add_attribute(cell_text, 'text', ListModel.COLUMN_DATE)
self.date_column.connect('clicked', self.__header_clicked_cb)
- self.append_column(self.date_column)
+ self.tree_view.append_column(self.date_column)
def __header_clicked_cb(self, column_clicked):
if column_clicked == self._title_column:
@@ -199,7 +206,7 @@ class ListView(gtk.TreeView):
else:
self._query['order_by'] = ['+timestamp']
- self._refresh()
+ self.refresh()
# Need to update the column indicators after the model has been reset
if self._query['order_by'] == ['-timestamp']:
@@ -230,8 +237,19 @@ class ListView(gtk.TreeView):
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 __favorite_set_data_cb(self, column, cell, tree_model, tree_iter):
- favorite = self.get_model()[tree_iter][ListModel.COLUMN_FAVORITE]
+ favorite = self._model[tree_iter][ListModel.COLUMN_FAVORITE]
if favorite:
client = gconf.client_get_default()
color = XoColor(client.get_string('/desktop/sugar/user/color'))
@@ -241,7 +259,7 @@ class ListView(gtk.TreeView):
cell.props.fill_color = style.COLOR_WHITE.get_svg()
def __favorite_clicked_cb(self, cell, path):
- row = self.get_model()[path]
+ row = self._model[path]
metadata = model.get(row[ListModel.COLUMN_UID])
if metadata['keep'] == '1':
metadata['keep'] = '0'
@@ -249,41 +267,212 @@ class ListView(gtk.TreeView):
metadata['keep'] = '1'
model.write(metadata, update_mtime=False)
- def update_dates(self):
- if not self.flags() & gtk.REALIZED:
+ 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()
+ self._start_progress_bar()
+
+ if self._model is not None:
+ self._model.stop()
+
+ 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()
+
+ # 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)
+
+ 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 _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
+
+ def __model_progress_cb(self, tree_model):
+ if time.time() - self._last_progress_bar_pulse > 0.05:
+ if self._progress_bar is not None:
+ self._progress_bar.pulse()
+ self._last_progress_bar_pulse = time.time()
+
+ 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()
+
+ 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()
+
+ def _stop_progress_bar(self):
+ if self.child != self._progress_bar:
return
+ self.remove(self.child)
+ self.add(self._scrolled_window)
+
+ def _show_message(self, message):
+ canvas = hippo.Canvas()
+ self.remove(self.child)
+ self.add(canvas)
+ canvas.show()
+
+ 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)
+
+ 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)
+
+ 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):
+ self.remove(self.child)
+ self.add(self._scrolled_window)
+ self._scrolled_window.show()
+ def update_dates(self):
logging.debug('ListView.update_dates')
- visible_range = self.get_visible_range()
+ visible_range = self.tree_view.get_visible_range()
if visible_range is None:
return
-
path, end_path = visible_range
while True:
- x, y, width, height = self.get_cell_area(path, self.date_column)
- x, y = self.convert_tree_to_widget_coords(x, y)
- self.queue_draw_area(x, y, width, height)
+ 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 = self.get_model().iter_next(
- self.get_model().get_iter(path))
- path = self.get_model().get_path(next_iter)
+ next_iter = self._model.iter_next(self._model.get_iter(path))
+ path = self._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):
+ 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]))
+ }
+
+ def __init__(self):
+ BaseListView.__init__(self)
+
+ self.cell_title.props.editable = True
+ self.cell_title.connect('edited', self.__cell_title_edited_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 __detail_cell_clicked_cb(self, cell, path):
- row = self.get_model()[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.get_model()[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.get_model()[path]
+ row = self._model[path]
metadata = model.get(row[ListModel.COLUMN_UID])
metadata['title'] = new_text
model.write(metadata, update_mtime=False)
diff --git a/src/jarabe/journal/misc.py b/src/jarabe/journal/misc.py
index 0fe4b1e..e6e5abf 100644
--- a/src/jarabe/journal/misc.py
+++ b/src/jarabe/journal/misc.py
@@ -18,8 +18,6 @@ import logging
import time
import traceback
import os
-import StringIO
-import cairo
from gettext import gettext as _
import gio
@@ -234,23 +232,3 @@ def is_bundle(metadata):
return is_activity_bundle(metadata) or is_content_bundle(metadata) or \
is_journal_bundle(metadata)
-def load_preview(metadata):
- if not metadata.has_key('preview') or \
- len(metadata['preview']) < 5:
- return None
-
- if metadata['preview'][1:4] == 'PNG':
- preview_data = metadata['preview']
- else:
- # TODO: We are close to be able to drop this.
- import base64
- preview_data = base64.b64decode(metadata['preview'])
-
- png_file = StringIO.StringIO(preview_data)
- try:
- surface = cairo.ImageSurface.create_from_png(png_file)
- except Exception, e:
- logging.error('Error while loading the preview: %r' % e)
- return None
-
- return surface
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index ede4fdf..15259bb 100644
--- a/src/jarabe/journal/model.py
+++ b/src/jarabe/journal/model.py
@@ -65,9 +65,7 @@ class _Cache(object):
def remove_all(self, entries):
for uid in [entry['uid'] for entry in entries]:
- obj = self._dict.get(uid)
- if obj is None:
- continue
+ obj = self._dict[uid]
self._array.remove(obj)
del self._dict[uid]
@@ -196,8 +194,8 @@ class BaseResultSet(object):
objects_excess = len(self._cache) - cache_limit
if objects_excess > 0:
self._cache.remove_all(self._cache[-objects_excess:])
- #else:
- # logging.debug('cache hit and no need to grow the cache')
+ else:
+ logging.debug('cache hit and no need to grow the cache')
return self._cache[self._position - self._offset]
@@ -396,24 +394,16 @@ def _get_mount_point(path):
else:
dir_path = dir_path.rsplit(os.sep, 1)[0]
-def get(object_id, reply_cb=None):
+def get(object_id):
"""Returns the metadata for an object
"""
if os.path.exists(object_id):
stat = os.stat(object_id)
metadata = _get_file_metadata(object_id, stat)
metadata['mountpoint'] = _get_mount_point(object_id)
-
- elif reply_cb is None:
+ else:
metadata = _get_datastore().get_properties(object_id, byte_arrays=True)
metadata['mountpoint'] = '/'
-
- else:
- _get_datastore().get_properties(object_id, byte_arrays=True,
- reply_handler=reply_cb,
- error_handler=lambda e: reply_cb(None))
- return None
-
return metadata
def get_file(object_id):
diff --git a/src/jarabe/journal/objectchooser.py b/src/jarabe/journal/objectchooser.py
index 57e9ceb..31bdba8 100644
--- a/src/jarabe/journal/objectchooser.py
+++ b/src/jarabe/journal/objectchooser.py
@@ -24,7 +24,7 @@ import wnck
from sugar.graphics import style
from sugar.graphics.toolbutton import ToolButton
-from jarabe.journal.objectview import ObjectView
+from jarabe.journal.listview import BaseListView
from jarabe.journal.listmodel import ListModel
from jarabe.journal.journaltoolbox import SearchToolbar
from jarabe.journal.volumestoolbar import VolumesToolbar
@@ -80,21 +80,19 @@ class ObjectChooser(gtk.Window):
self._toolbar = SearchToolbar()
self._toolbar.connect('query-changed', self.__query_changed_cb)
- self._toolbar.connect('view-changed', self.__view_changed_cb)
self._toolbar.set_size_request(-1, style.GRID_CELL_SIZE)
vbox.pack_start(self._toolbar, expand=False)
self._toolbar.show()
- self._object_view = ObjectView()
- self._object_view.props.hover_selection = True
- self._object_view.connect('entry-activated', self.__entry_activated_cb)
- vbox.pack_start(self._object_view)
- self._object_view.show()
+ self._list_view = ChooserListView()
+ self._list_view.connect('entry-activated', self.__entry_activated_cb)
+ vbox.pack_start(self._list_view)
+ self._list_view.show()
self._toolbar.set_mount_point('/')
-
+
width = gtk.gdk.screen_width() - style.GRID_CELL_SIZE * 2
- height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE * 2
+ height = gtk.gdk.screen_height() - style.GRID_CELL_SIZE * 2
self.set_size_request(width, height)
if what_filter:
@@ -127,10 +125,7 @@ class ObjectChooser(gtk.Window):
return self._selected_object_id
def __query_changed_cb(self, toolbar, query):
- self._object_view.update_with_query(query)
-
- def __view_changed_cb(self, sender, view):
- self._object_view.change_view(view)
+ self._list_view.update_with_query(query)
def __volume_changed_cb(self, volume_toolbar, mount_point):
logging.debug('Selected volume: %r.', mount_point)
@@ -139,7 +134,7 @@ class ObjectChooser(gtk.Window):
def __visibility_notify_event_cb(self, window, event):
logging.debug('visibility_notify_event_cb %r', self)
visible = event.state == gtk.gdk.VISIBILITY_FULLY_OBSCURED
- self._object_view.set_is_visible(visible)
+ self._list_view.set_is_visible(visible)
class TitleBox(VolumesToolbar):
__gtype_name__ = 'TitleBox'
@@ -166,3 +161,39 @@ class TitleBox(VolumesToolbar):
self.insert(tool_item, -1)
tool_item.show()
+
+class ChooserListView(BaseListView):
+ __gtype_name__ = 'ChooserListView'
+
+ __gsignals__ = {
+ 'entry-activated': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([str])),
+ }
+
+ def __init__(self):
+ BaseListView.__init__(self)
+
+ self.cell_icon.props.show_palette = False
+ self.tree_view.props.hover_selection = True
+
+ self.tree_view.connect('button-release-event',
+ self.__button_release_event_cb)
+
+ def __entry_activated_cb(self, entry):
+ self.emit('entry-activated', entry)
+
+ def __button_release_event_cb(self, tree_view, event):
+ if event.window != tree_view.get_bin_window():
+ return False
+
+ pos = tree_view.get_path_at_pos(event.x, event.y)
+ if pos is None:
+ return False
+
+ path, column_, x_, y_ = pos
+ uid = tree_view.get_model()[path][ListModel.COLUMN_UID]
+ self.emit('entry-activated', uid)
+
+ return False
+
diff --git a/src/jarabe/journal/objectmodel.py b/src/jarabe/journal/objectmodel.py
deleted file mode 100644
index 5c728ac..0000000
--- a/src/jarabe/journal/objectmodel.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# Copyright (C) 2009, 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
-# 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
-
-from sugar import util
-
-from jarabe.journal import misc
-from jarabe.journal.source import Source
-from jarabe.journal.lazymodel import LazyModel
-
-
-class ObjectModel(LazyModel):
-
- FIELD_FETCHED_FLAG = 50
-
- def __init__(self):
- LazyModel.__init__(self, Source.FIELDS_BASE, Source.FIELDS_CALC)
- self._fetch_queue = []
- self._object_delayed_fetch_handle = None
-
- def on_calc_value(self, row, column):
- if column == Source.FIELD_MODIFY_TIME:
- return util.timestamp_to_elapsed_string(
- int(row[Source.FIELD_TIMESTAMP]) or 0)
-
- if column == Source.FIELD_THUMB:
- if self.fetch_metadata(row):
- return row[Source.FIELD_THUMB]
- return None
-
- return None
-
- def fetch_metadata(self, row):
- if row.metadata['mountpoint'] != '/':
- # do not process non-ds objects
- return False
-
- if self.FIELD_FETCHED_FLAG in row:
- return True
-
- if row not in self._fetch_queue:
- self._fetch_queue.append(row)
- if len(self._fetch_queue) == 1:
- gobject.idle_add(self.__idle_cb)
-
- return False
-
- def __idle_cb(self):
- while len(self._fetch_queue):
- row = self._fetch_queue[0]
- if self.in_frame(row.path[0]):
- self.source.get_object(row, self.__get_object_cb)
- break
- del self._fetch_queue[0]
- return False
-
- def __get_object_cb(self, metadata):
- row = self._fetch_queue[0]
- del self._fetch_queue[0]
-
- if metadata is not None:
- row.metadata.update(metadata)
-
- row[Source.FIELD_THUMB] = misc.load_preview(metadata)
- row[self.FIELD_FETCHED_FLAG] = True
-
- self.emit('row-changed', row.path, row.iterator)
-
- if len(self._fetch_queue):
- gobject.idle_add(self.__idle_cb)
diff --git a/src/jarabe/journal/objectview.py b/src/jarabe/journal/objectview.py
deleted file mode 100644
index ab04a5b..0000000
--- a/src/jarabe/journal/objectview.py
+++ /dev/null
@@ -1,301 +0,0 @@
-# Copyright (C) 2009, Tomeu Vizoso, 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
-# 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 logging
-from gettext import gettext as _
-import time
-
-import gobject
-import gtk
-import hippo
-
-from sugar.graphics import style
-from sugar.graphics.icon import CanvasIcon, Icon
-
-from jarabe.journal import model
-from jarabe.journal.listview import ListView
-from jarabe.journal.thumbsview import ThumbsView
-from jarabe.journal.listmodel import ListModel
-from jarabe.journal.objectmodel import ObjectModel
-from jarabe.journal.source import Source, LocalSource
-
-UPDATE_INTERVAL = 300
-
-MESSAGE_EMPTY_JOURNAL = 0
-MESSAGE_NO_MATCH = 1
-
-VIEW_LIST = 0
-VIEW_THUMBS = 1
-
-VIEW_TYPES = [ListView, ThumbsView]
-
-PAGE_SIZE = 10
-
-
-class ObjectView(gtk.Bin):
-
- __gsignals__ = {
- 'clear-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([])),
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object])),
- 'entry-activated': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([str])),
- }
-
- def __init__(self):
- gobject.GObject.__init__(self)
-
- self._query = {}
- self._result_set = None
- self._progress_bar = None
- self._last_progress_bar_pulse = None
- self._model = ObjectModel()
- self._view_widgets = []
- self._view = VIEW_LIST
-
- self.connect('destroy', self.__destroy_cb)
-
- for view_class in VIEW_TYPES:
- view = view_class()
- view.modify_base(gtk.STATE_NORMAL,
- style.COLOR_WHITE.get_gdk_color())
- view.connect('detail-clicked', self.__detail_clicked_cb)
- view.connect('entry-activated', self.__entry_activated_cb)
- view.show()
-
- widget = gtk.ScrolledWindow()
- widget.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
- widget.show()
- widget.add(view)
- widget.view = view
- self._view_widgets.append(widget)
-
- # 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 set_hover_selection(self, hover_selection):
- for i in self._view_widgets:
- i.view.props.hover_selection = hover_selection
-
- hover_selection = gobject.property(type=bool, default=False,
- setter=set_hover_selection)
-
- 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 update_dates(self):
- if self._view == VIEW_LIST:
- # TODO in 0.88 VIEW_LIST will use lazymodel
- self._view_widgets[VIEW_LIST].view.update_dates()
- return
- self._model.recalc([Source.FIELD_MODIFY_TIME])
-
- def change_view(self, view):
- if self._view_widgets[view].parent is not None:
- return
- self._view = view
- if self.child is not None:
- self.remove(self.child)
- self.add(self._view_widgets[view])
- self._view_widgets[view].show()
- if view == VIEW_LIST:
- # TODO in 0.88 VIEW_LIST will use lazymodel
- return
- self._model.view = self._view_widgets[view].view
-
- 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._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 _refresh(self):
- logging.debug('ListView._refresh query %r' % self._query)
- self._stop_progress_bar()
-
- if self._result_set is not None:
- self._result_set.stop()
-
- self._result_set = model.find(self._query, PAGE_SIZE)
- self._result_set.ready.connect(self.__result_set_ready_cb)
- self._result_set.progress.connect(self.__result_set_progress_cb)
- self._result_set.setup()
-
- def __result_set_ready_cb(self, **kwargs):
- self._stop_progress_bar()
-
- if self._result_set.length == 0:
- if self._is_query_empty():
- self._show_message(MESSAGE_EMPTY_JOURNAL)
- else:
- self._show_message(MESSAGE_NO_MATCH)
- else:
- # TODO in 0.88 VIEW_LIST will use lazymodel
- self._view_widgets[VIEW_LIST].view.set_model(
- ListModel(self._result_set))
- self._model.source = LocalSource(self._result_set)
- self.change_view(self._view)
-
- def __result_set_progress_cb(self, **kwargs):
- if self._progress_bar is None:
- self._start_progress_bar()
-
- if time.time() - self._last_progress_bar_pulse > 0.05:
- if self._progress_bar is not None:
- self._progress_bar.pulse()
- self._last_progress_bar_pulse = time.time()
-
- 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
-
- 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 __destroy_cb(self, widget):
- if self._result_set is not None:
- self._result_set.stop()
-
- def _start_progress_bar(self):
- alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5)
- if self.child is not None:
- self.remove(self.child)
- self.add(alignment)
- alignment.show()
-
- 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()
-
- def _stop_progress_bar(self):
- if self.child != self._progress_bar:
- return
- if self.child is not None:
- self.remove(self.child)
- self.add(self._view_widgets[self._view])
- self._view_widgets[self._view].show()
- self._progress_bar = None
-
- def _show_message(self, message):
- canvas = hippo.Canvas()
- if self.child is not None:
- self.remove(self.child)
- self.add(canvas)
- canvas.show()
-
- 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)
-
- 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)
-
- 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 _set_dirty(self):
- if self._fully_obscured:
- self._dirty = True
- else:
- self._refresh()
-
- def __update_dates_timer_cb(self):
- self.update_dates()
- return True
-
- def __detail_clicked_cb(self, list_view, object_id):
- self.emit('detail-clicked', object_id)
-
- def __entry_activated_cb(self, sender, uid):
- self.emit('entry-activated', uid)
-
- 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()
diff --git a/src/jarabe/journal/smoothtable.py b/src/jarabe/journal/smoothtable.py
deleted file mode 100644
index 9f6af15..0000000
--- a/src/jarabe/journal/smoothtable.py
+++ /dev/null
@@ -1,346 +0,0 @@
-# Copyright (C) 2009, 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
-# 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 gtk
-import gobject
-import math
-import bisect
-import logging
-
-
-class SmoothTable(gtk.Container):
-
- __gsignals__ = {
- 'set-scroll-adjustments': (gobject.SIGNAL_RUN_FIRST, None,
- [gtk.Adjustment, gtk.Adjustment]),
- }
-
- def __init__(self, rows, columns, new_cell, fill_in):
- assert(rows and columns)
-
- self._rows = []
- self._adj = None
- self._adj_value_changed_id = None
- self._bin_window = None
- self._bin_rows = 0
- self._cell_height = 0
- self._reordered = None
- self._last_allocation = None
- self._fill_in = fill_in
- self._visible_rows = {}
-
- gtk.Container.__init__(self)
-
- for i in range(rows + 2):
- row = []
- for j in range(columns):
- cell = new_cell()
- cell.show()
- cell.set_parent(self)
- cell.size_allocate(gtk.gdk.Rectangle(-1, -1))
- row.append(cell)
- self._rows.append(row)
-
- self.connect('key-press-event', self.__key_press_event_cb)
-
- def get_columns(self):
- return len(self._rows[0])
-
- columns = property(get_columns)
-
- def get_rows(self):
- return len(self._rows) - 2
-
- rows = property(get_rows)
-
- def get_frame(self):
- if self._adj is None or self._cell_height == 0:
- return (0, 0)
- top = int(self._adj.value / self._cell_height)
- bottom = int(math.ceil(self._adj.value + self._adj.page_size) / \
- self._cell_height)
- return (top * self.columns, bottom * self.columns + (self.columns - 1))
-
- frame = property(get_frame)
-
- def get_bin_rows(self):
- return self._bin_rows
-
- def set_bin_rows(self, bin_rows):
- self._bin_rows = max(self.rows, bin_rows)
-
- if self._adj is None:
- return
-
- for row in self._rows:
- for cell in row:
- cell.size_allocate(gtk.gdk.Rectangle(-1, -1, 0, 0))
-
- self._setup_adjustment(force=True)
-
- bin_rows = property(get_bin_rows, set_bin_rows)
-
- def get_visible_cell(self, y, x):
- if x >= self.columns:
- return None
- row = self._visible_rows.get(y)
- if row is None:
- return None
- return row[x]
-
- def goto(self, row):
- if self._adj is None:
- return
- self._adj.props.value = row * self._cell_height
- self._adj.value_changed()
-
- def do_realize(self):
- self.set_flags(gtk.REALIZED)
-
- self.window = gtk.gdk.Window(
- self.get_parent_window(),
- window_type=gtk.gdk.WINDOW_CHILD,
- x=self.allocation.x,
- y=self.allocation.y,
- width=self.allocation.width,
- height=self.allocation.height,
- wclass=gtk.gdk.INPUT_OUTPUT,
- colormap=self.get_colormap(),
- event_mask=gtk.gdk.VISIBILITY_NOTIFY_MASK)
- self.window.set_user_data(self)
-
- self._bin_window = gtk.gdk.Window(
- self.window,
- window_type=gtk.gdk.WINDOW_CHILD,
- x=0,
- y=int(-self._adj.value),
- width=self.allocation.width,
- height=int(self._adj.upper),
- colormap=self.get_colormap(),
- wclass=gtk.gdk.INPUT_OUTPUT,
- event_mask=(self.get_events() | gtk.gdk.EXPOSURE_MASK |
- gtk.gdk.SCROLL_MASK))
- self._bin_window.set_user_data(self)
-
- self.set_style(self.style.attach(self.window))
- self.style.set_background(self.window, gtk.STATE_NORMAL)
- self.style.set_background(self._bin_window, gtk.STATE_NORMAL)
-
- for row in self._rows:
- for cell in row:
- cell.set_parent_window(self._bin_window)
-
- self.queue_resize()
-
- def do_size_allocate(self, allocation):
- if self._reordered is not None:
- if allocation == self._reordered:
- self._reordered = None
- return
- self._reordered = None
-
- self.allocation = allocation
- self._cell_height = allocation.height / self.rows
-
- self._setup_adjustment(force=True)
-
- if self.flags() & gtk.REALIZED:
- self.window.move_resize(*allocation)
- self._bin_window.resize(allocation.width, int(self._adj.upper))
-
- def do_unrealize(self):
- self._bin_window.set_user_data(None)
- self._bin_window.destroy()
- self._bin_window = None
- gtk.Container.do_unrealize(self)
-
- def do_style_set(self, style):
- gtk.Widget.do_style_set(self, style)
- if self.flags() & gtk.REALIZED:
- self.style.set_background(self._bin_window, gtk.STATE_NORMAL)
-
- def do_expose_event(self, event):
- if event.window != self._bin_window:
- return False
- gtk.Container.do_expose_event(self, event)
- return False
-
- def do_map(self):
- self.set_flags(gtk.MAPPED)
-
- for row in self._rows:
- for cell in row:
- cell.map()
-
- self._bin_window.show()
- self.window.show()
-
- def do_size_request(self, req):
- req.width = 0
- req.height = 0
-
- for row in self._rows:
- for cell in row:
- cell.size_request()
-
- def do_forall(self, include_internals, callback, data):
- for row in self._rows:
- for cell in row:
- callback(cell, data)
-
- def do_add(self, widget):
- pass
-
- def do_remove(self, widget):
- pass
-
- def do_set_scroll_adjustments(self, hadjustment, vadjustment):
- if vadjustment is None or vadjustment == self._adj:
- return
-
- if self._adj is not None:
- self._adj.disconnect(self._adj_value_changed_id)
-
- self._adj = vadjustment
- self._setup_adjustment()
-
- self._adj_value_changed_id = vadjustment.connect('value-changed',
- self.__adjustment_value_changed_cb)
-
- def _setup_adjustment(self, force=False):
- self._adj.lower = 0
- self._adj.upper = self._bin_rows * self._cell_height
- self._adj.page_size = self.allocation.height
- self._adj.changed()
-
- max_value = max(0, self._adj.upper - self._adj.page_size)
- if self._adj.value > max_value:
- self._adj.value = max_value
- self._adj.value_changed()
- elif force:
- self._adj.value_changed()
-
- def _allocate_row(self, row, cell_y):
- cell_x = 0
- cell_no = cell_y / self._cell_height * self.columns
- cell_row = cell_y / self._cell_height
-
- for cell_column, cell in enumerate(row):
- self._fill_in(cell, cell_row, cell_column)
-
- callocation = gtk.gdk.Rectangle(cell_x, cell_y)
- callocation.width = self.allocation.width / self.columns
- callocation.height = self._cell_height
- cell.size_allocate(callocation)
-
- cell_x += callocation.width
- cell_no += 1
-
- def _get_head(self):
- if self._adj is None:
- return 0
- return int(self._adj.value) - int(self._adj.value) % self._cell_height
-
- def __adjustment_value_changed_cb(self, sender=None):
- if not self.flags() & gtk.REALIZED or self._cell_height == 0 or \
- self._adj.value < 0 or self._adj.value > self._adj.upper - \
- (self.rows * self._cell_height):
- return
-
- spare_rows = []
- visible_rows = []
- page_end = int(self._adj.value + self._adj.page_size)
-
- if self._last_allocation != self.allocation:
- self._last_allocation = self.allocation
- spare_rows = [] + self._rows
- else:
-
- class IndexedRow:
-
- def __init__(self, row):
- self.row = row
-
- def __lt__(self, other):
- return self.row[0].allocation.y < other.row[0].allocation.y
-
- for row in self._rows:
- if row[0].allocation.y < 0 or \
- row[0].allocation.y > page_end or \
- (row[0].allocation.y + self._cell_height) < \
- self._adj.value:
- spare_rows.append(row)
- else:
- bisect.insort_right(visible_rows, IndexedRow(row))
-
- if not visible_rows or len(visible_rows) < self.rows + \
- (self._get_head() != visible_rows[0]):
- self._reordered = self.allocation
- self._visible_rows = {}
-
- def insert_spare_row(cell_y, end_y):
- while cell_y < end_y:
- if not spare_rows:
- logging.error('spare_rows should not be empty')
- return
- row = spare_rows.pop()
- self._allocate_row(row, cell_y)
- self._visible_rows[cell_y / self._cell_height] = row
- cell_y = cell_y + self._cell_height
-
- cell_y = self._get_head()
- for i in visible_rows:
- cell = i.row[0].allocation
- insert_spare_row(cell_y, cell.y)
- self._visible_rows[cell.y / self._cell_height] = i.row
- cell_y = cell.y + cell.height
- insert_spare_row(cell_y, page_end)
-
- self._bin_window.move(0, int(-self._adj.value))
- self.window.process_updates(True)
-
- def __key_press_event_cb(self, widget, event):
- if self._adj is None or self._cell_height == 0:
- return
-
- page = self.rows * self._cell_height
- uplimit = self._adj.upper - page
-
- if event.keyval == gtk.keysyms.Up:
- self._adj.value -= self._cell_height
-
- elif event.keyval == gtk.keysyms.Down:
- self._adj.value += min(uplimit - self._adj.value,
- self._cell_height)
-
- elif event.keyval in (gtk.keysyms.Page_Up, gtk.keysyms.KP_Page_Up):
- self._adj.value -= min(self._adj.value, page)
-
- elif event.keyval in (gtk.keysyms.Page_Down, gtk.keysyms.KP_Page_Down):
- self._adj.value += min(uplimit - self._adj.value, page)
-
- elif event.keyval in (gtk.keysyms.Home, gtk.keysyms.KP_Home):
- self._adj.value = 0
-
- elif event.keyval in (gtk.keysyms.End, gtk.keysyms.KP_End):
- self._adj.value = uplimit
-
- else:
- return False
-
- return True
-
-SmoothTable.set_set_scroll_adjustments_signal('set-scroll-adjustments')
diff --git a/src/jarabe/journal/source.py b/src/jarabe/journal/source.py
deleted file mode 100644
index 13bf814..0000000
--- a/src/jarabe/journal/source.py
+++ /dev/null
@@ -1,92 +0,0 @@
-# Copyright (C) 2009, 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
-# 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 cairo
-from gobject import GObject, SIGNAL_RUN_FIRST, TYPE_PYOBJECT
-
-from jarabe.journal import model
-
-
-class Source(GObject):
-
- FIELD_UID = 0
- FIELD_TITLE = 1
- FIELD_MTIME = 2
- FIELD_TIMESTAMP = 3
- FIELD_KEEP = 4
- FIELD_BUDDIES = 5
- FIELD_ICON_COLOR = 6
- FIELD_MIME_TYPE = 7
- FIELD_PROGRESS = 8
- FIELD_ACTIVITY = 9
- FIELD_MOUNT_POINT = 10
- FIELD_ACTIVITY_ID = 11
- FIELD_BUNDLE_ID = 12
-
- FIELD_FAVORITE = 30
- FIELD_MODIFY_TIME = 31
- FIELD_THUMB = 32
-
- FIELDS_BASE = {'uid': (FIELD_UID, str),
- 'title': (FIELD_TITLE, str),
- 'mtime': (FIELD_MTIME, str),
- 'timestamp': (FIELD_TIMESTAMP, int),
- 'keep': (FIELD_KEEP, int),
- 'buddies': (FIELD_BUDDIES, str),
- 'icon-color': (FIELD_ICON_COLOR, str),
- 'mime_type': (FIELD_MIME_TYPE, str),
- 'progress': (FIELD_MIME_TYPE, str),
- 'activity': (FIELD_ACTIVITY, str),
- 'mountpoint': (FIELD_ACTIVITY, str),
- 'activity_id': (FIELD_ACTIVITY_ID, str),
- 'bundle_id': (FIELD_BUNDLE_ID, str)}
-
- FIELDS_CALC = {'favorite': (FIELD_FAVORITE, bool),
- 'modify_time': (FIELD_MODIFY_TIME, str),
- 'thumb': (FIELD_THUMB, cairo.ImageSurface)}
-
-
-class LocalSource(Source):
-
- __gsignals__ = {
- 'objects-updated': (SIGNAL_RUN_FIRST, None, []),
- 'row-delayed-fetch': (SIGNAL_RUN_FIRST, None, 2 * [TYPE_PYOBJECT]),
- }
-
- def __init__(self, resultset):
- Source.__init__(self)
- self._resultset = resultset
-
- def get_count(self):
- return self._resultset.length
-
- def get_row(self, offset):
- if offset >= self.get_count():
- return False
- self._resultset.seek(offset)
- return self._resultset.read()
-
- def get_order(self):
- """ Get current order, returns (field_name, gtk.SortType) """
- pass
-
- def set_order(self, field_name, sort_type):
- """ Set current order """
- pass
-
- def get_object(self, metadata, cb):
- model.get(metadata['uid'], cb)
- return None
diff --git a/src/jarabe/journal/tableview.py b/src/jarabe/journal/tableview.py
deleted file mode 100644
index 7bccf38..0000000
--- a/src/jarabe/journal/tableview.py
+++ /dev/null
@@ -1,169 +0,0 @@
-# Copyright (C) 2009, 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
-# 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 gtk
-import math
-import hippo
-import gobject
-
-from sugar.graphics import style
-from sugar.graphics.roundbox import CanvasRoundBox
-from jarabe.journal.smoothtable import SmoothTable
-
-COLOR_BACKGROUND = style.COLOR_WHITE
-COLOR_SELECTED = style.COLOR_TEXT_FIELD_GREY
-
-
-class TableCell(object):
-
- def __init__(self):
- self.row = None
- self.table = None
-
- def do_fill_in(self):
- pass
-
-
-class TableView(SmoothTable):
-
- def __init__(self, cell_class, rows, columns):
- SmoothTable.__init__(self, rows, columns,
- lambda: self._create_cell(cell_class), self._do_fill_in)
-
- self._model = None
- self._hover_selection = False
- self._selected_cell = None
- self._row_changed_id = None
-
- def get_cursor(self):
- return (self.frame[0], )
-
- def set_cursor(self, cursor):
- self.goto(cursor)
-
- def get_model(self):
- return self._model
-
- def set_model(self, model):
- if self._model == model:
- return
-
- if self._model is not None and self._row_changed_id is not None:
- self._model.disconnect(self._row_changed_id)
-
- self._model = model
-
- if model is not None:
- self._row_changed_id = \
- self._model.connect('row-changed', self.__row_changed_cb)
-
- if model is not None:
- rows = math.ceil(float(model.iter_n_children(None)) / self.columns)
- self.bin_rows = int(rows)
-
- model = gobject.property(type=object,
- getter=get_model, setter=set_model)
-
- def get_hover_selection(self):
- return self._hover_selection
-
- def set_hover_selection(self, value):
- self._hover_selection = value
-
- hover_selection = gobject.property(type=object,
- getter=get_hover_selection, setter=set_hover_selection)
-
- def get_visible_range(self):
- return ((self.frame[0], ), (self.frame[1], ))
-
- def _create_cell(self, cell_class):
- canvas = hippo.Canvas()
- canvas.show()
- canvas.modify_bg(gtk.STATE_NORMAL, COLOR_BACKGROUND.get_gdk_color())
-
- sel_box = CanvasRoundBox()
- sel_box.props.border_color = COLOR_BACKGROUND.get_int()
- canvas.set_root(sel_box)
-
- cell = cell_class()
- cell.table = self
- sel_box.append(cell, hippo.PACK_EXPAND)
-
- canvas.connect('enter-notify-event',
- self.__enter_notify_event_cb, cell)
- canvas.connect('leave-notify-event', self.__leave_notify_event_cb)
-
- canvas.table_view_cell_sel_box = sel_box
- canvas.table_view_cell = cell
-
- return canvas
-
- def _do_fill_in(self, canvas, y, x, prepared_row=None):
-
- cell = canvas.table_view_cell
- sel_box = canvas.table_view_cell_sel_box
-
- if self._selected_cell == cell and cell.get_visible():
- bg_color = COLOR_SELECTED
- else:
- bg_color = COLOR_BACKGROUND
- sel_box.props.background_color = bg_color.get_int()
-
- cell.row = prepared_row
-
- if cell.row is None:
- cell_num = y * self.columns + x
-
- if cell_num < self._model.iter_n_children(None):
- row = self._model.get_row((cell_num, ), self.frame)
- if row is not None and row != False:
- cell.row = row
-
- if cell.row is None:
- cell.set_visible(False)
- else:
- cell.do_fill_in()
- cell.set_visible(True)
-
- def __enter_notify_event_cb(self, canvas, event, cell):
- if not self.hover_selection:
- return
-
- if cell.get_visible():
- sel_box = canvas.table_view_cell_sel_box
- sel_box.props.background_color = COLOR_SELECTED.get_int()
-
- self._selected_cell = cell
-
- def __leave_notify_event_cb(self, canvas, event):
- if not self.hover_selection:
- return
-
- sel_box = canvas.table_view_cell_sel_box
- sel_box.props.background_color = COLOR_BACKGROUND.get_int()
-
- self._selected_cell = None
-
- def __row_changed_cb(self, model, path, iterator):
- y = path[0] / self.columns
- x = path[0] % self.columns
-
- canvas = self.get_visible_cell(y, x)
- if canvas is None:
- return
-
- row = self._model.get_row(path)
- self._do_fill_in(canvas, y, x, row)
diff --git a/src/jarabe/journal/thumbsview.py b/src/jarabe/journal/thumbsview.py
deleted file mode 100644
index 4a59ff2..0000000
--- a/src/jarabe/journal/thumbsview.py
+++ /dev/null
@@ -1,287 +0,0 @@
-# Copyright (C) 2009, 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
-# 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 sys
-import gobject
-import hippo
-import pango
-
-from sugar.graphics import style
-from sugar.graphics.icon import CanvasIcon
-from sugar.graphics.xocolor import XoColor
-from sugar.graphics.palette import CanvasInvoker
-
-from jarabe.journal.keepicon import KeepIcon
-from jarabe.journal.source import Source
-from jarabe.journal.objectmodel import ObjectModel
-from jarabe.journal.tableview import TableView, TableCell
-from jarabe.journal.palettes import ObjectPalette
-from jarabe.journal import misc
-from jarabe.journal import model
-
-ROWS = 4
-COLUMNS = 5
-STAR_WIDTH = 30
-
-
-class ThumbsCell(TableCell, hippo.CanvasBox):
-
- def __init__(self):
- TableCell.__init__(self)
-
- self._last_uid = None
-
- hippo.CanvasBox.__init__(self,
- orientation=hippo.ORIENTATION_HORIZONTAL,
- padding_left=style.DEFAULT_SPACING,
- padding_top=style.DEFAULT_SPACING * 2,
- spacing=style.DEFAULT_PADDING)
-
- self.connect('button-release-event', self.__button_release_event_cb)
-
- # tools column
-
- tools_box = hippo.CanvasBox(
- spacing=style.DEFAULT_PADDING,
- orientation=hippo.ORIENTATION_VERTICAL,
- box_width=STAR_WIDTH)
- self.append(tools_box)
-
- self.keep = KeepIcon(False)
- self.keep.props.size = style.SMALL_ICON_SIZE
- self.keep.connect('activated', self.__star_activated_cb)
- tools_box.append(self.keep)
-
- details = DetailsIcon(
- size=style.SMALL_ICON_SIZE)
- details.connect('activated', self.__detail_activated_cb)
- tools_box.append(details)
-
- # main column
-
- main_box = hippo.CanvasBox(
- orientation=hippo.ORIENTATION_VERTICAL)
- self.append(main_box, hippo.PACK_EXPAND)
-
- self.allocation_box = hippo.CanvasBox()
- main_box.append(self.allocation_box, hippo.PACK_EXPAND)
-
- self.activity_box = hippo.CanvasBox(
- border=style.LINE_WIDTH,
- border_color=style.COLOR_BUTTON_GREY.get_int())
- self.allocation_box.append(self.activity_box, hippo.PACK_FIXED)
-
- self.thumb = ThumbCanvas(self,
- xalign=hippo.ALIGNMENT_START,
- yalign=hippo.ALIGNMENT_START)
- self.thumb.connect('detail-clicked', self.__detail_clicked_cb)
- self.activity_box.append(self.thumb, hippo.PACK_FIXED)
-
- self.activity_icon = ActivityIcon(self,
- xalign=hippo.ALIGNMENT_START,
- yalign=hippo.ALIGNMENT_START)
- self.activity_icon.connect('detail-clicked', self.__detail_clicked_cb)
- self.activity_box.append(self.activity_icon, hippo.PACK_EXPAND)
-
- self.title = hippo.CanvasText(
- padding_top=style.DEFAULT_PADDING,
- xalign=hippo.ALIGNMENT_START,
- size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END)
- main_box.append(self.title)
-
- self.date = hippo.CanvasText(
- xalign=hippo.ALIGNMENT_START,
- size_mode=hippo.CANVAS_SIZE_ELLIPSIZE_END)
- main_box.append(self.date)
-
- def do_fill_in(self):
- title_weight = pango.AttrWeight(pango.WEIGHT_BOLD)
- title_weight.start_index = 0
- title_weight.end_index = sys.maxint
- title_attributes = pango.AttrList()
- title_attributes.insert(title_weight)
-
- self.title.props.attributes = title_attributes
- self.title.props.text = self.row[Source.FIELD_TITLE] or ''
-
- self.date.props.text = self.row[Source.FIELD_MODIFY_TIME] or ''
- self.keep.props.keep = int(self.row[Source.FIELD_KEEP] or 0) == 1
-
- w, h = self.table.thumb_size
- self.activity_box.props.box_width = w
- self.activity_box.props.box_height = h
-
- thumb = self.row[Source.FIELD_THUMB]
-
- if self._last_uid == self.row[Source.FIELD_UID] and \
- not ObjectModel.FIELD_FETCHED_FLAG in self.row:
- # do not blink by preview while re-reading entries
- return
- else:
- self._last_uid = self.row[Source.FIELD_UID]
-
- if thumb is None:
- self.thumb.set_visible(False)
- self.activity_icon.set_visible(True)
- self.activity_icon.palette = None
- self.activity_icon.update_icon()
- else:
- self.activity_icon.set_visible(False)
- self.thumb.set_visible(True)
- self.thumb.props.scale_width = w - style.LINE_WIDTH * 2
- self.thumb.props.scale_height = h - style.LINE_WIDTH * 2
- self.thumb.props.image = thumb
- self.thumb.palette = None
- self.thumb.allocate(w, h, True)
- self.activity_box.set_position(self.thumb,
- style.LINE_WIDTH, style.LINE_WIDTH)
-
- def __star_activated_cb(self, keep_button):
- self.row.metadata['keep'] = not keep_button.props.keep and 1 or 0
- model.write(self.row.metadata, update_mtime=False)
-
- def __detail_activated_cb(self, button):
- self.table.emit('detail-clicked', self.row[Source.FIELD_UID])
-
- def __detail_clicked_cb(self, sender, uid):
- self.table.emit('detail-clicked', uid)
-
- def __button_release_event_cb(self, sender, event):
- if not self.table.props.hover_selection:
- return False
- uid = self.row[Source.FIELD_UID]
- self.table.emit('entry-activated', uid)
- return False
-
-
-class ActivityCanvas(object):
-
- def __init__(self, cell):
- self._cell = cell
- self.connect_after('button-release-event',
- self.__button_release_event_cb)
- self.palette = None
-
- def create_palette(self):
- if self._cell.table.props.hover_selection:
- return
- palette = ObjectPalette(self._cell.row.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 __button_release_event_cb(self, button, event):
- misc.resume(self._cell.row.metadata)
- return True
-
-
-class ActivityIcon(ActivityCanvas, CanvasIcon):
-
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([str])),
- }
-
- def __init__(self, cell, **kwargs):
- CanvasIcon.__init__(self, **kwargs)
- ActivityCanvas.__init__(self, cell)
-
- def update_icon(self):
- metadata = self._cell.row.metadata
- self.props.file_name = misc.get_icon_name(metadata)
-
- if misc.is_activity_bundle(metadata):
- self.props.fill_color = style.COLOR_TRANSPARENT.get_svg()
- self.props.stroke_color = style.COLOR_BUTTON_GREY.get_svg()
- else:
- if 'icon-color' in metadata and metadata['icon-color']:
- self.props.xo_color = XoColor(metadata['icon-color'])
-
-
-class ThumbCanvas(ActivityCanvas, hippo.CanvasImage):
-
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
- ([str])),
- }
-
- def __init__(self, cell, **kwargs):
- hippo.CanvasImage.__init__(self, **kwargs)
- ActivityCanvas.__init__(self, cell)
-
- self._palette_invoker = CanvasInvoker()
- self._palette_invoker.attach(self)
- self.connect('destroy', self.__destroy_cb)
-
- def __destroy_cb(self, icon):
- if self._palette_invoker is not None:
- self._palette_invoker.detach()
-
-
-class DetailsIcon(CanvasIcon):
-
- def __init__(self, **kwargs):
- CanvasIcon.__init__(self, **kwargs)
- self.props.icon_name = 'go-right'
- self.props.stroke_color = style.COLOR_TRANSPARENT.get_svg()
- self.connect('motion-notify-event', self.__motion_notify_event_cb)
- self.connect('activated', self.__on_leave_cb)
- self.__on_leave_cb(None)
-
- def __on_leave_cb(self, button):
- self.props.fill_color = style.COLOR_BUTTON_GREY.get_svg()
-
- def __motion_notify_event_cb(self, icon, event):
- if event.detail == hippo.MOTION_DETAIL_ENTER:
- icon.props.fill_color = style.COLOR_BLACK.get_svg()
- elif event.detail == hippo.MOTION_DETAIL_LEAVE:
- self.__on_leave_cb(None)
-
-
-class ThumbsView(TableView):
-
- __gsignals__ = {
- 'detail-clicked': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([object])),
- 'entry-activated': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([str])),
- }
-
- def __init__(self):
- TableView.__init__(self, ThumbsCell, ROWS, COLUMNS)
- self.thumb_size = (0, 0)
-
- def do_size_allocate(self, allocation):
- text_layout = self.create_pango_layout('W')
- w = allocation.width / COLUMNS - STAR_WIDTH - style.DEFAULT_SPACING - \
- style.DEFAULT_PADDING - style.LINE_WIDTH * 2
- h = allocation.height / ROWS - text_layout.get_pixel_size()[1] * 2 - \
- style.DEFAULT_SPACING * 2 - style.DEFAULT_PADDING - \
- style.LINE_WIDTH * 2
-
- # keep thumb size 4:3
- if w / 4. * 3. > h:
- w = int(h / 3. * 4.)
- else:
- h = int(w / 4. * 3.)
-
- self.thumb_size = (max(0, w), max(0, h))
-
- TableView.do_size_allocate(self, allocation)