OSX: Allow drag and drop of file onto viewer dock icon to view file

This commit is contained in:
Kovid Goyal 2016-05-22 18:45:22 +05:30
parent 1b7c86dbaf
commit 55c74f69d7
2 changed files with 42 additions and 7 deletions

View File

@ -603,7 +603,7 @@ class Py2App(object):
except: except:
self.warn('WARNING: Failed to byte-compile', y) self.warn('WARNING: Failed to byte-compile', y)
def create_app_clone(self, name, specialise_plist): def create_app_clone(self, name, specialise_plist, remove_doc_types=True):
info('\nCreating ' + name) info('\nCreating ' + name)
cc_dir = os.path.join(self.contents_dir, name, 'Contents') cc_dir = os.path.join(self.contents_dir, name, 'Contents')
exe_dir = join(cc_dir, 'MacOS') exe_dir = join(cc_dir, 'MacOS')
@ -614,7 +614,8 @@ class Py2App(object):
if x == 'Info.plist': if x == 'Info.plist':
plist = plistlib.readPlist(join(self.contents_dir, x)) plist = plistlib.readPlist(join(self.contents_dir, x))
specialise_plist(plist) specialise_plist(plist)
plist.pop('CFBundleDocumentTypes') if remove_doc_types:
plist.pop('CFBundleDocumentTypes')
exe = plist['CFBundleExecutable'] exe = plist['CFBundleExecutable']
# We cannot symlink the bundle executable as if we do, # We cannot symlink the bundle executable as if we do,
# codesigning fails # codesigning fails
@ -642,7 +643,8 @@ class Py2App(object):
@flush @flush
def create_gui_apps(self): def create_gui_apps(self):
def specialise_plist(launcher, plist): from calibre.customize.ui import all_input_formats
def specialise_plist(launcher, remove_types, plist):
plist['CFBundleDisplayName'] = plist['CFBundleName'] = { plist['CFBundleDisplayName'] = plist['CFBundleName'] = {
'ebook-viewer':'E-book Viewer', 'ebook-edit':'Edit Book', 'calibre-debug': 'calibre (debug)', 'ebook-viewer':'E-book Viewer', 'ebook-edit':'Edit Book', 'calibre-debug': 'calibre (debug)',
}[launcher] }[launcher]
@ -650,8 +652,14 @@ class Py2App(object):
if launcher != 'calibre-debug': if launcher != 'calibre-debug':
plist['CFBundleIconFile'] = launcher + '.icns' plist['CFBundleIconFile'] = launcher + '.icns'
plist['CFBundleIdentifier'] = 'com.calibre-ebook.' + launcher plist['CFBundleIdentifier'] = 'com.calibre-ebook.' + launcher
if not remove_types:
input_formats = sorted(all_input_formats())
e = plist['CFBundleDocumentTypes'][0]
exts = 'epub azw3'.split() if launcher == 'ebook-edit' else input_formats
e['CFBundleTypeExtensions'] = exts
for launcher in ('ebook-viewer', 'ebook-edit', 'calibre-debug'): for launcher in ('ebook-viewer', 'ebook-edit', 'calibre-debug'):
self.create_app_clone(launcher + '.app', partial(specialise_plist, launcher)) remove_types = launcher == 'calibre-debug'
self.create_app_clone(launcher + '.app', partial(specialise_plist, launcher, remove_types), remove_doc_types=remove_types)
@flush @flush
def copy_site(self): def copy_site(self):

View File

@ -7,7 +7,7 @@ from threading import Thread
from collections import namedtuple from collections import namedtuple
from PyQt5.Qt import ( from PyQt5.Qt import (
QApplication, Qt, QIcon, QTimer, QByteArray, QSize, QTime, QApplication, Qt, QIcon, QTimer, QByteArray, QSize, QTime, QObject,
QPropertyAnimation, QUrl, QInputDialog, QAction, QModelIndex, pyqtSignal) QPropertyAnimation, QUrl, QInputDialog, QAction, QModelIndex, pyqtSignal)
from calibre.gui2.viewer.ui import Main as MainWindow from calibre.gui2.viewer.ui import Main as MainWindow
@ -97,7 +97,7 @@ class EbookViewer(MainWindow):
msg_from_anotherinstance = pyqtSignal(object) msg_from_anotherinstance = pyqtSignal(object)
def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None, def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None,
start_in_fullscreen=False, continue_reading=False, listener=None): start_in_fullscreen=False, continue_reading=False, listener=None, file_events=()):
MainWindow.__init__(self, debug_javascript) MainWindow.__init__(self, debug_javascript)
self.view.magnification_changed.connect(self.magnification_changed) self.view.magnification_changed.connect(self.magnification_changed)
self.closed = False self.closed = False
@ -184,11 +184,14 @@ class EbookViewer(MainWindow):
self.set_bookmarks([]) self.set_bookmarks([])
self.load_theme_menu() self.load_theme_menu()
file_events.got_file.connect(self.load_ebook)
if pathtoebook is not None: if pathtoebook is not None:
f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at) f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at)
QTimer.singleShot(50, f) QTimer.singleShot(50, f)
elif continue_reading: elif continue_reading:
QTimer.singleShot(50, self.continue_reading) QTimer.singleShot(50, self.continue_reading)
else:
QTimer.singleShot(50, file_events.flush)
self.window_mode_changed = None self.window_mode_changed = None
self.toggle_toolbar_action = QAction(_('Show/hide controls'), self) self.toggle_toolbar_action = QAction(_('Show/hide controls'), self)
self.toggle_toolbar_action.setCheckable(True) self.toggle_toolbar_action.setCheckable(True)
@ -228,6 +231,10 @@ class EbookViewer(MainWindow):
t.timeout.connect(self.hide_cursor) t.timeout.connect(self.hide_cursor)
t.start() t.start()
def process_file_events(self):
if self.file_events:
self.load_ebook(self.file_events[-1])
def eventFilter(self, obj, ev): def eventFilter(self, obj, ev):
if ev.type() == ev.MouseMove: if ev.type() == ev.MouseMove:
if self.cursor_hidden: if self.cursor_hidden:
@ -1153,6 +1160,24 @@ def ensure_single_instance(args, open_at):
listener = create_listener() listener = create_listener()
return listener return listener
class EventAccumulator(QObject):
got_file = pyqtSignal(object)
def __init__(self):
QObject.__init__(self)
self.events = []
def __call__(self, paths):
for path in paths:
if os.path.exists(path):
self.events.append(path)
self.got_file.emit(path)
def flush(self):
if self.events:
self.got_file.emit(self.events[-1])
self.events = []
def main(args=sys.argv): def main(args=sys.argv):
# Ensure viewer can continue to function if GUI is closed # Ensure viewer can continue to function if GUI is closed
@ -1164,7 +1189,9 @@ def main(args=sys.argv):
open_at = float(opts.open_at.replace(',', '.')) if opts.open_at else None open_at = float(opts.open_at.replace(',', '.')) if opts.open_at else None
listener = None listener = None
override = 'calibre-ebook-viewer' if islinux else None override = 'calibre-ebook-viewer' if islinux else None
acc = EventAccumulator()
app = Application(args, override_program_name=override, color_prefs=vprefs) app = Application(args, override_program_name=override, color_prefs=vprefs)
app.file_event_hook = acc
app.load_builtin_fonts() app.load_builtin_fonts()
app.setWindowIcon(QIcon(I('viewer.png'))) app.setWindowIcon(QIcon(I('viewer.png')))
QApplication.setOrganizationName(ORG_NAME) QApplication.setOrganizationName(ORG_NAME)
@ -1180,7 +1207,7 @@ def main(args=sys.argv):
main = EbookViewer(args[1] if len(args) > 1 else None, main = EbookViewer(args[1] if len(args) > 1 else None,
debug_javascript=opts.debug_javascript, open_at=open_at, continue_reading=opts.continue_reading, debug_javascript=opts.debug_javascript, open_at=open_at, continue_reading=opts.continue_reading,
start_in_fullscreen=opts.full_screen, listener=listener) start_in_fullscreen=opts.full_screen, listener=listener, file_events=acc)
app.installEventFilter(main) app.installEventFilter(main)
# This is needed for paged mode. Without it, the first document that is # This is needed for paged mode. Without it, the first document that is
# loaded will have extra blank space at the bottom, as # loaded will have extra blank space at the bottom, as