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/resources/recipes/npr.recipe b/resources/recipes/npr.recipe
index 167c2125c7..8a4d2f9873 100644
--- a/resources/recipes/npr.recipe
+++ b/resources/recipes/npr.recipe
@@ -6,6 +6,7 @@ class AdvancedUserRecipe1257302745(BasicNewsRecipe):
language = 'en'
__author__ = 'onyxrev'
max_articles_per_feed = 100
+ no_stylesheets = True
remove_tags_before = {'class':'storytitle'}
remove_tags_after = dict(name='div', attrs={'id':'storytext' })
diff --git a/src/calibre/ebooks/metadata/__init__.py b/src/calibre/ebooks/metadata/__init__.py
index e45334777e..7a53abb067 100644
--- a/src/calibre/ebooks/metadata/__init__.py
+++ b/src/calibre/ebooks/metadata/__init__.py
@@ -295,11 +295,10 @@ class MetaInformation(object):
if val is not None:
setattr(self, attr, val)
- if mi.tags:
- if replace_metadata:
- self.tags = mi.tags
- else:
- self.tags += mi.tags
+ if replace_metadata:
+ self.tags = mi.tags
+ elif mi.tags:
+ self.tags += mi.tags
self.tags = list(set(self.tags))
if mi.author_sort_map:
@@ -313,14 +312,17 @@ class MetaInformation(object):
if len(other_cover) > len(self_cover):
self.cover_data = mi.cover_data
- my_comments = getattr(self, 'comments', '')
- other_comments = getattr(mi, 'comments', '')
- if not my_comments:
- my_comments = ''
- if not other_comments:
- other_comments = ''
- if len(other_comments.strip()) > len(my_comments.strip()):
- self.comments = other_comments
+ if replace_metadata:
+ self.comments = getattr(mi, 'comments', '')
+ else:
+ my_comments = getattr(self, 'comments', '')
+ other_comments = getattr(mi, 'comments', '')
+ if not my_comments:
+ my_comments = ''
+ if not other_comments:
+ other_comments = ''
+ if len(other_comments.strip()) > len(my_comments.strip()):
+ self.comments = other_comments
other_lang = getattr(mi, 'language', None)
if other_lang and other_lang.lower() != 'und':
diff --git a/src/calibre/gui2/actions.py b/src/calibre/gui2/actions.py
index 9522083b98..a49ff76e67 100644
--- a/src/calibre/gui2/actions.py
+++ b/src/calibre/gui2/actions.py
@@ -578,9 +578,7 @@ class DeleteAction(object): # {{{
if row is not None:
ci = view.model().index(row, 0)
if ci.isValid():
- view.setCurrentIndex(ci)
- sm = view.selectionModel()
- sm.select(ci, sm.Select)
+ view.set_current_row(row)
else:
if not confirm('
'+_('The selected books will be '
'permanently deleted '
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/choose_library.py b/src/calibre/gui2/dialogs/choose_library.py
index 03bd303112..f53f617448 100644
--- a/src/calibre/gui2/dialogs/choose_library.py
+++ b/src/calibre/gui2/dialogs/choose_library.py
@@ -10,7 +10,7 @@ import os
from PyQt4.Qt import QDialog
from calibre.gui2.dialogs.choose_library_ui import Ui_Dialog
-from calibre.gui2 import error_dialog, choose_dir, warning_dialog
+from calibre.gui2 import error_dialog, choose_dir
from calibre.constants import filesystem_encoding
from calibre import isbytestring, patheq
from calibre.utils.config import prefs
@@ -62,12 +62,6 @@ class ChooseLibrary(QDialog, Ui_Dialog):
return True
def perform_action(self, ac, loc):
- if ac in ('new', 'existing'):
- warning_dialog(self.parent(), _('Custom columns'),
- _('If you use custom columns and they differ between '
- 'libraries, you will have various problems. Best '
- 'to ensure you have the same custom columns in each '
- 'library.'), show=True)
if ac in ('new', 'existing'):
prefs['library_path'] = loc
self.callback(loc)
diff --git a/src/calibre/gui2/dialogs/config/__init__.py b/src/calibre/gui2/dialogs/config/__init__.py
index 84d55c8fb6..5dcc4feb1a 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)
@@ -488,6 +498,22 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
self.opt_gui_layout.setCurrentIndex(li)
self.opt_disable_animations.setChecked(config['disable_animations'])
self.opt_show_donate_button.setChecked(config['show_donate_button'])
+ idx = 0
+ for i, x in enumerate([(_('Small'), 'small'), (_('Medium'), 'medium'),
+ (_('Large'), 'large')]):
+ if x[1] == gprefs.get('toolbar_icon_size', 'medium'):
+ idx = i
+ self.opt_toolbar_icon_size.addItem(x[0], x[1])
+ self.opt_toolbar_icon_size.setCurrentIndex(idx)
+ idx = 0
+ for i, x in enumerate([(_('Automatic'), 'auto'), (_('Always'), 'always'),
+ (_('Never'), 'never')]):
+ if x[1] == gprefs.get('toolbar_text', 'auto'):
+ idx = i
+ self.opt_toolbar_text.addItem(x[0], x[1])
+ self.opt_toolbar_text.setCurrentIndex(idx)
+
+ self.category_view.setCurrentIndex(self.category_view.model().index_for_name(initial_category))
def check_port_value(self, *args):
port = self.port.value()
@@ -857,6 +883,10 @@ class ConfigDialog(ResizableDialog, Ui_Dialog):
config['disable_animations'] = bool(self.opt_disable_animations.isChecked())
config['show_donate_button'] = bool(self.opt_show_donate_button.isChecked())
gprefs['show_splash_screen'] = bool(self.show_splash_screen.isChecked())
+ for x in ('toolbar_icon_size', 'toolbar_text'):
+ w = getattr(self, 'opt_'+x)
+ data = w.itemData(w.currentIndex()).toString()
+ gprefs[x] = unicode(data)
fmts = []
for i in range(self.viewer.count()):
if self.viewer.item(i).checkState() == Qt.Checked:
@@ -942,6 +972,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/dialogs/config/config.ui b/src/calibre/gui2/dialogs/config/config.ui
index 5d6bff2467..df19aa2a26 100644
--- a/src/calibre/gui2/dialogs/config/config.ui
+++ b/src/calibre/gui2/dialogs/config/config.ui
@@ -346,21 +346,21 @@
-
+ Automatically send downloaded &news to ebook reader
-
+ &Delete news from library when it is automatically sent to reader
-
+
@@ -377,7 +377,7 @@
-
+
@@ -580,6 +580,41 @@
+
+
+
+ &Toolbar
+
+
+
+
+
+
+
+
+ &Icon size:
+
+
+ opt_toolbar_icon_size
+
+
+
+
+
+
+
+
+
+ Show &text under icons:
+
+
+ opt_toolbar_text
+
+
+
+
+
+
diff --git a/src/calibre/gui2/dialogs/saved_search_editor.py b/src/calibre/gui2/dialogs/saved_search_editor.py
index 6a8b790625..3f9f7ad437 100644
--- a/src/calibre/gui2/dialogs/saved_search_editor.py
+++ b/src/calibre/gui2/dialogs/saved_search_editor.py
@@ -25,8 +25,8 @@ class SavedSearchEditor(QDialog, Ui_SavedSearchEditor):
self.current_search_name = None
self.searches = {}
self.searches_to_delete = []
- for name in saved_searches.names():
- self.searches[name] = saved_searches.lookup(name)
+ for name in saved_searches().names():
+ self.searches[name] = saved_searches().lookup(name)
self.populate_search_list()
if initial_search is not None and initial_search in self.searches:
@@ -78,7 +78,7 @@ class SavedSearchEditor(QDialog, Ui_SavedSearchEditor):
if self.current_search_name:
self.searches[self.current_search_name] = unicode(self.search_text.toPlainText())
for name in self.searches_to_delete:
- saved_searches.delete(name)
+ saved_searches().delete(name)
for name in self.searches:
- saved_searches.add(name, self.searches[name])
+ saved_searches().add(name, self.searches[name])
QDialog.accept(self)
diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py
index 7b4af2d162..3dbf7ffffd 100644
--- a/src/calibre/gui2/dialogs/scheduler.py
+++ b/src/calibre/gui2/dialogs/scheduler.py
@@ -62,6 +62,7 @@ class SchedulerDialog(QDialog, Ui_Dialog):
self.search_done)
self.disconnect(self.recipe_model, SIGNAL('searched(PyQt_PyObject)'),
self.search.search_done)
+ self.search.search.disconnect()
self.recipe_model = None
def search_done(self, *args):
diff --git a/src/calibre/gui2/dialogs/tag_categories.py b/src/calibre/gui2/dialogs/tag_categories.py
index b7d64226ab..0a54696801 100644
--- a/src/calibre/gui2/dialogs/tag_categories.py
+++ b/src/calibre/gui2/dialogs/tag_categories.py
@@ -7,7 +7,6 @@ from PyQt4.QtCore import SIGNAL, Qt
from PyQt4.QtGui import QDialog, QIcon, QListWidgetItem
from calibre.gui2.dialogs.tag_categories_ui import Ui_TagCategories
-from calibre.utils.config import prefs
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.constants import islinux
@@ -63,7 +62,7 @@ class TagCategories(QDialog, Ui_TagCategories):
self.all_items.append(t)
self.all_items_dict[label+':'+n] = t
- self.categories = dict.copy(prefs['user_categories'])
+ self.categories = dict.copy(db.prefs.get('user_categories', {}))
if self.categories is None:
self.categories = {}
for cat in self.categories:
@@ -182,7 +181,7 @@ class TagCategories(QDialog, Ui_TagCategories):
def accept(self):
self.save_category()
- prefs['user_categories'] = self.categories
+ self.db.prefs['user_categories'] = self.categories
QDialog.accept(self)
def save_category(self):
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..012a9b5ce3 100644
--- a/src/calibre/gui2/layout.py
+++ b/src/calibre/gui2/layout.py
@@ -16,14 +16,14 @@ from PyQt4.Qt import QIcon, Qt, QWidget, QAction, QToolBar, QSize, \
from calibre.constants import __appname__, isosx
from calibre.gui2.search_box import SearchBox2, SavedSearchBox
from calibre.gui2.throbber import ThrobbingButton
-from calibre.gui2 import config, open_url
+from calibre.gui2 import config, open_url, gprefs
from calibre.gui2.widgets import ComboBoxWithHelp
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
class SaveMenu(QMenu): # {{{
@@ -228,12 +228,11 @@ class ToolBar(QToolBar): # {{{
self.setFloatable(False)
self.setOrientation(Qt.Horizontal)
self.setAllowedAreas(Qt.TopToolBarArea|Qt.BottomToolBarArea)
- self.setIconSize(QSize(ICON_SIZE, ICON_SIZE))
- self.setToolButtonStyle(Qt.ToolButtonTextUnderIcon)
self.setStyleSheet('QToolButton:checked { font-weight: bold }')
+ self.donate = donate
+ self.apply_settings()
self.all_actions = actions
- self.donate = donate
self.location_manager = location_manager
self.location_manager.locations_changed.connect(self.build_bar)
self.d_widget = QWidget()
@@ -242,6 +241,17 @@ class ToolBar(QToolBar): # {{{
donate.setAutoRaise(True)
donate.setCursor(Qt.PointingHandCursor)
self.build_bar()
+ self.preferred_width = self.sizeHint().width()
+
+ def apply_settings(self):
+ sz = gprefs.get('toolbar_icon_size', 'medium')
+ sz = {'small':24, 'medium':48, 'large':64}[sz]
+ self.setIconSize(QSize(sz, sz))
+ style = Qt.ToolButtonTextUnderIcon
+ if gprefs.get('toolbar_text', 'auto') == 'never':
+ style = Qt.ToolButtonIconOnly
+ self.setToolButtonStyle(style)
+ self.donate.set_normal_icon_size(sz, sz)
def contextMenuEvent(self, *args):
pass
@@ -262,7 +272,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)
@@ -292,11 +304,16 @@ class ToolBar(QToolBar): # {{{
a.setText(text)
def resizeEvent(self, ev):
- style = Qt.ToolButtonTextUnderIcon
- if self.size().width() < 1260:
- style = Qt.ToolButtonIconOnly
- self.setToolButtonStyle(style)
QToolBar.resizeEvent(self, ev)
+ style = Qt.ToolButtonTextUnderIcon
+ p = gprefs.get('toolbar_text', 'auto')
+ if p == 'never':
+ style = Qt.ToolButtonIconOnly
+
+ if p == 'auto' and self.preferred_width > self.width()+35:
+ style = Qt.ToolButtonIconOnly
+
+ self.setToolButtonStyle(style)
def database_changed(self, db):
pass
@@ -306,6 +323,62 @@ 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):
@@ -321,7 +394,6 @@ class MainWindowMixin(object):
self.centralwidget.setLayout(self._central_widget_layout)
self.resize(1012, 740)
self.donate_button = ThrobbingButton(self.centralwidget)
- self.donate_button.set_normal_icon_size(ICON_SIZE, ICON_SIZE)
self.location_manager = LocationManager(self)
self.init_scheduler(db)
@@ -341,7 +413,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 +443,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 +474,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 +604,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 c6c32f86f7..870157c81a 100644
--- a/src/calibre/gui2/library/views.py
+++ b/src/calibre/gui2/library/views.py
@@ -214,13 +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()
- name = unicode(self.objectName())
- if name:
- gprefs.set(name + ' books view state', state)
+ self.write_state(state)
def cleanup_sort_history(self, sort_history):
history = []
@@ -298,11 +302,27 @@ class BooksView(QTableView): # {{{
old_state['column_sizes'][name] += 12
return old_state
- def restore_state(self):
+ def get_old_state(self):
+ ans = None
name = unicode(self.objectName())
- old_state = None
if name:
- old_state = gprefs.get(name + ' books view state', None)
+ name += ' books view state'
+ db = getattr(self.model(), 'db', None)
+ if db is not None:
+ ans = db.prefs.get(name, None)
+ if ans is None:
+ ans = gprefs.get(name, None)
+ try:
+ del gprefs[name]
+ except:
+ pass
+ if ans is not None:
+ db.prefs[name] = ans
+ return ans
+
+
+ def restore_state(self):
+ old_state = self.get_old_state()
if old_state is None:
old_state = self.get_default_state()
@@ -370,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)
@@ -381,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:
@@ -507,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/search_box.py b/src/calibre/gui2/search_box.py
index dd7d0a63a0..cb6791bb3b 100644
--- a/src/calibre/gui2/search_box.py
+++ b/src/calibre/gui2/search_box.py
@@ -10,13 +10,12 @@ import re
from PyQt4.Qt import QComboBox, Qt, QLineEdit, QStringList, pyqtSlot, \
pyqtSignal, SIGNAL, QObject, QDialog, QCompleter, \
- QAction, QKeySequence
+ QAction, QKeySequence, QTimer
from calibre.gui2 import config
from calibre.gui2.dialogs.confirm_delete import confirm
from calibre.gui2.dialogs.saved_search_editor import SavedSearchEditor
from calibre.gui2.dialogs.search import SearchDialog
-from calibre.utils.config import prefs
from calibre.utils.search_query_parser import saved_searches
class SearchLineEdit(QLineEdit):
@@ -83,7 +82,9 @@ class SearchBox2(QComboBox):
self.help_state = False
self.as_you_type = True
self.prev_search = ''
- self.timer = None
+ self.timer = QTimer()
+ self.timer.setSingleShot(True)
+ self.timer.timeout.connect(self.timer_event, type=Qt.QueuedConnection)
self.setInsertPolicy(self.NoInsert)
self.setMaxCount(self.MAX_COUNT)
self.setSizeAdjustPolicy(self.AdjustToMinimumContentsLengthWithIcon)
@@ -117,9 +118,6 @@ class SearchBox2(QComboBox):
self.search.emit('')
self._in_a_search = False
self.setEditText(self.help_text)
- if self.timer is not None: # Turn off any timers that got started in setEditText
- self.killTimer(self.timer)
- self.timer = None
self.line_edit.home(False)
self.line_edit.setStyleSheet(
'QLineEdit { color: gray; background-color: %s; }' %
@@ -148,18 +146,15 @@ class SearchBox2(QComboBox):
self._in_a_search = False
if event.key() in (Qt.Key_Return, Qt.Key_Enter):
self.do_search()
- self.timer = self.startTimer(self.__class__.INTERVAL)
+ self.timer.start(1500)
def mouse_released(self, event):
self.normalize_state()
if self.as_you_type:
- self.timer = self.startTimer(self.__class__.INTERVAL)
+ self.timer.start(1500)
- def timerEvent(self, event):
- self.killTimer(event.timerId())
- if event.timerId() == self.timer:
- self.timer = None
- self.do_search()
+ def timer_event(self):
+ self.do_search()
def history_selected(self, text):
self.emit(SIGNAL('changed()'))
@@ -213,9 +208,6 @@ class SearchBox2(QComboBox):
return
self.normalize_state()
self.setEditText(txt)
- if self.timer is not None: # Turn off any timers that got started in setEditText
- self.killTimer(self.timer)
- self.timer = None
self.search.emit(txt)
self.line_edit.end(False)
self.initial_state = False
@@ -259,8 +251,7 @@ class SavedSearchBox(QComboBox):
self.setMinimumContentsLength(10)
self.tool_tip_text = self.toolTip()
- def initialize(self, _saved_searches, _search_box, colorize=False, help_text=_('Search')):
- self.saved_searches = _saved_searches
+ def initialize(self, _search_box, colorize=False, help_text=_('Search')):
self.search_box = _search_box
self.help_text = help_text
self.colorize = colorize
@@ -302,11 +293,11 @@ class SavedSearchBox(QComboBox):
self.normalize_state()
self.search_box.set_search_string(u'search:"%s"' % qname)
self.setEditText(qname)
- self.setToolTip(self.saved_searches.lookup(qname))
+ self.setToolTip(saved_searches().lookup(qname))
def initialize_saved_search_names(self):
self.clear()
- qnames = self.saved_searches.names()
+ qnames = saved_searches().names()
self.addItems(qnames)
self.setCurrentIndex(-1)
@@ -319,10 +310,10 @@ class SavedSearchBox(QComboBox):
idx = self.currentIndex
if idx < 0:
return
- ss = self.saved_searches.lookup(unicode(self.currentText()))
+ ss = saved_searches().lookup(unicode(self.currentText()))
if ss is None:
return
- self.saved_searches.delete(unicode(self.currentText()))
+ saved_searches().delete(unicode(self.currentText()))
self.clear_to_help()
self.search_box.clear_to_help()
self.emit(SIGNAL('changed()'))
@@ -332,8 +323,8 @@ class SavedSearchBox(QComboBox):
name = unicode(self.currentText())
if self.help_state or not name.strip():
name = unicode(self.search_box.text()).replace('"', '')
- self.saved_searches.delete(name)
- self.saved_searches.add(name, unicode(self.search_box.text()))
+ saved_searches().delete(name)
+ saved_searches().add(name, unicode(self.search_box.text()))
# now go through an initialization cycle to ensure that the combobox has
# the new search in it, that it is selected, and that the search box
# references the new search instead of the text in the search.
@@ -348,7 +339,7 @@ class SavedSearchBox(QComboBox):
idx = self.currentIndex();
if idx < 0:
return
- self.search_box.set_search_string(self.saved_searches.lookup(unicode(self.currentText())))
+ self.search_box.set_search_string(saved_searches().lookup(unicode(self.currentText())))
class SearchBoxMixin(object):
@@ -390,11 +381,12 @@ class SearchBoxMixin(object):
class SavedSearchBoxMixin(object):
- def __init__(self):
+ def __init__(self, db):
+ self.db = db
self.connect(self.saved_search, SIGNAL('changed()'), self.saved_searches_changed)
self.saved_searches_changed()
self.connect(self.clear_button, SIGNAL('clicked()'), self.saved_search.clear_to_help)
- self.saved_search.initialize(saved_searches, self.search, colorize=True,
+ self.saved_search.initialize(self.search, colorize=True,
help_text=_('Saved Searches'))
self.connect(self.save_search_button, SIGNAL('clicked()'),
self.saved_search.save_search_button_clicked)
@@ -409,9 +401,12 @@ class SavedSearchBoxMixin(object):
b = getattr(self, x+'_search_button')
b.setStatusTip(b.toolTip())
+ def set_database(self, db):
+ self.db = db
+ self.saved_searches_changed()
def saved_searches_changed(self):
- p = prefs['saved_searches'].keys()
+ p = saved_searches().names()
p.sort()
t = unicode(self.search_restriction.currentText())
self.search_restriction.clear() # rebuild the restrictions combobox using current saved searches
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py
index 2a9fb129ac..1565520ca1 100644
--- a/src/calibre/gui2/tag_view.py
+++ b/src/calibre/gui2/tag_view.py
@@ -17,7 +17,6 @@ from PyQt4.Qt import Qt, QTreeView, QApplication, pyqtSignal, \
from calibre.ebooks.metadata import title_sort
from calibre.gui2 import config, NONE
-from calibre.utils.config import prefs
from calibre.library.field_metadata import TagsIcons
from calibre.utils.search_query_parser import saved_searches
from calibre.gui2 import error_dialog
@@ -224,7 +223,7 @@ class TagsView(QTreeView): # {{{
# Always show the user categories editor
self.context_menu.addSeparator()
- if category in prefs['user_categories'].keys():
+ if category in self.db.prefs.get('user_categories', {}).keys():
self.context_menu.addAction(_('Manage User Categories'),
partial(self.context_menu_handler, action='manage_categories',
category=category))
@@ -426,10 +425,10 @@ class TagsModel(QAbstractItemModel): # {{{
for k in tb_cats.keys():
if tb_cats[k]['kind'] in ['user', 'search']:
del tb_cats[k]
- for user_cat in sorted(prefs['user_categories'].keys()):
+ for user_cat in sorted(self.db.prefs.get('user_categories', {}).keys()):
cat_name = user_cat+':' # add the ':' to avoid name collision
tb_cats.add_user_category(label=cat_name, name=user_cat)
- if len(saved_searches.names()):
+ if len(saved_searches().names()):
tb_cats.add_search_category(label='search', name=_('Searches'))
# Now get the categories
@@ -507,11 +506,11 @@ class TagsModel(QAbstractItemModel): # {{{
if key not in self.db.field_metadata:
return
if key == 'search':
- if val in saved_searches.names():
+ if val in saved_searches().names():
error_dialog(self.tags_view, _('Duplicate search name'),
_('The saved search name %s is already used.')%val).exec_()
return False
- saved_searches.rename(unicode(item.data(role).toString()), val)
+ saved_searches().rename(unicode(item.data(role).toString()), val)
self.tags_view.search_item_renamed.emit()
else:
if key == 'series':
diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py
index 72f7202504..ec8d07bdbe 100644
--- a/src/calibre/gui2/ui.py
+++ b/src/calibre/gui2/ui.py
@@ -199,7 +199,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
UpdateMixin.__init__(self, opts)
####################### Search boxes ########################
- SavedSearchBoxMixin.__init__(self)
+ SavedSearchBoxMixin.__init__(self, db)
SearchBoxMixin.__init__(self)
####################### Library view ########################
@@ -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
@@ -380,6 +380,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
self.tags_view.recount()
self.create_device_menu()
self.set_device_menu_items_state(bool(self.device_connected))
+ self.tool_bar.apply_settings()
def library_moved(self, newloc):
if newloc is None: return
@@ -392,6 +393,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, # {{{
self.library_view.model().set_book_on_device_func(self.book_on_device)
self.status_bar.clear_message()
self.search.clear_to_help()
+ self.saved_search.clear_to_help()
self.book_details.reset_info()
self.library_view.model().count_changed()
self.scheduler.database_changed(db)
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)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 85e951d4b0..e3530d3801 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -19,6 +19,7 @@ from calibre.library.schema_upgrades import SchemaUpgrade
from calibre.library.caches import ResultCache
from calibre.library.custom_columns import CustomColumns
from calibre.library.sqlite import connect, IntegrityError, DBThread
+from calibre.library.prefs import DBPrefs
from calibre.ebooks.metadata import string_to_authors, authors_to_string, \
MetaInformation
from calibre.ebooks.metadata.meta import get_metadata, metadata_from_formats
@@ -29,7 +30,7 @@ from calibre.customize.ui import run_plugins_on_import
from calibre.utils.filenames import ascii_filename
from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp
from calibre.utils.config import prefs, tweaks
-from calibre.utils.search_query_parser import saved_searches
+from calibre.utils.search_query_parser import saved_searches, set_saved_searches
from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
from calibre.utils.magick_draw import save_cover_data_to
@@ -140,6 +141,21 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.initialize_dynamic()
def initialize_dynamic(self):
+ self.prefs = DBPrefs(self)
+
+ # Migrate saved search and user categories to db preference scheme
+ def migrate_preference(key, default):
+ oldval = prefs[key]
+ if oldval != default:
+ self.prefs[key] = oldval
+ prefs[key] = default
+ if key not in self.prefs:
+ self.prefs[key] = default
+
+ migrate_preference('user_categories', {})
+ migrate_preference('saved_searches', {})
+ set_saved_searches(self, 'saved_searches')
+
self.conn.executescript('''
DROP TRIGGER IF EXISTS author_insert_trg;
CREATE TEMP TRIGGER author_insert_trg
@@ -268,10 +284,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
for k in tb_cats.keys():
if tb_cats[k]['kind'] in ['user', 'search']:
del tb_cats[k]
- for user_cat in sorted(prefs['user_categories'].keys()):
+ for user_cat in sorted(self.prefs.get('user_categories', {}).keys()):
cat_name = user_cat+':' # add the ':' to avoid name collision
tb_cats.add_user_category(label=cat_name, name=user_cat)
- if len(saved_searches.names()):
+ if len(saved_searches().names()):
tb_cats.add_search_category(label='search', name=_('Searches'))
self.book_on_device_func = None
@@ -843,7 +859,7 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
categories['formats'].sort(key = lambda x:x.name)
#### Now do the user-defined categories. ####
- user_categories = prefs['user_categories']
+ user_categories = self.prefs['user_categories']
# We want to use same node in the user category as in the source
# category. To do that, we need to find the original Tag node. There is
@@ -880,8 +896,8 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
icon = None
if icon_map and 'search' in icon_map:
icon = icon_map['search']
- for srch in saved_searches.names():
- items.append(Tag(srch, tooltip=saved_searches.lookup(srch), icon=icon))
+ for srch in saved_searches().names():
+ items.append(Tag(srch, tooltip=saved_searches().lookup(srch), icon=icon))
if len(items):
if icon_map is not None:
icon_map['search'] = icon_map['search']
diff --git a/src/calibre/library/prefs.py b/src/calibre/library/prefs.py
new file mode 100644
index 0000000000..ff9733aaa3
--- /dev/null
+++ b/src/calibre/library/prefs.py
@@ -0,0 +1,49 @@
+#!/usr/bin/env python
+# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
+
+__license__ = 'GPL v3'
+__copyright__ = '2010, Kovid Goyal '
+__docformat__ = 'restructuredtext en'
+
+import json
+
+from calibre.constants import preferred_encoding
+from calibre.utils.config import to_json, from_json
+
+class DBPrefs(dict):
+
+ def __init__(self, db):
+ dict.__init__(self)
+ self.db = db
+ for key, val in self.db.conn.get('SELECT key,val FROM preferences'):
+ val = self.raw_to_object(val)
+ dict.__setitem__(self, key, val)
+
+ def raw_to_object(self, raw):
+ if not isinstance(raw, unicode):
+ raw = raw.decode(preferred_encoding)
+ return json.loads(raw, object_hook=from_json)
+
+ def to_raw(self, val):
+ return json.dumps(val, indent=2, default=to_json)
+
+ def __getitem__(self, key):
+ return dict.__getitem__(self, key)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ self.db.conn.execute('DELETE FROM preferences WHERE key=?', (key,))
+ self.db.conn.commit()
+
+ def __setitem__(self, key, val):
+ raw = self.to_raw(val)
+ self.db.conn.execute('DELETE FROM preferences WHERE key=?', (key,))
+ self.db.conn.execute('INSERT INTO preferences (key,val) VALUES (?,?)', (key,
+ raw))
+ self.db.conn.commit()
+ dict.__setitem__(self, key, val)
+
+ def set(self, key, val):
+ self.__setitem__(key, val)
+
+
diff --git a/src/calibre/library/schema_upgrades.py b/src/calibre/library/schema_upgrades.py
index 1ba650f6fd..b08161abf2 100644
--- a/src/calibre/library/schema_upgrades.py
+++ b/src/calibre/library/schema_upgrades.py
@@ -387,3 +387,13 @@ class SchemaUpgrade(object):
self.conn.execute('UPDATE authors SET sort=author_to_author_sort(name)')
+ def upgrade_version_12(self):
+ 'DB based preference store'
+ script = '''
+ DROP TABLE IF EXISTS preferences;
+ CREATE TABLE preferences(id INTEGER PRIMARY KEY,
+ key TEXT NON NULL,
+ val TEXT NON NULL,
+ UNIQUE(key));
+ '''
+ self.conn.executescript(script)
diff --git a/src/calibre/manual/conversion.rst b/src/calibre/manual/conversion.rst
index 7df11575de..c8bc3ef665 100644
--- a/src/calibre/manual/conversion.rst
+++ b/src/calibre/manual/conversion.rst
@@ -349,7 +349,7 @@ table of contents, check the :guilabel:`Do not add detected chapters` option.
If less than the :guilabel:`Chapter threshold` number of chapters were detected, |app| will then add any hyperlinks
it finds in the input document to the Table of Contents. This often works well many input documents include a
-hyperlinked Table of Contents right at the start. The :guilabel:`Number fo links` option can be used to control
+hyperlinked Table of Contents right at the start. The :guilabel:`Number of links` option can be used to control
this behavior. If set to zero, no links are added. If set to a number greater than zero, at most that number of links
is added.
diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot
index 77dc4a9c2b..8ba38ad6bc 100644
--- a/src/calibre/translations/calibre.pot
+++ b/src/calibre/translations/calibre.pot
@@ -5,8 +5,8 @@
msgid ""
msgstr ""
"Project-Id-Version: calibre 0.7.9\n"
-"POT-Creation-Date: 2010-07-17 19:33+MDT\n"
-"PO-Revision-Date: 2010-07-17 19:33+MDT\n"
+"POT-Creation-Date: 2010-07-20 16:32+MDT\n"
+"PO-Revision-Date: 2010-07-20 16:32+MDT\n"
"Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n"
@@ -41,7 +41,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:235
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:278
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:281
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:395
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:397
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:20
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/book/base.py:21
#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/ereader.py:36
@@ -110,15 +110,15 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:110
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:135
#: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:137
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:897
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:906
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1189
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1192
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:855
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:864
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1149
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1152
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf.py:47
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:120
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:155
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:481
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:172
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:173
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:362
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:382
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:882
@@ -128,18 +128,18 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:185
#: /home/kovid/work/calibre/src/calibre/library/cli.py:213
#: /home/kovid/work/calibre/src/calibre/library/database.py:913
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:339
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:351
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:954
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1023
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1624
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1626
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1748
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:355
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:367
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:970
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1039
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1640
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1642
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1764
#: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:268
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:134
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:137
#: /home/kovid/work/calibre/src/calibre/library/server/xml.py:68
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:115
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:117
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:46
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:64
#: /home/kovid/work/calibre/src/calibre/utils/podofo/__init__.py:78
@@ -476,10 +476,10 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:807
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:813
#: /home/kovid/work/calibre/src/calibre/devices/usbms/device.py:841
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:243
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:171
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:184
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1508
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:244
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:187
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:200
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1524
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:132
msgid "News"
msgstr ""
@@ -1594,7 +1594,7 @@ msgstr ""
msgid "Comic"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:394
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:396
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/info.py:45
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:97
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:98
@@ -1607,7 +1607,7 @@ msgstr ""
msgid "Title"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:395
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:397
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:59
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:67
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:364
@@ -1615,18 +1615,18 @@ msgstr ""
msgid "Author(s)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:396
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:398
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:61
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:72
msgid "Publisher"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:397
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:399
#: /home/kovid/work/calibre/src/calibre/ebooks/pdf/manipulate/info.py:49
msgid "Producer"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:398
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:400
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:35
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:210
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:211
@@ -1638,9 +1638,9 @@ msgstr ""
msgid "Comments"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:406
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:408
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:27
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:46
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:45
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:73
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:306
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1075
@@ -1648,9 +1648,9 @@ msgstr ""
msgid "Tags"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:408
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:410
#: /home/kovid/work/calibre/src/calibre/gui2/book_details.py:26
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:46
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:45
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:74
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:323
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1084
@@ -1658,22 +1658,22 @@ msgstr ""
msgid "Series"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:409
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:411
msgid "Language"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:411
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:413
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1067
msgid "Timestamp"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:413
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:415
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/fetch_metadata.py:63
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:70
msgid "Published"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:415
+#: /home/kovid/work/calibre/src/calibre/ebooks/metadata/__init__.py:417
msgid "Rights"
msgstr ""
@@ -2576,14 +2576,14 @@ msgid "Main memory"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions.py:519
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:445
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:454
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:423
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:432
msgid "Storage Card A"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions.py:520
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:447
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:456
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:425
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:434
msgid "Storage Card B"
msgstr ""
@@ -2641,9 +2641,9 @@ msgid "Failed to download metadata:"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/actions.py:659
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:638
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:515
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:923
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:595
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:541
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:953
#: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:53
msgid "Error"
msgstr ""
@@ -2793,7 +2793,7 @@ msgid "The specified directory could not be processed."
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/add.py:263
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:839
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:797
msgid "No books"
msgstr ""
@@ -2907,14 +2907,14 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:79
#: /home/kovid/work/calibre/src/calibre/gui2/device_drivers/configwidget_ui.py:80
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library_ui.py:72
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:528
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:529
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:541
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:543
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:545
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:547
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:548
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:588
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:560
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:562
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:564
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:566
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:567
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:610
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:360
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:365
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:379
@@ -2976,7 +2976,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:118
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:119
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/book_info.py:122
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:220
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:230
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:312
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:100
msgid "Formats"
@@ -3158,8 +3158,8 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:20
#: /home/kovid/work/calibre/src/calibre/library/catalog.py:550
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1471
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1489
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1487
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1505
msgid "Catalog"
msgstr ""
@@ -4219,205 +4219,186 @@ msgstr ""
msgid "View book on device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:408
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:406
msgid "Set default send to device action"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:414
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:421
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:423
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:425
-msgid "Email to"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:425
-msgid " and delete from library"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:434
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:412
msgid "Send to main memory"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:436
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:414
msgid "Send to storage card A"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:438
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:416
msgid "Send to storage card B"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:443
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:452
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:421
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:430
msgid "Main Memory"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:470
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:441
msgid "Send and delete from library"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:471
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:442
msgid "Send specific format"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:510
-msgid "Connect to folder"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:516
-msgid "Connect to iTunes"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:521
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:478
msgid "Eject device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:529
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:486
msgid "Fetch annotations (experimental)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:639
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:596
msgid "Error communicating with device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:659
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:618
msgid "Select folder to open as device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:704
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:662
msgid "Failed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:710
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:668
msgid "Error talking to device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:711
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:669
msgid "There was a temporary error talking to the device. Please unplug and reconnect the device and or reboot."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:753
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:711
msgid "Device: "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:755
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:713
msgid " detected."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:840
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:798
msgid "selected to send"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:845
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:803
msgid "Choose format to send to device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:854
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:812
msgid "No device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:855
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:813
msgid "Cannot send: No device is connected"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:858
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:862
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:816
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:820
msgid "No card"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:859
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:863
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:817
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:821
msgid "Cannot send: Device has no storage card"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:904
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:862
msgid "E-book:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:907
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:865
msgid "Attached, you will find the e-book"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:908
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:866
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:181
msgid "by"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:909
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:867
msgid "in the %s format."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:922
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:880
msgid "Sending email to"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:952
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:960
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1052
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1114
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1233
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1241
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:910
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:918
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1012
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1074
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1193
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1201
msgid "No suitable formats"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:953
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:911
msgid "Auto convert the following books before sending via email?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:961
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:919
msgid "Could not email the following books as no suitable formats were found:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:979
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:937
msgid "Failed to email books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:980
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:938
msgid "Failed to email the following books:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:984
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:942
msgid "Sent by email:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1011
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:971
msgid "News:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1012
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:972
msgid "Attached is the"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1023
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:983
msgid "Sent news to"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1053
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1115
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1234
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1013
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1075
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1194
msgid "Auto convert the following books before uploading to the device?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1083
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1043
msgid "Sending catalogs to device."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1147
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1107
msgid "Sending news to device."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1200
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1160
msgid "Sending books to device."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1242
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1202
msgid "Could not upload the following books to the device, as no suitable formats were found. Convert the book(s) to a format supported by your device first."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1304
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1264
msgid "No space on device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1305
+#: /home/kovid/work/calibre/src/calibre/gui2/device.py:1265
msgid "
Cannot upload books to device there is no more free space available "
msgstr ""
@@ -4514,14 +4495,6 @@ msgstr ""
msgid "The folder %s is not empty. Please choose an empty folder"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library.py:66
-msgid "Custom columns"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library.py:67
-msgid "If you use custom columns and they differ between libraries, you will have various problems. Best to ensure you have the same custom columns in each library."
-msgstr ""
-
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_library_ui.py:66
msgid "Choose your calibre library"
msgstr ""
@@ -4584,11 +4557,7 @@ msgid ""
"Customization: "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:200
-msgid "Conversion"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:200
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:199
msgid "General"
msgstr ""
@@ -4597,180 +4566,208 @@ msgid "Interface"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:201
-msgid "Add/Save"
+msgid "Conversion"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:201
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:202
msgid ""
"Email\n"
"Delivery"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:202
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:203
+msgid "Add/Save"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:204
msgid "Advanced"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:202
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:205
msgid ""
"Content\n"
"Server"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:202
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:206
msgid "Plugins"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:220
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:230
msgid "Auto send"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:220
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:230
msgid "Email"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:225
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:235
msgid "Formats to email. The first matching format will be sent."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:226
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:236
msgid "If checked, downloaded news will be automatically mailed to this email address (provided it is in one of the listed formats)."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:300
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:310
msgid "new email address"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:482
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:492
msgid "Wide"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:483
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:493
msgid "Narrow"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:495
-msgid "System port selected"
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:502
+msgid "Medium"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:496
-msgid "The value %d you have chosen for the content server port is a system port. Your operating system may not allow the server to run on this port. To be safe choose a port number larger than 1024."
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:502
+msgid "Small"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:516
-msgid "Failed to install command line tools."
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:503
+msgid "Large"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:519
-msgid "Command line tools installed"
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:509
+msgid "Always"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:520
-msgid "Command line tools installed in"
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:509
+msgid "Automatic"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:510
+msgid "Never"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:521
+msgid "System port selected"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:522
+msgid "The value %d you have chosen for the content server port is a system port. Your operating system may not allow the server to run on this port. To be safe choose a port number larger than 1024."
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:542
+msgid "Failed to install command line tools."
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:545
+msgid "Command line tools installed"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:546
+msgid "Command line tools installed in"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:547
msgid "If you move calibre.app, you have to re-install the command line tools."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:572
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:598
msgid "No valid plugin path"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:573
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:599
msgid "%s is not a valid plugin path"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:576
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:602
msgid "Choose plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:588
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:614
msgid "Plugin cannot be disabled"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:589
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:615
msgid "The plugin: %s cannot be disabled"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:598
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:624
msgid "Plugin not customizable"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:599
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:625
msgid "Plugin: %s does not need customization"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:607
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:633
msgid "Customize"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:645
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:671
msgid "Cannot remove builtin plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:646
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:672
msgid " cannot be removed. It is a builtin plugin. Try disabling it instead."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:677
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:703
msgid "You must select a column to delete it"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:682
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:708
msgid "The selected column is not a custom column"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:683
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:709
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/confirm_delete_ui.py:48
msgid "Are you sure?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:684
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:710
msgid "Do you really want to delete column %s and all its data?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:751
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:777
msgid "Error log:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:758
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:784
msgid "Access log:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:786
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:812
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:312
msgid "Failed to start content server"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:811
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:837
msgid "Invalid size"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:812
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:838
msgid "The size %s is invalid. must be of the form widthxheight"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:869
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:899
msgid "Must restart"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:870
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:900
msgid "The changes you made require that Calibre be restarted. Please restart as soon as practical."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:904
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:934
msgid "Checking database integrity"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:924
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:954
msgid "Failed to check database integrity"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:929
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:959
msgid "Some inconsistencies found"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:930
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/__init__.py:960
msgid "The following books had formats listed in the database that are not actually available. The entries for the formats have been removed. You should check them manually. This can happen if you manipulate the files in the library folder directly."
msgstr ""
@@ -4885,285 +4882,297 @@ msgstr ""
msgid "Sending to &device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:514
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:386
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:523
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:533
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:458
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:599
#: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:201
msgid "Preferences"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:515
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:534
msgid "Show notification when &new version is available"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:516
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:535
msgid "Download &social metadata (tags/ratings/etc.) by default"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:517
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:536
msgid "&Overwrite author and title by default when fetching metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:518
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:537
msgid "Default network &timeout:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:519
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:538
msgid "Set the default timeout for network fetches (i.e. anytime we go out to the internet to get information)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:520
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:539
msgid " seconds"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:521
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:540
msgid "Choose &language (requires restart):"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:522
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:541
msgid "Normal"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:523
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:542
msgid "High"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:524
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:543
msgid "Low"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:525
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:544
msgid "Job &priority:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:526
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:545
msgid "Preferred &output format:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:527
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:546
msgid "Preferred &input format order:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:530
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:549
msgid "Use &Roman numerals for series number"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:531
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:550
msgid "Enable system &tray icon (needs restart)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:532
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:551
msgid "Show ¬ifications in system tray"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:533
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:552
msgid "Show &splash screen at startup"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:534
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:553
msgid "Show cover &browser in a separate window (needs restart)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:535
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:554
msgid "Show &average ratings in the tags browser"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:536
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:555
msgid "Search as you type"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:537
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:556
msgid "Automatically send downloaded &news to ebook reader"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:538
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:557
msgid "&Delete news from library when it is automatically sent to reader"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:539
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:558
msgid "&Number of covers to show in browse mode (needs restart):"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:540
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:559
msgid "Select visible &columns in library view"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:542
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:561
msgid "Remove a user-defined column"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:544
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:563
msgid "Add a user-defined column"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:546
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:565
msgid "Edit settings of a user-defined column"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:549
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:568
msgid "Use internal &viewer for:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:550
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:569
msgid "User Interface &layout (needs restart):"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:551
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:570
msgid "Disable all animations. Useful if you have a slow/old computer."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:552
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:571
msgid "Disable &animations"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:553
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:572
msgid "Show &donate button (restart)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:554
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:573
+msgid "&Toolbar"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:574
+msgid "&Icon size:"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:575
+msgid "Show &text under icons:"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:576
msgid "Add an email address to which to send books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:555
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:577
msgid "&Add email"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:556
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:578
msgid "Make &default"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:557
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:579
msgid "&Remove email"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:558
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:580
msgid "calibre can send your books to you (or your reader) by email. Emails will be automatically sent for downloaded news to all email addresses that have Auto-send checked."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:559
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:581
msgid "&Maximum number of waiting worker processes (needs restart):"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:560
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:582
msgid "&Check database integrity"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:561
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:583
msgid "&Install command line tools"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:562
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:584
msgid "Open calibre &configuration directory"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:563
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:585
msgid "Limit the max. simultaneous jobs to the available CPU &cores"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:564
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:586
msgid "Debug &device detection"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:565
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:587
msgid "calibre contains a network server that allows you to access your book collection using a browser from anywhere in the world. Any changes to the settings will only take effect after a server restart."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:566
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:588
msgid "Server &port:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:567
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:589
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:58
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:212
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:117
msgid "&Username:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:568
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:590
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:59
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:213
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:119
msgid "&Password:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:569
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:591
msgid "If you leave the password blank, anyone will be able to access your book collection using the web interface."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:570
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:592
msgid "The maximum size (widthxheight) for displayed covers. Larger covers are resized. "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:571
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:593
msgid "Max. &cover size:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:572
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:594
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/password_ui.py:60
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:214
msgid "&Show password"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:573
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:595
msgid "Max. &OPDS items per query:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:574
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:596
msgid "Max. OPDS &ungrouped items:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:575
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:597
msgid "&Start Server"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:576
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:598
msgid "St&op Server"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:577
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:599
msgid "&Test Server"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:578
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:600
msgid "Run server &automatically on startup"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:579
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:601
msgid "View &server logs"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:580
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:602
#: /home/kovid/work/calibre/src/calibre/gui2/wizard/stanza_ui.py:46
msgid ""
"
Remember to leave calibre running as the server only runs as long as calibre is running.\n"
"
Stanza should see your calibre collection automatically. If not, try adding the URL http://myhostname:8080 as a new catalog in the Stanza reader on your iPhone. Here myhostname should be the fully qualified hostname or the IP address of the computer calibre is running on."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:582
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:604
msgid "Here you can customize the behavior of Calibre by controlling what plugins it uses."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:583
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:605
msgid "Enable/&Disable plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:584
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:606
msgid "&Customize plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:585
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:607
msgid "&Remove plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:586
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:608
msgid "Add new plugin"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:587
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:609
msgid "Plugin &file:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:589
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/config/config_ui.py:611
msgid "&Add"
msgstr ""
@@ -5543,7 +5552,7 @@ msgid "Show job &details"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/jobs_ui.py:47
-msgid "Stop &all jobs"
+msgid "Stop &all non device jobs"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:24
@@ -5885,48 +5894,48 @@ msgstr ""
msgid "Change the contents of the saved search"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:119
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:120
msgid "Need username and password"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:120
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:121
msgid "You must provide a username and/or password to use this news source."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:171
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:172
msgid "Created by: "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:178
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:179
msgid "Last downloaded: never"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:193
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:194
msgid "%d days, %d hours and %d minutes ago"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:195
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:196
msgid "Last downloaded"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:219
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:220
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler_ui.py:192
msgid "Schedule news download"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:222
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:223
msgid "Add a custom news source"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:227
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:228
msgid "Download all scheduled new sources"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:327
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:328
msgid "No internet connection"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:328
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:329
msgid "Cannot download news as no internet connection is active"
msgstr ""
@@ -6110,21 +6119,21 @@ msgstr ""
msgid "Choose formats"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:46
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:45
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:80
msgid "Authors"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:46
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:45
#: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:111
msgid "Publishers"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:110
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:109
msgid " (not on any book)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:162
+#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_categories.py:161
msgid "The current tag category will be permanently deleted. Are you sure?"
msgstr ""
@@ -6226,12 +6235,12 @@ msgid "%s (was %s)"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_list_editor.py:70
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:501
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:500
msgid "Item is blank"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tag_list_editor.py:71
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:502
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:501
msgid "An item cannot be set to nothing. Delete it instead."
msgstr ""
@@ -6536,42 +6545,42 @@ msgstr ""
msgid "Similar books..."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:65
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:66
msgid "Add books to library"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:67
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:74
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:81
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:68
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:76
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:84
msgid "Manage collections"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:161
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:165
msgid "Cover Browser"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:179
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:183
msgid "Tag Browser"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:200
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:204
msgid "version"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:201
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:205
msgid "created by Kovid Goyal"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:219
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:223
msgid "Connected "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:228
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:232
msgid "Update found"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:272
-#: /home/kovid/work/calibre/src/calibre/gui2/init.py:281
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:276
+#: /home/kovid/work/calibre/src/calibre/gui2/init.py:285
msgid "Book Details"
msgstr ""
@@ -6660,7 +6669,7 @@ msgid "Show books in the main memory of the device"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:89
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:551
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:567
msgid "Card A"
msgstr ""
@@ -6669,7 +6678,7 @@ msgid "Show books in storage card A"
msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:91
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:553
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:569
msgid "Card B"
msgstr ""
@@ -6709,225 +6718,251 @@ msgstr ""
msgid "Delete current saved search"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:290
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:379
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:302
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:451
#: /home/kovid/work/calibre/src/calibre/library/server/opds.py:108
msgid "%d books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:375
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:334
+msgid "Connect to folder"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:340
+msgid "Connect to iTunes"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:354
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:360
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:362
+msgid "Email to"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:362
+msgid " and delete from library"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:373
+msgid "Setup email based sharing of books"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:446
msgid "A"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:375
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:446
msgid "Add books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:376
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:447
msgid "E"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:376
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:447
msgid "Edit metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:377
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:448
msgid "C"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:377
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:448
msgid "Convert books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:378
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:449
msgid "V"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:378
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:475
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:449
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:551
msgid "View"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:380
-msgid "Choose calibre library to work with"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:381
-msgid "F"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:381
-msgid "Fetch news"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:382
-msgid "S"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:382
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:458
-msgid "Save to disk"
-msgstr ""
-
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:383
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:450
msgid "Send to device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:384
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:452
+msgid "Choose calibre library to work with"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:453
+msgid "F"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:453
+msgid "Fetch news"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:454
+msgid "S"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:454
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:534
+msgid "Save to disk"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:455
+msgid "Connect/share"
+msgstr ""
+
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:456
msgid "Del"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:384
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:456
msgid "Remove books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:385
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:457
msgid "Browse the calibre User Manual"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:385
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:457
msgid "F1"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:385
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:457
msgid "Help"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:386
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:458
msgid "Ctrl+P"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:388
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:460
msgid "M"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:388
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:460
msgid "Merge book records"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:389
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:461
msgid "Open containing folder"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:391
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:463
msgid "Show book details"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:393
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:465
msgid "Books by same author"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:395
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:467
msgid "Books in this series"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:397
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:469
msgid "Books by this publisher"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:399
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:471
msgid "Books with the same tags"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:408
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:484
msgid "Edit metadata individually"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:411
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:487
msgid "Edit metadata in bulk"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:414
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:490
msgid "Download metadata and covers"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:417
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:493
msgid "Download only metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:419
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:495
msgid "Download only covers"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:422
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:498
msgid "Download only social metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:428
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:504
msgid "Merge into first selected book - delete others"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:431
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:507
msgid "Merge into first selected book - keep others"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:439
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:515
msgid "Add books from a single directory"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:441
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:517
msgid "Add books from directories, including sub-directories (One book per directory, assumes every ebook file is the same book in a different format)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:445
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:521
msgid "Add books from directories, including sub directories (Multiple books per directory, assumes every ebook file is a different book)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:448
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:524
msgid "Add Empty book. (Book entry with no formats)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:460
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:536
msgid "Save to disk in a single directory"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:462
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:538
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:374
msgid "Save only %s format to disk"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:466
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:542
#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:377
msgid "Save only %s format to disk in a single directory"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:476
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:552
msgid "View specific format"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:482
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:558
msgid "Remove selected books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:484
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:560
msgid "Remove files of a specific format from selected books.."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:487
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:563
msgid "Remove all formats from selected books, except..."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:490
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:566
msgid "Remove covers from selected books"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:493
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:569
msgid "Remove matching books from device"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:510
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:586
msgid "Convert individually"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:512
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:588
msgid "Bulk convert"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:516
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:592
msgid "Create catalog of books in your calibre library"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:524
+#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:600
msgid "Run welcome wizard"
msgstr ""
@@ -6958,7 +6993,7 @@ msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:679
#: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1182
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:399
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:398
msgid "The lookup/search name is \"{0}\""
msgstr ""
@@ -7018,11 +7053,11 @@ msgstr ""
msgid "Restore default layout"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:525
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:560
msgid "Not allowed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:526
+#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:561
msgid "Dropping onto a device is not supported. First add the book to the calibre library."
msgstr ""
@@ -7236,25 +7271,25 @@ msgstr ""
msgid "No matches found for this book"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:93
-#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:262
+#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:94
+#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:254
#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:554
msgid "Search"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:315
+#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:306
msgid "The selected search will be permanently deleted. Are you sure?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:357
+#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:348
msgid "Search (For Advanced Search click the button to the left)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:398
+#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:390
msgid "Saved Searches"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:406
+#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:398
msgid "Choose saved search or enter name for new saved search"
msgstr ""
@@ -7335,86 +7370,86 @@ msgstr ""
msgid "&Alternate shortcut:"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:191
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:190
msgid "Rename '%s'"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:195
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:194
msgid "Edit sort for '%s'"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:200
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:199
msgid "Hide category %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:203
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:202
msgid "Show category"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:207
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:206
msgid "Show all categories"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:214
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:218
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:213
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:217
msgid "Manage %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:221
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:220
msgid "Manage Saved Searches"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:228
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:232
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:227
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:231
msgid "Manage User Categories"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:433
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:275
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:432
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:291
msgid "Searches"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:511
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:510
msgid "Duplicate search name"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:512
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:511
msgid "The saved search name %s is already used."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:768
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:767
msgid "Sort by name"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:768
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:767
msgid "Sort by popularity"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:769
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:768
msgid "Sort by average rating"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:772
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:771
msgid "Set the sort order for entries in the Tag Browser"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:778
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:777
msgid "Match all"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:778
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:777
msgid "Match any"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:783
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:782
msgid "When selecting multiple entries in the Tag Browser match any or all of them"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:787
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:786
msgid "Manage &user categories"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:790
+#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:789
msgid "Add your own categories to the Tag Browser"
msgstr ""
@@ -7486,51 +7521,51 @@ msgstr ""
msgid "Cannot configure before calibre is restarted."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:402
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:404
msgid "No detailed info available"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:403
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:405
msgid "No detailed information is available for books on the device."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:455
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:483
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:457
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:485
msgid "Conversion Error"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:456
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:458
msgid "
Could not convert: %s
It is a DRMed book. You must first remove the DRM using third party tools."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:469
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:471
msgid "Recipe Disabled"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:484
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:486
msgid "Failed"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:524
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:526
msgid "is the result of the efforts of many volunteers from all over the world. If you find it useful, please consider donating to support its development. Your donation helps keep calibre development going."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:550
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:552
msgid "There are active jobs. Are you sure you want to quit?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:553
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:555
msgid ""
" is communicating with the device! \n"
" Quitting may cause corruption on the device. \n"
" Are you sure you want to quit?"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:557
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:559
msgid "WARNING: Active jobs"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:610
+#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:612
msgid "will keep running in the system tray. To close it, choose Quit in the context menu of the system tray."
msgstr ""
@@ -8772,35 +8807,35 @@ msgid ""
"For help on an individual command: %%prog command --help\n"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:70
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:71
msgid "%sAverage rating is %3.1f"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:549
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:565
msgid "Main"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1774
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1790
msgid "
Migrating old database to ebook library in %s
"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1803
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1819
msgid "Copying %s"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1820
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1836
msgid "Compacting database"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1913
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1929
msgid "Checking SQL integrity..."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1954
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1970
msgid "Checking for missing files."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/library/database2.py:1976
+#: /home/kovid/work/calibre/src/calibre/library/database2.py:1992
msgid "Checked id"
msgstr ""
@@ -8991,76 +9026,76 @@ msgstr ""
msgid "Newest"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:48
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:49
msgid ""
"%sUsage%s: %s\n"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:92
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:93
msgid "Created by "
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:93
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:94
msgid "Whenever you pass arguments to %prog that have spaces in them, enclose the arguments in quotation marks."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:670
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:678
msgid "Path to the database in which books are stored"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:672
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:680
msgid "Pattern to guess metadata from filenames"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:674
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:682
msgid "Access key for isbndb.com"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:676
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:684
msgid "Default timeout for network operations (seconds)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:678
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:686
msgid "Path to directory in which your library of books is stored"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:680
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:688
msgid "The language in which to display the user interface"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:682
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:690
msgid "The default output format for ebook conversions."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:686
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:694
msgid "Ordered list of formats to prefer for input."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:688
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:696
msgid "Read metadata from files"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:690
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:698
msgid "The priority of worker processes"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:692
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:700
msgid "Swap author first and last names when reading metadata"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:694
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:702
msgid "Add new formats to existing book records"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:699
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:707
msgid "List of named saved searches"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:700
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:708
msgid "User-created tag browser categories"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/config.py:702
+#: /home/kovid/work/calibre/src/calibre/utils/config.py:710
msgid "How and when calibre updates metadata on the device."
msgstr ""
@@ -9080,91 +9115,91 @@ msgstr ""
msgid "Working..."
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:93
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:95
msgid "Brazilian Portuguese"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:94
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:96
msgid "English (UK)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:95
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:97
msgid "Simplified Chinese"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:96
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:98
msgid "Chinese (HK)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:97
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:99
msgid "Traditional Chinese"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:98
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:100
msgid "English"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:99
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:101
msgid "English (Australia)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:100
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:102
msgid "English (New Zealand)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:101
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
msgid "English (Canada)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:102
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
msgid "English (India)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:103
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
msgid "English (Thailand)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:104
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
msgid "English (Cyprus)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:105
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
msgid "English (Pakistan)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:106
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:108
msgid "English (Israel)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:107
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:109
msgid "English (Singapore)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:108
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:110
msgid "English (Yemen)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:109
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:111
msgid "English (Ireland)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:110
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:112
msgid "English (China)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:111
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:113
msgid "Spanish (Paraguay)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:112
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:114
msgid "German (AT)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:113
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:115
msgid "Dutch (NL)"
msgstr ""
-#: /home/kovid/work/calibre/src/calibre/utils/localization.py:114
+#: /home/kovid/work/calibre/src/calibre/utils/localization.py:116
msgid "Dutch (BE)"
msgstr ""
diff --git a/src/calibre/utils/PythonMagickWand.py b/src/calibre/utils/PythonMagickWand.py
index cf9b5d167f..69a17d2297 100644
--- a/src/calibre/utils/PythonMagickWand.py
+++ b/src/calibre/utils/PythonMagickWand.py
@@ -66,7 +66,7 @@ and save it to a new file.
"""
-import ctypes, sys, os
+import ctypes, sys, os, glob
from ctypes import util
iswindows = 'win32' in sys.platform or 'win64' in sys.platform
isosx = 'darwin' in sys.platform
@@ -85,7 +85,8 @@ elif iswindows:
_lib = flib if isfrozen else 'CORE_RL_wand_'
else:
if isfrozen:
- _lib = os.path.join(sys.frozen_path, 'libMagickWand.so.2')
+ _lib = glob.glob(os.path.join(sys.frozen_path,
+ 'libMagickWand.so.*'))[-1]
else:
_lib = util.find_library('MagickWand')
if _lib is None:
diff --git a/src/calibre/utils/config.py b/src/calibre/utils/config.py
index 5c4bd55644..91b1dc70cc 100644
--- a/src/calibre/utils/config.py
+++ b/src/calibre/utils/config.py
@@ -6,15 +6,16 @@ __docformat__ = 'restructuredtext en'
'''
Manage application-wide preferences.
'''
-import os, re, cPickle, textwrap, traceback, plistlib, json, base64
+import os, re, cPickle, textwrap, traceback, plistlib, json, base64, datetime
from copy import deepcopy
from functools import partial
from optparse import OptionParser as _OptionParser
from optparse import IndentedHelpFormatter
+from collections import defaultdict
+
from calibre.constants import terminal_controller, iswindows, isosx, \
__appname__, __version__, __author__, plugins
from calibre.utils.lock import LockError, ExclusiveFile
-from collections import defaultdict
if os.environ.has_key('CALIBRE_CONFIG_DIRECTORY'):
config_dir = os.path.abspath(os.environ['CALIBRE_CONFIG_DIRECTORY'])
@@ -632,27 +633,34 @@ class XMLConfig(dict):
f.truncate()
f.write(raw)
+def to_json(obj):
+ if isinstance(obj, bytearray):
+ return {'__class__': 'bytearray',
+ '__value__': base64.standard_b64encode(bytes(obj))}
+ if isinstance(obj, datetime.datetime):
+ from calibre.utils.date import isoformat
+ return {'__class__': 'datetime.datetime',
+ '__value__': isoformat(obj, as_utc=True)}
+ raise TypeError(repr(obj) + ' is not JSON serializable')
+
+def from_json(obj):
+ if '__class__' in obj:
+ if obj['__class__'] == 'bytearray':
+ return bytearray(base64.standard_b64decode(obj['__value__']))
+ if obj['__class__'] == 'datetime.datetime':
+ from calibre.utils.date import parse_date
+ return parse_date(obj['__value__'], assume_utc=True)
+ return obj
+
class JSONConfig(XMLConfig):
EXTENSION = '.json'
- def to_json(self, obj):
- if isinstance(obj, bytearray):
- return {'__class__': 'bytearray',
- '__value__': base64.standard_b64encode(bytes(obj))}
- raise TypeError(repr(obj) + ' is not JSON serializable')
-
- def from_json(self, obj):
- if '__class__' in obj:
- if obj['__class__'] == 'bytearray':
- return bytearray(base64.standard_b64decode(obj['__value__']))
- return obj
-
def raw_to_object(self, raw):
- return json.loads(raw.decode('utf-8'), object_hook=self.from_json)
+ return json.loads(raw.decode('utf-8'), object_hook=from_json)
def to_raw(self):
- return json.dumps(self, indent=2, default=self.to_json)
+ return json.dumps(self, indent=2, default=to_json)
def __getitem__(self, key):
return dict.__getitem__(self, key)
diff --git a/src/calibre/utils/localization.py b/src/calibre/utils/localization.py
index e60a3233c6..94f3923acf 100644
--- a/src/calibre/utils/localization.py
+++ b/src/calibre/utils/localization.py
@@ -42,6 +42,8 @@ def get_lang():
lang = match.group()
if lang == 'zh':
lang = 'zh_CN'
+ if lang is None:
+ lang = 'en'
return lang
def messages_path(lang):
diff --git a/src/calibre/utils/search_query_parser.py b/src/calibre/utils/search_query_parser.py
index d6bf932b76..f01e40549a 100644
--- a/src/calibre/utils/search_query_parser.py
+++ b/src/calibre/utils/search_query_parser.py
@@ -21,7 +21,6 @@ import sys, string, operator
from calibre.utils.pyparsing import Keyword, Group, Forward, CharsNotIn, Suppress, \
OneOrMore, oneOf, CaselessLiteral, Optional, NoMatch, ParseException
from calibre.constants import preferred_encoding
-from calibre.utils.config import prefs
'''
This class manages access to the preference holding the saved search queries.
@@ -32,9 +31,13 @@ class SavedSearchQueries(object):
queries = {}
opt_name = ''
- def __init__(self, _opt_name):
+ def __init__(self, db, _opt_name):
self.opt_name = _opt_name;
- self.queries = prefs[self.opt_name]
+ self.db = db
+ if db is not None:
+ self.queries = db.prefs.get(self.opt_name, {})
+ else:
+ self.queries = {}
def force_unicode(self, x):
if not isinstance(x, unicode):
@@ -43,20 +46,20 @@ class SavedSearchQueries(object):
def add(self, name, value):
self.queries[self.force_unicode(name)] = self.force_unicode(value).strip()
- prefs[self.opt_name] = self.queries
+ self.db.prefs[self.opt_name] = self.queries
def lookup(self, name):
return self.queries.get(self.force_unicode(name), None)
def delete(self, name):
self.queries.pop(self.force_unicode(name), False)
- prefs[self.opt_name] = self.queries
+ self.db.prefs[self.opt_name] = self.queries
def rename(self, old_name, new_name):
self.queries[self.force_unicode(new_name)] = \
self.queries.get(self.force_unicode(old_name), None)
self.queries.pop(self.force_unicode(old_name), False)
- prefs[self.opt_name] = self.queries
+ self.db.prefs[self.opt_name] = self.queries
def names(self):
return sorted(self.queries.keys(),
@@ -66,8 +69,15 @@ class SavedSearchQueries(object):
Create a global instance of the saved searches. It is global so that the searches
are common across all instances of the parser (devices, library, etc).
'''
-saved_searches = SavedSearchQueries('saved_searches')
+ss = SavedSearchQueries(None, None)
+def set_saved_searches(db, opt_name):
+ global ss
+ ss = SavedSearchQueries(db, opt_name)
+
+def saved_searches():
+ global ss
+ return ss
class SearchQueryParser(object):
'''
@@ -209,7 +219,7 @@ class SearchQueryParser(object):
raise ParseException(query, len(query), 'undefined saved search', self)
if self.recurse_level > 5:
self.searches_seen.add(query)
- return self._parse(saved_searches.lookup(query))
+ return self._parse(saved_searches().lookup(query))
except: # convert all exceptions (e.g., missing key) to a parse error
raise ParseException(query, len(query), 'undefined saved search', self)
return self.get_matches(location, query)