Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAleksey Lim <alsroot@member.fsf.org>2010-01-21 16:48:24 (GMT)
committer Aleksey Lim <alsroot@member.fsf.org>2010-01-21 16:48:24 (GMT)
commit4d9e3297eff3d60ee0f3ae71d9641cf8325d2575 (patch)
treecba8543b5a92e7cd1d72fc921c777e87b508b9e8
parent2a0bbabf6acfb2bdbd29e3667f9461a1c559b043 (diff)
Implement thumbs
-rw-r--r--src/jarabe/journal/expandedentry.py2
-rw-r--r--src/jarabe/journal/homogenetable.py57
-rw-r--r--src/jarabe/journal/homogeneview.py9
-rw-r--r--src/jarabe/journal/listview.py4
-rw-r--r--src/jarabe/journal/model.py24
-rw-r--r--src/jarabe/journal/preview.py216
-rw-r--r--src/jarabe/journal/thumbsview.py102
-rw-r--r--src/jarabe/journal/view.py18
-rw-r--r--src/jarabe/journal/widgets.py107
9 files changed, 428 insertions, 111 deletions
diff --git a/src/jarabe/journal/expandedentry.py b/src/jarabe/journal/expandedentry.py
index 2a3ead0..d5f095b 100644
--- a/src/jarabe/journal/expandedentry.py
+++ b/src/jarabe/journal/expandedentry.py
@@ -140,7 +140,7 @@ class ExpandedEntry(hippo.CanvasBox):
return
self._metadata = metadata
- self._keep_icon.check_out(metadata)
+ self._keep_icon.fill_in(metadata)
self._icon = self._create_icon()
self._icon_box.clear()
diff --git a/src/jarabe/journal/homogenetable.py b/src/jarabe/journal/homogenetable.py
index 64a22ec..43ff4fe 100644
--- a/src/jarabe/journal/homogenetable.py
+++ b/src/jarabe/journal/homogenetable.py
@@ -45,6 +45,7 @@ class VHomogeneTable(gtk.Container):
'set-scroll-adjustments': (gobject.SIGNAL_RUN_FIRST, None,
[gtk.Adjustment, gtk.Adjustment]),
'cursor-changed': (gobject.SIGNAL_RUN_FIRST, None, [object]),
+ 'frame-scrolled': (gobject.SIGNAL_RUN_FIRST, None, []),
}
def __init__(self, cell_class, **kwargs):
@@ -64,6 +65,7 @@ class VHomogeneTable(gtk.Container):
self._selected_index = None
self._editable = True
self._pending_allocate = None
+ self._frame_range = None
gtk.Container.__init__(self, **kwargs)
@@ -115,9 +117,10 @@ class VHomogeneTable(gtk.Container):
def set_cell_count(self, count):
if self._cell_count == count:
return
+
self._cell_count = count
- self.refill()
self._setup_adjustment(dry_run=False)
+ self._resize_table()
"""Number of virtual cells
Defines maximal number of virtual rows, the minimal has being described
@@ -149,6 +152,21 @@ class VHomogeneTable(gtk.Container):
"""Selected cell"""
cursor = gobject.property(getter=get_cursor, setter=set_cursor)
+ def get_frame_range(self):
+ if self._frame_range is None:
+ return xrange(0)
+ else:
+ begin, end = self._frame_range
+ return xrange(begin, end + 1)
+
+ """Range of visible cells"""
+ frame_range = gobject.property(getter=get_frame_range)
+
+ @property
+ def frame_cells(self):
+ for cell in self._cell_cache:
+ yield cell.widget
+
def get_editable(self):
return self._editable
@@ -213,12 +231,12 @@ class VHomogeneTable(gtk.Container):
self._pos_changed()
- def refill(self):
+ def refill(self, cells=None):
"""Force VHomogeneTable widget to run filling method for all cells"""
for cell in self._cell_cache:
- cell.invalidate_pos()
- cell.index = -1
- self._allocate_rows(force=True)
+ if cells is None or cell.index in cells:
+ cell.index = -1
+ self._allocate_rows(force=False)
# gtk.Widget overrides
@@ -360,16 +378,6 @@ class VHomogeneTable(gtk.Container):
return True
@property
- def _frame_range(self):
- if self._empty:
- return xrange(0)
- else:
- first = self._pos_y / self._cell_height * self._column_count
- last = int(math.ceil(float(self._pos_y + self._page) / \
- self._cell_height) * self._column_count)
- return xrange(first, min(last, self.cell_count))
-
- @property
def _empty(self):
return not self._row_cache
@@ -501,6 +509,7 @@ class VHomogeneTable(gtk.Container):
for row in self._row_cache:
for cell in row:
cell.invalidate_pos()
+ cell.index = -1
self._cell_height = height / self._frame_row_count
self._setup_adjustment(dry_run=True)
@@ -561,6 +570,7 @@ class VHomogeneTable(gtk.Container):
spare_rows = []
visible_rows = []
+ frame_rows = []
page_end = pos + self._page
if force:
@@ -584,6 +594,7 @@ class VHomogeneTable(gtk.Container):
row = spare_rows.pop()
self._allocate_cells(row, cell_y)
cell_y = cell_y + self._cell_height
+ frame_rows.append(row)
# visible_rows could not be continuous
# lets try to add spare rows to missed points
@@ -591,16 +602,26 @@ class VHomogeneTable(gtk.Container):
for i in visible_rows:
cell = i.row[0].widget.allocation
try_insert_spare_row(cell_y, cell.y)
+ self._allocate_cells(i.row, cell.y)
cell_y = cell.y + cell.height
+ frame_rows.append(i.row)
try_insert_spare_row(cell_y, page_end)
- if self.editing and self._selected_index not in self._frame_range:
- self.editing = False
-
self._bin_window.move(0, int(-pos))
self._bin_window.process_updates(True)
+ if frame_rows:
+ frame_range = (frame_rows[0][0].index, frame_rows[-1][-1].index)
+ else:
+ frame_range = None
+ if frame_range != self._frame_range:
+ self._frame_range = frame_range
+ self.emit('frame-scrolled')
+
+ if self.editing and self._selected_index not in self.frame_range:
+ self.editing = False
+
def __adjustment_value_changed_cb(self, adjustment):
self._allocate_rows(force=False)
diff --git a/src/jarabe/journal/homogeneview.py b/src/jarabe/journal/homogeneview.py
index 764ecfe..3176e76 100644
--- a/src/jarabe/journal/homogeneview.py
+++ b/src/jarabe/journal/homogeneview.py
@@ -30,13 +30,14 @@ class Cell(gtk.EventBox):
gtk.EventBox.__init__(self)
self.select(False)
- def do_fill_in_cell_content(self, table, metadata):
+ def do_fill_in_cell_content(self, table, offset, metadata):
# needs to be overriden
pass
def do_fill_in(self, table, cell_index):
- table.result_set.seek(cell_index)
- self.do_fill_in_cell_content(table, table.result_set.read())
+ result_set = table.get_result_set()
+ result_set.seek(cell_index)
+ self.do_fill_in_cell_content(table, cell_index, result_set.read())
if table.hover_selection:
self.select(table.cursor == cell_index)
@@ -82,8 +83,6 @@ class HomogeneView(VHomogeneTable):
else:
self.cell_count = result_set_length
- result_set = property(get_result_set, set_result_set)
-
def __cursor_changed_cb(self, table, old_cursor):
if not self.hover_selection:
return
diff --git a/src/jarabe/journal/listview.py b/src/jarabe/journal/listview.py
index f4406b2..a820ca4 100644
--- a/src/jarabe/journal/listview.py
+++ b/src/jarabe/journal/listview.py
@@ -56,9 +56,9 @@ class _Cell(Cell):
self.show_all()
- def do_fill_in_cell_content(self, table, metadata):
+ def do_fill_in_cell_content(self, table, offset, metadata):
for i in self._row.get_children():
- i.check_out(metadata)
+ i.fill_in(metadata)
class ListView(HomogeneView):
diff --git a/src/jarabe/journal/model.py b/src/jarabe/journal/model.py
index 85b4f46..b512fb6 100644
--- a/src/jarabe/journal/model.py
+++ b/src/jarabe/journal/model.py
@@ -288,7 +288,8 @@ class InplaceResultSet(BaseResultSet):
metadata['mountpoint'] = self._mount_point
entries.append(metadata)
- logging.debug('InplaceResultSet.find took %f s.', time.time() - t)
+ logging.debug('InplaceResultSet.find took %f s. for %s entries',
+ time.time() - t, total_count)
return entries, total_count
@@ -408,16 +409,33 @@ def _get_mount_point(path):
else:
dir_path = dir_path.rsplit(os.sep, 1)[0]
-def get(object_id):
+def get(object_id, reply_cb=None):
"""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)
- else:
+ elif reply_cb is None:
metadata = _get_datastore().get_properties(object_id, byte_arrays=True)
metadata['mountpoint'] = '/'
+ else:
+ def apply_cb(metadata):
+ metadata['mountpoint'] = '/'
+ reply_cb(metadata)
+
+ def error_cb(e):
+ logging.error('Cannot get object %s', object_id)
+ reply_cb(None)
+
+ _get_datastore().get_properties(object_id, byte_arrays=True,
+ reply_handler=reply_cb,
+ error_handler=lambda e: reply_cb(None))
+ return None
+
+ if reply_cb is not None:
+ reply_cb(metadata)
+
return metadata
def get_file(object_id):
diff --git a/src/jarabe/journal/preview.py b/src/jarabe/journal/preview.py
new file mode 100644
index 0000000..1670884
--- /dev/null
+++ b/src/jarabe/journal/preview.py
@@ -0,0 +1,216 @@
+# 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
+# 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
+import os
+import math
+
+import gtk
+import gio
+import gobject
+
+from sugar import dispatch
+from sugar.util import LRU
+from sugar.graphics import style
+
+from jarabe.journal import model
+
+
+fetched = dispatch.Signal()
+
+THUMB_WIDTH = style.zoom(240)
+THUMB_HEIGHT = style.zoom(180)
+
+_CHUNK_SIZE = 1024 * 10 # 10K
+_MAX_FILESIZE = 1024 * 1024 * 10 # 10M
+
+_fetch_queue = []
+
+
+def fetch(offset, metadata):
+ entry = _CacheEntry(offset, metadata)
+
+ if entry not in _fetch_queue:
+ _fetch_queue.append(entry)
+ if len(_fetch_queue) == 1:
+ gobject.idle_add(_process_queue)
+
+def discard_queue(visible_range):
+ new_queue = []
+
+ for i in _fetch_queue:
+ if i.offset in visible_range:
+ new_queue.append(i)
+
+ global _fetch_queue
+ _fetch_queue = new_queue
+
+def _process_queue():
+ while len(_fetch_queue):
+ entry = _fetch_queue[0]
+
+ logging.debug('Loading preview for %s', entry.uid)
+
+ if entry.uid.startswith('/'):
+ if not os.path.isfile(entry.uid):
+ logging.warning('Preview %s is not a file', entry.uid)
+ _commit(entry, None)
+ elif os.path.getsize(entry.uid) > _MAX_FILESIZE:
+ logging.debug('Preview %s is too big to load', entry.uid)
+ _commit(entry, None)
+ else:
+ _AsyncLoader(entry)
+ else:
+ _load_props(entry)
+
+ break
+
+ return False
+
+def _commit(entry, pixbuf):
+ if not _fetch_queue or _fetch_queue[0] != entry:
+ logging.debug('Discard %r preview', entry.uid)
+ else:
+ del _fetch_queue[0]
+
+ if pixbuf is None:
+ logging.debug('Empty preview for %s', entry.uid)
+ else:
+ logging.debug('Ready preview for %s', entry.uid)
+ fetched.send(None, offset=entry.offset, pixbuf=pixbuf)
+
+ if len(_fetch_queue):
+ gobject.idle_add(_process_queue)
+
+def _load_preview(entry, preview):
+ if not preview:
+ logging.debug('Empty preview for %s', entry.uid)
+ _commit(entry, None)
+ return
+
+ if preview[1:4] != 'PNG':
+ # TODO: We are close to be able to drop this.
+ import base64
+ preview = base64.b64decode(preview)
+
+ loader = gtk.gdk.PixbufLoader()
+ loader.connect('size-prepared', _size_prepared_cb)
+
+ try:
+ loader.write(preview)
+ except Exception:
+ logging.exception('Can not load preview from metadata for %s',
+ entry.uid)
+ finally:
+ loader.close()
+
+ pixbuf = loader.get_pixbuf()
+ if pixbuf is None:
+ _commit(entry, None)
+ else:
+ _commit(entry, pixbuf)
+
+def _load_props(entry):
+
+ def reply_cb(props):
+ if props is None:
+ _commit(entry, None)
+ else:
+ _load_preview(entry, props.get('preview'))
+
+ model.get(entry.uid, reply_cb)
+
+def _size_prepared_cb(loader, width, height):
+ dest_width = THUMB_WIDTH
+ dest_height = THUMB_HEIGHT
+
+ if width == dest_width and height == dest_height:
+ return
+
+ ratio_width = float(dest_width) / width
+ ratio_height = float(dest_height) / height
+ ratio = min(ratio_width, ratio_height)
+
+ # preserve original ration
+ if ratio_width != ratio:
+ dest_width = int(math.ceil(width * ratio))
+ elif ratio_height != ratio:
+ dest_height = int(math.ceil(height * ratio))
+
+ loader.set_size(dest_width, dest_height)
+
+
+class _AsyncLoader(object):
+
+ def __init__(self, entry):
+ self._entry = entry
+
+ self._loader = gtk.gdk.PixbufLoader()
+ self._loader.connect('size-prepared', _size_prepared_cb)
+
+ self._stream = None
+ self._file = gio.File(entry.uid)
+ self._file.read_async(self.__file_read_async_cb)
+
+ def __file_read_async_cb(self, input_file, result):
+ try:
+ self._stream = self._file.read_finish(result)
+ except Exception:
+ logging.exception('Can not read preview for %s', self._entry.uid)
+ _commit(self._entry, None)
+ return
+
+ self._stream.read_async(_CHUNK_SIZE, self.__stream_read_async_cb,
+ gobject.PRIORITY_LOW)
+
+ def __stream_read_async_cb(self, input_stream, result):
+ data = self._stream.read_finish(result)
+
+ if data and self._process_loader(self._loader.write, data):
+ self._stream.read_async(_CHUNK_SIZE, self.__stream_read_async_cb,
+ gobject.PRIORITY_LOW)
+ return
+
+ if data is None:
+ logging.warning('Bad preview data from %s', self._entry.uid)
+
+ self._stream.close()
+
+ if self._process_loader(self._loader.close):
+ _commit(self._entry, self._loader.get_pixbuf())
+ else:
+ _commit(self._entry, None)
+
+ def _process_loader(self, method, *args):
+ try:
+ method(*args)
+ except Exception, e:
+ logging.debug('Can not process preview for %s: %r',
+ self._entry.uid, e)
+ return False
+ else:
+ return True
+
+class _CacheEntry(object):
+ uid = None
+ offset = None
+
+ def __init__(self, offset, metadata):
+ self.uid = metadata['uid']
+ self.offset = offset
+
+ def __cmp__(self, other):
+ return cmp((self.uid, self.offset), (other.uid, other.offset))
diff --git a/src/jarabe/journal/thumbsview.py b/src/jarabe/journal/thumbsview.py
index f2db248..b96698a 100644
--- a/src/jarabe/journal/thumbsview.py
+++ b/src/jarabe/journal/thumbsview.py
@@ -21,19 +21,10 @@ import logging
from jarabe.journal.homogeneview import HomogeneView
from jarabe.journal.homogeneview import Cell
from jarabe.journal.widgets import *
+from jarabe.journal import preview
-TOOLBAR_WIDTH = 20
-
-THUMB_WIDTH = 240
-THUMB_HEIGHT = 180
-
-TEXT_HEIGHT = gtk.EventBox().create_pango_layout('W').get_pixel_size()[1]
-
-CELL_WIDTH = THUMB_WIDTH + TOOLBAR_WIDTH + style.DEFAULT_PADDING + \
- style.DEFAULT_SPACING
-CELL_HEIGHT = THUMB_HEIGHT + TEXT_HEIGHT * 3 + style.DEFAULT_PADDING * 3 + \
- style.DEFAULT_SPACING
+_TEXT_HEIGHT = gtk.EventBox().create_pango_layout('W').get_pixel_size()[1]
class _Cell(Cell):
@@ -41,6 +32,8 @@ class _Cell(Cell):
def __init__(self):
Cell.__init__(self)
+ self._offset = None
+
cell = gtk.HBox()
self.add(cell)
@@ -49,43 +42,70 @@ class _Cell(Cell):
toolbar = gtk.VBox()
cell.pack_start(toolbar, expand=False)
- self._keep = KeepIcon(
- box_width=style.GRID_CELL_SIZE)
+ self._keep = KeepIcon()
toolbar.pack_start(self._keep, expand=False)
self._details = DetailsIcon()
toolbar.pack_start(self._details, expand=False)
- # thumb
+ # main
main = gtk.VBox()
cell.pack_end(main)
- #thumb = Thumb()
- #main.pack_end(thumb)
+ self._icon = ObjectIcon(
+ border=style.LINE_WIDTH,
+ border_color=style.COLOR_PANEL_GREY.get_int(),
+ box_width=preview.THUMB_WIDTH,
+ box_height=preview.THUMB_HEIGHT)
+ self._icon.show()
- # text
+ self._thumb = Thumb()
+ self._thumb.show()
- text = gtk.VBox()
- main.pack_end(text, expand=False)
+ self._thumb_box = gtk.HBox()
+ main.pack_start(self._thumb_box, expand=False)
self._title = Title(
max_line_count=2,
xalign=0, yalign=0, xscale=1, yscale=0)
- text.pack_start(self._title)
+ main.pack_start(self._title, expand=False)
self._date = Timestamp(
xalign=0.0,
ellipsize=pango.ELLIPSIZE_END)
- text.pack_end(self._date, expand=False)
+ main.pack_start(self._date, expand=False)
self.show_all()
- def do_fill_in_cell_content(self, table, metadata):
- self._keep.check_out(metadata)
- self._details.check_out(metadata)
- self._title.check_out(metadata)
- self._date.check_out(metadata)
+ def discard_thumb(self):
+ self._set_thumb_widget(self._icon)
+ self._offset = None
+
+ def do_fill_in_cell_content(self, table, offset, metadata):
+ self._keep.fill_in(metadata)
+ self._details.fill_in(metadata)
+ self._title.fill_in(metadata)
+ self._date.fill_in(metadata)
+ self._icon.fill_in(metadata)
+ self._thumb.fill_in(metadata)
+
+ if self._offset != offset:
+ self.discard_thumb()
+ preview.fetch(offset, metadata)
+ else:
+ self._set_thumb_widget(self._thumb)
+
+ def fill_pixbuf_in(self, offset, pixbuf):
+ self._offset = offset
+ self._thumb.image.set_from_pixbuf(pixbuf)
+ self._set_thumb_widget(self._thumb)
+
+ def _set_thumb_widget(self, widget):
+ if widget not in self._thumb_box.get_children():
+ for child in self._thumb_box.get_children():
+ self._thumb_box.remove(child)
+ self._thumb_box.pack_start(widget, expand=False)
class ThumbsView(HomogeneView):
@@ -93,9 +113,29 @@ class ThumbsView(HomogeneView):
def __init__(self):
HomogeneView.__init__(self, _Cell)
- def do_size_allocate(self, allocation):
- column_count = gtk.gdk.screen_width() / CELL_WIDTH
- row_count = gtk.gdk.screen_height() / CELL_HEIGHT
- self.frame_size = (row_count, column_count)
+ cell_width = preview.THUMB_WIDTH + style.SMALL_ICON_SIZE + \
+ style.DEFAULT_PADDING + style.DEFAULT_SPACING * 2
+ cell_height = preview.THUMB_HEIGHT + _TEXT_HEIGHT * 3 + \
+ style.DEFAULT_PADDING * 3 + style.DEFAULT_SPACING
+ self.cell_size = (cell_width, cell_height)
+
+ self.connect('frame-scrolled', self.__frame_scrolled_cb)
+ preview.fetched.connect(self.__preview_fetched_cb)
+
+ def set_result_set(self, result_set):
+ if result_set is self.get_result_set():
+ return
+
+ for cell in self.frame_cells:
+ cell.discard_thumb()
+
+ HomogeneView.set_result_set(self, result_set)
+
+ def __frame_scrolled_cb(self, table):
+ preview.discard_queue(table.frame_range)
- HomogeneView.do_size_allocate(self, allocation)
+ def __preview_fetched_cb(self, sender, signal, offset, pixbuf):
+ cell = self.get_cell(offset)
+ if cell is not None:
+ cell.fill_pixbuf_in(offset, pixbuf)
+ self.refill([offset])
diff --git a/src/jarabe/journal/view.py b/src/jarabe/journal/view.py
index 61b11dc..f2aa11d 100644
--- a/src/jarabe/journal/view.py
+++ b/src/jarabe/journal/view.py
@@ -63,6 +63,7 @@ class View(gtk.EventBox):
self._current_page = None
self._view = None
self._last_progress_bar_pulse = None
+ self._progress_bar = None
self._page_ctors = {
VIEW_LIST: lambda: self._view_new(ListView),
@@ -93,7 +94,7 @@ class View(gtk.EventBox):
# change view only if current page is view as well
self._page = view
self._view = view
- self.view.result_set = self._result_set
+ self.view.set_result_set(self._result_set)
view = property(get_view, set_view)
@@ -148,7 +149,7 @@ class View(gtk.EventBox):
self.refresh()
def refresh(self):
- logging.debug('View._refresh query %r', self._query)
+ logging.debug('View.refresh query %r', self._query)
if self._result_set is not None:
self._result_set.stop()
@@ -159,9 +160,6 @@ class View(gtk.EventBox):
self._result_set.progress.connect(self.__result_set_progress_cb)
self._result_set.setup()
- self._page = self._view
- self.view.result_set = self._result_set
-
def __result_set_ready_cb(self, **kwargs):
if self._result_set.length == 0:
if self._is_query_empty():
@@ -170,7 +168,7 @@ class View(gtk.EventBox):
self._page = _MESSAGE_NO_MATCH
else:
self._page = self._view
- self.view.result_set = self._result_set
+ self.view.set_result_set(self._result_set)
def _is_query_empty(self):
# FIXME: This is a hack, we shouldn't have to update this every time
@@ -188,7 +186,7 @@ class View(gtk.EventBox):
self._page = _MESSAGE_PROGRESS
if time.time() - self._last_progress_bar_pulse > 0.05:
- self.child.pulse()
+ self._progress_bar.pulse()
self._last_progress_bar_pulse = time.time()
def _view_new(self, view_class):
@@ -205,9 +203,9 @@ class View(gtk.EventBox):
def _progress_new(self):
alignment = gtk.Alignment(xalign=0.5, yalign=0.5, xscale=0.5)
- progress_bar = gtk.ProgressBar()
- progress_bar.props.pulse_step = 0.01
- alignment.add(progress_bar)
+ self._progress_bar = gtk.ProgressBar()
+ self._progress_bar.props.pulse_step = 0.01
+ alignment.add(self._progress_bar)
return alignment
diff --git a/src/jarabe/journal/widgets.py b/src/jarabe/journal/widgets.py
index 7593986..6febd86 100644
--- a/src/jarabe/journal/widgets.py
+++ b/src/jarabe/journal/widgets.py
@@ -29,7 +29,8 @@ import simplejson
from sugar.graphics import style
from sugar.graphics.icon import CanvasIcon
from sugar.graphics.xocolor import XoColor
-from sugar.graphics.palette import CanvasInvoker
+from sugar.graphics.palette import Invoker
+from sugar.graphics.palette import WidgetInvoker
from sugar.graphics.roundbox import CanvasRoundBox
from jarabe.journal.entry import Entry
@@ -38,6 +39,7 @@ from jarabe.journal.palettes import ObjectPalette
from jarabe.journal import misc
from jarabe.journal import model
from jarabe.journal import controler
+from jarabe.journal import preview
class KeepIconCanvas(CanvasIcon):
@@ -46,15 +48,15 @@ class KeepIconCanvas(CanvasIcon):
size=style.SMALL_ICON_SIZE,
**kwargs)
- self._metadata = None
+ self.metadata = None
self._prelight = False
self._keep_color = None
self.connect_after('activated', self.__activated_cb)
self.connect('motion-notify-event', self.__motion_notify_event_cb)
- def check_out(self, metadata):
- self._metadata = metadata
+ def fill_in(self, metadata):
+ self.metadata = metadata
keep = metadata.get('keep', "")
if keep.isdigit():
self._set_keep(int(keep))
@@ -96,7 +98,7 @@ class KeepIconCanvas(CanvasIcon):
self.props.xo_color = self._keep_color
def __activated_cb(self, icon):
- if not model.is_editable(self._metadata):
+ if not model.is_editable(self.metadata):
return
if self._keep_color is None:
@@ -104,8 +106,8 @@ class KeepIconCanvas(CanvasIcon):
else:
keep = 0
- self._metadata['keep'] = keep
- model.write(self._metadata, update_mtime=False)
+ self.metadata['keep'] = keep
+ model.write(self.metadata, update_mtime=False)
self._set_keep(keep)
@@ -116,19 +118,15 @@ def KeepIcon(**kwargs):
class _Launcher(object):
- def __init__(self, launching, detail):
+ def __init__(self, detail):
self.metadata = None
self._detail = detail
- self._launching = launching
- if launching:
- self.connect_after('button-release-event',
- self.__button_release_event_cb)
+ self.connect_after('button-release-event',
+ self.__button_release_event_cb)
def create_palette(self):
- if not self._launching or self.metadata is None:
- return
- else:
+ if self.metadata is not None:
return ObjectPalette(self.metadata, detail=self._detail)
def __button_release_event_cb(self, button, event):
@@ -139,12 +137,13 @@ class _Launcher(object):
class ObjectIconCanvas(_Launcher, CanvasIcon):
- def __init__(self, launching=True, detail=True, **kwargs):
+ def __init__(self, detail=True, **kwargs):
CanvasIcon.__init__(self, **kwargs)
- _Launcher.__init__(self, launching, detail)
+ _Launcher.__init__(self, detail)
- def check_out(self, metadata):
+ def fill_in(self, metadata):
self.metadata = metadata
+ self.palette = None
self.props.file_name = misc.get_icon_name(metadata)
@@ -164,26 +163,26 @@ class Title(gtk.Alignment):
def __init__(self, max_line_count=1, **kwargs):
gtk.Alignment.__init__(self, **kwargs)
- self._metadata = None
+ self.metadata = None
self._entry = Entry(max_line_count=max_line_count)
self.add(self._entry)
self._entry.connect_after('focus-out-event', self.__focus_out_event_cb)
- def check_out(self, metadata):
- self._metadata = metadata
+ def fill_in(self, metadata):
+ self.metadata = metadata
self._entry.props.text = metadata.get('title', _('Untitled'))
self._entry.props.editable = model.is_editable(metadata)
def __focus_out_event_cb(self, widget, event):
- old_title = self._metadata.get('title', None)
+ old_title = self.metadata.get('title', None)
new_title = self._entry.props.text
if old_title != new_title:
- self._metadata['title'] = new_title
- self._metadata['title_set_by_user'] = '1'
- model.write(self._metadata, update_mtime=False)
+ self.metadata['title'] = new_title
+ self.metadata['title_set_by_user'] = '1'
+ model.write(self.metadata, update_mtime=False)
class Buddies(gtk.Alignment):
@@ -201,7 +200,7 @@ class Buddies(gtk.Alignment):
self._buddies = gtk.HBox()
self._buddies.show()
- def check_out(self, metadata):
+ def fill_in(self, metadata):
if self.child is not None:
self.remove(self.child)
@@ -247,7 +246,7 @@ class Timestamp(gtk.Label):
def __init__(self, **kwargs):
gobject.GObject.__init__(self, **kwargs)
- def check_out(self, metadata):
+ def fill_in(self, metadata):
self.props.label = misc.get_date(metadata)
@@ -260,22 +259,22 @@ class DetailsIconCanvas(CanvasIcon):
size=style.SMALL_ICON_SIZE,
stroke_color=style.COLOR_TRANSPARENT.get_svg())
- self._metadata = None
+ self.metadata = None
self.connect('motion-notify-event', self.__motion_notify_event_cb)
self.connect_after('activated', self.__activated_cb)
self._set_leave_color()
- def check_out(self, metadata):
- self._metadata = metadata
+ def fill_in(self, metadata):
+ self.metadata = metadata
def _set_leave_color(self):
self.props.fill_color = style.COLOR_BUTTON_GREY.get_svg()
def __activated_cb(self, button):
self._set_leave_color()
- controler.objects.emit('detail-clicked', self._metadata['uid'])
+ controler.objects.emit('detail-clicked', self.metadata['uid'])
def __motion_notify_event_cb(self, icon, event):
if event.detail == hippo.MOTION_DETAIL_ENTER:
@@ -288,19 +287,45 @@ def DetailsIcon(**kwargs):
return _CanvasToWidget(DetailsIconCanvas, **kwargs)
-class ThumbCanvas(_Launcher, hippo.CanvasWidget):
+class Thumb(_Launcher, gtk.EventBox):
- def __init__(self, cell, **kwargs):
- hippo.CanvasWidget.__init__(self, **kwargs)
- _Launcher.__init__(self, cell)
+ def __init__(self, detail=True):
+ gtk.EventBox.__init__(self)
+ _Launcher.__init__(self, detail)
- self._palette_invoker = CanvasInvoker()
- self._palette_invoker.attach(self)
+ self.modify_fg(gtk.STATE_NORMAL,
+ style.COLOR_PANEL_GREY.get_gdk_color())
+ self.modify_bg(gtk.STATE_NORMAL,
+ style.COLOR_WHITE.get_gdk_color())
+
+ self.add_events(gtk.gdk.BUTTON_PRESS_MASK | \
+ gtk.gdk.BUTTON_RELEASE_MASK | \
+ gtk.gdk.LEAVE_NOTIFY_MASK | \
+ gtk.gdk.ENTER_NOTIFY_MASK)
+ self.set_size_request(preview.THUMB_WIDTH, preview.THUMB_HEIGHT)
+
+ self.connect_after('expose-event', self.__expose_event_cb)
+
+ self.image = gtk.Image()
+ self.image.show()
+ self.add(self.image)
+
+ self._invoker = WidgetInvoker(self)
+ self._invoker._position_hint = Invoker.AT_CURSOR
self.connect('destroy', self.__destroy_cb)
+ def __expose_event_cb(self, widget, event):
+ __, __, width, height = self.allocation
+ fg = self.style.fg_gc[gtk.STATE_NORMAL]
+ self.window.draw_rectangle(fg, False, 0, 0, width - 1, height - 1)
+
+ def fill_in(self, metadata):
+ self.metadata = metadata
+ self._invoker.palette = None
+
def __destroy_cb(self, icon):
- if self._palette_invoker is not None:
- self._palette_invoker.detach()
+ if self._invoker is not None:
+ self._invoker.detach()
class _BuddyIcon(CanvasIcon):
@@ -327,5 +352,5 @@ class _CanvasToWidget(hippo.Canvas):
self.root = canvas_class(**kwargs)
self.set_root(self.root)
- def check_out(self, metadata):
- self.root.check_out(metadata)
+ def fill_in(self, metadata):
+ self.root.fill_in(metadata)