Merge from trunk

This commit is contained in:
Charles Haley 2010-12-24 11:26:13 +00:00
commit 0415cf3266
72 changed files with 66215 additions and 53823 deletions

View File

@ -4,6 +4,101 @@
# for important features/bug fixes. # for important features/bug fixes.
# Also, each release can have new and improved recipes. # Also, each release can have new and improved recipes.
- version: 0.7.35
date: 2010-12-23
new features:
- title: "Add a simple to use Rich text editor for comments to the edit metadata dialog."
description: >
"You can now easily add formatting like bold/italic/lists/headings/colors/etc. to book comments via the
edit metadata dialog"
type: major
- title: "E-book viewer: Add a right click menu item 'Inspect' that allows you to inspect the underlying HTML/CSS source of the currently displayed content"
type: major
- title: "When deleting books from the library if a device is connected and the books are also present on the device ask the user if the books should be deleted from the device, the library, or both."
- title: "Add device drivers for Trekstore eBook Player 7, Sanda Bambook, ALuratek Color, Samsung Galaxy, LG Optimus, Motorola Droid 2 and Sunstech EB700"
tickets: [8021, 7966, 7973, 7956]
- title: "Add an entry to the menu of the calibre library button to select a random book from your calibre library"
tickets: [8010]
- title: "SONY driver: Add a couple of special extra collections for all books by author and all books by title, to workaround the broken sorting on newer SONY models. To enable these collections, go to Preferences->Plugins->Device Interface plugins and customize the SONY plugin."
- title: "Edit metadata dialog: When downloading metadata, make the table of matching books sortable"
tickets: [7951]
- title: "Add a success message after a database integrity check completes successfully"
- title: "Search and replace: When using regular expression mode, add a special input field '{template}' that allows use the templating language to create complex input fields. Also allow setting of series_index by search and replace using the same syntax as in the book list, namely, Series Name [series number]"
- title: "Bulk metadata edit: Add option to automatically set cover from the cover present in the actual ebook files"
tickets: [7947]
- title: "E-book viewer: Show format of current book in the title bar."
tickets: [7974]
- title: "Add a tweak to control how author names are displayed in the Tag Browser and Content Server"
- title: "FB2 Output: Restore sectionizing functionality"
bug fixes:
- title: "When in narrow layout, reserve 40% of available width in the book details panel for series/formats/etc and use the rest for comments"
tickets: [8028]
- title: "PDB Input: Fix failure to block-indent PML \t sections"
tickets: [8019]
- title: "Tag browser: When renaming items dont reset the library view and try not to scroll the Tag Browser itself"
- title: "Conversion pipeline: Fix broken link rewriting for inline CSS embedded in HTML"
- title: "Fix regression in 0.7.34 that broke recipes using extra_css to link to SONY device fonts"
tickets: [7995]
- title: "SONY driver: Don't upload thumbnails as they slow down post disconnect processing on older models"
- title: "Content server: Fix a bug that allowed remote users to read arbitrary png/gif/js/css/html files"
tickets: [7980]
- title: "On X11 initialize fontconfig in the GUI thread as Qt also uses fontconfig internally and fontconfig is not thread safe. Fixes a few random crashes on calibre strartup"
- title: "When using the remove specific format actions, only show available formats in the selected books"
tickets: [7967]
- title: "Linux binary build: If setting system default locale fails, try setting locale to en_US.UTF-8 instead"
- title: "Have the title sort tweak respected everywhere"
- title: "PocketBook 701 driver: Swap the main memory and card drives on windows"
- title: "Fix regression in templating that caused series_index to be shown even when book had no series"
tickets: [7949]
- title: "Content server: Fix regressiont hat broke browsing by rating"
- title: "Content server OPDS feeds: Fix parsing of author names as XML"
tickets: [7938]
improved recipes:
- Business Week Magazine
- Gazet van Antwerpen
- La Nacion
- New England Journal of Medicine
- Journal of Hospital Medicine
new recipes:
- title: "NRC Handelsblad (EPUB version)"
author: "veezh"
- title: "CND and wenxuecity - znjy"
author: "Derek Liang"
- title: "Mish's Global Economic Trend Analysis"
author: "Darko Miletic"
- version: 0.7.34 - version: 0.7.34
date: 2010-12-17 date: 2010-12-17

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

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.34' __version__ = '0.7.35'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>" __author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re import re

View File

@ -477,7 +477,8 @@ from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, \
SOVOS, PICO, SUNSTECH_EB700 SOVOS, PICO, SUNSTECH_EB700
from calibre.devices.sne.driver import SNE from calibre.devices.sne.driver import SNE
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \ from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, Q600, LUMIREAD, ALURATEK_COLOR, \
TREKSTOR
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
from calibre.devices.kobo.driver import KOBO from calibre.devices.kobo.driver import KOBO
from calibre.devices.bambook.driver import BAMBOOK from calibre.devices.bambook.driver import BAMBOOK
@ -603,6 +604,7 @@ plugins += [
LUMIREAD, LUMIREAD,
ALURATEK_COLOR, ALURATEK_COLOR,
BAMBOOK, BAMBOOK,
TREKSTOR,
ITUNES, ITUNES,
] ]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \ plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -224,3 +224,23 @@ class ALURATEK_COLOR(USBMS):
VENDOR_NAME = 'USB_2.0' VENDOR_NAME = 'USB_2.0'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'USB_FLASH_DRIVER' WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'USB_FLASH_DRIVER'
class TREKSTOR(USBMS):
name = 'Trekstor E-book player device interface'
gui_name = 'Trekstor'
description = _('Communicate with the Trekstor')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'osx', 'linux']
# Ordered list of supported formats
FORMATS = ['epub', 'txt', 'pdf']
VENDOR_ID = [0x1e68]
PRODUCT_ID = [0x0041]
BCD = [0x0002]
EBOOK_DIR_MAIN = 'Ebooks'
VENDOR_NAME = 'TREKSTOR'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_PLAYER_7'

View File

@ -0,0 +1,56 @@
#!/usr/bin/env python
# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
class RemoveFakeMargins(object):
'''
Try to detect and remove fake margins inserted by asinine ebook creation
software on each paragraph/wrapper div. Can be used only after CSS
flattening.
'''
def __call__(self, oeb, opts, log):
self.oeb, self.opts, self.log = oeb, opts, log
from calibre.ebooks.oeb.base import XPath, OEB_STYLES
stylesheet = None
for item in self.oeb.manifest:
if item.media_type.lower() in OEB_STYLES:
stylesheet = item.data
break
if stylesheet is None:
return
top_level_elements = {}
second_level_elements = {}
for x in self.oeb.spine:
root = x.data
body = XPath('//h:body')(root)
if body:
body = body[0]
if not hasattr(body, 'xpath'):
continue
# Check for margins on top level elements
for lb in XPath('./h:div|./h:p|./*/h:div|./*/h:p')(body):
cls = lb.get('class', '')
level = top_level_elements if lb.getparent() is body else \
second_level_elements
if cls not in level:
level[cls] = []
top_level_elements[cls] = []
level[cls].append(lb)
def get_margins(self, stylesheet, cls):
pass

View File

@ -249,7 +249,7 @@ class BookInfo(QWebView):
left_pane = u'<table>%s</table>'%rows left_pane = u'<table>%s</table>'%rows
right_pane = u'<div>%s</div>'%comments right_pane = u'<div>%s</div>'%comments
self.setHtml(templ%(u'<table><tr><td valign="top" ' self.setHtml(templ%(u'<table><tr><td valign="top" '
'style="padding-right:2em">%s</td><td valign="top">%s</td></tr></table>' 'style="padding-right:2em; width:40%%">%s</td><td valign="top">%s</td></tr></table>'
% (left_pane, right_pane))) % (left_pane, right_pane)))
def mouseDoubleClickEvent(self, ev): def mouseDoubleClickEvent(self, ev):

View File

@ -62,6 +62,8 @@ class EditorWidget(QWebView): # {{{
def __init__(self, parent=None): def __init__(self, parent=None):
QWebView.__init__(self, parent) QWebView.__init__(self, parent)
self.comments_pat = re.compile(r'<!--.*?-->', re.DOTALL)
for wac, name, icon, text, checkable in [ for wac, name, icon, text, checkable in [
('ToggleBold', 'bold', 'format-text-bold', _('Bold'), True), ('ToggleBold', 'bold', 'format-text-bold', _('Bold'), True),
('ToggleItalic', 'italic', 'format-text-italic', _('Italic'), ('ToggleItalic', 'italic', 'format-text-italic', _('Italic'),
@ -137,10 +139,19 @@ class EditorWidget(QWebView): # {{{
self.action_insert_link = QAction(QIcon(I('insert-link.png')), self.action_insert_link = QAction(QIcon(I('insert-link.png')),
_('Insert link'), self) _('Insert link'), self)
self.action_insert_link.triggered.connect(self.insert_link) self.action_insert_link.triggered.connect(self.insert_link)
self.action_clear = QAction(QIcon(I('edit-clear')), _('Clear'), self)
self.action_clear.triggered.connect(self.clear_text)
self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks) self.page().setLinkDelegationPolicy(QWebPage.DelegateAllLinks)
self.page().linkClicked.connect(self.link_clicked) self.page().linkClicked.connect(self.link_clicked)
self.setHtml('')
self.page().setContentEditable(True)
def clear_text(self, *args):
self.action_select_all.trigger()
self.action_cut.trigger()
def link_clicked(self, url): def link_clicked(self, url):
open_url(url) open_url(url)
@ -210,6 +221,7 @@ class EditorWidget(QWebView): # {{{
raw = unicode(self.page().mainFrame().toHtml()) raw = unicode(self.page().mainFrame().toHtml())
raw = xml_to_unicode(raw, strip_encoding_pats=True, raw = xml_to_unicode(raw, strip_encoding_pats=True,
resolve_entities=True)[0] resolve_entities=True)[0]
raw = self.comments_pat.sub('', raw)
try: try:
root = html.fromstring(raw) root = html.fromstring(raw)
@ -218,12 +230,17 @@ class EditorWidget(QWebView): # {{{
elems = [] elems = []
for body in root.xpath('//body'): for body in root.xpath('//body'):
if body.text:
elems.append(body.text)
elems += [html.tostring(x, encoding=unicode) for x in body if elems += [html.tostring(x, encoding=unicode) for x in body if
x.tag != 'script'] x.tag not in ('script', 'style')]
if len(elems) > 1: if len(elems) > 1:
ans = u'<div>%s</div>'%(u''.join(elems)) ans = u'<div>%s</div>'%(u''.join(elems))
else: else:
ans = u''.join(elems) ans = u''.join(elems)
if not ans.startswith('<'):
ans = '<p>%s</p>'%ans
ans = xml_replace_entities(ans) ans = xml_replace_entities(ans)
except: except:
import traceback import traceback
@ -500,6 +517,7 @@ class Editor(QWidget): # {{{
self.toolbar1.addAction(self.editor.action_redo) self.toolbar1.addAction(self.editor.action_redo)
self.toolbar1.addAction(self.editor.action_select_all) self.toolbar1.addAction(self.editor.action_select_all)
self.toolbar1.addAction(self.editor.action_remove_format) self.toolbar1.addAction(self.editor.action_remove_format)
self.toolbar1.addAction(self.editor.action_clear)
self.toolbar1.addSeparator() self.toolbar1.addSeparator()
for x in ('copy', 'cut', 'paste'): for x in ('copy', 'cut', 'paste'):
@ -519,7 +537,7 @@ class Editor(QWidget): # {{{
self.toolbar1.addAction(self.editor.action_block_style) self.toolbar1.addAction(self.editor.action_block_style)
w = self.toolbar1.widgetForAction(self.editor.action_block_style) w = self.toolbar1.widgetForAction(self.editor.action_block_style)
w.setPopupMode(w.InstantPopup) w.setPopupMode(w.InstantPopup)
self.toolbar1.addAction(self.editor.action_insert_link) self.toolbar2.addAction(self.editor.action_insert_link)
self.code_edit.textChanged.connect(self.code_dirtied) self.code_edit.textChanged.connect(self.code_dirtied)
self.editor.page().contentsChanged.connect(self.wyswyg_dirtied) self.editor.page().contentsChanged.connect(self.wyswyg_dirtied)

View File

@ -103,7 +103,7 @@ class PluginModel(QAbstractItemModel): # {{{
plugin = self.index_to_plugin(index) plugin = self.index_to_plugin(index)
if role == Qt.DisplayRole: if role == Qt.DisplayRole:
ver = '.'.join(map(str, plugin.version)) ver = '.'.join(map(str, plugin.version))
desc = '\n'.join(textwrap.wrap(plugin.description, 50)) desc = '\n'.join(textwrap.wrap(plugin.description, 100))
ans='%s (%s) %s %s\n%s'%(plugin.name, ver, _('by'), plugin.author, desc) ans='%s (%s) %s %s\n%s'%(plugin.name, ver, _('by'), plugin.author, desc)
c = plugin_customization(plugin) c = plugin_customization(plugin)
if c: if c:

View File

@ -51,6 +51,10 @@ def comments_to_html(comments):
if not isinstance(comments, unicode): if not isinstance(comments, unicode):
comments = comments.decode(preferred_encoding, 'replace') comments = comments.decode(preferred_encoding, 'replace')
if comments.lstrip().startswith('<'):
# Comment is already HTML do not mess with it
return comments
if '<' not in comments: if '<' not in comments:
comments = prepare_string_for_xml(comments) comments = prepare_string_for_xml(comments)
parts = [u'<p class="description">%s</p>'%x.replace(u'\n', u'<br />') parts = [u'<p class="description">%s</p>'%x.replace(u'\n', u'<br />')

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff