Merge from trunk

This commit is contained in:
Charles Haley 2011-01-23 11:52:45 +00:00
commit 3c541274dd
15 changed files with 457 additions and 128 deletions

View File

@ -4,10 +4,12 @@
# for important features/bug fixes. # for important features/bug fixes.
# Also, each release can have new and improved recipes. # Also, each release can have new and improved recipes.
- version: 0.7.41 - version: 0.7.42
date: 2011-01-21 date: 2011-01-21
new features: 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: "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" - title: "Conversion: The preprocess html option has now become a new 'Heuristic Processing' option which allows you to control exactly which heuristics are used"

View File

@ -1,5 +1,5 @@
__license__ = 'GPL v3' __license__ = 'GPL v3'
__copyright__ = '2010, Darko Miletic <darko.miletic at gmail.com>' __copyright__ = '2010-2011, Darko Miletic <darko.miletic at gmail.com>'
''' '''
nrc.nl nrc.nl
''' '''
@ -15,13 +15,18 @@ class Pagina12(BasicNewsRecipe):
oldest_article = 2 oldest_article = 2
max_articles_per_feed = 200 max_articles_per_feed = 200
no_stylesheets = True no_stylesheets = True
encoding = 'cp1252' encoding = 'utf8'
use_embedded_content = False use_embedded_content = False
language = 'nl' language = 'nl'
country = 'NL' country = 'NL'
remove_empty_feeds = True remove_empty_feeds = True
masthead_url = 'http://www.nrc.nl/nrc.nl/images/logo_nrc.png' 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 = { conversion_options = {
'comment' : description 'comment' : description
@ -30,21 +35,42 @@ class Pagina12(BasicNewsRecipe):
, 'language' : language , '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 = [ feeds = [
(u'Voorpagina' , u'http://feeds.feedburner.com/NRCHandelsbladVoorpagina' ) (u'Voor nieuws', u'http://www.nrc.nl/nieuws/categorie/nieuws/rss.php' )
,(u'Binnenland' , u'http://feeds.feedburner.com/NRCHandelsbladBinnenland' ) ,(u'Binnenland' , u'http://www.nrc.nl/nieuws/categorie/binnenland/rss.php' )
,(u'Buitenland' , u'http://feeds.feedburner.com/NRCHandelsbladBuitenland' ) ,(u'Buitenland' , u'http://www.nrc.nl/nieuws/categorie/buitenland/rss.php' )
,(u'Economie' , u'http://feeds.feedburner.com/NRCHandelsbladEconomie' ) ,(u'Economie' , u'http://www.nrc.nl/nieuws/categorie/economie/rss.php' )
,(u'Kunst & Film' , u'http://feeds.feedburner.com/nrc/NRCHandelsbladKunstEnFilm') ,(u'Cultuur' , u'http://www.nrc.nl/nieuws/categorie/cultuur/rss.php' )
,(u'Sport' , u'http://feeds.feedburner.com/NRCHandelsbladSport' ) ,(u'Sport' , u'http://www.nrc.nl/nieuws/categorie/sport/rss.php' )
,(u'Wetenschap ' , u'http://www.nrc.nl/rss/wetenschap' ) ,(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): 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

View File

@ -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'<span class="*?movietitle"*?>(.*?)</span>.*?<div class="*?headline"*?>(.*?)</div>(.*?)</div>'
patternCommentary = r'<div class="*?headline"*?>.*?(<a href="/apps/pbcs.dll/article\?AID=.*?COMMENTARY.*?" id="ltred">.*?</a>).*?<div class="blurb clear">(.*?)</div>'
patternPeople = r'<div class="*?headline"*?>.*?(<a href="/apps/pbcs.dll/article\?AID=.*?PEOPLE.*?" id="ltred">.*?</a>).*?<div class="blurb clear">(.*?)</div>'
patternGlossary = r'<div class="*?headline"*?>.*?(<a href="/apps/pbcs.dll/article\?AID=.*?GLOSSARY.*?" id="ltred">.*?</a>).*?<div class="blurb clear">(.*?)</div>'
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'<font.*?>.*?This is a printer friendly.*?</font>.*?<hr>', 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

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net' __copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
__appname__ = 'calibre' __appname__ = 'calibre'
__version__ = '0.7.41' __version__ = '0.7.42'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re import re

View File

@ -1541,7 +1541,10 @@ class MobiWriter(object):
exth.write(data) exth.write(data)
nrecs += 1 nrecs += 1
if term == 'rights' : 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(pack('>II', EXTH_CODES['rights'], len(rights) + 8))
exth.write(rights) exth.write(rights)

View File

@ -221,7 +221,10 @@ def rewrite_links(root, link_repl_func, resolve_base_href=False):
el.text): el.text):
stylesheet = parseString(el.text) stylesheet = parseString(el.text)
replaceUrls(stylesheet, link_repl_func) 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: if 'style' in el.attrib:
text = el.attrib['style'] text = el.attrib['style']
@ -234,8 +237,11 @@ def rewrite_links(root, link_repl_func, resolve_base_href=False):
set_property(item) set_property(item)
elif v.CSS_PRIMITIVE_VALUE == v.cssValueType: elif v.CSS_PRIMITIVE_VALUE == v.cssValueType:
set_property(v) 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

View File

@ -84,13 +84,9 @@ def meta_info_to_oeb_metadata(mi, m, log, override_input_metadata=False):
if not mi.is_null('rights'): if not mi.is_null('rights'):
m.clear('rights') m.clear('rights')
m.add('rights', mi.rights) m.add('rights', mi.rights)
elif override_input_metadata:
m.clear('rights')
if not mi.is_null('publication_type'): if not mi.is_null('publication_type'):
m.clear('publication_type') m.clear('publication_type')
m.add('publication_type', mi.publication_type) m.add('publication_type', mi.publication_type)
elif override_input_metadata:
m.clear('publication_type')
if not m.timestamp: if not m.timestamp:
m.add('timestamp', isoformat(now())) m.add('timestamp', isoformat(now()))

View File

@ -385,13 +385,27 @@ class ChooseLibraryAction(InterfaceAction):
prefs['library_path'] = loc prefs['library_path'] = loc
#from calibre.utils.mem import memory #from calibre.utils.mem import memory
#import weakref, gc #import weakref
#ref = weakref.ref(self.gui.library_view.model().db) #from PyQt4.Qt import QTimer
#before = memory()/1024**2 #self.dbref = weakref.ref(self.gui.library_view.model().db)
#self.before_mem = memory()/1024**2
self.gui.library_moved(loc) self.gui.library_moved(loc)
#print gc.get_referrers(ref)[0] #QTimer.singleShot(1000, self.debug_leak)
#for i in xrange(3): gc.collect()
#print 'leaked:', memory()/1024**2 - before 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): def qs_requested(self, idx, *args):
self.switch_requested(self.qs_locations[idx]) self.switch_requested(self.qs_locations[idx])

View File

@ -35,6 +35,10 @@ class RegexBuilder(QDialog, Ui_RegexBuilder):
self.connect(self.button_box, SIGNAL('clicked(QAbstractButton*)'), self.button_clicked) self.connect(self.button_box, SIGNAL('clicked(QAbstractButton*)'), self.button_clicked)
self.connect(self.regex, SIGNAL('textChanged(QString)'), self.regex_valid) self.connect(self.regex, SIGNAL('textChanged(QString)'), self.regex_valid)
self.connect(self.test, SIGNAL('clicked()'), self.do_test) 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): def regex_valid(self):
regex = unicode(self.regex.text()) regex = unicode(self.regex.text())
@ -42,17 +46,23 @@ class RegexBuilder(QDialog, Ui_RegexBuilder):
try: try:
re.compile(regex) re.compile(regex)
self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgba(0,255,0,20%); }') self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgba(0,255,0,20%); }')
return True
except: except:
self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgb(255,0,0,20%); }') self.regex.setStyleSheet('QLineEdit { color: black; background-color: rgb(255,0,0,20%); }')
return False
else: else:
self.regex.setStyleSheet('QLineEdit { color: black; background-color: white; }') self.regex.setStyleSheet('QLineEdit { color: black; background-color: white; }')
self.preview.setExtraSelections([]) 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): def do_test(self):
selections = [] selections = []
self.match_locs = []
if self.regex_valid(): if self.regex_valid():
text = unicode(self.preview.toPlainText()) text = unicode(self.preview.toPlainText())
regex = unicode(self.regex.text()) 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.start(), QTextCursor.MoveAnchor)
es.cursor.setPosition(match.end(), QTextCursor.KeepAnchor) es.cursor.setPosition(match.end(), QTextCursor.KeepAnchor)
selections.append(es) selections.append(es)
self.match_locs.append((match.start(), match.end()))
except: except:
pass pass
self.preview.setExtraSelections(selections) 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): def select_format(self, db, book_id):
format = None format = None
@ -122,11 +166,14 @@ class RegexEdit(QWidget, Ui_Edit):
def builder(self): def builder(self):
bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self) bld = RegexBuilder(self.db, self.book_id, self.edit.text(), self)
if bld.cancelled:
return
if bld.exec_() == bld.Accepted: if bld.exec_() == bld.Accepted:
self.edit.setText(bld.regex.text()) 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): def set_msg(self, msg):
self.msg.setText(msg) self.msg.setText(msg)

View File

@ -6,15 +6,102 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>662</width> <width>580</width>
<height>505</height> <height>503</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
<string>Regex Builder</string> <string>Regex Builder</string>
</property> </property>
<layout class="QGridLayout" name="gridLayout"> <layout class="QVBoxLayout" name="verticalLayout_2">
<item row="1" column="0" colspan="5"> <item>
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Regex:</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLineEdit" name="regex"/>
</item>
<item>
<widget class="QPushButton" name="test">
<property name="text">
<string>Test</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="label_3">
<property name="text">
<string>Occurrences:</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="occurrences">
<property name="text">
<string>0</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>298</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLabel" name="label_2">
<property name="text">
<string>Goto:</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="previous">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Previous</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="next">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>&amp;Next</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QGroupBox" name="groupBox"> <widget class="QGroupBox" name="groupBox">
<property name="title"> <property name="title">
<string>Preview</string> <string>Preview</string>
@ -36,32 +123,28 @@
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="2" column="4"> <item>
<widget class="QDialogButtonBox" name="button_box"> <layout class="QHBoxLayout" name="horizontalLayout_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Regex:</string>
</property>
</widget>
</item>
<item row="0" column="1" colspan="4">
<layout class="QHBoxLayout" name="horizontalLayout">
<item> <item>
<widget class="QLineEdit" name="regex"/> <spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>328</width>
<height>20</height>
</size>
</property>
</spacer>
</item> </item>
<item> <item>
<widget class="QPushButton" name="test"> <widget class="QDialogButtonBox" name="button_box">
<property name="text"> <property name="orientation">
<string>Test</string> <enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property> </property>
</widget> </widget>
</item> </item>

View File

@ -150,13 +150,13 @@ class GuiRunner(QObject):
if DEBUG: if DEBUG:
prints('Starting up...') prints('Starting up...')
def start_gui(self): def start_gui(self, db):
from calibre.gui2.ui import Main from calibre.gui2.ui import Main
main = Main(self.opts, gui_debug=self.gui_debug) main = Main(self.opts, gui_debug=self.gui_debug)
if self.splash_screen is not None: if self.splash_screen is not None:
self.splash_screen.showMessage(_('Initializing user interface...')) self.splash_screen.showMessage(_('Initializing user interface...'))
self.splash_screen.finish(main) 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: if DEBUG:
prints('Started up in', time.time() - self.startup_time) prints('Started up in', time.time() - self.startup_time)
add_filesystem_book = partial(main.iactions['Add Books'].add_filesystem_book, allow_device=False) 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) det_msg=traceback.format_exc(), show=True)
self.initialization_failed() self.initialization_failed()
self.db = db self.start_gui(db)
self.start_gui()
def initialize_db(self): def initialize_db(self):
db = None db = None

View File

@ -114,6 +114,9 @@ class TagsView(QTreeView): # {{{
def set_database(self, db, tag_match, sort_by): def set_database(self, db, tag_match, sort_by):
self.hidden_categories = config['tag_browser_hidden_categories'] 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, self._model = TagsModel(db, parent=self,
hidden_categories=self.hidden_categories, hidden_categories=self.hidden_categories,
search_restriction=None, search_restriction=None,
@ -371,6 +374,9 @@ class TagsView(QTreeView): # {{{
# model. Reason: it is much easier than reconstructing the browser tree. # model. Reason: it is much easier than reconstructing the browser tree.
def set_new_model(self, filter_categories_by=None): def set_new_model(self, filter_categories_by=None):
try: try:
old = getattr(self, '_model', None)
if old is not None:
old.break_cycles()
self._model = TagsModel(self.db, parent=self, self._model = TagsModel(self.db, parent=self,
hidden_categories=self.hidden_categories, hidden_categories=self.hidden_categories,
search_restriction=self.search_restriction, search_restriction=self.search_restriction,
@ -544,6 +550,9 @@ class TagsModel(QAbstractItemModel): # {{{
tooltip=tt, category_key=r) tooltip=tt, category_key=r)
self.refresh(data=data) self.refresh(data=data)
def break_cycles(self):
self.db = self.root_item = None
def mimeTypes(self): def mimeTypes(self):
return ["application/calibre+from_library"] return ["application/calibre+from_library"]
@ -1125,8 +1134,7 @@ class TagBrowserMixin(object): # {{{
def __init__(self, db): def __init__(self, db):
self.library_view.model().count_changed_signal.connect(self.tags_view.recount) self.library_view.model().count_changed_signal.connect(self.tags_view.recount)
self.tags_view.set_database(self.library_view.model().db, self.tags_view.set_database(db, self.tag_match, self.sort_by)
self.tag_match, self.sort_by)
self.tags_view.tags_marked.connect(self.search.set_search_string) 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.tag_list_edit.connect(self.do_tags_list_edit)
self.tags_view.user_category_edit.connect(self.do_user_categories_edit) self.tags_view.user_category_edit.connect(self.do_user_categories_edit)

View File

@ -42,6 +42,9 @@ class MetadataBackup(Thread): # {{{
def stop(self): def stop(self):
self.keep_running = False 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): def run(self):
while self.keep_running: while self.keep_running:
@ -185,6 +188,11 @@ class ResultCache(SearchQueryParser): # {{{
self.build_date_relop_dict() self.build_date_relop_dict()
self.build_numeric_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): def __getitem__(self, row):
return self._data[self._map_filtered[row]] return self._data[self._map_filtered[row]]

View File

@ -362,7 +362,9 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
self.last_update_check = self.last_modified() self.last_update_check = self.last_modified()
def break_cycles(self): 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): def initialize_database(self):
metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read() metadata_sqlite = open(P('metadata_sqlite.sql'), 'rb').read()

View File

@ -4,9 +4,9 @@
# #
msgid "" msgid ""
msgstr "" msgstr ""
"Project-Id-Version: calibre 0.7.41\n" "Project-Id-Version: calibre 0.7.42\n"
"POT-Creation-Date: 2011-01-21 12:09+MST\n" "POT-Creation-Date: 2011-01-21 14:53+MST\n"
"PO-Revision-Date: 2011-01-21 12:09+MST\n" "PO-Revision-Date: 2011-01-21 14:53+MST\n"
"Last-Translator: Automatically generated\n" "Last-Translator: Automatically generated\n"
"Language-Team: LANGUAGE\n" "Language-Team: LANGUAGE\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@ -3155,7 +3155,7 @@ msgid "Copy to Clipboard"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/__init__.py:222 #: /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" msgid "Copy"
msgstr "" msgstr ""
@ -4811,160 +4811,160 @@ msgstr ""
msgid "Tab template for catalog.ui" msgid "Tab template for catalog.ui"
msgstr "" 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" msgid "Bold"
msgstr "" 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" msgid "Italic"
msgstr "" 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" msgid "Underline"
msgstr "" 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" msgid "Strikethrough"
msgstr "" 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" msgid "Superscript"
msgstr "" 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" msgid "Subscript"
msgstr "" 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" msgid "Ordered list"
msgstr "" 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" msgid "Unordered list"
msgstr "" 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" msgid "Align left"
msgstr "" 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" msgid "Align center"
msgstr "" 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" msgid "Align right"
msgstr "" 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" msgid "Align justified"
msgstr "" 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" msgid "Undo"
msgstr "" 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" msgid "Redo"
msgstr "" 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" msgid "Remove formatting"
msgstr "" 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" msgid "Paste"
msgstr "" 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" msgid "Cut"
msgstr "" 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" msgid "Increase Indentation"
msgstr "" 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" msgid "Decrease Indentation"
msgstr "" 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" msgid "Select all"
msgstr "" 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" msgid "Foreground color"
msgstr "" 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" msgid "Background color"
msgstr "" 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" msgid "Style text block"
msgstr "" 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" msgid "Style the selected text block"
msgstr "" 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.py:33
#: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:145 #: /home/kovid/work/calibre/src/calibre/gui2/preferences/behavior_ui.py:145
msgid "Normal" msgid "Normal"
msgstr "" 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:126
#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:127 #: /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:128
#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:129 #: /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:130
#: /home/kovid/work/calibre/src/calibre/gui2/comments_editor.py:131
msgid "Heading" msgid "Heading"
msgstr "" 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" msgid "Pre-formatted"
msgstr "" 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" msgid "Blockquote"
msgstr "" 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" msgid "Address"
msgstr "" 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" msgid "Insert link"
msgstr "" 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:79
#: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:84 #: /home/kovid/work/calibre/src/calibre/gui2/shortcuts_ui.py:84
msgid "Clear" msgid "Clear"
msgstr "" 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" msgid "Choose foreground color"
msgstr "" 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" msgid "Choose background color"
msgstr "" 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" msgid "Create link"
msgstr "" 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" msgid "Enter URL"
msgstr "" 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" msgid "Normal view"
msgstr "" 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" msgid "HTML Source"
msgstr "" 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_bulk_ui.py:504
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:465 #: /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" msgid "&Basic metadata"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_bulk_ui.py:505 #: /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/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" msgid "&Custom metadata"
msgstr "" 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:122
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:128 #: /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:249
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:350 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:256
msgid "Could not read cover" msgid "Could not read cover"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:123 #: /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" msgid "Could not read cover from %s format"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:129 #: /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" msgid "The cover in the %s format is invalid"
msgstr "" msgstr ""
@ -7314,7 +7315,7 @@ msgid " The red color warns that the current title sort does not match the curre
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:468 #: /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/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:221
#: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:384 #: /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:471
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:479 #: /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:347
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:443 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:351
msgid "Save changes and edit the metadata of %s" msgid "Save changes and edit the metadata of %s"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:476 #: /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/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:211
#: /home/kovid/work/calibre/src/calibre/web/feeds/templates.py:401 #: /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 "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:944 #: /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" msgid "Permission denied"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single.py:945 #: /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?" msgid "Could not open %s. Is it being used by another program?"
msgstr "" msgstr ""
@ -7521,6 +7522,7 @@ msgid "Update metadata from the metadata in the selected format"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/dialogs/metadata_single_ui.py:464 #: /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" msgid "&Comments"
msgstr "" msgstr ""
@ -8958,19 +8960,32 @@ msgstr ""
msgid "Details" msgid "Details"
msgstr "" msgstr ""
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:64 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:66
#: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:302 #: /home/kovid/work/calibre/src/calibre/gui2/metadata/single.py:208
msgid "Edit Metadata" msgid "Edit Metadata"
msgstr "" 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" msgid "Change cover"
msgstr "" 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" msgid "Co&mments"
msgstr "" 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 #: /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." msgid "Restore settings to default values. You have to click Apply to actually save the default settings."
msgstr "" msgstr ""