merge from trunk

This commit is contained in:
Lee 2011-04-18 16:59:00 +08:00
commit 34986d6774
20 changed files with 690 additions and 332 deletions

View File

@ -19,6 +19,45 @@
# new recipes: # new recipes:
# - title: # - 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 - version: 0.7.55
date: 2011-04-15 date: 2011-04-15

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.55' __version__ = '0.7.56'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re, importlib import re, importlib

View File

@ -201,8 +201,9 @@ class ITUNES(DriverBase):
# 0x1294 iPhone 3GS # 0x1294 iPhone 3GS
# 0x1297 iPhone 4 # 0x1297 iPhone 4
# 0x129a iPad # 0x129a iPad
# 0x12a2 iPad2
VENDOR_ID = [0x05ac] VENDOR_ID = [0x05ac]
PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a] PRODUCT_ID = [0x1292,0x1293,0x1294,0x1297,0x1299,0x129a,0x12a2]
BCD = [0x01] BCD = [0x01]
# Plugboard ID # Plugboard ID
@ -421,7 +422,7 @@ class ITUNES(DriverBase):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':book.name(), '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, 'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
'dev_book':book, 'dev_book':book,
'uuid': book.composer() 'uuid': book.composer()
@ -459,7 +460,7 @@ class ITUNES(DriverBase):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':book.Name, '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, 'lib_book':library_books[this_book.path] if this_book.path in library_books else None,
'uuid': book.Composer, 'uuid': book.Composer,
'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub' 'format': 'pdf' if book.KindAsString.startswith('PDF') else 'epub'
@ -1021,7 +1022,9 @@ class ITUNES(DriverBase):
if isosx: if isosx:
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
format = file.rpartition('.')[2].lower() 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]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i], format, update_md=True) fpath = self._get_fpath(file, metadata[i], format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i]) db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -1034,9 +1037,11 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info("ITUNES.upload_books()") self.log.info("ITUNES.upload_books()")
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_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] = { self.cached_books[this_book.path] = {
'author': metadata[i].author, 'author': authors_to_string(metadata[i].authors),
'dev_book': db_added, 'dev_book': db_added,
'format': format, 'format': format,
'lib_book': lb_added, 'lib_book': lb_added,
@ -1055,7 +1060,9 @@ class ITUNES(DriverBase):
for (i,file) in enumerate(files): for (i,file) in enumerate(files):
format = file.rpartition('.')[2].lower() 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]) self._remove_existing_copy(path, metadata[i])
fpath = self._get_fpath(file, metadata[i],format, update_md=True) fpath = self._get_fpath(file, metadata[i],format, update_md=True)
db_added, lb_added = self._add_new_copy(fpath, metadata[i]) db_added, lb_added = self._add_new_copy(fpath, metadata[i])
@ -1075,9 +1082,11 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info("ITUNES.upload_books()") self.log.info("ITUNES.upload_books()")
self.log.info(" adding '%s' by '%s' uuid:%s to self.cached_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] = { self.cached_books[this_book.path] = {
'author': metadata[i].author[0], 'author': authors_to_string(metadata[i].authors),
'dev_book': db_added, 'dev_book': db_added,
'format': format, 'format': format,
'lib_book': lb_added, 'lib_book': lb_added,
@ -1190,7 +1199,7 @@ class ITUNES(DriverBase):
base_fn = base_fn.rpartition('.')[0] base_fn = base_fn.rpartition('.')[0]
db_added = self._find_device_book( db_added = self._find_device_book(
{ 'title': base_fn if format == 'pdf' else metadata.title, { 'title': base_fn if format == 'pdf' else metadata.title,
'author': metadata.authors[0], 'author': authors_to_string(metadata.authors),
'uuid': metadata.uuid, 'uuid': metadata.uuid,
'format': format}) 'format': format})
return db_added return db_added
@ -1255,7 +1264,7 @@ class ITUNES(DriverBase):
base_fn = base_fn.rpartition('.')[0] base_fn = base_fn.rpartition('.')[0]
added = self._find_library_book( added = self._find_library_book(
{ 'title': base_fn if format == 'pdf' else metadata.title, { 'title': base_fn if format == 'pdf' else metadata.title,
'author': metadata.author[0], 'author': authors_to_string(metadata.authors),
'uuid': metadata.uuid, 'uuid': metadata.uuid,
'format': format}) 'format': format})
return added return added
@ -1314,7 +1323,7 @@ class ITUNES(DriverBase):
with open(metadata.cover,'r+b') as cd: with open(metadata.cover,'r+b') as cd:
cover_data = cd.read() cover_data = cd.read()
except: 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)) self.log.error(" error scaling '%s' for '%s'" % (metadata.cover,metadata.title))
import traceback import traceback
@ -1389,7 +1398,7 @@ class ITUNES(DriverBase):
thumb_path = path.rpartition('.')[0] + '.jpg' thumb_path = path.rpartition('.')[0] + '.jpg'
zfw.writestr(thumb_path, thumb) zfw.writestr(thumb_path, thumb)
except: 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)) self.log.error(" error converting '%s' to thumb for '%s'" % (metadata.cover,metadata.title))
finally: finally:
try: try:
@ -1407,7 +1416,7 @@ class ITUNES(DriverBase):
if DEBUG: if DEBUG:
self.log.info(" ITUNES._create_new_book()") 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.datetime = time.gmtime()
this_book.db_id = None this_book.db_id = None
this_book.device_collections = [] this_book.device_collections = []
@ -2451,7 +2460,7 @@ class ITUNES(DriverBase):
for book in self.cached_books: for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid or \ if self.cached_books[book]['uuid'] == metadata.uuid or \
(self.cached_books[book]['title'] == metadata.title and \ (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.update_list.append(self.cached_books[book])
self._remove_from_device(self.cached_books[book]) self._remove_from_device(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2470,7 +2479,7 @@ class ITUNES(DriverBase):
for book in self.cached_books: for book in self.cached_books:
if self.cached_books[book]['uuid'] == metadata.uuid or \ if self.cached_books[book]['uuid'] == metadata.uuid or \
(self.cached_books[book]['title'] == metadata.title and \ (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.update_list.append(self.cached_books[book])
self._remove_from_iTunes(self.cached_books[book]) self._remove_from_iTunes(self.cached_books[book])
if DEBUG: if DEBUG:
@ -2939,13 +2948,13 @@ class ITUNES(DriverBase):
def _xform_metadata_via_plugboard(self, book, format): def _xform_metadata_via_plugboard(self, book, format):
''' Transform book metadata from plugboard templates ''' ''' Transform book metadata from plugboard templates '''
if DEBUG: if DEBUG:
self.log.info(" ITUNES._xform_metadata_via_plugboard()") self.log.info(" ITUNES._xform_metadata_via_plugboard()")
if self.plugboard_func: if self.plugboard_func:
pb = self.plugboard_func(self.DEVICE_PLUGBOARD_NAME, format, self.plugboards) pb = self.plugboard_func(self.DEVICE_PLUGBOARD_NAME, format, self.plugboards)
newmi = book.deepcopy_metadata() newmi = book.deepcopy_metadata()
newmi.template_to_attribute(book, pb) 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(" transforming %s using %s:" % (format, pb))
self.log.info(" title: %s %s" % (book.title, ">>> %s" % self.log.info(" title: %s %s" % (book.title, ">>> %s" %
newmi.title if book.title != newmi.title else '')) newmi.title if book.title != newmi.title else ''))
@ -3062,7 +3071,7 @@ class ITUNES_ASYNC(ITUNES):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':library_books[book].name(), 'title':library_books[book].name(),
'author':[library_books[book].artist()], 'author':library_books[book].artist().split(' & '),
'lib_book':library_books[book], 'lib_book':library_books[book],
'dev_book':None, 'dev_book':None,
'uuid': library_books[book].composer(), 'uuid': library_books[book].composer(),
@ -3102,7 +3111,7 @@ class ITUNES_ASYNC(ITUNES):
cached_books[this_book.path] = { cached_books[this_book.path] = {
'title':library_books[book].Name, 'title':library_books[book].Name,
'author':library_books[book].Artist, 'author':library_books[book].Artist.split(' & '),
'lib_book':library_books[book], 'lib_book':library_books[book],
'uuid': library_books[book].Composer, 'uuid': library_books[book].Composer,
'format': format 'format': format
@ -3288,7 +3297,7 @@ class Book(Metadata):
See ebooks.metadata.book.base See ebooks.metadata.book.base
''' '''
def __init__(self,title,author): def __init__(self,title,author):
Metadata.__init__(self, title, authors=[author]) Metadata.__init__(self, title, authors=author.split(' & '))
@property @property
def title_sorter(self): def title_sorter(self):

View File

@ -52,6 +52,9 @@ class CHMInput(InputFormatPlugin):
metadata = get_metadata_from_reader(self._chm_reader) metadata = get_metadata_from_reader(self._chm_reader)
self._chm_reader.CloseCHM() self._chm_reader.CloseCHM()
#print tdir
#from calibre import ipython
#ipython()
odi = options.debug_pipeline odi = options.debug_pipeline
options.debug_pipeline = None options.debug_pipeline = None

View File

@ -147,7 +147,8 @@ class CHMReader(CHMFile):
if self.hhc_path == '.hhc' and self.hhc_path not in files: if self.hhc_path == '.hhc' and self.hhc_path not in files:
from calibre import walk from calibre import walk
for x in walk(output_dir): 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) self.hhc_path = os.path.relpath(x, output_dir)
break break

View File

@ -17,6 +17,7 @@
#define BUFFER 6000 #define BUFFER 6000
#define MIN(x, y) ( ((x) < (y)) ? (x) : (y) ) #define MIN(x, y) ( ((x) < (y)) ? (x) : (y) )
#define MAX(x, y) ( ((x) > (y)) ? (x) : (y) )
typedef unsigned short int Byte; typedef unsigned short int Byte;
typedef struct { typedef struct {
@ -53,7 +54,7 @@ cpalmdoc_decompress(PyObject *self, PyObject *args) {
// Map chars to bytes // Map chars to bytes
for (j = 0; j < input_len; j++) for (j = 0; j < input_len; j++)
input[j] = (_input[j] < 0) ? _input[j]+256 : _input[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(); if (output == NULL) return PyErr_NoMemory();
while (i < input_len) { while (i < input_len) {

View File

@ -290,18 +290,38 @@ class Source(Plugin):
yield tok yield tok
def get_title_tokens(self, title, strip_joiners=True): def get_title_tokens(self, title, strip_joiners=True, strip_subtitle=False):
''' '''
Take a title and return a list of tokens useful for an AND search query. Take a title and return a list of tokens useful for an AND search query.
Excludes connectives(optionally) and punctuation. Excludes connectives(optionally) and punctuation.
''' '''
if title: if title:
# strip sub-titles # strip sub-titles
subtitle = re.compile(r'([\(\[\{].*?[\)\]\}]|[/:\\].*$)') if strip_subtitle:
if len(subtitle.sub('', title)) > 1: subtitle = re.compile(r'([\(\[\{].*?[\)\]\}]|[/:\\].*$)')
title = subtitle.sub('', title) if len(subtitle.sub('', title)) > 1:
pat = re.compile(r'''([-,:;+!@#$%^*(){}.`~"\s\[\]/]|'(?!s))''') title = subtitle.sub('', title)
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() tokens = title.split()
for token in tokens: for token in tokens:
token = token.strip() token = token.strip()

View File

@ -114,8 +114,12 @@ class ISBNMerge(object):
return self.results return self.results
def merge_metadata_results(self): def merge_metadata_results(self, merge_on_identifiers=False):
' Merge results with identical title and authors ' '''
Merge results with identical title and authors or an identical
identifier
'''
# First title/author
groups = {} groups = {}
for result in self.results: for result in self.results:
title = lower(result.title if result.title else '') title = lower(result.title if result.title else '')
@ -135,6 +139,44 @@ class ISBNMerge(object):
result = rgroup[0] result = rgroup[0]
self.results.append(result) 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')) self.results.sort(key=attrgetter('average_source_relevance'))
def merge_isbn_results(self): def merge_isbn_results(self):
@ -408,7 +450,7 @@ if __name__ == '__main__': # tests {{{
{'identifiers':{'isbn': '9780307459671'}, {'identifiers':{'isbn': '9780307459671'},
'title':'Invisible Gorilla', 'authors':['Christopher Chabris']}, 'title':'Invisible Gorilla', 'authors':['Christopher Chabris']},
[title_test('The Invisible Gorilla', [title_test('The Invisible Gorilla',
exact=True), authors_test(['Christopher F. Chabris', 'Daniel Simons'])] exact=True), authors_test(['Christopher Chabris', 'Daniel Simons'])]
), ),

View File

@ -15,14 +15,17 @@ from calibre.customize.ui import metadata_plugins
from calibre import prints, sanitize_file_name2 from calibre import prints, sanitize_file_name2
from calibre.ebooks.metadata import check_isbn from calibre.ebooks.metadata import check_isbn
from calibre.ebooks.metadata.sources.base import (create_log, from calibre.ebooks.metadata.sources.base import (create_log,
get_cached_cover_urls) get_cached_cover_urls, msprefs)
def isbn_test(isbn): def isbn_test(isbn):
isbn_ = check_isbn(isbn) isbn_ = check_isbn(isbn)
def test(mi): def test(mi):
misbn = check_isbn(mi.isbn) 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 return test
@ -32,8 +35,11 @@ def title_test(title, exact=False):
def test(mi): def test(mi):
mt = mi.title.lower() mt = mi.title.lower()
return (exact and mt == title) or \ if (exact and mt == title) or \
(not exact and title in mt) (not exact and title in mt):
return True
prints('Title test failed. Expected: \'%s\' found \'%s\''%(title, mt))
return False
return test return test
@ -42,7 +48,22 @@ def authors_test(authors):
def test(mi): def test(mi):
au = set([x.lower() for x in mi.authors]) 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 return test

View File

@ -24,7 +24,7 @@ from calibre.translations.dynamic import translate
from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.chardet import xml_to_unicode
from calibre.ebooks.oeb.entitydefs import ENTITYDEFS from calibre.ebooks.oeb.entitydefs import ENTITYDEFS
from calibre.ebooks.conversion.preprocess import CSSPreProcessor 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) RECOVER_PARSER = etree.XMLParser(recover=True, no_network=True)
@ -643,7 +643,7 @@ class Metadata(object):
return unicode(self.value).encode('ascii', 'xmlcharrefreplace') return unicode(self.value).encode('ascii', 'xmlcharrefreplace')
def __unicode__(self): def __unicode__(self):
return unicode(self.value) return as_unicode(self.value)
def to_opf1(self, dcmeta=None, xmeta=None, nsrmap={}): def to_opf1(self, dcmeta=None, xmeta=None, nsrmap={}):
attrib = {} attrib = {}

View File

@ -648,6 +648,18 @@ def open_url(qurl):
if isfrozen and islinux and paths: if isfrozen and islinux and paths:
os.environ['LD_LIBRARY_PATH'] = os.pathsep.join(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): def open_local_file(path):
if iswindows: if iswindows:

View File

@ -17,7 +17,7 @@ from calibre.gui2.actions import InterfaceAction
class GenerateCatalogAction(InterfaceAction): class GenerateCatalogAction(InterfaceAction):
name = 'Generate Catalog' 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']) dont_add_to = frozenset(['menubar-device', 'toolbar-device', 'context-menu-device'])
def generate_catalog(self): def generate_catalog(self):

View File

@ -483,8 +483,15 @@ class BookDetails(QWidget): # {{{
self.book_info.show_data(data) self.book_info.show_data(data)
self.cover_view.show_data(data) self.cover_view.show_data(data)
self._layout.do_layout(self.rect()) self._layout.do_layout(self.rect())
self.setToolTip('<p>'+_('Double-click to open Book Details window') + try:
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), '')) sz = self.cover_view.pixmap.size()
except:
sz = QSize(0, 0)
self.setToolTip(
'<p>'+_('Double-click to open Book Details window') +
'<br><br>' + _('Path') + ': ' + data.get(_('Path'), '') +
'<br><br>' + _('Cover size: %dx%d')%(sz.width(), sz.height())
)
def reset_info(self): def reset_info(self):
self.show_data({}) self.show_data({})

View File

@ -109,6 +109,8 @@ class BookInfo(QDialog, Ui_BookInfo):
pixmap = pixmap.scaled(new_width, new_height, pixmap = pixmap.scaled(new_width, new_height,
Qt.KeepAspectRatio, Qt.SmoothTransformation) Qt.KeepAspectRatio, Qt.SmoothTransformation)
self.cover.set_pixmap(pixmap) self.cover.set_pixmap(pixmap)
sz = pixmap.size()
self.cover.setToolTip(_('Cover size: %dx%d')%(sz.width(), sz.height()))
def refresh(self, row): def refresh(self, row):
if isinstance(row, QModelIndex): if isinstance(row, QModelIndex):

View File

@ -12,6 +12,7 @@ from zipfile import ZipFile, ZIP_DEFLATED, ZIP_STORED
from PyQt4.Qt import QDialog from PyQt4.Qt import QDialog
from calibre.constants import isosx, iswindows
from calibre.gui2 import open_local_file from calibre.gui2 import open_local_file
from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog from calibre.gui2.dialogs.tweak_epub_ui import Ui_Dialog
from calibre.libunzip import extract as zipextract from calibre.libunzip import extract as zipextract
@ -42,11 +43,19 @@ class TweakEpub(QDialog, Ui_Dialog):
self.move(parent_loc.x(),parent_loc.y()) self.move(parent_loc.x(),parent_loc.y())
def cleanup(self): 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 # Delete directory containing exploded ePub
if self._exploded is not None: if self._exploded is not None:
shutil.rmtree(self._exploded, ignore_errors=True) shutil.rmtree(self._exploded, ignore_errors=True)
def display_exploded(self): def display_exploded(self):
''' '''
Generic subprocess launch of native file browser Generic subprocess launch of native file browser

View File

@ -317,6 +317,8 @@ class BaseToolBar(QToolBar): # {{{
QToolBar.resizeEvent(self, ev) QToolBar.resizeEvent(self, ev)
style = self.get_text_style() style = self.get_text_style()
self.setToolButtonStyle(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): def get_text_style(self):
style = Qt.ToolButtonTextUnderIcon style = Qt.ToolButtonTextUnderIcon
@ -399,7 +401,10 @@ class ToolBar(BaseToolBar): # {{{
self.d_widget.layout().addWidget(self.donate_button) self.d_widget.layout().addWidget(self.donate_button)
if isosx: if isosx:
self.d_widget.setStyleSheet('QWidget, QToolButton {background-color: none; border: none; }') 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) bar.addWidget(self.d_widget)
self.showing_donate = True self.showing_donate = True
elif what in self.gui.iactions: elif what in self.gui.iactions:

View File

@ -73,13 +73,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
choices=sorted(list(choices), key=sort_key)) 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) self.change_font_button.clicked.connect(self.change_font)
def initialize(self): def initialize(self):
ConfigWidgetBase.initialize(self) ConfigWidgetBase.initialize(self)
self.current_font = gprefs['font'] self.current_font = self.initial_font = gprefs['font']
self.update_font_display() self.update_font_display()
def restore_defaults(self): def restore_defaults(self):
@ -119,7 +119,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def commit(self, *args): def commit(self, *args):
rr = ConfigWidgetBase.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 gprefs['font'] = self.current_font
QApplication.setFont(self.font_display.font()) QApplication.setFont(self.font_display.font())
rr = True rr = True

View File

@ -9,8 +9,8 @@ __docformat__ = 'restructuredtext en'
import os import os
from urlparse import urlparse from urlparse import urlparse
from PyQt4.Qt import (QWebView, QWebPage, QNetworkCookieJar, from PyQt4.Qt import QNetworkCookieJar, QFileDialog, QNetworkProxy
QFileDialog, QNetworkProxy) from PyQt4.QtWebKit import QWebView, QWebPage
from calibre import USER_AGENT, get_proxies, get_download_filename from calibre import USER_AGENT, get_proxies, get_download_filename
from calibre.ebooks import BOOK_EXTENSIONS from calibre.ebooks import BOOK_EXTENSIONS

View File

@ -88,6 +88,11 @@ class SystemTrayIcon(QSystemTrayIcon): # {{{
# }}} # }}}
_gui = None
def get_gui():
return _gui
class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin, TagBrowserMixin, CoverFlowMixin, LibraryViewMixin, SearchBoxMixin,
SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin, SavedSearchBoxMixin, SearchRestrictionMixin, LayoutMixin, UpdateMixin,
@ -97,7 +102,9 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
def __init__(self, opts, parent=None, gui_debug=None): def __init__(self, opts, parent=None, gui_debug=None):
global _gui
MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True) MainWindow.__init__(self, opts, parent=parent, disable_automatic_gc=True)
_gui = self
self.opts = opts self.opts = opts
self.device_connected = None self.device_connected = None
self.gui_debug = gui_debug self.gui_debug = gui_debug

File diff suppressed because it is too large Load Diff