diff options
author | Andi_G <andigros72@googlemail.com> | 2011-07-24 00:44:57 (GMT) |
---|---|---|
committer | Andi_G <andigros72@googlemail.com> | 2011-07-24 00:44:57 (GMT) |
commit | 3183c8fa15aadf9d6363fd49a9ee706ef696444b (patch) | |
tree | 6922b02e242e2335ed1452fe8aea447617299da3 | |
parent | 06ae426ec0a44b7daecdfab54710e9d6181f2566 (diff) |
added missing files
-rw-r--r-- | epubview/__init__.pyc | bin | 383 -> 0 bytes | |||
-rw-r--r-- | epubview/epub.pyc | bin | 4960 -> 0 bytes | |||
-rw-r--r-- | epubview/epubinfo.pyc | bin | 3644 -> 0 bytes | |||
-rw-r--r-- | epubview/epubview.pyc | bin | 19504 -> 0 bytes | |||
-rw-r--r-- | epubview/highlight_words.js | 89 | ||||
-rw-r--r-- | epubview/jobs.pyc | bin | 10512 -> 0 bytes | |||
-rw-r--r-- | epubview/navmap.pyc | bin | 4546 -> 0 bytes | |||
-rw-r--r-- | epubview/widgets.pyc | bin | 1778 -> 0 bytes | |||
-rw-r--r-- | evinceadapter.py | 259 | ||||
-rw-r--r-- | speech.py | 44 | ||||
-rw-r--r-- | textadapter.py | 516 |
11 files changed, 908 insertions, 0 deletions
diff --git a/epubview/__init__.pyc b/epubview/__init__.pyc Binary files differdeleted file mode 100644 index 2c23dd5..0000000 --- a/epubview/__init__.pyc +++ /dev/null diff --git a/epubview/epub.pyc b/epubview/epub.pyc Binary files differdeleted file mode 100644 index ad0d223..0000000 --- a/epubview/epub.pyc +++ /dev/null diff --git a/epubview/epubinfo.pyc b/epubview/epubinfo.pyc Binary files differdeleted file mode 100644 index fdf1ad8..0000000 --- a/epubview/epubinfo.pyc +++ /dev/null diff --git a/epubview/epubview.pyc b/epubview/epubview.pyc Binary files differdeleted file mode 100644 index abbada9..0000000 --- a/epubview/epubview.pyc +++ /dev/null diff --git a/epubview/highlight_words.js b/epubview/highlight_words.js new file mode 100644 index 0000000..ffa5e9a --- /dev/null +++ b/epubview/highlight_words.js @@ -0,0 +1,89 @@ + var parentElement; + var actualChild; + var actualWord; + var words; + var originalNode = null; + var modifiedNode = null; + + function trim(s) { + s = ( s || '' ).replace( /^\s+|\s+$/g, '' ); + return s.replace(/[\n\r\t]/g,' '); + } + + + function init() { + parentElement = document.getElementsByTagName("body")[0]; + actualChild = new Array(); + actualWord = 0; + actualChild.push(0); + } + + function highLightNextWordInt() { + var nodeList = parentElement.childNodes; + ini_posi = actualChild[actualChild.length - 1]; + for (var i=ini_posi; i < nodeList.length; i++) { + var node = nodeList[i]; + if ((node.nodeName == "#text") && (trim(node.nodeValue) != '')) { + node_text = trim(node.nodeValue); + words = node_text.split(" "); + if (actualWord < words.length) { + originalNode = document.createTextNode(node.nodeValue); + + prev_text = ''; + for (var p1 = 0; p1 < actualWord; p1++) { + prev_text = prev_text + words[p1] + " "; + } + var textNode1 = document.createTextNode(prev_text); + var textNode2 = document.createTextNode(words[actualWord]+" "); + post_text = ''; + for (var p2 = actualWord + 1; p2 < words.length; p2++) { + post_text = post_text + words[p2] + " "; + } + var textNode3 = document.createTextNode(post_text); + var newParagraph = document.createElement('p'); + var boldNode = document.createElement('b'); + boldNode.appendChild(textNode2); + newParagraph.appendChild(textNode1); + newParagraph.appendChild(boldNode); + newParagraph.appendChild(textNode3); + + parentElement.insertBefore(newParagraph, node); + parentElement.removeChild(node); + modifiedNode = newParagraph; + + actualWord = actualWord + 1; + if (actualWord >= words.length) { + actualChild.pop(); + actualChild[actualChild.length - 1] = actualChild[actualChild.length - 1] + 2; + actualWord = 0; + parentElement = parentElement.parentNode; + } + } + throw "exit"; + } else { + if (node.childNodes.length > 0) { + parentElement = node; + actualChild.push(0); + actualWord = 0; + highLightNextWordInt(); + actualChild.pop(); + } + } + } + return; + } + + + function highLightNextWord() { + if (typeof parentElement == "undefined") { + init(); + } + if (originalNode != null) { + modifiedNode.parentNode.insertBefore(originalNode, modifiedNode); + modifiedNode.parentNode.removeChild(modifiedNode); + } + try { + highLightNextWordInt(); + } catch(er) { + } + } diff --git a/epubview/jobs.pyc b/epubview/jobs.pyc Binary files differdeleted file mode 100644 index 4ba8785..0000000 --- a/epubview/jobs.pyc +++ /dev/null diff --git a/epubview/navmap.pyc b/epubview/navmap.pyc Binary files differdeleted file mode 100644 index 8eb322b..0000000 --- a/epubview/navmap.pyc +++ /dev/null diff --git a/epubview/widgets.pyc b/epubview/widgets.pyc Binary files differdeleted file mode 100644 index cffed84..0000000 --- a/epubview/widgets.pyc +++ /dev/null diff --git a/evinceadapter.py b/evinceadapter.py new file mode 100644 index 0000000..632ff96 --- /dev/null +++ b/evinceadapter.py @@ -0,0 +1,259 @@ +import gobject +import logging +import gtk + +import evince + +_logger = logging.getLogger('read-activity') + + +class EvinceViewer(): + + def __init__(self): + self._view_notify_zoom_handler = None + self._view = evince.View() + + def setup(self, activity): + self._activity = activity + self._view.connect('selection-changed', + activity._view_selection_changed_cb) + + activity._scrolled = gtk.ScrolledWindow() + activity._scrolled.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + activity._scrolled.props.shadow_type = gtk.SHADOW_NONE + + activity._scrolled.add(self._view) + self._view.show() + + activity._hbox.pack_start(activity._scrolled, expand=True, fill=True) + activity._scrolled.show() + + self.dpi = activity.dpi + + def load_document(self, file_path): + try: + self._document = evince.document_factory_get_document(file_path) + except gobject.GError, e: + _logger.error('Can not load document: %s', e) + return + else: + self._model = evince.DocumentModel() + self._model.set_document(self._document) + self._view.set_model(self._model) + + # set dpi + min_scale = self._model.get_min_scale() + max_scale = self._model.get_max_scale() + self._model.set_min_scale(min_scale * self.dpi / 72.0) + self._model.set_max_scale(max_scale * self.dpi / 72.0) + + def get_current_page(self): + return self._model.props.page + + def set_current_page(self, page): + if page >= self._document.get_n_pages(): + page = self._document.get_n_pages() - 1 + elif page < 0: + page = 0 + self._model.props.page = page + + def get_pagecount(self): + ''' + Returns the pagecount of the loaded file + ''' + return self._document.get_n_pages() + + def load_metadata(self, activity): + + self.metadata = activity.metadata + + if not self.metadata['title_set_by_user'] == '1': + title = self._document.get_title() + if title: + self.metadata['title'] = title + + sizing_mode = self.metadata.get('Read_sizing_mode', 'fit-width') + _logger.debug('Found sizing mode: %s', sizing_mode) + if sizing_mode == "best-fit": + self._model.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._model.props.sizing_mode = evince.SIZING_FREE + self._model.props.scale = \ + float(self.metadata.get('Read_zoom', '1.0')) + _logger.debug('Set zoom to %f', self._model.props.scale) + elif sizing_mode == "fit-width": + self._model.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 Read, for example. + _logger.warning("Unknown sizing_mode state '%s'", sizing_mode) + if self.metadata.get('Read_zoom', None) is not None: + self._model.props.scale = float(self.metadata['Read_zoom']) + + def update_metadata(self, activity): + self.metadata = activity.metadata + self.metadata['Read_zoom'] = str(self._model.props.scale) + + if self._model.props.sizing_mode == evince.SIZING_BEST_FIT: + self.metadata['Read_sizing_mode'] = "best-fit" + elif self._model.props.sizing_mode == evince.SIZING_FREE: + self.metadata['Read_sizing_mode'] = "free" + elif self._model.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._model.props.sizing_mode) + self.metadata['Read_sizing_mode'] = "fit-width" + + def can_highlight(self): + return False + + def can_do_text_to_speech(self): + return False + + def get_zoom(self): + ''' + Returns the current zoom level + ''' + return self._model.props.scale * 100 + + def set_zoom(self, value): + ''' + Sets the current zoom level + ''' + self._model.props.sizing_mode = evince.SIZING_FREE + + if not self._view_notify_zoom_handler: + return + + self._model.disconnect(self._view_notify_zoom_handler) + try: + self._model.props.scale = value / 100.0 + finally: + self._view_notify_zoom_handler = self._model.connect( + 'notify::scale', self._zoom_handler) + + def zoom_in(self): + ''' + Zooms in (increases zoom level by 0.1) + ''' + self._model.props.sizing_mode = evince.SIZING_FREE + self._view.zoom_in() + + def zoom_out(self): + ''' + Zooms out (decreases zoom level by 0.1) + ''' + self._model.props.sizing_mode = evince.SIZING_FREE + self._view.zoom_out() + + def zoom_to_width(self): + self._model.props.sizing_mode = evince.SIZING_FIT_WIDTH + + def can_zoom_in(self): + ''' + Returns True if it is possible to zoom in further + ''' + return self._view.can_zoom_in() + + def can_zoom_out(self): + ''' + Returns True if it is possible to zoom out further + ''' + return self._view.can_zoom_out() + + def can_zoom_to_width(self): + return True + + def zoom_to_best_fit(self): + self._model.props.sizing_mode = evince.SIZING_BEST_FIT + + def zoom_to_actual_size(self): + self._model.props.sizing_mode = evince.SIZING_FREE + self._model.props.scale = 1.0 + + def connect_zoom_handler(self, handler): + self._zoom_handler = handler + self._view_notify_zoom_handler = \ + self._model.connect('notify::scale', handler) + return self._view_notify_zoom_handler + + def setup_find_job(self, text, updated_cb): + 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', + updated_cb) + evince.Job.scheduler_push_job(self._find_job, + evince.JOB_PRIORITY_NONE) + return self._find_job, self._find_updated_handler + + def connect_page_changed_handler(self, handler): + self._model.connect('page-changed', handler) + + def update_toc(self, activity): + return False + + def find_set_highlight_search(self, set_highlight_search): + self._view.find_set_highlight_search(set_highlight_search) + + def find_next(self): + ''' + Highlights the next matching item for current search + ''' + self._view.find_next() + + def find_previous(self): + ''' + Highlights the previous matching item for current search + ''' + self._view.find_previous() + + def find_changed(self, job, page=None): + self._view.find_changed(job, page) + + def scroll(self, scrolltype, horizontal): + ''' + Scrolls through the pages. + Scrolling is horizontal if horizontal is set to True + Valid scrolltypes are: + gtk.SCROLL_PAGE_BACKWARD, gtk.SCROLL_PAGE_FORWARD, + gtk.SCROLL_STEP_BACKWARD, gtk.SCROLL_STEP_FORWARD, + gtk.SCROLL_START and gtk.SCROLL_END + ''' + _logger.error('scroll: %s', scrolltype) + + if scrolltype == gtk.SCROLL_PAGE_BACKWARD: + self._view.scroll(gtk.SCROLL_PAGE_BACKWARD, horizontal) + elif scrolltype == gtk.SCROLL_PAGE_FORWARD: + self._view.scroll(gtk.SCROLL_PAGE_FORWARD, horizontal) + elif scrolltype == gtk.SCROLL_STEP_BACKWARD: + self._scroll_step(False, horizontal) + elif scrolltype == gtk.SCROLL_STEP_FORWARD: + self._scroll_step(True, horizontal) + elif scrolltype == gtk.SCROLL_START: + self.set_current_page(0) + elif scrolltype == gtk.SCROLL_END: + self.set_current_page(self._document.get_n_pages()) + else: + print ('Got unsupported scrolltype %s' % str(scrolltype)) + + def _scroll_step(self, forward, horizontal): + if horizontal: + adj = self._activity._scrolled.get_hadjustment() + else: + adj = self._activity._scrolled.get_vadjustment() + value = adj.get_value() + step = adj.get_step_increment() + if forward: + adj.set_value(value + step) + else: + adj.set_value(value - step) + + def copy(self): + self._view.copy() diff --git a/speech.py b/speech.py new file mode 100644 index 0000000..d950fbd --- /dev/null +++ b/speech.py @@ -0,0 +1,44 @@ +# Copyright (C) 2008, 2009 James D. Simmons +# Copyright (C) 2009 Aleksey S. Lim +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + +import logging + +_logger = logging.getLogger('read-etexts-activity') + +supported = True + +try: + import gst + gst.element_factory_make('espeak') + from speech_gst import * + _logger.info('use gst-plugins-espeak') +except Exception, e: + _logger.info('disable gst-plugins-espeak: %s' % e) + try: + from speech_dispatcher import * + _logger.info('use speech-dispatcher') + except Exception, e: + supported = False + _logger.info('disable speech: %s' % e) + +voice = 'default' +pitch = 0 +rate = 0 + +highlight_cb = None +end_text_cb = None +reset_cb = None diff --git a/textadapter.py b/textadapter.py new file mode 100644 index 0000000..526f6f1 --- /dev/null +++ b/textadapter.py @@ -0,0 +1,516 @@ +import os +import zipfile +import logging +import gtk +import pango +import gobject +import threading + +from sugar import mime +from sugar.graphics import style + +import speech + +PAGE_SIZE = 38 + + +class TextViewer(gobject.GObject): + + __gsignals__ = { + 'zoom-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([int])), + 'page-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([int, int])), + 'selection-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), + } + + def setup(self, activity): + self._activity = activity + activity._scrolled = gtk.ScrolledWindow() + activity._scrolled.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) + activity._scrolled.props.shadow_type = gtk.SHADOW_NONE + + self._scrolled = activity._scrolled + + self.textview = gtk.TextView() + self.textview.set_editable(False) + self.textview.set_cursor_visible(False) + self.textview.set_left_margin(50) + self.textview.set_right_margin(50) + self.textview.set_wrap_mode(gtk.WRAP_WORD) + self.textview.connect('button-release-event', \ + self._view_buttonrelease_event_cb) + self.connect('selection-changed', + activity._view_selection_changed_cb) + + activity._scrolled.add(self.textview) + self.textview.show() + activity._scrolled.show() + activity._hbox.pack_start(activity._scrolled, expand=True, fill=True) + + self._font_size = style.zoom(10) + self.font_desc = pango.FontDescription("sans %d" % self._font_size) + self.textview.modify_font(self.font_desc) + self._zoom = 100 + self.font_zoom_relation = self._zoom / self._font_size + self._current_page = 0 + + self.highlight_tag = self.textview.get_buffer().create_tag() + self.highlight_tag.set_property('underline', 'single') + self.highlight_tag.set_property('foreground', 'black') + self.highlight_tag.set_property('background', 'yellow') + + # text to speech initialization + self.current_word = 0 + self.word_tuples = [] + self.spoken_word_tag = self.textview.get_buffer().create_tag() + self.spoken_word_tag.set_property('weight', pango.WEIGHT_BOLD) + self.normal_tag = self.textview.get_buffer().create_tag() + self.normal_tag.set_property('weight', pango.WEIGHT_NORMAL) + + def load_document(self, file_path): + + file_name = file_path.replace('file://', '') + mimetype = mime.get_for_file(file_path) + if mimetype == 'application/zip': + logging.error('opening zip file') + self.zf = zipfile.ZipFile(file_path.replace('file://', ''), 'r') + self.book_files = self.zf.namelist() + extract_path = os.path.join(self._activity.get_activity_root(), + 'instance') + for book_file in self.book_files: + if (book_file != 'annotations.pkl'): + self.zf.extract(book_file, extract_path) + file_name = os.path.join(extract_path, book_file) + + logging.error('opening file_name %s' % file_name) + self._etext_file = open(file_name, 'r') + + self.page_index = [0] + pagecount = 0 + linecount = 0 + while self._etext_file: + line = self._etext_file.readline() + if not line: + break + line_increment = (len(line) / 80) + 1 + linecount = linecount + line_increment + if linecount >= PAGE_SIZE: + position = self._etext_file.tell() + self.page_index.append(position) + linecount = 0 + pagecount = pagecount + 1 + self._pagecount = pagecount + 1 + self.set_current_page(0) + + speech.highlight_cb = self.highlight_next_word + + def _show_page(self, page_number): + position = self.page_index[page_number] + self._etext_file.seek(position) + linecount = 0 + label_text = '\n\n\n' + while linecount < PAGE_SIZE: + line = self._etext_file.readline() + if not line: + break + else: + label_text = label_text + unicode(line, "iso-8859-1") + line_increment = (len(line) / 80) + 1 + linecount = linecount + line_increment + textbuffer = self.textview.get_buffer() + label_text = label_text + '\n\n\n' + textbuffer.set_text(label_text) + self._prepare_text_to_speech(label_text) + + def can_highlight(self): + return True + + def get_selection_bounds(self): + if self.textview.get_buffer().get_selection_bounds(): + begin, end = self.textview.get_buffer().get_selection_bounds() + return [begin.get_offset(), end.get_offset()] + else: + return [] + + def get_cursor_position(self): + insert_mark = self.textview.get_buffer().get_insert() + return self.textview.get_buffer().get_iter_at_mark( \ + insert_mark).get_offset() + + def show_highlights(self, tuples_list): + textbuffer = self.textview.get_buffer() + bounds = textbuffer.get_bounds() + textbuffer.remove_all_tags(bounds[0], bounds[1]) + for highlight_tuple in tuples_list: + iterStart = textbuffer.get_iter_at_offset(highlight_tuple[0]) + iterEnd = textbuffer.get_iter_at_offset(highlight_tuple[1]) + textbuffer.apply_tag(self.highlight_tag, iterStart, iterEnd) + + def connect_page_changed_handler(self, handler): + self.connect('page-changed', handler) + + 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 + marked_up_text = '<speak> ' + while i < len(self.word_tuples): + word_tuple = self.word_tuples[i] + marked_up_text = marked_up_text + '<mark name="' + str(i) + '"/>' \ + + word_tuple[2] + i = i + 1 + print marked_up_text + return marked_up_text + '</speak>' + + 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 highlight_next_word(self, word_count): + if word_count < len(self.word_tuples): + word_tuple = self.word_tuples[word_count] + textbuffer = self.textview.get_buffer() + iterStart = textbuffer.get_iter_at_offset(word_tuple[0]) + iterEnd = textbuffer.get_iter_at_offset(word_tuple[1]) + bounds = textbuffer.get_bounds() + textbuffer.apply_tag(self.normal_tag, bounds[0], iterStart) + textbuffer.apply_tag(self.spoken_word_tag, iterStart, iterEnd) + v_adjustment = self._scrolled.get_vadjustment() + max = v_adjustment.upper - v_adjustment.page_size + max = max * word_count + max = max / len(self.word_tuples) + v_adjustment.value = max + self.current_word = word_count + return True + + def load_metadata(self, activity): + pass + + def set_current_page(self, page): + old_page = self._current_page + self._current_page = page + self._show_page(self._current_page) + self.emit('page-changed', old_page, self._current_page) + + def scroll(self, scrolltype, horizontal): + v_adjustment = self._scrolled.get_vadjustment() + v_value = v_adjustment.value + if scrolltype in (gtk.SCROLL_PAGE_BACKWARD, gtk.SCROLL_PAGE_FORWARD): + step = v_adjustment.page_increment + else: + step = v_adjustment.step_increment + + if scrolltype in (gtk.SCROLL_PAGE_BACKWARD, gtk.SCROLL_STEP_BACKWARD): + if v_value <= v_adjustment.lower: + self.previous_page() + v_adjustment.value = v_adjustment.upper - \ + v_adjustment.page_size + return + if v_value > v_adjustment.lower: + new_value = v_value - step + if new_value < v_adjustment.lower: + new_value = v_adjustment.lower + v_adjustment.value = new_value + elif scrolltype in (gtk.SCROLL_PAGE_FORWARD, gtk.SCROLL_STEP_FORWARD): + if v_value >= v_adjustment.upper - v_adjustment.page_size: + self.next_page() + return + if v_value < v_adjustment.upper - v_adjustment.page_size: + new_value = v_value + step + if new_value > v_adjustment.upper - v_adjustment.page_size: + new_value = v_adjustment.upper - v_adjustment.page_size + v_adjustment.value = new_value + elif scrolltype == gtk.SCROLL_START: + self.set_current_page(0) + elif scrolltype == gtk.SCROLL_END: + self.set_current_page(self._pagecount - 1) + + def previous_page(self): + v_adjustment = self._scrolled.get_vadjustment() + v_adjustment.value = v_adjustment.upper - v_adjustment.page_size + self.set_current_page(self.get_current_page() - 1) + + def next_page(self): + v_adjustment = self._scrolled.get_vadjustment() + v_adjustment.value = v_adjustment.lower + self.set_current_page(self.get_current_page() + 1) + + def get_current_page(self): + return self._current_page + + def get_pagecount(self): + return self._pagecount + + def update_toc(self, activity): + pass + + def handle_link(self, link): + pass + + def get_current_file(self): + pass + + def update_metadata(self, activity): + pass + + def copy(self): + self.textview.get_buffer().copy_clipboard(gtk.Clipboard()) + + def update_view_size(self, _scrolled): + pass + + def _view_buttonrelease_event_cb(self, view, event): + self._has_selection = \ + self.textview.get_buffer().get_selection_bounds() != () + self.emit('selection-changed') + + def get_has_selection(self): + return self._has_selection + + def find_set_highlight_search(self, True): + pass + + def setup_find_job(self, text, _find_updated_cb): + self._find_job = _JobFind(self._etext_file, start_page=0, + n_pages=self._pagecount, + text=text, case_sensitive=False) + self._find_updated_handler = self._find_job.connect('updated', + _find_updated_cb) + return self._find_job, self._find_updated_handler + + def find_next(self): + self._find_job.find_next() + + def find_previous(self): + self._find_job.find_previous() + + def find_changed(self, job, page): + self.set_current_page(job.get_page()) + self._show_found_text(job.get_founded_tuple()) + + def _show_found_text(self, founded_tuple): + textbuffer = self.textview.get_buffer() + tag = textbuffer.create_tag() + tag.set_property('weight', pango.WEIGHT_BOLD) + tag.set_property('foreground', 'white') + tag.set_property('background', 'black') + iterStart = textbuffer.get_iter_at_offset(founded_tuple[1]) + iterEnd = textbuffer.get_iter_at_offset(founded_tuple[2]) + textbuffer.apply_tag(tag, iterStart, iterEnd) + + def get_zoom(self): + return self.font_zoom_relation * self._font_size + + def connect_zoom_handler(self, handler): + self._view_notify_zoom_handler = \ + self.connect('zoom-changed', handler) + return self._view_notify_zoom_handler + + def set_zoom(self, value): + self._zoom = value + self._font_size = int(self._zoom / self.font_zoom_relation) + self.font_desc.set_size(self._font_size * 1024) + self.textview.modify_font(self.font_desc) + + def zoom_in(self): + self._set_font_size(self._font_size + 1) + + def zoom_out(self): + self._set_font_size(self._font_size - 1) + + def _set_font_size(self, size): + self._font_size = size + self.font_desc.set_size(self._font_size * 1024) + self.textview.modify_font(self.font_desc) + self._zoom = self.font_zoom_relation * self._font_size + self.emit('zoom-changed', self._zoom) + + def zoom_to_width(self): + pass + + def can_zoom_in(self): + return True + + def can_zoom_out(self): + return self._font_size > 1 + + def can_zoom_to_width(self): + return False + + def zoom_to_best_fit(self): + return False + + def zoom_to_actual_size(self): + return False + + +class _JobFind(gobject.GObject): + + __gsignals__ = { + 'updated': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + } + + def __init__(self, text_file, start_page, n_pages, text, \ + case_sensitive=False): + gobject.GObject.__init__(self) + gtk.gdk.threads_init() + + self._finished = False + self._text_file = text_file + self._start_page = start_page + self._n_pages = n_pages + self._text = text + self._case_sensitive = case_sensitive + 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_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 + + def find_next(self): + self.threads[-1].find_next() + + def find_previous(self): + self.threads[-1].find_previous() + + def get_page(self): + return self.threads[-1].get_page() + + def get_founded_tuple(self): + return self.threads[-1].get_founded_tuple() + + +class _SearchThread(threading.Thread): + + def __init__(self, obj): + threading.Thread.__init__(self) + self.obj = obj + self.stopthread = threading.Event() + + def _start_search(self): + pagecount = 0 + linecount = 0 + charcount = 0 + self._found_records = [] + self._current_found_item = -1 + self.obj._text_file.seek(0) + while self.obj._text_file: + line = unicode(self.obj._text_file.readline(), "iso-8859-1") + line_length = len(line) + if not line: + break + line_increment = (len(line) / 80) + 1 + linecount = linecount + line_increment + positions = self._allindices(line.lower(), self.obj._text.lower()) + for position in positions: + found_pos = charcount + position + 3 + found_tuple = (pagecount, found_pos, \ + len(self.obj._text) + found_pos) + self._found_records.append(found_tuple) + self._current_found_item = 0 + charcount = charcount + line_length + if linecount >= PAGE_SIZE: + linecount = 0 + charcount = 0 + pagecount = pagecount + 1 + if self._current_found_item == 0: + self.current_found_tuple = \ + self._found_records[self._current_found_item] + self._page = self.current_found_tuple[0] + + gtk.gdk.threads_enter() + self.obj._finished = True + self.obj.emit('updated') + gtk.gdk.threads_leave() + + return False + + def _allindices(self, line, search, listindex=None, offset=0): + if listindex is None: + listindex = [] + if (line.find(search) == -1): + return listindex + else: + offset = line.index(search) + offset + listindex.append(offset) + line = line[(line.index(search) + 1):] + return self._allindices(line, search, listindex, offset + 1) + + def run(self): + self._start_search() + + def stop(self): + self.stopthread.set() + + def find_next(self): + self._current_found_item = self._current_found_item + 1 + if self._current_found_item >= len(self._found_records): + self._current_found_item = len(self._found_records) - 1 + self.current_found_tuple = \ + self._found_records[self._current_found_item] + self._page = self.current_found_tuple[0] + self.obj.emit('updated') + + def find_previous(self): + self._current_found_item = self._current_found_item - 1 + if self._current_found_item <= 0: + self._current_found_item = 0 + self.current_found_tuple = \ + self._found_records[self._current_found_item] + self._page = self.current_found_tuple[0] + self.obj.emit('updated') + + def get_page(self): + return self._page + + def get_founded_tuple(self): + return self.current_found_tuple |