KG updates

This commit is contained in:
GRiker 2011-06-14 05:14:38 -06:00
commit de4534e150
15 changed files with 105 additions and 77 deletions

View File

@ -594,7 +594,7 @@ from calibre.devices.iliad.driver import ILIAD
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800 from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX from calibre.devices.kindle.driver import KINDLE, KINDLE2, KINDLE_DX
from calibre.devices.nook.driver import NOOK, NOOK_COLOR, NOOK_TSR from calibre.devices.nook.driver import NOOK, NOOK_COLOR
from calibre.devices.prs505.driver import PRS505 from calibre.devices.prs505.driver import PRS505
from calibre.devices.user_defined.driver import USER_DEFINED from calibre.devices.user_defined.driver import USER_DEFINED
from calibre.devices.android.driver import ANDROID, S60 from calibre.devices.android.driver import ANDROID, S60
@ -603,10 +603,11 @@ from calibre.devices.eslick.driver import ESLICK, EBK52
from calibre.devices.nuut2.driver import NUUT2 from calibre.devices.nuut2.driver import NUUT2
from calibre.devices.iriver.driver import IRIVER_STORY from calibre.devices.iriver.driver import IRIVER_STORY
from calibre.devices.binatone.driver import README from calibre.devices.binatone.driver import README
from calibre.devices.hanvon.driver import N516, EB511, ALEX, AZBOOKA, THEBOOK from calibre.devices.hanvon.driver import (N516, EB511, ALEX, AZBOOKA, THEBOOK,
LIBREAIR)
from calibre.devices.edge.driver import EDGE from calibre.devices.edge.driver import EDGE
from calibre.devices.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, \ from calibre.devices.teclast.driver import (TECLAST_K3, NEWSMY, IPAPYRUS,
SOVOS, PICO, SUNSTECH_EB700, ARCHOS7O, STASH, WEXLER SOVOS, PICO, SUNSTECH_EB700, ARCHOS7O, STASH, WEXLER)
from calibre.devices.sne.driver import SNE from calibre.devices.sne.driver import SNE
from calibre.devices.misc import (PALMPRE, AVANT, SWEEX, PDNOVEL, from calibre.devices.misc import (PALMPRE, AVANT, SWEEX, PDNOVEL,
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, LUMIREAD, ALURATEK_COLOR, GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, LUMIREAD, ALURATEK_COLOR,
@ -693,7 +694,7 @@ plugins += [
KINDLE, KINDLE,
KINDLE2, KINDLE2,
KINDLE_DX, KINDLE_DX,
NOOK, NOOK_COLOR, NOOK_TSR, NOOK, NOOK_COLOR,
PRS505, PRS505,
ANDROID, ANDROID,
S60, S60,
@ -716,7 +717,7 @@ plugins += [
EB600, EB600,
README, README,
N516, N516,
THEBOOK, THEBOOK, LIBREAIR,
EB511, EB511,
ELONEX, ELONEX,
TECLAST_K3, TECLAST_K3,

View File

@ -52,6 +52,18 @@ class THEBOOK(N516):
EBOOK_DIR_MAIN = 'My books' EBOOK_DIR_MAIN = 'My books'
WINDOWS_CARD_A_MEM = '_FILE-STOR_GADGE' WINDOWS_CARD_A_MEM = '_FILE-STOR_GADGE'
class LIBREAIR(N516):
name = 'Libre Air Driver'
gui_name = 'Libre Air'
description = _('Communicate with the Libre Air reader.')
author = 'Kovid Goyal'
FORMATS = ['epub', 'mobi', 'prc', 'fb2', 'rtf', 'txt', 'pdf']
BCD = [0x399]
VENDOR_NAME = 'ALURATEK'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = '_FILE-STOR_GADGET'
EBOOK_DIR_MAIN = 'Books'
class ALEX(N516): class ALEX(N516):
name = 'Alex driver' name = 'Alex driver'

View File

@ -81,55 +81,27 @@ class NOOK(USBMS):
return [x.replace('#', '_') for x in components] return [x.replace('#', '_') for x in components]
class NOOK_COLOR(NOOK): class NOOK_COLOR(NOOK):
gui_name = _('Nook Color') description = _('Communicate with the Nook Color and TSR eBook readers.')
description = _('Communicate with the Nook Color eBook reader.')
PRODUCT_ID = [0x002] PRODUCT_ID = [0x002, 0x003]
BCD = [0x216] BCD = [0x216]
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_DISK'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_DISK'
EBOOK_DIR_MAIN = 'My Files' EBOOK_DIR_MAIN = 'My Files'
def upload_cover(self, path, filename, metadata, filepath):
pass
def get_carda_ebook_dir(self, for_upload=False):
if for_upload:
return self.EBOOK_DIR_MAIN
return ''
def create_upload_path(self, path, mdata, fname, create_dirs=True): def create_upload_path(self, path, mdata, fname, create_dirs=True):
filepath = NOOK.create_upload_path(self, path, mdata, fname, is_news = mdata.tags and _('News') in mdata.tags
create_dirs=False) subdir = 'Magazines' if is_news else 'Books'
edm = self.EBOOK_DIR_MAIN path = os.path.join(path, subdir)
subdir = 'Books' return USBMS.create_upload_path(self, path, mdata, fname,
if mdata.tags: create_dirs=create_dirs)
if _('News') in mdata.tags:
subdir = 'Magazines'
filepath = filepath.replace(os.sep+edm+os.sep,
os.sep+edm+os.sep+subdir+os.sep)
filedir = os.path.dirname(filepath)
if create_dirs and not os.path.exists(filedir):
os.makedirs(filedir)
return filepath
def upload_cover(self, path, filename, metadata, filepath):
pass
def get_carda_ebook_dir(self, for_upload=False):
if for_upload:
return 'My Files/Books'
return ''
class NOOK_TSR(NOOK):
gui_name = _('Nook Simple')
description = _('Communicate with the Nook TSR eBook reader.')
PRODUCT_ID = [0x003]
BCD = [0x216]
EBOOK_DIR_MAIN = 'My Files/Books'
WINDOWS_MAIN_MEM = WINDOWS_CARD_A_MEM = 'EBOOK_DISK'
def upload_cover(self, path, filename, metadata, filepath):
pass
def get_carda_ebook_dir(self, for_upload=False):
if for_upload:
return 'My Files/Books'
return ''

View File

@ -394,6 +394,13 @@ class EPUBOutput(OutputFormatPlugin):
for tag in XPath('//h:img[@src]')(root): for tag in XPath('//h:img[@src]')(root):
tag.set('src', tag.get('src', '').replace('&', '')) tag.set('src', tag.get('src', '').replace('&', ''))
# ADE whimpers in fright when it encounters a <td> outside a
# <table>
in_table = XPath('ancestor::h:table')
for tag in XPath('//h:td|//h:tr|//h:th')(root):
if not in_table(tag):
tag.tag = XHTML('div')
special_chars = re.compile(u'[\u200b\u00ad]') special_chars = re.compile(u'[\u200b\u00ad]')
for elem in root.iterdescendants(): for elem in root.iterdescendants():
if getattr(elem, 'text', False): if getattr(elem, 'text', False):
@ -413,7 +420,7 @@ class EPUBOutput(OutputFormatPlugin):
rule.style.removeProperty('margin-left') rule.style.removeProperty('margin-left')
# padding-left breaks rendering in webkit and gecko # padding-left breaks rendering in webkit and gecko
rule.style.removeProperty('padding-left') rule.style.removeProperty('padding-left')
# Change whitespace:pre to pre-line to accommodate readers that # Change whitespace:pre to pre-wrap to accommodate readers that
# cannot scroll horizontally # cannot scroll horizontally
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE): for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
style = rule.style style = rule.style

View File

@ -455,13 +455,16 @@ class HTMLInput(InputFormatPlugin):
bhref = os.path.basename(link) bhref = os.path.basename(link)
id, href = self.oeb.manifest.generate(id='added', id, href = self.oeb.manifest.generate(id='added',
href=bhref) href=bhref)
guessed = self.guess_type(href)[0]
media_type = guessed or self.BINARY_MIME
if 'text' in media_type:
self.log.warn('Ignoring link to text file %r'%link_)
return None
self.oeb.log.debug('Added', link) self.oeb.log.debug('Added', link)
self.oeb.container = self.DirContainer(os.path.dirname(link), self.oeb.container = self.DirContainer(os.path.dirname(link),
self.oeb.log, ignore_opf=True) self.oeb.log, ignore_opf=True)
# Load into memory # Load into memory
guessed = self.guess_type(href)[0]
media_type = guessed or self.BINARY_MIME
item = self.oeb.manifest.add(id, href, media_type) item = self.oeb.manifest.add(id, href, media_type)
item.html_input_href = bhref item.html_input_href = bhref
if guessed in self.OEB_STYLES: if guessed in self.OEB_STYLES:

View File

@ -442,9 +442,12 @@ class MobiMLizer(object):
if tag in TABLE_TAGS and self.ignore_tables: if tag in TABLE_TAGS and self.ignore_tables:
tag = 'span' if tag == 'td' else 'div' tag = 'span' if tag == 'td' else 'div'
# GR: Added 'width', 'border' and 'scope' if tag == 'table':
css = style.cssdict()
if 'border' in css or 'border-width' in css:
elem.set('border', '1')
if tag in TABLE_TAGS: if tag in TABLE_TAGS:
for attr in ('rowspan', 'colspan','width','border','scope'): for attr in ('rowspan', 'colspan', 'width', 'border', 'scope'):
if attr in elem.attrib: if attr in elem.attrib:
istate.attrib[attr] = elem.attrib[attr] istate.attrib[attr] = elem.attrib[attr]
if tag == 'q': if tag == 'q':

View File

@ -1110,6 +1110,8 @@ class DeviceBooksModel(BooksModel): # {{{
if self.last_search: if self.last_search:
self.searched.emit(True) self.searched.emit(True)
def research(self, reset=True):
self.search(self.last_search, reset)
def sort(self, col, order, reset=True): def sort(self, col, order, reset=True):
descending = order != Qt.AscendingOrder descending = order != Qt.AscendingOrder
@ -1171,6 +1173,8 @@ class DeviceBooksModel(BooksModel): # {{{
self.custom_columns = {} self.custom_columns = {}
self.db = db self.db = db
self.map = list(range(0, len(db))) self.map = list(range(0, len(db)))
self.research(reset=False)
self.resort()
def cover(self, row): def cover(self, row):
item = self.db[self.map[row]] item = self.db[self.map[row]]
@ -1319,8 +1323,6 @@ class DeviceBooksModel(BooksModel): # {{{
ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname, ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname,
'left')] 'left')]
return QVariant(ans) return QVariant(ans)
return NONE return NONE
def headerData(self, section, orientation, role): def headerData(self, section, orientation, role):

View File

@ -48,7 +48,7 @@ class BooksView(QTableView): # {{{
files_dropped = pyqtSignal(object) files_dropped = pyqtSignal(object)
add_column_signal = pyqtSignal() add_column_signal = pyqtSignal()
def __init__(self, parent, modelcls=BooksModel): def __init__(self, parent, modelcls=BooksModel, use_edit_metadata_dialog=True):
QTableView.__init__(self, parent) QTableView.__init__(self, parent)
self.setEditTriggers(self.EditKeyPressed) self.setEditTriggers(self.EditKeyPressed)
@ -60,8 +60,12 @@ class BooksView(QTableView): # {{{
elif tweaks['doubleclick_on_library_view'] == 'edit_metadata': elif tweaks['doubleclick_on_library_view'] == 'edit_metadata':
# Must not enable single-click to edit, or the field will remain # Must not enable single-click to edit, or the field will remain
# open in edit mode underneath the edit metadata dialog # open in edit mode underneath the edit metadata dialog
self.doubleClicked.connect( if use_edit_metadata_dialog:
partial(parent.iactions['Edit Metadata'].edit_metadata, checked=False)) self.doubleClicked.connect(
partial(parent.iactions['Edit Metadata'].edit_metadata,
checked=False))
else:
self.setEditTriggers(self.DoubleClicked|self.editTriggers())
self.drag_allowed = True self.drag_allowed = True
self.setDragEnabled(True) self.setDragEnabled(True)
@ -792,7 +796,8 @@ class BooksView(QTableView): # {{{
class DeviceBooksView(BooksView): # {{{ class DeviceBooksView(BooksView): # {{{
def __init__(self, parent): def __init__(self, parent):
BooksView.__init__(self, parent, DeviceBooksModel) BooksView.__init__(self, parent, DeviceBooksModel,
use_edit_metadata_dialog=False)
self.can_add_columns = False self.can_add_columns = False
self.columns_resized = False self.columns_resized = False
self.resize_on_select = False self.resize_on_select = False

View File

@ -22,11 +22,7 @@ from calibre.library.coloring import (Rule, conditionable_columns,
class ConditionEditor(QWidget): # {{{ class ConditionEditor(QWidget): # {{{
def __init__(self, fm, parent=None): ACTION_MAP = {
QWidget.__init__(self, parent)
self.fm = fm
self.action_map = {
'bool' : ( 'bool' : (
(_('is true'), 'is true',), (_('is true'), 'is true',),
(_('is false'), 'is false'), (_('is false'), 'is false'),
@ -61,10 +57,17 @@ class ConditionEditor(QWidget): # {{{
(_('is set'), 'is set'), (_('is set'), 'is set'),
(_('is not set'), 'is not set'), (_('is not set'), 'is not set'),
), ),
} }
for x in ('float', 'rating', 'datetime'): for x in ('float', 'rating', 'datetime'):
self.action_map[x] = self.action_map['int'] ACTION_MAP[x] = ACTION_MAP['int']
def __init__(self, fm, parent=None):
QWidget.__init__(self, parent)
self.fm = fm
self.action_map = self.ACTION_MAP
self.l = l = QGridLayout(self) self.l = l = QGridLayout(self)
self.setLayout(l) self.setLayout(l)
@ -446,9 +449,15 @@ class RulesModel(QAbstractListModel): # {{{
def condition_to_html(self, condition): def condition_to_html(self, condition):
c, a, v = condition c, a, v = condition
action_name = a
for actions in ConditionEditor.ACTION_MAP.itervalues():
for trans, ac in actions:
if ac == a:
action_name = trans
return ( return (
_('<li>If the <b>%s</b> column <b>%s</b> value: <b>%s</b>') % _('<li>If the <b>%s</b> column <b>%s</b> value: <b>%s</b>') %
(c, a, prepare_string_for_xml(v))) (c, action_name, prepare_string_for_xml(v)))
# }}} # }}}

View File

@ -6,7 +6,7 @@ __license__ = 'GPL 3'
__copyright__ = '2011, John Schember <john@nachtimwald.com>' __copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QWidget, QIcon, QDialog) from PyQt4.Qt import (QWidget, QIcon, QDialog, QComboBox)
from calibre.gui2.store.config.chooser.adv_search_builder import AdvSearchBuilderDialog from calibre.gui2.store.config.chooser.adv_search_builder import AdvSearchBuilderDialog
from calibre.gui2.store.config.chooser.chooser_widget_ui import Ui_Form from calibre.gui2.store.config.chooser.chooser_widget_ui import Ui_Form
@ -18,6 +18,8 @@ class StoreChooserWidget(QWidget, Ui_Form):
self.setupUi(self) self.setupUi(self)
self.query.initialize('store_config_chooser_query') self.query.initialize('store_config_chooser_query')
self.query.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
self.query.setMinimumContentsLength(25)
self.adv_search_builder.setIcon(QIcon(I('search.png'))) self.adv_search_builder.setIcon(QIcon(I('search.png')))

View File

@ -7,7 +7,7 @@ __copyright__ = '2011, John Schember <john@nachtimwald.com>'
__docformat__ = 'restructuredtext en' __docformat__ = 'restructuredtext en'
from PyQt4.Qt import (Qt, QDialog, QIcon) from PyQt4.Qt import (Qt, QDialog, QIcon, QComboBox)
from calibre.gui2.store.mobileread.adv_search_builder import AdvSearchBuilderDialog from calibre.gui2.store.mobileread.adv_search_builder import AdvSearchBuilderDialog
from calibre.gui2.store.mobileread.models import BooksModel from calibre.gui2.store.mobileread.models import BooksModel
@ -21,6 +21,8 @@ class MobileReadStoreDialog(QDialog, Ui_Dialog):
self.plugin = plugin self.plugin = plugin
self.search_query.initialize('store_mobileread_search') self.search_query.initialize('store_mobileread_search')
self.search_query.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
self.search_query.setMinimumContentsLength(25)
self.adv_search_button.setIcon(QIcon(I('search.png'))) self.adv_search_button.setIcon(QIcon(I('search.png')))

View File

@ -10,7 +10,8 @@ import re
from random import shuffle from random import shuffle
from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox, QLabel, from PyQt4.Qt import (Qt, QDialog, QDialogButtonBox, QTimer, QCheckBox, QLabel,
QVBoxLayout, QIcon, QWidget, QTabWidget, QGridLayout) QVBoxLayout, QIcon, QWidget, QTabWidget, QGridLayout,
QComboBox)
from calibre.gui2 import JSONConfig, info_dialog from calibre.gui2 import JSONConfig, info_dialog
from calibre.gui2.progress_indicator import ProgressIndicator from calibre.gui2.progress_indicator import ProgressIndicator
@ -57,6 +58,8 @@ class SearchDialog(QDialog, Ui_Dialog):
# Set the search query # Set the search query
self.search_edit.setText(query) self.search_edit.setText(query)
self.search_edit.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
self.search_edit.setMinimumContentsLength(25)
# Create and add the progress indicator # Create and add the progress indicator
self.pi = ProgressIndicator(self, 24) self.pi = ProgressIndicator(self, 24)

View File

@ -610,7 +610,7 @@ class TagTreeItem(object): # {{{
self.temporary = temporary self.temporary = temporary
self.tag = Tag(data, category=category_key, self.tag = Tag(data, category=category_key,
is_editable=category_key not in ['news', 'search', 'identifiers'], is_editable=category_key not in ['news', 'search', 'identifiers'],
is_searchable=category_key not in ['news', 'search']) is_searchable=category_key not in ['search'])
elif self.type == self.TAG: elif self.type == self.TAG:
self.icon_state_map[0] = QVariant(data.icon) self.icon_state_map[0] = QVariant(data.icon)
@ -1642,7 +1642,13 @@ class TagsModel(QAbstractItemModel): # {{{
for node in self.category_nodes: for node in self.category_nodes:
if node.tag.state: if node.tag.state:
ans.append('%s:%s'%(node.category_key, node_searches[node.tag.state])) if node.category_key == "news":
if node_searches[node.tag.state] == 'true':
ans.append('tags:=news')
else:
ans.append('( not tags:=news )')
else:
ans.append('%s:%s'%(node.category_key, node_searches[node.tag.state]))
key = node.category_key key = node.category_key
for tag_item in node.child_tags(): for tag_item in node.child_tags():

View File

@ -417,7 +417,7 @@ You might find the following tips useful.
* Create a custom composite column to test templates. Once you have the column, you can change its template simply by double-clicking on the column. Hide the column when you are not testing. * Create a custom composite column to test templates. Once you have the column, you can change its template simply by double-clicking on the column. Hide the column when you are not testing.
* Templates can use other templates by referencing a composite custom column. * Templates can use other templates by referencing a composite custom column.
* In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template ``{null}``. This template will always evaluate to an empty string. * In a plugboard, you can set a field to empty (or whatever is equivalent to empty) by using the special template ``{}``. This template will always evaluate to an empty string.
* The technique described above to show numbers even if they have a zero value works with the standard field series_index. * The technique described above to show numbers even if they have a zero value works with the standard field series_index.
.. toctree:: .. toctree::

View File

@ -101,6 +101,7 @@ def get_custom_recipe_collection(*args):
if recipe_class is not None: if recipe_class is not None:
rmap['custom:%s'%id_] = recipe_class rmap['custom:%s'%id_] = recipe_class
except: except:
print 'Failed to load recipe from: %r'%fname
import traceback import traceback
traceback.print_exc() traceback.print_exc()
continue continue