diff --git a/Changelog.yaml b/Changelog.yaml index c750c4a5f3..24ec402a49 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -19,6 +19,45 @@ # new recipes: # - title: +- version: 0.7.56 + date: 2011-04-17 + + new features: + - title: "This is primarily a bug fix release that fixes a bug in 0.7.55 that caused calibre to rescan the files on the device every time the device is connected. If you updated to 0.7.55 it is highly recommended you update to 0.7.56" + + - title: "Device driver for Coby Kyros" + + - title: "Remove the quick access to search options from next to the search bar, as we now have a separate search highlights toggle button" + + - title: "MOBI Output: Ensure that MOBI files always have 8KB worth of null bytes at the end of record 0. This appears to be necessary for Amazon to be able to add DRM to calibre generated MOBI files sent to their publishing service." + + - title: "Add a tool to inspect MOBI files. To use: calibre-debug -m file.mobi" + + bug fixes: + - title: "Fixed regression taht caused calibre to rescan files on the device on every reconnect" + + - title: "Fix donate button causing the toolbar to be too large on OS X" + + - title: "MOBI Input: Fix detection of Table of Contents for MOBI files that have a page break between the location designated as the Table of Contents and the actual table of contents." + tickets: [763504] + + - title: "Comic Input: Fix handling of some CBZ files that have wrongly encoded non ASCII filenames on windows." + tickets: [763280] + + - title: "PML Input: Fix multi-line chapter title causing a spurious page break" + tickets: [763238] + + - title: "EPUB Input: Speed up processing of files with very large manifest/spines" + + - title: "Fix regression that broke cover:False searches in 0.7.55" + + improved recipes: + - Suedduetsche Zeitung + - Irish Times + - Big Oven + - NSPM + + - version: 0.7.55 date: 2011-04-15 diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 820a38aeff..6f26a63940 100644 --- a/src/calibre/constants.py +++ b/src/calibre/constants.py @@ -2,7 +2,7 @@ __license__ = 'GPL v3' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __docformat__ = 'restructuredtext en' __appname__ = 'calibre' -__version__ = '0.7.55' +__version__ = '0.7.56' __author__ = "Kovid Goyal " import re, importlib diff --git a/src/calibre/devices/apple/driver.py b/src/calibre/devices/apple/driver.py index 2cc478603a..d7811f0a22 100644 --- a/src/calibre/devices/apple/driver.py +++ b/src/calibre/devices/apple/driver.py @@ -201,8 +201,9 @@ class ITUNES(DriverBase): # 0x1294 iPhone 3GS # 0x1297 iPhone 4 # 0x129a iPad + # 0x12a2 iPad2 VENDOR_ID = [0x05ac] - PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a] + PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x12a2] BCD = [0x01] # Plugboard ID @@ -421,7 +422,7 @@ class ITUNES(DriverBase): cached_books[this_book.path] = { 'title':book.name(), - 'author':[book.artist()], + 'author':book.artist().split(' & '), 'lib_book':library_books[this_book.path] if this_book.path in library_books else None, 'dev_book':book, 'uuid': book.composer() @@ -459,7 +460,7 @@ class ITUNES(DriverBase): cached_books[this_book.path] = { 'title':book.Name, - 'author':book.Artist, + 'author':book.artist().split(' & '), 'lib_book':library_books[this_book.path] if this_book.path in library_books else None, 'uuid': book.Composer, 'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub' @@ -1021,7 +1022,9 @@ class ITUNES(DriverBase): if isosx: for (i,file) in enumerate(files): format = file.rpartition('.')[2].lower() - path = self.path_template % (metadata[i].title, metadata[i].author[0],format) + path = self.path_template % (metadata[i].title, + authors_to_string(metadata[i].authors), + format) self._remove_existing_copy(path, metadata[i]) fpath = self._get_fpath(file, metadata[i], format, update_md=True) db_added, lb_added = self._add_new_copy(fpath, metadata[i]) @@ -1034,9 +1037,11 @@ class ITUNES(DriverBase): if DEBUG: self.log.info("ITUNES.upload_books()") self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" % - ( metadata[i].title, metadata[i].author, metadata[i].uuid)) + (metadata[i].title, + authors_to_string(metadata[i].authors), + metadata[i].uuid)) self.cached_books[this_book.path] = { - 'author': metadata[i].author, + 'author': authors_to_string(metadata[i].authors), 'dev_book': db_added, 'format': format, 'lib_book': lb_added, @@ -1055,7 +1060,9 @@ class ITUNES(DriverBase): for (i,file) in enumerate(files): format = file.rpartition('.')[2].lower() - path = self.path_template % (metadata[i].title, metadata[i].author[0],format) + path = self.path_template % (metadata[i].title, + authors_to_string(metadata[i].authors), + format) self._remove_existing_copy(path, metadata[i]) fpath = self._get_fpath(file, metadata[i],format, update_md=True) db_added, lb_added = self._add_new_copy(fpath, metadata[i]) @@ -1075,9 +1082,11 @@ class ITUNES(DriverBase): if DEBUG: self.log.info("ITUNES.upload_books()") self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_books" % - ( metadata[i].title, metadata[i].author, metadata[i].uuid)) + (metadata[i].title, + authors_to_string(metadata[i].authors), + metadata[i].uuid)) self.cached_books[this_book.path] = { - 'author': metadata[i].author[0], + 'author': authors_to_string(metadata[i].authors), 'dev_book': db_added, 'format': format, 'lib_book': lb_added, @@ -1190,7 +1199,7 @@ class ITUNES(DriverBase): base_fn = base_fn.rpartition('.')[0] db_added = self._find_device_book( { 'title': base_fn if format == 'pdf' else metadata.title, - 'author': metadata.authors[0], + 'author': authors_to_string(metadata.authors), 'uuid': metadata.uuid, 'format': format}) return db_added @@ -1255,7 +1264,7 @@ class ITUNES(DriverBase): base_fn = base_fn.rpartition('.')[0] added = self._find_library_book( { 'title': base_fn if format == 'pdf' else metadata.title, - 'author': metadata.author[0], + 'author': authors_to_string(metadata.authors), 'uuid': metadata.uuid, 'format': format}) return added @@ -1314,7 +1323,7 @@ class ITUNES(DriverBase): with open(metadata.cover,'r+b') as cd: cover_data = cd.read() except: - self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0])) + self.problem_titles.append("'%s' by %s" % (metadata.title, authors_to_string(metadata.authors))) self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title)) import traceback @@ -1389,7 +1398,7 @@ class ITUNES(DriverBase): thumb_path = path.rpartition('.')[0] + '.jpg' zfw.writestr(thumb_path, thumb) except: - self.problem_titles.append("'%s' by %s" % (metadata.title, metadata.author[0])) + self.problem_titles.append("'%s' by %s" % (metadata.title, authors_to_string(metadata.authors))) self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title)) finally: try: @@ -1407,7 +1416,7 @@ class ITUNES(DriverBase): if DEBUG: self.log.info(" ITUNES._create_new_book()") - this_book = Book(metadata.title, authors_to_string(metadata.author)) + this_book = Book(metadata.title, authors_to_string(metadata.authors)) this_book.datetime = time.gmtime() this_book.db_id = None this_book.device_collections = [] @@ -2451,7 +2460,7 @@ class ITUNES(DriverBase): for book in self.cached_books: if self.cached_books[book]['uuid'] == metadata.uuid or \ (self.cached_books[book]['title'] == metadata.title and \ - self.cached_books[book]['author'] == metadata.authors[0]): + self.cached_books[book]['author'] == authors_to_string(metadata.authors)): self.update_list.append(self.cached_books[book]) self._remove_from_device(self.cached_books[book]) if DEBUG: @@ -2470,7 +2479,7 @@ class ITUNES(DriverBase): for book in self.cached_books: if self.cached_books[book]['uuid'] == metadata.uuid or \ (self.cached_books[book]['title'] == metadata.title and \ - self.cached_books[book]['author'] == metadata.authors[0]): + self.cached_books[book]['author'] == authors_to_string(metadata.authors)): self.update_list.append(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book]) if DEBUG: @@ -2939,13 +2948,13 @@ class ITUNES(DriverBase): def _xform_metadata_via_plugboard(self, book, format): ''' Transform book metadata from plugboard templates ''' if DEBUG: - self.log.info(" ITUNES._xform_metadata_via_plugboard()") + self.log.info(" ITUNES._xform_metadata_via_plugboard()") if self.plugboard_func: pb = self.plugboard_func(self.DEVICE_PLUGBOARD_NAME, format, self.plugboards) newmi = book.deepcopy_metadata() newmi.template_to_attribute(book, pb) - if DEBUG: + if pb is not None and DEBUG: self.log.info(" transforming %s using %s:" % (format, pb)) self.log.info(" title: %s %s" % (book.title, ">>> %s" % newmi.title if book.title != newmi.title else '')) @@ -3062,7 +3071,7 @@ class ITUNES_ASYNC(ITUNES): cached_books[this_book.path] = { 'title':library_books[book].name(), - 'author':[library_books[book].artist()], + 'author':library_books[book].artist().split(' & '), 'lib_book':library_books[book], 'dev_book':None, 'uuid': library_books[book].composer(), @@ -3102,7 +3111,7 @@ class ITUNES_ASYNC(ITUNES): cached_books[this_book.path] = { 'title':library_books[book].Name, - 'author':library_books[book].Artist, + 'author':library_books[book].Artist.split(' & '), 'lib_book':library_books[book], 'uuid': library_books[book].Composer, 'format': format @@ -3288,7 +3297,7 @@ class Book(Metadata): See ebooks.metadata.book.base ''' def __init__(self,title,author): - Metadata.__init__(self, title, authors=[author]) + Metadata.__init__(self, title, authors=author.split(' & ')) @property def title_sorter(self): diff --git a/src/calibre/ebooks/chm/input.py b/src/calibre/ebooks/chm/input.py index 61160e8dac..fce07c2359 100644 --- a/src/calibre/ebooks/chm/input.py +++ b/src/calibre/ebooks/chm/input.py @@ -52,6 +52,9 @@ class CHMInput(InputFormatPlugin): metadata = get_metadata_from_reader(self._chm_reader) self._chm_reader.CloseCHM() + #print tdir + #from calibre import ipython + #ipython() odi = options.debug_pipeline options.debug_pipeline = None diff --git a/src/calibre/ebooks/chm/reader.py b/src/calibre/ebooks/chm/reader.py index 34d228ef3b..7c9a6bf48a 100644 --- a/src/calibre/ebooks/chm/reader.py +++ b/src/calibre/ebooks/chm/reader.py @@ -147,7 +147,8 @@ class CHMReader(CHMFile): if self.hhc_path == '.hhc' and self.hhc_path not in files: from calibre import walk for x in walk(output_dir): - if os.path.basename(x).lower() in ('index.htm', 'index.html'): + if os.path.basename(x).lower() in ('index.htm', 'index.html', + 'contents.htm', 'contents.html'): self.hhc_path = os.path.relpath(x, output_dir) break diff --git a/src/calibre/ebooks/comic/input.py b/src/calibre/ebooks/comic/input.py index 56fa123249..56f7683c57 100755 --- a/src/calibre/ebooks/comic/input.py +++ b/src/calibre/ebooks/comic/input.py @@ -12,6 +12,7 @@ from Queue import Empty from calibre.customize.conversion import InputFormatPlugin, OptionRecommendation from calibre import extract, CurrentDir, prints +from calibre.constants import filesystem_encoding from calibre.ptempfile import PersistentTemporaryDirectory from calibre.utils.ipc.server import Server from calibre.utils.ipc.job import ParallelJob @@ -21,6 +22,10 @@ def extract_comic(path_to_comic_file): Un-archive the comic file. ''' tdir = PersistentTemporaryDirectory(suffix='_comic_extract') + if not isinstance(tdir, unicode): + # Needed in case the zip file has wrongly encoded unicode file/dir + # names + tdir = tdir.decode(filesystem_encoding) extract(path_to_comic_file, tdir) return tdir diff --git a/src/calibre/ebooks/compression/palmdoc.c b/src/calibre/ebooks/compression/palmdoc.c index 4d913dfd2b..6b07bb9cd5 100644 --- a/src/calibre/ebooks/compression/palmdoc.c +++ b/src/calibre/ebooks/compression/palmdoc.c @@ -17,6 +17,7 @@ #define BUFFER 6000 #define MIN(x, y) ( ((x) < (y)) ? (x) : (y) ) +#define MAX(x, y) ( ((x) > (y)) ? (x) : (y) ) typedef unsigned short int Byte; typedef struct { @@ -53,7 +54,7 @@ cpalmdoc_decompress(PyObject *self, PyObject *args) { // Map chars to bytes for (j = 0; j < input_len; j++) input[j] = (_input[j] < 0) ? _input[j]+256 : _input[j]; - output = (char *)PyMem_Malloc(sizeof(char)*BUFFER); + output = (char *)PyMem_Malloc(sizeof(char)*(MAX(BUFFER, 5*input_len))); if (output == NULL) return PyErr_NoMemory(); while (i < input_len) { diff --git a/src/calibre/ebooks/metadata/sources/base.py b/src/calibre/ebooks/metadata/sources/base.py index f322fcdb56..c9639bf531 100644 --- a/src/calibre/ebooks/metadata/sources/base.py +++ b/src/calibre/ebooks/metadata/sources/base.py @@ -294,8 +294,24 @@ class Source(Plugin): Excludes connectives and punctuation. ''' if title: - pat = re.compile(r'''[-,:;+!@#$%^&*(){}.`~"'\s\[\]/]''') - title = pat.sub(' ', title) + title_patterns = [(re.compile(pat, re.IGNORECASE), repl) for pat, repl in + [ + # Remove things like: (2010) (Omnibus) etc. + (r'(?i)[({\[](\d{4}|omnibus|anthology|hardcover|paperback|mass\s*market|edition|ed\.)[\])}]', ''), + # Remove any strings that contain the substring edition inside + # parentheses + (r'(?i)[({\[].*?(edition|ed.).*?[\]})]', ''), + # Remove commas used a separators in numbers + (r'(\d+),(\d+)', r'\1\2'), + # Remove hyphens only if they have whitespace before them + (r'(\s-)', ' '), + # Remove single quotes + (r"'", ''), + # Replace other special chars with a space + (r'''[:,;+!@#$%^&*(){}.`~"\s\[\]/]''', ' ') + ]] + for pat, repl in title_patterns: + title = pat.sub(repl, title) tokens = title.split() for token in tokens: token = token.strip() diff --git a/src/calibre/ebooks/metadata/sources/identify.py b/src/calibre/ebooks/metadata/sources/identify.py index 87c1e9a644..1fb1a74679 100644 --- a/src/calibre/ebooks/metadata/sources/identify.py +++ b/src/calibre/ebooks/metadata/sources/identify.py @@ -114,8 +114,12 @@ class ISBNMerge(object): return self.results - def merge_metadata_results(self): - ' Merge results with identical title and authors ' + def merge_metadata_results(self, merge_on_identifiers=False): + ''' + Merge results with identical title and authors or an identical + identifier + ''' + # First title/author groups = {} for result in self.results: title = lower(result.title if result.title else '') @@ -135,6 +139,44 @@ class ISBNMerge(object): result = rgroup[0] self.results.append(result) + if merge_on_identifiers: + # Now identifiers + groups, empty = {}, [] + for result in self.results: + key = set() + for typ, val in result.identifiers.iteritems(): + if typ and val: + key.add((typ, val)) + if key: + key = frozenset(key) + match = None + for candidate in list(groups): + if candidate.intersection(key): + # We have at least one identifier in common + match = candidate.union(key) + results = groups.pop(candidate) + results.append(result) + groups[match] = results + break + if match is None: + groups[key] = [result] + else: + empty.append(result) + + if len(groups) != len(self.results): + self.results = [] + for rgroup in groups.itervalues(): + rel = [r.average_source_relevance for r in rgroup] + if len(rgroup) > 1: + result = self.merge(rgroup, None, do_asr=False) + result.average_source_relevance = sum(rel)/len(rel) + elif rgroup: + result = rgroup[0] + self.results.append(result) + + if empty: + self.results.extend(empty) + self.results.sort(key=attrgetter('average_source_relevance')) def merge_isbn_results(self): @@ -408,7 +450,7 @@ if __name__ == '__main__': # tests {{{ {'identifiers':{'isbn': '9780307459671'}, 'title':'Invisible Gorilla', 'authors':['Christopher Chabris']}, [title_test('The Invisible Gorilla', - exact=True), authors_test(['Christopher F. Chabris', 'Daniel Simons'])] + exact=True), authors_test(['Christopher Chabris', 'Daniel Simons'])] ), diff --git a/src/calibre/ebooks/metadata/sources/test.py b/src/calibre/ebooks/metadata/sources/test.py index 284a7ba45e..e280b0c038 100644 --- a/src/calibre/ebooks/metadata/sources/test.py +++ b/src/calibre/ebooks/metadata/sources/test.py @@ -15,14 +15,17 @@ from calibre.customize.ui import metadata_plugins from calibre import prints, sanitize_file_name2 from calibre.ebooks.metadata import check_isbn from calibre.ebooks.metadata.sources.base import (create_log, - get_cached_cover_urls) + get_cached_cover_urls, msprefs) def isbn_test(isbn): isbn_ = check_isbn(isbn) def test(mi): misbn = check_isbn(mi.isbn) - return misbn and misbn == isbn_ + if misbn and misbn == isbn_: + return True + prints('ISBN test failed. Expected: \'%s\' found \'%s\''%(isbn_, misbn)) + return False return test @@ -32,8 +35,11 @@ def title_test(title, exact=False): def test(mi): mt = mi.title.lower() - return (exact and mt == title) or \ - (not exact and title in mt) + if (exact and mt == title) or \ + (not exact and title in mt): + return True + prints('Title test failed. Expected: \'%s\' found \'%s\''%(title, mt)) + return False return test @@ -42,7 +48,22 @@ def authors_test(authors): def test(mi): au = set([x.lower() for x in mi.authors]) - return au == authors + if msprefs['swap_author_names']: + def revert_to_fn_ln(a): + if ',' not in a: + return a + parts = a.split(',', 1) + t = parts[-1] + parts = parts[:-1] + parts.insert(0, t) + return ' '.join(parts) + + au = set([revert_to_fn_ln(x) for x in au]) + + if au == authors: + return True + prints('Author test failed. Expected: \'%s\' found \'%s\''%(authors, au)) + return False return test diff --git a/src/calibre/ebooks/mobi/reader.py b/src/calibre/ebooks/mobi/reader.py index 8877ecdd0b..a65649dfd2 100644 --- a/src/calibre/ebooks/mobi/reader.py +++ b/src/calibre/ebooks/mobi/reader.py @@ -716,6 +716,7 @@ class MobiReader(object): ent_pat = re.compile(r'&(\S+?);') if elems: tocobj = TOC() + found = False reached = False for x in root.iter(): if x == elems[-1]: @@ -732,7 +733,8 @@ class MobiReader(object): text = ent_pat.sub(entity_to_unicode, text) tocobj.add_item(toc.partition('#')[0], href[1:], text) - if reached and x.get('class', None) == 'mbp_pagebreak': + found = True + if reached and found and x.get('class', None) == 'mbp_pagebreak': break if tocobj is not None: opf.set_toc(tocobj) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index e5f2cace7f..58083f807f 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -24,7 +24,7 @@ from calibre.translations.dynamic import translate from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.oeb.entitydefs import ENTITYDEFS from calibre.ebooks.conversion.preprocess import CSSPreProcessor -from calibre import isbytestring +from calibre import isbytestring, as_unicode RECOVER_PARSER = etree.XMLParser(recover=True, no_network=True) @@ -643,7 +643,7 @@ class Metadata(object): return unicode(self.value).encode('ascii', 'xmlcharrefreplace') def __unicode__(self): - return unicode(self.value) + return as_unicode(self.value) def to_opf1(self, dcmeta=None, xmeta=None, nsrmap={}): attrib = {} diff --git a/src/calibre/gui2/__init__.py b/src/calibre/gui2/__init__.py index e39427021e..773aea3002 100644 --- a/src/calibre/gui2/__init__.py +++ b/src/calibre/gui2/__init__.py @@ -648,6 +648,18 @@ def open_url(qurl): if isfrozen and islinux and paths: os.environ['LD_LIBRARY_PATH'] = os.pathsep.join(paths) +def get_current_db(): + ''' + This method will try to return the current database in use by the user as + efficiently as possible, i.e. without constructing duplicate + LibraryDatabase objects. + ''' + from calibre.gui2.ui import get_gui + gui = get_gui() + if gui is not None and gui.current_db is not None: + return gui.current_db + from calibre.library import db + return db() def open_local_file(path): if iswindows: diff --git a/src/calibre/gui2/actions/catalog.py b/src/calibre/gui2/actions/catalog.py index fad6e59294..093985d041 100644 --- a/src/calibre/gui2/actions/catalog.py +++ b/src/calibre/gui2/actions/catalog.py @@ -17,7 +17,7 @@ from calibre.gui2.actions import InterfaceAction class GenerateCatalogAction(InterfaceAction): name = 'Generate Catalog' - action_spec = (_('Create a catalog of the books in your calibre library'), None, None, None) + action_spec = (_('Create a catalog of the books in your calibre library'), 'catalog.png', 'Catalog builder', None) dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device']) def generate_catalog(self): diff --git a/src/calibre/gui2/actions/store.py b/src/calibre/gui2/actions/store.py index f00497ad64..4e96960243 100644 --- a/src/calibre/gui2/actions/store.py +++ b/src/calibre/gui2/actions/store.py @@ -8,20 +8,20 @@ __docformat__ = 'restructuredtext en' from functools import partial -from PyQt4.Qt import Qt, QMenu, QToolButton, QDialog, QVBoxLayout +from PyQt4.Qt import QMenu from calibre.gui2.actions import InterfaceAction class StoreAction(InterfaceAction): name = 'Store' - action_spec = (_('Store'), 'store.png', None, None) - + action_spec = (_('Get books'), 'store.png', None, None) + def genesis(self): self.qaction.triggered.connect(self.search) self.store_menu = QMenu() self.load_menu() - + def load_menu(self): self.store_menu.clear() self.store_menu.addAction(_('Search'), self.search) @@ -29,11 +29,11 @@ class StoreAction(InterfaceAction): for n, p in self.gui.istores.items(): self.store_menu.addAction(n, partial(self.open_store, p)) self.qaction.setMenu(self.store_menu) - + def search(self): from calibre.gui2.store.search import SearchDialog sd = SearchDialog(self.gui.istores, self.gui) sd.exec_() - + def open_store(self, store_plugin): store_plugin.open(self.gui) diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py index 609c2b30f3..6c3dae3c94 100644 --- a/src/calibre/gui2/book_details.py +++ b/src/calibre/gui2/book_details.py @@ -483,8 +483,15 @@ class BookDetails(QWidget): # {{{ self.book_info.show_data(data) self.cover_view.show_data(data) self._layout.do_layout(self.rect()) - self.setToolTip('

'+_('Double-click to open Book Details window') + - '

' + _('Path') + ': ' + data.get(_('Path'), '')) + try: + sz = self.cover_view.pixmap.size() + except: + sz = QSize(0, 0) + self.setToolTip( + '

'+_('Double-click to open Book Details window') + + '

' + _('Path') + ': ' + data.get(_('Path'), '') + + '

' + _('Cover size: %dx%d')%(sz.width(), sz.height()) + ) def reset_info(self): self.show_data({}) diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py index e860579fdf..46d26c2f4a 100644 --- a/src/calibre/gui2/dialogs/book_info.py +++ b/src/calibre/gui2/dialogs/book_info.py @@ -109,6 +109,8 @@ class BookInfo(QDialog, Ui_BookInfo): pixmap = pixmap.scaled(new_width, new_height, Qt.KeepAspectRatio, Qt.SmoothTransformation) self.cover.set_pixmap(pixmap) + sz = pixmap.size() + self.cover.setToolTip(_('Cover size: %dx%d')%(sz.width(), sz.height())) def refresh(self, row): if isinstance(row, QModelIndex): diff --git a/src/calibre/gui2/dialogs/scheduler.py b/src/calibre/gui2/dialogs/scheduler.py index 9b1fe67f29..b25d66979d 100644 --- a/src/calibre/gui2/dialogs/scheduler.py +++ b/src/calibre/gui2/dialogs/scheduler.py @@ -68,7 +68,7 @@ class DaysOfWeek(Base): def initialize(self, typ=None, val=None): if typ is None: typ = 'day/time' - val = (-1, 9, 0) + val = (-1, 6, 0) if typ == 'day/time': val = convert_day_time_schedule(val) @@ -118,7 +118,7 @@ class DaysOfMonth(Base): def initialize(self, typ=None, val=None): if val is None: - val = ((1,), 9, 0) + val = ((1,), 6, 0) days_of_month, hour, minute = val self.days.setText(', '.join(map(str, map(int, days_of_month)))) self.time.setTime(QTime(hour, minute)) @@ -380,7 +380,7 @@ class SchedulerDialog(QDialog, Ui_Dialog): if d < timedelta(days=366): ld_text = tm else: - typ, sch = 'day/time', (-1, 9, 0) + typ, sch = 'day/time', (-1, 6, 0) sch_widget = {'day/time': 0, 'days_of_week': 0, 'days_of_month':1, 'interval':2}[typ] rb = getattr(self, list(self.SCHEDULE_TYPES)[sch_widget]) diff --git a/src/calibre/gui2/dialogs/tweak_epub.py b/src/calibre/gui2/dialogs/tweak_epub.py index db6e93fd7a..a42fb07e40 100755 --- a/src/calibre/gui2/dialogs/tweak_epub.py +++ b/src/calibre/gui2/dialogs/tweak_epub.py @@ -12,6 +12,7 @@ from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED from PyQt4.Qt import QDialog +from calibre.constants import isosx, iswindows from calibre.gui2 import open_local_file from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog from calibre.libunzip import extract as zipextract @@ -42,11 +43,19 @@ class TweakEpub(QDialog, Ui_Dialog): self.move(parent_loc.x(),parent_loc.y()) def cleanup(self): + if isosx: + try: + import appscript + self.finder = appscript.app('Finder') + self.finder.Finder_windows[os.path.basename(self._exploded)].close() + except: + # appscript fails to load on 10.4 + pass + # Delete directory containing exploded ePub if self._exploded is not None: shutil.rmtree(self._exploded, ignore_errors=True) - def display_exploded(self): ''' Generic subprocess launch of native file browser diff --git a/src/calibre/gui2/layout.py b/src/calibre/gui2/layout.py index e5ec5a9131..c72b074463 100644 --- a/src/calibre/gui2/layout.py +++ b/src/calibre/gui2/layout.py @@ -317,6 +317,8 @@ class BaseToolBar(QToolBar): # {{{ QToolBar.resizeEvent(self, ev) style = self.get_text_style() self.setToolButtonStyle(style) + if hasattr(self, 'd_widget') and hasattr(self.d_widget, 'filler'): + self.d_widget.filler.setVisible(style != Qt.ToolButtonIconOnly) def get_text_style(self): style = Qt.ToolButtonTextUnderIcon @@ -399,7 +401,10 @@ class ToolBar(BaseToolBar): # {{{ self.d_widget.layout().addWidget(self.donate_button) if isosx: self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }') - self.d_widget.layout().addWidget(QLabel(u'\u00a0')) + self.d_widget.layout().setContentsMargins(0,0,0,0) + self.d_widget.setContentsMargins(0,0,0,0) + self.d_widget.filler = QLabel(u'\u00a0') + self.d_widget.layout().addWidget(self.d_widget.filler) bar.addWidget(self.d_widget) self.showing_donate = True elif what in self.gui.iactions: diff --git a/src/calibre/gui2/metadata/basic_widgets.py b/src/calibre/gui2/metadata/basic_widgets.py index 25034bbfbd..9502fcb205 100644 --- a/src/calibre/gui2/metadata/basic_widgets.py +++ b/src/calibre/gui2/metadata/basic_widgets.py @@ -223,7 +223,7 @@ class AuthorSortEdit(EnLineEdit): LABEL = _('Author s&ort:') def __init__(self, parent, authors_edit, autogen_button, db, - copy_as_to_a_action): + copy_a_to_as_action, copy_as_to_a_action): EnLineEdit.__init__(self, parent) self.authors_edit = authors_edit self.db = db @@ -242,6 +242,7 @@ class AuthorSortEdit(EnLineEdit): self.textChanged.connect(self.update_state) autogen_button.clicked.connect(self.auto_generate) + copy_a_to_as_action.triggered.connect(self.auto_generate) copy_as_to_a_action.triggered.connect(self.copy_to_authors) self.update_state() diff --git a/src/calibre/gui2/metadata/single.py b/src/calibre/gui2/metadata/single.py index 215a41a68d..52b9e99872 100644 --- a/src/calibre/gui2/metadata/single.py +++ b/src/calibre/gui2/metadata/single.py @@ -109,10 +109,12 @@ class MetadataSingleDialogBase(ResizableDialog): 'Using this button to create author sort will change author sort from' ' red to green.')) b.m = m = QMenu() - ac = m.addAction(QIcon(I('back.png')), _('Set author from author sort')) + ac = m.addAction(QIcon(I('forward.png')), _('Set author sort from author')) + ac2 = m.addAction(QIcon(I('back.png')), _('Set author from author sort')) b.setMenu(m) self.authors = AuthorsEdit(self) - self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac) + self.author_sort = AuthorSortEdit(self, self.authors, b, self.db, ac, + ac2) self.basic_metadata_widgets.extend([self.authors, self.author_sort]) self.swap_title_author_button = QToolButton(self) diff --git a/src/calibre/gui2/preferences/__init__.py b/src/calibre/gui2/preferences/__init__.py index 1669e24059..649a58448d 100644 --- a/src/calibre/gui2/preferences/__init__.py +++ b/src/calibre/gui2/preferences/__init__.py @@ -319,9 +319,12 @@ def show_config_widget(category, name, gui=None, show_restart_msg=False, :return: True iff a restart is required for the changes made by the user to take effect ''' + from calibre.gui2 import gprefs pl = get_plugin(category, name) d = ConfigDialog(parent) d.resize(750, 550) + conf_name = 'config_widget_dialog_geometry_%s_%s'%(category, name) + geom = gprefs.get(conf_name, None) d.setWindowTitle(_('Configure ') + name) d.setWindowIcon(QIcon(I('config.png'))) bb = QDialogButtonBox(d) @@ -345,7 +348,11 @@ def show_config_widget(category, name, gui=None, show_restart_msg=False, mygui = True w.genesis(gui) w.initialize() + if geom is not None: + d.restoreGeometry(geom) d.exec_() + geom = bytearray(d.saveGeometry()) + gprefs[conf_name] = geom rr = getattr(d, 'restart_required', False) if show_restart_msg and rr: from calibre.gui2 import warning_dialog diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py index 71b9e38667..9f06d9a6ab 100644 --- a/src/calibre/gui2/preferences/look_feel.py +++ b/src/calibre/gui2/preferences/look_feel.py @@ -73,13 +73,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): choices=sorted(list(choices), key=sort_key)) - self.current_font = None + self.current_font = self.initial_font = None self.change_font_button.clicked.connect(self.change_font) def initialize(self): ConfigWidgetBase.initialize(self) - self.current_font = gprefs['font'] + self.current_font = self.initial_font = gprefs['font'] self.update_font_display() def restore_defaults(self): @@ -119,7 +119,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form): def commit(self, *args): rr = ConfigWidgetBase.commit(self, *args) - if self.current_font != gprefs['font']: + if self.current_font != self.initial_font: gprefs['font'] = self.current_font QApplication.setFont(self.font_display.font()) rr = True diff --git a/src/calibre/gui2/store/__init__.py b/src/calibre/gui2/store/__init__.py index 73d0d0a8d4..26bafd2c95 100644 --- a/src/calibre/gui2/store/__init__.py +++ b/src/calibre/gui2/store/__init__.py @@ -19,27 +19,27 @@ class StorePlugin(object): # {{{ If two :class:`StorePlugin` objects have the same name, the one with higher priority takes precedence. - + Sub-classes must implement :meth:`open`, and :meth:`search`. - + Regarding :meth:`open`. Most stores only make themselves available though a web site thus most store plugins will open using :class:`calibre.gui2.store.web_store_dialog.WebStoreDialog`. This will open a modal window and display the store website in a QWebView. - + Sub-classes should implement and use the :meth:`genesis` if they require plugin specific initialization. They should not override or otherwise reimplement :meth:`__init__`. - + Once initialized, this plugin has access to the main calibre GUI via the :attr:`gui` member. You can access other plugins by name, for example:: self.gui.istores['Amazon Kindle'] - + Plugin authors can use affiliate programs within their plugin. The distribution of money earned from a store plugin is 70/30. 70% going to the pluin author / maintainer and 30% going to the calibre project. - + The easiest way to handle affiliate money payouts is to randomly select between the author's affiliate id and calibre's affiliate id so that 70% of the time the author's id is used. @@ -49,61 +49,61 @@ class StorePlugin(object): # {{{ self.gui = gui self.name = name self.base_plugin = None - + def open(self, gui, parent=None, detail_item=None, external=False): ''' Open the store. - + :param gui: The main GUI. This will be used to have the job system start downloading an item from the store. - + :param parent: The parent of the store dialog. This is used to create modal dialogs. - + :param detail_item: A plugin specific reference to an item in the store that the user should be shown. - + :param external: When False open an internal dialog with the store. When True open the users default browser to the store's web site. :param:`detail_item` should still be respected when external is True. ''' raise NotImplementedError() - + def search(self, query, max_results=10, timeout=60): ''' Searches the store for items matching query. This should return items as a generator. - + Don't be lazy with the search! Load as much data as possible in the :class:`calibre.gui2.store.search_result.SearchResult` object. If you have to parse multiple pages to get all of the data then do so. However, if data (such as cover_url) isn't available because the store does not display cover images then it's okay to ignore it. - + Also, by default search results can only include ebooks. A plugin can offer users an option to include physical books in the search results but this must be disabled by default. - + If a store doesn't provide search on it's own use something like a site specific google search to get search results for this funtion. - + :param query: The string query search with. :param max_results: The maximum number of results to return. :param timeout: The maximum amount of time in seconds to spend download the search results. - + :return: :class:`calibre.gui2.store.search_result.SearchResult` objects - item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store. + item_data is plugin specific and is used in :meth:`open` to open to a specifc place in the store. ''' raise NotImplementedError() - + def get_settings(self): ''' This is only useful for plugins that implement :attr:`config_widget` that is the only way to save settings. This is used by plugins to get the saved settings and apply when necessary. - + :return: A dictionary filled with the settings used by this plugin. ''' @@ -117,23 +117,23 @@ class StorePlugin(object): # {{{ Plugin specific initialization. ''' pass - + def config_widget(self): ''' See :class:`calibre.customize.Plugin` for details. ''' raise NotImplementedError() - + def save_settings(self, config_widget): ''' See :class:`calibre.customize.Plugin` for details. ''' raise NotImplementedError() - + def customization_help(self, gui=False): ''' See :class:`calibre.customize.Plugin` for details. ''' raise NotImplementedError() -# }}} \ No newline at end of file +# }}} diff --git a/src/calibre/gui2/store/amazon_plugin.py b/src/calibre/gui2/store/amazon_plugin.py index 0b42ee1308..51986ee4df 100644 --- a/src/calibre/gui2/store/amazon_plugin.py +++ b/src/calibre/gui2/store/amazon_plugin.py @@ -21,14 +21,14 @@ from calibre.gui2.store import StorePlugin from calibre.gui2.store.search_result import SearchResult class AmazonKindleStore(StorePlugin): - + def open(self, parent=None, detail_item=None, external=False): ''' Amazon comes with a number of difficulties. - + QWebView has major issues with Amazon.com. The largest of issues is it simply doesn't work on a number of pages. - + When connecting to a number parts of Amazon.com (Kindle library for instance) QNetworkAccessManager fails to connect with a NetworkError of 399 - ProtocolFailure. The strange thing is, @@ -37,19 +37,19 @@ class AmazonKindleStore(StorePlugin): the QNetworkAccessManager decides there was a NetworkError it does not download the page from Amazon. So I can't even set the HTML in the QWebView myself. - + There is http://bugreports.qt.nokia.com/browse/QTWEBKIT-259 an open bug about the issue but it is not correct. We can set the useragent (Arora does) to something else and the above issue will persist. This http://developer.qt.nokia.com/forums/viewthread/793 gives a bit more information about the issue but as of now (27/Feb/2011) there is no solution or work around. - + We cannot change the The linkDelegationPolicy to allow us to avoid QNetworkAccessManager because it only works links. Forms aren't included so the same issue persists on any part of the site (login) that use a form to load a new page. - + Using an aStore was evaluated but I've decided against using it. There are three major issues with an aStore. Because checkout is handled by sending the user to Amazon we can't put it in a QWebView. @@ -57,7 +57,7 @@ class AmazonKindleStore(StorePlugin): nicer. Also, we cannot put the aStore in a QWebView and let it open the redirection the users default browser because the cookies with the shopping cart won't transfer. - + Another issue with the aStore is how it handles the referral. It only counts the referral for the items in the shopping card / the item that directed the user to Amazon. Kindle books do not use the shopping @@ -65,44 +65,44 @@ class AmazonKindleStore(StorePlugin): instance we would only get referral credit for the one book that the aStore directs to Amazon that the user buys. Any other purchases we won't get credit for. - + The last issue with the aStore is performance. Even though it's an Amazon site it's alow. So much slower than Amazon.com that it makes me not want to browse books using it. The look and feel are lesser issues. So is the fact that it almost seems like the purchase is with calibre. This can cause some support issues because we can't do much for issues with Amazon.com purchase hiccups. - + Another option that was evaluated was the Product Advertising API. The reasons against this are complexity. It would take a lot of work to basically re-create Amazon.com within calibre. The Product Advertising API is also designed with being run on a server not in an app. The signing keys would have to be made avaliable to ever calibre user which means bad things could be done with our account. - + The Product Advertising API also assumes the same browser for easy shopping cart transfer to Amazon. With QWebView not working and there not being an easy way to transfer cookies between a QWebView and the users default browser this won't work well. - + We could create our own website on the calibre server and create an Amazon Product Advertising API store. However, this goes back to the complexity argument. Why spend the time recreating Amazon.com - + The final and largest issue against using the Product Advertising API is the Efficiency Guidelines: - + "Each account used to access the Product Advertising API will be allowed an initial usage limit of 2,000 requests per hour. Each account will receive an additional 500 requests per hour (up to a maximum of 25,000 requests per hour) for every $1 of shipped item revenue driven per hour in a trailing 30-day period. Usage thresholds are recalculated daily based - on revenue performance." - + on revenue performance." + With over two million users a limit of 2,000 request per hour could render our store unusable for no other reason than Amazon rate limiting our traffic. - + The best (I use the term lightly here) solution is to open Amazon.com in the users default browser and set the affiliate id as part of the url. ''' @@ -119,14 +119,14 @@ class AmazonKindleStore(StorePlugin): def search(self, query, max_results=10, timeout=60): url = 'http://www.amazon.com/s/url=search-alias%3Ddigital-text&field-keywords=' + urllib2.quote(query) br = browser() - + counter = max_results with closing(br.open(url, timeout=timeout)) as f: doc = html.fromstring(f.read()) for data in doc.xpath('//div[@class="productData"]'): if counter <= 0: break - + # Even though we are searching digital-text only Amazon will still # put in results for non Kindle books (author pages). Se we need # to explicitly check if the item is a Kindle book and ignore it @@ -134,7 +134,7 @@ class AmazonKindleStore(StorePlugin): type = ''.join(data.xpath('//span[@class="format"]/text()')) if 'kindle' not in type.lower(): continue - + # We must have an asin otherwise we can't easily reference the # book later. asin_href = None @@ -148,25 +148,25 @@ class AmazonKindleStore(StorePlugin): continue else: continue - + cover_url = '' if asin_href: cover_img = data.xpath('//div[@class="productImage"]/a[@href="%s"]/img/@src' % asin_href) if cover_img: cover_url = cover_img[0] - + title = ''.join(data.xpath('div[@class="productTitle"]/a/text()')) author = ''.join(data.xpath('div[@class="productTitle"]/span[@class="ptBrand"]/text()')) author = author.split('by')[-1] price = ''.join(data.xpath('div[@class="newPrice"]/span/text()')) - + counter -= 1 - + s = SearchResult() s.cover_url = cover_url s.title = title.strip() s.author = author.strip() s.price = price.strip() s.detail_item = asin.strip() - + yield s diff --git a/src/calibre/gui2/store/bewrite_plugin.py b/src/calibre/gui2/store/bewrite_plugin.py index ffdb3cd4a2..37bd9cf9a5 100644 --- a/src/calibre/gui2/store/bewrite_plugin.py +++ b/src/calibre/gui2/store/bewrite_plugin.py @@ -6,7 +6,6 @@ __license__ = 'GPL 3' __copyright__ = '2011, John Schember ' __docformat__ = 'restructuredtext en' -import re import urllib2 from contextlib import closing @@ -22,7 +21,7 @@ from calibre.gui2.store.search_result import SearchResult from calibre.gui2.store.web_store_dialog import WebStoreDialog class BeWriteStore(BasicStoreConfig, StorePlugin): - + def open(self, parent=None, detail_item=None, external=False): settings = self.get_settings() url = 'http://www.bewrite.net/mm5/merchant.mvc?Screen=SFNT' @@ -42,9 +41,9 @@ class BeWriteStore(BasicStoreConfig, StorePlugin): def search(self, query, max_results=10, timeout=60): url = 'http://www.bewrite.net/mm5/merchant.mvc?Search_Code=B&Screen=SRCH&Search=' + urllib2.quote(query) - + br = browser() - + counter = max_results with closing(br.open(url, timeout=timeout)) as f: doc = html.fromstring(f.read()) @@ -55,12 +54,12 @@ class BeWriteStore(BasicStoreConfig, StorePlugin): id = ''.join(data.xpath('.//a/@href')) if not id: continue - - heading = ''.join(data.xpath('./td[2]//text()')) + + heading = ''.join(data.xpath('./td[2]//text()')) title, q, author = heading.partition('by ') cover_url = '' price = '' - + with closing(br.open(id.strip(), timeout=timeout/4)) as nf: idata = html.fromstring(nf.read()) price = ''.join(idata.xpath('//div[@id="content"]//td[contains(text(), "ePub")]/text()')) @@ -68,14 +67,14 @@ class BeWriteStore(BasicStoreConfig, StorePlugin): cover_img = idata.xpath('//div[@id="content"]//img[1]/@src') if cover_img: cover_url = 'http://www.bewrite.net/mm5/' + cover_img[0] - + counter -= 1 - + s = SearchResult() s.cover_url = cover_url.strip() s.title = title.strip() s.author = author.strip() s.price = price.strip() s.detail_item = id.strip() - + yield s diff --git a/src/calibre/gui2/store/search.py b/src/calibre/gui2/store/search.py index 970aaf61d2..1d263959ef 100644 --- a/src/calibre/gui2/store/search.py +++ b/src/calibre/gui2/store/search.py @@ -13,9 +13,8 @@ from random import shuffle from threading import Thread from Queue import Queue -from PyQt4.Qt import Qt, QAbstractItemModel, QDialog, QTimer, QVariant, \ - QModelIndex, QPixmap, QSize, QCheckBox, QVBoxLayout, QHBoxLayout, \ - QPushButton, QString, QByteArray +from PyQt4.Qt import (Qt, QAbstractItemModel, QDialog, QTimer, QVariant, + QModelIndex, QPixmap, QSize, QCheckBox, QVBoxLayout) from calibre import browser from calibre.gui2 import NONE @@ -35,7 +34,7 @@ class SearchDialog(QDialog, Ui_Dialog): def __init__(self, istores, *args): QDialog.__init__(self, *args) self.setupUi(self) - + self.config = DynamicConfig('store_search') # We keep a cache of store plugins and reference them by name. @@ -44,7 +43,7 @@ class SearchDialog(QDialog, Ui_Dialog): # Check for results and hung threads. self.checker = QTimer() self.hang_check = 0 - + self.model = Matches() self.results_view.setModel(self.model) @@ -59,7 +58,7 @@ class SearchDialog(QDialog, Ui_Dialog): stores_group_layout.addWidget(cbox) setattr(self, 'store_check_' + x, cbox) stores_group_layout.addStretch() - + # Create and add the progress indicator self.pi = ProgressIndicator(self, 24) self.bottom_layout.insertWidget(0, self.pi) @@ -71,9 +70,9 @@ class SearchDialog(QDialog, Ui_Dialog): self.select_invert_stores.clicked.connect(self.stores_select_invert) self.select_none_stores.clicked.connect(self.stores_select_none) self.finished.connect(self.dialog_closed) - + self.restore_state() - + def resize_columns(self): total = 600 # Cover @@ -87,19 +86,19 @@ class SearchDialog(QDialog, Ui_Dialog): self.results_view.setColumnWidth(3, int(total*.10)) # Store self.results_view.setColumnWidth(4, int(total*.20)) - + def do_search(self, checked=False): # Stop all running threads. self.checker.stop() self.search_pool.abort() # Clear the visible results. self.results_view.model().clear_results() - + # Don't start a search if there is nothing to search for. query = unicode(self.search_edit.text()) if not query.strip(): return - + # Plugins are in alphebetic order. Randomize the # order of plugin names. This way plugins closer # to a don't have an unfair advantage over @@ -117,12 +116,12 @@ class SearchDialog(QDialog, Ui_Dialog): self.checker.start(100) self.search_pool.start_threads() self.pi.startAnimation() - + def save_state(self): self.config['store_search_geometry'] = self.saveGeometry() self.config['store_search_store_splitter_state'] = self.store_splitter.saveState() self.config['store_search_results_view_column_width'] = [self.results_view.columnWidth(i) for i in range(self.model.columnCount())] - + store_check = {} for n in self.store_plugins: store_check[n] = getattr(self, 'store_check_' + n).isChecked() @@ -132,11 +131,11 @@ class SearchDialog(QDialog, Ui_Dialog): geometry = self.config['store_search_geometry'] if geometry: self.restoreGeometry(geometry) - + splitter_state = self.config['store_search_store_splitter_state'] if splitter_state: self.store_splitter.restoreState(splitter_state) - + results_cwidth = self.config['store_search_results_view_column_width'] if results_cwidth: for i, x in enumerate(results_cwidth): @@ -145,7 +144,7 @@ class SearchDialog(QDialog, Ui_Dialog): self.results_view.setColumnWidth(i, x) else: self.resize_columns() - + store_check = self.config['store_search_store_checked'] if store_check: for n in store_check: @@ -165,7 +164,7 @@ class SearchDialog(QDialog, Ui_Dialog): if not self.search_pool.threads_running() and not self.search_pool.has_tasks(): self.checker.stop() self.pi.stopAnimation() - + while self.search_pool.has_results(): res = self.search_pool.get_result() if res: @@ -189,15 +188,15 @@ class SearchDialog(QDialog, Ui_Dialog): def stores_select_all(self): for check in self.get_store_checks(): check.setChecked(True) - + def stores_select_invert(self): for check in self.get_store_checks(): check.setChecked(not check.isChecked()) - + def stores_select_none(self): for check in self.get_store_checks(): check.setChecked(False) - + def dialog_closed(self, result): self.model.closing() self.search_pool.abort() @@ -208,46 +207,46 @@ class GenericDownloadThreadPool(object): ''' add_task must be implemented in a subclass. ''' - + def __init__(self, thread_type, thread_count): self.thread_type = thread_type self.thread_count = thread_count - + self.tasks = Queue() self.results = Queue() self.threads = [] - + def add_task(self): raise NotImplementedError() - + def start_threads(self): for i in range(self.thread_count): t = self.thread_type(self.tasks, self.results) self.threads.append(t) t.start() - + def abort(self): self.tasks = Queue() self.results = Queue() for t in self.threads: t.abort() self.threads = [] - + def has_tasks(self): return not self.tasks.empty() - + def get_result(self): return self.results.get() - + def get_result_no_wait(self): return self.results.get_nowait() - + def result_count(self): return len(self.results) - + def has_results(self): return not self.results.empty() - + def threads_running(self): for t in self.threads: if t.is_alive(): @@ -260,7 +259,7 @@ class SearchThreadPool(GenericDownloadThreadPool): Threads will run until there is no work or abort is called. Create and start new threads using start_threads(). Reset by calling abort(). - + Example: sp = SearchThreadPool(SearchThread, 3) add tasks using add_task(...) @@ -270,13 +269,13 @@ class SearchThreadPool(GenericDownloadThreadPool): add tasks using add_task(...) sp.start_threads() ''' - + def add_task(self, query, store_name, store_plugin, timeout): self.tasks.put((query, store_name, store_plugin, timeout)) class SearchThread(Thread): - + def __init__(self, tasks, results): Thread.__init__(self) self.daemon = True @@ -286,7 +285,7 @@ class SearchThread(Thread): def abort(self): self._run = False - + def run(self): while self._run and not self.tasks.empty(): try: @@ -305,7 +304,7 @@ class CoverThreadPool(GenericDownloadThreadPool): ''' Once started all threads run until abort is called. ''' - + def add_task(self, search_result, update_callback, timeout=5): self.tasks.put((search_result, update_callback, timeout)) @@ -318,12 +317,12 @@ class CoverThread(Thread): self.tasks = tasks self.results = results self._run = True - + self.br = browser() def abort(self): self._run = False - + def run(self): while self._run: try: @@ -354,13 +353,13 @@ class Matches(QAbstractItemModel): def closing(self): self.cover_pool.abort() - + def clear_results(self): self.matches = [] self.cover_pool.abort() self.cover_pool.start_threads() self.reset() - + def add_result(self, result): self.layoutAboutToBeChanged.emit() self.matches.append(result) @@ -391,7 +390,7 @@ class Matches(QAbstractItemModel): def columnCount(self, *args): return len(self.HEADERS) - + def headerData(self, section, orientation, role): if role != Qt.DisplayRole: return NONE @@ -434,7 +433,7 @@ class Matches(QAbstractItemModel): elif col == 3: text = result.price if len(text) < 3 or text[-3] not in ('.', ','): - text += '00' + text += '00' text = re.sub(r'\D', '', text) text = text.rjust(6, '0') elif col == 4: @@ -444,7 +443,7 @@ class Matches(QAbstractItemModel): def sort(self, col, order, reset=True): if not self.matches: return - descending = order == Qt.DescendingOrder + descending = order == Qt.DescendingOrder self.matches.sort(None, lambda x: sort_key(unicode(self.data_as_text(x, col))), descending) diff --git a/src/calibre/gui2/store/web_control.py b/src/calibre/gui2/store/web_control.py index b7ab75975d..874328f872 100644 --- a/src/calibre/gui2/store/web_control.py +++ b/src/calibre/gui2/store/web_control.py @@ -9,8 +9,8 @@ __docformat__ = 'restructuredtext en' import os from urlparse import urlparse -from PyQt4.Qt import QWebView, QWebPage, QNetworkCookieJar, QNetworkRequest, QString, \ - QFileDialog, QNetworkProxy +from PyQt4.Qt import QNetworkCookieJar, QFileDialog, QNetworkProxy +from PyQt4.QtWebKit import QWebView, QWebPage from calibre import USER_AGENT, get_proxies, get_download_filename from calibre.ebooks import BOOK_EXTENSIONS @@ -35,13 +35,13 @@ class NPWebView(QWebView): proxy.setPassword(proxy_parts.password) proxy.setHostName(proxy_parts.hostname) proxy.setPort(proxy_parts.port) - self.page().networkAccessManager().setProxy(proxy) - + self.page().networkAccessManager().setProxy(proxy) + self.page().setForwardUnsupportedContent(True) self.page().unsupportedContent.connect(self.start_download) self.page().downloadRequested.connect(self.start_download) self.page().networkAccessManager().sslErrors.connect(self.ignore_ssl_errors) - + def createWindow(self, type): if type == QWebPage.WebBrowserWindow: return self @@ -50,17 +50,17 @@ class NPWebView(QWebView): def set_gui(self, gui): self.gui = gui - + def set_tags(self, tags): self.tags = tags - + def start_download(self, request): if not self.gui: return - + url = unicode(request.url().toString()) cf = self.get_cookies() - + filename = get_download_filename(url, cf) ext = os.path.splitext(filename)[1][1:].lower() if ext not in BOOK_EXTENSIONS: @@ -76,21 +76,21 @@ class NPWebView(QWebView): def ignore_ssl_errors(self, reply, errors): reply.ignoreSslErrors(errors) - + def get_cookies(self): ''' Writes QNetworkCookies to Mozilla cookie .txt file. - + :return: The file path to the cookie file. ''' cf = PersistentTemporaryFile(suffix='.txt') - + cf.write('# Netscape HTTP Cookie File\n\n') - + for c in self.page().networkAccessManager().cookieJar().allCookies(): cookie = [] domain = unicode(c.domain()) - + cookie.append(domain) cookie.append('TRUE' if domain.startswith('.') else 'FALSE') cookie.append(unicode(c.path())) @@ -98,15 +98,15 @@ class NPWebView(QWebView): cookie.append(unicode(c.expirationDate().toTime_t())) cookie.append(unicode(c.name())) cookie.append(unicode(c.value())) - + cf.write('\t'.join(cookie)) cf.write('\n') - + cf.close() return cf.name class NPWebPage(QWebPage): - + def userAgentForUrl(self, url): return USER_AGENT diff --git a/src/calibre/gui2/ui.py b/src/calibre/gui2/ui.py index f234d48739..9a4e0ca70a 100644 --- a/src/calibre/gui2/ui.py +++ b/src/calibre/gui2/ui.py @@ -88,6 +88,11 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{ # }}} +_gui = None + +def get_gui(): + return _gui + class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin, SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin, @@ -97,7 +102,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ def __init__(self, opts, parent=None, gui_debug=None): + global _gui MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True) + _gui = self self.opts = opts self.device_connected = None self.gui_debug = gui_debug diff --git a/src/calibre/library/cli.py b/src/calibre/library/cli.py index f062aecc26..b1a8236151 100644 --- a/src/calibre/library/cli.py +++ b/src/calibre/library/cli.py @@ -426,7 +426,7 @@ def do_show_metadata(db, id, as_opf): mi = OPFCreator(os.getcwd(), mi) mi.render(sys.stdout) else: - print unicode(mi).encode(preferred_encoding) + prints(unicode(mi)) def show_metadata_option_parser(): parser = get_parser(_( diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 9f38c7fb8b..8895eb64b3 100644 --- a/src/calibre/translations/calibre.pot +++ b/src/calibre/translations/calibre.pot @@ -4,9 +4,9 @@ # msgid "" msgstr "" -"Project-Id-Version: calibre 0.7.55\n" -"POT-Creation-Date: 2011-04-15 09:45+MDT\n" -"PO-Revision-Date: 2011-04-15 09:45+MDT\n" +"Project-Id-Version: calibre 0.7.56\n" +"POT-Creation-Date: 2011-04-17 09:36+MDT\n" +"PO-Revision-Date: 2011-04-17 09:36+MDT\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -34,7 +34,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/chm/input.py:99 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/input.py:102 #: /home/kovid/work/calibre/src/calibre/ebooks/chm/metadata.py:56 -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:430 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:435 #: /home/kovid/work/calibre/src/calibre/ebooks/epub/periodical.py:127 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:100 #: /home/kovid/work/calibre/src/calibre/ebooks/fb2/input.py:102 @@ -84,8 +84,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:125 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:159 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:667 -#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:883 #: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:885 +#: /home/kovid/work/calibre/src/calibre/ebooks/mobi/reader.py:887 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:49 #: /home/kovid/work/calibre/src/calibre/ebooks/odt/input.py:51 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1001 @@ -151,7 +151,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:188 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/bulk_download.py:112 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/bulk_download2.py:252 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:320 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:324 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:156 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:160 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main.py:199 @@ -160,11 +160,11 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/library/database2.py:500 #: /home/kovid/work/calibre/src/calibre/library/database2.py:508 #: /home/kovid/work/calibre/src/calibre/library/database2.py:519 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1801 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1925 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2914 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2916 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3049 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1800 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1924 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2913 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2915 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3048 #: /home/kovid/work/calibre/src/calibre/library/server/mobile.py:233 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:156 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:159 @@ -222,6 +222,11 @@ msgstr "" msgid "Preferences" msgstr "" +#: /home/kovid/work/calibre/src/calibre/customize/__init__.py:609 +#: /home/kovid/work/calibre/src/calibre/gui2/store/search.py:346 +msgid "Store" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/customize/builtins.py:19 msgid "Follow all local links in an HTML file and create a ZIP file containing all linked files. This plugin is run every time you add an HTML file to the library." msgstr "" @@ -289,195 +294,251 @@ msgstr "" msgid "Set metadata from %s files" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:872 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:880 msgid "Look and Feel" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:874 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:886 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:897 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:908 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:920 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:882 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:894 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:905 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:916 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:928 msgid "Interface" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:878 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:886 msgid "Adjust the look and feel of the calibre interface to suit your tastes" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:884 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:892 msgid "Behavior" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:890 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:898 msgid "Change the way calibre behaves" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:895 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:903 #: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:218 msgid "Add your own columns" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:901 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:909 msgid "Add/remove your own columns to the calibre book list" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:906 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:914 msgid "Toolbar" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:912 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:920 msgid "Customize the toolbars and context menus, changing which actions are available in each" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:918 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:926 msgid "Searching" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:924 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:932 msgid "Customize the way searching for books works in calibre" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:929 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:937 msgid "Input Options" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:931 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:942 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:953 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:939 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:950 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:961 msgid "Conversion" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:935 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:943 msgid "Set conversion options specific to each input format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:940 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:948 msgid "Common Options" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:946 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:954 msgid "Set conversion options common to all formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:951 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:959 msgid "Output Options" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:957 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:965 msgid "Set conversion options specific to each output format" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:962 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:970 msgid "Adding books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:964 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:976 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:988 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1000 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:972 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:984 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:996 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1008 msgid "Import/Export" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:968 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:976 msgid "Control how calibre reads metadata from files when adding books" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:974 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:982 msgid "Saving books to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:980 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:988 msgid "Control how calibre exports files from its database to disk when using Save to disk" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:986 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:994 msgid "Sending books to devices" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:992 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1000 msgid "Control how calibre transfers files to your ebook reader" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:998 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1006 msgid "Metadata plugboards" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1004 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1012 msgid "Change metadata fields before saving/sending" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1009 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1017 msgid "Template Functions" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1011 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1058 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1070 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1081 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1019 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1066 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1078 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1089 msgid "Advanced" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1015 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1023 msgid "Create your own template functions" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1020 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1028 msgid "Sharing books by email" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1022 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1034 -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1047 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1030 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1042 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1055 msgid "Sharing" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1026 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1034 msgid "Setup sharing of books via email. Can be used for automatic sending of downloaded news to your devices" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1032 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1040 msgid "Sharing over the net" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1038 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1046 msgid "Setup the calibre Content Server which will give you access to your calibre library from anywhere, on any device, over the internet" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1045 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1053 #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/fetch.py:57 msgid "Metadata download" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1051 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1059 msgid "Control how calibre downloads ebook metadata from the net" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1056 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:268 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1064 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:269 msgid "Plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1062 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1070 msgid "Add/remove/customize various bits of calibre functionality" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1068 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1076 msgid "Tweaks" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1074 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1082 msgid "Fine tune how calibre behaves in various contexts" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1079 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1087 msgid "Miscellaneous" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1085 +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1093 msgid "Miscellaneous advanced configuration" msgstr "" +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1107 +msgid "Kindle books from Amazon" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1112 +msgid "Ebooks for readers." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1117 +msgid "Books, Textbooks, eBooks, Toys, Games and More." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1122 +msgid "Publishers of fine books." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1127 +msgid "World Famous eBook Store." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1132 +msgid "The digital bookstore." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1137 +msgid "entertain, enrich, inspire." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1142 +msgid "Read anywhere." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1147 +msgid "The first producer of free ebooks." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1152 +msgid "eReading: anytime. anyplace." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1157 +msgid "The best ebooks at the best price: free!" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1162 +msgid "Ebooks handcrafted with the utmost care" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1167 +msgid "One web page for every book." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/customize/builtins.py:1172 +msgid "Your ebook. Your way." +msgstr "" + #: /home/kovid/work/calibre/src/calibre/customize/conversion.py:102 msgid "Conversion Input" msgstr "" @@ -627,31 +688,31 @@ msgstr "" msgid "This profile is intended for the Sanda Bambook." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:26 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:27 msgid "Installed plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:27 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:28 msgid "Mapping for filetype plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:28 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:29 msgid "Local plugin customization" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:29 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:30 msgid "Disabled plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:30 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:31 msgid "Enabled plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:490 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:502 msgid "Initialization of plugin %s failed with traceback:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:523 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:535 msgid "" " %prog options\n" "\n" @@ -659,31 +720,31 @@ msgid "" " " msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:529 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:541 msgid "Add a plugin by specifying the path to the zip file containing it." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:531 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:543 msgid "Remove a custom plugin by name. Has no effect on builtin plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:533 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:545 msgid "Customize plugin. Specify name of plugin and customization string separated by a comma." msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:535 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:547 msgid "List all installed plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:537 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:549 msgid "Enable the named plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/customize/ui.py:539 +#: /home/kovid/work/calibre/src/calibre/customize/ui.py:551 msgid "Disable the named plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/debug.py:150 +#: /home/kovid/work/calibre/src/calibre/debug.py:152 msgid "Debug log" msgstr "" @@ -760,6 +821,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:477 #: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:1094 #: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:3119 +#: /home/kovid/work/calibre/src/calibre/gui2/ebook_download.py:106 msgid "finished" msgstr "" @@ -787,7 +849,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/scheduler.py:445 #: /home/kovid/work/calibre/src/calibre/library/database2.py:299 #: /home/kovid/work/calibre/src/calibre/library/database2.py:312 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2778 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2777 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:159 msgid "News" msgstr "" @@ -795,8 +857,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/devices/apple/driver.py:2656 #: /home/kovid/work/calibre/src/calibre/gui2/catalog/catalog_epub_mobi.py:65 #: /home/kovid/work/calibre/src/calibre/library/catalog.py:634 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2740 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:2758 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2739 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:2757 msgid "Catalog" msgstr "" @@ -1356,83 +1418,83 @@ msgstr "" msgid "Get device information..." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:197 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:202 msgid "Rendered %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:200 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:205 msgid "Failed %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:254 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:259 msgid "" "Failed to process comic: \n" "\n" "%s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:273 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:278 msgid "Number of colors for grayscale image conversion. Default: %default. Values of less than 256 may result in blurred text on your device if you are creating your comics in EPUB format." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:277 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:282 msgid "Disable normalize (improve contrast) color range for pictures. Default: False" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:280 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:285 msgid "Maintain picture aspect ratio. Default is to fill the screen." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:282 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:287 msgid "Disable sharpening." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:284 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:289 msgid "Disable trimming of comic pages. For some comics, trimming might remove content as well as borders." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:287 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:292 msgid "Don't split landscape images into two portrait images" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:289 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:294 msgid "Keep aspect ratio and scale image using screen height as image width for viewing in landscape mode." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:292 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:297 msgid "Used for right-to-left publications like manga. Causes landscape pages to be split into portrait pages from right to left." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:296 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:301 msgid "Enable Despeckle. Reduces speckle noise. May greatly increase processing time." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:299 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:304 msgid "Don't sort the files found in the comic alphabetically by name. Instead use the order they were added to the comic." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:303 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:308 msgid "The format that images in the created ebook are converted to. You can experiment to see which format gives you optimal size and look on your device." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:307 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:312 msgid "Apply no processing to the image" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:309 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:314 msgid "Do not convert the image to grayscale (black and white)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:311 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:316 msgid "Specify the image size as widthxheight pixels. Normally, an image size is automatically calculated from the output profile, this option overrides it." msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:315 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:320 msgid "When converting a CBC do not add links to each page to the TOC. Note this only applies if the TOC has more than one section" msgstr "" -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:454 -#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:466 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:459 +#: /home/kovid/work/calibre/src/calibre/ebooks/comic/input.py:471 msgid "Page" msgstr "" @@ -2273,6 +2335,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1028 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single_download.py:128 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/metadata_sources.py:151 +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_plugin.py:195 +#: /home/kovid/work/calibre/src/calibre/gui2/store/search.py:346 #: /home/kovid/work/calibre/src/calibre/library/field_metadata.py:331 #: /home/kovid/work/calibre/src/calibre/library/server/opds.py:574 msgid "Title" @@ -2283,6 +2347,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:66 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:438 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1029 +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_plugin.py:195 +#: /home/kovid/work/calibre/src/calibre/gui2/store/search.py:346 msgid "Author(s)" msgstr "" @@ -2689,6 +2755,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/metadata/opf2.py:1356 #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/base.py:1491 +#: /home/kovid/work/calibre/src/calibre/gui2/store/search.py:346 msgid "Cover" msgstr "" @@ -2848,7 +2915,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/ebooks/oeb/transforms/cover.py:98 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk.py:176 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:220 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:723 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:734 msgid "Book %s of %s" msgstr "" @@ -3364,7 +3431,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/add.py:30 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:308 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:534 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:545 msgid "Books" msgstr "" @@ -3706,7 +3773,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/jobs.py:457 #: /home/kovid/work/calibre/src/calibre/gui2/jobs.py:463 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/columns.py:102 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:274 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:275 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email.py:223 msgid "Are you sure?" msgstr "" @@ -3738,8 +3805,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:310 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:106 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/restore_library.py:111 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:286 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:340 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:287 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:341 msgid "Success" msgstr "" @@ -3782,7 +3849,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/choose_library.py:404 #: /home/kovid/work/calibre/src/calibre/gui2/actions/copy_to_library.py:167 #: /home/kovid/work/calibre/src/calibre/gui2/actions/save_to_disk.py:101 -#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:829 +#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:831 msgid "Not allowed" msgstr "" @@ -3811,7 +3878,7 @@ msgid "Bulk convert" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/actions/convert.py:86 -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:518 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:554 msgid "Cannot convert" msgstr "" @@ -4357,6 +4424,19 @@ msgstr "" msgid "Books with the same tags" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/actions/store.py:18 +msgid "Get books" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/actions/store.py:27 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:92 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:276 +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:106 +#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:653 +#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:280 +msgid "Search" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/actions/tweak_epub.py:15 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/tweak_epub_ui.py:54 msgid "Tweak ePub" @@ -4691,6 +4771,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts.py:48 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:78 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:83 +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:110 #: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:351 msgid "None" msgstr "" @@ -4805,6 +4886,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/template_functions_ui.py:95 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/toolbar_ui.py:98 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/tweaks_ui.py:87 +#: /home/kovid/work/calibre/src/calibre/gui2/store/basic_config_widget_ui.py:37 #: /home/kovid/work/calibre/src/calibre/gui2/wizard/send_email_ui.py:123 msgid "Form" msgstr "" @@ -5683,38 +5765,38 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:180 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:171 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:666 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:677 msgid "Choose cover for " msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:187 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:178 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:674 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:685 msgid "Cannot read" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:188 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:179 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:675 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:686 msgid "You do not have permission to read the file: " msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:196 #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:203 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:187 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:683 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:694 msgid "Error reading file" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:197 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:188 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:684 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:695 msgid "

There was an error reading from file:
" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata.py:204 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:196 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:694 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:705 msgid " is not a valid picture" msgstr "" @@ -5775,7 +5857,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/metadata_ui.py:171 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:537 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:430 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:848 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:859 msgid "Tags categorize the book. This is particularly useful while searching.

They can be any words or phrases, separated by commas." msgstr "" @@ -5783,7 +5865,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:544 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:433 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:214 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:296 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:307 msgid "&Series:" msgstr "" @@ -5793,7 +5875,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:546 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:434 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:435 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:295 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:306 msgid "List of known series. You can add new series." msgstr "" @@ -5981,6 +6063,7 @@ msgid "Occurrences:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/convert/regex_builder_ui.py:94 +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_store_dialog_ui.py:64 msgid "0" msgstr "" @@ -6067,6 +6150,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/comicconf_ui.py:96 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/message_box_ui.py:52 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/progress_ui.py:53 +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_store_dialog_ui.py:61 msgid "Dialog" msgstr "" @@ -6391,7 +6475,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:215 #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:248 #: /home/kovid/work/calibre/src/calibre/gui2/library/delegates.py:252 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1031 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1042 msgid "Undefined" msgstr "" @@ -6535,7 +6619,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/device.py:611 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/misc.py:41 -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:304 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:305 #: /home/kovid/work/calibre/src/calibre/utils/ipc/job.py:54 msgid "Error" msgstr "" @@ -6895,6 +6979,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/choose_format_device_ui.py:49 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/delete_matching_from_device.py:76 #: /home/kovid/work/calibre/src/calibre/gui2/library/models.py:1201 +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_plugin.py:195 msgid "Format" msgstr "" @@ -7454,7 +7539,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:530 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:424 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:806 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:817 msgid "&Rating:" msgstr "" @@ -7462,7 +7547,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:532 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:425 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:426 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:807 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:818 msgid "Rating of this book. 0-5 stars" msgstr "" @@ -7483,7 +7568,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:539 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:431 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:432 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:148 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:152 msgid "Open Tag Editor" msgstr "" @@ -7536,7 +7621,7 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:558 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:440 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1015 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1026 msgid "&Date:" msgstr "" @@ -7609,14 +7694,14 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:581 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:465 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:456 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:609 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:460 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:613 msgid "&Basic metadata" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:582 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:466 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:463 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:467 msgid "&Custom metadata" msgstr "" @@ -7776,38 +7861,38 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:122 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:128 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:274 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:281 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:278 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:285 msgid "Could not read cover" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:123 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:275 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:279 msgid "Could not read cover from %s format" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:129 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:282 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:286 msgid "The cover in the %s format is invalid" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:158 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:766 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:777 msgid "Cover size: %dx%d pixels" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:195 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:693 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:704 msgid "Not a valid picture" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:214 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:717 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:728 msgid "Specify title and author" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:215 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:718 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:729 msgid "You must specify a title and author before generating a cover" msgstr "" @@ -7850,44 +7935,44 @@ msgid "The cover is not a valid picture" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:307 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:532 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:543 msgid "Choose formats for " msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:338 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:564 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:575 msgid "No permission" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:339 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:565 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:576 msgid "You do not have permission to read the following files:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:366 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:367 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:595 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:596 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:606 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:607 msgid "No format selected" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:378 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:607 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:618 msgid "Could not read metadata" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:379 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:608 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:619 msgid "Could not read metadata from %s format" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:453 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:232 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:233 msgid " The green color indicates that the current author sort matches the current author" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:456 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:235 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:236 msgid " The red color indicates that the current author sort does not match the current author. No action is required if this is what you want." msgstr "" @@ -7911,8 +7996,8 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:475 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:484 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:411 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:416 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:415 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:420 msgid "Save changes and edit the metadata of %s" msgstr "" @@ -7927,22 +8012,22 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:690 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:695 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:954 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:965 msgid "This ISBN number is valid" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:698 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:957 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:968 msgid "This ISBN number is invalid" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:783 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:883 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:894 msgid "Tags changed" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:784 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:884 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:895 msgid "You have changed the tags. In order to use the tags editor, you must either discard or apply these changes. Apply changes?" msgstr "" @@ -7971,12 +8056,12 @@ msgid "You must specify at least one of ISBN, Title, Authors or Publisher" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:961 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:358 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:362 msgid "Permission denied" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:962 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:359 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:363 msgid "Could not open %s. Is it being used by another program?" msgstr "" @@ -7996,12 +8081,12 @@ msgid "" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:413 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:118 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:122 msgid "Swap the author and title" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:415 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:107 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:106 msgid "" "Automatically create the author sort entry based on the current author entry.\n" "Using this button to create author sort will change author sort from red to green." @@ -8028,7 +8113,7 @@ msgid "" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:436 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:125 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:129 msgid "Remove unused series (Series that have no books)" msgstr "" @@ -8041,7 +8126,7 @@ msgid "dd MMM yyyy" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:442 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1066 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1077 msgid "Publishe&d:" msgstr "" @@ -8050,7 +8135,7 @@ msgid "&Fetch metadata from server" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:448 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:627 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:638 msgid "&Browse" msgstr "" @@ -8059,7 +8144,7 @@ msgid "Remove border (if any) from cover" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:450 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:629 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:640 msgid "T&rim" msgstr "" @@ -8068,12 +8153,12 @@ msgid "Reset cover to default" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:452 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:631 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:642 msgid "&Remove" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:453 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:637 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:648 msgid "Download co&ver" msgstr "" @@ -8082,7 +8167,7 @@ msgid "Generate a default cover based on the title and author" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:455 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:638 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:649 msgid "&Generate cover" msgstr "" @@ -8099,7 +8184,7 @@ msgid "Remove the selected formats for this book from the database." msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:461 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:450 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:461 msgid "Set the cover for the book from the selected format" msgstr "" @@ -8108,7 +8193,7 @@ msgid "Update metadata from the metadata in the selected format" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:464 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:674 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:678 msgid "&Comments" msgstr "" @@ -8524,7 +8609,7 @@ msgid "&Author:" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/search_ui.py:215 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:847 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:858 msgid "Ta&gs:" msgstr "" @@ -8997,6 +9082,26 @@ msgstr "" msgid "Failed to download from %r with error: %s" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/ebook_download.py:41 +msgid "No file specified to download." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/ebook_download.py:66 +msgid "Not a support ebook format." +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/ebook_download.py:87 +msgid "Downloading %s" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/ebook_download.py:99 +msgid "Downloading" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/ebook_download.py:103 +msgid "Failed to download ebook" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/email.py:91 msgid "Email %s to %s" msgstr "" @@ -9273,7 +9378,7 @@ msgid "Show books in the main memory of the device" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:67 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1017 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1016 msgid "Card A" msgstr "" @@ -9282,7 +9387,7 @@ msgid "Show books in storage card A" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/layout.py:69 -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1019 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1018 msgid "Card B" msgstr "" @@ -9322,23 +9427,15 @@ msgstr "" msgid "Reset Quick Search" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:207 -msgid "Change the way searching for books works" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:219 +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:213 msgid "Copy current search text (instead of search name)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:225 +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:219 msgid "Save current search under the name shown in the box" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:231 -msgid "Delete current saved search" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:267 +#: /home/kovid/work/calibre/src/calibre/gui2/layout.py:254 msgid "Donate" msgstr "" @@ -9435,7 +9532,7 @@ msgstr "" msgid "Restore default layout" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:830 +#: /home/kovid/work/calibre/src/calibre/gui2/library/views.py:832 msgid "Dropping onto a device is not supported. First add the book to the calibre library." msgstr "" @@ -9492,6 +9589,7 @@ msgid "Previous Page" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/lrf_renderer/main_ui.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/store/web_store_dialog_ui.py:62 #: /home/kovid/work/calibre/src/calibre/gui2/viewer/main_ui.py:193 msgid "Back" msgstr "" @@ -9654,49 +9752,49 @@ msgstr "" msgid "Author s&ort:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:352 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:363 msgid "&Number:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:433 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:444 msgid "" "Last modified: %s\n" "\n" "Double click to view" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:736 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:747 msgid "Invalid cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:737 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:748 msgid "Could not change cover as the image is invalid." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:764 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:775 msgid "This book has no cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:814 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:825 msgid "stars" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:907 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:918 msgid "I&ds:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:908 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:919 msgid "" "Edit the identifiers for this book. For example: \n" "\n" "%s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:964 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:975 msgid "&Publisher:" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1034 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/basic_widgets.py:1045 msgid "Clear date" msgstr "" @@ -9837,40 +9935,48 @@ msgid "Downloaded metadata fields" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:75 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:233 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:237 msgid "Edit Metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:164 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:112 +msgid "Set author sort from author" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:113 +msgid "Set author from author sort" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:168 msgid "&Download metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:174 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:178 msgid "Change how calibre downloads metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:504 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:693 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:508 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:697 msgid "Change cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:553 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:557 msgid "Co&mments" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:592 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:596 msgid "&Metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:597 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:601 msgid "&Cover and formats" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:616 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:620 msgid "Configure metadata downloading" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:662 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:666 msgid "C&ustom metadata" msgstr "" @@ -9946,7 +10052,7 @@ msgstr "" msgid "Restore settings to default values. You have to click Apply to actually save the default settings." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/__init__.py:325 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/__init__.py:328 msgid "Configure " msgstr "" @@ -10948,71 +11054,71 @@ msgstr "" msgid "Search for plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:225 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:226 msgid "No matches" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:226 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:227 msgid "Could not find any matching plugins" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:267 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:268 msgid "Add plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:275 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:276 msgid "Installing plugins is a security risk. Plugins can contain a virus/malware. Only install it if you got it from a trusted source. Are you sure you want to proceed?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:287 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:288 msgid "Plugin {0} successfully installed under {1} plugins. You may have to restart calibre for the plugin to take effect." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:295 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:296 msgid "No valid plugin path" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:296 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:297 msgid "%s is not a valid plugin path" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:305 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:306 msgid "Select an actual plugin under %s to customize" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:311 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:312 msgid "Plugin cannot be disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:312 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:313 msgid "The plugin: %s cannot be disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:322 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:323 msgid "Plugin not customizable" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:323 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:324 msgid "Plugin: %s does not need customization" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:329 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:330 msgid "Must restart" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:330 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:331 msgid "You must restart calibre before you can configure the %s plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:335 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:336 msgid "Plugin {0} successfully removed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:343 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:344 msgid "Cannot remove builtin plugin" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:344 +#: /home/kovid/work/calibre/src/calibre/gui2/preferences/plugins.py:345 msgid " cannot be removed. It is a builtin plugin. Try disabling it instead." msgstr "" @@ -11242,7 +11348,7 @@ msgid "Here you can control how calibre will save your books when you click the msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/preferences/server.py:70 -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:382 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:418 msgid "Failed to start content server" msgstr "" @@ -11591,34 +11697,23 @@ msgstr "" msgid "Apply any changes you made to this tweak" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:93 -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:277 -#: /home/kovid/work/calibre/src/calibre/gui2/widgets.py:653 -#: /home/kovid/work/calibre/src/calibre/library/server/browse.py:280 -msgid "Search" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:321 -msgid "The selected search will be permanently deleted. Are you sure?" -msgstr "" - -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:364 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:346 msgid "Search (For Advanced Search click the button to the left)" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:388 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:369 msgid "Enable or disable search highlighting." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:447 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:424 msgid "Saved Searches" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:449 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:426 msgid "Choose saved search or enter name for new saved search" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:462 +#: /home/kovid/work/calibre/src/calibre/gui2/search_box.py:439 #: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:34 #: /home/kovid/work/calibre/src/calibre/gui2/search_restriction_mixin.py:42 msgid "*Current search" @@ -11696,6 +11791,73 @@ msgstr "" msgid "&Alternate shortcut:" msgstr "" +#: +#: /home/kovid/work/calibre/src/calibre/gui2/store/basic_config_widget_ui.py:38 +msgid "Added Tags:" +msgstr "" + +#: +#: /home/kovid/work/calibre/src/calibre/gui2/store/basic_config_widget_ui.py:39 +msgid "Open store in external web browswer" +msgstr "" + +#: +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_store_dialog_ui.py:62 +msgid "Search:" +msgstr "" + +#: +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_store_dialog_ui.py:63 +msgid "Books:" +msgstr "" + +#: +#: /home/kovid/work/calibre/src/calibre/gui2/store/mobileread_store_dialog_ui.py:65 +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:111 +#: /home/kovid/work/calibre/src/calibre/gui2/store/web_store_dialog_ui.py:63 +msgid "Close" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/search.py:346 +msgid "Price" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:104 +msgid "calibre Store Search" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:105 +msgid "Query:" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:107 +msgid "Stores" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:108 +msgid "All" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/search_ui.py:109 +msgid "Invert" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/web_control.py:69 +msgid "File is not a supported ebook type. Save to disk?" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/web_store_dialog_ui.py:59 +msgid "Home" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/web_store_dialog_ui.py:60 +msgid "Reload" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/store/web_store_dialog_ui.py:61 +msgid "%p%" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:345 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:375 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:404 @@ -11760,11 +11922,13 @@ msgid "Manage %s" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:454 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1844 msgid "Manage Saved Searches" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:462 #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:466 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1842 msgid "Manage User Categories" msgstr "" @@ -11816,52 +11980,68 @@ msgstr "" msgid "The saved search name %s is already used." msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1834 +msgid "Manage Authors" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1836 +msgid "Manage Series" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1838 +msgid "Manage Publishers" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1840 +msgid "Manage Tags" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1852 msgid "Invalid search restriction" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1837 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1853 msgid "The current search restriction is invalid" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1853 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1869 msgid "New Category" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1904 -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1907 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1920 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1923 msgid "Delete user category" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1905 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1921 msgid "%s is not a user category" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1908 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1924 msgid "%s contains items. Do you really want to delete it?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1929 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1945 msgid "Remove category" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1930 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1946 msgid "User category %s does not exist" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1949 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1965 msgid "Add to user category" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1950 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:1966 msgid "A user category %s does not exist" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2073 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2089 msgid "Find item in tag browser" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2076 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2092 msgid "" "Search for items. This is a \"contains\" search; items containing the\n" "text anywhere in the name will be found. You can limit the search\n" @@ -11871,60 +12051,60 @@ msgid "" "containing the text \"foo\"" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2085 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2101 msgid "ALT+f" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2089 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2105 msgid "F&ind" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2090 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2106 msgid "Find the first/next matching item" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2095 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2111 msgid "Collapse all categories" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2119 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2135 msgid "No More Matches.

Click Find again to go to first match" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2132 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2148 msgid "Sort by name" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2132 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2148 msgid "Sort by popularity" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2133 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2149 msgid "Sort by average rating" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2136 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2152 msgid "Set the sort order for entries in the Tag Browser" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2143 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2159 msgid "Match all" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2143 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2159 msgid "Match any" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2148 +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2164 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:2152 -msgid "Manage &user categories" +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2171 +msgid "Manage authors, tags, etc" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2155 -msgid "Add your own categories to the Tag Browser" +#: /home/kovid/work/calibre/src/calibre/gui2/tag_view.py:2172 +msgid "All of these category_managers are available by right-clicking on items in the tag browser above" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/tools.py:65 @@ -11965,58 +12145,58 @@ msgstr "" msgid "The following books have already been converted to %s format. Do you wish to reconvert them?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:156 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:189 msgid "&Donate to support calibre" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:189 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:225 msgid "&Restore" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:194 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:230 msgid "&Eject connected device" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:239 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:275 msgid "Calibre Quick Start Guide" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:305 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:341 msgid "Debug mode" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:306 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:342 msgid "You have started calibre in debug mode. After you quit calibre, the debug log will be available in the file: %s

The log will be displayed automatically." msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:506 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:542 msgid "Conversion Error" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:529 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:565 msgid "Recipe Disabled" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:545 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:581 msgid "Failed" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:578 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:614 msgid "There are active jobs. Are you sure you want to quit?" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:581 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:617 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:585 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:621 msgid "Active jobs" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:653 +#: /home/kovid/work/calibre/src/calibre/gui2/ui.py:689 msgid "will keep running in the system tray. To close it, choose Quit in the context menu of the system tray." msgstr "" @@ -13613,19 +13793,19 @@ msgstr "" msgid "%sAverage rating is %3.1f" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:1015 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:1014 msgid "Main" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3075 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3074 msgid "

Migrating old database to ebook library in %s

" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3104 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3103 msgid "Copying %s" msgstr "" -#: /home/kovid/work/calibre/src/calibre/library/database2.py:3121 +#: /home/kovid/work/calibre/src/calibre/library/database2.py:3120 msgid "Compacting database" msgstr ""