Quickview panel: Add actions to the context menu to search for book in library, open in viewer, etc. Fixes #1891765 [Enhancement Request: Add "Edit Metadata" and "view book details" to quickview right-click menu.](https://bugs.launchpad.net/calibre/+bug/1891765)

Merge branch 'master' of https://github.com/cbhaley/calibre into master
This commit is contained in:
Kovid Goyal 2020-08-16 20:46:10 +05:30
commit 12e64d6e8b
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 128 additions and 100 deletions

View File

@ -104,8 +104,6 @@ class ShowQuickviewAction(InterfaceAction):
default_keys=('Shift+S',), action=self.search_action, default_keys=('Shift+S',), action=self.search_action,
group=self.action_spec[0]) group=self.action_spec[0])
self.search_action.triggered.connect(self.search_quickview) self.search_action.triggered.connect(self.search_quickview)
self.search_action.changed.connect(self.set_search_shortcut_tooltip)
self.menuless_qaction.changed.connect(self.set_search_shortcut_tooltip)
self.qv_button = QuickviewButton(self.gui, self) self.qv_button = QuickviewButton(self.gui, self)
@ -139,15 +137,9 @@ class ShowQuickviewAction(InterfaceAction):
self.current_instance = Quickview(self.gui, index, self.qaction.shortcut()) self.current_instance = Quickview(self.gui, index, self.qaction.shortcut())
self.current_instance.reopen_after_dock_change.connect(self.open_quickview) self.current_instance.reopen_after_dock_change.connect(self.open_quickview)
self.set_search_shortcut_tooltip()
self.current_instance.show() self.current_instance.show()
self.current_instance.quickview_closed.connect(self.qv_button.set_state_to_show) self.current_instance.quickview_closed.connect(self.qv_button.set_state_to_show)
def set_search_shortcut_tooltip(self):
if self.current_instance and not self.current_instance.is_closed:
self.current_instance.addAction(self.focus_bl_action)
self.current_instance.set_search_shortcut_tooltip(self.search_action.shortcut().toString())
def open_quickview(self): def open_quickview(self):
''' '''
QV moved from/to dock. Close and reopen the pane/window. QV moved from/to dock. Close and reopen the pane/window.

View File

@ -173,6 +173,7 @@ class Quickview(QDialog, Ui_Quickview):
self.current_key = None # current lookup key in books list self.current_key = None # current lookup key in books list
self.last_search = None self.last_search = None
self.no_valid_items = False self.no_valid_items = False
self.follow_library_view = True
self.apply_vls.setCheckState(Qt.Checked if gprefs['qv_respects_vls'] self.apply_vls.setCheckState(Qt.Checked if gprefs['qv_respects_vls']
else Qt.Unchecked) else Qt.Unchecked)
@ -184,6 +185,8 @@ class Quickview(QDialog, Ui_Quickview):
self.items.currentTextChanged.connect(self.item_selected) self.items.currentTextChanged.connect(self.item_selected)
self.items.setProperty('highlight_current_item', 150) self.items.setProperty('highlight_current_item', 150)
self.items.itemDoubleClicked.connect(self.item_doubleclicked) self.items.itemDoubleClicked.connect(self.item_doubleclicked)
self.items.setContextMenuPolicy(Qt.CustomContextMenu)
self.items.customContextMenuRequested.connect(self.show_item_context_menu)
focus_filter = WidgetFocusFilter(self.items) focus_filter = WidgetFocusFilter(self.items)
focus_filter.focus_entered_signal.connect(self.focus_entered) focus_filter.focus_entered_signal.connect(self.focus_entered)
@ -202,7 +205,7 @@ class Quickview(QDialog, Ui_Quickview):
self.refresh_button.clicked.connect(self.refill) self.refresh_button.clicked.connect(self.refill)
self.tab_order_widgets = [self.items, self.books_table, self.lock_qv, self.tab_order_widgets = [self.items, self.books_table, self.lock_qv,
self.dock_button, self.search_button, self.refresh_button, self.dock_button, self.refresh_button,
self.close_button] self.close_button]
for idx,widget in enumerate(self.tab_order_widgets): for idx,widget in enumerate(self.tab_order_widgets):
widget.installEventFilter(WidgetTabFilter(widget, idx, self.tab_pressed_signal)) widget.installEventFilter(WidgetTabFilter(widget, idx, self.tab_pressed_signal))
@ -236,20 +239,16 @@ class Quickview(QDialog, Ui_Quickview):
self.view.clicked.connect(self.slave) self.view.clicked.connect(self.slave)
self.view.selectionModel().currentColumnChanged.connect(self.column_slave) self.view.selectionModel().currentColumnChanged.connect(self.column_slave)
QCoreApplication.instance().aboutToQuit.connect(self.save_state) QCoreApplication.instance().aboutToQuit.connect(self.save_state)
self.search_button.clicked.connect(self.do_search)
self.view.model().new_bookdisplay_data.connect(self.book_was_changed) self.view.model().new_bookdisplay_data.connect(self.book_was_changed)
self.close_button.setDefault(False) self.close_button.setDefault(False)
self.close_button_tooltip = _('The Quickview shortcut ({0}) shows/hides the Quickview panel') self.close_button_tooltip = _('The Quickview shortcut ({0}) shows/hides the Quickview panel')
self.search_button_tooltip = _('Search in the library view for the currently highlighted selection')
self.search_button.setToolTip(self.search_button_tooltip)
if self.is_pane: if self.is_pane:
self.dock_button.setText(_('Undock')) self.dock_button.setText(_('Undock'))
self.dock_button.setToolTip(_('Pop up the quickview panel into its own floating window')) self.dock_button.setToolTip(_('Pop up the quickview panel into its own floating window'))
self.dock_button.setIcon(QIcon(I('arrow-up.png'))) self.dock_button.setIcon(QIcon(I('arrow-up.png')))
# Remove the ampersands from the buttons because shortcuts exist. # Remove the ampersands from the buttons because shortcuts exist.
self.lock_qv.setText(_('Lock Quickview contents')) self.lock_qv.setText(_('Lock Quickview contents'))
self.search_button.setText(_('Search'))
self.refresh_button.setText(_('Refresh')) self.refresh_button.setText(_('Refresh'))
self.gui.quickview_splitter.add_quickview_dialog(self) self.gui.quickview_splitter.add_quickview_dialog(self)
self.close_button.setVisible(False) self.close_button.setVisible(False)
@ -268,6 +267,10 @@ class Quickview(QDialog, Ui_Quickview):
self.view_icon = QIcon(I('view.png')) self.view_icon = QIcon(I('view.png'))
self.view_plugin = self.gui.iactions['View'] self.view_plugin = self.gui.iactions['View']
self.edit_metadata_icon = QIcon(I('edit_input.png'))
self.quickview_icon = QIcon(I('quickview.png'))
self.select_book_icon = QIcon(I('library.png'))
self.search_icon = QIcon(I('search.png'))
self.books_table.setContextMenuPolicy(Qt.CustomContextMenu) self.books_table.setContextMenuPolicy(Qt.CustomContextMenu)
self.books_table.customContextMenuRequested.connect(self.show_context_menu) self.books_table.customContextMenuRequested.connect(self.show_context_menu)
@ -288,14 +291,40 @@ class Quickview(QDialog, Ui_Quickview):
tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text()) tb.item_search.lineEdit().setText(self.current_key + ':=' + item.text())
tb.do_find() tb.do_find()
def show_item_context_menu(self, point):
item = self.items.currentItem()
self.context_menu = QMenu(self)
self.context_menu.addAction(self.search_icon, _('Search for item in tag browser'),
partial(self.item_doubleclicked, item))
self.context_menu.addAction(self.search_icon, _('Search for item in library'),
partial(self.do_search, follow_library_view=False))
self.context_menu.popup(self.items.mapToGlobal(point))
self.context_menu = QMenu(self)
def show_context_menu(self, point): def show_context_menu(self, point):
index = self.books_table.indexAt(point) index = self.books_table.indexAt(point)
row = index.row()
column = index.column()
item = self.books_table.item(index.row(), 0) item = self.books_table.item(index.row(), 0)
if item is None: if item is None:
return False return False
book_id = int(item.data(Qt.UserRole)) book_id = int(item.data(Qt.UserRole))
self.context_menu = QMenu(self) book_displayed = self.book_displayed_in_library_view(book_id)
self.context_menu.addAction(self.view_icon, _('View'), m = self.context_menu = QMenu(self)
a = m.addAction(self.select_book_icon, _('Select book in library'),
partial(self.select_book, book_id))
a.setEnabled(book_displayed)
m.addAction(self.search_icon, _('Search for item in library'),
partial(self.do_search, follow_library_view=False))
a = m.addAction(self.edit_metadata_icon, _('Edit book metadata'),
partial(self.edit_metadata, book_id, follow_library_view=False))
a.setEnabled(book_displayed)
a = m.addAction(self.quickview_icon, _('Quickview this cell'),
partial(self.quickview_item, row, column))
a.setEnabled(self.is_category(self.column_order[column]) and
book_displayed and not self.lock_qv.isChecked())
m.addSeparator()
m.addAction(self.view_icon, _('Open book in viewer'),
partial(self.view_plugin._view_calibre_books, [book_id])) partial(self.view_plugin._view_calibre_books, [book_id]))
self.context_menu.popup(self.books_table.mapToGlobal(point)) self.context_menu.popup(self.books_table.mapToGlobal(point))
return True return True
@ -324,16 +353,8 @@ class Quickview(QDialog, Ui_Quickview):
self._refresh(self.current_book_id, self.current_key) self._refresh(self.current_book_id, self.current_key)
def set_search_text(self, txt): def set_search_text(self, txt):
if txt:
self.search_button.setEnabled(True)
else:
self.search_button.setEnabled(False)
self.last_search = txt self.last_search = txt
def set_search_shortcut_tooltip(self, search_sc):
if self.is_pane:
self.search_button.setToolTip(self.search_button_tooltip + ' (' + search_sc + ')')
def focus_entered(self, obj): def focus_entered(self, obj):
if obj == self.books_table: if obj == self.books_table:
self.books_table_set_search_string(self.books_table.currentRow(), self.books_table_set_search_string(self.books_table.currentRow(),
@ -400,11 +421,15 @@ class Quickview(QDialog, Ui_Quickview):
self.reopen_after_dock_change.emit() self.reopen_after_dock_change.emit()
# search button # search button
def do_search(self): def do_search(self, follow_library_view=True):
if self.no_valid_items: if self.no_valid_items:
return return
if self.last_search is not None: if self.last_search is not None:
try:
self.follow_library_view = follow_library_view
self.gui.search.set_search_string(self.last_search) self.gui.search.set_search_string(self.last_search)
finally:
self.follow_library_view = True
def book_was_changed(self, mi): def book_was_changed(self, mi):
''' '''
@ -412,7 +437,7 @@ class Quickview(QDialog, Ui_Quickview):
book info current. This means that prev and next in edit metadata will move book info current. This means that prev and next in edit metadata will move
the current book and change quickview the current book and change quickview
''' '''
if self.is_closed or self.current_column is None: if self.is_closed or self.current_column is None or not self.follow_library_view:
return return
# There is an ordering problem when libraries are changed. The library # There is an ordering problem when libraries are changed. The library
# view is changed, triggering a book_was_changed signal. Unfortunately # view is changed, triggering a book_was_changed signal. Unfortunately
@ -457,13 +482,16 @@ class Quickview(QDialog, Ui_Quickview):
traceback.print_exc() traceback.print_exc()
self.indicate_no_items() self.indicate_no_items()
def is_category(self, key):
return key is not None and self.fm[key]['is_category']
def _refresh(self, book_id, key): def _refresh(self, book_id, key):
''' '''
Actually fill in the left-hand panel from the information in the Actually fill in the left-hand panel from the information in the
selected column of the selected book selected column of the selected book
''' '''
# Only show items for categories # Only show items for categories
if key is None or not self.fm[key]['is_category']: if not self.is_category(key):
if self.current_key is None: if self.current_key is None:
self.indicate_no_items() self.indicate_no_items()
return return
@ -643,28 +671,62 @@ class Quickview(QDialog, Ui_Quickview):
def return_pressed(self): def return_pressed(self):
row = self.books_table.currentRow() row = self.books_table.currentRow()
if gprefs['qv_retkey_changes_column']: if gprefs['qv_retkey_changes_column']:
self.select_book(row, self.books_table.currentColumn()) self.select_book_and_qv(row, self.books_table.currentColumn())
else: else:
self.select_book(row, self.key_to_table_widget_column(self.current_key)) self.select_book_and_qv(row, self.key_to_table_widget_column(self.current_key))
def book_not_in_view_error(self):
from calibre.gui2 import error_dialog
error_dialog(self, _('Quickview: Book not in library view'),
_('The book you selected is not currently displayed in '
'the library view, perhaps because of a search or a '
'virtual library, so Quickview cannot select it.'),
show=True,
show_copy_button=False)
def book_displayed_in_library_view(self, book_id):
try:
self.db.data.index(book_id)
return True
except:
return False
def quickview_item(self, row, column):
self.select_book_and_qv(row, column)
def book_doubleclicked(self, row, column): def book_doubleclicked(self, row, column):
if self.no_valid_items: if self.no_valid_items:
return return
try: try:
if gprefs['qv_dclick_changes_column']: if gprefs['qv_dclick_changes_column']:
self.select_book(row, column) self.quickview_item(row, column)
else: else:
self.select_book(row, self.key_to_table_widget_column(self.current_key)) self.quickview_item(row, self.key_to_table_widget_column(self.current_key))
except: except:
from calibre.gui2 import error_dialog self.book_not_in_view_error()
error_dialog(self, _('Quickview: Book not in library view'),
_('The book you selected is not currently displayed in '
'the library view, perhaps because of a search, so '
'Quickview cannot select it.'),
show=True,
show_copy_button=False)
def select_book(self, row, column): def edit_metadata(self, book_id, follow_library_view=True):
try:
self.follow_library_view = follow_library_view
self.view.select_rows([book_id])
em = find_plugin('Edit Metadata')
if em and em.actual_plugin_:
em.actual_plugin_.edit_metadata(None)
finally:
self.follow_library_view = True
def select_book(self, book_id):
'''
Select a book in the library view without changing the QV lists
'''
try:
self.follow_library_view = False
self.view.select_cell(self.db.data.id_to_index(book_id),
self.current_column)
finally:
self.follow_library_view = True
def select_book_and_qv(self, row, column):
''' '''
row and column both refer the qv table. In particular, column is not row and column both refer the qv table. In particular, column is not
the logical column in the book list. the logical column in the book list.
@ -673,13 +735,13 @@ class Quickview(QDialog, Ui_Quickview):
if item is None: if item is None:
return return
book_id = int(self.books_table.item(row, column).data(Qt.UserRole)) book_id = int(self.books_table.item(row, column).data(Qt.UserRole))
if not self.book_displayed_in_library_view(book_id):
self.book_not_in_view_error()
return
key = self.column_order[column] key = self.column_order[column]
modifiers = int(QApplication.keyboardModifiers()) modifiers = int(QApplication.keyboardModifiers())
if modifiers in (Qt.CTRL, Qt.SHIFT): if modifiers in (Qt.CTRL, Qt.SHIFT):
self.view.select_rows([book_id]) self.edit_metadata(book_id)
em = find_plugin('Edit Metadata')
if em and em.actual_plugin_:
em.actual_plugin_.edit_metadata(None)
else: else:
self.view.select_cell(self.db.data.id_to_index(book_id), self.view.select_cell(self.db.data.id_to_index(book_id),
self.view.column_map.index(key)) self.view.column_map.index(key))
@ -692,14 +754,14 @@ class Quickview(QDialog, Ui_Quickview):
''' '''
called when the column is changed on the booklist called when the column is changed on the booklist
''' '''
if gprefs['qv_follows_column']: if self.follow_library_view and gprefs['qv_follows_column']:
self.slave(current) self.slave(current)
def slave(self, current): def slave(self, current):
''' '''
called when a book is clicked on the library view called when a book is clicked on the library view
''' '''
if self.is_closed: if self.is_closed or not self.follow_library_view:
return return
self.refresh(current) self.refresh(current)
self.view.activateWindow() self.view.activateWindow()

View File

@ -86,17 +86,6 @@
</property> </property>
</widget> </widget>
</item> </item>
<item>
<widget class="QCheckBox" name="lock_qv">
<property name="text">
<string>&amp;Lock Quickview contents</string>
</property>
<property name="toolTip">
<string>&lt;p&gt;Select to prevent Quickview from changing content when the
selection on the library view is changed&lt;/p&gt;</string>
</property>
</widget>
</item>
<item> <item>
<spacer> <spacer>
<property name="orientation"> <property name="orientation">
@ -111,54 +100,16 @@
</spacer> </spacer>
</item> </item>
<item> <item>
<widget class="QPushButton" name="dock_button"> <widget class="QCheckBox" name="lock_qv">
<property name="text"> <property name="text">
<string>&amp;Dock</string> <string>&amp;Lock Quickview contents</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="search_button">
<property name="text">
<string>&amp;Search</string>
</property> </property>
<property name="toolTip"> <property name="toolTip">
<string>Search in the library view for the currently highlighted selection</string> <string>&lt;p&gt;Select to prevent Quickview from changing content when the
</property> selection on the library view is changed&lt;/p&gt;</string>
<property name="autoDefault">
<bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QPushButton" name="refresh_button"> <widget class="QPushButton" name="refresh_button">
<property name="text"> <property name="text">
@ -185,6 +136,29 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QPushButton" name="dock_button">
<property name="text">
<string>&amp;Dock</string>
</property>
<property name="autoDefault">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<spacer>
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item> <item>
<widget class="QPushButton" name="close_button"> <widget class="QPushButton" name="close_button">
<property name="text"> <property name="text">