Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSayamindu Dasgupta <sayamindu@gmail.com>2009-07-20 20:36:51 (GMT)
committer Sayamindu Dasgupta <sayamindu@gmail.com>2009-07-20 20:36:51 (GMT)
commit0957516a88ef7216fc1e23f9ab2224562af3d91c (patch)
tree039e7d17ce05a25fde1f3b20db406363dfb53a0b
parentaee4aefc8c9536a18156a4db8d59d8c767f78729 (diff)
parent9760c4c9acdf0ea1cf4c57ded8c65eb4390ca171 (diff)
Merge branch 'master' of git://git.sugarlabs.org/read/epub-support
-rw-r--r--AUTHORS1
-rw-r--r--activity/activity.info2
-rw-r--r--activity/mimetypes.xml7
-rw-r--r--epubadapter.py67
-rw-r--r--epubview/__init__.py24
-rw-r--r--epubview/epub.py133
-rw-r--r--epubview/epubinfo.py90
-rw-r--r--epubview/epubview.py480
-rw-r--r--epubview/jobs.py248
-rwxr-xr-xepubview/modules/webkit.sobin0 -> 132372 bytes
-rw-r--r--epubview/navmap.py64
-rw-r--r--epubview/widgets.py27
-rw-r--r--readactivity.py149
-rw-r--r--readtoolbar.py116
14 files changed, 1319 insertions, 89 deletions
diff --git a/AUTHORS b/AUTHORS
index fe61bd3..e09d58b 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -7,3 +7,4 @@ Simon Schampijer <simon@schampijer.de>
Reinier Heeres <reinier@heeres.eu>
Guillaume Desmottes <guillaume.desmottes@collabora.co.uk>
Morgan Collett <morgan.collett@gmail.com>
+Sayamindu Dasgupta <sayamindu@laptop.org> (Current maintainer)
diff --git a/activity/activity.info b/activity/activity.info
index 92f0684..15729b4 100644
--- a/activity/activity.info
+++ b/activity/activity.info
@@ -5,5 +5,5 @@ icon = activity-read
exec = sugar-activity readactivity.ReadActivity
show_launcher = no
activity_version = 67
-mime_types = application/pdf;image/vnd.djvu;image/x.djvu;image/tiff;application/x-cbz;application/x-cbr
+mime_types = application/pdf;image/vnd.djvu;image/x.djvu;image/tiff;application/x-cbz;application/x-cbr;application/epub+zip
license = GPLv2+
diff --git a/activity/mimetypes.xml b/activity/mimetypes.xml
new file mode 100644
index 0000000..5c27572
--- /dev/null
+++ b/activity/mimetypes.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
+ <mime-type type="application/epub+zip">
+ <comment xml:lang="en">Epub document</comment>
+ <glob pattern="*.epub"/>
+ </mime-type>
+</mime-info>
diff --git a/epubadapter.py b/epubadapter.py
new file mode 100644
index 0000000..3fa4576
--- /dev/null
+++ b/epubadapter.py
@@ -0,0 +1,67 @@
+import gobject
+import logging
+
+import epubview
+
+_logger = logging.getLogger('read-activity')
+
+class View(epubview.EpubView):
+ def __init__(self):
+ epubview.EpubView.__init__(self)
+
+ def _try_load_page(self, n):
+ if self._ready:
+ self._load_page(n)
+ return False
+ else:
+ return True
+
+ def set_screen_dpi(self, dpi):
+ return
+
+ def find_set_highlight_search(self, set_highlight_search):
+ 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
+ # supplied page when the book has loaded completely
+ n += 1
+ if self._ready:
+ self._load_page(n)
+ else:
+ gobject.timeout_add(200, self._try_load_page, n)
+
+ def get_current_page(self):
+ return int(self._loaded_page - 1)
+
+ def find_changed(self, job, page = None):
+ self._find_changed(job)
+
+ def update_view_size(self, widget):
+ return
+
+ def handle_link(self, link):
+ self._load_file(link)
+
+
+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())
+
+ def has_document_links(self):
+ return True
+
+ 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)
diff --git a/epubview/__init__.py b/epubview/__init__.py
new file mode 100644
index 0000000..81a3175
--- /dev/null
+++ b/epubview/__init__.py
@@ -0,0 +1,24 @@
+# Copyright 2009 One Laptop Per Child
+# Author: Sayamindu Dasgupta <sayamindu@laptop.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import gobject
+
+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
diff --git a/epubview/epub.py b/epubview/epub.py
new file mode 100644
index 0000000..81de9d7
--- /dev/null
+++ b/epubview/epub.py
@@ -0,0 +1,133 @@
+# Copyright 2009 One Laptop Per Child
+# Author: Sayamindu Dasgupta <sayamindu@laptop.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import zipfile
+import tempfile
+import os, os.path
+from lxml import etree
+import shutil
+
+import navmap, epubinfo
+
+
+class _Epub(object):
+ def __init__(self, filepath):
+ self._filepath = filepath
+ self._zobject = None
+ self._obffile = None
+ self._obfpath = None
+ self._ncxpath = None
+ self._ncxfile = 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_obf()
+ self._get_ncx()
+
+ self._ncxfile = self._zobject.open(self._ncxpath)
+ self._navmap = navmap.NavMap(self._ncxfile, self._basepath)
+
+ self._obffile = self._zobject.open(self._obfpath)
+ self._info = epubinfo.EpubInfo(self._obffile)
+
+ self._unzip()
+
+ def _unzip(self):
+ #self._zobject.extractall(path = self._tempdir) # This is broken upto python 2.7
+ 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
+ name = name[1:]
+ if name.endswith(os.path.sep) or name.endswith('\\'):
+ os.makedirs(name)
+ else:
+ self._zobject.extract(name)
+ os.chdir(orig_cwd)
+
+
+ def _get_obf(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'):
+ if element.get('media-type') == 'application/oebps-package+xml':
+ self._obfpath = element.get('full-path')
+
+ if self._obfpath.rpartition('/')[0]:
+ self._basepath = self._obfpath.rpartition('/')[0] + '/'
+ else:
+ self._basepath = ''
+
+ containerfile.close()
+
+
+ def _get_ncx(self):
+ obffile = self._zobject.open(self._obfpath)
+
+ tree = etree.parse(obffile)
+ root = tree.getroot()
+
+ for element in root.iterfind('.//{http://www.idpf.org/2007/opf}item'):
+ if element.get('media-type') == 'application/x-dtbncx+xml' or \
+ element.get('id') == 'ncx':
+ self._ncxpath = self._basepath + element.get('href')
+
+ obffile.close()
+
+
+ def _verify(self):
+ '''
+ 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 mimetype != 'application/epub+zip':
+ return False
+
+ return True
+
+ def get_toc_model(self):
+ return self._navmap.get_gtktreestore()
+
+ def get_flattoc(self):
+ return self._navmap.get_flattoc()
+
+ def get_basedir(self):
+ return self._tempdir
+
+ def get_info(self):
+ return self._info
+
+ def close(self):
+ self._zobject.close()
+ shutil.rmtree(self._tempdir)
diff --git a/epubview/epubinfo.py b/epubview/epubinfo.py
new file mode 100644
index 0000000..ddf9ad5
--- /dev/null
+++ b/epubview/epubinfo.py
@@ -0,0 +1,90 @@
+import os
+from lxml import etree
+
+
+class EpubInfo(): #TODO: Cover the entire DC range
+ def __init__(self, file):
+ self._tree = etree.parse(file)
+ self._root = self._tree.getroot()
+ 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()
+ self.subject = self._get_subject()
+ self.source = self._get_source()
+ 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')
+ except AttributeError:
+ 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):
+ try:
+ ret = self._get_data('.//{http://purl.org/dc/elements/1.1/}source')
+ except AttributeError:
+ return None
+
+ return ret
+
+ def _get_rights(self):
+ try:
+ 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')
+
+ if element is not None:
+ 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')
+ 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'):
+ subjectlist.append(element.text)
+ except AttributeError:
+ return None
+
+ return subjectlist \ No newline at end of file
diff --git a/epubview/epubview.py b/epubview/epubview.py
new file mode 100644
index 0000000..f59fb9c
--- /dev/null
+++ b/epubview/epubview.py
@@ -0,0 +1,480 @@
+# Copyright 2009 One Laptop Per Child
+# Author: Sayamindu Dasgupta <sayamindu@laptop.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+import gtk, gtk.gdk
+import gobject
+import widgets
+
+import os.path
+import math
+import shutil
+
+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)
+ }
+ __gsignals__ = {
+ 'page-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
+ self._paginator = None
+ self._loaded_page = -1
+ #self._old_scrollval = -1
+ self._loaded_filename = None
+ self._pagecount = -1
+ self.__going_fwd = True
+ self.__going_back = False
+ self.__page_changed = False
+ self.has_selection = False
+ self.zoom = 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', '/')
+ settings = self._view.get_settings()
+ settings.props.default_font_family = 'DejaVu LGC Serif'
+ settings.props.enable_plugins = False
+ settings.props.default_encoding = 'utf-8'
+ 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._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._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)
+
+ def set_document(self, epubdocumentinstance):
+ 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
+ else:
+ raise AttributeError, 'unknown property %s' % property.name
+
+ def do_set_property(self, property, value):
+ if property.name == 'zoom':
+ self.__set_zoom(value)
+ else:
+ raise AttributeError, 'unknown property %s' % property.name
+
+ def get_has_selection(self):
+ return self.get_property('has-selection')
+
+ def get_zoom(self):
+ return self.get_property('zoom')
+
+ def set_zoom(self, value):
+ self.set_property('zoom', value)
+
+ def zoom_in(self):
+ if self.can_zoom_in():
+ self.set_zoom(self.get_zoom() + 0.1)
+ return True
+ else:
+ return False
+
+ def zoom_out(self):
+ if self.can_zoom_out():
+ self.set_zoom(self.get_zoom() - 0.1)
+ return True
+ else:
+ return False
+
+ def can_zoom_in(self):
+ if self.zoom < 4:
+ return True
+ else:
+ return False
+
+ def can_zoom_out(self):
+ if self.zoom > 0.5:
+ return True
+ else:
+ return False
+
+ def get_current_page(self):
+ return self._loaded_page
+
+ def get_current_file(self):
+ #return self._loaded_filename
+ if self._paginator:
+ return self._paginator.get_file_for_pageno(self._loaded_page)
+ else:
+ return None
+
+ def get_pagecount(self):
+ return self._pagecount
+
+ def set_current_page(self, n):
+ if n < 1 or n > self._pagecount:
+ return False
+ self._load_page(n)
+ return True
+
+ def next_page(self):
+ if self._loaded_page == self._pagecount:
+ return False
+ self._load_next_page()
+ return True
+
+ def previous_page(self):
+ if self._loaded_page == 1:
+ return False
+ self._load_prev_page()
+ return True
+
+ def scroll(self, scrolltype, horizontal):
+ if scrolltype == gtk.SCROLL_PAGE_BACKWARD:
+ self.__going_back = True
+ self.__going_fwd = False
+ if not self._do_page_transition():
+ self._view.move_cursor(gtk.MOVEMENT_PAGES, -1)
+ elif scrolltype == gtk.SCROLL_PAGE_FORWARD:
+ self.__going_back = False
+ self.__going_fwd = True
+ if not self._do_page_transition():
+ self._view.move_cursor(gtk.MOVEMENT_PAGES, 1)
+ else:
+ print ('Got unsupported scrolltype %s' % str(scrolltype))
+
+ def copy(self):
+ self._view.copy_clipboard()
+
+ def find_next(self):
+ 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):
+ return
+ else:
+ 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):
+ 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):
+ return
+ else:
+ 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)
+
+ def _find_changed(self, job):
+ 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
+
+ def __set_has_selection(self, value):
+ if value != self.has_selection:
+ self.has_selection = value
+ self.notify('has-selection')
+
+ def _view_populate_popup_cb(self, view, menu):
+ menu.destroy() #HACK
+ return
+
+ def _view_selection_changed_cb(self, view):
+ # FIXME: This does not seem to be implemented in
+ # webkitgtk yet
+ print view.has_selection()
+
+ def _view_buttonrelease_event_cb(self, view, event):
+ # Ugly hack
+ 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':
+ 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):
+ if event.direction == gtk.gdk.SCROLL_DOWN:
+ self.__going_back = False
+ self.__going_fwd = True
+ elif event.direction == gtk.gdk.SCROLL_UP:
+ self.__going_back = True
+ self.__going_fwd = False
+
+ self._do_page_transition()
+
+ def _do_page_transition(self):
+ if self.__going_fwd:
+ if self._v_vscrollbar.get_value() >= \
+ self._v_vscrollbar.props.adjustment.props.upper - \
+ self._v_vscrollbar.props.adjustment.props.page_size:
+ self._load_next_file()
+ return True
+ elif self.__going_back:
+ 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):
+ 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
+
+ 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)))
+ 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(), \
+ self.__search_fwd, False)
+ self.__in_search = False
+ else:
+ if self.__going_back:
+ # We need to scroll to the last page
+ self._scroll_page_end()
+ else:
+ self._scroll_page()
+
+ 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[1]))
+
+ 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
+ scrollval = scrollbar.get_value()
+ 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._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
+
+ scrollfactor_next = self._paginator.get_scrollfactor_pos_for_pageno(self._loaded_page + 1)
+ if scrollval > 0:
+ scrollfactor = scrollval/(scroll_upper - scroll_page_size)
+ else:
+ scrollfactor = 0
+ if scrollfactor >= scrollfactor_next:
+ self._on_page_changed(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):
+ return
+
+ scrollfactor_cur = self._paginator.get_scrollfactor_pos_for_pageno(self._loaded_page)
+ if scrollval > 0:
+ 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.__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')
+
+ def _load_page(self, pageno):
+ if pageno > self._pagecount or pageno < 1:
+ #TODO: Cause an exception
+ return
+
+ self._on_page_changed(pageno)
+ filename = self._paginator.get_file_for_pageno(pageno)
+ if filename != self._loaded_filename:
+ #self._loaded_filename = filename
+ if filename.endswith('xml'):
+ dest = filename.replace('xml', 'xhtml')
+ shutil.copy(filename.replace('file://', ''), dest.replace('file://', ''))
+ self._view.open(dest)
+ else:
+ self._view.open(filename)
+ else:
+ self._scroll_page()
+
+ def _load_next_file(self):
+ cur_file = self._paginator.get_file_for_pageno(self._loaded_page)
+ pageno = self._loaded_page
+ while pageno < self._paginator.get_total_pagecount():
+ pageno += 1
+ if self._paginator.get_file_for_pageno(pageno) != cur_file:
+ break
+
+ self._load_page(pageno)
+
+ def _load_file(self, path):
+ #TODO: This is a bit suboptimal - fix it
+ for pageno in range(1, self.get_pagecount()):
+ filepath = self._paginator.get_file_for_pageno(pageno)
+ if filepath.endswith(path):
+ self._load_page(pageno)
+ break
+
+ def _load_prev_file(self):
+ cur_file = self._paginator.get_file_for_pageno(self._loaded_page)
+ pageno = self._loaded_page
+ while pageno > 1:
+ 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):
+ if 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_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_JUMP or \
+ scrolltype == gtk.SCROLL_PAGE_FORWARD or \
+ scrolltype == gtk.SCROLL_PAGE_BACKWARD:
+ if value > self._scrollbar.props.adjustment.props.upper:
+ self._load_page(self._pagecount)
+ 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
+
+ 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)
+ self._view.grab_focus()
+ 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
new file mode 100644
index 0000000..de276b3
--- /dev/null
+++ b/epubview/jobs.py
@@ -0,0 +1,248 @@
+# Copyright 2009 One Laptop Per Child
+# Author: Sayamindu Dasgupta <sayamindu@laptop.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+import gobject
+import gtk
+import widgets
+import cairo
+
+import math
+import os.path
+import BeautifulSoup
+
+import epub
+
+import threading
+
+PAGE_WIDTH = 135
+PAGE_HEIGHT = 216
+
+def _pixel_to_mm(pixel, dpi):
+ 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)
+ self.obj = obj
+ self.stopthread = threading.Event()
+
+ def _start_search(self):
+ for entry in self.obj.flattoc:
+ if self.stopthread.isSet():
+ break
+ name, file = entry
+ filepath = os.path.join(self.obj._document.get_basedir(), file)
+ f = open(filepath)
+ if self._searchfile(f):
+ self.obj._matchfilelist.append(file)
+ f.close()
+
+ gtk.gdk.threads_enter()
+ 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 tag.string.find(self.obj._text) > -1:
+ return True
+
+ return False
+
+ 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,
+ ([]))
+ }
+ 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()
+ options.set_hint_style(cairo.HINT_STYLE_MEDIUM)
+ options.set_antialias(cairo.ANTIALIAS_GRAY)
+ 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()
+
+ settings = self._temp_view.get_settings()
+ settings.props.default_font_family = 'DejaVu LGC Serif'
+ settings.props.sans_serif_font_family = 'DejaVu LGC Sans'
+ 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
+ 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.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.unrealize()
+
+ 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))
+ for i in range(1, int(math.ceil(pages) + 1)):
+ if pages - i < 0:
+ 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)
+ self._bookheight += pageheight
+
+ 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):
+ return self._pagemap[pageno][0]
+
+ def get_scrollfactor_pos_for_pageno(self, pageno):
+ return self._pagemap[pageno][1]
+
+ def get_scrollfactor_len_for_pageno(self, pageno):
+ return self._pagemap[pageno][2]
+
+ def get_pagecount_for_file(self, file):
+ return self._filedict[file][0]
+
+ def get_remfactor_for_file(self, file):
+ return self._filedict[file][1]
+
+ def get_total_pagecount(self):
+ return self._pagecount
+
+ def get_total_height(self):
+ return self._bookheight
+
+
+class _JobFind(gobject.GObject):
+ __gsignals__ = {
+ 'updated': (gobject.SIGNAL_RUN_FIRST,
+ gobject.TYPE_NONE,
+ ([]))
+ }
+ def __init__(self, document, start_page, n_pages, text, case_sensitive=False):
+ gobject.GObject.__init__(self)
+ gtk.threads_init()
+
+ self._finished = False
+ self._document = document
+ self._start_page = start_page
+ self._n_pages = n_pages
+ self._text = text
+ self._case_sensitive = case_sensitive
+ self.flattoc = self._document.get_flattoc()
+ self._matchfilelist = []
+ self._current_file_index = 0
+ self.threads = []
+
+ s_thread = SearchThread(self)
+ self.threads.append(s_thread)
+ s_thread.start()
+
+ def cancel(self):
+ for s_thread in self.threads:
+ s_thread.stop()
+
+ def is_finished(self):
+ return self._finished
+
+ def get_next_file(self):
+ 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):
+ 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):
+ return self._text
+
+ def get_case_sensitive(self):
+ return self._case_sensitive
diff --git a/epubview/modules/webkit.so b/epubview/modules/webkit.so
new file mode 100755
index 0000000..f8dce2a
--- /dev/null
+++ b/epubview/modules/webkit.so
Binary files differ
diff --git a/epubview/navmap.py b/epubview/navmap.py
new file mode 100644
index 0000000..0ba0b8f
--- /dev/null
+++ b/epubview/navmap.py
@@ -0,0 +1,64 @@
+from lxml import etree
+import gtk
+
+class NavPoint(object):
+ def __init__(self, label, contentsrc, children = []):
+ self._label = label
+ self._contentsrc = contentsrc
+ self._children = children
+
+ def get_label(self):
+ return self._label
+
+ def get_contentsrc(self):
+ return self._contentsrc
+
+ def get_children(self):
+ return self._children
+
+
+class NavMap(object):
+ def __init__(self, file, basepath):
+ self._basepath = basepath
+ self._tree = etree.parse(file)
+ self._root = self._tree.getroot()
+ self._gtktreestore = gtk.TreeStore(str, str)
+ self._flattoc = []
+
+ self._populate_toc()
+
+ 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'):
+ 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')
+ return text.text
+
+ def _getcontent(self, navpoint):
+ text = navpoint.find('./{http://www.daisy.org/z3986/2005/ncx/}content/')
+ return self._basepath + text.get('src')
+
+ def _process_navpoint(self, navpoint, parent = None):
+ title = self._gettitle(navpoint)
+ content = self._getcontent(navpoint)
+
+ iter = self._gtktreestore.append(parent, [title, content])
+ self._flattoc.append((title, content))
+
+ 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:
+ return
+
+ def get_gtktreestore(self):
+ return self._gtktreestore
+
+ def get_flattoc(self):
+ return self._flattoc
+
+#t = TocParser('/home/sayamindu/Desktop/Test/OPS/fb.ncx') \ No newline at end of file
diff --git a/epubview/widgets.py b/epubview/widgets.py
new file mode 100644
index 0000000..19d2dc9
--- /dev/null
+++ b/epubview/widgets.py
@@ -0,0 +1,27 @@
+try:
+ import webkit
+except ImportError:
+ import os, sys
+ sys.path.append(os.path.join(os.environ['SUGAR_BUNDLE_PATH'], 'epubview', 'modules'))
+ import webkit
+
+import gtk
+
+
+class _WebView(webkit.WebView):
+ def __init__(self):
+ webkit.WebView.__init__(self)
+
+ def get_page_height(self):
+ #TODO: Need to check status of page load
+ js = 'oldtitle=document.title;document.title=document.body.clientHeight;'
+ self.execute_script(js)
+ ret = self.get_main_frame().get_title()
+ js = 'document.title=oldtitle;'
+ self.execute_script(js)
+ return int(ret)
+
+ def add_bottom_padding(self, incr):
+ js = ('var newdiv = document.createElement("div");newdiv.style.height = "%dpx";document.body.appendChild(newdiv);' % incr)
+ self.execute_script(js)
+
diff --git a/readactivity.py b/readactivity.py
index c876e22..ca1fcbd 100644
--- a/readactivity.py
+++ b/readactivity.py
@@ -36,6 +36,8 @@ from sugar.graphics.objectchooser import ObjectChooser
from readtoolbar import EditToolbar, ReadToolbar, ViewToolbar
from readsidebar import Sidebar
+import epubadapter
+
_HARDWARE_MANAGER_INTERFACE = 'org.laptop.HardwareManager'
_HARDWARE_MANAGER_SERVICE = 'org.laptop.HardwareManager'
@@ -102,6 +104,7 @@ class ReadActivity(activity.Activity):
# if we use evince-2.24
evince.evince_embed_init()
+ self._epub = False
self._document = None
self._fileserver = None
self._object_id = handle.object_id
@@ -111,17 +114,14 @@ class ReadActivity(activity.Activity):
_logger.debug('Starting Read...')
- 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._view = None
self._sidebar = Sidebar()
self._sidebar.show()
toolbox = activity.ActivityToolbox(self)
- self._edit_toolbar = EditToolbar(self._view)
+ self._edit_toolbar = EditToolbar()
self._edit_toolbar.undo.props.visible = False
self._edit_toolbar.redo.props.visible = False
self._edit_toolbar.separator.props.visible = False
@@ -131,11 +131,11 @@ class ReadActivity(activity.Activity):
toolbox.add_toolbar(_('Edit'), self._edit_toolbar)
self._edit_toolbar.show()
- self._read_toolbar = ReadToolbar(self._view, self._sidebar)
+ self._read_toolbar = ReadToolbar(self._sidebar)
toolbox.add_toolbar(_('Read'), self._read_toolbar)
self._read_toolbar.show()
- self._view_toolbar = ViewToolbar(self._view)
+ self._view_toolbar = ViewToolbar()
self._view_toolbar.connect('needs-update-size',
self.__view_toolbar_needs_update_size_cb)
self._view_toolbar.connect('go-fullscreen',
@@ -146,20 +146,11 @@ class ReadActivity(activity.Activity):
self.set_toolbox(toolbox)
toolbox.show()
- 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 = gtk.HBox()
+ self._hbox.pack_start(self._sidebar, expand=False, fill=False)
- hbox = gtk.HBox()
- hbox.pack_start(self._sidebar, expand=False, fill=False)
- hbox.pack_start(self._scrolled, expand=True, fill=True)
-
- self.set_canvas(hbox)
- self._scrolled.show()
- hbox.show()
+ self.set_canvas(self._hbox)
+ self._hbox.show()
# Set up for idle suspend
self._idle_timer = 0
@@ -316,15 +307,16 @@ class ReadActivity(activity.Activity):
self.metadata['Read_zoom'] = str(self._view.props.zoom)
- if self._view.props.sizing_mode == evince.SIZING_BEST_FIT:
- self.metadata['Read_sizing_mode'] = "best-fit"
- elif self._view.props.sizing_mode == evince.SIZING_FREE:
- self.metadata['Read_sizing_mode'] = "free"
- elif self._view.props.sizing_mode == evince.SIZING_FIT_WIDTH:
- self.metadata['Read_sizing_mode'] = "fit-width"
- else:
- _logger.error("Don't know how to save sizing_mode state '%s'" %
- self._view.props.sizing_mode)
+ if not self._epub:
+ if self._view.props.sizing_mode == evince.SIZING_BEST_FIT:
+ self.metadata['Read_sizing_mode'] = "best-fit"
+ elif self._view.props.sizing_mode == evince.SIZING_FREE:
+ self.metadata['Read_sizing_mode'] = "free"
+ elif self._view.props.sizing_mode == evince.SIZING_FIT_WIDTH:
+ self.metadata['Read_sizing_mode'] = "fit-width"
+ else:
+ _logger.error("Don't know how to save sizing_mode state '%s'" %
+ self._view.props.sizing_mode)
self.metadata['Read_sizing_mode'] = "fit-width"
self.metadata['Read_search'] = \
@@ -452,13 +444,63 @@ class ReadActivity(activity.Activity):
self.watch_for_tubes()
gobject.idle_add(self._get_document)
+ def _setup_evince_viewer(self):
+ self._view = evince.View()
+ 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)
+
+
+ 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://
"""
- self._document = evince.factory_get_document(filepath)
+ mimetype = mime.get_for_file(filepath)
+ if mimetype == 'application/epub+zip':
+ self._epub = True
+ self._setup_epub_viewer()
+ self._document = epubadapter.EpubDocument(self._view, filepath.replace('file://', ''))
+ else:
+ self._setup_evince_viewer()
+ self._document = evince.factory_get_document(filepath)
+
+ self._view_toolbar.set_view(self._view)
+ self._edit_toolbar.set_view(self._view)
+ self._read_toolbar.set_view(self._view)
+
self._want_document = False
self._view.set_document(self._document)
self._edit_toolbar.set_document(self._document)
@@ -469,33 +511,35 @@ class ReadActivity(activity.Activity):
if info and info.title:
self.metadata['title'] = info.title
- current_page = int(self.metadata.get('Read_current_page', '0'))
- self._document.get_page_cache().set_current_page(current_page)
-
- sizing_mode = self.metadata.get('Read_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
- self._view.update_view_size(self._scrolled)
- elif sizing_mode == "free":
- self._view.props.sizing_mode = evince.SIZING_FREE
- self._view.props.zoom = float(self.metadata.get('Read_zoom', '1.0'))
- _logger.debug('Set zoom to %f', self._view.props.zoom)
- elif sizing_mode == "fit-width":
- self._view.props.sizing_mode = evince.SIZING_FIT_WIDTH
- self._view.update_view_size(self._scrolled)
- else:
- # this may happen when we get a document from a buddy with a later
- # version of Read, for example.
- _logger.warning("Unknown sizing_mode state '%s'", sizing_mode)
- if self.metadata.get('Read_zoom', None) is not None:
- self._view.props.zoom = float(self.metadata['Read_zoom'])
+ if not self._epub:
+ sizing_mode = self.metadata.get('Read_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
+ self._view.update_view_size(self._scrolled)
+ elif sizing_mode == "free":
+ self._view.props.sizing_mode = evince.SIZING_FREE
+ self._view.props.zoom = float(self.metadata.get('Read_zoom', '1.0'))
+ _logger.debug('Set zoom to %f', self._view.props.zoom)
+ elif sizing_mode == "fit-width":
+ self._view.props.sizing_mode = evince.SIZING_FIT_WIDTH
+ self._view.update_view_size(self._scrolled)
+ else:
+ # this may happen when we get a document from a buddy with a later
+ # version of Read, for example.
+ _logger.warning("Unknown sizing_mode state '%s'", sizing_mode)
+ if self.metadata.get('Read_zoom', None) is not None:
+ self._view.props.zoom = float(self.metadata['Read_zoom'])
self._view_toolbar._update_zoom_buttons()
self._edit_toolbar._search_entry.props.text = \
self.metadata.get('Read_search', '')
+ current_page = int(self.metadata.get('Read_current_page', '0'))
+ _logger.debug('Setting page to: %d', current_page)
+ self._document.get_page_cache().set_current_page(current_page)
+
# We've got the document, so if we're a shared activity, offer it
try:
if self.get_shared():
@@ -593,7 +637,8 @@ class ReadActivity(activity.Activity):
_logger.debug("Keyname Release: %s, time: %s", keyname, event.time)
def __view_toolbar_needs_update_size_cb(self, view_toolbar):
- self._view.update_view_size(self._scrolled)
+ if not self._epub:
+ self._view.update_view_size(self._scrolled)
def __view_toolbar_go_fullscreen_cb(self, view_toolbar):
self.fullscreen()
diff --git a/readtoolbar.py b/readtoolbar.py
index d79c1ed..9af7ac4 100644
--- a/readtoolbar.py
+++ b/readtoolbar.py
@@ -23,6 +23,8 @@ import gobject
import gtk
import evince
+import epubadapter
+
import md5
from sugar.graphics.toolbutton import ToolButton
@@ -49,11 +51,10 @@ def get_md5(filename): #FIXME: Should be moved somewhere else
class EditToolbar(activity.EditToolbar):
__gtype_name__ = 'EditToolbar'
- def __init__(self, evince_view):
+ def __init__(self):
activity.EditToolbar.__init__(self)
- self._evince_view = evince_view
- self._evince_view.find_set_highlight_search(True)
+ self._evince_view = None
self._document = None
self._find_job = None
@@ -97,6 +98,10 @@ class EditToolbar(activity.EditToolbar):
self.insert(self._next, -1)
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
@@ -112,9 +117,13 @@ class EditToolbar(activity.EditToolbar):
self._clear_find_job()
text = self._search_entry.props.text
if text != "":
- 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)
+ 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)
else:
# FIXME: highlight nothing
pass
@@ -148,7 +157,7 @@ class EditToolbar(activity.EditToolbar):
def _find_changed_cb(self, page, spec):
self._update_find_buttons()
- def _find_updated_cb(self, job, page):
+ def _find_updated_cb(self, job, page = None):
self._evince_view.find_changed(job, page)
def _find_prev_cb(self, button):
@@ -182,10 +191,10 @@ class EditToolbar(activity.EditToolbar):
class ReadToolbar(gtk.Toolbar):
__gtype_name__ = 'ReadToolbar'
- def __init__(self, evince_view, sidebar):
+ def __init__(self, sidebar):
gtk.Toolbar.__init__(self)
- self._evince_view = evince_view
+ self._evince_view = None
self._sidebar = sidebar
self._document = None
@@ -200,7 +209,7 @@ class ReadToolbar(gtk.Toolbar):
palette.menu.append(self._prev_bookmark)
self._prev_bookmark.show_all()
self._back.connect('clicked', self._go_back_cb)
- self._prev_page.connect('activate', self._go_back_cb)
+ self._prev_page.connect('activate', self._go_back_page_cb)
self._prev_bookmark.connect('activate', self._prev_bookmark_activate_cb)
self.insert(self._back, -1)
self._back.show()
@@ -216,7 +225,7 @@ class ReadToolbar(gtk.Toolbar):
palette.menu.append(self._next_bookmark)
self._next_bookmark.show_all()
self._forward.connect('clicked', self._go_forward_cb)
- self._next_page.connect('activate', self._go_forward_cb)
+ self._next_page.connect('activate', self._go_forward_page_cb)
self._next_bookmark.connect('activate', self._next_bookmark_activate_cb)
self.insert(self._forward, -1)
self._forward.show()
@@ -286,7 +295,10 @@ class ReadToolbar(gtk.Toolbar):
self.insert(bookmarkitem, -1)
bookmarkitem.show_all()
-
+
+ def set_view(self, view):
+ self._evince_view = view
+
def set_document(self, document, filepath):
filehash = get_md5(filepath)
self._document = document
@@ -315,11 +327,17 @@ class ReadToolbar(gtk.Toolbar):
self._document.get_page_cache().set_current_page(page)
entry.props.text = str(page + 1)
-
+
def _go_back_cb(self, button):
+ self._evince_view.scroll(gtk.SCROLL_PAGE_BACKWARD, False)
+
+ def _go_forward_cb(self, button):
+ self._evince_view.scroll(gtk.SCROLL_PAGE_FORWARD, False)
+
+ def _go_back_page_cb(self, button):
self._evince_view.previous_page()
- def _go_forward_cb(self, button):
+ def _go_forward_page_cb(self, button):
self._evince_view.next_page()
def _prev_bookmark_activate_cb(self, menuitem):
@@ -345,7 +363,7 @@ class ReadToolbar(gtk.Toolbar):
else:
self._sidebar.del_bookmark(page)
- def _page_changed_cb(self, page, proxy):
+ def _page_changed_cb(self, page, proxy = None):
self._update_nav_buttons()
if hasattr(self._document, 'has_document_links'):
if self._document.has_document_links():
@@ -391,11 +409,19 @@ class ReadToolbar(gtk.Toolbar):
def _toc_select_active_page_foreach(self, model, path, iter, current_page):
link = self._toc_model.get(iter, 1)[0]
- if current_page == link.get_page():
- self._navigator.set_active_iter(iter)
- return True
+ if not hasattr(link, 'get_page'):
+ #FIXME: This needs to be implemented in epubadapter, not here
+ filepath = self._evince_view.get_current_file()
+ if filepath.endswith(link):
+ self._navigator.set_active_iter(iter)
+ return True
else:
- return False
+ if current_page == link.get_page():
+ self._navigator.set_active_iter(iter)
+ return True
+
+ return False
+
def _toc_select_active_page(self):
iter = self._navigator.get_active_iter()
@@ -403,9 +429,14 @@ class ReadToolbar(gtk.Toolbar):
current_link = self._toc_model.get(iter, 1)[0]
current_page = self._document.get_page_cache().get_current_page()
- if current_link.get_page() == current_page:
- # Nothing to do
- return
+
+ if not hasattr(current_link, 'get_page'):
+ filepath = self._evince_view.get_current_file()
+ if filepath is None or filepath.endswith(current_link):
+ return
+ else:
+ if current_link.get_page() == current_page:
+ return
self._navigator.handler_block(self.__navigator_changed_handler_id)
self._toc_model.foreach(self._toc_select_active_page_foreach, current_page)
@@ -424,10 +455,10 @@ class ViewToolbar(gtk.Toolbar):
([]))
}
- def __init__(self, evince_view):
+ def __init__(self):
gtk.Toolbar.__init__(self)
- self._evince_view = evince_view
+ self._evince_view = None
self._document = None
self._zoom_out = ToolButton('zoom-out')
@@ -466,7 +497,6 @@ class ViewToolbar(gtk.Toolbar):
self._zoom_spin = gtk.SpinButton()
self._zoom_spin.set_range(5.409, 400)
self._zoom_spin.set_increments(1, 10)
- self._zoom_spin.props.value = self._evince_view.props.zoom * 100
self._zoom_spin_notify_value_handler = self._zoom_spin.connect(
'notify::value', self._zoom_spin_notify_value_cb)
tool_item.add(self._zoom_spin)
@@ -479,11 +509,6 @@ class ViewToolbar(gtk.Toolbar):
self.insert(tool_item_zoom_perc_label, -1)
tool_item_zoom_perc_label.show()
- self._view_notify_zoom_handler = self._evince_view.connect(
- 'notify::zoom', self._view_notify_zoom_cb)
-
- self._update_zoom_buttons()
-
spacer = gtk.SeparatorToolItem()
spacer.props.draw = False
self.insert(spacer, -1)
@@ -495,10 +520,24 @@ class ViewToolbar(gtk.Toolbar):
self.insert(self._fullscreen, -1)
self._fullscreen.show()
+ 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()
+
+
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:
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
+ 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(
@@ -513,7 +552,8 @@ class ViewToolbar(gtk.Toolbar):
'notify::value', self._zoom_spin_notify_value_cb)
def zoom_in(self):
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
+ if hasattr(self._evince_view.props, 'sizing_mode'):
+ self._evince_view.props.sizing_mode = evince.SIZING_FREE
self._evince_view.zoom_in()
self._update_zoom_buttons()
@@ -521,7 +561,8 @@ class ViewToolbar(gtk.Toolbar):
self.zoom_in()
def zoom_out(self):
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
+ if hasattr(self._evince_view.props, 'sizing_mode'):
+ self._evince_view.props.sizing_mode = evince.SIZING_FREE
self._evince_view.zoom_out()
self._update_zoom_buttons()
@@ -529,7 +570,8 @@ class ViewToolbar(gtk.Toolbar):
self.zoom_out()
def zoom_to_width(self):
- self._evince_view.props.sizing_mode = evince.SIZING_FIT_WIDTH
+ if hasattr(self._evince_view.props, 'sizing_mode'):
+ self._evince_view.props.sizing_mode = evince.SIZING_FIT_WIDTH
self.emit('needs-update-size')
self._update_zoom_buttons()
@@ -541,12 +583,14 @@ class ViewToolbar(gtk.Toolbar):
self._zoom_out.props.sensitive = self._evince_view.can_zoom_out()
def _zoom_to_fit_menu_item_activate_cb(self, menu_item):
- self._evince_view.props.sizing_mode = evince.SIZING_BEST_FIT
+ 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._update_zoom_buttons()
def _actual_size_menu_item_activate_cb(self, menu_item):
- self._evince_view.props.sizing_mode = evince.SIZING_FREE
+ 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._update_zoom_buttons()