Fixes #2018227 [User Categories: "Hide empty categories" failing for sub-categories](https://bugs.launchpad.net/calibre/+bug/2018227)
This commit is contained in:
Kovid Goyal 2023-05-02 07:20:27 +05:30
commit 1ac0457238
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
4 changed files with 62 additions and 8 deletions

View File

@ -5,6 +5,7 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from functools import partial
from calibre.gui2.actions import InterfaceAction from calibre.gui2.actions import InterfaceAction
@ -12,13 +13,19 @@ from calibre.gui2.actions import InterfaceAction
class OpenFolderAction(InterfaceAction): class OpenFolderAction(InterfaceAction):
name = 'Open Folder' name = 'Open Folder'
action_spec = (_('Open containing folder'), 'document_open.png', action_spec = (_('Open book folder'), 'document_open.png',
_('Open the folder containing the current book\'s files'), _('O')) _('Open the folder containing the current book\'s files'), _('O'))
dont_add_to = frozenset(('context-menu-device',)) dont_add_to = frozenset(('context-menu-device',))
action_type = 'current' action_type = 'current'
action_add_menu = True
action_menu_clone_qaction = _('Open book folder')
def genesis(self): def genesis(self):
self.qaction.triggered.connect(self.gui.iactions['View'].view_folder) va = self.gui.iactions['View'].view_folder
self.qaction.triggered.connect(va)
a = self.create_menu_action(self.qaction.menu(), 'show-data-folder',
_('Open book data folder'), icon='document_open.png', shortcut=tuple())
a.triggered.connect(partial(va, data_folder=True))
def location_selected(self, loc): def location_selected(self, loc):
enabled = loc == 'library' enabled = loc == 'library'

View File

@ -263,7 +263,7 @@ class ViewAction(InterfaceAction):
'cannot be stopped until complete. Do you wish to continue?' 'cannot be stopped until complete. Do you wish to continue?'
) % num, show_copy_button=False, skip_dialog_name=skip_dialog_name) ) % num, show_copy_button=False, skip_dialog_name=skip_dialog_name)
def view_folder(self, *args): def view_folder(self, *args, **kwargs):
rows = self.gui.current_view().selectionModel().selectedRows() rows = self.gui.current_view().selectionModel().selectedRows()
if not rows or len(rows) == 0: if not rows or len(rows) == 0:
d = error_dialog(self.gui, _('Cannot open folder'), d = error_dialog(self.gui, _('Cannot open folder'),
@ -272,12 +272,25 @@ class ViewAction(InterfaceAction):
return return
if not self._view_check(len(rows), max_=10, skip_dialog_name='open-folder-many-check'): if not self._view_check(len(rows), max_=10, skip_dialog_name='open-folder-many-check'):
return return
data_folder = kwargs.get('data_folder', False)
db = self.gui.current_db db = self.gui.current_db
for i, row in enumerate(rows): for i, row in enumerate(rows):
self.gui.extra_files_watcher.watch_book(db.id(row.row())) self.gui.extra_files_watcher.watch_book(db.id(row.row()))
path = db.abspath(row.row()) path = db.abspath(row.row())
if path: if path:
open_local_file(path) if data_folder:
path = os.path.join(path, DATA_DIR_NAME)
if not os.path.exists(path):
try:
os.mkdir(path)
except Exception as e:
error_dialog(self.gui, _('Failed to create folder'), str(e), show=True)
continue
try:
open_local_file(path)
except Exception as e:
# We shouldn't get here ...
error_dialog(self.gui, _('Cannot open folder'), str(e), show=True)
if ismacos and i < len(rows) - 1: if ismacos and i < len(rows) - 1:
time.sleep(0.1) # Finder cannot handle multiple folder opens time.sleep(0.1) # Finder cannot handle multiple folder opens
@ -289,7 +302,14 @@ class ViewAction(InterfaceAction):
def view_data_folder_for_id(self, id_): def view_data_folder_for_id(self, id_):
self.gui.extra_files_watcher.watch_book(id_) self.gui.extra_files_watcher.watch_book(id_)
path = self.gui.current_db.abspath(id_, index_is_id=True) path = self.gui.current_db.abspath(id_, index_is_id=True)
open_local_file(os.path.join(path, DATA_DIR_NAME)) path = os.path.join(path, DATA_DIR_NAME)
if not os.path.exists(path):
try:
os.mkdir(path)
except Exception as e:
error_dialog(self.gui, _('Failed to create folder'), str(e), show=True)
return
open_local_file(path)
def view_book(self, triggered): def view_book(self, triggered):
rows = self.gui.current_view().selectionModel().selectedRows() rows = self.gui.current_view().selectionModel().selectedRows()

View File

@ -405,6 +405,8 @@ class BooksModel(QAbstractTableModel): # {{{
def refresh_rows(self, rows, current_row=-1): def refresh_rows(self, rows, current_row=-1):
self._clear_caches() self._clear_caches()
cc = self.columnCount(QModelIndex()) - 1 cc = self.columnCount(QModelIndex()) - 1
for r in rows:
self.db.new_api.clear_extra_files_cache(self.db.id(r))
for first_row, last_row in group_numbers(rows): for first_row, last_row in group_numbers(rows):
self.dataChanged.emit(self.index(first_row, 0), self.index(last_row, cc)) self.dataChanged.emit(self.index(first_row, 0), self.index(last_row, cc))
if current_row >= 0 and first_row <= current_row <= last_row: if current_row >= 0 and first_row <= current_row <= last_row:

View File

@ -816,13 +816,38 @@ class TagsModel(QAbstractItemModel): # {{{
process_one_node(category, collapse_model, self.db.new_api.fields['rating'].book_value_map, process_one_node(category, collapse_model, self.db.new_api.fields['rating'].book_value_map,
state_map.get(category.category_key, {})) state_map.get(category.category_key, {}))
# Fix up the node tree, reordering as needed and deleting undisplayed nodes # Fix up the node tree, reordering as needed and deleting undisplayed
# nodes. First, remove empty user category subnodes if needed. This is a
# recursive process because the hierarchical categories were combined
# together in process_one_node (above), which also computes the child
# count.
if self.prefs['tag_browser_hide_empty_categories']:
def process_uc_children(parent, depth):
new_children = []
for node in parent.children:
if node.type == TagTreeItem.CATEGORY:
# I could De Morgan's this but I think it is more
# understandable this way
if node.category_key.startswith('@') and len(node.children) == 0:
pass
else:
new_children.append(node)
process_uc_children(node, depth+1)
else:
new_children.append(node)
parent.children = new_children
for node in self.root_item.children:
if node.category_key.startswith('@'):
process_uc_children(node, 1)
# Now check the standard categories and root-level user categories,
# removing any hidden categories and if needed, empty categories
new_children = [] new_children = []
for node in self.root_item.children: for node in self.root_item.children:
if self.prefs['tag_browser_hide_empty_categories'] and len(node.child_tags()) == 0:
continue
key = node.category_key key = node.category_key
if key in self.row_map: if key in self.row_map:
if self.prefs['tag_browser_hide_empty_categories'] and len(node.child_tags()) == 0:
continue
if self.hidden_categories: if self.hidden_categories:
if key in self.hidden_categories: if key in self.hidden_categories:
continue continue