mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix #7643 ("Copy Structure" command). Add more tweaks to control how the enxt available series number is calculated (#7892). Add a tweak to control layout of custom metadata edit panel (#7920). Allow editing of title sort via the Edit metadata dialog.
This commit is contained in:
commit
f414aaf447
@ -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)
|
||||
@ -235,3 +246,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
|
@ -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:
|
||||
|
@ -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)
|
||||
|
@ -49,11 +49,26 @@
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="empty_library">
|
||||
<property name="text">
|
||||
<string>&Create an empty library at the new location</string>
|
||||
</property>
|
||||
</widget>
|
||||
<layout class="QHBoxLayout" name="hbox1">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="empty_library">
|
||||
<property name="text">
|
||||
<string>&Create an empty library at the new location</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="copy_structure">
|
||||
<property name="text">
|
||||
<string>&Copy structure from the current library</string>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Copy the custom columns, saved searches, column widths, plugboards,
|
||||
user categories, and other information from the old to the new library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0" colspan="3">
|
||||
<widget class="QRadioButton" name="move_library">
|
||||
|
@ -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 = '<p>' + textwrap.fill(base+'<br><br>'+
|
||||
ok_tooltip = '<p>' + textwrap.fill(base+'<br><br>'+
|
||||
_(' The green color indicates that the current '
|
||||
'author sort matches the current author'))
|
||||
self.bad_aus_tooltip = '<p>'+textwrap.fill(base + '<br><br>'+
|
||||
bad_tooltip = '<p>'+textwrap.fill(base + '<br><br>'+
|
||||
_(' 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 = '<p>' + textwrap.fill(base+'<br><br>'+
|
||||
_(' The green color indicates that the current '
|
||||
'title sort matches the current title'))
|
||||
bad_tooltip = '<p>'+textwrap.fill(base + '<br><br>'+
|
||||
_(' 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 = ''
|
||||
@ -598,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):
|
||||
@ -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,12 +683,16 @@ 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())
|
||||
self.authors.setText(title)
|
||||
self.author_sort.setText('')
|
||||
|
||||
self.deduce_author_sort()
|
||||
self.deduce_title_sort()
|
||||
|
||||
def initialize_combos(self):
|
||||
self.initalize_authors()
|
||||
@ -804,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
|
||||
@ -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)
|
||||
|
@ -100,27 +100,27 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="2">
|
||||
<widget class="QToolButton" name="swap_button">
|
||||
<property name="toolTip">
|
||||
<string>Swap the author and title</string>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
<string>Title &sort: </string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/swap.png</normaloff>:/images/swap.png</iconset>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
<property name="buddy">
|
||||
<cstring>title_sort</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="1" column="1">
|
||||
<widget class="EnLineEdit" name="title_sort">
|
||||
<property name="toolTip">
|
||||
<string>Specify how this book should be sorted when by title. For example, The Exorcist might be sorted as Exorcist, The.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="text">
|
||||
<string>&Author(s): </string>
|
||||
@ -133,7 +133,14 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="2" column="1">
|
||||
<widget class="EnComboBox" name="authors">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Author S&ort: </string>
|
||||
@ -146,34 +153,15 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="EnLineEdit" name="author_sort">
|
||||
<property name="toolTip">
|
||||
<string>Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles.
|
||||
<item row="3" column="1">
|
||||
<widget class="EnLineEdit" name="author_sort">
|
||||
<property name="toolTip">
|
||||
<string>Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles.
|
||||
If the box is colored green, then text matches the individual author's sort strings. If it is colored red, then the authors and this text do not match.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="auto_author_sort">
|
||||
<property name="toolTip">
|
||||
<string>Automatically create the author sort entry based on the current author entry.
|
||||
Using this button to create author sort will change author sort from red to green.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/auto_author_sort.png</normaloff>:/images/auto_author_sort.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="0">
|
||||
<item row="4" column="0">
|
||||
<widget class="QLabel" name="label_6">
|
||||
<property name="text">
|
||||
<string>&Rating:</string>
|
||||
@ -186,7 +174,7 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="2">
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="QSpinBox" name="rating">
|
||||
<property name="toolTip">
|
||||
<string>Rating of this book. 0-5 stars</string>
|
||||
@ -205,7 +193,7 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="0">
|
||||
<item row="5" column="0">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>&Publisher: </string>
|
||||
@ -218,7 +206,14 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="0">
|
||||
<item row="5" column="1" colspan="2">
|
||||
<widget class="EnComboBox" name="publisher">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<widget class="QLabel" name="label_4">
|
||||
<property name="text">
|
||||
<string>Ta&gs: </string>
|
||||
@ -231,32 +226,7 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="2">
|
||||
<layout class="QHBoxLayout" name="_2">
|
||||
<item>
|
||||
<widget class="TagsLineEdit" name="tags">
|
||||
<property name="toolTip">
|
||||
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="tag_editor_button">
|
||||
<property name="toolTip">
|
||||
<string>Open Tag Editor</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open Tag Editor</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/chapters.png</normaloff>:/images/chapters.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="0">
|
||||
<item row="7" column="0">
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>&Series:</string>
|
||||
@ -272,7 +242,7 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1" colspan="2">
|
||||
<item row="7" column="1">
|
||||
<layout class="QHBoxLayout" name="_3">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
@ -293,59 +263,9 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="remove_series_button">
|
||||
<property name="toolTip">
|
||||
<string>Remove unused series (Series that have no books)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/trash.png</normaloff>:/images/trash.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="8" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>IS&BN:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>isbn</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" colspan="2">
|
||||
<widget class="QLineEdit" name="isbn"/>
|
||||
</item>
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Publishe&d:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>pubdate</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1" colspan="2">
|
||||
<widget class="EnComboBox" name="publisher">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="1" colspan="2">
|
||||
<widget class="QDoubleSpinBox" name="series_index">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
@ -358,34 +278,23 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1" colspan="2">
|
||||
<widget class="QDateEdit" name="pubdate">
|
||||
<property name="displayFormat">
|
||||
<string>MMM yyyy</string>
|
||||
<item row="9" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>IS&BN:</string>
|
||||
</property>
|
||||
<property name="calendarPopup">
|
||||
<bool>true</bool>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="EnComboBox" name="authors">
|
||||
<property name="editable">
|
||||
<bool>true</bool>
|
||||
<property name="buddy">
|
||||
<cstring>isbn</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1" colspan="2">
|
||||
<widget class="QDateEdit" name="date">
|
||||
<property name="displayFormat">
|
||||
<string>dd MMM yyyy</string>
|
||||
</property>
|
||||
<property name="calendarPopup">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
<widget class="QLineEdit" name="isbn"/>
|
||||
</item>
|
||||
<item row="9" column="0">
|
||||
<item row="10" column="0">
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>&Date:</string>
|
||||
@ -398,9 +307,194 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1" colspan="2">
|
||||
<widget class="QDateEdit" name="date">
|
||||
<property name="displayFormat">
|
||||
<string>dd MMM yyyy</string>
|
||||
</property>
|
||||
<property name="calendarPopup">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Publishe&d:</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
|
||||
</property>
|
||||
<property name="buddy">
|
||||
<cstring>pubdate</cstring>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="11" column="1" colspan="2">
|
||||
<widget class="QDateEdit" name="pubdate">
|
||||
<property name="displayFormat">
|
||||
<string>MMM yyyy</string>
|
||||
</property>
|
||||
<property name="calendarPopup">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="2" rowspan="4">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_7">
|
||||
<item>
|
||||
<spacer name="verticalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="auto_title_sort">
|
||||
<property name="toolTip">
|
||||
<string>Automatically create the author sort entry based on the current author entry.
|
||||
Using this button to create author sort will change author sort from red to green.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/auto_author_sort.png</normaloff>:/images/auto_author_sort.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="swap_button">
|
||||
<property name="toolTip">
|
||||
<string>Swap the author and title</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/swap.png</normaloff>:/images/swap.png</iconset>
|
||||
</property>
|
||||
<property name="iconSize">
|
||||
<size>
|
||||
<width>16</width>
|
||||
<height>16</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="auto_author_sort">
|
||||
<property name="toolTip">
|
||||
<string>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.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/auto_author_sort.png</normaloff>:/images/auto_author_sort.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<layout class="QHBoxLayout" name="_2">
|
||||
<item>
|
||||
<widget class="TagsLineEdit" name="tags">
|
||||
<property name="toolTip">
|
||||
<string>Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="6" column="2">
|
||||
<widget class="QToolButton" name="tag_editor_button">
|
||||
<property name="toolTip">
|
||||
<string>Open Tag Editor</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Open Tag Editor</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/chapters.png</normaloff>:/images/chapters.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="7" column="2">
|
||||
<widget class="QToolButton" name="remove_series_button">
|
||||
<property name="toolTip">
|
||||
<string>Remove unused series (Series that have no books)</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../../resources/images.qrc">
|
||||
<normaloff>:/images/trash.png</normaloff>:/images/trash.png</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="fetch_metadata_button">
|
||||
<property name="text">
|
||||
<string>&Fetch metadata from server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
@ -420,13 +514,6 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="fetch_metadata_button">
|
||||
<property name="text">
|
||||
<string>&Fetch metadata from server</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="layoutWidget_2">
|
||||
@ -744,10 +831,12 @@ Using this button to create author sort will change author sort from red to gree
|
||||
</customwidgets>
|
||||
<tabstops>
|
||||
<tabstop>title</tabstop>
|
||||
<tabstop>auto_title_sort</tabstop>
|
||||
<tabstop>title_sort</tabstop>
|
||||
<tabstop>swap_button</tabstop>
|
||||
<tabstop>authors</tabstop>
|
||||
<tabstop>author_sort</tabstop>
|
||||
<tabstop>auto_author_sort</tabstop>
|
||||
<tabstop>author_sort</tabstop>
|
||||
<tabstop>rating</tabstop>
|
||||
<tabstop>publisher</tabstop>
|
||||
<tabstop>tags</tabstop>
|
||||
@ -758,20 +847,22 @@ Using this button to create author sort will change author sort from red to gree
|
||||
<tabstop>isbn</tabstop>
|
||||
<tabstop>date</tabstop>
|
||||
<tabstop>pubdate</tabstop>
|
||||
<tabstop>comments</tabstop>
|
||||
<tabstop>fetch_metadata_button</tabstop>
|
||||
<tabstop>add_format_button</tabstop>
|
||||
<tabstop>remove_format_button</tabstop>
|
||||
<tabstop>comments</tabstop>
|
||||
<tabstop>button_set_cover</tabstop>
|
||||
<tabstop>button_set_metadata</tabstop>
|
||||
<tabstop>formats</tabstop>
|
||||
<tabstop>add_format_button</tabstop>
|
||||
<tabstop>remove_format_button</tabstop>
|
||||
<tabstop>cover_path</tabstop>
|
||||
<tabstop>cover_button</tabstop>
|
||||
<tabstop>trim_cover_button</tabstop>
|
||||
<tabstop>reset_cover</tabstop>
|
||||
<tabstop>fetch_cover_button</tabstop>
|
||||
<tabstop>generate_cover_button</tabstop>
|
||||
<tabstop>button_box</tabstop>
|
||||
<tabstop>scrollArea</tabstop>
|
||||
<tabstop>central_widget</tabstop>
|
||||
<tabstop>button_box</tabstop>
|
||||
</tabstops>
|
||||
<resources>
|
||||
<include location="../../../../resources/images.qrc"/>
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
@ -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:
|
||||
dbprefs = 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:
|
||||
dbprefs[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):
|
||||
@ -1365,14 +1384,42 @@ 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]
|
||||
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):
|
||||
'''
|
||||
@ -1565,6 +1612,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
|
||||
@ -1746,18 +1807,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()
|
||||
|
||||
|
@ -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;
|
||||
''')
|
||||
|
Loading…
x
Reference in New Issue
Block a user