diff --git a/Changelog.yaml b/Changelog.yaml index 95ad28b12a..7dd78a3d63 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -30,7 +30,7 @@ - title: "Book details panel: Clicking an author name now searches Goodreads by default instead of Wikipedia. Can be changed in Preferences->Interface->Look & feel->Book details" - - title: "Kobo driver: Add support for the Kobo Auro H2O Edition 2" + - title: "Kobo driver: Add support for the Kobo Aura H2O Edition 2" bug fixes: - title: "PDF output: Fix a regression in the previous release that broke conversion to PDF for some files" @@ -75,7 +75,7 @@ - title: "Add API to pre-process image data in recipes easily" - - title: "Fetch e-book metadata: add option to use only a single metadata plugin" + - title: "fetch-ebook-metadata.exe: add option to use only a single metadata plugin" bug fixes: - title: "E-book viewer: Fix a regression in the previous release that broke printing from inside the viewer" @@ -152,7 +152,7 @@ - title: "PDF output: Fix a regression that broke PDF output for documents containing mathematics" tickets: [1673983] - - title: "E-book viewer: Fix a regression that broke the Clear recently read books' action in the viewer" + - title: 'E-book viewer: Fix a regression that broke the "Clear recently read books" action in the viewer' improved recipes: - Go Comics @@ -463,7 +463,7 @@ - title: "Add support for new Kobo firmware version 4.2" - - title: "Allow using Amazon_in, Amazon_au, Amazon_com identifiers in the Book details panel" + - title: "Allow using amazon_in, amazon_au, amazon_com identifiers in the Book details panel" tickets: [1649371] bug fixes: @@ -477,7 +477,7 @@ - title: "Edit book: Fix the 'Search ignoring markup tool' not ignoring comments/processing instructions, etc" tickets: [1651160] - - title: "CSS Transforms: Fix 'is'/'is not' rules not matching current color" + - title: "CSS Transforms: Fix 'is'/'is not' rules not matching 'currentColor'" tickets: [1650930] - title: "E-book viewer: Make the swipe up gesture move to next section instead of previous section" diff --git a/recipes/nytimes.recipe b/recipes/nytimes.recipe index ebe4b09379..3763804257 100644 --- a/recipes/nytimes.recipe +++ b/recipes/nytimes.recipe @@ -231,7 +231,8 @@ class NYTimes(BasicNewsRecipe): re.compile('commentCount'), 'lede-container', 'credit', - 'caption-video' + 'caption-video', + 'upshot-social' ]}), dict( attrs={'class': lambda x: x and 'related-coverage-marginalia' in x.split()}), diff --git a/recipes/nytimes_sub.recipe b/recipes/nytimes_sub.recipe index 94007285e7..317a55fdf9 100644 --- a/recipes/nytimes_sub.recipe +++ b/recipes/nytimes_sub.recipe @@ -231,7 +231,8 @@ class NYTimes(BasicNewsRecipe): re.compile('commentCount'), 'lede-container', 'credit', - 'caption-video' + 'caption-video', + 'upshot-social' ]}), dict( attrs={'class': lambda x: x and 'related-coverage-marginalia' in x.split()}), diff --git a/src/calibre/db/cli/cmd_add.py b/src/calibre/db/cli/cmd_add.py index 2e00cfb8a0..e5edcaf403 100644 --- a/src/calibre/db/cli/cmd_add.py +++ b/src/calibre/db/cli/cmd_add.py @@ -20,6 +20,7 @@ from calibre.utils.localization import canonicalize_lang readonly = False version = 0 # change this if you change signature of implementation() + def to_stream(data): ans = BytesIO(data[1]) ans.name = data[0] diff --git a/src/calibre/db/fields.py b/src/calibre/db/fields.py index e00307187b..036a04f771 100644 --- a/src/calibre/db/fields.py +++ b/src/calibre/db/fields.py @@ -25,6 +25,7 @@ from calibre.utils.localization import calibre_langcode_to_name def bool_sort_key(bools_are_tristate): return (lambda x:{True: 1, False: 2, None: 3}.get(x, 3)) if bools_are_tristate else lambda x:{True: 1, False: 2, None: 2}.get(x, 2) + IDENTITY = lambda x: x @@ -756,4 +757,3 @@ def create_field(name, table, bools_are_tristate, get_template_functions): elif table.metadata['datatype'] == 'series': cls = SeriesField return cls(name, table, bools_are_tristate, get_template_functions) - diff --git a/src/calibre/devices/kobo/driver.py b/src/calibre/devices/kobo/driver.py index 9bd21b1665..867079701a 100644 --- a/src/calibre/devices/kobo/driver.py +++ b/src/calibre/devices/kobo/driver.py @@ -1451,15 +1451,15 @@ class KOBOTOUCH(KOBO): def open_linux(self): super(KOBOTOUCH, self).open_linux() - + self.swap_drives_if_needed() def open_osx(self): # Just dump some info to the logs. super(KOBOTOUCH, self).open_osx() - # Wrap some debugging output in a try/except so that it unlikely to break things completely. - try: + # Wrap some debugging output in a try/except so that it unlikely to break things completely. + try: if DEBUG: from calibre.constants import plugins usbobserver, usbobserver_err = plugins['usbobserver'] diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index e6d83ce5e1..21d8d48168 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -201,6 +201,23 @@ class SearchBar(QFrame): # {{{ self.vl_sep.setFrameStyle(QFrame.VLine | QFrame.Sunken) l.addWidget(self.vl_sep) + parent.sort_sep = QFrame(self) + parent.sort_sep.setFrameStyle(QFrame.VLine | QFrame.Sunken) + parent.sort_sep.setVisible(False) + parent.sort_button = self.sort_button = sb = QToolButton(self) + sb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) + sb.setToolTip(_('Change how the displayed books are sorted')) + sb.setCursor(Qt.PointingHandCursor) + sb.setPopupMode(QToolButton.InstantPopup) + sb.setAutoRaise(True) + sb.setText(_('Sort')) + sb.setIcon(QIcon(I('sort.png'))) + sb.setMenu(QMenu()) + sb.menu().aboutToShow.connect(self.populate_sort_menu) + sb.setVisible(False) + l.addWidget(sb) + l.addWidget(parent.sort_sep) + x = parent.search = SearchBox2(self) x.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) x.setObjectName("search") @@ -230,6 +247,10 @@ class SearchBar(QFrame): # {{{ _('Do Quick Search (you can also press the Enter key)')) x = parent.highlight_only_button = QToolButton(self) + x.setAutoRaise(True) + x.setText(_('Highlight')) + x.setCursor(Qt.PointingHandCursor) + x.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) x.setIcon(QIcon(I('arrow-down.png'))) l.addWidget(x) @@ -270,23 +291,6 @@ class SearchBar(QFrame): # {{{ l.addWidget(x) x.setVisible(not tweaks['show_saved_search_box']) - parent.sort_sep = QFrame(self) - parent.sort_sep.setFrameStyle(QFrame.VLine | QFrame.Sunken) - parent.sort_sep.setVisible(False) - l.addWidget(parent.sort_sep) - parent.sort_button = self.sort_button = sb = QToolButton(self) - sb.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) - sb.setToolTip(_('Change how the displayed books are sorted')) - sb.setCursor(Qt.PointingHandCursor) - sb.setPopupMode(QToolButton.InstantPopup) - sb.setAutoRaise(True) - sb.setText(_('Sort')) - sb.setIcon(QIcon(I('sort.png'))) - sb.setMenu(QMenu()) - sb.menu().aboutToShow.connect(self.populate_sort_menu) - sb.setVisible(False) - l.addWidget(sb) - def populate_sort_menu(self): from calibre.gui2.ui import get_gui get_gui().iactions['Sort By'].update_menu(self.sort_button.menu()) diff --git a/src/calibre/gui2/search_box.py b/src/calibre/gui2/search_box.py index 3cf8bf6544..f244677eb2 100644 --- a/src/calibre/gui2/search_box.py +++ b/src/calibre/gui2/search_box.py @@ -492,10 +492,13 @@ class SearchBoxMixin(object): # {{{ self.focus_to_library() def set_highlight_only_button_icon(self): + b = self.highlight_only_button if config['highlight_search_matches']: - self.highlight_only_button.setIcon(QIcon(I('highlight_only_on.png'))) + b.setIcon(QIcon(I('highlight_only_on.png'))) + b.setText(_('Filter')) else: - self.highlight_only_button.setIcon(QIcon(I('highlight_only_off.png'))) + b.setIcon(QIcon(I('highlight_only_off.png'))) + b.setText(_('Highlight')) self.highlight_only_button.setVisible(gprefs['show_highlight_toggle_button']) self.library_view.model().set_highlight_only(config['highlight_search_matches']) diff --git a/src/calibre/gui2/tag_browser/ui.py b/src/calibre/gui2/tag_browser/ui.py index f6551569f7..0cd82af9de 100644 --- a/src/calibre/gui2/tag_browser/ui.py +++ b/src/calibre/gui2/tag_browser/ui.py @@ -341,6 +341,22 @@ class TagBrowserMixin(object): # {{{ # }}} +class FindBox(HistoryLineEdit): # {{{ + + def keyPressEvent(self, event): + k = event.key() + if k not in (Qt.Key_Up, Qt.Key_Down): + return HistoryLineEdit.keyPressEvent(self, event) + self.blockSignals(True) + if k == Qt.Key_Down and self.currentIndex() == 0 and not self.lineEdit().text(): + self.setCurrentIndex(1), self.setCurrentIndex(0) + event.accept() + else: + HistoryLineEdit.keyPressEvent(self, event) + self.blockSignals(False) +# }}} + + class TagBrowserBar(QWidget): # {{{ def __init__(self, parent): @@ -365,7 +381,7 @@ class TagBrowserBar(QWidget): # {{{ self.label = la = QLabel(self) la.setText(_('Tag browser')) - self.item_search = HistoryLineEdit(parent) + self.item_search = FindBox(parent) self.item_search.setMinimumContentsLength(5) self.item_search.setSizeAdjustPolicy(self.item_search.AdjustToMinimumContentsLengthWithIcon) self.item_search.initialize('tag_browser_search') @@ -380,26 +396,24 @@ class TagBrowserBar(QWidget): # {{{ ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser find box', - _('Find item'), default_keys=(), + _('Find next match'), default_keys=(), action=ac, group=_('Tag browser')) ac.triggered.connect(self.set_focus_to_find_box) self.search_button = QToolButton() self.search_button.setCursor(Qt.PointingHandCursor) - self.search_button.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) self.search_button.setIcon(QIcon(I('search.png'))) - self.search_button.setText(_('Find')) self.search_button.setToolTip(_('Find the first/next matching item')) ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser find button', - _('Find button'), default_keys=(), + _('Find in Tag browser'), default_keys=(), action=ac, group=_('Tag browser')) ac.triggered.connect(self.search_button.click) self.toggle_search_button = b = QToolButton(self) le = self.item_search.lineEdit() - le.addAction(QIcon(I('window-close.png')), le.LeadingPosition).triggered.connect(self.toggle_search_button.click) + le.addAction(QIcon(I('window-close.png')), le.LeadingPosition).triggered.connect(self.close_find_box) b.setCursor(Qt.PointingHandCursor) b.setIcon(QIcon(I('search.png'))) b.setCheckable(True) @@ -409,6 +423,11 @@ class TagBrowserBar(QWidget): # {{{ b.toggled.connect(self.update_searchbar_state) self.update_searchbar_state() + def close_find_box(self): + self.item_search.setCurrentIndex(0) + self.item_search.setCurrentText('') + self.toggle_search_button.click() + def set_focus_to_find_box(self): self.toggle_search_button.setChecked(True) self.item_search.setFocus() @@ -489,7 +508,7 @@ class TagBrowserWidget(QWidget): # {{{ ac = QAction(parent) parent.addAction(ac) parent.keyboard.register_shortcut('tag browser alter', - _('Alter Tag browser'), default_keys=(), + _('Change Tag browser'), default_keys=(), action=ac, group=_('Tag browser')) ac.triggered.connect(l.showMenu) diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py index 9e9e3130a5..2a47fc5823 100644 --- a/src/calibre/utils/formatter.py +++ b/src/calibre/utils/formatter.py @@ -12,8 +12,7 @@ import re, string, traceback from calibre import prints from calibre.constants import DEBUG -from calibre.utils.formatter_functions import formatter_functions, compile_user_function -from calibre.utils.config import tweaks +from calibre.utils.formatter_functions import formatter_functions class _Parser(object): @@ -174,6 +173,7 @@ class _Parser(object): else: self.error(_('expression is not function or constant')) + class TemplateFormatter(string.Formatter): ''' Provides a format function that substitutes '' for any missing value diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index bd3d20ae6e..c5feaf0688 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -1635,6 +1635,7 @@ class UserFunction(FormatterUserFunction): cls = locals_['UserFunction'](name, doc, arg_count, eval_func) return cls + def compile_user_template_functions(funcs): compiled_funcs = {} for func in funcs: @@ -1653,6 +1654,7 @@ def compile_user_template_functions(funcs): traceback.print_exc() return compiled_funcs + def load_user_template_functions(library_uuid, funcs, precompiled_user_functions=None): unload_user_template_functions(library_uuid) if precompiled_user_functions: @@ -1661,5 +1663,6 @@ def load_user_template_functions(library_uuid, funcs, precompiled_user_functions compiled_funcs = compile_user_template_functions(funcs) formatter_functions().register_functions(library_uuid, compiled_funcs.values()) + def unload_user_template_functions(library_uuid): formatter_functions().unregister_functions(library_uuid)