diff --git a/resources/images/connect_share.svg b/resources/images/connect_share.svg new file mode 100644 index 0000000000..ab582ddc57 --- /dev/null +++ b/resources/images/connect_share.svg @@ -0,0 +1,5123 @@ + + +image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/calibre/gui2/device.py b/src/calibre/gui2/device.py index c919547956..cdbe31de65 100644 --- a/src/calibre/gui2/device.py +++ b/src/calibre/gui2/device.py @@ -395,8 +395,6 @@ class DeviceAction(QAction): # {{{ class DeviceMenu(QMenu): # {{{ fetch_annotations = pyqtSignal() - connect_to_folder = pyqtSignal() - connect_to_itunes = pyqtSignal() disconnect_mounted_device = pyqtSignal() 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.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 = [ ('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 actions, desc in ( (basic_actions, ''), @@ -502,21 +473,7 @@ class DeviceMenu(QMenu): # {{{ config['default_send_to_device_action'] = repr(action) self.group.triggered.connect(self.change_default_action) - if opts.accounts: - self.addSeparator() - self.addMenu(self.email_to_menu) - 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.setEnabled(False) @@ -638,6 +595,8 @@ 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), @@ -675,21 +634,20 @@ 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.connect(self._sync_menu, SIGNAL('sync(PyQt_PyObject, PyQt_PyObject, PyQt_PyObject)'), self.dispatch_sync_event) 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) if self.device_connected: - self._sync_menu.connect_to_folder_action.setEnabled(False) - self._sync_menu.connect_to_itunes_action.setEnabled(False) + 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._sync_menu.connect_to_folder_action.setEnabled(True) - self._sync_menu.connect_to_itunes_action.setEnabled(True) + 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): @@ -726,16 +684,16 @@ class DeviceMixin(object): # {{{ def set_device_menu_items_state(self, connected): if connected: - self._sync_menu.connect_to_folder_action.setEnabled(False) - self._sync_menu.connect_to_itunes_action.setEnabled(False) + 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._sync_menu.connect_to_folder_action.setEnabled(True) - self._sync_menu.connect_to_itunes_action.setEnabled(True) + 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) @@ -983,6 +941,8 @@ class DeviceMixin(object): # {{{ else: self.status_bar.show_message(_('Sent by email:') + ', '.join(good), 5000) + if remove: + self.library_view.model().delete_books_by_id(remove) def cover_to_thumbnail(self, data): p = QPixmap() diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py index 84d55c8fb6..1deb025c73 100644 --- a/src/calibre/gui2/dialogs/config/__init__.py +++ b/src/calibre/gui2/dialogs/config/__init__.py @@ -195,22 +195,32 @@ class PluginModel(QAbstractItemModel): 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): QStringListModel.__init__(self, *args) - self.setStringList([_('General'), _('Interface'), _('Conversion'), - _('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')]))) + self.setStringList([x[1] for x in self.CATEGORIES]) def data(self, index, role): if role == Qt.DecorationRole: - return self.icons[index.row()] + return QVariant(QIcon(I(self.CATEGORIES[index.row()][2]))) 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): def __init__(self, accounts): @@ -332,7 +342,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): def category_current_changed(self, n, p): 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) self._category_model = CategoryModel() @@ -461,7 +472,6 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.button_osx_symlinks.setVisible(isosx) self.separate_cover_flow.setChecked(config['separate_cover_flow']) self.setup_email_page() - self.category_view.setCurrentIndex(self.category_view.model().index(0)) self.delete_news.setEnabled(bool(self.sync_news.isChecked())) self.connect(self.sync_news, SIGNAL('toggled(bool)'), self.delete_news.setEnabled) @@ -489,6 +499,8 @@ class ConfigDialog(ResizableDialog, Ui_Dialog): self.opt_disable_animations.setChecked(config['disable_animations']) 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): port = self.port.value() if port < 1025: @@ -942,6 +954,5 @@ if __name__ == '__main__': from PyQt4.Qt import QApplication app = QApplication([]) d=ConfigDialog(None, LibraryDatabase2('/tmp')) - d.category_view.setCurrentIndex(d.category_view.model().index(0)) d.show() app.exec_() diff --git a/src/calibre/gui2/init.py b/src/calibre/gui2/init.py index 254d2c3d00..a3ae5b77aa 100644 --- a/src/calibre/gui2/init.py +++ b/src/calibre/gui2/init.py @@ -59,6 +59,7 @@ class LibraryViewMixin(object): # {{{ self.action_open_containing_folder, self.action_show_book_details, self.action_del, + self.action_conn_share, add_to_library = None, edit_device_collections=None, similar_menu=similar_menu) @@ -67,21 +68,24 @@ class LibraryViewMixin(object): # {{{ edit_device_collections = (_('Manage collections'), partial(self.edit_device_collections, oncard=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, edit_device_collections=edit_device_collections) edit_device_collections = (_('Manage collections'), partial(self.edit_device_collections, oncard='carda')) 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, edit_device_collections=edit_device_collections) edit_device_collections = (_('Manage collections'), partial(self.edit_device_collections, oncard='cardb')) 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, edit_device_collections=edit_device_collections) diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index 8604587649..b0ddf5eb0d 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -22,6 +22,7 @@ from calibre import human_readable from calibre.utils.config import prefs from calibre.ebooks import BOOK_EXTENSIONS from calibre.gui2.dialogs.scheduler import Scheduler +from calibre.utils.smtp import config as email_config ICON_SIZE = 48 @@ -262,7 +263,9 @@ class ToolBar(QToolBar): # {{{ ch.setCursor(Qt.PointingHandCursor) ch.setAutoRaise(True) 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: self.addAction(x) @@ -306,6 +309,60 @@ class ToolBar(QToolBar): # {{{ class Action(QAction): 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): def __init__(self, db): @@ -341,7 +398,6 @@ class MainWindowMixin(object): self.scheduler.start_recipe_fetch.connect( self.download_scheduled_recipe, type=Qt.QueuedConnection) - def read_toolbar_settings(self): pass @@ -372,18 +428,19 @@ class MainWindowMixin(object): setattr(self, 'action_'+name, 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(2, 2, 3, 'convert', _('Convert books'), 'convert.svg', _('C')) 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')) - ac(5, 5, 3, 'news', _('Fetch news'), 'news.svg', _('F')) - ac(6, 6, 0, 'save', _('Save to disk'), 'save.svg', _('S')) - ac(7, 0, 0, 'sync', _('Send to device'), 'sync.svg') - ac(8, 8, 3, 'del', _('Remove books'), 'trash.svg', _('Del')) - ac(9, 9, 3, 'help', _('Help'), 'help.svg', _('F1'), _("Browse the calibre User Manual")) - ac(10, 10, 0, 'preferences', _('Preferences'), 'config.svg', _('Ctrl+P')) + ac(6, 6, 3, 'news', _('Fetch news'), 'news.svg', _('F')) + ac(7, 7, 0, 'save', _('Save to disk'), 'save.svg', _('S')) + ac(8, 8, 0, 'conn_share', _('Connect/share'), 'connect_share.svg') + ac(9, 9, 3, 'del', _('Remove books'), 'trash.svg', _('Del')) + 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, 'merge', _('Merge book records'), 'merge_books.svg', _('M')) 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.triggered.connect( 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) md = QMenu() @@ -528,6 +589,7 @@ class MainWindowMixin(object): for x in (self.preferences_action, self.action_preferences): x.triggered.connect(self.do_config) + return all_actions # }}} diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py index 13434e444d..870157c81a 100644 --- a/src/calibre/gui2/library/views.py +++ b/src/calibre/gui2/library/views.py @@ -390,7 +390,7 @@ class BooksView(QTableView): # {{{ # Context Menu {{{ 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, edit_device_collections=None): self.setContextMenuPolicy(Qt.DefaultContextMenu) @@ -401,6 +401,8 @@ class BooksView(QTableView): # {{{ self.context_menu.addAction(send_to_device) if convert is not None: 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(save) if open_folder is not None: diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index 72f7202504..cc6a182201 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -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 - def do_config(self, *args): + def do_config(self, checked=False, initial_category='general'): if self.job_manager.has_jobs(): d = error_dialog(self, _('Cannot configure'), _('Cannot configure while there are running jobs.')) @@ -363,7 +363,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{ d.exec_() return d = ConfigDialog(self, self.library_view, - server=self.content_server) + server=self.content_server, initial_category=initial_category) d.exec_() self.content_server = d.server