diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py index 8c92aa8a6e..cfebe796a3 100644 --- a/src/calibre/devices/usbms/books.py +++ b/src/calibre/devices/usbms/books.py @@ -203,6 +203,8 @@ class CollectionsBookList(BookList): val = [orig_val] elif fm['datatype'] == 'text' and fm['is_multiple']: val = orig_val + elif fm['datatype'] == 'composite' and fm['is_multiple']: + val = [v.strip() for v in val.split(fm['is_multiple'])] else: val = [val] diff --git a/src/calibre/ebooks/metadata/book/base.py b/src/calibre/ebooks/metadata/book/base.py index ff22cd3608..167ae52fa3 100644 --- a/src/calibre/ebooks/metadata/book/base.py +++ b/src/calibre/ebooks/metadata/book/base.py @@ -483,7 +483,7 @@ class Metadata(object): self_tags = self.get(x, []) self.set_user_metadata(x, meta) # get... did the deepcopy other_tags = other.get(x, []) - if meta['is_multiple']: + if meta['datatype'] == 'text' and meta['is_multiple']: # Case-insensitive but case preserving merging lotags = [t.lower() for t in other_tags] lstags = [t.lower() for t in self_tags] diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 0683f2cb91..8a97183ffe 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -519,6 +519,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): val = [] if fm['is_multiple'] else [''] elif not fm['is_multiple']: val = [val] + elif fm['datatype'] == 'composite': + val = [v.strip() for v in val.split(fm['is_multiple'])] elif field == 'authors': val = [v.replace('|', ',') for v in val] else: diff --git a/src/calibre/gui2/preferences/columns.py b/src/calibre/gui2/preferences/columns.py index 03a50e6f3a..92aafccce0 100644 --- a/src/calibre/gui2/preferences/columns.py +++ b/src/calibre/gui2/preferences/columns.py @@ -163,8 +163,9 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): elif '*edited' in self.custcols[c]: cc = self.custcols[c] db.set_custom_column_metadata(cc['colnum'], name=cc['name'], - label=cc['label'], - display = self.custcols[c]['display']) + label=cc['label'], + display = self.custcols[c]['display'], + notify=False) if '*must_restart' in self.custcols[c]: must_restart = True return must_restart diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py index f476845f8b..fcbaaf181f 100644 --- a/src/calibre/gui2/preferences/create_custom_column.py +++ b/src/calibre/gui2/preferences/create_custom_column.py @@ -41,6 +41,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): 'text':_('Yes/No'), 'is_multiple':False}, 10:{'datatype':'composite', 'text':_('Column built from other columns'), 'is_multiple':False}, + 11:{'datatype':'*composite', + 'text':_('Column built from other columns, behaves like tags'), 'is_multiple':True}, } def __init__(self, parent, editing, standard_colheads, standard_colnames): @@ -99,7 +101,9 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): c = parent.custcols[col] self.column_name_box.setText(c['label']) self.column_heading_box.setText(c['name']) - ct = c['datatype'] if not c['is_multiple'] else '*text' + ct = c['datatype'] + if c['is_multiple']: + ct = '*' + ct self.orig_column_number = c['colnum'] self.orig_column_name = col column_numbers = dict(map(lambda x:(self.column_types[x]['datatype'], x), @@ -109,7 +113,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): if ct == 'datetime': if c['display'].get('date_format', None): self.date_format_box.setText(c['display'].get('date_format', '')) - elif ct == 'composite': + elif ct in ['composite', '*composite']: self.composite_box.setText(c['display'].get('composite_template', '')) sb = c['display'].get('composite_sort', 'text') vals = ['text', 'number', 'date', 'bool'] @@ -167,7 +171,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): getattr(self, 'date_format_'+x).setVisible(col_type == 'datetime') for x in ('box', 'default_label', 'label', 'sort_by', 'sort_by_label', 'make_category'): - getattr(self, 'composite_'+x).setVisible(col_type == 'composite') + getattr(self, 'composite_'+x).setVisible(col_type in ['composite', '*composite']) for x in ('box', 'default_label', 'label'): getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration') self.use_decorations.setVisible(col_type in ['text', 'composite', 'enumeration']) @@ -187,8 +191,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): 'because these names are reserved for the index of a series column.')) col_heading = unicode(self.column_heading_box.text()).strip() col_type = self.column_types[self.column_type_box.currentIndex()]['datatype'] - if col_type == '*text': - col_type='text' + if col_type[0] == '*': + col_type = col_type[1:] is_multiple = True else: is_multiple = False @@ -249,11 +253,10 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): elif col_type == 'text' and is_multiple: display_dict = {'is_names': self.is_names.isChecked()} - if col_type in ['text', 'composite', 'enumeration']: + if col_type in ['text', 'composite', 'enumeration'] and not is_multiple: display_dict['use_decorations'] = self.use_decorations.checkState() if not self.editing_col: - db.field_metadata self.parent.custcols[key] = { 'label':col, 'name':col_heading, diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 5f4cfcba07..4d696afe91 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -751,7 +751,7 @@ class ResultCache(SearchQueryParser): # {{{ if loc not in exclude_fields: # time for text matching if is_multiple_cols[loc] is not None: - vals = item[loc].split(is_multiple_cols[loc]) + vals = [v.strip() for v in item[loc].split(is_multiple_cols[loc])] else: vals = [item[loc]] ### make into list to make _match happy if _match(q, vals, matchkind): diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index 8eed121b21..187d718a39 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -182,7 +182,7 @@ class CustomColumns(object): else: is_category = False if v['is_multiple']: - is_m = '|' + is_m = ',' if v['datatype'] == 'composite' else '|' else: is_m = None tn = 'custom_column_{0}'.format(v['num']) @@ -318,7 +318,7 @@ class CustomColumns(object): self.conn.commit() def set_custom_column_metadata(self, num, name=None, label=None, - is_editable=None, display=None): + is_editable=None, display=None, notify=True): changed = False if name is not None: self.conn.execute('UPDATE custom_columns SET name=? WHERE id=?', @@ -340,6 +340,9 @@ class CustomColumns(object): if changed: self.conn.commit() + if notify: + self.notify('metadata', []) + return changed def set_custom_bulk_multiple(self, ids, add=[], remove=[], @@ -595,7 +598,7 @@ class CustomColumns(object): raise ValueError('%r is not a supported data type'%datatype) normalized = datatype not in ('datetime', 'comments', 'int', 'bool', 'float', 'composite') - is_multiple = is_multiple and datatype in ('text',) + is_multiple = is_multiple and datatype in ('text', 'composite') num = self.conn.execute( ('INSERT INTO ' 'custom_columns(label,name,datatype,is_multiple,editable,display,normalized)' diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 7b4d52dbcd..aad33e808e 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1354,6 +1354,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): cat = tb_cats[category] if cat['datatype'] == 'composite' and \ cat['display'].get('make_category', False): + tids[category] = {} tcategories[category] = {} md.append((category, cat['rec_index'], cat['is_multiple'], cat['datatype'] == 'composite')) @@ -1401,9 +1402,18 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): except: prints('get_categories: item', val, 'is not in', cat, 'list!') else: - vals = book[dex].split(mult) + vals = [v.strip() for v in book[dex].split(mult) if v] + if is_comp: + for val in vals: + if val not in tids: + tids[cat][val] = (0, val) + item = tcategories[cat].get(val, None) + if not item: + item = tag_class(val, val) + tcategories[cat][val] = item + item.c += 1 + item.id = val for val in vals: - if not val: continue try: (item_id, sort_val) = tids[cat][val] # let exceptions fly item = tcategories[cat].get(val, None)