From 7717fe6d0537854ea1278f2c354ea043bd8b875d Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 1 Aug 2013 21:27:53 +0530 Subject: [PATCH] Sync the main and grid views --- src/calibre/gui2/library/alternate_views.py | 64 ++++++++++++++++++++- src/calibre/gui2/library/views.py | 12 ++-- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/library/alternate_views.py b/src/calibre/gui2/library/alternate_views.py index 479c071784..0aada05b7e 100644 --- a/src/calibre/gui2/library/alternate_views.py +++ b/src/calibre/gui2/library/alternate_views.py @@ -6,16 +6,28 @@ from __future__ import (unicode_literals, division, absolute_import, __license__ = 'GPL v3' __copyright__ = '2013, Kovid Goyal ' +import itertools, operator from time import time from collections import OrderedDict from threading import Lock, Event, Thread from Queue import Queue +from functools import wraps from PyQt4.Qt import ( - QListView, QSize, QStyledItemDelegate, QModelIndex, Qt, QImage, pyqtSignal, QPalette, QColor) + QListView, QSize, QStyledItemDelegate, QModelIndex, Qt, QImage, pyqtSignal, + QPalette, QColor, QItemSelection) from calibre import fit_image +def sync(func): + @wraps(func) + def ans(self, *args, **kwargs): + if self.break_link or self.current_view is self.main_view: + return + with self: + return func(self, *args, **kwargs) + return ans + class AlternateViews(object): def __init__(self, main_view): @@ -23,6 +35,8 @@ class AlternateViews(object): self.stack_positions = {None:0} self.current_view = self.main_view = main_view self.stack = None + self.break_link = False + self.main_connected = False def set_stack(self, stack): self.stack = stack @@ -34,6 +48,8 @@ class AlternateViews(object): self.stack.addWidget(view) self.stack.setCurrentIndex(0) view.setModel(self.main_view._model) + view.selectionModel().currentChanged.connect(self.slave_current_changed) + view.selectionModel().selectionChanged.connect(self.slave_selection_changed) def show_view(self, key=None): view = self.views[key] @@ -42,13 +58,45 @@ class AlternateViews(object): self.stack.setCurrentIndex(self.stack_positions[key]) self.current_view = view if view is not self.main_view: + self.main_current_changed(self.main_view.currentIndex()) + self.main_selection_changed() view.shown() + if not self.main_connected: + self.main_connected = True + self.main_view.selectionModel().currentChanged.connect(self.main_current_changed) + self.main_view.selectionModel().selectionChanged.connect(self.main_selection_changed) + view.setFocus(Qt.OtherFocusReason) def set_database(self, db, stage=0): for view in self.views.itervalues(): if view is not self.main_view: view.set_database(db, stage=stage) + def __enter__(self): + self.break_link = True + + def __exit__(self, *args): + self.break_link = False + + @sync + def slave_current_changed(self, current, *args): + self.main_view.set_current_row(current.row(), for_sync=True) + + @sync + def slave_selection_changed(self, *args): + rows = {r.row() for r in self.current_view.selectionModel().selectedIndexes()} + self.main_view.select_rows(rows, using_ids=False, change_current=False, scroll=False) + + @sync + def main_current_changed(self, current, *args): + self.current_view.set_current_row(current.row()) + + @sync + def main_selection_changed(self, *args): + rows = {r.row() for r in self.main_view.selectionModel().selectedIndexes()} + self.current_view.select_rows(rows) + + class CoverCache(dict): def __init__(self, limit=200): @@ -232,5 +280,19 @@ class GridView(QListView): else: self.delegate.cover_cache.clear() + def select_rows(self, rows): + sel = QItemSelection() + sm = self.selectionModel() + m = self.model() + # Create a range based selector for each set of contiguous rows + # as supplying selectors for each individual row causes very poor + # performance if a large number of rows has to be selected. + for k, g in itertools.groupby(enumerate(rows), lambda (i,x):i-x): + group = list(map(operator.itemgetter(1), g)) + sel.merge(QItemSelection(m.index(min(group), 0), m.index(max(group), 0)), sm.Select) + sm.select(sel, sm.ClearAndSelect) + def set_current_row(self, row): + sm = self.selectionModel() + sm.setCurrentIndex(self.model().index(row, 0), sm.NoUpdate) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 257540c53d..c1292c1b6b 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -863,7 +863,7 @@ class BooksView(QTableView): # {{{ self.scrollTo(self.model().index(row, i), self.PositionAtCenter) break - def set_current_row(self, row=0, select=True): + def set_current_row(self, row=0, select=True, for_sync=False): if row > -1 and row < self.model().rowCount(QModelIndex()): h = self.horizontalHeader() logical_indices = list(range(h.count())) @@ -876,10 +876,14 @@ class BooksView(QTableView): # {{{ pairs.sort(cmp=lambda x,y:cmp(x[1], y[1])) i = pairs[0][0] index = self.model().index(row, i) - self.setCurrentIndex(index) - if select: + if for_sync: sm = self.selectionModel() - sm.select(index, sm.ClearAndSelect|sm.Rows) + sm.setCurrentIndex(index, sm.NoUpdate) + else: + self.setCurrentIndex(index) + if select: + sm = self.selectionModel() + sm.select(index, sm.ClearAndSelect|sm.Rows) def keyPressEvent(self, ev): val = self.horizontalScrollBar().value()