Sync to trunk.

This commit is contained in:
John Schember 2011-05-05 18:24:36 -04:00
commit 5f9a4c42b9
10 changed files with 121 additions and 32 deletions

View File

@ -23,6 +23,9 @@ wWinMain(HINSTANCE Inst, HINSTANCE PrevInst,
ret = execute_python_entrypoint(BASENAME, MODULE, FUNCTION, ret = execute_python_entrypoint(BASENAME, MODULE, FUNCTION,
stdout_redirect, stderr_redirect); stdout_redirect, stderr_redirect);
if (stdout != NULL) fclose(stdout);
if (stderr != NULL) fclose(stderr);
DeleteFile(stdout_redirect); DeleteFile(stdout_redirect);
DeleteFile(stderr_redirect); DeleteFile(stderr_redirect);

View File

@ -13,6 +13,7 @@ from Queue import Queue, Empty
from threading import Thread from threading import Thread
from io import BytesIO from io import BytesIO
from operator import attrgetter from operator import attrgetter
from urlparse import urlparse
from calibre.customize.ui import metadata_plugins, all_metadata_plugins from calibre.customize.ui import metadata_plugins, all_metadata_plugins
from calibre.ebooks.metadata.sources.base import create_log, msprefs from calibre.ebooks.metadata.sources.base import create_log, msprefs
@ -458,6 +459,14 @@ def urls_from_identifiers(identifiers): # {{{
if oclc: if oclc:
ans.append(('OCLC', 'oclc', oclc, ans.append(('OCLC', 'oclc', oclc,
'http://www.worldcat.org/oclc/'+oclc)) 'http://www.worldcat.org/oclc/'+oclc))
url = identifiers.get('uri', None)
if url is None:
url = identifiers.get('url', None)
if url and url.startswith('http'):
url = url[:8].replace('|', ':') + url[8:].replace('|', ',')
parts = urlparse(url)
name = parts.netloc
ans.append((name, 'url', url, url))
return ans return ans
# }}} # }}}

View File

@ -7,6 +7,8 @@ __docformat__ = 'restructuredtext en'
Convert an ODT file into a Open Ebook Convert an ODT file into a Open Ebook
''' '''
import os import os
from lxml import etree
from odf.odf2xhtml import ODF2XHTML from odf.odf2xhtml import ODF2XHTML
from calibre import CurrentDir, walk from calibre import CurrentDir, walk
@ -23,7 +25,51 @@ class Extract(ODF2XHTML):
with open(name, 'wb') as f: with open(name, 'wb') as f:
f.write(data) f.write(data)
def __call__(self, stream, odir): def filter_css(self, html, log):
root = etree.fromstring(html)
style = root.xpath('//*[local-name() = "style" and @type="text/css"]')
if style:
style = style[0]
css = style.text
if css:
style.text, sel_map = self.do_filter_css(css)
for x in root.xpath('//*[@class]'):
extra = []
orig = x.get('class')
for cls in orig.split():
extra.extend(sel_map.get(cls, []))
if extra:
x.set('class', orig + ' ' + ' '.join(extra))
html = etree.tostring(root, encoding='utf-8',
xml_declaration=True)
return html
def do_filter_css(self, css):
from cssutils import parseString
from cssutils.css import CSSRule
sheet = parseString(css)
rules = list(sheet.cssRules.rulesOfType(CSSRule.STYLE_RULE))
sel_map = {}
count = 0
for r in rules:
# Check if we have only class selectors for this rule
nc = [x for x in r.selectorList if not
x.selectorText.startswith('.')]
if len(r.selectorList) > 1 and not nc:
# Replace all the class selectors with a single class selector
# This will be added to the class attribute of all elements
# that have one of these selectors.
replace_name = 'c_odt%d'%count
count += 1
for sel in r.selectorList:
s = sel.selectorText[1:]
if s not in sel_map:
sel_map[s] = []
sel_map[s].append(replace_name)
r.selectorText = '.'+replace_name
return sheet.cssText, sel_map
def __call__(self, stream, odir, log):
from calibre.utils.zipfile import ZipFile from calibre.utils.zipfile import ZipFile
from calibre.ebooks.metadata.meta import get_metadata from calibre.ebooks.metadata.meta import get_metadata
from calibre.ebooks.metadata.opf2 import OPFCreator from calibre.ebooks.metadata.opf2 import OPFCreator
@ -32,13 +78,17 @@ class Extract(ODF2XHTML):
if not os.path.exists(odir): if not os.path.exists(odir):
os.makedirs(odir) os.makedirs(odir)
with CurrentDir(odir): with CurrentDir(odir):
print 'Extracting ODT file...' log('Extracting ODT file...')
html = self.odf2xhtml(stream) html = self.odf2xhtml(stream)
# A blanket img specification like this causes problems # A blanket img specification like this causes problems
# with EPUB output as the contaiing element often has # with EPUB output as the containing element often has
# an absolute height and width set that is larger than # an absolute height and width set that is larger than
# the available screen real estate # the available screen real estate
html = html.replace('img { width: 100%; height: 100%; }', '') html = html.replace('img { width: 100%; height: 100%; }', '')
try:
html = self.filter_css(html, log)
except:
log.exception('Failed to filter CSS, conversion may be slow')
with open('index.xhtml', 'wb') as f: with open('index.xhtml', 'wb') as f:
f.write(html.encode('utf-8')) f.write(html.encode('utf-8'))
zf = ZipFile(stream, 'r') zf = ZipFile(stream, 'r')
@ -67,7 +117,7 @@ class ODTInput(InputFormatPlugin):
def convert(self, stream, options, file_ext, log, def convert(self, stream, options, file_ext, log,
accelerators): accelerators):
return Extract()(stream, '.') return Extract()(stream, '.', log)
def postprocess_book(self, oeb, opts, log): def postprocess_book(self, oeb, opts, log):
# Fix <p><div> constructs as the asinine epubchecker complains # Fix <p><div> constructs as the asinine epubchecker complains

View File

@ -15,7 +15,6 @@ import cStringIO
from lxml import etree from lxml import etree
from calibre.ebooks.metadata import authors_to_string from calibre.ebooks.metadata import authors_to_string
from calibre.utils.filenames import ascii_text
from calibre.utils.magick.draw import save_cover_data_to, identify_data from calibre.utils.magick.draw import save_cover_data_to, identify_data
TAGS = { TAGS = {

View File

@ -246,7 +246,8 @@ class ChooseLibraryAction(InterfaceAction):
def delete_requested(self, name, location): def delete_requested(self, name, location):
loc = location.replace('/', os.sep) loc = location.replace('/', os.sep)
if not question_dialog(self.gui, _('Are you sure?'), '<p>'+ if not question_dialog(self.gui, _('Are you sure?'), '<p>'+
_('<b style="color: red">All files</b> from <br><br><b>%s</b><br><br> will be ' _('<b style="color: red">All files</b> (not just ebooks) '
'from <br><br><b>%s</b><br><br> will be '
'<b>permanently deleted</b>. Are you sure?') % loc, '<b>permanently deleted</b>. Are you sure?') % loc,
show_copy_button=False): show_copy_button=False):
return return

View File

@ -439,10 +439,16 @@ class BooksView(QTableView): # {{{
if tweaks['sort_columns_at_startup'] is not None: if tweaks['sort_columns_at_startup'] is not None:
sh = [] sh = []
for c,d in tweaks['sort_columns_at_startup']: try:
if not isinstance(d, bool): for c,d in tweaks['sort_columns_at_startup']:
d = True if d == 0 else False if not isinstance(d, bool):
sh.append((c, d)) d = True if d == 0 else False
sh.append((c, d))
except:
# Ignore invalid tweak values as users seem to often get them
# wrong
import traceback
traceback.print_exc()
old_state['sort_history'] = sh old_state['sort_history'] = sh
self.apply_state(old_state) self.apply_state(old_state)

View File

@ -299,13 +299,13 @@ def run_gui(opts, args, actions, listener, app, gui_debug=None):
if getattr(runner.main, 'debug_on_restart', False): if getattr(runner.main, 'debug_on_restart', False):
run_in_debug_mode() run_in_debug_mode()
else: else:
import subprocess
print 'Restarting with:', e, sys.argv print 'Restarting with:', e, sys.argv
if hasattr(sys, 'frameworks_dir'): if hasattr(sys, 'frameworks_dir'):
app = os.path.dirname(os.path.dirname(sys.frameworks_dir)) app = os.path.dirname(os.path.dirname(sys.frameworks_dir))
import subprocess
subprocess.Popen('sleep 3s; open '+app, shell=True) subprocess.Popen('sleep 3s; open '+app, shell=True)
else: else:
os.execvp(e, sys.argv) subprocess.Popen([e] + sys.argv[1:])
else: else:
if iswindows: if iswindows:
try: try:

View File

@ -298,11 +298,12 @@ class AuthorSortEdit(EnLineEdit):
self.current_val = self.db.author_sort_from_authors(authors) self.current_val = self.db.author_sort_from_authors(authors)
def initialize(self, db, id_): def initialize(self, db, id_):
self.current_val = db.author_sort(id_, index_is_id=True) self.current_val = self.original_val = db.author_sort(id_, index_is_id=True)
def commit(self, db, id_): def commit(self, db, id_):
aus = self.current_val aus = self.current_val
db.set_author_sort(id_, aus, notify=False, commit=False) if aus != self.original_val:
db.set_author_sort(id_, aus, notify=False, commit=False)
return True return True
# }}} # }}}

View File

@ -33,7 +33,7 @@ from calibre import isbytestring
from calibre.utils.filenames import ascii_filename from calibre.utils.filenames import ascii_filename
from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp from calibre.utils.date import utcnow, now as nowf, utcfromtimestamp
from calibre.utils.config import prefs, tweaks, from_json, to_json from calibre.utils.config import prefs, tweaks, from_json, to_json
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key, strcmp
from calibre.utils.search_query_parser import saved_searches, set_saved_searches from calibre.utils.search_query_parser import saved_searches, set_saved_searches
from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format from calibre.ebooks import BOOK_EXTENSIONS, check_ebook_format
from calibre.utils.magick.draw import save_cover_data_to from calibre.utils.magick.draw import save_cover_data_to
@ -1920,6 +1920,18 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
result.append(r) result.append(r)
return ' & '.join(result).replace('|', ',') return ' & '.join(result).replace('|', ',')
def _update_author_in_cache(self, id_, ss, final_authors):
self.conn.execute('UPDATE books SET author_sort=? WHERE id=?', (ss, id_))
self.data.set(id_, self.FIELD_MAP['authors'],
','.join([a.replace(',', '|') for a in final_authors]),
row_is_id=True)
self.data.set(id_, self.FIELD_MAP['author_sort'], ss, row_is_id=True)
aum = self.authors_with_sort_strings(id_, index_is_id=True)
self.data.set(id_, self.FIELD_MAP['au_map'],
':#:'.join([':::'.join((au.replace(',', '|'), aus)) for (au, aus) in aum]),
row_is_id=True)
def _set_authors(self, id, authors, allow_case_change=False): def _set_authors(self, id, authors, allow_case_change=False):
if not authors: if not authors:
authors = [_('Unknown')] authors = [_('Unknown')]
@ -1933,14 +1945,17 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
a = a.strip().replace(',', '|') a = a.strip().replace(',', '|')
if not isinstance(a, unicode): if not isinstance(a, unicode):
a = a.decode(preferred_encoding, 'replace') a = a.decode(preferred_encoding, 'replace')
aus = self.conn.get('SELECT id, name FROM authors WHERE name=?', (a,)) aus = self.conn.get('SELECT id, name, sort FROM authors WHERE name=?', (a,))
if aus: if aus:
aid, name = aus[0] aid, name, sort = aus[0]
# Handle change of case # Handle change of case
if name != a: if name != a:
if allow_case_change: if allow_case_change:
self.conn.execute('''UPDATE authors ns = author_to_author_sort(a.replace('|', ','))
SET name=? WHERE id=?''', (a, aid)) if strcmp(sort, ns) == 0:
sort = ns
self.conn.execute('''UPDATE authors SET name=?, sort=?
WHERE id=?''', (a, sort, aid))
case_change = True case_change = True
else: else:
a = name a = name
@ -1957,17 +1972,14 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
bks = self.conn.get('''SELECT book FROM books_authors_link bks = self.conn.get('''SELECT book FROM books_authors_link
WHERE author=?''', (aid,)) WHERE author=?''', (aid,))
books_to_refresh |= set([bk[0] for bk in bks]) books_to_refresh |= set([bk[0] for bk in bks])
for bk in books_to_refresh:
ss = self.author_sort_from_book(id, index_is_id=True)
aus = self.author_sort(bk, index_is_id=True)
if strcmp(aus, ss) == 0:
self._update_author_in_cache(bk, ss, final_authors)
# This can repeat what was done above in rare cases. Let it.
ss = self.author_sort_from_book(id, index_is_id=True) ss = self.author_sort_from_book(id, index_is_id=True)
self.conn.execute('UPDATE books SET author_sort=? WHERE id=?', self._update_author_in_cache(id, ss, final_authors)
(ss, id))
self.data.set(id, self.FIELD_MAP['authors'],
','.join([a.replace(',', '|') for a in final_authors]),
row_is_id=True)
self.data.set(id, self.FIELD_MAP['author_sort'], ss, row_is_id=True)
aum = self.authors_with_sort_strings(id, index_is_id=True)
self.data.set(id, self.FIELD_MAP['au_map'],
':#:'.join([':::'.join((au.replace(',', '|'), aus)) for (au, aus) in aum]),
row_is_id=True)
return books_to_refresh return books_to_refresh
def set_authors(self, id, authors, notify=True, commit=True, def set_authors(self, id, authors, notify=True, commit=True,

View File

@ -841,11 +841,19 @@ ol, ul { padding-left: 2em; }
self.styledict[name] = styles self.styledict[name] = styles
# Write the styles to HTML # Write the styles to HTML
self.writeout(self.default_styles) self.writeout(self.default_styles)
# Changed by Kovid to not write out endless copies of the same style
css_styles = {}
for name in self.stylestack: for name in self.stylestack:
styles = self.styledict.get(name) styles = self.styledict.get(name)
css2 = self.cs.convert_styles(styles) css2 = tuple(self.cs.convert_styles(styles).iteritems())
self.writeout("%s {\n" % name) if css2 in css_styles:
for style, val in css2.items(): css_styles[css2].append(name)
else:
css_styles[css2] = [name]
for css2, names in css_styles.iteritems():
self.writeout("%s {\n" % ', '.join(names))
for style, val in css2:
self.writeout("\t%s: %s;\n" % (style, val) ) self.writeout("\t%s: %s;\n" % (style, val) )
self.writeout("}\n") self.writeout("}\n")