diff options
author | Gonzalo Odiard <godiard@gmail.com> | 2012-10-05 20:49:44 (GMT) |
---|---|---|
committer | Gonzalo Odiard <godiard@gmail.com> | 2012-10-09 13:30:31 (GMT) |
commit | c411b44e29d2f156ccd2838e5edfbb2655598d97 (patch) | |
tree | f79239faf138e2635c843e650a94aa814fa19d57 | |
parent | 065e32d5ab0c42fcf51336d933cdc99febd3acca (diff) |
Show the bookmarks in a tray like in Browse activity
The preview is saved in another table in the sqlite db.
Signed-off-by: Gonzalo Odiard <gonzalo@laptop.org>
-rw-r--r-- | linkbutton.py | 108 | ||||
-rw-r--r-- | readactivity.py | 119 | ||||
-rw-r--r-- | readdb.py | 53 | ||||
-rw-r--r-- | readtoolbar.py | 17 |
4 files changed, 294 insertions, 3 deletions
diff --git a/linkbutton.py b/linkbutton.py new file mode 100644 index 0000000..3fd4579 --- /dev/null +++ b/linkbutton.py @@ -0,0 +1,108 @@ +# Copyright (C) 2007, One Laptop Per Child +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the +# Free Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. + +from gi.repository import Gtk +from gi.repository import Gdk +from gi.repository import GdkPixbuf +from gi.repository import GObject +from gi.repository import Rsvg + +import os +import StringIO +import cairo +from gettext import gettext as _ +import re +import gc +import logging + +from sugar3.graphics.palette import Palette +from sugar3.graphics.tray import TrayButton +from sugar3.graphics import style + + +class LinkButton(TrayButton, GObject.GObject): + __gtype_name__ = 'LinkButton' + __gsignals__ = { + 'remove_link': (GObject.SignalFlags.RUN_FIRST, + None, ([int])), + 'go_to_bookmark': (GObject.SignalFlags.RUN_FIRST, + None, ([int])), + } + + def __init__(self, buf, color, title, owner, page): + TrayButton.__init__(self) + + # Color read from the Journal may be Unicode, but Rsvg needs + # it as single byte string: + if isinstance(color, unicode): + color = str(color) + self.set_image(buf, color.split(',')[1], color.split(',')[0]) + + self.page = int(page) + info = title + '\n' + owner + self.setup_rollover_options(info) + + def set_image(self, buf, fill='#0000ff', stroke='#4d4c4f'): + img = Gtk.Image() + str_buf = StringIO.StringIO(buf) + thumb_surface = cairo.ImageSurface.create_from_png(str_buf) + + bg_width, bg_height = style.zoom(120), style.zoom(110) + bg_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, bg_width, + bg_height) + context = cairo.Context(bg_surface) + # draw a rectangle in the background with the selected colors + context.set_line_width(style.zoom(10)) + context.set_source_rgba(*style.Color(fill).get_rgba()) + context.rectangle(0, 0, bg_width, bg_height) + context.fill_preserve() + context.set_source_rgba(*style.Color(stroke).get_rgba()) + context.stroke() + # add the screenshot + dest_x = style.zoom(10) + dest_y = style.zoom(20) + context.set_source_surface(thumb_surface, dest_x, dest_y) + thumb_width, thumb_height = style.zoom(100), style.zoom(80) + context.rectangle(dest_x, dest_y, thumb_width, thumb_height) + context.fill() + + pixbuf_bg = Gdk.pixbuf_get_from_surface(bg_surface, 0, 0, + bg_width, bg_height) + + img.set_from_pixbuf(pixbuf_bg) + self.set_icon_widget(img) + img.show() + + def setup_rollover_options(self, info): + palette = Palette(info, text_maxlen=50) + self.set_palette(palette) + + menu_item = Gtk.MenuItem(_('Go to Bookmark')) + menu_item.connect('activate', self.go_to_bookmark_cb) + palette.menu.append(menu_item) + menu_item.show() + + menu_item = Gtk.MenuItem(_('Remove')) + menu_item.connect('activate', self.item_remove_cb) + palette.menu.append(menu_item) + menu_item.show() + + def item_remove_cb(self, widget): + self.emit('remove_link', self.page) + + def go_to_bookmark_cb(self, widget): + self.emit('go_to_bookmark', self.page) diff --git a/readactivity.py b/readactivity.py index 9efdbeb..a4272a5 100644 --- a/readactivity.py +++ b/readactivity.py @@ -23,6 +23,8 @@ import time from gettext import gettext as _ import re import md5 +import StringIO +import cairo import dbus from gi.repository import GObject @@ -42,8 +44,10 @@ from sugar3.graphics.toggletoolbutton import ToggleToolButton from sugar3.graphics.alert import ConfirmationAlert from sugar3.activity.widgets import ActivityToolbarButton from sugar3.activity.widgets import StopButton +from sugar3.graphics.tray import HTray from sugar3 import network from sugar3 import mime +from sugar3 import profile from sugar3.datastore import datastore from sugar3.graphics.objectchooser import ObjectChooser @@ -54,6 +58,7 @@ from readtoolbar import ViewToolbar from bookmarkview import BookmarkView from readdb import BookmarkManager from sugarmenuitem import SugarMenuItem +from linkbutton import LinkButton _HARDWARE_MANAGER_INTERFACE = 'org.laptop.HardwareManager' _HARDWARE_MANAGER_SERVICE = 'org.laptop.HardwareManager' @@ -151,6 +156,9 @@ class ReadActivity(activity.Activity): self._bookmark_view.connect('bookmark-changed', self._update_bookmark_cb) + tray = HTray() + self.set_tray(tray, Gtk.PositionType.BOTTOM) + toolbar_box = ToolbarBox() self.activity_button = ActivityToolbarButton(self) @@ -177,6 +185,8 @@ class ReadActivity(activity.Activity): self.__view_toolbar_go_fullscreen_cb) self._view_toolbar.connect('toggle-index-show', self.__toogle_navigator_cb) + self._view_toolbar.connect('toggle-tray-show', + self.__toogle_tray_cb) view_toolbar_button = ToolbarButton( page=self._view_toolbar, icon_name='toolbar-view') @@ -444,6 +454,14 @@ class ReadActivity(activity.Activity): if scrollbar_pos > -1: self._view.set_vertical_pos(scrollbar_pos) + def __toogle_tray_cb(self, button, visible): + if visible: + logging.error('Show tray') + self.tray.show() + else: + logging.error('Hide tray') + self.tray.hide() + def __num_page_entry_insert_text_cb(self, entry, text, length, position): if not re.match('[0-9]', text): entry.emit_stop_by_name('insert-text') @@ -853,6 +871,24 @@ class ReadActivity(activity.Activity): filehash = get_md5(filepath) self._bookmarkmanager = BookmarkManager(filehash) + self._bookmarkmanager.connect('added_bookmark', + self._added_bookmark_cb) + self._bookmarkmanager.connect('removed_bookmark', + self._removed_bookmark_cb) + + # Add the bookmarks to the tray + color = profile.get_color().to_string() + owner = profile.get_nick_name() + for bookmark in self._bookmarkmanager.get_bookmarks(): + page = bookmark.page_no + title = _('Page %d') % page + thumb = self._bookmarkmanager.get_bookmark_preview(page) + if thumb is None: + logging.error('Preview NOT FOUND') + thumb = self._get_screenshot() + logging.error('Add link to tray page %d preview %s', page, thumb) + self._add_link_totray(page, thumb, color, title, owner) + self._bookmark_view.set_bookmarkmanager(self._bookmarkmanager) self._update_toc() self._view.connect_page_changed_handler(self.__page_changed_cb) @@ -1042,3 +1078,86 @@ class ReadActivity(activity.Activity): def __view_toolbar_go_fullscreen_cb(self, view_toolbar): self.fullscreen() + + def _added_bookmark_cb(self, bookmarkmanager, page): + logging.error('Bookmark added page %d', page) + title = _('Page %d') % page + color = profile.get_color().to_string() + owner = profile.get_nick_name() + thumb = self._get_screenshot() + self._add_link_totray(page, thumb, color, title, owner) + bookmarkmanager.add_bookmark_preview(page - 1, thumb) + + def _removed_bookmark_cb(self, bookmarkmanager, page): + logging.error('Bookmark removed page %d', page) + # remove button from tray + for button in self.tray.get_children(): + if button.page == page: + self.tray.remove_item(button) + if len(self.tray.get_children()) == 0: + self.tray.hide() + self._view_toolbar.traybutton.props.active = False + + def _add_link_totray(self, page, buf, color, title, owner): + ''' add a link to the tray ''' + item = LinkButton(buf, color, title, owner, page) + item.connect('clicked', self._bookmark_button_clicked_cb, page) + item.connect('remove_link', self._bookmark_button_removed_cb) + self.tray.show() + self.tray.add_item(item) + item.show() + self._view_toolbar.traybutton.props.active = True + + def _bookmark_button_clicked_cb(self, button, page): + self._view.set_current_page(page - 1) + + def _bookmark_button_removed_cb(self, button, page): + self._bookmark_view.del_bookmark(page - 1) + + def _get_screenshot(self): + """Copied from activity.get_preview() + """ + if self.canvas is None or not hasattr(self.canvas, 'get_window'): + return None + + window = self.canvas.get_window() + alloc = self.canvas.get_allocation() + + dummy_cr = Gdk.cairo_create(window) + target = dummy_cr.get_target() + canvas_width, canvas_height = alloc.width, alloc.height + screenshot_surface = target.create_similar(cairo.CONTENT_COLOR, + canvas_width, canvas_height) + del dummy_cr, target + + cr = cairo.Context(screenshot_surface) + r, g, b, a_ = style.COLOR_PANEL_GREY.get_rgba() + cr.set_source_rgb(r, g, b) + cr.paint() + self.canvas.draw(cr) + del cr + + preview_width, preview_height = style.zoom(100), style.zoom(80) + preview_surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, + preview_width, preview_height) + cr = cairo.Context(preview_surface) + + scale_w = preview_width * 1.0 / canvas_width + scale_h = preview_height * 1.0 / canvas_height + scale = min(scale_w, scale_h) + + translate_x = int((preview_width - (canvas_width * scale)) / 2) + translate_y = int((preview_height - (canvas_height * scale)) / 2) + + cr.translate(translate_x, translate_y) + cr.scale(scale, scale) + + cr.set_source_rgba(1, 1, 1, 0) + cr.set_operator(cairo.OPERATOR_SOURCE) + cr.paint() + cr.set_source_surface(screenshot_surface) + cr.paint() + + preview_str = StringIO.StringIO() + preview_surface.write_to_png(preview_str) + return preview_str.getvalue() @@ -21,7 +21,9 @@ import os import shutil import sqlite3 import time +import base64 +from gi.repository import GObject from sugar3 import profile from readbookmark import Bookmark @@ -75,9 +77,24 @@ def _init_db_highlights(conn): conn.commit() -class BookmarkManager: +def _init_db_previews(conn): + conn.execute('CREATE TABLE IF NOT EXISTS PREVIEWS ' + + '(md5 TEXT, page INTEGER, ' + + 'preview)') + conn.commit() + + +class BookmarkManager(GObject.GObject): + + __gsignals__ = { + 'added_bookmark': (GObject.SignalFlags.RUN_FIRST, + None, ([int])), + 'removed_bookmark': (GObject.SignalFlags.RUN_FIRST, + None, ([int])), + } def __init__(self, filehash): + GObject.GObject.__init__(self) self._filehash = filehash dbpath = _init_db() @@ -86,6 +103,7 @@ class BookmarkManager: self._conn = sqlite3.connect(dbpath) _init_db_highlights(self._conn) + _init_db_previews(self._conn) self._conn.text_factory = lambda x: unicode(x, "utf-8", "ignore") @@ -98,6 +116,7 @@ class BookmarkManager: self._color = profile.get_color().to_string() def add_bookmark(self, page, content, local=1): + logging.debug('add_bookmark page %d', page) # locale = 0 means that this is a bookmark originally # created by the person who originally shared the file timestamp = time.time() @@ -108,16 +127,40 @@ class BookmarkManager: self._conn.commit() self._resync_bookmark_cache() + self.emit('added_bookmark', page + 1) def del_bookmark(self, page): # We delete only the locally made bookmark - + logging.debug('del_bookmark page %d', page) t = (self._filehash, page, self._user) self._conn.execute('delete from bookmarks ' + \ 'where md5=? and page=? and user=?', t) self._conn.commit() - + self._del_bookmark_preview(page) self._resync_bookmark_cache() + self.emit('removed_bookmark', page + 1) + + def add_bookmark_preview(self, page, preview): + logging.debug('add_bookmark_preview page %d', page) + t = (self._filehash, page, base64.b64encode(preview)) + self._conn.execute('insert into previews values ' + \ + '(?, ?, ?)', t) + self._conn.commit() + + def _del_bookmark_preview(self, page): + logging.debug('del_bookmark_preview page %d', page) + t = (self._filehash, page) + self._conn.execute('delete from previews ' + \ + 'where md5=? and page=?', t) + self._conn.commit() + + def get_bookmark_preview(self, page): + logging.debug('get_bookmark page %d', page) + rows = self._conn.execute('select preview from previews ' + \ + 'where md5=? and page=?', (self._filehash, page)) + for row in rows: + return base64.b64decode(row[0]) + return None def _populate_bookmarks(self): # TODO: Figure out if caching the entire set of bookmarks @@ -127,6 +170,10 @@ class BookmarkManager: for row in rows: self._bookmarks.append(Bookmark(row)) + logging.debug('loading %d bookmarks', len(self._bookmarks)) + + def get_bookmarks(self): + return self._bookmarks def get_bookmarks_for_page(self, page): bookmarks = [] diff --git a/readtoolbar.py b/readtoolbar.py index 737c55d..6d1e02b 100644 --- a/readtoolbar.py +++ b/readtoolbar.py @@ -167,6 +167,8 @@ class ViewToolbar(Gtk.Toolbar): ([])), 'toggle-index-show': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, ([bool])), + 'toggle-tray-show': (GObject.SignalFlags.RUN_FIRST, GObject.TYPE_NONE, + ([bool])), } def __init__(self): @@ -224,6 +226,14 @@ class ViewToolbar(Gtk.Toolbar): self.insert(self._fullscreen, -1) self._fullscreen.show() + self.traybutton = ToggleToolButton('tray-show') + self.traybutton.set_icon_name('tray-favourite') + self.traybutton.connect('toggled', self.__tray_toggled_cb) + #self.traybutton.props.sensitive = False + self.traybutton.props.active = False + self.insert(self.traybutton, -1) + self.traybutton.show() + self._view_notify_zoom_handler = None def set_view(self, view): @@ -275,3 +285,10 @@ class ViewToolbar(Gtk.Toolbar): def _fullscreen_cb(self, button): self.emit('go-fullscreen') + + def __tray_toggled_cb(self, button): + self.emit('toggle-tray-show', button.get_active()) + if button.props.active: + self.traybutton.set_tooltip(_('Show Tray')) + else: + self.traybutton.set_tooltip(_('Hide Tray')) |