Sync to trunk.

This commit is contained in:
John Schember 2009-06-11 16:13:21 -04:00
commit 1c3a9c0b6e
14 changed files with 187 additions and 203 deletions

View File

@ -534,8 +534,6 @@ File ::8B8655B8-3823-AA02-1CDA-02F5AD4677C0 -name fetch-ebook-metadata.exe -pare
File ::8FC3D58A-F3FA-A2D1-711F-F4C3128620CE -name podofo.dll -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::88034147-A376-B759-D48C-5F17A47E8CC3 -name ebook-meta.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::6E6F2A97-6614-F2A6-1751-95C099B909FD -name pdfmanipulate.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::AC456A9A-6653-5852-D1F7-B04E2C1AA481 -name isbndb.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::94580010-4CE4-8EC0-3834-9A2BCCB28C70 -name isbndb.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::BA087530-7CF4-5460-C9AA-DE4A796C9AEB -name pdfmanipulate.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::6AF80BFC-A813-074D-897A-856744A91949 -name ebook-convert.exe.local -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40
File ::F8C59A30-9E90-0861-5604-95E7C69C3C80 -name ebook-convert.exe -parent 8E5D85A4-7608-47A1-CF7C-309060D5FF40

View File

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

View File

@ -403,8 +403,9 @@ def fix_ids(main, carda, cardb):
for child in db.root_element.childNodes:
if child.nodeType == child.ELEMENT_NODE and child.hasAttribute('id'):
id_map[child.getAttribute('id')] = str(cid)
child.setAttribute("sourceid",
'0' if getattr(child, 'tagName', '').endswith('playlist') else '1')
child.setAttribute('sourceid', '1')
#child.setAttribute("sourceid",
# '0' if getattr(child, 'tagName', '').endswith('playlist') else '1')
child.setAttribute('id', str(cid))
cid += 1

View File

@ -25,7 +25,7 @@ class DRMError(ValueError):
BOOK_EXTENSIONS = ['lrf', 'rar', 'zip', 'rtf', 'lit', 'txt', 'htm', 'xhtm',
'html', 'xhtml', 'pdf', 'pdb', 'prc', 'mobi', 'azw', 'doc',
'epub', 'fb2', 'djvu', 'lrx', 'cbr', 'cbz', 'oebzip',
'rb', 'imp', 'odt']
'rb', 'imp', 'odt', 'chm']
class HTMLRenderer(object):

View File

@ -63,14 +63,13 @@ def roman(num):
def fmt_sidx(i, fmt='%.2f', use_roman=False):
if i is None or i == '':
i = 1
i = float(i)
try:
i = float(i)
except TypeError:
return str(i)
if int(i) == float(i):
return roman(int(i)) if use_roman else '%d'%int(i)
try:
return fmt%i
except TypeError:
return fmt%float(i)
return fmt%i
class Resource(object):
'''

View File

@ -491,12 +491,12 @@ class MobiWriter(object):
else :
tbsType = 6
tbSequence = decint(tbsType, DECINT_BACKWARD)
tbSequence += decint(0x00, DECINT_BACKWARD)
tbSequence = decint(tbsType, DECINT_FORWARD)
tbSequence += decint(0x00, DECINT_FORWARD)
# Don't write a nodecount for opening type 2 record
if tbsType != 2 :
tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
tbSequence += decint(len(tbSequence) + 1, DECINT_BACKWARD)
tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
else :
# Determine tbsType for HTMLRecords > 0
@ -520,12 +520,12 @@ class MobiWriter(object):
shiftedNCXEntry |= tbsType
# Assemble the TBS
tbSequence = decint(shiftedNCXEntry, DECINT_BACKWARD)
tbSequence += decint(0x00, DECINT_BACKWARD)
tbSequence = decint(shiftedNCXEntry, DECINT_FORWARD)
tbSequence += decint(0x00, DECINT_FORWARD)
# Don't write a nodecount for terminating type 2 record
if tbsType != 2 :
tbSequence += chr(self._HTMLRecords[nrecords].currentSectionNodeCount)
tbSequence += decint(len(tbSequence) + 1, DECINT_BACKWARD)
tbSequence += decint(len(tbSequence) + 1, DECINT_FORWARD)
# print "record %d: tbsType %d" % (nrecords, tbsType)
self._tbSequence = tbSequence

View File

@ -810,6 +810,12 @@ class Manifest(object):
r = [x for x in data.iterdescendants(etree.Element) if 'microsoft-com' in x.tag]
for x in r:
x.tag = XHTML('span')
# Remove lang redefinition inserted by the amazing Microsoft Word!
body = xpath(data, '/h:html/h:body')[0]
for key in list(body.attrib.keys()):
if key == 'lang' or key.endswith('}lang'):
body.attrib.pop(key)
return data
def _parse_css(self, data):
@ -1327,7 +1333,9 @@ class TOC(object):
return len(list(self.iter())) - 1
def next_play_order(self):
return max([x.play_order for x in self.iter()])+1
entries = [x.play_order for x in self.iter()]
base = max(entries) if entries else 0
return base+1
def has_href(self, href):
for x in self.iter():

View File

@ -112,19 +112,19 @@ class PluginModel(QAbstractItemModel):
return QModelIndex()
if parent.isValid():
return self.createIndex(row, column, parent.row())
return self.createIndex(row, column, 1+parent.row())
else:
return self.createIndex(row, column, -1)
return self.createIndex(row, column, 0)
def parent(self, index):
if not index.isValid() or index.internalId() == -1:
if not index.isValid() or index.internalId() == 0:
return QModelIndex()
return self.createIndex(index.internalId(), 0, -1)
return self.createIndex(index.internalId()-1, 0, 0)
def rowCount(self, parent):
if not parent.isValid():
return len(self.categories)
if parent.internalId() == -1:
if parent.internalId() == 0:
category = self.categories[parent.row()]
return len(self._data[category])
return 0

View File

@ -182,12 +182,12 @@ class RecipeModel(QAbstractItemModel, SearchQueryParser):
self.reset()
def index(self, row, column, parent):
return self.createIndex(row, column, parent.row() if parent.isValid() else -1)
return self.createIndex(row, column, parent.row()+1 if parent.isValid() else 0)
def parent(self, index):
if index.internalId() == -1:
if index.internalId() == 0:
return QModelIndex()
return self.createIndex(index.internalId(), 0, -1)
return self.createIndex(index.internalId()-1, 0, 0)
def columnCount(self, parent):
if not parent.isValid() or not parent.parent().isValid():

View File

@ -726,7 +726,7 @@ class BooksView(TableView):
paths = self.paths_from_event(event)
event.setDropAction(Qt.CopyAction)
event.accept()
self.emit(SIGNAL('files_dropped(PyQt_PyObject)'), paths, Qt.QueuedConnection)
self.emit(SIGNAL('files_dropped(PyQt_PyObject)'), paths)
def set_database(self, db):

View File

@ -696,7 +696,7 @@
<customwidget>
<class>TagsView</class>
<extends>QTreeView</extends>
<header>tags.h</header>
<header>calibre/gui2/tag_view.h</header>
</customwidget>
</customwidgets>
<resources>

View File

@ -0,0 +1,151 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
Browsing book collection by tags.
'''
from PyQt4.Qt import QStandardItemModel, Qt, QTreeView, QStandardItem, \
QFont, SIGNAL, QSize, QIcon, QPoint, QPixmap
from calibre.gui2 import config
class TagsView(QTreeView):
def __init__(self, *args):
QTreeView.__init__(self, *args)
self.setUniformRowHeights(True)
self.setCursor(Qt.PointingHandCursor)
self.setIconSize(QSize(30, 30))
def set_database(self, db, match_all, popularity):
self._model = TagsModel(db)
self.popularity = popularity
self.match_all = match_all
self.setModel(self._model)
self.connect(self, SIGNAL('clicked(QModelIndex)'), self.toggle)
self.popularity.setChecked(config['sort_by_popularity'])
self.connect(self.popularity, SIGNAL('stateChanged(int)'), self.sort_changed)
def sort_changed(self, state):
config.set('sort_by_popularity', state == Qt.Checked)
self.model().refresh()
def toggle(self, index):
if self._model.toggle(index):
self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
self._model.tokens(), self.match_all.isChecked())
def recount(self, *args):
ci = self.currentIndex()
if not ci.isValid():
ci = self.indexAt(QPoint(10, 10))
self.model().refresh()
if ci.isValid():
self.scrollTo(ci, QTreeView.PositionAtTop)
class CategoryItem(QStandardItem):
def __init__(self, category, display_text, tags, icon, font, icon_map):
self.category = category
self.tags = tags
QStandardItem.__init__(self, icon, display_text)
self.setFont(font)
self.setSelectable(False)
self.setSizeHint(QSize(100, 40))
self.setEditable(False)
for tag in tags:
self.appendRow(TagItem(tag, icon_map))
class TagItem(QStandardItem):
def __init__(self, tag, icon_map):
self.icon_map = icon_map
self.tag = tag
QStandardItem.__init__(self, tag.as_string())
self.set_icon()
self.setEditable(False)
self.setSelectable(False)
def toggle(self):
self.tag.state = (self.tag.state + 1)%3
self.set_icon()
def set_icon(self):
self.setIcon(self.icon_map[self.tag.state])
class TagsModel(QStandardItemModel):
categories = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('Tags')]
row_map = ['author', 'series', 'format', 'publisher', 'news', 'tag']
def __init__(self, db):
self.cmap = tuple(map(QIcon, [':/images/user_profile.svg',
':/images/series.svg', ':/images/book.svg', ':/images/publisher.png',
':/images/news.svg', ':/images/tags.svg']))
p = QPixmap(30, 30)
p.fill(Qt.transparent)
self.icon_map = [QIcon(p), QIcon(':/images/plus.svg'),
QIcon(':/images/minus.svg')]
QStandardItemModel.__init__(self)
self.db = db
self.ignore_next_search = False
self._data = {}
self.bold_font = QFont()
self.bold_font.setBold(True)
self.refresh()
self.db.add_listener(self.database_changed)
def database_changed(self, event, ids):
self.refresh()
def refresh(self):
old_data = self._data
self._data = self.db.get_categories(config['sort_by_popularity'])
for key in old_data.keys():
for tag in old_data[key]:
try:
index = self._data[key].index(tag)
if index > -1:
self._data[key][index].state = tag.state
except:
continue
self.clear()
root = self.invisibleRootItem()
for r, category in enumerate(self.row_map):
tags = self._data.get(category, [])
root.appendRow(CategoryItem(category, self.categories[r],
self._data[category], self.cmap[r], self.bold_font, self.icon_map))
#self.reset()
def reinit(self, *args, **kwargs):
if not self.ignore_next_search:
for category in self._data.values():
for tag in category:
tag.state = 0
self.reset()
self.ignore_next_search = False
def toggle(self, index):
if index.parent().isValid():
category = self.row_map[index.parent().row()]
tag = self._data[category][index.row()]
self.invisibleRootItem().child(index.parent().row()).child(index.row()).toggle()
self.ignore_next_search = True
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index)
return True
return False
def tokens(self):
ans = []
for key in self.row_map:
for tag in self._data[key]:
category = key if key != 'news' else 'tag'
if tag.state > 0:
prefix = ' not ' if tag.state == 2 else ''
ans.append('%s%s:"%s"'%(prefix, category, tag))
return ans

View File

@ -1,173 +0,0 @@
#!/usr/bin/env python
__license__ = 'GPL v3'
__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
__docformat__ = 'restructuredtext en'
'''
Browsing book collection by tags.
'''
from PyQt4.Qt import QAbstractItemModel, Qt, QVariant, QTreeView, QModelIndex, \
QFont, SIGNAL, QSize, QColor, QIcon, QPoint
from calibre.gui2 import config
NONE = QVariant()
class TagsView(QTreeView):
def __init__(self, *args):
QTreeView.__init__(self, *args)
self.setUniformRowHeights(True)
self.setCursor(Qt.PointingHandCursor)
self.setIconSize(QSize(30, 30))
def set_database(self, db, match_all, popularity):
self._model = TagsModel(db)
self.popularity = popularity
self.match_all = match_all
self.setModel(self._model)
self.connect(self, SIGNAL('clicked(QModelIndex)'), self.toggle)
self.popularity.setChecked(config['sort_by_popularity'])
self.connect(self.popularity, SIGNAL('stateChanged(int)'), self.sort_changed)
def sort_changed(self, state):
config.set('sort_by_popularity', state == Qt.Checked)
self.model().refresh()
def toggle(self, index):
if self._model.toggle(index):
self.emit(SIGNAL('tags_marked(PyQt_PyObject, PyQt_PyObject)'),
self._model.tokens(), self.match_all.isChecked())
def recount(self, *args):
ci = self.currentIndex()
if not ci.isValid():
ci = self.indexAt(QPoint(10, 10))
self.model().refresh()
if ci.isValid():
self.scrollTo(ci, QTreeView.PositionAtTop)
class TagsModel(QAbstractItemModel):
categories = [_('Authors'), _('Series'), _('Formats'), _('Publishers'), _('News'), _('Tags')]
row_map = {0: 'author', 1:'series', 2:'format', 3:'publisher', 4:'news', 5:'tag'}
def __init__(self, db):
QAbstractItemModel.__init__(self)
self.db = db
self.ignore_next_search = False
self._data = {}
self.refresh()
self.bold_font = QFont()
self.bold_font.setBold(True)
self.bold_font = QVariant(self.bold_font)
self.status_map = [QColor(200,200,200, 0), QIcon(':/images/plus.svg'), QIcon(':/images/minus.svg')]
self.status_map = list(map(QVariant, self.status_map))
self.cmap = [QIcon(':/images/user_profile.svg'), QIcon(':/images/series.svg'), QIcon(':/images/book.svg'), QIcon(':/images/publisher.png'), QIcon(':/images/news.svg'), QIcon(':/images/tags.svg')]
self.cmap = list(map(QVariant, self.cmap))
self.db.add_listener(self.database_changed)
def database_changed(self, event, ids):
self.refresh()
def refresh(self):
old_data = self._data
self._data = self.db.get_categories(config['sort_by_popularity'])
for key in old_data.keys():
for tag in old_data[key]:
try:
index = self._data[key].index(tag)
if index > -1:
self._data[key][index].state = tag.state
except:
continue
self.reset()
def reinit(self, *args, **kwargs):
if not self.ignore_next_search:
for category in self._data.values():
for tag in category:
tag.state = 0
self.reset()
self.ignore_next_search = False
def toggle(self, index):
if index.parent().isValid():
category = self.row_map[index.parent().row()]
tag = self._data[category][index.row()]
tag.state = (tag.state + 1)%3
self.ignore_next_search = True
self.emit(SIGNAL('dataChanged(QModelIndex,QModelIndex)'), index, index)
return True
return False
def tokens(self):
ans = []
for key in self.row_map.values():
for tag in self._data[key]:
category = key if key != 'news' else 'tag'
if tag.state > 0:
prefix = ' not ' if tag.state == 2 else ''
ans.append('%s%s:"%s"'%(prefix, category, tag))
return ans
def index(self, row, col, parent=QModelIndex()):
if parent.isValid():
if parent.parent().isValid(): # parent is a tag
return QModelIndex()
try:
category = self.row_map[parent.row()]
except KeyError:
return QModelIndex()
if col == 0 and row < len(self._data[category]):
return self.createIndex(row, col, parent.row())
return QModelIndex()
if col == 0 and row < len(self.categories):
return self.createIndex(row, col, -1)
return QModelIndex()
def parent(self, index):
if not index.isValid() or index.internalId() < 0:
return QModelIndex()
return self.createIndex(index.internalId(), 0, -1)
def rowCount(self, parent):
if not parent or not parent.isValid():
return len(self.categories)
if not parent.parent().isValid():
return len(self._data[self.row_map[parent.row()]])
return 0
def columnCount(self, parent):
return 1
def flags(self, index):
if not index.isValid():
return Qt.NoItemFlags
return Qt.ItemIsEnabled
def category_data(self, index, role):
if role == Qt.DisplayRole:
row = index.row()
return QVariant(self.categories[row])
if role == Qt.FontRole:
return self.bold_font
if role == Qt.SizeHintRole:
return QVariant(QSize(100, 40))
if role == Qt.DecorationRole:
return self.cmap[index.row()]
return NONE
def tag_data(self, index, role):
category = self.row_map[index.parent().row()]
if role == Qt.DisplayRole:
return QVariant(self._data[category][index.row()].as_string())
if role == Qt.DecorationRole:
return self.status_map[self._data[category][index.row()].state]
return NONE
def data(self, index, role):
if not index.parent().isValid():
return self.category_data(index, role)
if not index.parent().parent().isValid():
return self.tag_data(index, role)
return NONE

View File

@ -334,7 +334,7 @@ def post_install():
os.chdir(config_dir)
for f in os.listdir('.'):
if os.stat(f).st_uid == 0:
os.rmdir(f)
os.rmdir(f) if os.path.isdir(f) else os.unlink(f)
if os.stat(config_dir).st_uid == 0:
os.rmdir(config_dir)