diff --git a/src/calibre/gui2/dialogs/quickview.py b/src/calibre/gui2/dialogs/quickview.py index aff37ea152..3a69368730 100644 --- a/src/calibre/gui2/dialogs/quickview.py +++ b/src/calibre/gui2/dialogs/quickview.py @@ -5,11 +5,13 @@ __docformat__ = 'restructuredtext en' from PyQt4.Qt import (Qt, QDialog, QAbstractItemView, QTableWidgetItem, - QListWidgetItem, QByteArray, QModelIndex, QCoreApplication) + QListWidgetItem, QByteArray, QCoreApplication, + QApplication) +from calibre.customize.ui import find_plugin +from calibre.gui2 import gprefs from calibre.gui2.dialogs.quickview_ui import Ui_Quickview from calibre.utils.icu import sort_key -from calibre.gui2 import gprefs class TableItem(QTableWidgetItem): ''' @@ -55,8 +57,9 @@ class Quickview(QDialog, Ui_Quickview): self.is_closed = False self.current_book_id = None self.current_key = None - self.use_current_key_for_next_refresh = False self.last_search = None + self.current_column = None + self.current_item = None self.items.setSelectionMode(QAbstractItemView.SingleSelection) self.items.currentTextChanged.connect(self.item_selected) @@ -87,16 +90,24 @@ class Quickview(QDialog, Ui_Quickview): # Add the data self.refresh(row) - self.view.selectionModel().currentChanged[QModelIndex,QModelIndex].connect(self.slave) + self.view.clicked.connect(self.slave) QCoreApplication.instance().aboutToQuit.connect(self.save_state) self.search_button.clicked.connect(self.do_search) + view.model().new_bookdisplay_data.connect(self.book_was_changed) # search button def do_search(self): if self.last_search is not None: - self.use_current_key_for_next_refresh = True self.gui.search.set_search_string(self.last_search) + # Called when book information is changed in the library view. Make that + # book current. This means that prev and next in edit metadata will move + # the current book. + def book_was_changed(self, mi): + if self.is_closed or self.current_column is None: + return + self.refresh(self.view.model().index(self.db.row(mi.id), self.current_column)) + # clicks on the items listWidget def item_selected(self, txt): self.fill_in_books_box(unicode(txt)) @@ -104,22 +115,15 @@ class Quickview(QDialog, Ui_Quickview): # Given a cell in the library view, display the information def refresh(self, idx): bv_row = idx.row() - key = self.view.model().column_map[idx.column()] - + self.current_column = idx.column() + key = self.view.model().column_map[self.current_column] book_id = self.view.model().id(bv_row) - # Double-clicking on a book to show it in the library view will result - # in a signal emitted for column 1 of the book row. Use the original - # column for this signal. - if self.use_current_key_for_next_refresh: + # Only show items for categories + if not self.db.field_metadata[key]['is_category']: + if self.current_key is None: + return key = self.current_key - self.use_current_key_for_next_refresh = False - else: - # Only show items for categories - if not self.db.field_metadata[key]['is_category']: - if self.current_key is None: - return - key = self.current_key self.items_label.setText('{0} ({1})'.format( self.db.field_metadata[key]['name'], key)) @@ -147,6 +151,7 @@ class Quickview(QDialog, Ui_Quickview): self.items.blockSignals(False) def fill_in_books_box(self, selected_item): + self.current_item = selected_item # Do a bit of fix-up on the items so that the search works. if selected_item.startswith('.'): sv = '.' + selected_item @@ -162,19 +167,26 @@ class Quickview(QDialog, Ui_Quickview): select_item = None self.books_table.setSortingEnabled(False) + tt = ('

' + + _('Double-click on a book to change the selection in the library view. ' + 'Shift- or control-double-click to edit the metadata of a book') + + '

') for row, b in enumerate(books): mi = self.db.get_metadata(b, index_is_id=True, get_user_categories=False) a = TableItem(mi.title, mi.title_sort) a.setData(Qt.UserRole, b) + a.setToolTip(tt) self.books_table.setItem(row, 0, a) if b == self.current_book_id: select_item = a a = TableItem(' & '.join(mi.authors), mi.author_sort) + a.setToolTip(tt) self.books_table.setItem(row, 1, a) series = mi.format_field('series')[1] if series is None: series = '' a = TableItem(series, series) + a.setToolTip(tt) self.books_table.setItem(row, 2, a) self.books_table.setRowHeight(row, self.books_table_row_height) @@ -201,11 +213,16 @@ class Quickview(QDialog, Ui_Quickview): self.save_state() def book_doubleclicked(self, row, column): - self.use_current_key_for_next_refresh = True - self.view.select_rows([self.books_table.item(row, 0).data(Qt.UserRole).toInt()[0]]) + book_id = self.books_table.item(row, 0).data(Qt.UserRole).toInt()[0] + self.view.select_rows([book_id]) + modifiers = int(QApplication.keyboardModifiers()) + if modifiers in (Qt.CTRL, Qt.SHIFT): + em = find_plugin('Edit Metadata') + if em is not None: + em.actual_plugin_.edit_metadata(None) # called when a book is clicked on the library view - def slave(self, current, previous): + def slave(self, current): if self.is_closed: return self.refresh(current) diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py index 4c1cec6462..63264e6379 100644 --- a/src/calibre/utils/formatter_functions.py +++ b/src/calibre/utils/formatter_functions.py @@ -12,7 +12,7 @@ import inspect, re, traceback from calibre.utils.titlecase import titlecase from calibre.utils.icu import capitalize, strcmp, sort_key -from calibre.utils.date import parse_date, format_date +from calibre.utils.date import parse_date, format_date, now, UNDEFINED_DATE class FormatterFunctions(object): @@ -579,7 +579,7 @@ class BuiltinSubitems(BuiltinFormatterFunction): class BuiltinFormatDate(BuiltinFormatterFunction): name = 'format_date' arg_count = 2 - category = 'Get values from metadata' + category = 'Date functions' __doc__ = doc = _('format_date(val, format_string) -- format the value, ' 'which must be a date, using the format_string, returning a string. ' 'The formatting codes are: ' @@ -754,6 +754,39 @@ class BuiltinMergeLists(BuiltinFormatterFunction): res.append(i) return ', '.join(sorted(res, key=sort_key)) +class BuiltinToday(BuiltinFormatterFunction): + name = 'today' + arg_count = 0 + category = 'Date functions' + __doc__ = doc = _('today() -- ' + 'return a date string for today. This value is designed for use in ' + 'format_date or days_between, but can be manipulated like any ' + 'other string. The date is in ISO format.') + def evaluate(self, formatter, kwargs, mi, locals): + return format_date(now(), 'iso') + +class BuiltinDaysBetween(BuiltinFormatterFunction): + name = 'days_between' + arg_count = 2 + category = 'Date functions' + __doc__ = doc = _('days_between(date1, date2) -- ' + 'return the number of days between date1 and date2. The number is ' + 'positive if date1 is greater than date2, otherwise negative. If ' + 'either date1 or date2 are not dates, the function returns the ' + 'empty string.') + def evaluate(self, formatter, kwargs, mi, locals, date1, date2): + try: + d1 = parse_date(date1) + if d1 == UNDEFINED_DATE: + return '' + d2 = parse_date(date2) + if d2 == UNDEFINED_DATE: + return '' + except: + return '' + i = d1 - d2 + return str(i.days) + builtin_add = BuiltinAdd() builtin_and = BuiltinAnd() @@ -763,6 +796,7 @@ builtin_capitalize = BuiltinCapitalize() builtin_cmp = BuiltinCmp() builtin_contains = BuiltinContains() builtin_count = BuiltinCount() +builtin_days_between= BuiltinDaysBetween() builtin_divide = BuiltinDivide() builtin_eval = BuiltinEval() builtin_first_non_empty = BuiltinFirstNonEmpty() @@ -795,6 +829,7 @@ builtin_switch = BuiltinSwitch() builtin_template = BuiltinTemplate() builtin_test = BuiltinTest() builtin_titlecase = BuiltinTitlecase() +builtin_today = BuiltinToday() builtin_uppercase = BuiltinUppercase() class FormatterUserFunction(FormatterFunction):