diff --git a/manual/conversion.rst b/manual/conversion.rst index feae2a4273..817821a9b1 100644 --- a/manual/conversion.rst +++ b/manual/conversion.rst @@ -672,6 +672,7 @@ Some limitations of PDF input are: * Links and Tables of Contents are not supported * PDFs that use embedded non-unicode fonts to represent non-English characters will result in garbled output for those characters * Some PDFs are made up of photographs of the page with OCRed text behind them. In such cases |app| uses the OCRed text, which can be very different from what you see when you view the PDF file + * PDFs that are used to display complex text, like right to left languages and math typesetting will not convert correctly To re-iterate **PDF is a really, really bad** format to use as input. If you absolutely must use PDF, then be prepared for an output ranging anywhere from decent to unusable, depending on the input PDF. diff --git a/recipes/tvn24.recipe b/recipes/tvn24.recipe index ed0eae574f..06629e8873 100644 --- a/recipes/tvn24.recipe +++ b/recipes/tvn24.recipe @@ -30,11 +30,6 @@ class tvn24(BasicNewsRecipe): feeds = [(u'Najnowsze', u'http://www.tvn24.pl/najnowsze.xml'), ] #(u'Polska', u'www.tvn24.pl/polska.xml'), (u'\u015awiat', u'http://www.tvn24.pl/swiat.xml'), (u'Sport', u'http://www.tvn24.pl/sport.xml'), (u'Biznes', u'http://www.tvn24.pl/biznes.xml'), (u'Meteo', u'http://www.tvn24.pl/meteo.xml'), (u'Micha\u0142ki', u'http://www.tvn24.pl/michalki.xml'), (u'Kultura', u'http://www.tvn24.pl/kultura.xml')] - def preprocess_html(self, soup): - for item in soup.findAll(style=True): - del item['style'] - return soup - def preprocess_html(self, soup): for alink in soup.findAll('a'): if alink.string is not None: diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index fef05c9820..5251e701b5 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -21,7 +21,6 @@ from calibre.utils.config import config_dir, dynamic, prefs from calibre.utils.date import now, parse_date from calibre.utils.zipfile import ZipFile -# DEBUG = False DEBUG = CALIBRE_DEBUG def strftime(fmt='%Y/%m/%d %H:%M:%S', dt=None): @@ -861,7 +860,6 @@ class ITUNES(DriverBase): Note that most of the initialization is necessarily performed in can_handle(), as we need to talk to iTunes to discover if there's a connected iPod ''' - if self.iTunes is None: raise OpenFeedback(self.ITUNES_SANDBOX_LOCKOUT_MESSAGE) @@ -2156,27 +2154,28 @@ class ITUNES(DriverBase): if 'iPod' in self.sources: connected_device = self.sources['iPod'] device = self.iTunes.sources[connected_device] - dev_books = None - for pl in device.playlists(): - if pl.special_kind() == appscript.k.Books: - if DEBUG: - logger().info(" Book playlist: '%s'" % (pl.name())) - dev_books = pl.file_tracks() - break - else: - logger().error(" book_playlist not found") - - for book in dev_books: - if book.kind() in self.Audiobooks: - if DEBUG: - logger().info(" ignoring '%s' of type '%s'" % (book.name(), book.kind())) + if device.playlists() is not None: + dev_books = None + for pl in device.playlists(): + if pl.special_kind() == appscript.k.Books: + if DEBUG: + logger().info(" Book playlist: '%s'" % (pl.name())) + dev_books = pl.file_tracks() + break else: - if DEBUG: - logger().info(" %-40.40s %-30.30s %-40.40s [%s]" % - (book.name(), book.artist(), book.composer(), book.kind())) - device_books.append(book) - if DEBUG: - logger().info() + logger().error(" book_playlist not found") + + for book in dev_books: + if book.kind() in self.Audiobooks: + if DEBUG: + logger().info(" ignoring '%s' of type '%s'" % (book.name(), book.kind())) + else: + if DEBUG: + logger().info(" %-40.40s %-30.30s %-40.40s [%s]" % + (book.name(), book.artist(), book.composer(), book.kind())) + device_books.append(book) + if DEBUG: + logger().info() elif iswindows: import pythoncom @@ -2186,29 +2185,29 @@ class ITUNES(DriverBase): pythoncom.CoInitialize() connected_device = self.sources['iPod'] device = self.iTunes.sources.ItemByName(connected_device) - - dev_books = None - for pl in device.Playlists: - if pl.Kind == self.PlaylistKind.index('User') and \ - pl.SpecialKind == self.PlaylistSpecialKind.index('Books'): - if DEBUG: - logger().info(" Books playlist: '%s'" % (pl.Name)) - dev_books = pl.Tracks - break - else: - if DEBUG: - logger().info(" no Books playlist found") - - for book in dev_books: - if book.KindAsString in self.Audiobooks: - if DEBUG: - logger().info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString)) + if device.Playlists is not None: + dev_books = None + for pl in device.Playlists: + if pl.Kind == self.PlaylistKind.index('User') and \ + pl.SpecialKind == self.PlaylistSpecialKind.index('Books'): + if DEBUG: + logger().info(" Books playlist: '%s'" % (pl.Name)) + dev_books = pl.Tracks + break else: if DEBUG: - logger().info(" %-40.40s %-30.30s %-40.40s [%s]" % (book.Name, book.Artist, book.Composer, book.KindAsString)) - device_books.append(book) - if DEBUG: - logger().info() + logger().info(" no Books playlist found") + + for book in dev_books: + if book.KindAsString in self.Audiobooks: + if DEBUG: + logger().info(" ignoring '%s' of type '%s'" % (book.Name, book.KindAsString)) + else: + if DEBUG: + logger().info(" %-40.40s %-30.30s %-40.40s [%s]" % (book.Name, book.Artist, book.Composer, book.KindAsString)) + device_books.append(book) + if DEBUG: + logger().info() finally: pythoncom.CoUninitialize() diff --git a/src/calibre/gui2/actions/__init__.py b/src/calibre/gui2/actions/__init__.py index fa67130a1c..b01b9fbfcd 100644 --- a/src/calibre/gui2/actions/__init__.py +++ b/src/calibre/gui2/actions/__init__.py @@ -101,6 +101,11 @@ class InterfaceAction(QObject): #: on calibre as a whole action_type = 'global' + #: If True, then this InterfaceAction will have the opportunity to interact + #: with drag and drop events. See the methods, :meth:`accept_enter_event`, + #: :meth`:accept_drag_move_event`, :meth:`drop_event` for details. + accepts_drops = False + def __init__(self, parent, site_customization): QObject.__init__(self, parent) self.setObjectName(self.name) @@ -108,6 +113,21 @@ class InterfaceAction(QObject): self.site_customization = site_customization self.interface_action_base_plugin = None + def accept_enter_event(self, event, mime_data): + ''' This method should return True iff this interface action is capable + of handling the drag event. ''' + return False + + def accept_drag_move_event(self, event, mime_data): + ''' This method should return True iff this interface action is capable + of handling the drag event. ''' + return False + + def drop_event(self, event, mime_data): + ''' This method should perform some useful action and return True + iff this interface action is capable of handling the drag event. ''' + return False + def do_genesis(self): self.Dispatcher = partial(Dispatcher, parent=self) self.create_action() diff --git a/src/calibre/gui2/bars.py b/src/calibre/gui2/bars.py index 50b3f3e7f5..8c2b527d62 100644 --- a/src/calibre/gui2/bars.py +++ b/src/calibre/gui2/bars.py @@ -8,8 +8,8 @@ __copyright__ = '2011, Kovid Goyal ' __docformat__ = 'restructuredtext en' -from PyQt4.Qt import (QObject, QToolBar, Qt, QSize, QToolButton, QVBoxLayout, - QLabel, QWidget, QAction, QMenuBar, QMenu) +from PyQt4.Qt import (Qt, QAction, QLabel, QMenu, QMenuBar, QObject, + QToolBar, QToolButton, QSize, QVBoxLayout, QWidget) from calibre.constants import isosx from calibre.gui2 import gprefs @@ -116,20 +116,37 @@ class ToolBar(QToolBar): # {{{ ch.setPopupMode(menu_mode) return ch - #support drag&drop from/to library from/to reader/card + # support drag&drop from/to library, from/to reader/card, enabled plugins + def check_iactions_for_drag(self, event, md, func): + if self.added_actions: + pos = event.pos() + for iac in self.gui.iactions.itervalues(): + if iac.accepts_drops: + aa = iac.qaction + w = self.widgetForAction(aa) + func = getattr(iac, func) + if (( (w is not None and w.geometry().contains(pos)) or + aa.menu().geometry().contains(pos)) and func(event, md)): + return True + return False + def dragEnterEvent(self, event): md = event.mimeData() if md.hasFormat("application/calibre+from_library") or \ md.hasFormat("application/calibre+from_device"): event.setDropAction(Qt.CopyAction) event.accept() + return + + if self.check_iactions_for_drag(event, md, 'accept_enter_event'): + event.accept() else: event.ignore() def dragMoveEvent(self, event): allowed = False md = event.mimeData() - #Drop is only allowed in the location manager widget's different from the selected one + # Drop is only allowed in the location manager widget's different from the selected one for ac in self.location_manager.available_actions: w = self.widgetForAction(ac) if w is not None: @@ -141,12 +158,15 @@ class ToolBar(QToolBar): # {{{ break if allowed: event.acceptProposedAction() + return + + if self.check_iactions_for_drag(event, md, 'accept_drag_move_event'): + event.acceptProposedAction() else: event.ignore() def dropEvent(self, event): data = event.mimeData() - mime = 'application/calibre+from_library' if data.hasFormat(mime): ids = list(map(int, str(data.data(mime)).split())) @@ -160,6 +180,7 @@ class ToolBar(QToolBar): # {{{ tgt = None self.gui.sync_to_device(tgt, False, send_ids=ids) event.accept() + return mime = 'application/calibre+from_device' if data.hasFormat(mime): @@ -168,6 +189,13 @@ class ToolBar(QToolBar): # {{{ self.gui.iactions['Add Books'].add_books_from_device( self.gui.current_view(), paths=paths) event.accept() + return + + # Give added_actions an opportunity to process the drag&drop event + if self.check_iactions_for_drag(event, data, 'drop_event'): + event.accept() + else: + event.ignore() # }}}