From 779922d1f33a483b912ce53de03438fca15cc835 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 24 Dec 2010 11:25:23 +0000 Subject: [PATCH 1/3] Enhancement #8030: View "next testexample" in Multiple Value Metadata Fields --- src/calibre/gui2/dialogs/metadata_bulk.py | 25 +++++++-- src/calibre/gui2/dialogs/metadata_bulk.ui | 68 +++++++++++++++++------ 2 files changed, 70 insertions(+), 23 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index dc691c4ffe..bde5cae128 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -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('

'+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 diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index dca7abc82c..d945909f96 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -478,7 +478,7 @@ Future conversion of these books will use the default settings. - Search mode: + Search &mode: search_mode @@ -559,7 +559,7 @@ Future conversion of these books will use the default settings. Check this box if the search string must match exactly upper and lower case. Uncheck it if case is to be ignored - Case sensitive + Cas&e sensitive true @@ -588,7 +588,7 @@ Future conversion of these books will use the default settings. - Apply function after replace: + &Apply function after replace: replace_func @@ -641,7 +641,7 @@ If blank, the source field is used if the field is modifiable - Mode: + M&ode: replace_mode @@ -658,11 +658,11 @@ If blank, the source field is used if the field is modifiable - 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 + Specifies whether a comma should be put between values when copying from a +multiple-valued field to a single-valued field - use comma + &Use comma true @@ -687,7 +687,7 @@ nothing should be put between the original text and the inserted text - Test &text + Test text test_text @@ -695,14 +695,48 @@ nothing should be put between the original text and the inserted text - - - Test re&sult - - - test_result - - + + + + + Test result + + + test_result + + + + + + + Qt::Horizontal + + + + 20 + 0 + + + + + + + + Multi&ple separator: + + + multiple_separator + + + + + + + Used when displaying test results to separate values in multiple-valued fields + + + + @@ -823,7 +857,7 @@ nothing should be put between the original text and the inserted text destination_field replace_mode comma_separated - scrollArea11 + multiple_separator test_text test_result From 68e32e6ee51456bea907413e98409c59b5b70f50 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 24 Dec 2010 13:11:02 +0000 Subject: [PATCH 2/3] Enhancement #8033: Sony collection name format changes --- resources/default_tweaks.py | 61 ++++++++++++++++++++---------- src/calibre/devices/usbms/books.py | 34 +++++++++++++---- 2 files changed, 67 insertions(+), 28 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index a420cd7d44..d01999e766 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -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 tweak 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 diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index ba005c4e6d..73afd770c1 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -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 From be1dc1059c97eab461f6e1dfd98551cdd9829454 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 24 Dec 2010 13:37:27 +0000 Subject: [PATCH 3/3] Enhancement #8032: Double click to edit metadata --- resources/default_tweaks.py | 6 ++++-- src/calibre/gui2/library/views.py | 5 +++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index d01999e766..efcd004acd 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -265,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' diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index a6285c6656..8dad4c21b1 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -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)