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 @@
+
+
+
\ No newline at end of file
diff --git a/resources/images/dictionary.png b/resources/images/dictionary.png
deleted file mode 100644
index e9bd55d918..0000000000
Binary files a/resources/images/dictionary.png and /dev/null differ
diff --git a/resources/images/dictionary.svg b/resources/images/dictionary.svg
new file mode 100644
index 0000000000..37b17baf48
--- /dev/null
+++ b/resources/images/dictionary.svg
@@ -0,0 +1,1009 @@
+
+
+
+
\ No newline at end of file
diff --git a/resources/images/user_profile.svg b/resources/images/user_profile.svg
index 0aecc0c1f7..3b2f36131a 100644
--- a/resources/images/user_profile.svg
+++ b/resources/images/user_profile.svg
@@ -2,7 +2,7 @@
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 3efde4824d..870157c81a 100644
--- a/src/calibre/gui2/library/views.py
+++ b/src/calibre/gui2/library/views.py
@@ -214,14 +214,17 @@ class BooksView(QTableView): # {{{
state['column_sizes'][name] = h.sectionSize(i)
return state
+ def write_state(self, state):
+ db = getattr(self.model(), 'db', None)
+ name = unicode(self.objectName())
+ if name and db is not None:
+ db.prefs.set(name + ' books view state', state)
+
def save_state(self):
# Only save if we have been initialized (set_database called)
if len(self.column_map) > 0 and self.was_restored:
state = self.get_state()
- db = getattr(self.model(), 'db', None)
- name = unicode(self.objectName())
- if name and db is not None:
- db.prefs.set(name + ' books view state', state)
+ self.write_state(state)
def cleanup_sort_history(self, sort_history):
history = []
@@ -387,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)
@@ -398,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:
@@ -524,6 +529,19 @@ class DeviceBooksView(BooksView): # {{{
self.context_menu.popup(event.globalPos())
event.accept()
+ def get_old_state(self):
+ ans = None
+ name = unicode(self.objectName())
+ if name:
+ name += ' books view state'
+ ans = gprefs.get(name, None)
+ return ans
+
+ def write_state(self, state):
+ name = unicode(self.objectName())
+ if name:
+ gprefs.set(name + ' books view state', state)
+
def set_database(self, db):
self._model.set_database(db)
self.restore_state()
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index 6bcf564cc3..9913e320e9 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
diff --git a/src/calibre/gui2/viewer/documentview.py b/src/calibre/gui2/viewer/documentview.py
index bd4bd0a01a..4653529095 100644
--- a/src/calibre/gui2/viewer/documentview.py
+++ b/src/calibre/gui2/viewer/documentview.py
@@ -448,7 +448,7 @@ class DocumentView(QWebView):
self.unimplemented_actions = list(map(self.pageAction,
[d.DownloadImageToDisk, d.OpenLinkInNewWindow, d.DownloadLinkToDisk,
d.OpenImageInNewWindow, d.OpenLink]))
- self.dictionary_action = QAction(QIcon(I('dictionary.png')),
+ self.dictionary_action = QAction(QIcon(I('dictionary.svg')),
_('&Lookup in dictionary'), self)
self.dictionary_action.setShortcut(Qt.CTRL+Qt.Key_L)
self.dictionary_action.triggered.connect(self.lookup)