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 @@
# 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 @@
__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 @@ def comments_factory(db, key, parent):
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 widget_factory(typ, key):
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 widget_factory(typ, key):
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 @@ def genesis(self, gui):
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.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 @@ def data(self, index, role):
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 @@ def get_esc_lang(l):
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 @@ def get_esc_lang(l):
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 @@ def initialize(self):
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 @@ def restore_defaults(self):
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 @@ def commit(self, *args):
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
+
+
+
+
+
+
+
+