From cc9bb5fb57a78d64f8c392ec4d7e260909709580 Mon Sep 17 00:00:00 2001 From: Charles Haley Date: Tue, 1 Feb 2022 15:56:36 +0000 Subject: [PATCH] Add a tab to Look/feel to choose which fields to show in edit metadata as well as set their order. --- resources/default_tweaks.py | 11 --- src/calibre/gui2/custom_column_widgets.py | 72 +++++++++-------- src/calibre/gui2/preferences/behavior.py | 4 - src/calibre/gui2/preferences/behavior.ui | 17 ---- src/calibre/gui2/preferences/look_feel.py | 40 +++++++++- src/calibre/gui2/preferences/look_feel.ui | 96 +++++++++++++++++++++++ 6 files changed, 174 insertions(+), 66 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index ba6c80d234..167aac5187 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -434,17 +434,6 @@ locale_for_sorting = '' # columns. If False, one column is used. metadata_single_use_2_cols_for_custom_fields = True -#: Order of custom column(s) in edit metadata -# Controls the order that custom columns are listed in edit metadata single -# and bulk. The columns listed in the tweak are displayed first and in the -# order provided. Any columns not listed are displayed after the listed ones, -# in alphabetical order. Do note that this tweak does not change the size of -# the edit widgets. Putting comments widgets in this list may result in some -# odd widget spacing when using two-column mode. -# Enter a comma-separated list of custom field lookup names, as in -# metadata_edit_custom_column_order = ['#genre', '#mytags', '#etc'] -metadata_edit_custom_column_order = [] - #: Edit metadata custom column label width and elision point # Set the width of custom column labels shown in the edit metadata dialogs. # If metadata_edit_elide_labels is True then labels wider than the width diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index c8c1a4728b..f1b6e4d83f 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -6,6 +6,7 @@ __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' import os +from collections import OrderedDict from functools import partial from qt.core import (Qt, QComboBox, QLabel, QSpinBox, QDoubleSpinBox, @@ -749,10 +750,31 @@ widgets = { def field_sort_key(y, fm=None): m1 = fm[y] name = icu_lower(m1['name']) - n1 = 'zzzzz' + name if m1['datatype'] == 'comments' and m1.get('display', {}).get('interpret_as') != 'short-text' else name + n1 = 'zzzzz' + name if column_is_comments(y, fm) else name return sort_key(n1) +def column_is_comments(key, fm): + return (fm[key]['datatype'] == 'comments' and + fm[key].get('display', {}).get('interpret_as') != 'short-text') + + +def get_field_list(db, use_defaults=False): + fm = db.field_metadata + fields = fm.custom_field_keys(include_composites=False) + displayable = db.prefs.get('edit_metadata_custom_columns_to_display', None) + if use_defaults or displayable is None: + fields.sort(key=partial(field_sort_key, fm=fm)) + return [(k, True) for k in fields] + else: + field_set = set(fields) + result = OrderedDict({k:v for k,v in displayable if k in field_set}) + for k in fields: + if k not in result: + result[k] = True + return [(k,v) for k,v in result.items()] + + def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, parent=None): def widget_factory(typ, key): if bulk: @@ -765,36 +787,22 @@ def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, pa fm = db.field_metadata # Get list of all non-composite custom fields. We must make widgets for these - fields = fm.custom_field_keys(include_composites=False) - cols_to_display = fields - cols_to_display.sort(key=partial(field_sort_key, fm=fm)) - - # This will contain the fields in the order to display them - cols = [] - - # The fields named here must be first in the widget list - tweak_cols = tweaks['metadata_edit_custom_column_order'] - comments_in_tweak = 0 - for key in (tweak_cols or ()): - # Add the key if it really exists in the database - if key in cols_to_display: - cols.append(key) - if fm[key]['datatype'] == 'comments' and fm[key].get('display', {}).get('interpret_as') != 'short-text': - comments_in_tweak += 1 - - # Add all the remaining fields - comments_not_in_tweak = 0 - for key in cols_to_display: - if key not in cols: - cols.append(key) - if fm[key]['datatype'] == 'comments' and fm[key].get('display', {}).get('interpret_as') != 'short-text': - comments_not_in_tweak += 1 + cols = [k[0] for k in get_field_list(db) if k[1]] + # This deals with the historical behavior where comments fields go to the + # bottom, starting on the left hand side. If a comment field is moved to + # somewhere else then it isn't moved to either side. + comments_at_end = 0 + for k in cols[::-1]: + if not column_is_comments(k, fm): + break + comments_at_end += 1 + comments_not_at_end = len([k for k in cols if column_is_comments(k, fm)]) - comments_at_end count = len(cols) layout_rows_for_comments = 9 if two_column: - turnover_point = int(((count - comments_not_in_tweak + 1) + - int(comments_in_tweak*(layout_rows_for_comments-1)))/2) + turnover_point = int(((count - comments_at_end + 1) + + int(comments_not_at_end*(layout_rows_for_comments-1)))/2) else: # Avoid problems with multi-line widgets turnover_point = count + 1000 @@ -813,22 +821,22 @@ def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, pa dt = fm[key]['datatype'] if dt == 'composite' or (bulk and dt == 'comments'): continue - is_comments = dt == 'comments' and fm[key].get('display', {}).get('interpret_as') != 'short-text' + is_comments = column_is_comments(key, fm) w = widget_factory(dt, fm[key]['colnum']) ans.append(w) if two_column and is_comments: # Here for compatibility with old layout. Comments always started # in the left column - comments_in_tweak -= 1 + comments_not_at_end -= 1 # no special processing if the comment field was named in the tweak - if comments_in_tweak < 0 and comments_not_in_tweak > 0: + if comments_not_at_end < 0 and comments_at_end > 0: # Force a turnover, adding comments widgets below max_row. # Save the row to return to if we turn over again column = 0 row = max_row base_row = row - turnover_point = row + int((comments_not_in_tweak * layout_rows_for_comments)/2) - comments_not_in_tweak = 0 + turnover_point = row + int((comments_at_end * layout_rows_for_comments)/2) + comments_at_end = 0 l = QGridLayout() if is_comments: diff --git a/src/calibre/gui2/preferences/behavior.py b/src/calibre/gui2/preferences/behavior.py index 6058d3b00a..61e75dfe77 100644 --- a/src/calibre/gui2/preferences/behavior.py +++ b/src/calibre/gui2/preferences/behavior.py @@ -76,10 +76,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): r('bools_are_tristate', db.prefs, restart_required=True) r('numeric_collation', prefs, restart_required=True) - r = self.register - choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1'), - (_('All on 1 tab'), 'alt2')] - r('edit_metadata_single_layout', gprefs, choices=choices) def initialize(self): ConfigWidgetBase.initialize(self) diff --git a/src/calibre/gui2/preferences/behavior.ui b/src/calibre/gui2/preferences/behavior.ui index 4b6b3dfe22..e4917da2ca 100644 --- a/src/calibre/gui2/preferences/behavior.ui +++ b/src/calibre/gui2/preferences/behavior.ui @@ -75,13 +75,6 @@ - - - - Choose a different layout for the Edit metadata dialog. The compact metadata layout favors editing custom metadata over changing covers and formats. - - - @@ -175,16 +168,6 @@ - - - - Edit metadata (single) &layout: - - - opt_edit_metadata_single_layout - - - diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index a4874f20f0..07c8d8c5fe 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -22,6 +22,7 @@ from calibre.ebooks.metadata.book.render import DEFAULT_AUTHOR_LINK from calibre.constants import ismacos, iswindows from calibre.ebooks.metadata.sources.prefs import msprefs from calibre.gui2 import default_author_link +from calibre.gui2.custom_column_widgets import get_field_list as em_get_field_list from calibre.gui2.dialogs.template_dialog import TemplateDialog from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList from calibre.gui2.preferences.look_feel_ui import Ui_Form @@ -259,8 +260,8 @@ class DisplayedFields(QAbstractListModel): # {{{ except: pass if not name: - name = field - return name + return field + return f'{name} ({field})' if role == Qt.ItemDataRole.CheckStateRole: return Qt.CheckState.Checked if visible else Qt.CheckState.Unchecked if role == Qt.ItemDataRole.DecorationRole and field.startswith('#'): @@ -328,6 +329,23 @@ def move_field_down(widget, model): # }}} +class EMDisplayedFields(DisplayedFields): # {{{ + def __init__(self, db, parent=None): + DisplayedFields.__init__(self, db, parent) + + def initialize(self, use_defaults=False): + self.beginResetModel() + self.fields = [[x[0], x[1]] for x in + em_get_field_list(self.db, use_defaults=use_defaults)] + self.endResetModel() + self.changed = True + + def commit(self): + if self.changed: + self.db.new_api.set_pref('edit_metadata_custom_columns_to_display', self.fields) +# }}} + + class QVDisplayedFields(DisplayedFields): # {{{ def __init__(self, db, parent=None): @@ -515,6 +533,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): key=lambda x:sort_key(x[0])) r('field_under_covers_in_grid', db.prefs, choices=choices) + choices = [(_('Default'), 'default'), (_('Compact Metadata'), 'alt1'), + (_('All on 1 tab'), 'alt2')] + r('edit_metadata_single_layout', gprefs, + choices=[(_('Default'), 'default'), (_('Compact Metadata'), 'alt1'), + (_('All on 1 tab'), 'alt2')]) + self.current_font = self.initial_font = None self.change_font_button.clicked.connect(self.change_font) @@ -527,6 +551,15 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): connect_lambda(self.df_down_button.clicked, self, lambda self: move_field_down(self.field_display_order, self.display_model)) + self.em_display_model = EMDisplayedFields(self.gui.current_db, + self.em_display_order) + self.em_display_model.dataChanged.connect(self.changed_signal) + self.em_display_order.setModel(self.em_display_model) + connect_lambda(self.em_up_button.clicked, self, + lambda self: move_field_up(self.em_display_order, self.em_display_model)) + connect_lambda(self.em_down_button.clicked, self, + lambda self: move_field_down(self.em_display_order, self.em_display_model)) + self.qv_display_model = QVDisplayedFields(self.gui.current_db, self.qv_display_order) self.qv_display_model.dataChanged.connect(self.changed_signal) @@ -648,6 +681,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.current_font = self.initial_font = font self.update_font_display() self.display_model.initialize() + self.em_display_model.initialize() self.qv_display_model.initialize() db = self.gui.current_db mi = [] @@ -709,6 +743,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.changed_signal.emit() self.update_font_display() self.display_model.restore_defaults() + self.em_display_model.restore_defaults() self.qv_display_model.restore_defaults() self.edit_rules.clear() self.icon_rules.clear() @@ -779,6 +814,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): QApplication.setFont(self.font_display.font()) rr = True self.display_model.commit() + self.em_display_model.commit() self.qv_display_model.commit() self.edit_rules.commit(self.gui.current_db.prefs) self.icon_rules.commit(self.gui.current_db.prefs) diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui index 70c6450000..54dd5a627c 100644 --- a/src/calibre/gui2/preferences/look_feel.ui +++ b/src/calibre/gui2/preferences/look_feel.ui @@ -881,6 +881,102 @@ A value of zero means calculate automatically. + + + + :/images/book.png:/images/book.png + + + Edit metadata + + + + + + + + Edit metadata (single) &layout: + + + opt_edit_metadata_single_layout + + + + + + + Choose a different layout for the Edit metadata dialog. The compact metadata layout favors editing custom metadata over changing covers and formats. + + + + + + + Qt::Horizontal + + + + 20 + 40 + + + + + + + + + + Select the custom columns to display in the dialogs and their order + + + + + + Move down + + + + :/images/arrow-down.png:/images/arrow-down.png + + + + + + + Move up + + + + :/images/arrow-up.png:/images/arrow-up.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + true + + + + + + + +