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):