diff --git a/src/calibre/customize/builtins.py b/src/calibre/customize/builtins.py
index ec0f28273f..bdac284af2 100644
--- a/src/calibre/customize/builtins.py
+++ b/src/calibre/customize/builtins.py
@@ -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,
diff --git a/src/calibre/devices/hanvon/driver.py b/src/calibre/devices/hanvon/driver.py
index f9dec178c6..3798257c2d 100644
--- a/src/calibre/devices/hanvon/driver.py
+++ b/src/calibre/devices/hanvon/driver.py
@@ -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'
diff --git a/src/calibre/devices/nook/driver.py b/src/calibre/devices/nook/driver.py
index e09fb7eaf9..2fe8f667b0 100644
--- a/src/calibre/devices/nook/driver.py
+++ b/src/calibre/devices/nook/driver.py
@@ -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)
diff --git a/src/calibre/ebooks/epub/output.py b/src/calibre/ebooks/epub/output.py
index bea90eeba8..bb515f95a4 100644
--- a/src/calibre/ebooks/epub/output.py
+++ b/src/calibre/ebooks/epub/output.py
@@ -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
outside a
+ #
+ 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
diff --git a/src/calibre/ebooks/html/input.py b/src/calibre/ebooks/html/input.py
index 3d5f6c00ef..ce6c46c6cf 100644
--- a/src/calibre/ebooks/html/input.py
+++ b/src/calibre/ebooks/html/input.py
@@ -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:
diff --git a/src/calibre/ebooks/mobi/mobiml.py b/src/calibre/ebooks/mobi/mobiml.py
index 2275552c15..493767e233 100644
--- a/src/calibre/ebooks/mobi/mobiml.py
+++ b/src/calibre/ebooks/mobi/mobiml.py
@@ -442,9 +442,12 @@ 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'):
+ for attr in ('rowspan', 'colspan', 'width', 'border', 'scope'):
if attr in elem.attrib:
istate.attrib[attr] = elem.attrib[attr]
if tag == 'q':
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 72655afd12..f49c6db59a 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -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):
diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py
index f59473851f..3ca898d15a 100644
--- a/src/calibre/gui2/library/views.py
+++ b/src/calibre/gui2/library/views.py
@@ -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
- self.doubleClicked.connect(
- partial(parent.iactions['Edit Metadata'].edit_metadata, checked=False))
+ if use_edit_metadata_dialog:
+ self.doubleClicked.connect(
+ 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
diff --git a/src/calibre/gui2/preferences/coloring.py b/src/calibre/gui2/preferences/coloring.py
index b3c7873b45..3b581be701 100644
--- a/src/calibre/gui2/preferences/coloring.py
+++ b/src/calibre/gui2/preferences/coloring.py
@@ -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'),
@@ -61,10 +57,17 @@ class ConditionEditor(QWidget): # {{{
(_('is set'), 'is set'),
(_('is not set'), 'is not set'),
),
- }
+ }
- for x in ('float', 'rating', 'datetime'):
- self.action_map[x] = self.action_map['int']
+ for x in ('float', 'rating', 'datetime'):
+ 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 (
_('If the %s column %s value: %s') %
- (c, a, prepare_string_for_xml(v)))
+ (c, action_name, prepare_string_for_xml(v)))
# }}}
diff --git a/src/calibre/gui2/store/config/chooser/chooser_widget.py b/src/calibre/gui2/store/config/chooser/chooser_widget.py
index a9399028f8..5e7eca8c66 100644
--- a/src/calibre/gui2/store/config/chooser/chooser_widget.py
+++ b/src/calibre/gui2/store/config/chooser/chooser_widget.py
@@ -6,7 +6,7 @@ __license__ = 'GPL 3'
__copyright__ = '2011, John Schember '
__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')))
diff --git a/src/calibre/gui2/store/mobileread/store_dialog.py b/src/calibre/gui2/store/mobileread/store_dialog.py
index 8908c9bb68..749f96a614 100644
--- a/src/calibre/gui2/store/mobileread/store_dialog.py
+++ b/src/calibre/gui2/store/mobileread/store_dialog.py
@@ -7,7 +7,7 @@ __copyright__ = '2011, John Schember '
__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')))
diff --git a/src/calibre/gui2/store/search/search.py b/src/calibre/gui2/store/search/search.py
index f406b4923e..7db4d1bbaf 100644
--- a/src/calibre/gui2/store/search/search.py
+++ b/src/calibre/gui2/store/search/search.py
@@ -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)
diff --git a/src/calibre/gui2/tag_view.py b/src/calibre/gui2/tag_view.py
index da5029bab3..a6a852fbdd 100644
--- a/src/calibre/gui2/tag_view.py
+++ b/src/calibre/gui2/tag_view.py
@@ -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,7 +1642,13 @@ class TagsModel(QAbstractItemModel): # {{{
for node in self.category_nodes:
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
for tag_item in node.child_tags():
diff --git a/src/calibre/manual/template_lang.rst b/src/calibre/manual/template_lang.rst
index 079af59286..02a77432c9 100644
--- a/src/calibre/manual/template_lang.rst
+++ b/src/calibre/manual/template_lang.rst
@@ -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::
diff --git a/src/calibre/web/feeds/recipes/collection.py b/src/calibre/web/feeds/recipes/collection.py
index 1eb504d282..dbedef6dbe 100644
--- a/src/calibre/web/feeds/recipes/collection.py
+++ b/src/calibre/web/feeds/recipes/collection.py
@@ -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
|