mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Annots browser: Allow browsing upto 4096 annotations with no search query
This commit is contained in:
parent
1f3b5993b7
commit
21f3348ab7
@ -1820,12 +1820,49 @@ class DB(object):
|
|||||||
raise FTSQueryError(fts_engine_query, query, e)
|
raise FTSQueryError(fts_engine_query, query, e)
|
||||||
|
|
||||||
def all_annotations_for_book(self, book_id):
|
def all_annotations_for_book(self, book_id):
|
||||||
for (fmt, user_type, user, data) in self.execute('SELECT format, user_type, user, annot_data FROM annotations WHERE book=?', (book_id,)):
|
for (fmt, user_type, user, data) in self.execute('SELECT id, book, format, user_type, user, annot_data FROM annotations WHERE book=?', (book_id,)):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
yield {'format': fmt, 'user_type': user_type, 'user': user, 'annotation': json.loads(data)}
|
yield {'format': fmt, 'user_type': user_type, 'user': user, 'annotation': json.loads(data)}
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def all_annotations(self, restrict_to_user=None, limit=None, annotation_type=None):
|
||||||
|
ls = json.loads
|
||||||
|
q = 'SELECT id, book, format, user_type, user, annot_data FROM annotations'
|
||||||
|
data = []
|
||||||
|
if restrict_to_user or annotation_type:
|
||||||
|
q += ' WHERE '
|
||||||
|
if restrict_to_user is not None:
|
||||||
|
data.extend(restrict_to_user)
|
||||||
|
q += ' user_type = ? AND user = ?'
|
||||||
|
if annotation_type:
|
||||||
|
data.append(annotation_type)
|
||||||
|
q += ' annot_type = ? '
|
||||||
|
q += ' ORDER BY timestamp'
|
||||||
|
if limit is not None:
|
||||||
|
q += ' LIMIT %d' % limit
|
||||||
|
for (rowid, book_id, fmt, user_type, user, annot_data) in self.execute(q, tuple(data)):
|
||||||
|
try:
|
||||||
|
annot = ls(annot_data)
|
||||||
|
atype = annot['type']
|
||||||
|
except Exception:
|
||||||
|
continue
|
||||||
|
text = ''
|
||||||
|
if atype == 'bookmark':
|
||||||
|
text = annot['title']
|
||||||
|
elif atype == 'highlight':
|
||||||
|
text = annot.get('highlighted_text') or ''
|
||||||
|
yield {
|
||||||
|
'id': rowid,
|
||||||
|
'book_id': book_id,
|
||||||
|
'format': fmt,
|
||||||
|
'user_type': user_type,
|
||||||
|
'user': user,
|
||||||
|
'text': text,
|
||||||
|
'annotation': annot,
|
||||||
|
}
|
||||||
|
|
||||||
def all_annotation_users(self):
|
def all_annotation_users(self):
|
||||||
return self.execute('SELECT DISTINCT user_type, user FROM annotations')
|
return self.execute('SELECT DISTINCT user_type, user FROM annotations')
|
||||||
|
|
||||||
|
@ -2310,6 +2310,10 @@ class Cache(object):
|
|||||||
def all_annotation_types(self):
|
def all_annotation_types(self):
|
||||||
return tuple(self.backend.all_annotation_types())
|
return tuple(self.backend.all_annotation_types())
|
||||||
|
|
||||||
|
@read_api
|
||||||
|
def all_annotations(self, restrict_to_user=None, limit=None, annotation_type=None):
|
||||||
|
return tuple(self.backend.all_annotations(restrict_to_user, limit, annotation_type))
|
||||||
|
|
||||||
@read_api
|
@read_api
|
||||||
def search_annotations(
|
def search_annotations(
|
||||||
self,
|
self,
|
||||||
|
@ -9,7 +9,7 @@ from textwrap import fill
|
|||||||
|
|
||||||
from PyQt5.Qt import (
|
from PyQt5.Qt import (
|
||||||
QApplication, QCheckBox, QComboBox, QCursor, QFont, QHBoxLayout, QIcon, QLabel,
|
QApplication, QCheckBox, QComboBox, QCursor, QFont, QHBoxLayout, QIcon, QLabel,
|
||||||
QPalette, QPushButton, QSize, QSplitter, Qt, QTextBrowser, QToolButton,
|
QPalette, QPushButton, QSize, QSplitter, Qt, QTextBrowser, QTimer, QToolButton,
|
||||||
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, pyqtSignal
|
QTreeWidget, QTreeWidgetItem, QVBoxLayout, QWidget, pyqtSignal
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -43,16 +43,17 @@ class BusyCursor(object):
|
|||||||
class AnnotsResultsDelegate(ResultsDelegate):
|
class AnnotsResultsDelegate(ResultsDelegate):
|
||||||
|
|
||||||
add_ellipsis = False
|
add_ellipsis = False
|
||||||
|
emphasize_text = True
|
||||||
|
|
||||||
def result_data(self, result):
|
def result_data(self, result):
|
||||||
if not isinstance(result, dict):
|
if not isinstance(result, dict):
|
||||||
return None, None, None, None
|
return None, None, None, None
|
||||||
full_text = result['text'].replace('0x1f', ' ')
|
full_text = result['text'].replace('\x1f', ' ')
|
||||||
parts = full_text.split('\x1d')
|
parts = full_text.split('\x1d', 2)
|
||||||
before = after = ''
|
before = after = ''
|
||||||
if len(parts) > 2:
|
if len(parts) > 2:
|
||||||
before, text = parts[:2]
|
before, text = parts[:2]
|
||||||
after = ' '.join(parts[2:]).replace('\x1d', '')
|
after = parts[2].replace('\x1d', '')
|
||||||
elif len(parts) == 2:
|
elif len(parts) == 2:
|
||||||
before, text = parts
|
before, text = parts
|
||||||
else:
|
else:
|
||||||
@ -82,8 +83,9 @@ class ResultsList(QTreeWidget):
|
|||||||
if isinstance(r, dict):
|
if isinstance(r, dict):
|
||||||
self.open_annotation.emit(r['book_id'], r['format'], r['annotation'])
|
self.open_annotation.emit(r['book_id'], r['format'], r['annotation'])
|
||||||
|
|
||||||
def set_results(self, results):
|
def set_results(self, results, emphasize_text):
|
||||||
self.clear()
|
self.clear()
|
||||||
|
self.delegate.emphasize_text = emphasize_text
|
||||||
self.number_of_results = 0
|
self.number_of_results = 0
|
||||||
self.item_map = []
|
self.item_map = []
|
||||||
book_id_map = {}
|
book_id_map = {}
|
||||||
@ -191,8 +193,12 @@ class Restrictions(QWidget):
|
|||||||
tb.clear()
|
tb.clear()
|
||||||
tb.addItem(' ', ' ')
|
tb.addItem(' ', ' ')
|
||||||
for user_type, user in db.all_annotation_users():
|
for user_type, user in db.all_annotation_users():
|
||||||
q = '{}: {}'.format(user_type, user)
|
q = display_name = '{}: {}'.format(user_type, user)
|
||||||
tb.addItem(q, '{}:{}'.format(user_type, user))
|
if q == 'web: *':
|
||||||
|
display_name = _('Anonymous content server users')
|
||||||
|
elif q == 'local: viewer':
|
||||||
|
display_name = _('Local viewer users')
|
||||||
|
tb.addItem(display_name, '{}:{}'.format(user_type, user))
|
||||||
if before:
|
if before:
|
||||||
row = tb.findData(before)
|
row = tb.findData(before)
|
||||||
if row > -1:
|
if row > -1:
|
||||||
@ -218,7 +224,7 @@ class BrowsePanel(QWidget):
|
|||||||
l.addLayout(h)
|
l.addLayout(h)
|
||||||
self.search_box = sb = SearchBox(self)
|
self.search_box = sb = SearchBox(self)
|
||||||
sb.initialize('library-annotations-browser-search-box')
|
sb.initialize('library-annotations-browser-search-box')
|
||||||
sb.cleared.connect(self.cleared)
|
sb.cleared.connect(self.cleared, type=Qt.QueuedConnection)
|
||||||
sb.lineEdit().returnPressed.connect(self.show_next)
|
sb.lineEdit().returnPressed.connect(self.show_next)
|
||||||
sb.lineEdit().setPlaceholderText(_('Enter words to search for'))
|
sb.lineEdit().setPlaceholderText(_('Enter words to search for'))
|
||||||
h.addWidget(sb)
|
h.addWidget(sb)
|
||||||
@ -251,45 +257,46 @@ class BrowsePanel(QWidget):
|
|||||||
db = current_db()
|
db = current_db()
|
||||||
self.search_box.setFocus(Qt.OtherFocusReason)
|
self.search_box.setFocus(Qt.OtherFocusReason)
|
||||||
self.restrictions.re_initialize(db)
|
self.restrictions.re_initialize(db)
|
||||||
self.cleared()
|
self.current_query = None
|
||||||
|
self.results_list.clear()
|
||||||
|
|
||||||
def sizeHint(self):
|
def sizeHint(self):
|
||||||
return QSize(450, 600)
|
return QSize(450, 600)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def restrict_to_user(self):
|
||||||
|
user = self.restrictions.user_box.currentData()
|
||||||
|
if user and ':' in user:
|
||||||
|
return user.split(':', 1)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def effective_query(self):
|
def effective_query(self):
|
||||||
text = self.search_box.lineEdit().text().strip()
|
text = self.search_box.lineEdit().text().strip()
|
||||||
if not text:
|
|
||||||
return None
|
|
||||||
atype = self.restrictions.types_box.currentData()
|
atype = self.restrictions.types_box.currentData()
|
||||||
if not atype or not atype.strip():
|
|
||||||
atype = None
|
|
||||||
user = self.restrictions.user_box.currentData()
|
|
||||||
restrict_to_user = None
|
|
||||||
if user and ':' in user:
|
|
||||||
restrict_to_user = user.split(':', 1)
|
|
||||||
return {
|
return {
|
||||||
'fts_engine_query': text,
|
'fts_engine_query': text,
|
||||||
'annotation_type': atype,
|
'annotation_type': (atype or '').strip(),
|
||||||
'restrict_to_user': restrict_to_user,
|
'restrict_to_user': self.restrict_to_user,
|
||||||
'use_stemming': bool(self.use_stemmer.isChecked()),
|
'use_stemming': bool(self.use_stemmer.isChecked()),
|
||||||
}
|
}
|
||||||
|
|
||||||
def cleared(self):
|
def cleared(self):
|
||||||
self.current_query = None
|
self.current_query = None
|
||||||
self.results_list.clear()
|
self.effective_query_changed()
|
||||||
|
|
||||||
def do_find(self, backwards=False):
|
def do_find(self, backwards=False):
|
||||||
q = self.effective_query
|
q = self.effective_query
|
||||||
if not q:
|
|
||||||
return
|
|
||||||
if q == self.current_query:
|
if q == self.current_query:
|
||||||
self.results_list.show_next(backwards)
|
self.results_list.show_next(backwards)
|
||||||
return
|
return
|
||||||
with BusyCursor():
|
with BusyCursor():
|
||||||
db = current_db()
|
db = current_db()
|
||||||
results = db.search_annotations(highlight_start='\x1d', highlight_end='\x1d', snippet_size=64, **q)
|
if not q['fts_engine_query']:
|
||||||
self.results_list.set_results(results)
|
results = db.all_annotations(restrict_to_user=q['restrict_to_user'], limit=4096, annotation_type=q['annotation_type'])
|
||||||
|
else:
|
||||||
|
results = db.search_annotations(highlight_start='\x1d', highlight_end='\x1d', snippet_size=64, **q)
|
||||||
|
|
||||||
|
self.results_list.set_results(results, bool(q['fts_engine_query']))
|
||||||
self.current_query = q
|
self.current_query = q
|
||||||
|
|
||||||
def effective_query_changed(self):
|
def effective_query_changed(self):
|
||||||
@ -454,6 +461,8 @@ class AnnotationsBrowser(Dialog):
|
|||||||
h.addWidget(us), h.addStretch(10), h.addWidget(self.bb)
|
h.addWidget(us), h.addStretch(10), h.addWidget(self.bb)
|
||||||
|
|
||||||
def show_dialog(self):
|
def show_dialog(self):
|
||||||
|
if self.browse_panel.current_query is None:
|
||||||
|
QTimer.singleShot(80, self.browse_panel.effective_query_changed)
|
||||||
if self.parent() is None:
|
if self.parent() is None:
|
||||||
self.exec_()
|
self.exec_()
|
||||||
else:
|
else:
|
||||||
|
@ -17,6 +17,7 @@ from calibre.gui2.widgets2 import HistoryComboBox
|
|||||||
class ResultsDelegate(QStyledItemDelegate): # {{{
|
class ResultsDelegate(QStyledItemDelegate): # {{{
|
||||||
|
|
||||||
add_ellipsis = True
|
add_ellipsis = True
|
||||||
|
emphasize_text = True
|
||||||
|
|
||||||
def result_data(self, result):
|
def result_data(self, result):
|
||||||
if not hasattr(result, 'is_hidden'):
|
if not hasattr(result, 'is_hidden'):
|
||||||
@ -37,8 +38,11 @@ class ResultsDelegate(QStyledItemDelegate): # {{{
|
|||||||
c = p.color(group, c)
|
c = p.color(group, c)
|
||||||
painter.setPen(c)
|
painter.setPen(c)
|
||||||
font = option.font
|
font = option.font
|
||||||
emphasis_font = QFont(font)
|
if self.emphasize_text:
|
||||||
emphasis_font.setBold(True)
|
emphasis_font = QFont(font)
|
||||||
|
emphasis_font.setBold(True)
|
||||||
|
else:
|
||||||
|
emphasis_font = font
|
||||||
flags = Qt.AlignTop | Qt.TextSingleLine | Qt.TextIncludeTrailingSpaces
|
flags = Qt.AlignTop | Qt.TextSingleLine | Qt.TextIncludeTrailingSpaces
|
||||||
rect = option.rect.adjusted(option.decorationSize.width() + 4 if is_hidden else 0, 0, 0, 0)
|
rect = option.rect.adjusted(option.decorationSize.width() + 4 if is_hidden else 0, 0, 0, 0)
|
||||||
painter.setClipRect(rect)
|
painter.setClipRect(rect)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user