Migrate device actions and other minor actions

This commit is contained in:
Kovid Goyal 2010-08-12 22:34:40 -06:00
parent 886db1b6a8
commit 72c2d636b0
10 changed files with 255 additions and 143 deletions

View File

@ -613,6 +613,29 @@ class ActionSaveToDisk(InterfaceActionBase):
name = 'Save To Disk'
actual_plugin = 'calibre.gui2.actions.save_to_disk:SaveToDiskAction'
class ActionShowBookDetails(InterfaceActionBase):
name = 'Show Book Details'
actual_plugin = 'calibre.gui2.actions.show_book_details:ShowBookDetailsAction'
class ActionRestart(InterfaceActionBase):
name = 'Restart'
actual_plugin = 'calibre.gui2.actions.restart:RestartAction'
class ActionOpenFolder(InterfaceActionBase):
name = 'OpenFolder'
actual_plugin = 'calibre.gui2.actions.open:OpenFolderAction'
class ActionSendToDevice(InterfaceActionBase):
name = 'Send To Device'
actual_plugin = 'calibre.gui2.actions.device:SendToDeviceAction'
class ActionConnectShare(InterfaceActionBase):
name = 'Connect Share'
actual_plugin = 'calibre.gui2.actions.device:ConnectShareAction'
plugins += [ActionAdd, ActionFetchAnnotations, ActionGenerateCatalog,
ActionConvert, ActionDelete, ActionEditMetadata, ActionView,
ActionFetchNews, ActionSaveToDisk]
ActionFetchNews, ActionSaveToDisk, ActionShowBookDetails,
ActionRestart, ActionOpenFolder, ActionConnectShare,
ActionSendToDevice, ]

View File

@ -32,6 +32,7 @@ class InterfaceAction(QObject):
def do_genesis(self):
self.Dispatcher = partial(Dispatcher, parent=self)
self.create_action()
self.gui.addAction(self.qaction)
self.genesis()
def create_action(self, spec=None, attr='qaction'):

View File

@ -0,0 +1,143 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from functools import partial
from PyQt4.Qt import QToolButton, QMenu, pyqtSignal, QIcon
from calibre.gui2.actions import InterfaceAction
from calibre.utils.smtp import config as email_config
class ShareConnMenu(QMenu): # {{{
connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal()
config_email = pyqtSignal()
toggle_server = pyqtSignal()
def __init__(self, parent=None):
QMenu.__init__(self, parent)
mitem = self.addAction(QIcon(I('devices/folder.svg')), _('Connect to folder'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_folder.emit())
self.connect_to_folder_action = mitem
mitem = self.addAction(QIcon(I('devices/itunes.png')),
_('Connect to iTunes'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
self.connect_to_itunes_action = mitem
self.addSeparator()
self.toggle_server_action = \
self.addAction(QIcon(I('network-server.svg')),
_('Start Content Server'))
self.toggle_server_action.triggered.connect(lambda x:
self.toggle_server.emit())
self.addSeparator()
self.email_actions = []
def server_state_changed(self, running):
text = _('Start Content Server')
if running:
text = _('Stop Content Server')
self.toggle_server_action.setText(text)
def build_email_entries(self, sync_menu):
from calibre.gui2.device import DeviceAction
for ac in self.email_actions:
self.removeAction(ac)
self.email_actions = []
self.memory = []
opts = email_config().parse()
if opts.accounts:
self.email_to_menu = QMenu(_('Email to')+'...', self)
keys = sorted(opts.accounts.keys())
for account in keys:
formats, auto, default = opts.accounts[account]
dest = 'mail:'+account+';'+formats
action1 = DeviceAction(dest, False, False, I('mail.svg'),
_('Email to')+' '+account)
action2 = DeviceAction(dest, True, False, I('mail.svg'),
_('Email to')+' '+account+ _(' and delete from library'))
map(self.email_to_menu.addAction, (action1, action2))
map(self.memory.append, (action1, action2))
if default:
map(self.addAction, (action1, action2))
map(self.email_actions.append, (action1, action2))
self.email_to_menu.addSeparator()
action1.a_s.connect(sync_menu.action_triggered)
action2.a_s.connect(sync_menu.action_triggered)
ac = self.addMenu(self.email_to_menu)
self.email_actions.append(ac)
else:
ac = self.addAction(_('Setup email based sharing of books'))
self.email_actions.append(ac)
ac.triggered.connect(self.setup_email)
def setup_email(self, *args):
self.config_email.emit()
def set_state(self, device_connected):
self.connect_to_folder_action.setEnabled(not device_connected)
self.connect_to_itunes_action.setEnabled(not device_connected)
# }}}
class SendToDeviceAction(InterfaceAction):
name = 'Send To Device'
action_spec = (_('Send to device'), 'sync.svg', None, _('D'))
def genesis(self):
self.qaction.triggered.connect(self.do_sync)
self.gui.create_device_menu()
def location_selected(self, loc):
enabled = loc == 'library'
self.qaction.setEnabled(enabled)
def do_sync(self, *args):
self.gui._sync_action_triggered()
class ConnectShareAction(InterfaceAction):
name = 'Connect Share'
action_spec = (_('Connect/share'), 'connect_share.svg', None, None)
popup_type = QToolButton.InstantPopup
def genesis(self):
self.share_conn_menu = ShareConnMenu(self.gui)
self.share_conn_menu.toggle_server.connect(self.toggle_content_server)
self.share_conn_menu.config_email.connect(partial(
self.gui.iactions['Preferences'].do_config,
initial_category='email'))
self.qaction.setMenu(self.share_conn_menu)
self.share_conn_menu.connect_to_folder.connect(self.gui.connect_to_folder)
self.share_conn_menu.connect_to_itunes.connect(self.gui.connect_to_itunes)
def location_selected(self, loc):
enabled = loc == 'library'
self.qaction.setEnabled(enabled)
def set_state(self, device_connected):
self.share_conn_menu.set_state(device_connected)
def build_email_entries(self):
m = self.gui.iactions['Send To Device'].qaction.menu()
self.share_conn_menu.build_email_entries(m)
def content_server_state_changed(self, running):
self.share_conn_menu.server_state_changed(running)
def toggle_content_server(self):
if self.gui.content_server is None:
self.gui.start_content_server()
else:
self.gui.content_server.exit()
self.gui.content_server = None

View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.actions import InterfaceAction
class OpenFolderAction(InterfaceAction):
name = 'Open Folder'
action_spec = (_('Open containing folder'), 'document_open.svg', None,
_('O'))
def genesis(self):
self.action_open_containing_folder.triggered.connect(self.iactions['View'].view_folder)
def location_selected(self, loc):
enabled = loc == 'library'
self.qaction.setEnabled(enabled)

View File

@ -0,0 +1,22 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.actions import InterfaceAction
class RestartAction(InterfaceAction):
name = 'Restart'
action_spec = (_('&Restart'), None, None, _('Ctrl+R'))
def genesis(self):
self.qaction.triggered.connect(self.restart)
def restart(self, *args):
self.gui.quit(restart=True)

View File

@ -0,0 +1,31 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from calibre.gui2.actions import InterfaceAction
from calibre.gui2.dialogs.book_info import BookInfo
from calibre.gui2 import error_dialog
class ShowBookDetailsAction(InterfaceAction):
name = 'Show Book Details'
action_spec = (_('Show book details'), 'dialog_information.svg', None,
_('I'))
def genesis(self):
self.qaction.triggered.connect(self.show_book_info)
def show_book_info(self, *args):
if self.gui.current_view() is not self.gui.library_view:
error_dialog(self.gui, _('No detailed info available'),
_('No detailed information is available for books '
'on the device.')).exec_()
return
index = self.gui.library_view.currentIndex()
if index.isValid():
BookInfo(self.gui, self.gui.library_view, index).show()

View File

@ -608,8 +608,6 @@ class DeviceMixin(object): # {{{
self.device_error_dialog = error_dialog(self, _('Error'),
_('Error communicating with device'), ' ')
self.device_error_dialog.setModal(Qt.NonModal)
self.share_conn_menu.connect_to_folder.connect(self.connect_to_folder)
self.share_conn_menu.connect_to_itunes.connect(self.connect_to_itunes)
self.emailer = Emailer()
self.emailer.start()
self.device_manager = DeviceManager(Dispatcher(self.device_detected),
@ -647,21 +645,18 @@ class DeviceMixin(object): # {{{
def create_device_menu(self):
self._sync_menu = DeviceMenu(self)
self.share_conn_menu.build_email_entries(self._sync_menu)
self.action_sync.setMenu(self._sync_menu)
self.iactions['Send To Device'].qaction.setMenu(self._sync_menu)
self.iactions['Connect Share'].build_email_entries()
self.connect(self._sync_menu,
SIGNAL('sync(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
self.dispatch_sync_event)
self._sync_menu.fetch_annotations.connect(
self.iactions['Fetch Annotations'].fetch_annotations)
self._sync_menu.disconnect_mounted_device.connect(self.disconnect_mounted_device)
self.iactions['Connect Share'].set_state(self.device_connected)
if self.device_connected:
self.share_conn_menu.connect_to_folder_action.setEnabled(False)
self.share_conn_menu.connect_to_itunes_action.setEnabled(False)
self._sync_menu.disconnect_mounted_device_action.setEnabled(True)
else:
self.share_conn_menu.connect_to_folder_action.setEnabled(True)
self.share_conn_menu.connect_to_itunes_action.setEnabled(True)
self._sync_menu.disconnect_mounted_device_action.setEnabled(False)
def device_job_exception(self, job):
@ -697,17 +692,14 @@ class DeviceMixin(object): # {{{
# Device connected {{{
def set_device_menu_items_state(self, connected):
self.iactions['Connect Share'].set_state(connected)
if connected:
self.share_conn_menu.connect_to_folder_action.setEnabled(False)
self.share_conn_menu.connect_to_itunes_action.setEnabled(False)
self._sync_menu.disconnect_mounted_device_action.setEnabled(True)
self._sync_menu.enable_device_actions(True,
self.device_manager.device.card_prefix(),
self.device_manager.device)
self.eject_action.setEnabled(True)
else:
self.share_conn_menu.connect_to_folder_action.setEnabled(True)
self.share_conn_menu.connect_to_itunes_action.setEnabled(True)
self._sync_menu.disconnect_mounted_device_action.setEnabled(False)
self._sync_menu.enable_device_actions(False)
self.eject_action.setEnabled(False)

View File

@ -306,7 +306,7 @@ class LayoutMixin(object): # {{{
def finalize_layout(self):
self.status_bar.initialize(self.system_tray_icon)
self.book_details.show_book_info.connect(self.show_book_info)
self.book_details.show_book_info.connect(self.iactions['Show Book Details'].show_book_info)
self.book_details.files_dropped.connect(self.iactions['Add Books'].files_dropped_on_book)
self.book_details.open_containing_folder.connect(self.iactions['View'].view_folder_for_id)
self.book_details.view_specific_format.connect(self.iactions['View'].view_format_by_id)

View File

@ -20,7 +20,6 @@ from calibre.gui2 import config, open_url, gprefs
from calibre.gui2.widgets import ComboBoxWithHelp
from calibre import human_readable
from calibre.gui2.dialogs.scheduler import Scheduler
from calibre.utils.smtp import config as email_config
@ -306,77 +305,6 @@ class ToolBar(QToolBar): # {{{
class Action(QAction):
pass
class ShareConnMenu(QMenu): # {{{
connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal()
config_email = pyqtSignal()
toggle_server = pyqtSignal()
def __init__(self, parent=None):
QMenu.__init__(self, parent)
mitem = self.addAction(QIcon(I('devices/folder.svg')), _('Connect to folder'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_folder.emit())
self.connect_to_folder_action = mitem
mitem = self.addAction(QIcon(I('devices/itunes.png')),
_('Connect to iTunes'))
mitem.setEnabled(True)
mitem.triggered.connect(lambda x : self.connect_to_itunes.emit())
self.connect_to_itunes_action = mitem
self.addSeparator()
self.toggle_server_action = \
self.addAction(QIcon(I('network-server.svg')),
_('Start Content Server'))
self.toggle_server_action.triggered.connect(lambda x:
self.toggle_server.emit())
self.addSeparator()
self.email_actions = []
def server_state_changed(self, running):
text = _('Start Content Server')
if running:
text = _('Stop Content Server')
self.toggle_server_action.setText(text)
def build_email_entries(self, sync_menu):
from calibre.gui2.device import DeviceAction
for ac in self.email_actions:
self.removeAction(ac)
self.email_actions = []
self.memory = []
opts = email_config().parse()
if opts.accounts:
self.email_to_menu = QMenu(_('Email to')+'...', self)
keys = sorted(opts.accounts.keys())
for account in keys:
formats, auto, default = opts.accounts[account]
dest = 'mail:'+account+';'+formats
action1 = DeviceAction(dest, False, False, I('mail.svg'),
_('Email to')+' '+account)
action2 = DeviceAction(dest, True, False, I('mail.svg'),
_('Email to')+' '+account+ _(' and delete from library'))
map(self.email_to_menu.addAction, (action1, action2))
map(self.memory.append, (action1, action2))
if default:
map(self.addAction, (action1, action2))
map(self.email_actions.append, (action1, action2))
self.email_to_menu.addSeparator()
action1.a_s.connect(sync_menu.action_triggered)
action2.a_s.connect(sync_menu.action_triggered)
ac = self.addMenu(self.email_to_menu)
self.email_actions.append(ac)
else:
ac = self.addAction(_('Setup email based sharing of books'))
self.email_actions.append(ac)
ac.triggered.connect(self.setup_email)
def setup_email(self, *args):
self.config_email.emit()
# }}}
class MainWindowMixin(object):
def __init__(self, db):
@ -442,17 +370,11 @@ class MainWindowMixin(object):
setattr(self, 'action_'+name, action)
all_actions.append(action)
ac(-1, 4, 0, 'sync', _('Send to device'), 'sync.svg')
ac(5, 5, 3, 'choose_library', _('%d books')%0, 'lt.png',
tooltip=_('Choose calibre library to work with'))
ac(8, 8, 0, 'conn_share', _('Connect/share'), 'connect_share.svg')
ac(10, 10, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual"))
ac(11, 11, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P'))
ac(-1, -1, 0, 'open_containing_folder', _('Open containing folder'),
'document_open.svg')
ac(-1, -1, 0, 'show_book_details', _('Show book details'),
'dialog_information.svg')
ac(-1, -1, 0, 'books_by_same_author', _('Books by same author'),
'user_profile.svg')
ac(-1, -1, 0, 'books_in_this_series', _('Books in this series'),
@ -462,24 +384,10 @@ class MainWindowMixin(object):
ac(-1, -1, 0, 'books_with_the_same_tags', _('Books with the same tags'),
'tags.svg')
self.share_conn_menu = ShareConnMenu(self)
self.share_conn_menu.toggle_server.connect(self.toggle_content_server)
self.share_conn_menu.config_email.connect(partial(self.do_config,
initial_category='email'))
self.action_conn_share.setMenu(self.share_conn_menu)
self.action_help.triggered.connect(self.show_help)
self.action_open_containing_folder.setShortcut(Qt.Key_O)
self.addAction(self.action_open_containing_folder)
self.action_open_containing_folder.triggered.connect(self.iactions['View'].view_folder)
self.action_sync.setShortcut(Qt.Key_D)
self.action_sync.setEnabled(True)
self.create_device_menu()
self.action_sync.triggered.connect(
self._sync_action_triggered)
pm = QMenu()
@ -498,12 +406,5 @@ class MainWindowMixin(object):
def show_help(self, *args):
open_url(QUrl('http://calibre-ebook.com/user_manual'))
def content_server_state_changed(self, running):
self.share_conn_menu.server_state_changed(running)
def toggle_content_server(self):
if self.content_server is None:
self.start_content_server()
else:
self.content_server.exit()
self.content_server = None

View File

@ -15,7 +15,7 @@ from threading import Thread
from PyQt4.Qt import Qt, SIGNAL, QTimer, \
QPixmap, QMenu, QIcon, pyqtSignal, \
QDialog, \
QSystemTrayIcon, QApplication, QKeySequence, QAction, \
QSystemTrayIcon, QApplication, QKeySequence, \
QMessageBox, QHelpEvent
from calibre import prints
@ -35,7 +35,6 @@ from calibre.gui2.layout import MainWindowMixin
from calibre.gui2.device import DeviceMixin
from calibre.gui2.jobs import JobManager, JobsDialog, JobsButton
from calibre.gui2.dialogs.config import ConfigDialog
from calibre.gui2.dialogs.book_info import BookInfo
from calibre.gui2.init import LibraryViewMixin, LayoutMixin
from calibre.gui2.search_box import SearchBoxMixin, SavedSearchBoxMixin
from calibre.gui2.search_restriction_mixin import SearchRestrictionMixin
@ -172,22 +171,13 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
QIcon(I('eject.svg')), _('&Eject connected device'))
self.eject_action.setEnabled(False)
self.addAction(self.quit_action)
self.action_restart = QAction(_('&Restart'), self)
self.addAction(self.action_restart)
self.system_tray_menu.addAction(self.quit_action)
self.quit_action.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_Q))
self.action_restart.setShortcut(QKeySequence(Qt.CTRL + Qt.Key_R))
self.action_show_book_details.setShortcut(QKeySequence(Qt.Key_I))
self.addAction(self.action_show_book_details)
self.system_tray_icon.setContextMenu(self.system_tray_menu)
self.connect(self.quit_action, SIGNAL('triggered(bool)'), self.quit)
self.connect(self.donate_action, SIGNAL('triggered(bool)'), self.donate)
self.connect(self.restore_action, SIGNAL('triggered()'),
self.show_windows)
self.connect(self.action_show_book_details,
SIGNAL('triggered(bool)'), self.show_book_info)
self.connect(self.action_restart, SIGNAL('triggered()'),
self.restart)
self.connect(self.system_tray_icon,
SIGNAL('activated(QSystemTrayIcon::ActivationReason)'),
self.system_tray_icon_activated)
@ -270,7 +260,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
from calibre.library.server import server_config
self.content_server = start_threaded_server(
self.library_view.model().db, server_config().parse())
self.content_server.state_callback = Dispatcher(self.content_server_state_changed)
self.content_server.state_callback = Dispatcher(
self.iactions['Connect Share'].content_server_state_changed)
self.content_server.state_callback(True)
self.test_server_timer = QTimer.singleShot(10000, self.test_server)
@ -381,7 +372,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
self.content_server = d.server
if self.content_server is not None:
self.content_server.state_callback = \
Dispatcher(self.content_server_state_changed)
Dispatcher(self.iactions['Connect Share'].content_server_state_changed)
self.content_server.state_callback(self.content_server.is_running)
if d.result() == d.Accepted:
@ -411,15 +402,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
self.scheduler.database_changed(db)
prefs['library_path'] = self.library_path
def show_book_info(self, *args):
if self.current_view() is not self.library_view:
error_dialog(self, _('No detailed info available'),
_('No detailed information is available for books '
'on the device.')).exec_()
return
index = self.library_view.currentIndex()
if index.isValid():
BookInfo(self, self.library_view, index).show()
def location_selected(self, location):
'''
@ -434,12 +416,8 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
for action in self.iactions.values():
action.location_selected(location)
if location == 'library':
self.action_open_containing_folder.setEnabled(True)
self.action_sync.setEnabled(True)
self.search_restriction.setEnabled(True)
else:
self.action_open_containing_folder.setEnabled(False)
self.action_sync.setEnabled(False)
self.search_restriction.setEnabled(False)
# Reset the view in case something changed while it was invisible
self.current_view().reset()
@ -503,9 +481,6 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
dynamic.set('sort_history', self.library_view.model().sort_history)
self.save_layout_state()
def restart(self):
self.quit(restart=True)
def quit(self, checked=True, restart=False):
if not self.confirm_quit():
return