mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Merge branch 'master' of https://github.com/cbhaley/calibre
This commit is contained in:
commit
988759188a
@ -423,6 +423,8 @@ def create_defs():
|
|||||||
defs['tb_search_order'] = {'0': 1, '1': 2, '2': 3, '3': 4, '4': 0}
|
defs['tb_search_order'] = {'0': 1, '1': 2, '2': 3, '3': 4, '4': 0}
|
||||||
defs['search_tool_bar_shows_text'] = True
|
defs['search_tool_bar_shows_text'] = True
|
||||||
defs['allow_keyboard_search_in_library_views'] = True
|
defs['allow_keyboard_search_in_library_views'] = True
|
||||||
|
defs['show_links_in_tag_brouser'] = False
|
||||||
|
defs['show_notes_in_tag_brouser'] = False
|
||||||
|
|
||||||
def migrate_tweak(tweak_name, pref_name):
|
def migrate_tweak(tweak_name, pref_name):
|
||||||
# If the tweak has been changed then leave the tweak in the file so
|
# If the tweak has been changed then leave the tweak in the file so
|
||||||
|
@ -646,6 +646,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
|
|||||||
r('language', prefs, choices=choices, restart_required=True, setting=LanguageSetting)
|
r('language', prefs, choices=choices, restart_required=True, setting=LanguageSetting)
|
||||||
|
|
||||||
r('show_avg_rating', config)
|
r('show_avg_rating', config)
|
||||||
|
r('show_links_in_tag_brouser', gprefs)
|
||||||
|
r('show_notes_in_tag_brouser', gprefs)
|
||||||
r('disable_animations', config)
|
r('disable_animations', config)
|
||||||
r('systray_icon', config, restart_required=True)
|
r('systray_icon', config, restart_required=True)
|
||||||
r('show_splash_screen', gprefs)
|
r('show_splash_screen', gprefs)
|
||||||
|
@ -1290,66 +1290,99 @@ structure and you want to use the same column order for each one.</p></str
|
|||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="0">
|
<item row="5" column="0" colspan="3">
|
||||||
<widget class="QCheckBox" name="opt_show_avg_rating">
|
<layout class="QGridLayout" name="gridlayout_22">
|
||||||
<property name="text">
|
<item row="0" column="0">
|
||||||
<string>Show &average ratings</string>
|
<widget class="QCheckBox" name="opt_show_avg_rating">
|
||||||
</property>
|
<property name="text">
|
||||||
<property name="checked">
|
<string>Show &average ratings</string>
|
||||||
<bool>true</bool>
|
</property>
|
||||||
</property>
|
<property name="toolTip">
|
||||||
</widget>
|
<string>Show the average rating per item indication in the Tag browser</string>
|
||||||
</item>
|
</property>
|
||||||
<item row="5" column="2">
|
<property name="checked">
|
||||||
<widget class="QCheckBox" name="opt_tag_browser_show_tooltips">
|
<bool>true</bool>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Show &tooltips</string>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
</widget>
|
<item row="0" column="1">
|
||||||
</item>
|
<widget class="QCheckBox" name="opt_show_links_in_tag_brouser">
|
||||||
<item row="6" column="0">
|
<property name="text">
|
||||||
<widget class="QCheckBox" name="opt_tag_browser_show_counts">
|
<string>Show &links icons</string>
|
||||||
<property name="toolTip">
|
</property>
|
||||||
<string><p>Show counts for items in the Tag browser. Such as the number of books
|
<property name="toolTip">
|
||||||
|
<string>Show an icon if the item has an attached link</string>
|
||||||
|
</property>
|
||||||
|
<property name="checked">
|
||||||
|
<bool>true</bool>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<widget class="QCheckBox" name="opt_tag_browser_show_tooltips">
|
||||||
|
<property name="text">
|
||||||
|
<string>Show &tooltips</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_tag_browser_show_counts">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><p>Show counts for items in the Tag browser. Such as the number of books
|
||||||
by each author, the number of authors, etc. If you turn it off, you can still
|
by each author, the number of authors, etc. If you turn it off, you can still
|
||||||
see the counts by hovering your mouse over any item.</p></string>
|
see the counts by hovering your mouse over any item.</p></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Show &counts</string>
|
<string>Show &counts</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="6" column="2">
|
<item row="1" column="1">
|
||||||
<widget class="QCheckBox" name="opt_tag_browser_old_look">
|
<widget class="QCheckBox" name="opt_show_notes_in_tag_brouser">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Use &alternating row colors</string>
|
<string>Show &notes icons</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<property name="toolTip">
|
||||||
</item>
|
<string>Show an icon if the item has an attached note</string>
|
||||||
<item row="7" column="0">
|
</property>
|
||||||
<widget class="QCheckBox" name="opt_tag_browser_hide_empty_categories">
|
<property name="checked">
|
||||||
<property name="toolTip">
|
<bool>true</bool>
|
||||||
<string><p>When checked, calibre will automatically hide any category
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="1" column="2">
|
||||||
|
<widget class="QCheckBox" name="opt_tag_browser_old_look">
|
||||||
|
<property name="text">
|
||||||
|
<string>Use &alternating row colors</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0">
|
||||||
|
<widget class="QCheckBox" name="opt_tag_browser_hide_empty_categories">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string><p>When checked, calibre will automatically hide any category
|
||||||
(a column, custom or standard) that has no items to show. For example, some
|
(a column, custom or standard) that has no items to show. For example, some
|
||||||
categories might not have values when using Virtual libraries. Checking this
|
categories might not have values when using Virtual libraries. Checking this
|
||||||
box will cause these empty categories to be hidden.</p></string>
|
box will cause these empty categories to be hidden.</p></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Hide empt&y categories (columns)</string>
|
<string>Hide empt&y categories (columns)</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="7" column="2">
|
<item row="2" column="2">
|
||||||
<widget class="QCheckBox" name="opt_tag_browser_always_autocollapse">
|
<widget class="QCheckBox" name="opt_tag_browser_always_autocollapse">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><p>When checked, Find in the Tag browser will show all items
|
<string><p>When checked, Find in the Tag browser will show all items
|
||||||
that match the search instead of the first one. If Hide empty categories is
|
that match the search instead of the first one. If Hide empty categories is
|
||||||
also checked then only categories containing a matched item will be shown.</p></string>
|
also checked then only categories containing a matched item will be shown.</p></string>
|
||||||
</property>
|
</property>
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Find &shows all items that match</string>
|
<string>Find &shows all items that match</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="8" column="0" colspan="3">
|
<item row="8" column="0" colspan="3">
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
@ -235,10 +235,16 @@ class TagTreeItem: # {{{
|
|||||||
ar = self.average_rating
|
ar = self.average_rating
|
||||||
if ar:
|
if ar:
|
||||||
tt.append(_('Average rating for books in this category: %.1f') % ar)
|
tt.append(_('Average rating for books in this category: %.1f') % ar)
|
||||||
elif self.type == self.TAG and ar is not None:
|
elif self.type == self.TAG:
|
||||||
tt.append(_('Books in this category are unrated'))
|
if ar is not None:
|
||||||
if self.type == self.TAG and tag.category != 'search':
|
tt.append(_('Books in this category are unrated'))
|
||||||
tt.append(_('Number of books: %s') % self.item_count)
|
if tag.category != 'search':
|
||||||
|
tt.append(_('Number of books: %s') % self.item_count)
|
||||||
|
from calibre.gui2.ui import get_gui
|
||||||
|
db = get_gui().current_db.new_api
|
||||||
|
link = db.get_link_map(tag.category).get(tag.original_name)
|
||||||
|
if link:
|
||||||
|
tt.append(_('Link: %s') % link)
|
||||||
return '\n'.join(tt)
|
return '\n'.join(tt)
|
||||||
return None
|
return None
|
||||||
if role == DRAG_IMAGE_ROLE:
|
if role == DRAG_IMAGE_ROLE:
|
||||||
@ -365,6 +371,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
self._build_in_progress = False
|
self._build_in_progress = False
|
||||||
self.reread_collapse_model({}, rebuild=False)
|
self.reread_collapse_model({}, rebuild=False)
|
||||||
self.show_error_after_event_loop_tick_signal.connect(self.on_show_error_after_event_loop_tick, type=Qt.ConnectionType.QueuedConnection)
|
self.show_error_after_event_loop_tick_signal.connect(self.on_show_error_after_event_loop_tick, type=Qt.ConnectionType.QueuedConnection)
|
||||||
|
self.reset_notes_and_link_maps()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def gui_parent(self):
|
def gui_parent(self):
|
||||||
@ -441,6 +448,43 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
self._run_rebuild()
|
self._run_rebuild()
|
||||||
self.endResetModel()
|
self.endResetModel()
|
||||||
|
|
||||||
|
def _cached_notes_map(self, category):
|
||||||
|
if self.notes_map is None:
|
||||||
|
self.notes_map = {}
|
||||||
|
if category not in self.notes_map:
|
||||||
|
try:
|
||||||
|
self.notes_map[category] = (self.db.new_api.get_all_items_that_have_notes(category),
|
||||||
|
self.db.new_api.get_item_name_map(category))
|
||||||
|
except:
|
||||||
|
self.notes_map[category] = (frozenset(), {})
|
||||||
|
return self.notes_map[category]
|
||||||
|
|
||||||
|
def _cached_link_map(self, category):
|
||||||
|
if self.link_map is None:
|
||||||
|
self.link_map = {}
|
||||||
|
if category not in self.link_map:
|
||||||
|
try:
|
||||||
|
self.link_map[category] = self.db.new_api.get_link_map(category)
|
||||||
|
except Exception:
|
||||||
|
self.link_map[category] = {}
|
||||||
|
return self.link_map[category]
|
||||||
|
|
||||||
|
def category_has_notes(self, category):
|
||||||
|
return len(self._cached_notes_map(category)[0]) > 0
|
||||||
|
|
||||||
|
def item_has_note(self, category, item_name):
|
||||||
|
notes_map, item_id_map = self._cached_notes_map(category)
|
||||||
|
return item_id_map.get(item_name) in notes_map
|
||||||
|
|
||||||
|
def category_has_links(self, category):
|
||||||
|
return len(self._cached_link_map(category)) > 0
|
||||||
|
|
||||||
|
def item_has_link(self, category, item_name):
|
||||||
|
return item_name in self._cached_link_map(category)
|
||||||
|
|
||||||
|
def reset_notes_and_link_maps(self):
|
||||||
|
self.link_map = self.notes_map = None
|
||||||
|
|
||||||
def rebuild_node_tree(self, state_map={}):
|
def rebuild_node_tree(self, state_map={}):
|
||||||
if self._build_in_progress:
|
if self._build_in_progress:
|
||||||
print('Tag browser build already in progress')
|
print('Tag browser build already in progress')
|
||||||
@ -455,6 +499,7 @@ class TagsModel(QAbstractItemModel): # {{{
|
|||||||
self._build_in_progress = False
|
self._build_in_progress = False
|
||||||
|
|
||||||
def _run_rebuild(self, state_map={}):
|
def _run_rebuild(self, state_map={}):
|
||||||
|
self.reset_notes_and_link_maps()
|
||||||
for node in itervalues(self.node_map):
|
for node in itervalues(self.node_map):
|
||||||
node.break_cycles()
|
node.break_cycles()
|
||||||
del node # Clear reference to node in the current frame
|
del node # Clear reference to node in the current frame
|
||||||
|
@ -514,6 +514,7 @@ class TagBrowserMixin: # {{{
|
|||||||
m = self.library_view.model()
|
m = self.library_view.model()
|
||||||
ids = [m.id(r) for r in rows]
|
ids = [m.id(r) for r in rows]
|
||||||
|
|
||||||
|
self.tags_view.model().reset_notes_and_link_maps()
|
||||||
m.refresh(reset=False)
|
m.refresh(reset=False)
|
||||||
m.research()
|
m.research()
|
||||||
self.library_view.select_rows(ids)
|
self.library_view.select_rows(ids)
|
||||||
@ -778,12 +779,26 @@ class TagBrowserWidget(QFrame): # {{{
|
|||||||
_('Configure Tag browser'), default_keys=(),
|
_('Configure Tag browser'), default_keys=(),
|
||||||
action=ac, group=_('Tag browser'))
|
action=ac, group=_('Tag browser'))
|
||||||
ac.triggered.connect(l.showMenu)
|
ac.triggered.connect(l.showMenu)
|
||||||
|
|
||||||
l.m.aboutToShow.connect(self.about_to_show_configure_menu)
|
l.m.aboutToShow.connect(self.about_to_show_configure_menu)
|
||||||
|
# Show/hide counts
|
||||||
l.m.show_counts_action = ac = l.m.addAction('counts')
|
l.m.show_counts_action = ac = l.m.addAction('counts')
|
||||||
ac.triggered.connect(self.toggle_counts)
|
ac.triggered.connect(self.toggle_counts)
|
||||||
|
# Show/hide average rating
|
||||||
l.m.show_avg_rating_action = ac = l.m.addAction(QIcon.ic('rating.png'), 'avg rating')
|
l.m.show_avg_rating_action = ac = l.m.addAction(QIcon.ic('rating.png'), 'avg rating')
|
||||||
ac.triggered.connect(self.toggle_avg_rating)
|
ac.triggered.connect(self.toggle_avg_rating)
|
||||||
|
# Show/hide notes icon
|
||||||
|
l.m.show_notes_icon_action = ac = l.m.addAction(QIcon.ic('notes.png'), 'notes icon')
|
||||||
|
ac.triggered.connect(self.toggle_notes_icon)
|
||||||
|
parent.keyboard.register_shortcut('tag browser toggle notes',
|
||||||
|
_('Toggle notes icons'), default_keys=(),
|
||||||
|
action=ac, group=_('Tag browser'))
|
||||||
|
# Show/hide links icon
|
||||||
|
l.m.show_links_icon_action = ac = l.m.addAction(QIcon.ic('external-link.png'), 'links icon')
|
||||||
|
ac.triggered.connect(self.toggle_links_icon)
|
||||||
|
parent.keyboard.register_shortcut('tag browser toggle links',
|
||||||
|
_('Toggle links icons'), default_keys=(),
|
||||||
|
action=ac, group=_('Tag browser'))
|
||||||
|
|
||||||
sb = l.m.addAction(QIcon.ic('sort.png'), _('Sort by'))
|
sb = l.m.addAction(QIcon.ic('sort.png'), _('Sort by'))
|
||||||
sb.m = l.sort_menu = QMenu(l.m)
|
sb.m = l.sort_menu = QMenu(l.m)
|
||||||
sb.setMenu(sb.m)
|
sb.setMenu(sb.m)
|
||||||
@ -857,6 +872,12 @@ class TagBrowserWidget(QFrame): # {{{
|
|||||||
ac = self.alter_tb.m.show_avg_rating_action
|
ac = self.alter_tb.m.show_avg_rating_action
|
||||||
ac.setText(_('Hide average rating') if config['show_avg_rating'] else _('Show average rating'))
|
ac.setText(_('Hide average rating') if config['show_avg_rating'] else _('Show average rating'))
|
||||||
ac.setIcon(QIcon.ic('minus.png' if config['show_avg_rating'] else 'plus.png'))
|
ac.setIcon(QIcon.ic('minus.png' if config['show_avg_rating'] else 'plus.png'))
|
||||||
|
ac = self.alter_tb.m.show_notes_icon_action
|
||||||
|
ac.setText(_('Hide notes icon') if gprefs['show_notes_in_tag_brouser'] else _('Show notes icon'))
|
||||||
|
ac.setIcon(QIcon.ic('minus.png' if gprefs['show_notes_in_tag_brouser'] else 'plus.png'))
|
||||||
|
ac = self.alter_tb.m.show_links_icon_action
|
||||||
|
ac.setText(_('Hide links icon') if gprefs['show_links_in_tag_brouser'] else _('Show links icon'))
|
||||||
|
ac.setIcon(QIcon.ic('minus.png' if gprefs['show_links_in_tag_brouser'] else 'plus.png'))
|
||||||
|
|
||||||
def filter_book_list(self):
|
def filter_book_list(self):
|
||||||
self.tags_view.model().set_in_tag_browser()
|
self.tags_view.model().set_in_tag_browser()
|
||||||
@ -864,9 +885,19 @@ class TagBrowserWidget(QFrame): # {{{
|
|||||||
|
|
||||||
def toggle_counts(self):
|
def toggle_counts(self):
|
||||||
gprefs['tag_browser_show_counts'] ^= True
|
gprefs['tag_browser_show_counts'] ^= True
|
||||||
|
self.tags_view.recount_with_position_based_index()
|
||||||
|
|
||||||
def toggle_avg_rating(self):
|
def toggle_avg_rating(self):
|
||||||
config['show_avg_rating'] ^= True
|
config['show_avg_rating'] ^= True
|
||||||
|
self.tags_view.recount_with_position_based_index()
|
||||||
|
|
||||||
|
def toggle_notes_icon(self):
|
||||||
|
gprefs['show_notes_in_tag_brouser'] ^= True
|
||||||
|
self.tags_view.recount_with_position_based_index()
|
||||||
|
|
||||||
|
def toggle_links_icon(self):
|
||||||
|
gprefs['show_links_in_tag_brouser'] ^= True
|
||||||
|
self.tags_view.recount_with_position_based_index()
|
||||||
|
|
||||||
def save_state(self):
|
def save_state(self):
|
||||||
gprefs.set('tag browser search box visible', self.toggle_search_button.isChecked())
|
gprefs.set('tag browser search box visible', self.toggle_search_button.isChecked())
|
||||||
|
@ -8,6 +8,7 @@ __docformat__ = 'restructuredtext en'
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
|
from collections import defaultdict
|
||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from qt.core import (
|
from qt.core import (
|
||||||
@ -22,7 +23,7 @@ from calibre.constants import config_dir
|
|||||||
from calibre.ebooks.metadata import rating_to_stars
|
from calibre.ebooks.metadata import rating_to_stars
|
||||||
from calibre.gui2 import (
|
from calibre.gui2 import (
|
||||||
FunctionDispatcher, choose_files, config, empty_index, gprefs, pixmap_to_data,
|
FunctionDispatcher, choose_files, config, empty_index, gprefs, pixmap_to_data,
|
||||||
question_dialog, rating_font,
|
question_dialog, rating_font, safe_open_url,
|
||||||
)
|
)
|
||||||
from calibre.gui2.dialogs.edit_category_notes import EditNoteDialog
|
from calibre.gui2.dialogs.edit_category_notes import EditNoteDialog
|
||||||
from calibre.gui2.complete2 import EditWithComplete
|
from calibre.gui2.complete2 import EditWithComplete
|
||||||
@ -43,6 +44,9 @@ class TagDelegate(QStyledItemDelegate): # {{{
|
|||||||
self.rating_pat = re.compile(r'[%s]' % rating_to_stars(3, True))
|
self.rating_pat = re.compile(r'[%s]' % rating_to_stars(3, True))
|
||||||
self.rating_font = QFont(rating_font())
|
self.rating_font = QFont(rating_font())
|
||||||
self.tags_view = tags_view
|
self.tags_view = tags_view
|
||||||
|
self.links_icon = QIcon.ic('external-link.png')
|
||||||
|
self.notes_icon = QIcon.ic('notes.png')
|
||||||
|
self.blank_icon = QIcon()
|
||||||
|
|
||||||
def draw_average_rating(self, item, style, painter, option, widget):
|
def draw_average_rating(self, item, style, painter, option, widget):
|
||||||
rating = item.average_rating
|
rating = item.average_rating
|
||||||
@ -86,6 +90,30 @@ class TagDelegate(QStyledItemDelegate): # {{{
|
|||||||
hover = option.state & QStyle.StateFlag.State_MouseOver
|
hover = option.state & QStyle.StateFlag.State_MouseOver
|
||||||
is_search = (True if item.type == TagTreeItem.TAG and
|
is_search = (True if item.type == TagTreeItem.TAG and
|
||||||
item.tag.category == 'search' else False)
|
item.tag.category == 'search' else False)
|
||||||
|
|
||||||
|
show_notes = gprefs['show_notes_in_tag_brouser']
|
||||||
|
show_links = gprefs['show_links_in_tag_brouser']
|
||||||
|
if item.type == TagTreeItem.TAG:
|
||||||
|
category = item.tag.category
|
||||||
|
name = item.tag.original_name
|
||||||
|
tv = self.tags_view
|
||||||
|
m = tv._model
|
||||||
|
if show_links and m.category_has_links(category):
|
||||||
|
icon = self.links_icon if m.item_has_link(category, name) else self.blank_icon
|
||||||
|
width = int(tr.height()/2)
|
||||||
|
r = QRect(tr)
|
||||||
|
r.setRight(r.right() - 1), r.setLeft(r.right() - width - 4)
|
||||||
|
tv.category_button_positions[category]['links'] = (r.left(), r.left()+r.width())
|
||||||
|
icon.paint(painter, r, option.decorationAlignment, QIcon.Mode.Normal, QIcon.State.On)
|
||||||
|
tr.setRight(r.left() - 1)
|
||||||
|
if show_notes and m.category_has_notes(category):
|
||||||
|
icon = self.notes_icon if m.item_has_note(category, name) else self.blank_icon
|
||||||
|
width = int(tr.height()/2)
|
||||||
|
r = QRect(tr)
|
||||||
|
r.setRight(r.right() - 1), r.setLeft(r.right() - width - 4)
|
||||||
|
tv.category_button_positions[category]['notes'] = (r.left(), r.left()+r.width())
|
||||||
|
icon.paint(painter, r, option.decorationAlignment, QIcon.Mode.Normal, QIcon.State.On)
|
||||||
|
tr.setRight(r.left() - 1)
|
||||||
if not is_search and (hover or gprefs['tag_browser_show_counts']):
|
if not is_search and (hover or gprefs['tag_browser_show_counts']):
|
||||||
count = str(index.data(COUNT_ROLE))
|
count = str(index.data(COUNT_ROLE))
|
||||||
width = painter.fontMetrics().boundingRect(count).width()
|
width = painter.fontMetrics().boundingRect(count).width()
|
||||||
@ -219,6 +247,12 @@ class TagsView(QTreeView): # {{{
|
|||||||
self.plus_icon = QIcon.ic('plus.png')
|
self.plus_icon = QIcon.ic('plus.png')
|
||||||
self.minus_icon = QIcon.ic('minus.png')
|
self.minus_icon = QIcon.ic('minus.png')
|
||||||
|
|
||||||
|
# Dict for recording the positions of the fake buttons for category tag
|
||||||
|
# lines. It is recorded per category because we can't guarantee the
|
||||||
|
# order that items are painted. The numbers get updated whenever an item
|
||||||
|
# is painted, which deals with resizing.
|
||||||
|
self.category_button_positions = defaultdict(dict)
|
||||||
|
|
||||||
self._model = TagsModel(self)
|
self._model = TagsModel(self)
|
||||||
self._model.search_item_renamed.connect(self.search_item_renamed)
|
self._model.search_item_renamed.connect(self.search_item_renamed)
|
||||||
self._model.refresh_required.connect(self.refresh_required,
|
self._model.refresh_required.connect(self.refresh_required,
|
||||||
@ -332,7 +366,7 @@ class TagsView(QTreeView): # {{{
|
|||||||
match_pop = 0
|
match_pop = 0
|
||||||
self.alter_tb.match_menu.actions()[match_pop].setChecked(True)
|
self.alter_tb.match_menu.actions()[match_pop].setChecked(True)
|
||||||
if not self.made_connections:
|
if not self.made_connections:
|
||||||
self.clicked.connect(self.toggle)
|
self.clicked.connect(self.toggle_on_mouse_click)
|
||||||
self.customContextMenuRequested.connect(self.show_context_menu)
|
self.customContextMenuRequested.connect(self.show_context_menu)
|
||||||
self.refresh_required.connect(self.recount, type=Qt.ConnectionType.QueuedConnection)
|
self.refresh_required.connect(self.recount, type=Qt.ConnectionType.QueuedConnection)
|
||||||
self.alter_tb.sort_menu.triggered.connect(self.sort_changed)
|
self.alter_tb.sort_menu.triggered.connect(self.sort_changed)
|
||||||
@ -430,6 +464,8 @@ class TagsView(QTreeView): # {{{
|
|||||||
|
|
||||||
def mousePressEvent(self, event):
|
def mousePressEvent(self, event):
|
||||||
if event.buttons() & Qt.MouseButton.LeftButton:
|
if event.buttons() & Qt.MouseButton.LeftButton:
|
||||||
|
# Record the press point for processing during the clicked signal
|
||||||
|
self.mouse_clicked_point = event.pos()
|
||||||
# Only remember a possible drag start if the item is drag enabled
|
# Only remember a possible drag start if the item is drag enabled
|
||||||
dex = self.indexAt(event.pos())
|
dex = self.indexAt(event.pos())
|
||||||
if self._model.flags(dex) & Qt.ItemFlag.ItemIsDragEnabled:
|
if self._model.flags(dex) & Qt.ItemFlag.ItemIsDragEnabled:
|
||||||
@ -486,11 +522,37 @@ class TagsView(QTreeView): # {{{
|
|||||||
joiner = ' and ' if self.match_all else ' or '
|
joiner = ' and ' if self.match_all else ' or '
|
||||||
return joiner.join(tokens)
|
return joiner.join(tokens)
|
||||||
|
|
||||||
|
def click_in_button_range(self, val, category, kind):
|
||||||
|
range_tuple = self.category_button_positions[category].get(kind)
|
||||||
|
return range_tuple and range_tuple[0] <= val <= range_tuple[1]
|
||||||
|
|
||||||
def toggle_current_index(self):
|
def toggle_current_index(self):
|
||||||
ci = self.currentIndex()
|
ci = self.currentIndex()
|
||||||
if ci.isValid():
|
if ci.isValid():
|
||||||
self.toggle(ci)
|
self.toggle(ci)
|
||||||
|
|
||||||
|
def toggle_on_mouse_click(self, index):
|
||||||
|
# Check if one of the link or note icons was clicked. If so, deal with
|
||||||
|
# it here and don't do the real toggle
|
||||||
|
t = self._model.data(index, Qt.UserRole)
|
||||||
|
if t.type == TagTreeItem.TAG:
|
||||||
|
db = self._model.db.new_api
|
||||||
|
category = t.tag.category
|
||||||
|
orig_name = t.tag.original_name
|
||||||
|
x = self.mouse_clicked_point.x()
|
||||||
|
if self.click_in_button_range(x, category, 'notes'):
|
||||||
|
from calibre.gui2.dialogs.show_category_note import ShowNoteDialog
|
||||||
|
item_id = db.get_item_id(category, orig_name)
|
||||||
|
if db.notes_for(category, item_id):
|
||||||
|
ShowNoteDialog(category, item_id, db, parent=self).show()
|
||||||
|
return
|
||||||
|
if self.click_in_button_range(x, category, 'links'):
|
||||||
|
link = db.get_link_map(category).get(orig_name)
|
||||||
|
if link:
|
||||||
|
safe_open_url(link)
|
||||||
|
return
|
||||||
|
self._toggle(index, None)
|
||||||
|
|
||||||
def toggle(self, index):
|
def toggle(self, index):
|
||||||
self._toggle(index, None)
|
self._toggle(index, None)
|
||||||
|
|
||||||
@ -811,6 +873,19 @@ class TagsView(QTreeView): # {{{
|
|||||||
self.context_menu.addAction(_('Edit link for %s')%display_name(tag),
|
self.context_menu.addAction(_('Edit link for %s')%display_name(tag),
|
||||||
partial(self.context_menu_handler,
|
partial(self.context_menu_handler,
|
||||||
action='edit_author_link', index=tag.id)).setIcon(QIcon.ic('insert-link.png'))
|
action='edit_author_link', index=tag.id)).setIcon(QIcon.ic('insert-link.png'))
|
||||||
|
elif self.db.new_api.has_link_map(key):
|
||||||
|
self.context_menu.addAction(_('Edit link for %s')%display_name(tag),
|
||||||
|
partial(self.context_menu_handler, action='open_editor',
|
||||||
|
category=tag.original_name if tag else None,
|
||||||
|
key=key))
|
||||||
|
|
||||||
|
if self.db.new_api.field_supports_notes(key):
|
||||||
|
item_id = self.db.new_api.get_item_id(tag.category, tag.original_name)
|
||||||
|
has_note = self._model.item_has_note(key, tag.original_name) #bool(self.db.new_api.notes_for(tag.category, item_id))
|
||||||
|
self.context_menu.addAction(self.edit_metadata_icon,
|
||||||
|
(_('Edit note for %s') if has_note else _('Create note for %s'))%display_name(tag),
|
||||||
|
partial(self.context_menu_handler, action='edit_note',
|
||||||
|
index=index, extra=item_id, category=tag.category))
|
||||||
|
|
||||||
# is_editable is also overloaded to mean 'can be added
|
# is_editable is also overloaded to mean 'can be added
|
||||||
# to a User category'
|
# to a User category'
|
||||||
@ -848,14 +923,6 @@ class TagsView(QTreeView): # {{{
|
|||||||
m.addAction(self.minus_icon,
|
m.addAction(self.minus_icon,
|
||||||
_('Remove %s from selected books') % display_name(tag),
|
_('Remove %s from selected books') % display_name(tag),
|
||||||
partial(self.context_menu_handler, action='remove_tag', index=index))
|
partial(self.context_menu_handler, action='remove_tag', index=index))
|
||||||
|
|
||||||
item_id = self.db.new_api.get_item_id(tag.category, tag.original_name)
|
|
||||||
has_note = bool(self.db.new_api.notes_for(tag.category, item_id))
|
|
||||||
self.context_menu.addAction(self.edit_metadata_icon,
|
|
||||||
(_('Edit note for %s') if has_note else _('Create note for %s'))%display_name(tag),
|
|
||||||
partial(self.context_menu_handler, action='edit_note',
|
|
||||||
index=index, extra=item_id, category=tag.category))
|
|
||||||
|
|
||||||
elif key == 'search' and tag.is_searchable:
|
elif key == 'search' and tag.is_searchable:
|
||||||
self.context_menu.addAction(self.rename_icon,
|
self.context_menu.addAction(self.rename_icon,
|
||||||
_('Rename %s')%display_name(tag),
|
_('Rename %s')%display_name(tag),
|
||||||
|
Loading…
x
Reference in New Issue
Block a user