diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index 3e573be026..0812325157 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -912,6 +912,10 @@ class ActionPickRandom(InterfaceActionBase): actual_plugin = 'calibre.gui2.actions.random:PickRandomAction' description = _('Choose a random book from your calibre library') +class ActionSortBy(InterfaceActionBase): + name = 'Sort By' + actual_plugin = 'calibre.gui2.actions.sort:SortByAction' + description = _('Sort the list of books') class ActionStore(InterfaceActionBase): name = 'Store' @@ -943,7 +947,7 @@ plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog, ActionSendToDevice, ActionHelp, ActionPreferences, ActionSimilarBooks, ActionAddToLibrary, ActionEditCollections, ActionMatchBooks, ActionChooseLibrary, ActionCopyToLibrary, ActionTweakEpub, ActionNextMatch, ActionStore, - ActionPluginUpdater, ActionPickRandom, ActionEditToC] + ActionPluginUpdater, ActionPickRandom, ActionEditToC, ActionSortBy] # }}} diff --git a/src/calibre/gui2/actions/sort.py b/src/calibre/gui2/actions/sort.py new file mode 100644 index 0000000000..14bd641020 --- /dev/null +++ b/src/calibre/gui2/actions/sort.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__license__ = 'GPL v3' +__copyright__ = '2013, Kovid Goyal ' + +from PyQt4.Qt import QToolButton, QAction, pyqtSignal, QIcon + +from calibre.gui2.actions import InterfaceAction +from calibre.utils.icu import sort_key + +class SortAction(QAction): + + sort_requested = pyqtSignal(object, object) + + def __init__(self, text, key, ascending, parent): + QAction.__init__(self, text, parent) + self.key, self.ascending = key, ascending + self.triggered.connect(self) + + def __call__(self): + self.sort_requested(self.key, self.ascending) + +class SortByAction(InterfaceAction): + + name = 'Sort By' + action_spec = (_('Sort By'), 'arrow-up.png', _('Sort the list of books'), None) + action_type = 'current' + popup_type = QToolButton.InstantPopup + action_add_menu = True + dont_add_to = frozenset([ + 'toolbar', 'toolbar-device', 'context-menu-device', 'toolbar-child', + 'menubar', 'menubar-device', 'context-menu-cover-browser']) + + def genesis(self): + self.sorted_icon = QIcon(I('ok.png')) + + def location_selected(self, loc): + self.qaction.setEnabled(loc == 'library') + + def update_menu(self): + menu = self.qaction.menu() + for action in menu.actions(): + action.sort_requested.disconnect() + menu.clear() + lv = self.gui.library_view + m = lv.model() + db = m.db + try: + sort_col, order = m.sorted_on + except TypeError: + sort_col, order = 'date', True + fm = db.field_metadata + name_map = {fm[k]['name']:k for k in fm.sortable_field_keys() if fm[k]['name']} + self._sactions = [] + for name in sorted(name_map, key=sort_key): + key = name_map[name] + if key == 'title': + continue + if key == 'sort': + name = _('Title') + if key == 'ondevice' and self.gui.device_connected is None: + continue + ascending = True + if key == sort_col: + name = _('%s [reverse current sort]') % name + ascending = not order + sac = SortAction(name, key, ascending, menu) + if key == sort_col: + sac.setIcon(self.sorted_icon) + sac.sort_requested.connect(self.sort_requested) + menu.addAction(sac) + + def sort_requested(self, key, ascending): + self.gui.library_view.sort_by_named_field(key, ascending) + + diff --git a/src/calibre/gui2/library/alternate_views.py b/src/calibre/gui2/library/alternate_views.py index 656d8baaff..ec3a788017 100644 --- a/src/calibre/gui2/library/alternate_views.py +++ b/src/calibre/gui2/library/alternate_views.py @@ -15,9 +15,10 @@ from Queue import Queue from functools import wraps, partial from PyQt4.Qt import ( - QListView, QSize, QStyledItemDelegate, QModelIndex, Qt, QImage, pyqtSignal, QTimer, - QPalette, QColor, QItemSelection, QPixmap, QMenu, QApplication, QMimeData, QIcon, - QUrl, QDrag, QPoint, QPainter, QRect, pyqtProperty, QPropertyAnimation, QEasingCurve) + QListView, QSize, QStyledItemDelegate, QModelIndex, Qt, QImage, pyqtSignal, + QTimer, QPalette, QColor, QItemSelection, QPixmap, QMenu, QApplication, + QMimeData, QUrl, QDrag, QPoint, QPainter, QRect, pyqtProperty, + QPropertyAnimation, QEasingCurve) from calibre import fit_image from calibre.gui2 import gprefs, config @@ -215,7 +216,6 @@ class AlternateViews(object): view.setModel(self.main_view._model) view.selectionModel().currentChanged.connect(self.slave_current_changed) view.selectionModel().selectionChanged.connect(self.slave_selection_changed) - view.sort_requested.connect(self.main_view.sort_by_named_field) view.files_dropped.connect(self.main_view.files_dropped) def show_view(self, key=None): @@ -440,7 +440,6 @@ def join_with_timeout(q, timeout=2): class GridView(QListView): update_item = pyqtSignal(object) - sort_requested = pyqtSignal(object, object) files_dropped = pyqtSignal(object) def __init__(self, parent): @@ -648,31 +647,17 @@ class GridView(QListView): def contextMenuEvent(self, event): if self.context_menu is not None: - lv = self.gui.library_view menu = self._temp_menu = QMenu(self) - sm = QMenu(_('Sort by'), menu) - db = self.model().db - for col in lv.visible_columns: - m = db.metadata_for_field(col) - last = self.model().sorted_on - ascending = True - extra = '' - if last[0] == col: - ascending = not last[1] - extra = ' [%s]' % _('reverse current sort') - ac = sm.addAction('%s%s' % (m.get('name', col), extra), partial(self.do_sort, col, ascending)) - if last[0] == col: - ac.setIcon(QIcon(I('ok.png'))) - + sac = self.gui.iactions['Sort By'] + sort_added = tuple(ac for ac in self.context_menu.actions() if ac is sac.qaction) + if not sort_added: + menu.addAction(sac.qaction) for ac in self.context_menu.actions(): menu.addAction(ac) - menu.addMenu(sm).setIcon(QIcon(I('arrow-up.png'))) + sac.update_menu() menu.popup(event.globalPos()) event.accept() - def do_sort(self, column, ascending): - self.sort_requested.emit(column, ascending) - def get_selected_ids(self): m = self.model() return [m.id(i) for i in self.selectionModel().selectedIndexes()] diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 0b22999b27..34716445cc 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -157,6 +157,7 @@ class BooksView(QTableView): # {{{ def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True): QTableView.__init__(self, parent) + self.gui = parent self.setProperty('highlight_current_item', 150) self.row_sizing_done = False self.alternate_views = AlternateViews(self) @@ -713,6 +714,10 @@ class BooksView(QTableView): # {{{ self.edit_collections_action = edit_collections_action def contextMenuEvent(self, event): + sac = self.gui.iactions['Sort By'] + sort_added = tuple(ac for ac in self.context_menu.actions() if ac is sac.qaction) + if sort_added: + sac.update_menu() self.context_menu.popup(event.globalPos()) event.accept() # }}}