Sync to trunk.

This commit is contained in:
John Schember 2011-12-06 19:30:27 -05:00
commit 60e8fb65bf
18 changed files with 172 additions and 89 deletions

View File

@ -5,8 +5,8 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
description = 'News as provide by The Metro -UK' description = 'News as provide by The Metro -UK'
__author__ = 'Dave Asbury' __author__ = 'Dave Asbury'
#last update 3/12/11
cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg' cover_url = 'http://profile.ak.fbcdn.net/hprofile-ak-snc4/276636_117118184990145_2132092232_n.jpg'
no_stylesheets = True no_stylesheets = True
oldest_article = 1 oldest_article = 1
max_articles_per_feed = 20 max_articles_per_feed = 20
@ -26,15 +26,17 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
keep_only_tags = [ keep_only_tags = [
dict(name='h1'),dict(name='h2', attrs={'class':'h2'}), dict(name='h1'),dict(name='h2', attrs={'class':'h2'}),
dict(attrs={'class':['img-cnt figure']}), dict(attrs={'class':['img-cnt figure']}),
dict(attrs={'class':['art-img']}), dict(attrs={'class':['art-img']}),
dict(name='div', attrs={'class':'art-lft'}), dict(name='div', attrs={'class':'art-lft'}),
dict(name='p') dict(name='p')
] ]
remove_tags = [dict(name='div', attrs={'class':[ 'news m12 clrd clr-b p5t shareBtm', 'commentForm', 'metroCommentInnerWrap', remove_tags = [
'art-rgt','pluck-app pluck-comm','news m12 clrd clr-l p5t', 'flt-r' ]}), dict(name = 'div',attrs={'id' : ['comments-news','formSubmission']}),
dict(attrs={'class':[ 'metroCommentFormWrap','commentText','commentsNav','avatar','submDateAndTime']}) dict(name='div', attrs={'class':[ 'news m12 clrd clr-b p5t shareBtm', 'commentForm', 'metroCommentInnerWrap',
'art-rgt','pluck-app pluck-comm','news m12 clrd clr-l p5t', 'flt-r','username','clrd' ]}),
dict(attrs={'class':['username', 'metroCommentFormWrap','commentText','commentsNav','avatar','submDateAndTime','addYourComment','displayName']})
,dict(name='div', attrs={'class' : 'clrd art-fd fd-gr1-b'}) ,dict(name='div', attrs={'class' : 'clrd art-fd fd-gr1-b'})
] ]
feeds = [ feeds = [
@ -42,9 +44,9 @@ class AdvancedUserRecipe1306097511(BasicNewsRecipe):
extra_css = ''' extra_css = '''
body {font: sans-serif medium;}' body {font: sans-serif medium;}'
h1 {text-align : center; font-family:Arial,Helvetica,sans-serif; font-size:20px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold;} h1 {text-align : center; font-family:Arial,Helvetica,sans-serif; font-size:20px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold;}
h2 {text-align : center;color:#4D4D4D;font-family:Arial,Helvetica,sans-serif; font-size:15px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; } h2 {text-align : center;color:#4D4D4D;font-family:Arial,Helvetica,sans-serif; font-size:15px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:bold; }
span{ font-size:9.5px; font-weight:bold;font-style:italic} span{ font-size:9.5px; font-weight:bold;font-style:italic}
p { text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;} p { text-align: justify; font-family:Arial,Helvetica,sans-serif; font-size:11px; font-size-adjust:none; font-stretch:normal; font-style:normal; font-variant:normal; font-weight:normal;}
''' '''

View File

@ -7,11 +7,14 @@ class TVXS(BasicNewsRecipe):
__author__ = 'hargikas' __author__ = 'hargikas'
description = 'News from Greece' description = 'News from Greece'
max_articles_per_feed = 100 max_articles_per_feed = 100
oldest_article = 100 oldest_article = 3
simultaneous_downloads = 1
publisher = 'TVXS' publisher = 'TVXS'
category = 'news, GR' category = 'news, GR'
language = 'el' language = 'el'
encoding = None encoding = None
use_embedded_content = False
remove_empty_feeds = True
#conversion_options = { 'linearize_tables': True} #conversion_options = { 'linearize_tables': True}
no_stylesheets = True no_stylesheets = True
remove_tags_before = dict(name='h1',attrs={'class':'print-title'}) remove_tags_before = dict(name='h1',attrs={'class':'print-title'})

View File

@ -131,7 +131,7 @@ class ZeitEPUBAbo(BasicNewsRecipe):
browser.form['pass']=self.password browser.form['pass']=self.password
browser.submit() browser.submit()
# now find the correct file, we will still use the ePub file # now find the correct file, we will still use the ePub file
epublink = browser.find_link(text_regex=re.compile('.*Ausgabe als Datei im ePub-Format.*')) epublink = browser.find_link(text_regex=re.compile('.*Download als Datei im ePub-Format für eReader.*'))
response = browser.follow_link(epublink) response = browser.follow_link(epublink)
self.report_progress(1,_('next step')) self.report_progress(1,_('next step'))

View File

@ -224,7 +224,7 @@ class TREKSTOR(USBMS):
FORMATS = ['epub', 'txt', 'pdf'] FORMATS = ['epub', 'txt', 'pdf']
VENDOR_ID = [0x1e68] VENDOR_ID = [0x1e68]
PRODUCT_ID = [0x0041, 0x0042, 0x0052, PRODUCT_ID = [0x0041, 0x0042, 0x0052, 0x004e,
0x003e # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091 0x003e # This is for the EBOOK_PLAYER_5M https://bugs.launchpad.net/bugs/792091
] ]
BCD = [0x0002] BCD = [0x0002]

View File

@ -296,6 +296,13 @@ class PRST1(USBMS):
lpath = row[0].replace('\\', '/') lpath = row[0].replace('\\', '/')
db_books[lpath] = row[1] db_books[lpath] = row[1]
# Work-around for Sony Bug (SD Card DB not using right SQLite sequence)
if source_id == 1:
sdcard_sequence_start = '4294967296'
query = 'UPDATE sqlite_sequence SET seq = ? WHERE seq < ?'
t = (sdcard_sequence_start, sdcard_sequence_start,)
cursor.execute(query, t)
for book in booklist: for book in booklist:
# Run through plugboard if needed # Run through plugboard if needed
if plugboard is not None: if plugboard is not None:
@ -322,12 +329,10 @@ class PRST1(USBMS):
title = newmi.title or _('Unknown') title = newmi.title or _('Unknown')
# Get modified date # Get modified date
# If there was a detected offset, use that. Otherwise use UTC (same as Sony software)
modified_date = os.path.getmtime(book.path) * 1000 modified_date = os.path.getmtime(book.path) * 1000
if self.device_offset is not None: if self.device_offset is not None:
modified_date = modified_date + self.device_offset modified_date = modified_date + self.device_offset
else:
time_offset = -time.altzone if time.daylight else -time.timezone
modified_date = modified_date + (time_offset * 1000)
if lpath not in db_books: if lpath not in db_books:
query = ''' query = '''
@ -578,17 +583,17 @@ class PRST1(USBMS):
# Setting this to the SONY periodical schema apparently causes errors # Setting this to the SONY periodical schema apparently causes errors
# with some periodicals, therefore set it to null, since the special # with some periodicals, therefore set it to null, since the special
# periodical navigation doesn't work anyway. # periodical navigation doesn't work anyway.
periodical_schema = 'null' periodical_schema = None
query = ''' query = '''
UPDATE books UPDATE books
SET conforms_to = %s, SET conforms_to = ?,
periodical_name = ?, periodical_name = ?,
description = ?, description = ?,
publication_date = ? publication_date = ?
WHERE _id = ? WHERE _id = ?
'''%periodical_schema '''
t = (name, None, pubdate, book.bookId,) t = (periodical_schema, name, None, pubdate, book.bookId,)
cursor.execute(query, t) cursor.execute(query, t)
connection.commit() connection.commit()

View File

@ -53,7 +53,6 @@ def substitute_entites(raw):
_CHARSET_ALIASES = { "macintosh" : "mac-roman", _CHARSET_ALIASES = { "macintosh" : "mac-roman",
"x-sjis" : "shift-jis" } "x-sjis" : "shift-jis" }
def force_encoding(raw, verbose, assume_utf8=False): def force_encoding(raw, verbose, assume_utf8=False):
from calibre.constants import preferred_encoding from calibre.constants import preferred_encoding
try: try:
@ -74,6 +73,36 @@ def force_encoding(raw, verbose, assume_utf8=False):
encoding = 'utf-8' encoding = 'utf-8'
return encoding return encoding
def detect_xml_encoding(raw, verbose=False, assume_utf8=False):
if not raw or isinstance(raw, unicode):
return raw, None
for x in ('utf8', 'utf-16-le', 'utf-16-be'):
bom = getattr(codecs, 'BOM_'+x.upper().replace('-16', '16').replace(
'-', '_'))
if raw.startswith(bom):
return raw[len(bom):], x
encoding = None
for pat in ENCODING_PATS:
match = pat.search(raw)
if match:
encoding = match.group(1)
break
if encoding is None:
encoding = force_encoding(raw, verbose, assume_utf8=assume_utf8)
if encoding.lower().strip() == 'macintosh':
encoding = 'mac-roman'
if encoding.lower().replace('_', '-').strip() in (
'gb2312', 'chinese', 'csiso58gb231280', 'euc-cn', 'euccn',
'eucgb2312-cn', 'gb2312-1980', 'gb2312-80', 'iso-ir-58'):
# Microsoft Word exports to HTML with encoding incorrectly set to
# gb2312 instead of gbk. gbk is a superset of gb2312, anyway.
encoding = 'gbk'
try:
codecs.lookup(encoding)
except LookupError:
encoding = 'utf-8'
return raw, encoding
def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False, def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False,
resolve_entities=False, assume_utf8=False): resolve_entities=False, assume_utf8=False):
@ -83,43 +112,16 @@ def xml_to_unicode(raw, verbose=False, strip_encoding_pats=False,
prints a warning if detection confidence is < 100% prints a warning if detection confidence is < 100%
@return: (unicode, encoding used) @return: (unicode, encoding used)
''' '''
encoding = None
if not raw: if not raw:
return u'', encoding return u'', None
raw, encoding = detect_xml_encoding(raw, verbose=verbose,
assume_utf8=assume_utf8)
if not isinstance(raw, unicode): if not isinstance(raw, unicode):
if raw.startswith(codecs.BOM_UTF8): raw = raw.decode(encoding, 'replace')
raw, encoding = raw.decode('utf-8')[1:], 'utf-8'
elif raw.startswith(codecs.BOM_UTF16_LE):
raw, encoding = raw.decode('utf-16-le')[1:], 'utf-16-le'
elif raw.startswith(codecs.BOM_UTF16_BE):
raw, encoding = raw.decode('utf-16-be')[1:], 'utf-16-be'
if not isinstance(raw, unicode):
for pat in ENCODING_PATS:
match = pat.search(raw)
if match:
encoding = match.group(1)
break
if encoding is None:
encoding = force_encoding(raw, verbose, assume_utf8=assume_utf8)
try:
if encoding.lower().strip() == 'macintosh':
encoding = 'mac-roman'
if encoding.lower().replace('_', '-').strip() in (
'gb2312', 'chinese', 'csiso58gb231280', 'euc-cn', 'euccn',
'eucgb2312-cn', 'gb2312-1980', 'gb2312-80', 'iso-ir-58'):
# Microsoft Word exports to HTML with encoding incorrectly set to
# gb2312 instead of gbk. gbk is a superset of gb2312, anyway.
encoding = 'gbk'
raw = raw.decode(encoding, 'replace')
except LookupError:
encoding = 'utf-8'
raw = raw.decode(encoding, 'replace')
if strip_encoding_pats: if strip_encoding_pats:
raw = strip_encoding_declarations(raw) raw = strip_encoding_declarations(raw)
if resolve_entities: if resolve_entities:
raw = substitute_entites(raw) raw = substitute_entites(raw)
return raw, encoding return raw, encoding

View File

@ -18,7 +18,7 @@ from functools import partial
from itertools import izip from itertools import izip
from calibre.customize.conversion import InputFormatPlugin from calibre.customize.conversion import InputFormatPlugin
from calibre.ebooks.chardet import xml_to_unicode from calibre.ebooks.chardet import detect_xml_encoding
from calibre.customize.conversion import OptionRecommendation from calibre.customize.conversion import OptionRecommendation
from calibre.constants import islinux, isbsd, iswindows from calibre.constants import islinux, isbsd, iswindows
from calibre import unicode_path, as_unicode from calibre import unicode_path, as_unicode
@ -121,7 +121,7 @@ class HTMLFile(object):
if not self.is_binary: if not self.is_binary:
if not encoding: if not encoding:
encoding = xml_to_unicode(src[:4096], verbose=verbose)[-1] encoding = detect_xml_encoding(src[:4096], verbose=verbose)[1]
self.encoding = encoding self.encoding = encoding
else: else:
self.encoding = encoding self.encoding = encoding

View File

@ -30,6 +30,8 @@ CONTENT_TAGS = set(['img', 'hr', 'br'])
NOT_VTAGS = HEADER_TAGS | NESTABLE_TAGS | TABLE_TAGS | SPECIAL_TAGS | \ NOT_VTAGS = HEADER_TAGS | NESTABLE_TAGS | TABLE_TAGS | SPECIAL_TAGS | \
CONTENT_TAGS CONTENT_TAGS
LEAF_TAGS = set(['base', 'basefont', 'frame', 'link', 'meta', 'area', 'br',
'col', 'hr', 'img', 'input', 'param'])
PAGE_BREAKS = set(['always', 'left', 'right']) PAGE_BREAKS = set(['always', 'left', 'right'])
COLLAPSE = re.compile(r'[ \t\r\n\v]+') COLLAPSE = re.compile(r'[ \t\r\n\v]+')
@ -246,7 +248,17 @@ class MobiMLizer(object):
last.text = None last.text = None
else: else:
last = bstate.body[-1] last = bstate.body[-1]
last.addprevious(anchor) # We use append instead of addprevious so that inline
# anchors in large blocks point to the correct place. See
# https://bugs.launchpad.net/calibre/+bug/899831
# This could potentially break if inserting an anchor at
# this point in the markup is illegal, but I cannot think
# of such a case offhand.
if barename(last.tag) in LEAF_TAGS:
last.addprevious(anchor)
else:
last.append(anchor)
istate.ids.clear() istate.ids.clear()
if not text: if not text:
return return
@ -528,7 +540,11 @@ class MobiMLizer(object):
old_mim = self.opts.mobi_ignore_margins old_mim = self.opts.mobi_ignore_margins
self.opts.mobi_ignore_margins = False self.opts.mobi_ignore_margins = False
if text or tag in CONTENT_TAGS or tag in NESTABLE_TAGS: if (text or tag in CONTENT_TAGS or tag in NESTABLE_TAGS or (
# We have an id but no text and no children, the id should still
# be added.
istate.ids and tag in ('a', 'span', 'i', 'b', 'u') and
len(elem)==0)):
self.mobimlize_content(tag, text, bstate, istates) self.mobimlize_content(tag, text, bstate, istates)
for child in elem: for child in elem:
self.mobimlize_elem(child, stylizer, bstate, istates) self.mobimlize_elem(child, stylizer, bstate, istates)

View File

@ -18,7 +18,8 @@ from calibre.ebooks.chardet import xml_to_unicode
from calibre.utils.zipfile import safe_replace from calibre.utils.zipfile import safe_replace
from calibre.utils.config import DynamicConfig from calibre.utils.config import DynamicConfig
from calibre.utils.logging import Log from calibre.utils.logging import Log
from calibre import guess_type, prints, prepare_string_for_xml from calibre import (guess_type, prints, prepare_string_for_xml,
xml_replace_entities)
from calibre.ebooks.oeb.transforms.cover import CoverManager from calibre.ebooks.oeb.transforms.cover import CoverManager
from calibre.constants import filesystem_encoding from calibre.constants import filesystem_encoding
@ -96,13 +97,19 @@ class EbookIterator(object):
self.ebook_ext = ext.replace('original_', '') self.ebook_ext = ext.replace('original_', '')
def search(self, text, index, backwards=False): def search(self, text, index, backwards=False):
text = text.lower() text = prepare_string_for_xml(text.lower())
pmap = [(i, path) for i, path in enumerate(self.spine)] pmap = [(i, path) for i, path in enumerate(self.spine)]
if backwards: if backwards:
pmap.reverse() pmap.reverse()
for i, path in pmap: for i, path in pmap:
if (backwards and i < index) or (not backwards and i > index): if (backwards and i < index) or (not backwards and i > index):
if text in open(path, 'rb').read().decode(path.encoding).lower(): with open(path, 'rb') as f:
raw = f.read().decode(path.encoding)
try:
raw = xml_replace_entities(raw)
except:
pass
if text in raw.lower():
return i return i
def find_missing_css_files(self): def find_missing_css_files(self):

View File

@ -9,7 +9,8 @@ import os
from functools import partial from functools import partial
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog, from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize) QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize,
QCoreApplication)
from calibre import isbytestring from calibre import isbytestring
from calibre.constants import filesystem_encoding, iswindows from calibre.constants import filesystem_encoding, iswindows
@ -384,11 +385,18 @@ class ChooseLibraryAction(InterfaceAction):
_('Database integrity check failed, click Show details' _('Database integrity check failed, click Show details'
' for details.'), show=True, det_msg=d.error[1]) ' for details.'), show=True, det_msg=d.error[1])
d = CheckLibraryDialog(self.gui, m.db) self.gui.status_bar.show_message(
if not d.do_exec(): _('Starting library scan, this may take a while'))
info_dialog(self.gui, _('No problems found'), try:
_('The files in your library match the information ' QCoreApplication.processEvents()
'in the database.'), show=True) d = CheckLibraryDialog(self.gui, m.db)
if not d.do_exec():
info_dialog(self.gui, _('No problems found'),
_('The files in your library match the information '
'in the database.'), show=True)
finally:
self.gui.status_bar.clear_message()
def switch_requested(self, location): def switch_requested(self, location):
if not self.change_library_allowed(): if not self.change_library_allowed():

View File

@ -9,7 +9,7 @@ from PyQt4.Qt import QThread, QObject, Qt, QProgressDialog, pyqtSignal, QTimer
from calibre.gui2.dialogs.progress import ProgressDialog from calibre.gui2.dialogs.progress import ProgressDialog
from calibre.gui2 import (question_dialog, error_dialog, info_dialog, gprefs, from calibre.gui2 import (question_dialog, error_dialog, info_dialog, gprefs,
warning_dialog) warning_dialog, available_width)
from calibre.ebooks.metadata.opf2 import OPF from calibre.ebooks.metadata.opf2 import OPF
from calibre.ebooks.metadata import MetaInformation from calibre.ebooks.metadata import MetaInformation
from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG from calibre.constants import preferred_encoding, filesystem_encoding, DEBUG
@ -244,6 +244,7 @@ class Adder(QObject): # {{{
def __init__(self, parent, db, callback, spare_server=None): def __init__(self, parent, db, callback, spare_server=None):
QObject.__init__(self, parent) QObject.__init__(self, parent)
self.pd = ProgressDialog(_('Adding...'), parent=parent) self.pd = ProgressDialog(_('Adding...'), parent=parent)
self.pd.setMaximumWidth(min(600, int(available_width()*0.75)))
self.spare_server = spare_server self.spare_server = spare_server
self.db = db self.db = db
self.pd.setModal(True) self.pd.setModal(True)

View File

@ -25,7 +25,8 @@ from calibre.utils.logging import Log
class BulkConfig(Config): class BulkConfig(Config):
def __init__(self, parent, db, preferred_output_format=None): def __init__(self, parent, db, preferred_output_format=None,
has_saved_settings=True):
ResizableDialog.__init__(self, parent) ResizableDialog.__init__(self, parent)
self.setup_output_formats(db, preferred_output_format) self.setup_output_formats(db, preferred_output_format)
@ -54,6 +55,12 @@ class BulkConfig(Config):
rb = self.buttonBox.button(self.buttonBox.RestoreDefaults) rb = self.buttonBox.button(self.buttonBox.RestoreDefaults)
rb.setVisible(False) rb.setVisible(False)
self.groups.setMouseTracking(True) self.groups.setMouseTracking(True)
if not has_saved_settings:
o = self.opt_individual_saved_settings
o.setEnabled(False)
o.setToolTip(_('None of the selected books have saved conversion '
'settings.'))
o.setChecked(False)
def setup_pipeline(self, *args): def setup_pipeline(self, *args):

View File

@ -230,8 +230,6 @@ class Text(Base):
def setup_ui(self, parent): def setup_ui(self, parent):
self.sep = self.col_metadata['multiple_seps'] self.sep = self.col_metadata['multiple_seps']
values = self.all_values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
if self.col_metadata['is_multiple']: if self.col_metadata['is_multiple']:
w = MultiCompleteLineEdit(parent) w = MultiCompleteLineEdit(parent)
@ -239,7 +237,6 @@ class Text(Base):
if self.sep['ui_to_list'] == '&': if self.sep['ui_to_list'] == '&':
w.set_space_before_sep(True) w.set_space_before_sep(True)
w.set_add_separator(tweaks['authors_completer_append_separator']) w.set_add_separator(tweaks['authors_completer_append_separator'])
w.update_items_cache(values)
w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred)
else: else:
w = MultiCompleteComboBox(parent) w = MultiCompleteComboBox(parent)
@ -249,16 +246,19 @@ class Text(Base):
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w] self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w]
def initialize(self, book_id): def initialize(self, book_id):
values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
self.widgets[1].clear()
self.widgets[1].update_items_cache(values)
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
self.initial_val = val self.initial_val = val
val = self.normalize_db_val(val) val = self.normalize_db_val(val)
self.widgets[1].update_items_cache(self.all_values)
if self.col_metadata['is_multiple']: if self.col_metadata['is_multiple']:
self.setter(val) self.setter(val)
else: else:
idx = None idx = None
for i, c in enumerate(self.all_values): for i, c in enumerate(values):
if c == val: if c == val:
idx = i idx = i
self.widgets[1].addItem(c) self.widgets[1].addItem(c)
@ -287,8 +287,6 @@ class Text(Base):
class Series(Base): class Series(Base):
def setup_ui(self, parent): def setup_ui(self, parent):
values = self.all_values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
w = MultiCompleteComboBox(parent) w = MultiCompleteComboBox(parent)
w.set_separator(None) w.set_separator(None)
w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon)
@ -305,6 +303,8 @@ class Series(Base):
self.widgets.append(w) self.widgets.append(w)
def initialize(self, book_id): def initialize(self, book_id):
values = list(self.db.all_custom(num=self.col_id))
values.sort(key=sort_key)
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True) s_index = self.db.get_custom_extra(book_id, num=self.col_id, index_is_id=True)
if s_index is None: if s_index is None:
@ -314,11 +314,12 @@ class Series(Base):
self.initial_val = val self.initial_val = val
val = self.normalize_db_val(val) val = self.normalize_db_val(val)
idx = None idx = None
for i, c in enumerate(self.all_values): self.name_widget.clear()
for i, c in enumerate(values):
if c == val: if c == val:
idx = i idx = i
self.name_widget.addItem(c) self.name_widget.addItem(c)
self.name_widget.update_items_cache(self.all_values) self.name_widget.update_items_cache(values)
self.name_widget.setEditText('') self.name_widget.setEditText('')
if idx is not None: if idx is not None:
self.widgets[1].setCurrentIndex(idx) self.widgets[1].setCurrentIndex(idx)

View File

@ -5,8 +5,9 @@ __license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>' __copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
import os import os, itertools, operator
from functools import partial from functools import partial
from future_builtins import map
from PyQt4.Qt import (QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, from PyQt4.Qt import (QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal,
QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication, QModelIndex, QIcon, QItemSelection, QMimeData, QDrag, QApplication,
@ -793,8 +794,13 @@ class BooksView(QTableView): # {{{
sel = QItemSelection() sel = QItemSelection()
m = self.model() m = self.model()
max_col = m.columnCount(QModelIndex()) - 1 max_col = m.columnCount(QModelIndex()) - 1
for row in rows: # Create a range based selector for each set of contiguous rows
sel.select(m.index(row, 0), m.index(row, max_col)) # as supplying selectors for each individual row causes very poor
# performance if a large number of rows has to be selected.
for k, g in itertools.groupby(enumerate(rows), lambda (i,x):i-x):
group = list(map(operator.itemgetter(1), g))
sel.merge(QItemSelection(m.index(min(group), 0),
m.index(max(group), max_col)), sm.Select)
sm.select(sel, sm.ClearAndSelect) sm.select(sel, sm.ClearAndSelect)
def get_selected_ids(self): def get_selected_ids(self):

View File

@ -112,7 +112,10 @@ def convert_bulk_ebook(parent, queue, db, book_ids, out_format=None, args=[]):
if total == 0: if total == 0:
return None, None, None return None, None, None
d = BulkConfig(parent, db, out_format) has_saved_settings = db.has_conversion_options(book_ids)
d = BulkConfig(parent, db, out_format,
has_saved_settings=has_saved_settings)
if d.exec_() != QDialog.Accepted: if d.exec_() != QDialog.Accepted:
return None return None

View File

@ -359,7 +359,7 @@ class Main(MainWindow, MainWindowMixin, DeviceMixin, EmailMixin, # {{{
'log will be displayed automatically.')%self.gui_debug, show=True) 'log will be displayed automatically.')%self.gui_debug, show=True)
def esc(self, *args): def esc(self, *args):
self.search.clear() self.clear_button.click()
def start_content_server(self, check_started=True): def start_content_server(self, check_started=True):
from calibre.library.server.main import start_threaded_server from calibre.library.server.main import start_threaded_server

View File

@ -172,7 +172,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
STATE_VERSION = 1 STATE_VERSION = 1
def __init__(self, pathtoebook=None, debug_javascript=False): def __init__(self, pathtoebook=None, debug_javascript=False, open_at=None):
MainWindow.__init__(self, None) MainWindow.__init__(self, None)
self.setupUi(self) self.setupUi(self)
self.view.magnification_changed.connect(self.magnification_changed) self.view.magnification_changed.connect(self.magnification_changed)
@ -280,7 +280,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
if pathtoebook is not None: if pathtoebook is not None:
f = functools.partial(self.load_ebook, pathtoebook) f = functools.partial(self.load_ebook, pathtoebook, open_at=open_at)
QTimer.singleShot(50, f) QTimer.singleShot(50, f)
self.view.setMinimumSize(100, 100) self.view.setMinimumSize(100, 100)
self.toc.setCursor(Qt.PointingHandCursor) self.toc.setCursor(Qt.PointingHandCursor)
@ -457,8 +457,8 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
def goto_end(self): def goto_end(self):
self.goto_page(self.pos.maximum()) self.goto_page(self.pos.maximum())
def goto_page(self, new_page): def goto_page(self, new_page, loaded_check=True):
if self.current_page is not None: if self.current_page is not None or not loaded_check:
for page in self.iterator.spine: for page in self.iterator.spine:
if new_page >= page.start_page and new_page <= page.max_page: if new_page >= page.start_page and new_page <= page.max_page:
try: try:
@ -672,7 +672,7 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
except: except:
traceback.print_exc() traceback.print_exc()
def load_ebook(self, pathtoebook): def load_ebook(self, pathtoebook, open_at=None):
if self.iterator is not None: if self.iterator is not None:
self.save_current_position() self.save_current_position()
self.iterator.__exit__() self.iterator.__exit__()
@ -731,10 +731,17 @@ class EbookViewer(MainWindow, Ui_EbookViewer):
self.current_index = -1 self.current_index = -1
QApplication.instance().alert(self, 5000) QApplication.instance().alert(self, 5000)
previous = self.set_bookmarks(self.iterator.bookmarks) previous = self.set_bookmarks(self.iterator.bookmarks)
if previous is not None: if open_at is None and previous is not None:
self.goto_bookmark(previous) self.goto_bookmark(previous)
else: else:
self.next_document() if open_at is None:
self.next_document()
else:
if open_at > self.pos.maximum():
open_at = self.pos.maximum()
if open_at < self.pos.minimum():
open_at = self.pos.minimum()
self.goto_page(open_at, loaded_check=False)
def set_vscrollbar_value(self, pagenum): def set_vscrollbar_value(self, pagenum):
self.vertical_scrollbar.blockSignals(True) self.vertical_scrollbar.blockSignals(True)
@ -804,6 +811,9 @@ def config(defaults=None):
help=_('Remember last used window size')) help=_('Remember last used window size'))
c.add_opt('debug_javascript', ['--debug-javascript'], default=False, c.add_opt('debug_javascript', ['--debug-javascript'], default=False,
help=_('Print javascript alert and console messages to the console')) help=_('Print javascript alert and console messages to the console'))
c.add_opt('open_at', ['--open-at'], default=None,
help=_('The position at which to open the specified book. The position is '
'a location as displayed in the top left corner of the viewer.'))
return c return c
@ -823,13 +833,17 @@ def main(args=sys.argv):
parser = option_parser() parser = option_parser()
opts, args = parser.parse_args(args) opts, args = parser.parse_args(args)
pid = os.fork() if False and (islinux or isbsd) else -1 pid = os.fork() if False and (islinux or isbsd) else -1
try:
open_at = float(opts.open_at)
except:
open_at = None
if pid <= 0: if pid <= 0:
app = Application(args) app = Application(args)
app.setWindowIcon(QIcon(I('viewer.png'))) app.setWindowIcon(QIcon(I('viewer.png')))
QApplication.setOrganizationName(ORG_NAME) QApplication.setOrganizationName(ORG_NAME)
QApplication.setApplicationName(APP_UID) QApplication.setApplicationName(APP_UID)
main = EbookViewer(args[1] if len(args) > 1 else None, main = EbookViewer(args[1] if len(args) > 1 else None,
debug_javascript=opts.debug_javascript) debug_javascript=opts.debug_javascript, open_at=open_at)
sys.excepthook = main.unhandled_exception sys.excepthook = main.unhandled_exception
main.show() main.show()
if opts.raise_window: if opts.raise_window:

View File

@ -1085,6 +1085,14 @@ ALTER TABLE books ADD COLUMN isbn TEXT DEFAULT "" COLLATE NOCASE;
return cPickle.loads(str(data)) return cPickle.loads(str(data))
return None return None
def has_conversion_options(self, ids, format='PIPE'):
ids = tuple(ids)
if len(ids) > 50000:
return True
return self.conn.get('''
SELECT data FROM conversion_options WHERE book IN %r AND
format=? LIMIT 1'''%(ids,), (format,), all=False) is not None
def delete_conversion_options(self, id, format, commit=True): def delete_conversion_options(self, id, format, commit=True):
self.conn.execute('DELETE FROM conversion_options WHERE book=? AND format=?', self.conn.execute('DELETE FROM conversion_options WHERE book=? AND format=?',
(id, format.upper())) (id, format.upper()))