From f054562ecbd678502091dc07a981f14e42ceee8d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 21 Jan 2015 12:53:08 +0530 Subject: [PATCH] Implement double clicking on image to jump to its references --- src/calibre/ebooks/oeb/polish/report.py | 14 ++++---- src/calibre/gui2/tweak_book/boss.py | 6 ++-- src/calibre/gui2/tweak_book/reports.py | 43 +++++++++++++++++++++++-- 3 files changed, 49 insertions(+), 14 deletions(-) diff --git a/src/calibre/ebooks/oeb/polish/report.py b/src/calibre/ebooks/oeb/polish/report.py index a93cc163b8..ca7fd16e62 100644 --- a/src/calibre/ebooks/oeb/polish/report.py +++ b/src/calibre/ebooks/oeb/polish/report.py @@ -55,15 +55,15 @@ def file_data(container): yield File(name, posixpath.dirname(name), posixpath.basename(name), safe_size(container, name), get_category(name, container.mime_map.get(name, ''))) -Image = namedtuple('Image', 'name mime_type usage size basename width height') +Image = namedtuple('Image', 'name mime_type usage size basename id width height') -L = namedtuple('Location', 'name line_number offset word') -def Location(name, line_number=None, offset=0, word=None): - return L(name, line_number, offset, word) +L = namedtuple('Location', 'name line_number text_on_line word_on_line character_offset') +def Location(name, line_number=None, text_on_line=None, word_on_line=None, character_offset=None): + return L(name, line_number, text_on_line, word_on_line, character_offset) def sort_locations(locations): def sort_key(l): - return (numeric_sort_key(l.name), l.line_number, l.offset, l.word) + return (numeric_sort_key(l.name), l.line_number, l.character_offset) return sorted(locations, key=sort_key) def link_data(container): @@ -76,13 +76,13 @@ def link_data(container): if target and container.exists(target): mt = container.mime_map.get(target) if mt and mt.startswith('image/'): - image_usage[target].add(Location(name, line_number, offset)) + image_usage[target].add(Location(name, line_number, text_on_line=href)) image_data = [] for name, mt in container.mime_map.iteritems(): if mt.startswith('image/') and container.exists(name): image_data.append(Image(name, mt, sort_locations(image_usage.get(name, set())), safe_size(container, name), - posixpath.basename(name), *safe_img_data(container, name, mt))) + posixpath.basename(name), len(image_data), *safe_img_data(container, name, mt))) return tuple(image_data) def gather_data(container): diff --git a/src/calibre/gui2/tweak_book/boss.py b/src/calibre/gui2/tweak_book/boss.py index 6215016aab..6023290a5d 100644 --- a/src/calibre/gui2/tweak_book/boss.py +++ b/src/calibre/gui2/tweak_book/boss.py @@ -1162,9 +1162,7 @@ class Boss(QObject): def reports_edit_requested(self, location): name = location.name mt = current_container().mime_map.get(name, guess_type(name)) - editor = self.edit_file_requested(name, None, mt) - if editor is not None: - pass + self.edit_file_requested(name, None, mt) def image_activated(self, name): mt = current_container().mime_map.get(name, guess_type(name)) @@ -1250,7 +1248,7 @@ class Boss(QObject): def edit_file_requested(self, name, syntax, mime): if name in editors: self.gui.central.show_editor(editors[name]) - return + return editors[name] syntax = syntax or syntax_from_mime(name, mime) if not syntax: return error_dialog( diff --git a/src/calibre/gui2/tweak_book/reports.py b/src/calibre/gui2/tweak_book/reports.py index f318fcc48b..00f95cba01 100644 --- a/src/calibre/gui2/tweak_book/reports.py +++ b/src/calibre/gui2/tweak_book/reports.py @@ -10,7 +10,9 @@ from threading import Thread from future_builtins import map from operator import itemgetter from functools import partial +from collections import defaultdict +import regex from PyQt5.Qt import ( QSize, QStackedLayout, QLabel, QVBoxLayout, Qt, QWidget, pyqtSignal, QAbstractTableModel, QTableView, QSortFilterProxyModel, QIcon, QListWidget, @@ -18,6 +20,7 @@ from PyQt5.Qt import ( QStyledItemDelegate, QModelIndex, QRect, QStyle, QPalette, QTimer, QMenu) from calibre import human_readable, fit_image +from calibre.ebooks.oeb.polish.container import guess_type from calibre.ebooks.oeb.polish.report import gather_data, Location from calibre.gui2 import error_dialog, question_dialog from calibre.gui2.tweak_book import current_container, tprefs @@ -254,6 +257,39 @@ class FilesWidget(QWidget): # }}} +class Jump(object): # {{{ + + def __init__(self): + self.pos_map = defaultdict(lambda : -1) + + def clear(self): + self.pos_map.clear() + + def __call__(self, key, locations): + self.pos_map[key] = (self.pos_map[key] + 1) % len(locations) + loc = locations[self.pos_map[key]] + from calibre.gui2.tweak_book.boss import get_boss + boss = get_boss() + if boss is None: + return + name = loc.name + mt = current_container().mime_map.get(name, guess_type(name)) + editor = boss.edit_file_requested(name, None, mt) + if editor is None: + return + editor = editor.editor + if loc.line_number is not None: + block = editor.document().findBlockByNumber(loc.line_number - 1) # blockNumber() is zero based + if not block.isValid(): + return + c = editor.textCursor() + c.setPosition(block.position(), c.MoveAnchor) + editor.setTextCursor(c) + if loc.text_on_line is not None: + editor.find(regex.compile(regex.escape(loc.text_on_line))) + +jump = Jump() # }}} + # Images {{{ class ImagesDelegate(QStyledItemDelegate): @@ -384,9 +420,9 @@ class ImagesWidget(QWidget): QTimer.singleShot(0, self.files.resizeRowsToContents) def double_clicked(self, index): - location = self.model.location(index) - if location is not None: - self.edit_requested.emit(location) + entry = index.data(Qt.UserRole) + if entry is not None: + jump((id(self), entry.id), entry.usage) def customize_context_menu(self, menu, selected_locations, current_location): if current_location is not None: @@ -436,6 +472,7 @@ class ReportsWidget(QWidget): self.reports.setCurrentRow(current_page) def __call__(self, data): + jump.clear() self.files(data) self.images(data)