diff --git a/Changelog.yaml b/Changelog.yaml index 4def62b6b2..1fcb3f0e0b 100644 --- a/Changelog.yaml +++ b/Changelog.yaml @@ -4,10 +4,12 @@ # for important features/bug fixes. # Also, each release can have new and improved recipes. -- version: 0.7.41 +- version: 0.7.42 date: 2011-01-21 new features: + - title: "0.7.42 is a re-release of 0.7.41, because conversion to MOBI was broken in 0.7.41" + - title: "Conversions: Replace the remove header/footer options with a more geenric search replace option, that allows you to not only remove but also replace text" - title: "Conversion: The preprocess html option has now become a new 'Heuristic Processing' option which allows you to control exactly which heuristics are used" diff --git a/resources/recipes/nrc.nl.recipe b/resources/recipes/nrc.nl.recipe index 6e90a05aac..60522ff90e 100644 --- a/resources/recipes/nrc.nl.recipe +++ b/resources/recipes/nrc.nl.recipe @@ -1,5 +1,5 @@ __license__ = 'GPL v3' -__copyright__ = '2010, Darko Miletic ' +__copyright__ = '2010-2011, Darko Miletic ' ''' nrc.nl ''' @@ -15,13 +15,18 @@ class Pagina12(BasicNewsRecipe): oldest_article = 2 max_articles_per_feed = 200 no_stylesheets = True - encoding = 'cp1252' + encoding = 'utf8' use_embedded_content = False language = 'nl' country = 'NL' remove_empty_feeds = True masthead_url = 'http://www.nrc.nl/nrc.nl/images/logo_nrc.png' - extra_css = ' body{font-family: Verdana,Arial,Helvetica,sans-serif } img{margin-bottom: 0.4em} h1,h2,h3{text-align:left} ' + extra_css = """ + body{font-family: Georgia,serif } + img{margin-bottom: 0.4em; display: block} + .bijschrift,.sectie{font-size: x-small} + .sectie{color: gray} + """ conversion_options = { 'comment' : description @@ -30,21 +35,42 @@ class Pagina12(BasicNewsRecipe): , 'language' : language } - keep_only_tags = [dict(name='div',attrs={'class':'article clearfix'})] - - + keep_only_tags = [dict(attrs={'class':'uitstekendekeus'})] + remove_tags = [ + dict(name=['meta','base','link','object','embed']) + ,dict(attrs={'class':['reclamespace','tags-and-sharing']}) + ] + remove_attributes=['lang'] + feeds = [ - (u'Voorpagina' , u'http://feeds.feedburner.com/NRCHandelsbladVoorpagina' ) - ,(u'Binnenland' , u'http://feeds.feedburner.com/NRCHandelsbladBinnenland' ) - ,(u'Buitenland' , u'http://feeds.feedburner.com/NRCHandelsbladBuitenland' ) - ,(u'Economie' , u'http://feeds.feedburner.com/NRCHandelsbladEconomie' ) - ,(u'Kunst & Film' , u'http://feeds.feedburner.com/nrc/NRCHandelsbladKunstEnFilm') - ,(u'Sport' , u'http://feeds.feedburner.com/NRCHandelsbladSport' ) - ,(u'Wetenschap ' , u'http://www.nrc.nl/rss/wetenschap' ) + (u'Voor nieuws', u'http://www.nrc.nl/nieuws/categorie/nieuws/rss.php' ) + ,(u'Binnenland' , u'http://www.nrc.nl/nieuws/categorie/binnenland/rss.php' ) + ,(u'Buitenland' , u'http://www.nrc.nl/nieuws/categorie/buitenland/rss.php' ) + ,(u'Economie' , u'http://www.nrc.nl/nieuws/categorie/economie/rss.php' ) + ,(u'Cultuur' , u'http://www.nrc.nl/nieuws/categorie/cultuur/rss.php' ) + ,(u'Sport' , u'http://www.nrc.nl/nieuws/categorie/sport/rss.php' ) + ,(u'Wetenschap ', u'http://www.nrc.nl/nieuws/categorie/wetenschap-nieuws/rss.php') ] - def print_version(self, url): - return url + '?service=Print' - def preprocess_html(self, soup): - return self.adeify_images(soup) + for item in soup.findAll(style=True): + del item['style'] + for item in soup.findAll('a'): + limg = item.find('img') + if item.string is not None: + str = item.string + item.replaceWith(str) + else: + if limg: + item.name = 'div' + atritems =['href','target','rel'] + for atit in atritems: + if item.has_key(atit): + del item[atit] + else: + str = self.tag_to_string(item) + item.replaceWith(str) + for item in soup.findAll('img'): + if not item.has_key('alt'): + item['alt'] = 'image' + return soup diff --git a/resources/recipes/roger_ebert.recipe b/resources/recipes/roger_ebert.recipe new file mode 100644 index 0000000000..2ea5b52a45 --- /dev/null +++ b/resources/recipes/roger_ebert.recipe @@ -0,0 +1,120 @@ +import re +import urllib2 +from calibre.web.feeds.news import BasicNewsRecipe +from calibre.ebooks.BeautifulSoup import BeautifulSoup, SoupStrainer + +class Ebert(BasicNewsRecipe): + title = 'Roger Ebert' + __author__ = 'Shane Erstad' + description = 'Roger Ebert Movie Reviews' + publisher = 'Chicago Sun Times' + category = 'movies' + oldest_article = 8 + max_articles_per_feed = 100 + no_stylesheets = True + use_embedded_content = False + encoding = 'utf-8' + masthead_url = 'http://rogerebert.suntimes.com/graphics/global/roger.jpg' + language = 'en' + remove_empty_feeds = False + PREFIX = 'http://rogerebert.suntimes.com' + patternReviews = r'(.*?).*?
(.*?)
(.*?)' + patternCommentary = r'
.*?(.*?).*?
(.*?)
' + patternPeople = r'
.*?(.*?).*?
(.*?)
' + patternGlossary = r'
.*?(.*?).*?
(.*?)
' + + + + conversion_options = { + 'comment' : description + , 'tags' : category + , 'publisher' : publisher + , 'language' : language + , 'linearize_tables' : True + } + + + feeds = [ + (u'Reviews' , u'http://rogerebert.suntimes.com/apps/pbcs.dll/section?category=reviews' ) + ,(u'Commentary' , u'http://rogerebert.suntimes.com/apps/pbcs.dll/section?category=COMMENTARY') + ,(u'Great Movies' , u'http://rogerebert.suntimes.com/apps/pbcs.dll/section?category=REVIEWS08') + ,(u'People' , u'http://rogerebert.suntimes.com/apps/pbcs.dll/section?category=PEOPLE') + ,(u'Glossary' , u'http://rogerebert.suntimes.com/apps/pbcs.dll/section?category=GLOSSARY') + + ] + + preprocess_regexps = [ + (re.compile(r'.*?This is a printer friendly.*?.*?
', re.DOTALL|re.IGNORECASE), + lambda m: '') + ] + + + + def print_version(self, url): + return url + '&template=printart' + + def parse_index(self): + totalfeeds = [] + lfeeds = self.get_feeds() + for feedobj in lfeeds: + feedtitle, feedurl = feedobj + self.log('\tFeedurl: ', feedurl) + self.report_progress(0, _('Fetching feed')+' %s...'%(feedtitle if feedtitle else feedurl)) + articles = [] + page = urllib2.urlopen(feedurl).read() + + if feedtitle == 'Reviews' or feedtitle == 'Great Movies': + pattern = self.patternReviews + elif feedtitle == 'Commentary': + pattern = self.patternCommentary + elif feedtitle == 'People': + pattern = self.patternPeople + elif feedtitle == 'Glossary': + pattern = self.patternGlossary + + + regex = re.compile(pattern, re.IGNORECASE|re.DOTALL) + + for match in regex.finditer(page): + if feedtitle == 'Reviews' or feedtitle == 'Great Movies': + movietitle = match.group(1) + thislink = match.group(2) + description = match.group(3) + elif feedtitle == 'Commentary' or feedtitle == 'People' or feedtitle == 'Glossary': + thislink = match.group(1) + description = match.group(2) + + self.log(thislink) + + for link in BeautifulSoup(thislink, parseOnlyThese=SoupStrainer('a')): + thisurl = self.PREFIX + link['href'] + thislinktext = self.tag_to_string(link) + + if feedtitle == 'Reviews' or feedtitle == 'Great Movies': + thistitle = movietitle + elif feedtitle == 'Commentary' or feedtitle == 'People' or feedtitle == 'Glossary': + thistitle = thislinktext + + if thistitle == '': + thistitle = 'Ebert Journal Post' + + """ + pattern2 = r'AID=\/(.*?)\/' + reg2 = re.compile(pattern2, re.IGNORECASE|re.DOTALL) + match2 = reg2.search(thisurl) + date = match2.group(1) + c = time.strptime(match2.group(1),"%Y%m%d") + date=time.strftime("%a, %b %d, %Y", c) + self.log(date) + """ + + articles.append({ + 'title' :thistitle + ,'date' :'' + ,'url' :thisurl + ,'description':description + }) + totalfeeds.append((feedtitle, articles)) + + return totalfeeds + diff --git a/src/calibre/constants.py b/src/calibre/constants.py index 86b0f56315..e5792aeff8 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.41' +__version__ = '0.7.42' __author__ = "Kovid Goyal " import re diff --git a/src/calibre/ebooks/mobi/writer.py b/src/calibre/ebooks/mobi/writer.py index ed102ecc80..2a71ecd43b 100644 --- a/src/calibre/ebooks/mobi/writer.py +++ b/src/calibre/ebooks/mobi/writer.py @@ -1541,7 +1541,10 @@ class MobiWriter(object): exth.write(data) nrecs += 1 if term == 'rights' : - rights = unicode(oeb.metadata.rights[0]).encode('utf-8') + try: + rights = unicode(oeb.metadata.rights[0]).encode('utf-8') + except: + rights = 'Unknown' exth.write(pack('>II', EXTH_CODES['rights'], len(rights) + 8)) exth.write(rights) diff --git a/src/calibre/ebooks/oeb/base.py b/src/calibre/ebooks/oeb/base.py index e11f6b45be..9389964962 100644 --- a/src/calibre/ebooks/oeb/base.py +++ b/src/calibre/ebooks/oeb/base.py @@ -221,7 +221,10 @@ def rewrite_links(root, link_repl_func, resolve_base_href=False): el.text): stylesheet = parseString(el.text) replaceUrls(stylesheet, link_repl_func) - el.text = '\n'+stylesheet.cssText + '\n' + repl = stylesheet.cssText + if isbytestring(repl): + repl = repl.decode('utf-8') + el.text = '\n'+ repl + '\n' if 'style' in el.attrib: text = el.attrib['style'] @@ -234,8 +237,11 @@ def rewrite_links(root, link_repl_func, resolve_base_href=False): set_property(item) elif v.CSS_PRIMITIVE_VALUE == v.cssValueType: set_property(v) - el.attrib['style'] = stext.cssText.replace('\n', ' ').replace('\r', + repl = stext.cssText.replace('\n', ' ').replace('\r', ' ') + if isbytestring(repl): + repl = repl.decode('utf-8') + el.attrib['style'] = repl diff --git a/src/calibre/ebooks/oeb/transforms/metadata.py b/src/calibre/ebooks/oeb/transforms/metadata.py index dc52f3298c..f1ce31f25b 100644 --- a/src/calibre/ebooks/oeb/transforms/metadata.py +++ b/src/calibre/ebooks/oeb/transforms/metadata.py @@ -84,13 +84,9 @@ def meta_info_to_oeb_metadata(mi, m, log, override_input_metadata=False): if not mi.is_null('rights'): m.clear('rights') m.add('rights', mi.rights) - elif override_input_metadata: - m.clear('rights') if not mi.is_null('publication_type'): m.clear('publication_type') m.add('publication_type', mi.publication_type) - elif override_input_metadata: - m.clear('publication_type') if not m.timestamp: m.add('timestamp', isoformat(now())) diff --git a/src/calibre/gui2/actions/choose_library.py b/src/calibre/gui2/actions/choose_library.py index 930e5e29aa..d726241432 100644 --- a/src/calibre/gui2/actions/choose_library.py +++ b/src/calibre/gui2/actions/choose_library.py @@ -385,13 +385,27 @@ class ChooseLibraryAction(InterfaceAction): prefs['library_path'] = loc #from calibre.utils.mem import memory - #import weakref, gc - #ref = weakref.ref(self.gui.library_view.model().db) - #before = memory()/1024**2 + #import weakref + #from PyQt4.Qt import QTimer + #self.dbref = weakref.ref(self.gui.library_view.model().db) + #self.before_mem = memory()/1024**2 self.gui.library_moved(loc) - #print gc.get_referrers(ref)[0] - #for i in xrange(3): gc.collect() - #print 'leaked:', memory()/1024**2 - before + #QTimer.singleShot(1000, self.debug_leak) + + def debug_leak(self): + import gc + from calibre.utils.mem import memory + ref = self.dbref + for i in xrange(3): gc.collect() + if ref() is not None: + print 11111, ref() + for r in gc.get_referrers(ref())[:10]: + print r + print + print 'before:', self.before_mem + print 'after:', memory()/1024**2 + self.dbref = self.before_mem = None + def qs_requested(self, idx, *args): self.switch_requested(self.qs_locations[idx]) diff --git a/src/calibre/gui2/convert/regex_builder.py b/src/calibre/gui2/convert/regex_builder.py index ca6e429176..bdcbe3356d 100644 --- a/src/calibre/gui2/convert/regex_builder.py +++ b/src/calibre/gui2/convert/regex_builder.py @@ -35,6 +35,10 @@ class RegexBuilder(QDialog, Ui_RegexBuilder): self.connect(self.button_box, SIGNAL('clicked(QAbstractButton*)'), self.button_clicked) self.connect(self.regex, SIGNAL('textChanged(QString)'), self.regex_valid) self.connect(self.test, SIGNAL('clicked()'), self.do_test) + self.connect(self.previous, SIGNAL('clicked()'), self.goto_previous) + self.connect(self.next, SIGNAL('clicked()'), self.goto_next) + + self.match_locs = [] def regex_valid(self): regex = unicode(self.regex.text()) @@ -42,17 +46,23 @@ class RegexBuilder(QDialog, Ui_RegexBuilder): try: re.compile(regex) self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgba(0,255,0,20%); }') + return True except: self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgb(255,0,0,20%); }') - return False else: self.regex.setStyleSheet('QLineEdit { color: black; background-color: white; }') self.preview.setExtraSelections([]) - return False - return True + + self.match_locs = [] + self.next.setEnabled(False) + self.previous.setEnabled(False) + self.occurrences.setText('0') + + return False def do_test(self): selections = [] + self.match_locs = [] if self.regex_valid(): text = unicode(self.preview.toPlainText()) regex = unicode(self.regex.text()) @@ -66,9 +76,43 @@ class RegexBuilder(QDialog, Ui_RegexBuilder): es.cursor.setPosition(match.start(), QTextCursor.MoveAnchor) es.cursor.setPosition(match.end(), QTextCursor.KeepAnchor) selections.append(es) + self.match_locs.append((match.start(), match.end())) except: pass self.preview.setExtraSelections(selections) + if self.match_locs: + self.next.setEnabled(True) + self.previous.setEnabled(True) + self.occurrences.setText(str(len(self.match_locs))) + + def goto_previous(self): + pos = self.preview.textCursor().position() + if self.match_locs: + match_loc = len(self.match_locs) - 1 + for i in xrange(len(self.match_locs) - 1, -1, -1): + loc = self.match_locs[i][1] + if pos > loc: + match_loc = i + break + self.goto_loc(self.match_locs[match_loc][1], operation=QTextCursor.Left, n=self.match_locs[match_loc][1] - self.match_locs[match_loc][0]) + + def goto_next(self): + pos = self.preview.textCursor().position() + if self.match_locs: + match_loc = 0 + for i in xrange(len(self.match_locs)): + loc = self.match_locs[i][0] + if pos < loc: + match_loc = i + break + self.goto_loc(self.match_locs[match_loc][0], n=self.match_locs[match_loc][1] - self.match_locs[match_loc][0]) + + def goto_loc(self, loc, operation=QTextCursor.Right, mode=QTextCursor.KeepAnchor, n=0): + cursor = QTextCursor(self.preview.document()) + cursor.setPosition(loc) + if n: + cursor.movePosition(operation, mode, n) + self.preview.setTextCursor(cursor) def select_format(self, db, book_id): format = None @@ -122,11 +166,14 @@ class RegexEdit(QWidget, Ui_Edit): def builder(self): bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self) - if bld.cancelled: - return if bld.exec_() == bld.Accepted: self.edit.setText(bld.regex.text()) + def setObjectName(self, *args): + QWidget.setObjectName(self, *args) + if hasattr(self, 'edit'): + self.edit.initialize('regex_edit_'+unicode(self.objectName())) + def set_msg(self, msg): self.msg.setText(msg) diff --git a/src/calibre/gui2/convert/regex_builder.ui b/src/calibre/gui2/convert/regex_builder.ui index af17917676..63aaa89f36 100644 --- a/src/calibre/gui2/convert/regex_builder.ui +++ b/src/calibre/gui2/convert/regex_builder.ui @@ -6,15 +6,102 @@ 0 0 - 662 - 505 + 580 + 503 Regex Builder - - + + + + + + + Regex: + + + + + + + + + + + + Test + + + + + + + + + + + + + Occurrences: + + + + + + + 0 + + + + + + + Qt::Horizontal + + + + 298 + 20 + + + + + + + + + + Goto: + + + + + + + false + + + &Previous + + + + + + + false + + + &Next + + + + + + + + Preview @@ -36,32 +123,28 @@ - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - Regex: - - - - - + + - + + + Qt::Horizontal + + + + 328 + 20 + + + - - - Test + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok diff --git a/src/calibre/gui2/main.py b/src/calibre/gui2/main.py index aaca398e44..b88b1d680d 100644 --- a/src/calibre/gui2/main.py +++ b/src/calibre/gui2/main.py @@ -150,13 +150,13 @@ class GuiRunner(QObject): if DEBUG: prints('Starting up...') - def start_gui(self): + def start_gui(self, db): from calibre.gui2.ui import Main main = Main(self.opts, gui_debug=self.gui_debug) if self.splash_screen is not None: self.splash_screen.showMessage(_('Initializing user interface...')) self.splash_screen.finish(main) - main.initialize(self.library_path, self.db, self.listener, self.actions) + main.initialize(self.library_path, db, self.listener, self.actions) if DEBUG: prints('Started up in', time.time() - self.startup_time) add_filesystem_book = partial(main.iactions['Add Books'].add_filesystem_book, allow_device=False) @@ -200,8 +200,7 @@ class GuiRunner(QObject): det_msg=traceback.format_exc(), show=True) self.initialization_failed() - self.db = db - self.start_gui() + self.start_gui(db) def initialize_db(self): db = None diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py index 36b5eea940..00f34aa171 100644 --- a/src/calibre/gui2/tag_view.py +++ b/src/calibre/gui2/tag_view.py @@ -114,6 +114,9 @@ class TagsView(QTreeView): # {{{ def set_database(self, db, tag_match, sort_by): self.hidden_categories = config['tag_browser_hidden_categories'] + old = getattr(self, '_model', None) + if old is not None: + old.break_cycles() self._model = TagsModel(db, parent=self, hidden_categories=self.hidden_categories, search_restriction=None, @@ -371,6 +374,9 @@ class TagsView(QTreeView): # {{{ # model. Reason: it is much easier than reconstructing the browser tree. def set_new_model(self, filter_categories_by=None): try: + old = getattr(self, '_model', None) + if old is not None: + old.break_cycles() self._model = TagsModel(self.db, parent=self, hidden_categories=self.hidden_categories, search_restriction=self.search_restriction, @@ -544,6 +550,9 @@ class TagsModel(QAbstractItemModel): # {{{ tooltip=tt, category_key=r) self.refresh(data=data) + def break_cycles(self): + self.db = self.root_item = None + def mimeTypes(self): return ["application/calibre+from_library"] @@ -1125,8 +1134,7 @@ class TagBrowserMixin(object): # {{{ def __init__(self, db): self.library_view.model().count_changed_signal.connect(self.tags_view.recount) - self.tags_view.set_database(self.library_view.model().db, - self.tag_match, self.sort_by) + self.tags_view.set_database(db, self.tag_match, self.sort_by) self.tags_view.tags_marked.connect(self.search.set_search_string) self.tags_view.tag_list_edit.connect(self.do_tags_list_edit) self.tags_view.user_category_edit.connect(self.do_user_categories_edit) diff --git a/src/calibre/library/caches.py b/src/calibre/library/caches.py index 4168360d3a..291d71f572 100644 --- a/src/calibre/library/caches.py +++ b/src/calibre/library/caches.py @@ -42,6 +42,9 @@ class MetadataBackup(Thread): # {{{ def stop(self): self.keep_running = False + # Break cycles so that this object doesn't hold references to db + self.do_write = self.get_metadata_for_dump = self.clear_dirtied = \ + self.set_dirtied = self.db = None def run(self): while self.keep_running: @@ -185,6 +188,11 @@ class ResultCache(SearchQueryParser): # {{{ self.build_date_relop_dict() self.build_numeric_relop_dict() + def break_cycles(self): + self._data = self.field_metadata = self.FIELD_MAP = \ + self.numeric_search_relops = self.date_search_relops = \ + self.all_search_locations = None + def __getitem__(self, row): return self._data[self._map_filtered[row]] diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py index 4b6ab247c5..e0ddcfd7c8 100644 --- a/src/calibre/library/database2.py +++ b/src/calibre/library/database2.py @@ -362,7 +362,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns): self.last_update_check = self.last_modified() def break_cycles(self): - self.data = self.field_metadata = self.prefs = self.listeners = None + self.data.break_cycles() + self.data = self.field_metadata = self.prefs = self.listeners = \ + self.refresh_ondevice = None def initialize_database(self): metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read() diff --git a/src/calibre/translations/calibre.pot b/src/calibre/translations/calibre.pot index 2355ac1c6c..6a837e5833 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.41\n" -"POT-Creation-Date: 2011-01-21 12:09+MST\n" -"PO-Revision-Date: 2011-01-21 12:09+MST\n" +"Project-Id-Version: calibre 0.7.42\n" +"POT-Creation-Date: 2011-01-21 14:53+MST\n" +"PO-Revision-Date: 2011-01-21 14:53+MST\n" "Last-Translator: Automatically generated\n" "Language-Team: LANGUAGE\n" "MIME-Version: 1.0\n" @@ -3155,7 +3155,7 @@ msgid "Copy to Clipboard" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:222 -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:95 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:96 msgid "Copy" msgstr "" @@ -4811,160 +4811,160 @@ msgstr "" msgid "Tab template for catalog.ui" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:68 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:69 msgid "Bold" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:69 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:70 msgid "Italic" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:72 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:73 msgid "Underline" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:74 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:75 msgid "Strikethrough" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:76 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:77 msgid "Superscript" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:78 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:79 msgid "Subscript" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:80 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:81 msgid "Ordered list" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:82 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:83 msgid "Unordered list" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:85 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:86 msgid "Align left" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:87 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:88 msgid "Align center" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:89 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:90 msgid "Align right" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:91 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:92 msgid "Align justified" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:92 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:93 msgid "Undo" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:93 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:94 msgid "Redo" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:94 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:95 msgid "Remove formatting" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:96 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:97 msgid "Paste" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:97 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:98 msgid "Cut" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:99 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:100 msgid "Increase Indentation" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:101 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:102 msgid "Decrease Indentation" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:103 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:104 msgid "Select all" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:108 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:109 msgid "Foreground color" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:113 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:114 msgid "Background color" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:117 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:118 msgid "Style text block" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:119 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:120 msgid "Style the selected text block" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:124 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:125 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior.py:33 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:145 msgid "Normal" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:125 #: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:126 #: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:127 #: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:128 #: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:129 #: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:130 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:131 msgid "Heading" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:131 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:132 msgid "Pre-formatted" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:132 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:133 msgid "Blockquote" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:133 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:134 msgid "Address" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:140 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:141 msgid "Insert link" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:142 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:143 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:79 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:84 msgid "Clear" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:160 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:161 msgid "Choose foreground color" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:166 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:167 msgid "Choose background color" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:171 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:172 msgid "Create link" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:172 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:173 msgid "Enter URL" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:516 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:522 msgid "Normal view" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:517 +#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:523 msgid "HTML Source" msgstr "" @@ -7053,13 +7053,14 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:504 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:465 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:186 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:380 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:509 msgid "&Basic metadata" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:505 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:466 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:193 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:387 msgid "&Custom metadata" msgstr "" @@ -7189,18 +7190,18 @@ 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:343 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:350 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:249 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:256 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:344 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:250 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:351 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:257 msgid "The cover in the %s format is invalid" msgstr "" @@ -7314,7 +7315,7 @@ msgid " The red color warns that the current title sort does not match the curre msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:468 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:45 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:47 #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:102 #: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:221 #: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:384 @@ -7323,13 +7324,13 @@ msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:471 #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:479 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:439 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:443 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:347 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:351 msgid "Save changes and edit the metadata of %s" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:476 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:42 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:44 #: /home/kovid/work/calibre/src/calibre/library/server/browse.py:103 #: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:211 #: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:401 @@ -7382,12 +7383,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:944 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:395 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:302 msgid "Permission denied" msgstr "" #: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:945 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:396 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:303 msgid "Could not open %s. Is it being used by another program?" msgstr "" @@ -7521,6 +7522,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:568 msgid "&Comments" msgstr "" @@ -8958,19 +8960,32 @@ msgstr "" msgid "Details" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:64 -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:302 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:66 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:208 msgid "Edit Metadata" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:232 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:426 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:587 msgid "Change cover" msgstr "" -#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:280 +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:474 msgid "Co&mments" msgstr "" +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:496 +msgid "&Metadata" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:501 +msgid "&Cover and formats" +msgstr "" + +#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:556 +msgid "C&ustom metadata" +msgstr "" + #: /home/kovid/work/calibre/src/calibre/gui2/preferences/__init__.py:36 msgid "Restore settings to default values. You have to click Apply to actually save the default settings." msgstr ""