From d40c4576fa602329d4233b7a69a44f66529eea40 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 7 May 2011 10:50:36 +0100 Subject: [PATCH 01/23] Add two buttons: manage authors and paste isbn --- src/calibre/gui2/metadata/basic_widgets.py | 8 +++- src/calibre/gui2/metadata/single.py | 48 ++++++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index 00fa0e26e8..b2c5d471ae 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -10,7 +10,7 @@ __docformat__ = 'restructuredtext en' import textwrap, re, os from PyQt4.Qt import (Qt, QDateEdit, QDate, pyqtSignal, QMessageBox, - QIcon, QToolButton, QWidget, QLabel, QGridLayout, + QIcon, QToolButton, QWidget, QLabel, QGridLayout, QApplication, QDoubleSpinBox, QListWidgetItem, QSize, QPixmap, QPushButton, QSpinBox, QLineEdit, QSizePolicy) @@ -1037,6 +1037,12 @@ class IdentifiersEdit(QLineEdit): # {{{ self.setToolTip(tt+extra) self.setStyleSheet('QLineEdit { background-color: %s }'%col) + def paste_isbn(self): + cb = QApplication.clipboard() + vals = self.current_val + vals['isbn'] = cb.text() + self.current_val = vals + # }}} class PublisherEdit(MultiCompleteComboBox): # {{{ diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index f0a05d171f..0df2014438 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -125,6 +125,13 @@ class MetadataSingleDialogBase(ResizableDialog): 'Swap the author and title')) self.swap_title_author_button.clicked.connect(self.swap_title_author) + self.manage_authors_button = QToolButton(self) + self.manage_authors_button.setIcon(QIcon(I('user_profile.png'))) + self.manage_authors_button.setToolTip('

' + _( + 'Manage authors. Use to rename authors and correct ' + 'individual author\'s sort values') + '

') + self.manage_authors_button.clicked.connect(self.authors.manage_authors) + self.series = SeriesEdit(self) self.remove_unused_series_button = QToolButton(self) self.remove_unused_series_button.setToolTip( @@ -161,6 +168,12 @@ class MetadataSingleDialogBase(ResizableDialog): self.clear_identifiers_button = QToolButton(self) self.clear_identifiers_button.setIcon(QIcon(I('trash.png'))) self.clear_identifiers_button.clicked.connect(self.identifiers.clear) + self.paste_isbn_button = QToolButton(self) + self.paste_isbn_button.setToolTip('

' + + _('Paste the contents of the clipboard into the ' + 'identifiers box prefixed with isbn:') + '

') + self.paste_isbn_button.setIcon(QIcon(I('edit-paste.png'))) + self.paste_isbn_button.clicked.connect(self.identifiers.paste_isbn) self.publisher = PublisherEdit(self) self.basic_metadata_widgets.append(self.publisher) @@ -499,7 +512,8 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ sto(one, two) sto(two, three) - tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) + tl.addWidget(self.swap_title_author_button, 0, 0, 1, 1) + tl.addWidget(self.manage_authors_button, 1, 0, 1, 1) create_row(0, self.title, self.deduce_title_sort_button, self.title_sort) sto(self.title_sort, self.authors) @@ -508,6 +522,7 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ create_row(2, self.series, self.remove_unused_series_button, self.series_index, icon='trash.png') sto(self.series_index, self.swap_title_author_button) + sto(self.swap_title_author_button, self.manage_authors_button) tl.addWidget(self.formats_manager, 0, 6, 3, 1) @@ -518,7 +533,7 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ self.tabs[0].gb = gb = QGroupBox(_('Change cover'), self) gb.l = l = QGridLayout() gb.setLayout(l) - sto(self.swap_title_author_button, self.cover.buttons[0]) + sto(self.manage_authors_button, self.cover.buttons[0]) for i, b in enumerate(self.cover.buttons[:3]): l.addWidget(b, 0, i, 1, 1) sto(b, self.cover.buttons[i+1]) @@ -532,10 +547,16 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ w.setLayout(w.l) l.setMargin(0) self.splitter.addWidget(w) - def create_row2(row, widget, button=None): + def create_row2(row, widget, button=None, front_button=None): row += 1 ql = BuddyLabel(widget) - l.addWidget(ql, row, 0, 1, 1) + if front_button: + ltl = QHBoxLayout() + ltl.addWidget(front_button) + ltl.addWidget(ql) + l.addLayout(ltl, row, 0, 1, 1) + else: + l.addWidget(ql, row, 0, 1, 1) l.addWidget(widget, row, 1, 1, 2 if button is None else 1) if button is not None: l.addWidget(button, row, 2, 1, 1) @@ -550,8 +571,10 @@ class MetadataSingleDialog(MetadataSingleDialogBase): # {{{ create_row2(1, self.rating) sto(self.rating, self.tags) create_row2(2, self.tags, self.tags_editor_button) - sto(self.tags_editor_button, self.identifiers) - create_row2(3, self.identifiers, self.clear_identifiers_button) + sto(self.tags_editor_button, self.paste_isbn_button) + sto(self.paste_isbn_button, self.identifiers) + create_row2(3, self.identifiers, self.clear_identifiers_button, + front_button=self.paste_isbn_button) sto(self.clear_identifiers_button, self.timestamp) create_row2(4, self.timestamp, self.timestamp.clear_button) sto(self.timestamp.clear_button, self.pubdate) @@ -648,6 +671,8 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ sto(widget, tab_to) tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) + tl.addWidget(self.manage_authors_button, 2, 0, 1, 1) + tl.addWidget(self.paste_isbn_button, 11, 0, 1, 1) create_row(0, self.title, self.title_sort, button=self.deduce_title_sort_button, span=2, @@ -669,6 +694,9 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ button=self.timestamp.clear_button, icon='trash.png') create_row(11, self.identifiers, self.comments, button=self.clear_identifiers_button, icon='trash.png') + sto(self.clear_identifiers_button, self.swap_title_author_button) + sto(self.swap_title_author_button, self.manage_authors_button) + sto(self.manage_authors_button, self.paste_isbn_button) tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), 12, 1, 1 ,1) @@ -708,7 +736,6 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ gb = QGroupBox(_('Change cover'), tab1) l = QGridLayout() gb.setLayout(l) - sto(self.swap_title_author_button, self.cover.buttons[0]) for i, b in enumerate(self.cover.buttons[:3]): l.addWidget(b, 0, i, 1, 1) sto(b, self.cover.buttons[i+1]) @@ -780,6 +807,8 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{ sto(widget, tab_to) tl.addWidget(self.swap_title_author_button, 0, 0, 2, 1) + tl.addWidget(self.manage_authors_button, 2, 0, 2, 1) + tl.addWidget(self.paste_isbn_button, 11, 0, 1, 1) create_row(0, self.title, self.title_sort, button=self.deduce_title_sort_button, span=2, @@ -801,6 +830,9 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{ button=self.timestamp.clear_button, icon='trash.png') create_row(11, self.identifiers, self.comments, button=self.clear_identifiers_button, icon='trash.png') + sto(self.clear_identifiers_button, self.swap_title_author_button) + sto(self.swap_title_author_button, self.manage_authors_button) + sto(self.manage_authors_button, self.paste_isbn_button) tl.addItem(QSpacerItem(1, 1, QSizePolicy.Fixed, QSizePolicy.Expanding), 12, 1, 1 ,1) @@ -842,7 +874,7 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{ lb = QGridLayout() gb.setLayout(lb) lb.addWidget(self.cover, 0, 0, 1, 3, alignment=Qt.AlignCenter) - sto(self.clear_identifiers_button, self.cover.buttons[0]) + sto(self.manage_authors_button, self.cover.buttons[0]) for i, b in enumerate(self.cover.buttons[:3]): lb.addWidget(b, 1, i, 1, 1) sto(b, self.cover.buttons[i+1]) From 200477bef67b542c9c757c4e0eb6df2921e93dcf Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 7 May 2011 12:10:29 +0100 Subject: [PATCH 02/23] Fix the user device to not use 0x0000 as a default ID. --- src/calibre/devices/user_defined/driver.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/calibre/devices/user_defined/driver.py b/src/calibre/devices/user_defined/driver.py index c496422255..d613a09508 100644 --- a/src/calibre/devices/user_defined/driver.py +++ b/src/calibre/devices/user_defined/driver.py @@ -68,9 +68,9 @@ class USER_DEFINED(USBMS): 'is prepended to any send_to_device template') + '

', ] EXTRA_CUSTOMIZATION_DEFAULT = [ - '0x0000', - '0x0000', - '0x0000', + '0xffff', + '0xffff', + '0xffff', None, '', '', From d58cf62ffc9168e99328b096109466a4a7ee56a1 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 7 May 2011 08:44:39 -0600 Subject: [PATCH 03/23] Added *.DS_Store to .bzrignore --- .bzrignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.bzrignore b/.bzrignore index a071a4f2f6..aa6637bc7a 100644 --- a/.bzrignore +++ b/.bzrignore @@ -30,3 +30,4 @@ nbproject/ .project .pydevproject .settings/ +*.DS_Store From 3ea425cc86d20837ba5b0a40c8918f18dd5de109 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 7 May 2011 17:30:52 +0100 Subject: [PATCH 04/23] Move the metadata download buttons on the Alt1 and Alt2 layouts. Change the size policy on the custom metadata group box to permit expansion. --- src/calibre/gui2/metadata/single.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index 0df2014438..84f0405fc6 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -69,7 +69,9 @@ class MetadataSingleDialogBase(ResizableDialog): self.setLayout(self.l) self.l.setMargin(0) self.l.addWidget(self.scroll_area) - self.l.addWidget(self.button_box) + ll = self.button_box_layout = QHBoxLayout() + self.l.addLayout(ll) + ll.addWidget(self.button_box) self.setWindowIcon(QIcon(I('edit_input.png'))) self.setWindowTitle(_('Edit Metadata')) @@ -647,13 +649,13 @@ class MetadataSingleDialogAlt1(MetadataSingleDialogBase): # {{{ self.tabs[0].l.addWidget(gb, 0, 0, 1, 1) gb.setLayout(tl) - self.button_box.addButton(self.fetch_metadata_button, - QDialogButtonBox.ActionRole) + self.button_box_layout.insertWidget(0, self.fetch_metadata_button) self.config_metadata_button.setToolButtonStyle(Qt.ToolButtonTextOnly) self.config_metadata_button.setText(_('Configure metadata downloading')) - self.button_box.addButton(self.config_metadata_button, - QDialogButtonBox.ActionRole) - sto(self.button_box, self.title) + self.button_box_layout.insertWidget(1, self.config_metadata_button) + sto(self.button_box, self.fetch_metadata_button) + sto(self.fetch_metadata_button, self.config_metadata_button) + sto(self.config_metadata_button, self.title) def create_row(row, widget, tab_to, button=None, icon=None, span=1): ql = BuddyLabel(widget) @@ -783,13 +785,13 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{ l.addWidget(gb, 0, 0, 1, 1) gb.setLayout(tl) - self.button_box.addButton(self.fetch_metadata_button, - QDialogButtonBox.ActionRole) + self.button_box_layout.insertWidget(0, self.fetch_metadata_button) self.config_metadata_button.setToolButtonStyle(Qt.ToolButtonTextOnly) self.config_metadata_button.setText(_('Configure metadata downloading')) - self.button_box.addButton(self.config_metadata_button, - QDialogButtonBox.ActionRole) - sto(self.button_box, self.title) + self.button_box_layout.insertWidget(1, self.config_metadata_button) + sto(self.button_box, self.fetch_metadata_button) + sto(self.fetch_metadata_button, self.config_metadata_button) + sto(self.config_metadata_button, self.title) def create_row(row, widget, tab_to, button=None, icon=None, span=1): ql = BuddyLabel(widget) @@ -852,7 +854,7 @@ class MetadataSingleDialogAlt2(MetadataSingleDialogBase): # {{{ l.addWidget(gb, 0, 1, 1, 1) sp = QSizePolicy() sp.setVerticalStretch(10) - sp.setHorizontalPolicy(QSizePolicy.Fixed) + sp.setHorizontalPolicy(QSizePolicy.Minimum) sp.setVerticalPolicy(QSizePolicy.Expanding) gb.setSizePolicy(sp) self.set_custom_metadata_tab_order() From 3eea169827680e6759e6dfb4e2ed58c43fccd80b Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 7 May 2011 11:20:59 -0600 Subject: [PATCH 05/23] Added title_sort output option to CSV and XML catalog formats --- src/calibre/customize/__init__.py | 2 +- src/calibre/library/catalog.py | 16 ++++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index ddd713d4ce..6118e5583b 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -449,7 +449,7 @@ class CatalogPlugin(Plugin): # {{{ ['author_sort','authors','comments','cover','formats', 'id','isbn','ondevice','pubdate','publisher','rating', 'series_index','series','size','tags','timestamp', - 'title','uuid']) + 'title_sort','title','uuid']) all_custom_fields = set(db.custom_field_keys()) all_fields = all_std_fields.union(all_custom_fields) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 8c1f35f579..05752f4a29 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -16,6 +16,7 @@ from calibre.customize import CatalogPlugin from calibre.customize.conversion import OptionRecommendation, DummyReporter from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, Tag, NavigableString from calibre.ebooks.chardet import substitute_entites +from calibre.ebooks.metadata import title_sort as _title_sort from calibre.library.save_to_disk import preprocess_template from calibre.ptempfile import PersistentTemporaryDirectory from calibre.utils.bibtex import BibTeX @@ -27,7 +28,7 @@ from calibre.utils.logging import default_log as log from calibre.utils.magick.draw import thumbnail from calibre.utils.zipfile import ZipFile, ZipInfo -FIELDS = ['all', 'title', 'author_sort', 'authors', 'comments', +FIELDS = ['all', 'title', 'title_sort', 'author_sort', 'authors', 'comments', 'cover', 'formats','id', 'isbn', 'ondevice', 'pubdate', 'publisher', 'rating', 'series_index', 'series', 'size', 'tags', 'timestamp', 'uuid'] @@ -66,7 +67,7 @@ class CSV_XML(CatalogPlugin): # {{{ dest = 'sort_by', action = None, help = _('Output field to sort on.\n' - 'Available fields: author_sort, id, rating, size, timestamp, title.\n' + 'Available fields: author_sort, id, rating, size, timestamp, title_sort\n' "Default: '%default'\n" "Applies to: CSV, XML output formats"))] @@ -76,7 +77,7 @@ class CSV_XML(CatalogPlugin): # {{{ if opts.verbose: opts_dict = vars(opts) - log("%s(): Generating %s" % (self.name,self.fmt)) + log("%s(): Generating %s" % (self.name,self.fmt.upper())) if opts.connected_device['is_device_connected']: log(" connected_device: %s" % opts.connected_device['name']) if opts_dict['search_text']: @@ -126,8 +127,11 @@ class CSV_XML(CatalogPlugin): # {{{ for field in fields: if field.startswith('#'): item = db.get_field(entry['id'],field,index_is_id=True) + elif field == 'title_sort': + item = _title_sort(unicode(entry['title'])) else: item = entry[field] + if item is None: outstr.append('""') continue @@ -167,7 +171,7 @@ class CSV_XML(CatalogPlugin): # {{{ item = getattr(E, field.replace('#','_'))(val) record.append(item) - for field in ('id', 'uuid', 'title', 'publisher', 'rating', 'size', + for field in ('id', 'uuid', 'publisher', 'rating', 'size', 'isbn','ondevice'): if field in fields: val = r[field] @@ -178,6 +182,10 @@ class CSV_XML(CatalogPlugin): # {{{ item = getattr(E, field)(val) record.append(item) + if 'title' in fields: + title = E.title(r['title'], sort=_title_sort(unicode(r['title']))) + record.append(title) + if 'authors' in fields: aus = E.authors(sort=r['author_sort']) for au in r['authors']: From 493d1a2ed2d90ce7bebaffd866a94d6202d2e855 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 7 May 2011 18:48:48 +0100 Subject: [PATCH 06/23] Deal with case of very long custom column names by word-wrapping the label. --- src/calibre/gui2/custom_column_widgets.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index d1acd2ed83..c94913ea2c 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -439,6 +439,7 @@ def populate_metadata_page(layout, db, book_id, bulk=False, two_column=False, pa w = widget_factory(dt, col) ans.append(w) for c in range(0, len(w.widgets), 2): + w.widgets[c].setWordWrap(True) w.widgets[c].setBuddy(w.widgets[c+1]) layout.addWidget(w.widgets[c], row, column) layout.addWidget(w.widgets[c+1], row, column+1) From 6f1d668a66cd3c56a837752cf5642c4a595da3f2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 7 May 2011 12:45:18 -0600 Subject: [PATCH 07/23] Korea Herald by Seongkyoun Yoo. Fixes #779153 (add news recipe for korean) --- recipes/korea_herald.recipe | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 recipes/korea_herald.recipe diff --git a/recipes/korea_herald.recipe b/recipes/korea_herald.recipe new file mode 100644 index 0000000000..829906338c --- /dev/null +++ b/recipes/korea_herald.recipe @@ -0,0 +1,36 @@ +__license__ = 'GPL v3' +__copyright__ = '2011, Seongkyoun Yoo ' +''' +Profile to download KoreaHerald +''' +from calibre.web.feeds.news import BasicNewsRecipe + +class KoreaHerald(BasicNewsRecipe): + title = u'KoreaHerald' + language = 'en' + description = u'Korea Herald News articles' + __author__ = 'Seongkyoun Yoo' + oldest_article = 10 + recursions = 3 + max_articles_per_feed = 10 + no_stylesheets = True + keep_only_tags = [ + dict(id=['contentLeft', '_article']) + ] + + remove_tags = [ + dict(name='iframe'), + dict(name='div', attrs={'class':['left','htit2', 'navigation','banner_txt','banner_img']}), + dict(name='ul', attrs={'class':['link_icon', 'flow_icon','detailTextAD110113']}), + ] + + feeds = [ + ('All News','http://www.koreaherald.com/rss/020000000000.xml'), + ('National','http://www.koreaherald.com/rss/020100000000.xml'), + ('Business','http://www.koreaherald.com/rss/020200000000.xml'), + ('Life&Style','http://www.koreaherald.com/rss/020300000000.xml'), + ('Entertainment','http://www.koreaherald.com/rss/020400000000.xml'), + ('Sports','http://www.koreaherald.com/rss/020500000000.xml'), + ('Opinion','http://www.koreaherald.com/rss/020600000000.xml'), + ('English Cafe','http://www.koreaherald.com/rss/021000000000.xml'), + ] From a361a349ca91123e4fb592e823c4e2f5fc6e9495 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 7 May 2011 14:15:25 -0600 Subject: [PATCH 08/23] GwR revision to fetch title_sort from data_dict --- src/calibre/library/catalog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 05752f4a29..7a4ea1b0e1 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -128,7 +128,7 @@ class CSV_XML(CatalogPlugin): # {{{ if field.startswith('#'): item = db.get_field(entry['id'],field,index_is_id=True) elif field == 'title_sort': - item = _title_sort(unicode(entry['title'])) + item = entry['sort'] else: item = entry[field] @@ -183,7 +183,7 @@ class CSV_XML(CatalogPlugin): # {{{ record.append(item) if 'title' in fields: - title = E.title(r['title'], sort=_title_sort(unicode(r['title']))) + title = E.title(r['title'], sort=r['sort']) record.append(title) if 'authors' in fields: From 1dc466a15eafd21952a21bf0c30e0250a5d63713 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sat, 7 May 2011 14:18:10 -0600 Subject: [PATCH 09/23] Removed unnecessary import of title_sort --- src/calibre/library/catalog.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 7a4ea1b0e1..a19534191b 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -16,7 +16,6 @@ from calibre.customize import CatalogPlugin from calibre.customize.conversion import OptionRecommendation, DummyReporter from calibre.ebooks.BeautifulSoup import BeautifulSoup, BeautifulStoneSoup, Tag, NavigableString from calibre.ebooks.chardet import substitute_entites -from calibre.ebooks.metadata import title_sort as _title_sort from calibre.library.save_to_disk import preprocess_template from calibre.ptempfile import PersistentTemporaryDirectory from calibre.utils.bibtex import BibTeX From edfb376516acd3b58097cf21571a3af9a0957a1c Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sun, 8 May 2011 09:51:46 +0100 Subject: [PATCH 10/23] Change titlecase to use 'Mac' processing only when language is a variant of English --- src/calibre/utils/titlecase.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/calibre/utils/titlecase.py b/src/calibre/utils/titlecase.py index 1f153dd5fe..285f55f22d 100755 --- a/src/calibre/utils/titlecase.py +++ b/src/calibre/utils/titlecase.py @@ -10,6 +10,7 @@ License: http://www.opensource.org/licenses/mit-license.php import re from calibre.utils.icu import capitalize +from calibre.utils.config import prefs __all__ = ['titlecase'] __version__ = '0.5' @@ -67,11 +68,12 @@ def titlecase(text): line.append(icu_lower(word)) continue - match = MAC_MC.match(word) - if match and not match.group(2)[:3] in ('hin', 'ht'): - line.append("%s%s" % (capitalize(match.group(1)), - capitalize(match.group(2)))) - continue + if prefs['language'].startswith('en'): + match = MAC_MC.match(word) + if match and not match.group(2)[:3] in ('hin', 'ht'): + line.append("%s%s" % (capitalize(match.group(1)), + capitalize(match.group(2)))) + continue hyphenated = [] for item in word.split('-'): From 534ba212196b5141b8d6ecad49f6b22995522dc2 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sun, 8 May 2011 03:33:31 -0600 Subject: [PATCH 11/23] Added genesis() to GenerateCatalogAction, enabling catalog builder to be launched from toolbar --- src/calibre/gui2/actions/catalog.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py index 093985d041..45544d8246 100644 --- a/src/calibre/gui2/actions/catalog.py +++ b/src/calibre/gui2/actions/catalog.py @@ -20,6 +20,9 @@ class GenerateCatalogAction(InterfaceAction): action_spec = (_('Create a catalog of the books in your calibre library'), 'catalog.png', 'Catalog builder', None) dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device']) + def genesis(self): + self.qaction.triggered.connect(self.generate_catalog) + def generate_catalog(self): rows = self.gui.library_view.selectionModel().selectedRows() if not rows or len(rows) < 2: From f219f0144dc58a8b45fd9b194284119df62cba08 Mon Sep 17 00:00:00 2001 From: GRiker Date: Sun, 8 May 2011 04:58:04 -0600 Subject: [PATCH 12/23] Added customization_help() to Source, as calibre-customize was failing on builtin Metadata source plugins. --- src/calibre/ebooks/metadata/sources/base.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index 3eff9b11b3..e74e4f5042 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -212,6 +212,9 @@ class Source(Plugin): def is_customizable(self): return True + def customization_help(self): + return 'This plugin can only be customized using the GUI' + def config_widget(self): from calibre.gui2.metadata.config import ConfigWidget return ConfigWidget(self) From c49c187cb6341ac62d2fa0cad28b48c0b4a69bc7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 8 May 2011 09:12:55 -0600 Subject: [PATCH 13/23] Fix #779322 (ebook-convert can't output to "../filename.epub") --- src/calibre/ebooks/conversion/cli.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/conversion/cli.py b/src/calibre/ebooks/conversion/cli.py index f1d5d5fe1b..b03887469d 100644 --- a/src/calibre/ebooks/conversion/cli.py +++ b/src/calibre/ebooks/conversion/cli.py @@ -68,7 +68,8 @@ def check_command_line_options(parser, args, log): raise SystemExit(1) output = args[2] - if output.startswith('.') and output != '.': + if output.startswith('.') and (output != '.' and not + output.startswith('..')): output = os.path.splitext(os.path.basename(input))[0]+output output = os.path.abspath(output) From 81a501604128274d364c651ec7b37c3499fbd2d2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 8 May 2011 09:36:46 -0600 Subject: [PATCH 14/23] Updated recipes for Fronda and Rzeczpospolita --- recipes/fronda.recipe | 17 +++++++++++------ recipes/icons/rzeczpospolita.png | Bin 0 -> 1195 bytes recipes/rzeczpospolita.recipe | 11 ++++++++++- 3 files changed, 21 insertions(+), 7 deletions(-) create mode 100644 recipes/icons/rzeczpospolita.png diff --git a/recipes/fronda.recipe b/recipes/fronda.recipe index f57c7ecccb..452dca9068 100644 --- a/recipes/fronda.recipe +++ b/recipes/fronda.recipe @@ -21,14 +21,19 @@ class Fronda(BasicNewsRecipe): feeds = [(u'Infformacje', u'http://fronda.pl/news/feed')] - keep_only_tags = [dict(name='h1', attrs={'class':'big'}), - dict(name='ul', attrs={'class':'about clear'}), - dict(name='div', attrs={'class':'content'})] + keep_only_tags = [dict(name='h2', attrs={'class':'news_title'}), + dict(name='div', attrs={'class':'naglowek_tresc'}), + dict(name='div', attrs={'id':'czytaj'}) ] + + remove_tags = [dict(name='a', attrs={'class':'print'})] + preprocess_regexps = [ (re.compile(i[0], re.IGNORECASE | re.DOTALL), i[1]) for i in - [ (r'Drukuj', lambda match: ''), - (r'

.*

', lambda match: ''), + [ (r'

.*

', lambda match: ''), (r'

.*

', lambda match: ''), (r'

W.* lektury.*

', lambda match: ''), - (r'

Zobacz t.*?', lambda match: '') ] + (r'

Zobacz t.*?', lambda match: ''), + (r']*> 

', lambda match: ''), + (r'


', lambda match: ''), + (r']*>', re.DOTALL|re.IGNORECASE), lambda match: ''), - (re.compile(r'', re.DOTALL|re.IGNORECASE), lambda match: ''),] - keep_only_tags = [dict(name = 'td',attrs={'class':'bloghead'}),dict(name = 'td',attrs={'class':'blogfliess'})] - remove_tags = [dict(name='img'), dict(name='td',attrs={'class':'blogbottom'}), dict(name='td',attrs={'class':'forum'})] + + keep_only_tags = [dict(name = 'div',attrs={'class':'head'}),dict(name = 'div',attrs={'class':'leftbox'}),dict(name='td',attrs={'class':'strict'})] + remove_tags = [ dict(name='td',attrs={'class':'blogbottom'}), + dict(name='div',attrs={'class':'forum'}), dict(name='div',attrs={'class':'social'}),dict(name='div',attrs={'class':'blog-letter p-news'}), + dict(name='div',attrs={'class':'blog-sub'}),dict(name='div',attrs={'class':'version-div'}),dict(name='div',attrs={'id':'breadcrumb'}) + ,dict(attrs={'class':'tp-url'}),dict(attrs={'class':'blog-name entry_'}) ] + + remove_tags_after = [dict(name='span', attrs={'class':['breadcrumb']})] + feeds = [(u'News', u'http://www.heise.de/tp/news-atom.xml')] @@ -39,15 +39,8 @@ class TelepolisNews(BasicNewsRecipe): html2epub_options = 'publisher="' + publisher + '"\ncomments="' + description + '"\ntags="' + category + '"' - def get_article_url(self, article): - '''if the linked article is of kind artikel don't take it''' - if (article.link.count('artikel') > 1) : - return None - return article.link def preprocess_html(self, soup): mtag = '' soup.head.insert(0,mtag) return soup - - From 6610a6ae4abe3ff9ff37e4fce03885153cd207f6 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 9 May 2011 09:03:50 -0600 Subject: [PATCH 21/23] Add Ziua Veche by Silviu Cotoara --- recipes/icons/ziuaveche.png | Bin 0 -> 554 bytes recipes/ziuaveche.recipe | 53 ++++++++++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 recipes/icons/ziuaveche.png create mode 100644 recipes/ziuaveche.recipe diff --git a/recipes/icons/ziuaveche.png b/recipes/icons/ziuaveche.png new file mode 100644 index 0000000000000000000000000000000000000000..91152b01eebb841025ddad4c88c68610d07f75fe GIT binary patch literal 554 zcmV+_0@eMAP) zUy?q%E32N!Yir2O&m({3DstDa0i{WIWDKG_$h1D$1We!>`rF$;BA8fQ1k4!t s>?sJqzW*?M6Vd1!`R8T+2Mhpz0Py&o=d{A2MF0Q*07*qoM6N<$g5NItGynhq literal 0 HcmV?d00001 diff --git a/recipes/ziuaveche.recipe b/recipes/ziuaveche.recipe new file mode 100644 index 0000000000..61df768e0a --- /dev/null +++ b/recipes/ziuaveche.recipe @@ -0,0 +1,53 @@ +# -*- coding: utf-8 -*- +#!/usr/bin/env python + +__license__ = 'GPL v3' +__copyright__ = u'2011, Silviu Cotoar\u0103' +''' +ziuaveche.ro +''' + +from calibre.web.feeds.news import BasicNewsRecipe + +class ZiuaVeche(BasicNewsRecipe): + title = u'Ziua Veche' + __author__ = u'Silviu Cotoar\u0103' + description = 'Cotidian online' + publisher = 'Ziua Veche' + oldest_article = 5 + language = 'ro' + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + category = 'Ziare,Cotidiane,Stiri' + encoding = 'utf-8' + cover_url = 'http://www.ziuaveche.ro/wp-content/themes/tema/images/zv-logo-alb-old.png' + + conversion_options = { + 'comments' : description + ,'tags' : category + ,'language' : language + ,'publisher' : publisher + } + + + keep_only_tags = [ + dict(name='div', attrs={'id':'singlePost'}) + + ] + + remove_tags = [ + dict(name='div', attrs={'id':'LikePluginPagelet'}) + + ] + + remove_tags_after = [ + dict(name='div', attrs={'id':'LikePluginPagelet'}) + ] + + feeds = [ + (u'Feeds', u'http://www.ziuaveche.ro/feed/rss') + ] + + def preprocess_html(self, soup): + return self.adeify_images(soup) From 85e1100db35f9ddfaffcec619b67e6bd272c749b Mon Sep 17 00:00:00 2001 From: GRiker Date: Mon, 9 May 2011 17:44:33 -0600 Subject: [PATCH 22/23] Added support for custom icons in MessageBox --- src/calibre/gui2/dialogs/message_box.py | 27 +++++++++++++++---------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/src/calibre/gui2/dialogs/message_box.py b/src/calibre/gui2/dialogs/message_box.py index f9354a0cfc..fdec19dc69 100644 --- a/src/calibre/gui2/dialogs/message_box.py +++ b/src/calibre/gui2/dialogs/message_box.py @@ -19,17 +19,23 @@ class MessageBox(QDialog, Ui_Dialog): # {{{ INFO = 2 QUESTION = 3 - def __init__(self, type_, title, msg, det_msg='', show_copy_button=True, - parent=None): + def __init__(self, type_, title, msg, + det_msg='', + q_icon=None, + show_copy_button=True, + parent=None): QDialog.__init__(self, parent) - icon = { - self.ERROR : 'error', - self.WARNING: 'warning', - self.INFO: 'information', - self.QUESTION: 'question', - }[type_] - icon = 'dialog_%s.png'%icon - self.icon = QIcon(I(icon)) + if q_icon is None: + icon = { + self.ERROR : 'error', + self.WARNING: 'warning', + self.INFO: 'information', + self.QUESTION: 'question', + }[type_] + icon = 'dialog_%s.png'%icon + self.icon = QIcon(I(icon)) + else: + self.icon = q_icon self.setupUi(self) self.setWindowTitle(title) @@ -44,7 +50,6 @@ class MessageBox(QDialog, Ui_Dialog): # {{{ self.bb.ActionRole) self.ctc_button.clicked.connect(self.copy_to_clipboard) - self.show_det_msg = _('Show &details') self.hide_det_msg = _('Hide &details') self.det_msg_toggle = self.bb.addButton(self.show_det_msg, self.bb.ActionRole) From c11164f9820faef3a9eb235033e51daf7c0a6e60 Mon Sep 17 00:00:00 2001 From: GRiker Date: Mon, 9 May 2011 17:58:16 -0600 Subject: [PATCH 23/23] Added Product IDs for iPad2 (Wifi) and iPad2 (CDMA) --- src/calibre/devices/apple/driver.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 922afc4338..b7d5ac36d2 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -203,9 +203,11 @@ class ITUNES(DriverBase): # 0x1294 iPhone 3GS # 0x1297 iPhone 4 # 0x129a iPad - # 0x12a2 iPad2 + # 0x129f iPad2 (WiFi) + # 0x12a2 iPad2 (GSM) + # 0x12a3 iPad2 (CDMA) VENDOR_ID = [0x05ac] - PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x129f,0x12a2] + PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x129f,0x12a2,0x12a3] BCD = [0x01] # Plugboard ID