Split Send to device into two actions

This commit is contained in:
Kovid Goyal 2010-07-19 17:59:52 -06:00
parent f7d5dbaf5f
commit e41fe501dd
7 changed files with 5243 additions and 81 deletions

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 214 KiB

View File

@ -395,8 +395,6 @@ class DeviceAction(QAction): # {{{
class DeviceMenu(QMenu): # {{{ class DeviceMenu(QMenu): # {{{
fetch_annotations = pyqtSignal() fetch_annotations = pyqtSignal()
connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal()
disconnect_mounted_device = pyqtSignal() disconnect_mounted_device = pyqtSignal()
def __init__(self, parent=None): def __init__(self, parent=None):
@ -408,26 +406,6 @@ class DeviceMenu(QMenu): # {{{
self.set_default_menu = QMenu(_('Set default send to device action')) self.set_default_menu = QMenu(_('Set default send to device action'))
self.set_default_menu.setIcon(QIcon(I('config.svg'))) self.set_default_menu.setIcon(QIcon(I('config.svg')))
opts = email_config().parse()
default_account = None
if opts.accounts:
self.email_to_menu = self.addMenu(_('Email to')+'...')
keys = sorted(opts.accounts.keys())
for account in keys:
formats, auto, default = opts.accounts[account]
dest = 'mail:'+account+';'+formats
if default:
default_account = (dest, False, False, I('mail.svg'),
_('Email to')+' '+account)
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))
self.email_to_menu.addSeparator()
action1.a_s.connect(self.action_triggered)
action2.a_s.connect(self.action_triggered)
basic_actions = [ basic_actions = [
('main:', False, False, I('reader.svg'), ('main:', False, False, I('reader.svg'),
@ -457,13 +435,6 @@ class DeviceMenu(QMenu): # {{{
] ]
if default_account is not None:
for x in (basic_actions, delete_actions):
ac = list(default_account)
if x is delete_actions:
ac[1] = True
x.insert(1, tuple(ac))
for menu in (self, self.set_default_menu): for menu in (self, self.set_default_menu):
for actions, desc in ( for actions, desc in (
(basic_actions, ''), (basic_actions, ''),
@ -502,21 +473,7 @@ class DeviceMenu(QMenu): # {{{
config['default_send_to_device_action'] = repr(action) config['default_send_to_device_action'] = repr(action)
self.group.triggered.connect(self.change_default_action) self.group.triggered.connect(self.change_default_action)
if opts.accounts:
self.addSeparator()
self.addMenu(self.email_to_menu)
self.addSeparator() self.addSeparator()
mitem = self.addAction(QIcon(I('document_open.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
mitem = self.addAction(QIcon(I('eject.svg')), _('Eject device')) mitem = self.addAction(QIcon(I('eject.svg')), _('Eject device'))
mitem.setEnabled(False) mitem.setEnabled(False)
@ -638,6 +595,8 @@ class DeviceMixin(object): # {{{
self.device_error_dialog = error_dialog(self, _('Error'), self.device_error_dialog = error_dialog(self, _('Error'),
_('Error communicating with device'), ' ') _('Error communicating with device'), ' ')
self.device_error_dialog.setModal(Qt.NonModal) 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 = Emailer()
self.emailer.start() self.emailer.start()
self.device_manager = DeviceManager(Dispatcher(self.device_detected), self.device_manager = DeviceManager(Dispatcher(self.device_detected),
@ -675,21 +634,20 @@ class DeviceMixin(object): # {{{
def create_device_menu(self): def create_device_menu(self):
self._sync_menu = DeviceMenu(self) self._sync_menu = DeviceMenu(self)
self.share_conn_menu.build_email_entries(self._sync_menu)
self.action_sync.setMenu(self._sync_menu) self.action_sync.setMenu(self._sync_menu)
self.connect(self._sync_menu, self.connect(self._sync_menu,
SIGNAL('sync(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'), SIGNAL('sync(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'),
self.dispatch_sync_event) self.dispatch_sync_event)
self._sync_menu.fetch_annotations.connect(self.fetch_annotations) self._sync_menu.fetch_annotations.connect(self.fetch_annotations)
self._sync_menu.connect_to_folder.connect(self.connect_to_folder)
self._sync_menu.connect_to_itunes.connect(self.connect_to_itunes)
self._sync_menu.disconnect_mounted_device.connect(self.disconnect_mounted_device) self._sync_menu.disconnect_mounted_device.connect(self.disconnect_mounted_device)
if self.device_connected: if self.device_connected:
self._sync_menu.connect_to_folder_action.setEnabled(False) self.share_conn_menu.connect_to_folder_action.setEnabled(False)
self._sync_menu.connect_to_itunes_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.disconnect_mounted_device_action.setEnabled(True)
else: else:
self._sync_menu.connect_to_folder_action.setEnabled(True) self.share_conn_menu.connect_to_folder_action.setEnabled(True)
self._sync_menu.connect_to_itunes_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.disconnect_mounted_device_action.setEnabled(False)
def device_job_exception(self, job): def device_job_exception(self, job):
@ -726,16 +684,16 @@ class DeviceMixin(object): # {{{
def set_device_menu_items_state(self, connected): def set_device_menu_items_state(self, connected):
if connected: if connected:
self._sync_menu.connect_to_folder_action.setEnabled(False) self.share_conn_menu.connect_to_folder_action.setEnabled(False)
self._sync_menu.connect_to_itunes_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.disconnect_mounted_device_action.setEnabled(True)
self._sync_menu.enable_device_actions(True, self._sync_menu.enable_device_actions(True,
self.device_manager.device.card_prefix(), self.device_manager.device.card_prefix(),
self.device_manager.device) self.device_manager.device)
self.eject_action.setEnabled(True) self.eject_action.setEnabled(True)
else: else:
self._sync_menu.connect_to_folder_action.setEnabled(True) self.share_conn_menu.connect_to_folder_action.setEnabled(True)
self._sync_menu.connect_to_itunes_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.disconnect_mounted_device_action.setEnabled(False)
self._sync_menu.enable_device_actions(False) self._sync_menu.enable_device_actions(False)
self.eject_action.setEnabled(False) self.eject_action.setEnabled(False)
@ -983,6 +941,8 @@ class DeviceMixin(object): # {{{
else: else:
self.status_bar.show_message(_('Sent by email:') + ', '.join(good), self.status_bar.show_message(_('Sent by email:') + ', '.join(good),
5000) 5000)
if remove:
self.library_view.model().delete_books_by_id(remove)
def cover_to_thumbnail(self, data): def cover_to_thumbnail(self, data):
p = QPixmap() p = QPixmap()

View File

@ -195,22 +195,32 @@ class PluginModel(QAbstractItemModel):
class CategoryModel(QStringListModel): class CategoryModel(QStringListModel):
CATEGORIES = [
('general', _('General'), 'dialog_information.svg'),
('interface', _('Interface'), 'lookfeel.svg'),
('conversion', _('Conversion'), 'convert.svg'),
('email', _('Email\nDelivery'), 'mail.svg'),
('add/save', _('Add/Save'), 'save.svg'),
('advanced', _('Advanced'), 'view.svg'),
('server', _('Content\nServer'), 'network-server.svg'),
('plugins', _('Plugins'), 'plugins.svg'),
]
def __init__(self, *args): def __init__(self, *args):
QStringListModel.__init__(self, *args) QStringListModel.__init__(self, *args)
self.setStringList([_('General'), _('Interface'), _('Conversion'), self.setStringList([x[1] for x in self.CATEGORIES])
_('Email\nDelivery'), _('Add/Save'),
_('Advanced'), _('Content\nServer'), _('Plugins')])
self.icons = list(map(QVariant, map(QIcon,
[I('dialog_information.svg'), I('lookfeel.svg'),
I('convert.svg'),
I('mail.svg'), I('save.svg'), I('view.svg'),
I('network-server.svg'), I('plugins.svg')])))
def data(self, index, role): def data(self, index, role):
if role == Qt.DecorationRole: if role == Qt.DecorationRole:
return self.icons[index.row()] return QVariant(QIcon(I(self.CATEGORIES[index.row()][2])))
return QStringListModel.data(self, index, role) return QStringListModel.data(self, index, role)
def index_for_name(self, name):
for i, x in enumerate(self.CATEGORIES):
if x[0] == name:
return self.index(i)
return self.index(0)
class EmailAccounts(QAbstractTableModel): class EmailAccounts(QAbstractTableModel):
def __init__(self, accounts): def __init__(self, accounts):
@ -332,7 +342,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
def category_current_changed(self, n, p): def category_current_changed(self, n, p):
self.stackedWidget.setCurrentIndex(n.row()) self.stackedWidget.setCurrentIndex(n.row())
def __init__(self, parent, library_view, server=None): def __init__(self, parent, library_view, server=None,
initial_category='general'):
ResizableDialog.__init__(self, parent) ResizableDialog.__init__(self, parent)
self._category_model = CategoryModel() self._category_model = CategoryModel()
@ -461,7 +472,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.button_osx_symlinks.setVisible(isosx) self.button_osx_symlinks.setVisible(isosx)
self.separate_cover_flow.setChecked(config['separate_cover_flow']) self.separate_cover_flow.setChecked(config['separate_cover_flow'])
self.setup_email_page() self.setup_email_page()
self.category_view.setCurrentIndex(self.category_view.model().index(0))
self.delete_news.setEnabled(bool(self.sync_news.isChecked())) self.delete_news.setEnabled(bool(self.sync_news.isChecked()))
self.connect(self.sync_news, SIGNAL('toggled(bool)'), self.connect(self.sync_news, SIGNAL('toggled(bool)'),
self.delete_news.setEnabled) self.delete_news.setEnabled)
@ -489,6 +499,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.opt_disable_animations.setChecked(config['disable_animations']) self.opt_disable_animations.setChecked(config['disable_animations'])
self.opt_show_donate_button.setChecked(config['show_donate_button']) self.opt_show_donate_button.setChecked(config['show_donate_button'])
self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category))
def check_port_value(self, *args): def check_port_value(self, *args):
port = self.port.value() port = self.port.value()
if port < 1025: if port < 1025:
@ -942,6 +954,5 @@ if __name__ == '__main__':
from PyQt4.Qt import QApplication from PyQt4.Qt import QApplication
app = QApplication([]) app = QApplication([])
d=ConfigDialog(None, LibraryDatabase2('/tmp')) d=ConfigDialog(None, LibraryDatabase2('/tmp'))
d.category_view.setCurrentIndex(d.category_view.model().index(0))
d.show() d.show()
app.exec_() app.exec_()

View File

@ -59,6 +59,7 @@ class LibraryViewMixin(object): # {{{
self.action_open_containing_folder, self.action_open_containing_folder,
self.action_show_book_details, self.action_show_book_details,
self.action_del, self.action_del,
self.action_conn_share,
add_to_library = None, add_to_library = None,
edit_device_collections=None, edit_device_collections=None,
similar_menu=similar_menu) similar_menu=similar_menu)
@ -67,21 +68,24 @@ class LibraryViewMixin(object): # {{{
edit_device_collections = (_('Manage collections'), edit_device_collections = (_('Manage collections'),
partial(self.edit_device_collections, oncard=None)) partial(self.edit_device_collections, oncard=None))
self.memory_view.set_context_menu(None, None, None, self.memory_view.set_context_menu(None, None, None,
self.action_view, self.action_save, None, None, self.action_del, self.action_view, self.action_save, None, None,
self.action_del, None,
add_to_library=add_to_library, add_to_library=add_to_library,
edit_device_collections=edit_device_collections) edit_device_collections=edit_device_collections)
edit_device_collections = (_('Manage collections'), edit_device_collections = (_('Manage collections'),
partial(self.edit_device_collections, oncard='carda')) partial(self.edit_device_collections, oncard='carda'))
self.card_a_view.set_context_menu(None, None, None, self.card_a_view.set_context_menu(None, None, None,
self.action_view, self.action_save, None, None, self.action_del, self.action_view, self.action_save, None, None,
self.action_del, None,
add_to_library=add_to_library, add_to_library=add_to_library,
edit_device_collections=edit_device_collections) edit_device_collections=edit_device_collections)
edit_device_collections = (_('Manage collections'), edit_device_collections = (_('Manage collections'),
partial(self.edit_device_collections, oncard='cardb')) partial(self.edit_device_collections, oncard='cardb'))
self.card_b_view.set_context_menu(None, None, None, self.card_b_view.set_context_menu(None, None, None,
self.action_view, self.action_save, None, None, self.action_del, self.action_view, self.action_save, None, None,
self.action_del, None,
add_to_library=add_to_library, add_to_library=add_to_library,
edit_device_collections=edit_device_collections) edit_device_collections=edit_device_collections)

View File

@ -22,6 +22,7 @@ from calibre import human_readable
from calibre.utils.config import prefs from calibre.utils.config import prefs
from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS
from calibre.gui2.dialogs.scheduler import Scheduler from calibre.gui2.dialogs.scheduler import Scheduler
from calibre.utils.smtp import config as email_config
ICON_SIZE = 48 ICON_SIZE = 48
@ -262,7 +263,9 @@ class ToolBar(QToolBar): # {{{
ch.setCursor(Qt.PointingHandCursor) ch.setCursor(Qt.PointingHandCursor)
ch.setAutoRaise(True) ch.setAutoRaise(True)
if ac.menu() is not None: if ac.menu() is not None:
ch.setPopupMode(ch.MenuButtonPopup) name = getattr(ac, 'action_name', None)
ch.setPopupMode(ch.InstantPopup if name == 'conn_share'
else ch.MenuButtonPopup)
for x in actions: for x in actions:
self.addAction(x) self.addAction(x)
@ -306,6 +309,60 @@ class ToolBar(QToolBar): # {{{
class Action(QAction): class Action(QAction):
pass pass
class ShareConnMenu(QMenu):
connect_to_folder = pyqtSignal()
connect_to_itunes = pyqtSignal()
config_email = 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.email_actions = []
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 = []
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))
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): class MainWindowMixin(object):
def __init__(self, db): def __init__(self, db):
@ -341,7 +398,6 @@ class MainWindowMixin(object):
self.scheduler.start_recipe_fetch.connect( self.scheduler.start_recipe_fetch.connect(
self.download_scheduled_recipe, type=Qt.QueuedConnection) self.download_scheduled_recipe, type=Qt.QueuedConnection)
def read_toolbar_settings(self): def read_toolbar_settings(self):
pass pass
@ -372,18 +428,19 @@ class MainWindowMixin(object):
setattr(self, 'action_'+name, action) setattr(self, 'action_'+name, action)
all_actions.append(action) all_actions.append(action)
ac(0, 7, 0, 'add', _('Add books'), 'add_book.svg', _('A')) ac(0, 0, 0, 'add', _('Add books'), 'add_book.svg', _('A'))
ac(1, 1, 0, 'edit', _('Edit metadata'), 'edit_input.svg', _('E')) ac(1, 1, 0, 'edit', _('Edit metadata'), 'edit_input.svg', _('E'))
ac(2, 2, 3, 'convert', _('Convert books'), 'convert.svg', _('C')) ac(2, 2, 3, 'convert', _('Convert books'), 'convert.svg', _('C'))
ac(3, 3, 0, 'view', _('View'), 'view.svg', _('V')) ac(3, 3, 0, 'view', _('View'), 'view.svg', _('V'))
ac(4, 4, 3, 'choose_library', _('%d books')%0, 'lt.png', 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')) tooltip=_('Choose calibre library to work with'))
ac(5, 5, 3, 'news', _('Fetch news'), 'news.svg', _('F')) ac(6, 6, 3, 'news', _('Fetch news'), 'news.svg', _('F'))
ac(6, 6, 0, 'save', _('Save to disk'), 'save.svg', _('S')) ac(7, 7, 0, 'save', _('Save to disk'), 'save.svg', _('S'))
ac(7, 0, 0, 'sync', _('Send to device'), 'sync.svg') ac(8, 8, 0, 'conn_share', _('Connect/share'), 'connect_share.svg')
ac(8, 8, 3, 'del', _('Remove books'), 'trash.svg', _('Del')) ac(9, 9, 3, 'del', _('Remove books'), 'trash.svg', _('Del'))
ac(9, 9, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual")) ac(10, 10, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual"))
ac(10, 10, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P')) ac(11, 11, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P'))
ac(-1, -1, 0, 'merge', _('Merge book records'), 'merge_books.svg', _('M')) ac(-1, -1, 0, 'merge', _('Merge book records'), 'merge_books.svg', _('M'))
ac(-1, -1, 0, 'open_containing_folder', _('Open containing folder'), ac(-1, -1, 0, 'open_containing_folder', _('Open containing folder'),
@ -402,6 +459,10 @@ class MainWindowMixin(object):
self.action_news.setMenu(self.scheduler.news_menu) self.action_news.setMenu(self.scheduler.news_menu)
self.action_news.triggered.connect( self.action_news.triggered.connect(
self.scheduler.show_dialog) self.scheduler.show_dialog)
self.share_conn_menu = ShareConnMenu(self)
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_help.triggered.connect(self.show_help)
md = QMenu() md = QMenu()
@ -528,6 +589,7 @@ class MainWindowMixin(object):
for x in (self.preferences_action, self.action_preferences): for x in (self.preferences_action, self.action_preferences):
x.triggered.connect(self.do_config) x.triggered.connect(self.do_config)
return all_actions return all_actions
# }}} # }}}

View File

@ -390,7 +390,7 @@ class BooksView(QTableView): # {{{
# Context Menu {{{ # Context Menu {{{
def set_context_menu(self, edit_metadata, send_to_device, convert, view, def set_context_menu(self, edit_metadata, send_to_device, convert, view,
save, open_folder, book_details, delete, save, open_folder, book_details, delete, conn_share,
similar_menu=None, add_to_library=None, similar_menu=None, add_to_library=None,
edit_device_collections=None): edit_device_collections=None):
self.setContextMenuPolicy(Qt.DefaultContextMenu) self.setContextMenuPolicy(Qt.DefaultContextMenu)
@ -401,6 +401,8 @@ class BooksView(QTableView): # {{{
self.context_menu.addAction(send_to_device) self.context_menu.addAction(send_to_device)
if convert is not None: if convert is not None:
self.context_menu.addAction(convert) self.context_menu.addAction(convert)
if conn_share is not None:
self.context_menu.addAction(conn_share)
self.context_menu.addAction(view) self.context_menu.addAction(view)
self.context_menu.addAction(save) self.context_menu.addAction(save)
if open_folder is not None: if open_folder is not None:

View File

@ -351,7 +351,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
return self.memory_view.model().db, self.card_a_view.model().db, self.card_b_view.model().db return self.memory_view.model().db, self.card_a_view.model().db, self.card_b_view.model().db
def do_config(self, *args): def do_config(self, checked=False, initial_category='general'):
if self.job_manager.has_jobs(): if self.job_manager.has_jobs():
d = error_dialog(self, _('Cannot configure'), d = error_dialog(self, _('Cannot configure'),
_('Cannot configure while there are running jobs.')) _('Cannot configure while there are running jobs.'))
@ -363,7 +363,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
d.exec_() d.exec_()
return return
d = ConfigDialog(self, self.library_view, d = ConfigDialog(self, self.library_view,
server=self.content_server) server=self.content_server, initial_category=initial_category)
d.exec_() d.exec_()
self.content_server = d.server self.content_server = d.server