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',
'GENERIC-', 'ZTE', 'MID', 'QUALCOMM', 'PANDIGIT', 'HYSTON',
'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',
'__UMS_COMPOSITE', '_MB200', 'MASS_STORAGE', '_-_CARD', 'SGH-I897',
'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',
'XT910', 'BOOK_A10', 'USB_2.0_DRIVER', 'I9100T', 'P999DW',
'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',
'FILE-STOR_GADGET', 'SGH-T959_CARD', 'SGH-T959', 'SAMSUNG_ANDROID', 'GT-P1000_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):
log = 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'):
if getattr(opts, x, None) is not None:
setattr(opts, x, abspath(getattr(opts, x)))

View File

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

View File

@ -113,6 +113,11 @@ class HTMLFile(object):
raise IOError(msg)
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 encoding:
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 "(.*?)"')
_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):
'''
Iterate over all links in a OEB Document.
@ -938,13 +955,10 @@ class Manifest(object):
if isinstance(data, etree._Element):
ans = xml2str(data, pretty_print=self.oeb.pretty_print)
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
# incorrectly by some browser based renderers
ans = re.sub(
# 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)
ans = close_self_closing_tags(ans)
return ans
if isinstance(data, unicode):
return data.encode('utf-8')

View File

@ -10,7 +10,7 @@ from functools import partial
from PyQt4.Qt import (QMenu, Qt, QInputDialog, QToolButton, QDialog,
QDialogButtonBox, QGridLayout, QLabel, QLineEdit, QIcon, QSize,
QCoreApplication)
QCoreApplication, pyqtSignal)
from calibre import isbytestring, sanitize_file_name_unicode
from calibre.constants import filesystem_encoding, iswindows
@ -142,6 +142,7 @@ class ChooseLibraryAction(InterfaceAction):
dont_add_to = frozenset(['context-menu-device'])
action_add_menu = True
action_menu_clone_qaction = _('Switch/create library...')
restore_view_state = pyqtSignal(object)
def genesis(self):
self.base_text = _('%d books')
@ -206,6 +207,17 @@ class ChooseLibraryAction(InterfaceAction):
self.maintenance_menu.addAction(ac)
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):
self.gui.iactions['Pick Random Book'].pick_random()
@ -221,6 +233,13 @@ class ChooseLibraryAction(InterfaceAction):
def library_changed(self, db):
self.stats.library_used(db)
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):
self.library_changed(self.gui.library_view.model().db)
@ -401,8 +420,11 @@ class ChooseLibraryAction(InterfaceAction):
def switch_requested(self, location):
if not self.change_library_allowed():
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)
exists = self.gui.library_view.model().db.exists_at(loc)
exists = db.exists_at(loc)
if not exists:
d = MovedDialog(self.stats, location, self.gui)
ret = d.exec_()

View File

@ -126,7 +126,8 @@ class BulkConfig(Config):
def setup_output_formats(self, db, preferred_output_format):
if preferred_output_format:
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')
preferred_output_format = preferred_output_format if \
preferred_output_format and preferred_output_format \

View File

@ -242,7 +242,8 @@ class Config(ResizableDialog, Ui_Dialog):
preferred_output_format):
if preferred_output_format:
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')
input_format, input_formats = get_input_format_for_book(db, book_id,
preferred_input_format)

View File

@ -32,8 +32,10 @@ class PreserveViewState(object): # {{{
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.require_selected_ids = require_selected_ids
self.selected_ids = set()
self.current_id = None
self.preserve_hpos = preserve_hpos
@ -51,15 +53,28 @@ class PreserveViewState(object): # {{{
traceback.print_exc()
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:
self.view.current_id = self.current_id
self.view.select_rows(self.selected_ids, using_ids=True,
scroll=False, change_current=self.current_id is None)
if self.selected_ids:
self.view.select_rows(self.selected_ids, using_ids=True,
scroll=False, change_current=self.current_id is None)
if self.preserve_vpos:
self.view.verticalScrollBar().setValue(self.vscroll)
if self.preserve_hpos:
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): # {{{

View File

@ -10,7 +10,8 @@ __docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QWidget, QDialog, QLabel, QGridLayout, QComboBox, QSize,
QLineEdit, QIntValidator, QDoubleValidator, QFrame, QColor, Qt, QIcon,
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.utils.icu import sort_key
@ -259,33 +260,36 @@ class RuleEditor(QDialog): # {{{
l.addWidget(l3, 2, 2)
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.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(
_('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)
sa.setMinimumHeight(300)
sa.setMinimumWidth(950)
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')),
_('Add another condition'))
l.addWidget(b, 5, 0, 1, 5)
l.addWidget(b, 5, 0, 1, 6)
b.clicked.connect(self.add_blank_condition)
self.l5 = l5 = QLabel(_('You can disable a condition by'
' blanking all of its boxes'))
l.addWidget(l5, 6, 0, 1, 5)
l.addWidget(l5, 6, 0, 1, 6)
self.bb = bb = QDialogButtonBox(
QDialogButtonBox.Ok|QDialogButtonBox.Cancel)
bb.accepted.connect(self.accept)
bb.rejected.connect(self.reject)
l.addWidget(bb, 7, 0, 1, 5)
l.addWidget(bb, 7, 0, 1, 6)
self.conditions_widget = QWidget(self)
sa.setWidget(self.conditions_widget)
@ -308,8 +312,21 @@ class RuleEditor(QDialog): # {{{
self.color_box.addItems(QColor.colorNames())
self.color_box.setCurrentIndex(0)
self.update_color_label()
self.color_box.currentIndexChanged.connect(self.update_color_label)
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):
c = ConditionEditor(self.fm, parent=self.conditions_widget)
self.conditions.append(c)
@ -610,14 +627,13 @@ class EditRules(QWidget): # {{{
# }}}
if __name__ == '__main__':
from PyQt4.Qt import QApplication
app = QApplication([])
from calibre.library import db
db = db()
if False:
if True:
d = RuleEditor(db.field_metadata)
d.add_blank_condition()
d.exec_()