mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
E-book viewer: Implement searching for items in the Table of Contents. Fixes #1374875 [[Enhancement] Search Table of Contents (only) in E-book viewer](https://bugs.launchpad.net/calibre/+bug/1374875)
This commit is contained in:
parent
258954e17b
commit
3c5c5ad8f4
@ -197,6 +197,8 @@ def _config(): # {{{
|
||||
help='Search history for the main GUI')
|
||||
c.add_opt('viewer_search_history', default=[],
|
||||
help='Search history for the ebook viewer')
|
||||
c.add_opt('viewer_toc_search_history', default=[],
|
||||
help='Search history for the ToC in the ebook viewer')
|
||||
c.add_opt('lrf_viewer_search_history', default=[],
|
||||
help='Search history for the LRF viewer')
|
||||
c.add_opt('scheduler_search_history', default=[],
|
||||
|
@ -157,6 +157,7 @@ class ConfigDialog(QDialog, Ui_Dialog):
|
||||
def clear_search_history(self):
|
||||
from calibre.gui2 import config
|
||||
config['viewer_search_history'] = []
|
||||
config['viewer_toc_search_history'] = []
|
||||
|
||||
def save_theme(self):
|
||||
themename, ok = QInputDialog.getText(self, _('Theme name'),
|
||||
|
@ -125,6 +125,7 @@ class EbookViewer(MainWindow):
|
||||
self.search.search.connect(self.find)
|
||||
self.search.focus_to_library.connect(lambda: self.view.setFocus(Qt.OtherFocusReason))
|
||||
self.toc.pressed[QModelIndex].connect(self.toc_clicked)
|
||||
self.toc.searched.connect(partial(self.toc_clicked, force=True))
|
||||
self.reference.goto.connect(self.goto)
|
||||
self.bookmarks.edited.connect(self.bookmarks_edited)
|
||||
self.bookmarks.activated.connect(self.goto_bookmark)
|
||||
|
@ -8,13 +8,19 @@ __copyright__ = '2012, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||
__docformat__ = 'restructuredtext en'
|
||||
|
||||
import re
|
||||
from PyQt5.Qt import (QStandardItem, QStandardItemModel, Qt, QFont,
|
||||
QTreeView)
|
||||
from PyQt5.Qt import (
|
||||
QStandardItem, QStandardItemModel, Qt, QFont, QTreeView, QWidget,
|
||||
QHBoxLayout, QToolButton, QIcon, QModelIndex, pyqtSignal)
|
||||
|
||||
from calibre.ebooks.metadata.toc import TOC as MTOC
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.search_box import SearchBox2
|
||||
from calibre.utils.icu import primary_contains
|
||||
|
||||
class TOCView(QTreeView):
|
||||
|
||||
searched = pyqtSignal(object)
|
||||
|
||||
def __init__(self, *args):
|
||||
QTreeView.__init__(self, *args)
|
||||
self.setMinimumWidth(80)
|
||||
@ -47,6 +53,35 @@ class TOCView(QTreeView):
|
||||
self.unsetCursor()
|
||||
return QTreeView.mouseMoveEvent(self, ev)
|
||||
|
||||
class TOCSearch(QWidget):
|
||||
|
||||
def __init__(self, toc_view, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.toc_view = toc_view
|
||||
self.l = l = QHBoxLayout(self)
|
||||
self.search = s = SearchBox2(self)
|
||||
self.search.setMinimumContentsLength(15)
|
||||
self.search.initialize('viewer_toc_search_history', help_text=_('Search Table of Contents'))
|
||||
self.search.setToolTip(_('Search for text in the Table of Contents'))
|
||||
s.search.connect(self.do_search)
|
||||
self.go = b = QToolButton(self)
|
||||
b.setIcon(QIcon(I('search.png')))
|
||||
b.clicked.connect(s.do_search)
|
||||
b.setToolTip(_('Find next match'))
|
||||
l.addWidget(s), l.addWidget(b)
|
||||
|
||||
def do_search(self, text):
|
||||
if not text or not text.strip():
|
||||
return
|
||||
index = self.toc_view.model().search(text)
|
||||
if index.isValid():
|
||||
self.toc_view.searched.emit(index)
|
||||
else:
|
||||
error_dialog(self.toc_view, _('No matches found'), _(
|
||||
'There are no Table of Contents entries matching: %s') % text, show=True)
|
||||
self.search.search_done(True)
|
||||
|
||||
|
||||
class TOCItem(QStandardItem):
|
||||
|
||||
def __init__(self, spine, toc, depth, all_items, parent=None):
|
||||
@ -65,6 +100,7 @@ class TOCItem(QStandardItem):
|
||||
for t in toc:
|
||||
self.appendRow(TOCItem(spine, t, depth+1, all_items, parent=self))
|
||||
self.setFlags(Qt.ItemIsEnabled)
|
||||
self.is_current_search_result = False
|
||||
spos = 0
|
||||
for i, si in enumerate(spine):
|
||||
if si == self.abspath:
|
||||
@ -201,6 +237,14 @@ class TOCItem(QStandardItem):
|
||||
if changed:
|
||||
self.setFont(self.emphasis_font if is_being_viewed else self.normal_font)
|
||||
|
||||
def set_current_search_result(self, yes):
|
||||
if yes and not self.is_current_search_result:
|
||||
self.setText(self.text() + ' ◄')
|
||||
self.is_current_search_result = True
|
||||
elif not yes and self.is_current_search_result:
|
||||
self.setText(self.text()[:-2])
|
||||
self.is_current_search_result = False
|
||||
|
||||
def __repr__(self):
|
||||
return 'TOC Item: %s %s#%s'%(self.title, self.abspath, self.fragment)
|
||||
|
||||
@ -211,6 +255,7 @@ class TOC(QStandardItemModel):
|
||||
|
||||
def __init__(self, spine, toc=None):
|
||||
QStandardItemModel.__init__(self)
|
||||
self.current_query = {'text':'', 'index':-1, 'items':()}
|
||||
if toc is None:
|
||||
toc = MTOC()
|
||||
self.all_items = depth_first = []
|
||||
@ -269,5 +314,22 @@ class TOC(QStandardItemModel):
|
||||
if item is current_entry:
|
||||
found = True
|
||||
|
||||
def find_items(self, query):
|
||||
for item in self.all_items:
|
||||
if primary_contains(query, item.text()):
|
||||
yield item
|
||||
|
||||
|
||||
def search(self, query):
|
||||
cq = self.current_query
|
||||
if cq['items'] and -1 < cq['index'] < len(cq['items']):
|
||||
cq['items'][cq['index']].set_current_search_result(False)
|
||||
if cq['text'] != query:
|
||||
items = tuple(self.find_items(query))
|
||||
cq.update({'text':query, 'items':items, 'index':-1})
|
||||
if len(cq['items']) > 0:
|
||||
cq['index'] = (cq['index'] + 1) % len(cq['items'])
|
||||
item = cq['items'][cq['index']]
|
||||
item.set_current_search_result(True)
|
||||
index = self.indexFromItem(item)
|
||||
return index
|
||||
return QModelIndex()
|
||||
|
@ -13,14 +13,14 @@ from PyQt5.Qt import (
|
||||
QIcon, QWidget, Qt, QGridLayout, QScrollBar, QToolBar, QAction,
|
||||
QToolButton, QMenu, QDoubleSpinBox, pyqtSignal, QLineEdit,
|
||||
QRegExpValidator, QRegExp, QPalette, QColor, QBrush, QPainter,
|
||||
QDockWidget, QSize, QWebView, QLabel)
|
||||
QDockWidget, QSize, QWebView, QLabel, QVBoxLayout)
|
||||
|
||||
from calibre.gui2 import rating_font, workaround_broken_under_mouse
|
||||
from calibre.gui2.main_window import MainWindow
|
||||
from calibre.gui2.search_box import SearchBox2
|
||||
from calibre.gui2.viewer.documentview import DocumentView
|
||||
from calibre.gui2.viewer.bookmarkmanager import BookmarkManager
|
||||
from calibre.gui2.viewer.toc import TOCView
|
||||
from calibre.gui2.viewer.toc import TOCView, TOCSearch
|
||||
|
||||
class DoubleSpinBox(QDoubleSpinBox): # {{{
|
||||
|
||||
@ -236,9 +236,13 @@ class Main(MainWindow):
|
||||
self.tool_bar2.addWidget(self.search)
|
||||
|
||||
self.toc_dock = d = QDockWidget(_('Table of Contents'), self)
|
||||
self.toc = TOCView(self)
|
||||
self.toc_container = w = QWidget(self)
|
||||
w.l = QVBoxLayout(w)
|
||||
self.toc = TOCView(w)
|
||||
self.toc_search = TOCSearch(self.toc, parent=w)
|
||||
w.l.addWidget(self.toc), w.l.addWidget(self.toc_search), w.l.setContentsMargins(0, 0, 0, 0)
|
||||
d.setObjectName('toc-dock')
|
||||
d.setWidget(self.toc)
|
||||
d.setWidget(w)
|
||||
d.close() # starts out hidden
|
||||
self.addDockWidget(Qt.LeftDockWidgetArea, d)
|
||||
d.setAllowedAreas(Qt.LeftDockWidgetArea | Qt.RightDockWidgetArea)
|
||||
|
Loading…
x
Reference in New Issue
Block a user