From ac6ac17eaad5859e570d82470d7c7dbb00d02477 Mon Sep 17 00:00:00 2001 From: Sayamindu Dasgupta Date: Sun, 15 Feb 2009 23:28:29 +0000 Subject: Bookmark support --- diff --git a/read.db b/read.db new file mode 100644 index 0000000..5268778 --- /dev/null +++ b/read.db Binary files differ diff --git a/readactivity.py b/readactivity.py index b462a4b..9cf1338 100644 --- a/readactivity.py +++ b/readactivity.py @@ -34,6 +34,7 @@ from sugar.datastore import datastore from sugar.graphics.objectchooser import ObjectChooser from readtoolbar import EditToolbar, ReadToolbar, ViewToolbar +from readsidebar import Sidebar _HARDWARE_MANAGER_INTERFACE = 'org.laptop.HardwareManager' _HARDWARE_MANAGER_SERVICE = 'org.laptop.HardwareManager' @@ -109,6 +110,9 @@ class ReadActivity(activity.Activity): self._view.set_screen_dpi(_get_screen_dpi()) self._view.connect('notify::has-selection', self._view_notify_has_selection_cb) + + self._sidebar = Sidebar() + self._sidebar.show() toolbox = activity.ActivityToolbox(self) @@ -122,7 +126,7 @@ class ReadActivity(activity.Activity): toolbox.add_toolbar(_('Edit'), self._edit_toolbar) self._edit_toolbar.show() - self._read_toolbar = ReadToolbar(self._view) + self._read_toolbar = ReadToolbar(self._view, self._sidebar) toolbox.add_toolbar(_('Read'), self._read_toolbar) self._read_toolbar.show() @@ -137,15 +141,20 @@ class ReadActivity(activity.Activity): self.set_toolbox(toolbox) toolbox.show() - scrolled = gtk.ScrolledWindow() - scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - scrolled.props.shadow_type = gtk.SHADOW_NONE + self._scrolled = gtk.ScrolledWindow() + self._scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + self._scrolled.props.shadow_type = gtk.SHADOW_NONE - scrolled.add(self._view) + self._scrolled.add(self._view) self._view.show() - - self.set_canvas(scrolled) - scrolled.show() + + hbox = gtk.HBox() + hbox.pack_start(self._sidebar, expand=False, fill=False) + hbox.pack_start(self._scrolled, expand=True, fill=True) + + self.set_canvas(hbox) + self._scrolled.show() + hbox.show() # Set up for idle suspend self._idle_timer = 0 @@ -170,9 +179,9 @@ class ReadActivity(activity.Activity): _HARDWARE_MANAGER_OBJECT_PATH) self._service = dbus.Interface(proxy, _HARDWARE_MANAGER_INTERFACE) - scrolled.props.vadjustment.connect("value-changed", + self._scrolled.props.vadjustment.connect("value-changed", self._user_action_cb) - scrolled.props.hadjustment.connect("value-changed", + self._scrolled.props.hadjustment.connect("value-changed", self._user_action_cb) self.connect("focus-in-event", self._focus_in_event_cb) self.connect("focus-out-event", self._focus_out_event_cb) @@ -447,7 +456,7 @@ class ReadActivity(activity.Activity): self._want_document = False self._view.set_document(self._document) self._edit_toolbar.set_document(self._document) - self._read_toolbar.set_document(self._document) + self._read_toolbar.set_document(self._document, filepath) if not self.metadata['title_set_by_user'] == '1': info = self._document.get_info() @@ -461,14 +470,14 @@ class ReadActivity(activity.Activity): _logger.debug('Found sizing mode: %s', sizing_mode) if sizing_mode == "best-fit": self._view.props.sizing_mode = evince.SIZING_BEST_FIT - self._view.update_view_size(self.canvas) + self._view.update_view_size(self._scrolled) elif sizing_mode == "free": self._view.props.sizing_mode = evince.SIZING_FREE self._view.props.zoom = float(self.metadata.get('Read_zoom', '1.0')) _logger.debug('Set zoom to %f', self._view.props.zoom) elif sizing_mode == "fit-width": self._view.props.sizing_mode = evince.SIZING_FIT_WIDTH - self._view.update_view_size(self.canvas) + self._view.update_view_size(self._scrolled) else: # this may happen when we get a document from a buddy with a later # version of Read, for example. @@ -578,7 +587,7 @@ class ReadActivity(activity.Activity): _logger.debug("Keyname Release: %s, time: %s", keyname, event.time) def __view_toolbar_needs_update_size_cb(self, view_toolbar): - self._view.update_view_size(self.canvas) + self._view.update_view_size(self._scrolled) def __view_toolbar_go_fullscreen_cb(self, view_toolbar): self.fullscreen() diff --git a/readbookmark.py b/readbookmark.py new file mode 100644 index 0000000..532e618 --- /dev/null +++ b/readbookmark.py @@ -0,0 +1,15 @@ +class Bookmark: + def __init__(self, data): + self.md5 = data[0] + self.page_no = data[1] + self.title = data[2] + self.timestamp = data[3] + self.nick = data[4] + self.color = data[5] + self.local = data[6] + + def belongstopage(self, page_no): + return self.page_no == page_no + + def is_local(self): + return bool(self.local) \ No newline at end of file diff --git a/readdb.py b/readdb.py new file mode 100644 index 0000000..592313b --- /dev/null +++ b/readdb.py @@ -0,0 +1,65 @@ +import logging + +import sqlite3 +import time + +import gconf + +from readbookmark import Bookmark + +_logger = logging.getLogger('read-activity') + +class BookmarkManager: + def __init__(self, hash, dbpath='read.db'): + self._hash = hash + self._conn = sqlite3.connect(dbpath) + self._cur = self._conn.cursor() + + self._bookmarks = [] + self._populate_bookmarks() + + def add_bookmark(self, page, title, local=1): + # locale = 0 means that this is a bookmark originally + # created by the person who originally shared the file + timestamp = time.time() + client = gconf.client_get_default() + user = client.get_string("/desktop/sugar/user/nick") + color = client.get_string("/desktop/sugar/user/color") + + t = (self._hash, page, title, timestamp, user, color, local) + self._cur.execute('insert into bookmarks values (?, ?, ?, ?, ?, ?, ?)', t) + self._conn.commit() + + self._resync_bookmark_cache() + + def del_bookmark(self, page): + client = gconf.client_get_default() + user = client.get_string("/desktop/sugar/user/nick") + + # We delete only the locally made bookmark + + t = (self._hash, page, user) + self._cur.execute('delete from bookmarks where md5=? and page=? and user=?', t) + self._conn.commit() + + self._resync_bookmark_cache() + + def _populate_bookmarks(self): + # TODO: Figure out if caching the entire set of bookmarks is a good idea or not + self._cur.execute('select * from bookmarks where md5=?', (self._hash,)) + + for row in self._cur: + self._bookmarks.append(Bookmark(row)) + + def get_bookmarks_for_page(self, page): + bookmarks = [] + for bookmark in self._bookmarks: + if bookmark.belongstopage(page): + bookmarks.append(bookmark) + + return bookmarks + + def _resync_bookmark_cache(self): + # To be called when a new bookmark has been added/removed + self._bookmarks = [] + self._populate_bookmarks() \ No newline at end of file diff --git a/readsidebar.py b/readsidebar.py new file mode 100644 index 0000000..508376e --- /dev/null +++ b/readsidebar.py @@ -0,0 +1,78 @@ +import logging + +import gtk + +from sugar.graphics.icon import Icon +from sugar.graphics.xocolor import XoColor + +from readbookmark import Bookmark +from readdb import BookmarkManager + +from gettext import gettext as _ + +_logger = logging.getLogger('read-activity') + +class Sidebar(gtk.EventBox): + def __init__(self): + gtk.EventBox.__init__(self) + self.set_size_request(20, -1) + # Take care of the background first + white = gtk.gdk.color_parse("white") + self.modify_bg(gtk.STATE_NORMAL, white) + + self._box = gtk.VButtonBox() + self._box.set_layout(gtk.BUTTONBOX_CENTER) + self.add(self._box) + + self._box.show() + self.show() + + self._bookmarks = [] + self._bookmark_manager = None + self._is_showing_local_bookmark = False + + def _add_bookmark_icon(self, bookmark): + xocolor = XoColor(bookmark.color) + bookmark_icon = Icon(icon_name = 'emblem-favorite', \ + pixel_size = 18, xo_color = xocolor) + bookmark_icon.set_tooltip_text(_("Stupid tooltip")) + self._box.pack_start(bookmark_icon ,expand=False,fill=False) + bookmark_icon.show_all() + + self._bookmarks.append(bookmark_icon) + + if bookmark.is_local(): + self._is_showing_local_bookmark = True + + def _clear_bookmarks(self): + for bookmark_icon in self._bookmarks: + bookmark_icon.hide() #XXX: Is this needed?? + bookmark_icon.destroy() + + self._bookmarks = [] + + self._is_showing_local_bookmark = False + + def set_bookmarkmanager(self, filehash): + self._bookmark_manager = BookmarkManager(filehash) + + def update_for_page(self, page): + self._clear_bookmarks() + if self._bookmark_manager is None: + return + + bookmarks = self._bookmark_manager.get_bookmarks_for_page(page) + + for bookmark in bookmarks: + self._add_bookmark_icon(bookmark) + + def add_bookmark(self, page): + self._bookmark_manager.add_bookmark(page, '') #TODO: Implement title support + self.update_for_page(page) + + def del_bookmark(self, page): + self._bookmark_manager.del_bookmark(page) + self.update_for_page(page) + + def is_showing_local_bookmark(self): + return self._is_showing_local_bookmark \ No newline at end of file diff --git a/readtoolbar.py b/readtoolbar.py index aa22b3b..5baf310 100644 --- a/readtoolbar.py +++ b/readtoolbar.py @@ -23,10 +23,28 @@ import gobject import gtk import evince +import md5 + from sugar.graphics.toolbutton import ToolButton +from sugar.graphics.toggletoolbutton import ToggleToolButton from sugar.graphics.menuitem import MenuItem from sugar.graphics import iconentry from sugar.activity import activity +from sugar.graphics.icon import Icon +from sugar.graphics.xocolor import XoColor + +def get_md5(filename): #FIXME: Should be moved somewhere else + filename = filename.replace('file://', '') #XXX: hack + fh = open(filename) + digest = md5.new() + while 1: + buf = fh.read(4096) + if buf == "": + break + digest.update(buf) + fh.close() + return digest.hexdigest() + class EditToolbar(activity.EditToolbar): __gtype_name__ = 'EditToolbar' @@ -164,10 +182,11 @@ class EditToolbar(activity.EditToolbar): class ReadToolbar(gtk.Toolbar): __gtype_name__ = 'ReadToolbar' - def __init__(self, evince_view): + def __init__(self, evince_view, sidebar): gtk.Toolbar.__init__(self) self._evince_view = evince_view + self._sidebar = sidebar self._document = None self._back = ToolButton('go-previous') @@ -236,13 +255,28 @@ class ReadToolbar(gtk.Toolbar): self.insert(navitem, -1) navitem.show() - - def set_document(self, document): + spacer = gtk.SeparatorToolItem() + self.insert(spacer, -1) + spacer.show() + + bookmarkitem = gtk.ToolItem() + self._bookmarker = ToggleToolButton('emblem-favorite') + self._bookmarker_toggle_handler_id = self._bookmarker.connect('toggled', + self._bookmarker_toggled_cb) + + bookmarkitem.add(self._bookmarker) + + self.insert(bookmarkitem, -1) + bookmarkitem.show_all() + + def set_document(self, document, filepath): + hash = get_md5(filepath) self._document = document page_cache = self._document.get_page_cache() page_cache.connect('page-changed', self._page_changed_cb) self._update_nav_buttons() self._update_toc() + self._sidebar.set_bookmarkmanager(hash) def _num_page_entry_insert_text_cb(self, entry, text, length, position): if not re.match('[0-9]', text): @@ -269,13 +303,26 @@ class ReadToolbar(gtk.Toolbar): def _go_forward_cb(self, button): self._evince_view.next_page() + + def _bookmarker_toggled_cb(self, button): + page = self._document.get_page_cache().get_current_page() + if self._bookmarker.props.active: + self._sidebar.add_bookmark(page) + else: + self._sidebar.del_bookmark(page) def _page_changed_cb(self, page, proxy): self._update_nav_buttons() if hasattr(self._document, 'has_document_links'): if self._document.has_document_links(): self._toc_select_active_page() + + self._sidebar.update_for_page(self._document.get_page_cache().get_current_page()) + self._bookmarker.handler_block(self._bookmarker_toggle_handler_id) + self._bookmarker.props.active = self._sidebar.is_showing_local_bookmark() + self._bookmarker.handler_unblock(self._bookmarker_toggle_handler_id) + def _update_nav_buttons(self): current_page = self._document.get_page_cache().get_current_page() self._back.props.sensitive = current_page > 0 -- cgit v0.9.1