From 949156cf381c63a9207b103f9ef3f74c3bf4f4ff Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 18:23:57 -0600 Subject: [PATCH 01/45] Enable Esc key in preferences window --- src/calibre/gui2/preferences/main.py | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index 9b3f932a08..596cd5cc0a 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -9,8 +9,8 @@ import textwrap from functools import partial from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ - QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, \ - QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton + QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \ + QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction from calibre.constants import __appname__, __version__ from calibre.gui2 import gprefs, min_available_height, available_width, \ @@ -41,7 +41,7 @@ class StatusBar(QStatusBar): # {{{ # }}} -class Category(QWidget): +class Category(QWidget): # {{{ plugin_activated = pyqtSignal(object) @@ -82,8 +82,9 @@ class Category(QWidget): def triggered(self, plugin, *args): self.plugin_activated.emit(plugin) +# }}} -class Browser(QScrollArea): +class Browser(QScrollArea): # {{{ show_plugin = pyqtSignal(object) @@ -122,7 +123,7 @@ class Browser(QScrollArea): self._layout.addWidget(w) w.plugin_activated.connect(self.show_plugin.emit) - +# }}} class Preferences(QMainWindow): @@ -141,6 +142,10 @@ class Preferences(QMainWindow): nh = min(self.height(), nh) nw = min(self.width(), nw) self.resize(nw, nh) + self.esc_action = QAction(self) + self.addAction(self.esc_action) + self.esc_action.setShortcut(QKeySequence(Qt.Key_Escape)) + self.esc_action.triggered.connect(self.esc) geom = gprefs.get('preferences_window_geometry', None) if geom is not None: @@ -236,6 +241,12 @@ class Preferences(QMainWindow): self.stack.setCurrentIndex(0) self.setWindowIcon(QIcon(I('config.png'))) + def esc(self, *args): + if self.stack.currentIndex() == 1: + self.hide_plugin() + elif self.stack.currentIndex() == 0: + self.close() + def commit(self, *args): try: must_restart = self.showing_widget.commit() From 8e755bf2053d6ba58b25ab848e992a460e01b064 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 18:48:44 -0600 Subject: [PATCH 02/45] ... --- src/calibre/gui2/preferences/main.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index 596cd5cc0a..b1fc22b8ae 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -12,7 +12,7 @@ from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \ QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction -from calibre.constants import __appname__, __version__ +from calibre.constants import __appname__, __version__, islinux from calibre.gui2 import gprefs, min_available_height, available_width, \ warning_dialog from calibre.gui2.preferences import init_gui, AbortCommit, get_plugin @@ -151,6 +151,10 @@ class Preferences(QMainWindow): if geom is not None: self.restoreGeometry(geom) + # Center + if islinux: + self.move(gui.rect().center() - self.rect().center()) + self.setWindowModality(Qt.WindowModal) self.setWindowTitle(__appname__ + ' - ' + _('Preferences')) self.setWindowIcon(QIcon(I('config.png'))) From 6b1ed5910982a12be13dc2e9ff86a37843dd3011 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 18:57:24 -0600 Subject: [PATCH 03/45] Add close button to preference category browser on OS X --- src/calibre/gui2/preferences/main.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index b1fc22b8ae..048096b1a0 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -10,9 +10,10 @@ from functools import partial from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \ - QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction + QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction, \ + QPushButton -from calibre.constants import __appname__, __version__, islinux +from calibre.constants import __appname__, __version__, islinux, isosx from calibre.gui2 import gprefs, min_available_height, available_width, \ warning_dialog from calibre.gui2.preferences import init_gui, AbortCommit, get_plugin @@ -87,6 +88,7 @@ class Category(QWidget): # {{{ class Browser(QScrollArea): # {{{ show_plugin = pyqtSignal(object) + close_signal = pyqtSignal() def __init__(self, parent=None): QScrollArea.__init__(self, parent) @@ -117,12 +119,20 @@ class Browser(QScrollArea): # {{{ self.container = QWidget(self) self.container.setLayout(self._layout) self.setWidget(self.container) + if isosx: + self.close_button = QPushButton(_('Close')) + self.close_button.clicked.connect(self.close_requested) + self._layout.addWidget(self.close_button) + for name, plugins in self.category_map.items(): w = Category(name, plugins, self) self.widgets.append(w) self._layout.addWidget(w) w.plugin_activated.connect(self.show_plugin.emit) + def close_requested(self, *args): + self.close_signal.emit() + # }}} class Preferences(QMainWindow): @@ -165,6 +175,7 @@ class Preferences(QMainWindow): self.stack = QStackedWidget(self) self.setCentralWidget(self.stack) self.browser = Browser(self) + self.browser.close_signal.connect(self.close, type=Qt.QueuedConnection) self.browser.show_plugin.connect(self.show_plugin) self.stack.addWidget(self.browser) self.scroll_area = QScrollArea(self) From e21692917791f63b34d65172b263a011c1892acb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 19:00:24 -0600 Subject: [PATCH 04/45] ... --- src/calibre/gui2/preferences/main.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index 048096b1a0..94bddadb42 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -11,7 +11,7 @@ from functools import partial from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \ QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction, \ - QPushButton + QPushButton, QHBoxLayout from calibre.constants import __appname__, __version__, islinux, isosx from calibre.gui2 import gprefs, min_available_height, available_width, \ @@ -120,9 +120,13 @@ class Browser(QScrollArea): # {{{ self.container.setLayout(self._layout) self.setWidget(self.container) if isosx: + self._osxl = QHBoxLayout() self.close_button = QPushButton(_('Close')) self.close_button.clicked.connect(self.close_requested) - self._layout.addWidget(self.close_button) + self._osxl.addStretch(10) + self._osxl.addWidget(self.close_button) + self._osxl.addStretch(10) + self._layout.addLayout(self._osxl) for name, plugins in self.category_map.items(): w = Category(name, plugins, self) From 74343442d0160be539612d37ff3fa0c2714764d0 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 19:07:36 -0600 Subject: [PATCH 05/45] ... --- src/calibre/gui2/preferences/main.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index 94bddadb42..a596ad5b6b 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -147,7 +147,7 @@ class Preferences(QMainWindow): self.must_restart = False self.committed = False - self.resize(900, 700) + self.resize(900, 760 if isosx else 710) nh, nw = min_available_height()-25, available_width()-10 if nh < 0: nh = 800 @@ -277,7 +277,7 @@ class Preferences(QMainWindow): warning_dialog(self, _('Restart needed'), _('Some of the changes you made require a restart.' ' Please restart calibre as soon as possible.'), - show=True) + show=True, show_copy_button=False) self.showing_widget.refresh_gui(self.gui) self.hide_plugin() From 4849c4269ba667eb927915bd074f012e8094a0ae Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 19:22:56 -0600 Subject: [PATCH 06/45] The Walrus Magazine by Tony Stegall --- resources/images/news/walrusmag.png | Bin 0 -> 569 bytes resources/recipes/walrusmag.recipe | 46 ++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 resources/images/news/walrusmag.png create mode 100644 resources/recipes/walrusmag.recipe diff --git a/resources/images/news/walrusmag.png b/resources/images/news/walrusmag.png new file mode 100644 index 0000000000000000000000000000000000000000..17030d94b8929b670ed38495f2f6e9cd2166fa1b GIT binary patch literal 569 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!60wlNoGJgf6n3BBRT^Rni_n+Ah2>S z4={E+nQaFWEGuwK2hw1@3^B*n9%f)*O!IVc4AD3r8))m{6ewYP_j`!QX8AKxGK-Eh zaY?I4X%uO|_YxJ(xeTsbSegEg1W%&V2j8B`Mib|RYo)Fn5qpQuHbK>NN zHW8Mr&S?^X7bTfiuJC$yqVdc7rEiO+TbH*=G&=ZPTDt4xQzpk1-)}D8{^X+@)5k?$ z)2g5OcJMz;2%7zB=e63Ia$Q2YACGi@&O3B8v1YDs<@+@=vpy#uTJVRJL4lcbl2!`i zqVsE}Gv@j`MsHF)ztQHDUonqIz2ixS1s{{I|6D8>dQ|E3OV;Od3&MhXL|6RNJ}wv$ zIN|xEwr>UtUMSVSysV!5bXnFu)s&roD|W3rk{Z(1qO;*Q=fuvA-|=F{AJ=T-xF*)x zciX`&S}?5OKux^C&HV-29?LH~Wx^SG?sY-coO#!;T^8hIIV7KO`+1-6oJF<3;8!hi zjVMV;EJ?LWE=mPb3`Pcq2D%28x(22phL%=F23CeZwwaZIf!Wk8*HJX&=BH$)RU&IJ bGPE)@0V0To2_>; Date: Sun, 5 Sep 2010 20:13:52 -0600 Subject: [PATCH 07/45] Fix #6723 (problem sending email) --- src/calibre/utils/smtp.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/utils/smtp.py b/src/calibre/utils/smtp.py index 350d762e59..230a983b74 100644 --- a/src/calibre/utils/smtp.py +++ b/src/calibre/utils/smtp.py @@ -50,8 +50,9 @@ def get_mx(host, verbose=0): if verbose: print 'Find mail exchanger for', host answers = list(dns.resolver.query(host, 'MX')) - answers.sort(cmp=lambda x, y: cmp(int(x.preference), int(y.preference))) - return [str(x.exchange) for x in answers] + answers.sort(cmp=lambda x, y: cmp(int(getattr(x, 'preference', sys.maxint)), + int(getattr(y, 'preference', sys.maxint)))) + return [str(x.exchange) for x in answers if hasattr(x, 'exchange')] def sendmail_direct(from_, to, msg, timeout, localhost, verbose): import smtplib From 9e59f00645147cb771bdd8ec0039324c280f22a2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 21:37:49 -0600 Subject: [PATCH 08/45] BuckMasters In The Kitchen by Tony Stegall --- resources/recipes/buckmasters.recipe | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 resources/recipes/buckmasters.recipe diff --git a/resources/recipes/buckmasters.recipe b/resources/recipes/buckmasters.recipe new file mode 100644 index 0000000000..e3d852fa07 --- /dev/null +++ b/resources/recipes/buckmasters.recipe @@ -0,0 +1,42 @@ +from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import Tag + +class AdvancedUserRecipe1282101454(BasicNewsRecipe): + title = 'BuckMasters In The Kitchen' + language = 'en' + __author__ = 'TonytheBookworm & Starson17' + description = 'Learn how to cook all those outdoor varments' + publisher = 'BuckMasters.com' + category = 'food,cooking,recipes' + oldest_article = 365 + max_articles_per_feed = 100 + conversion_options = {'linearize_tables' : True} + masthead_url = 'http://www.buckmasters.com/Portals/_default/Skins/BM_10/images/header_bg.jpg' + keep_only_tags = [ + dict(name='table', attrs={'class':['containermaster_black']}) + ] + remove_tags_after = [dict(name='div', attrs={'align':['left']})] + feeds = [ + ('Recipes', 'http://www.buckmasters.com/DesktopModules/DnnForge%20-%20NewsArticles/RSS.aspx?TabID=292&ModuleID=658&MaxCount=25'), + ] + + def preprocess_html(self, soup): + item = soup.find('a', attrs={'class':['MenuTopSelected']}) + if item: + item.parent.extract() + for img_tag in soup.findAll('img'): + parent_tag = img_tag.parent + if parent_tag.name == 'a': + new_tag = Tag(soup,'p') + new_tag.insert(0,img_tag) + parent_tag.replaceWith(new_tag) + elif parent_tag.name == 'p': + if not self.tag_to_string(parent_tag) == '': + new_div = Tag(soup,'div') + new_tag = Tag(soup,'p') + new_tag.insert(0,img_tag) + parent_tag.replaceWith(new_div) + new_div.insert(0,new_tag) + new_div.insert(1,parent_tag) + return soup + From 81635326a39821a4123285cf0ed8b0429be7d77e Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 5 Sep 2010 22:49:38 -0600 Subject: [PATCH 09/45] ... --- src/calibre/gui2/dialogs/metadata_single.ui | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.ui b/src/calibre/gui2/dialogs/metadata_single.ui index 4ea68f539d..7184192eba 100644 --- a/src/calibre/gui2/dialogs/metadata_single.ui +++ b/src/calibre/gui2/dialogs/metadata_single.ui @@ -646,7 +646,7 @@ - Download &cover + Download co&ver From 536eb116a57c4292807c8a01dca349e65611a9f5 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 6 Sep 2010 07:41:53 +0100 Subject: [PATCH 10/45] Fix bug #6718 --- src/calibre/library/database2.py | 3 +-- src/calibre/library/field_metadata.py | 12 +----------- 2 files changed, 2 insertions(+), 13 deletions(-) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 4b6fb2f7f2..3b7efd500d 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -144,6 +144,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.initialize_dynamic() def initialize_dynamic(self): + self.field_metadata = FieldMetadata() #Ensure we start with a clean copy self.prefs = DBPrefs(self) defs = self.prefs.defaults defs['gui_restriction'] = defs['cs_restriction'] = '' @@ -2151,8 +2152,6 @@ books_series_link feeds os.remove(self.dbpath) shutil.copyfile(dest, self.dbpath) self.connect() - self.field_metadata.remove_dynamic_categories() - self.field_metadata.remove_custom_fields() self.initialize_dynamic() self.refresh() if os.path.exists(dest): diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index e28b6d422a..09dd024b66 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -306,7 +306,7 @@ class FieldMetadata(dict): self._tb_cats[k]['label'] = k self._tb_cats[k]['display'] = {} self._tb_cats[k]['is_editable'] = True - self._add_search_terms_to_map(k, self._tb_cats[k]['search_terms']) + self._add_search_terms_to_map(k, v['search_terms']) self.custom_field_prefix = '#' self.get = self._tb_cats.get @@ -408,16 +408,6 @@ class FieldMetadata(dict): self._add_search_terms_to_map(key, [key]) self.custom_label_to_key_map[label+'_index'] = key - def remove_custom_fields(self): - for key in self.get_custom_fields(): - del self._tb_cats[key] - - def remove_dynamic_categories(self): - for key in list(self._tb_cats.keys()): - val = self._tb_cats[key] - if val['is_category'] and val['kind'] in ('user', 'search'): - del self._tb_cats[key] - def cc_series_index_column_for(self, key): return self._tb_cats[key]['rec_index'] + 1 From 8a027ac60cadea17efc3b8eeb55bbba24f920e65 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 6 Sep 2010 14:18:00 +0100 Subject: [PATCH 11/45] Fix problem where db.set_author forced a recalculation of author_sort whenever editing metadata of a book with a unique author (only book with that author). The problem arose because the author was deleted from the DB then recreated, causing the trigger to be fired. --- src/calibre/gui2/dialogs/metadata_single.py | 1 + src/calibre/library/database2.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 1393a50738..82b6ac8cbf 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -760,6 +760,7 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): _('Could not open %s. Is it being used by another' ' program?')%fname, show=True) raise + self.db.clean() self.save_state() QDialog.accept(self) if callable(self.accepted_callback): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 3b7efd500d..93c42ea342 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -1126,7 +1126,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): if not authors: authors = [_('Unknown')] self.conn.execute('DELETE FROM books_authors_link WHERE book=?',(id,)) - self.conn.execute('DELETE FROM authors WHERE (SELECT COUNT(id) FROM books_authors_link WHERE author=authors.id) < 1') for a in authors: if not a: continue From 3e87ee26c2dbb11055268ad9da76b06b10bd50fb Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Sep 2010 08:23:39 -0600 Subject: [PATCH 12/45] ... --- src/calibre/gui2/preferences/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index a596ad5b6b..6524faec2d 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -125,7 +125,7 @@ class Browser(QScrollArea): # {{{ self.close_button.clicked.connect(self.close_requested) self._osxl.addStretch(10) self._osxl.addWidget(self.close_button) - self._osxl.addStretch(10) + #self._osxl.addStretch(10) self._layout.addLayout(self._osxl) for name, plugins in self.category_map.items(): From e3e65c3f001396cb819a1d9d4b67071f6758a6fc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Sep 2010 08:48:31 -0600 Subject: [PATCH 13/45] Journal Gazette by cynvision --- resources/images/news/journalgazette.png | Bin 0 -> 414 bytes resources/recipes/journalgazette.recipe | 64 +++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 resources/images/news/journalgazette.png create mode 100644 resources/recipes/journalgazette.recipe diff --git a/resources/images/news/journalgazette.png b/resources/images/news/journalgazette.png new file mode 100644 index 0000000000000000000000000000000000000000..f9b3316cabdd28cdd2ecbc8965bf79cbc4842c87 GIT binary patch literal 414 zcmV;P0b%}$P)0tP@mE#Wl^wVP}%1r#L712ik}dsQJO zvg@=`tm}+c_elz@6;5T@Yn~`wptbVstp&PH@23Z@Gb#hd($av3`Q`QV zuU_oX&L) Date: Mon, 6 Sep 2010 10:53:47 -0400 Subject: [PATCH 14/45] Alt-M keyboard shortcut added for Merge - keep others (Safe Merge) --- src/calibre/gui2/actions/edit_metadata.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/actions/edit_metadata.py b/src/calibre/gui2/actions/edit_metadata.py index 7a1e589d2a..f0232d9859 100644 --- a/src/calibre/gui2/actions/edit_metadata.py +++ b/src/calibre/gui2/actions/edit_metadata.py @@ -51,7 +51,8 @@ class EditMetadataAction(InterfaceAction): self.merge_books) mb.addSeparator() mb.addAction(_('Merge into first selected book - keep others'), - partial(self.merge_books, safe_merge=True)) + partial(self.merge_books, safe_merge=True), + Qt.AltModifier+Qt.Key_M) self.merge_menu = mb self.action_merge.setMenu(mb) md.addSeparator() From 4693a3c0c62e39d5daebe8fd3e5eca47e028b0d1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Sep 2010 11:00:39 -0600 Subject: [PATCH 15/45] Fix #6728 (WSJ (free) recipe) --- resources/recipes/wsj_free.recipe | 153 ++++++++++++++++++++++++++++++ 1 file changed, 153 insertions(+) create mode 100644 resources/recipes/wsj_free.recipe diff --git a/resources/recipes/wsj_free.recipe b/resources/recipes/wsj_free.recipe new file mode 100644 index 0000000000..7f3664f1c4 --- /dev/null +++ b/resources/recipes/wsj_free.recipe @@ -0,0 +1,153 @@ +#!/usr/bin/env python +__license__ = 'GPL v3' +__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' +__docformat__ = 'restructuredtext en' + +from calibre.web.feeds.news import BasicNewsRecipe +import copy + +class WallStreetJournal(BasicNewsRecipe): + + title = 'Wall Street Journal (free)' + __author__ = 'Kovid Goyal, Sujata Raman, Joshua Oster-Morris, Starson17' + description = 'News and current affairs' + language = 'en' + cover_url = 'http://dealbreaker.com/images/thumbs/Wall%20Street%20Journal%20A1.JPG' + max_articles_per_feed = 1000 + timefmt = ' [%a, %b %d, %Y]' + no_stylesheets = True + + extra_css = '''h1{color:#093D72 ; font-size:large ; font-family:Georgia,"Century Schoolbook","Times New Roman",Times,serif; } + h2{color:#474537; font-family:Georgia,"Century Schoolbook","Times New Roman",Times,serif; font-size:small; font-style:italic;} + .subhead{color:gray; font-family:Georgia,"Century Schoolbook","Times New Roman",Times,serif; font-size:small; font-style:italic;} + .insettipUnit {color:#666666; font-family:Arial,Sans-serif;font-size:xx-small } + .targetCaption{ font-size:x-small; color:#333333; font-family:Arial,Helvetica,sans-serif} + .article{font-family :Arial,Helvetica,sans-serif; font-size:x-small} + .tagline {color:#333333; font-size:xx-small} + .dateStamp {color:#666666; font-family:Arial,Helvetica,sans-serif} + h3{color:blue ;font-family:Arial,Helvetica,sans-serif; font-size:xx-small} + .byline{color:blue;font-family:Arial,Helvetica,sans-serif; font-size:xx-small} + h6{color:#333333; font-family:Georgia,"Century Schoolbook","Times New Roman",Times,serif; font-size:small;font-style:italic; } + .paperLocation{color:#666666; font-size:xx-small}''' + + remove_tags_before = dict(name='h1') + remove_tags = [ + dict(id=["articleTabs_tab_article", "articleTabs_tab_comments", "articleTabs_tab_interactive","articleTabs_tab_video","articleTabs_tab_map","articleTabs_tab_slideshow"]), + {'class':['footer_columns','network','insetCol3wide','interactive','video','slideshow','map','insettip','insetClose','more_in', "insetContent", 'articleTools_bottom', 'aTools', "tooltip", "adSummary", "nav-inline"]}, + dict(name='div', attrs={'data-flash-settings':True}), + {'class':['insetContent embedType-interactive insetCol3wide','insetCol6wide','insettipUnit']}, + dict(rel='shortcut icon'), + ] + remove_tags_after = [dict(id="article_story_body"), {'class':"article story"},] + + def postprocess_html(self, soup, first): + for tag in soup.findAll(name=['table', 'tr', 'td']): + tag.name = 'div' + + for tag in soup.findAll('div', dict(id=["articleThumbnail_1", "articleThumbnail_2", "articleThumbnail_3", "articleThumbnail_4", "articleThumbnail_5", "articleThumbnail_6", "articleThumbnail_7"])): + tag.extract() + + return soup + + def wsj_get_index(self): + return self.index_to_soup('http://online.wsj.com/itp') + + def wsj_add_feed(self,feeds,title,url): + self.log('Found section:', title) + if url.endswith('whatsnews'): + articles = self.wsj_find_wn_articles(url) + else: + articles = self.wsj_find_articles(url) + if articles: + feeds.append((title, articles)) + return feeds + + def parse_index(self): + soup = self.wsj_get_index() + + date = soup.find('span', attrs={'class':'date-date'}) + if date is not None: + self.timefmt = ' [%s]'%self.tag_to_string(date) + + feeds = [] + div = soup.find('div', attrs={'class':'itpHeader'}) + div = div.find('ul', attrs={'class':'tab'}) + for a in div.findAll('a', href=lambda x: x and '/itp/' in x): + pageone = a['href'].endswith('pageone') + if pageone: + title = 'Front Section' + url = 'http://online.wsj.com' + a['href'] + feeds = self.wsj_add_feed(feeds,title,url) + title = 'What''s News' + url = url.replace('pageone','whatsnews') + feeds = self.wsj_add_feed(feeds,title,url) + else: + title = self.tag_to_string(a) + url = 'http://online.wsj.com' + a['href'] + feeds = self.wsj_add_feed(feeds,title,url) + return feeds + + def wsj_find_wn_articles(self, url): + soup = self.index_to_soup(url) + articles = [] + + whats_news = soup.find('div', attrs={'class':lambda x: x and 'whatsNews-simple' in x}) + if whats_news is not None: + for a in whats_news.findAll('a', href=lambda x: x and '/article/' in x): + container = a.findParent(['p']) + meta = a.find(attrs={'class':'meta_sectionName'}) + if meta is not None: + meta.extract() + title = self.tag_to_string(a).strip() + url = a['href'] + desc = '' + if container is not None: + desc = self.tag_to_string(container) + + articles.append({'title':title, 'url':url, + 'description':desc, 'date':''}) + + self.log('\tFound WN article:', title) + + return articles + + def wsj_find_articles(self, url): + soup = self.index_to_soup(url) + + whats_news = soup.find('div', attrs={'class':lambda x: x and 'whatsNews-simple' in x}) + if whats_news is not None: + whats_news.extract() + + articles = [] + + flavorarea = soup.find('div', attrs={'class':lambda x: x and 'ahed' in x}) + if flavorarea is not None: + flavorstory = flavorarea.find('a', href=lambda x: x and x.startswith('/article')) + if flavorstory is not None: + flavorstory['class'] = 'mjLinkItem' + metapage = soup.find('span', attrs={'class':lambda x: x and 'meta_sectionName' in x}) + if metapage is not None: + flavorstory.append( copy.copy(metapage) ) #metapage should always be A1 because that should be first on the page + + for a in soup.findAll('a', attrs={'class':'mjLinkItem'}, href=True): + container = a.findParent(['li', 'div']) + meta = a.find(attrs={'class':'meta_sectionName'}) + if meta is not None: + meta.extract() + title = self.tag_to_string(a).strip() + ' [%s]'%self.tag_to_string(meta) + url = 'http://online.wsj.com'+a['href'] + desc = '' + p = container.find('p') + if p is not None: + desc = self.tag_to_string(p) + + articles.append({'title':title, 'url':url, + 'description':desc, 'date':''}) + + self.log('\tFound article:', title) + + return articles + + def cleanup(self): + self.browser.open('http://online.wsj.com/logout?url=http://online.wsj.com') + From 438c9d8ba48192ed9fe89a6c5c09db13c3398105 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Sep 2010 11:11:13 -0600 Subject: [PATCH 16/45] Change delete confirmation message --- src/calibre/gui2/actions/delete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/actions/delete.py b/src/calibre/gui2/actions/delete.py index e50131afe3..0343c6df84 100644 --- a/src/calibre/gui2/actions/delete.py +++ b/src/calibre/gui2/actions/delete.py @@ -159,7 +159,7 @@ class DeleteAction(InterfaceAction): if self.gui.stack.currentIndex() == 0: if not confirm('

'+_('The selected books will be ' 'permanently deleted and the files ' - 'removed from your computer. Are you sure?') + 'removed from your calibre library. Are you sure?') +'

', 'library_delete_books', self.gui): return ci = view.currentIndex() From 28f62fd4dd718049c837dc39ab274222d0d803d3 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 6 Sep 2010 18:32:03 +0100 Subject: [PATCH 17/45] 1) take db.clean out of metadata_single.py 2) add db.clean to shutdown func 3) add db.clean to 2 cli funcs that can delete an author 4) add db.ondevice() to return the ondevice string --- src/calibre/gui2/dialogs/metadata_single.py | 1 - src/calibre/gui2/ui.py | 1 + src/calibre/library/cli.py | 2 ++ src/calibre/library/database2.py | 2 +- 4 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/dialogs/metadata_single.py b/src/calibre/gui2/dialogs/metadata_single.py index 82b6ac8cbf..1393a50738 100644 --- a/src/calibre/gui2/dialogs/metadata_single.py +++ b/src/calibre/gui2/dialogs/metadata_single.py @@ -760,7 +760,6 @@ class MetadataSingleDialog(ResizableDialog, Ui_MetadataSingleDialog): _('Could not open %s. Is it being used by another' ' program?')%fname, show=True) raise - self.db.clean() self.save_state() QDialog.accept(self) if callable(self.accepted_callback): diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 84f5a1a2c9..d84dea7376 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -522,6 +522,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ def shutdown(self, write_settings=True): + self.db.clean() for action in self.iactions.values(): if not action.shutting_down(): return diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index ea42fe5998..c17911cc3a 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -324,6 +324,7 @@ def do_remove(db, ids): db.delete_book(y) send_message() + db.clean() def remove_option_parser(): return get_parser(_( @@ -449,6 +450,7 @@ def command_show_metadata(args, dbpath): def do_set_metadata(db, id, stream): mi = OPF(stream) db.set_metadata(id, mi) + db.clean() do_show_metadata(db, id, False) send_message() diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 93c42ea342..23ec60d320 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -332,7 +332,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): for prop in ('author_sort', 'authors', 'comment', 'comments', 'isbn', 'publisher', 'rating', 'series', 'series_index', 'tags', - 'title', 'timestamp', 'uuid', 'pubdate'): + 'title', 'timestamp', 'uuid', 'pubdate', 'ondevice'): setattr(self, prop, functools.partial(get_property, loc=self.FIELD_MAP['comments' if prop == 'comment' else prop])) setattr(self, 'title_sort', functools.partial(get_property, From f7321f2c57869cfbe98c8f354f60ea6d9679f46f Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Mon, 6 Sep 2010 19:01:07 +0100 Subject: [PATCH 18/45] Fix shutdown code --- src/calibre/gui2/ui.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index d84dea7376..151f58f6af 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -522,7 +522,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ def shutdown(self, write_settings=True): - self.db.clean() + l = getattr(self, 'library_view', None) + if l: + l = getattr(l, 'model', None); + if l: + l = l().db + if l: + l.clean() for action in self.iactions.values(): if not action.shutting_down(): return From 2501e40e43c1e6fd0373bd97018236cc0f3979ec Mon Sep 17 00:00:00 2001 From: GRiker Date: Mon, 6 Sep 2010 12:04:46 -0700 Subject: [PATCH 19/45] GR changes supporting ondevice --- src/calibre/gui2/actions/catalog.py | 2 +- src/calibre/gui2/tools.py | 24 +- src/calibre/library/catalog.py | 659 ++++++++++++++-------------- src/calibre/library/cli.py | 9 +- 4 files changed, 357 insertions(+), 337 deletions(-) diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py index 6feaec978d..d965c6d814 100644 --- a/src/calibre/gui2/actions/catalog.py +++ b/src/calibre/gui2/actions/catalog.py @@ -33,7 +33,7 @@ class GenerateCatalogAction(InterfaceAction): show=True) # Calling gui2.tools:generate_catalog() - ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager.device) + ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager) if ret is None: return diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index caef82ab81..7a516bb4ff 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -238,7 +238,7 @@ def fetch_scheduled_recipe(arg): return 'gui_convert', args, _('Fetch news from ')+arg['title'], fmt.upper(), [pt] -def generate_catalog(parent, dbspec, ids, device): +def generate_catalog(parent, dbspec, ids, device_manager): from calibre.gui2.dialogs.catalog import Catalog # Build the Catalog dialog in gui2.dialogs.catalog @@ -252,9 +252,18 @@ def generate_catalog(parent, dbspec, ids, device): # Profile the connected device # Parallel initialization in calibre.library.cli:command_catalog() - connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None} + connected_device = { + 'is_device_connected': device_manager.is_device_connected, + 'kind': device_manager.connected_device_kind, + 'name': None, + 'save_template': None, + 'serial': None, + 'storage': None + } - if device: + if device_manager.is_device_connected: + device = device_manager.device + connected_device['name'] = device.gui_name try: storage = [] if device._main_prefix: @@ -263,11 +272,10 @@ def generate_catalog(parent, dbspec, ids, device): storage.append(os.path.join(device._card_a_prefix, device.EBOOK_DIR_CARD_A)) if device._card_b_prefix: storage.append(os.path.join(device._card_b_prefix, device.EBOOK_DIR_CARD_B)) - connected_device = { 'storage': storage, - 'serial': device.detected_device.serial if \ - hasattr(device.detected_device,'serial') else None, - 'save_template': device.save_template(), - 'name': device.gui_name} + connected_device['storage'] = storage + connected_device['serial'] = device.detected_device.serial if \ + hasattr(device.detected_device,'serial') else None + connected_device['save_template'] = device.save_template() except: pass diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 33279c30d8..4b5cb6bb84 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -67,6 +67,8 @@ class CSV_XML(CatalogPlugin): if opts.verbose: opts_dict = vars(opts) log("%s(): Generating %s" % (self.name,self.fmt)) + if opts.connected_device['is_device_connected']: + log(" connected_device: %s" % opts.connected_device['name']) if opts_dict['search_text']: log(" --search='%s'" % opts_dict['search_text']) @@ -81,7 +83,6 @@ class CSV_XML(CatalogPlugin): else: log(" Fields: %s" % opts_dict['fields']) - # If a list of ids are provided, don't use search_text if opts.ids: opts.search_text = None @@ -888,302 +889,302 @@ class EPUB_MOBI(CatalogPlugin): self.__totalSteps += 2 # Accessors - ''' - @dynamic_property - def xxxx(self): - def fget(self): - return self.__ - def fset(self, val): - self.__ = val - return property(fget=fget, fset=fset) - ''' + if True: + ''' + @dynamic_property + def xxxx(self): + def fget(self): + return self.__ + def fset(self, val): + self.__ = val + return property(fget=fget, fset=fset) + ''' + @dynamic_property + def authorClip(self): + def fget(self): + return self.__authorClip + def fset(self, val): + self.__authorClip = val + return property(fget=fget, fset=fset) + @dynamic_property + def authors(self): + def fget(self): + return self.__authors + def fset(self, val): + self.__authors = val + return property(fget=fget, fset=fset) + @dynamic_property + def basename(self): + def fget(self): + return self.__basename + def fset(self, val): + self.__basename = val + return property(fget=fget, fset=fset) + @dynamic_property + def bookmarked_books(self): + def fget(self): + return self.__bookmarked_books + def fset(self, val): + self.__bookmarked_books = val + return property(fget=fget, fset=fset) + @dynamic_property + def booksByAuthor(self): + def fget(self): + return self.__booksByAuthor + def fset(self, val): + self.__booksByAuthor = val + return property(fget=fget, fset=fset) + @dynamic_property + def booksByDateRead(self): + def fget(self): + return self.__booksByDateRead + def fset(self, val): + self.__booksByDateRead = val + return property(fget=fget, fset=fset) + @dynamic_property + def booksByTitle(self): + def fget(self): + return self.__booksByTitle + def fset(self, val): + self.__booksByTitle = val + return property(fget=fget, fset=fset) + @dynamic_property + def booksByTitle_noSeriesPrefix(self): + def fget(self): + return self.__booksByTitle_noSeriesPrefix + def fset(self, val): + self.__booksByTitle_noSeriesPrefix = val + return property(fget=fget, fset=fset) + @dynamic_property + def catalogPath(self): + def fget(self): + return self.__catalogPath + def fset(self, val): + self.__catalogPath = val + return property(fget=fget, fset=fset) + @dynamic_property + def contentDir(self): + def fget(self): + return self.__contentDir + def fset(self, val): + self.__contentDir = val + return property(fget=fget, fset=fset) + @dynamic_property + def currentStep(self): + def fget(self): + return self.__currentStep + def fset(self, val): + self.__currentStep = val + return property(fget=fget, fset=fset) + @dynamic_property + def creator(self): + def fget(self): + return self.__creator + def fset(self, val): + self.__creator = val + return property(fget=fget, fset=fset) + @dynamic_property + def db(self): + def fget(self): + return self.__db + return property(fget=fget) + @dynamic_property + def descriptionClip(self): + def fget(self): + return self.__descriptionClip + def fset(self, val): + self.__descriptionClip = val + return property(fget=fget, fset=fset) + @dynamic_property + def error(self): + def fget(self): + return self.__error + return property(fget=fget) + @dynamic_property + def generateForKindle(self): + def fget(self): + return self.__generateForKindle + def fset(self, val): + self.__generateForKindle = val + return property(fget=fget, fset=fset) + @dynamic_property + def generateRecentlyRead(self): + def fget(self): + return self.__generateRecentlyRead + def fset(self, val): + self.__generateRecentlyRead = val + return property(fget=fget, fset=fset) + @dynamic_property + def genres(self): + def fget(self): + return self.__genres + def fset(self, val): + self.__genres = val + return property(fget=fget, fset=fset) + @dynamic_property + def genre_tags_dict(self): + def fget(self): + return self.__genre_tags_dict + def fset(self, val): + self.__genre_tags_dict = val + return property(fget=fget, fset=fset) + @dynamic_property + def htmlFileList(self): + def fget(self): + return self.__htmlFileList + def fset(self, val): + self.__htmlFileList = val + return property(fget=fget, fset=fset) + @dynamic_property + def libraryPath(self): + def fget(self): + return self.__libraryPath + def fset(self, val): + self.__libraryPath = val + return property(fget=fget, fset=fset) + @dynamic_property + def markerTags(self): + def fget(self): + return self.__markerTags + def fset(self, val): + self.__markerTags = val + return property(fget=fget, fset=fset) + @dynamic_property + def ncxSoup(self): + def fget(self): + return self.__ncxSoup + def fset(self, val): + self.__ncxSoup = val + return property(fget=fget, fset=fset) + @dynamic_property + def opts(self): + def fget(self): + return self.__opts + return property(fget=fget) + @dynamic_property + def playOrder(self): + def fget(self): + return self.__playOrder + def fset(self,val): + self.__playOrder = val + return property(fget=fget, fset=fset) + @dynamic_property + def plugin(self): + def fget(self): + return self.__plugin + return property(fget=fget) + @dynamic_property + def progressInt(self): + def fget(self): + return self.__progressInt + def fset(self, val): + self.__progressInt = val + return property(fget=fget, fset=fset) + @dynamic_property + def progressString(self): + def fget(self): + return self.__progressString + def fset(self, val): + self.__progressString = val + return property(fget=fget, fset=fset) + @dynamic_property + def reporter(self): + def fget(self): + return self.__reporter + def fset(self, val): + self.__reporter = val + return property(fget=fget, fset=fset) + @dynamic_property + def stylesheet(self): + def fget(self): + return self.__stylesheet + def fset(self, val): + self.__stylesheet = val + return property(fget=fget, fset=fset) + @dynamic_property + def thumbs(self): + def fget(self): + return self.__thumbs + def fset(self, val): + self.__thumbs = val + return property(fget=fget, fset=fset) + def thumbWidth(self): + def fget(self): + return self.__thumbWidth + def fset(self, val): + self.__thumbWidth = val + return property(fget=fget, fset=fset) + def thumbHeight(self): + def fget(self): + return self.__thumbHeight + def fset(self, val): + self.__thumbHeight = val + return property(fget=fget, fset=fset) + @dynamic_property + def title(self): + def fget(self): + return self.__title + def fset(self, val): + self.__title = val + return property(fget=fget, fset=fset) + @dynamic_property + def totalSteps(self): + def fget(self): + return self.__totalSteps + return property(fget=fget) + @dynamic_property + def useSeriesPrefixInTitlesSection(self): + def fget(self): + return self.__useSeriesPrefixInTitlesSection + def fset(self, val): + self.__useSeriesPrefixInTitlesSection = val + return property(fget=fget, fset=fset) + @dynamic_property + def verbose(self): + def fget(self): + return self.__verbose + def fset(self, val): + self.__verbose = val + return property(fget=fget, fset=fset) - @dynamic_property - def authorClip(self): - def fget(self): - return self.__authorClip - def fset(self, val): - self.__authorClip = val - return property(fget=fget, fset=fset) - @dynamic_property - def authors(self): - def fget(self): - return self.__authors - def fset(self, val): - self.__authors = val - return property(fget=fget, fset=fset) - @dynamic_property - def basename(self): - def fget(self): - return self.__basename - def fset(self, val): - self.__basename = val - return property(fget=fget, fset=fset) - @dynamic_property - def bookmarked_books(self): - def fget(self): - return self.__bookmarked_books - def fset(self, val): - self.__bookmarked_books = val - return property(fget=fget, fset=fset) - @dynamic_property - def booksByAuthor(self): - def fget(self): - return self.__booksByAuthor - def fset(self, val): - self.__booksByAuthor = val - return property(fget=fget, fset=fset) - @dynamic_property - def booksByDateRead(self): - def fget(self): - return self.__booksByDateRead - def fset(self, val): - self.__booksByDateRead = val - return property(fget=fget, fset=fset) - @dynamic_property - def booksByTitle(self): - def fget(self): - return self.__booksByTitle - def fset(self, val): - self.__booksByTitle = val - return property(fget=fget, fset=fset) - @dynamic_property - def booksByTitle_noSeriesPrefix(self): - def fget(self): - return self.__booksByTitle_noSeriesPrefix - def fset(self, val): - self.__booksByTitle_noSeriesPrefix = val - return property(fget=fget, fset=fset) - @dynamic_property - def catalogPath(self): - def fget(self): - return self.__catalogPath - def fset(self, val): - self.__catalogPath = val - return property(fget=fget, fset=fset) - @dynamic_property - def contentDir(self): - def fget(self): - return self.__contentDir - def fset(self, val): - self.__contentDir = val - return property(fget=fget, fset=fset) - @dynamic_property - def currentStep(self): - def fget(self): - return self.__currentStep - def fset(self, val): - self.__currentStep = val - return property(fget=fget, fset=fset) - @dynamic_property - def creator(self): - def fget(self): - return self.__creator - def fset(self, val): - self.__creator = val - return property(fget=fget, fset=fset) - @dynamic_property - def db(self): - def fget(self): - return self.__db - return property(fget=fget) - @dynamic_property - def descriptionClip(self): - def fget(self): - return self.__descriptionClip - def fset(self, val): - self.__descriptionClip = val - return property(fget=fget, fset=fset) - @dynamic_property - def error(self): - def fget(self): - return self.__error - return property(fget=fget) - @dynamic_property - def generateForKindle(self): - def fget(self): - return self.__generateForKindle - def fset(self, val): - self.__generateForKindle = val - return property(fget=fget, fset=fset) - @dynamic_property - def generateRecentlyRead(self): - def fget(self): - return self.__generateRecentlyRead - def fset(self, val): - self.__generateRecentlyRead = val - return property(fget=fget, fset=fset) - @dynamic_property - def genres(self): - def fget(self): - return self.__genres - def fset(self, val): - self.__genres = val - return property(fget=fget, fset=fset) - @dynamic_property - def genre_tags_dict(self): - def fget(self): - return self.__genre_tags_dict - def fset(self, val): - self.__genre_tags_dict = val - return property(fget=fget, fset=fset) - @dynamic_property - def htmlFileList(self): - def fget(self): - return self.__htmlFileList - def fset(self, val): - self.__htmlFileList = val - return property(fget=fget, fset=fset) - @dynamic_property - def libraryPath(self): - def fget(self): - return self.__libraryPath - def fset(self, val): - self.__libraryPath = val - return property(fget=fget, fset=fset) - @dynamic_property - def markerTags(self): - def fget(self): - return self.__markerTags - def fset(self, val): - self.__markerTags = val - return property(fget=fget, fset=fset) - @dynamic_property - def ncxSoup(self): - def fget(self): - return self.__ncxSoup - def fset(self, val): - self.__ncxSoup = val - return property(fget=fget, fset=fset) - @dynamic_property - def opts(self): - def fget(self): - return self.__opts - return property(fget=fget) - @dynamic_property - def playOrder(self): - def fget(self): - return self.__playOrder - def fset(self,val): - self.__playOrder = val - return property(fget=fget, fset=fset) - @dynamic_property - def plugin(self): - def fget(self): - return self.__plugin - return property(fget=fget) - @dynamic_property - def progressInt(self): - def fget(self): - return self.__progressInt - def fset(self, val): - self.__progressInt = val - return property(fget=fget, fset=fset) - @dynamic_property - def progressString(self): - def fget(self): - return self.__progressString - def fset(self, val): - self.__progressString = val - return property(fget=fget, fset=fset) - @dynamic_property - def reporter(self): - def fget(self): - return self.__reporter - def fset(self, val): - self.__reporter = val - return property(fget=fget, fset=fset) - @dynamic_property - def stylesheet(self): - def fget(self): - return self.__stylesheet - def fset(self, val): - self.__stylesheet = val - return property(fget=fget, fset=fset) - @dynamic_property - def thumbs(self): - def fget(self): - return self.__thumbs - def fset(self, val): - self.__thumbs = val - return property(fget=fget, fset=fset) - def thumbWidth(self): - def fget(self): - return self.__thumbWidth - def fset(self, val): - self.__thumbWidth = val - return property(fget=fget, fset=fset) - def thumbHeight(self): - def fget(self): - return self.__thumbHeight - def fset(self, val): - self.__thumbHeight = val - return property(fget=fget, fset=fset) - @dynamic_property - def title(self): - def fget(self): - return self.__title - def fset(self, val): - self.__title = val - return property(fget=fget, fset=fset) - @dynamic_property - def totalSteps(self): - def fget(self): - return self.__totalSteps - return property(fget=fget) - @dynamic_property - def useSeriesPrefixInTitlesSection(self): - def fget(self): - return self.__useSeriesPrefixInTitlesSection - def fset(self, val): - self.__useSeriesPrefixInTitlesSection = val - return property(fget=fget, fset=fset) - @dynamic_property - def verbose(self): - def fget(self): - return self.__verbose - def fset(self, val): - self.__verbose = val - return property(fget=fget, fset=fset) - - @dynamic_property - def NOT_READ_SYMBOL(self): - def fget(self): - return '' if self.generateForKindle else \ - '%s' % self.opts.read_tag - return property(fget=fget) - @dynamic_property - def READING_SYMBOL(self): - def fget(self): - return '' if self.generateForKindle else \ - '%s' % self.opts.read_tag - return property(fget=fget) - @dynamic_property - def READ_SYMBOL(self): - def fget(self): - return '' if self.generateForKindle else \ - '%s' % self.opts.read_tag - return property(fget=fget) - @dynamic_property - def FULL_RATING_SYMBOL(self): - def fget(self): - return "★" if self.generateForKindle else "*" - return property(fget=fget) - @dynamic_property - def EMPTY_RATING_SYMBOL(self): - def fget(self): - return "☆" if self.generateForKindle else ' ' - return property(fget=fget) - @dynamic_property - def READ_PROGRESS_SYMBOL(self): - def fget(self): - return "▪" if self.generateForKindle else '+' - return property(fget=fget) - @dynamic_property - def UNREAD_PROGRESS_SYMBOL(self): - def fget(self): - return "▫" if self.generateForKindle else '-' - return property(fget=fget) + @dynamic_property + def NOT_READ_SYMBOL(self): + def fget(self): + return '' if self.generateForKindle else \ + '%s' % self.opts.read_tag + return property(fget=fget) + @dynamic_property + def READING_SYMBOL(self): + def fget(self): + return '' if self.generateForKindle else \ + '%s' % self.opts.read_tag + return property(fget=fget) + @dynamic_property + def READ_SYMBOL(self): + def fget(self): + return '' if self.generateForKindle else \ + '%s' % self.opts.read_tag + return property(fget=fget) + @dynamic_property + def FULL_RATING_SYMBOL(self): + def fget(self): + return "★" if self.generateForKindle else "*" + return property(fget=fget) + @dynamic_property + def EMPTY_RATING_SYMBOL(self): + def fget(self): + return "☆" if self.generateForKindle else ' ' + return property(fget=fget) + @dynamic_property + def READ_PROGRESS_SYMBOL(self): + def fget(self): + return "▪" if self.generateForKindle else '+' + return property(fget=fget) + @dynamic_property + def UNREAD_PROGRESS_SYMBOL(self): + def fget(self): + return "▫" if self.generateForKindle else '-' + return property(fget=fget) # Methods def buildSources(self): @@ -1206,7 +1207,6 @@ class EPUB_MOBI(CatalogPlugin): self.generateOPF() self.generateNCXHeader() - self.generateNCXDescriptions("Descriptions") self.generateNCXByAuthor("Authors") if self.opts.generate_titles: self.generateNCXByTitle("Titles") @@ -1215,6 +1215,7 @@ class EPUB_MOBI(CatalogPlugin): if self.generateRecentlyRead: self.generateNCXByDateRead("Recently Read") self.generateNCXByGenre("Genres") + self.generateNCXDescriptions("Descriptions") self.writeNCX() return True @@ -1570,8 +1571,8 @@ class EPUB_MOBI(CatalogPlugin): if title['series']: # title
series series_index brTag = Tag(soup,'br') - title_tokens = title['title'].split(': ') - emTag.insert(0, escape(NavigableString(title_tokens[1]))) + title_tokens = list(title['title'].partition(':')) + emTag.insert(0, escape(NavigableString(title_tokens[2].strip()))) emTag.insert(1, brTag) smallTag = Tag(soup,'small') smallTag.insert(0, escape(NavigableString(title_tokens[0]))) @@ -1747,8 +1748,8 @@ class EPUB_MOBI(CatalogPlugin): nspt = deepcopy(self.booksByTitle) for book in nspt: if book['series']: - tokens = book['title'].split(': ') - book['title'] = '%s (%s)' % (tokens[1], tokens[0]) + tokens = book['title'].partition(':') + book['title'] = '%s (%s)' % (tokens[2].strip(), tokens[0]) book['title_sort'] = self.generateSortTitle(book['title']) nspt = sorted(nspt, key=lambda x:(x['title_sort'].upper(), x['title_sort'].upper())) @@ -1929,7 +1930,7 @@ class EPUB_MOBI(CatalogPlugin): current_series = book['series'] pSeriesTag = Tag(soup,'p') pSeriesTag['class'] = "series" - pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + book['series'])) + pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s Series' % book['series'])) divTag.insert(dtc,pSeriesTag) dtc += 1 if current_series and not book['series']: @@ -2046,7 +2047,7 @@ class EPUB_MOBI(CatalogPlugin): current_series = new_entry['series'] pSeriesTag = Tag(soup,'p') pSeriesTag['class'] = "series" - pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + new_entry['series'])) + pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s Series' % new_entry['series'])) divTag.insert(dtc,pSeriesTag) dtc += 1 if current_series and not new_entry['series']: @@ -2186,8 +2187,8 @@ class EPUB_MOBI(CatalogPlugin): nspt = deepcopy(self.booksByTitle) for book in nspt: if book['series']: - tokens = book['title'].split(': ') - book['title'] = '%s (%s)' % (tokens[1], tokens[0]) + tokens = book['title'].partition(':') + book['title'] = '%s (%s)' % (tokens[2].strip(), tokens[0]) book['title_sort'] = self.generateSortTitle(book['title']) self.booksByDateRange = sorted(nspt, key=lambda x:(x['timestamp'], x['timestamp']),reverse=True) @@ -2683,22 +2684,7 @@ class EPUB_MOBI(CatalogPlugin): # HTML files - add books to manifest and spine sort_descriptions_by = self.booksByAuthor if self.opts.sort_descriptions_by_author \ else self.booksByTitle - for book in sort_descriptions_by: - # manifest - itemTag = Tag(soup, "item") - itemTag['href'] = "content/book_%d.html" % int(book['id']) - itemTag['id'] = "book%d" % int(book['id']) - itemTag['media-type'] = "application/xhtml+xml" - manifest.insert(mtc, itemTag) - mtc += 1 - - # spine - itemrefTag = Tag(soup, "itemref") - itemrefTag['idref'] = "book%d" % int(book['id']) - spine.insert(stc, itemrefTag) - stc += 1 - - # Add other html_files to manifest and spine + # Add html_files to manifest and spine for file in self.htmlFileList: itemTag = Tag(soup, "item") @@ -2734,6 +2720,21 @@ class EPUB_MOBI(CatalogPlugin): spine.insert(stc, itemrefTag) stc += 1 + for book in sort_descriptions_by: + # manifest + itemTag = Tag(soup, "item") + itemTag['href'] = "content/book_%d.html" % int(book['id']) + itemTag['id'] = "book%d" % int(book['id']) + itemTag['media-type'] = "application/xhtml+xml" + manifest.insert(mtc, itemTag) + mtc += 1 + + # spine + itemrefTag = Tag(soup, "itemref") + itemrefTag['idref'] = "book%d" % int(book['id']) + spine.insert(stc, itemrefTag) + stc += 1 + # Guide referenceTag = Tag(soup, "reference") referenceTag['type'] = 'masthead' @@ -2821,15 +2822,15 @@ class EPUB_MOBI(CatalogPlugin): navLabelTag = Tag(ncx_soup, "navLabel") textTag = Tag(ncx_soup, "text") if book['series']: - tokens = book['title'].split(': ') + tokens = list(book['title'].partition(':')) if self.generateForKindle: # Don't include Author for Kindle textTag.insert(0, NavigableString(self.formatNCXText('%s (%s)' % \ - (tokens[1], tokens[0]), dest='title'))) + (tokens[2].strip(), tokens[0]), dest='title'))) else: # Include Author for non-Kindle textTag.insert(0, NavigableString(self.formatNCXText('%s · %s (%s)' % \ - (tokens[1], book['author'], tokens[0]), dest='title'))) + (tokens[2].strip(), book['author'], tokens[0]), dest='title'))) else: if self.generateForKindle: # Don't include Author for Kindle @@ -3752,7 +3753,7 @@ class EPUB_MOBI(CatalogPlugin): current_series = book['series'] pSeriesTag = Tag(soup,'p') pSeriesTag['class'] = "series" - pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + book['series'])) + pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s Series' % book['series'])) divTag.insert(dtc,pSeriesTag) dtc += 1 @@ -3897,7 +3898,7 @@ class EPUB_MOBI(CatalogPlugin):

-

+
@@ -4231,6 +4232,7 @@ class EPUB_MOBI(CatalogPlugin): op = opts.output_profile if op is None: op = 'default' + if opts.connected_device['name'] and 'kindle' in opts.connected_device['name'].lower(): opts.connected_kindle = True if opts.connected_device['serial'] and opts.connected_device['serial'][:4] in ['B004','B005']: @@ -4256,7 +4258,8 @@ class EPUB_MOBI(CatalogPlugin): opts.exclude_genre = '\[^.\]' build_log.append(" converting empty exclude_genre to '\[^.\]'") - if opts.connected_device['name']: + if opts.connected_device['is_device_connected'] and \ + opts.connected_device['kind'] == 'device': if opts.connected_device['serial']: build_log.append(u" connected_device: '%s' #%s%s " % \ (opts.connected_device['name'], @@ -4270,6 +4273,8 @@ class EPUB_MOBI(CatalogPlugin): for storage in opts.connected_device['storage']: if storage: build_log.append(u" mount point: %s" % storage) + else: + build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) opts_dict = vars(opts) if opts_dict['ids']: diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index ea42fe5998..3c2e7c09cd 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -672,7 +672,14 @@ def command_catalog(args, dbpath): # No support for connected device in CLI environment # Parallel initialization in calibre.gui2.tools:generate_catalog() - opts.connected_device = { 'storage':None,'serial':None,'save_template':None,'name':None} + opts.connected_device = { + 'is_device_connected': False, + 'kind': device_manager.connected_device_kind, + 'name': None, + 'save_template': None, + 'serial': None, + 'storage': None, + } with plugin: plugin.run(args[1], opts, get_db(dbpath, opts)) From 4aa06dae4dbc86ed86ff74a5c08280b8e73fc239 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Sep 2010 15:43:51 -0600 Subject: [PATCH 20/45] Add icon to bar in preferences window --- src/calibre/gui2/preferences/main.py | 44 +++++++++++++++++++++------- 1 file changed, 34 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index 6524faec2d..5e004fb444 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -10,7 +10,7 @@ from functools import partial from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \ - QToolBar, QSize, pyqtSignal, QSizePolicy, QToolButton, QAction, \ + QToolBar, QSize, pyqtSignal, QPixmap, QToolButton, QAction, \ QPushButton, QHBoxLayout from calibre.constants import __appname__, __version__, islinux, isosx @@ -20,6 +20,8 @@ from calibre.gui2.preferences import init_gui, AbortCommit, get_plugin from calibre.customize.ui import preferences_plugins from calibre.utils.ordered_dict import OrderedDict +ICON_SIZE = 32 + class StatusBar(QStatusBar): # {{{ def __init__(self, parent=None): @@ -42,6 +44,33 @@ class StatusBar(QStatusBar): # {{{ # }}} +class BarTitle(QWidget): + + def __init__(self, parent=None): + QWidget.__init__(self, parent) + self._layout = QHBoxLayout() + self.setLayout(self._layout) + self._layout.addStretch(10) + self.icon = QLabel('') + self.icon.setMaximumHeight(ICON_SIZE) + self._layout.addWidget(self.icon) + self.title = QLabel('') + self.title.setStyleSheet('QLabel { font-weight: bold }') + self.title.setAlignment(Qt.AlignLeft | Qt.AlignCenter) + self._layout.addWidget(self.title) + self._layout.addStretch(10) + + def show_plugin(self, plugin): + self.pmap = QPixmap(plugin.icon).scaled(ICON_SIZE, ICON_SIZE, + Qt.KeepAspectRatio, Qt.SmoothTransformation) + self.icon.setPixmap(self.pmap) + self.title.setText(plugin.gui_name) + tt = plugin.description + self.setStatusTip(tt) + tt = textwrap.fill(tt) + self.setToolTip(tt) + self.setWhatsThis(tt) + class Category(QWidget): # {{{ plugin_activated = pyqtSignal(object) @@ -189,7 +218,7 @@ class Preferences(QMainWindow): self.bar = QToolBar(self) self.addToolBar(self.bar) self.bar.setVisible(False) - self.bar.setIconSize(QSize(32, 32)) + self.bar.setIconSize(QSize(ICON_SIZE, ICON_SIZE)) self.bar.setMovable(False) self.bar.setFloatable(False) self.bar.setToolButtonStyle(Qt.ToolButtonTextBesideIcon) @@ -197,13 +226,8 @@ class Preferences(QMainWindow): self.commit) self.cancel_action = self.bar.addAction(QIcon(I('window-close.png')), _('&Cancel'), self.cancel) - self.bar_filler = QLabel('') - self.bar_filler.setSizePolicy(QSizePolicy.Expanding, - QSizePolicy.Preferred) - self.bar_filler.setStyleSheet( - 'QLabel { font-weight: bold }') - self.bar_filler.setAlignment(Qt.AlignHCenter | Qt.AlignCenter) - self.bar.addWidget(self.bar_filler) + self.bar_title = BarTitle(self.bar) + self.bar.addWidget(self.bar_title) self.restore_action = self.bar.addAction(QIcon(I('clear_left.png')), _('Restore &defaults'), self.restore_defaults) for ac, tt in [('apply', _('Save changes')), @@ -247,7 +271,7 @@ class Preferences(QMainWindow): self.restore_action.setToolTip(textwrap.fill(tt)) self.restore_action.setWhatsThis(textwrap.fill(tt)) self.restore_action.setStatusTip(tt) - self.bar_filler.setText(plugin.gui_name) + self.bar_title.show_plugin(plugin) self.setWindowIcon(QIcon(plugin.icon)) self.bar.setVisible(True) From 6132923db6382f32728e6a37778b233eadbb3980 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Sep 2010 16:44:20 -0600 Subject: [PATCH 21/45] Nicer close button on OSX in the preferencs window --- src/calibre/gui2/preferences/main.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index 5e004fb444..d55f2d90d1 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -11,7 +11,7 @@ from functools import partial from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ QScrollArea, QStackedWidget, QVBoxLayout, QLabel, QFrame, QKeySequence, \ QToolBar, QSize, pyqtSignal, QPixmap, QToolButton, QAction, \ - QPushButton, QHBoxLayout + QDialogButtonBox, QHBoxLayout from calibre.constants import __appname__, __version__, islinux, isosx from calibre.gui2 import gprefs, min_available_height, available_width, \ @@ -52,7 +52,6 @@ class BarTitle(QWidget): self.setLayout(self._layout) self._layout.addStretch(10) self.icon = QLabel('') - self.icon.setMaximumHeight(ICON_SIZE) self._layout.addWidget(self.icon) self.title = QLabel('') self.title.setStyleSheet('QLabel { font-weight: bold }') @@ -117,7 +116,6 @@ class Category(QWidget): # {{{ class Browser(QScrollArea): # {{{ show_plugin = pyqtSignal(object) - close_signal = pyqtSignal() def __init__(self, parent=None): QScrollArea.__init__(self, parent) @@ -148,14 +146,6 @@ class Browser(QScrollArea): # {{{ self.container = QWidget(self) self.container.setLayout(self._layout) self.setWidget(self.container) - if isosx: - self._osxl = QHBoxLayout() - self.close_button = QPushButton(_('Close')) - self.close_button.clicked.connect(self.close_requested) - self._osxl.addStretch(10) - self._osxl.addWidget(self.close_button) - #self._osxl.addStretch(10) - self._layout.addLayout(self._osxl) for name, plugins in self.category_map.items(): w = Category(name, plugins, self) @@ -163,8 +153,6 @@ class Browser(QScrollArea): # {{{ self._layout.addWidget(w) w.plugin_activated.connect(self.show_plugin.emit) - def close_requested(self, *args): - self.close_signal.emit() # }}} @@ -206,9 +194,15 @@ class Preferences(QMainWindow): self.setStatusBar(self.status_bar) self.stack = QStackedWidget(self) - self.setCentralWidget(self.stack) + self.cw = QWidget(self) + self.cw.setLayout(QVBoxLayout()) + self.cw.layout().addWidget(self.stack) + self.bb = QDialogButtonBox(QDialogButtonBox.Close) + self.cw.layout().addWidget(self.bb) + self.bb.rejected.connect(self.close, type=Qt.QueuedConnection) + self.setCentralWidget(self.cw) + self.bb.setVisible(isosx) self.browser = Browser(self) - self.browser.close_signal.connect(self.close, type=Qt.QueuedConnection) self.browser.show_plugin.connect(self.show_plugin) self.stack.addWidget(self.browser) self.scroll_area = QScrollArea(self) From 1ab350b02202b673954e583a21615147f3bde393 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 6 Sep 2010 16:54:18 -0600 Subject: [PATCH 22/45] Cleaner connection to db when generating catalog --- src/calibre/gui2/convert/gui_conversion.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/calibre/gui2/convert/gui_conversion.py b/src/calibre/gui2/convert/gui_conversion.py index 148dc328ad..3072b8757a 100644 --- a/src/calibre/gui2/convert/gui_conversion.py +++ b/src/calibre/gui2/convert/gui_conversion.py @@ -27,13 +27,8 @@ def gui_catalog(fmt, title, dbspec, ids, out_file_name, sync, fmt_options, conne notification=DummyReporter(), log=None): if log is None: log = Log() - if dbspec is None: - from calibre.utils.config import prefs - from calibre.library.database2 import LibraryDatabase2 - dbpath = prefs['library_path'] - db = LibraryDatabase2(dbpath) - else: # To be implemented in the future - pass + from calibre.library import db + db = db() # Create a minimal OptionParser that we can append to parser = OptionParser() From 3404b367b966330937cd0ef08d690c60e216ad29 Mon Sep 17 00:00:00 2001 From: GRiker Date: Tue, 7 Sep 2010 04:23:10 -0700 Subject: [PATCH 23/45] GwR changes via CHaley --- src/calibre/gui2/actions/catalog.py | 9 +++++++-- src/calibre/library/catalog.py | 3 +-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py index d965c6d814..74d3874c1e 100644 --- a/src/calibre/gui2/actions/catalog.py +++ b/src/calibre/gui2/actions/catalog.py @@ -26,14 +26,19 @@ class GenerateCatalogAction(InterfaceAction): rows = xrange(self.gui.library_view.model().rowCount(QModelIndex())) ids = map(self.gui.library_view.model().id, rows) - dbspec = None if not ids: return error_dialog(self.gui, _('No books selected'), _('No books selected to generate catalog for'), show=True) + db = self.gui.library_view.model().db + dbspec = {} + for id in ids: + dbspec[id] = {'ondevice':db.ondevice(id, index_is_id=True)} + db.catalog_plugin_on_device_temp_mapping = dbspec + # Calling gui2.tools:generate_catalog() - ret = generate_catalog(self.gui, dbspec, ids, self.gui.device_manager) + ret = generate_catalog(self.gui, db, ids, self.gui.device_manager) if ret is None: return diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 75e51ff3ca..a23b86bdef 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -104,8 +104,7 @@ class CSV_XML(CatalogPlugin): # Output the entry fields for entry in data: - print "%s [%s]" % (entry['title'],entry['id']) - print "ondevice: %s" % db.ondevice(entry['id'], index_is_id=True) + print "%s [%s] ondevice: %s" % (entry['title'],entry['id'], repr(db.catalog_plugin_on_device_temp_mapping[entry['id']])) outstr = [] for field in fields: item = entry[field] From 563b6d0b2e85062706ed5e72b928e79207deadfc Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 7 Sep 2010 13:04:45 +0100 Subject: [PATCH 24/45] Fix problem where book matching did not record how books were matched, which prevented downstream matching such as 'delete matching books' --- src/calibre/gui2/device.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index 5fb8b1028b..aba949230f 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1393,10 +1393,12 @@ class DeviceMixin(object): # {{{ self.db_book_uuid_cache[mi.uuid] = mi # Now iterate through all the books on the device, setting the - # in_library field Fastest and most accurate key is the uuid. Second is + # in_library field. Fastest and most accurate key is the uuid. Second is # the application_id, which is really the db key, but as this can # accidentally match across libraries we also verify the title. The - # db_id exists on Sony devices. Fallback is title and author match + # db_id exists on Sony devices. Fallback is title and author match. + # We set the application ID so that we can reproduce book matching, + # necessary for identifying copies of books. update_metadata = prefs['manage_device_metadata'] == 'on_connect' for booklist in booklists: @@ -1418,12 +1420,15 @@ class DeviceMixin(object): # {{{ if d is not None: if getattr(book, 'application_id', None) in d['db_ids']: book.in_library = True + # application already matches db_id, so no need to set it if update_metadata: book.smart_update(d['db_ids'][book.application_id], replace_metadata=True) continue if book.db_id in d['db_ids']: book.in_library = True + book.application_id = \ + d['db_ids'][book.db_id].application_id if update_metadata: book.smart_update(d['db_ids'][book.db_id], replace_metadata=True) @@ -1435,11 +1440,15 @@ class DeviceMixin(object): # {{{ book_authors = re.sub('(?u)\W|[_]', '', book_authors) if book_authors in d['authors']: book.in_library = True + book.application_id = \ + d['authors'][book_authors].application_id if update_metadata: book.smart_update(d['authors'][book_authors], replace_metadata=True) elif book_authors in d['author_sort']: book.in_library = True + book.application_id = \ + d['author_sort'][book_authors].application_id if update_metadata: book.smart_update(d['author_sort'][book_authors], replace_metadata=True) From d9d1981fbea307ab1d2799856c7e5c7bd2a849f1 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 7 Sep 2010 14:18:14 +0100 Subject: [PATCH 25/45] Add a count of how many books on the device matched a particular copy in the library. Useful because books on the device can accumulate when sent after changing metadata used in the file path. The UUID doesn't change, so we can detect that they are copies of the same book. searching for 'ondevice:books' will find instances of more than one copy. --- src/calibre/gui2/device.py | 33 +++++++++++++++++++++++++++++--- src/calibre/library/database2.py | 5 +++-- 2 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index aba949230f..099cae4ba6 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -1306,16 +1306,26 @@ class DeviceMixin(object): # {{{ self.library_view.model().refresh_ids(list(changed)) def book_on_device(self, id, format=None, reset=False): - loc = [None, None, None] + ''' + Return an indication of whether the given book represented by its db id + is on the currently connected device. It returns a 4 element list. The + first three elements represent memory locations main, carda, and cardb, + and are true if the book is identifiably in that memory. The fourth + is the a count of how many instances of the book were found across all + the memory locations. + ''' + loc = [None, None, None, 0] if reset: self.book_db_title_cache = None self.book_db_uuid_cache = None + self.book_db_id_counts = None return if self.book_db_title_cache is None: self.book_db_title_cache = [] self.book_db_uuid_cache = [] + self.book_db_id_counts = {} for i, l in enumerate(self.booklists()): self.book_db_title_cache.append({}) self.book_db_uuid_cache.append(set()) @@ -1333,6 +1343,10 @@ class DeviceMixin(object): # {{{ db_id = book.db_id if db_id is not None: self.book_db_title_cache[i][book_title]['db_ids'].add(db_id) + # increment the count of books on the device with this + # db_id. + c = self.book_db_id_counts.get(db_id, 0) + self.book_db_id_counts[db_id] = c + 1 uuid = getattr(book, 'uuid', None) if uuid is not None: self.book_db_uuid_cache[i].add(uuid) @@ -1351,7 +1365,13 @@ class DeviceMixin(object): # {{{ if mi.authors and \ re.sub('(?u)\W|[_]', '', authors_to_string(mi.authors).lower()) \ in cache['authors']: + # We really shouldn't get here, because set_books_in_library + # should have set the db_ids for the books, and therefore + # the if just above should have found them. Mark the book + # anyway, and print a message about the situation loc[i] = True + print 'book_on_device: matched title/author but not db_id!', \ + mi.title, authors_to_string(mi.authors) continue # Also check author sort, because it can be used as author in # some formats @@ -1360,9 +1380,16 @@ class DeviceMixin(object): # {{{ in cache['authors']: loc[i] = True continue + loc[3] = self.book_db_id_counts.get(id, 0) return loc def set_books_in_library(self, booklists, reset=False): + ''' + Set the ondevice indications in the device database. + This method should be called before book_on_device is called, because + it sets the application_id for matched books. Book_on_device uses that + to both speed up matching and to count matches. + ''' # Force a reset if the caches are not initialized if reset or not hasattr(self, 'db_book_title_cache'): # It might be possible to get here without having initialized the @@ -1428,7 +1455,7 @@ class DeviceMixin(object): # {{{ if book.db_id in d['db_ids']: book.in_library = True book.application_id = \ - d['db_ids'][book.db_id].application_id + d['db_ids'][book.db_id].application_id if update_metadata: book.smart_update(d['db_ids'][book.db_id], replace_metadata=True) @@ -1441,7 +1468,7 @@ class DeviceMixin(object): # {{{ if book_authors in d['authors']: book.in_library = True book.application_id = \ - d['authors'][book_authors].application_id + d['authors'][book_authors].application_id if update_metadata: book.smart_update(d['authors'][book_authors], replace_metadata=True) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 23ec60d320..52b5f2d4e6 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -640,16 +640,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): def book_on_device_string(self, id): loc = [] + count = 0 on = self.book_on_device(id) if on is not None: - m, a, b = on + m, a, b, count = on if m is not None: loc.append(_('Main')) if a is not None: loc.append(_('Card A')) if b is not None: loc.append(_('Card B')) - return ', '.join(loc) + return ', '.join(loc) + ((' (%s books)'%count) if count > 1 else '') def set_book_on_device_func(self, func): self.book_on_device_func = func From 800db18ff1b14b3452d2340d9161c7400aa5cf44 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 09:11:48 -0600 Subject: [PATCH 26/45] Add a close button to preferences window --- src/calibre/gui2/preferences/__init__.py | 5 +++++ src/calibre/gui2/preferences/main.py | 5 ++--- src/calibre/manual/plugins.rst | 8 ++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index 045d7ceebb..b9614bcf8c 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -18,6 +18,11 @@ class AbortCommit(Exception): class ConfigWidgetInterface(object): + ''' + This class defines the interface that all widgets displayed in the + Preferences dialog must implement. To create a plugin for a new + ''' + changed_signal = None supports_restoring_to_defaults = True restore_defaults_desc = _('Restore settings to default values. ' diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index d55f2d90d1..f6a20d74d9 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -13,7 +13,7 @@ from PyQt4.Qt import QMainWindow, Qt, QIcon, QStatusBar, QFont, QWidget, \ QToolBar, QSize, pyqtSignal, QPixmap, QToolButton, QAction, \ QDialogButtonBox, QHBoxLayout -from calibre.constants import __appname__, __version__, islinux, isosx +from calibre.constants import __appname__, __version__, islinux from calibre.gui2 import gprefs, min_available_height, available_width, \ warning_dialog from calibre.gui2.preferences import init_gui, AbortCommit, get_plugin @@ -164,7 +164,7 @@ class Preferences(QMainWindow): self.must_restart = False self.committed = False - self.resize(900, 760 if isosx else 710) + self.resize(900, 720) nh, nw = min_available_height()-25, available_width()-10 if nh < 0: nh = 800 @@ -201,7 +201,6 @@ class Preferences(QMainWindow): self.cw.layout().addWidget(self.bb) self.bb.rejected.connect(self.close, type=Qt.QueuedConnection) self.setCentralWidget(self.cw) - self.bb.setVisible(isosx) self.browser = Browser(self) self.browser.show_plugin.connect(self.show_plugin) self.stack.addWidget(self.browser) diff --git a/src/calibre/manual/plugins.rst b/src/calibre/manual/plugins.rst index 8b6919db90..3aca648a23 100644 --- a/src/calibre/manual/plugins.rst +++ b/src/calibre/manual/plugins.rst @@ -165,3 +165,11 @@ User Interface Actions :members: :member-order: bysource +Preferences Plugins +-------------------------- + +.. autoclass:: calibre.customize.PreferencesPlugin + :show-inheritance: + :members: + :member-order: bysource + From a9dd65b819079dd0151823bc7ed2ac186474e0ef Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 09:20:26 -0600 Subject: [PATCH 27/45] Improved nrcnext --- resources/recipes/ncrnext.recipe | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/resources/recipes/ncrnext.recipe b/resources/recipes/ncrnext.recipe index e03da301fa..6585cc9665 100644 --- a/resources/recipes/ncrnext.recipe +++ b/resources/recipes/ncrnext.recipe @@ -22,10 +22,19 @@ class NrcNextRecipe(BasicNewsRecipe): remove_tags = [] remove_tags.append(dict(name = 'div', attrs = {'class' : 'meta'})) + remove_tags.append(dict(name = 'p', attrs = {'class' : 'meta'})) remove_tags.append(dict(name = 'div', attrs = {'class' : 'datumlabel'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'sharing-is-caring'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'navigation'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'reageer'})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'comment odd alt thread-odd thread-alt depth-1 reactie '})) + remove_tags.append(dict(name = 'div', attrs = {'class' : 'comment even thread-even depth-1 reactie '})) remove_tags.append(dict(name = 'ul', attrs = {'class' : 'cats single'})) remove_tags.append(dict(name = 'ul', attrs = {'class' : 'cats onderwerpen'})) remove_tags.append(dict(name = 'ul', attrs = {'class' : 'cats rubrieken'})) + remove_tags.append(dict(name = 'h3', attrs = {'class' : 'reacties'})) + + extra_css = ''' body {font-family: verdana, arial, helvetica, geneva, sans-serif; text-align: left;} @@ -41,20 +50,18 @@ class NrcNextRecipe(BasicNewsRecipe): feeds[u'koken'] = u'http://www.nrcnext.nl/koken/' feeds[u'geld & werk'] = u'http://www.nrcnext.nl/geld-en-werk/' feeds[u'vandaag'] = u'http://www.nrcnext.nl' - feeds[u'city life in afrika'] = u'http://www.nrcnext.nl/city-life-in-afrika/' + # feeds[u'city life in afrika'] = u'http://www.nrcnext.nl/city-life-in-afrika/' answer = [] articles = {} indices = [] for index, feed in feeds.items() : soup = self.index_to_soup(feed) - - for post in soup.findAll(True, attrs={'class' : 'post'}) : + for post in soup.findAll(True, attrs={'class' : 'post '}) : # Find the links to the actual articles and rember the location they're pointing to and the title a = post.find('a', attrs={'rel' : 'bookmark'}) href = a['href'] title = self.tag_to_string(a) - if index == 'columnisten' : # In this feed/page articles can be written by more than one author. # It is nice to see their names in the titles. @@ -74,7 +81,8 @@ class NrcNextRecipe(BasicNewsRecipe): indices.append(index) # Now, sort the temporary list of feeds in the order they appear on the website - indices = self.sort_index_by(indices, {u'columnisten' : 1, u'koken' : 3, u'geld & werk' : 2, u'vandaag' : 0, u'city life in afrika' : 4}) + # indices = self.sort_index_by(indices, {u'columnisten' : 1, u'koken' : 3, u'geld & werk' : 2, u'vandaag' : 0, u'city life in afrika' : 4}) + indices = self.sort_index_by(indices, {u'columnisten' : 1, u'koken' : 3, u'geld & werk' : 2, u'vandaag' : 0}) # Apply this sort order to the actual list of feeds and articles answer = [(key, articles[key]) for key in indices if articles.has_key(key)] From 58ed4caf9130a645d463355aee67c800ad3b9b17 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 09:47:46 -0600 Subject: [PATCH 28/45] Fix #6733 (Updated recipe for Mexican newspaper La Jornada) --- resources/recipes/la_jornada.recipe | 42 +++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/resources/recipes/la_jornada.recipe b/resources/recipes/la_jornada.recipe index 2e1a3bb50d..afeae08201 100644 --- a/resources/recipes/la_jornada.recipe +++ b/resources/recipes/la_jornada.recipe @@ -1,15 +1,16 @@ __license__ = 'GPL v3' -__copyright__ = '2010, Darko Miletic ' +__copyright__ = '2010, Darko Miletic , Rogelio Domínguez ' ''' www.jornada.unam.mx ''' +import re from calibre import strftime from calibre.web.feeds.news import BasicNewsRecipe class LaJornada_mx(BasicNewsRecipe): title = 'La Jornada (Mexico)' - __author__ = 'Darko Miletic' + __author__ = 'Darko Miletic/Rogelio Domínguez' description = 'Noticias del diario mexicano La Jornada' publisher = 'DEMOS, Desarrollo de Medios, S.A. de C.V.' category = 'news, Mexico' @@ -20,12 +21,26 @@ class LaJornada_mx(BasicNewsRecipe): use_embedded_content = False language = 'es' remove_empty_feeds = True - cover_url = strftime("http://www.jornada.unam.mx/%Y/%m/%d/planitas/portadita.jpg") + cover_url = strftime("http://www.jornada.unam.mx/%Y/%m/%d/portada.pdf") masthead_url = 'http://www.jornada.unam.mx/v7.0/imagenes/la-jornada-trans.png' + publication_type = 'newspaper' extra_css = """ body{font-family: "Times New Roman",serif } .cabeza{font-size: xx-large; font-weight: bold } - .credito-articulo{font-size: 1.3em} + .documentFirstHeading{font-size: xx-large; font-weight: bold } + .credito-articulo{font-variant: small-caps; font-weight: bold } + .foto{text-align: center} + .pie-foto{font-size: 0.9em} + .credito{font-weight: bold; margin-left: 1em} + .credito-autor{font-variant: small-caps; font-weight: bold } + .credito-titulo{text-align: right} + .hemero{text-align: right; font-size: 0.9em; margin-bottom: 0.5em } + .loc{font-weight: bold} + .carton{text-align: center} + .credit{font-weight: bold} + .text{margin-top: 1.4em} + p.inicial{display: inline; font-size: xx-large; font-weight: bold} + p.s-s{display: inline; text-indent: 0} """ conversion_options = { @@ -35,15 +50,21 @@ class LaJornada_mx(BasicNewsRecipe): , 'language' : language } + preprocess_regexps = [ + (re.compile( r'
(.*)

' + ,re.DOTALL|re.IGNORECASE) + ,lambda match: '

' + match.group(1) + '

') + ] + keep_only_tags = [ - dict(name='div', attrs={'class':['documentContent','cabeza','sumarios','text']}) + dict(name='div', attrs={'class':['documentContent','cabeza','sumarios','credito-articulo','text','carton']}) ,dict(name='div', attrs={'id':'renderComments'}) ] - remove_tags = [dict(name='div', attrs={'class':'buttonbar'})] + remove_tags = [dict(name='div', attrs={'class':['buttonbar','comment-cont']})] feeds = [ - (u'Ultimas noticias' , u'http://www.jornada.unam.mx/ultimas/news/RSS' ) - ,(u'Opinion' , u'http://www.jornada.unam.mx/rss/opinion.xml' ) + (u'Opinion' , u'http://www.jornada.unam.mx/rss/opinion.xml' ) + ,(u'Cartones' , u'http://www.jornada.unam.mx/rss/cartones.xml' ) ,(u'Politica' , u'http://www.jornada.unam.mx/rss/politica.xml' ) ,(u'Economia' , u'http://www.jornada.unam.mx/rss/economia.xml' ) ,(u'Mundo' , u'http://www.jornada.unam.mx/rss/mundo.xml' ) @@ -55,6 +76,7 @@ class LaJornada_mx(BasicNewsRecipe): ,(u'Gastronomia' , u'http://www.jornada.unam.mx/rss/gastronomia.xml' ) ,(u'Espectaculos' , u'http://www.jornada.unam.mx/rss/espectaculos.xml' ) ,(u'Deportes' , u'http://www.jornada.unam.mx/rss/deportes.xml' ) + ,(u'Ultimas noticias' , u'http://www.jornada.unam.mx/ultimas/news/RSS' ) ] def preprocess_html(self, soup): @@ -62,3 +84,7 @@ class LaJornada_mx(BasicNewsRecipe): del item['style'] return soup + def get_article_url(self, article): + rurl = article.get('link', None) + return rurl.rpartition('&partner=')[0] + From 6823d962f6d6bdca4cf5ebeccf9ab8695532f482 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 11:04:52 -0600 Subject: [PATCH 29/45] Use proper technique for displaying default message in status bars --- src/calibre/gui2/init.py | 18 ++++++------------ src/calibre/gui2/preferences/main.py | 15 ++++++--------- 2 files changed, 12 insertions(+), 21 deletions(-) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 74f511e6a2..042e0c578d 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -145,20 +145,23 @@ class StatusBar(QStatusBar): # {{{ self._font = QFont() self._font.setBold(True) self.setFont(self._font) + self.defmsg = QLabel(self.default_message) + self.defmsg.setFont(self._font) + self.addWidget(self.defmsg) def initialize(self, systray=None): self.systray = systray self.notifier = get_notifier(systray) - self.messageChanged.connect(self.message_changed, - type=Qt.QueuedConnection) - self.message_changed('') def device_connected(self, devname): self.device_string = _('Connected ') + devname + self.defmsg.setText(self.default_message + ' ..::.. ' + + self.device_string) self.clearMessage() def device_disconnected(self): self.device_string = '' + self.defmsg.setText(self.default_message) self.clearMessage() def new_version_available(self, ver, url): @@ -188,15 +191,6 @@ class StatusBar(QStatusBar): # {{{ def clear_message(self): self.clearMessage() - def message_changed(self, msg): - if not msg or msg.isEmpty() or msg.isNull() or \ - not unicode(msg).strip(): - extra = '' - if self.device_string: - extra = ' ..::.. ' + self.device_string - self.showMessage(self.default_message + extra) - - # }}} class LayoutMixin(object): # {{{ diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index f6a20d74d9..a15a6dcb22 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -33,18 +33,13 @@ class StatusBar(QStatusBar): # {{{ self._font.setBold(True) self.setFont(self._font) - self.messageChanged.connect(self.message_changed, - type=Qt.QueuedConnection) - self.message_changed('') - - def message_changed(self, msg): - if not msg or msg.isEmpty() or msg.isNull() or \ - not unicode(msg).strip(): - self.showMessage(self.default_message) + self.w = QLabel(self.default_message) + self.w.setFont(self._font) + self.addWidget(self.w) # }}} -class BarTitle(QWidget): +class BarTitle(QWidget): # {{{ def __init__(self, parent=None): QWidget.__init__(self, parent) @@ -70,6 +65,8 @@ class BarTitle(QWidget): self.setToolTip(tt) self.setWhatsThis(tt) +# }}} + class Category(QWidget): # {{{ plugin_activated = pyqtSignal(object) From 16b9abe24cab71334d6eec5d11be0badddf26505 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 12:34:52 -0600 Subject: [PATCH 30/45] Document the preferences plugins system --- src/calibre/customize/__init__.py | 10 +++- src/calibre/gui2/preferences/__init__.py | 58 +++++++++++++++++++++++- src/calibre/manual/plugins.rst | 8 ++++ 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/src/calibre/customize/__init__.py b/src/calibre/customize/__init__.py index 083bc03723..238c491158 100644 --- a/src/calibre/customize/__init__.py +++ b/src/calibre/customize/__init__.py @@ -371,6 +371,13 @@ class InterfaceActionBase(Plugin): # {{{ class PreferencesPlugin(Plugin): # {{{ + ''' + A plugin representing a widget displayed in the Preferences dialog. + + This plugin has only one important method :meth:`create_widget`. The + various fields of the plugin control how it is categorized in the UI. + ''' + supported_platforms = ['windows', 'osx', 'linux'] author = 'Kovid Goyal' type = _('Preferences') @@ -406,7 +413,8 @@ class PreferencesPlugin(Plugin): # {{{ def create_widget(self, parent=None): ''' Create and return the actual Qt widget used for setting this group of - preferences. The widget must implement the ConfigWidgetInterface. + preferences. The widget must implement the + :class:`calibre.gui2.preferences.ConfigWidgetInterface`. The default implementation uses :attr:`config_widget` to instantiate the widget. diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index b9614bcf8c..eb8740114a 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -20,21 +20,42 @@ class ConfigWidgetInterface(object): ''' This class defines the interface that all widgets displayed in the - Preferences dialog must implement. To create a plugin for a new + Preferences dialog must implement. See :class:`ConfigWidgetBase` for + a base class that implements this interface and defines various conveninece + methods as well. ''' + #: This signal must be emitted whenever the user changes a value in this + #: widget changed_signal = None + + #: Set to True iff the :meth:`restore_to_defaults` method is implemented. supports_restoring_to_defaults = True + + #: The tooltip for the Restore to defaults button restore_defaults_desc = _('Restore settings to default values. ' 'You have to click Apply to actually save the default settings.') def genesis(self, gui): + ''' + Called once before the widget is displayed, should perform any + necessary setup. + + :param gui: The main calibre graphical user interface + ''' raise NotImplementedError() def initialize(self): + ''' + Should set all config values to their initial values (the values + stored in the config files). + ''' raise NotImplementedError() def restore_defaults(self): + ''' + Should set all config values to their defaults. + ''' pass def commit(self): @@ -47,6 +68,12 @@ class ConfigWidgetInterface(object): return False def refresh_gui(self, gui): + ''' + Called once after this widget is committed. Responsible for causing the + gui to reread any changed settings. Note that by default the GUI + re-initializes various elements anyway, so most widgets won't need to + use this method. + ''' pass class Setting(object): @@ -175,6 +202,20 @@ class CommaSeparatedList(Setting): class ConfigWidgetBase(QWidget, ConfigWidgetInterface): + ''' + Base class that contains code to easily add standard config widgets like + checkboxes, combo boxes, text fields and so on. See the :meth:`register` + method. + + This class automatically handles change notification, resetting to default, + translation between gui objects and config objects, etc. for registered + settings. + + If your config widget inherits from this class but includes setting that + are not registered, you should override the :class:`ConfigWidgetInterface` methods + and call the base class methods inside the overrides. + ''' + changed_signal = pyqtSignal() supports_restoring_to_defaults = True @@ -186,6 +227,21 @@ class ConfigWidgetBase(QWidget, ConfigWidgetInterface): def register(self, name, config_obj, gui_name=None, choices=None, restart_required=False, empty_string_is_None=True, setting=Setting): + ''' + Register a setting. + + :param name: The setting name + :param config: The config object that reads/writes the setting + :param gui_name: The name of the GUI object that presents an interface + to change the setting. By default it is assumed to be + ``'opt_' + name``. + :param choices: If this setting is a multiple choice (combobox) based + setting, the list of choices. The list is a list of two + element tuples of the form: ``[(gui name, value), ...]`` + :param setting: The class responsible for managing this setting. The + default class handles almost all cases, so this param + is rarely used. + ''' setting = setting(name, config_obj, self, gui_name=gui_name, choices=choices, restart_required=restart_required, empty_string_is_None=empty_string_is_None) diff --git a/src/calibre/manual/plugins.rst b/src/calibre/manual/plugins.rst index 3aca648a23..1b9b47ed3d 100644 --- a/src/calibre/manual/plugins.rst +++ b/src/calibre/manual/plugins.rst @@ -173,3 +173,11 @@ Preferences Plugins :members: :member-order: bysource +.. autoclass:: calibre.gui2.preferences.ConfigWidgetInterface + :members: + :member-order: bysource + +.. autoclass:: calibre.gui2.preferences.ConfigWidgetBase + :members: + :member-order: bysource + From 3433aa300f8594c555b609a3c9f948f5a8bc9905 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 12:36:59 -0600 Subject: [PATCH 31/45] Warn user that changing tweaks needs a restart --- src/calibre/gui2/preferences/tweaks.py | 3 +- src/calibre/gui2/preferences/tweaks.ui | 2 +- src/calibre/translations/calibre.pot | 238 ++++++++++++------------- 3 files changed, 122 insertions(+), 121 deletions(-) diff --git a/src/calibre/gui2/preferences/tweaks.py b/src/calibre/gui2/preferences/tweaks.py index 62bc87c490..2bd765986d 100644 --- a/src/calibre/gui2/preferences/tweaks.py +++ b/src/calibre/gui2/preferences/tweaks.py @@ -47,7 +47,8 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): det_msg=traceback.format_exc(), show=True) raise AbortCommit('abort') write_tweaks(raw) - return ConfigWidgetBase.commit(self) + ConfigWidgetBase.commit(self) + return True if __name__ == '__main__': diff --git a/src/calibre/gui2/preferences/tweaks.ui b/src/calibre/gui2/preferences/tweaks.ui index 3234f4be9b..8546873552 100644 --- a/src/calibre/gui2/preferences/tweaks.ui +++ b/src/calibre/gui2/preferences/tweaks.ui @@ -17,7 +17,7 @@ - Values for the tweaks are shown below. Edit them to change the behavior of calibre + Values for the tweaks are shown below. Edit them to change the behavior of calibre. Your changes will only take effect after a restart of calibre. true diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 116d4d6fce..b793393f23 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.7.17\n" -"POT-Creation-Date: 2010-09-05 17:35+MDT\n" -"PO-Revision-Date: 2010-09-05 17:35+MDT\n" +"POT-Creation-Date: 2010-09-07 12:36+MDT\n" +"PO-Revision-Date: 2010-09-07 12:36+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -103,8 +103,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/rtf/input.py:249 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:323 #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:330 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:289 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:292 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:290 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:293 #: /home/kovid/work/calibre/src/calibre/gui2/add.py:137 #: /home/kovid/work/calibre/src/calibre/gui2/add.py:144 #: /home/kovid/work/calibre/src/calibre/gui2/convert/__init__.py:42 @@ -129,13 +129,13 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:186 #: /home/kovid/work/calibre/src/calibre/library/cli.py:213 #: /home/kovid/work/calibre/src/calibre/library/database.py:913 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:375 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:387 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1057 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1126 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1825 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1827 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1954 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:376 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:388 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1059 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1128 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1826 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1828 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1955 #: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:211 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:137 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:140 @@ -173,12 +173,12 @@ msgstr "" msgid "User Interface Action" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:376 +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:383 #: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:17 #: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:22 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:150 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:213 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:234 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:187 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:251 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:272 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:206 msgid "Preferences" msgstr "" @@ -642,9 +642,9 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:823 #: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:851 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:244 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:192 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:205 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1694 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:193 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:206 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1695 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:132 msgid "News" msgstr "" @@ -2767,10 +2767,10 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/catalog.py:31 #: /home/kovid/work/calibre/src/calibre/gui2/actions/convert.py:86 #: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:115 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:74 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:140 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:176 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:203 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:75 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:141 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:177 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:204 #: /home/kovid/work/calibre/src/calibre/gui2/actions/save_to_disk.py:91 msgid "No books selected" msgstr "" @@ -3060,7 +3060,7 @@ msgid "Deleting books from device." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:160 -msgid "The selected books will be permanently deleted and the files removed from your computer. Are you sure?" +msgid "The selected books will be permanently deleted and the files removed from your calibre library. Are you sure?" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/delete.py:179 @@ -3166,39 +3166,39 @@ msgstr "" msgid "Merge into first selected book - keep others" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:73 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:74 msgid "Cannot download metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:96 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:97 msgid "social metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:98 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:99 msgid "covers" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:98 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:99 msgid "metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:103 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:104 msgid "Downloading %s for %d book(s)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:124 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:125 msgid "Failed to download some metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:125 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:126 msgid "Failed to download metadata for the following:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:128 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:129 msgid "Failed to download metadata:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:129 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:130 #: /home/kovid/work/calibre/src/calibre/gui2/device.py:607 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc.py:65 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc.py:112 @@ -3206,29 +3206,29 @@ msgstr "" msgid "Error" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:139 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:175 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:140 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:176 msgid "Cannot edit metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:202 -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:205 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:203 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:206 msgid "Cannot merge books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:206 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:207 msgid "At least two books must be selected for merging" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:210 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:211 msgid "All book formats and metadata from the selected books will be added to the first selected book.

The second and subsequently selected books will not be deleted or changed.

Please confirm you want to proceed." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:221 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:222 msgid "All book formats and metadata from the selected books will be merged into the first selected book.

After merger the second and subsequently selected books will be deleted.

All book formats of the first selected book will be kept and any duplicate formats in the second and subsequently selected books will be permanently deleted from your computer.

Are you sure you want to proceed?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:233 +#: /home/kovid/work/calibre/src/calibre/gui2/actions/edit_metadata.py:234 msgid "You are about to merge more than 5 books. Are you sure you want to proceed?" msgstr "" @@ -3852,8 +3852,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:20 #: /home/kovid/work/calibre/src/calibre/library/catalog.py:550 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1657 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1675 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1658 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1676 msgid "Catalog" msgstr "" @@ -5738,7 +5738,7 @@ msgid "Reset cover to default" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:410 -msgid "Download &cover" +msgid "Download co&ver" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:411 @@ -6481,12 +6481,12 @@ msgid "Shift+Alt+T" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/init.py:138 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:26 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:29 msgid "version" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/init.py:139 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:27 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:30 msgid "created by Kovid Goyal" msgstr "" @@ -6494,20 +6494,20 @@ msgstr "" msgid "Connected " msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:166 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:169 msgid "Update found" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:210 -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:220 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:204 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:214 msgid "Book Details" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:212 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:206 msgid "Alt+D" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/init.py:222 +#: /home/kovid/work/calibre/src/calibre/gui2/init.py:216 msgid "Shift+Alt+D" msgstr "" @@ -6596,7 +6596,7 @@ msgid "Show books in the main memory of the device" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:66 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:648 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:650 msgid "Card A" msgstr "" @@ -6605,7 +6605,7 @@ msgid "Show books in storage card A" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:68 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:650 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:652 msgid "Card B" msgstr "" @@ -6946,7 +6946,7 @@ msgstr "" msgid "No matches found for this book" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/__init__.py:23 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/__init__.py:36 msgid "Restore settings to default values. You have to click Apply to actually save the default settings." msgstr "" @@ -7405,36 +7405,36 @@ msgstr "" msgid "Show &text under icons:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:172 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:215 msgid "&Apply" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:175 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:218 msgid "&Cancel" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:184 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:222 msgid "Restore &defaults" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:185 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:223 msgid "Save changes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:186 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:224 msgid "Cancel and return to overview" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:221 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:259 msgid "Restoring to defaults not supported for" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:247 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:291 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/server.py:120 msgid "Restart needed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:248 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:292 msgid "Some of the changes you made require a restart. Please restart calibre as soon as possible." msgstr "" @@ -7830,7 +7830,7 @@ msgid "The tweaks you entered are invalid, try resetting the tweaks to default a msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/tweaks_ui.py:50 -msgid "Values for the tweaks are shown below. Edit them to change the behavior of calibre" +msgid "Values for the tweaks are shown below. Edit them to change the behavior of calibre. Your changes will only take effect after a restart of calibre." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/tweaks_ui.py:51 @@ -7975,7 +7975,7 @@ msgid "Manage User Categories" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:435 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:300 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:301 msgid "Searches" msgstr "" @@ -8110,7 +8110,7 @@ msgstr "" msgid "WARNING: Active jobs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:573 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:579 msgid "will keep running in the system tray. To close it, choose Quit in the context menu of the system tray." msgstr "" @@ -9133,33 +9133,33 @@ msgstr "" msgid "You must specify at least one file to add" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:329 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:330 msgid "" "%prog remove ids\n" "\n" "Remove the books identified by ids from the database. ids should be a comma separated list of id numbers (you can get id numbers by using the list command). For example, 23,34,57-85\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:344 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:345 msgid "You must specify at least one book to remove" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:363 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:364 msgid "" "%prog add_format [options] id ebook_file\n" "\n" "Add the ebook in ebook_file to the available formats for the logical book identified by id. You can get id by using the list command. If the format already exists, it is replaced.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:378 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:379 msgid "You must specify an id and an ebook file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:383 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:384 msgid "ebook file must have an extension" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:391 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:392 msgid "" "\n" "%prog remove_format [options] id fmt\n" @@ -9167,11 +9167,11 @@ msgid "" "Remove the format fmt from the logical book identified by id. You can get id by using the list command. fmt should be a file extension like LRF or TXT or EPUB. If the logical book does not have fmt available, do nothing.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:408 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:409 msgid "You must specify an id and a format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:426 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:427 msgid "" "\n" "%prog show_metadata [options] id\n" @@ -9180,15 +9180,15 @@ msgid "" "id is an id number from the list command.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:434 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:435 msgid "Print metadata in OPF form (XML)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:443 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:444 msgid "You must specify an id" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:456 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:458 msgid "" "\n" "%prog set_metadata [options] id /path/to/metadata.opf\n" @@ -9199,11 +9199,11 @@ msgid "" "show_metadata command.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:472 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:474 msgid "You must specify an id and a metadata file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:492 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:494 msgid "" "%prog export [options] ids\n" "\n" @@ -9212,27 +9212,27 @@ msgid "" "an opf file). You can get id numbers from the list command.\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:500 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:502 msgid "Export all books in database, ignoring the list of ids." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:502 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:504 msgid "Export books to the specified directory. Default is" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:504 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:506 msgid "Export all books into a single directory" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:511 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:513 msgid "Specifying this switch will turn this behavior off." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:534 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:536 msgid "You must specify some ids or the %s option" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:547 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:549 msgid "" "%prog add_custom_column [options] label name datatype\n" "\n" @@ -9241,19 +9241,19 @@ msgid "" "datatype is one of: {0}\n" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:556 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:558 msgid "This column stores tag like data (i.e. multiple comma separated values). Only applies if datatype is text." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:560 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:562 msgid "A dictionary of options to customize how the data in this column will be interpreted." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:573 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:575 msgid "You must specify label, name and datatype" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:631 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:633 msgid "" "\n" " %prog catalog /path/to/destination.(csv|epub|mobi|xml ...) [options]\n" @@ -9263,29 +9263,29 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:645 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:647 msgid "" "Comma-separated list of database IDs to catalog.\n" "If declared, --search is ignored.\n" "Default: all" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:649 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:651 msgid "" "Filter the results by the search query. For the format of the search query, please see the search-related documentation in the User Manual.\n" "Default: no filtering" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:655 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:657 #: /home/kovid/work/calibre/src/calibre/web/fetch/simple.py:505 msgid "Show detailed output information. Useful for debugging" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:668 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:670 msgid "Error: You must specify a catalog output file" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:710 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:712 msgid "" "\n" " %prog set_custom [options] column id value\n" @@ -9297,15 +9297,15 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:721 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:723 msgid "If the column stores multiple values, append the specified values to the existing ones, instead of replacing them." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:732 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:734 msgid "Error: You must specify a field name, id and value" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:751 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:753 msgid "" "\n" " %prog custom_columns [options]\n" @@ -9314,19 +9314,19 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:758 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:760 msgid "Show details for each column." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:770 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:772 msgid "You will lose all data in the column: %r. Are you sure (y/n)? " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:772 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:774 msgid "y" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:778 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:780 msgid "" "\n" " %prog remove_custom_column [options] label\n" @@ -9336,15 +9336,15 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:786 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:788 msgid "Do not ask for confirmation" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:796 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:798 msgid "Error: You must specify a column label" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:803 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:805 msgid "" "\n" " %prog saved_searches [options] list\n" @@ -9357,39 +9357,39 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:821 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:823 msgid "Error: You must specify an action (add|remove|list)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:829 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:831 msgid "Name:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:830 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:832 msgid "Search string:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:836 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:838 msgid "Error: You must specify a name and a search string" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:839 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:841 msgid "added" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:844 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:846 msgid "Error: You must specify a name" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:847 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:849 msgid "removed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:851 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:853 msgid "Error: Action %s not recognized, must be one of: (add|remove|list)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/cli.py:865 +#: /home/kovid/work/calibre/src/calibre/library/cli.py:867 msgid "" "%%prog command [options] [arguments]\n" "\n" @@ -9413,31 +9413,31 @@ msgstr "" msgid "%sAverage rating is %3.1f" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:646 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:648 msgid "Main" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1980 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1981 msgid "

Migrating old database to ebook library in %s

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2009 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2010 msgid "Copying %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2026 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2027 msgid "Compacting database" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2119 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2120 msgid "Checking SQL integrity..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2160 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2159 msgid "Checking for missing files." msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2182 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2181 msgid "Checked id" msgstr "" @@ -9833,7 +9833,7 @@ msgstr "" msgid "Failed to authenticate with server: %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/utils/smtp.py:229 +#: /home/kovid/work/calibre/src/calibre/utils/smtp.py:230 msgid "Control email delivery" msgstr "" From 705a6e7d9df101aaf66ae1febb9da087fa2be439 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 12:55:37 -0600 Subject: [PATCH 32/45] Update User Manual for new Preferences Dialog --- src/calibre/gui2/preferences/__init__.py | 2 +- src/calibre/manual/custom.py | 2 +- src/calibre/manual/customize.rst | 4 ++-- src/calibre/manual/develop.rst | 2 +- src/calibre/manual/faq.rst | 18 +++++++++--------- src/calibre/manual/portable.rst | 4 ++-- src/calibre/manual/viewer.rst | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index eb8740114a..6db5a90790 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -63,7 +63,7 @@ class ConfigWidgetInterface(object): Save any changed settings. Return True if the changes require a restart, False otherwise. Raise an :class:`AbortCommit` exception to indicate that an error occurred. You are responsible for giving the - suer feedback about what the error is and how to correct it. + user feedback about what the error is and how to correct it. ''' return False diff --git a/src/calibre/manual/custom.py b/src/calibre/manual/custom.py index eb0a65ac33..80eeb59e0b 100644 --- a/src/calibre/manual/custom.py +++ b/src/calibre/manual/custom.py @@ -28,7 +28,7 @@ Command Line Interface .. image:: ../images/cli.png -On OS X you have to go to Preferences->Advanced and click install command line +On OS X you have to go to Preferences->Advanced->Miscellaneous and click install command line tools to make the command line tools available. On other platforms, just start a terminal and type the command. diff --git a/src/calibre/manual/customize.rst b/src/calibre/manual/customize.rst index 0dd2e349f3..c35defc0b0 100644 --- a/src/calibre/manual/customize.rst +++ b/src/calibre/manual/customize.rst @@ -11,7 +11,7 @@ Customizing |app| *recipes* to add new sources of online content to |app| in the Section :ref:`news`. Here, you will learn, first, how to use environment variables and *tweaks* to customize |app|'s behavior, and then how to specify your own static resources like icons and templates to override the defaults and finally how to -use *plugins* to add funtionality to |app|. +use *plugins* to add functionality to |app|. .. contents:: :depth: 2 @@ -45,7 +45,7 @@ All static resources are stored in the resources sub-folder of the calibre insta from the calibre website it will be :file:`/opt/calibre/resources`. These paths can change depending on where you choose to install |app|. You should not change the files in this resources folder, as your changes will get overwritten the next time you update |app|. Instead, go to -:guilabel:`Preferences->Advanced` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. Place the files in the appropriate sub folders, for example place images in :file:`resources/images`, etc. +:guilabel:`Preferences->Advanced->Miscellaneous` and click :guilabel:`Open calibre configuration directory`. In this configuration directory, create a sub-folder called resources and place the files you want to override in it. Place the files in the appropriate sub folders, for example place images in :file:`resources/images`, etc. |app| will automatically use your custom file in preference to the builtin one the next time it is started. For example, if you wanted to change the icon for the :guilabel:`Remove books` action, you would first look in the builtin resources folder and see that the relevant file is diff --git a/src/calibre/manual/develop.rst b/src/calibre/manual/develop.rst index ca067e45bc..f95d51bfca 100644 --- a/src/calibre/manual/develop.rst +++ b/src/calibre/manual/develop.rst @@ -123,7 +123,7 @@ the previously checked out calibre code directory, for example:: cd /Users/kovid/work/calibre -calibre is the directory that contains the src and resources sub directories. Ensure you have installed the |app| commandline tools via Preferences->Advanced in the |app| GUI. +calibre is the directory that contains the src and resources sub directories. Ensure you have installed the |app| commandline tools via :guilabel:Preferences->Advanced->Miscellaneous in the |app| GUI. The next step is to set the environment variable ``CALIBRE_DEVELOP_FROM`` to the absolute path to the src directory. So, following the example above, it would be ``/Users/kovid/work/calibre/src``. Apple diff --git a/src/calibre/manual/faq.rst b/src/calibre/manual/faq.rst index 0a32f442c0..b93444f4c3 100644 --- a/src/calibre/manual/faq.rst +++ b/src/calibre/manual/faq.rst @@ -62,7 +62,7 @@ How do I convert my file containing non-English characters, or smart quotes? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are two aspects to this problem: 1. Knowing the encoding of the source file: |app| tries to guess what character encoding your source files use, but often, this is impossible, so you need to tell it what encoding to use. This can be done in the GUI via the :guilabel:`Input character encoding` field in the :guilabel:`Look & Feel` section. The command-line tools all have an :option:`--input-encoding` option. - 2. When adding HTML files to |app|, you may need to tell |app| what encoding the files are in. To do this go to Preferences->Plugins->File Type plugins and customize the HTML2Zip plugin, telling it what encoding your HTML files are in. Now when you add HTML files to |app| they will be correctly processed. HTML files from different sources often have different encodings, so you may have to change this setting repeatedly. A common encoding for many files from the web is ``cp1252`` and I would suggest you try that first. Note that when converting HTML files, leave the input encoding setting mentioned above blank. This is because the HTML2ZIP plugin automatically converts the HTML files to a standard encoding (utf-8). + 2. When adding HTML files to |app|, you may need to tell |app| what encoding the files are in. To do this go to :guilabel:`Preferences->Advanced->Plugins->File Type plugins` and customize the HTML2Zip plugin, telling it what encoding your HTML files are in. Now when you add HTML files to |app| they will be correctly processed. HTML files from different sources often have different encodings, so you may have to change this setting repeatedly. A common encoding for many files from the web is ``cp1252`` and I would suggest you try that first. Note that when converting HTML files, leave the input encoding setting mentioned above blank. This is because the HTML2ZIP plugin automatically converts the HTML files to a standard encoding (utf-8). 3. Embedding fonts: If you are generating an LRF file to read on your SONY Reader, you are limited by the fact that the Reader only supports a few non-English characters in the fonts it comes pre-loaded with. You can work around this problem by embedding a unicode-aware font that supports the character set your file uses into the LRF file. You should embed atleast a serif and a sans-serif font. Be aware that embedding fonts significantly slows down page-turn speed on the reader. @@ -92,7 +92,7 @@ We just need some information from you: * What e-book formats does your device support? * Is there a special directory on the device in which all e-book files should be placed? * We also need information about your device that |app| will collect automatically. First, if your - device supports SD cards, insert them. Then connect your device. In calibre go to Preferences->Advanced + device supports SD cards, insert them. Then connect your device. In calibre go to :guilabel:`Preferences->Advanced->Miscellaneous` and click the "Debug device detection" button. This will create some debug output. Copy it to a file and repeat the process, this time with your device disconnected. * Send both the above outputs to us with the other information and we will write a device driver for your @@ -109,11 +109,11 @@ of which books are members are shown on the device view. When you send a book to the reader, |app| will add the book to collections based on the metadata for that book. By default, collections are created from tags and series. You can control what metadata is used by going to -Preferences->Plugins->Device Interface plugins and customizing the SONY device interface plugin. If you remove all +:guilabel:`Preferences->Advanced->Plugins->Device Interface plugins` and customizing the SONY device interface plugin. If you remove all values, |app| will not add the book to any collection. Collection management is largely controlled by the 'Metadata management' option found at -Preferences->Add/Save->Sending to device. If set to 'Manual' (the default), managing collections is left to +:guilabel:`Preferences->Import/Export->Sending books to devices`. If set to 'Manual' (the default), managing collections is left to the user; |app| will not delete already existing collections for a book on your reader when you resend the book to the reader, but |app| will add the book to collections if necessary. To ensure that the collections for a book are based only on current |app| metadata, first delete the books from the reader, then resend the @@ -185,8 +185,8 @@ The easiest way to browse your |app| collection on your Apple device (iPad/iPhon First perform the following steps in |app| - * Set the Preferred Output Format in |app| to EPUB (The output format can be set under Preferences->General) - * Set the output profile to iPad (this will work for iPhone/iPods as well), under Preferences->Conversion->Page Setup + * Set the Preferred Output Format in |app| to EPUB (The output format can be set under :guilabel:`Preferences->Interface->Behavior`) + * Set the output profile to iPad (this will work for iPhone/iPods as well), under :guilabel:`Preferences->Conversion->Common Options->Page Setup` * Convert the books you want to read on your iPhone to EPUB format by selecting them and clicking the Convert button. * Turn on the Content Server in |app|'s preferences and leave |app| running. @@ -217,7 +217,7 @@ Can I access my |app| books using the web browser in my Kindle or other reading ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |app| has a *Content Server* that exports the books in |app| as a web page. You can turn it on under -Preferences->Content Server. Then just point the web browser on your device to the computer running +:guilabel:`Preferences->Network->Sharing over the net`. Then just point the web browser on your device to the computer running the Content Server and you will be able to browse your book collection. For example, if the computer running the server has IP address 63.45.128.5, in the browser, you would type:: @@ -277,14 +277,14 @@ In |app|, you would instead use tags to mark genre and read status and then just Why doesn't |app| have a column for foo? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -|app| is designed to have columns for the most frequently and widely used fields. In addition, you can add any columns you like. Columns can be added via Preferences->Interface. +|app| is designed to have columns for the most frequently and widely used fields. In addition, you can add any columns you like. Columns can be added via :guilabel:`Preferences->Interface->Add your own columns`. Watch the tutorial `UI Power tips `_ to learn how to create your own columns. How do I move my |app| library from one computer to another? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Simply copy the |app| library folder from the old to the new computer. You can find out what the library folder is by clicking the calibre icon in the toolbar. The very first item is the path to the library folder. Now on the new computer, start |app| for the first time. It will run the Welcome Wizard asking you for the location of the |app| library. Point it to the previously copied folder. -Note that if you are transferring between different types of computers (for example Windows to OS X) then after doing the above you should also go to Preferences->Advanced and click the Check database integrity button. It will warn you about missing files, if any, which you should then transfer by hand. +Note that if you are transferring between different types of computers (for example Windows to OS X) then after doing the above you should also go to :guilabel:`Preferences->Advanced->Miscellaneous` and click the "Check database integrity button". It will warn you about missing files, if any, which you should then transfer by hand. Content From The Web diff --git a/src/calibre/manual/portable.rst b/src/calibre/manual/portable.rst index 2a88107842..a2c8e323d8 100644 --- a/src/calibre/manual/portable.rst +++ b/src/calibre/manual/portable.rst @@ -46,8 +46,8 @@ The steps required to prepare the USB stick are as follows: * Deselect the options for creating Menu shortcuts; creating a calibre shortcut on the desktop; and adding Calibre to the path * Create the CalibreLibrary folder inside the Calibre_Root_Folder. If you have an existing Calibre library copy it and all its contents to the CalibreLibrary folder. If you do not already have a library do not worry as a new one will be created at this location when Calibre is started. - * Create the CalibreConfig folder inside the Calibre_Root_Folder. This will hold your personal Calibre configuration settings. If you have an existing Calibre installation and want to copy the current settings then copy the contents of your current configuration folder to the CalibreConfig folder. You can find the location of your current configuration folder by going to Preferences->Advanced and clicking the “Open calibre configuration Directory” button. - * When you have started Calibre, go into Preferences->General and check that you have set the Job Priority to ‘Low’. This setting keeps single-processor Windows systems responsive without affecting Calibre performance to any noticeable degree. On multi-processor or multi-core systems this setting does not matter as much, but setting it will do no harm. + * Create the CalibreConfig folder inside the Calibre_Root_Folder. This will hold your personal Calibre configuration settings. If you have an existing Calibre installation and want to copy the current settings then copy the contents of your current configuration folder to the CalibreConfig folder. You can find the location of your current configuration folder by going to :guilabel:`Preferences->Advanced->Miscellaneous` and clicking the “Open calibre configuration Directory” button. + * When you have started Calibre, go into :guilabel:`Preferences->Interface->Behavior` and check that you have set the Job Priority to ‘Low’. This setting keeps single-processor Windows systems responsive without affecting Calibre performance to any noticeable degree. On multi-processor or multi-core systems this setting does not matter as much, but setting it will do no harm. Using calibre-portable.bat --------------------------- diff --git a/src/calibre/manual/viewer.rst b/src/calibre/manual/viewer.rst index dd70674b99..8fdbb4b021 100644 --- a/src/calibre/manual/viewer.rst +++ b/src/calibre/manual/viewer.rst @@ -18,7 +18,7 @@ Starting the viewer You can view any of the books in your |app| library by selecting the book and pressing the View button. This will open up the book in the e-book viewer. You can also launch the viewer by itself, from the Start menu in windows or using the command :command:`ebook-viewer` in Linux and OS X (you have to install the command line tools on OS X -first by going to Preferences->Advanced). +first by going to :guilabel:`Preferences->Advanced->Miscellaneous`). Navigating around an e-book ----------------------------- From de298693eea28332fefdca0ea3cad2e5ea2b6389 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 13:12:23 -0600 Subject: [PATCH 33/45] Preferences dialog: Dont show close button in sub panels. Disallow further preference editing after modifying custom columns. Fix gui re-init code not running after dialg closed --- src/calibre/gui2/actions/preferences.py | 11 --------- src/calibre/gui2/preferences/__init__.py | 5 ++++ src/calibre/gui2/preferences/columns.py | 2 ++ src/calibre/gui2/preferences/main.py | 29 ++++++++++++++++++++---- src/calibre/translations/calibre.pot | 28 +++++++++++++---------- 5 files changed, 48 insertions(+), 27 deletions(-) diff --git a/src/calibre/gui2/actions/preferences.py b/src/calibre/gui2/actions/preferences.py index a379698140..d9957bd70d 100644 --- a/src/calibre/gui2/actions/preferences.py +++ b/src/calibre/gui2/actions/preferences.py @@ -42,15 +42,4 @@ class PreferencesAction(InterfaceAction): d = Preferences(self.gui, initial_plugin=initial_plugin) d.show() - if d.committed: - self.gui.must_restart_before_config = d.must_restart - self.gui.tags_view.set_new_model() # in case columns changed - self.gui.tags_view.recount() - self.gui.create_device_menu() - self.gui.set_device_menu_items_state(bool(self.gui.device_connected)) - self.gui.tool_bar.build_bar() - self.gui.build_context_menus() - self.gui.tool_bar.apply_settings() - - diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index 6db5a90790..7267716ea8 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -36,6 +36,10 @@ class ConfigWidgetInterface(object): restore_defaults_desc = _('Restore settings to default values. ' 'You have to click Apply to actually save the default settings.') + #: If True the Preferences dialog will not allow the user to set any more + #: preferences. Only has effect if :meth:`commit` returns True. + restart_critical = False + def genesis(self, gui): ''' Called once before the widget is displayed, should perform any @@ -218,6 +222,7 @@ class ConfigWidgetBase(QWidget, ConfigWidgetInterface): changed_signal = pyqtSignal() supports_restoring_to_defaults = True + restart_critical = False def __init__(self, parent=None): QWidget.__init__(self, parent) diff --git a/src/calibre/gui2/preferences/columns.py b/src/calibre/gui2/preferences/columns.py index 973e214c2f..aa2a1a5958 100644 --- a/src/calibre/gui2/preferences/columns.py +++ b/src/calibre/gui2/preferences/columns.py @@ -16,6 +16,8 @@ from calibre.gui2 import error_dialog, question_dialog, ALL_COLUMNS class ConfigWidget(ConfigWidgetBase, Ui_Form): + restart_critical = True + def genesis(self, gui): self.gui = gui db = self.gui.library_view.model().db diff --git a/src/calibre/gui2/preferences/main.py b/src/calibre/gui2/preferences/main.py index a15a6dcb22..6653fe2b67 100644 --- a/src/calibre/gui2/preferences/main.py +++ b/src/calibre/gui2/preferences/main.py @@ -264,6 +264,7 @@ class Preferences(QMainWindow): self.bar_title.show_plugin(plugin) self.setWindowIcon(QIcon(plugin.icon)) self.bar.setVisible(True) + self.bb.setVisible(False) def hide_plugin(self): @@ -273,6 +274,7 @@ class Preferences(QMainWindow): self.bar.setVisible(False) self.stack.setCurrentIndex(0) self.setWindowIcon(QIcon(I('config.png'))) + self.bb.setVisible(True) def esc(self, *args): if self.stack.currentIndex() == 1: @@ -285,15 +287,24 @@ class Preferences(QMainWindow): must_restart = self.showing_widget.commit() except AbortCommit: return + rc = self.showing_widget.restart_critical self.committed = True if must_restart: self.must_restart = True - warning_dialog(self, _('Restart needed'), - _('Some of the changes you made require a restart.' - ' Please restart calibre as soon as possible.'), - show=True, show_copy_button=False) + msg = _('Some of the changes you made require a restart.' + ' Please restart calibre as soon as possible.') + if rc: + msg = _('The changes you have made require calibre be ' + 'restarted immediately. You will not be allowed ' + 'set any more preferences, until you restart.') + + + warning_dialog(self, _('Restart needed'), msg, show=True, + show_copy_button=False) self.showing_widget.refresh_gui(self.gui) self.hide_plugin() + if must_restart and rc: + self.close() def cancel(self, *args): @@ -305,6 +316,16 @@ class Preferences(QMainWindow): def closeEvent(self, *args): gprefs.set('preferences_window_geometry', bytearray(self.saveGeometry())) + if self.committed: + self.gui.must_restart_before_config = self.must_restart + self.gui.tags_view.set_new_model() # in case columns changed + self.gui.tags_view.recount() + self.gui.create_device_menu() + self.gui.set_device_menu_items_state(bool(self.gui.device_connected)) + self.gui.tool_bar.build_bar() + self.gui.build_context_menus() + self.gui.tool_bar.apply_settings() + return QMainWindow.closeEvent(self, *args) if __name__ == '__main__': diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index b793393f23..19e9d55fd4 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -5,8 +5,8 @@ msgid "" msgstr "" "Project-Id-Version: calibre 0.7.17\n" -"POT-Creation-Date: 2010-09-07 12:36+MDT\n" -"PO-Revision-Date: 2010-09-07 12:36+MDT\n" +"POT-Creation-Date: 2010-09-07 13:11+MDT\n" +"PO-Revision-Date: 2010-09-07 13:11+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -178,7 +178,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/preferences.py:22 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:187 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:251 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:272 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:273 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:206 msgid "Preferences" msgstr "" @@ -2888,7 +2888,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:186 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/confirm_delete_ui.py:53 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:100 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:102 msgid "Are you sure?" msgstr "" @@ -7073,15 +7073,15 @@ msgstr "" msgid "Use internal &viewer for:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:94 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:96 msgid "You must select a column to delete it" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:99 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:101 msgid "The selected column is not a custom column" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:101 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:103 msgid "Do you really want to delete column %s and all its data?" msgstr "" @@ -7429,13 +7429,17 @@ msgstr "" msgid "Restoring to defaults not supported for" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:291 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/server.py:120 -msgid "Restart needed" +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:294 +msgid "Some of the changes you made require a restart. Please restart calibre as soon as possible." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:292 -msgid "Some of the changes you made require a restart. Please restart calibre as soon as possible." +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:297 +msgid "The changes you have made require calibre be restarted immediately. You will not be allowed set any more preferences, until you restart." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/main.py:302 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/server.py:120 +msgid "Restart needed" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc.py:49 From d9514e394e7a9091e437bf33e3cce11a8f065478 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 13:20:53 -0600 Subject: [PATCH 34/45] Nicer error message when reanming a library fails --- src/calibre/gui2/actions/choose_library.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/actions/choose_library.py b/src/calibre/gui2/actions/choose_library.py index 955c751d19..79406da40c 100644 --- a/src/calibre/gui2/actions/choose_library.py +++ b/src/calibre/gui2/actions/choose_library.py @@ -177,7 +177,16 @@ class ChooseLibraryAction(InterfaceAction): return error_dialog(self.gui, _('Already exists'), _('The folder %s already exists. Delete it first.') % newloc, show=True) - os.rename(loc, newloc) + try: + os.rename(loc, newloc) + except: + import traceback + error_dialog(self.gui, _('Rename failed'), + _('Failed to rename the library at %s. ' + 'The most common cause for this is if one of the files' + ' in the library is open in another program.') % loc, + det_msg=traceback.format_exc(), show=True) + return self.stats.rename(location, newloc) self.build_menus() From b6bd654948dfa0313a9dbb7266b8547ae986d616 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Tue, 7 Sep 2010 20:41:45 +0100 Subject: [PATCH 35/45] Fix two custom column preferences editor problems: 1) cannot edit a just-edited column. Get a message that it is not a custom column. 2) cannot delete a newly-added column. It disappears from the list but is created anyway. --- src/calibre/gui2/preferences/columns.py | 5 ++++- src/calibre/gui2/preferences/create_custom_column.py | 1 - 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/preferences/columns.py b/src/calibre/gui2/preferences/columns.py index aa2a1a5958..c1b9230f42 100644 --- a/src/calibre/gui2/preferences/columns.py +++ b/src/calibre/gui2/preferences/columns.py @@ -105,7 +105,10 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): return self.opt_columns.item(idx).setCheckState(False) self.opt_columns.takeItem(idx) - self.custcols[col]['*deleteme'] = True + if self.custcols[col]['colnum'] is None: + del self.custcols[col] # A newly-added column was deleted + else: + self.custcols[col]['*deleteme'] = True self.changed_signal.emit() def add_custcol(self): diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py index 9cad1293a9..e8ab8707e2 100644 --- a/src/calibre/gui2/preferences/create_custom_column.py +++ b/src/calibre/gui2/preferences/create_custom_column.py @@ -161,7 +161,6 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn): else: idx = self.parent.opt_columns.currentRow() item = self.parent.opt_columns.item(idx) - item.setData(Qt.UserRole, QVariant(key)) item.setText(col_heading) self.parent.custcols[self.orig_column_name]['label'] = col self.parent.custcols[self.orig_column_name]['name'] = col_heading From 6bfd24c27888d276a3986cbd4eb98ac8f185a9a3 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 14:02:09 -0600 Subject: [PATCH 36/45] Add an action ot the context menu for the book list headers to add custom columns --- src/calibre/gui2/init.py | 3 +++ src/calibre/gui2/library/views.py | 11 +++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 042e0c578d..223efcf95b 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -32,6 +32,9 @@ class LibraryViewMixin(object): # {{{ def __init__(self, db): self.library_view.files_dropped.connect(self.iactions['Add Books'].files_dropped, type=Qt.QueuedConnection) + self.library_view.add_column_signal.connect(partial(self.iactions['Preferences'].do_config, + initial_plugin=('Interface', 'Custom Columns')), + type=Qt.QueuedConnection) for func, args in [ ('connect_to_search_box', (self.search, self.search_done)), diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 966180467c..cfb2b53a4e 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -23,6 +23,7 @@ from calibre.gui2.library import DEFAULT_SORT class BooksView(QTableView): # {{{ files_dropped = pyqtSignal(object) + add_column_signal = pyqtSignal() def __init__(self, parent, modelcls=BooksModel): QTableView.__init__(self, parent) @@ -54,6 +55,7 @@ class BooksView(QTableView): # {{{ self.selectionModel().currentRowChanged.connect(self._model.current_changed) # {{{ Column Header setup + self.can_add_columns = True self.was_restored = False self.column_header = self.horizontalHeader() self.column_header.setMovable(True) @@ -93,6 +95,8 @@ class BooksView(QTableView): # {{{ self.sortByColumn(idx, Qt.DescendingOrder) elif action == 'defaults': self.apply_state(self.get_default_state()) + elif action == 'addcustcol': + self.add_column_signal.emit() elif action.startswith('align_'): alignment = action.partition('_')[-1] self._model.change_alignment(column, alignment) @@ -166,6 +170,12 @@ class BooksView(QTableView): # {{{ partial(self.column_header_context_handler, action='defaults', column=col)) + if self.can_add_columns: + self.column_header_context_menu.addAction( + _('Add your own columns'), + partial(self.column_header_context_handler, + action='addcustcol', column=col)) + self.column_header_context_menu.popup(self.column_header.mapToGlobal(pos)) # }}} @@ -494,6 +504,7 @@ class DeviceBooksView(BooksView): # {{{ def __init__(self, parent): BooksView.__init__(self, parent, DeviceBooksModel) + self.can_add_columns = False self.columns_resized = False self.resize_on_select = False self.rating_delegate = None From 556ed59e96a5ef26e5b17a0a986f402c08558f24 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 14:13:12 -0600 Subject: [PATCH 37/45] ... --- src/calibre/gui2/library/views.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index cfb2b53a4e..d67d286aeb 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -9,7 +9,7 @@ import os from functools import partial from PyQt4.Qt import QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, \ - QModelIndex + QModelIndex, QIcon from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, \ TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, \ @@ -172,6 +172,7 @@ class BooksView(QTableView): # {{{ if self.can_add_columns: self.column_header_context_menu.addAction( + QIcon(I('column.png')), _('Add your own columns'), partial(self.column_header_context_handler, action='addcustcol', column=col)) From c79032285c48943e01de23104fca3e74123e837c Mon Sep 17 00:00:00 2001 From: GRiker Date: Tue, 7 Sep 2010 14:11:07 -0700 Subject: [PATCH 38/45] GwR revisions supporting Series Section --- resources/catalog/stylesheet.css | 24 +++++++----- src/calibre/library/catalog.py | 66 ++++++++++++++++++++++---------- 2 files changed, 60 insertions(+), 30 deletions(-) diff --git a/resources/catalog/stylesheet.css b/resources/catalog/stylesheet.css index 4f9ca9ac41..ff338fb130 100644 --- a/resources/catalog/stylesheet.css +++ b/resources/catalog/stylesheet.css @@ -6,7 +6,7 @@ p.title { text-align:center; font-style:italic; font-size:xx-large; - border-bottom: solid black 4px; + border-bottom: solid black 2px; } p.author { @@ -17,6 +17,15 @@ p.author { font-size:large; } +p.author_index { + font-size:large; + font-weight:bold; + text-align:left; + margin-top:0px; + margin-bottom:0px; + text-indent: 0em; + } + p.tags { margin-top:0em; margin-bottom:0em; @@ -47,19 +56,12 @@ p.letter_index { margin-bottom:0px; } -p.author_index { - font-size:large; - text-align:left; - margin-top:0px; - margin-bottom:0px; - text-indent: 0em; - } - p.series { - text-align: left; + font-style:italic; margin-top:0px; margin-bottom:0px; margin-left:2em; + text-align: left; text-indent:-2em; } @@ -88,6 +90,8 @@ p.date_read { } hr.series_divider { + border-style:solid; + border-width:thin; width:50%; margin-left:1em; margin-top:0em; diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index 05e596d3fb..fb6dd08705 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -1760,12 +1760,13 @@ class EPUB_MOBI(CatalogPlugin): if not self.__generateForKindle: # We don't need this because the Kindle shows section titles #

By Title

- h2Tag = Tag(soup, "h2") + pTag = Tag(soup, "p") + pTag['class'] = 'title' aTag = Tag(soup, "a") aTag['name'] = "bytitle" - h2Tag.insert(0,aTag) - h2Tag.insert(1,NavigableString('By Title (%d)' % len(self.booksByTitle))) - body.insert(btc,h2Tag) + pTag.insert(0,aTag) + pTag.insert(1,NavigableString('By Title')) + body.insert(btc,pTag) btc += 1 #

@@ -1938,12 +1939,10 @@ class EPUB_MOBI(CatalogPlugin): current_series = None pAuthorTag = Tag(soup, "p") pAuthorTag['class'] = "author_index" - emTag = Tag(soup, "em") aTag = Tag(soup, "a") aTag['name'] = "%s" % self.generateAuthorAnchor(current_author) aTag.insert(0,NavigableString(current_author)) - emTag.insert(0,aTag) - pAuthorTag.insert(0,emTag) + pAuthorTag.insert(0,aTag) divTag.insert(dtc,pAuthorTag) dtc += 1 @@ -1961,7 +1960,17 @@ class EPUB_MOBI(CatalogPlugin): current_series = book['series'] pSeriesTag = Tag(soup,'p') pSeriesTag['class'] = "series" - pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series'])) + + if self.opts.generate_series: + aTag = Tag(soup,'a') + aTag['href'] = "%s.html#%s_series" % ('BySeries', + re.sub('\W','',book['series']).lower()) + aTag.insert(0, book['series']) + pSeriesTag.insert(0, NavigableString(self.NOT_READ_SYMBOL)) + pSeriesTag.insert(1, aTag) + else: + pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series'])) + divTag.insert(dtc,pSeriesTag) dtc += 1 if current_series and not book['series']: @@ -2004,13 +2013,15 @@ class EPUB_MOBI(CatalogPlugin): if not self.__generateForKindle: # Insert the

tag with book_count at the head #

By Author

- h2Tag = Tag(soup, "h2") + pTag = Tag(soup, "p") + pTag['class'] = 'title' aTag = Tag(soup, "a") anchor_name = friendly_name.lower() aTag['name'] = anchor_name.replace(" ","") - h2Tag.insert(0,aTag) - h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, book_count))) - body.insert(btc,h2Tag) + pTag.insert(0,aTag) + #h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, book_count))) + pTag.insert(1,NavigableString('%s' % (friendly_name))) + body.insert(btc,pTag) btc += 1 # Add the divTag to the body @@ -2590,7 +2601,22 @@ class EPUB_MOBI(CatalogPlugin): aTag = Tag(soup, "a") aTag['href'] = "book_%d.html" % (int(float(book['id']))) # Use series, series index if avail else just title - aTag.insert(0,'%d. %s · %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors']))) + #aTag.insert(0,'%d. %s · %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors']))) + + # Link to book + aTag.insert(0,'%d. %s' % (book['series_index'],escape(book['title']))) + pBookTag.insert(ptc, aTag) + ptc += 1 + + # · + pBookTag.insert(ptc, NavigableString(' · ')) + ptc += 1 + + # Link to author + aTag = Tag(soup, "a") + aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", + self.generateAuthorAnchor(' & '.join(book['authors']))) + aTag.insert(0, NavigableString(' & '.join(book['authors']))) pBookTag.insert(ptc, aTag) ptc += 1 @@ -2600,13 +2626,15 @@ class EPUB_MOBI(CatalogPlugin): if not self.__generateForKindle: # Insert the

tag with book_count at the head #

By Series

- h2Tag = Tag(soup, "h2") + pTag = Tag(soup, "p") + pTag['class'] = 'title' aTag = Tag(soup, "a") anchor_name = friendly_name.lower() aTag['name'] = anchor_name.replace(" ","") - h2Tag.insert(0,aTag) - h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, series_count))) - body.insert(btc,h2Tag) + pTag.insert(0,aTag) + #h2Tag.insert(1,NavigableString('%s (%d)' % (friendly_name, series_count))) + pTag.insert(1,NavigableString('%s' % friendly_name)) + body.insert(btc,pTag) btc += 1 # Add the divTag to the body @@ -4003,12 +4031,10 @@ class EPUB_MOBI(CatalogPlugin): current_series = None pAuthorTag = Tag(soup, "p") pAuthorTag['class'] = "author_index" - emTag = Tag(soup, "em") aTag = Tag(soup, "a") aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", self.generateAuthorAnchor(book['author'])) aTag.insert(0, book['author']) - emTag.insert(0,aTag) - pAuthorTag.insert(0,emTag) + pAuthorTag.insert(0,aTag) divTag.insert(dtc,pAuthorTag) dtc += 1 From d156b38449785b02f1d95a88251dd380b7468b48 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 7 Sep 2010 18:30:23 -0600 Subject: [PATCH 39/45] ... --- resources/recipes/nejm.recipe | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/resources/recipes/nejm.recipe b/resources/recipes/nejm.recipe index 415377b5c2..c860413926 100644 --- a/resources/recipes/nejm.recipe +++ b/resources/recipes/nejm.recipe @@ -26,13 +26,13 @@ class NYTimes(BasicNewsRecipe): #TO LOGIN def get_browser(self): br = BasicNewsRecipe.get_browser() - br.open('http://content.nejm.org/cgi/login?uri=/') - br.select_form(nr=0) - br['username'] = self.username - br['code'] = self.password + br.open('http://www.nejm.org/action/showLogin?uri=http://www.nejm.org/') + br.select_form(name='frmLogin') + br['login'] = self.username + br['password'] = self.password response = br.submit() raw = response.read() - if 'Welcome' not in raw: + if '>Sign Out<' not in raw: raise Exception('Login failed. Check your username and password') return br From 8ea09e65cc130750c58c81d60eef95d4eae2bd1a Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 8 Sep 2010 13:32:48 +0100 Subject: [PATCH 40/45] Save a copy of field_metadata into the preferences table for use by non-calibre applications. --- src/calibre/library/database2.py | 3 +++ src/calibre/library/field_metadata.py | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 52b5f2d4e6..05bab95ffe 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -338,6 +338,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): setattr(self, 'title_sort', functools.partial(get_property, loc=self.FIELD_MAP['sort'])) + # Save the current field_metadata for applications like calibre2opds + self.prefs['field_metadata'] = self.field_metadata.all_metadata() + def initialize_database(self): metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read() self.conn.executescript(metadata_sqlite) diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 09dd024b66..449cb289ce 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -371,6 +371,12 @@ class FieldMetadata(dict): def get_custom_fields(self): return [l for l in self._tb_cats if self._tb_cats[l]['is_custom']] + def all_metadata(self): + l = {} + for k in self._tb_cats: + l[k] = self._tb_cats[k] + return l + def get_custom_field_metadata(self): l = {} for k in self._tb_cats: From 4bdeb656966ce95eee1025a0fc34287c4fdb91a4 Mon Sep 17 00:00:00 2001 From: GRiker Date: Wed, 8 Sep 2010 06:49:35 -0700 Subject: [PATCH 41/45] GwR revisions --- resources/catalog/stylesheet.css | 20 +++--- src/calibre/devices/apple/driver.py | 14 ++--- src/calibre/library/catalog.py | 96 +++++++++++++++++++---------- 3 files changed, 80 insertions(+), 50 deletions(-) diff --git a/resources/catalog/stylesheet.css b/resources/catalog/stylesheet.css index ff338fb130..4c5045d378 100644 --- a/resources/catalog/stylesheet.css +++ b/resources/catalog/stylesheet.css @@ -22,7 +22,7 @@ p.author_index { font-weight:bold; text-align:left; margin-top:0px; - margin-bottom:0px; + margin-bottom:-2px; text-indent: 0em; } @@ -58,10 +58,10 @@ p.letter_index { p.series { font-style:italic; - margin-top:0px; + margin-top:2px; margin-bottom:0px; margin-left:2em; - text-align: left; + text-align:left; text-indent:-2em; } @@ -89,13 +89,13 @@ p.date_read { text-indent:-6em; } -hr.series_divider { - border-style:solid; - border-width:thin; - width:50%; - margin-left:1em; - margin-top:0em; - margin-bottom:0em; +hr.description_divider { + width:90%; + margin-left:5%; + border-top: solid white 0px; + border-right: solid white 0px; + border-bottom: solid black 1px; + border-left: solid white 0px; } hr.annotations_divider { diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 916c88f203..afd7958265 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -2303,9 +2303,9 @@ class ITUNES(DriverBase): # Delete existing from Device|Books, add to self.update_list # for deletion from booklist[0] during add_books_to_metadata for book in self.cached_books: - if self.cached_books[book]['uuid'] == metadata.uuid and \ - self.cached_books[book]['title'] == metadata.title and \ - self.cached_books[book]['author'] == metadata.authors[0]: + if self.cached_books[book]['uuid'] == metadata.uuid or \ + (self.cached_books[book]['title'] == metadata.title and \ + self.cached_books[book]['author'] == metadata.authors[0]): self.update_list.append(self.cached_books[book]) self._remove_from_device(self.cached_books[book]) if DEBUG: @@ -2322,9 +2322,9 @@ class ITUNES(DriverBase): # Delete existing from Library|Books, add to self.update_list # for deletion from booklist[0] during add_books_to_metadata for book in self.cached_books: - if self.cached_books[book]['uuid'] == metadata.uuid and \ - self.cached_books[book]['title'] == metadata.title and \ - self.cached_books[book]['author'] == metadata.authors[0]: + if self.cached_books[book]['uuid'] == metadata.uuid or \ + (self.cached_books[book]['title'] == metadata.title and \ + self.cached_books[book]['author'] == metadata.authors[0]): self.update_list.append(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book]) if DEBUG: @@ -2488,7 +2488,7 @@ class ITUNES(DriverBase): zf_opf.close() # If 'News' in tags, tweak the title/author for friendlier display in iBooks - if _('News') in metadata.tags: + if _('News') or _('Catalog') in metadata.tags: if metadata.title.find('[') > 0: metadata.title = metadata.title[:metadata.title.find('[')-1] date_as_author = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) diff --git a/src/calibre/library/catalog.py b/src/calibre/library/catalog.py index fb6dd08705..a02766c9b4 100644 --- a/src/calibre/library/catalog.py +++ b/src/calibre/library/catalog.py @@ -1765,7 +1765,7 @@ class EPUB_MOBI(CatalogPlugin): aTag = Tag(soup, "a") aTag['name'] = "bytitle" pTag.insert(0,aTag) - pTag.insert(1,NavigableString('By Title')) + pTag.insert(1,NavigableString('Titles')) body.insert(btc,pTag) btc += 1 @@ -1775,7 +1775,7 @@ class EPUB_MOBI(CatalogPlugin): dtc = 0 current_letter = "" - # 2/14/10 7:11 AM Experimental: re-sort title list without leading series/series_index + # Re-sort title list without leading series/series_index if not self.useSeriesPrefixInTitlesSection: nspt = deepcopy(self.booksByTitle) for book in nspt: @@ -1868,7 +1868,7 @@ class EPUB_MOBI(CatalogPlugin): # Write books by author A-Z self.updateProgressFullStep("'Authors'") - friendly_name = "By Author" + friendly_name = "Authors" soup = self.generateHTMLEmptyHeader(friendly_name) body = soup.find('body') @@ -1946,6 +1946,7 @@ class EPUB_MOBI(CatalogPlugin): divTag.insert(dtc,pAuthorTag) dtc += 1 + ''' # Insert an
between non-series and series if not current_series and non_series_books and book['series']: # Insert an
@@ -1953,6 +1954,7 @@ class EPUB_MOBI(CatalogPlugin): hrTag['class'] = "series_divider" divTag.insert(dtc,hrTag) dtc += 1 + ''' # Check for series if book['series'] and book['series'] != current_series: @@ -1966,10 +1968,11 @@ class EPUB_MOBI(CatalogPlugin): aTag['href'] = "%s.html#%s_series" % ('BySeries', re.sub('\W','',book['series']).lower()) aTag.insert(0, book['series']) - pSeriesTag.insert(0, NavigableString(self.NOT_READ_SYMBOL)) - pSeriesTag.insert(1, aTag) + #pSeriesTag.insert(0, NavigableString(self.NOT_READ_SYMBOL)) + pSeriesTag.insert(0, aTag) else: - pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series'])) + #pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series'])) + pSeriesTag.insert(0,NavigableString('%s' % book['series'])) divTag.insert(dtc,pSeriesTag) dtc += 1 @@ -1998,15 +2001,18 @@ class EPUB_MOBI(CatalogPlugin): aTag = Tag(soup, "a") aTag['href'] = "book_%d.html" % (int(float(book['id']))) - # Use series, series index if avail else just title + # Use series, series index if avail else just title, + year of publication if current_series: - aTag.insert(0,escape(book['title'][len(book['series'])+1:])) + aTag.insert(0,'%s (%s)' % (escape(book['title'][len(book['series'])+1:]), + book['date'].split()[1])) else: - aTag.insert(0,escape(book['title'])) + aTag.insert(0,'%s (%s)' % (escape(book['title']), + book['date'].split()[1])) non_series_books += 1 pBookTag.insert(ptc, aTag) ptc += 1 + divTag.insert(dtc, pBookTag) dtc += 1 @@ -2065,15 +2071,14 @@ class EPUB_MOBI(CatalogPlugin): current_series = None pAuthorTag = Tag(soup, "p") pAuthorTag['class'] = "author_index" - emTag = Tag(soup, "em") aTag = Tag(soup, "a") aTag['name'] = "%s" % self.generateAuthorAnchor(current_author) aTag.insert(0,NavigableString(current_author)) - emTag.insert(0,aTag) - pAuthorTag.insert(0,emTag) + pAuthorTag.insert(0,aTag) divTag.insert(dtc,pAuthorTag) dtc += 1 + ''' # Insert an
between non-series and series if not current_series and non_series_books and new_entry['series']: # Insert an
@@ -2081,6 +2086,7 @@ class EPUB_MOBI(CatalogPlugin): hrTag['class'] = "series_divider" divTag.insert(dtc,hrTag) dtc += 1 + ''' # Check for series if new_entry['series'] and new_entry['series'] != current_series: @@ -2088,7 +2094,14 @@ class EPUB_MOBI(CatalogPlugin): current_series = new_entry['series'] pSeriesTag = Tag(soup,'p') pSeriesTag['class'] = "series" - pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % new_entry['series'])) + if self.opts.generate_series: + aTag = Tag(soup,'a') + aTag['href'] = "%s.html#%s_series" % ('BySeries', + re.sub('\W','',new_entry['series']).lower()) + aTag.insert(0, new_entry['series']) + pSeriesTag.insert(0, aTag) + else: + pSeriesTag.insert(0,NavigableString('%s' % new_entry['series'])) divTag.insert(dtc,pSeriesTag) dtc += 1 if current_series and not new_entry['series']: @@ -2205,13 +2218,14 @@ class EPUB_MOBI(CatalogPlugin): if not self.__generateForKindle: #

By Author

- h2Tag = Tag(soup, "h2") + pTag = Tag(soup, "p") + pTag['class'] = 'title' aTag = Tag(soup, "a") anchor_name = friendly_name.lower() aTag['name'] = anchor_name.replace(" ","") - h2Tag.insert(0,aTag) - h2Tag.insert(1,NavigableString('%s' % friendly_name)) - body.insert(btc,h2Tag) + pTag.insert(0,aTag) + pTag.insert(1,NavigableString('%s' % friendly_name)) + body.insert(btc,pTag) btc += 1 #

@@ -2254,11 +2268,14 @@ class EPUB_MOBI(CatalogPlugin): dtc = add_books_to_HTML_by_date_range(date_range_list, date_range, dtc) date_range_list = [book] + ''' if books_added_in_date_range: # Add an


separating date ranges from months hrTag = Tag(soup,'hr') + hrTag['class'] = "description_divider" divTag.insert(dtc,hrTag) dtc += 1 + ''' # >>>> Books by month <<<< # Sort titles case-insensitive for by month using series prefix @@ -2509,10 +2526,7 @@ class EPUB_MOBI(CatalogPlugin): # Fetch the database as a dictionary self.booksBySeries = self.plugin.search_sort_db(self.db, self.opts) - for series_item in self.booksBySeries: - print ' %s %s %s' % (series_item['series'],series_item['series_index'],series_item['title']) - - friendly_name = "By Series" + friendly_name = "Series" soup = self.generateHTMLEmptyHeader(friendly_name) body = soup.find('body') @@ -2604,7 +2618,9 @@ class EPUB_MOBI(CatalogPlugin): #aTag.insert(0,'%d. %s · %s' % (book['series_index'],escape(book['title']), ' & '.join(book['authors']))) # Link to book - aTag.insert(0,'%d. %s' % (book['series_index'],escape(book['title']))) + aTag.insert(0,'%d. %s (%s)' % (book['series_index'], + escape(book['title']), + strftime(u'%Y', book['pubdate'].timetuple()))) pBookTag.insert(ptc, aTag) ptc += 1 @@ -2615,8 +2631,8 @@ class EPUB_MOBI(CatalogPlugin): # Link to author aTag = Tag(soup, "a") aTag['href'] = "%s.html#%s" % ("ByAlphaAuthor", - self.generateAuthorAnchor(' & '.join(book['authors']))) - aTag.insert(0, NavigableString(' & '.join(book['authors']))) + self.generateAuthorAnchor(escape(' & '.join(book['authors'])))) + aTag.insert(0, NavigableString(' & '.join(book['authors']))) pBookTag.insert(ptc, aTag) ptc += 1 @@ -2979,7 +2995,8 @@ class EPUB_MOBI(CatalogPlugin): navLabelTag.insert(0, textTag) navPointTag.insert(0, navLabelTag) contentTag = Tag(soup, 'content') - contentTag['src'] = "content/book_%d.html" % int(self.booksByTitle[0]['id']) + #contentTag['src'] = "content/book_%d.html" % int(self.booksByTitle[0]['id']) + contentTag['src'] = "content/ByAlphaAuthor.html" navPointTag.insert(1, contentTag) cmiTag = Tag(soup, '%s' % 'calibre:meta-img') cmiTag['name'] = "mastheadImage" @@ -4015,7 +4032,7 @@ class EPUB_MOBI(CatalogPlugin): btc += 1 titleTag = body.find(attrs={'class':'title'}) - titleTag.insert(0,NavigableString('%s' % escape(self.getFriendlyGenreTag(genre)))) + titleTag.insert(0,NavigableString('%s' % escape(self.getFriendlyGenreTag(genre)))) # Insert the books by author list divTag = body.find(attrs={'class':'authors'}) @@ -4038,6 +4055,7 @@ class EPUB_MOBI(CatalogPlugin): divTag.insert(dtc,pAuthorTag) dtc += 1 + ''' # Insert an
between non-series and series if not current_series and non_series_books and book['series']: # Insert an
@@ -4045,6 +4063,7 @@ class EPUB_MOBI(CatalogPlugin): hrTag['class'] = "series_divider" divTag.insert(dtc,hrTag) dtc += 1 + ''' # Check for series if book['series'] and book['series'] != current_series: @@ -4052,7 +4071,14 @@ class EPUB_MOBI(CatalogPlugin): current_series = book['series'] pSeriesTag = Tag(soup,'p') pSeriesTag['class'] = "series" - pSeriesTag.insert(0,NavigableString(self.NOT_READ_SYMBOL + '%s' % book['series'])) + if self.opts.generate_series: + aTag = Tag(soup,'a') + aTag['href'] = "%s.html#%s_series" % ('BySeries', + re.sub('\W','',book['series']).lower()) + aTag.insert(0, book['series']) + pSeriesTag.insert(0, aTag) + else: + pSeriesTag.insert(0,NavigableString('%s' % book['series'])) divTag.insert(dtc,pSeriesTag) dtc += 1 @@ -4109,7 +4135,7 @@ class EPUB_MOBI(CatalogPlugin): def generateHTMLDescriptionHeader(self, title): title_border = '' if self.opts.fmt == 'epub' else \ - '

' + '
' header = ''' @@ -4155,7 +4181,7 @@ class EPUB_MOBI(CatalogPlugin):   -

+
@@ -4524,7 +4550,8 @@ class EPUB_MOBI(CatalogPlugin): opts.fmt = self.fmt = path_to_output.rpartition('.')[2] # Add local options - opts.creator = "calibre" + opts.creator = '%s, %s %s, %s' % (strftime('%A'), strftime('%B'), strftime('%d').lstrip('0'), strftime('%Y')) + opts.creator_sort_as = '%s %s' % ('calibre', strftime('%Y-%m-%d')) opts.connected_kindle = False # Finalize output_profile @@ -4569,9 +4596,12 @@ class EPUB_MOBI(CatalogPlugin): build_log.append(u" mount point: %s" % storage) else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) - for storage in opts.connected_device['storage']: - if storage: - build_log.append(u" mount point: %s" % storage) + try: + for storage in opts.connected_device['storage']: + if storage: + build_log.append(u" mount point: %s" % storage) + except: + build_log.append(u" (no mount points)") else: build_log.append(u" connected_device: '%s'" % opts.connected_device['name']) From 82877ca6f48960329f384c695b4a8fbd6a11e94c Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 8 Sep 2010 10:03:30 -0600 Subject: [PATCH 42/45] Fix #6701 (New e-Book reader / Gemei - GM2000) --- src/calibre/customize/builtins.py | 3 ++- src/calibre/devices/misc.py | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py index d95a7d1c8a..4c87236e71 100644 --- a/src/calibre/customize/builtins.py +++ b/src/calibre/customize/builtins.py @@ -461,7 +461,7 @@ from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK from calibre.devices.edge.driver import EDGE from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS from calibre.devices.sne.driver import SNE -from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN +from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, GEMEI from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG from calibre.devices.kobo.driver import KOBO @@ -570,6 +570,7 @@ plugins += [ KOGAN, PDNOVEL, SPECTRA, + GEMEI, ITUNES, ] plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index 7e8f5fbdd3..bb75ceabd1 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -108,4 +108,23 @@ class PDNOVEL(USBMS): with open('%s.jpg' % os.path.join(path, filename), 'wb') as coverfile: coverfile.write(coverdata[2]) +class GEMEI(USBMS): + name = 'Gemei Device Interface' + gui_name = 'GM2000' + description = _('Communicate with the GM2000') + author = 'Kovid Goyal' + supported_platforms = ['windows', 'osx', 'linux'] + + # Ordered list of supported formats + FORMATS = ['epub', 'chm', 'html', 'pdb', 'pdf', 'txt'] + + VENDOR_ID = [0x07c4] + PRODUCT_ID = [0xa4a5] + BCD = None + + VENDOR_NAME = 'CHINA' + WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'CHIP' + + EBOOK_DIR_MAIN = 'eBooks' + SUPPORTS_SUB_DIRS = True From 9242bc4f8a2e1519c6870b3d5e4e280cfcc9de37 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 8 Sep 2010 17:13:34 +0100 Subject: [PATCH 43/45] 1) Save field_metadata to preference on shutdown and in cli.py 2) refactor to use remove_dynamic_categories --- src/calibre/gui2/tag_view.py | 4 +--- src/calibre/gui2/ui.py | 7 ++++++- src/calibre/library/cli.py | 6 ++++++ src/calibre/library/database2.py | 7 +------ src/calibre/library/field_metadata.py | 6 ++++++ 5 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 72f0efd9bf..a64eb2eb9a 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -424,10 +424,8 @@ class TagsModel(QAbstractItemModel): # {{{ self.categories = [] # Reconstruct the user categories, putting them into metadata + self.db.field_metadata.remove_dynamic_categories() tb_cats = self.db.field_metadata - for k in tb_cats.keys(): - if tb_cats[k]['kind'] in ['user', 'search']: - del tb_cats[k] for user_cat in sorted(self.db.prefs.get('user_categories', {}).keys()): cat_name = user_cat+':' # add the ':' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 624935f279..1fc43116e6 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -523,11 +523,16 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ def shutdown(self, write_settings=True): try: - cf = self.library_view.model().db.clean + db = self.library_view.model().db + cf = db.clean except: pass else: cf() + # Save the current field_metadata for applications like calibre2opds + # Goes here, because if cf is valid, db is valid. + db.field_metadata.remove_dynamic_categories() + db.prefs['field_metadata'] = db.field_metadata.all_metadata() for action in self.iactions.values(): if not action.shutting_down(): return diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index c17911cc3a..6b27021c2c 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -576,6 +576,9 @@ def command_add_custom_column(args, dbpath): return 1 do_add_custom_column(get_db(dbpath, opts), args[0], args[1], args[2], opts.is_multiple, json.loads(opts.display)) + # Re-open the DB so that field_metadata is reflects the column changes + db = get_db(dbpath, opts) + db.prefs['field_metadata'] = db.field_metadata.all_metadata() return 0 def catalog_option_parser(args): @@ -799,6 +802,9 @@ def command_remove_custom_column(args, dbpath): return 1 do_remove_custom_column(get_db(dbpath, opts), args[0], opts.force) + # Re-open the DB so that field_metadata is reflects the column changes + db = get_db(dbpath, opts) + db.prefs['field_metadata'] = db.field_metadata.all_metadata() return 0 def saved_searches_option_parser(): diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 05bab95ffe..cc4ddb1c17 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -290,10 +290,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): # Reconstruct the user categories, putting them into field_metadata # Assumption is that someone else will fix them if they change. + self.field_metadata.remove_dynamic_categories() tb_cats = self.field_metadata - for k in tb_cats.keys(): - if tb_cats[k]['kind'] in ['user', 'search']: - del tb_cats[k] for user_cat in sorted(self.prefs.get('user_categories', {}).keys()): cat_name = user_cat+':' # add the ':' to avoid name collision tb_cats.add_user_category(label=cat_name, name=user_cat) @@ -338,9 +336,6 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): setattr(self, 'title_sort', functools.partial(get_property, loc=self.FIELD_MAP['sort'])) - # Save the current field_metadata for applications like calibre2opds - self.prefs['field_metadata'] = self.field_metadata.all_metadata() - def initialize_database(self): metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read() self.conn.executescript(metadata_sqlite) diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 449cb289ce..66cdee51f0 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -414,6 +414,12 @@ class FieldMetadata(dict): self._add_search_terms_to_map(key, [key]) self.custom_label_to_key_map[label+'_index'] = key + def remove_dynamic_categories(self): + for key in list(self._tb_cats.keys()): + val = self._tb_cats[key] + if val['is_category'] and val['kind'] in ('user', 'search'): + del self._tb_cats[key] + def cc_series_index_column_for(self, key): return self._tb_cats[key]['rec_index'] + 1 From 6f93adb2acda69303e76afc9c92d530a6252a7a6 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Wed, 8 Sep 2010 17:22:09 +0100 Subject: [PATCH 44/45] Leave the dynamic fields in when writing the field_metadata preference --- src/calibre/gui2/ui.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 1fc43116e6..f40eed1fcd 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -531,7 +531,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ cf() # Save the current field_metadata for applications like calibre2opds # Goes here, because if cf is valid, db is valid. - db.field_metadata.remove_dynamic_categories() db.prefs['field_metadata'] = db.field_metadata.all_metadata() for action in self.iactions.values(): if not action.shutting_down(): From 4f52525c9252caa181a9b9eff79e1c0ce0e7f694 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 8 Sep 2010 10:29:47 -0600 Subject: [PATCH 45/45] Fix #6729 (Missing XPath statement during batch convesion) --- src/calibre/devices/apple/driver.py | 2 +- src/calibre/gui2/convert/bulk.py | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index afd7958265..c6abe595b6 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -82,7 +82,7 @@ class ITUNES(DriverBase): ''' name = 'Apple device interface' - gui_name = 'Apple device' + gui_name = _('Apple device') icon = I('devices/ipad.png') description = _('Communicate with iTunes/iBooks.') supported_platforms = ['osx','windows'] diff --git a/src/calibre/gui2/convert/bulk.py b/src/calibre/gui2/convert/bulk.py index f0fdf9a9a1..1affea4e2a 100644 --- a/src/calibre/gui2/convert/bulk.py +++ b/src/calibre/gui2/convert/bulk.py @@ -58,7 +58,8 @@ class BulkConfig(Config): output_path = 'dummy.'+output_format log = Log() log.outputs = [] - self.plumber = Plumber(input_path, output_path, log) + self.plumber = Plumber(input_path, output_path, log, + merge_plugin_recs=False) def widget_factory(cls): return cls(self.stack, self.plumber.get_option_by_name,