From 336294fe358e23794a35c7122fd635aba077c9ad Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 10 Jul 2010 14:07:00 +0100 Subject: [PATCH 01/19] Recover from UI changes --- src/calibre/gui2/layout.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index f3b650f531..7b70224872 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -12,6 +12,7 @@ from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ from calibre.constants import __appname__, filesystem_encoding from calibre.gui2.search_box import SearchBox2, SavedSearchBox +from calibre.gui2.widgets import ComboBoxWithHelp from calibre.gui2.throbber import ThrobbingButton from calibre.gui2 import NONE from calibre import human_readable @@ -280,12 +281,7 @@ class SearchBar(QWidget): # {{{ self._layout = l = QHBoxLayout() self.setLayout(self._layout) - self.restriction_label = QLabel(_("&Restrict to:")) - l.addWidget(self.restriction_label) - self.restriction_label.setSizePolicy(QSizePolicy.Minimum, - QSizePolicy.Minimum) - - x = QComboBox(self) + x = ComboBoxWithHelp(self) x.setMaximumSize(QSize(150, 16777215)) x.setObjectName("search_restriction") x.setToolTip(_("Books display will be restricted to those matching the selected saved search")) @@ -344,7 +340,6 @@ class SearchBar(QWidget): # {{{ x.setToolTip(_("Delete current saved search")) self.label.setBuddy(parent.search) - self.restriction_label.setBuddy(parent.search_restriction) # }}} From f9896d01f31a638dc87b884600b90fd7cf4e147e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 10 Jul 2010 15:40:38 +0100 Subject: [PATCH 02/19] Put GPL line back in (don't know how it disappeared) --- src/calibre/gui2/widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/widgets.py b/src/calibre/gui2/widgets.py index e3874fdc3a..97758482fc 100644 --- a/src/calibre/gui2/widgets.py +++ b/src/calibre/gui2/widgets.py @@ -1,3 +1,4 @@ +__license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal ' ''' Miscellaneous widgets used in the GUI From 62a5b4d459a9dc9dbb24c99e411376c43cb79f6a Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 11 Jul 2010 10:04:49 +0100 Subject: [PATCH 03/19] Fixes to work around Qt strangeness with date editing --- src/calibre/gui2/custom_column_widgets.py | 9 +++++++-- src/calibre/gui2/library/delegates.py | 8 ++++---- src/calibre/library/caches.py | 4 ++-- src/calibre/utils/date.py | 2 +- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 2a9c81e8ee..58c2f15452 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -132,9 +132,11 @@ class DateEdit(QDateEdit): def focusInEvent(self, x): self.setSpecialValueText('') + QDateEdit.focusInEvent(self, x) def focusOutEvent(self, x): self.setSpecialValueText(_('Undefined')) + QDateEdit.focusOutEvent(self, x) class DateTime(Base): @@ -142,7 +144,10 @@ class DateTime(Base): self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), DateEdit(parent)] w = self.widgets[1] - w.setDisplayFormat('dd MMM yyyy') + format = self.col_metadata['display'].get('date_format','') + if not format: + format = 'dd MMM yyyy' + w.setDisplayFormat(format) w.setCalendarPopup(True) w.setMinimumDate(UNDEFINED_QDATE) w.setSpecialValueText(_('Undefined')) @@ -156,7 +161,7 @@ class DateTime(Base): def getter(self): val = self.widgets[1].date() - if val == UNDEFINED_QDATE: + if val <= UNDEFINED_QDATE: val = None else: val = qt_to_dt(val) diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index 529055ecd2..40f7a2e4e0 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -96,7 +96,7 @@ class DateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d == UNDEFINED_QDATE: + if d <= UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), 'dd MMM yyyy') @@ -116,7 +116,7 @@ class PubDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d == UNDEFINED_QDATE: + if d <= UNDEFINED_QDATE: return '' format = tweaks['gui_pubdate_display_format'] if format is None: @@ -194,7 +194,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d == UNDEFINED_QDATE: + if d <= UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), self.format) @@ -217,7 +217,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def setModelData(self, editor, model, index): val = editor.date() - if val == UNDEFINED_QDATE: + if val <= UNDEFINED_QDATE: val = None model.setData(index, QVariant(val), Qt.EditRole) diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index d46ae23d90..af950a36fc 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -209,13 +209,13 @@ class ResultCache(SearchQueryParser): if query == 'false': for item in self._data: if item is None: continue - if item[loc] is None or item[loc] == UNDEFINED_DATE: + if item[loc] is None or item[loc] <= UNDEFINED_DATE: matches.add(item[0]) return matches if query == 'true': for item in self._data: if item is None: continue - if item[loc] is not None and item[loc] != UNDEFINED_DATE: + if item[loc] is not None and item[loc] > UNDEFINED_DATE: matches.add(item[0]) return matches diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index c5fba13f57..b25940d23d 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -44,7 +44,7 @@ parse_date_day_first = compute_locale_info_for_parse_date() utc_tz = _utc_tz = tzutc() local_tz = _local_tz = SafeLocalTimeZone() -UNDEFINED_DATE = datetime(101,1,1, tzinfo=utc_tz) +UNDEFINED_DATE = datetime(1000,1,1, tzinfo=utc_tz) def parse_date(date_string, assume_utc=False, as_utc=True, default=None): ''' From abdee7d012c253f198271a9ea838e3e447807f6c Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 11 Jul 2010 10:51:56 +0100 Subject: [PATCH 04/19] Disable editing metadata on the device view when automatic update is chosen. --- src/calibre/gui2/library/models.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 9f1a72b021..89008735fe 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1216,7 +1216,9 @@ class DeviceBooksModel(BooksModel): # {{{ return done def set_editable(self, editable): - self.editable = editable + # Cannot edit if metadata is sent on connect. Reason: changes will + # revert to what is in the library on next connect. + self.editable = editable and prefs['manage_device_metadata']!='on_connect' def set_search_restriction(self, s): pass From 30436a9e6aa58ff5009733ccbb9f71519d8e4ffb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Jul 2010 09:28:27 -0600 Subject: [PATCH 05/19] Fix #6100 (Improved aldiko support) --- src/calibre/devices/android/driver.py | 10 ++++++++++ src/calibre/devices/usbms/device.py | 4 ++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 5642235b31..9951dc57ad 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -70,6 +70,16 @@ class ANDROID(USBMS): dirs = [x.strip() for x in dirs.split(',')] self.EBOOK_DIR_MAIN = dirs + def get_main_ebook_dir(self, for_upload=False): + dirs = self.EBOOK_DIR_MAIN + if not for_upload: + def aldiko_tweak(x): + return 'eBooks' if x == 'eBooks/import' else x + if isinstance(dirs, basestring): + dirs = [dirs] + dirs = list(map(aldiko_tweak, dirs)) + return dirs + class S60(USBMS): name = 'S60 driver' diff --git a/src/calibre/devices/usbms/device.py b/src/calibre/devices/usbms/device.py index 55790420f2..c07b7fd761 100644 --- a/src/calibre/devices/usbms/device.py +++ b/src/calibre/devices/usbms/device.py @@ -732,7 +732,7 @@ class Device(DeviceConfig, DevicePlugin): traceback.print_exc() self._main_prefix = self._card_a_prefix = self._card_b_prefix = None - def get_main_ebook_dir(self): + def get_main_ebook_dir(self, for_upload=False): return self.EBOOK_DIR_MAIN def _sanity_check(self, on_card, files): @@ -750,7 +750,7 @@ class Device(DeviceConfig, DevicePlugin): path = os.path.join(self._card_b_prefix, *(self.EBOOK_DIR_CARD_B.split('/'))) else: - candidates = self.get_main_ebook_dir() + candidates = self.get_main_ebook_dir(for_upload=True) if isinstance(candidates, basestring): candidates = [candidates] candidates = [ From ba94ea9670eb7010328d721830881199cee1a842 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Jul 2010 09:47:41 -0600 Subject: [PATCH 06/19] oops --- src/calibre/gui2/convert/structure_detection.ui | 6 +----- src/calibre/gui2/custom_column_widgets.py | 9 ++------- src/calibre/gui2/library/delegates.py | 8 ++++---- src/calibre/gui2/library/models.py | 4 +--- src/calibre/library/caches.py | 4 ++-- src/calibre/utils/date.py | 2 +- 6 files changed, 11 insertions(+), 22 deletions(-) diff --git a/src/calibre/gui2/convert/structure_detection.ui b/src/calibre/gui2/convert/structure_detection.ui index 67fd0a31b2..e4414473f5 100644 --- a/src/calibre/gui2/convert/structure_detection.ui +++ b/src/calibre/gui2/convert/structure_detection.ui @@ -28,11 +28,7 @@ - - - 30 - - + diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 58c2f15452..2a9c81e8ee 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -132,11 +132,9 @@ class DateEdit(QDateEdit): def focusInEvent(self, x): self.setSpecialValueText('') - QDateEdit.focusInEvent(self, x) def focusOutEvent(self, x): self.setSpecialValueText(_('Undefined')) - QDateEdit.focusOutEvent(self, x) class DateTime(Base): @@ -144,10 +142,7 @@ class DateTime(Base): self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), DateEdit(parent)] w = self.widgets[1] - format = self.col_metadata['display'].get('date_format','') - if not format: - format = 'dd MMM yyyy' - w.setDisplayFormat(format) + w.setDisplayFormat('dd MMM yyyy') w.setCalendarPopup(True) w.setMinimumDate(UNDEFINED_QDATE) w.setSpecialValueText(_('Undefined')) @@ -161,7 +156,7 @@ class DateTime(Base): def getter(self): val = self.widgets[1].date() - if val <= UNDEFINED_QDATE: + if val == UNDEFINED_QDATE: val = None else: val = qt_to_dt(val) diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py index 40f7a2e4e0..529055ecd2 100644 --- a/src/calibre/gui2/library/delegates.py +++ b/src/calibre/gui2/library/delegates.py @@ -96,7 +96,7 @@ class DateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d <= UNDEFINED_QDATE: + if d == UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), 'dd MMM yyyy') @@ -116,7 +116,7 @@ class PubDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d <= UNDEFINED_QDATE: + if d == UNDEFINED_QDATE: return '' format = tweaks['gui_pubdate_display_format'] if format is None: @@ -194,7 +194,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def displayText(self, val, locale): d = val.toDate() - if d <= UNDEFINED_QDATE: + if d == UNDEFINED_QDATE: return '' return format_date(d.toPyDate(), self.format) @@ -217,7 +217,7 @@ class CcDateDelegate(QStyledItemDelegate): # {{{ def setModelData(self, editor, model, index): val = editor.date() - if val <= UNDEFINED_QDATE: + if val == UNDEFINED_QDATE: val = None model.setData(index, QVariant(val), Qt.EditRole) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 89008735fe..9f1a72b021 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1216,9 +1216,7 @@ class DeviceBooksModel(BooksModel): # {{{ return done def set_editable(self, editable): - # Cannot edit if metadata is sent on connect. Reason: changes will - # revert to what is in the library on next connect. - self.editable = editable and prefs['manage_device_metadata']!='on_connect' + self.editable = editable def set_search_restriction(self, s): pass diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index af950a36fc..d46ae23d90 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -209,13 +209,13 @@ class ResultCache(SearchQueryParser): if query == 'false': for item in self._data: if item is None: continue - if item[loc] is None or item[loc] <= UNDEFINED_DATE: + if item[loc] is None or item[loc] == UNDEFINED_DATE: matches.add(item[0]) return matches if query == 'true': for item in self._data: if item is None: continue - if item[loc] is not None and item[loc] > UNDEFINED_DATE: + if item[loc] is not None and item[loc] != UNDEFINED_DATE: matches.add(item[0]) return matches diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index b25940d23d..c5fba13f57 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -44,7 +44,7 @@ parse_date_day_first = compute_locale_info_for_parse_date() utc_tz = _utc_tz = tzutc() local_tz = _local_tz = SafeLocalTimeZone() -UNDEFINED_DATE = datetime(1000,1,1, tzinfo=utc_tz) +UNDEFINED_DATE = datetime(101,1,1, tzinfo=utc_tz) def parse_date(date_string, assume_utc=False, as_utc=True, default=None): ''' From de0aea0eb7630ed467997d66d15f7b6790b10483 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 11 Jul 2010 09:52:15 -0600 Subject: [PATCH 07/19] Possible fix for conversion box being too wide --- src/calibre/gui2/convert/structure_detection.ui | 6 +++++- src/calibre/gui2/convert/xexp_edit.ui | 9 +++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/convert/structure_detection.ui b/src/calibre/gui2/convert/structure_detection.ui index e4414473f5..2e97c0d3ca 100644 --- a/src/calibre/gui2/convert/structure_detection.ui +++ b/src/calibre/gui2/convert/structure_detection.ui @@ -28,7 +28,11 @@ - + + + 20 + + diff --git a/src/calibre/gui2/convert/xexp_edit.ui b/src/calibre/gui2/convert/xexp_edit.ui index 1b0196a8a1..f98eb8b1b8 100644 --- a/src/calibre/gui2/convert/xexp_edit.ui +++ b/src/calibre/gui2/convert/xexp_edit.ui @@ -43,6 +43,15 @@ 0 + + + 500 + 16777215 + + + + 30 + From 924b8a3b3eac3afdf3059dc2a7df7002049bd8a0 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 11 Jul 2010 17:16:03 +0100 Subject: [PATCH 08/19] Remove change to default date, reverting it to year 101 --- src/calibre/utils/date.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/utils/date.py b/src/calibre/utils/date.py index b25940d23d..c5fba13f57 100644 --- a/src/calibre/utils/date.py +++ b/src/calibre/utils/date.py @@ -44,7 +44,7 @@ parse_date_day_first = compute_locale_info_for_parse_date() utc_tz = _utc_tz = tzutc() local_tz = _local_tz = SafeLocalTimeZone() -UNDEFINED_DATE = datetime(1000,1,1, tzinfo=utc_tz) +UNDEFINED_DATE = datetime(101,1,1, tzinfo=utc_tz) def parse_date(date_string, assume_utc=False, as_utc=True, default=None): ''' From acdc3d864a4402dc6df778d1cbc7d8de2da43233 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 12 Jul 2010 10:05:14 +0100 Subject: [PATCH 09/19] Add 'set to today' buttons to metadata editors --- src/calibre/gui2/custom_column_widgets.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 58c2f15452..fed1de93a1 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -10,9 +10,10 @@ from functools import partial from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \ QDate, QGroupBox, QVBoxLayout, QPlainTextEdit, QSizePolicy, \ - QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL + QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL, \ + QPushButton -from calibre.utils.date import qt_to_dt +from calibre.utils.date import qt_to_dt, now from calibre.gui2.widgets import TagsLineEdit, EnComboBox from calibre.gui2 import UNDEFINED_QDATE from calibre.utils.config import tweaks @@ -138,19 +139,24 @@ class DateEdit(QDateEdit): self.setSpecialValueText(_('Undefined')) QDateEdit.focusOutEvent(self, x) + def set_to_today(self): + self.setDate(now()) + class DateTime(Base): def setup_ui(self, parent): - self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), - DateEdit(parent)] + cm = self.col_metadata + self.widgets = [QLabel('&'+cm['name']+':', parent), DateEdit(parent), + QLabel(''), QPushButton(_('Set \'%s\' to today')%cm['name'], parent)] w = self.widgets[1] - format = self.col_metadata['display'].get('date_format','') + format = cm['display'].get('date_format','') if not format: format = 'dd MMM yyyy' w.setDisplayFormat(format) w.setCalendarPopup(True) w.setMinimumDate(UNDEFINED_QDATE) w.setSpecialValueText(_('Undefined')) + self.widgets[3].clicked.connect(w.set_to_today) def setter(self, val): if val is None: From 0e64fff394eef54ae77ce33329fef1a8b7648e7e Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 12 Jul 2010 10:15:43 +0100 Subject: [PATCH 10/19] Put back some of the changes that the merge from trunk removed. --- src/calibre/gui2/custom_column_widgets.py | 2 ++ src/calibre/gui2/library/models.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index 14a1a4abdc..96232fe85f 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -133,9 +133,11 @@ class DateEdit(QDateEdit): def focusInEvent(self, x): self.setSpecialValueText('') + QDateEdit.focusInEvent(self, x) def focusOutEvent(self, x): self.setSpecialValueText(_('Undefined')) + QDateEdit.focusOutEvent(self, x) def set_to_today(self): self.setDate(now()) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index 9f1a72b021..89008735fe 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -1216,7 +1216,9 @@ class DeviceBooksModel(BooksModel): # {{{ return done def set_editable(self, editable): - self.editable = editable + # Cannot edit if metadata is sent on connect. Reason: changes will + # revert to what is in the library on next connect. + self.editable = editable and prefs['manage_device_metadata']!='on_connect' def set_search_restriction(self, s): pass From f2dd86faecc7296caa982479f2459de9709147b7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 Jul 2010 07:56:13 -0600 Subject: [PATCH 11/19] Fix #6152 ("Edit meta information" should be "Edit metadata") --- src/calibre/gui2/layout.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index c44efa2354..1c853cbdff 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -403,7 +403,7 @@ class MainWindowMixin(object): ac('add', _('Add books'), 'add_book.svg', _('A')) ac('del', _('Remove books'), 'trash.svg', _('Del')) - ac('edit', _('Edit meta info'), 'edit_input.svg', _('E')) + ac('edit', _('Edit metadata'), 'edit_input.svg', _('E')) ac('merge', _('Merge book records'), 'merge_books.svg', _('M')) ac('sync', _('Send to device'), 'sync.svg') ac('save', _('Save to disk'), 'save.svg', _('S')) From e16310c70a3ef7548c5dc9aa8ff37af50090bfa5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 Jul 2010 08:08:12 -0600 Subject: [PATCH 12/19] El PAin Impresso by DM --- resources/images/news/elpais_impreso.png | Bin 0 -> 717 bytes resources/recipes/elpais_impreso.recipe | 86 +++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 resources/images/news/elpais_impreso.png create mode 100644 resources/recipes/elpais_impreso.recipe diff --git a/resources/images/news/elpais_impreso.png b/resources/images/news/elpais_impreso.png new file mode 100644 index 0000000000000000000000000000000000000000..35dcaf2d44455f3e97f3985c9df6483463370f5b GIT binary patch literal 717 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Y)RhkE)4%caKYZ?lYt_f1s;*b zK-vS0-A-oPfdtD69Mgd`SU*F|v9*U87#P2Kx;TbdoL)M4zyIYxk)!tO!&q2VS{L4! z)X=07)$*(RXk&tjmxYDoH;<#Op{wKG z`Zryz`dTXL!1(ysR__0Ec1<~1{Q2GHG|oj#7VGX;o~qC~_v410+*!R(HUENZ)3Pj1 z{+!&-*<{qZ)#k0NA%hZwQdh@@9dnd-@8WZK7v0D|)5bdLw&$D9;{Ok;~o&qZPnZ!E0qTk?yesUhbb`wkAJrVbO22?0#!0(j3_mMR^6(OBp9 zO}%3Cmzkf=^UB%Oxi5`zx$+@SN|i%N?)}m%jVGV7YtGaxj99hc)yG9g%C;MsOjxjV zt#Bp_#~vXMjm5_j-o548{@yn6+gGEs2S<$yYbJE3yZsDZ_h7c_k5}g$byF5Bd$4Fv z#0jC-clx>}O1(%z9xt1G$hX|uCeOL~+s3%q}slw#4@vGG@Jv-WiV&D+;Y zhq~@jSaeoRs&D4%^w*3tXZaYL&itqCk=C^0!PdYgtr?H+e9`S{UB4r$$6PpiI>!dx zTTN!h1~OcHWo2Iyo)w%sAoFa^!t1RMF7O2CJ=^|8nz4>qey*ru;cO3mU~*6`ag8WR zNi0dVN-jzTQVd20h6cI@=DLPPAqEy!24+?!Cb~e Date: Mon, 12 Jul 2010 08:15:20 -0600 Subject: [PATCH 13/19] ... --- resources/recipes/ap.recipe | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/resources/recipes/ap.recipe b/resources/recipes/ap.recipe index 572c0aa392..0118cf0726 100644 --- a/resources/recipes/ap.recipe +++ b/resources/recipes/ap.recipe @@ -12,9 +12,9 @@ class AssociatedPress(BasicNewsRecipe): max_articles_per_feed = 15 html2lrf_options = ['--force-page-break-before-tag="chapter"'] - - - preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in + + + preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in [ (r'.*?' , lambda match : ''), (r'.*?', lambda match : ''), @@ -25,10 +25,10 @@ class AssociatedPress(BasicNewsRecipe): (r'

', lambda match : '

'), (r'Learn more about our Privacy Policy.*?', lambda match : ''), ] - ] - + ] + + - feeds = [ ('AP Headlines', 'http://hosted.ap.org/lineups/TOPHEADS-rss_2.0.xml?SITE=ORAST&SECTION=HOME'), ('AP US News', 'http://hosted.ap.org/lineups/USHEADS-rss_2.0.xml?SITE=CAVIC&SECTION=HOME'), ('AP World News', 'http://hosted.ap.org/lineups/WORLDHEADS-rss_2.0.xml?SITE=SCAND&SECTION=HOME'), @@ -38,4 +38,4 @@ class AssociatedPress(BasicNewsRecipe): ('AP Health News', 'http://hosted.ap.org/lineups/HEALTHHEADS-rss_2.0.xml?SITE=FLDAY&SECTION=HOME'), ('AP Science News', 'http://hosted.ap.org/lineups/SCIENCEHEADS-rss_2.0.xml?SITE=OHCIN&SECTION=HOME'), ('AP Strange News', 'http://hosted.ap.org/lineups/STRANGEHEADS-rss_2.0.xml?SITE=WCNC&SECTION=HOME'), - ] \ No newline at end of file + ] From 0b1ac482a5e01cc3cfbd0d8c2ce57cd015499a7d Mon Sep 17 00:00:00 2001 From: GRiker Date: Mon, 12 Jul 2010 09:53:52 -0600 Subject: [PATCH 14/19] GwR sorting key fix for floats --- src/calibre/devices/apple/driver.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 3156542a92..618fc27545 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -2586,14 +2586,20 @@ class ITUNES(DriverBase): if metadata.series and self.settings().read_metadata: if DEBUG: self.log.info(" using Series name as Genre") + + # Format the index as a sort key + index = metadata.series_index + integer = int(index) + fraction = index-integer + series_index = '%04d%s' % (integer, str('%0.4f' % fraction).lstrip('0')) if lb_added: - lb_added.sort_name.set("%s %04f" % (metadata.series, metadata.series_index)) + lb_added.sort_name.set("%s %s" % (metadata.series, series_index)) lb_added.genre.set(metadata.series) lb_added.episode_ID.set(metadata.series) lb_added.episode_number.set(metadata.series_index) if db_added: - db_added.sort_name.set("%s %04f" % (metadata.series, metadata.series_index)) + db_added.sort_name.set("%s %s" % (metadata.series, series_index)) db_added.genre.set(metadata.series) db_added.episode_ID.set(metadata.series) db_added.episode_number.set(metadata.series_index) @@ -2658,8 +2664,13 @@ class ITUNES(DriverBase): if metadata.series and self.settings().read_metadata: if DEBUG: self.log.info(" using Series name as Genre") + # Format the index as a sort key + index = metadata.series_index + integer = int(index) + fraction = index-integer + series_index = '%04d%%s' % (integer, str('%0.4f' % fraction).lstrip('0')) if lb_added: - lb_added.SortName = "%s %04f" % (metadata.series, metadata.series_index) + lb_added.SortName = "%s %s" % (metadata.series, series_index) lb_added.Genre = metadata.series lb_added.EpisodeID = metadata.series try: @@ -2667,7 +2678,7 @@ class ITUNES(DriverBase): except: pass if db_added: - db_added.SortName = "%s %04f" % (metadata.series, metadata.series_index) + db_added.SortName = "%s %s" % (metadata.series, series_index) db_added.Genre = metadata.series db_added.EpisodeID = metadata.series try: From 6a4ff480599ef7aae33abea1aa9e7facffbd2c6e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 12 Jul 2010 16:55:01 -0600 Subject: [PATCH 15/19] Fix #6156 (Error running Calibre on Xen virtual machine with Ubuntu Lucid 10.04) --- src/calibre/devices/scanner.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/scanner.py b/src/calibre/devices/scanner.py index ceba5d37d0..dd789dd668 100644 --- a/src/calibre/devices/scanner.py +++ b/src/calibre/devices/scanner.py @@ -98,6 +98,9 @@ class LinuxScanner(object): def __call__(self): ans = set([]) + if not self.ok: + raise RuntimeError('DeviceScanner requires the /sys filesystem to work.') + for x in os.listdir(self.base): base = os.path.join(self.base, x) ven = os.path.join(base, 'idVendor') @@ -145,8 +148,6 @@ class DeviceScanner(object): def __init__(self, *args): if isosx and osx_scanner is None: raise RuntimeError('The Python extension usbobserver must be available on OS X.') - if islinux and not linux_scanner.ok: - raise RuntimeError('DeviceScanner requires the /sys filesystem to work.') self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner self.devices = [] From c56a4a5aaf6b6f3cd360808381aabfb7c0b6e574 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 13 Jul 2010 12:12:03 -0600 Subject: [PATCH 16/19] Fix #6165 (Error communicating with the device) --- resources/images/donate.svg | 53 ++--- resources/images/lt.png | Bin 0 -> 30594 bytes src/calibre/devices/prs505/sony_cache.py | 25 ++- src/calibre/gui2/device.py | 1 + src/calibre/gui2/dialogs/config/__init__.py | 7 - src/calibre/gui2/dialogs/config/config.ui | 48 ----- src/calibre/gui2/init.py | 6 - src/calibre/gui2/layout.py | 214 +++++++++++-------- src/calibre/gui2/search_restriction_mixin.py | 1 + src/calibre/gui2/ui.py | 9 +- 10 files changed, 169 insertions(+), 195 deletions(-) create mode 100644 resources/images/lt.png diff --git a/resources/images/donate.svg b/resources/images/donate.svg index b17d0ec7a0..603e672f6f 100644 --- a/resources/images/donate.svg +++ b/resources/images/donate.svg @@ -1,24 +1,31 @@ + + sodipodi:docname="donate.svg"> + @@ -180,8 +187,8 @@ inkscape:pageopacity="0.0" inkscape:pageshadow="2" inkscape:zoom="7.851329" - inkscape:cx="92.691163" - inkscape:cy="92.473338" + inkscape:cx="60.937831" + inkscape:cy="61.488995" inkscape:current-layer="layer1" showgrid="false" inkscape:document-units="px" @@ -189,10 +196,11 @@ guidetolerance="0.1px" showguides="true" inkscape:guide-bbox="true" - inkscape:window-width="1106" - inkscape:window-height="958" - inkscape:window-x="597" - inkscape:window-y="25"> + inkscape:window-width="1680" + inkscape:window-height="997" + inkscape:window-x="-4" + inkscape:window-y="30" + inkscape:window-maximized="1"> - - + + + Oz@Z0f2-7z;ux~O9+4z06=<WDR*FRcSTFz- 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 literal 0 HcmV?d00001 diff --git a/src/calibre/devices/prs505/sony_cache.py b/src/calibre/devices/prs505/sony_cache.py index b87ca937bc..3ac35df9b2 100644 --- a/src/calibre/devices/prs505/sony_cache.py +++ b/src/calibre/devices/prs505/sony_cache.py @@ -10,10 +10,10 @@ from base64 import b64decode from uuid import uuid4 from lxml import etree -from calibre import prints, guess_type +from calibre import prints, guess_type, isbytestring from calibre.devices.errors import DeviceError from calibre.devices.usbms.driver import debug_print -from calibre.constants import DEBUG +from calibre.constants import DEBUG, preferred_encoding from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.metadata import authors_to_string, title_sort @@ -473,6 +473,13 @@ class XMLCache(object): # if the case of a tie, and hope it is right. timestamp = os.path.getmtime(path) rec_date = record.get('date', None) + + def clean(x): + if isbytestring(x): + x = x.decode(preferred_encoding, 'replace') + x.replace(u'\0', '') + return x + if not getattr(book, '_new_book', False): # book is not new if strftime(timestamp, zone=time.gmtime) == rec_date: gtz_count += 1 @@ -486,19 +493,19 @@ class XMLCache(object): tz = time.gmtime debug_print("Using GMT TZ for new book", book.lpath) date = strftime(timestamp, zone=tz) - record.set('date', date) + record.set('date', clean(date)) - record.set('size', str(os.stat(path).st_size)) + record.set('size', clean(str(os.stat(path).st_size))) title = book.title if book.title else _('Unknown') - record.set('title', title) + record.set('title', clean(title)) ts = book.title_sort if not ts: ts = title_sort(title) - record.set('titleSorter', ts) + record.set('titleSorter', clean(ts)) if self.use_author_sort and book.author_sort is not None: - record.set('author', book.author_sort) + record.set('author', clean(book.author_sort)) else: - record.set('author', authors_to_string(book.authors)) + record.set('author', clean(authors_to_string(book.authors))) ext = os.path.splitext(path)[1] if ext: ext = ext[1:].lower() @@ -506,7 +513,7 @@ class XMLCache(object): if mime is None: mime = guess_type('a.'+ext)[0] if mime is not None: - record.set('mime', mime) + record.set('mime', clean(mime)) if 'sourceid' not in record.attrib: record.set('sourceid', '1') if 'id' not in record.attrib: diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 91afac8aa2..d81918c307 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -765,6 +765,7 @@ class DeviceMixin(object): # {{{ self.book_details.reset_info() self.location_view.setCurrentIndex(self.location_view.model().index(0)) self.refresh_ondevice_info (device_connected = False) + self.tool_bar.device_status_changed(bool(connected)) def info_read(self, job): ''' diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index bf9dc0a623..b064dc53c2 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -334,7 +334,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): def __init__(self, parent, library_view, server=None): ResizableDialog.__init__(self, parent) - self.ICON_SIZES = {0:QSize(48, 48), 1:QSize(32,32), 2:QSize(24,24)} self._category_model = CategoryModel() self.category_view.currentChanged = self.category_current_changed @@ -389,10 +388,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.add_custcol_button.clicked.connect(self.add_custcol) self.edit_custcol_button.clicked.connect(self.edit_custcol) - icons = config['toolbar_icon_size'] - self.toolbar_button_size.setCurrentIndex(0 if icons == self.ICON_SIZES[0] else 1 if icons == self.ICON_SIZES[1] else 2) - self.show_toolbar_text.setChecked(config['show_text_in_toolbar']) - output_formats = sorted(available_output_formats()) output_formats.remove('oeb') for f in output_formats: @@ -845,8 +840,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): must_restart = self.apply_custom_column_changes() - config['toolbar_icon_size'] = self.ICON_SIZES[self.toolbar_button_size.currentIndex()] - config['show_text_in_toolbar'] = bool(self.show_toolbar_text.isChecked()) config['separate_cover_flow'] = bool(self.separate_cover_flow.isChecked()) config['disable_tray_notification'] = not self.systray_notifications.isChecked() p = {0:'normal', 1:'high', 2:'low'}[self.priority.currentIndex()] diff --git a/src/calibre/gui2/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui index b473ee7846..5f890631b2 100644 --- a/src/calibre/gui2/dialogs/config/config.ui +++ b/src/calibre/gui2/dialogs/config/config.ui @@ -422,54 +422,6 @@ - - - Toolbar - - - - - - - Large - - - - - Medium - - - - - Small - - - - - - - - &Button size in toolbar - - - toolbar_button_size - - - - - - - Show &text in toolbar buttons - - - true - - - - - - - diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 2d038d9ddc..2474685522 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -176,12 +176,6 @@ class ToolbarMixin(object): # {{{ def show_help(self, *args): open_url(QUrl('http://calibre-ebook.com/user_manual')) - def read_toolbar_settings(self): - self.tool_bar.setIconSize(config['toolbar_icon_size']) - self.tool_bar.setToolButtonStyle( - Qt.ToolButtonTextUnderIcon if \ - config['show_text_in_toolbar'] else \ - Qt.ToolButtonIconOnly) # }}} diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 1c853cbdff..0228249e8d 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -5,6 +5,8 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' +from operator import attrgetter + from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ QAbstractListModel, QFont, QApplication, QPalette, pyqtSignal, QToolButton, \ QModelIndex, QListView, QAbstractButton, QPainter, QPixmap, QColor, \ @@ -13,41 +15,11 @@ from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, QVariant, \ from calibre.constants import __appname__, filesystem_encoding from calibre.gui2.search_box import SearchBox2, SavedSearchBox from calibre.gui2.throbber import ThrobbingButton -from calibre.gui2 import NONE +from calibre.gui2 import NONE, config from calibre.gui2.widgets import ComboBoxWithHelp from calibre import human_readable -class ToolBar(QToolBar): # {{{ - - def __init__(self, parent=None): - QToolBar.__init__(self, parent) - self.setContextMenuPolicy(Qt.PreventContextMenu) - self.setMovable(False) - self.setFloatable(False) - self.setOrientation(Qt.Horizontal) - self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) - self.setIconSize(QSize(48, 48)) - self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) - - def add_actions(self, *args): - self.left_space = QWidget(self) - self.left_space.setSizePolicy(QSizePolicy.Expanding, - QSizePolicy.Minimum) - self.addWidget(self.left_space) - for action in args: - if action is None: - self.addSeparator() - else: - self.addAction(action) - self.right_space = QWidget(self) - self.right_space.setSizePolicy(QSizePolicy.Expanding, - QSizePolicy.Minimum) - self.addWidget(self.right_space) - - def contextMenuEvent(self, *args): - pass - -# }}} +ICON_SIZE = 48 # Location View {{{ @@ -191,14 +163,15 @@ class LocationView(QListView): self.setTabKeyNavigation(True) self.setProperty("showDropIndicator", True) self.setSelectionMode(self.SingleSelection) - self.setIconSize(QSize(40, 40)) + self.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) self.setMovement(self.Static) self.setFlow(self.LeftToRight) - self.setGridSize(QSize(175, 90)) + self.setGridSize(QSize(175, ICON_SIZE)) self.setViewMode(self.ListMode) self.setWordWrap(True) self.setObjectName("location_view") - self.setMaximumHeight(74) + self.setMaximumSize(QSize(600, ICON_SIZE+16)) + self.setMinimumWidth(400) def eject_clicked(self, *args): self.unmount_device.emit() @@ -207,6 +180,10 @@ class LocationView(QListView): self.model().count = new_count self.model().reset() + @property + def book_count(self): + return self.model().count + def current_changed(self, current, previous): if current.isValid(): i = current.row() @@ -248,12 +225,15 @@ class EjectButton(QAbstractButton): def __init__(self, parent): QAbstractButton.__init__(self, parent) self.mouse_over = False + self.setMouseTracking(True) def enterEvent(self, event): self.mouse_over = True + QAbstractButton.enterEvent(self, event) def leaveEvent(self, event): self.mouse_over = False + QAbstractButton.leaveEvent(self, event) def paintEvent(self, event): painter = QPainter(self) @@ -344,33 +324,84 @@ class SearchBar(QWidget): # {{{ # }}} -class LocationBar(ToolBar): # {{{ +class ToolBar(QToolBar): # {{{ def __init__(self, actions, donate, location_view, parent=None): - ToolBar.__init__(self, parent) - - for ac in actions: - self.addAction(ac) - - self.addWidget(location_view) - self.w = QWidget() - self.w.setLayout(QVBoxLayout()) - self.w.layout().addWidget(donate) - donate.setAutoRaise(True) - donate.setCursor(Qt.PointingHandCursor) - self.addWidget(self.w) - self.setIconSize(QSize(50, 50)) + QToolBar.__init__(self, parent) + self.setContextMenuPolicy(Qt.PreventContextMenu) + self.setMovable(False) + self.setFloatable(False) + self.setOrientation(Qt.Horizontal) + self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea) + self.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon) - def button_for_action(self, ac): - b = QToolButton(self) - b.setDefaultAction(ac) - for x in ('ToolTip', 'StatusTip', 'WhatsThis'): - getattr(b, 'set'+x)(b.text()) + self.showing_device = False + self.all_actions = actions + self.donate = donate + self.location_view = location_view + self.d_widget = QWidget() + self.d_widget.setLayout(QVBoxLayout()) + self.d_widget.layout().addWidget(donate) + donate.setAutoRaise(True) + donate.setCursor(Qt.PointingHandCursor) + self.build_bar() + + def contextMenuEvent(self, *args): + pass + + def device_status_changed(self, connected): + self.showing_device = connected + self.build_bar() + + def build_bar(self): + order_field = 'device' if self.showing_device else 'normal' + o = attrgetter(order_field+'_order') + sepvals = [2] if self.showing_device else [1] + sepvals += [3] + actions = [x for x in self.all_actions if o(x) > -1] + actions.sort(cmp=lambda x,y : cmp(o(x), o(y))) + self.clear() + for x in actions: + self.addAction(x) + ch = self.widgetForAction(x) + ch.setCursor(Qt.PointingHandCursor) + ch.setAutoRaise(True) + + if x.action_name == 'choose_library': + self.location_action = self.addWidget(self.location_view) + self.choose_action = x + if config['show_donate_button']: + self.addWidget(self.d_widget) + if x.action_name not in ('choose_library', 'help'): + ch.setPopupMode(ch.MenuButtonPopup) + + + for x in actions: + if x.separator_before in sepvals: + self.insertSeparator(x) + + + self.location_action.setVisible(self.showing_device) + self.choose_action.setVisible(not self.showing_device) + + def count_changed(self, new_count): + text = _('%d books')%new_count + a = self.choose_action + a.setText(text) + + def resizeEvent(self, ev): + style = Qt.ToolButtonTextUnderIcon + if self.size().width() < 1260: + style = Qt.ToolButtonIconOnly + self.setToolButtonStyle(style) + QToolBar.resizeEvent(self, ev) - return b # }}} +class Action(QAction): + pass + class MainWindowMixin(object): def __init__(self): @@ -385,12 +416,19 @@ class MainWindowMixin(object): self.centralwidget.setLayout(self._central_widget_layout) self.resize(1012, 740) self.donate_button = ThrobbingButton(self.centralwidget) - self.donate_button.set_normal_icon_size(64, 64) + self.donate_button.set_normal_icon_size(ICON_SIZE, ICON_SIZE) # Actions {{{ - def ac(name, text, icon, shortcut=None, tooltip=None): - action = QAction(QIcon(I(icon)), text, self) + all_actions = [] + + def ac(normal_order, device_order, separator_before, + name, text, icon, shortcut=None, tooltip=None): + action = Action(QIcon(I(icon)), text, self) + action.normal_order = normal_order + action.device_order = device_order + action.separator_before = separator_before + action.action_name = name text = tooltip if tooltip else text action.setToolTip(text) action.setStatusTip(text) @@ -400,56 +438,46 @@ class MainWindowMixin(object): if shortcut: action.setShortcut(shortcut) setattr(self, 'action_'+name, action) + all_actions.append(action) - ac('add', _('Add books'), 'add_book.svg', _('A')) - ac('del', _('Remove books'), 'trash.svg', _('Del')) - ac('edit', _('Edit metadata'), 'edit_input.svg', _('E')) - ac('merge', _('Merge book records'), 'merge_books.svg', _('M')) - ac('sync', _('Send to device'), 'sync.svg') - ac('save', _('Save to disk'), 'save.svg', _('S')) - ac('news', _('Fetch news'), 'news.svg', _('F')) - ac('convert', _('Convert books'), 'convert.svg', _('C')) - ac('view', _('View'), 'view.svg', _('V')) - ac('open_containing_folder', _('Open containing folder'), + ac(0, 7, 0, 'add', _('Add books'), 'add_book.svg', _('A')) + ac(1, 1, 0, 'edit', _('Edit metadata'), 'edit_input.svg', _('E')) + ac(2, 2, 3, 'convert', _('Convert books'), 'convert.svg', _('C')) + ac(3, 3, 0, 'view', _('View'), 'view.svg', _('V')) + ac(4, 4, 3, 'choose_library', _('%d books')%0, 'lt.png', + tooltip=_('Choose calibre library to work with')) + ac(5, 5, 3, 'news', _('Fetch news'), 'news.svg', _('F')) + ac(6, 6, 0, 'save', _('Save to disk'), 'save.svg', _('S')) + ac(7, 0, 0, 'sync', _('Send to device'), 'sync.svg') + ac(8, 8, 3, 'del', _('Remove books'), 'trash.svg', _('Del')) + ac(9, 9, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual")) + ac(10, 10, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P')) + + ac(-1, -1, 0, 'merge', _('Merge book records'), 'merge_books.svg', _('M')) + ac(-1, -1, 0, 'open_containing_folder', _('Open containing folder'), 'document_open.svg') - ac('show_book_details', _('Show book details'), + ac(-1, -1, 0, 'show_book_details', _('Show book details'), 'dialog_information.svg') - ac('books_by_same_author', _('Books by same author'), + ac(-1, -1, 0, 'books_by_same_author', _('Books by same author'), 'user_profile.svg') - ac('books_in_this_series', _('Books in this series'), + ac(-1, -1, 0, 'books_in_this_series', _('Books in this series'), 'books_in_series.svg') - ac('books_by_this_publisher', _('Books by this publisher'), + ac(-1, -1, 0, 'books_by_this_publisher', _('Books by this publisher'), 'publisher.png') - ac('books_with_the_same_tags', _('Books with the same tags'), + ac(-1, -1, 0, 'books_with_the_same_tags', _('Books with the same tags'), 'tags.svg') - ac('preferences', _('Preferences'), 'config.svg', _('Ctrl+P')) - ac('help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual")) # }}} - self.tool_bar = ToolBar(self) - self.addToolBar(Qt.BottomToolBarArea, self.tool_bar) - self.tool_bar.add_actions(self.action_convert, self.action_view, - None, self.action_edit, None, - self.action_save, self.action_del, - None, - self.action_help, None, self.action_preferences) - self.location_view = LocationView(self.centralwidget) self.search_bar = SearchBar(self) - self.location_bar = LocationBar([self.action_add, self.action_sync, - self.action_news], self.donate_button, self.location_view, self) - self.addToolBar(Qt.TopToolBarArea, self.location_bar) + self.tool_bar = ToolBar(all_actions, self.donate_button, self.location_view, self) + self.addToolBar(Qt.TopToolBarArea, self.tool_bar) l = self.centralwidget.layout() l.addWidget(self.search_bar) - for ch in list(self.tool_bar.children()) + list(self.location_bar.children()): - if isinstance(ch, QToolButton): - ch.setCursor(Qt.PointingHandCursor) - ch.setAutoRaise(True) - if ch is not self.donate_button: - ch.setPopupMode(ch.MenuButtonPopup) - + def read_toolbar_settings(self): + pass diff --git a/src/calibre/gui2/search_restriction_mixin.py b/src/calibre/gui2/search_restriction_mixin.py index f677c839d8..a4186ad8d1 100644 --- a/src/calibre/gui2/search_restriction_mixin.py +++ b/src/calibre/gui2/search_restriction_mixin.py @@ -13,6 +13,7 @@ class SearchRestrictionMixin(object): self.search_restriction.setSizeAdjustPolicy(self.search_restriction.AdjustToMinimumContentsLengthWithIcon) self.search_restriction.setMinimumContentsLength(10) self.search_restriction.setStatusTip(self.search_restriction.toolTip()) + self.search_count.setText(_("(all books)")) ''' Adding and deleting books while restricted creates a complexity. When added, diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 6bd7b2b502..ba4c637932 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -167,8 +167,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ self.eject_action = self.system_tray_menu.addAction( QIcon(I('eject.svg')), _('&Eject connected device')) self.eject_action.setEnabled(False) - if not config['show_donate_button']: - self.donate_button.setVisible(False) self.addAction(self.quit_action) self.action_restart = QAction(_('&Restart'), self) self.addAction(self.action_restart) @@ -220,8 +218,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ if self.system_tray_icon.isVisible() and opts.start_in_tray: self.hide_windows() - self.library_view.model().count_changed_signal.connect \ - (self.location_view.count_changed) + for t in (self.location_view, self.tool_bar): + self.library_view.model().count_changed_signal.connect \ + (t.count_changed) if not gprefs.get('quick_start_guide_added', False): from calibre.ebooks.metadata import MetaInformation mi = MetaInformation(_('Calibre Quick Start Guide'), ['John Schember']) @@ -274,8 +273,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, ToolbarMixin, # {{{ SIGNAL('start_recipe_fetch(PyQt_PyObject)'), self.download_scheduled_recipe, Qt.QueuedConnection) - self.location_view.setCurrentIndex(self.location_view.model().index(0)) - self.keyboard_interrupt.connect(self.quit, type=Qt.QueuedConnection) AddAction.__init__(self) From 9a4b661ac67c14c1b1aa6e576ea804f68cccc114 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 14 Jul 2010 08:57:56 -0600 Subject: [PATCH 17/19] Fix google reader recipes --- resources/recipes/greader.recipe | 72 +++++++++++++------------- resources/recipes/greader_uber.recipe | 73 +++++++++++++-------------- 2 files changed, 70 insertions(+), 75 deletions(-) diff --git a/resources/recipes/greader.recipe b/resources/recipes/greader.recipe index cbf4c0226b..75c273b162 100644 --- a/resources/recipes/greader.recipe +++ b/resources/recipes/greader.recipe @@ -1,37 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReader(BasicNewsRecipe): - title = 'Google Reader' - description = 'This recipe downloads feeds you have tagged from your Google Reader account.' - needs_subscription = True - __author__ = 'davec' - base_url = 'http://www.google.com/reader/atom/' - max_articles_per_feed = 50 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser() - - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - sid = re.search('SID=(\S*)', response.read()).group(1) - - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - cookies.set_cookie(mechanize.Cookie(None, 'SID', sid, None, False, '.google.com', True, True, '/', True, False, None, True, '', '', None)) - return br - - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0] - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReader(BasicNewsRecipe): + title = 'Google Reader' + description = 'This recipe fetches from your Google Reader account unread Starred items and unread Feeds you have placed in a folder via the manage subscriptions feature.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0] + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds diff --git a/resources/recipes/greader_uber.recipe b/resources/recipes/greader_uber.recipe index ee48e7069d..c98762fe28 100644 --- a/resources/recipes/greader_uber.recipe +++ b/resources/recipes/greader_uber.recipe @@ -1,38 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReaderUber(BasicNewsRecipe): - title = 'Google Reader Uber' - description = 'This recipe downloads all unread feedsfrom your Google Reader account.' - needs_subscription = True - __author__ = 'rollercoaster, davec' - base_url = 'http://www.google.com/reader/atom/' - oldest_article = 365 - max_articles_per_feed = 250 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser() - - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - sid = re.search('SID=(\S*)', response.read()).group(1) - - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - cookies.set_cookie(mechanize.Cookie(None, 'SID', sid, None, False, '.google.com', True, True, '/', True, False, None, True, '', '', None)) - return br - - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0].replace('broadcast','reading-list') - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReaderUber(BasicNewsRecipe): + title = 'Google Reader uber' + description = 'Fetches all feeds from your Google Reader account including the uncategorized items.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0].replace('broadcast','reading-list') + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds From 8a5a5e2ad13d1285afb6f9ed6a6d58787ddbb7e0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 14 Jul 2010 09:01:01 -0600 Subject: [PATCH 18/19] Waco Tribune Herald by rty --- resources/recipes/greader.recipe | 70 +++++++++++++-------------- resources/recipes/greader_uber.recipe | 70 +++++++++++++-------------- resources/recipes/waco_tribune.recipe | 34 +++++++++++++ 3 files changed, 104 insertions(+), 70 deletions(-) create mode 100644 resources/recipes/waco_tribune.recipe diff --git a/resources/recipes/greader.recipe b/resources/recipes/greader.recipe index 75c273b162..2c9d5aa015 100644 --- a/resources/recipes/greader.recipe +++ b/resources/recipes/greader.recipe @@ -1,35 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReader(BasicNewsRecipe): - title = 'Google Reader' - description = 'This recipe fetches from your Google Reader account unread Starred items and unread Feeds you have placed in a folder via the manage subscriptions feature.' - needs_subscription = True - __author__ = 'davec, rollercoaster, Starson17' - base_url = 'http://www.google.com/reader/atom/' - oldest_article = 365 - max_articles_per_feed = 250 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser(self) - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - auth = re.search('Auth=(\S*)', response.read()).group(1) - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] - return br - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0] - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReader(BasicNewsRecipe): + title = 'Google Reader' + description = 'This recipe fetches from your Google Reader account unread Starred items and unread Feeds you have placed in a folder via the manage subscriptions feature.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0] + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds diff --git a/resources/recipes/greader_uber.recipe b/resources/recipes/greader_uber.recipe index c98762fe28..5e02cdef5d 100644 --- a/resources/recipes/greader_uber.recipe +++ b/resources/recipes/greader_uber.recipe @@ -1,35 +1,35 @@ -import urllib, re, mechanize -from calibre.web.feeds.recipes import BasicNewsRecipe -from calibre import __appname__ - -class GoogleReaderUber(BasicNewsRecipe): - title = 'Google Reader uber' - description = 'Fetches all feeds from your Google Reader account including the uncategorized items.' - needs_subscription = True - __author__ = 'davec, rollercoaster, Starson17' - base_url = 'http://www.google.com/reader/atom/' - oldest_article = 365 - max_articles_per_feed = 250 - get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed - use_embedded_content = True - - def get_browser(self): - br = BasicNewsRecipe.get_browser(self) - if self.username is not None and self.password is not None: - request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), - ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) - response = br.open('https://www.google.com/accounts/ClientLogin', request) - auth = re.search('Auth=(\S*)', response.read()).group(1) - cookies = mechanize.CookieJar() - br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) - br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] - return br - - def get_feeds(self): - feeds = [] - soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') - for id in soup.findAll(True, attrs={'name':['id']}): - url = id.contents[0].replace('broadcast','reading-list') - feeds.append((re.search('/([^/]*)$', url).group(1), - self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) - return feeds +import urllib, re, mechanize +from calibre.web.feeds.recipes import BasicNewsRecipe +from calibre import __appname__ + +class GoogleReaderUber(BasicNewsRecipe): + title = 'Google Reader uber' + description = 'Fetches all feeds from your Google Reader account including the uncategorized items.' + needs_subscription = True + __author__ = 'davec, rollercoaster, Starson17' + base_url = 'http://www.google.com/reader/atom/' + oldest_article = 365 + max_articles_per_feed = 250 + get_options = '?n=%d&xt=user/-/state/com.google/read' % max_articles_per_feed + use_embedded_content = True + + def get_browser(self): + br = BasicNewsRecipe.get_browser(self) + if self.username is not None and self.password is not None: + request = urllib.urlencode([('Email', self.username), ('Passwd', self.password), + ('service', 'reader'), ('accountType', 'HOSTED_OR_GOOGLE'), ('source', __appname__)]) + response = br.open('https://www.google.com/accounts/ClientLogin', request) + auth = re.search('Auth=(\S*)', response.read()).group(1) + cookies = mechanize.CookieJar() + br = mechanize.build_opener(mechanize.HTTPCookieProcessor(cookies)) + br.addheaders = [('Authorization', 'GoogleLogin auth='+auth)] + return br + + def get_feeds(self): + feeds = [] + soup = self.index_to_soup('http://www.google.com/reader/api/0/tag/list') + for id in soup.findAll(True, attrs={'name':['id']}): + url = id.contents[0].replace('broadcast','reading-list') + feeds.append((re.search('/([^/]*)$', url).group(1), + self.base_url + urllib.quote(url.encode('utf-8')) + self.get_options)) + return feeds diff --git a/resources/recipes/waco_tribune.recipe b/resources/recipes/waco_tribune.recipe new file mode 100644 index 0000000000..18eb61fb26 --- /dev/null +++ b/resources/recipes/waco_tribune.recipe @@ -0,0 +1,34 @@ +from calibre.web.feeds.news import BasicNewsRecipe + +class AdvancedUserRecipe1278773519(BasicNewsRecipe): + title = u'Waco Tribune Herald' + __author__ = 'rty' + pubisher = 'A Robinson Media Company' + description = 'Waco, Texas, Newspaper' + category = 'News, Texas, Waco' + oldest_article = 7 + max_articles_per_feed = 100 + + feeds = [ + (u'News', u'http://www.wacotrib.com/news/index.rss2'), + (u'Sports', u'http://www.wacotrib.com/sports/index.rss2'), + (u'AccessWaco', u'http://www.wacotrib.com/accesswaco/index.rss2'), + (u'Opinions', u'http://www.wacotrib.com/opinion/index.rss2') + ] + + remove_javascript = True + use_embedded_content = False + no_stylesheets = True + language = 'en' + encoding = 'utf-8' + conversion_options = {'linearize_tables':True} + masthead_url = 'http://media.wacotrib.com/designimages/wacotrib_logo.jpg' + keep_only_tags = [ + dict(name='div', attrs={'class':'twoColumn left'}), + ] + remove_tags = [ + dict(name='div', attrs={'class':'right blueLinks'}), + ] + remove_tags_after = [ + dict(name='div', attrs={'class':'dottedRule'}), + ] From 01d5dcff6fbeeb1bb24bce807962743cdf8d9e9c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 14 Jul 2010 09:03:20 -0600 Subject: [PATCH 19/19] Support for another variant of the Samsung Galaxy --- src/calibre/devices/android/driver.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/devices/android/driver.py b/src/calibre/devices/android/driver.py index 9951dc57ad..5d9d094b26 100644 --- a/src/calibre/devices/android/driver.py +++ b/src/calibre/devices/android/driver.py @@ -30,7 +30,8 @@ class ANDROID(USBMS): 0x18d1 : { 0x4e11 : [0x0100, 0x226], 0x4e12: [0x0100, 0x226]}, # Samsung - 0x04e8 : { 0x681d : [0x0222, 0x0400], 0x681c : [0x0222, 0x0224]}, + 0x04e8 : { 0x681d : [0x0222, 0x0400], + 0x681c : [0x0222, 0x0224, 0x0400]}, # Acer 0x502 : { 0x3203 : [0x0100]},