mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Viewer: Show search results grouped by section
This commit is contained in:
parent
3f739922b1
commit
1a39df6e83
@ -10,9 +10,9 @@ from threading import Thread
|
|||||||
|
|
||||||
import regex
|
import regex
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QCheckBox, QComboBox, QHBoxLayout, QIcon, QLabel, QListWidget, QListWidgetItem,
|
QCheckBox, QComboBox, QFont, QHBoxLayout, QIcon, QLabel, QStaticText, QStyle,
|
||||||
QStaticText, QStyle, QStyledItemDelegate, Qt, QToolButton, QVBoxLayout, QWidget,
|
QStyledItemDelegate, Qt, QToolButton, QTreeWidget, QTreeWidgetItem, QVBoxLayout,
|
||||||
pyqtSignal
|
QWidget, pyqtSignal
|
||||||
)
|
)
|
||||||
|
|
||||||
from calibre.ebooks.conversion.search_replace import REGEX_FLAGS
|
from calibre.ebooks.conversion.search_replace import REGEX_FLAGS
|
||||||
@ -457,6 +457,8 @@ class ResultsDelegate(QStyledItemDelegate): # {{{
|
|||||||
def paint(self, painter, option, index):
|
def paint(self, painter, option, index):
|
||||||
QStyledItemDelegate.paint(self, painter, option, index)
|
QStyledItemDelegate.paint(self, painter, option, index)
|
||||||
result = index.data(Qt.UserRole)
|
result = index.data(Qt.UserRole)
|
||||||
|
if not isinstance(result, SearchResult):
|
||||||
|
return
|
||||||
painter.save()
|
painter.save()
|
||||||
p = option.palette
|
p = option.palette
|
||||||
c = p.HighlightedText if option.state & QStyle.State_Selected else p.Text
|
c = p.HighlightedText if option.state & QStyle.State_Selected else p.Text
|
||||||
@ -479,80 +481,111 @@ class ResultsDelegate(QStyledItemDelegate): # {{{
|
|||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
class Results(QListWidget): # {{{
|
class Results(QTreeWidget): # {{{
|
||||||
|
|
||||||
show_search_result = pyqtSignal(object)
|
show_search_result = pyqtSignal(object)
|
||||||
current_result_changed = pyqtSignal(object)
|
current_result_changed = pyqtSignal(object)
|
||||||
|
|
||||||
def __init__(self, parent=None):
|
def __init__(self, parent=None):
|
||||||
QListWidget.__init__(self, parent)
|
QTreeWidget.__init__(self, parent)
|
||||||
|
self.setHeaderHidden(True)
|
||||||
self.setFocusPolicy(Qt.NoFocus)
|
self.setFocusPolicy(Qt.NoFocus)
|
||||||
self.delegate = ResultsDelegate(self)
|
self.delegate = ResultsDelegate(self)
|
||||||
self.setItemDelegate(self.delegate)
|
self.setItemDelegate(self.delegate)
|
||||||
self.itemClicked.connect(self.item_activated)
|
self.itemClicked.connect(self.item_activated)
|
||||||
self.blank_icon = QIcon(I('blank.png'))
|
self.blank_icon = QIcon(I('blank.png'))
|
||||||
|
self.not_found_icon = QIcon(I('dialog_warning.png'))
|
||||||
self.currentItemChanged.connect(self.current_item_changed)
|
self.currentItemChanged.connect(self.current_item_changed)
|
||||||
|
self.section_font = QFont(self.font())
|
||||||
|
self.section_font.setItalic(True)
|
||||||
|
self.section_map = {}
|
||||||
|
self.search_results = []
|
||||||
|
self.item_map = {}
|
||||||
|
|
||||||
def current_item_changed(self, item):
|
def current_item_changed(self, current, previous):
|
||||||
self.current_result_changed.emit(item.data(Qt.UserRole))
|
r = current.data(0, Qt.UserRole)
|
||||||
|
if isinstance(r, SearchResult):
|
||||||
|
self.current_result_changed.emit(r)
|
||||||
|
|
||||||
def add_result(self, result):
|
def add_result(self, result):
|
||||||
item = QListWidgetItem(' ', self)
|
section_title = _('Unknown')
|
||||||
item.setData(Qt.UserRole, result)
|
section_id = -1
|
||||||
item.setIcon(self.blank_icon)
|
toc_nodes = getattr(result, 'toc_nodes', ()) or ()
|
||||||
if getattr(result, 'file_name'):
|
if toc_nodes:
|
||||||
toc_nodes = result.toc_nodes
|
section_title = toc_nodes[-1].get('title') or _('Unknown')
|
||||||
if toc_nodes:
|
section_id = toc_nodes[-1].get('id')
|
||||||
lines = []
|
if section_id is None:
|
||||||
for i, node in enumerate(toc_nodes):
|
section_id = -1
|
||||||
lines.append(('\xa0\xa0' * i) + (node.get('title') or _('Unknown')))
|
section_key = section_id, section_title
|
||||||
tt = _('In section:') + '\n' + '\n'.join(lines)
|
section = self.section_map.get(section_key)
|
||||||
else:
|
if section is None:
|
||||||
tt = _('In internal file: {}').format(result.file_name)
|
section = QTreeWidgetItem([section_title], 1)
|
||||||
item.setData(Qt.ToolTipRole, tt)
|
section.setFlags(Qt.ItemIsEnabled)
|
||||||
return self.count()
|
section.setFont(0, self.section_font)
|
||||||
|
self.section_map[section_key] = section
|
||||||
|
self.addTopLevelItem(section)
|
||||||
|
section.setExpanded(True)
|
||||||
|
item = QTreeWidgetItem(section, [' '], 2)
|
||||||
|
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled | Qt.ItemNeverHasChildren)
|
||||||
|
item.setData(0, Qt.UserRole, result)
|
||||||
|
item.setData(0, Qt.UserRole + 1, len(self.search_results))
|
||||||
|
item.setIcon(0, self.blank_icon)
|
||||||
|
self.item_map[len(self.search_results)] = item
|
||||||
|
self.search_results.append(result)
|
||||||
|
return self.number_of_results
|
||||||
|
|
||||||
def item_activated(self):
|
def item_activated(self):
|
||||||
i = self.currentItem()
|
i = self.currentItem()
|
||||||
if i:
|
if i:
|
||||||
sr = i.data(Qt.UserRole)
|
sr = i.data(0, Qt.UserRole)
|
||||||
if not sr.is_hidden:
|
if isinstance(sr, SearchResult):
|
||||||
self.show_search_result.emit(sr)
|
if not sr.is_hidden:
|
||||||
|
self.show_search_result.emit(sr)
|
||||||
|
|
||||||
def find_next(self, previous):
|
def find_next(self, previous):
|
||||||
if self.count() < 1:
|
if self.number_of_results < 1:
|
||||||
return
|
return
|
||||||
i = self.currentRow()
|
item = self.currentItem()
|
||||||
|
if item is None:
|
||||||
|
return
|
||||||
|
i = int(item.data(0, Qt.UserRole + 1))
|
||||||
i += -1 if previous else 1
|
i += -1 if previous else 1
|
||||||
i %= self.count()
|
i %= self.number_of_results
|
||||||
self.setCurrentRow(i)
|
self.setCurrentItem(self.item_map[i])
|
||||||
self.item_activated()
|
self.item_activated()
|
||||||
|
|
||||||
def search_result_not_found(self, sr):
|
def search_result_not_found(self, sr):
|
||||||
for i in range(self.count()):
|
for i in range(self.number_of_results):
|
||||||
item = self.item(i)
|
item = self.item_map[i]
|
||||||
r = item.data(Qt.UserRole)
|
r = item.data(0, Qt.UserRole)
|
||||||
if r.is_result(sr):
|
if r.is_result(sr):
|
||||||
r.is_hidden = True
|
r.is_hidden = True
|
||||||
item.setIcon(QIcon(I('dialog_warning.png')))
|
item.setIcon(self.not_found_icon)
|
||||||
break
|
break
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def current_result_is_hidden(self):
|
def current_result_is_hidden(self):
|
||||||
item = self.currentItem()
|
item = self.currentItem()
|
||||||
if item and item.data(Qt.UserRole) and item.data(Qt.UserRole).is_hidden:
|
if item is not None:
|
||||||
return True
|
sr = item.data(0, Qt.UserRole)
|
||||||
|
if isinstance(sr, SearchResult) and sr.is_hidden:
|
||||||
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def number_of_results(self):
|
def number_of_results(self):
|
||||||
return self.count()
|
return len(self.search_results)
|
||||||
|
|
||||||
def clear_all_results(self):
|
def clear_all_results(self):
|
||||||
|
self.section_map = {}
|
||||||
|
self.item_map = {}
|
||||||
|
self.search_results = []
|
||||||
self.clear()
|
self.clear()
|
||||||
|
|
||||||
def select_first_result(self):
|
def select_first_result(self):
|
||||||
self.setCurrentRow(0)
|
if self.number_of_results:
|
||||||
|
item = self.item_map[0]
|
||||||
|
self.setCurrentItem(item)
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user