Refactor sideDock
This commit is contained in:
@@ -462,7 +462,8 @@ class MainUI(QtWidgets.QMainWindow, mainwindow.Ui_MainWindow):
|
|||||||
|
|
||||||
def add_bookmark(self):
|
def add_bookmark(self):
|
||||||
if self.tabWidget.currentIndex() != 0:
|
if self.tabWidget.currentIndex() != 0:
|
||||||
self.tabWidget.widget(self.tabWidget.currentIndex()).add_bookmark()
|
current_tab = self.tabWidget.currentWidget()
|
||||||
|
current_tab.sideDock.bookmarks.add_bookmark()
|
||||||
|
|
||||||
def resizeEvent(self, event=None):
|
def resizeEvent(self, event=None):
|
||||||
if event:
|
if event:
|
||||||
|
@@ -482,15 +482,15 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
|
|||||||
self.parent.sideDock.setWindowOpacity(.95)
|
self.parent.sideDock.setWindowOpacity(.95)
|
||||||
|
|
||||||
self.current_annotation = None
|
self.current_annotation = None
|
||||||
self.parent.annotationListView.clearSelection()
|
self.parent.sideDock.annotations.annotationListView.clearSelection()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.annotation_mode = True
|
self.annotation_mode = True
|
||||||
self.viewport().setCursor(QtCore.Qt.IBeamCursor)
|
self.viewport().setCursor(QtCore.Qt.IBeamCursor)
|
||||||
self.parent.sideDock.hide()
|
self.parent.sideDock.hide()
|
||||||
|
|
||||||
selected_index = self.parent.annotationListView.currentIndex()
|
selected_index = self.parent.sideDock.annotations.annotationListView.currentIndex()
|
||||||
self.current_annotation = self.parent.annotationModel.data(
|
self.current_annotation = self.parent.sideDock.annotationModel.data(
|
||||||
selected_index, QtCore.Qt.UserRole)
|
selected_index, QtCore.Qt.UserRole)
|
||||||
logger.info('Selected annotation: ' + self.current_annotation['name'])
|
logger.info('Selected annotation: ' + self.current_annotation['name'])
|
||||||
|
|
||||||
@@ -619,14 +619,14 @@ class PliantQTextBrowser(QtWidgets.QTextBrowser):
|
|||||||
action = contextMenu.exec_(self.sender().mapToGlobal(position))
|
action = contextMenu.exec_(self.sender().mapToGlobal(position))
|
||||||
|
|
||||||
if action == addBookMarkAction:
|
if action == addBookMarkAction:
|
||||||
self.parent.add_bookmark(cursor_at_mouse.position())
|
self.parent.sideDock.bookmarks.add_bookmark(cursor_at_mouse.position())
|
||||||
|
|
||||||
if action == defineAction:
|
if action == defineAction:
|
||||||
self.main_window.definitionDialog.find_definition(selection)
|
self.main_window.definitionDialog.find_definition(selection)
|
||||||
|
|
||||||
if action == searchAction:
|
if action == searchAction:
|
||||||
if selection and selection != '':
|
if selection and selection != '':
|
||||||
self.parent.searchLineEdit.setText(selection)
|
self.parent.sideDock.search.searchLineEdit.setText(selection)
|
||||||
self.parent.toggle_side_dock(2, True)
|
self.parent.toggle_side_dock(2, True)
|
||||||
|
|
||||||
if action == searchGoogleAction:
|
if action == searchGoogleAction:
|
||||||
|
@@ -14,9 +14,12 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import uuid
|
||||||
|
|
||||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||||
|
|
||||||
from lector.models import BookmarkProxyModel
|
from lector.models import BookmarkProxyModel
|
||||||
|
from lector.threaded import BackGroundTextSearch
|
||||||
|
|
||||||
|
|
||||||
class PliantDockWidget(QtWidgets.QDockWidget):
|
class PliantDockWidget(QtWidgets.QDockWidget):
|
||||||
@@ -26,6 +29,27 @@ class PliantDockWidget(QtWidgets.QDockWidget):
|
|||||||
self.notes_only = notes_only
|
self.notes_only = notes_only
|
||||||
self.contentView = contentView
|
self.contentView = contentView
|
||||||
self.current_annotation = None
|
self.current_annotation = None
|
||||||
|
self.parent = parent
|
||||||
|
|
||||||
|
# Models
|
||||||
|
# The following models belong to the sideDock
|
||||||
|
# bookmarkModel, bookmarkProxyModel
|
||||||
|
# annotationModel
|
||||||
|
# searchResultsModel
|
||||||
|
self.bookmarkModel = None
|
||||||
|
self.bookmarkProxyModel = None
|
||||||
|
self.annotationModel = None
|
||||||
|
self.searchResultsModel = None
|
||||||
|
|
||||||
|
# References
|
||||||
|
# All widgets belong to these
|
||||||
|
self.bookmarks = None
|
||||||
|
self.annotations = None
|
||||||
|
self.search = None
|
||||||
|
|
||||||
|
# Widgets
|
||||||
|
# Except this one
|
||||||
|
self.sideDockTabWidget = None
|
||||||
|
|
||||||
def showEvent(self, event=None):
|
def showEvent(self, event=None):
|
||||||
viewport_topRight = self.contentView.mapToGlobal(
|
viewport_topRight = self.contentView.mapToGlobal(
|
||||||
@@ -60,96 +84,434 @@ class PliantDockWidget(QtWidgets.QDockWidget):
|
|||||||
def set_annotation(self, annotation):
|
def set_annotation(self, annotation):
|
||||||
self.current_annotation = annotation
|
self.current_annotation = annotation
|
||||||
|
|
||||||
|
def populate(self):
|
||||||
|
self.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
|
||||||
|
self.setTitleBarWidget(QtWidgets.QWidget(self)) # Removes titlebar
|
||||||
|
self.sideDockTabWidget = QtWidgets.QTabWidget(self)
|
||||||
|
self.setWidget(self.sideDockTabWidget)
|
||||||
|
|
||||||
|
# This order is important
|
||||||
|
self.bookmarkModel = QtGui.QStandardItemModel(self)
|
||||||
|
self.bookmarkProxyModel = BookmarkProxyModel(self)
|
||||||
|
self.bookmarks = Bookmarks(self)
|
||||||
|
self.bookmarks.generate_bookmark_model()
|
||||||
|
|
||||||
|
if not self.parent.are_we_doing_images_only:
|
||||||
|
self.annotationModel = QtGui.QStandardItemModel(self)
|
||||||
|
self.annotations = Annotations(self)
|
||||||
|
self.annotations.generate_annotation_model()
|
||||||
|
|
||||||
|
self.searchResultsModel = QtGui.QStandardItemModel(self)
|
||||||
|
self.search = Search(self)
|
||||||
|
|
||||||
def closeEvent(self, event):
|
def closeEvent(self, event):
|
||||||
self.hide()
|
self.hide()
|
||||||
|
# Ignoring this event prevents application closure
|
||||||
# Ignoring this event prevents application closure when everything is fullscreened
|
# when everything is fullscreened
|
||||||
event.ignore()
|
event.ignore()
|
||||||
|
|
||||||
|
|
||||||
# TODO
|
# For the following classes, the parent is the sideDock
|
||||||
# Maybe subclass PliantDockWidget for this
|
# The parentTab is the parent... tab. So self.parent.parent
|
||||||
def populate_sideDock(tabWidget):
|
class Bookmarks:
|
||||||
tabWidget.sideDock.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
|
def __init__(self, parent):
|
||||||
tabWidget.sideDock.setTitleBarWidget(QtWidgets.QWidget())
|
self.parent = parent
|
||||||
tabWidget.sideDockTabWidget = QtWidgets.QTabWidget()
|
self.parentTab = self.parent.parent
|
||||||
tabWidget.sideDock.setWidget(tabWidget.sideDockTabWidget)
|
self.bookmarkTreeView = QtWidgets.QTreeView(self.parent)
|
||||||
|
self._translate = QtCore.QCoreApplication.translate
|
||||||
|
self.bookmarks_string = self._translate('SideDock', 'Bookmarks')
|
||||||
|
|
||||||
# Bookmark tree view and model
|
self.create_widgets()
|
||||||
tabWidget.bookmarkTreeView = QtWidgets.QTreeView(tabWidget)
|
|
||||||
tabWidget.bookmarkTreeView.setHeaderHidden(True)
|
|
||||||
tabWidget.bookmarkTreeView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
|
||||||
tabWidget.bookmarkTreeView.customContextMenuRequested.connect(
|
|
||||||
tabWidget.generate_bookmark_context_menu)
|
|
||||||
tabWidget.bookmarkTreeView.clicked.connect(tabWidget.navigate_to_bookmark)
|
|
||||||
bookmarks_string = tabWidget._translate('Tab', 'Bookmarks')
|
|
||||||
tabWidget.sideDockTabWidget.addTab(tabWidget.bookmarkTreeView, bookmarks_string)
|
|
||||||
|
|
||||||
tabWidget.bookmarkModel = QtGui.QStandardItemModel(tabWidget)
|
def create_widgets(self):
|
||||||
tabWidget.bookmarkProxyModel = BookmarkProxyModel(tabWidget)
|
self.bookmarkTreeView.setHeaderHidden(True)
|
||||||
tabWidget.generate_bookmark_model()
|
self.bookmarkTreeView.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
|
||||||
|
self.bookmarkTreeView.customContextMenuRequested.connect(
|
||||||
|
self.generate_bookmark_context_menu)
|
||||||
|
self.bookmarkTreeView.clicked.connect(self.navigate_to_bookmark)
|
||||||
|
|
||||||
# Annotation list view and model
|
# Add widget to side dock
|
||||||
# Leave this without a parent or it shows up in the image viewer
|
self.parent.sideDockTabWidget.addTab(
|
||||||
tabWidget.annotationListView = QtWidgets.QListView()
|
self.bookmarkTreeView, self.bookmarks_string)
|
||||||
tabWidget.annotationListView.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
|
|
||||||
tabWidget.annotationListView.doubleClicked.connect(tabWidget.contentView.toggle_annotation_mode)
|
|
||||||
annotations_string = tabWidget._translate('Tab', 'Annotations')
|
|
||||||
|
|
||||||
tabWidget.annotationModel = QtGui.QStandardItemModel(tabWidget)
|
def add_bookmark(self, position=None):
|
||||||
tabWidget.generate_annotation_model()
|
identifier = uuid.uuid4().hex[:10]
|
||||||
|
description = self._translate('SideDock', 'New bookmark')
|
||||||
|
|
||||||
# Search view and model
|
if self.parentTab.are_we_doing_images_only:
|
||||||
tabWidget.searchLineEdit = QtWidgets.QLineEdit()
|
chapter = self.parentTab.metadata['position']['current_chapter']
|
||||||
tabWidget.searchLineEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
|
cursor_position = 0
|
||||||
tabWidget.searchLineEdit.setClearButtonEnabled(True)
|
else:
|
||||||
search_string = tabWidget._translate('Tab', 'Search')
|
chapter, cursor_position = self.parent.contentView.record_position(True)
|
||||||
tabWidget.searchLineEdit.setPlaceholderText(search_string)
|
if position: # Should be the case when called from the context menu
|
||||||
|
cursor_position = position
|
||||||
|
|
||||||
search_book_string = tabWidget._translate('Tab', 'Search entire book')
|
self.parentTab.metadata['bookmarks'][identifier] = {
|
||||||
tabWidget.searchBookButton = QtWidgets.QToolButton()
|
'chapter': chapter,
|
||||||
tabWidget.searchBookButton.setIcon(
|
'cursor_position': cursor_position,
|
||||||
tabWidget.main_window.QImageFactory.get_image('view-readermode'))
|
'description': description}
|
||||||
tabWidget.searchBookButton.setToolTip(search_book_string)
|
|
||||||
tabWidget.searchBookButton.setCheckable(True)
|
|
||||||
tabWidget.searchBookButton.setAutoRaise(True)
|
|
||||||
|
|
||||||
case_sensitive_string = tabWidget._translate('Tab', 'Match case')
|
self.parent.setVisible(True)
|
||||||
tabWidget.caseSensitiveSearchButton = QtWidgets.QToolButton()
|
self.parent.sideDockTabWidget.setCurrentIndex(0)
|
||||||
tabWidget.caseSensitiveSearchButton.setIcon(
|
self.add_bookmark_to_model(
|
||||||
tabWidget.main_window.QImageFactory.get_image('search-case'))
|
description, chapter, cursor_position, identifier, True)
|
||||||
tabWidget.caseSensitiveSearchButton.setToolTip(case_sensitive_string)
|
|
||||||
tabWidget.caseSensitiveSearchButton.setCheckable(True)
|
|
||||||
tabWidget.caseSensitiveSearchButton.setAutoRaise(True)
|
|
||||||
|
|
||||||
match_word_string = tabWidget._translate('Tab', 'Match word')
|
def add_bookmark_to_model(
|
||||||
tabWidget.matchWholeWordButton = QtWidgets.QToolButton()
|
self, description, chapter_number, cursor_position,
|
||||||
tabWidget.matchWholeWordButton.setIcon(
|
identifier, new_bookmark=False):
|
||||||
tabWidget.main_window.QImageFactory.get_image('search-word'))
|
|
||||||
tabWidget.matchWholeWordButton.setToolTip(match_word_string)
|
|
||||||
tabWidget.matchWholeWordButton.setCheckable(True)
|
|
||||||
tabWidget.matchWholeWordButton.setAutoRaise(True)
|
|
||||||
|
|
||||||
tabWidget.searchOptionsLayout = QtWidgets.QHBoxLayout()
|
def edit_new_bookmark(parent_item):
|
||||||
tabWidget.searchOptionsLayout.setContentsMargins(0, 3, 0, 0)
|
new_child = parent_item.child(parent_item.rowCount() - 1, 0)
|
||||||
tabWidget.searchOptionsLayout.addWidget(tabWidget.searchLineEdit)
|
source_index = self.parent.bookmarkModel.indexFromItem(new_child)
|
||||||
tabWidget.searchOptionsLayout.addWidget(tabWidget.searchBookButton)
|
edit_index = self.bookmarkTreeView.model().mapFromSource(source_index)
|
||||||
tabWidget.searchOptionsLayout.addWidget(tabWidget.caseSensitiveSearchButton)
|
self.parent.activateWindow()
|
||||||
tabWidget.searchOptionsLayout.addWidget(tabWidget.matchWholeWordButton)
|
self.bookmarkTreeView.setFocus()
|
||||||
|
self.bookmarkTreeView.setCurrentIndex(edit_index)
|
||||||
|
self.bookmarkTreeView.edit(edit_index)
|
||||||
|
|
||||||
# Leave this without a parent or it shows up in the image viewer
|
def get_chapter_name(chapter_number):
|
||||||
tabWidget.searchResultsTreeView = QtWidgets.QTreeView()
|
for i in reversed(self.parentTab.metadata['toc']):
|
||||||
tabWidget.searchResultsTreeView.setHeaderHidden(True)
|
if i[2] <= chapter_number:
|
||||||
tabWidget.searchResultsTreeView.setEditTriggers(QtWidgets.QTreeView.NoEditTriggers)
|
return i[1]
|
||||||
tabWidget.searchResultsTreeView.clicked.connect(tabWidget.navigate_to_search_result)
|
return 'Unknown'
|
||||||
|
|
||||||
tabWidget.searchTabLayout = QtWidgets.QVBoxLayout()
|
bookmark = QtGui.QStandardItem()
|
||||||
tabWidget.searchTabLayout.addLayout(tabWidget.searchOptionsLayout)
|
bookmark.setData(False, QtCore.Qt.UserRole + 10) # Is Parent
|
||||||
tabWidget.searchTabLayout.addWidget(tabWidget.searchResultsTreeView)
|
bookmark.setData(chapter_number, QtCore.Qt.UserRole) # Chapter number
|
||||||
tabWidget.searchTabLayout.setContentsMargins(0, 0, 0, 0)
|
bookmark.setData(cursor_position, QtCore.Qt.UserRole + 1) # Cursor Position
|
||||||
tabWidget.searchTabWidget = QtWidgets.QWidget()
|
bookmark.setData(identifier, QtCore.Qt.UserRole + 2) # Identifier
|
||||||
tabWidget.searchTabWidget.setLayout(tabWidget.searchTabLayout)
|
bookmark.setData(description, QtCore.Qt.DisplayRole) # Description
|
||||||
|
bookmark_chapter_name = get_chapter_name(chapter_number)
|
||||||
|
|
||||||
if not tabWidget.are_we_doing_images_only:
|
for i in range(self.parent.bookmarkModel.rowCount()):
|
||||||
tabWidget.sideDockTabWidget.addTab(tabWidget.searchTabWidget, search_string)
|
parentIndex = self.parent.bookmarkModel.index(i, 0)
|
||||||
tabWidget.sideDockTabWidget.addTab(tabWidget.annotationListView, annotations_string)
|
parent_chapter_number = parentIndex.data(QtCore.Qt.UserRole)
|
||||||
|
parent_chapter_name = parentIndex.data(QtCore.Qt.DisplayRole)
|
||||||
|
|
||||||
|
# This prevents duplication of the bookmark in the new
|
||||||
|
# navigation model
|
||||||
|
if ((parent_chapter_number <= chapter_number) and
|
||||||
|
(parent_chapter_name == bookmark_chapter_name)):
|
||||||
|
bookmarkParent = self.parent.bookmarkModel.itemFromIndex(parentIndex)
|
||||||
|
bookmarkParent.appendRow(bookmark)
|
||||||
|
if new_bookmark:
|
||||||
|
edit_new_bookmark(bookmarkParent)
|
||||||
|
return
|
||||||
|
|
||||||
|
# In case no parent item exists
|
||||||
|
bookmarkParent = QtGui.QStandardItem()
|
||||||
|
bookmarkParent.setData(True, QtCore.Qt.UserRole + 10) # Is Parent
|
||||||
|
bookmarkParent.setFlags(bookmarkParent.flags() & ~QtCore.Qt.ItemIsEditable) # Is Editable
|
||||||
|
bookmarkParent.setData(get_chapter_name(chapter_number), QtCore.Qt.DisplayRole)
|
||||||
|
bookmarkParent.setData(chapter_number, QtCore.Qt.UserRole)
|
||||||
|
|
||||||
|
bookmarkParent.appendRow(bookmark)
|
||||||
|
self.parent.bookmarkModel.appendRow(bookmarkParent)
|
||||||
|
if new_bookmark:
|
||||||
|
edit_new_bookmark(bookmarkParent)
|
||||||
|
|
||||||
|
def navigate_to_bookmark(self, index):
|
||||||
|
if not index.isValid():
|
||||||
|
return
|
||||||
|
|
||||||
|
is_parent = self.parent.bookmarkProxyModel.data(
|
||||||
|
index, QtCore.Qt.UserRole + 10)
|
||||||
|
if is_parent:
|
||||||
|
chapter_number = self.parent.bookmarkProxyModel.data(
|
||||||
|
index, QtCore.Qt.UserRole)
|
||||||
|
self.parentTab.set_content(chapter_number, True)
|
||||||
|
return
|
||||||
|
|
||||||
|
chapter = self.parent.bookmarkProxyModel.data(
|
||||||
|
index, QtCore.Qt.UserRole)
|
||||||
|
cursor_position = self.parent.bookmarkProxyModel.data(
|
||||||
|
index, QtCore.Qt.UserRole + 1)
|
||||||
|
|
||||||
|
self.parentTab.set_content(chapter, True)
|
||||||
|
if not self.parentTab.are_we_doing_images_only:
|
||||||
|
self.parentTab.set_cursor_position(cursor_position)
|
||||||
|
|
||||||
|
def generate_bookmark_model(self):
|
||||||
|
for i in self.parentTab.metadata['bookmarks'].items():
|
||||||
|
description = i[1]['description']
|
||||||
|
chapter = i[1]['chapter']
|
||||||
|
cursor_position = i[1]['cursor_position']
|
||||||
|
identifier = i[0]
|
||||||
|
self.add_bookmark_to_model(
|
||||||
|
description, chapter, cursor_position, identifier)
|
||||||
|
|
||||||
|
self.generate_bookmark_proxy_model()
|
||||||
|
|
||||||
|
def generate_bookmark_proxy_model(self):
|
||||||
|
self.parent.bookmarkProxyModel.setSourceModel(self.parent.bookmarkModel)
|
||||||
|
self.parent.bookmarkProxyModel.setSortCaseSensitivity(False)
|
||||||
|
self.parent.bookmarkProxyModel.setSortRole(QtCore.Qt.UserRole)
|
||||||
|
self.parent.bookmarkProxyModel.sort(0)
|
||||||
|
self.bookmarkTreeView.setModel(self.parent.bookmarkProxyModel)
|
||||||
|
|
||||||
|
def generate_bookmark_context_menu(self, position):
|
||||||
|
index = self.bookmarkTreeView.indexAt(position)
|
||||||
|
if not index.isValid():
|
||||||
|
return
|
||||||
|
|
||||||
|
is_parent = self.parent.bookmarkProxyModel.data(
|
||||||
|
index, QtCore.Qt.UserRole + 10)
|
||||||
|
if is_parent:
|
||||||
|
return
|
||||||
|
|
||||||
|
bookmarkMenu = QtWidgets.QMenu()
|
||||||
|
editAction = bookmarkMenu.addAction(
|
||||||
|
self.parentTab.main_window.QImageFactory.get_image('edit-rename'),
|
||||||
|
self._translate('Tab', 'Edit'))
|
||||||
|
deleteAction = bookmarkMenu.addAction(
|
||||||
|
self.parentTab.main_window.QImageFactory.get_image('trash-empty'),
|
||||||
|
self._translate('Tab', 'Delete'))
|
||||||
|
|
||||||
|
action = bookmarkMenu.exec_(
|
||||||
|
self.bookmarkTreeView.mapToGlobal(position))
|
||||||
|
|
||||||
|
if action == editAction:
|
||||||
|
self.bookmarkTreeView.edit(index)
|
||||||
|
|
||||||
|
if action == deleteAction:
|
||||||
|
child_index = self.parent.bookmarkProxyModel.mapToSource(index)
|
||||||
|
parent_index = child_index.parent()
|
||||||
|
child_rows = self.parent.bookmarkModel.itemFromIndex(parent_index).rowCount()
|
||||||
|
delete_uuid = self.parent.bookmarkModel.data(
|
||||||
|
child_index, QtCore.Qt.UserRole + 2)
|
||||||
|
|
||||||
|
self.parentTab.metadata['bookmarks'].pop(delete_uuid)
|
||||||
|
|
||||||
|
self.parent.bookmarkModel.removeRow(child_index.row(), child_index.parent())
|
||||||
|
if child_rows == 1:
|
||||||
|
self.parent.bookmarkModel.removeRow(parent_index.row())
|
||||||
|
|
||||||
|
|
||||||
|
class Annotations:
|
||||||
|
def __init__(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
self.parentTab = self.parent.parent
|
||||||
|
self.annotationListView = QtWidgets.QListView(self.parent)
|
||||||
|
self._translate = QtCore.QCoreApplication.translate
|
||||||
|
self.annotations_string = self._translate('SideDock', 'Annotations')
|
||||||
|
|
||||||
|
self.create_widgets()
|
||||||
|
|
||||||
|
def create_widgets(self):
|
||||||
|
self.annotationListView.setEditTriggers(QtWidgets.QListView.NoEditTriggers)
|
||||||
|
self.annotationListView.doubleClicked.connect(
|
||||||
|
self.parent.contentView.toggle_annotation_mode)
|
||||||
|
|
||||||
|
# Add widget to side dock
|
||||||
|
self.parent.sideDockTabWidget.addTab(
|
||||||
|
self.annotationListView, self.annotations_string)
|
||||||
|
|
||||||
|
def generate_annotation_model(self):
|
||||||
|
# TODO
|
||||||
|
# Annotation previews will require creation of a
|
||||||
|
# QStyledItemDelegate
|
||||||
|
|
||||||
|
saved_annotations = self.parent.main_window.settings['annotations']
|
||||||
|
if not saved_annotations:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Create annotation model
|
||||||
|
for i in saved_annotations:
|
||||||
|
item = QtGui.QStandardItem()
|
||||||
|
item.setText(i['name'])
|
||||||
|
item.setData(i, QtCore.Qt.UserRole)
|
||||||
|
self.parent.annotationModel.appendRow(item)
|
||||||
|
self.annotationListView.setModel(self.parent.annotationModel)
|
||||||
|
|
||||||
|
|
||||||
|
class Search:
|
||||||
|
def __init__(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
self.parentTab = self.parent.parent
|
||||||
|
|
||||||
|
self.searchThread = BackGroundTextSearch()
|
||||||
|
self.searchOptionsLayout = QtWidgets.QHBoxLayout()
|
||||||
|
self.searchTabLayout = QtWidgets.QVBoxLayout()
|
||||||
|
self.searchTimer = QtCore.QTimer(self.parent)
|
||||||
|
self.searchLineEdit = QtWidgets.QLineEdit(self.parent)
|
||||||
|
self.searchBookButton = QtWidgets.QToolButton(self.parent)
|
||||||
|
self.caseSensitiveSearchButton = QtWidgets.QToolButton(self.parent)
|
||||||
|
self.matchWholeWordButton = QtWidgets.QToolButton(self.parent)
|
||||||
|
self.searchResultsTreeView = QtWidgets.QTreeView(self.parent)
|
||||||
|
|
||||||
|
self.create_widgets()
|
||||||
|
|
||||||
|
def create_widgets(self):
|
||||||
|
self.searchThread.finished.connect(self.generate_search_result_model)
|
||||||
|
|
||||||
|
self.searchTimer.setSingleShot(True)
|
||||||
|
self.searchTimer.timeout.connect(self.set_search_options)
|
||||||
|
|
||||||
|
self.searchLineEdit.textChanged.connect(
|
||||||
|
lambda: self.searchLineEdit.setStyleSheet(
|
||||||
|
QtWidgets.QLineEdit.styleSheet(self.parent)))
|
||||||
|
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))
|
||||||
|
self.matchWholeWordButton.clicked.connect(
|
||||||
|
lambda: self.searchTimer.start(100))
|
||||||
|
|
||||||
|
self.searchLineEdit.setFocusPolicy(QtCore.Qt.StrongFocus)
|
||||||
|
self.searchLineEdit.setClearButtonEnabled(True)
|
||||||
|
self._translate = QtCore.QCoreApplication.translate
|
||||||
|
self.search_string = self._translate('SideDock', 'Search')
|
||||||
|
self.searchLineEdit.setPlaceholderText(self.search_string)
|
||||||
|
|
||||||
|
search_book_string = self._translate('SideDock', 'Search entire book')
|
||||||
|
|
||||||
|
self.searchBookButton.setIcon(
|
||||||
|
self.parent.main_window.QImageFactory.get_image('view-readermode'))
|
||||||
|
self.searchBookButton.setToolTip(search_book_string)
|
||||||
|
self.searchBookButton.setCheckable(True)
|
||||||
|
self.searchBookButton.setAutoRaise(True)
|
||||||
|
self.searchBookButton.setIconSize(QtCore.QSize(20, 20))
|
||||||
|
|
||||||
|
case_sensitive_string = self._translate('SideDock', 'Match case')
|
||||||
|
|
||||||
|
self.caseSensitiveSearchButton.setIcon(
|
||||||
|
self.parent.main_window.QImageFactory.get_image('search-case'))
|
||||||
|
self.caseSensitiveSearchButton.setToolTip(case_sensitive_string)
|
||||||
|
self.caseSensitiveSearchButton.setCheckable(True)
|
||||||
|
self.caseSensitiveSearchButton.setAutoRaise(True)
|
||||||
|
self.caseSensitiveSearchButton.setIconSize(QtCore.QSize(20, 20))
|
||||||
|
|
||||||
|
match_word_string = self._translate('SideDock', 'Match word')
|
||||||
|
self.matchWholeWordButton.setIcon(
|
||||||
|
self.parent.main_window.QImageFactory.get_image('search-word'))
|
||||||
|
self.matchWholeWordButton.setToolTip(match_word_string)
|
||||||
|
self.matchWholeWordButton.setCheckable(True)
|
||||||
|
self.matchWholeWordButton.setAutoRaise(True)
|
||||||
|
self.matchWholeWordButton.setIconSize(QtCore.QSize(20, 20))
|
||||||
|
|
||||||
|
self.searchOptionsLayout.setContentsMargins(0, 3, 0, 0)
|
||||||
|
self.searchOptionsLayout.addWidget(self.searchLineEdit)
|
||||||
|
self.searchOptionsLayout.addWidget(self.searchBookButton)
|
||||||
|
self.searchOptionsLayout.addWidget(self.caseSensitiveSearchButton)
|
||||||
|
self.searchOptionsLayout.addWidget(self.matchWholeWordButton)
|
||||||
|
|
||||||
|
self.searchResultsTreeView.setHeaderHidden(True)
|
||||||
|
self.searchResultsTreeView.setEditTriggers(
|
||||||
|
QtWidgets.QTreeView.NoEditTriggers)
|
||||||
|
self.searchResultsTreeView.clicked.connect(
|
||||||
|
self.navigate_to_search_result)
|
||||||
|
|
||||||
|
self.searchTabLayout.addLayout(self.searchOptionsLayout)
|
||||||
|
self.searchTabLayout.addWidget(self.searchResultsTreeView)
|
||||||
|
self.searchTabLayout.setContentsMargins(0, 0, 0, 0)
|
||||||
|
self.searchTabWidget = QtWidgets.QWidget(self.parent)
|
||||||
|
self.searchTabWidget.setLayout(self.searchTabLayout)
|
||||||
|
|
||||||
|
# Add widget to side dock
|
||||||
|
self.parent.sideDockTabWidget.addTab(
|
||||||
|
self.searchTabWidget, self.search_string)
|
||||||
|
|
||||||
|
def set_search_options(self):
|
||||||
|
def generate_title_content_pair(required_chapters):
|
||||||
|
title_content_list = []
|
||||||
|
for i in self.parentTab.metadata['toc']:
|
||||||
|
if i[2] in required_chapters:
|
||||||
|
title_content_list.append(
|
||||||
|
(i[1], self.parentTab.metadata['content'][i[2] - 1], i[2]))
|
||||||
|
return title_content_list
|
||||||
|
|
||||||
|
# Select either the current chapter or all chapters
|
||||||
|
# Function name is descriptive
|
||||||
|
chapter_numbers = (self.parentTab.metadata['position']['current_chapter'],)
|
||||||
|
if self.searchBookButton.isChecked():
|
||||||
|
chapter_numbers = [i + 1 for i in range(len(self.parentTab.metadata['content']))]
|
||||||
|
search_content = generate_title_content_pair(chapter_numbers)
|
||||||
|
|
||||||
|
self.searchThread.set_search_options(
|
||||||
|
search_content,
|
||||||
|
self.searchLineEdit.text(),
|
||||||
|
self.caseSensitiveSearchButton.isChecked(),
|
||||||
|
self.matchWholeWordButton.isChecked())
|
||||||
|
self.searchThread.start()
|
||||||
|
|
||||||
|
def generate_search_result_model(self):
|
||||||
|
self.parent.searchResultsModel.clear()
|
||||||
|
search_results = self.searchThread.search_results
|
||||||
|
for i in search_results:
|
||||||
|
parentItem = QtGui.QStandardItem()
|
||||||
|
parentItem.setData(True, QtCore.Qt.UserRole) # Is parent?
|
||||||
|
parentItem.setData(i, QtCore.Qt.UserRole + 3) # Display text for label
|
||||||
|
|
||||||
|
for j in search_results[i]:
|
||||||
|
childItem = QtGui.QStandardItem(parentItem)
|
||||||
|
childItem.setData(False, QtCore.Qt.UserRole) # Is parent?
|
||||||
|
childItem.setData(j[3], 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.parent.searchResultsModel.appendRow(parentItem)
|
||||||
|
|
||||||
|
self.searchResultsTreeView.setModel(self.parent.searchResultsModel)
|
||||||
|
self.searchResultsTreeView.expandToDepth(1)
|
||||||
|
|
||||||
|
# Reset stylesheet in case something is found
|
||||||
|
if search_results:
|
||||||
|
self.searchLineEdit.setStyleSheet(
|
||||||
|
QtWidgets.QLineEdit.styleSheet(self.parent))
|
||||||
|
|
||||||
|
# Or set to Red in case nothing is found
|
||||||
|
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.parent.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.parent.searchResultsModel.rowCount()):
|
||||||
|
parentItem = self.parent.searchResultsModel.item(parent_iter)
|
||||||
|
parentIndex = self.parent.searchResultsModel.index(parent_iter, 0)
|
||||||
|
generate_label(parentIndex)
|
||||||
|
|
||||||
|
for child_iter in range(parentItem.rowCount()):
|
||||||
|
childIndex = self.parent.searchResultsModel.index(child_iter, 0, parentIndex)
|
||||||
|
generate_label(childIndex)
|
||||||
|
|
||||||
|
def navigate_to_search_result(self, index):
|
||||||
|
if not index.isValid():
|
||||||
|
return
|
||||||
|
|
||||||
|
is_parent = self.parent.searchResultsModel.data(index, QtCore.Qt.UserRole)
|
||||||
|
if is_parent:
|
||||||
|
return
|
||||||
|
|
||||||
|
chapter_number = self.parent.searchResultsModel.data(index, QtCore.Qt.UserRole + 1)
|
||||||
|
cursor_position = self.parent.searchResultsModel.data(index, QtCore.Qt.UserRole + 2)
|
||||||
|
search_term = self.parent.searchResultsModel.data(index, QtCore.Qt.UserRole + 4)
|
||||||
|
|
||||||
|
self.parentTab.set_content(chapter_number, True)
|
||||||
|
if not self.parentTab.are_we_doing_images_only:
|
||||||
|
self.parentTab.set_cursor_position(
|
||||||
|
cursor_position, len(search_term))
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
@@ -27,23 +27,19 @@ class BookmarkProxyModel(QtCore.QSortFilterProxyModel):
|
|||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(BookmarkProxyModel, self).__init__(parent)
|
super(BookmarkProxyModel, self).__init__(parent)
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
|
self.parentTab = self.parent.parent
|
||||||
self.filter_text = None
|
self.filter_text = None
|
||||||
|
|
||||||
def setFilterParams(self, filter_text):
|
def setFilterParams(self, filter_text):
|
||||||
self.filter_text = filter_text
|
self.filter_text = filter_text
|
||||||
|
|
||||||
def filterAcceptsRow(self, row, parent):
|
|
||||||
# TODO
|
|
||||||
# Connect this to the search bar
|
|
||||||
return True
|
|
||||||
|
|
||||||
def setData(self, index, value, role):
|
def setData(self, index, value, role):
|
||||||
if role == QtCore.Qt.EditRole:
|
if role == QtCore.Qt.EditRole:
|
||||||
source_index = self.mapToSource(index)
|
source_index = self.mapToSource(index)
|
||||||
identifier = self.sourceModel().data(source_index, QtCore.Qt.UserRole + 2)
|
identifier = self.sourceModel().data(source_index, QtCore.Qt.UserRole + 2)
|
||||||
|
|
||||||
self.sourceModel().setData(source_index, value, QtCore.Qt.DisplayRole)
|
self.sourceModel().setData(source_index, value, QtCore.Qt.DisplayRole)
|
||||||
self.parent.metadata['bookmarks'][identifier]['description'] = value
|
self.parentTab.metadata['bookmarks'][identifier]['description'] = value
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@@ -19,14 +19,12 @@
|
|||||||
# Double page, Continuous etc
|
# Double page, Continuous etc
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import uuid
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt5 import QtWidgets, QtGui, QtCore
|
from PyQt5 import QtWidgets, QtGui, QtCore
|
||||||
|
|
||||||
from lector.sorter import resize_image
|
from lector.sorter import resize_image
|
||||||
from lector.threaded import BackGroundTextSearch
|
from lector.dockwidgets import PliantDockWidget
|
||||||
from lector.dockwidgets import PliantDockWidget, populate_sideDock
|
|
||||||
from lector.contentwidgets import PliantQGraphicsView, PliantQTextBrowser
|
from lector.contentwidgets import PliantQGraphicsView, PliantQTextBrowser
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
@@ -137,13 +135,14 @@ class Tab(QtWidgets.QWidget):
|
|||||||
self.contentView.setVerticalScrollBarPolicy(
|
self.contentView.setVerticalScrollBarPolicy(
|
||||||
QtCore.Qt.ScrollBarAsNeeded)
|
QtCore.Qt.ScrollBarAsNeeded)
|
||||||
|
|
||||||
# Create a common dock for annotations and bookmarks
|
# Create a common dock for bookmarks, annotations, and search
|
||||||
# It is populated by the following method
|
self.sideDock = PliantDockWidget(
|
||||||
self.sideDock = PliantDockWidget(self.main_window, False, self.contentView)
|
self.main_window, False, self.contentView, self)
|
||||||
populate_sideDock(self)
|
self.sideDock.populate()
|
||||||
|
|
||||||
# Create the annotation notes dock
|
# Create the annotation notes dock
|
||||||
self.annotationNoteDock = PliantDockWidget(self.main_window, True, self.contentView)
|
self.annotationNoteDock = PliantDockWidget(
|
||||||
|
self.main_window, True, self.contentView, self)
|
||||||
self.annotationNoteDock.setWindowTitle(self._translate('Tab', 'Note'))
|
self.annotationNoteDock.setWindowTitle(self._translate('Tab', 'Note'))
|
||||||
self.annotationNoteDock.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
|
self.annotationNoteDock.setFeatures(QtWidgets.QDockWidget.DockWidgetClosable)
|
||||||
self.annotationNoteDock.hide()
|
self.annotationNoteDock.hide()
|
||||||
@@ -166,29 +165,6 @@ class Tab(QtWidgets.QWidget):
|
|||||||
self.annotationNoteDock.setWindowOpacity(.95)
|
self.annotationNoteDock.setWindowOpacity(.95)
|
||||||
self.sideDock.hide()
|
self.sideDock.hide()
|
||||||
|
|
||||||
# Create search references
|
|
||||||
if not self.are_we_doing_images_only:
|
|
||||||
self.searchResultsModel = None
|
|
||||||
|
|
||||||
self.searchThread = BackGroundTextSearch()
|
|
||||||
self.searchThread.finished.connect(self.generate_search_result_model)
|
|
||||||
|
|
||||||
self.searchTimer = QtCore.QTimer()
|
|
||||||
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))
|
|
||||||
self.matchWholeWordButton.clicked.connect(
|
|
||||||
lambda: self.searchTimer.start(100))
|
|
||||||
|
|
||||||
# Create tab in the central tab widget
|
# Create tab in the central tab widget
|
||||||
title = self.metadata['title']
|
title = self.metadata['title']
|
||||||
if self.main_window.settings['attenuate_titles'] and len(title) > 30:
|
if self.main_window.settings['attenuate_titles'] and len(title) > 30:
|
||||||
@@ -214,17 +190,17 @@ class Tab(QtWidgets.QWidget):
|
|||||||
|
|
||||||
def toggle_side_dock(self, tab_required, override_hide=False):
|
def toggle_side_dock(self, tab_required, override_hide=False):
|
||||||
if (self.sideDock.isVisible()
|
if (self.sideDock.isVisible()
|
||||||
and self.sideDockTabWidget.currentIndex() == tab_required
|
and self.sideDock.sideDockTabWidget.currentIndex() == tab_required
|
||||||
and not override_hide):
|
and not override_hide):
|
||||||
self.sideDock.hide()
|
self.sideDock.hide()
|
||||||
elif not self.sideDock.isVisible():
|
elif not self.sideDock.isVisible():
|
||||||
self.sideDock.show()
|
self.sideDock.show()
|
||||||
if tab_required == 2:
|
if tab_required == 2:
|
||||||
self.sideDock.activateWindow()
|
self.sideDock.activateWindow()
|
||||||
self.searchLineEdit.setFocus()
|
self.sideDock.search.searchLineEdit.setFocus()
|
||||||
self.searchLineEdit.selectAll()
|
self.sideDock.search.searchLineEdit.selectAll()
|
||||||
|
|
||||||
self.sideDockTabWidget.setCurrentIndex(tab_required)
|
self.sideDock.sideDockTabWidget.setCurrentIndex(tab_required)
|
||||||
|
|
||||||
def update_last_accessed_time(self):
|
def update_last_accessed_time(self):
|
||||||
self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime()
|
self.metadata['last_accessed'] = QtCore.QDateTime().currentDateTime()
|
||||||
@@ -238,7 +214,8 @@ class Tab(QtWidgets.QWidget):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.main_window.lib_ref.libraryModel.setData(
|
self.main_window.lib_ref.libraryModel.setData(
|
||||||
matching_item[0], self.metadata['last_accessed'], QtCore.Qt.UserRole + 12)
|
matching_item[0],
|
||||||
|
self.metadata['last_accessed'], QtCore.Qt.UserRole + 12)
|
||||||
except IndexError: # The file has been deleted
|
except IndexError: # The file has been deleted
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@@ -329,17 +306,20 @@ class Tab(QtWidgets.QWidget):
|
|||||||
|
|
||||||
ksToggleBookmarks = QtWidgets.QShortcut(
|
ksToggleBookmarks = QtWidgets.QShortcut(
|
||||||
QtGui.QKeySequence('Ctrl+B'), self.contentView)
|
QtGui.QKeySequence('Ctrl+B'), self.contentView)
|
||||||
ksToggleBookmarks.activated.connect(lambda: self.toggle_side_dock(0))
|
ksToggleBookmarks.activated.connect(
|
||||||
|
lambda: self.toggle_side_dock(0))
|
||||||
|
|
||||||
# Shortcuts not required for comic view functionality
|
# Shortcuts not required for comic view functionality
|
||||||
if not self.are_we_doing_images_only:
|
if not self.are_we_doing_images_only:
|
||||||
ksToggleAnnotations = QtWidgets.QShortcut(
|
ksToggleAnnotations = QtWidgets.QShortcut(
|
||||||
QtGui.QKeySequence('Ctrl+N'), self.contentView)
|
QtGui.QKeySequence('Ctrl+N'), self.contentView)
|
||||||
ksToggleAnnotations.activated.connect(lambda: self.toggle_side_dock(1))
|
ksToggleAnnotations.activated.connect(
|
||||||
|
lambda: self.toggle_side_dock(1))
|
||||||
|
|
||||||
ksToggleSearch = QtWidgets.QShortcut(
|
ksToggleSearch = QtWidgets.QShortcut(
|
||||||
QtGui.QKeySequence('Ctrl+F'), self.contentView)
|
QtGui.QKeySequence('Ctrl+F'), self.contentView)
|
||||||
ksToggleSearch.activated.connect(lambda: self.toggle_side_dock(2))
|
ksToggleSearch.activated.connect(
|
||||||
|
lambda: self.toggle_side_dock(2))
|
||||||
|
|
||||||
def generate_toc_model(self):
|
def generate_toc_model(self):
|
||||||
# The toc list is:
|
# The toc list is:
|
||||||
@@ -548,7 +528,8 @@ class Tab(QtWidgets.QWidget):
|
|||||||
|
|
||||||
current_index = self.main_window.bookToolBar.tocBox.currentIndex()
|
current_index = self.main_window.bookToolBar.tocBox.currentIndex()
|
||||||
if current_index == 0:
|
if current_index == 0:
|
||||||
block_format.setAlignment(QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
|
block_format.setAlignment(
|
||||||
|
QtCore.Qt.AlignVCenter | QtCore.Qt.AlignHCenter)
|
||||||
else:
|
else:
|
||||||
block_format.setAlignment(alignment_dict[text_alignment])
|
block_format.setAlignment(alignment_dict[text_alignment])
|
||||||
|
|
||||||
@@ -570,259 +551,6 @@ class Tab(QtWidgets.QWidget):
|
|||||||
if old_position == new_position:
|
if old_position == new_position:
|
||||||
break
|
break
|
||||||
|
|
||||||
def generate_annotation_model(self):
|
|
||||||
# TODO
|
|
||||||
# Annotation previews will require creation of a
|
|
||||||
# QStyledItemDelegate
|
|
||||||
|
|
||||||
saved_annotations = self.main_window.settings['annotations']
|
|
||||||
if not saved_annotations:
|
|
||||||
return
|
|
||||||
|
|
||||||
# Create annotation model
|
|
||||||
for i in saved_annotations:
|
|
||||||
item = QtGui.QStandardItem()
|
|
||||||
item.setText(i['name'])
|
|
||||||
item.setData(i, QtCore.Qt.UserRole)
|
|
||||||
self.annotationModel.appendRow(item)
|
|
||||||
self.annotationListView.setModel(self.annotationModel)
|
|
||||||
|
|
||||||
def add_bookmark(self, position=None):
|
|
||||||
identifier = uuid.uuid4().hex[:10]
|
|
||||||
description = self._translate('Tab', 'New bookmark')
|
|
||||||
|
|
||||||
if self.are_we_doing_images_only:
|
|
||||||
chapter = self.metadata['position']['current_chapter']
|
|
||||||
cursor_position = 0
|
|
||||||
else:
|
|
||||||
chapter, cursor_position = self.contentView.record_position(True)
|
|
||||||
if position: # Should be the case when called from the context menu
|
|
||||||
cursor_position = position
|
|
||||||
|
|
||||||
self.metadata['bookmarks'][identifier] = {
|
|
||||||
'chapter': chapter,
|
|
||||||
'cursor_position': cursor_position,
|
|
||||||
'description': description}
|
|
||||||
|
|
||||||
self.sideDock.setVisible(True)
|
|
||||||
self.sideDockTabWidget.setCurrentIndex(0)
|
|
||||||
self.add_bookmark_to_model(
|
|
||||||
description, chapter, cursor_position, identifier, True)
|
|
||||||
|
|
||||||
def add_bookmark_to_model(
|
|
||||||
self, description, chapter_number, cursor_position,
|
|
||||||
identifier, new_bookmark=False):
|
|
||||||
|
|
||||||
def edit_new_bookmark(parent_item):
|
|
||||||
new_child = parent_item.child(parent_item.rowCount() - 1, 0)
|
|
||||||
source_index = self.bookmarkModel.indexFromItem(new_child)
|
|
||||||
edit_index = self.bookmarkTreeView.model().mapFromSource(source_index)
|
|
||||||
self.sideDock.activateWindow()
|
|
||||||
self.bookmarkTreeView.setFocus()
|
|
||||||
self.bookmarkTreeView.setCurrentIndex(edit_index)
|
|
||||||
self.bookmarkTreeView.edit(edit_index)
|
|
||||||
|
|
||||||
def get_chapter_name(chapter_number):
|
|
||||||
for i in reversed(self.metadata['toc']):
|
|
||||||
if i[2] <= chapter_number:
|
|
||||||
return i[1]
|
|
||||||
return 'Unknown'
|
|
||||||
|
|
||||||
bookmark = QtGui.QStandardItem()
|
|
||||||
bookmark.setData(False, QtCore.Qt.UserRole + 10) # Is Parent
|
|
||||||
bookmark.setData(chapter_number, QtCore.Qt.UserRole) # Chapter number
|
|
||||||
bookmark.setData(cursor_position, QtCore.Qt.UserRole + 1) # Cursor Position
|
|
||||||
bookmark.setData(identifier, QtCore.Qt.UserRole + 2) # Identifier
|
|
||||||
bookmark.setData(description, QtCore.Qt.DisplayRole) # Description
|
|
||||||
bookmark_chapter_name = get_chapter_name(chapter_number)
|
|
||||||
|
|
||||||
for i in range(self.bookmarkModel.rowCount()):
|
|
||||||
parentIndex = self.bookmarkModel.index(i, 0)
|
|
||||||
parent_chapter_number = parentIndex.data(QtCore.Qt.UserRole)
|
|
||||||
parent_chapter_name = parentIndex.data(QtCore.Qt.DisplayRole)
|
|
||||||
|
|
||||||
# This prevents duplication of the bookmark in the new
|
|
||||||
# navigation model
|
|
||||||
if ((parent_chapter_number <= chapter_number) and
|
|
||||||
(parent_chapter_name == bookmark_chapter_name)):
|
|
||||||
bookmarkParent = self.bookmarkModel.itemFromIndex(parentIndex)
|
|
||||||
bookmarkParent.appendRow(bookmark)
|
|
||||||
if new_bookmark:
|
|
||||||
edit_new_bookmark(bookmarkParent)
|
|
||||||
return
|
|
||||||
|
|
||||||
# In case no parent item exists
|
|
||||||
bookmarkParent = QtGui.QStandardItem()
|
|
||||||
bookmarkParent.setData(True, QtCore.Qt.UserRole + 10) # Is Parent
|
|
||||||
bookmarkParent.setFlags(bookmarkParent.flags() & ~QtCore.Qt.ItemIsEditable) # Is Editable
|
|
||||||
bookmarkParent.setData(get_chapter_name(chapter_number), QtCore.Qt.DisplayRole)
|
|
||||||
bookmarkParent.setData(chapter_number, QtCore.Qt.UserRole)
|
|
||||||
|
|
||||||
bookmarkParent.appendRow(bookmark)
|
|
||||||
self.bookmarkModel.appendRow(bookmarkParent)
|
|
||||||
if new_bookmark:
|
|
||||||
edit_new_bookmark(bookmarkParent)
|
|
||||||
|
|
||||||
def navigate_to_bookmark(self, index):
|
|
||||||
if not index.isValid():
|
|
||||||
return
|
|
||||||
|
|
||||||
is_parent = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole + 10)
|
|
||||||
if is_parent:
|
|
||||||
chapter_number = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole)
|
|
||||||
self.set_content(chapter_number, True)
|
|
||||||
return
|
|
||||||
|
|
||||||
chapter = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole)
|
|
||||||
cursor_position = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole + 1)
|
|
||||||
|
|
||||||
self.set_content(chapter, True)
|
|
||||||
if not self.are_we_doing_images_only:
|
|
||||||
self.set_cursor_position(cursor_position)
|
|
||||||
|
|
||||||
def generate_bookmark_model(self):
|
|
||||||
for i in self.metadata['bookmarks'].items():
|
|
||||||
description = i[1]['description']
|
|
||||||
chapter = i[1]['chapter']
|
|
||||||
cursor_position = i[1]['cursor_position']
|
|
||||||
identifier = i[0]
|
|
||||||
self.add_bookmark_to_model(
|
|
||||||
description, chapter, cursor_position, identifier)
|
|
||||||
|
|
||||||
self.generate_bookmark_proxy_model()
|
|
||||||
|
|
||||||
def generate_bookmark_proxy_model(self):
|
|
||||||
self.bookmarkProxyModel.setSourceModel(self.bookmarkModel)
|
|
||||||
self.bookmarkProxyModel.setSortCaseSensitivity(False)
|
|
||||||
self.bookmarkProxyModel.setSortRole(QtCore.Qt.UserRole)
|
|
||||||
self.bookmarkProxyModel.sort(0)
|
|
||||||
self.bookmarkTreeView.setModel(self.bookmarkProxyModel)
|
|
||||||
|
|
||||||
def generate_bookmark_context_menu(self, position):
|
|
||||||
index = self.bookmarkTreeView.indexAt(position)
|
|
||||||
if not index.isValid():
|
|
||||||
return
|
|
||||||
|
|
||||||
is_parent = self.bookmarkProxyModel.data(index, QtCore.Qt.UserRole + 10)
|
|
||||||
if is_parent:
|
|
||||||
return
|
|
||||||
|
|
||||||
bookmarkMenu = QtWidgets.QMenu()
|
|
||||||
editAction = bookmarkMenu.addAction(
|
|
||||||
self.main_window.QImageFactory.get_image('edit-rename'),
|
|
||||||
self._translate('Tab', 'Edit'))
|
|
||||||
deleteAction = bookmarkMenu.addAction(
|
|
||||||
self.main_window.QImageFactory.get_image('trash-empty'),
|
|
||||||
self._translate('Tab', 'Delete'))
|
|
||||||
|
|
||||||
action = bookmarkMenu.exec_(
|
|
||||||
self.bookmarkTreeView.mapToGlobal(position))
|
|
||||||
|
|
||||||
if action == editAction:
|
|
||||||
self.bookmarkTreeView.edit(index)
|
|
||||||
|
|
||||||
if action == deleteAction:
|
|
||||||
child_index = self.bookmarkProxyModel.mapToSource(index)
|
|
||||||
parent_index = child_index.parent()
|
|
||||||
child_rows = self.bookmarkModel.itemFromIndex(parent_index).rowCount()
|
|
||||||
delete_uuid = self.bookmarkModel.data(
|
|
||||||
child_index, QtCore.Qt.UserRole + 2)
|
|
||||||
|
|
||||||
self.metadata['bookmarks'].pop(delete_uuid)
|
|
||||||
|
|
||||||
self.bookmarkModel.removeRow(child_index.row(), child_index.parent())
|
|
||||||
if child_rows == 1:
|
|
||||||
self.bookmarkModel.removeRow(parent_index.row())
|
|
||||||
|
|
||||||
def set_search_options(self):
|
|
||||||
def generate_title_content_pair(required_chapters):
|
|
||||||
title_content_list = []
|
|
||||||
for i in self.metadata['toc']:
|
|
||||||
if i[2] in required_chapters:
|
|
||||||
title_content_list.append(
|
|
||||||
(i[1], self.metadata['content'][i[2] - 1], i[2]))
|
|
||||||
return title_content_list
|
|
||||||
|
|
||||||
# Select either the current chapter or all chapters
|
|
||||||
# Function name is descriptive
|
|
||||||
chapter_numbers = (self.metadata['position']['current_chapter'],)
|
|
||||||
if self.searchBookButton.isChecked():
|
|
||||||
chapter_numbers = [i + 1 for i in range(len(self.metadata['content']))]
|
|
||||||
search_content = generate_title_content_pair(chapter_numbers)
|
|
||||||
|
|
||||||
self.searchThread.set_search_options(
|
|
||||||
search_content,
|
|
||||||
self.searchLineEdit.text(),
|
|
||||||
self.caseSensitiveSearchButton.isChecked(),
|
|
||||||
self.matchWholeWordButton.isChecked())
|
|
||||||
self.searchThread.start()
|
|
||||||
|
|
||||||
def generate_search_result_model(self):
|
|
||||||
self.searchResultsModel = QtGui.QStandardItemModel()
|
|
||||||
search_results = self.searchThread.search_results
|
|
||||||
for i in search_results:
|
|
||||||
parentItem = QtGui.QStandardItem()
|
|
||||||
parentItem.setData(True, QtCore.Qt.UserRole) # Is parent?
|
|
||||||
parentItem.setData(i, QtCore.Qt.UserRole + 3) # Display text for label
|
|
||||||
|
|
||||||
for j in search_results[i]:
|
|
||||||
childItem = QtGui.QStandardItem(parentItem)
|
|
||||||
childItem.setData(False, QtCore.Qt.UserRole) # Is parent?
|
|
||||||
childItem.setData(j[3], 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)
|
|
||||||
|
|
||||||
# Reset stylesheet in case something is found
|
|
||||||
if search_results:
|
|
||||||
self.searchLineEdit.setStyleSheet(
|
|
||||||
QtWidgets.QLineEdit.styleSheet(self))
|
|
||||||
|
|
||||||
# Or set to Red in case nothing is found
|
|
||||||
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
|
|
||||||
|
|
||||||
is_parent = self.searchResultsModel.data(index, QtCore.Qt.UserRole)
|
|
||||||
if is_parent:
|
|
||||||
return
|
|
||||||
|
|
||||||
chapter_number = 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.set_content(chapter_number, True)
|
|
||||||
if not self.are_we_doing_images_only:
|
|
||||||
self.set_cursor_position(
|
|
||||||
cursor_position, len(search_term))
|
|
||||||
|
|
||||||
def hide_mouse(self):
|
def hide_mouse(self):
|
||||||
self.contentView.viewport().setCursor(QtCore.Qt.BlankCursor)
|
self.contentView.viewport().setCursor(QtCore.Qt.BlankCursor)
|
||||||
|
|
||||||
@@ -839,20 +567,6 @@ class Tab(QtWidgets.QWidget):
|
|||||||
self.main_window.closeEvent()
|
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 PliantQGraphicsScene(QtWidgets.QGraphicsScene):
|
class PliantQGraphicsScene(QtWidgets.QGraphicsScene):
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
super(PliantQGraphicsScene, self).__init__(parent)
|
super(PliantQGraphicsScene, self).__init__(parent)
|
||||||
@@ -933,7 +647,8 @@ class DragDropTableView(QtWidgets.QTableView):
|
|||||||
self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
|
self.setDragDropMode(QtWidgets.QAbstractItemView.InternalMove)
|
||||||
self.setFrameShape(QtWidgets.QFrame.Box)
|
self.setFrameShape(QtWidgets.QFrame.Box)
|
||||||
self.setFrameShadow(QtWidgets.QFrame.Plain)
|
self.setFrameShadow(QtWidgets.QFrame.Plain)
|
||||||
self.setSizeAdjustPolicy(QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)
|
self.setSizeAdjustPolicy(
|
||||||
|
QtWidgets.QAbstractScrollArea.AdjustToContentsOnFirstShow)
|
||||||
self.setEditTriggers(
|
self.setEditTriggers(
|
||||||
QtWidgets.QAbstractItemView.DoubleClicked |
|
QtWidgets.QAbstractItemView.DoubleClicked |
|
||||||
QtWidgets.QAbstractItemView.EditKeyPressed |
|
QtWidgets.QAbstractItemView.EditKeyPressed |
|
||||||
|
Reference in New Issue
Block a user