mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Add a tweak to allow double clicking on the book list to open the edit metadata dialog (#8032). Add a tweak to use a template for formatting SONY collection names (Fixes #8033 (Sony collection format)). Bulk edit metadata, search and replace: Show all values for multiple fields in the text region, separated by ::: (#8030)
This commit is contained in:
commit
05b3f6adef
@ -135,32 +135,53 @@ auto_connect_to_folder = ''
|
||||
# metadata management is set to automatic. Collections on Sonys are named
|
||||
# depending upon whether the field is standard or custom. A collection derived
|
||||
# from a standard field is named for the value in that field. For example, if
|
||||
# the standard 'series' column contains the name 'Darkover', then the series
|
||||
# will be named 'Darkover'. A collection derived from a custom field will have
|
||||
# the name of the field added to the value. For example, if a custom series
|
||||
# the standard 'series' column contains the value 'Darkover', then the
|
||||
# collection name is 'Darkover'. A collection derived from a custom field will
|
||||
# have the name of the field added to the value. For example, if a custom series
|
||||
# column named 'My Series' contains the name 'Darkover', then the collection
|
||||
# will be named 'Darkover (My Series)'. If two books have fields that generate
|
||||
# the same collection name, then both books will be in that collection. This
|
||||
# tweak lets you specify for a standard or custom field the value to be put
|
||||
# inside the parentheses. You can use it to add a parenthetical description to a
|
||||
# will by default be named 'Darkover (My Series)'. For purposes of this
|
||||
# documentation, 'Darkover' is called the value and 'My Series' is called the
|
||||
# category. If two books have fields that generate the same collection name,
|
||||
# then both books will be in that collection.
|
||||
# This set of tweaks lets you specify for a standard or custom field how
|
||||
# the collections are to be named. You can use it to add a description to a
|
||||
# standard field, for example 'Foo (Tag)' instead of the 'Foo'. You can also use
|
||||
# it to force multiple fields to end up in the same collection. For example, you
|
||||
# could force the values in 'series', '#my_series_1', and '#my_series_2' to
|
||||
# appear in collections named 'some_value (Series)', thereby merging all of the
|
||||
# fields into one set of collections. The syntax of this tweak is
|
||||
# {'field_lookup_name':'name_to_use', 'lookup_name':'name', ...}
|
||||
# Example 1: I want three series columns to be merged into one set of
|
||||
# collections. If the column lookup names are 'series', '#series_1' and
|
||||
# '#series_2', and if I want nothing in the parenthesis, then the value to use
|
||||
# in the tweak value would be:
|
||||
# sony_collection_renaming_rules={'series':'', '#series_1':'', '#series_2':''}
|
||||
# Example 2: I want the word '(Series)' to appear on collections made from
|
||||
# series, and the word '(Tag)' to appear on collections made from tags. Use:
|
||||
# sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'}
|
||||
# Example 3: I want 'series' and '#myseries' to be merged, and for the
|
||||
# collection name to have '(Series)' appended. The renaming rule is:
|
||||
# sony_collection_renaming_rules={'series':'Series', '#myseries':'Series'}
|
||||
# fields into one set of collections.
|
||||
# There are two related tweaks. The first determines the category name to use
|
||||
# for a metadata field. The second is a template, used to determines how the
|
||||
# value and category are combined to create the collection name.
|
||||
# The syntax of the first tweak, sony_collection_renaming_rules, is:
|
||||
# {'field_lookup_name':'category_name_to_use', 'lookup_name':'name', ...}
|
||||
# The second tweak, sony_collection_name_template, is a template. It uses the
|
||||
# same template language as plugboards and save templates. This tweak controls
|
||||
# how the value and category are combined together to make the collection name.
|
||||
# The only two fields available are {category} and {value}. The {value} field is
|
||||
# never empty. The {category} field can be empty. The default is to put the
|
||||
# value first, then the category enclosed in parentheses, it is isn't empty:
|
||||
# '{value} {category:|(|)}'
|
||||
# Examples: The first three examples assume that the second tweak
|
||||
# has not been changed.
|
||||
# 1: I want three series columns to be merged into one set of collections. The
|
||||
# column lookup names are 'series', '#series_1' and '#series_2'. I want nothing
|
||||
# in the parenthesis. The value to use in the tweak value would be:
|
||||
# sony_collection_renaming_rules={'series':'', '#series_1':'', '#series_2':''}
|
||||
# 2: I want the word '(Series)' to appear on collections made from series, and
|
||||
# the word '(Tag)' to appear on collections made from tags. Use:
|
||||
# sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'}
|
||||
# 3: I want 'series' and '#myseries' to be merged, and for the collection name
|
||||
# to have '(Series)' appended. The renaming rule is:
|
||||
# sony_collection_renaming_rules={'series':'Series', '#myseries':'Series'}
|
||||
# 4: Same as example 2, but instead of having the category name in parentheses
|
||||
# and appended to the value, I want it prepended and separated by a colon, such
|
||||
# as in Series: Darkover. I must change the template used to format the category name
|
||||
# The resulting two tweaks are:
|
||||
# sony_collection_renaming_rules={'series':'Series', 'tags':'Tag'}
|
||||
# sony_collection_name_template='{category:||: }{value}'
|
||||
sony_collection_renaming_rules={}
|
||||
sony_collection_name_template='{value}{category:| (|)}'
|
||||
|
||||
|
||||
# Specify how sony collections are sorted. This tweak is only applicable if
|
||||
@ -244,8 +265,10 @@ generate_cover_title_font = None
|
||||
generate_cover_foot_font = None
|
||||
|
||||
|
||||
# Behavior of doubleclick on the books list. Choices:
|
||||
# open_viewer, do_nothing, edit_cell. Default: open_viewer.
|
||||
# Behavior of doubleclick on the books list. Choices: open_viewer, do_nothing,
|
||||
# edit_cell, edit_metadata. Selecting edit_metadata has the side effect of
|
||||
# disabling editing a field using a single click.
|
||||
# Default: open_viewer.
|
||||
# Example: doubleclick_on_library_view = 'do_nothing'
|
||||
doubleclick_on_library_view = 'open_viewer'
|
||||
|
||||
@ -265,4 +288,4 @@ locale_for_sorting = ''
|
||||
# Set whether to use one or two columns for custom metadata when editing
|
||||
# metadata one book at a time. If True, then the fields are laid out using two
|
||||
# columns. If False, one column is used.
|
||||
metadata_single_use_2_cols_for_custom_fields = True
|
||||
metadata_single_use_2_cols_for_custom_fields = True
|
||||
|
@ -14,6 +14,22 @@ from calibre.constants import preferred_encoding
|
||||
from calibre import isbytestring, force_unicode
|
||||
from calibre.utils.config import prefs, tweaks
|
||||
from calibre.utils.icu import strcmp
|
||||
from calibre.utils.formatter import TemplateFormatter
|
||||
|
||||
class SafeFormat(TemplateFormatter):
|
||||
'''
|
||||
Provides a format function that substitutes '' for any missing value
|
||||
'''
|
||||
|
||||
def get_value(self, key, args, kwargs):
|
||||
try:
|
||||
if key in kwargs:
|
||||
return kwargs[key]
|
||||
return key
|
||||
except:
|
||||
return key
|
||||
|
||||
safe_formatter = SafeFormat()
|
||||
|
||||
class Book(Metadata):
|
||||
def __init__(self, prefix, lpath, size=None, other=None):
|
||||
@ -107,23 +123,25 @@ class CollectionsBookList(BookList):
|
||||
return sortattr
|
||||
return None
|
||||
|
||||
def compute_category_name(self, attr, category, field_meta):
|
||||
def compute_category_name(self, field_key, field_value, field_meta):
|
||||
renames = tweaks['sony_collection_renaming_rules']
|
||||
attr_name = renames.get(attr, None)
|
||||
if attr_name is None:
|
||||
field_name = renames.get(field_key, None)
|
||||
if field_name is None:
|
||||
if field_meta['is_custom']:
|
||||
attr_name = '(%s)'%field_meta['name']
|
||||
field_name = field_meta['name']
|
||||
else:
|
||||
attr_name = ''
|
||||
elif attr_name != '':
|
||||
attr_name = '(%s)'%attr_name
|
||||
cat_name = '%s %s'%(category, attr_name)
|
||||
field_name = ''
|
||||
cat_name = safe_formatter.safe_format(
|
||||
fmt=tweaks['sony_collection_name_template'],
|
||||
kwargs={'category':field_name, 'value':field_value},
|
||||
error_value='', book=None)
|
||||
return cat_name.strip()
|
||||
|
||||
def get_collections(self, collection_attributes):
|
||||
from calibre.devices.usbms.driver import debug_print
|
||||
debug_print('Starting get_collections:', prefs['manage_device_metadata'])
|
||||
debug_print('Renaming rules:', tweaks['sony_collection_renaming_rules'])
|
||||
debug_print('Formatting template:', tweaks['sony_collection_name_template'])
|
||||
debug_print('Sorting rules:', tweaks['sony_collection_sorting_rules'])
|
||||
|
||||
# Complexity: we can use renaming rules only when using automatic
|
||||
|
@ -414,6 +414,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.s_r_template.completer().setCaseSensitivity(Qt.CaseSensitive)
|
||||
|
||||
self.s_r_search_mode_changed(self.search_mode.currentIndex())
|
||||
self.multiple_separator.setFixedWidth(30)
|
||||
self.multiple_separator.setText(' ::: ')
|
||||
self.multiple_separator.textChanged.connect(self.s_r_separator_changed)
|
||||
|
||||
def s_r_get_field(self, mi, field):
|
||||
if field:
|
||||
@ -451,19 +454,22 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
mi = self.db.get_metadata(self.ids[i], index_is_id=True)
|
||||
src = unicode(self.search_field.currentText())
|
||||
t = self.s_r_get_field(mi, src)
|
||||
w.setText(''.join(t[0:1]))
|
||||
w.setText(unicode(self.multiple_separator.text()).join(t))
|
||||
|
||||
if self.search_mode.currentIndex() == 0:
|
||||
self.destination_field.setCurrentIndex(idx)
|
||||
else:
|
||||
self.s_r_destination_field_changed(self.destination_field.currentText())
|
||||
self.s_r_paint_results(None)
|
||||
|
||||
def s_r_destination_field_changed(self, txt):
|
||||
txt = unicode(txt)
|
||||
if not txt:
|
||||
txt = unicode(self.search_field.currentText())
|
||||
self.comma_separated.setEnabled(True)
|
||||
if txt:
|
||||
fm = self.db.metadata_for_field(txt)
|
||||
if fm['is_multiple']:
|
||||
if txt and txt in self.writable_fields:
|
||||
self.destination_field_fm = self.db.metadata_for_field(txt)
|
||||
if self.destination_field_fm['is_multiple']:
|
||||
self.comma_separated.setEnabled(False)
|
||||
self.comma_separated.setChecked(True)
|
||||
self.s_r_paint_results(None)
|
||||
@ -493,6 +499,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
self.s_r_heading.setText('<p>'+self.main_heading + self.regexp_heading)
|
||||
self.s_r_paint_results(None)
|
||||
|
||||
def s_r_separator_changed(self, txt):
|
||||
self.s_r_search_field_changed(self.search_field.currentIndex())
|
||||
|
||||
def s_r_set_colors(self):
|
||||
if self.s_r_error is not None:
|
||||
col = 'rgb(255, 0, 0, 20%)'
|
||||
@ -592,8 +601,12 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
||||
wr = getattr(self, 'book_%d_result'%(i+1))
|
||||
try:
|
||||
result = self.s_r_do_regexp(mi)
|
||||
t = self.s_r_do_destination(mi, result[0:1])
|
||||
t = self.s_r_replace_mode_separator().join(t)
|
||||
t = self.s_r_do_destination(mi, result)
|
||||
if len(result) > 1 and self.destination_field_fm is not None and \
|
||||
self.destination_field_fm['is_multiple']:
|
||||
t = unicode(self.multiple_separator.text()).join(t)
|
||||
else:
|
||||
t = self.s_r_replace_mode_separator().join(t)
|
||||
wr.setText(t)
|
||||
except Exception as e:
|
||||
self.s_r_error = e
|
||||
|
@ -478,7 +478,7 @@ Future conversion of these books will use the default settings.</string>
|
||||
<item>
|
||||
<widget class="QLabel" name="xlabel_24">
|
||||
<property name="text">
|
||||
<string>Search mode:</string>
|
||||
<string>Search &mode:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>search_mode</cstring>
|
||||
@ -559,7 +559,7 @@ Future conversion of these books will use the default settings.</string>
|
||||
<string>Check this box if the search string must match exactly upper and lower case. Uncheck it if case is to be ignored</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Case sensitive</string>
|
||||
<string>Cas&e sensitive</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@ -588,7 +588,7 @@ Future conversion of these books will use the default settings.</string>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_41">
|
||||
<property name="text">
|
||||
<string>Apply function after replace:</string>
|
||||
<string>&Apply function after replace:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>replace_func</cstring>
|
||||
@ -641,7 +641,7 @@ If blank, the source field is used if the field is modifiable</string>
|
||||
<item>
|
||||
<widget class="QLabel" name="replace_mode_label">
|
||||
<property name="text">
|
||||
<string>Mode:</string>
|
||||
<string>M&ode:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>replace_mode</cstring>
|
||||
@ -658,11 +658,11 @@ If blank, the source field is used if the field is modifiable</string>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="comma_separated">
|
||||
<property name="toolTip">
|
||||
<string>If the replace mode is prepend or append, then this box indicates whether a comma or
|
||||
nothing should be put between the original text and the inserted text</string>
|
||||
<string>Specifies whether a comma should be put between values when copying from a
|
||||
multiple-valued field to a single-valued field</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>use comma</string>
|
||||
<string>&Use comma</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
@ -687,7 +687,7 @@ nothing should be put between the original text and the inserted text</string>
|
||||
<item row="8" column="1">
|
||||
<widget class="QLabel" name="xlabel_3">
|
||||
<property name="text">
|
||||
<string>Test &text</string>
|
||||
<string>Test text</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>test_text</cstring>
|
||||
@ -695,14 +695,48 @@ nothing should be put between the original text and the inserted text</string>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="2">
|
||||
<widget class="QLabel" name="label_51">
|
||||
<property name="text">
|
||||
<string>Test re&sult</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>test_result</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_21">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_51">
|
||||
<property name="text">
|
||||
<string>Test result</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>test_result</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="HSpacer_347">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="xlabel_41">
|
||||
<property name="text">
|
||||
<string>Multi&ple separator:</string>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>multiple_separator</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="multiple_separator">
|
||||
<property name="toolTip">
|
||||
<string>Used when displaying test results to separate values in multiple-valued fields</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="9" column="0" colspan="4">
|
||||
<widget class="QScrollArea" name="scrollArea11">
|
||||
@ -823,7 +857,7 @@ nothing should be put between the original text and the inserted text</string>
|
||||
<tabstop>destination_field</tabstop>
|
||||
<tabstop>replace_mode</tabstop>
|
||||
<tabstop>comma_separated</tabstop>
|
||||
<tabstop>scrollArea11</tabstop>
|
||||
<tabstop>multiple_separator</tabstop>
|
||||
<tabstop>test_text</tabstop>
|
||||
<tabstop>test_result</tabstop>
|
||||
</tabstops>
|
||||
|
@ -57,6 +57,11 @@ class BooksView(QTableView): # {{{
|
||||
elif tweaks['doubleclick_on_library_view'] == 'open_viewer':
|
||||
self.setEditTriggers(self.SelectedClicked|self.editTriggers())
|
||||
self.doubleClicked.connect(parent.iactions['View'].view_triggered)
|
||||
elif tweaks['doubleclick_on_library_view'] == 'edit_metadata':
|
||||
# Must not enable single-click to edit, or the field will remain
|
||||
# open in edit mode underneath the edit metadata dialog
|
||||
self.doubleClicked.connect(
|
||||
partial(parent.iactions['Edit Metadata'].edit_metadata, checked=False))
|
||||
|
||||
self.drag_allowed = True
|
||||
self.setDragEnabled(True)
|
||||
|
Loading…
x
Reference in New Issue
Block a user