This commit is contained in:
Sengian 2012-05-05 15:34:39 +02:00
commit 7fc2bbc947
10 changed files with 106 additions and 26 deletions

View File

@ -177,7 +177,7 @@ class ANDROID(USBMS):
'TELECHIP', 'HUAWEI', 'T-MOBILE', 'SEMC', 'LGE', 'NVIDIA', 'TELECHIP', 'HUAWEI', 'T-MOBILE', 'SEMC', 'LGE', 'NVIDIA',
'GENERIC-', 'ZTE', 'MID', 'QUALCOMM', 'PANDIGIT', 'HYSTON', 'GENERIC-', 'ZTE', 'MID', 'QUALCOMM', 'PANDIGIT', 'HYSTON',
'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP', 'VIZIO', 'GOOGLE', 'FREESCAL', 'KOBO_INC', 'LENOVO', 'ROCKCHIP',
'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C'] 'POCKET', 'ONDA_MID', 'ZENITHIN', 'INGENIC', 'PMID701C', 'PD']
WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE', WINDOWS_MAIN_MEM = ['ANDROID_PHONE', 'A855', 'A853', 'INC.NEXUS_ONE',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897', '__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-I9000', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID',
@ -193,7 +193,7 @@ class ANDROID(USBMS):
'GT-I9003_CARD', 'XT912', 'FILE-CD_GADGET', 'RK29_SDK', 'MB855', 'GT-I9003_CARD', 'XT912', 'FILE-CD_GADGET', 'RK29_SDK', 'MB855',
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW', 'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD', 'USB_2.0_DRIVER', 'KTABLET_PC', 'INGENIC', 'GT-I9001_CARD', 'USB_2.0_DRIVER',
'GT-S5830L_CARD'] 'GT-S5830L_CARD', 'UNIVERSE']
WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897', WINDOWS_CARD_A_MEM = ['ANDROID_PHONE', 'GT-I9000_CARD', 'SGH-I897',
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD', 'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_CARD',
'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD', 'A70S', 'A101IT', '7', 'INCREDIBLE', 'A7EB', 'SGH-T849_CARD',

View File

@ -304,7 +304,10 @@ def read_sr_patterns(path, log=None):
def main(args=sys.argv): def main(args=sys.argv):
log = Log() log = Log()
parser, plumber = create_option_parser(args, log) parser, plumber = create_option_parser(args, log)
opts = parser.parse_args(args)[0] opts, leftover_args = parser.parse_args(args)
if len(leftover_args) > 3:
log.error('Extra arguments not understood:', u', '.join(leftover_args[3:]))
return 1
for x in ('read_metadata_from_opf', 'cover'): for x in ('read_metadata_from_opf', 'cover'):
if getattr(opts, x, None) is not None: if getattr(opts, x, None) is not None:
setattr(opts, x, abspath(getattr(opts, x))) setattr(opts, x, abspath(getattr(opts, x)))

View File

@ -148,7 +148,7 @@ class HeuristicProcessor(object):
return wordcount.words return wordcount.words
def markup_italicis(self, html): def markup_italicis(self, html):
self.log.debug("\n\n\nitalicize debugging \n\n\n") #self.log.debug("\n\n\nitalicize debugging \n\n\n")
ITALICIZE_WORDS = [ ITALICIZE_WORDS = [
'Etc.', 'etc.', 'viz.', 'ie.', 'i.e.', 'Ie.', 'I.e.', 'eg.', 'Etc.', 'etc.', 'viz.', 'ie.', 'i.e.', 'Ie.', 'I.e.', 'eg.',
'e.g.', 'Eg.', 'E.g.', 'et al.', 'et cetera', 'n.b.', 'N.b.', 'e.g.', 'Eg.', 'E.g.', 'et al.', 'et cetera', 'n.b.', 'N.b.',
@ -184,6 +184,9 @@ class HeuristicProcessor(object):
except OverflowError: except OverflowError:
# match.group(0) was too large to be compiled into a regex # match.group(0) was too large to be compiled into a regex
continue continue
except re.error:
# the match was not a valid regular expression
continue
return html return html

View File

@ -113,6 +113,11 @@ class HTMLFile(object):
raise IOError(msg) raise IOError(msg)
raise IgnoreFile(msg, err.errno) raise IgnoreFile(msg, err.errno)
if not src:
if level == 0:
raise ValueError('The file %s is empty'%self.path)
self.is_binary = True
if not self.is_binary: if not self.is_binary:
if not encoding: if not encoding:
encoding = detect_xml_encoding(src[:4096], verbose=verbose)[1] encoding = detect_xml_encoding(src[:4096], verbose=verbose)[1]

View File

@ -81,6 +81,23 @@ _css_url_re = re.compile(r'url\s*\([\'"]{0,1}(.*?)[\'"]{0,1}\)', re.I)
_css_import_re = re.compile(r'@import "(.*?)"') _css_import_re = re.compile(r'@import "(.*?)"')
_archive_re = re.compile(r'[^ ]+') _archive_re = re.compile(r'[^ ]+')
# Tags that should not be self closed in epub output
self_closing_bad_tags = {'a', 'abbr', 'address', 'article', 'aside', 'audio', 'b',
'bdo', 'blockquote', 'body', 'button', 'cite', 'code', 'dd', 'del', 'details',
'dfn', 'div', 'dl', 'dt', 'em', 'fieldset', 'figcaption', 'figure', 'footer',
'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header', 'hgroup', 'i', 'ins', 'kbd',
'label', 'legend', 'li', 'map', 'mark', 'meter', 'nav', 'ol', 'output', 'p',
'pre', 'progress', 'q', 'rp', 'rt', 'samp', 'section', 'select', 'small',
'span', 'strong', 'sub', 'summary', 'sup', 'textarea', 'time', 'ul', 'var',
'video'}
_self_closing_pat = re.compile(
r'<(?P<tag>%s)(?=[\s/])(?P<arg>[^>]*)/>'%('|'.join(self_closing_bad_tags)),
re.IGNORECASE)
def close_self_closing_tags(raw):
return _self_closing_pat.sub(r'<\g<tag>\g<arg>></\g<tag>>', raw)
def iterlinks(root, find_links_in_css=True): def iterlinks(root, find_links_in_css=True):
''' '''
Iterate over all links in a OEB Document. Iterate over all links in a OEB Document.
@ -938,13 +955,10 @@ class Manifest(object):
if isinstance(data, etree._Element): if isinstance(data, etree._Element):
ans = xml2str(data, pretty_print=self.oeb.pretty_print) ans = xml2str(data, pretty_print=self.oeb.pretty_print)
if self.media_type in OEB_DOCS: if self.media_type in OEB_DOCS:
# Convert self closing div|span|a|video|audio|iframe tags # Convert self closing div|span|a|video|audio|iframe|etc tags
# to normally closed ones, as they are interpreted # to normally closed ones, as they are interpreted
# incorrectly by some browser based renderers # incorrectly by some browser based renderers
ans = re.sub( ans = close_self_closing_tags(ans)
# tag name followed by either a space or a /
r'<(?P<tag>div|a|span|video|audio|iframe)(?=[\s/])(?P<arg>[^>]*)/>',
r'<\g<tag>\g<arg>></\g<tag>>', ans)
return ans return ans
if isinstance(data, unicode): if isinstance(data, unicode):
return data.encode('utf-8') return data.encode('utf-8')

View File

@ -10,7 +10,7 @@ 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) QCoreApplication, pyqtSignal)
from calibre import isbytestring, sanitize_file_name_unicode from calibre import isbytestring, sanitize_file_name_unicode
from calibre.constants import filesystem_encoding, iswindows from calibre.constants import filesystem_encoding, iswindows
@ -142,6 +142,7 @@ class ChooseLibraryAction(InterfaceAction):
dont_add_to = frozenset(['context-menu-device']) dont_add_to = frozenset(['context-menu-device'])
action_add_menu = True action_add_menu = True
action_menu_clone_qaction = _('Switch/create library...') action_menu_clone_qaction = _('Switch/create library...')
restore_view_state = pyqtSignal(object)
def genesis(self): def genesis(self):
self.base_text = _('%d books') self.base_text = _('%d books')
@ -206,6 +207,17 @@ class ChooseLibraryAction(InterfaceAction):
self.maintenance_menu.addAction(ac) self.maintenance_menu.addAction(ac)
self.choose_menu.addMenu(self.maintenance_menu) self.choose_menu.addMenu(self.maintenance_menu)
self.view_state_map = {}
self.restore_view_state.connect(self._restore_view_state,
type=Qt.QueuedConnection)
@property
def preserve_state_on_switch(self):
ans = getattr(self, '_preserve_state_on_switch', None)
if ans is None:
self._preserve_state_on_switch = ans = \
self.gui.library_view.preserve_state(require_selected_ids=False)
return ans
def pick_random(self, *args): def pick_random(self, *args):
self.gui.iactions['Pick Random Book'].pick_random() self.gui.iactions['Pick Random Book'].pick_random()
@ -221,6 +233,13 @@ class ChooseLibraryAction(InterfaceAction):
def library_changed(self, db): def library_changed(self, db):
self.stats.library_used(db) self.stats.library_used(db)
self.build_menus() self.build_menus()
state = self.view_state_map.get(self.stats.canonicalize_path(
db.library_path), None)
if state is not None:
self.restore_view_state.emit(state)
def _restore_view_state(self, state):
self.preserve_state_on_switch.state = state
def initialization_complete(self): def initialization_complete(self):
self.library_changed(self.gui.library_view.model().db) self.library_changed(self.gui.library_view.model().db)
@ -401,8 +420,11 @@ class ChooseLibraryAction(InterfaceAction):
def switch_requested(self, location): def switch_requested(self, location):
if not self.change_library_allowed(): if not self.change_library_allowed():
return return
db = self.gui.library_view.model().db
current_lib = self.stats.canonicalize_path(db.library_path)
self.view_state_map[current_lib] = self.preserve_state_on_switch.state
loc = location.replace('/', os.sep) loc = location.replace('/', os.sep)
exists = self.gui.library_view.model().db.exists_at(loc) exists = db.exists_at(loc)
if not exists: if not exists:
d = MovedDialog(self.stats, location, self.gui) d = MovedDialog(self.stats, location, self.gui)
ret = d.exec_() ret = d.exec_()

View File

@ -126,7 +126,8 @@ class BulkConfig(Config):
def setup_output_formats(self, db, preferred_output_format): def setup_output_formats(self, db, preferred_output_format):
if preferred_output_format: if preferred_output_format:
preferred_output_format = preferred_output_format.lower() preferred_output_format = preferred_output_format.lower()
output_formats = sorted(available_output_formats()) output_formats = sorted(available_output_formats(),
key=lambda x:{'EPUB':'!A', 'MOBI':'!B'}.get(x.upper(), x))
output_formats.remove('oeb') output_formats.remove('oeb')
preferred_output_format = preferred_output_format if \ preferred_output_format = preferred_output_format if \
preferred_output_format and preferred_output_format \ preferred_output_format and preferred_output_format \

View File

@ -242,7 +242,8 @@ class Config(ResizableDialog, Ui_Dialog):
preferred_output_format): preferred_output_format):
if preferred_output_format: if preferred_output_format:
preferred_output_format = preferred_output_format.lower() preferred_output_format = preferred_output_format.lower()
output_formats = sorted(available_output_formats()) output_formats = sorted(available_output_formats(),
key=lambda x:{'EPUB':'!A', 'MOBI':'!B'}.get(x.upper(), x))
output_formats.remove('oeb') output_formats.remove('oeb')
input_format, input_formats = get_input_format_for_book(db, book_id, input_format, input_formats = get_input_format_for_book(db, book_id,
preferred_input_format) preferred_input_format)

View File

@ -32,8 +32,10 @@ class PreserveViewState(object): # {{{
and dont affect the scroll position. and dont affect the scroll position.
''' '''
def __init__(self, view, preserve_hpos=True, preserve_vpos=True): def __init__(self, view, preserve_hpos=True, preserve_vpos=True,
require_selected_ids=True):
self.view = view self.view = view
self.require_selected_ids = require_selected_ids
self.selected_ids = set() self.selected_ids = set()
self.current_id = None self.current_id = None
self.preserve_hpos = preserve_hpos self.preserve_hpos = preserve_hpos
@ -51,15 +53,28 @@ class PreserveViewState(object): # {{{
traceback.print_exc() traceback.print_exc()
def __exit__(self, *args): def __exit__(self, *args):
if self.selected_ids: if self.selected_ids or not self.require_selected_ids:
if self.current_id is not None: if self.current_id is not None:
self.view.current_id = self.current_id self.view.current_id = self.current_id
self.view.select_rows(self.selected_ids, using_ids=True, if self.selected_ids:
scroll=False, change_current=self.current_id is None) self.view.select_rows(self.selected_ids, using_ids=True,
scroll=False, change_current=self.current_id is None)
if self.preserve_vpos: if self.preserve_vpos:
self.view.verticalScrollBar().setValue(self.vscroll) self.view.verticalScrollBar().setValue(self.vscroll)
if self.preserve_hpos: if self.preserve_hpos:
self.view.horizontalScrollBar().setValue(self.hscroll) self.view.horizontalScrollBar().setValue(self.hscroll)
@dynamic_property
def state(self):
def fget(self):
self.__enter__()
return {x:getattr(self, x) for x in ('selected_ids', 'current_id',
'vscroll', 'hscroll')}
def fset(self, state):
for k, v in state.iteritems(): setattr(self, k, v)
self.__exit__()
return property(fget=fget, fset=fset)
# }}} # }}}
class BooksView(QTableView): # {{{ class BooksView(QTableView): # {{{

View File

@ -10,7 +10,8 @@ __docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize, from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize,
QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon, QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon,
QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton, QScrollArea, QPushButton, QVBoxLayout, QDialogButtonBox, QToolButton,
QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem) QListView, QAbstractListModel, pyqtSignal, QSizePolicy, QSpacerItem,
QApplication)
from calibre import prepare_string_for_xml from calibre import prepare_string_for_xml
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
@ -259,33 +260,36 @@ class RuleEditor(QDialog): # {{{
l.addWidget(l3, 2, 2) l.addWidget(l3, 2, 2)
self.color_box = QComboBox(self) self.color_box = QComboBox(self)
self.color_label = QLabel('Sample text Sample text')
self.color_label.setTextFormat(Qt.RichText)
l.addWidget(self.color_box, 2, 3) l.addWidget(self.color_box, 2, 3)
l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 4) l.addWidget(self.color_label, 2, 4)
l.addItem(QSpacerItem(10, 10, QSizePolicy.Expanding), 2, 5)
self.l4 = l4 = QLabel( self.l4 = l4 = QLabel(
_('Only if the following conditions are all satisfied:')) _('Only if the following conditions are all satisfied:'))
l.addWidget(l4, 3, 0, 1, 5) l.addWidget(l4, 3, 0, 1, 6)
self.scroll_area = sa = QScrollArea(self) self.scroll_area = sa = QScrollArea(self)
sa.setMinimumHeight(300) sa.setMinimumHeight(300)
sa.setMinimumWidth(950) sa.setMinimumWidth(950)
sa.setWidgetResizable(True) sa.setWidgetResizable(True)
l.addWidget(sa, 4, 0, 1, 5) l.addWidget(sa, 4, 0, 1, 6)
self.add_button = b = QPushButton(QIcon(I('plus.png')), self.add_button = b = QPushButton(QIcon(I('plus.png')),
_('Add another condition')) _('Add another condition'))
l.addWidget(b, 5, 0, 1, 5) l.addWidget(b, 5, 0, 1, 6)
b.clicked.connect(self.add_blank_condition) b.clicked.connect(self.add_blank_condition)
self.l5 = l5 = QLabel(_('You can disable a condition by' self.l5 = l5 = QLabel(_('You can disable a condition by'
' blanking all of its boxes')) ' blanking all of its boxes'))
l.addWidget(l5, 6, 0, 1, 5) l.addWidget(l5, 6, 0, 1, 6)
self.bb = bb = QDialogButtonBox( self.bb = bb = QDialogButtonBox(
QDialogButtonBox.Ok|QDialogButtonBox.Cancel) QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
bb.accepted.connect(self.accept) bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject) bb.rejected.connect(self.reject)
l.addWidget(bb, 7, 0, 1, 5) l.addWidget(bb, 7, 0, 1, 6)
self.conditions_widget = QWidget(self) self.conditions_widget = QWidget(self)
sa.setWidget(self.conditions_widget) sa.setWidget(self.conditions_widget)
@ -308,8 +312,21 @@ class RuleEditor(QDialog): # {{{
self.color_box.addItems(QColor.colorNames()) self.color_box.addItems(QColor.colorNames())
self.color_box.setCurrentIndex(0) self.color_box.setCurrentIndex(0)
self.update_color_label()
self.color_box.currentIndexChanged.connect(self.update_color_label)
self.resize(self.sizeHint()) self.resize(self.sizeHint())
def update_color_label(self):
pal = QApplication.palette()
bg1 = unicode(pal.color(pal.Base).name())
bg2 = unicode(pal.color(pal.AlternateBase).name())
c = unicode(self.color_box.currentText())
self.color_label.setText('''
<span style="color: {c}; background-color: {bg1}">&nbsp;{st}&nbsp;</span>
<span style="color: {c}; background-color: {bg2}">&nbsp;{st}&nbsp;</span>
'''.format(c=c, bg1=bg1, bg2=bg2, st=_('Sample Text')))
def add_blank_condition(self): def add_blank_condition(self):
c = ConditionEditor(self.fm, parent=self.conditions_widget) c = ConditionEditor(self.fm, parent=self.conditions_widget)
self.conditions.append(c) self.conditions.append(c)
@ -610,14 +627,13 @@ class EditRules(QWidget): # {{{
# }}} # }}}
if __name__ == '__main__': if __name__ == '__main__':
from PyQt4.Qt import QApplication
app = QApplication([]) app = QApplication([])
from calibre.library import db from calibre.library import db
db = db() db = db()
if False: if True:
d = RuleEditor(db.field_metadata) d = RuleEditor(db.field_metadata)
d.add_blank_condition() d.add_blank_condition()
d.exec_() d.exec_()