From 3e149dac274e18f4e06d2da96f66cf56ddcf8033 Mon Sep 17 00:00:00 2001 From: John Schember Date: Wed, 9 Mar 2011 07:37:54 -0500 Subject: [PATCH 1/9] Add text extension to txt input. --- src/calibre/ebooks/__init__.py | 2 +- src/calibre/ebooks/txt/input.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/calibre/ebooks/__init__.py b/src/calibre/ebooks/__init__.py index 0ae640113a..c5bac936b5 100644 --- a/src/calibre/ebooks/__init__.py +++ b/src/calibre/ebooks/__init__.py @@ -25,7 +25,7 @@ class DRMError(ValueError): class ParserError(ValueError): pass -BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'txtz', 'htm', 'xhtm', +BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'txtz', 'text', 'htm', 'xhtm', 'html', 'xhtml', 'pdf', 'pdb', 'pdr', 'prc', 'mobi', 'azw', 'doc', 'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'cbc', 'oebzip', 'rb', 'imp', 'odt', 'chm', 'tpz', 'azw1', 'pml', 'pmlz', 'mbp', 'tan', 'snb'] diff --git a/src/calibre/ebooks/txt/input.py b/src/calibre/ebooks/txt/input.py index 1c49eb9b35..3c256fda7a 100644 --- a/src/calibre/ebooks/txt/input.py +++ b/src/calibre/ebooks/txt/input.py @@ -22,7 +22,7 @@ class TXTInput(InputFormatPlugin): name = 'TXT Input' author = 'John Schember' description = 'Convert TXT files to HTML' - file_types = set(['txt', 'txtz']) + file_types = set(['txt', 'txtz', 'text']) options = set([ OptionRecommendation(name='paragraph_type', recommended_value='auto', From 859e3caa53b3df61f2248c13589e057fd20ac697 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 09:20:47 -0700 Subject: [PATCH 2/9] Pass none for library_uuid when opening a device in contexts where no library is available --- src/calibre/devices/__init__.py | 3 +-- src/calibre/devices/prs500/cli/main.py | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/calibre/devices/__init__.py b/src/calibre/devices/__init__.py index 0d62a8f619..1918a36cc8 100644 --- a/src/calibre/devices/__init__.py +++ b/src/calibre/devices/__init__.py @@ -30,7 +30,6 @@ def strftime(epoch, zone=time.gmtime): def get_connected_device(): from calibre.customize.ui import device_plugins from calibre.devices.scanner import DeviceScanner - import uuid dev = None scanner = DeviceScanner() scanner.scan() @@ -48,7 +47,7 @@ def get_connected_device(): for d in connected_devices: try: - d.open(str(uuid.uuid4())) + d.open() except: continue else: diff --git a/src/calibre/devices/prs500/cli/main.py b/src/calibre/devices/prs500/cli/main.py index 8a73f3fa23..6d568b01a2 100755 --- a/src/calibre/devices/prs500/cli/main.py +++ b/src/calibre/devices/prs500/cli/main.py @@ -6,7 +6,7 @@ Provides a command-line and optional graphical interface to the SONY Reader PRS- For usage information run the script. """ -import StringIO, sys, time, os, uuid +import StringIO, sys, time, os from optparse import OptionParser from calibre import __version__, __appname__ @@ -213,7 +213,7 @@ def main(): for d in connected_devices: try: - d.open(str(uuid.uuid4())) + d.open(None) except: continue else: From 438c87e022eb2e81c4cc160fd32eb92b35956666 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 11:11:13 -0700 Subject: [PATCH 3/9] Conversion: When using the Level x Table of Contents options, support the case when the level 1,2,3 items are spread over multiple HTML files. --- .../ebooks/oeb/transforms/structure.py | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/calibre/ebooks/oeb/transforms/structure.py b/src/calibre/ebooks/oeb/transforms/structure.py index 0db9b153df..fc338da692 100644 --- a/src/calibre/ebooks/oeb/transforms/structure.py +++ b/src/calibre/ebooks/oeb/transforms/structure.py @@ -13,6 +13,7 @@ from urlparse import urlparse from calibre.ebooks.oeb.base import XPNSMAP, TOC, XHTML, xml2text from calibre.ebooks import ConversionError +from calibre.utils.ordered_dict import OrderedDict def XPath(x): try: @@ -95,10 +96,8 @@ class DetectStructure(object): self.log.exception('Failed to mark chapter') def create_level_based_toc(self): - if self.opts.level1_toc is None: - return - for item in self.oeb.spine: - self.add_leveled_toc_items(item) + if self.opts.level1_toc is not None: + self.add_leveled_toc_items() def create_toc_from_chapters(self): counter = self.oeb.toc.next_play_order() @@ -145,49 +144,57 @@ class DetectStructure(object): return text, href - def add_leveled_toc_items(self, item): - level1 = XPath(self.opts.level1_toc)(item.data) - level1_order = [] - document = item - + def add_leveled_toc_items(self): + added = OrderedDict() + added2 = OrderedDict() counter = 1 - if level1: - added = {} - for elem in level1: + for document in self.oeb.spine: + previous_level1 = list(added.itervalues())[-1] if added else None + previous_level2 = list(added2.itervalues())[-1] if added2 else None + + for elem in XPath(self.opts.level1_toc)(document.data): text, _href = self.elem_to_link(document, elem, counter) counter += 1 if text: node = self.oeb.toc.add(text, _href, play_order=self.oeb.toc.next_play_order()) - level1_order.append(node) added[elem] = node #node.add(_('Top'), _href) - if self.opts.level2_toc is not None: - added2 = {} - level2 = list(XPath(self.opts.level2_toc)(document.data)) - for elem in level2: + + if self.opts.level2_toc is not None and added: + for elem in XPath(self.opts.level2_toc)(document.data): level1 = None for item in document.data.iterdescendants(): - if item in added.keys(): + if item in added: level1 = added[item] - elif item == elem and level1 is not None: + elif item == elem: + if level1 is None: + if previous_level1 is None: + break + level1 = previous_level1 text, _href = self.elem_to_link(document, elem, counter) counter += 1 if text: added2[elem] = level1.add(text, _href, play_order=self.oeb.toc.next_play_order()) - if self.opts.level3_toc is not None: - level3 = list(XPath(self.opts.level3_toc)(document.data)) - for elem in level3: + break + + if self.opts.level3_toc is not None and added2: + for elem in XPath(self.opts.level3_toc)(document.data): level2 = None for item in document.data.iterdescendants(): - if item in added2.keys(): + if item in added2: level2 = added2[item] - elif item == elem and level2 is not None: + elif item == elem: + if level2 is None: + if previous_level2 is None: + break + level2 = previous_level2 text, _href = \ self.elem_to_link(document, elem, counter) counter += 1 if text: level2.add(text, _href, - play_order=self.oeb.toc.next_play_order()) + play_order=self.oeb.toc.next_play_order()) + break From b66e308c277fad6f3bcfc32cb1cfc8b91e9a5020 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 12:03:20 -0700 Subject: [PATCH 4/9] When adding a GUI plugin, prompt the user for where the plugin should be displayed --- .../gui2/dialogs/choose_plugin_toolbars.py | 61 +++++++++++++++++++ src/calibre/gui2/preferences/plugins.py | 35 ++++++++++- 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 src/calibre/gui2/dialogs/choose_plugin_toolbars.py diff --git a/src/calibre/gui2/dialogs/choose_plugin_toolbars.py b/src/calibre/gui2/dialogs/choose_plugin_toolbars.py new file mode 100644 index 0000000000..5639286519 --- /dev/null +++ b/src/calibre/gui2/dialogs/choose_plugin_toolbars.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai + +from __future__ import (unicode_literals, division, absolute_import, + print_function) + +__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' +__docformat__ = 'restructuredtext en' +__license__ = 'GPL v3' + + +from PyQt4.Qt import QDialog, QVBoxLayout, QLabel, QDialogButtonBox, \ + QListWidget, QAbstractItemView +from PyQt4 import QtGui + +class ChoosePluginToolbarsDialog(QDialog): + + def __init__(self, parent, plugin, locations): + QDialog.__init__(self, parent) + self.locations = locations + + self.setWindowTitle( + _('Add "%s" to toolbars or menus')%plugin.name) + + self._layout = QVBoxLayout(self) + self.setLayout(self._layout) + + self._header_label = QLabel( + _('Select the toolbars and/or menus to add "%s" to:') % + plugin.name) + self._layout.addWidget(self._header_label) + + self._locations_list = QListWidget(self) + self._locations_list.setSelectionMode(QAbstractItemView.MultiSelection) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred, + QtGui.QSizePolicy.Minimum) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + self._locations_list.setSizePolicy(sizePolicy) + for key, text in locations: + self._locations_list.addItem(text) + self._layout.addWidget(self._locations_list) + + self._footer_label = QLabel( + _('You can also customise the plugin locations ' + 'using Preferences -> Customise the toolbar')) + self._layout.addWidget(self._footer_label) + + button_box = QDialogButtonBox(QDialogButtonBox.Ok | + QDialogButtonBox.Cancel) + button_box.accepted.connect(self.accept) + button_box.rejected.connect(self.reject) + self._layout.addWidget(button_box) + self.resize(self.sizeHint()) + + def selected_locations(self): + selected = [] + for row in self._locations_list.selectionModel().selectedRows(): + selected.append(self.locations[row.row()]) + return selected + diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py index acf42fee16..740119831e 100644 --- a/src/calibre/gui2/preferences/plugins.py +++ b/src/calibre/gui2/preferences/plugins.py @@ -16,9 +16,10 @@ from calibre.customize.ui import initialized_plugins, is_disabled, enable_plugin disable_plugin, plugin_customization, add_plugin, \ remove_plugin from calibre.gui2 import NONE, error_dialog, info_dialog, choose_files, \ - question_dialog + question_dialog, gprefs from calibre.utils.search_query_parser import SearchQueryParser from calibre.utils.icu import lower +from calibre.utils.ordered_dict import OrderedDict class PluginModel(QAbstractItemModel, SearchQueryParser): # {{{ @@ -281,6 +282,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): self._plugin_model.populate() self._plugin_model.reset() self.changed_signal.emit() + self.check_for_add_to_toolbars(plugin) info_dialog(self, _('Success'), _('Plugin {0} successfully installed under ' ' {1} plugins. You may have to restart calibre ' @@ -342,6 +344,37 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): plugin.name + _(' cannot be removed. It is a ' 'builtin plugin. Try disabling it instead.')).exec_() + def check_for_add_to_toolbars(self, plugin): + from calibre.gui2.preferences.toolbar import ConfigWidget + from calibre.customize import InterfaceActionBase + + if not isinstance(plugin, InterfaceActionBase): + return + + all_locations = OrderedDict(ConfigWidget.LOCATIONS) + plugin_action = plugin.load_actual_plugin(self.gui) + installed_actions = OrderedDict([ + (key, list(gprefs.get('action-layout-'+key, []))) + for key in all_locations]) + + # If already installed in a GUI container, do nothing + for action_names in installed_actions.itervalues(): + if plugin_action.name in action_names: + return + + allowed_locations = [(key, text) for key, text in + all_locations.iteritems() if key + not in plugin_action.dont_add_to] + if not allowed_locations: + return # This plugin doesn't want to live in the GUI + + from calibre.gui2.dialogs.choose_plugin_toolbars import ChoosePluginToolbarsDialog + d = ChoosePluginToolbarsDialog(self, plugin_action, allowed_locations) + if d.exec_() == d.Accepted: + for key, text in d.selected_locations(): + installed_actions = list(gprefs['action-layout-'+key]) + installed_actions.append(plugin_action.name) + gprefs['action-layout-'+key] = tuple(installed_actions) if __name__ == '__main__': from PyQt4.Qt import QApplication From dffe798a482344e18af3709cc616360e2622f051 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 12:04:05 -0700 Subject: [PATCH 5/9] ... --- src/calibre/gui2/dialogs/choose_plugin_toolbars.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/dialogs/choose_plugin_toolbars.py b/src/calibre/gui2/dialogs/choose_plugin_toolbars.py index 5639286519..ddf8e162e8 100644 --- a/src/calibre/gui2/dialogs/choose_plugin_toolbars.py +++ b/src/calibre/gui2/dialogs/choose_plugin_toolbars.py @@ -26,7 +26,7 @@ class ChoosePluginToolbarsDialog(QDialog): self.setLayout(self._layout) self._header_label = QLabel( - _('Select the toolbars and/or menus to add "%s" to:') % + _('Select the toolbars and/or menus to add %s to:') % plugin.name) self._layout.addWidget(self._header_label) From 32aca79f74cb032f35fbfd3427c64e29e174dba7 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 12:39:37 -0700 Subject: [PATCH 6/9] Fix #9295 (problem with article redirects on La Nacion website) --- resources/recipes/lanacion.recipe | 100 ++++++++++++++++++++---------- 1 file changed, 66 insertions(+), 34 deletions(-) diff --git a/resources/recipes/lanacion.recipe b/resources/recipes/lanacion.recipe index 05e777ec67..425aa9b193 100644 --- a/resources/recipes/lanacion.recipe +++ b/resources/recipes/lanacion.recipe @@ -1,5 +1,5 @@ __license__ = 'GPL v3' -__copyright__ = '2008-2010, Darko Miletic ' +__copyright__ = '2008-2011, Darko Miletic ' ''' lanacion.com.ar ''' @@ -19,9 +19,10 @@ class Lanacion(BasicNewsRecipe): language = 'es_AR' publication_type = 'newspaper' remove_empty_feeds = True - masthead_url = 'http://www.lanacion.com.ar/imgs/layout/logos/ln341x47.gif' - extra_css = """ h1{font-family: Georgia,serif} - h2{color: #626262} + masthead_url = 'http://www.lanacion.com.ar/_ui/desktop/imgs/layout/logos/ln341x47.gif' + extra_css = """ + h1{font-family: Georgia,serif} + h2{color: #626262; font-weight: normal; font-size: 1.1em} body{font-family: Arial,sans-serif} img{margin-top: 0.5em; margin-bottom: 0.2em; display: block} .notaFecha{color: #808080} @@ -37,47 +38,78 @@ class Lanacion(BasicNewsRecipe): , 'language' : language } - keep_only_tags = [dict(name='div', attrs={'class':['nota floatFix','topNota','nota','post']})] + keep_only_tags = [dict(name='div', attrs={'id':'content'})] + remove_tags = [ dict(name='div' , attrs={'class':'notaComentario floatFix noprint' }) ,dict(name='ul' , attrs={'class':['cajaHerramientas cajaTop noprint','herramientas noprint']}) - ,dict(name='div' , attrs={'class':['cajaHerramientas noprint','cajaHerramientas floatFix'] }) - ,dict(attrs={'class':['titulosMultimedia','derecha','techo color','encuesta','izquierda compartir','floatFix','videoCentro']}) + ,dict(name='div' , attrs={'class':['titulosMultimedia','herramientas noprint','cajaHerramientas noprint','cajaHerramientas floatFix'] }) + ,dict(attrs={'class':['izquierda','espacio17','espacio10','espacio20','floatFix ultimasNoticias','relacionadas','titulosMultimedia','derecha','techo color','encuesta','izquierda compartir','floatFix','videoCentro']}) ,dict(name=['iframe','embed','object','form','base','hr','meta','link','input']) ] + remove_tags_after = dict(attrs={'class':['tags','nota-destacado']}) remove_attributes = ['height','width','visible','onclick','data-count','name'] feeds = [ - (u'Ultimas noticias' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?origen=2' ) - ,(u'Politica' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=30' ) - ,(u'Economia' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=272' ) - ,(u'Deportes' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=131' ) - ,(u'Informacion General' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=21' ) - ,(u'Cultura' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=1' ) - ,(u'Opinion' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=28' ) - ,(u'Espectaculos' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=120' ) - ,(u'Exterior' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7' ) - ,(u'Ciencia&Salud' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=498' ) - ,(u'Revista' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=494' ) - ,(u'Enfoques' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=421' ) - ,(u'Comercio Exterior' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=347' ) - ,(u'Tecnologia' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=432' ) - ,(u'Arquitectura' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=366' ) - ,(u'Turismo' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=504' ) - ,(u'Al volante' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=371' ) - ,(u'El Campo' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=337' ) - ,(u'Moda y Belleza' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=1312' ) - ,(u'Inmuebles Comerciales', u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=1363' ) - ,(u'Countries' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=1348' ) - ,(u'adnCultura' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=6734' ) - ,(u'The Wall Street Journal Americas', u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=6373' ) - ,(u'Estilo de vida' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7353' ) - ,(u'Management' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7380' ) - ,(u'Bicentenario' , u'http://www.lanacion.com.ar/herramientas/rss/index.asp?categoria_id=7276' ) + (u'Ultimas Noticias' , u'http://servicios.lanacion.com.ar/herramientas/rss/origen=2' ) + ,(u'Politica' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=30' ) + ,(u'Deportes' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=131' ) + ,(u'Economia' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=272' ) + ,(u'Informacion General' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=21' ) + ,(u'Cultura' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=1' ) + ,(u'Opinion' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=28' ) + ,(u'Espectaculos' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=120' ) + ,(u'Exterior' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=7' ) + ,(u'Ciencia&Salud' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=498' ) + ,(u'Revista' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=494' ) + ,(u'Enfoques' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=421' ) + ,(u'Comercio Exterior' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=347' ) + ,(u'Tecnologia' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=432' ) + ,(u'Arquitectura' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=366' ) + ,(u'Turismo' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=504' ) + ,(u'Al volante' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=371' ) + ,(u'El Campo' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=337' ) + ,(u'Moda y Belleza' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=1312') + ,(u'Inmuebles Comerciales', u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=1363') + ,(u'Countries' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=1348') + ,(u'adnCultura' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=6734') + ,(u'The WSJ Americas' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=6373') + ,(u'Comunidad' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=1344') + ,(u'Management' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=7380') + ,(u'Bicentenario' , u'http://servicios.lanacion.com.ar/herramientas/rss/categoria_id=7276') ] + + def get_browser(self): + br = BasicNewsRecipe.get_browser() + br.set_debug_redirects(True) + br.set_debug_responses(True) + br.set_debug_http(True) + return br + + def get_article_url(self, article): + link = BasicNewsRecipe.get_article_url(self,article) + if link.startswith('http://blogs.lanacion') and not link.endswith('/'): + return None + return link + def preprocess_html(self, soup): for item in soup.findAll(style=True): del item['style'] - return self.adeify_images(soup) + for item in soup.findAll('a'): + limg = item.find('img') + if item.string is not None: + str = item.string + item.replaceWith(str) + else: + if limg: + item.name = 'div' + item.attrs = [] + else: + str = self.tag_to_string(item) + item.replaceWith(str) + for item in soup.findAll('img'): + if not item.has_key('alt'): + item['alt'] = 'image' + return soup From 0710e436c9cb86e897d81b6068a4dafc072ae366 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 12:41:02 -0700 Subject: [PATCH 7/9] ... --- src/calibre/gui2/preferences/plugins.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/calibre/gui2/preferences/plugins.py b/src/calibre/gui2/preferences/plugins.py index 740119831e..85221766f2 100644 --- a/src/calibre/gui2/preferences/plugins.py +++ b/src/calibre/gui2/preferences/plugins.py @@ -372,7 +372,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): d = ChoosePluginToolbarsDialog(self, plugin_action, allowed_locations) if d.exec_() == d.Accepted: for key, text in d.selected_locations(): - installed_actions = list(gprefs['action-layout-'+key]) + installed_actions = list(gprefs.get('action-layout-'+key, [])) installed_actions.append(plugin_action.name) gprefs['action-layout-'+key] = tuple(installed_actions) From f20c34b6f66ff85f424347cc7b269ecdb9c51223 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 13:57:23 -0700 Subject: [PATCH 8/9] Oakland North by noah --- resources/recipes/oakland_north.recipe | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 resources/recipes/oakland_north.recipe diff --git a/resources/recipes/oakland_north.recipe b/resources/recipes/oakland_north.recipe new file mode 100644 index 0000000000..0ad165be40 --- /dev/null +++ b/resources/recipes/oakland_north.recipe @@ -0,0 +1,23 @@ +from calibre.web.feeds.news import BasicNewsRecipe +import re + +class AdvancedUserRecipe1299640653(BasicNewsRecipe): + title = u'Oakland North' + oldest_article = 30 + max_articles_per_feed = 100 + + language = 'en' + __author__ = 'noah' + description = 'Oakland North' + category = 'news' + no_stylesheets = True + + masthead_url = 'http://oaklandnorth.net/wp-content/themes/oaklandnorth/images/masthead.png' + + keep_only_tags = [dict(name='div', attrs={'class':re.compile(r'\bpost\b(?!-)', re.IGNORECASE)})] + + remove_tags_after = [dict(name='p', attrs={'class':'post-postscript'})] + + remove_tags = [dict(name='p', attrs={'class':'post-postscript'})] + + feeds = [(u'All Headlines', u'http://oaklandnorth.net/feed/')] From c93547878cb3109a7b9e89cc5f9641e49d919422 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Wed, 9 Mar 2011 15:09:16 -0700 Subject: [PATCH 9/9] Fix #9343 (Convert from rtf - bug?) --- src/calibre/ebooks/rtf2xml/tokenize.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/calibre/ebooks/rtf2xml/tokenize.py b/src/calibre/ebooks/rtf2xml/tokenize.py index 3dcaf0fcb1..25640586db 100755 --- a/src/calibre/ebooks/rtf2xml/tokenize.py +++ b/src/calibre/ebooks/rtf2xml/tokenize.py @@ -46,7 +46,8 @@ class Tokenize: def __remove_uc_chars(self, startchar, token): for i in xrange(startchar, len(token)): - if token[i] == " ": + #handle the case of an uc char with a terminating blank before ansi char + if token[i] == " " and self.__uc_char: continue elif self.__uc_char: self.__uc_char -= 1