mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
f75f4db165
BIN
resources/images/marked-text.png
Normal file
BIN
resources/images/marked-text.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.8 KiB |
@ -370,8 +370,9 @@ class View:
|
||||
'''
|
||||
old_marked_ids = set(self.marked_ids)
|
||||
if not hasattr(id_dict, 'items'):
|
||||
# Simple list. Make it a dict of string 'true'
|
||||
self.marked_ids = dict.fromkeys(id_dict, 'true')
|
||||
# Simple list. Make it a dict entry of string 'true'
|
||||
self.marked_ids = {k: (self.marked_ids[k] if k in self.marked_ids else 'true')
|
||||
for k in id_dict}
|
||||
else:
|
||||
# Ensure that all the items in the dict are text
|
||||
self.marked_ids = {k: str(v) for k, v in iteritems(id_dict)}
|
||||
|
@ -198,7 +198,16 @@ class GoogleBooks(Source):
|
||||
goog = identifiers.get('google', None)
|
||||
if goog is not None:
|
||||
return ('google', goog, 'https://books.google.com/books?id=%s' % goog)
|
||||
# }}}
|
||||
|
||||
def id_from_url(self, url): # {{{
|
||||
url_pattern = '(?:http|https)://books\.google\.com/books\?id=(?P<id_>.+)'
|
||||
url_pattern = re.compile(url_pattern)
|
||||
id_ = url_pattern.search(url).group('id_')
|
||||
if id_:
|
||||
return ('google', id_)
|
||||
else:
|
||||
return None
|
||||
# }}}
|
||||
|
||||
def create_query(self, title=None, authors=None, identifiers={}, capitalize_isbn=False): # {{{
|
||||
|
@ -4,12 +4,81 @@
|
||||
__license__ = 'GPL v3'
|
||||
__copyright__ = '2013, Kovid Goyal <kovid at kovidgoyal.net>'
|
||||
|
||||
from collections import defaultdict
|
||||
from functools import partial
|
||||
|
||||
from qt.core import QTimer, QApplication, Qt, QEvent
|
||||
from qt.core import (QTimer, QApplication, Qt, QEvent, QDialog, QMenu, QIcon,
|
||||
QDialogButtonBox, QPushButton, QLabel, QGridLayout)
|
||||
|
||||
from calibre.gui2 import error_dialog
|
||||
from calibre.gui2.actions import InterfaceAction
|
||||
from calibre.gui2.widgets2 import HistoryComboBox
|
||||
from calibre.utils.icu import sort_key
|
||||
|
||||
|
||||
class MyHistoryComboBox(HistoryComboBox):
|
||||
# This is here so we can change the following two class variables without
|
||||
# affecting other users of the HistoryComboBox class
|
||||
max_history_items = 10
|
||||
min_history_entry_length = 1
|
||||
|
||||
|
||||
class MarkWithTextDialog(QDialog):
|
||||
|
||||
def __init__(self, gui):
|
||||
QDialog.__init__(self, parent=gui)
|
||||
self.gui = gui
|
||||
self.setWindowTitle(_('Mark books with text label'))
|
||||
layout = QGridLayout()
|
||||
layout.setColumnStretch(1, 10)
|
||||
self.setLayout(layout)
|
||||
|
||||
self.text_box = textbox = MyHistoryComboBox()
|
||||
textbox.initialize('mark_with_text')
|
||||
|
||||
history = textbox.all_items
|
||||
button_rows = min(4, len(history))
|
||||
for i in range(0, button_rows):
|
||||
if i == 0:
|
||||
layout.addWidget(QLabel(_('Recently used values:')), 0, 0, 1, 2)
|
||||
button = QPushButton()
|
||||
text = history[i]
|
||||
button.setText(text)
|
||||
button.clicked.connect(partial(self.button_pushed, text=text))
|
||||
row = i + 1
|
||||
layout.addWidget(button, row, 1)
|
||||
label = QLabel('&' + str(row))
|
||||
label.setBuddy(button)
|
||||
layout.addWidget(label, row, 0)
|
||||
if button_rows > 0:
|
||||
layout.addWidget(QLabel(_('Enter a value:')), button_rows+1, 0, 1, 2)
|
||||
label = QLabel('&' + str(button_rows+1))
|
||||
else:
|
||||
label = QLabel('')
|
||||
label.setBuddy(textbox)
|
||||
layout.addWidget(label, button_rows+2, 0, 1, 1)
|
||||
layout.addWidget(textbox, button_rows+2, 1)
|
||||
textbox.setFocus()
|
||||
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok |
|
||||
QDialogButtonBox.StandardButton.Cancel)
|
||||
button_box.accepted.connect(self.accept)
|
||||
button_box.rejected.connect(self.reject)
|
||||
layout.addWidget(button_box, button_rows+3, 0, 1, 2)
|
||||
|
||||
def text(self):
|
||||
return self.text_box.text()
|
||||
|
||||
def button_pushed(self, checked, text=''):
|
||||
self.text_box.setText(text)
|
||||
self.text_box.save_history()
|
||||
self.accept()
|
||||
|
||||
def accept(self):
|
||||
if not self.text_box.text():
|
||||
d = error_dialog(self.gui, _('Value cannot be empty'), _('You must provide a value'))
|
||||
d.exec_()
|
||||
else:
|
||||
super().accept()
|
||||
|
||||
|
||||
class MarkBooksAction(InterfaceAction):
|
||||
@ -49,12 +118,18 @@ class MarkBooksAction(InterfaceAction):
|
||||
self.toggle_ids(book_ids)
|
||||
|
||||
def genesis(self):
|
||||
self.search_icon = QIcon.ic('search.png')
|
||||
self.qaction.triggered.connect(self.toggle_selected)
|
||||
self.menu = m = self.qaction.menu()
|
||||
m.aboutToShow.connect(self.about_to_show_menu)
|
||||
ma = partial(self.create_menu_action, m)
|
||||
self.show_marked_action = a = ma('mark_with_text', _('Mark books with text label'), icon='marked-text.png')
|
||||
a.triggered.connect(self.mark_with_text)
|
||||
self.show_marked_action = a = ma('show-marked', _('Show marked books'), icon='search.png', shortcut='Shift+Ctrl+M')
|
||||
a.triggered.connect(self.show_marked)
|
||||
self.show_marked_with_text = QMenu(_('Show marked books with text label'))
|
||||
self.show_marked_with_text.setIcon(self.search_icon)
|
||||
m.addMenu(self.show_marked_with_text)
|
||||
self.clear_marked_action = a = ma('clear-all-marked', _('Clear all marked books'), icon='clear_left.png')
|
||||
a.triggered.connect(self.clear_all_marked)
|
||||
m.addSeparator()
|
||||
@ -86,9 +161,22 @@ class MarkBooksAction(InterfaceAction):
|
||||
|
||||
def about_to_show_menu(self):
|
||||
db = self.gui.current_db
|
||||
num = len(frozenset(db.data.marked_ids).intersection(db.new_api.all_book_ids()))
|
||||
marked_ids = db.data.marked_ids
|
||||
num = len(frozenset(marked_ids).intersection(db.new_api.all_book_ids()))
|
||||
text = _('Show marked book') if num == 1 else (_('Show marked books') + (' (%d)' % num))
|
||||
self.show_marked_action.setText(text)
|
||||
counts = dict()
|
||||
for v in marked_ids.values():
|
||||
counts[v] = counts.get(v, 0) + 1
|
||||
labels = sorted(counts.keys(), key=sort_key)
|
||||
self.show_marked_with_text.clear()
|
||||
if len(labels):
|
||||
self.show_marked_with_text.setEnabled(True)
|
||||
for t in labels:
|
||||
ac = self.show_marked_with_text.addAction(self.search_icon, f'{t} ({counts[t]})')
|
||||
ac.triggered.connect(partial(self.show_marked_text, txt=t))
|
||||
else:
|
||||
self.show_marked_with_text.setEnabled(False)
|
||||
|
||||
def location_selected(self, loc):
|
||||
enabled = loc == 'library'
|
||||
@ -116,6 +204,9 @@ class MarkBooksAction(InterfaceAction):
|
||||
def show_marked(self):
|
||||
self.gui.search.set_search_string('marked:true')
|
||||
|
||||
def show_marked_text(self, txt=None):
|
||||
self.gui.search.set_search_string(f'marked:"={txt}"')
|
||||
|
||||
def clear_all_marked(self):
|
||||
self.gui.current_db.data.set_marked_ids(())
|
||||
if str(self.gui.search.text()).startswith('marked:'):
|
||||
@ -139,3 +230,18 @@ class MarkBooksAction(InterfaceAction):
|
||||
else:
|
||||
mids.pop(book_id, None)
|
||||
db.data.set_marked_ids(mids)
|
||||
|
||||
def mark_with_text(self):
|
||||
book_ids = self._get_selected_ids()
|
||||
if not book_ids:
|
||||
return
|
||||
dialog = MarkWithTextDialog(self.gui)
|
||||
if dialog.exec_() != QDialog.DialogCode.Accepted:
|
||||
return
|
||||
txt = dialog.text()
|
||||
txt = txt if txt else 'true'
|
||||
db = self.gui.current_db
|
||||
mids = db.data.marked_ids.copy()
|
||||
for book_id in book_ids:
|
||||
mids[book_id] = txt
|
||||
db.data.set_marked_ids(mids)
|
||||
|
@ -232,6 +232,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
# remember that the cover grid view needs a larger version of the icon,
|
||||
# anyway)
|
||||
self.marked_icon = QIcon(I('marked.png'))
|
||||
self.marked_text_icon = QIcon(I('marked-text.png'))
|
||||
self.bool_blank_icon_as_icon = QIcon(self.bool_blank_icon)
|
||||
self.row_decoration = None
|
||||
self.device_connected = False
|
||||
@ -1072,7 +1073,12 @@ class BooksModel(QAbstractTableModel): # {{{
|
||||
return (section+1)
|
||||
if role == Qt.ItemDataRole.DecorationRole:
|
||||
try:
|
||||
return self.marked_icon if self.db.data.get_marked(self.db.data.index_to_id(section)) else self.row_decoration
|
||||
m = self.db.data.get_marked(self.db.data.index_to_id(section))
|
||||
if m:
|
||||
i = self.marked_icon if m == 'true' else self.marked_text_icon
|
||||
else:
|
||||
i = self.row_decoration
|
||||
return i
|
||||
except (ValueError, IndexError):
|
||||
pass
|
||||
return None
|
||||
|
@ -946,6 +946,8 @@ class BooksView(QTableView): # {{{
|
||||
# This is needed otherwise Qt does not always update the
|
||||
# viewport correctly. See https://bugs.launchpad.net/bugs/1404697
|
||||
self.row_header.viewport().update()
|
||||
# refresh the rows because there might be a composite that uses marked_books()
|
||||
self.model().refresh_rows(changed)
|
||||
else:
|
||||
# Marked items have either appeared or all been removed
|
||||
self.model().set_row_decoration(current_marked)
|
||||
|
Loading…
x
Reference in New Issue
Block a user