From f30be27ccfc054effacd96455757ff66da672a6a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sun, 4 Dec 2011 16:26:22 +0530 Subject: [PATCH 01/15] Fix #899839 (News "Die Zeit (subscription only" doesn't work (patch applied)) --- recipes/zeitde_sub.recipe | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/recipes/zeitde_sub.recipe b/recipes/zeitde_sub.recipe index 583e86e7ac..dfa52e8504 100644 --- a/recipes/zeitde_sub.recipe +++ b/recipes/zeitde_sub.recipe @@ -131,7 +131,7 @@ class ZeitEPUBAbo(BasicNewsRecipe): browser.form['pass']=self.password browser.submit() # now find the correct file, we will still use the ePub file - epublink = browser.find_link(text_regex=re.compile('.*Ausgabe als Datei im ePub-Format.*')) + epublink = browser.find_link(text_regex=re.compile('.*Download als Datei im ePub-Format für eReader.*')) response = browser.follow_link(epublink) self.report_progress(1,_('next step')) From 6a61953e736d79ed873870616c4377bfc1eafbe5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 5 Dec 2011 08:27:02 +0530 Subject: [PATCH 02/15] Prevent the adding books dialog from becoming too wide --- src/calibre/gui2/add.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/gui2/add.py b/src/calibre/gui2/add.py index 928f68573b..2fc14c8238 100644 --- a/src/calibre/gui2/add.py +++ b/src/calibre/gui2/add.py @@ -9,7 +9,7 @@ from PyQt4.Qt import QThread, QObject, Qt, QProgressDialog, pyqtSignal, QTimer from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2 import (question_dialog, error_dialog, info_dialog, gprefs, - warning_dialog) + warning_dialog, available_width) from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata import MetaInformation from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG @@ -244,6 +244,7 @@ class Adder(QObject): # {{{ def __init__(self, parent, db, callback, spare_server=None): QObject.__init__(self, parent) self.pd = ProgressDialog(_('Adding...'), parent=parent) + self.pd.setMaximumWidth(min(600, int(available_width()*0.75))) self.spare_server = spare_server self.db = db self.pd.setModal(True) From 21b74885082e0b2c8766e5bd1b7268e47127dcf1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 5 Dec 2011 09:47:59 +0530 Subject: [PATCH 03/15] Bulk convert dialog: Disable the Use saved conversion settings checkbox when none of the books being converted has saved conversion settings --- src/calibre/gui2/convert/bulk.py | 9 ++++++++- src/calibre/gui2/tools.py | 5 ++++- src/calibre/library/database.py | 5 +++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/convert/bulk.py b/src/calibre/gui2/convert/bulk.py index 576b3ca3e7..abb66c1fd8 100644 --- a/src/calibre/gui2/convert/bulk.py +++ b/src/calibre/gui2/convert/bulk.py @@ -25,7 +25,8 @@ from calibre.utils.logging import Log class BulkConfig(Config): - def __init__(self, parent, db, preferred_output_format=None): + def __init__(self, parent, db, preferred_output_format=None, + has_saved_settings=True): ResizableDialog.__init__(self, parent) self.setup_output_formats(db, preferred_output_format) @@ -54,6 +55,12 @@ class BulkConfig(Config): rb = self.buttonBox.button(self.buttonBox.RestoreDefaults) rb.setVisible(False) self.groups.setMouseTracking(True) + if not has_saved_settings: + o = self.opt_individual_saved_settings + o.setEnabled(False) + o.setToolTip(_('None of the selected books have saved conversion ' + 'settings.')) + o.setChecked(False) def setup_pipeline(self, *args): diff --git a/src/calibre/gui2/tools.py b/src/calibre/gui2/tools.py index 021617aa4a..f1df707ad4 100644 --- a/src/calibre/gui2/tools.py +++ b/src/calibre/gui2/tools.py @@ -112,7 +112,10 @@ def convert_bulk_ebook(parent, queue, db, book_ids, out_format=None, args=[]): if total == 0: return None, None, None - d = BulkConfig(parent, db, out_format) + has_saved_settings = db.has_conversion_options(book_ids) + + d = BulkConfig(parent, db, out_format, + has_saved_settings=has_saved_settings) if d.exec_() != QDialog.Accepted: return None diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index 2138b2f1eb..86316c738e 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -1085,6 +1085,11 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; return cPickle.loads(str(data)) return None + def has_conversion_options(self, ids, format='PIPE'): + return self.conn.get(''' + SELECT data FROM conversion_options WHERE book IN %r AND + format=? LIMIT 1'''%(tuple(ids),), (format,), all=False) is not None + def delete_conversion_options(self, id, format, commit=True): self.conn.execute('DELETE FROM conversion_options WHERE book=? AND format=?', (id, format.upper())) From 7eb2914c679de90503e7581a5a8b358055e83f67 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 5 Dec 2011 10:31:34 +0530 Subject: [PATCH 04/15] ... --- src/calibre/library/database.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/calibre/library/database.py b/src/calibre/library/database.py index 86316c738e..fc29df5f02 100644 --- a/src/calibre/library/database.py +++ b/src/calibre/library/database.py @@ -1086,9 +1086,12 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE; return None def has_conversion_options(self, ids, format='PIPE'): + ids = tuple(ids) + if len(ids) > 50000: + return True return self.conn.get(''' SELECT data FROM conversion_options WHERE book IN %r AND - format=? LIMIT 1'''%(tuple(ids),), (format,), all=False) is not None + format=? LIMIT 1'''%(ids,), (format,), all=False) is not None def delete_conversion_options(self, id, format, commit=True): self.conn.execute('DELETE FROM conversion_options WHERE book=? AND format=?', From a78aa3e12a7e1d3d1f023ddb5403b7d74b28c1f2 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 5 Dec 2011 11:11:47 +0530 Subject: [PATCH 05/15] Greatly reduce the delay at the end of a bulk metadata edit operation that operates on a very large number (thousands) of books --- src/calibre/gui2/library/views.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 6d04cbd443..3244a35545 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -5,8 +5,9 @@ __license__ = 'GPL v3' __copyright__ = '2010, Kovid Goyal ' __docformat__ = 'restructuredtext en' -import os +import os, itertools, operator from functools import partial +from future_builtins import map from PyQt4.Qt import (QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication, @@ -793,8 +794,13 @@ class BooksView(QTableView): # {{{ sel = QItemSelection() m = self.model() max_col = m.columnCount(QModelIndex()) - 1 - for row in rows: - sel.select(m.index(row, 0), m.index(row, max_col)) + # Create a range based selector for each set of contiguous rows + # as supplying selectors for each individual row causes very poor + # performance if a large number of rows has to be selected. + for k, g in itertools.groupby(enumerate(rows), lambda (i,x):i-x): + group = list(map(operator.itemgetter(1), g)) + sel.merge(QItemSelection(m.index(min(group), 0), + m.index(max(group), max_col)), sm.Select) sm.select(sel, sm.ClearAndSelect) def get_selected_ids(self): From e45cd49796dada0cb3de17fae543716948331545 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 5 Dec 2011 11:52:18 +0530 Subject: [PATCH 06/15] Have the Esc shortcut perform exactly the same set of actions as clicking the clear button. Fixes #900048 ([Enhancement] Request keyboard shortcut for Reset Quick Search) --- src/calibre/gui2/ui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 63db7a561c..e035777c5e 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -359,7 +359,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ 'log will be displayed automatically.')%self.gui_debug, show=True) def esc(self, *args): - self.search.clear() + self.clear_button.click() def start_content_server(self, check_started=True): from calibre.library.server.main import start_threaded_server From 2d5501cc9e59edb3e207bc7d5d79a20417ffc48a Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Dec 2011 08:52:43 +0530 Subject: [PATCH 07/15] E-book viewer: Fix searching for text that is represented as entities in the underlying HTML. Fixes #899573 (Private bug) --- src/calibre/ebooks/oeb/iterator.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/calibre/ebooks/oeb/iterator.py b/src/calibre/ebooks/oeb/iterator.py index 2e423a25a1..bfd2954cd1 100644 --- a/src/calibre/ebooks/oeb/iterator.py +++ b/src/calibre/ebooks/oeb/iterator.py @@ -18,7 +18,8 @@ from calibre.ebooks.chardet import xml_to_unicode from calibre.utils.zipfile import safe_replace from calibre.utils.config import DynamicConfig from calibre.utils.logging import Log -from calibre import guess_type, prints, prepare_string_for_xml +from calibre import (guess_type, prints, prepare_string_for_xml, + xml_replace_entities) from calibre.ebooks.oeb.transforms.cover import CoverManager from calibre.constants import filesystem_encoding @@ -96,13 +97,19 @@ class EbookIterator(object): self.ebook_ext = ext.replace('original_', '') def search(self, text, index, backwards=False): - text = text.lower() + text = prepare_string_for_xml(text.lower()) pmap = [(i, path) for i, path in enumerate(self.spine)] if backwards: pmap.reverse() for i, path in pmap: if (backwards and i < index) or (not backwards and i > index): - if text in open(path, 'rb').read().decode(path.encoding).lower(): + with open(path, 'rb') as f: + raw = f.read().decode(path.encoding) + try: + raw = xml_replace_entities(raw) + except: + pass + if text in raw.lower(): return i def find_missing_css_files(self): From 1aa2dc2a3cca578b4606331081dcde61be0eedfa Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Dec 2011 10:09:52 +0530 Subject: [PATCH 08/15] MOBI Output: Handle links to inline anchors placed inside large blocks of text correctly, i.e. the link should not point to the start of the block. Fixes #899831 (hyperlinks sometimes broken mobi output) --- src/calibre/ebooks/mobi/mobiml.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/mobiml.py b/src/calibre/ebooks/mobi/mobiml.py index ca48fb0a7f..1a822ce1bd 100644 --- a/src/calibre/ebooks/mobi/mobiml.py +++ b/src/calibre/ebooks/mobi/mobiml.py @@ -30,6 +30,8 @@ CONTENT_TAGS = set(['img', 'hr', 'br']) NOT_VTAGS = HEADER_TAGS | NESTABLE_TAGS | TABLE_TAGS | SPECIAL_TAGS | \ CONTENT_TAGS +LEAF_TAGS = set(['base', 'basefont', 'frame', 'link', 'meta', 'area', 'br', +'col', 'hr', 'img', 'input', 'param']) PAGE_BREAKS = set(['always', 'left', 'right']) COLLAPSE = re.compile(r'[ \t\r\n\v]+') @@ -246,7 +248,17 @@ class MobiMLizer(object): last.text = None else: last = bstate.body[-1] - last.addprevious(anchor) + # We use append instead of addprevious so that inline + # anchors in large blocks point to the correct place. See + # https://bugs.launchpad.net/calibre/+bug/899831 + # This could potentially break if inserting an anchor at + # this point in the markup is illegal, but I cannot think + # of such a case offhand. + if barename(last.tag) in LEAF_TAGS: + last.addprevious(anchor) + else: + last.append(anchor) + istate.ids.clear() if not text: return From b6abccf853b634c498cbd8c30c9b91515b24c692 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Dec 2011 10:53:57 +0530 Subject: [PATCH 09/15] MOBI Output: Do not ignore an empty anchor at the end of a block element. --- src/calibre/ebooks/mobi/mobiml.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/mobi/mobiml.py b/src/calibre/ebooks/mobi/mobiml.py index 1a822ce1bd..38d4cdde06 100644 --- a/src/calibre/ebooks/mobi/mobiml.py +++ b/src/calibre/ebooks/mobi/mobiml.py @@ -540,7 +540,11 @@ class MobiMLizer(object): old_mim = self.opts.mobi_ignore_margins self.opts.mobi_ignore_margins = False - if text or tag in CONTENT_TAGS or tag in NESTABLE_TAGS: + if (text or tag in CONTENT_TAGS or tag in NESTABLE_TAGS or ( + # We have an id but no text and no children, the id should still + # be added. + istate.ids and tag in ('a', 'span', 'i', 'b', 'u') and + len(elem)==0)): self.mobimlize_content(tag, text, bstate, istates) for child in elem: self.mobimlize_elem(child, stylizer, bstate, istates) From 83a095de32a0dc19632b5ac48fbb5e9e3568aeda Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Tue, 6 Dec 2011 19:18:49 +0530 Subject: [PATCH 10/15] ... --- src/calibre/devices/misc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/devices/misc.py b/src/calibre/devices/misc.py index 4f3e914f75..742ea6b0f1 100644 --- a/src/calibre/devices/misc.py +++ b/src/calibre/devices/misc.py @@ -224,7 +224,7 @@ class TREKSTOR(USBMS): FORMATS = ['epub', 'txt', 'pdf'] VENDOR_ID = [0x1e68] - PRODUCT_ID = [0x0041, 0x0042, 0x0052, + PRODUCT_ID = [0x0041, 0x0042, 0x0052, 0x004e, 0x003e # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091 ] BCD = [0x0002] From 28d4a74d4c51488f2b2eb65b1e49a0a531bc2cb2 Mon Sep 17 00:00:00 2001 From: Alex Stanev Date: Tue, 6 Dec 2011 16:09:19 +0200 Subject: [PATCH 11/15] Improvements for Chitanka store search --- src/calibre/gui2/store/stores/chitanka_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/store/stores/chitanka_plugin.py b/src/calibre/gui2/store/stores/chitanka_plugin.py index a1a22797c8..58ef109dba 100644 --- a/src/calibre/gui2/store/stores/chitanka_plugin.py +++ b/src/calibre/gui2/store/stores/chitanka_plugin.py @@ -40,9 +40,9 @@ class ChitankaStore(BasicStoreConfig, StorePlugin): d.exec_() def search(self, query, max_results=10, timeout=60): - # check for cyrilic symbols before performing search + # check for cyrillic symbols before performing search uquery = unicode(query.strip(), 'utf-8') - reObj = re.search(u'^[а-яА-Я\\d]{4,}[а-яА-Я\\d\\s]*$', uquery) + reObj = re.search(u'^[а-яА-Я\\d\\s]{3,}$', uquery) if not reObj: return From 0c72fca935669803aa85280e2433b02d2748416f Mon Sep 17 00:00:00 2001 From: Alex Stanev Date: Tue, 6 Dec 2011 16:23:25 +0200 Subject: [PATCH 12/15] Improvements for e-knigi store search --- src/calibre/gui2/store/stores/eknigi_plugin.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/gui2/store/stores/eknigi_plugin.py b/src/calibre/gui2/store/stores/eknigi_plugin.py index ee87b771be..7d88465f62 100644 --- a/src/calibre/gui2/store/stores/eknigi_plugin.py +++ b/src/calibre/gui2/store/stores/eknigi_plugin.py @@ -46,9 +46,9 @@ class eKnigiStore(BasicStoreConfig, StorePlugin): d.exec_() def search(self, query, max_results=10, timeout=60): - # check for cyrilic symbols before performing search + # check for cyrillic symbols before performing search uquery = unicode(query.strip(), 'utf-8') - reObj = re.search(u'^[а-яА-Я\\d]{2,}[а-яА-Я\\d\\s]*$', uquery) + reObj = re.search(u'^[а-яА-Я\\d\\s]{2,}$', uquery) if not reObj: return From e5ff044db3f3e7e55bb23da7d847790067250ee7 Mon Sep 17 00:00:00 2001 From: John Schember Date: Tue, 6 Dec 2011 19:34:48 -0500 Subject: [PATCH 13/15] Store: Make search entry have focus in search dialog. --- src/calibre/gui2/store/search/search.ui | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/calibre/gui2/store/search/search.ui b/src/calibre/gui2/store/search/search.ui index ba5cb2b975..83f42cd0d1 100644 --- a/src/calibre/gui2/store/search/search.ui +++ b/src/calibre/gui2/store/search/search.ui @@ -14,7 +14,7 @@ Get Books - + :/images/store.png:/images/store.png @@ -82,8 +82,8 @@ 0 0 - 173 - 106 + 193 + 127 @@ -254,6 +254,19 @@
widgets.h
+ + search_edit + search + results_view + store_list + select_all_stores + select_invert_stores + select_none_stores + configure + open_external + close + adv_search_button + From 92e18a475545596a4274a14a2bf1b217195188f0 Mon Sep 17 00:00:00 2001 From: John Schember Date: Tue, 6 Dec 2011 19:36:39 -0500 Subject: [PATCH 14/15] Store: Search, increase default max results per store to 15. --- src/calibre/gui2/store/search/search.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py index 9413c51a6e..c8eb53ade9 100644 --- a/src/calibre/gui2/store/search/search.py +++ b/src/calibre/gui2/store/search/search.py @@ -252,7 +252,7 @@ class SearchDialog(QDialog, Ui_Dialog): # Milliseconds self.hang_time = self.config.get('hang_time', 75) * 1000 - self.max_results = self.config.get('max_results', 10) + self.max_results = self.config.get('max_results', 15) self.should_open_external = self.config.get('open_external', True) # Number of threads to run for each type of operation From b9dfec645fb852f98131d116f0b0e750d2d57a01 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 7 Dec 2011 08:55:54 +0530 Subject: [PATCH 15/15] Fix ampersands in titles not displaying in the Cover Browser --- src/calibre/gui2/cover_flow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/cover_flow.py b/src/calibre/gui2/cover_flow.py index 0352c9603f..4ba6c198f2 100644 --- a/src/calibre/gui2/cover_flow.py +++ b/src/calibre/gui2/cover_flow.py @@ -70,7 +70,7 @@ if pictureflow is not None: ans = '' except: ans = '' - return ans + return ans.replace('&', '&&') def subtitle(self, index): try: