From 201e4ed09b5e0808de76c32c34635a2a2784476e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 17 Dec 2010 09:44:51 +0000 Subject: [PATCH 1/4] Make title_sort editable in metadata_single --- src/calibre/gui2/dialogs/metadata_single.py | 61 ++++++++++++++--- src/calibre/gui2/dialogs/metadata_single.ui | 76 +++++++++++++++------ src/calibre/library/database2.py | 14 ++++ src/calibre/library/schema_upgrades.py | 10 +++ 4 files changed, 131 insertions(+), 30 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 4a9bb784c8..7c6da7c1f9 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -23,7 +23,7 @@ from calibre.gui2.dialogs.tag_editor import TagEditor from calibre.gui2.widgets import ProgressIndicator from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks.metadata import string_to_authors, \ - authors_to_string, check_isbn + authors_to_string, check_isbn, title_sort from calibre.ebooks.metadata.covers import download_cover from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata import MetaInformation @@ -444,13 +444,24 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.cover_fetcher = None self.bc_box.layout().setAlignment(self.cover, Qt.AlignCenter|Qt.AlignHCenter) base = unicode(self.author_sort.toolTip()) - self.ok_aus_tooltip = '

' + textwrap.fill(base+'

'+ + ok_tooltip = '

' + textwrap.fill(base+'

'+ _(' The green color indicates that the current ' 'author sort matches the current author')) - self.bad_aus_tooltip = '

'+textwrap.fill(base + '

'+ + bad_tooltip = '

'+textwrap.fill(base + '

'+ _(' The red color indicates that the current ' - 'author sort does not match the current author')) + 'author sort does not match the current author. ' + 'No action is required if this is what you want.')) + self.aus_tooltips = (ok_tooltip, bad_tooltip) + base = unicode(self.title_sort.toolTip()) + ok_tooltip = '

' + textwrap.fill(base+'

'+ + _(' The green color indicates that the current ' + 'title sort matches the current title')) + bad_tooltip = '

'+textwrap.fill(base + '

'+ + _(' The red color warns that the current ' + 'title sort does not match the current title. ' + 'No action is required if this is what you want.')) + self.ts_tooltips = (ok_tooltip, bad_tooltip) self.row_delta = 0 if prev: self.prev_button = QPushButton(QIcon(I('back.png')), _('Previous'), @@ -506,7 +517,13 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.remove_unused_series) QObject.connect(self.auto_author_sort, SIGNAL('clicked()'), self.deduce_author_sort) + QObject.connect(self.auto_title_sort, SIGNAL('clicked()'), + self.deduce_title_sort) self.trim_cover_button.clicked.connect(self.trim_cover) + self.connect(self.title_sort, SIGNAL('textChanged(const QString&)'), + self.title_sort_box_changed) + self.connect(self.title, SIGNAL('textChanged(const QString&)'), + self.title_box_changed) self.connect(self.author_sort, SIGNAL('textChanged(const QString&)'), self.author_sort_box_changed) self.connect(self.authors, SIGNAL('editTextChanged(const QString&)'), @@ -523,6 +540,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.title.setText(db.title(row)) + self.title_sort.setText(db.title_sort(row)) isbn = db.isbn(self.id, index_is_id=True) if not isbn: isbn = '' @@ -610,27 +628,40 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): for c in range(2, len(ans[i].widgets), 2): w.setTabOrder(ans[i].widgets[c-1], ans[i].widgets[c+1]) + def title_box_changed(self, txt): + ts = unicode(txt) + ts = title_sort(ts) + self.mark_box_as_ok(control = self.title_sort, tt=self.ts_tooltips, + normal=(unicode(self.title_sort.text()) == ts)) + + def title_sort_box_changed(self, txt): + ts = unicode(txt) + self.mark_box_as_ok(control = self.title_sort, tt=self.ts_tooltips, + normal=(title_sort(unicode(self.title.text())) == ts)) + def authors_box_changed(self, txt): aus = unicode(txt) aus = re.sub(r'\s+et al\.$', '', aus) aus = self.db.author_sort_from_authors(string_to_authors(aus)) - self.mark_author_sort(normal=(unicode(self.author_sort.text()) == aus)) + self.mark_box_as_ok(control = self.author_sort, tt=self.aus_tooltips, + normal=(unicode(self.author_sort.text()) == aus)) def author_sort_box_changed(self, txt): au = unicode(self.authors.text()) au = re.sub(r'\s+et al\.$', '', au) au = self.db.author_sort_from_authors(string_to_authors(au)) - self.mark_author_sort(normal=(au == txt)) + self.mark_box_as_ok(control = self.author_sort, tt=self.aus_tooltips, + normal=(au == txt)) - def mark_author_sort(self, normal=True): + def mark_box_as_ok(self, control, tt, normal=True): if normal: col = 'rgb(0, 255, 0, 20%)' else: col = 'rgb(255, 0, 0, 20%)' - self.author_sort.setStyleSheet('QLineEdit { color: black; ' - 'background-color: %s; }'%col) - tt = self.ok_aus_tooltip if normal else self.bad_aus_tooltip - self.author_sort.setToolTip(tt) + control.setStyleSheet('QLineEdit { color: black; ' + 'background-color: %s; }'%col) + tt = tt[0] if normal else tt[1] + control.setToolTip(tt) def validate_isbn(self, isbn): isbn = unicode(isbn).strip() @@ -652,6 +683,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): authors = string_to_authors(au) self.author_sort.setText(self.db.author_sort_from_authors(authors)) + def deduce_title_sort(self): + ts = unicode(self.title.text()) + self.title_sort.setText(title_sort(ts)) + def swap_title_author(self): title = self.title.text() self.title.setText(self.authors.text()) @@ -838,6 +873,10 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): title = unicode(self.title.text()).strip() if title != self.original_title: self.db.set_title(self.id, title, notify=False) + # This must be after setting the title because of the DB update trigger + ts = unicode(self.title_sort.text()).strip() + if ts: + self.db.set_title_sort(self.id, ts, notify=False, commit=False) au = unicode(self.authors.text()).strip() if au and au != self.original_author: self.db.set_authors(self.id, string_to_authors(au), notify=False) diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 2bd85e30bb..02e6eb01e2 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -100,7 +100,7 @@ - + Swap the author and title @@ -121,6 +121,41 @@ + + + Title sort: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + title_sort + + + + + + + Specify how this book should be sorted when by title. For example, The Exorcist might be sorted as Exorcist, The. + + + + + + + Automatically create the title sort entry based on the current title entry. +Using this button to create title sort will change title sort from red to green. + + + ... + + + + :/images/auto_author_sort.png:/images/auto_author_sort.png + + + + &Author(s): @@ -133,7 +168,7 @@ - + Author S&ort: @@ -146,7 +181,7 @@ - + @@ -173,7 +208,7 @@ Using this button to create author sort will change author sort from red to gree - + &Rating: @@ -186,7 +221,7 @@ Using this button to create author sort will change author sort from red to gree - + Rating of this book. 0-5 stars @@ -205,7 +240,7 @@ Using this button to create author sort will change author sort from red to gree - + &Publisher: @@ -218,7 +253,7 @@ Using this button to create author sort will change author sort from red to gree - + Ta&gs: @@ -231,7 +266,7 @@ Using this button to create author sort will change author sort from red to gree - + @@ -256,7 +291,7 @@ Using this button to create author sort will change author sort from red to gree - + &Series: @@ -272,7 +307,7 @@ Using this button to create author sort will change author sort from red to gree - + 5 @@ -309,7 +344,7 @@ Using this button to create author sort will change author sort from red to gree - + IS&BN: @@ -322,10 +357,10 @@ Using this button to create author sort will change author sort from red to gree - + - + Publishe&d: @@ -338,14 +373,14 @@ Using this button to create author sort will change author sort from red to gree - + true - + false @@ -358,7 +393,7 @@ Using this button to create author sort will change author sort from red to gree - + MMM yyyy @@ -368,14 +403,14 @@ Using this button to create author sort will change author sort from red to gree - + true - + dd MMM yyyy @@ -385,7 +420,7 @@ Using this button to create author sort will change author sort from red to gree - + &Date: @@ -744,8 +779,11 @@ Using this button to create author sort will change author sort from red to gree title + title_sort + auto_title_sort swap_button authors + swap_button author_sort auto_author_sort rating diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 23375995ae..b09c2bfc7c 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1565,6 +1565,20 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if notify: self.notify('metadata', [id]) + def set_title_sort(self, id, title_sort, notify=True, commit=True): + if not title_sort: + return False + if isbytestring(title_sort): + title_sort = title_sort.decode(preferred_encoding, 'replace') + self.conn.execute('UPDATE books SET sort=? WHERE id=?', (title_sort, id)) + self.data.set(id, self.FIELD_MAP['sort'], title_sort, row_is_id=True) + self.dirtied([id], commit=False) + if commit: + self.conn.commit() + if notify: + self.notify('metadata', [id]) + return True + def _set_title(self, id, title): if not title: return False diff --git a/src/calibre/library/schema_upgrades.py b/src/calibre/library/schema_upgrades.py index 5e581e3f86..4bf7e0ac6a 100644 --- a/src/calibre/library/schema_upgrades.py +++ b/src/calibre/library/schema_upgrades.py @@ -429,3 +429,13 @@ class SchemaUpgrade(object): 'Remove commas from tags' self.conn.execute("UPDATE tags SET name=REPLACE(name, ',', ';')") + def upgrade_version_16(self): + self.conn.executescript(''' + DROP TRIGGER IF EXISTS books_update_trg; + CREATE TRIGGER books_update_trg + AFTER UPDATE ON books + BEGIN + UPDATE books SET sort=title_sort(NEW.title) + WHERE id=NEW.id AND OLD.title <> NEW.title; + END; + ''') From 95fcf6d3affb9d8057bfb248cf72b521232e13bb Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 17 Dec 2010 10:10:43 +0000 Subject: [PATCH 2/4] Enhancement #7920 (Tab for custom MetaData) -- added a tweak to switch to single-column mode --- resources/default_tweaks.py | 6 ++++++ src/calibre/gui2/dialogs/metadata_single.py | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index 081112444a..cd746ccdaa 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -235,3 +235,9 @@ doubleclick_on_library_view = 'open_viewer' # Example: locale_for_sorting = 'fr' -- sort using French rules. # Example: locale_for_sorting = 'nb' -- sort using Norwegian rules. 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 \ No newline at end of file diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 7c6da7c1f9..99fe2017dd 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -616,8 +616,8 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): w = self.central_widget.widget(1) layout = w.layout() self.custom_column_widgets, self.__cc_spacers = \ - populate_metadata_page(layout, self.db, self.id, - parent=w, bulk=False, two_column=True) + populate_metadata_page(layout, self.db, self.id, parent=w, bulk=False, + two_column=tweaks['metadata_single_use_2_cols_for_custom_fields']) self.__custom_col_layouts = [layout] ans = self.custom_column_widgets for i in range(len(ans)-1): From b12e505f5c24b27dc24ccdd87c383ed781be86e8 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 17 Dec 2010 11:50:50 +0000 Subject: [PATCH 3/4] Enhancement #7892: New tweak options for series number increment --- resources/default_tweaks.py | 15 ++++++- src/calibre/gui2/custom_column_widgets.py | 8 ++-- src/calibre/gui2/dialogs/metadata_single.py | 2 +- src/calibre/gui2/library/models.py | 2 +- src/calibre/library/cli.py | 2 +- src/calibre/library/custom_columns.py | 16 +++---- src/calibre/library/database2.py | 48 ++++++++++++++++----- 7 files changed, 65 insertions(+), 28 deletions(-) diff --git a/resources/default_tweaks.py b/resources/default_tweaks.py index cd746ccdaa..750af9efa7 100644 --- a/resources/default_tweaks.py +++ b/resources/default_tweaks.py @@ -12,13 +12,24 @@ defaults. # The algorithm used to assign a new book in an existing series a series number. +# New series numbers assigned using this tweak are always integer values, except +# if a constant non-integer is specified. # Possible values are: -# next - Next available number +# next - First available integer larger than the largest existing number +# first_free - First available integer larger than 0 +# next_free - First available integer larger than the smallest existing number +# last_free - First available integer smaller than the largest existing number +# Return largest existing + 1 if no free number is found # const - Assign the number 1 always +# a number - Assign that number always. The number is not in quotes. Note that +# 0.0 can be used here. +# Examples: +# series_index_auto_increment = 'next' +# series_index_auto_increment = 'next_free' +# series_index_auto_increment = 16.5 series_index_auto_increment = 'next' - # The algorithm used to copy author to author_sort # Possible values are: # invert: use "fn ln" -> "ln, fn" (the original algorithm) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 5ab8bb6940..6e4fc0a0ac 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -303,7 +303,7 @@ class Series(Base): if val == '': val = s_index = None elif s_index == 0.0: - if tweaks['series_index_auto_increment'] == 'next': + if tweaks['series_index_auto_increment'] != 'const': s_index = self.db.get_next_cc_series_num_for(val, num=self.col_id) else: @@ -572,7 +572,6 @@ class BulkSeries(BulkBase): val = None if clear else self.normalize_ui_val(val) if clear or val != '': extras = [] - next_index = self.db.get_next_cc_series_num_for(val, num=self.col_id) for book_id in book_ids: if clear: extras.append(None) @@ -581,9 +580,8 @@ class BulkSeries(BulkBase): if force_start: s_index = at_value at_value += 1 - elif tweaks['series_index_auto_increment'] == 'next': - s_index = next_index - next_index += 1 + elif tweaks['series_index_auto_increment'] != 'const': + s_index = self.db.get_next_cc_series_num_for(val, num=self.col_id) else: s_index = 1.0 else: diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 99fe2017dd..133f449550 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -839,7 +839,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): series = unicode(self.series.text()).strip() if series and series != self.original_series_name: ns = 1 - if tweaks['series_index_auto_increment'] == 'next': + if tweaks['series_index_auto_increment'] != 'const': ns = self.db.get_next_series_num_for(series) self.series_index.setValue(ns) self.original_series_name = series diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index e0d5c64961..661f21e53d 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -772,7 +772,7 @@ class BooksModel(QAbstractTableModel): # {{{ self.db.set_series_index(id, float(match.group(1))) val = pat.sub('', val).strip() elif val: - if tweaks['series_index_auto_increment'] == 'next': + if tweaks['series_index_auto_increment'] != 'const': ni = self.db.get_next_series_num_for(val) if ni != 1: self.db.set_series_index(id, ni) diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index 01e8ad449b..c98b3be4d3 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -707,7 +707,7 @@ def parse_series_string(db, label, value): val = pat.sub('', val).strip() s_index = float(match.group(1)) elif val: - if tweaks['series_index_auto_increment'] == 'next': + if tweaks['series_index_auto_increment'] != 'const': s_index = db.get_next_cc_series_num_for(val, label=label) else: s_index = 1.0 diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py index f544a9bf7e..e95ace2cd4 100644 --- a/src/calibre/library/custom_columns.py +++ b/src/calibre/library/custom_columns.py @@ -8,12 +8,12 @@ __docformat__ = 'restructuredtext en' import json, re from functools import partial -from math import floor from calibre import prints from calibre.constants import preferred_encoding from calibre.library.field_metadata import FieldMetadata from calibre.utils.date import parse_date +from calibre.utils.config import tweaks class CustomColumns(object): @@ -261,15 +261,15 @@ class CustomColumns(object): series_id = self.conn.get('SELECT id from %s WHERE value=?'%table, (series,), all=False) if series_id is None: + if isinstance(tweaks['series_index_auto_increment'], (int, float)): + return float(tweaks['series_index_auto_increment']) return 1.0 - # get the label of the associated series number table - series_num = self.conn.get(''' - SELECT MAX({lt}.extra) FROM {lt} + series_indices = self.conn.get(''' + SELECT {lt}.extra FROM {lt} WHERE {lt}.book IN (SELECT book FROM {lt} where value=?) - '''.format(lt=lt), (series_id,), all=False) - if series_num is None: - return 1.0 - return floor(series_num+1) + ORDER BY {lt}.extra + '''.format(lt=lt), (series_id,)) + return self._get_next_series_num_for_list(series_indices) def all_custom(self, label=None, num=None): if label is not None: diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index b09c2bfc7c..b9ab54b80a 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -8,7 +8,7 @@ The database used to store ebook metadata ''' import os, sys, shutil, cStringIO, glob, time, functools, traceback, re from itertools import repeat -from math import floor +from math import ceil from Queue import Queue from PyQt4.QtGui import QImage @@ -1365,14 +1365,43 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): series_id = self.conn.get('SELECT id from series WHERE name=?', (series,), all=False) if series_id is None: + if isinstance(tweaks['series_index_auto_increment'], (int, float)): + return float(tweaks['series_index_auto_increment']) return 1.0 - series_num = self.conn.get( - ('SELECT MAX(series_index) FROM books WHERE id IN ' - '(SELECT book FROM books_series_link where series=?)'), - (series_id,), all=False) - if series_num is None: + series_indices = self.conn.get( + ('SELECT series_index FROM books WHERE id IN ' + '(SELECT book FROM books_series_link where series=?) ' + 'ORDER BY series_index'), + (series_id,)) + return self._get_next_series_num_for_list(series_indices) + + def _get_next_series_num_for_list(self, series_indices): + if not series_indices: + if isinstance(tweaks['series_index_auto_increment'], (int, float)): + return float(tweaks['series_index_auto_increment']) return 1.0 - return floor(series_num+1) + series_indices = [x[0] for x in series_indices] + print series_indices + if tweaks['series_index_auto_increment'] == 'next': + return series_indices[-1] + 1 + if tweaks['series_index_auto_increment'] == 'first_free': + for i in range(1, 10000): + if i not in series_indices: + return i + # really shouldn't get here. + if tweaks['series_index_auto_increment'] == 'next_free': + for i in range(int(ceil(series_indices[0])), 10000): + if i not in series_indices: + return i + # really shouldn't get here. + if tweaks['series_index_auto_increment'] == 'last_free': + for i in range(int(ceil(series_indices[-1])), 0, -1): + if i not in series_indices: + return i + return series_indices[-1] + 1 + if isinstance(tweaks['series_index_auto_increment'], (int, float)): + return float(tweaks['series_index_auto_increment']) + return 1.0 def set(self, row, column, val): ''' @@ -1760,18 +1789,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): FROM books, books_series_link as lt WHERE books.id = lt.book AND lt.series=? ORDER BY books.series_index''', (old_id,)) - # Get the next series index - index = self.get_next_series_num_for(new_name) # Now update the link table self.conn.execute('''UPDATE books_series_link SET series=? WHERE series=?''',(new_id, old_id,)) # Now set the indices for (book_id,) in books: + # Get the next series index + index = self.get_next_series_num_for(new_name) self.conn.execute('''UPDATE books SET series_index=? WHERE id=?''',(index, book_id,)) - index = index + 1 self.dirty_books_referencing('series', new_id, commit=False) self.conn.commit() From a93200983246cf28ee8931f853184e6ea13ced19 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Fri, 17 Dec 2010 13:26:37 +0000 Subject: [PATCH 4/4] #7643: option to copy one db's 'structure' to the new DB when creating a new library. --- src/calibre/gui2/dialogs/choose_library.py | 7 +++++- src/calibre/gui2/dialogs/choose_library.ui | 25 +++++++++++++++++----- src/calibre/gui2/ui.py | 7 ++++-- src/calibre/library/database2.py | 21 +++++++++++++++++- 4 files changed, 51 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/dialogs/choose_library.py b/src/calibre/gui2/dialogs/choose_library.py index 32d45c6043..033b038b65 100644 --- a/src/calibre/gui2/dialogs/choose_library.py +++ b/src/calibre/gui2/dialogs/choose_library.py @@ -32,6 +32,11 @@ class ChooseLibrary(QDialog, Ui_Dialog): loc = unicode(self.old_location.text()).format(lp) self.old_location.setText(loc) self.browse_button.clicked.connect(self.choose_loc) + self.empty_library.toggled.connect(self.empty_library_toggled) + self.copy_structure.setEnabled(False) + + def empty_library_toggled(self, to_what): + self.copy_structure.setEnabled(to_what) def choose_loc(self, *args): loc = choose_dir(self, 'choose library location', @@ -64,7 +69,7 @@ class ChooseLibrary(QDialog, Ui_Dialog): def perform_action(self, ac, loc): if ac in ('new', 'existing'): prefs['library_path'] = loc - self.callback(loc) + self.callback(loc, copy_structure=self.copy_structure.isChecked()) else: move_library(self.db.library_path, loc, self.parent(), self.callback) diff --git a/src/calibre/gui2/dialogs/choose_library.ui b/src/calibre/gui2/dialogs/choose_library.ui index 6cf1b4386c..c281ecc0f4 100644 --- a/src/calibre/gui2/dialogs/choose_library.ui +++ b/src/calibre/gui2/dialogs/choose_library.ui @@ -49,11 +49,26 @@ - - - &Create an empty library at the new location - - + + + + + &Create an empty library at the new location + + + + + + + &Copy structure from the current library + + + Copy the custom columns, saved searches, column widths, plugboards, +user categories, and other information from the old to the new library + + + + diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index dd3925e732..f97589e466 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -378,13 +378,16 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ def booklists(self): return self.memory_view.model().db, self.card_a_view.model().db, self.card_b_view.model().db - def library_moved(self, newloc): + def library_moved(self, newloc, copy_structure=False): if newloc is None: return + default_prefs = None try: olddb = self.library_view.model().db + if copy_structure: + default_prefs = olddb.prefs except: olddb = None - db = LibraryDatabase2(newloc) + db = LibraryDatabase2(newloc, default_prefs=default_prefs) if self.content_server is not None: self.content_server.set_database(db) self.library_path = newloc diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index b9ab54b80a..27266f2f9a 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -113,7 +113,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): def exists_at(cls, path): return path and os.path.exists(os.path.join(path, 'metadata.db')) - def __init__(self, library_path, row_factory=False): + def __init__(self, library_path, row_factory=False, default_prefs=None): self.field_metadata = FieldMetadata() self.dirtied_queue = Queue() if not os.path.exists(library_path): @@ -127,10 +127,29 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if isinstance(self.dbpath, unicode) and not iswindows: self.dbpath = self.dbpath.encode(filesystem_encoding) + apply_default_prefs = not os.path.exists(self.dbpath) self.connect() + self.is_case_sensitive = not iswindows and not isosx and \ not os.path.exists(self.dbpath.replace('metadata.db', 'MeTAdAtA.dB')) SchemaUpgrade.__init__(self) + + # if we are to copy the prefs and structure from some other DB, then + # we need to do it before we call initialize_dynamic + if apply_default_prefs and default_prefs is not None: + prefs = DBPrefs(self) + for key in default_prefs: + # be sure that prefs not to be copied are listed below + if key in ['news_to_be_synced']: + continue + try: + prefs[key] = default_prefs[key] + except: + pass # ignore options that don't exist anymore + fmvals = [f for f in default_prefs['field_metadata'].values() if f['is_custom']] + for f in fmvals: + self.create_custom_column(f['label'], f['name'], f['datatype'], + f['is_multiple'] is not None, f['is_editable'], f['display']) self.initialize_dynamic() def get_property(self, idx, index_is_id=False, loc=-1):