From 49686b709316277277d98199ba8bfb1bdb848b5c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 4 Oct 2013 18:01:08 +0530 Subject: [PATCH] Indicate marked books in the main book list as well Books marked with the Temp Marker plugin will now have an icon next to their row number to indicate they have been marked in the main book list. --- src/calibre/db/view.py | 12 +++++++++++- src/calibre/gui2/library/alternate_views.py | 2 +- src/calibre/gui2/library/models.py | 6 +++++- src/calibre/gui2/library/views.py | 21 ++++++++++++++++++++- 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/src/calibre/db/view.py b/src/calibre/db/view.py index 80453e6878..74242fcf66 100644 --- a/src/calibre/db/view.py +++ b/src/calibre/db/view.py @@ -77,6 +77,7 @@ class View(object): def __init__(self, cache): self.cache = cache self.marked_ids = {} + self.marked_listeners = {} self.search_restriction_book_count = 0 self.search_restriction = self.base_restriction = '' self.search_restriction_name = self.base_restriction_name = '' @@ -127,6 +128,9 @@ class View(object): self.full_map_is_sorted = True self.sort_history = [('id', True)] + def add_marked_listener(self, func): + self.marked_listeners[id(func)] = weakref.ref(func) + def add_to_sort_history(self, items): self.sort_history = uniq((list(items) + list(self.sort_history)), operator.itemgetter(0))[:tweaks['maximum_resort_levels']] @@ -370,7 +374,13 @@ class View(object): id_dict.itervalues()))) # This invalidates all searches in the cache even though the cache may # be shared by multiple views. This is not ideal, but... - self.cache.clear_search_caches(old_marked_ids | set(self.marked_ids)) + cmids = set(self.marked_ids) + self.cache.clear_search_caches(old_marked_ids | cmids) + if old_marked_ids != cmids: + for funcref in self.marked_listeners.itervalues(): + func = funcref() + if func is not None: + func(old_marked_ids, cmids) def refresh(self, field=None, ascending=True, clear_caches=True, do_search=True): self._map = tuple(sorted(self.cache.all_book_ids())) diff --git a/src/calibre/gui2/library/alternate_views.py b/src/calibre/gui2/library/alternate_views.py index 87c6c30fa2..c4a0b79c3a 100644 --- a/src/calibre/gui2/library/alternate_views.py +++ b/src/calibre/gui2/library/alternate_views.py @@ -422,7 +422,7 @@ class CoverDelegate(QStyledItemDelegate): try: p = self.marked_emblem except AttributeError: - p = self.marked_emblem = QPixmap(I('rating.png')).scaled(48, 48, transformMode=Qt.SmoothTransformation) + p = self.marked_emblem = m.marked_icon.pixmap(48, 48) drect = QRect(orect) drect.setLeft(drect.left() + right_adjust) drect.setRight(drect.left() + p.width()) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 1345ae24cd..811c186c20 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -111,7 +111,7 @@ class ColumnIcon(object): # {{{ d = os.path.join(config_dir, 'cc_icons', icon) if (os.path.exists(d)): bm = QPixmap(d) - bm = bm.scaled(128, 128, aspectRatioMode= Qt.KeepAspectRatio, + bm = bm.scaled(128, 128, aspectRatioMode=Qt.KeepAspectRatio, transformMode=Qt.SmoothTransformation) icon_bitmaps.append(bm) total_width += bm.width() @@ -193,6 +193,8 @@ class BooksModel(QAbstractTableModel): # {{{ self.bool_yes_icon = QIcon(I('ok.png')) self.bool_no_icon = QIcon(I('list_remove.png')) self.bool_blank_icon = QIcon(I('blank.png')) + self.marked_icon = QIcon(I('rating.png')) + self.row_decoration = NONE self.device_connected = False self.ids_to_highlight = [] self.ids_to_highlight_set = set() @@ -921,6 +923,8 @@ class BooksModel(QAbstractTableModel): # {{{ if role == Qt.DisplayRole: # orientation is vertical return QVariant(section+1) + if role == Qt.DecorationRole: + return self.marked_icon if self.db.data.get_marked(self.db.data.index_to_id(section)) else self.row_decoration return NONE def flags(self, index): diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index b59cb49a11..a006f9bd33 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -22,7 +22,7 @@ from calibre.gui2.library.delegates import (RatingDelegate, PubDateDelegate, from calibre.gui2.library.models import BooksModel, DeviceBooksModel from calibre.gui2.library.alternate_views import AlternateViews, setup_dnd_interface from calibre.utils.config import tweaks, prefs -from calibre.gui2 import error_dialog, gprefs +from calibre.gui2 import error_dialog, gprefs, FunctionDispatcher from calibre.gui2.library import DEFAULT_SORT from calibre.constants import filesystem_encoding from calibre import force_unicode @@ -63,6 +63,11 @@ class HeaderView(QHeaderView): # {{{ opt.state |= QStyle.State_MouseOver sm = self.selectionModel() if opt.orientation == Qt.Vertical: + try: + opt.icon = model.headerData(logical_index, opt.orientation, Qt.DecorationRole) + opt.iconAlignment = Qt.AlignVCenter + except TypeError: + pass if sm.isRowSelected(logical_index, QModelIndex()): opt.state |= QStyle.State_Sunken @@ -214,6 +219,7 @@ class BooksView(QTableView): # {{{ self.setSortingEnabled(True) self.selectionModel().currentRowChanged.connect(self._model.current_changed) self.preserve_state = partial(PreserveViewState, self) + self.marked_changed_listener = FunctionDispatcher(self.marked_changed) # {{{ Column Header setup self.can_add_columns = True @@ -229,6 +235,7 @@ class BooksView(QTableView): # {{{ self.column_header.customContextMenuRequested.connect(self.show_column_header_context_menu) self.column_header.sectionResized.connect(self.column_resized, Qt.QueuedConnection) self.row_header = HeaderView(Qt.Vertical, self) + self.row_header.setResizeMode(self.row_header.Fixed) self.setVerticalHeader(self.row_header) # }}} @@ -682,7 +689,19 @@ class BooksView(QTableView): # {{{ self.publisher_delegate.set_auto_complete_function(db.all_publishers) self.alternate_views.set_database(db, stage=1) + def marked_changed(self, old_marked, current_marked): + if bool(old_marked) == bool(current_marked): + changed = old_marked | current_marked + sections = tuple(map(self.model().db.data.id_to_index, changed)) + self.row_header.headerDataChanged(Qt.Vertical, min(sections), max(sections)) + else: + # Marked items have either appeared or all been removed + self.model().row_decoration = self.model().bool_blank_icon if current_marked else None + self.row_header.headerDataChanged(Qt.Vertical, 0, self.row_header.count()-1) + self.row_header.geometriesChanged.emit() + def database_changed(self, db): + db.data.add_marked_listener(self.marked_changed_listener) for i in range(self.model().columnCount(None)): if self.itemDelegateForColumn(i) in (self.rating_delegate, self.timestamp_delegate, self.pubdate_delegate,