Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAndi_G <andigros72@googlemail.com>2011-07-24 00:42:35 (GMT)
committer Andi_G <andigros72@googlemail.com>2011-07-24 00:42:35 (GMT)
commit06ae426ec0a44b7daecdfab54710e9d6181f2566 (patch)
treed0ab46ea53a251480b49418298427211e76dfdff
parentb0b980e500f5707757d5b29233deb93004e117d8 (diff)
now based on the latest read release
-rw-r--r--MANIFEST1
-rw-r--r--annoactivity.py390
-rw-r--r--annobookmark.py90
-rw-r--r--epubadapter.py132
-rw-r--r--epubview/__init__.py2
-rw-r--r--epubview/epub.py90
-rw-r--r--epubview/epubinfo.py49
-rw-r--r--epubview/epubview.py422
-rw-r--r--epubview/jobs.py143
-rw-r--r--epubview/navmap.py69
-rw-r--r--epubview/widgets.py33
-rw-r--r--readdb.py524
-rw-r--r--readsidebar.py149
-rw-r--r--readtoolbar.py243
-rw-r--r--readtopbar.py203
15 files changed, 1349 insertions, 1191 deletions
diff --git a/MANIFEST b/MANIFEST
index 3bb3b51..d684d15 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -6,6 +6,7 @@ annobookmark.py
readtopbar.py
readsidebar.py
annoactivity.py
+annoicon.py
anno_v1.db
readtoolbar.py
setup.py
diff --git a/annoactivity.py b/annoactivity.py
index e5a3bbc..ec52f49 100644
--- a/annoactivity.py
+++ b/annoactivity.py
@@ -25,17 +25,16 @@ import re
import md5
import dbus
-import evince
import gobject
import gtk
import pango
-from glib import GError
import telepathy
from sugar.activity import activity
from sugar.graphics.toolbutton import ToolButton
from sugar.graphics.toolbarbox import ToolbarBox
from sugar.graphics.toolbarbox import ToolbarButton
+from sugar.graphics.toolcombobox import ToolComboBox
from sugar.graphics.toggletoolbutton import ToggleToolButton
from sugar.graphics.menuitem import MenuItem
from sugar.activity.widgets import ActivityToolbarButton
@@ -43,21 +42,29 @@ from sugar.activity.widgets import StopButton
from sugar import network
from sugar import mime
-from jarabe.journal import journalactivity
+#from jarabe.journal import journalactivity
from sugar.datastore import datastore
from sugar.graphics.objectchooser import ObjectChooser
-from readtoolbar import EditToolbar, ViewToolbar
+from readtoolbar import EditToolbar
+from readtoolbar import ViewToolbar
+from readtoolbar import SpeechToolbar
from readsidebar import Sidebar
from readtopbar import TopBar
+from readdb import AnnotationManager
+import epubadapter
+import evinceadapter
+import textadapter
+import speech
-_EPUB_SUPPORT = True
-try:
- import epubadapter
-except:
- _EPUB_SUPPORT = False
-
+#
+#_EPUB_SUPPORT = True
+#try:
+# import epubadapter
+#except ImportError, e:
+# _EPUB_SUPPORT = False
+# logging.warning('Epub support disabled because: %s' % e)
_HARDWARE_MANAGER_INTERFACE = 'org.laptop.HardwareManager'
_HARDWARE_MANAGER_SERVICE = 'org.laptop.HardwareManager'
@@ -131,24 +138,25 @@ class AnnoActivity(activity.Activity):
def __init__(self, handle):
activity.Activity.__init__(self, handle)
- if hasattr(evince, 'evince_embed_init'):
- # if we use evince-2.24
- evince.evince_embed_init()
-
- self._epub = False
+ #self._epub = False
self._document = None
self._fileserver = None
self._object_id = handle.object_id
- self._url = ''
+ #self._url = ''
+ self._toc_model = None
+
+ self._mimetype = None
self.connect('key-press-event', self._key_press_event_cb)
self.connect('key-release-event', self._key_release_event_cb)
- self.connect('window-state-event', self._window_state_event_cb)
+ #self.connect('window-state-event', self._window_state_event_cb)
_logger.debug('Starting Anno...')
self._view = None
-
+ self.dpi = _get_screen_dpi()
+ #self._model = None
+
self._sidebar = Sidebar()
self._sidebar.show()
@@ -174,8 +182,6 @@ class AnnoActivity(activity.Activity):
edit_toolbar_button.show()
self._view_toolbar = ViewToolbar()
- self._view_toolbar.connect('needs-update-size',
- self.__view_toolbar_needs_update_size_cb)
self._view_toolbar.connect('go-fullscreen',
self.__view_toolbar_go_fullscreen_cb)
view_toolbar_button = ToolbarButton(
@@ -213,12 +219,14 @@ class AnnoActivity(activity.Activity):
spacer.show()
navigator_toolbar = gtk.Toolbar()
- navigator_item = gtk.ToolItem()
+ #navigator_item = gtk.ToolItem()
self._navigator = self._create_navigator()
- navigator_item.add(self._navigator)
+ #navigator_item.add(self._navigator)
+ combotool = ToolComboBox(self._navigator)
+ navigator_toolbar.insert(combotool, -1)
+
self._navigator.show()
- navigator_toolbar.insert(navigator_item, -1)
- navigator_item.show()
+ combotool.show()
self._navigator_toolbar_button = ToolbarButton(page=navigator_toolbar,
icon_name='view-list')
navigator_toolbar.show()
@@ -258,8 +266,26 @@ class AnnoActivity(activity.Activity):
toolbar_box.toolbar.insert(annotator_downloader_item, -1)
annotator_downloader_item.show()
+
+
+ self._highlight_item = gtk.ToolItem()
+ self._highlight = ToggleToolButton('format-text-underline')
+ self._highlight.set_tooltip(_('Highlight'))
+ self._highlight.props.sensitive = False
+ self._highlight_id = self._highlight.connect('clicked', \
+ self.__highlight_cb)
+ self._highlight_item.add(self._highlight)
+ toolbar_box.toolbar.insert(self._highlight_item, -1)
+ self._highlight_item.show_all()
+
+ self.speech_toolbar = SpeechToolbar(self)
+ self.speech_toolbar_button = ToolbarButton(page=self.speech_toolbar,
+ icon_name='speak')
+ toolbar_box.toolbar.insert(self.speech_toolbar_button, -1)
+
+
stop_button = StopButton(self)
- stop_button.props.accelerator = '<Ctrl><Shift>Q'
+ #stop_button.props.accelerator = '<Ctrl><Shift>Q'
toolbar_box.toolbar.insert(stop_button, -1)
stop_button.show()
@@ -294,7 +320,7 @@ class AnnoActivity(activity.Activity):
self._tempfile = None
self._close_requested = False
- self._journal = journalactivity.get_journal()
+ #self._journal = journalactivity.get_journal()
fname = os.path.join('/etc', 'inhibit-ebook-sleep')
@@ -344,6 +370,21 @@ class AnnoActivity(activity.Activity):
#else:
# self._load_document('file:///home/smcv/tmp/test.pdf')
+
+ def fullscreen(self):
+ self._topbar.show_all()
+ activity.Activity.fullscreen(self)
+
+
+ def unfullscreen(self):
+ self._topbar.hide()
+ activity.Activity.unfullscreen(self)
+
+
+
+
+
+
def _create_back_button(self):
back = ToolButton('go-previous')
back.set_tooltip(_('Back'))
@@ -436,12 +477,13 @@ class AnnoActivity(activity.Activity):
else:
page = 0
- if page >= self._document.get_n_pages():
- page = self._document.get_n_pages() - 1
- elif page < 0:
- page = 0
+ #if page >= self._view.get_pagecount():
+ # page = self._view.get_pagecount() - 1
+ #elif page < 0:
+ # page = 0
- self._document.get_page_cache().set_current_page(page)
+ #self._model.props.page = page
+ self._view.set_current_page(page)
entry.props.text = str(page + 1)
def __go_back_cb(self, button):
@@ -456,38 +498,66 @@ class AnnoActivity(activity.Activity):
def __go_forward_page_cb(self, button):
self._view.next_page()
+ def __highlight_cb(self, button):
+ tuples_list = self._annotationmanager.get_highlights(
+ self._view.get_current_page())
+ selection_tuple = self._view.get_selection_bounds()
+ cursor_position = self._view.get_cursor_position()
+
+ old_highlight_found = None
+ for compare_tuple in tuples_list:
+ if selection_tuple:
+ if selection_tuple[0] >= compare_tuple[0] and \
+ selection_tuple[1] <= compare_tuple[1]:
+ old_highlight_found = compare_tuple
+ break
+ if cursor_position >= compare_tuple[0] and \
+ cursor_position <= compare_tuple[1]:
+ old_highlight_found = compare_tuple
+ break
+
+ if old_highlight_found == None:
+ self._annotationmanager.add_highlight(
+ self._view.get_current_page(), selection_tuple)
+ else:
+ self._annotationmanager.del_highlight(
+ self._view.get_current_page(), old_highlight_found)
+
+ self._view.show_highlights(self._annotationmanager.get_highlights(
+ self._view.get_current_page()))
+
+
+
+
def __prev_annotation_activate_cb(self, menuitem):
- page = self._document.get_page_cache().get_current_page()
+ page = self._view.get_current_page()
annomanager = self._sidebar.get_annotationmanager()
prev_anno = annomanager.get_prev_annotation(page)
if prev_anno is not None:
_logger.debug('prev annotation page is %d' % prev_anno.page)
- self._document.get_page_cache().set_current_page(prev_anno.page + 1)
-
+ self._view.set_current_page(prev_anno.page)
def __next_annotation_activate_cb(self, menuitem):
- page = self._document.get_page_cache().get_current_page()
+ page = self._view.get_current_page()
annomanager = self._sidebar.get_annotationmanager()
next_anno= annomanager.get_next_annotation(page)
if next_anno is not None:
_logger.debug('next annotation page is %d' % next_anno.page)
- self._document.get_page_cache().set_current_page(next_anno.page + 1)
-
-
+ self._view.set_current_page(next_anno.page)
def __bookmarker_toggled_cb(self, button):
- page = self._document.get_page_cache().get_current_page()
+ page = self._view.get_current_page()
if self._bookmarker.props.active:
self._sidebar.add_bookmark(page)
else:
self._sidebar.del_bookmark(page)
def __annotator_toggled_cb(self, button):
- page = self._document.get_page_cache().get_current_page()
+ page = self._view.get_current_page()
if self._annotator.props.active:
self._sidebar.add_annotation(page)
else:
@@ -500,45 +570,39 @@ class AnnoActivity(activity.Activity):
def __annotator_downloader_toggled_cb(self, button):
self._sidebar.download_annotations()
-
- def __page_changed_cb(self, page, proxy = None):
+ def __page_changed_cb(self, model, page_from, page_to):
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())
+ if self._toc_model != None:
+ self._toc_select_active_page()
+ self._sidebar.update_for_page(self._view.get_current_page())
self._annotator.handler_block(self._annotator_toggle_handler_id)
- self._annotator.props.active = self._sidebar.is_showing_local_bookmark()
+ self._annotator.props.active = self._sidebar.is_showing_local_annotation()
self._annotator.handler_unblock(self._annotator_toggle_handler_id)
+ tuples_list = self._annotationmanager.get_highlights(
+ self._view.get_current_page())
+ if self._view.can_highlight():
+ self._view.show_highlights(tuples_list)
+
+
+
def _update_nav_buttons(self):
- current_page = self._document.get_page_cache().get_current_page()
+ current_page = self._view.get_current_page()
self._back_button.props.sensitive = current_page > 0
self._forward_button.props.sensitive = \
- current_page < self._document.get_n_pages() - 1
+ current_page < self._view.get_pagecount() - 1
self._num_page_entry.props.text = str(current_page + 1)
self._total_page_label.props.label = \
- ' / ' + str(self._document.get_n_pages())
+ ' / ' + str(self._view.get_pagecount())
def _update_toc(self):
- if hasattr(self._document, 'has_document_links'):
- if self._document.has_document_links():
- self._navigator_toolbar_button.show()
- self._navigator.show_all()
-
- self._toc_model = self._document.get_links_model()
- self._navigator.set_model(self._toc_model)
- self._navigator.set_active(0)
-
- self._navigator_changed_handler_id = \
- self._navigator.connect('changed',
- self.__navigator_changed_cb)
-
- self._toc_select_active_page()
+ if self._view.update_toc(self):
+ self._navigator_changed_handler_id = \
+ self._navigator.connect('changed', self.__navigator_changed_cb)
+
def __navigator_changed_cb(self, combobox):
iter = self._navigator.get_active_iter()
@@ -565,8 +629,7 @@ class AnnoActivity(activity.Activity):
iter = self._navigator.get_active_iter()
current_link = self._toc_model.get(iter, 1)[0]
- current_page = self._document.get_page_cache().get_current_page()
-
+ current_page = self._view.get_current_page()
if not hasattr(current_link, 'get_page'):
filepath = self._view.get_current_file()
@@ -600,6 +663,7 @@ class AnnoActivity(activity.Activity):
if jobject and jobject.file_path:
self.read_file(jobject.file_path)
properties = jobject.metadata.get_dictionary().copy()
+ _logger.debug('\n\n\nthe metadata properties: %s' % str(properties))
self._url = properties['url']
finally:
chooser.destroy()
@@ -669,23 +733,13 @@ class AnnoActivity(activity.Activity):
try:
self.metadata['Anno_current_page'] = \
- str(self._document.get_page_cache().get_current_page())
-
- self.metadata['Anno_zoom'] = str(self._view.props.zoom)
+ str(self._model.props.page)
+ self.metadata['Anno_zoom'] = str(self._view.get_current_page())
+
self.metadata['url'] = self._url
- if not self._epub:
- if self._view.props.sizing_mode == evince.SIZING_BEST_FIT:
- self.metadata['Anno_sizing_mode'] = "best-fit"
- elif self._view.props.sizing_mode == evince.SIZING_FREE:
- self.metadata['Anno_sizing_mode'] = "free"
- elif self._view.props.sizing_mode == evince.SIZING_FIT_WIDTH:
- self.metadata['Anno_sizing_mode'] = "fit-width"
- else:
- _logger.error("Don't know how to save sizing_mode state '%s'" %
- self._view.props.sizing_mode)
- self.metadata['Anno_sizing_mode'] = "fit-width"
-
+ self._view.update_metadata(self)
+
self.metadata['Anno_search'] = \
self._edit_toolbar._search_entry.props.text
@@ -694,6 +748,7 @@ class AnnoActivity(activity.Activity):
self.metadata['Anno_search'] = \
self._edit_toolbar._search_entry.props.text
+
self.metadata['activity'] = self.get_bundle_id()
self.metadata['url'] = self._url
_logger.debug(str('set url to %s' % self._url))
@@ -814,109 +869,50 @@ class AnnoActivity(activity.Activity):
self.watch_for_tubes()
gobject.idle_add(self._get_document)
- def _setup_epub_viewer(self):
- self._view = epubadapter.View()
- self._view.set_screen_dpi(_get_screen_dpi())
- self._view.connect('notify::has-selection',
- self._view_notify_has_selection_cb)
-
- self._hbox.pack_start(self._view, expand=True, fill=True)
- self._view.show_all()
-
- def _setup_evince_viewer(self):
- self._view = evince.View()
- self._view.set_screen_dpi(_get_screen_dpi())
- self._view.connect('notify::has-selection',
- self._view_notify_has_selection_cb)
-
- self._scrolled = gtk.ScrolledWindow()
- self._scrolled.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
- self._scrolled.props.shadow_type = gtk.SHADOW_NONE
-
- self._scrolled.add(self._view)
- self._view.show()
-
- self._hbox.pack_start(self._scrolled, expand=True, fill=True)
- self._scrolled.show()
-
def _load_document(self, filepath):
"""Load the specified document and set up the UI.
filepath -- string starting with file://
-
+
"""
- _logger.debug('_load_document filepath: %s ' % filepath )
+ filename = filepath.replace('file://', '')
+ if not os.path.exists(filename) or os.path.getsize(filename) == 0:
+ return
mimetype = mime.get_for_file(filepath)
+ self._mimetype = mimetype
if mimetype == 'application/epub+zip':
- if not _EPUB_SUPPORT:
- self.close()
- self._epub = True
- self._setup_epub_viewer()
- self._document = epubadapter.EpubDocument(self._view, filepath.replace('file://', ''))
+ self._view = epubadapter.EpubViewer()
+ elif mimetype == 'text/plain' or mimetype == 'application/zip':
+ self._view = textadapter.TextViewer()
else:
- self._setup_evince_viewer()
- #get the url from the object in the journal that matches this file path
+ self._view = evinceadapter.EvinceViewer()
- try:
- self._document = evince.factory_get_document(filepath)
- except GError, e:
- _logger.error('Can not load document: %s', e)
- return
+ self._view.setup(self)
+ self._view.load_document(filepath)
+
+ self._want_document = False
self._view_toolbar.set_view(self._view)
self._edit_toolbar.set_view(self._view)
- self._want_document = False
- self._view.set_document(self._document)
- self._edit_toolbar.set_document(self._document)
- self._topbar.set_document(self._document)
+ self._topbar.set_view(self._view)
filehash = get_md5(filepath)
- self._sidebar.set_bookmarkmanager(filehash)
- self._sidebar.set_annotationmanager(filehash, mimetype)
-
+ self._annotationmanager = AnnotationManager(filehash, self._mimetype, self._sidebar)
+ self._sidebar.set_annotationmanager(self._annotationmanager)
self._update_nav_buttons()
self._update_toc()
-
- page_cache = self._document.get_page_cache()
- page_cache.connect('page-changed', self.__page_changed_cb)
-
- if not self.metadata['title_set_by_user'] == '1':
- info = self._document.get_info()
- if info and info.title:
- self.metadata['title'] = info.title
-
- if not self._epub:
- sizing_mode = self.metadata.get('Anno_sizing_mode', 'fit-width')
- _logger.debug('Found sizing mode: %s', sizing_mode)
- if sizing_mode == "best-fit":
- self._view.props.sizing_mode = evince.SIZING_BEST_FIT
- if hasattr(self._view, 'update_view_size'):
- 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('Anno_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
- if hasattr(self._view, 'update_view_size'):
- self._view.update_view_size(self._scrolled)
- else:
- # this may happen when we get a document from a buddy with a later
- # version of Anno, for example.
- _logger.warning("Unknown sizing_mode state '%s'", sizing_mode)
- if self.metadata.get('Anno_zoom', None) is not None:
- self._view.props.zoom = float(self.metadata['Anno_zoom'])
-
- self._view_toolbar._update_zoom_buttons()
+ self._view.connect_page_changed_handler(self.__page_changed_cb)
+ self._view.load_metadata(self)
+ self._update_toolbars()
self._edit_toolbar._search_entry.props.text = \
self.metadata.get('Anno_search', '')
current_page = int(self.metadata.get('Anno_current_page', '0'))
_logger.debug('Setting page to: %d', current_page)
- self._document.get_page_cache().set_current_page(current_page)
+ self._view.set_current_page(current_page)
# We've got the document, so if we're a shared activity, offer it
try:
@@ -926,6 +922,14 @@ class AnnoActivity(activity.Activity):
except Exception, e:
_logger.debug('Sharing failed: %s', e)
+ def _update_toolbars(self):
+ self._view_toolbar._update_zoom_buttons()
+ if not self._view.can_highlight():
+ self._highlight_item.hide()
+ if speech.supported and self._view.can_do_text_to_speech():
+ self.speech_toolbar_button.show()
+ self.speech_toolbar_button.show()
+
def _share_document(self):
"""Share the document."""
# FIXME: should ideally have the fileserver listen on a Unix socket
@@ -988,8 +992,35 @@ class AnnoActivity(activity.Activity):
self.watch_for_tubes()
self._share_document()
- def _view_notify_has_selection_cb(self, view, pspec):
- self._edit_toolbar.copy.set_sensitive(self._view.props.has_selection)
+ def _view_selection_changed_cb(self, view):
+ self._edit_toolbar.copy.props.sensitive = view.get_has_selection()
+ if self._view.can_highlight():
+ # Verify if the selection already exist or the cursor
+ # is in a highlighted area
+ cursor_position = self._view.get_cursor_position()
+ logging.debug('cursor position %d' % cursor_position)
+ selection_tuple = self._view.get_selection_bounds()
+ tuples_list = self._annotationmanager.get_highlights( \
+ self._view.get_current_page())
+ in_bounds = False
+ for highlight_tuple in tuples_list:
+ logging.debug('control tuple %s' % str(highlight_tuple))
+ if selection_tuple:
+ if selection_tuple[0] >= highlight_tuple[0] and \
+ selection_tuple[1] <= highlight_tuple[1]:
+ in_bounds = True
+ break
+ if cursor_position >= highlight_tuple[0] and \
+ cursor_position <= highlight_tuple[1]:
+ in_bounds = True
+ break
+
+ self._highlight.props.sensitive = \
+ view.get_has_selection() or in_bounds
+
+ self._highlight.handler_block(self._highlight_id)
+ self._highlight.set_active(in_bounds)
+ self._highlight.handler_unblock(self._highlight_id)
def _edit_toolbar_copy_cb(self, button):
self._view.copy()
@@ -1006,6 +1037,30 @@ class AnnoActivity(activity.Activity):
elif keyname == 'KP_End':
self._view_toolbar.zoom_out()
return True
+ elif keyname == 'Home':
+ self._view.scroll(gtk.SCROLL_START, False)
+ return True
+ elif keyname == 'End':
+ self._view.scroll(gtk.SCROLL_END, False)
+ return True
+ elif keyname == 'Page_Up' or keyname == 'KP_Page_Up':
+ self._view.scroll(gtk.SCROLL_PAGE_BACKWARD, False)
+ return True
+ elif keyname == 'Page_Down' or keyname == 'KP_Page_Down':
+ self._view.scroll(gtk.SCROLL_PAGE_FORWARD, False)
+ return True
+ elif keyname == 'Up' or keyname == 'KP_Up':
+ self._view.scroll(gtk.SCROLL_STEP_BACKWARD, False)
+ return True
+ elif keyname == 'Down' or keyname == 'KP_Down':
+ self._view.scroll(gtk.SCROLL_STEP_FORWARD, False)
+ return True
+ elif keyname == 'Left' or keyname == 'KP_Left':
+ self._view.scroll(gtk.SCROLL_STEP_BACKWARD, True)
+ return True
+ elif keyname == 'Right' or keyname == 'KP_Right':
+ self._view.scroll(gtk.SCROLL_STEP_FORWARD, True)
+ return True
else:
return False
@@ -1014,15 +1069,6 @@ class AnnoActivity(activity.Activity):
#_logger.debug("Keyname Release: %s, time: %s", keyname, event.time)
return False
- def _window_state_event_cb(self, window, event):
- if not (event.changed_mask & gtk.gdk.WINDOW_STATE_FULLSCREEN):
- return False
-
- if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN:
- self._topbar.show_all()
- else:
- self._topbar.hide()
-
def __view_toolbar_needs_update_size_cb(self, view_toolbar):
if hasattr(self._view, 'update_view_size'):
self._view.update_view_size(self._scrolled)
diff --git a/annobookmark.py b/annobookmark.py
index b1d7468..b5ac9fe 100644
--- a/annobookmark.py
+++ b/annobookmark.py
@@ -15,8 +15,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-import cjson
+import simplejson
+#import cjson
import logging
+import gconf
+from sugar.graphics.xocolor import XoColor
_logger = logging.getLogger('anno-activity')
@@ -41,14 +44,17 @@ class Bookmark:
if self.content == '' or self.content is None:
return ''
- note = cjson.decode(self.content)
+ note = simplejson.loads(self.content)
+ #note = cjson.decode(self.content)
+
return note['title']
def get_note_body(self):
if self.content == '' or self.content is None:
return ''
- note = cjson.decode(self.content)
+ note = simplejson.loads(self.content)
+ #note = cjson.decode(self.content)
return note['body']
@@ -62,22 +68,31 @@ class AnnoBookmark:
self.title = data[3]
self.content = data[4]
self.bodyurl = data[5]
- self.context = data[6]
- self.created = data[7]
- self.modified = data[8]
- self.creator = data[9]
- self.annotates = data[10]
- self.rdf = data[11]
- self.color = data[12]
+ self.texttitle = data[6]
+ self.textcreator = data[7]
+ self.created = data[8]
+ self.modified = data[9]
+ self.creator = data[10]
+ self.annotates = data[11]
+
+ if isinstance(data[12], str) or isinstance(data[12], unicode):
+ self.color = XoColor(data[12])
+ elif isinstance(data[12], XoColor):
+ self.color = data[12]
+ else:
+ self.color = XoColor(" ")
+
self.local = data[13]
self.mimetype = data[14]
self.uuid = data[15]
self.annotationurl = data[16]
- self.bodysvg = data[17]
if ( ( self.uuid == None ) or ( len( self.uuid ) == 0 ) ) and ( self.md5 != None ) and ( self.id != None ):
self.uuid = "urn:sugaruuid:" + self.creator + "-" + self.md5 + "-" + str(self.id)
_logger.debug('annobookmark annotates is %s' % self.annotates)
+ if ( self.annotationurl == None ):
+ self.annotationurl = ''
+
def __str__(self):
r = str( "A bookmark: id: %s \nuuid: %s" % ( str( self.id ), self.uuid ) )
@@ -85,12 +100,10 @@ class AnnoBookmark:
r += str( "\npage: %d\ntitle: %s" % ( self.page, self.title ) )
r += str( "\ncontent: %s" % self.content )
r += str( "\nbodyurl: %s \nannotationurl: %s" % ( self.bodyurl, self.annotationurl ) )
- r += str( "\ncontext: %s \ncreated: %s" % ( self.context, str( self.created ) ) )
- r += str( "\nmodified: %s \ncreator: %s" % ( str( self.modified ), self.creator ) )
+ r += str( "\ncreated: %s \nmodified: %s" % ( self.created, str( self.modified ) ) )
+ r += str( "\ncreator: %s" % ( self.creator ) )
r += str( "\nannotates: %s \nmimetype: %s" % ( self.annotates, self.mimetype ) )
- r += str( "\nrdf: %s" % self.rdf )
r += str( "\ncolor: %s \nlocal: %s" % ( str( self.color ), str( self.local ) ) )
- r += str( "\nbodysvg: %s" % self.bodysvg )
return r
@@ -116,13 +129,6 @@ class AnnoBookmark:
return ''
return self.content
- def set_bodysvg(self, bodysvg):
- self.bodysvg = bodysvg
-
-
- def get_bodysvg(self):
- return self.bodysvg
-
def get_id(self):
return self.id
@@ -147,9 +153,6 @@ class AnnoBookmark:
def set_annotationurl(self, url):
self.annotationurl = url
- def get_context(self):
- return self.context
-
def get_created(self):
return self.created
@@ -162,12 +165,18 @@ class AnnoBookmark:
def get_annotates(self):
return self.annotates
- def get_rdf(self):
- return self.rdf
+ def get_texttitle(self):
+ return self.texttitle
- def set_rdf(self, rdf):
- self.rdf = rdf
+ def set_texttitle(self, title):
+ self.texttitle = title
+
+ def get_textcreator(self):
+ return self.texttitle
+ def set_textcreator(self, creator):
+ self.textcreator = creator
+
def get_color(self):
return self.color
@@ -179,3 +188,26 @@ class AnnoBookmark:
def get_uuid(self):
return self.uuid
+
+ def get_json(self):
+ arr = {
+ 'id' : self.id,
+ 'md5' : self.md5,
+ 'page' : self.page,
+ 'title' : self.title,
+ 'content' : self.content,
+ 'bodyurl' : self.bodyurl,
+ 'texttitle' : self.texttitle,
+ 'textcreator' : self.textcreator,
+ 'created' : self.created,
+ 'modified' : self.modified,
+ 'creator' : self.creator,
+ 'annotates' : self.annotates,
+ 'color' : self.color.to_string(),
+ 'local' : self.local,
+ 'mimetype' : self.mimetype,
+ 'uuid' : self.uuid,
+ 'annotationurl' : self.annotationurl
+ }
+ return simplejson.dumps(arr)
+ #return cjson.encode(arr)
diff --git a/epubadapter.py b/epubadapter.py
index cfb50d2..8def465 100644
--- a/epubadapter.py
+++ b/epubadapter.py
@@ -2,13 +2,104 @@ import gobject
import logging
import epubview
+import speech
-_logger = logging.getLogger('anno-activity')
+from cStringIO import StringIO
+
+_logger = logging.getLogger('read-activity')
+
+
+class EpubViewer(epubview.EpubView):
-class View(epubview.EpubView):
def __init__(self):
epubview.EpubView.__init__(self)
+ def setup(self, activity):
+ self.set_screen_dpi(activity.dpi)
+ self.connect('selection-changed',
+ activity._view_selection_changed_cb)
+
+ activity._hbox.pack_start(self, expand=True, fill=True)
+ self.show_all()
+ # text to speech initialization
+ self.current_word = 0
+ self.word_tuples = []
+
+ def load_document(self, file_path):
+ self.set_document(EpubDocument(self, file_path.replace('file://', '')))
+ speech.highlight_cb = self.highlight_next_word
+ speech.end_text_cb = self.get_more_text
+
+ def load_metadata(self, activity):
+
+ self.metadata = activity.metadata
+
+ if not self.metadata['title_set_by_user'] == '1':
+ title = self._epub._info._get_title()
+ if title:
+ self.metadata['title'] = title
+
+ def update_metadata(self, activity):
+ pass
+
+ def zoom_to_width(self):
+ pass
+
+ def zoom_to_best_fit(self):
+ pass
+
+ def zoom_to_actual_size(self):
+ pass
+
+ def can_zoom_to_width(self):
+ return False
+
+ def can_highlight(self):
+ return False
+
+ def can_do_text_to_speech(self):
+ return True
+
+ def get_marked_words(self):
+ "Adds a mark between each word of text."
+ i = self.current_word
+ file_str = StringIO()
+ file_str.write('<speak> ')
+ end_range = i + 40
+ if end_range > len(self.word_tuples):
+ end_range = len(self.word_tuples)
+ for word_tuple in self.word_tuples[self.current_word:end_range]:
+ file_str.write('<mark name="' + str(i) + '"/>' + word_tuple[2])
+ i = i + 1
+ self.current_word = i
+ file_str.write('</speak>')
+ return file_str.getvalue()
+
+ def get_more_text(self):
+ if self.current_word < len(self.word_tuples):
+ speech.stop()
+ more_text = self.get_marked_words()
+ speech.play(more_text)
+
+ def highlight_next_word(self, word_count):
+ pass
+ """
+ TODO: disabled because javascript can't be executed
+ with the velocity needed
+ self.current_word = word_count
+ self._view.highlight_next_word()
+ return True
+ """
+
+ def connect_zoom_handler(self, handler):
+ self._zoom_handler = handler
+ self._view_notify_zoom_handler = \
+ self.connect('notify::scale', handler)
+ return self._view_notify_zoom_handler
+
+ def connect_page_changed_handler(self, handler):
+ self.connect('page-changed', handler)
+
def _try_load_page(self, n):
if self._ready:
self._load_page(n)
@@ -20,11 +111,12 @@ class View(epubview.EpubView):
return
def find_set_highlight_search(self, set_highlight_search):
+ #TODO : what is this?
return
def set_current_page(self, n):
# When the book is being loaded, calling this does not help
- # In such a situation, we go into a loop and try to load the
+ # In such a situation, we go into a loop and try to load the
# supplied page when the book has loaded completely
n += 1
if self._ready:
@@ -35,21 +127,39 @@ class View(epubview.EpubView):
def get_current_page(self):
return int(self._loaded_page - 1)
- def find_changed(self, job, page = None):
+ def update_toc(self, activity):
+ if self._epub.has_document_links():
+ activity._navigator_toolbar_button.show()
+ activity._navigator.show_all()
+
+ activity._toc_model = self._epub.get_links_model()
+ activity._navigator.set_model(activity._toc_model)
+ activity._navigator.set_active(0)
+ return True
+ else:
+ return False
+
+ def find_changed(self, job, page=None):
self._find_changed(job)
def handle_link(self, link):
self._load_file(link)
+ def setup_find_job(self, text, updated_cb):
+ self._find_job = JobFind(document=self._epub,
+ start_page=0, n_pages=self.get_pagecount(),
+ text=text, case_sensitive=False)
+ self._find_updated_handler = self._find_job.connect('updated',
+ updated_cb)
+ return self._find_job, self._find_updated_handler
+
class EpubDocument(epubview.Epub):
+
def __init__(self, view, docpath):
epubview.Epub.__init__(self, docpath)
self._page_cache = view
- def get_page_cache(self):
- return self._page_cache
-
def get_n_pages(self):
return int(self._page_cache.get_pagecount())
@@ -59,6 +169,10 @@ class EpubDocument(epubview.Epub):
def get_links_model(self):
return self.get_toc_model()
+
class JobFind(epubview.JobFind):
- def __init__(self, document, start_page, n_pages, text, case_sensitive=False):
- epubview.JobFind.__init__(self, document, start_page, n_pages, text, case_sensitive=False)
+
+ def __init__(self, document, start_page, n_pages, text,
+ case_sensitive=False):
+ epubview.JobFind.__init__(self, document, start_page, n_pages, text,
+ case_sensitive=False)
diff --git a/epubview/__init__.py b/epubview/__init__.py
index 81a3175..5051bdf 100644
--- a/epubview/__init__.py
+++ b/epubview/__init__.py
@@ -21,4 +21,4 @@ gobject.threads_init()
from epub import _Epub as Epub
from epubview import _View as EpubView
-from jobs import _JobFind as JobFind \ No newline at end of file
+from jobs import _JobFind as JobFind
diff --git a/epubview/epub.py b/epubview/epub.py
index 71267aa..063a982 100644
--- a/epubview/epub.py
+++ b/epubview/epub.py
@@ -17,11 +17,12 @@
import zipfile
import tempfile
-import os, os.path
+import os
from lxml import etree
import shutil
-import navmap, epubinfo
+import navmap
+import epubinfo
class _Epub(object):
@@ -32,28 +33,31 @@ class _Epub(object):
self._ncxpath = None
self._basepath = None
self._tempdir = tempfile.mkdtemp()
-
+
if not self._verify():
print 'Warning: This does not seem to be a valid epub file'
-
+
self._get_opf()
self._get_ncx()
-
+
ncxfile = self._zobject.open(self._ncxpath)
- opffile = self._zobject.open(self._opfpath)
+ opffile = self._zobject.open(self._opfpath)
self._navmap = navmap.NavMap(opffile, ncxfile, self._basepath)
-
+
opffile = self._zobject.open(self._opfpath)
- self._info = epubinfo.EpubInfo(opffile)
-
+ self._info = epubinfo.EpubInfo(opffile)
+
self._unzip()
-
+
def _unzip(self):
- #self._zobject.extractall(path = self._tempdir) # This is broken upto python 2.7
+ # This is broken upto python 2.7
+ #self._zobject.extractall(path = self._tempdir)
orig_cwd = os.getcwd()
os.chdir(self._tempdir)
for name in self._zobject.namelist():
- if name.startswith(os.path.sep): # Some weird zip file entries start with a slash, and we don't want to write to the root directory
+ # Some weird zip file entries start with a slash,
+ # and we don't want to write to the root directory
+ if name.startswith(os.path.sep):
name = name[1:]
if name.endswith(os.path.sep) or name.endswith('\\'):
os.makedirs(name)
@@ -61,28 +65,27 @@ class _Epub(object):
self._zobject.extract(name)
os.chdir(orig_cwd)
-
def _get_opf(self):
containerfile = self._zobject.open('META-INF/container.xml')
-
+
tree = etree.parse(containerfile)
root = tree.getroot()
-
- for element in root.iterfind('.//{urn:oasis:names:tc:opendocument:xmlns:container}rootfile'):
+
+ for element in root.iterfind(
+ './/{urn:oasis:names:tc:opendocument:xmlns:container}rootfile'):
if element.get('media-type') == 'application/oebps-package+xml':
self._opfpath = element.get('full-path')
-
- if self._opfpath.rpartition('/')[0]:
+
+ if self._opfpath.rpartition('/')[0]:
self._basepath = self._opfpath.rpartition('/')[0] + '/'
else:
self._basepath = ''
-
- containerfile.close()
+ containerfile.close()
def _get_ncx(self):
opffile = self._zobject.open(self._opfpath)
-
+
tree = etree.parse(opffile)
root = tree.getroot()
@@ -92,61 +95,64 @@ class _Epub(object):
for element in root.iterfind('.//{http://www.idpf.org/2007/opf}item'):
if element.get('id') == tocid:
self._ncxpath = self._basepath + element.get('href')
-
+
opffile.close()
def _verify(self):
'''
- Method to crudely check to verify that what we
+ Method to crudely check to verify that what we
are dealing with is a epub file or not
'''
if not os.path.exists(self._filepath):
return False
-
+
self._zobject = zipfile.ZipFile(self._filepath)
-
+
if not 'mimetype' in self._zobject.namelist():
return False
-
+
mtypefile = self._zobject.open('mimetype')
mimetype = mtypefile.readline()
-
- if not mimetype.startswith('application/epub+zip'): # Some files seem to have trailing characters
+
+ # Some files seem to have trailing characters
+ if not mimetype.startswith('application/epub+zip'):
return False
-
+
return True
-
+
def get_toc_model(self):
'''
Returns a GtkTreeModel representation of the
Epub table of contents
- '''
+ '''
return self._navmap.get_gtktreestore()
-
+
def get_flattoc(self):
'''
Returns a flat (linear) list of files to be
rendered.
- '''
+ '''
return self._navmap.get_flattoc()
-
+
def get_basedir(self):
'''
Returns the base directory where the contents of the
epub has been unzipped
'''
return self._tempdir
-
+
def get_info(self):
'''
- Returns a EpubInfo object for the open Epub file
- '''
- return self._info
-
+ Returns a EpubInfo object title
+ '''
+ return self._info.title
+
def close(self):
'''
- Cleans up (closes open zip files and deletes uncompressed content of Epub.
- Please call this when a file is being closed or during application exit.
- '''
+ Cleans up (closes open zip files and deletes
+ uncompressed content of Epub.
+ Please call this when a file is being closed or during
+ application exit.
+ '''
self._zobject.close()
shutil.rmtree(self._tempdir)
diff --git a/epubview/epubinfo.py b/epubview/epubinfo.py
index d25dfc2..b4e5c22 100644
--- a/epubview/epubinfo.py
+++ b/epubview/epubinfo.py
@@ -2,12 +2,16 @@ import os
from lxml import etree
-class EpubInfo(): #TODO: Cover the entire DC range
+class EpubInfo():
+
+ #TODO: Cover the entire DC range
+
def __init__(self, opffile):
self._tree = etree.parse(opffile)
self._root = self._tree.getroot()
- self._e_metadata = self._root.find('{http://www.idpf.org/2007/opf}metadata')
-
+ self._e_metadata = self._root.find(
+ '{http://www.idpf.org/2007/opf}metadata')
+
self.title = self._get_title()
self.creator = self._get_creator()
self.date = self._get_date()
@@ -16,34 +20,34 @@ class EpubInfo(): #TODO: Cover the entire DC range
self.rights = self._get_rights()
self.identifier = self._get_identifier()
self.language = self._get_language()
-
-
+
def _get_data(self, tagname):
element = self._e_metadata.find(tagname)
return element.text
-
+
def _get_title(self):
try:
ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}title')
except AttributeError:
return None
-
+
return ret
-
+
def _get_creator(self):
try:
- ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}creator')
+ ret = self._get_data(
+ './/{http://purl.org/dc/elements/1.1/}creator')
except AttributeError:
- return None
+ return None
return ret
-
+
def _get_date(self):
#TODO: iter
try:
ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}date')
except AttributeError:
return None
-
+
return ret
def _get_source(self):
@@ -51,7 +55,7 @@ class EpubInfo(): #TODO: Cover the entire DC range
ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}source')
except AttributeError:
return None
-
+
return ret
def _get_rights(self):
@@ -59,32 +63,35 @@ class EpubInfo(): #TODO: Cover the entire DC range
ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}rights')
except AttributeError:
return None
-
+
return ret
def _get_identifier(self):
#TODO: iter
- element = self._e_metadata.find('.//{http://purl.org/dc/elements/1.1/}identifier')
+ element = self._e_metadata.find(
+ './/{http://purl.org/dc/elements/1.1/}identifier')
if element is not None:
- return {'id':element.get('id'), 'value':element.text}
+ return {'id': element.get('id'), 'value': element.text}
else:
return None
def _get_language(self):
try:
- ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}language')
+ ret = self._get_data(
+ './/{http://purl.org/dc/elements/1.1/}language')
except AttributeError:
return None
-
+
return ret
def _get_subject(self):
try:
subjectlist = []
- for element in self._e_metadata.iterfind('.//{http://purl.org/dc/elements/1.1/}subject'):
+ for element in self._e_metadata.iterfind(
+ './/{http://purl.org/dc/elements/1.1/}subject'):
subjectlist.append(element.text)
except AttributeError:
return None
-
- return subjectlist \ No newline at end of file
+
+ return subjectlist
diff --git a/epubview/epubview.py b/epubview/epubview.py
index 595eb20..c4b3df0 100644
--- a/epubview/epubview.py
+++ b/epubview/epubview.py
@@ -15,42 +15,43 @@
# 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, gtk.gdk
+import gtk
import gobject
import widgets
import os.path
import math
import shutil
+import BeautifulSoup
from epub import _Epub
from jobs import _JobPaginator as _Paginator
-
LOADING_HTML = '''
<div style="width:100%;height:100%;text-align:center;padding-top:50%;">
<h1>Loading...</h1>
</div>
'''
+
class _View(gtk.HBox):
+
__gproperties__ = {
- 'has-selection' : (gobject.TYPE_BOOLEAN, 'whether has selection',
- 'whether the widget has selection or not',
- 0, gobject.PARAM_READABLE),
- 'zoom' : (gobject.TYPE_FLOAT, 'the zoom level',
- 'the zoom level of the widget',
- 0.5, 4.0, 1.0, gobject.PARAM_READWRITE)
- }
+ 'scale': (gobject.TYPE_FLOAT, 'the zoom level',
+ 'the zoom level of the widget',
+ 0.5, 4.0, 1.0, gobject.PARAM_READWRITE),
+ }
__gsignals__ = {
- 'page-changed': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
- }
+ 'page-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([int, int])),
+ 'selection-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
+ ([])),
+ }
+
def __init__(self):
gobject.threads_init()
gtk.HBox.__init__(self)
-
+
self.connect("destroy", self._destroy_cb)
self._ready = False
@@ -62,13 +63,13 @@ class _View(gtk.HBox):
self.__going_fwd = True
self.__going_back = False
self.__page_changed = False
- self.has_selection = False
- self.zoom = 1.0
+ self._has_selection = False
+ self.scale = 1.0
self._epub = None
self._findjob = None
self.__in_search = False
self.__search_fwd = True
-
+
self._sw = gtk.ScrolledWindow()
self._view = widgets._WebView()
self._view.load_string(LOADING_HTML, 'text/html', 'utf-8', '/')
@@ -79,69 +80,86 @@ class _View(gtk.HBox):
self._view.connect('load-finished', self._view_load_finished_cb)
self._view.connect('scroll-event', self._view_scroll_event_cb)
self._view.connect('key-press-event', self._view_keypress_event_cb)
- self._view.connect('button-release-event', self._view_buttonrelease_event_cb)
- self._view.connect('selection-changed', self._view_selection_changed_cb)
- self._view.connect_after('populate-popup', self._view_populate_popup_cb)
-
+ self._view.connect('button-release-event',
+ self._view_buttonrelease_event_cb)
+ self._view.connect('selection-changed',
+ self._view_selection_changed_cb)
+ self._view.connect_after('populate-popup',
+ self._view_populate_popup_cb)
+
self._sw.add(self._view)
self._sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
self._v_vscrollbar = self._sw.get_vscrollbar()
- self._v_scrollbar_value_changed_cb_id = self._v_vscrollbar.connect('value-changed', \
- self._v_scrollbar_value_changed_cb)
+ self._v_scrollbar_value_changed_cb_id = \
+ self._v_vscrollbar.connect('value-changed', \
+ self._v_scrollbar_value_changed_cb)
self._scrollbar = gtk.VScrollbar()
self._scrollbar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
- self._scrollbar_change_value_cb_id = self._scrollbar.connect('change-value', \
- self._scrollbar_change_value_cb)
- self.pack_start(self._sw, expand = True, fill = True)
- self.pack_start(self._scrollbar, expand = False, fill = False)
-
- self._view.set_flags(gtk.CAN_DEFAULT|gtk.CAN_FOCUS)
-
+ self._scrollbar_change_value_cb_id = \
+ self._scrollbar.connect('change-value', \
+ self._scrollbar_change_value_cb)
+ self.pack_start(self._sw, expand=True, fill=True)
+ self.pack_start(self._scrollbar, expand=False, fill=False)
+
+ self._view.set_flags(gtk.CAN_DEFAULT | gtk.CAN_FOCUS)
+
def set_document(self, epubdocumentinstance):
'''
Sets document (should be a Epub instance)
- '''
+ '''
self._epub = epubdocumentinstance
gobject.idle_add(self._paginate)
def do_get_property(self, property):
if property.name == 'has-selection':
- return self.has_selection
- elif property.name == 'zoom':
- return self.zoom
+ return self._has_selection
+ elif property.name == 'scale':
+ return self.scale
else:
- raise AttributeError, 'unknown property %s' % property.name
+ raise AttributeError('unknown property %s' % property.name)
def do_set_property(self, property, value):
- if property.name == 'zoom':
+ if property.name == 'scale':
self.__set_zoom(value)
else:
- raise AttributeError, 'unknown property %s' % property.name
-
+ raise AttributeError('unknown property %s' % property.name)
+
def get_has_selection(self):
'''
Returns True if any part of the content is selected
- '''
- return self.get_property('has-selection')
-
+ '''
+ return self._has_selection
+
def get_zoom(self):
'''
Returns the current zoom level
- '''
- return self.get_property('zoom')
-
+ '''
+ return self.get_property('scale') * 100.0
+
def set_zoom(self, value):
'''
Sets the current zoom level
- '''
- self.set_property('zoom', value)
-
+ '''
+ self._view.set_zoom_level(value / 100.0)
+
+ def _get_scale(self):
+ '''
+ Returns the current zoom level
+ '''
+ return self.get_property('scale')
+
+ def _set_scale(self, value):
+ '''
+ Sets the current zoom level
+ '''
+ self.set_property('scale', value)
+
def zoom_in(self):
'''
Zooms in (increases zoom level by 0.1)
- '''
+ '''
if self.can_zoom_in():
- self.set_zoom(self.get_zoom() + 0.1)
+ self._set_scale(self._get_scale() + 0.1)
return True
else:
return False
@@ -149,18 +167,18 @@ class _View(gtk.HBox):
def zoom_out(self):
'''
Zooms out (decreases zoom level by 0.1)
- '''
+ '''
if self.can_zoom_out():
- self.set_zoom(self.get_zoom() - 0.1)
+ self._set_scale(self._get_scale() - 0.1)
return True
else:
return False
-
+
def can_zoom_in(self):
'''
Returns True if it is possible to zoom in further
- '''
- if self.zoom < 4:
+ '''
+ if self.scale < 4:
return True
else:
return False
@@ -168,58 +186,58 @@ class _View(gtk.HBox):
def can_zoom_out(self):
'''
Returns True if it is possible to zoom out further
- '''
- if self.zoom > 0.5:
+ '''
+ if self.scale > 0.5:
return True
else:
return False
-
+
def get_current_page(self):
'''
Returns the currently loaded page
- '''
+ '''
return self._loaded_page
-
+
def get_current_file(self):
'''
Returns the currently loaded XML file
- '''
+ '''
#return self._loaded_filename
- if self._paginator:
+ if self._paginator:
return self._paginator.get_file_for_pageno(self._loaded_page)
else:
return None
-
+
def get_pagecount(self):
'''
Returns the pagecount of the loaded file
- '''
+ '''
return self._pagecount
-
+
def set_current_page(self, n):
'''
Loads page number n
- '''
+ '''
if n < 1 or n > self._pagecount:
return False
self._load_page(n)
return True
-
+
def next_page(self):
'''
Loads next page if possible
Returns True if transition to next page is possible and done
- '''
+ '''
if self._loaded_page == self._pagecount:
return False
self._load_next_page()
return True
-
+
def previous_page(self):
'''
Loads previous page if possible
Returns True if transition to previous page is possible and done
- '''
+ '''
if self._loaded_page == 1:
return False
self._load_prev_page()
@@ -229,8 +247,11 @@ class _View(gtk.HBox):
'''
Scrolls through the pages.
Scrolling is horizontal if horizontal is set to True
- Valid scrolltypes are: gtk.SCROLL_PAGE_BACKWARD and gtk.SCROLL_PAGE_FORWARD
- '''
+ Valid scrolltypes are:
+ gtk.SCROLL_PAGE_BACKWARD, gtk.SCROLL_PAGE_FORWARD,
+ gtk.SCROLL_STEP_BACKWARD, gtk.SCROLL_STEP_FORWARD
+ gtk.SCROLL_STEP_START and gtk.SCROLL_STEP_STOP
+ '''
if scrolltype == gtk.SCROLL_PAGE_BACKWARD:
self.__going_back = True
self.__going_fwd = False
@@ -241,43 +262,67 @@ class _View(gtk.HBox):
self.__going_fwd = True
if not self._do_page_transition():
self._view.move_cursor(gtk.MOVEMENT_PAGES, 1)
+ elif scrolltype == gtk.SCROLL_STEP_BACKWARD:
+ self.__going_fwd = False
+ self.__going_back = True
+ if not self._do_page_transition():
+ self._view.move_cursor(gtk.MOVEMENT_DISPLAY_LINES, -1)
+ elif scrolltype == gtk.SCROLL_STEP_FORWARD:
+ self.__going_fwd = True
+ self.__going_back = False
+ if not self._do_page_transition():
+ self._view.move_cursor(gtk.MOVEMENT_DISPLAY_LINES, 1)
+ elif scrolltype == gtk.SCROLL_START:
+ self.__going_back = True
+ self.__going_fwd = False
+ if not self._do_page_transition():
+ self.set_current_page(1)
+ elif scrolltype == gtk.SCROLL_END:
+ self.__going_back = False
+ self.__going_fwd = True
+ if not self._do_page_transition():
+ self.set_current_page(self._pagecount - 1)
else:
print ('Got unsupported scrolltype %s' % str(scrolltype))
-
+
def copy(self):
'''
Copies the current selection to clipboard.
- '''
+ '''
self._view.copy_clipboard()
-
+
def find_next(self):
'''
Highlights the next matching item for current search
- '''
+ '''
self._view.grab_focus()
self._view.grab_default()
- if self._view.search_text(self._findjob.get_search_text(), \
- self._findjob.get_case_sensitive(), True, False):
+ if self._view.search_text(self._findjob.get_search_text(),
+ self._findjob.get_case_sensitive(),
+ True, False):
return
else:
- path = os.path.join(self._epub.get_basedir(), self._findjob.get_next_file())
+ path = os.path.join(self._epub.get_basedir(),
+ self._findjob.get_next_file())
self.__in_search = True
self.__search_fwd = True
self._load_file(path)
-
+
def find_previous(self):
'''
Highlights the previous matching item for current search
- '''
+ '''
self._view.grab_focus()
self._view.grab_default()
- if self._view.search_text(self._findjob.get_search_text(), \
- self._findjob.get_case_sensitive(), False, False):
+ if self._view.search_text(self._findjob.get_search_text(),
+ self._findjob.get_case_sensitive(),
+ False, False):
return
else:
- path = os.path.join(self._epub.get_basedir(), self._findjob.get_prev_file())
+ path = os.path.join(self._epub.get_basedir(),
+ self._findjob.get_prev_file())
self.__in_search = True
self.__search_fwd = False
self._load_file(path)
@@ -286,42 +331,43 @@ class _View(gtk.HBox):
self._view.grab_focus()
self._view.grab_default()
self._findjob = job
- #self._view.search_text(self._findjob.get_search_text(), \
- # self._findjob.get_case_sensitive(), True, False)
self.find_next()
-
+
def __set_zoom(self, value):
self._view.set_zoom_level(value)
- self.zoom = value
-
+ self.scale = value
+
def __set_has_selection(self, value):
- if value != self.has_selection:
- self.has_selection = value
- self.notify('has-selection')
-
+ if value != self._has_selection:
+ self._has_selection = value
+ self.emit('selection-changed')
+
def _view_populate_popup_cb(self, view, menu):
- menu.destroy() #HACK
+ menu.destroy() # HACK
return
-
+
def _view_selection_changed_cb(self, view):
- # FIXME: This does not seem to be implemented in
+ # FIXME: This does not seem to be implemented in
# webkitgtk yet
- print view.has_selection()
-
+ print "epubview _view_selection_changed_cb", view.has_selection()
+ self.__set_has_selection(view.has_selection())
+
def _view_buttonrelease_event_cb(self, view, event):
# Ugly hack
+ print "epubview _view_buttonrelease_event_cb", view.has_selection(), \
+ view.can_copy_clipboard(), view.can_cut_clipboard()
self.__set_has_selection(view.can_copy_clipboard() \
| view.can_cut_clipboard())
-
+
def _view_keypress_event_cb(self, view, event):
name = gtk.gdk.keyval_name(event.keyval)
- if name == 'Page_Down' or name == 'Down':
+ if name == 'Page_Down' or name == 'Down':
self.__going_back = False
self.__going_fwd = True
elif name == 'Page_Up' or name == 'Up':
self.__going_back = True
self.__going_fwd = False
-
+
self._do_page_transition()
def _view_scroll_event_cb(self, view, event):
@@ -332,8 +378,8 @@ class _View(gtk.HBox):
self.__going_back = True
self.__going_fwd = False
- self._do_page_transition()
-
+ self._do_page_transition()
+
def _do_page_transition(self):
if self.__going_fwd:
if self._v_vscrollbar.get_value() >= \
@@ -342,35 +388,37 @@ class _View(gtk.HBox):
self._load_next_file()
return True
elif self.__going_back:
- if self._v_vscrollbar.get_value() == self._v_vscrollbar.props.adjustment.props.lower:
+ if self._v_vscrollbar.get_value() == \
+ self._v_vscrollbar.props.adjustment.props.lower:
self._load_prev_file()
return True
-
+
return False
-
+
def _view_load_finished_cb(self, v, frame):
-
- # Normally the line below would not be required - ugly workaround for
+ # Normally the line below would not be required - ugly workaround for
# possible Webkit bug. See : https://bugs.launchpad.net/bugs/483231
self._sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_NEVER)
-
+
filename = self._view.props.uri.replace('file://', '')
if os.path.exists(filename.replace('xhtml', 'xml')):
- filename = filename.replace('xhtml', 'xml') # Hack for making javascript work
-
- filename = filename.split('#')[0] # Get rid of anchors
-
+ # Hack for making javascript work
+ filename = filename.replace('xhtml', 'xml')
+
+ filename = filename.split('#')[0] # Get rid of anchors
+
if self._loaded_page < 1 or filename == None:
return False
-
+
self._loaded_filename = filename
-
+
remfactor = self._paginator.get_remfactor_for_file(filename)
pages = self._paginator.get_pagecount_for_file(filename)
- extra = int(math.ceil(remfactor * self._view.get_page_height()/(pages-remfactor)))
+ extra = int(math.ceil(remfactor *
+ self._view.get_page_height() / (pages - remfactor)))
if extra > 0:
self._view.add_bottom_padding(extra)
-
+
if self.__in_search:
self._view.search_text(self._findjob.get_search_text(), \
self._findjob.get_case_sensitive(), \
@@ -382,49 +430,81 @@ class _View(gtk.HBox):
self._scroll_page_end()
else:
self._scroll_page()
-
+
base_pageno = self._paginator.get_base_pageno_for_file(filename)
scrollval = self._v_vscrollbar.get_value()
scroll_upper = self._v_vscrollbar.props.adjustment.props.upper
- if scroll_upper == 0: # This is a one page file
+ if scroll_upper == 0: # This is a one page file
pageno = base_pageno
else:
- offset = (scrollval/scroll_upper) * self._paginator.get_pagecount_for_file(filename)
+ offset = (scrollval / scroll_upper) * \
+ self._paginator.get_pagecount_for_file(filename)
pageno = math.floor(base_pageno + offset)
-
+
if pageno != self._loaded_page:
- self._on_page_changed(int(pageno))
-
+ self._on_page_changed(0, int(pageno))
+
+ # prepare text to speech
+ html_file = open(self._loaded_filename)
+ soup = BeautifulSoup.BeautifulSoup(html_file)
+ body = soup.find('body')
+ tags = body.findAll(text=True)
+ self._all_text = ''.join([tag for tag in tags])
+ self._prepare_text_to_speech(self._all_text)
+
+ def _prepare_text_to_speech(self, page_text):
+ i = 0
+ j = 0
+ word_begin = 0
+ word_end = 0
+ ignore_chars = [' ', '\n', u'\r', '_', '[', '{', ']', '}', '|',
+ '<', '>', '*', '+', '/', '\\']
+ ignore_set = set(ignore_chars)
+ self.word_tuples = []
+ len_page_text = len(page_text)
+ while i < len_page_text:
+ if page_text[i] not in ignore_set:
+ word_begin = i
+ j = i
+ while j < len_page_text and page_text[j] not in ignore_set:
+ j = j + 1
+ word_end = j
+ i = j
+ word_tuple = (word_begin, word_end,
+ page_text[word_begin: word_end])
+ if word_tuple[2] != u'\r':
+ self.word_tuples.append(word_tuple)
+ i = i + 1
def _scroll_page_end(self):
v_upper = self._v_vscrollbar.props.adjustment.props.upper
v_page_size = self._v_vscrollbar.props.adjustment.props.page_size
self._v_vscrollbar.set_value(v_upper)
-
+
def _scroll_page(self):
pageno = self._loaded_page
-
+
v_upper = self._v_vscrollbar.props.adjustment.props.upper
v_page_size = self._v_vscrollbar.props.adjustment.props.page_size
-
+
scrollfactor = self._paginator.get_scrollfactor_pos_for_pageno(pageno)
self._v_vscrollbar.set_value((v_upper - v_page_size) * scrollfactor)
-
+
def _paginate(self):
filelist = []
for i in self._epub._navmap.get_flattoc():
filelist.append(os.path.join(self._epub._tempdir, i))
-
+
self._paginator = _Paginator(filelist)
self._paginator.connect('paginated', self._paginated_cb)
-
+
def _load_next_page(self):
self._load_page(self._loaded_page + 1)
def _load_prev_page(self):
self._load_page(self._loaded_page - 1)
-
+
def _v_scrollbar_value_changed_cb(self, scrollbar):
if self._loaded_page < 1:
return
@@ -432,58 +512,87 @@ class _View(gtk.HBox):
scroll_upper = self._v_vscrollbar.props.adjustment.props.upper
scroll_page_size = self._v_vscrollbar.props.adjustment.props.page_size
- if self.__going_fwd == True and not self._loaded_page == self._pagecount:
+ if self.__going_fwd == True and \
+ not self._loaded_page == self._pagecount:
if self._paginator.get_file_for_pageno(self._loaded_page) != \
self._paginator.get_file_for_pageno(self._loaded_page + 1):
- return # We don't need this if the next page is in another file
+ # We don't need this if the next page is in another file
+ return
- scrollfactor_next = self._paginator.get_scrollfactor_pos_for_pageno(self._loaded_page + 1)
+ scrollfactor_next = \
+ self._paginator.get_scrollfactor_pos_for_pageno(
+ self._loaded_page + 1)
if scrollval > 0:
- scrollfactor = scrollval/(scroll_upper - scroll_page_size)
+ scrollfactor = scrollval / (scroll_upper - scroll_page_size)
else:
scrollfactor = 0
if scrollfactor >= scrollfactor_next:
- self._on_page_changed(self._loaded_page + 1)
+ self._on_page_changed(self._loaded_page, self._loaded_page + 1)
elif self.__going_back == True and self._loaded_page > 1:
if self._paginator.get_file_for_pageno(self._loaded_page) != \
- self._paginator.get_file_for_pageno(self._loaded_page - 1):
+ self._paginator.get_file_for_pageno(
+ self._loaded_page - 1):
return
- scrollfactor_cur = self._paginator.get_scrollfactor_pos_for_pageno(self._loaded_page)
+ scrollfactor_cur = \
+ self._paginator.get_scrollfactor_pos_for_pageno(
+ self._loaded_page)
if scrollval > 0:
- scrollfactor = scrollval/(scroll_upper - scroll_page_size)
+ scrollfactor = scrollval / (scroll_upper - scroll_page_size)
else:
scrollfactor = 0
if scrollfactor <= scrollfactor_cur:
- self._on_page_changed(self._loaded_page - 1)
-
- def _on_page_changed(self, pageno):
+ self._on_page_changed(self._loaded_page, self._loaded_page - 1)
+
+ def _on_page_changed(self, oldpage, pageno):
self.__page_changed = True
self._loaded_page = pageno
self._scrollbar.handler_block(self._scrollbar_change_value_cb_id)
self._scrollbar.set_value(pageno)
self._scrollbar.handler_unblock(self._scrollbar_change_value_cb_id)
- self.emit('page-changed')
-
+ self.emit('page-changed', oldpage, pageno)
+
def _load_page(self, pageno):
if pageno > self._pagecount or pageno < 1:
#TODO: Cause an exception
return
- self._on_page_changed(pageno)
+ self._on_page_changed(self._loaded_page, pageno)
filename = self._paginator.get_file_for_pageno(pageno)
if filename != self._loaded_filename:
#self._loaded_filename = filename
+
+ """
+ TODO: disabled because javascript can't be executed
+ with the velocity needed
+ # Copy javascript to highligth text to speech
+ destpath, destname = os.path.split(filename.replace('file://', ''))
+ shutil.copy('./epubview/highlight_words.js', destpath)
+ self._insert_js_reference(filename.replace('file://', ''),
+ destpath)
+ """
+
if filename.endswith('xml'):
dest = filename.replace('xml', 'xhtml')
- shutil.copy(filename.replace('file://', ''), dest.replace('file://', ''))
+ shutil.copy(filename.replace('file://', ''),
+ dest.replace('file://', ''))
self._view.open(dest)
else:
self._view.open(filename)
else:
self._scroll_page()
-
+
+ def _insert_js_reference(self, file_name, path):
+ js_reference = '<script type="text/javascript" ' + \
+ 'src="./highlight_words.js"></script>'
+ o = open(file_name + '.tmp', 'a')
+ for line in open(file_name):
+ line = line.replace('</head>', js_reference + '</head>')
+ o.write(line + "\n")
+ o.close()
+ shutil.copy(file_name + '.tmp', file_name)
+
def _load_next_file(self):
if self._loaded_page == self._pagecount:
return
@@ -493,7 +602,7 @@ class _View(gtk.HBox):
pageno += 1
if self._paginator.get_file_for_pageno(pageno) != cur_file:
break
-
+
self._load_page(pageno)
def _load_file(self, path):
@@ -513,7 +622,7 @@ class _View(gtk.HBox):
pageno -= 1
if self._paginator.get_file_for_pageno(pageno) != cur_file:
break
-
+
self._load_page(pageno)
def _scrollbar_change_value_cb(self, range, scrolltype, value):
@@ -526,7 +635,7 @@ class _View(gtk.HBox):
self.__going_fwd = False
self.__going_back = True
if not self._do_page_transition():
- self._view.move_cursor(gtk.MOVEMENT_DISPLAY_LINES, -1)
+ self._view.move_cursor(gtk.MOVEMENT_DISPLAY_LINES, -1)
elif scrolltype == gtk.SCROLL_JUMP or \
scrolltype == gtk.SCROLL_PAGE_FORWARD or \
scrolltype == gtk.SCROLL_PAGE_BACKWARD:
@@ -535,19 +644,21 @@ class _View(gtk.HBox):
else:
self._load_page(round(value))
else:
- print 'Warning: unknown scrolltype %s with value %f' % (str(scrolltype), value)
-
- self._scrollbar.set_value(self._loaded_page) #FIXME: This should not be needed here
-
+ print 'Warning: unknown scrolltype %s with value %f' \
+ % (str(scrolltype), value)
+
+ #FIXME: This should not be needed here
+ self._scrollbar.set_value(self._loaded_page)
+
if self.__page_changed == True:
self.__page_changed = False
return False
else:
return True
-
+
def _paginated_cb(self, object):
self._ready = True
-
+
self._pagecount = self._paginator.get_total_pagecount()
self._scrollbar.set_range(1.0, self._pagecount - 1.0)
self._scrollbar.set_increments(1.0, 1.0)
@@ -555,9 +666,6 @@ class _View(gtk.HBox):
self._view.grab_default()
if self._loaded_page < 1:
self._load_page(1)
-
-
def _destroy_cb(self, widget):
self._epub.close()
-
diff --git a/epubview/jobs.py b/epubview/jobs.py
index 19c8434..a0b6771 100644
--- a/epubview/jobs.py
+++ b/epubview/jobs.py
@@ -32,19 +32,21 @@ import threading
PAGE_WIDTH = 135
PAGE_HEIGHT = 216
+
def _pixel_to_mm(pixel, dpi):
- inches = pixel/dpi
- return int(inches/0.03937)
+ inches = pixel / dpi
+ return int(inches / 0.03937)
+
def _mm_to_pixel(mm, dpi):
inches = mm * 0.03937
return int(inches * dpi)
-
class SearchThread(threading.Thread):
+
def __init__(self, obj):
- threading.Thread.__init__ (self)
+ threading.Thread.__init__(self)
self.obj = obj
self.stopthread = threading.Event()
@@ -57,50 +59,49 @@ class SearchThread(threading.Thread):
if self._searchfile(f):
self.obj._matchfilelist.append(entry)
f.close()
-
+
gtk.gdk.threads_enter()
- self.obj._finished = True
+ self.obj._finished = True
self.obj.emit('updated')
gtk.gdk.threads_leave()
-
+
return False
-
+
def _searchfile(self, fileobj):
soup = BeautifulSoup.BeautifulSoup(fileobj)
body = soup.find('body')
tags = body.findChildren(True)
for tag in tags:
- if not tag.string is None:
+ if not tag.string is None:
if tag.string.find(self.obj._text) > -1:
return True
-
+
return False
- def run (self):
+ def run(self):
self._start_search()
-
+
def stop(self):
self.stopthread.set()
-
class _JobPaginator(gobject.GObject):
+
__gsignals__ = {
- 'paginated': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
- }
+ 'paginated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
+ }
+
def __init__(self, filelist):
gobject.GObject.__init__(self)
-
+
self._filelist = filelist
self._filedict = {}
self._pagemap = {}
-
+
self._bookheight = 0
self._count = 0
self._pagecount = 0
-
+
self._screen = gtk.gdk.screen_get_default()
self._old_fontoptions = self._screen.get_font_options()
options = cairo.FontOptions()
@@ -109,7 +110,7 @@ class _JobPaginator(gobject.GObject):
options.set_subpixel_order(cairo.SUBPIXEL_ORDER_DEFAULT)
options.set_hint_metrics(cairo.HINT_METRICS_DEFAULT)
self._screen.set_font_options(options)
-
+
self._temp_win = gtk.Window()
self._temp_view = widgets._WebView()
@@ -119,93 +120,97 @@ class _JobPaginator(gobject.GObject):
settings.props.serif_font_family = 'DejaVu LGC Serif'
settings.props.monospace_font_family = 'DejaVu LGC Sans Mono'
settings.props.enforce_96_dpi = True
- #settings.props.auto_shrink_images = False #FIXME: This does not seem to work
+ #FIXME: This does not seem to work
+ #settings.props.auto_shrink_images = False
settings.props.enable_plugins = False
settings.props.default_font_size = 12
settings.props.default_monospace_font_size = 10
settings.props.default_encoding = 'utf-8'
-
+
sw = gtk.ScrolledWindow()
sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_NEVER)
self._dpi = 96
- sw.set_size_request(_mm_to_pixel(PAGE_WIDTH, self._dpi), _mm_to_pixel(PAGE_HEIGHT, self._dpi))
+ sw.set_size_request(_mm_to_pixel(PAGE_WIDTH, self._dpi),
+ _mm_to_pixel(PAGE_HEIGHT, self._dpi))
sw.add(self._temp_view)
self._temp_win.add(sw)
self._temp_view.connect('load-finished', self._page_load_finished_cb)
-
+
self._temp_win.show_all()
self._temp_win.unmap()
-
+
self._temp_view.open(self._filelist[self._count])
-
+
def _page_load_finished_cb(self, v, frame):
f = v.get_main_frame()
pageheight = v.get_page_height()
-
+
if pageheight <= _mm_to_pixel(PAGE_HEIGHT, self._dpi):
pages = 1
else:
- pages = pageheight/float(_mm_to_pixel(PAGE_HEIGHT, self._dpi))
+ pages = pageheight / float(_mm_to_pixel(PAGE_HEIGHT, self._dpi))
for i in range(1, int(math.ceil(pages) + 1)):
if pages - i < 0:
- pagelen = (pages - math.floor(pages))/pages
+ pagelen = (pages - math.floor(pages)) / pages
else:
- pagelen = 1/pages
- self._pagemap[float(self._pagecount + i)] = (f.props.uri, (i-1)/math.ceil(pages), pagelen)
-
- self._pagecount += math.ceil(pages)
- self._filedict[f.props.uri.replace('file://', '')] = (math.ceil(pages), math.ceil(pages) - pages)
+ pagelen = 1 / pages
+ self._pagemap[float(self._pagecount + i)] = \
+ (f.props.uri, (i - 1) / math.ceil(pages), pagelen)
+
+ self._pagecount += int(math.ceil(pages))
+ self._filedict[f.props.uri.replace('file://', '')] = \
+ (math.ceil(pages), math.ceil(pages) - pages)
self._bookheight += pageheight
-
- if self._count+1 >= len(self._filelist):
+
+ if self._count + 1 >= len(self._filelist):
self._temp_win.destroy()
self._screen.set_font_options(self._old_fontoptions)
self.emit('paginated')
else:
self._count += 1
self._temp_view.open(self._filelist[self._count])
-
-
+
def get_file_for_pageno(self, pageno):
'''
Returns the file in which pageno occurs
- '''
+ '''
return self._pagemap[pageno][0]
-
+
def get_scrollfactor_pos_for_pageno(self, pageno):
'''
Returns the position scrollfactor (fraction) for pageno
- '''
+ '''
return self._pagemap[pageno][1]
def get_scrollfactor_len_for_pageno(self, pageno):
'''
Returns the length scrollfactor (fraction) for pageno
- '''
+ '''
return self._pagemap[pageno][2]
-
+
def get_pagecount_for_file(self, filename):
'''
Returns the number of pages in file
- '''
+ '''
return self._filedict[filename][0]
def get_base_pageno_for_file(self, filename):
'''
Returns the pageno which begins in filename
- '''
+ '''
for key in self._pagemap.keys():
if self._pagemap[key][0].replace('file://', '') == filename:
return key
-
+
return None
def get_remfactor_for_file(self, filename):
'''
- Returns the remainder factor (1 - fraction length of last page in file)
- '''
+ Returns the remainder
+ factor (1 - fraction length of last page in file)
+ '''
return self._filedict[filename][1]
-
+
def get_total_pagecount(self):
'''
Returns the total pagecount for the Epub file
@@ -215,20 +220,20 @@ class _JobPaginator(gobject.GObject):
def get_total_height(self):
'''
Returns the total height of the Epub in pixels
- '''
+ '''
return self._bookheight
class _JobFind(gobject.GObject):
__gsignals__ = {
- 'updated': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
+ 'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
- def __init__(self, document, start_page, n_pages, text, case_sensitive=False):
+
+ def __init__(self, document, start_page, n_pages, text,
+ case_sensitive=False):
gobject.GObject.__init__(self)
gtk.gdk.threads_init()
-
+
self._finished = False
self._document = document
self._start_page = start_page
@@ -239,58 +244,58 @@ class _JobFind(gobject.GObject):
self._matchfilelist = []
self._current_file_index = 0
self.threads = []
-
+
s_thread = SearchThread(self)
self.threads.append(s_thread)
s_thread.start()
-
+
def cancel(self):
'''
Cancels the search job
- '''
+ '''
for s_thread in self.threads:
s_thread.stop()
-
+
def is_finished(self):
'''
Returns True if the entire search job has been finished
- '''
+ '''
return self._finished
-
+
def get_next_file(self):
'''
Returns the next file which has the search pattern
- '''
+ '''
self._current_file_index += 1
try:
path = self._matchfilelist[self._current_file_index]
except IndexError:
self._current_file_index = 0
path = self._matchfilelist[self._current_file_index]
-
+
return path
def get_prev_file(self):
'''
Returns the previous file which has the search pattern
- '''
+ '''
self._current_file_index -= 1
try:
path = self._matchfilelist[self._current_file_index]
except IndexError:
self._current_file_index = -1
path = self._matchfilelist[self._current_file_index]
-
+
return path
def get_search_text(self):
'''
Returns the search text
- '''
+ '''
return self._text
-
+
def get_case_sensitive(self):
'''
Returns True if the search is case-sensitive
- '''
+ '''
return self._case_sensitive
diff --git a/epubview/navmap.py b/epubview/navmap.py
index a0a1799..612f2d1 100644
--- a/epubview/navmap.py
+++ b/epubview/navmap.py
@@ -1,18 +1,20 @@
from lxml import etree
import gtk
+
class NavPoint(object):
- def __init__(self, label, contentsrc, children = []):
+
+ def __init__(self, label, contentsrc, children=[]):
self._label = label
self._contentsrc = contentsrc
- self._children = children
-
+ self._children = children
+
def get_label(self):
return self._label
-
+
def get_contentsrc(self):
return self._contentsrc
-
+
def get_children(self):
return self._children
@@ -25,69 +27,74 @@ class NavMap(object):
self._root = self._tree.getroot()
self._gtktreestore = gtk.TreeStore(str, str)
self._flattoc = []
-
+
self._populate_flattoc()
self._populate_toc()
-
+
def _populate_flattoc(self):
tree = etree.parse(self._opffile)
root = tree.getroot()
-
+
itemmap = {}
manifest = root.find('.//{http://www.idpf.org/2007/opf}manifest')
for element in manifest.iterfind('{http://www.idpf.org/2007/opf}item'):
itemmap[element.get('id')] = element
-
- spine = root.find('.//{http://www.idpf.org/2007/opf}spine')
+
+ spine = root.find('.//{http://www.idpf.org/2007/opf}spine')
for element in spine.iterfind('{http://www.idpf.org/2007/opf}itemref'):
idref = element.get('idref')
href = itemmap[idref].get('href')
self._flattoc.append(self._basepath + href)
-
+
self._opffile.close()
-
+
def _populate_toc(self):
- navmap = self._root.find('{http://www.daisy.org/z3986/2005/ncx/}navMap')
- for navpoint in navmap.iterfind('./{http://www.daisy.org/z3986/2005/ncx/}navPoint'):
+ navmap = self._root.find(
+ '{http://www.daisy.org/z3986/2005/ncx/}navMap')
+ for navpoint in navmap.iterfind(
+ './{http://www.daisy.org/z3986/2005/ncx/}navPoint'):
self._process_navpoint(navpoint)
-
+
def _gettitle(self, navpoint):
- text = navpoint.find('./{http://www.daisy.org/z3986/2005/ncx/}navLabel/{http://www.daisy.org/z3986/2005/ncx/}text')
+ text = navpoint.find('./{http://www.daisy.org/z3986/2005/ncx/}' +
+ 'navLabel/{http://www.daisy.org/z3986/2005/ncx/}text')
return text.text
def _getcontent(self, navpoint):
- text = navpoint.find('./{http://www.daisy.org/z3986/2005/ncx/}content/')
+ text = navpoint.find(
+ './{http://www.daisy.org/z3986/2005/ncx/}content/')
return self._basepath + text.get('src')
- def _process_navpoint(self, navpoint, parent = None):
+ def _process_navpoint(self, navpoint, parent=None):
title = self._gettitle(navpoint)
content = self._getcontent(navpoint)
-
+
#print title, content
-
+
iter = self._gtktreestore.append(parent, [title, content])
#self._flattoc.append((title, content))
-
- childnavpointlist = list(navpoint.iterfind('./{http://www.daisy.org/z3986/2005/ncx/}navPoint'))
-
+
+ childnavpointlist = list(navpoint.iterfind(
+ './{http://www.daisy.org/z3986/2005/ncx/}navPoint'))
+
if len(childnavpointlist):
for childnavpoint in childnavpointlist:
- self._process_navpoint(childnavpoint, parent = iter)
- else:
+ self._process_navpoint(childnavpoint, parent=iter)
+ else:
return
-
+
def get_gtktreestore(self):
'''
Returns a GtkTreeModel representation of the
Epub table of contents
- '''
+ '''
return self._gtktreestore
-
+
def get_flattoc(self):
'''
Returns a flat (linear) list of files to be
rendered.
- '''
+ '''
return self._flattoc
-
-#t = TocParser('/home/sayamindu/Desktop/Test/OPS/fb.ncx') \ No newline at end of file
+
+#t = TocParser('/home/sayamindu/Desktop/Test/OPS/fb.ncx')
diff --git a/epubview/widgets.py b/epubview/widgets.py
index d80c45a..a603c34 100644
--- a/epubview/widgets.py
+++ b/epubview/widgets.py
@@ -5,27 +5,40 @@ import gtk
class _WebView(webkit.WebView):
def __init__(self):
webkit.WebView.__init__(self)
-
+
def get_page_height(self):
'''
Gets height (in pixels) of loaded (X)HTML page.
This is done via javascript at the moment
- '''
- #TODO: Need to check status of page load
- js = 'oldtitle=document.title;document.title=Math.max(document.body.scrollHeight, document.body.offsetHeight,document.documentElement.clientHeight, document.documentElement.scrollHeight, document.documentElement.offsetHeight);'
+ '''
+ js = 'oldtitle=document.title;' + \
+ 'if (document.body == null) {' + \
+ 'document.title = 0} else {' + \
+ 'document.title=Math.max(document.body.scrollHeight, ' + \
+ 'document.body.offsetHeight,document.documentElement.clientHeight,' + \
+ 'document.documentElement.scrollHeight, ' + \
+ 'document.documentElement.offsetHeight)};'
self.execute_script(js)
ret = self.get_main_frame().get_title()
js = 'document.title=oldtitle;'
self.execute_script(js)
- if ret is None:
+ try:
+ return int(ret)
+ except ValueError:
return 0
- return int(ret)
-
+
def add_bottom_padding(self, incr):
'''
Adds incr pixels of padding to the end of the loaded (X)HTML page.
This is done via javascript at the moment
- '''
- js = ('var newdiv = document.createElement("div");newdiv.style.height = "%dpx";document.body.appendChild(newdiv);' % incr)
+ '''
+ js = ('var newdiv = document.createElement("div");' + \
+ 'newdiv.style.height = "%dpx";document.body.appendChild(newdiv);' \
+ % incr)
self.execute_script(js)
-
+
+ def highlight_next_word(self):
+ '''
+ Highlight next word (for text to speech)
+ '''
+ self.execute_script('highLightNextWord();')
diff --git a/readdb.py b/readdb.py
index 169b998..e308203 100644
--- a/readdb.py
+++ b/readdb.py
@@ -20,15 +20,19 @@ import logging
import os, os.path
import shutil
import sqlite3
+import random
+import hashlib
import time
import gconf
-import cjson
+import simplejson
+#import cjson
import urllib, urllib2
import re
from xml.dom import minidom
from sugar.datastore import datastore
from sugar import mime
from annobookmark import AnnoBookmark, Bookmark
+from sugar.graphics.xocolor import XoColor
_logger = logging.getLogger('anno-activity')
@@ -36,7 +40,6 @@ _logger = logging.getLogger('anno-activity')
def _init_db():
dbdir = os.path.join(os.environ['SUGAR_ACTIVITY_ROOT'], 'data')
dbpath = os.path.join(dbdir, 'anno_v1.db')
- olddbpath = os.path.join(dbdir, 'anno.db')
srcpath = os.path.join(os.environ['SUGAR_BUNDLE_PATH'], 'anno_v1.db')
@@ -45,35 +48,42 @@ def _init_db():
return dbpath
#Situation 1: DB is non-existent at all
- if not os.path.exists(dbpath) and not os.path.exists(olddbpath):
+ if not os.path.exists(dbpath):
try:
os.makedirs(dbdir)
except:
pass
shutil.copy(srcpath, dbpath)
return dbpath
-
+
+
#Situation 2: DB is outdated
- if not os.path.exists(dbpath) and os.path.exists(olddbpath):
- shutil.copy(olddbpath, dbpath)
-
+ """
+ if not os.path.exists(dbpath):
conn = sqlite3.connect(dbpath)
- conn.execute("CREATE TABLE temp_bookmarks AS SELECT md5, page, 'content', timestamp, user, color, local FROM bookmarks")
- conn.execute("ALTER TABLE bookmarks RENAME TO bookmarks_old")
- conn.execute("ALTER TABLE temp_bookmarks RENAME TO bookmarks")
- conn.execute("DROP TABLE bookmarks_old")
#conn.execute("DROP TABLE annotations")
- conn.execute("CREATE TABLE annotations (id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, context, created TIMESTAMP, modified TIMESTAMP, creator, annotates, rdf, color, local, mimetype, uuid, annotationurl, bodysvg)")
+ conn.execute("CREATE TABLE annotations (id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, texttitle, textcreator, created TIMESTAMP, modified TIMESTAMP, creator, annotates, color, local, mimetype, uuid, annotationurl)")
+
conn.execute("CREATE TABLE annuserid (username, userid)")
conn.commit()
conn.close()
return dbpath
-
+ """
# Should not reach this point
return None
+def _init_db_highlights(conn):
+ conn.execute('CREATE TABLE IF NOT EXISTS HIGHLIGHTS ' +
+ '(md5 TEXT, page INTEGER, ' +
+ 'init_pos INTEGER, end_pos INTEGER)')
+ conn.commit()
+
+
+
+
+
class BookmarkManager:
def __init__(self, filehash):
self._filehash = filehash
@@ -171,21 +181,6 @@ class BookmarkManager:
#working on: upon update of an annotation, all annotations are deleted and new one's created => needs to be fixed
class AnnotationManager:
- #Namespaces
- #cnt http://www.w3.org/2008/content# Content in RDF [Content]
- #dc http://purl.org/dc/elements/1.1/ Dublin Core elements [DC Elements]
- #dcterms http://purl.org/dc/terms/ Dublin Core terms [DC Terms]
- #foaf http://xmlns.com/foaf/0.1/ Friend of a Friend vocabulary terms [FOAF]
- #oac http://www.openannotation.org/ns/ OAC vocabulary terms [OAC]
- #ore http://www.openarchives.org/ore/terms/ ORE vocabulary terms
- #rdf http://www.w3.org/1999/02/22-rdf-syntax-ns# RDF vocabulary terms [RDF Vocabulary]
- #rdfs http://www.w3.org/2001/01/rdf-schema# RDF Schema vocabulary [RDF Vocabulary]
- #bibo http://purl.org/ontology/bibo/ Bibliographic vocabulary
- #oac:Annotation http://www.openannotation.org/ns/Annotation
- #oac:hasTarget http://www.openannotation.org/ns/hasTarget
- #oac:hasBody http://www.openannotation.org/ns/hasBody
- #oac:Target http://www.openannotation.org/ns/Target
- #oac:Body http://www.openannotation.org/ns/Body
def __init__(self, filehash, mimetype, sidebar):
@@ -207,56 +202,112 @@ class AnnotationManager:
self._annotationurl = ''
self._bodyurl = ''
self._annotates = ''
- self._context = ''
self._modified = ''
self._bodysvg = ''
self._id = ''
self._rdf = ''
self.modifiedtolerance = 10
- #self._annotationserver='http://localhost/wordpress/wp-content/plugins/annotation/annotation.php'
- self._annotationserver='http://www.andreasgros.net/wp-content/plugins/annotation/annotation.php'
+ #self._annotationserver='http://localhost/anno/index.php'
+ self._annotationserver='http://anno.treehouse.su/anno/index.php'
+ #self._annotationserver='http://www.andreasgros.net/wp-content/plugins/annotation/annotation.php'
self.get_etext_url()
self._to_delete = []
+
+ self._texttitle = ''
+ self._textcreator = ''
+ self._annojson = ''
+ self.remotecreators = []
+ self.remotecolors = {}
+ self._highlights = {}
dbpath = _init_db()
assert dbpath != None
self._conn = sqlite3.connect(dbpath)
+
+ _init_db_highlights(self._conn)
+
self._conn.text_factory = lambda x: unicode(x, 'utf-8', 'ignore')
self._annotations = []
self._populate_annotations()
-
- def parse_userid(self, rdf):
- userid = ''
- dom = minidom.parseString(rdf)
- userid = self.get_node_data(dom.getElementsByTagName("dcterms:identifier")[0])
- _logger.debug('got this userid from the server: %s' % userid)
- return userid
+
+ def get_highlights(self, page):
+ try:
+ return self._highlights[page]
+ except KeyError:
+ self._highlights[page] = []
+ return self._highlights[page]
+
+
+
+ def add_highlight(self, page, highlight_tuple):
+ logging.error('Adding hg page %d %s' % (page, highlight_tuple))
+ self.get_highlights(page).append(highlight_tuple)
+
+ t = (self._filehash, page, highlight_tuple[0],
+ highlight_tuple[1])
+ self._conn.execute('insert into highlights values ' + \
+ '(?, ?, ?, ?)', t)
+ self._conn.commit()
+
+ def del_highlight(self, page, highlight_tuple):
+ self._highlights[page].remove(highlight_tuple)
+ t = (self._filehash, page, highlight_tuple[0], \
+ highlight_tuple[1])
+ self._conn.execute('delete from highlights ' + \
+ 'where md5=? and page=? and init_pos=? and end_pos=?', \
+ t)
+ self._conn.commit()
+
+ def _populate_highlights(self):
+ rows = self._conn.execute('select * from highlights ' + \
+ 'where md5=? order by page', (self._filehash, ))
+ for row in rows:
+ page = row[1]
+ init_pos = row[2]
+ end_pos = row[3]
+ highlight_tuple = [init_pos, end_pos]
+ self.get_highlights(page).append(highlight_tuple)
def get_userid_for_username(self, user):
+ needsupdate = False
+ needsinsert = False
userid = ''
rows = self._conn.execute('select userid from annuserid where username=?', (user, ))
if ( rows != None ):
r = rows.fetchone()
- if r != None:
- userid = r[0]
- else:
- url = self._annotationserver
- values = {'getidforuser' : user}
- try:
- data = urllib.urlencode(values)
- req = urllib2.Request(url, data)
- response = urllib2.urlopen(req)
- rdf = response.read()
- _logger.debug("\n\ngot this userid rdf %s\n\n" % rdf)
- userid = self.parse_userid(rdf)
- _logger.debug("\nuserid is %s\n\n" % userid)
- except Exception, detail:
- _logger.debug("userid fetching failed; detail: %s ", detail)
- self._conn.execute('insert into annuserid values (?, ?)', (user, userid))
- self._conn.commit()
+ if ( r != None ):
+ if ( len(r[0]) > 0 ):
+ userid = r[0]
+ else:
+ needsupdate = True
+ else:
+ needsinsert = True
+ if ( rows == None ) or needsupdate or needsinsert:
+ url = self._annotationserver
+ values = {'getidforuser' : user}
+ try:
+ data = urllib.urlencode(values)
+ req = urllib2.Request(url, data)
+ response = urllib2.urlopen(req)
+ jsonstr = response.read()
+ _logger.debug("\n\ngot this userid json %s\n\n" % jsonstr )
+ json_arr = simplejson.loads( jsonstr )
+ _logger.debug("userid - json_arr %s" % json_arr )
+ userid = json_arr['userid']
+ _logger.debug("\nuserid is %s\n\n" % userid)
+ except Exception, detail:
+ _logger.debug("userid fetching failed; detail: %s ", detail)
+ if not needsupdate or needsinsert:
+ _logger.debug('insert user, userid %s', str((user, userid)))
+ self._conn.execute( 'insert into annuserid values (?, ?)', (user, userid) )
+ else:
+ _logger.debug('updating userid %s', userid)
+ self._conn.execute( 'update annuserid set userid=? where username=?', ( userid, user ) )
+ self._conn.commit()
+ _logger.debug('userid: found %s', userid)
return userid
@@ -268,11 +319,14 @@ class AnnotationManager:
client = gconf.client_get_default()
user = client.get_string("/desktop/sugar/user/nick")
color = client.get_string("/desktop/sugar/user/color")
-
+ self._color = color
+ _logger.debug('got this color: %s' % color)
if self._userid == '':
- self._userid = self.get_userid_for_username(user)
+ self._userid = self.get_userid_for_username( self.get_user_string( user ) )
+ _logger.debug('got this userid: %s' % self._userid)
- note = cjson.decode(content)
+ note = simplejson.loads(content)
+ #note = cjson.decode(content)
self._annotitle = note['title']
self._content = note['body']
self._creator = self._userid
@@ -284,7 +338,6 @@ class AnnotationManager:
#send annotation to server -> get url back, store url in body-url
#update annotation body url in local db
- self._bodysvg = self.assembleSVG(self._annotitle, self._content)
#check the last id from the database store
row = self._conn.execute('select id from annotations order by id desc limit 1')
@@ -293,13 +346,14 @@ class AnnotationManager:
if r != None:
aid = int(r[0]) + 1
- t = (aid, self._filehash, page, self._annotitle, self._content, self._bodyurl, self._context, self._created, self._modified, self._userid, self._annotates, '', self._color, self._local, self._mimetype, None, None, self._bodysvg)
+ #(id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, texttitle, textcreator, created TIMESTAMP, modified TIMESTAMP, creator, annotates, color, local, mimetype, uuid, annotationurl)
+ t = (aid, self._filehash, page, self._annotitle, self._content, self._bodyurl, self._texttitle, self._textcreator, self._created, self._modified, self._userid, self._annotates, self._color, self._local, self._mimetype, None, None)
annotation = AnnoBookmark(t)
- self._rdf = self.create_rdf_representation(annotation)
-
- t = (aid, self._filehash, page, self._annotitle, self._content, self._bodyurl, self._context, self._created, self._modified, self._creator, self._annotates, self._rdf, self._color, self._local, self._mimetype, annotation.get_uuid(), None, self._bodysvg)
- self._conn.execute('insert into annotations values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', t)
+ self._annojson = annotation.get_json()
+ #(id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, texttitle, textcreator, created TIMESTAMP, modified TIMESTAMP, creator, annotates, color, local, mimetype, uuid, annotationurl)
+ t = (aid, self._filehash, page, self._annotitle, self._content, self._bodyurl, self._texttitle, self._textcreator, self._created, self._modified, self._creator, self._annotates, self._color, self._local, self._mimetype, annotation.get_uuid(), None)
+ self._conn.execute('insert into annotations values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', t)
self._conn.commit()
self._resync_annotation_cache()
self.current_annotation = self._annotations[-1]
@@ -311,7 +365,8 @@ class AnnotationManager:
# local = 0 means that this is a bookmark originally
# created by the person who originally shared the file
print 'edit annotation called'
- note = cjson.decode(content)
+ note = simplejson.loads(content)
+ #note = cjson.decode(content)
annotitle = note['title']
annocontent = note['body']
@@ -320,8 +375,6 @@ class AnnotationManager:
a.set_modified(time.time())
a.set_note_title(annotitle)
a.set_note_body(annocontent)
- a.set_bodysvg(self.assembleSVG(annotitle, annocontent))
- a.set_rdf(self.create_rdf_representation(a))
self.update_annotation_db_record(a)
break
@@ -335,11 +388,13 @@ class AnnotationManager:
rows = self._conn.execute('select uuid from annotations where md5=? and id=?', [self._filehash, annotation_id])
row = rows.fetchone()
self._to_delete.append(row[0])
- _logger.debug(str('schedule %s for deletion' % row[0]))
+ _logger.debug(str('schedule annotation %s for deletion' % str(row)))
# We delete only the locally made annotation
t = (self._filehash, annotation_id, self._userid)
+ _logger.debug(str('t for deletion is %s' % str(t)))
self._conn.execute('delete from annotations where md5=? and id=? and creator=?', t)
- self._conn.commit()
+ check = self._conn.commit()
+ _logger.debug('deletion check %s ', str(check))
self._resync_annotation_cache()
@@ -347,7 +402,8 @@ class AnnotationManager:
def _populate_annotations(self):
# TODO: Figure out if caching the entire set of annotations is a good idea or not
- rows = self._conn.execute('select id, md5, page, title, content, bodyurl, context, created, modified, creator, annotates, rdf, color, local, mimetype, uuid, annotationurl, bodysvg from annotations where md5=? order by page', [self._filehash])
+ #(id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, texttitle, textcreator, created TIMESTAMP, modified TIMESTAMP, creator, annotates, color, local, mimetype, uuid, annotationurl)
+ rows = self._conn.execute('select id, md5, page, title, content, bodyurl, texttitle, textcreator, created, modified, creator, annotates, color, local, mimetype, uuid, annotationurl from annotations where md5=? order by page', [self._filehash])
for row in rows:
self._annotations.append(AnnoBookmark(row))
_logger.debug('picked %s', self._annotations[-1].get_note_body())
@@ -399,15 +455,18 @@ class AnnotationManager:
if len(prevpages) > 0:
page = prevpages[-1]
else:
- page = pages_with_annotations[-1]
- self.current_annotation = id_ann_map[pages[page][0]]
- t = pages[self.current_annotation.page]
- tind = t.index(self.current_annotation.id)
- if tind > 0: #prev annotation on the same page
- self.current_annotation = id_ann_map[t[tind - 1]]
- else:
- self.current_annotation = id_ann_map[pages[pages_with_annotations[( pages_with_annotations.index(self.current_annotation.page) - 1 ) % len(pages_with_annotations)]][-1]]
- return self.current_annotation
+ if len(pages_with_annotations) > 0:
+ page = pages_with_annotations[-1]
+ if page in pages.keys():
+ self.current_annotation = id_ann_map[pages[page][0]]
+ if not self.current_annotation == None:
+ t = pages[self.current_annotation.page]
+ tind = t.index(self.current_annotation.id)
+ if tind > 0: #prev annotation on the same page
+ self.current_annotation = id_ann_map[t[tind - 1]]
+ else:
+ self.current_annotation = id_ann_map[pages[pages_with_annotations[( pages_with_annotations.index(self.current_annotation.page) - 1 ) % len(pages_with_annotations)]][-1]]
+ return self.current_annotation
return None
@@ -424,16 +483,19 @@ class AnnotationManager:
if len(nextpages) > 0:
page = nextpages[0]
else:
- page = pages_with_annotations[0]
- self.current_annotation = id_ann_map[pages[page][0]]
- t = pages[self.current_annotation.page]
- _logger.debug('ids for current page %s' % str(t))
- tind = t.index(self.current_annotation.id)
- if tind < len(t) - 1: #next annotation on the same page
- self.current_annotation = id_ann_map[t[tind + 1]]
- else:
- self.current_annotation = id_ann_map[pages[pages_with_annotations[( pages_with_annotations.index(self.current_annotation.page) + 1 ) % len(pages_with_annotations)]][0]]
- return self.current_annotation
+ if len(pages_with_annotations) > 0:
+ page = pages_with_annotations[0]
+ if page in pages.keys():
+ self.current_annotation = id_ann_map[pages[page][0]]
+ if not self.current_annotation == None:
+ t = pages[self.current_annotation.page]
+ _logger.debug('ids for current page %s' % str(t))
+ tind = t.index(self.current_annotation.id)
+ if tind < len(t) - 1: #next annotation on the same page
+ self.current_annotation = id_ann_map[t[tind + 1]]
+ else:
+ self.current_annotation = id_ann_map[pages[pages_with_annotations[( pages_with_annotations.index(self.current_annotation.page) + 1 ) % len(pages_with_annotations)]][0]]
+ return self.current_annotation
return None
@@ -488,118 +550,16 @@ class AnnotationManager:
- def parse_annotations(self, rdf):
+ def parse_annotations(self, json):
+ annoarr = simplejson.loads(json)
+ #annoarr = cjson.decode(json)
remote_annotations = []
- _logger.debug("parse_annotations: The RDF: %s", rdf)
- dom = minidom.parseString(rdf)
- annotations = dom.getElementsByTagName("oac:Annotation")
- t1 = []
- t2 = []
- clientuuid = None
- title = None
- creator = None
- page = None
- created = None
- annotates = None
- contextabout = None
- mimetype = None
- contentnode = None
- contentuuid = None
- annourl = None
- bodyurl = None
- modified = None
-
- for annotationdomelement in annotations:
- _logger.debug(str('\n\nparsing annotation dom element xml %s\n\n' % annotationdomelement.toxml() ))
- clientuuid = annotationdomelement.attributes[u'rdf:about'].value
- title = self.get_node_data(annotationdomelement.getElementsByTagName("dc:title")[0])
- creator = self.get_node_data(annotationdomelement.getElementsByTagName("dcterms:creator")[0])
- page = self.get_node_data(annotationdomelement.getElementsByTagName("bibo:pageStart")[0])
- if len(page) > 0:
- page = int(page)
- created = self.get_node_data(annotationdomelement.getElementsByTagName("oac:when")[0])
- created = self.makeTimeStampFromDateTimeString(created)
- annotates = self.get_node_attribute(annotationdomelement.getElementsByTagName("oac:hasTarget")[0], "rdf:resource")
- contextabout = self.get_node_attribute(annotationdomelement.getElementsByTagName("oac:contextAbout")[0], "rdf:resource")
- mimetype = self.get_node_data(annotationdomelement.getElementsByTagName("dc:type")[0])
- context = self.get_node_attribute(annotationdomelement.getElementsByTagName("oac:Context")[0], "rdf:resource")
- contentnodes = annotationdomelement.getElementsByTagName("oac:hasContent")
- if ( contentnodes != None ) and ( len(contentnodes) > 0 ):
- contentuuid = self.get_node_attribute(contentnodes[0], "rdf:about")
- annourl = self.get_node_attribute(contentnodes[0].getElementsByTagName("rdf:type")[0], "rdf:resource")
- bodyurl = self.get_node_data(contentnodes[0].getElementsByTagName("oac:body")[0])
- modified = self.get_node_data(annotationdomelement.getElementsByTagName("dcterms:modified")[0])
- modified = self.makeTimeStampFromDateTimeString(modified)
- t1.append({'page' : page, 'bodyurl' : bodyurl, 'contextabout' : contextabout, 'created' : created, 'modified' : modified, 'creator' : creator, 'annotates' : annotates, 'mimetype' : mimetype, 'clientuuid' : clientuuid, 'annourl' : annourl })
-
- descriptions = dom.getElementsByTagName("rdf:Description")
- for desc in descriptions:
- contentsvgnode = desc.getElementsByTagName("cnt:chars")[0]
- contentsvg = contentsvgnode.toxml()
- tspans = contentsvgnode.getElementsByTagName("svg:tspan")
- notetitle = ''
- notecontent = ''
- for ts in tspans:
- if ts.attributes["id"].value == 'notetitle':
- notetitle = ts.firstChild.data
- if ts.attributes["id"].value == 'notecontent':
- notecontent = ts.firstChild.data
- t2.append({'contentsvg' : contentsvg, 'notetitle' : notetitle, 'notecontent' : notecontent})
- if (len(t1) > 0) and (len(t1) == len(t2)):
- for i in range(len(t1)):
- remote_annotations.append(AnnoBookmark((None, self._filehash, t1[i]['page'], t2[i]['notetitle'], t2[i]['notecontent'], t1[i]['bodyurl'], t1[i]['contextabout'], t1[i]['created'], t1[i]['modified'], t1[i]['creator'], t1[i]['annotates'], '', '', '', t1[i]['mimetype'], t1[i]['clientuuid'], t1[i]['annourl'], t2[i]['contentsvg'])))
- else:
- _logger.debug('Parse annotation error: RDF parsing went wrong: %s' % rdf)
- return remote_annotations
-
-
-
- def assemble_annotation(self, annotationdomelement):
- _logger.debug("parse annotationdomelements %s ", annotationdomelement.toxml())
- #clientuuid = annotationdomelement.firstChild.attributes["rdf:about"]
- #_logger.debug("parse annotationdomelements: uuid: %s", clientuuid)
- #title = annotationdomelement.getElementsByTagName("dc:title")[0].firstChild.data
- #_logger.debug("parse annotationdomelements: title: %s", title)
- #creator = annotationdomelement.getElementsByTagName("dcterms:creator")[0].firstChild.data
- #_logger.debug("parse annotationdomelements: creator: %s", creator)
- #created = annotationdomelement.getElementsByTagName("oac:when")[0].firstChild.data
- #_logger.debug("parse annotationdomelements: created: %s", created)
-
-
-
-
- def get_annotationurl_from_server_response(self, rdf):
- annotationurl = ''
- dom = minidom.parseString(rdf)
- urlnodes = dom.getElementsByTagName("rdf:Description")
- if ( urlnodes != None ):
- node = urlnodes[0]
- if "rdf:about" in node.attributes.keys():
- annotationurl = node.attributes["rdf:about"].value
- return annotationurl
+ #(id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, texttitle, textcreator, created TIMESTAMP, modified TIMESTAMP, creator, annotates, color, local, mimetype, uuid, annotationurl)
+ for a in annoarr:
+ t = (a['id'], a['md5'], a['page'], a['title'], a['content'], a['bodyurl'], a['texttitle'], a['textcreator'], a['created'], a['modified'], a['creator'], a['annotates'], a['color'], a['local'], a['mimetype'], a['uuid'], a['annotationurl'])
+ remote_annotations.append(AnnoBookmark(t))
-
-
- #check for the uuid and pick the right one
- def get_bodyurl_from_server_response(self, rdf):
- bodyurl = ''
- dom = minidom.parseString(rdf)
- urlnodes = dom.getElementsByTagName("oac:body")
- if ( urlnodes != None ):
- node = urlnodes[0]
- if "rdf:resource" in node.attributes.keys():
- bodyurl = node.attributes["rdf:resource"].value
- return bodyurl
-
-
- #check for the uuid and pick the right one
- def get_modified_from_server_response(self, rdf):
- modified = ''
- dom = minidom.parseString(rdf)
- modified = self.get_node_data(dom.getElementsByTagName("dcterms:modified")[0])
- if modified != None:
- modified = self.makeTimeStampFromDateTimeString(modified)
- return modified
+ return remote_annotations
@@ -626,19 +586,22 @@ class AnnotationManager:
def insert_annotation_db_record(self, annotation):
- t = (None, annotation.get_filehash(), annotation.get_page(), annotation.get_note_title(), annotation.get_note_body(), annotation.get_bodyurl(), annotation.get_context(), annotation.get_created(), annotation.get_modified(), annotation.get_creator(), annotation.get_annotates(), annotation.get_rdf(), annotation.get_color(), annotation.is_local(), annotation.get_mimetype(), annotation.get_uuid(), annotation.get_annotationurl(), annotation.get_bodysvg())
- self._conn.execute('insert into annotations values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', t)
+ #(id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, texttitle, textcreator, created TIMESTAMP, modified TIMESTAMP, creator, annotates, color, local, mimetype, uuid, annotationurl)
+ t = (None, annotation.get_filehash(), annotation.get_page(), annotation.get_note_title(), annotation.get_note_body(), annotation.get_bodyurl(), annotation.get_texttitle(), annotation.get_textcreator(), annotation.get_created(), annotation.get_modified(), annotation.get_creator(), annotation.get_annotates(), annotation.get_color().to_string(), annotation.is_local(), annotation.get_mimetype(), annotation.get_uuid(), annotation.get_annotationurl())
+ self._conn.execute('insert into annotations values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)', t)
self._conn.commit()
self.current_annotation = annotation
def update_annotation_db_record(self, annotation):
- t = (annotation.get_filehash(), annotation.get_page(), annotation.get_note_title(), annotation.get_note_body(), annotation.get_bodyurl(), annotation.get_context(), annotation.get_created(), annotation.get_modified(), annotation.get_creator(), annotation.get_annotates(), annotation.get_rdf(), annotation.get_color(), annotation.is_local(), annotation.get_mimetype(), annotation.get_uuid(), annotation.get_annotationurl(), annotation.get_bodysvg(), annotation.get_id())
- self._conn.execute('update annotations set md5=?, page=?, title=?, content=?, bodyurl=?, context=?, created=?, modified=?, creator=?, annotates=?, rdf=?, color=?, local=?, mimetype=?, uuid=?, annotationurl=?, bodysvg=? where id=?', t)
+ #(id INTEGER PRIMARY KEY, md5, page, title, content, bodyurl, texttitle, textcreator, created TIMESTAMP, modified TIMESTAMP, creator, annotates, color, local, mimetype, uuid, annotationurl)
+ t = (annotation.get_filehash(), annotation.get_page(), annotation.get_note_title(), annotation.get_note_body(), annotation.get_bodyurl(), annotation.get_texttitle(), annotation.get_textcreator(), annotation.get_created(), annotation.get_modified(), annotation.get_creator(), annotation.get_annotates(), annotation.get_color().to_string(), annotation.is_local(), annotation.get_mimetype(), annotation.get_uuid(), annotation.get_annotationurl(), annotation.get_id())
+ self._conn.execute('update annotations set md5=?, page=?, title=?, content=?, bodyurl=?, texttitle=?, textcreator=?, created=?, modified=?, creator=?, annotates=?, color=?, local=?, mimetype=?, uuid=?, annotationurl=? where id=?', t)
self._conn.commit()
self.current_annotation = annotation
+
def makeDateTimeFromTimeStamp(self, tstamp):
return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime(tstamp))
@@ -647,99 +610,10 @@ class AnnotationManager:
return time.mktime(time.strptime(datetimestr, "%Y-%m-%d %H:%M:%S"))
- def create_rdf_representation(self, annotation):
- rstr = "<rdf:RDF xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" "
- rstr += "xmlns:rdfs=\"http://www.w3.org/2001/01/rdf-schema#\" "
- rstr += "xmlns:cnt=\"http://www.w3.org/2008/content\" "
- rstr += "xmlns:dc=\"http://purl.org/dc/elements/1.1/\" "
- rstr += "xmlns:dcterms=\"http://purl.org/dc/terms/\" "
- rstr += "xmlns:foaf=\"http://xmlns.com/foaf/0.1/\" "
- rstr += "xmlns:oac=\"http://www.openannotation.org/ns/\" "
- rstr += "xmlns:ore=\"http://www.openarchives.org/ore/terms/\" "
- rstr += "xmlns:bibo=\"http://purl.org/ontology/bibo/\">"
-
- rstr += "<oac:Annotation rdf:about=\"" + annotation.get_uuid() + "\">"
- rstr += "<bibo:pageStart>" + str(annotation.get_page()) + "</bibo:pageStart>"
- rstr += "<dc:title>" + annotation.get_note_title() + "</dc:title>"
- rstr += "<oac:hasTarget rdf:resource=\"" + annotation.get_annotates() + "\" dcterms:isPartOf=\"" + annotation.get_annotates() + "\"/>"
- rstr += "<oac:hasTargetContext>"
- rstr += "<oac:TargetContext>"
- rstr += "<oac:contextAbout rdf:resource=\"" + annotation.get_annotates() + "\"/>"
- rstr += "<dc:type>" + annotation.get_mimetype() + "</dc:type>"
- rstr += "</oac:TargetContext>"
- rstr += "</oac:hasTargetContext>"
-
- rstr += "<oac:hasContentContext>"
- rstr += "<oac:Context rdf:resource=\"" + annotation.get_context() + "\">"
- rstr += "<oac:contextAbout>"
- rstr += annotation.get_annotates()
- rstr += "</oac:contextAbout>"
- rstr += '<oac:when>%s</oac:when>' % self.makeDateTimeFromTimeStamp(annotation.get_created())
- rstr += "</oac:Context>"
- rstr += "</oac:hasContentContext>"
-
- rstr += "<rdf:type rdf:resource=\"http://www.w3.org/2000/10/annotation-ns#Annotation\"/>"
- rstr += "<oac:predicate rdf:resource=\"http://www.openannotation.org/ns/annotates\"/>"
- rstr += "<dcterms:creator>" + annotation.get_creator() + "</dcterms:creator>"
-
- if (annotation.get_annotationurl() != None) and (len(annotation.get_annotationurl()) > 0) and (annotation.get_bodyurl() != None) and (len(annotation.get_bodyurl()) > 0):
- rstr += "<oac:hasContent rdf:about=\"" + annotation.get_uuid() + "\">"
- rstr += "<rdf:type rdf:resource=\"" + annotation.get_annotationurl() + "\"/>"
- rstr += "<dc:title>" + annotation.get_note_title() + "</dc:title>"
- rstr += "<oac:hasBody>"
- rstr += "<rdf:type rdf:resource=\"http://www.openannotation.org/ns/Note\"/>"
- rstr += "<oac:body>"
- rstr += annotation.get_bodyurl()
- rstr += "</oac:body>"
- rstr += "</oac:hasBody>"
- rstr += '<dcterms:modified> %s </dcterms:modified>' % self.makeDateTimeFromTimeStamp(annotation.get_modified())
- rstr += "</oac:hasContent>"
-
- rstr += "</oac:Annotation>"
- rstr += "<rdf:Description rdf:about=\"" + annotation.get_annotates() + "\">"
- rstr += "<ore:Proxy>"
- rstr += "<ore:proxyIn rdf:nodeID=\"anno\" />"
- rstr += "<ore:proxyFor rdf:resource=\"" + annotation.get_annotates() + "\">"
- rstr += "<oac:hasSegmentDescription>"
- rstr += "<oac:SegmentDescription dc:format=\"image/svg+xml\">"
- rstr += "<cnt:ContentAsText>"
- rstr += "<cnt:chars>"
- rstr += annotation.get_bodysvg()
- rstr += "</cnt:chars>"
- rstr += "<cnt:characterEncoding>utf-8</cnt:characterEncoding>"
- rstr += "</cnt:ContentAsText>"
- rstr += "</oac:SegmentDescription>"
- rstr += "</oac:hasSegmentDescription>"
- rstr += "</ore:proxyFor>"
- rstr += "</ore:Proxy>"
- rstr += "</rdf:Description>"
- rstr += "</rdf:RDF>"
- return rstr
-
-
-
- def assembleSVG(self, annotitle, annocontent):
- #svg = "<?xml version=\"1.0\"?>"
- svg = "<svg xmlns:svg=\"http://www.w3.org/2000/svg\" xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" width=\"500\" height=\"500\" id=\"svg2\">"
- svg += "<g id=\"layer1\">"
- svg += "<rect width=\"500\" height=\"500\" x=\"0\" y=\"0\" id=\"rect2816\" style=\"fill:none;stroke:#000000;stroke-opacity:1\"/>"
- svg += "<text x=\"2\" y=\"2\" id=\"text2818\" xml:space=\"preserve\" style=\"font-size:16px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans\">"
- svg += "<tspan x=\"10\" y=\"50\" id=\"notetitle\">" + annotitle + "</tspan>"
- svg += "</text>"
-
- svg += "<text x=\"2\" y=\"150\" id=\"text2830\" xml:space=\"preserve\" style=\"font-size:12px;font-style:normal;font-weight:normal;fill:#000000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans\">"
- svg += "<tspan x=\"10\" y=\"150\" id=\"notecontent\">" + annocontent + "</tspan>"
- svg += "</text>"
- svg += "</g>"
- svg += "</svg>"
- return svg
-
-
-
def download_annotations(self):
url = self._annotationserver
annotations = []
- annordf = ""
+ annojson = ""
_logger.debug("contacting annotationserver %s", url)
if self._annotates == "":
self._annotates = "_"
@@ -749,12 +623,12 @@ class AnnotationManager:
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
- annordf = response.read()
+ annojson = response.read()
except Exception, detail:
_logger.debug("readdb: failure at initial sync request f. annotations; detail: %s ", detail)
- if (annordf != None) and (len(annordf) > 0):
- anno_arr = self.parse_annotations(annordf)
+ if (annojson != None) and (len(annojson) > 0):
+ anno_arr = self.parse_annotations(annojson)
_logger.debug('length anno_arr %d', len(anno_arr))
remote_uuids = []
if len(anno_arr) > 0:
@@ -762,6 +636,8 @@ class AnnotationManager:
_logger.debug('remote_uuids %s', remote_uuids)
#check the modified timestamps
localuuids = [la.get_uuid() for la in self._annotations]
+ self.remotecreators = []
+ self.remotecolors = {}
for a in anno_arr:
uuid = a.get_uuid()
if uuid in localuuids:
@@ -782,20 +658,31 @@ class AnnotationManager:
_logger.debug(str('remote annotation is outdated, sending %s' % self._annotations[ind]))
self.send_annotation_to_server(self._annotations[ind])
else:
+ remotecreator = a.get_creator()
+ if not remotecreator in self.remotecreators:
+ self.remotecreators.append(remotecreator)
+ self.remotecolors[remotecreator] = XoColor()
+ a.color = self.remotecolors[remotecreator]
self._annotations.append(a)
self.insert_annotation_db_record(a)
self._sidebar.update_for_page(a.page)
+ def get_user_string( self, user ):
+ m = hashlib.md5()
+ #m.update( str( "%s%d" % ( user, random.randint( 0, 100000000 ) ) ) )
+ m.update( str( "%s" % ( user ) ) )
+ return m.hexdigest()
+
+
def sync_annotations(self):
url = self._annotationserver
annotations = []
- annordf = ""
_logger.debug("contacting annotationserver %s", url)
if self._annotates == "":
- self._annotates = "_"
+ self._annotates = self._texttitle
#check if there are annotations to be deleted:
if len(self._to_delete) > 0:
for delete_anid in self._to_delete:
@@ -804,18 +691,17 @@ class AnnotationManager:
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
- annordf = response.read()
+ annojson = response.read()
self._to_delete.remove(delete_anid)
- _logger.debug("\nafter delete, rdf is: %s\n\n" % annordf)
+ _logger.debug("\nafter delete, json is: %s\n\n" % annojson)
except Exception, detail:
_logger.debug("readdb: failure at request f. deleting annotations; detail: %s ", detail)
else:
#get annotations from server
client = gconf.client_get_default()
user = client.get_string("/desktop/sugar/user/nick")
-
if self._userid == '':
- self._userid = self.get_userid_for_username(user)
+ self._userid = self.get_userid_for_username( self.get_user_string( user ) )
self._creator = self._userid
values = {'w3c_hasTarget' : self._annotates, 'creator' : self._creator }
@@ -824,11 +710,13 @@ class AnnotationManager:
data = urllib.urlencode(values)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req)
- annordf = response.read()
+ annojson = response.read()
+ _logger.debug('downloaded annotations -- annojson is: %s ' % annojson)
+
except Exception, detail:
_logger.debug("readdb: failure at initial sync request f. annotations; detail: %s ", detail)
- if (annordf != None) and (len(annordf) > 0):
- anno_arr = self.parse_annotations(annordf)
+ if (annojson != None) and (len(annojson) > 0):
+ anno_arr = self.parse_annotations(annojson)
_logger.debug('length anno_arr %d', len(anno_arr))
remote_uuids = []
if len(anno_arr) > 0:
@@ -865,23 +753,27 @@ class AnnotationManager:
def send_annotation_to_server(self, annotation):
url = self._annotationserver
- annordf = self.create_rdf_representation(annotation)
- _logger.debug('it is a new or updated annotation, trying to send it to server, rdf: %s', annotation)
+ annojson = annotation.get_json()
+ _logger.debug('it is a new or updated annotation, trying to send it to server, json: %s', annojson)
try:
- req = urllib2.Request(url, annordf)
- req.add_header('Content-Type', 'text/xml')
+ req = urllib2.Request(url, annojson, {'Content-Type': 'application/json', "Accept": "application/json"} )
+ #req = urllib2.Request(url, annojson, {"Content-type": "application/x-www-form-urlencoded"y} )
response = urllib2.urlopen(req)
- re_rdf = response.read()
- _logger.debug('rdf response from the server: %s', re_rdf)
- annourl = self.get_annotationurl_from_server_response(re_rdf)
- _logger.debug('got this annourl back from server: %s', annourl)
+ re_json = response.read()
+ json_arr = simplejson.loads(re_json)
+ _logger.debug('json response from the server: %s', re_json )
+
+ annourl = json_arr['annotationurl']
+ _logger.debug('got this annourl back from server: %s', annourl )
if annourl != None:
- annotation.set_annotationurl(annourl)
- bodyurl = self.get_bodyurl_from_server_response(re_rdf)
+ annotation.set_annotationurl( annourl )
+
+ bodyurl = json_arr['bodyurl']
_logger.debug('got this bodyurl back from server: %s', bodyurl)
if bodyurl != None:
annotation.set_bodyurl(bodyurl)
- self.update_annotation_db_record(annotation)
+
+ self.update_annotation_db_record( annotation )
except Exception, detail:
_logger.debug("readdb: sending annotation failed: %s ", detail)
diff --git a/readsidebar.py b/readsidebar.py
index 40c8922..abb1740 100644
--- a/readsidebar.py
+++ b/readsidebar.py
@@ -20,14 +20,17 @@ import time
import gtk
+from sugar.graphics import style
from sugar.graphics.icon import Icon
from sugar.graphics.xocolor import XoColor
from sugar import profile
from sugar.util import timestamp_to_elapsed_string
from annobookmark import AnnoBookmark, Bookmark
-from readdb import BookmarkManager, AnnotationManager
+from readdb import AnnotationManager
from readdialog import BookmarkAddDialog, BookmarkEditDialog, AnnotationAddDialog, AnnotationEditDialog
+from annoicon import AnnoIcon
+
from gettext import gettext as _
@@ -38,7 +41,7 @@ _logger = logging.getLogger('anno-activity')
class Sidebar(gtk.EventBox):
def __init__(self):
gtk.EventBox.__init__(self)
- self.set_size_request(20, -1)
+ self.set_size_request(22, -1)
# Take care of the background first
white = gtk.gdk.color_parse("white")
self.modify_bg(gtk.STATE_NORMAL, white)
@@ -57,72 +60,15 @@ class Sidebar(gtk.EventBox):
self._annotation_icon_query_tooltip_cb_ids = []
self._bookmark_manager = None
self._annotation_manager = None
- self._is_showing_local_bookmark = False
self._is_showing_local_annotation = False
- self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
-
-
-
-
- def _add_bookmark_icon(self, bookmark):
- xocolor = XoColor(bookmark.color)
- self._bookmark_icon = Icon(icon_name = 'emblem-favorite', \
- pixel_size = 18, xo_color = xocolor)
-
- self._bookmark_icon.props.has_tooltip = True
- self.__bookmark_icon_query_tooltip_cb_id = \
- self._bookmark_icon.connect('query_tooltip', self.__bookmark_icon_query_tooltip_cb, \
- bookmark)
-
- self.__event_cb_id = \
- self.connect('event', self.__event_cb, bookmark)
-
- self._box.pack_start(self._bookmark_icon ,expand=False,fill=False)
- self._bookmark_icon.show_all()
-
- if bookmark.is_local():
- self._is_showing_local_bookmark = True
-
- def __bookmark_icon_query_tooltip_cb(self, widget, x, y, keyboard_mode, tip, bookmark):
- tooltip_header = bookmark.get_note_title()
- tooltip_body = bookmark.get_note_body()
- #TRANS: This goes like Bookmark added by User 5 days ago (the elapsed string gets translated
- #TRANS: automatically)
- tooltip_footer = (_('Bookmark added by %(user)s %(time)s') \
- % {'user': bookmark.nick, 'time': timestamp_to_elapsed_string(bookmark.timestamp)})
-
- vbox = gtk.VBox()
-
- l = gtk.Label('<big>%s</big>' % tooltip_header)
- l.set_use_markup(True)
- l.set_width_chars(40)
- l.set_line_wrap(True)
- vbox.pack_start(l, expand = False, fill = False)
- l.show()
-
- l = gtk.Label('%s' % tooltip_body)
- l.set_use_markup(True)
- l.set_alignment(0, 0)
- l.set_padding(2, 6)
- l.set_width_chars(40)
- l.set_line_wrap(True)
- l.set_justify(gtk.JUSTIFY_FILL)
- vbox.pack_start(l, expand = True, fill = True)
- l.show()
-
- l = gtk.Label('<small><i>%s</i></small>' % tooltip_footer)
- l.set_use_markup(True)
- l.set_width_chars(40)
- l.set_line_wrap(True)
- vbox.pack_start(l, expand = False, fill = False)
- l.show()
+ #self.add_events(gtk.gdk.BUTTON_PRESS_MASK)
- tip.set_custom(vbox)
- return True
-
+ def is_showing_local_annotation(self):
+ return self._is_showing_local_annotation
+
@@ -145,42 +91,16 @@ class Sidebar(gtk.EventBox):
return False
-
-
- def _clear_bookmarks(self):
- if self._bookmark_icon is not None:
- self._bookmark_icon.disconnect(self.__bookmark_icon_query_tooltip_cb_id)
- self.disconnect(self.__event_cb_id)
-
- self._bookmark_icon.hide() #XXX: Is this needed??
- self._bookmark_icon.destroy()
-
- self._bookmark_icon = None
-
- 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 _clear_annotations(self):
if len(self._annotation_icons) > 0:
for i in range(len(self._annotation_icons)):
annotation_icon = self._annotation_icons[i]
- annotation_icon.disconnect(self._annotation_icon_query_tooltip_cb_ids[i])
+ annotation_icon._disconnect()
annotation_icon.hide()
annotation_icon.destroy()
- _logger.debug('disconnecting icon: %d' % self._annotation_event_ids[i] )
- self.disconnect(self._annotation_event_ids[i])
self._annotation_icons[i] = None
- #self._annotation_icon = None
self._annotation_icons = []
self._annotation_icon_query_tooltip_cb_ids = []
self._annotation_event_ids = []
@@ -188,8 +108,8 @@ class Sidebar(gtk.EventBox):
- def set_annotationmanager(self, filehash, mimetype):
- self._annotation_manager = AnnotationManager(filehash, mimetype, self)
+ def set_annotationmanager(self, annotation_manager):
+ self._annotation_manager = annotation_manager
def get_annotationmanager(self):
@@ -203,31 +123,6 @@ class Sidebar(gtk.EventBox):
self._add_annotation_icon(annotation)
- def add_bookmark(self, page):
- bookmark_title = (_("%s's bookmark") % profile.get_nick_name())
- bookmark_content = (_("Bookmark for page %d") % page)
- dialog = BookmarkAddDialog(parent_xid = self.get_toplevel().window.xid, \
- dialog_title = _("Add notes for bookmark: "), \
- bookmark_title = bookmark_title, \
- bookmark_content = bookmark_content, page = page, \
- sidebarinstance = self)
- dialog.show_all()
-
-
- def _real_add_bookmark(self, page, content):
- self._bookmark_manager.add_bookmark(page, unicode(content))
- 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
-
-
def sync_annotations(self):
self._annotation_manager.sync_annotations()
@@ -263,22 +158,19 @@ class Sidebar(gtk.EventBox):
def del_annotation(self, page, annotation_id):
+ _logger.debug('annotation %d scheduled for deletion' % annotation_id)
self._annotation_manager.del_annotation(annotation_id)
self.update_for_page(page)
def _add_annotation_icon(self, annotation):
- xocolor = XoColor(annotation.color)
- annotation_icon = Icon(icon_name = 'emblem-favorite', \
- pixel_size = 18, xo_color = xocolor)
-
- annotation_icon.props.has_tooltip = True
- self._annotation_icon_query_tooltip_cb_ids.append(annotation_icon.connect('query_tooltip', self.__annotation_icon_query_tooltip_cb, annotation))
-
- self._annotation_event_ids.append(self.connect('event', self.__event_cb, annotation))
+ #xocolor = XoColor(annotation.color)
+ xocolor = annotation.color
+ annotation_icon = AnnoIcon(icon_name = 'emblem-favorite', \
+ xo_color = xocolor, annotation=annotation, \
+ event_cb=self.__event_cb)
- _logger.debug('connecting event %d' % self._annotation_event_ids[-1])
self._box.pack_start(annotation_icon ,expand=False,fill=False)
annotation_icon.show_all()
self._annotation_icons.append(annotation_icon)
@@ -286,8 +178,7 @@ class Sidebar(gtk.EventBox):
if annotation.is_local():
self._is_showing_local_annotation = True
-
-
+ """
def __annotation_icon_query_tooltip_cb(self, widget, x, y, keyboard_mode, tip, annotation):
tooltip_header = annotation.get_note_title()
tooltip_body = annotation.get_note_body()
@@ -325,5 +216,5 @@ class Sidebar(gtk.EventBox):
tip.set_custom(vbox)
return True
-
+ """
diff --git a/readtoolbar.py b/readtoolbar.py
index 53a756f..043ac05 100644
--- a/readtoolbar.py
+++ b/readtoolbar.py
@@ -15,30 +15,33 @@
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
from gettext import gettext as _
+import logging
import gobject
import gtk
-import evince
-
-try:
- import epubadapter
-except:
- pass
+import os
+import simplejson
+from sugar.graphics.combobox import ComboBox
from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.toggletoolbutton import ToggleToolButton
+from sugar.graphics.toolcombobox import ToolComboBox
from sugar.graphics.menuitem import MenuItem
from sugar.graphics import iconentry
from sugar.activity import activity
+import speech
+
+
class EditToolbar(activity.EditToolbar):
+
__gtype_name__ = 'EditToolbar'
def __init__(self):
activity.EditToolbar.__init__(self)
- self._evince_view = None
+ self._view = None
- self._document = None
self._find_job = None
search_item = gtk.ToolItem()
@@ -75,11 +78,8 @@ class EditToolbar(activity.EditToolbar):
self._next.show()
def set_view(self, view):
- self._evince_view = view
- self._evince_view.find_set_highlight_search(True)
-
- def set_document(self, document):
- self._document = document
+ self._view = view
+ self._view.find_set_highlight_search(True)
def _clear_find_job(self):
if self._find_job is None:
@@ -93,13 +93,8 @@ class EditToolbar(activity.EditToolbar):
self._clear_find_job()
text = self._search_entry.props.text
if text != "":
- try:
- self._find_job = evince.JobFind(document=self._document, start_page=0, n_pages=self._document.get_n_pages(), text=text, case_sensitive=False)
- self._find_updated_handler = self._find_job.connect('updated', self._find_updated_cb)
- evince.job_scheduler_push_job(self._find_job, evince.JOB_PRIORITY_NONE)
- except TypeError:
- self._find_job = epubadapter.JobFind(document=self._document, start_page=0, n_pages=self._document.get_n_pages(), text=text, case_sensitive=False)
- self._find_updated_handler = self._find_job.connect('updated', self._find_updated_cb)
+ self._find_job, self._find_updated_handler = \
+ self._view.setup_find_job(text, self._find_updated_cb)
else:
# FIXME: highlight nothing
pass
@@ -108,14 +103,14 @@ class EditToolbar(activity.EditToolbar):
self._update_find_buttons()
def _search_find_next(self):
- self._evince_view.find_next()
+ self._view.find_next()
def _search_find_last(self):
# FIXME: does Evince support find last?
return
def _search_find_prev(self):
- self._evince_view.find_previous()
+ self._view.find_previous()
def _search_entry_activate_cb(self, entry):
if self._search_entry_changed:
@@ -124,24 +119,29 @@ class EditToolbar(activity.EditToolbar):
self._search_find_next()
def _search_entry_changed_cb(self, entry):
+ logging.debug('Search entry: %s' % (entry.props.text))
self._search_entry_changed = True
self._update_find_buttons()
-# Automatically start search, maybe after timeout?
-# self._search_find_first()
+ # gobject.timeout_add(500, self._search_entry_timeout_cb)
+ #
+ #def _search_entry_timeout_cb(self):
+ # self._clear_find_job()
+ # self._search_find_first()
+ # return False
def _find_changed_cb(self, page, spec):
self._update_find_buttons()
- def _find_updated_cb(self, job, page = None):
- self._evince_view.find_changed(job, page)
+ def _find_updated_cb(self, job, page=None):
+ self._view.find_changed(job, page)
def _find_prev_cb(self, button):
if self._search_entry_changed:
self._search_find_last()
else:
self._search_find_prev()
-
+
def _find_next_cb(self, button):
if self._search_entry_changed:
self._search_find_first()
@@ -169,20 +169,14 @@ class ViewToolbar(gtk.Toolbar):
__gtype_name__ = 'ViewToolbar'
__gsignals__ = {
- 'needs-update-size': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([])),
- 'go-fullscreen': (gobject.SIGNAL_RUN_FIRST,
- gobject.TYPE_NONE,
- ([]))
+ 'go-fullscreen': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])),
}
def __init__(self):
gtk.Toolbar.__init__(self)
- self._evince_view = None
- self._document = None
-
+ self._view = None
+
self._zoom_out = ToolButton('zoom-out')
self._zoom_out.set_tooltip(_('Zoom out'))
self._zoom_out.connect('clicked', self._zoom_out_cb)
@@ -194,7 +188,7 @@ class ViewToolbar(gtk.Toolbar):
self._zoom_in.connect('clicked', self._zoom_in_cb)
self.insert(self._zoom_in, -1)
self._zoom_in.show()
-
+
self._zoom_to_width = ToolButton('zoom-best-fit')
self._zoom_to_width.set_tooltip(_('Zoom to width'))
self._zoom_to_width.connect('clicked', self._zoom_to_width_cb)
@@ -245,76 +239,181 @@ class ViewToolbar(gtk.Toolbar):
self._view_notify_zoom_handler = None
def set_view(self, view):
- self._evince_view = view
- self._zoom_spin.props.value = self._evince_view.props.zoom * 100
- self._view_notify_zoom_handler = self._evince_view.connect(
- 'notify::zoom', self._view_notify_zoom_cb)
- self._update_zoom_buttons()
+ self._view = view
+
+ self._zoom_spin.props.value = self._view.get_zoom()
+ self._view_notify_zoom_handler = \
+ self._view.connect_zoom_handler(self._view_notify_zoom_cb)
+ self._update_zoom_buttons()
def _zoom_spin_notify_value_cb(self, zoom_spin, pspec):
- if not self._view_notify_zoom_handler:
- return
- self._evince_view.disconnect(self._view_notify_zoom_handler)
- try:
- if hasattr(self._evince_view.props, 'sizing_mode'):
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
- self._evince_view.props.zoom = zoom_spin.props.value / 100.0
- finally:
- self._view_notify_zoom_handler = self._evince_view.connect(
- 'notify::zoom', self._view_notify_zoom_cb)
+ self._view.set_zoom(zoom_spin.props.value)
- def _view_notify_zoom_cb(self, evince_view, pspec):
+ def _view_notify_zoom_cb(self, model, pspec):
self._zoom_spin.disconnect(self._zoom_spin_notify_value_handler)
try:
- self._zoom_spin.props.value = round(evince_view.props.zoom * 100.0)
+ self._zoom_spin.props.value = round(self._view.get_zoom())
finally:
self._zoom_spin_notify_value_handler = self._zoom_spin.connect(
'notify::value', self._zoom_spin_notify_value_cb)
def zoom_in(self):
- if hasattr(self._evince_view.props, 'sizing_mode'):
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
- self._evince_view.zoom_in()
+ self._view.zoom_in()
self._update_zoom_buttons()
def _zoom_in_cb(self, button):
self.zoom_in()
def zoom_out(self):
- if hasattr(self._evince_view.props, 'sizing_mode'):
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
- self._evince_view.zoom_out()
+ self._view.zoom_out()
self._update_zoom_buttons()
-
+
def _zoom_out_cb(self, button):
self.zoom_out()
def zoom_to_width(self):
- if hasattr(self._evince_view.props, 'sizing_mode'):
- self._evince_view.props.sizing_mode = evince.SIZING_FIT_WIDTH
- self.emit('needs-update-size')
+ self._view.zoom_to_width()
self._update_zoom_buttons()
def _zoom_to_width_cb(self, button):
self.zoom_to_width()
def _update_zoom_buttons(self):
- self._zoom_in.props.sensitive = self._evince_view.can_zoom_in()
- self._zoom_out.props.sensitive = self._evince_view.can_zoom_out()
+ self._zoom_in.props.sensitive = self._view.can_zoom_in()
+ self._zoom_out.props.sensitive = self._view.can_zoom_out()
+ self._zoom_to_width.props.sensitive = self._view.can_zoom_to_width()
def _zoom_to_fit_menu_item_activate_cb(self, menu_item):
- if hasattr(self._evince_view.props, 'sizing_mode'): #XXX
- self._evince_view.props.sizing_mode = evince.SIZING_BEST_FIT
- self.emit('needs-update-size')
+ self._view.zoom_to_best_fit()
self._update_zoom_buttons()
def _actual_size_menu_item_activate_cb(self, menu_item):
- if hasattr(self._evince_view.props, 'sizing_mode'):
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
- self._evince_view.props.zoom = 1.0
+ self._view.zoom_to_actual_size()
self._update_zoom_buttons()
def _fullscreen_cb(self, button):
self.emit('go-fullscreen')
+
+
+class SpeechToolbar(gtk.Toolbar):
+
+ def __init__(self, activity):
+ gtk.Toolbar.__init__(self)
+ voicebar = gtk.Toolbar()
+ self._activity = activity
+ if not speech.supported:
+ return
+
+ self.load_speech_parameters()
+
+ self.sorted_voices = [i for i in speech.voices()]
+ self.sorted_voices.sort(self.compare_voices)
+ default = 0
+ for voice in self.sorted_voices:
+ if voice[0] == speech.voice[0]:
+ break
+ default = default + 1
+
+ # Play button
+ self.play_btn = ToggleToolButton('media-playback-start')
+ self.play_btn.show()
+ self.play_btn.connect('toggled', self.play_cb)
+ self.insert(self.play_btn, -1)
+ self.play_btn.set_tooltip(_('Play / Pause'))
+
+ self.voice_combo = ComboBox()
+ for voice in self.sorted_voices:
+ self.voice_combo.append_item(voice, voice[0])
+ self.voice_combo.set_active(default)
+
+ self.voice_combo.connect('changed', self.voice_changed_cb)
+ combotool = ToolComboBox(self.voice_combo)
+ self.insert(combotool, -1)
+ combotool.show()
+
+ self.pitchadj = gtk.Adjustment(0, -100, 100, 1, 10, 0)
+ pitchbar = gtk.HScale(self.pitchadj)
+ pitchbar.set_draw_value(False)
+ pitchbar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ pitchbar.set_size_request(150, 15)
+ self.pitchadj.set_value(speech.pitch)
+ pitchtool = gtk.ToolItem()
+ pitchtool.add(pitchbar)
+ pitchtool.show()
+ self.insert(pitchtool, -1)
+ pitchbar.show()
+
+ self.rateadj = gtk.Adjustment(0, -100, 100, 1, 10, 0)
+ ratebar = gtk.HScale(self.rateadj)
+ ratebar.set_draw_value(False)
+ ratebar.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ ratebar.set_size_request(150, 15)
+ self.rateadj.set_value(speech.rate)
+ ratetool = gtk.ToolItem()
+ ratetool.add(ratebar)
+ ratetool.show()
+ self.insert(ratetool, -1)
+ ratebar.show()
+ self.pitchadj.connect("value_changed", self.pitch_adjusted_cb)
+ self.rateadj.connect("value_changed", self.rate_adjusted_cb)
+
+ def compare_voices(self, a, b):
+ if a[0].lower() == b[0].lower():
+ return 0
+ if a[0] .lower() < b[0].lower():
+ return -1
+ if a[0] .lower() > b[0].lower():
+ return 1
+
+ def voice_changed_cb(self, combo):
+ speech.voice = combo.props.value
+ speech.say(speech.voice[0])
+ self.save_speech_parameters()
+
+ def pitch_adjusted_cb(self, get):
+ speech.pitch = int(get.value)
+ speech.say(_("pitch adjusted"))
+ self.save_speech_parameters()
+
+ def rate_adjusted_cb(self, get):
+ speech.rate = int(get.value)
+ speech.say(_("rate adjusted"))
+ self.save_speech_parameters()
+
+ def load_speech_parameters(self):
+ speech_parameters = {}
+ data_path = os.path.join(self._activity.get_activity_root(), 'data')
+ data_file_name = os.path.join(data_path, 'speech_params.json')
+ if os.path.exists(data_file_name):
+ f = open(data_file_name, 'r')
+ try:
+ speech_parameters = simplejson.load(f)
+ speech.pitch = speech_parameters['pitch']
+ speech.rate = speech_parameters['rate']
+ speech.voice = speech_parameters['voice']
+ finally:
+ f.close()
+
+ def save_speech_parameters(self):
+ speech_parameters = {}
+ speech_parameters['pitch'] = speech.pitch
+ speech_parameters['rate'] = speech.rate
+ speech_parameters['voice'] = speech.voice
+ data_path = os.path.join(self._activity.get_activity_root(), 'data')
+ data_file_name = os.path.join(data_path, 'speech_params.json')
+ f = open(data_file_name, 'w')
+ try:
+ simplejson.dump(speech_parameters, f)
+ finally:
+ f.close()
+
+ def play_cb(self, widget):
+ if widget.get_active():
+ self.play_btn.set_named_icon('media-playback-pause')
+ if speech.is_stopped():
+ speech.play(self._activity._view.get_marked_words())
+ else:
+ self.play_btn.set_named_icon('media-playback-start')
+ speech.stop()
diff --git a/readtopbar.py b/readtopbar.py
index 3f338f5..93b15b9 100644
--- a/readtopbar.py
+++ b/readtopbar.py
@@ -16,137 +16,64 @@
# 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, gobject
+import gtk
+import gobject
import dbus
+import logging
from sugar.graphics import style
from sugar.graphics.icon import Icon, get_icon_state
from gettext import gettext as _
-_LEVEL_PROP = 'battery.charge_level.percentage'
-_CHARGING_PROP = 'battery.rechargeable.is_charging'
-_DISCHARGING_PROP = 'battery.rechargeable.is_discharging'
-_PRESENT_PROP = 'battery.present'
_ICON_NAME = 'battery'
-# Taken from sugar/extensions/deviceicon/battery.py
-class BattMan(gobject.GObject):
- __gproperties__ = {
- 'level' : (int, None, None, 0, 100, 0,
- gobject.PARAM_READABLE),
- 'charging' : (bool, None, None, False,
- gobject.PARAM_READABLE),
- 'discharging' : (bool, None, None, False,
- gobject.PARAM_READABLE),
- 'present' : (bool, None, None, False,
- gobject.PARAM_READABLE)
- }
-
- def __init__(self, udi):
- gobject.GObject.__init__(self)
-
- bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
- proxy = bus.get_object('org.freedesktop.Hal', udi,
- follow_name_owner_changes=True)
- self._battery = dbus.Interface(proxy, 'org.freedesktop.Hal.Device')
- bus.add_signal_receiver(self._battery_changed,
- 'PropertyModified',
- 'org.freedesktop.Hal.Device',
- 'org.freedesktop.Hal',
- udi)
-
- self._level = self._get_level()
- self._charging = self._get_charging()
- self._discharging = self._get_discharging()
- self._present = self._get_present()
-
- def _get_level(self):
- try:
- return self._battery.GetProperty(_LEVEL_PROP)
- except dbus.DBusException:
- logging.error('Cannot access %s' % _LEVEL_PROP)
- return 0
-
- def _get_charging(self):
- try:
- return self._battery.GetProperty(_CHARGING_PROP)
- except dbus.DBusException:
- logging.error('Cannot access %s' % _CHARGING_PROP)
- return False
-
- def _get_discharging(self):
- try:
- return self._battery.GetProperty(_DISCHARGING_PROP)
- except dbus.DBusException:
- logging.error('Cannot access %s' % _DISCHARGING_PROP)
- return False
-
- def _get_present(self):
- try:
- return self._battery.GetProperty(_PRESENT_PROP)
- except dbus.DBusException:
- logging.error('Cannot access %s' % _PRESENT_PROP)
- return False
-
- def do_get_property(self, pspec):
- if pspec.name == 'level':
- return self._level
- if pspec.name == 'charging':
- return self._charging
- if pspec.name == 'discharging':
- return self._discharging
- if pspec.name == 'present':
- return self._present
-
- def get_type(self):
- return 'battery'
-
- def _battery_changed(self, num_changes, changes_list):
- for change in changes_list:
- if change[0] == _LEVEL_PROP:
- self._level = self._get_level()
- self.notify('level')
- elif change[0] == _CHARGING_PROP:
- self._charging = self._get_charging()
- self.notify('charging')
- elif change[0] == _DISCHARGING_PROP:
- self._discharging = self._get_discharging()
- self.notify('discharging')
- elif change[0] == _PRESENT_PROP:
- self._present = self._get_present()
- self.notify('present')
+_UP_DEVICE_IFACE = 'org.freedesktop.UPower.Device'
+_UP_TYPE_BATTERY = 2
class _TopBar(gtk.HBox):
__gproperties__ = {
- 'completion-level' : (float, None, None, 0.0, 100.0, 0.0,
- gobject.PARAM_READWRITE),
+ 'completion-level': (float, None, None, 0.0, 100.0, 0.0,
+ gobject.PARAM_READWRITE),
}
def __init__(self):
gtk.HBox.__init__(self)
- self.set_border_width(int(style.DEFAULT_SPACING/2.0))
+ self.set_border_width(int(style.DEFAULT_SPACING / 2.0))
self.set_spacing(style.DEFAULT_SPACING * 4)
self._completion_level = 0
self._progressbar = None
+ self._icon = None
+ self._battery_props = None
- bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
- proxy = bus.get_object('org.freedesktop.Hal',
- '/org/freedesktop/Hal/Manager')
- hal_manager = dbus.Interface(proxy, 'org.freedesktop.Hal.Manager')
- udis = hal_manager.FindDeviceByCapability('battery')
- if len(udis) > 0:
- self._battery = BattMan(udis[0]) # TODO: Support more than one battery
- self._battery.connect('notify::level', \
- self._battery_level_changed_cb)
- else:
- self._battery = None
+ try:
+ bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
+ up_proxy = bus.get_object('org.freedesktop.UPower',
+ '/org/freedesktop/UPower')
+ upower = dbus.Interface(up_proxy, 'org.freedesktop.UPower')
+
+ for device_path in upower.EnumerateDevices():
+ device = bus.get_object('org.freedesktop.UPower', device_path)
+ device_prop_iface = dbus.Interface(device,
+ dbus.PROPERTIES_IFACE)
+ device_type = device_prop_iface.Get(_UP_DEVICE_IFACE, 'Type')
+ if device_type != _UP_TYPE_BATTERY:
+ continue
+
+ device.connect_to_signal('Changed',
+ self.__battery_properties_changed_cb,
+ dbus_interface=_UP_DEVICE_IFACE)
+ self._battery_props = dbus.Interface(device,
+ dbus.PROPERTIES_IFACE)
+ break
- self._icon = None
+ except dbus.DBusException:
+ logging.warning("Could not connect to UPower, won't show battery"
+ "level.")
self._setup()
@@ -154,51 +81,61 @@ class _TopBar(gtk.HBox):
if property.name == 'completion-level':
return self._completion_level
else:
- raise AttributeError, 'unknown property %s' % property.name
+ raise AttributeError('unknown property %s' % property.name)
def do_set_property(self, property, value):
if property.name == 'completion-level':
self.set_completion_level(value)
else:
- raise AttributeError, 'unknown property %s' % property.name
+ raise AttributeError('unknown property %s' % property.name)
def set_completion_level(self, value):
self._completion_level = value
- self._progressbar.set_fraction(self._completion_level/100.0)
+ self._progressbar.set_fraction(self._completion_level / 100.0)
+
+ def _get_battery_level(self):
+ try:
+ return self._battery_props.Get(_UP_DEVICE_IFACE,
+ 'Percentage')
+ except dbus.DBusException:
+ logging.exception('Error determining battery level:')
+ return 0
def _setup(self):
self._progressbar = gtk.ProgressBar()
self._progressbar.props.discrete_blocks = 10
- self._progressbar.set_fraction(self._completion_level/100.0)
- self.pack_start(self._progressbar, expand = True, fill = True)
- if self._battery is not None:
- icon_name = get_icon_state(_ICON_NAME, self._battery.props.level, step=-5)
- self._icon = Icon(icon_name=icon_name)
- self.pack_start(self._icon, expand = False, fill = False)
-
- def _battery_level_changed_cb(self, pspec, param):
- icon_name = get_icon_state(_ICON_NAME, self._battery.props.level, step=-5)
+ self._progressbar.set_fraction(self._completion_level / 100.0)
+ self.pack_start(self._progressbar, expand=True, fill=True)
+ if self._battery_props is None:
+ return
+
+ level = self._get_battery_level()
+ icon_name = get_icon_state(_ICON_NAME, level, step=-5)
+ self._icon = Icon(icon_name=icon_name)
+ self.pack_start(self._icon, expand=False, fill=False)
+
+ def __battery_properties_changed_cb(self):
+ level = self._get_battery_level()
+ icon_name = get_icon_state(_ICON_NAME, level, step=-5)
self._icon.props.icon_name = icon_name
+
class TopBar(_TopBar):
+
def __init__(self):
_TopBar.__init__(self)
- self._document = None
+ self._view = None
- def set_document(self, document):
- self._document = document
+ def set_view(self, view):
+ self._view = view
+ self._view.connect_page_changed_handler(self._page_changed_cb)
- page_cache = self._document.get_page_cache()
- page_cache.connect('page-changed', self._page_changed_cb)
-
- def _page_changed_cb(self, page, proxy = None):
- current_page = self._document.get_page_cache().get_current_page()
- n_pages = self._document.get_n_pages()
-
- self.set_completion_level(current_page * 100/n_pages)
+ def _page_changed_cb(self, model, page_from, page_to):
+ current_page = self._view.get_current_page()
+ n_pages = self._view.get_pagecount()
+ completion_level = int(float(current_page) * 100 / float(n_pages))
+ self.set_completion_level(completion_level)
#TRANS: Translate this as Page i of m (eg: Page 4 of 334)
- self._progressbar.set_text(_("Page %i of %i") % (current_page, n_pages))
-
-
-
+ self._progressbar.set_text(
+ _("Page %i of %i") % (current_page, n_pages))