mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
KG updates
This commit is contained in:
commit
de4534e150
@ -594,7 +594,7 @@ from calibre.devices.iliad.driver import ILIAD
|
||||
from calibre.devices.irexdr.driver import IREXDR1000, IREXDR800
|
||||
from calibre.devices.jetbook.driver import JETBOOK, MIBUK, JETBOOK_MINI
|
||||
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.user_defined.driver import USER_DEFINED
|
||||
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.iriver.driver import IRIVER_STORY
|
||||
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.teclast.driver import TECLAST_K3, NEWSMY, IPAPYRUS, \
|
||||
SOVOS, PICO, SUNSTECH_EB700, ARCHOS7O, STASH, WEXLER
|
||||
from calibre.devices.teclast.driver import (TECLAST_K3, NEWSMY, IPAPYRUS,
|
||||
SOVOS, PICO, SUNSTECH_EB700, ARCHOS7O, STASH, WEXLER)
|
||||
from calibre.devices.sne.driver import SNE
|
||||
from calibre.devices.misc import (PALMPRE, AVANT, SWEEX, PDNOVEL,
|
||||
GEMEI, VELOCITYMICRO, PDNOVEL_KOBO, LUMIREAD, ALURATEK_COLOR,
|
||||
@ -693,7 +694,7 @@ plugins += [
|
||||
KINDLE,
|
||||
KINDLE2,
|
||||
KINDLE_DX,
|
||||
NOOK, NOOK_COLOR, NOOK_TSR,
|
||||
NOOK, NOOK_COLOR,
|
||||
PRS505,
|
||||
ANDROID,
|
||||
S60,
|
||||
@ -716,7 +717,7 @@ plugins += [
|
||||
EB600,
|
||||
README,
|
||||
N516,
|
||||
THEBOOK,
|
||||
THEBOOK, LIBREAIR,
|
||||
EB511,
|
||||
ELONEX,
|
||||
TECLAST_K3,
|
||||
|
@ -52,6 +52,18 @@ class THEBOOK(N516):
|
||||
EBOOK_DIR_MAIN = 'My books'
|
||||
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):
|
||||
|
||||
name = 'Alex driver'
|
||||
|
@ -81,55 +81,27 @@ class NOOK(USBMS):
|
||||
return [x.replace('#', '_') for x in components]
|
||||
|
||||
class NOOK_COLOR(NOOK):
|
||||
gui_name = _('Nook Color')
|
||||
description = _('Communicate with the Nook Color eBook reader.')
|
||||
description = _('Communicate with the Nook Color and TSR eBook readers.')
|
||||
|
||||
PRODUCT_ID = [0x002]
|
||||
PRODUCT_ID = [0x002, 0x003]
|
||||
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'
|
||||
|
||||
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):
|
||||
filepath = NOOK.create_upload_path(self, path, mdata, fname,
|
||||
create_dirs=False)
|
||||
edm = self.EBOOK_DIR_MAIN
|
||||
subdir = 'Books'
|
||||
if mdata.tags:
|
||||
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 ''
|
||||
is_news = mdata.tags and _('News') in mdata.tags
|
||||
subdir = 'Magazines' if is_news else 'Books'
|
||||
path = os.path.join(path, subdir)
|
||||
return USBMS.create_upload_path(self, path, mdata, fname,
|
||||
create_dirs=create_dirs)
|
||||
|
||||
|
||||
|
@ -394,6 +394,13 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
for tag in XPath('//h:img[@src]')(root):
|
||||
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]')
|
||||
for elem in root.iterdescendants():
|
||||
if getattr(elem, 'text', False):
|
||||
@ -413,7 +420,7 @@ class EPUBOutput(OutputFormatPlugin):
|
||||
rule.style.removeProperty('margin-left')
|
||||
# padding-left breaks rendering in webkit and gecko
|
||||
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
|
||||
for rule in stylesheet.data.cssRules.rulesOfType(CSSRule.STYLE_RULE):
|
||||
style = rule.style
|
||||
|
@ -455,13 +455,16 @@ class HTMLInput(InputFormatPlugin):
|
||||
bhref = os.path.basename(link)
|
||||
id, href = self.oeb.manifest.generate(id='added',
|
||||
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.container = self.DirContainer(os.path.dirname(link),
|
||||
self.oeb.log, ignore_opf=True)
|
||||
# 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.html_input_href = bhref
|
||||
if guessed in self.OEB_STYLES:
|
||||
|
@ -442,7 +442,10 @@ class MobiMLizer(object):
|
||||
if tag in TABLE_TAGS and self.ignore_tables:
|
||||
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:
|
||||
for attr in ('rowspan', 'colspan', 'width', 'border', 'scope'):
|
||||
if attr in elem.attrib:
|
||||
|
@ -1110,6 +1110,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
if self.last_search:
|
||||
self.searched.emit(True)
|
||||
|
||||
def research(self, reset=True):
|
||||
self.search(self.last_search, reset)
|
||||
|
||||
def sort(self, col, order, reset=True):
|
||||
descending = order != Qt.AscendingOrder
|
||||
@ -1171,6 +1173,8 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
self.custom_columns = {}
|
||||
self.db = db
|
||||
self.map = list(range(0, len(db)))
|
||||
self.research(reset=False)
|
||||
self.resort()
|
||||
|
||||
def cover(self, row):
|
||||
item = self.db[self.map[row]]
|
||||
@ -1319,8 +1323,6 @@ class DeviceBooksModel(BooksModel): # {{{
|
||||
ans = Qt.AlignVCenter | ALIGNMENT_MAP[self.alignment_map.get(cname,
|
||||
'left')]
|
||||
return QVariant(ans)
|
||||
|
||||
|
||||
return NONE
|
||||
|
||||
def headerData(self, section, orientation, role):
|
||||
|
@ -48,7 +48,7 @@ class BooksView(QTableView): # {{{
|
||||
files_dropped = pyqtSignal(object)
|
||||
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)
|
||||
|
||||
self.setEditTriggers(self.EditKeyPressed)
|
||||
@ -60,8 +60,12 @@ class BooksView(QTableView): # {{{
|
||||
elif tweaks['doubleclick_on_library_view'] == 'edit_metadata':
|
||||
# Must not enable single-click to edit, or the field will remain
|
||||
# open in edit mode underneath the edit metadata dialog
|
||||
if use_edit_metadata_dialog:
|
||||
self.doubleClicked.connect(
|
||||
partial(parent.iactions['Edit Metadata'].edit_metadata, checked=False))
|
||||
partial(parent.iactions['Edit Metadata'].edit_metadata,
|
||||
checked=False))
|
||||
else:
|
||||
self.setEditTriggers(self.DoubleClicked|self.editTriggers())
|
||||
|
||||
self.drag_allowed = True
|
||||
self.setDragEnabled(True)
|
||||
@ -792,7 +796,8 @@ class BooksView(QTableView): # {{{
|
||||
class DeviceBooksView(BooksView): # {{{
|
||||
|
||||
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.columns_resized = False
|
||||
self.resize_on_select = False
|
||||
|
@ -22,11 +22,7 @@ from calibre.library.coloring import (Rule, conditionable_columns,
|
||||
|
||||
class ConditionEditor(QWidget): # {{{
|
||||
|
||||
def __init__(self, fm, parent=None):
|
||||
QWidget.__init__(self, parent)
|
||||
self.fm = fm
|
||||
|
||||
self.action_map = {
|
||||
ACTION_MAP = {
|
||||
'bool' : (
|
||||
(_('is true'), 'is true',),
|
||||
(_('is false'), 'is false'),
|
||||
@ -64,7 +60,14 @@ class ConditionEditor(QWidget): # {{{
|
||||
}
|
||||
|
||||
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.setLayout(l)
|
||||
@ -446,9 +449,15 @@ class RulesModel(QAbstractListModel): # {{{
|
||||
|
||||
def condition_to_html(self, 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 (
|
||||
_('<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)))
|
||||
|
||||
# }}}
|
||||
|
||||
|
@ -6,7 +6,7 @@ __license__ = 'GPL 3'
|
||||
__copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||
__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.chooser_widget_ui import Ui_Form
|
||||
@ -18,6 +18,8 @@ class StoreChooserWidget(QWidget, Ui_Form):
|
||||
self.setupUi(self)
|
||||
|
||||
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')))
|
||||
|
||||
|
@ -7,7 +7,7 @@ __copyright__ = '2011, John Schember <john@nachtimwald.com>'
|
||||
__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.models import BooksModel
|
||||
@ -21,6 +21,8 @@ class MobileReadStoreDialog(QDialog, Ui_Dialog):
|
||||
|
||||
self.plugin = plugin
|
||||
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')))
|
||||
|
||||
|
@ -10,7 +10,8 @@ import re
|
||||
from random import shuffle
|
||||
|
||||
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.progress_indicator import ProgressIndicator
|
||||
@ -57,6 +58,8 @@ class SearchDialog(QDialog, Ui_Dialog):
|
||||
|
||||
# Set the search query
|
||||
self.search_edit.setText(query)
|
||||
self.search_edit.setSizeAdjustPolicy(QComboBox.AdjustToMinimumContentsLengthWithIcon)
|
||||
self.search_edit.setMinimumContentsLength(25)
|
||||
|
||||
# Create and add the progress indicator
|
||||
self.pi = ProgressIndicator(self, 24)
|
||||
|
@ -610,7 +610,7 @@ class TagTreeItem(object): # {{{
|
||||
self.temporary = temporary
|
||||
self.tag = Tag(data, category=category_key,
|
||||
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:
|
||||
self.icon_state_map[0] = QVariant(data.icon)
|
||||
@ -1642,6 +1642,12 @@ class TagsModel(QAbstractItemModel): # {{{
|
||||
|
||||
for node in self.category_nodes:
|
||||
if 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
|
||||
|
@ -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.
|
||||
* 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.
|
||||
|
||||
.. toctree::
|
||||
|
@ -101,6 +101,7 @@ def get_custom_recipe_collection(*args):
|
||||
if recipe_class is not None:
|
||||
rmap['custom:%s'%id_] = recipe_class
|
||||
except:
|
||||
print 'Failed to load recipe from: %r'%fname
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
continue
|
||||
|
Loading…
x
Reference in New Issue
Block a user