diff --git a/src/libprs500/gui2/lrf_renderer/document.py b/src/libprs500/gui2/lrf_renderer/document.py index c369176132..2fe4533d20 100644 --- a/src/libprs500/gui2/lrf_renderer/document.py +++ b/src/libprs500/gui2/lrf_renderer/document.py @@ -14,7 +14,7 @@ ## 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. '''''' -import collections +import collections, itertools from PyQt4.QtCore import Qt, QByteArray, SIGNAL from PyQt4.QtGui import QGraphicsRectItem, QGraphicsScene, QPen, \ @@ -127,7 +127,7 @@ class _Canvas(QGraphicsRectItem): if isinstance(line, QGraphicsItem): line.setParentItem(self) line.setPos(x + line.getx(textwidth), y) - y += line.height + y += line.height + line.line_space else: y += line.height if not block.has_content: @@ -165,6 +165,18 @@ class _Canvas(QGraphicsRectItem): self.is_full = y > self.max_y-5 ib.has_content = False + def search(self, phrase): + matches = [] + for child in self.children(): + if hasattr(child, 'search'): + res = child.search(phrase) + if res: + if isinstance(res, list): + matches += res + else: + matches.append(res) + return matches + class Canvas(_Canvas, ContentObject): @@ -273,6 +285,7 @@ class Page(_Canvas): def add_block(self, block): self.layout_block(block, 0, self.current_y) + class Chapter(object): @@ -291,6 +304,15 @@ class Chapter(object): def screen(self, odd): return self.oddscreen if odd else self.evenscreen + + def search(self, phrase): + pages = [] + for i in range(len(self.pages)): + matches = self.pages[i].search(phrase) + if matches: + pages.append([i, matches]) + return pages + class History(collections.deque): @@ -331,6 +353,9 @@ class Document(QGraphicsScene): self.link_map = {} self.chapter_map = {} self.history = History() + self.last_search = iter([]) + if not opts.white_background: + self.setBackgroundBrush(QBrush(QColor(0xee, 0xee, 0xee))) def page_of(self, oid): for chapter in self.chapters: @@ -492,3 +517,22 @@ class Document(QGraphicsScene): def show_page_at_percent(self, p): num = self.num_of_pages*(p/100.) self.show_page(num) + + def search(self, phrase): + if not phrase: + return + matches = [] + for i in range(len(self.chapters)): + cmatches = self.chapters[i].search(phrase) + for match in cmatches: + match[0] += sum(self.chapter_layout[:i])+1 + matches += cmatches + self.last_search = itertools.cycle(matches) + self.next_match() + + def next_match(self): + page_num = self.last_search.next()[0] + if self.current_page == page_num: + self.update() + self.show_page(page_num) + \ No newline at end of file diff --git a/src/libprs500/gui2/lrf_renderer/main.py b/src/libprs500/gui2/lrf_renderer/main.py index 3642148013..fc84bad006 100644 --- a/src/libprs500/gui2/lrf_renderer/main.py +++ b/src/libprs500/gui2/lrf_renderer/main.py @@ -22,7 +22,7 @@ from PyQt4.QtCore import Qt, QObject, SIGNAL, QCoreApplication, QThread from libprs500 import __appname__, __version__, __author__, setup_cli_handlers, islinux from libprs500.ebooks.lrf.parser import LRFDocument -from libprs500.gui2 import ORG_NAME, APP_UID +from libprs500.gui2 import ORG_NAME, APP_UID, error_dialog from libprs500.gui2.dialogs.conversion_error import ConversionErrorDialog from libprs500.gui2.lrf_renderer.main_ui import Ui_MainWindow from libprs500.gui2.main_window import MainWindow @@ -65,6 +65,8 @@ class Main(QObject, Ui_MainWindow, MainWindow): self.search.help_text = 'Search' self.search.clear_to_help() + QObject.connect(self.search, SIGNAL('search(PyQt_PyObject, PyQt_PyObject)'), self.find) + self.last_search = None self.action_next_page.setShortcuts(QKeySequence.MoveToNextPage) self.action_previous_page.setShortcuts(QKeySequence.MoveToPreviousPage) @@ -91,6 +93,12 @@ class Main(QObject, Ui_MainWindow, MainWindow): self.stack.setCurrentIndex(1) self.renderer.start() + def find(self, search, refinement): + self.last_search = search + try: + self.document.search(search) + except StopIteration: + error_dialog(self.window, 'No matches found', 'No matches for the search phrase %s were found.'%(search,)).exec_() def parsed(self, *args): if self.renderer.lrf is not None: @@ -100,13 +108,13 @@ class Main(QObject, Ui_MainWindow, MainWindow): self.document_title = self.renderer.lrf.metadata.title if self.opts.profile: import cProfile - render, lrf = self.document.render, self.renderer.lrf - cProfile.runctx('render(lrf)', globals(), locals(), lrf.metadata.title+'.stats') + lrf = self.renderer.lrf + cProfile.runctx('self.document.render(lrf)', globals(), locals(), lrf.metadata.title+'.stats') print 'Stats written to', self.renderer.lrf.metadata.title+'.stats' else: start = time.time() self.document.render(self.renderer.lrf) - print 'Rendering time:', time.time()-start, 'seconds' + print 'Layout time:', time.time()-start, 'seconds' self.renderer.lrf = None self.graphics_view.setScene(self.document) self.graphics_view.show() @@ -132,7 +140,7 @@ class Main(QObject, Ui_MainWindow, MainWindow): self.progress_bar.setMinimum(0) self.progress_bar.setMaximum(num) self.progress_bar.setValue(0) - self.progress_label.setText('Rendering '+ self.document_title) + self.progress_label.setText('Laying out '+ self.document_title) else: self.progress_bar.setValue(self.progress_bar.value()+1) QCoreApplication.processEvents() @@ -171,6 +179,8 @@ def option_parser(): default=False, action='store_true', dest='visual_debug') parser.add_option('--disable-hyphenation', dest='hyphenate', default=True, action='store_false', help='Disable hyphenation. Should significantly speed up rendering.') + parser.add_option('--white-background', dest='white_background', default=False, action='store_true', + help='By default the background is off white as I find this easier on the eyes. Use this option to make the background pure white.') parser.add_option('--profile', dest='profile', default=False, action='store_true', help='Profile the LRF renderer') return parser diff --git a/src/libprs500/gui2/lrf_renderer/text.py b/src/libprs500/gui2/lrf_renderer/text.py index cd022b7aac..14f64d2dfb 100644 --- a/src/libprs500/gui2/lrf_renderer/text.py +++ b/src/libprs500/gui2/lrf_renderer/text.py @@ -317,23 +317,25 @@ class TextBlock(object): return s class Link(QGraphicsRectItem): - inactive_brush = QBrush(QColor(0x00, 0x00, 0x00, 0x09)) + inactive_brush = QBrush(QColor(0xff, 0xff, 0xff, 0xff)) active_brush = QBrush(QColor(0x00, 0x00, 0x00, 0x59)) def __init__(self, parent, start, stop, refobj, slot): QGraphicsRectItem.__init__(self, start, 0, stop-start, parent.height, parent) self.refobj = refobj self.slot = slot - self.setBrush(self.__class__.inactive_brush) + self.brush = self.__class__.inactive_brush self.setPen(QPen(Qt.NoPen)) self.setCursor(Qt.PointingHandCursor) self.setAcceptsHoverEvents(True) def hoverEnterEvent(self, event): - self.setBrush(self.__class__.active_brush) + self.brush = self.__class__.active_brush + self.parentItem().update() def hoverLeaveEvent(self, event): - self.setBrush(self.__class__.inactive_brush) + self.brush = self.__class__.inactive_brush + self.parentItem().update() def mousePressEvent(self, event): self.hoverLeaveEvent(None) @@ -371,6 +373,7 @@ class Line(QGraphicsItem): self.tokens.append(plot) self.current_width += plot.width self.height = max(self.height, plot.height) + self.add_space(6) def populate(self, phrase, ts, process_space=True): phrase_pos = 0 @@ -446,7 +449,6 @@ class Line(QGraphicsItem): self.width = float(self.current_width) if self.height == 0: self.height = baselineskip - self.height += linespace self.height = float(self.height) self.vdebug = vdebug @@ -470,12 +472,24 @@ class Line(QGraphicsItem): painter.drawRect(self.boundingRect()) painter.restore() painter.save() + painter.setPen(QPen(Qt.NoPen)) + for c in self.children(): + painter.setBrush(c.brush) + painter.drawRect(c.boundingRect()) + painter.restore() + painter.save() for tok in self.tokens: if isinstance(tok, (int, float)): x += tok elif isinstance(tok, Word): painter.setFont(tok.font) p = painter.pen() + if tok.highlight: + painter.save() + painter.setPen(QPen(Qt.NoPen)) + painter.setBrush(QBrush(Qt.yellow)) + painter.drawRect(x, 0, tok.width, tok.height) + painter.restore() painter.setPen(QPen(tok.text_color)) painter.drawText(x, y, tok.string) painter.setPen(p) @@ -485,6 +499,30 @@ class Line(QGraphicsItem): x += tok.width painter.restore() + def search(self, phrase): + tokens = phrase.lower().split() + if len(tokens) < 1: return None + for i in range(len(self.tokens)): + if not isinstance(self.tokens[i], Word): + continue + src = qstring_to_unicode(self.tokens[i].string).lower() + self.tokens[i].highlight = False + if tokens[0] in src: + if len(tokens) == 1: + self.tokens[i].highlight = True + return self + else: + match = True + for j in range(len(tokens)): + if i+j >= len(self.tokens) or tokens[j].lower() != qstring_to_unicode(self.tokens[i+j].string).lower(): + match = False + break + self.tokens[i+j].highlight = True + if match: + return self + return None + + def getx(self, textwidth): if self.align == 'head': return self.offset @@ -497,7 +535,7 @@ class Line(QGraphicsItem): s = u'' for tok in self.tokens: if isinstance(tok, (int, float)): - s += ' :%.1f: '%(tok,) + s += ' ' elif isinstance(tok, Word): s += qstring_to_unicode(tok.string) return s @@ -512,6 +550,7 @@ class Word(object): self.string, self.width, self.height = QString(string), width, height self.font = font self.text_color = ts.textcolor + self.highlight = False def main(args=sys.argv): return 0