diff --git a/src/calibre/devices/kindle/apnx.py b/src/calibre/devices/kindle/apnx.py index 07fd51ea7a..d7d69e1f0a 100644 --- a/src/calibre/devices/kindle/apnx.py +++ b/src/calibre/devices/kindle/apnx.py @@ -69,7 +69,7 @@ class APNXBuilder(object): if not pages: pages = self.get_pages_accurate(mobi_file_path) else: - raise('no valid accurate method chosen use fast') + raise Exception('%r is not a valid apnx generation method' % method) except: # Fall back to the fast parser if we can't # use the accurate one. Typically this is diff --git a/src/calibre/devices/kindle/driver.py b/src/calibre/devices/kindle/driver.py index e4f19023a9..d3b5555c86 100644 --- a/src/calibre/devices/kindle/driver.py +++ b/src/calibre/devices/kindle/driver.py @@ -83,7 +83,6 @@ class KINDLE(USBMS): 'replace') return mi - def get_annotations(self, path_map): MBP_FORMATS = [u'azw', u'mobi', u'prc', u'txt'] mbp_formats = set(MBP_FORMATS) @@ -182,16 +181,16 @@ class KINDLE(USBMS): spanTag['style'] = 'font-weight:bold' if bookmark.book_format == 'pdf': spanTag.insert(0,NavigableString( - _("%(time)s
Last Page Read: %(loc)d (%(pr)d%%)") % \ - dict(time=strftime(u'%x', timestamp.timetuple()), - loc=last_read_location, - pr=percent_read))) + _("%(time)s
Last Page Read: %(loc)d (%(pr)d%%)") % dict( + time=strftime(u'%x', timestamp.timetuple()), + loc=last_read_location, + pr=percent_read))) else: spanTag.insert(0,NavigableString( - _("%(time)s
Last Page Read: Location %(loc)d (%(pr)d%%)") % \ - dict(time=strftime(u'%x', timestamp.timetuple()), - loc=last_read_location, - pr=percent_read))) + _("%(time)s
Last Page Read: Location %(loc)d (%(pr)d%%)") % dict( + time=strftime(u'%x', timestamp.timetuple()), + loc=last_read_location, + pr=percent_read))) divTag.insert(dtc, spanTag) dtc += 1 @@ -207,23 +206,23 @@ class KINDLE(USBMS): for location in sorted(user_notes): if user_notes[location]['text']: annotations.append( - _('Location %(dl)d • %(typ)s
%(text)s
') % \ - dict(dl=user_notes[location]['displayed_location'], - typ=user_notes[location]['type'], - text=(user_notes[location]['text'] if \ - user_notes[location]['type'] == 'Note' else \ - '%s' % user_notes[location]['text']))) + _('Location %(dl)d • %(typ)s
%(text)s
') % dict( + dl=user_notes[location]['displayed_location'], + typ=user_notes[location]['type'], + text=(user_notes[location]['text'] if + user_notes[location]['type'] == 'Note' else + '%s' % user_notes[location]['text']))) else: if bookmark.book_format == 'pdf': annotations.append( - _('Page %(dl)d • %(typ)s
') % \ - dict(dl=user_notes[location]['displayed_location'], - typ=user_notes[location]['type'])) + _('Page %(dl)d • %(typ)s
') % dict( + dl=user_notes[location]['displayed_location'], + typ=user_notes[location]['type'])) else: annotations.append( - _('Location %(dl)d • %(typ)s
') % \ - dict(dl=user_notes[location]['displayed_location'], - typ=user_notes[location]['type'])) + _('Location %(dl)d • %(typ)s
') % dict( + dl=user_notes[location]['displayed_location'], + typ=user_notes[location]['type'])) for annotation in annotations: divTag.insert(dtc, annotation) @@ -232,7 +231,6 @@ class KINDLE(USBMS): ka_soup.insert(0,divTag) return ka_soup - def add_annotation_to_library(self, db, db_id, annotation): from calibre.ebooks.BeautifulSoup import Tag from calibre.ebooks.metadata import MetaInformation @@ -278,7 +276,7 @@ class KINDLE(USBMS): mi.comments = last_update db.set_metadata(mc_id[0], mi) else: - mi = MetaInformation('My Clippings', authors = ['Kindle']) + mi = MetaInformation('My Clippings', authors=['Kindle']) mi.tags = ['Clippings'] mi.comments = last_update db.add_books([bm.value['path']], ['txt'], [mi]) @@ -290,7 +288,9 @@ class KINDLE2(KINDLE): FORMATS = ['azw', 'mobi', 'azw3', 'prc', 'azw1', 'tpz', 'azw4', 'pobi', 'pdf', 'txt'] DELETE_EXTS = KINDLE.DELETE_EXTS + ['.mbp1', '.mbs', '.sdr', '.han'] - # On the Touch, there's also .asc files, but not using the same basename (for X-Ray & End Actions), azw3f & azw3r files, but all of them are in the .sdr sidecar folder + # On the Touch, there's also .asc files, but not using the same basename + # (for X-Ray & End Actions), azw3f & azw3r files, but all of them are in + # the .sdr sidecar folder PRODUCT_ID = [0x0002, 0x0004] BCD = [0x0100] @@ -298,49 +298,54 @@ class KINDLE2(KINDLE): # SUPPORTS_SUB_DIRS_FOR_SCAN = True EXTRA_CUSTOMIZATION_MESSAGE = [ - _('Send page number information when sending books') + - ':::' + - _('The Kindle 3 and newer versions can use page number information ' - 'in MOBI files. With this option, calibre will calculate and send' - ' this information to the Kindle when uploading MOBI files by' - ' USB. Note that the page numbers do not correspond to any paper' - ' book.'), - _('Use slower but more accurate page number calculation') + - ':::' + - _('There are two ways to generate the page number information. Using the more accurate ' - 'generator will produce pages that correspond better to a printed book. ' - 'However, this method is slower and will slow down sending files ' - 'to the Kindle.'), - _('Accurate calculation method') + - ':::' + - _('There are multiple methods to accurately calculate the page numbers. "accurate" which ' - 'is an estimation based on the number of chapters, paragraphs, and visible lines in the book. ' - 'This method is designed to simulate an average paperback book where there are 32 lines per ' - 'page and a maximum of 70 characters per line. \n\n' - 'The "pagebreak" method uses the presense of tags within the book to ' - 'determine pages.'), - _('Custom column name to retrieve page counts from') + - ':::' + - _('If you have a custom column in your library that you use to ' - 'store the page count of books, you can have calibre use that ' - 'information, instead of calculating a page count. Specify the ' - 'name of the custom column here, for example, #pages. '), + _('Send page number information when sending books') + ':::' + _( + 'The Kindle 3 and newer versions can use page number information' + ' in MOBI files. With this option, calibre will calculate and send' + ' this information to the Kindle when uploading MOBI files by' + ' USB. Note that the page numbers do not correspond to any paper' + ' book.'), + _('Page count calculation method') + ':::' + '

' + _( + 'There are multiple ways to generate the page number information.' + ' If a page count is given then the book will be divided into that many pages.' + ' Otherwise the number of pages will be approximated using one of the following' + ' methods.

' + 'Methods other than "fast" are going to be much slower.' + ' Further, if "pagebreak" fails to determine a page count accurate will be used, and if ' + ' "accurate" fails fast will be used.'), + _('Custom column name to retrieve page counts from') + ':::' + _( + 'If you have a custom column in your library that you use to' + ' store the page count of books, you can have calibre use that' + ' information, instead of calculating a page count. Specify the' + ' name of the custom column here, for example, #pages.'), ] EXTRA_CUSTOMIZATION_DEFAULT = [ True, - False, - 'accurate', + 'fast', '', ] OPT_APNX = 0 - OPT_APNX_ACCURATE = 1 - OPT_APNX_ACCURATE_METHOD = 2 - OPT_APNX_CUST_COL = 3 + OPT_APNX_METHOD = 1 + OPT_APNX_CUST_COL = 2 + EXTRA_CUSTOMIZATION_CHOICES = {OPT_APNX_METHOD:{'fast', 'accurate', 'pagebreak'}} + # x330 on the PaperWhite THUMBNAIL_HEIGHT = 330 # x262 on the Touch. Doesn't choke on x330, though. + @classmethod + def migrate_extra_customization(cls, vals): + if isinstance(vals[cls.OPT_APNX_METHOD], bool): + # Previously this option used to be a bool + vals[cls.OPT_APNX_METHOD] = 'accurate' if vals[cls.OPT_APNX_METHOD] else 'fast' + return vals + def formats_to_scan_for(self): ans = USBMS.formats_to_scan_for(self) | {'azw3'} return ans @@ -408,7 +413,8 @@ class KINDLE2(KINDLE): if not coverdata or not coverdata[2]: return thumb_dir = os.path.join(self._main_prefix, 'system', 'thumbnails') - if not os.path.exists(thumb_dir): return + if not os.path.exists(thumb_dir): + return from calibre.ebooks.mobi.reader.headers import MetadataHeader with lopen(filepath, 'rb') as f: @@ -451,12 +457,8 @@ class KINDLE2(KINDLE): apnx_path = '%s.apnx' % os.path.join(path, filename) apnx_builder = APNXBuilder() try: - method = None - if opts.extra_customization[self.OPT_APNX_ACCURATE]: - method = opts.extra_customization[self.OPT_APNX_ACCURATE_METHOD] - apnx_builder.write_apnx(filepath, apnx_path, - method=method, - page_count=custom_page_count) + method = opts.extra_customization[self.OPT_APNX_METHOD] + apnx_builder.write_apnx(filepath, apnx_path, method=method, page_count=custom_page_count) except: print 'Failed to generate APNX' import traceback diff --git a/src/calibre/devices/usbms/deviceconfig.py b/src/calibre/devices/usbms/deviceconfig.py index b2eadd7461..2a750ba559 100644 --- a/src/calibre/devices/usbms/deviceconfig.py +++ b/src/calibre/devices/usbms/deviceconfig.py @@ -27,9 +27,13 @@ class DeviceConfig(object): #: EXTRA_CUSTOMIZATION_MESSAGE you *must* set this as well. EXTRA_CUSTOMIZATION_DEFAULT = None + #: A dictionary providing choices for options that should be displayed as a + #: combo box to the user. The dictionary maps extra #: customization indexes + #: to a set of choices. + EXTRA_CUSTOMIZATION_CHOICES = None + SUPPORTS_SUB_DIRS = False - SUPPORTS_SUB_DIRS_FOR_SCAN = False # This setting is used when scanning for - # books when SUPPORTS_SUB_DIRS is False + SUPPORTS_SUB_DIRS_FOR_SCAN = False # This setting is used when scanning for books when SUPPORTS_SUB_DIRS is False SUPPORTS_SUB_DIRS_DEFAULT = True MUST_READ_METADATA = False @@ -41,7 +45,6 @@ class DeviceConfig(object): #: If True the user can add new formats to the driver USER_CAN_ADD_NEW_FORMATS = True - @classmethod def _default_save_template(cls): from calibre.library.save_to_disk import config @@ -81,7 +84,7 @@ class DeviceConfig(object): from calibre.gui2.device_drivers.configwidget import ConfigWidget cw = ConfigWidget(cls.settings(), cls.FORMATS, cls.SUPPORTS_SUB_DIRS, cls.MUST_READ_METADATA, cls.SUPPORTS_USE_AUTHOR_SORT, - cls.EXTRA_CUSTOMIZATION_MESSAGE, cls) + cls.EXTRA_CUSTOMIZATION_MESSAGE, cls, extra_customization_choices=cls.EXTRA_CUSTOMIZATION_CHOICES) return cw @classmethod @@ -103,6 +106,8 @@ class DeviceConfig(object): continue if hasattr(config_widget.opt_extra_customization[i], 'isChecked'): ec.append(config_widget.opt_extra_customization[i].isChecked()) + elif hasattr(config_widget.opt_extra_customization[i], 'currentText'): + ec.append(unicode(config_widget.opt_extra_customization[i].currentText()).strip()) else: ec.append(unicode(config_widget.opt_extra_customization[i].text()).strip()) else: @@ -113,6 +118,10 @@ class DeviceConfig(object): st = unicode(config_widget.opt_save_template.text()) proxy['save_template'] = st + @classmethod + def migrate_extra_customization(cls, vals): + return vals + @classmethod def settings(cls): opts = cls._config().parse() @@ -121,9 +130,7 @@ class DeviceConfig(object): opts.extra_customization = [] if not isinstance(opts.extra_customization, list): opts.extra_customization = [opts.extra_customization] - for i,d in enumerate(cls.EXTRA_CUSTOMIZATION_DEFAULT): - if i >= len(opts.extra_customization): - opts.extra_customization.append(d) + opts.extra_customization = cls.migrate_extra_customization(opts.extra_customization) return opts @classmethod diff --git a/src/calibre/gui2/device_drivers/configwidget.py b/src/calibre/gui2/device_drivers/configwidget.py index 17c97b7ba0..512415a81d 100644 --- a/src/calibre/gui2/device_drivers/configwidget.py +++ b/src/calibre/gui2/device_drivers/configwidget.py @@ -7,7 +7,7 @@ __docformat__ = 'restructuredtext en' import textwrap from PyQt4.Qt import (QWidget, QListWidgetItem, Qt, QVariant, QLabel, - QLineEdit, QCheckBox) + QLineEdit, QCheckBox, QComboBox) from calibre.gui2 import error_dialog, question_dialog from calibre.gui2.device_drivers.configwidget_ui import Ui_ConfigWidget @@ -18,7 +18,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): def __init__(self, settings, all_formats, supports_subdirs, must_read_metadata, supports_use_author_sort, - extra_customization_message, device): + extra_customization_message, device, extra_customization_choices=None): QWidget.__init__(self) Ui_ConfigWidget.__init__(self) @@ -62,6 +62,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): else: self.opt_use_author_sort.hide() if extra_customization_message: + extra_customization_choices = extra_customization_choices or {} def parse_msg(m): msg, _, tt = m.partition(':::') if m else ('', '', '') return msg.strip(), textwrap.fill(tt.strip(), 100) @@ -84,6 +85,14 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): self.opt_extra_customization.append(QCheckBox(label_text)) self.opt_extra_customization[-1].setToolTip(tt) self.opt_extra_customization[i].setChecked(bool(settings.extra_customization[i])) + elif i in extra_customization_choices: + cb = QComboBox(self) + self.opt_extra_customization.append(cb) + l = QLabel(label_text) + l.setToolTip(tt), cb.setToolTip(tt), l.setBuddy(cb), cb.setToolTip(tt) + for li in sorted(extra_customization_choices[i]): + self.opt_extra_customization[i].addItem(li) + cb.setCurrentIndex(max(0, cb.findText(settings.extra_customization[i]))) else: self.opt_extra_customization.append(QLineEdit(self)) l = QLabel(label_text) @@ -111,7 +120,6 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): self.extra_layout.addWidget(self.opt_extra_customization, 1, 0) self.opt_save_template.setText(settings.save_template) - def up_column(self): idx = self.columns.currentRow() if idx > 0: @@ -125,7 +133,8 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): self.columns.setCurrentRow(idx+1) def format_map(self): - formats = [unicode(self.columns.item(i).data(Qt.UserRole).toString()) for i in range(self.columns.count()) if self.columns.item(i).checkState()==Qt.Checked] + formats = [unicode(self.columns.item(i).data(Qt.UserRole).toString()) + for i in range(self.columns.count()) if self.columns.item(i).checkState()==Qt.Checked] return formats def use_subdirs(self): @@ -156,7 +165,7 @@ class ConfigWidget(QWidget, Ui_ConfigWidget): return True except Exception as err: error_dialog(self, _('Invalid template'), - '

'+_('The template %s is invalid:')%tmpl + \ + '

'+_('The template %s is invalid:')%tmpl + '
'+unicode(err), show=True) return False