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:
Kovid Goyal 2014-10-04 19:13:19 +05:30
parent 258954e17b
commit 3c5c5ad8f4
5 changed files with 77 additions and 7 deletions

View File

@ -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=[],

View File

@ -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'),

View File

@ -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)

View File

@ -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()

View File

@ -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)