mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
OSX: Allow drag and drop of file onto viewer dock icon to view file
This commit is contained in:
parent
1b7c86dbaf
commit
55c74f69d7
@ -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,6 +614,7 @@ 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)
|
||||||
|
if remove_doc_types:
|
||||||
plist.pop('CFBundleDocumentTypes')
|
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,
|
||||||
@ -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):
|
||||||
|
@ -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
|
||||||
|
Loading…
x
Reference in New Issue
Block a user