From f1be85806e73839fd9fbef1b6bd1af8619918fa1 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 11 Sep 2010 09:51:42 +0100 Subject: [PATCH 01/10] Fix #6771 - Search renaming exception. --- src/calibre/gui2/tag_view.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index a64eb2eb9a..519d533ff6 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -512,7 +512,8 @@ class TagsModel(QAbstractItemModel): # {{{ _('The saved search name %s is already used.')%val).exec_() return False saved_searches().rename(unicode(item.data(role).toString()), val) - self.tags_view.search_item_renamed.emit() + item.tag.name = val + self.tags_view.search_item_renamed.emit() # Does a refresh else: if key == 'series': self.db.rename_series(item.tag.id, val) @@ -526,8 +527,8 @@ class TagsModel(QAbstractItemModel): # {{{ self.db.rename_custom_item(item.tag.id, val, label=self.db.field_metadata[key]['label']) self.tags_view.tag_item_renamed.emit() - item.tag.name = val - self.refresh() # Should work, because no categories can have disappeared + item.tag.name = val + self.refresh() # Should work, because no categories can have disappeared if path: idx = self.index_for_path(path) if idx.isValid(): @@ -669,7 +670,7 @@ class TagBrowserMixin(object): # {{{ self.tags_view.saved_search_edit.connect(self.do_saved_search_edit) self.tags_view.author_sort_edit.connect(self.do_author_sort_edit) self.tags_view.tag_item_renamed.connect(self.do_tag_item_renamed) - self.tags_view.search_item_renamed.connect(self.saved_search.clear_to_help) + self.tags_view.search_item_renamed.connect(self.saved_searches_changed) self.edit_categories.clicked.connect(lambda x: self.do_user_categories_edit()) From 86e68579f32972a2424771a7f3e84d046d630283 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sat, 11 Sep 2010 08:39:40 -0400 Subject: [PATCH 02/10] PDF Input: Fix bug #6734, add additional matching for unicode characters. --- src/calibre/ebooks/conversion/preprocess.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/calibre/ebooks/conversion/preprocess.py b/src/calibre/ebooks/conversion/preprocess.py index f7b803974f..256bcce6fc 100644 --- a/src/calibre/ebooks/conversion/preprocess.py +++ b/src/calibre/ebooks/conversion/preprocess.py @@ -166,6 +166,17 @@ class HTMLPreProcessor(object): (re.compile(u'`\s*()*\s*O', re.UNICODE), lambda match: u'Ò'), (re.compile(u'`\s*()*\s*u', re.UNICODE), lambda match: u'ù'), (re.compile(u'`\s*()*\s*U', re.UNICODE), lambda match: u'Ù'), + # ` with letter before + (re.compile(u'a\s*()*\s*`', re.UNICODE), lambda match: u'à'), + (re.compile(u'A\s*()*\s*`', re.UNICODE), lambda match: u'À'), + (re.compile(u'e\s*()*\s*`', re.UNICODE), lambda match: u'è'), + (re.compile(u'E\s*()*\s*`', re.UNICODE), lambda match: u'È'), + (re.compile(u'i\s*()*\s*`', re.UNICODE), lambda match: u'ì'), + (re.compile(u'I\s*()*\s*`', re.UNICODE), lambda match: u'Ì'), + (re.compile(u'o\s*()*\s*`', re.UNICODE), lambda match: u'ò'), + (re.compile(u'O\s*()*\s*`', re.UNICODE), lambda match: u'Ò'), + (re.compile(u'u\s*()*\s*`', re.UNICODE), lambda match: u'ù'), + (re.compile(u'U\s*()*\s*`', re.UNICODE), lambda match: u'Ù'), # ´ (re.compile(u'´\s*()*\s*a', re.UNICODE), lambda match: u'á'), From c4071a245d256642568aa8fc827a8e8516f0df98 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 11 Sep 2010 13:40:27 +0100 Subject: [PATCH 03/10] Fix library sorting problem introduced by calling model.refresh() in the device connection sequence. --- src/calibre/gui2/library/models.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py index bb47508531..8ad0cd6818 100644 --- a/src/calibre/gui2/library/models.py +++ b/src/calibre/gui2/library/models.py @@ -121,10 +121,8 @@ class BooksModel(QAbstractTableModel): # {{{ def set_device_connected(self, is_connected): self.device_connected = is_connected self.db.refresh_ondevice() - self.refresh() + self.refresh() # does a resort() self.research() - if is_connected and self.sorted_on[0] == 'ondevice': - self.resort() def set_book_on_device_func(self, func): self.book_on_device = func @@ -249,7 +247,7 @@ class BooksModel(QAbstractTableModel): # {{{ # the search and count records for restrictions self.searched.emit(True) - def sort(self, col, order, reset=True): + def sort(self, col, order, reset=True, update_history=True): if not self.db: return self.about_to_be_sorted.emit(self.db.id) @@ -260,23 +258,23 @@ class BooksModel(QAbstractTableModel): # {{{ self.clear_caches() self.reset() self.sorted_on = (label, order) - self.sort_history.insert(0, self.sorted_on) + if update_history: + self.sort_history.insert(0, self.sorted_on) self.sorting_done.emit(self.db.index) def refresh(self, reset=True): - try: - col = self.column_map.index(self.sorted_on[0]) - except: - col = 0 self.db.refresh(field=None) - self.sort(col, self.sorted_on[1], reset=reset) + self.resort(reset=reset) - def resort(self, reset=True): - try: - col = self.column_map.index(self.sorted_on[0]) - except ValueError: - col = 0 - self.sort(col, self.sorted_on[1], reset=reset) + def resort(self, reset=True, history=5): # Bug report needed history=4 :) + for col,ord in reversed(self.sort_history[:history]): + try: + col = self.column_map.index(col) + except ValueError: + col = 0 + self.sort(col, ord, reset=False, update_history=False) + if reset: + self.reset() def research(self, reset=True): self.search(self.last_search, reset=reset) From 96478da323e642febb94c2c1a2c9826a6b3dddb7 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sat, 11 Sep 2010 08:48:47 -0400 Subject: [PATCH 04/10] PLM Input: Fix cleanup code. --- src/calibre/ebooks/pml/pmlconverter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/pml/pmlconverter.py b/src/calibre/ebooks/pml/pmlconverter.py index 166695ff5c..3a4454725a 100644 --- a/src/calibre/ebooks/pml/pmlconverter.py +++ b/src/calibre/ebooks/pml/pmlconverter.py @@ -216,7 +216,7 @@ class PML_HTMLizer(object): html = re.sub(r'(?u)%s\s*%s' % (open % '.*?', close), '', html) else: html = re.sub(r'(?u)%s\s*%s' % (open, close), '', html) - html = re.sub(r'

\s*

', '', html) + html = re.sub(r'(?imu)

\s*

', '', html) return html def start_line(self): From dc7bc5dd5d890278d7f43377e9df944675888fc6 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sat, 11 Sep 2010 09:01:34 -0400 Subject: [PATCH 05/10] PML Input: Fix bug #6770, put toc link after header so toc link goes to correct page. --- src/calibre/ebooks/pml/pmlconverter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/pml/pmlconverter.py b/src/calibre/ebooks/pml/pmlconverter.py index 3a4454725a..6e479a71ef 100644 --- a/src/calibre/ebooks/pml/pmlconverter.py +++ b/src/calibre/ebooks/pml/pmlconverter.py @@ -556,7 +556,7 @@ class PML_HTMLizer(object): text = t else: self.toc.add_item(os.path.basename(self.file_name), id, value) - text = '%s' % (id, t) + text = '%s' % (t, id) elif c == 'm': empty = False src = self.code_value(line) From c2b3c445e17a38b5599393c943036c6c448886da Mon Sep 17 00:00:00 2001 From: John Schember Date: Sat, 11 Sep 2010 09:09:08 -0400 Subject: [PATCH 06/10] PML Input: Remove emtpy lines. --- src/calibre/ebooks/pml/pmlconverter.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/calibre/ebooks/pml/pmlconverter.py b/src/calibre/ebooks/pml/pmlconverter.py index 6e479a71ef..b0fc15197a 100644 --- a/src/calibre/ebooks/pml/pmlconverter.py +++ b/src/calibre/ebooks/pml/pmlconverter.py @@ -207,6 +207,7 @@ class PML_HTMLizer(object): while html != old: old = html html = self.cleanup_html_remove_redundant(html) + html = re.sub(r'(?imu)^\s*', '', html) return html def cleanup_html_remove_redundant(self, html): From ef8408869cebac380474deb971c4b6910680c895 Mon Sep 17 00:00:00 2001 From: John Schember Date: Sat, 11 Sep 2010 09:13:23 -0400 Subject: [PATCH 07/10] TXT Output: preserve spaces, handle tab character correct. is reduced to a single space by many renderers. --- src/calibre/ebooks/txt/processor.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/ebooks/txt/processor.py b/src/calibre/ebooks/txt/processor.py index a12e8a0761..dac1e34df7 100644 --- a/src/calibre/ebooks/txt/processor.py +++ b/src/calibre/ebooks/txt/processor.py @@ -77,7 +77,7 @@ def separate_paragraphs_print_formatted(txt): def preserve_spaces(txt): txt = txt.replace(' ', ' ') - txt = txt.replace('\t', ' ') + txt = txt.replace('\t', '    ') return txt def opf_writer(path, opf_name, manifest, spine, mi): From a58aa5f0e5f455defefe94c10f372d33763e9b75 Mon Sep 17 00:00:00 2001 From: Charles Haley <> Date: Sat, 11 Sep 2010 15:37:11 +0100 Subject: [PATCH 08/10] Fix bug reported in forum: http://www.mobileread.com/forums/showthread.php?t=98242 cache.refresh still used a parameter when calling search that was removed some releases ago. --- src/calibre/library/caches.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index b9c1211c7f..2096180f3c 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -549,7 +549,7 @@ class ResultCache(SearchQueryParser): self.sort(field, ascending) self._map_filtered = list(self._map) if self.search_restriction: - self.search('', return_matches=False, ignore_search_restriction=False) + self.search('', return_matches=False) def seriescmp(self, sidx, siidx, x, y, library_order=None): try: From 3766f34aab8b6ae8b78570fb51d17bd92edc39a7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Sep 2010 11:54:54 -0600 Subject: [PATCH 09/10] Fix regression in filename shortening that caused loss of filename extension --- src/calibre/utils/filenames.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/calibre/utils/filenames.py b/src/calibre/utils/filenames.py index 9fd57ab53c..47ccbe73c2 100644 --- a/src/calibre/utils/filenames.py +++ b/src/calibre/utils/filenames.py @@ -54,10 +54,8 @@ def shorten_components_to(length, components): r = x[0] if x is components[-1] else '' else: if x is components[-1]: - b, _, e = x.rpartition('.') - if not b and e: - b = e - e = '' + b, e = os.path.splitext(x) + if e == '.': e = '' r = b[:-delta]+e if r.startswith('.'): r = x[0]+r else: From e531b517670e90cf99b8255fd47775e50450d7d1 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 11 Sep 2010 16:16:57 -0600 Subject: [PATCH 10/10] Code organization --- src/calibre/library/caches.py | 48 ++++++++++++++++----------- src/calibre/library/field_metadata.py | 5 ++- 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 2096180f3c..eb0ceb3fe4 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -141,6 +141,8 @@ class ResultCache(SearchQueryParser): for x in self.iterall(): yield x[idx] + # Search functions {{{ + def universal_set(self): return set([i[0] for i in self._data if i is not None]) @@ -462,6 +464,30 @@ class ResultCache(SearchQueryParser): continue return matches + def search(self, query, return_matches=False): + ans = self.search_getting_ids(query, self.search_restriction) + if return_matches: + return ans + self._map_filtered = ans + + def search_getting_ids(self, query, search_restriction): + q = '' + if not query or not query.strip(): + q = search_restriction + else: + q = query + if search_restriction: + q = u'%s (%s)' % (search_restriction, query) + if not q: + return list(self._map) + matches = sorted(self.parse(q)) + return [id for id in self._map if id in matches] + + def set_search_restriction(self, s): + self.search_restriction = s + + # }}} + def remove(self, id): self._data[id] = None if id in self._map: @@ -551,6 +577,8 @@ class ResultCache(SearchQueryParser): if self.search_restriction: self.search('', return_matches=False) + # Sorting functions {{{ + def seriescmp(self, sidx, siidx, x, y, library_order=None): try: if library_order: @@ -615,24 +643,6 @@ class ResultCache(SearchQueryParser): self._map.sort(cmp=fcmp, reverse=not ascending) self._map_filtered = [id for id in self._map if id in self._map_filtered] - def search(self, query, return_matches=False): - ans = self.search_getting_ids(query, self.search_restriction) - if return_matches: - return ans - self._map_filtered = ans + # }}} - def search_getting_ids(self, query, search_restriction): - q = '' - if not query or not query.strip(): - q = search_restriction - else: - q = query - if search_restriction: - q = u'%s (%s)' % (search_restriction, query) - if not q: - return list(self._map) - matches = sorted(self.parse(q)) - return [id for id in self._map if id in matches] - def set_search_restriction(self, s): - self.search_restriction = s diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py index 66cdee51f0..096dfa66fe 100644 --- a/src/calibre/library/field_metadata.py +++ b/src/calibre/library/field_metadata.py @@ -69,6 +69,8 @@ class FieldMetadata(dict): VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime', 'int', 'float', 'bool', 'series']) + # Builtin metadata {{{ + _field_metadata = [ ('authors', {'table':'authors', 'column':'name', @@ -287,7 +289,8 @@ class FieldMetadata(dict): 'search_terms':[], 'is_custom':False, 'is_category':False}), - ] + ] + # }}} # search labels that are not db columns search_items = [ 'all',