diff --git a/TODO b/TODO
index da9eeb8..5c1b1f3 100644
--- a/TODO
+++ b/TODO
@@ -62,6 +62,7 @@ TODO
✓ Make the bookmark dock float over the reading area
✓ Spacebar should not cut off lines at the top
✓ Track open bookmark windows so they can be closed quickly at exit
+ ✓ Search document using QTextCursor
Double page / column view
✓ For comics
Caching is currently non fuctional
@@ -70,7 +71,6 @@ TODO
Annotation preview in listView
Disable buttons for annotations, search in images
Adjust key navigation according to viewport dimensions
- Search document using QTextCursor
Redo context menu order
Filetypes:
✓ pdf support
diff --git a/lector/__main__.py b/lector/__main__.py
index 7f0f80d..7c14d8a 100755
--- a/lector/__main__.py
+++ b/lector/__main__.py
@@ -643,6 +643,14 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
if self.bookToolBar.fontButton.isChecked():
self.bookToolBar.customize_view_on()
+ # Disable irrelevant buttons in image view mode
+ if current_tab.are_we_doing_images_only:
+ self.bookToolBar.searchButton.setEnabled(False)
+ self.bookToolBar.annotationButton.setEnabled(False)
+ else:
+ self.bookToolBar.searchButton.setEnabled(True)
+ self.bookToolBar.annotationButton.setEnabled(True)
+
current_position = current_metadata['position']
current_toc = [i[0] for i in current_metadata['content']]
diff --git a/lector/threaded.py b/lector/threaded.py
index 270f972..79669ac 100644
--- a/lector/threaded.py
+++ b/lector/threaded.py
@@ -15,6 +15,7 @@
# along with this program. If not, see .
import os
+import re
import pathlib
from multiprocessing.dummy import Pool
@@ -217,18 +218,25 @@ class BackGroundTextSearch(QtCore.QThread):
surroundingTextCursor = QtGui.QTextCursor(chapterDocument)
surroundingTextCursor.setPosition(
result_position, QtGui.QTextCursor.MoveAnchor)
+ # Get words before and after
surroundingTextCursor.movePosition(
- QtGui.QTextCursor.WordLeft, QtGui.QTextCursor.MoveAnchor, 2)
+ QtGui.QTextCursor.WordLeft, QtGui.QTextCursor.MoveAnchor, 3)
surroundingTextCursor.movePosition(
- QtGui.QTextCursor.NextWord, QtGui.QTextCursor.KeepAnchor, 5) # 2n + 1
+ QtGui.QTextCursor.NextWord, QtGui.QTextCursor.KeepAnchor, 6)
surrounding_text = surroundingTextCursor.selection().toPlainText()
surrounding_text = surrounding_text.replace('\n', ' ')
+ # Case insensitive replace for find results
+ replace_pattern = re.compile(re.escape(self.search_text), re.IGNORECASE)
+ surrounding_text = replace_pattern.sub(
+ f'{self.search_text}', surrounding_text)
+
try:
self.search_results[chapter].append(
- (result_position, surrounding_text))
+ (result_position, surrounding_text, self.search_text))
except KeyError:
- self.search_results[chapter] = [(result_position, surrounding_text)]
+ self.search_results[chapter] = [
+ (result_position, surrounding_text, self.search_text)]
new_position = result_position + len(self.search_text)
findResultCursor = chapterDocument.find(
diff --git a/lector/toolbars.py b/lector/toolbars.py
index d34468d..76beee0 100644
--- a/lector/toolbars.py
+++ b/lector/toolbars.py
@@ -74,12 +74,12 @@ class BookToolBar(QtWidgets.QToolBar):
self.fontButton.setCheckable(True)
self.fontButton.triggered.connect(self.toggle_font_settings)
self.bookSeparator1 = self.addSeparator()
- self.addAction(self.searchButton)
- self.bookSeparator2 = self.addSeparator()
self.addAction(self.annotationButton)
- self.bookSeparator3 = self.addSeparator()
+ self.bookSeparator2 = self.addSeparator()
self.addAction(self.addBookmarkButton)
self.addAction(self.bookmarkButton)
+ self.bookSeparator3 = self.addSeparator()
+ self.addAction(self.searchButton)
self.bookSeparator4 = self.addSeparator()
self.addAction(self.distractionFreeButton)
self.addAction(self.fullscreenButton)
@@ -307,9 +307,11 @@ class BookToolBar(QtWidgets.QToolBar):
self.annotationButton,
self.addBookmarkButton,
self.bookmarkButton,
+ self.searchButton,
self.distractionFreeButton,
self.fullscreenButton,
self.tocBoxAction,
+ self.bookSeparator1,
self.bookSeparator2,
self.bookSeparator3,
self.bookSeparator4]
diff --git a/lector/widgets.py b/lector/widgets.py
index 1c3e9b4..a93ef04 100644
--- a/lector/widgets.py
+++ b/lector/widgets.py
@@ -130,18 +130,18 @@ class Tab(QtWidgets.QWidget):
self.sideDock.setWidget(self.sideDockTabWidget)
# Annotation list view and model
- self.annotationListView = QtWidgets.QListView(self.sideDockTabWidget)
- # self.annotationListView.setResizeMode(QtWidgets.QListWidget.Adjust)
+ self.annotationListView = QtWidgets.QListView()
self.annotationListView.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
self.annotationListView.doubleClicked.connect(self.contentView.toggle_annotation_mode)
annotations_string = self._translate('Tab', 'Annotations')
- self.sideDockTabWidget.addTab(self.annotationListView, annotations_string)
+ if not self.are_we_doing_images_only:
+ self.sideDockTabWidget.addTab(self.annotationListView, annotations_string)
self.annotationModel = QtGui.QStandardItemModel(self)
self.generate_annotation_model()
# Bookmark tree view and model
- self.bookmarkTreeView = QtWidgets.QTreeView(self.sideDockTabWidget)
+ self.bookmarkTreeView = QtWidgets.QTreeView()
self.bookmarkTreeView.setHeaderHidden(True)
self.bookmarkTreeView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.bookmarkTreeView.customContextMenuRequested.connect(
@@ -155,14 +155,14 @@ class Tab(QtWidgets.QWidget):
self.generate_bookmark_model()
# Search view and model
- self.searchLineEdit = QtWidgets.QLineEdit(self.sideDockTabWidget)
+ self.searchLineEdit = QtWidgets.QLineEdit()
self.searchLineEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
self.searchLineEdit.setClearButtonEnabled(True)
search_string = self._translate('Tab', 'Search')
self.searchLineEdit.setPlaceholderText(search_string)
search_book_string = self._translate('Tab', 'Search entire book')
- self.searchBookButton = QtWidgets.QToolButton(self.sideDockTabWidget)
+ self.searchBookButton = QtWidgets.QToolButton()
self.searchBookButton.setIcon(
self.main_window.QImageFactory.get_image('view-readermode'))
self.searchBookButton.setToolTip(search_book_string)
@@ -170,7 +170,7 @@ class Tab(QtWidgets.QWidget):
self.searchBookButton.setAutoRaise(True)
case_sensitive_string = self._translate('Tab', 'Match case')
- self.caseSensitiveSearchButton = QtWidgets.QToolButton(self.sideDockTabWidget)
+ self.caseSensitiveSearchButton = QtWidgets.QToolButton()
self.caseSensitiveSearchButton.setIcon(
self.main_window.QImageFactory.get_image('search-case'))
self.caseSensitiveSearchButton.setToolTip(case_sensitive_string)
@@ -178,7 +178,7 @@ class Tab(QtWidgets.QWidget):
self.caseSensitiveSearchButton.setAutoRaise(True)
match_word_string = self._translate('Tab', 'Match word')
- self.matchWholeWordButton = QtWidgets.QToolButton(self.sideDockTabWidget)
+ self.matchWholeWordButton = QtWidgets.QToolButton()
self.matchWholeWordButton.setIcon(
self.main_window.QImageFactory.get_image('search-word'))
self.matchWholeWordButton.setToolTip(match_word_string)
@@ -192,19 +192,20 @@ class Tab(QtWidgets.QWidget):
self.searchOptionsLayout.addWidget(self.caseSensitiveSearchButton)
self.searchOptionsLayout.addWidget(self.matchWholeWordButton)
- self.searchResultsTreeView = QtWidgets.QTreeView(self.sideDockTabWidget)
+ self.searchResultsTreeView = QtWidgets.QTreeView()
self.searchResultsTreeView.setHeaderHidden(True)
self.searchResultsTreeView.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
self.searchResultsTreeView.clicked.connect(self.navigate_to_search_result)
- self.searchTabLayout = QtWidgets.QVBoxLayout(self.sideDockTabWidget)
+ self.searchTabLayout = QtWidgets.QVBoxLayout()
self.searchTabLayout.addLayout(self.searchOptionsLayout)
self.searchTabLayout.addWidget(self.searchResultsTreeView)
self.searchTabLayout.setContentsMargins(0, 0, 0, 0)
- self.searchTabWidget = QtWidgets.QWidget(self.sideDockTabWidget)
+ self.searchTabWidget = QtWidgets.QWidget()
self.searchTabWidget.setLayout(self.searchTabLayout)
- self.sideDockTabWidget.addTab(self.searchTabWidget, search_string)
+ if not self.are_we_doing_images_only:
+ self.sideDockTabWidget.addTab(self.searchTabWidget, search_string)
# Create the annotation notes dock
self.annotationNoteDock = PliantDockWidget(self.main_window, True, self.contentView)
@@ -241,6 +242,8 @@ class Tab(QtWidgets.QWidget):
self.searchTimer.setSingleShot(True)
self.searchTimer.timeout.connect(self.set_search_options)
+ self.searchLineEdit.textChanged.connect(
+ lambda: self.searchLineEdit.setStyleSheet(QtWidgets.QLineEdit.styleSheet(self)))
self.searchLineEdit.textChanged.connect(lambda: self.searchTimer.start(500))
self.searchBookButton.clicked.connect(lambda: self.searchTimer.start(100))
self.caseSensitiveSearchButton.clicked.connect(lambda: self.searchTimer.start(100))
@@ -705,23 +708,46 @@ class Tab(QtWidgets.QWidget):
search_results = self.searchThread.search_results
for i in search_results:
parentItem = QtGui.QStandardItem()
- parentItem.setText(i)
- parentItem.setData(True, QtCore.Qt.UserRole)
+ parentItem.setData(True, QtCore.Qt.UserRole) # Is parent?
+ parentItem.setData(i, QtCore.Qt.UserRole + 3) # Display text for label
chapter_index = self.main_window.bookToolBar.tocBox.findText(
i, QtCore.Qt.MatchExactly)
for j in search_results[i]:
- childItem = QtGui.QStandardItem()
- childItem.setText(j[1])
+ childItem = QtGui.QStandardItem(parentItem)
childItem.setData(False, QtCore.Qt.UserRole) # Is parent?
childItem.setData(chapter_index, QtCore.Qt.UserRole + 1) # Chapter index
childItem.setData(j[0], QtCore.Qt.UserRole + 2) # Cursor Position
+ childItem.setData(j[1], QtCore.Qt.UserRole + 3) # Display text for label
+ childItem.setData(j[2], QtCore.Qt.UserRole + 4) # Search term
parentItem.appendRow(childItem)
self.searchResultsModel.appendRow(parentItem)
self.searchResultsTreeView.setModel(self.searchResultsModel)
self.searchResultsTreeView.expandToDepth(1)
+ if not search_results and len(self.searchLineEdit.text()) > 2:
+ self.searchLineEdit.setStyleSheet("QLineEdit {color: red;}")
+
+ # We'll be putting in labels instead of making a delegate
+ # QLabels can understand RTF, and they also have the somewhat
+ # distinct advantage of being a lot less work than a delegate
+
+ def generate_label(index):
+ label_text = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 3)
+ labelWidget = PliantLabelWidget(index, self.navigate_to_search_result)
+ labelWidget.setText(label_text)
+ self.searchResultsTreeView.setIndexWidget(index, labelWidget)
+
+ for parent_iter in range(self.searchResultsModel.rowCount()):
+ parentItem = self.searchResultsModel.item(parent_iter)
+ parentIndex = self.searchResultsModel.index(parent_iter, 0)
+ generate_label(parentIndex)
+
+ for child_iter in range(parentItem.rowCount()):
+ childIndex = self.searchResultsModel.index(child_iter, 0, parentIndex)
+ generate_label(childIndex)
+
def navigate_to_search_result(self, index):
if not index.isValid():
return
@@ -732,11 +758,12 @@ class Tab(QtWidgets.QWidget):
chapter_index = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 1)
cursor_position = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 2)
+ search_term = self.searchResultsModel.data(index, QtCore.Qt.UserRole + 4)
self.main_window.bookToolBar.tocBox.setCurrentIndex(chapter_index)
if not self.are_we_doing_images_only:
self.set_cursor_position(
- cursor_position, len(self.searchLineEdit.text()))
+ cursor_position, len(search_term))
def hide_mouse(self):
self.contentView.viewport().setCursor(QtCore.Qt.BlankCursor)
@@ -754,6 +781,19 @@ class Tab(QtWidgets.QWidget):
self.main_window.closeEvent()
+class PliantLabelWidget(QtWidgets.QLabel):
+ # This is a hack to get clickable / editable appearance
+ # search results in the tree view.
+
+ def __init__(self, index, navigate_to_search_result):
+ super(PliantLabelWidget, self).__init__()
+ self.index = index
+ self.navigate_to_search_result = navigate_to_search_result
+
+ def mousePressEvent(self, QMouseEvent):
+ self.navigate_to_search_result(self.index)
+ QtWidgets.QLabel.mousePressEvent(self, QMouseEvent)
+
class PliantDockWidget(QtWidgets.QDockWidget):
def __init__(self, main_window, notes_only, contentView, parent=None):
super(PliantDockWidget, self).__init__()