From a21bf30ff8207ec9a4e644d1ffbfcbee58a346c6 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 08:41:16 +0000 Subject: [PATCH 1/9] 1) finish JSON-storage of template function source code 2) fix for #8388. This fix was chosen because it emulates the behavior in 0.7.38, where get_metadata returned empty author lists --- .../gui2/preferences/template_functions.py | 15 +++++++++++++-- src/calibre/library/database2.py | 5 ++++- src/calibre/library/sqlite.py | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/preferences/template_functions.py b/src/calibre/gui2/preferences/template_functions.py index 2e16b0f4c3..8ffd65b2b5 100644 --- a/src/calibre/gui2/preferences/template_functions.py +++ b/src/calibre/gui2/preferences/template_functions.py @@ -5,7 +5,7 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import traceback +import json, traceback from calibre.gui2 import error_dialog from calibre.gui2.preferences import ConfigWidgetBase, test_widget @@ -73,6 +73,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self.textBrowser.setHtml(help_text) def initialize(self): + try: + with open(P('template-functions.json'), 'rb') as f: + self.builtin_source_dict = json.load(f, encoding='utf-8') + except: + self.builtin_source_dict = {} + self.funcs = formatter_functions.get_functions() self.builtins = formatter_functions.get_builtins() @@ -179,8 +185,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): func = self.funcs[txt] self.argument_count.setValue(func.arg_count) self.documentation.setText(func.doc) - self.program.setPlainText(func.program_text) if txt in self.builtins: + if hasattr(func, 'program_text'): + self.program.setPlainText(func.program_text) + elif txt in self.builtin_source_dict: + self.program.setPlainText(self.builtin_source_dict[txt]) + else: + self.program.setPlainText(_('function source code not available')) self.documentation.setReadOnly(True) self.argument_count.setReadOnly(True) self.program.setReadOnly(True) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 3a2109e01e..df094347b8 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -690,7 +690,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): mi = Metadata(None) aut_list = row[fm['au_map']] - aut_list = [p.split(':::') for p in aut_list.split(':#:')] + if not aut_list: + aut_list = [] + else: + aut_list = [p.split(':::') for p in aut_list.split(':#:')] aum = [] aus = {} for (author, author_sort) in aut_list: diff --git a/src/calibre/library/sqlite.py b/src/calibre/library/sqlite.py index 83f19b8711..622d6b8459 100644 --- a/src/calibre/library/sqlite.py +++ b/src/calibre/library/sqlite.py @@ -100,7 +100,7 @@ class AumSortedConcatenate(object): keys = self.ans.keys() l = len(keys) if l == 0: - return 'Unknown:::Unknown' + return None if l == 1: return self.ans[keys[0]] return ':#:'.join([self.ans[v] for v in sorted(keys)]) From e0b2d0b62a6b1f4c4a2f37cdcdffa5e1744b892b Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 10:34:05 +0000 Subject: [PATCH 2/9] Add function documentation to template editing dialog (F2 on a composite column) --- src/calibre/gui2/dialogs/template_dialog.py | 40 +++++++++- src/calibre/gui2/dialogs/template_dialog.ui | 81 ++++++++++++++++----- 2 files changed, 103 insertions(+), 18 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index 60d4025ef9..62accdc842 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -3,8 +3,11 @@ __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __license__ = 'GPL v3' +import json + from PyQt4.Qt import Qt, QDialog, QDialogButtonBox from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog +from calibre.utils.formatter_functions import formatter_functions class TemplateDialog(QDialog, Ui_TemplateDialog): @@ -17,9 +20,44 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint)) self.setWindowIcon(icon) + self.textbox.setTabStopWidth(10) + self.source_code.setTabStopWidth(10) + self.documentation.setReadOnly(True) + self.source_code.setReadOnly(True) + if text is not None: self.textbox.setPlainText(text) - self.textbox.setTabStopWidth(50) self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK')) self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel')) + try: + with open(P('template-functions.json'), 'rb') as f: + self.builtin_source_dict = json.load(f, encoding='utf-8') + except: + self.builtin_source_dict = {} + + self.funcs = formatter_functions.get_functions() + self.builtins = formatter_functions.get_builtins() + + func_names = sorted(self.funcs) + self.function.clear() + self.function.addItem('') + self.function.addItems(func_names) + self.function.setCurrentIndex(0) + self.function.currentIndexChanged[str].connect(self.function_changed) + + print self.textbox.tabStopWidth() + print self.source_code.tabStopWidth() + + def function_changed(self, toWhat): + name = unicode(toWhat) + self.source_code.clear() + self.documentation.clear() + if name in self.funcs: + self.documentation.setPlainText(self.funcs[name].doc) + if name in self.builtins: + if name in self.builtin_source_dict: + self.source_code.setPlainText(self.builtin_source_dict[name]) + else: + self.source_code.setPlainText(self.funcs[name].program_text) + diff --git a/src/calibre/gui2/dialogs/template_dialog.ui b/src/calibre/gui2/dialogs/template_dialog.ui index a30d6ef273..e1980a8397 100644 --- a/src/calibre/gui2/dialogs/template_dialog.ui +++ b/src/calibre/gui2/dialogs/template_dialog.ui @@ -6,8 +6,8 @@ 0 0 - 500 - 235 + 588 + 546 @@ -19,21 +19,68 @@ Edit Comments - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + Function name: + + + + + + + + + + Documentation: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Python code: + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + 16777215 + 75 + + + + + + + + + + From 4a9e8bcb2fd0d60438e3265defb358891de6bc75 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 10:48:43 +0000 Subject: [PATCH 3/9] Remove some print statements. --- src/calibre/gui2/dialogs/template_dialog.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py index 62accdc842..174056ef80 100644 --- a/src/calibre/gui2/dialogs/template_dialog.py +++ b/src/calibre/gui2/dialogs/template_dialog.py @@ -46,9 +46,6 @@ class TemplateDialog(QDialog, Ui_TemplateDialog): self.function.setCurrentIndex(0) self.function.currentIndexChanged[str].connect(self.function_changed) - print self.textbox.tabStopWidth() - print self.source_code.tabStopWidth() - def function_changed(self, toWhat): name = unicode(toWhat) self.source_code.clear() From 5c4154bb0d37a273e4925c2daa4dc15b4e9f752b Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 16 Jan 2011 11:26:34 +0000 Subject: [PATCH 4/9] Make date.format_date render UNDEFINED_DATE as ''. This makes composite columns and templates behave in the same fashion as the GUI has for some time. --- src/calibre/utils/date.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index f025a0c9bf..2551b90788 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -148,6 +148,9 @@ def format_date(dt, format, assume_utc=False, as_utc=False): if len(mo.group(0)) == 2: return '%02d'%(dt.year % 100) return '%04d'%dt.year + if dt == UNDEFINED_DATE: + return '' + format = re.sub('d{1,4}', format_day, format) format = re.sub('M{1,4}', format_month, format) return re.sub('yyyy|yy', format_year, format) From e131f99db8e5a80a68e4e28a152848effb7ece76 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 09:18:37 -0700 Subject: [PATCH 5/9] Cleanup layout of bulk metadata edit dialog --- src/calibre/gui2/dialogs/metadata_bulk.ui | 236 +++++++++++----------- 1 file changed, 115 insertions(+), 121 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index 9240cd1af8..d52bc2cb89 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -75,13 +75,31 @@ - - - - A&utomatically set author sort + + + + true + + + + + + A&utomatically set author sort + + + + + + + &Swap title and author + + + + + @@ -95,7 +113,7 @@ - + Specify how the author(s) of this book should be sorted. For example Charles Dickens should be sorted as Dickens, Charles. @@ -115,7 +133,7 @@ - + Rating of this book. 0-5 stars @@ -156,7 +174,7 @@ - + true @@ -220,7 +238,7 @@ Check this box to remove all tags from the books. - Remove all + Remove &all @@ -241,52 +259,35 @@ - - - - - List of known series. You can add new series. - - - List of known series. You can add new series. - - - true - - - QComboBox::InsertAlphabetically - - - QComboBox::AdjustToContents - - - - - - - If checked, the series will be cleared - - - Clear series - - - - - - - Qt::Horizontal - - - - 20 - 0 - - - - - + + + List of known series. You can add new series. + + + List of known series. You can add new series. + + + true + + + QComboBox::InsertAlphabetically + + + QComboBox::AdjustToContents + + - + + + + If checked, the series will be cleared + + + &Clear series + + + + @@ -297,7 +298,7 @@ you selected them. So if you selected Book A and then Book B, Book A will have series number 1 and Book B series number 2. - Automatically number books in this series + &Automatically number books in this series @@ -312,7 +313,7 @@ for that series. Checking this box will tell calibre to start numbering from the value in the box - Force numbers to start with + &Force numbers to start with: @@ -332,19 +333,6 @@ from the value in the box - - - - Qt::Horizontal - - - - 20 - 10 - - - - @@ -358,59 +346,56 @@ from the value in the box - - - - - - true - - - - - - - &Swap title and author - - - - - - - Force the title to be in title case. If both this and swap authors are checked, -title and author are swapped before the title case is set - - - Change title to title case - - - - - - - Remove stored conversion settings for the selected books. - -Future conversion of these books will use the default settings. - - - Remove &stored conversion settings for the selected books - - - - - - - Qt::Vertical - - + + - 20 - 40 + 120 + 16777215 - + - + + + + + + Force the title to be in title case. If both this and swap authors are checked, +title and author are swapped before the title case is set + + + Change title to title &case + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remove stored conversion settings for the selected books. + +Future conversion of these books will use the default settings. + + + Remove &stored conversion settings for the selected books + + + + + + Change &cover @@ -440,6 +425,19 @@ Future conversion of these books will use the default settings. + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -902,14 +900,10 @@ not multiple and the destination field is multiple remove_tags remove_all_tags series - clear_series autonumber_series series_numbering_restarts series_start_number remove_format - remove_conversion_settings - swap_title_and_author - change_title_to_title_case button_box search_field search_mode From b83b89ce7437a8e0b7547cf6ac8a27112ee243ae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 10:09:14 -0700 Subject: [PATCH 6/9] Fix #7568 (Allow bulk editing of Published date) --- src/calibre/gui2/dialogs/metadata_bulk.py | 31 ++- src/calibre/gui2/dialogs/metadata_bulk.ui | 108 ++++++-- src/calibre/gui2/dialogs/metadata_single.py | 13 +- src/calibre/gui2/dialogs/metadata_single.ui | 287 ++++++++++---------- 4 files changed, 274 insertions(+), 165 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index 5ea8f00148..da6e92c26a 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -15,15 +15,16 @@ from calibre.ebooks.metadata import string_to_authors, authors_to_string from calibre.ebooks.metadata.book.base import composite_formatter from calibre.ebooks.metadata.meta import get_metadata from calibre.gui2.custom_column_widgets import populate_metadata_page -from calibre.gui2 import error_dialog, ResizableDialog +from calibre.gui2 import error_dialog, ResizableDialog, UNDEFINED_QDATE from calibre.gui2.progress_indicator import ProgressIndicator from calibre.utils.config import dynamic from calibre.utils.titlecase import titlecase from calibre.utils.icu import sort_key, capitalize -from calibre.utils.config import prefs +from calibre.utils.config import prefs, tweaks from calibre.utils.magick.draw import identify_data +from calibre.utils.date import qt_to_dt -def get_cover_data(path): +def get_cover_data(path): # {{{ old = prefs['read_file_metadata'] if not old: prefs['read_file_metadata'] = True @@ -46,7 +47,7 @@ def get_cover_data(path): prefs['read_file_metadata'] = old return cdata, area - +# }}} class MyBlockingBusy(QDialog): # {{{ @@ -132,7 +133,8 @@ class MyBlockingBusy(QDialog): # {{{ remove_all, remove, add, au, aus, do_aus, rating, pub, do_series, \ do_autonumber, do_remove_format, remove_format, do_swap_ta, \ do_remove_conv, do_auto_author, series, do_series_restart, \ - series_start_value, do_title_case, cover_action, clear_series = self.args + series_start_value, do_title_case, cover_action, clear_series, \ + pubdate = self.args # first loop: do author and title. These will commit at the end of each @@ -209,6 +211,9 @@ class MyBlockingBusy(QDialog): # {{{ if clear_series: self.db.set_series(id, '', notify=False, commit=False) + if pubdate is not None: + self.db.set_pubdate(id, pubdate, notify=False, commit=False) + if do_series: if do_series_restart: if self.series_start_value is None: @@ -288,6 +293,12 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.series.editTextChanged.connect(self.series_changed) self.tag_editor_button.clicked.connect(self.tag_editor) self.autonumber_series.stateChanged[int].connect(self.auto_number_changed) + self.pubdate.setMinimumDate(UNDEFINED_QDATE) + pubdate_format = tweaks['gui_pubdate_display_format'] + if pubdate_format is not None: + self.pubdate.setDisplayFormat(pubdate_format) + self.pubdate.setSpecialValueText(_('Undefined')) + self.clear_pubdate_button.clicked.connect(self.clear_pubdate) if len(self.db.custom_field_keys(include_composites=False)) == 0: self.central_widget.removeTab(1) @@ -304,6 +315,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.central_widget.setCurrentIndex(tab) self.exec_() + def clear_pubdate(self, *args): + self.pubdate.setDate(UNDEFINED_QDATE) + def button_clicked(self, which): if which == self.button_box.button(QDialogButtonBox.Apply): self.do_again = True @@ -783,6 +797,10 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): do_remove_conv = self.remove_conversion_settings.isChecked() do_auto_author = self.auto_author_sort.isChecked() do_title_case = self.change_title_to_title_case.isChecked() + pubdate = None + if self.apply_pubdate.isChecked(): + pubdate = qt_to_dt(self.pubdate.date()) + cover_action = None if self.cover_remove.isChecked(): cover_action = 'remove' @@ -794,7 +812,8 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): args = (remove_all, remove, add, au, aus, do_aus, rating, pub, do_series, do_autonumber, do_remove_format, remove_format, do_swap_ta, do_remove_conv, do_auto_author, series, do_series_restart, - series_start_value, do_title_case, cover_action, clear_series) + series_start_value, do_title_case, cover_action, clear_series, + pubdate) bb = MyBlockingBusy(_('Applying changes to %d books.\nPhase {0} {1}%%.') %len(self.ids), args, self.db, self.ids, diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index d52bc2cb89..b14c31c9d1 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -220,6 +220,9 @@ &Remove tags: + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + remove_tags @@ -260,6 +263,12 @@ + + + 0 + 0 + + List of known series. You can add new series. @@ -273,7 +282,10 @@ QComboBox::InsertAlphabetically - QComboBox::AdjustToContents + QComboBox::AdjustToMinimumContentsLengthWithIcon + + + 40 @@ -335,27 +347,52 @@ from the value in the box - - + + - Remove &format: + &Published: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - remove_format + pubdate - - - - - 120 - 16777215 - + + + + + + MMM yyyy + + + + + + + Clear published date + + + ... + + + + :/images/trash.png:/images/trash.png + + + + + + + + + &Apply date - + @@ -395,7 +432,7 @@ Future conversion of these books will use the default settings. - + Change &cover @@ -425,7 +462,7 @@ Future conversion of these books will use the default settings. - + Qt::Vertical @@ -438,6 +475,42 @@ Future conversion of these books will use the default settings. + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + Remove &format: + + + remove_format + + + + + + + + 120 + 16777215 + + + + @@ -798,8 +871,8 @@ not multiple and the destination field is multiple 0 0 - 197 - 60 + 826 + 313 @@ -903,7 +976,6 @@ not multiple and the destination field is multiple autonumber_series series_numbering_restarts series_start_number - remove_format button_box search_field search_mode diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index ede605343b..e4efdf0470 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -16,7 +16,7 @@ from PyQt4.Qt import SIGNAL, QObject, Qt, QTimer, QDate, \ from calibre.gui2 import error_dialog, file_icon_provider, dynamic, \ choose_files, choose_images, ResizableDialog, \ - warning_dialog, question_dialog + warning_dialog, question_dialog, UNDEFINED_QDATE from calibre.gui2.dialogs.metadata_single_ui import Ui_MetadataSingleDialog from calibre.gui2.dialogs.fetch_metadata import FetchMetadata from calibre.gui2.dialogs.tag_editor import TagEditor @@ -491,11 +491,15 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.formats.setAcceptDrops(True) self.cover_changed = False self.cpixmap = None - self.pubdate.setMinimumDate(QDate(100,1,1)) + self.pubdate.setMinimumDate(UNDEFINED_QDATE) pubdate_format = tweaks['gui_pubdate_display_format'] if pubdate_format is not None: self.pubdate.setDisplayFormat(pubdate_format) - self.date.setMinimumDate(QDate(100,1,1)) + self.date.setMinimumDate(UNDEFINED_QDATE) + self.pubdate.setSpecialValueText(_('Undefined')) + self.date.setSpecialValueText(_('Undefined')) + self.clear_pubdate_button.clicked.connect(self.clear_pubdate) + self.connect(self.cover, SIGNAL('cover_changed(PyQt_PyObject)'), self.cover_dropped) QObject.connect(self.cover_button, SIGNAL("clicked(bool)"), \ @@ -615,6 +619,9 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): self.show() + def clear_pubdate(self, *args): + self.pubdate.setDate(UNDEFINED_QDATE) + def create_custom_column_editors(self): w = self.central_widget.widget(1) layout = w.layout() diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 6d31342dcf..60c221be1a 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -100,6 +100,112 @@ + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 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 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Swap the author and title + + + ... + + + + :/images/swap.png:/images/swap.png + + + + 16 + 16 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + 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. + + + ... + + + + :/images/auto_author_sort.png:/images/auto_author_sort.png + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + @@ -226,6 +332,31 @@ If the box is colored green, then text matches the individual author's sort stri + + + + + + Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. + + + + + + + + + Open Tag Editor + + + Open Tag Editor + + + + :/images/chapters.png:/images/chapters.png + + + @@ -265,6 +396,20 @@ If the box is colored green, then text matches the individual author's sort stri + + + + Remove unused series (Series that have no books) + + + ... + + + + :/images/trash.png:/images/trash.png + + + @@ -330,7 +475,7 @@ If the box is colored green, then text matches the individual author's sort stri - + MMM yyyy @@ -340,144 +485,10 @@ If the box is colored green, then text matches the individual author's sort stri - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - 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 - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Swap the author and title - - - ... - - - - :/images/swap.png:/images/swap.png - - - - 16 - 16 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - 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. - - - ... - - - - :/images/auto_author_sort.png:/images/auto_author_sort.png - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Tags categorize the book. This is particularly useful while searching. <br><br>They can be any words or phrases, separated by commas. - - - - - - - + + - Open Tag Editor - - - Open Tag Editor - - - - :/images/chapters.png:/images/chapters.png - - - - - - - Remove unused series (Series that have no books) - - - ... + Clear published date From 4e5d5bbce0ed89e1ff33836ac2222243fbb0cb24 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 11:45:18 -0700 Subject: [PATCH 7/9] MOBI Input: SPecial case handling of emptu div tags with a defined height used as paragraph separators. Fixes #8391 (formatting issues when converting from .azw to .mobi. Not duplicating space between paragraphs.) --- src/calibre/ebooks/mobi/reader.py | 12 +++++++++++- src/calibre/manual/faq.rst | 5 +++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index e07418f41c..2f397006a1 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -542,7 +542,17 @@ class MobiReader(object): elif tag.tag == 'img': tag.set('height', height) else: - styles.append('margin-top: %s' % self.ensure_unit(height)) + if tag.tag == 'div' and not tag.text and \ + (not tag.tail or not tag.tail.strip()) and \ + not len(list(tag.iterdescendants())): + # Paragraph spacer + # Insert nbsp so that the element is never + # discarded by a renderer + tag.text = u'\u00a0' # nbsp + styles.append('height: %s' % + self.ensure_unit(height)) + else: + styles.append('margin-top: %s' % self.ensure_unit(height)) if attrib.has_key('width'): width = attrib.pop('width').strip() if width and re.search(r'\d+', width): diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 0e8c101620..ee72bf6fdb 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -450,6 +450,11 @@ How do I use purchased EPUB books with |app|? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Most purchased EPUB books have `DRM `_. This prevents |app| from opening them. You can still use |app| to store and transfer them to your e-book reader. First, you must authorize your reader on a windows machine with Adobe Digital Editions. Once this is done, EPUB books transferred with |app| will work fine on your reader. When you purchase an epub book from a website, you will get an ".acsm" file. This file should be opened with Adobe Digital Editions, which will then download the actual ".epub" e-book. The e-book file will be stored in the folder "My Digital Editions", from where you can add it to |app|. +I am getting a "Permission Denied" error? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +A permission denied error can occur because of many possible reasons, none of them having anything to do with |app|. You can get permission denied errors if you are using an SD card with write protect enabled. Or if you, or some program you used changed the file permissions of the files in question to read only. Or if there is a filesystem error on the device which caused your operating system to mount the filesystem in read only mode or mark a particular file as read only pending recovery. Or if the files have their owner set to a user other than you. You will need to fix the underlying cause of the permissions error before resuming to use |app|. Read the error message carefully, see what file it points to and fix the permissions on that file. + Can I have the comment metadata show up on my reader? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 91ccd9ad749a0ef88fff14971312c77d1f3838ae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 12:31:19 -0700 Subject: [PATCH 8/9] Add AZW to the default list of internall viewed formats --- src/calibre/gui2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index 6a9becee50..c94b99f141 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -85,7 +85,7 @@ def _config(): c.add_opt('LRF_ebook_viewer_options', default=None, help=_('Options for the LRF ebook viewer')) c.add_opt('internally_viewed_formats', default=['LRF', 'EPUB', 'LIT', - 'MOBI', 'PRC', 'HTML', 'FB2', 'PDB', 'RB', 'SNB'], + 'MOBI', 'PRC', 'AZW', 'HTML', 'FB2', 'PDB', 'RB', 'SNB'], help=_('Formats that are viewed using the internal viewer')) c.add_opt('column_map', default=ALL_COLUMNS, help=_('Columns to be displayed in the book list')) From 4d428dfa9a90023876413cab30f6d38fefdac620 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 16 Jan 2011 12:34:49 -0700 Subject: [PATCH 9/9] Bulk metadata edit: Check apply date automatically whenever user changes the date in the pubdate field --- src/calibre/gui2/dialogs/metadata_bulk.py | 4 ++++ src/calibre/gui2/dialogs/metadata_bulk.ui | 7 +++++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py index da6e92c26a..302766a92d 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.py +++ b/src/calibre/gui2/dialogs/metadata_bulk.py @@ -299,6 +299,7 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.pubdate.setDisplayFormat(pubdate_format) self.pubdate.setSpecialValueText(_('Undefined')) self.clear_pubdate_button.clicked.connect(self.clear_pubdate) + self.pubdate.dateChanged.connect(self.do_apply_pubdate) if len(self.db.custom_field_keys(include_composites=False)) == 0: self.central_widget.removeTab(1) @@ -315,6 +316,9 @@ class MetadataBulkDialog(ResizableDialog, Ui_MetadataBulkDialog): self.central_widget.setCurrentIndex(tab) self.exec_() + def do_apply_pubdate(self, *args): + self.apply_pubdate.setChecked(True) + def clear_pubdate(self, *args): self.pubdate.setDate(UNDEFINED_QDATE) diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui index b14c31c9d1..5690a8e555 100644 --- a/src/calibre/gui2/dialogs/metadata_bulk.ui +++ b/src/calibre/gui2/dialogs/metadata_bulk.ui @@ -367,6 +367,9 @@ from the value in the box MMM yyyy + + true + @@ -871,8 +874,8 @@ not multiple and the destination field is multiple 0 0 - 826 - 313 + 197 + 60