Merge from trunk

This commit is contained in:
Charles Haley 2010-10-04 07:58:19 +01:00
commit 134bbcdbd0
42 changed files with 45835 additions and 31564 deletions

View File

@ -4,6 +4,52 @@
# for important features/bug fixes.
# Also, each release can have new and improved recipes.
- version: 0.7.22
date: 2010-10-03
new features:
- title: "Drag and drop books from your calibre library"
type: major
description: >
"You can now drag and drop books from your calibre library. You can drag them to the desktop or to a file explorer, to copy them to your computer. You can drag them to the
device icon in calibre to send them to the device. You can also drag and drop books from the device view in calibre to the calibre library icon or the operating
system to copy them from the device."
- title: "There were many minor bug fixes for various bugs caused by the major changes in 0.7.21. So if you have updated to 0.7.21, it is highly recommended you update to 0.7.22"
- title: "Driver for the VelocityMicro ebook reader device"
- title: "Add a tweak to control how articles in titles are processed during sorting"
- title: "Add a new format type 'device_db' to plugboards to control the metadata displayed in book lists on SONY devices."
bug fixes:
- title: "Fix ISBN not being read from filenames in 0.7.21"
tickets: [7054]
- title: "Fix instant Search for text not found causes unhandled exception when conversion jobs are running"
tickets: [7043]
- title: "Fix removing a publisher causes an error in 0.7.21"
tickets: [7046]
- title: "MOBI Output: Fix some images being distorted in 0.7.21"
tickets: [7049]
- title: "Fix regression that broke bulk conversion of books without covers in 0.7.21"
- title: "Fix regression that broke add and set_metadata commands in calibredb in 0.7.21"
- title: "Workaround for Qt bug in file open dialogs in linux that causes multiple file selection to ignore files with two or more spaces in the file name"
- title: "Conversion pipeline: Fix regression in 0.7.21 that broke conversion of LIT/EPUB documents that specified no title in their OPF files"
- title: "Fix regression that broke iPad driver in 0.7.21"
improved recipes:
- Washington Post
- version: 0.7.21
date: 2010-10-01

View File

@ -1,3 +1,4 @@
from calibre.web.feeds.news import re
from calibre.web.feeds.recipes import BasicNewsRecipe
from BeautifulSoup import Tag
@ -10,26 +11,31 @@ class RevistaMuyInteresante(BasicNewsRecipe):
language = 'es'
no_stylesheets = True
remove_attributes = ['style', 'font']
remove_javascript = True
extra_css = ' .txt_articulo{ font-family: sans-serif; font-size: medium; text-align: justify } .contentheading{font-family: serif; font-size: large; font-weight: bold; color: #000000; text-align: center}'
#then we add our own style(s) like this:
extra_css = '''
.contentheading{font-weight: bold}
p {font-size: 4px;font-family: Times New Roman;}
'''
def preprocess_html(self, soup):
for item in soup.findAll(style=True):
del item['style']
for img_tag in soup.findAll('img'):
parent_tag = img_tag.parent
if parent_tag.name == 'td':
if not parent_tag.get('class') == 'txt_articulo': break
imagen = img_tag
new_tag = Tag(soup,'p')
img_tag.replaceWith(new_tag)
div = soup.find(attrs={'class':'article_category'})
div.insert(0,imagen)
imagen = img_tag
new_tag = Tag(soup,'p')
img_tag.replaceWith(new_tag)
div = soup.find(attrs={'class':'article_category'})
div.insert(0,imagen)
break
return soup
preprocess_regexps = [
(re.compile(r'<td class="contentheading" width="100%">.*?</td>', re.DOTALL|re.IGNORECASE), lambda match: '<td class="contentheading">' + match.group().replace('<td class="contentheading" width="100%">','').strip().replace('</td>','').strip() + '</td>'),
]
keep_only_tags = [dict(name='div', attrs={'class':['article']}),dict(name='td', attrs={'class':['txt_articulo']})]
remove_tags = [
@ -37,6 +43,7 @@ class RevistaMuyInteresante(BasicNewsRecipe):
,dict(name='div', attrs={'id':['comment']})
,dict(name='td', attrs={'class':['buttonheading']})
,dict(name='div', attrs={'class':['tags_articles']})
,dict(name='table', attrs={'class':['pagenav']})
]
remove_tags_after = dict(name='div', attrs={'class':'tags_articles'})
@ -71,8 +78,33 @@ class RevistaMuyInteresante(BasicNewsRecipe):
for title, url in [
('Historia',
'http://www.muyinteresante.es/historia-articulos'),
('Ciencia',
'http://www.muyinteresante.es/ciencia-articulos'),
('Naturaleza',
'http://www.muyinteresante.es/naturaleza-articulos'),
('Tecnología',
'http://www.muyinteresante.es/tecnologia-articulos'),
('Salud',
'http://www.muyinteresante.es/salud-articulos'),
('Más Muy',
'http://www.muyinteresante.es/muy'),
('Innova - Automoción',
'http://www.muyinteresante.es/articulos-innovacion-autos'),
('Innova - Salud',
'http://www.muyinteresante.es/articulos-innovacion-salud'),
('Innova - Medio Ambiente',
'http://www.muyinteresante.es/articulos-innovacion-medio-ambiente'),
('Innova - Alimentación',
'http://www.muyinteresante.es/articulos-innovacion-alimentacion'),
('Innova - Sociedad',
'http://www.muyinteresante.es/articulos-innovacion-sociedad'),
('Innova - Tecnología',
'http://www.muyinteresante.es/articulos-innovacion-tecnologia'),
('Innova - Ocio',
'http://www.muyinteresante.es/articulos-innovacion-ocio'),
]:
articles = self.nz_parse_section(url)
if articles:
feeds.append((title, articles))
return feeds

View File

@ -2,7 +2,7 @@ __license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
__appname__ = 'calibre'
__version__ = '0.7.21'
__version__ = '0.7.22'
__author__ = "Kovid Goyal <kovid@kovidgoyal.net>"
import re

View File

@ -460,7 +460,8 @@ from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK
from calibre.devices.edge.driver import EDGE
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, SOVOS
from calibre.devices.sne.driver import SNE
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, GEMEI
from calibre.devices.misc import PALMPRE, AVANT, SWEEX, PDNOVEL, KOGAN, \
GEMEI, VELOCITYMICRO
from calibre.devices.folder_device.driver import FOLDER_DEVICE_FOR_CONFIG
from calibre.devices.kobo.driver import KOBO
@ -572,6 +573,7 @@ plugins += [
PDNOVEL,
SPECTRA,
GEMEI,
VELOCITYMICRO,
ITUNES,
]
plugins += [x for x in list(locals().values()) if isinstance(x, type) and \

View File

@ -20,7 +20,7 @@ class IREXDR1000(USBMS):
# Ordered list of supported formats
# Be sure these have an entry in calibre.devices.mime
FORMATS = ['epub', 'mobi', 'prc', 'html', 'pdf', 'txt']
FORMATS = ['epub', 'mobi', 'prc', 'html', 'pdf', 'djvu', 'txt']
VENDOR_ID = [0x1e6b]
PRODUCT_ID = [0x001]

View File

@ -108,6 +108,24 @@ class PDNOVEL(USBMS):
with open('%s.jpg' % os.path.join(path, filename), 'wb') as coverfile:
coverfile.write(coverdata[2])
class VELOCITYMICRO(USBMS):
name = 'VelocityMicro device interface'
gui_name = 'VelocityMicro'
description = _('Communicate with the VelocityMicro')
author = 'Kovid Goyal'
supported_platforms = ['windows', 'linux', 'osx']
FORMATS = ['epub', 'pdb', 'txt', 'html', 'pdf']
VENDOR_ID = [0x18d1]
PRODUCT_ID = [0xb015]
BCD = [0x224]
VENDOR_NAME = 'ANDROID'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '__UMS_COMPOSITE'
EBOOK_DIR_MAIN = 'eBooks'
SUPPORTS_SUB_DIRS = False
class GEMEI(USBMS):
name = 'Gemei Device Interface'
gui_name = 'GM2000'

View File

@ -14,7 +14,6 @@ from calibre.devices.prs505 import CACHE_XML
from calibre.devices.prs505.sony_cache import XMLCache
from calibre import __appname__
from calibre.devices.usbms.books import CollectionsBookList
from calibre.utils.config import tweaks
class PRS505(USBMS):
@ -171,4 +170,4 @@ class PRS505(USBMS):
def set_plugboard(self, pb):
debug_print('PRS505: use plugboard', pb)
self.plugboard = pb
self.plugboard = pb

View File

@ -151,7 +151,8 @@ class CHMReader(CHMFile):
continue
raise
self._extracted = True
files = os.listdir(output_dir)
files = [x for x in os.listdir(output_dir) if
os.path.isfile(os.path.join(output_dir, x))]
if self.hhc_path not in files:
for f in files:
if f.lower() == self.hhc_path.lower():

View File

@ -31,12 +31,14 @@ class CoverManager(object):
</style>
</head>
<body>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%%" height="100%%" viewBox="__viewbox__"
preserveAspectRatio="__ar__">
<image width="__width__" height="__height__" xlink:href="%s"/>
</svg>
<div>
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="100%%" height="100%%" viewBox="__viewbox__"
preserveAspectRatio="__ar__">
<image width="__width__" height="__height__" xlink:href="%s"/>
</svg>
</div>
</body>
</html>
''')

View File

@ -425,8 +425,10 @@ class BooksView(QTableView): # {{{
Accept a drop event and return a list of paths that can be read from
and represent files with extensions.
'''
if event.mimeData().hasFormat('text/uri-list'):
urls = [unicode(u.toLocalFile()) for u in event.mimeData().urls()]
md = event.mimeData()
if md.hasFormat('text/uri-list') and not \
md.hasFormat('application/calibre+from_library'):
urls = [unicode(u.toLocalFile()) for u in md.urls()]
return [u for u in urls if os.path.splitext(u)[1] and os.access(u, os.R_OK)]
def drag_icon(self, cover, multiple):
@ -465,8 +467,25 @@ class BooksView(QTableView): # {{{
ids = ' '.join(map(str, selected))
md = QMimeData()
md.setData('application/calibre+from_library', ids)
md.setUrls([QUrl.fromLocalFile(db.abspath(i, index_is_id=True))
for i in selected])
fmt = prefs['output_format']
def url_for_id(i):
ans = db.format_abspath(i, fmt, index_is_id=True)
if ans is None:
fmts = db.formats(i, index_is_id=True)
if fmts:
fmts = fmts.split(',')
else:
fmts = []
for f in fmts:
ans = db.format_abspath(i, f, index_is_id=True)
if ans is not None:
break
if ans is None:
ans = db.abspath(i, index_is_id=True)
return QUrl.fromLocalFile(ans)
md.setUrls([url_for_id(i) for i in selected])
drag = QDrag(self)
drag.setMimeData(md)
cover = self.drag_icon(m.cover(self.currentIndex().row()),

View File

@ -79,6 +79,8 @@ class TagsView(QTreeView): # {{{
self.setHeaderHidden(True)
self.setItemDelegate(TagDelegate(self))
self.made_connections = False
self.setAcceptDrops(True)
self.setDropIndicatorShown(True)
def set_database(self, db, tag_match, sort_by):
self.hidden_categories = config['tag_browser_hidden_categories']
@ -104,6 +106,49 @@ class TagsView(QTreeView): # {{{
def database_changed(self, event, ids):
self.refresh_required.emit()
def dragEnterEvent(self, event):
md = event.mimeData()
if md.hasFormat("application/calibre+from_library"):
event.setDropAction(Qt.CopyAction)
event.accept()
else:
event.ignore()
def dragMoveEvent(self, event):
allowed = False
idx = self.indexAt(event.pos())
m = self.model()
p = m.parent(idx)
if idx.isValid() and p.isValid():
item = m.data(p, Qt.UserRole)
if item.type == TagTreeItem.CATEGORY and \
item.category_key in \
('tags', 'series', 'authors', 'rating', 'publisher'):
allowed = True
if allowed:
event.acceptProposedAction()
else:
event.ignore()
def dropEvent(self, event):
idx = self.indexAt(event.pos())
m = self.model()
p = m.parent(idx)
if idx.isValid() and p.isValid():
item = m.data(p, Qt.UserRole)
if item.type == TagTreeItem.CATEGORY and \
item.category_key in \
('tags', 'series', 'authors', 'rating', 'publisher'):
child = m.data(idx, Qt.UserRole)
md = event.mimeData()
mime = 'application/calibre+from_library'
ids = list(map(int, str(md.data(mime)).split()))
self.handle_drop(item, child, ids)
event.accept()
def handle_drop(self, parent, child, ids):
print 'Dropped ids:', ids
@property
def match_all(self):
return self.tag_match and self.tag_match.currentIndex() > 0
@ -326,6 +371,8 @@ class TagTreeItem(object): # {{{
self.children.append(child)
def data(self, role):
if role == Qt.UserRole:
return self
if self.type == self.TAG:
return self.tag_data(role)
if self.type == self.CATEGORY:
@ -544,8 +591,14 @@ class TagsModel(QAbstractItemModel): # {{{
def headerData(self, *args):
return NONE
def flags(self, *args):
return Qt.ItemIsEnabled|Qt.ItemIsSelectable|Qt.ItemIsEditable
def flags(self, index, *args):
ans = Qt.ItemIsEnabled|Qt.ItemIsSelectable|Qt.ItemIsEditable
if index.isValid() and self.parent(index).isValid():
ans |= Qt.ItemIsDropEnabled
return ans
def supportedDropActions(self):
return Qt.CopyAction|Qt.MoveAction
def path_for_index(self, index):
ans = []

View File

@ -8,7 +8,6 @@ __docformat__ = 'restructuredtext en'
import os, traceback, cStringIO, re
from calibre import prints
from calibre.constants import DEBUG
from calibre.utils.config import Config, StringConfig, tweaks
from calibre.utils.formatter import TemplateFormatter

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

View File

@ -18,8 +18,9 @@ If this module is run, it will perform a series of unit tests.
import sys, string, operator
from calibre.utils.pyparsing import CaselessKeyword, Group, Forward, CharsNotIn, Suppress, \
OneOrMore, MatchFirst, CaselessLiteral, Optional, NoMatch, ParseException
from calibre.utils.pyparsing import CaselessKeyword, Group, Forward, \
CharsNotIn, Suppress, OneOrMore, MatchFirst, CaselessLiteral, \
Optional, NoMatch, ParseException, QuotedString
from calibre.constants import preferred_encoding
@ -127,18 +128,21 @@ class SearchQueryParser(object):
location |= l
location = Optional(location, default='all')
word_query = CharsNotIn(string.whitespace + '()')
quoted_query = Suppress('"')+CharsNotIn('"')+Suppress('"')
#quoted_query = Suppress('"')+CharsNotIn('"')+Suppress('"')
quoted_query = QuotedString('"', escChar='\\')
query = quoted_query | word_query
Token = Group(location + query).setResultsName('token')
if test:
print 'Testing Token parser:'
Token.validate()
failed = SearchQueryParser.run_tests(Token, 'token',
(
('tag:asd', ['tag', 'asd']),
('ddsä', ['all', 'ddsä']),
('"one two"', ['all', 'one two']),
('title:"one two"', ['title', 'one two']),
(u'ddsä', ['all', u'ddsä']),
('"one \\"two"', ['all', 'one "two']),
('title:"one \\"1.5\\" two"', ['title', 'one "1.5" two']),
('title:abc"def', ['title', 'abc"def']),
)
)
@ -167,7 +171,7 @@ class SearchQueryParser(object):
).setResultsName("or") | And)
if test:
Or.validate()
#Or.validate()
self._tests_failed = bool(failed)
self._parser = Or
@ -240,6 +244,8 @@ class SearchQueryParser(object):
'''
return set([])
# Testing {{{
class Tester(SearchQueryParser):
texts = {
@ -599,3 +605,6 @@ def main(args=sys.argv):
if __name__ == '__main__':
sys.exit(main())
# }}}