Add framework for allowing interface actions to accept drag and drop events. Apple driver: Fix bug preventing sending books to iBooks if no books have been previously added to iBooks. Fixes #1141078 ('NoneType' object is not iterable)

This commit is contained in:
Kovid Goyal 2013-03-04 09:11:11 +05:30
commit 7e004def33
5 changed files with 96 additions and 53 deletions

View File

@ -672,6 +672,7 @@ Some limitations of PDF input are:
* Links and Tables of Contents are not supported * 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 * 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 * 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 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. output ranging anywhere from decent to unusable, depending on the input PDF.

View File

@ -30,11 +30,6 @@ class tvn24(BasicNewsRecipe):
feeds = [(u'Najnowsze', u'http://www.tvn24.pl/najnowsze.xml'), ] 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')] #(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): def preprocess_html(self, soup):
for alink in soup.findAll('a'): for alink in soup.findAll('a'):
if alink.string is not None: if alink.string is not None:

View File

@ -21,7 +21,6 @@ from calibre.utils.config import config_dir, dynamic, prefs
from calibre.utils.date import now, parse_date from calibre.utils.date import now, parse_date
from calibre.utils.zipfile import ZipFile from calibre.utils.zipfile import ZipFile
# DEBUG = False
DEBUG = CALIBRE_DEBUG DEBUG = CALIBRE_DEBUG
def strftime(fmt='%Y/%m/%d %H:%M:%S', dt=None): 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 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 we need to talk to iTunes to discover if there's a connected iPod
''' '''
if self.iTunes is None: if self.iTunes is None:
raise OpenFeedback(self.ITUNES_SANDBOX_LOCKOUT_MESSAGE) raise OpenFeedback(self.ITUNES_SANDBOX_LOCKOUT_MESSAGE)
@ -2156,6 +2154,7 @@ class ITUNES(DriverBase):
if 'iPod' in self.sources: if 'iPod' in self.sources:
connected_device = self.sources['iPod'] connected_device = self.sources['iPod']
device = self.iTunes.sources[connected_device] device = self.iTunes.sources[connected_device]
if device.playlists() is not None:
dev_books = None dev_books = None
for pl in device.playlists(): for pl in device.playlists():
if pl.special_kind() == appscript.k.Books: if pl.special_kind() == appscript.k.Books:
@ -2186,7 +2185,7 @@ class ITUNES(DriverBase):
pythoncom.CoInitialize() pythoncom.CoInitialize()
connected_device = self.sources['iPod'] connected_device = self.sources['iPod']
device = self.iTunes.sources.ItemByName(connected_device) device = self.iTunes.sources.ItemByName(connected_device)
if device.Playlists is not None:
dev_books = None dev_books = None
for pl in device.Playlists: for pl in device.Playlists:
if pl.Kind == self.PlaylistKind.index('User') and \ if pl.Kind == self.PlaylistKind.index('User') and \

View File

@ -101,6 +101,11 @@ class InterfaceAction(QObject):
#: on calibre as a whole #: on calibre as a whole
action_type = 'global' 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): def __init__(self, parent, site_customization):
QObject.__init__(self, parent) QObject.__init__(self, parent)
self.setObjectName(self.name) self.setObjectName(self.name)
@ -108,6 +113,21 @@ class InterfaceAction(QObject):
self.site_customization = site_customization self.site_customization = site_customization
self.interface_action_base_plugin = None 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): def do_genesis(self):
self.Dispatcher = partial(Dispatcher, parent=self) self.Dispatcher = partial(Dispatcher, parent=self)
self.create_action() self.create_action()

View File

@ -8,8 +8,8 @@ __copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QObject, QToolBar, Qt, QSize, QToolButton, QVBoxLayout, from PyQt4.Qt import (Qt, QAction, QLabel, QMenu, QMenuBar, QObject,
QLabel, QWidget, QAction, QMenuBar, QMenu) QToolBar, QToolButton, QSize, QVBoxLayout, QWidget)
from calibre.constants import isosx from calibre.constants import isosx
from calibre.gui2 import gprefs from calibre.gui2 import gprefs
@ -116,13 +116,30 @@ class ToolBar(QToolBar): # {{{
ch.setPopupMode(menu_mode) ch.setPopupMode(menu_mode)
return ch 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): def dragEnterEvent(self, event):
md = event.mimeData() md = event.mimeData()
if md.hasFormat("application/calibre+from_library") or \ if md.hasFormat("application/calibre+from_library") or \
md.hasFormat("application/calibre+from_device"): md.hasFormat("application/calibre+from_device"):
event.setDropAction(Qt.CopyAction) event.setDropAction(Qt.CopyAction)
event.accept() event.accept()
return
if self.check_iactions_for_drag(event, md, 'accept_enter_event'):
event.accept()
else: else:
event.ignore() event.ignore()
@ -141,12 +158,15 @@ class ToolBar(QToolBar): # {{{
break break
if allowed: if allowed:
event.acceptProposedAction() event.acceptProposedAction()
return
if self.check_iactions_for_drag(event, md, 'accept_drag_move_event'):
event.acceptProposedAction()
else: else:
event.ignore() event.ignore()
def dropEvent(self, event): def dropEvent(self, event):
data = event.mimeData() data = event.mimeData()
mime = 'application/calibre+from_library' mime = 'application/calibre+from_library'
if data.hasFormat(mime): if data.hasFormat(mime):
ids = list(map(int, str(data.data(mime)).split())) ids = list(map(int, str(data.data(mime)).split()))
@ -160,6 +180,7 @@ class ToolBar(QToolBar): # {{{
tgt = None tgt = None
self.gui.sync_to_device(tgt, False, send_ids=ids) self.gui.sync_to_device(tgt, False, send_ids=ids)
event.accept() event.accept()
return
mime = 'application/calibre+from_device' mime = 'application/calibre+from_device'
if data.hasFormat(mime): if data.hasFormat(mime):
@ -168,6 +189,13 @@ class ToolBar(QToolBar): # {{{
self.gui.iactions['Add Books'].add_books_from_device( self.gui.iactions['Add Books'].add_books_from_device(
self.gui.current_view(), paths=paths) self.gui.current_view(), paths=paths)
event.accept() 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()
# }}} # }}}