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,