diff options
author | Sayamindu Dasgupta <sayamindu@gmail.com> | 2009-06-25 22:05:32 (GMT) |
---|---|---|
committer | Sayamindu Dasgupta <sayamindu@gmail.com> | 2009-06-25 22:05:32 (GMT) |
commit | a39b0e3068d04563fb1c8c1c2d071cdbd31a67df (patch) | |
tree | e23c6864ecd8bbc8d16147109b9095f658d7b58c | |
parent | 3274873efe3e2ff5bc9d0c7d6710797018092e20 (diff) | |
parent | ce69715f8a8036a2681d459d1fef9f9533dd50dc (diff) |
Fix merge conflicts
-rw-r--r-- | activity/activity.info | 4 | ||||
-rw-r--r-- | read.db | bin | 0 -> 2048 bytes | |||
-rw-r--r-- | readactivity.py | 42 | ||||
-rw-r--r-- | readbookmark.py | 32 | ||||
-rw-r--r-- | readdb.py | 122 | ||||
-rw-r--r-- | readsidebar.py | 104 | ||||
-rw-r--r-- | readtoolbar.py | 93 |
7 files changed, 373 insertions, 24 deletions
diff --git a/activity/activity.info b/activity/activity.info index dd6a51b..e0dcd5d 100644 --- a/activity/activity.info +++ b/activity/activity.info @@ -4,6 +4,6 @@ service_name = org.laptop.sugar.ReadActivity icon = activity-read exec = sugar-activity readactivity.ReadActivity show_launcher = no -activity_version = 67 -mime_types = application/pdf;image/vnd.djvu;image/x.djvu;image/tiff +activity_version = 63 +mime_types = application/pdf;image/vnd.djvu;image/x.djvu;image/tiff;application/x-cbz;application/x-cbr license = GPLv2+ Binary files differdiff --git a/readactivity.py b/readactivity.py index 75e847e..c876e22 100644 --- a/readactivity.py +++ b/readactivity.py @@ -35,6 +35,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' @@ -100,7 +101,7 @@ class ReadActivity(activity.Activity): if hasattr(evince, 'evince_embed_init'): # if we use evince-2.24 evince.evince_embed_init() - + self._document = None self._fileserver = None self._object_id = handle.object_id @@ -114,6 +115,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) @@ -127,7 +131,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() @@ -142,15 +146,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 @@ -175,9 +184,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) @@ -276,8 +285,9 @@ class ReadActivity(activity.Activity): def read_file(self, file_path): """Load a file from the datastore on activity start.""" _logger.debug('ReadActivity.read_file: %s', file_path) + extension = os.path.splitext(file_path)[1] tempfile = os.path.join(self.get_activity_root(), 'instance', - 'tmp%i' % time.time()) + 'tmp%i%s' % (time.time(), extension)) os.link(file_path, tempfile) self._tempfile = tempfile self._load_document('file://' + self._tempfile) @@ -452,7 +462,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() @@ -466,14 +476,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. @@ -583,7 +593,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..e4a410e --- /dev/null +++ b/readbookmark.py @@ -0,0 +1,32 @@ +# Copyright 2009 One Laptop Per Child +# Author: Sayamindu Dasgupta <sayamindu@laptop.org> +# +# 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 + +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..c7d290a --- /dev/null +++ b/readdb.py @@ -0,0 +1,122 @@ +# Copyright 2009 One Laptop Per Child +# Author: Sayamindu Dasgupta <sayamindu@laptop.org> +# +# 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, os.path +import shutil +import sqlite3 +import time + +import gconf + +from readbookmark import Bookmark + +_logger = logging.getLogger('read-activity') + +class BookmarkManager: + def __init__(self, filehash, dbfile='read.db'): + self._filehash = filehash + + dbpath = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'], 'data', \ + dbfile) + + if not os.path.exists(dbpath): + # This makes me nervous + srcpath = os.path.join(os.environ['SUGAR_BUNDLE_PATH'], 'read.db') + shutil.copy(srcpath, dbpath) + + self._conn = sqlite3.connect(dbpath) + + 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._filehash, page, title, timestamp, user, color, local) + self._conn.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._filehash, page, user) + self._conn.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 + rows = self._conn.execute('select * from bookmarks where md5=? order by page', (self._filehash,)) + + for row in rows: + 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() + + + def get_prev_bookmark_for_page(self, page, wrap = True): + if not len(self._bookmarks): + return None + + if page <= self._bookmarks[0].page_no and wrap: + return self._bookmarks[-1] + else: + for i in range(page-1, -1, -1): + for bookmark in self._bookmarks: + if bookmark.belongstopage(i): + return bookmark + + return None + + + def get_next_bookmark_for_page(self, page, wrap = True): + if not len(self._bookmarks): + return None + + if page >= self._bookmarks[-1].page_no and wrap: + return self._bookmarks[0] + else: + for i in range(page+1, self._bookmarks[-1].page_no + 1): + for bookmark in self._bookmarks: + if bookmark.belongstopage(i): + return bookmark + + return None diff --git a/readsidebar.py b/readsidebar.py new file mode 100644 index 0000000..d5280cd --- /dev/null +++ b/readsidebar.py @@ -0,0 +1,104 @@ +# Copyright 2009 One Laptop Per Child +# Author: Sayamindu Dasgupta <sayamindu@laptop.org> +# +# 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 time + +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) + + tooltip_text = (_('Bookmark added by %(user)s on %(time)s') \ + % {'user': bookmark.nick, 'time': time.ctime(bookmark.timestamp)}) + bookmark_icon.set_tooltip_text(tooltip_text) + + 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 get_bookmarkmanager(self): + return (self._bookmark_manager) + + 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 + diff --git a/readtoolbar.py b/readtoolbar.py index 5c812c9..d79c1ed 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,23 +182,42 @@ 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') self._back.set_tooltip(_('Back')) self._back.props.sensitive = False + palette = self._back.get_palette() + self._prev_page = MenuItem(text_label= _("Previous page")) + palette.menu.append(self._prev_page) + self._prev_page.show_all() + self._prev_bookmark = MenuItem(text_label= _("Previous bookmark")) + palette.menu.append(self._prev_bookmark) + self._prev_bookmark.show_all() self._back.connect('clicked', self._go_back_cb) + self._prev_page.connect('activate', self._go_back_cb) + self._prev_bookmark.connect('activate', self._prev_bookmark_activate_cb) self.insert(self._back, -1) self._back.show() self._forward = ToolButton('go-next') self._forward.set_tooltip(_('Forward')) self._forward.props.sensitive = False + palette = self._forward.get_palette() + self._next_page = MenuItem(text_label= _("Next page")) + palette.menu.append(self._next_page) + self._next_page.show_all() + self._next_bookmark = MenuItem(text_label= _("Next bookmark")) + palette.menu.append(self._next_bookmark) + self._next_bookmark.show_all() self._forward.connect('clicked', self._go_forward_cb) + self._next_page.connect('activate', self._go_forward_cb) + self._next_bookmark.connect('activate', self._next_bookmark_activate_cb) self.insert(self._forward, -1) self._forward.show() @@ -229,20 +266,35 @@ class ReadToolbar(gtk.Toolbar): cell = gtk.CellRendererText() self._navigator.pack_start(cell, True) self._navigator.add_attribute(cell, 'text', 0) - self._navigator.props.sensitive = False + self._navigator.props.visible = False navitem.add(self._navigator) self.insert(navitem, -1) - navitem.show_all() + 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): + filehash = 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(filehash) def _num_page_entry_insert_text_cb(self, entry, text, length, position): if not re.match('[0-9]', text): @@ -269,13 +321,42 @@ class ReadToolbar(gtk.Toolbar): def _go_forward_cb(self, button): self._evince_view.next_page() + + def _prev_bookmark_activate_cb(self, menuitem): + page = self._document.get_page_cache().get_current_page() + bookmarkmanager = self._sidebar.get_bookmarkmanager() + + prev_bookmark = bookmarkmanager.get_prev_bookmark_for_page(page) + if prev_bookmark is not None: + self._document.get_page_cache().set_current_page(prev_bookmark.page_no) + + def _next_bookmark_activate_cb(self, menuitem): + page = self._document.get_page_cache().get_current_page() + bookmarkmanager = self._sidebar.get_bookmarkmanager() + + next_bookmark = bookmarkmanager.get_next_bookmark_for_page(page) + if next_bookmark is not None: + self._document.get_page_cache().set_current_page(next_bookmark.page_no) + + 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 @@ -289,7 +370,7 @@ class ReadToolbar(gtk.Toolbar): def _update_toc(self): if hasattr(self._document, 'has_document_links'): if self._document.has_document_links(): - self._navigator.props.sensitive = True + self._navigator.show_all() self._toc_model = self._document.get_links_model() self._navigator.set_model(self._toc_model) |