From b06596fffbeb4102ed573fee1366e345e64db6d9 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Fri, 24 Dec 2010 22:44:04 +0000
Subject: [PATCH 1/7] First try at using the new comments editor for custom
comments columns
---
src/calibre/gui2/book_details.py | 4 +-
src/calibre/gui2/custom_column_widgets.py | 9 +--
src/calibre/gui2/dialogs/comments_dialog.py | 5 +-
src/calibre/gui2/dialogs/comments_dialog.ui | 37 ++++++-----
src/calibre/gui2/dialogs/template_dialog.py | 25 +++++++
src/calibre/gui2/dialogs/template_dialog.ui | 73 +++++++++++++++++++++
src/calibre/gui2/library/delegates.py | 24 +++++--
src/calibre/gui2/library/models.py | 2 +
8 files changed, 153 insertions(+), 26 deletions(-)
create mode 100644 src/calibre/gui2/dialogs/template_dialog.py
create mode 100644 src/calibre/gui2/dialogs/template_dialog.ui
diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py
index a3caa82e4b..50ce72686a 100644
--- a/src/calibre/gui2/book_details.py
+++ b/src/calibre/gui2/book_details.py
@@ -44,7 +44,9 @@ def render_rows(data):
key = key.decode(preferred_encoding, 'replace')
if isinstance(txt, str):
txt = txt.decode(preferred_encoding, 'replace')
- if '' not in txt:
+ if key.endswith(u':html'):
+ key = key[:-5]
+ elif '' not in txt:
txt = prepare_string_for_xml(txt)
if 'id' in data:
if key == _('Path'):
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index ca9243e51e..bf66ea7235 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -15,6 +15,7 @@ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \
from calibre.utils.date import qt_to_dt, now
from calibre.gui2.widgets import TagsLineEdit, EnComboBox
+from calibre.gui2.comments_editor import Editor as CommentsEditor
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.utils.config import tweaks
from calibre.utils.icu import sort_key
@@ -186,9 +187,9 @@ class Comments(Base):
self._box = QGroupBox(parent)
self._box.setTitle('&'+self.col_metadata['name'])
self._layout = QVBoxLayout()
- self._tb = QPlainTextEdit(self._box)
+ self._tb = CommentsEditor(self._box)
self._tb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
- self._tb.setTabChangesFocus(True)
+ #self._tb.setTabChangesFocus(True)
self._layout.addWidget(self._tb)
self._box.setLayout(self._layout)
self.widgets = [self._box]
@@ -196,10 +197,10 @@ class Comments(Base):
def setter(self, val):
if val is None:
val = ''
- self._tb.setPlainText(val)
+ self._tb.html = val
def getter(self):
- val = unicode(self._tb.toPlainText()).strip()
+ val = unicode(self._tb.html).strip()
if not val:
val = None
return val
diff --git a/src/calibre/gui2/dialogs/comments_dialog.py b/src/calibre/gui2/dialogs/comments_dialog.py
index 5d53448b94..51b29fa989 100644
--- a/src/calibre/gui2/dialogs/comments_dialog.py
+++ b/src/calibre/gui2/dialogs/comments_dialog.py
@@ -5,6 +5,7 @@ __license__ = 'GPL v3'
from PyQt4.Qt import Qt, QDialog, QDialogButtonBox
from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog
+from calibre.library.comments import comments_to_html
class CommentsDialog(QDialog, Ui_CommentsDialog):
@@ -18,8 +19,8 @@ class CommentsDialog(QDialog, Ui_CommentsDialog):
self.setWindowIcon(icon)
if text is not None:
- self.textbox.setPlainText(text)
- self.textbox.setTabChangesFocus(True)
+ self.textbox.html = comments_to_html(text)
+ # self.textbox.setTabChangesFocus(True)
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
diff --git a/src/calibre/gui2/dialogs/comments_dialog.ui b/src/calibre/gui2/dialogs/comments_dialog.ui
index dccfa48652..9c6e6cb861 100644
--- a/src/calibre/gui2/dialogs/comments_dialog.ui
+++ b/src/calibre/gui2/dialogs/comments_dialog.ui
@@ -19,22 +19,29 @@
Edit Comments
-
- -
-
-
- -
-
-
- Qt::Horizontal
-
-
- QDialogButtonBox::Cancel|QDialogButtonBox::Ok
-
-
-
-
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+ Editor
+ QWidget
+ calibre/gui2/comments_editor.h
+
+
diff --git a/src/calibre/gui2/dialogs/template_dialog.py b/src/calibre/gui2/dialogs/template_dialog.py
new file mode 100644
index 0000000000..aaa4e2bb9a
--- /dev/null
+++ b/src/calibre/gui2/dialogs/template_dialog.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python
+__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
+__docformat__ = 'restructuredtext en'
+__license__ = 'GPL v3'
+
+from PyQt4.Qt import Qt, QDialog, QDialogButtonBox
+from calibre.gui2.dialogs.template_dialog_ui import Ui_TemplateDialog
+
+class TemplateDialog(QDialog, Ui_TemplateDialog):
+
+ def __init__(self, parent, text):
+ QDialog.__init__(self, parent)
+ Ui_TemplateDialog.__init__(self)
+ self.setupUi(self)
+ # Remove help icon on title bar
+ icon = self.windowIcon()
+ self.setWindowFlags(self.windowFlags()&(~Qt.WindowContextHelpButtonHint))
+ self.setWindowIcon(icon)
+
+ if text is not None:
+ self.textbox.setPlainText(text)
+ self.textbox.setTabChangesFocus(True)
+ self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
+ self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))
+
diff --git a/src/calibre/gui2/dialogs/template_dialog.ui b/src/calibre/gui2/dialogs/template_dialog.ui
new file mode 100644
index 0000000000..3eacace2c5
--- /dev/null
+++ b/src/calibre/gui2/dialogs/template_dialog.ui
@@ -0,0 +1,73 @@
+
+
+ TemplateDialog
+
+
+
+ 0
+ 0
+ 336
+ 235
+
+
+
+
+ 0
+ 0
+
+
+
+ Edit Comments
+
+
+ -
+
+
+ -
+
+
+ Qt::Horizontal
+
+
+ QDialogButtonBox::Cancel|QDialogButtonBox::Ok
+
+
+
+
+
+
+
+
+ buttonBox
+ accepted()
+ TemplateDialog
+ accept()
+
+
+ 229
+ 211
+
+
+ 157
+ 234
+
+
+
+
+ buttonBox
+ rejected()
+ TemplateDialog
+ reject()
+
+
+ 297
+ 217
+
+
+ 286
+ 234
+
+
+
+
+
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py
index fe7e7d55ba..2ae6cf2936 100644
--- a/src/calibre/gui2/library/delegates.py
+++ b/src/calibre/gui2/library/delegates.py
@@ -13,7 +13,7 @@ from PyQt4.Qt import QColor, Qt, QModelIndex, QSize, \
QPen, QStyle, QPainter, QStyleOptionViewItemV4, \
QIcon, QDoubleSpinBox, QVariant, QSpinBox, \
QStyledItemDelegate, QCompleter, \
- QComboBox
+ QComboBox, QTextDocument
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.gui2.widgets import EnLineEdit, TagsLineEdit
@@ -22,6 +22,8 @@ from calibre.utils.config import tweaks
from calibre.utils.formatter import validation_formatter
from calibre.utils.icu import sort_key
from calibre.gui2.dialogs.comments_dialog import CommentsDialog
+from calibre.gui2.dialogs.template_dialog import TemplateDialog
+
class RatingDelegate(QStyledItemDelegate): # {{{
COLOR = QColor("blue")
@@ -294,6 +296,20 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
Delegate for comments data.
'''
+ def paint(self, painter, option, index):
+ document = QTextDocument()
+ value = index.data(Qt.DisplayRole)
+# if value.isValid() and not value.isNull():
+# QString text("This is highlighted.");
+ text = value.toString()
+ document.setHtml(text);
+ painter.save()
+ painter.setClipRect(option.rect)
+ painter.translate(option.rect.topLeft());
+ document.drawContents(painter);
+ painter.restore()
+# painter.translate(-option.rect.topLeft());
+
def createEditor(self, parent, option, index):
m = index.model()
col = m.column_map[index.column()]
@@ -301,11 +317,11 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
editor = CommentsDialog(parent, text)
d = editor.exec_()
if d:
- m.setData(index, QVariant(editor.textbox.toPlainText()), Qt.EditRole)
+ m.setData(index, QVariant(editor.textbox.html), Qt.EditRole)
return None
def setModelData(self, editor, model, index):
- model.setData(index, QVariant(editor.textbox.toPlainText()), Qt.EditRole)
+ model.setData(index, QVariant(editor.textbox.html), Qt.EditRole)
# }}}
class CcBoolDelegate(QStyledItemDelegate): # {{{
@@ -351,7 +367,7 @@ class CcTemplateDelegate(QStyledItemDelegate): # {{{
def createEditor(self, parent, option, index):
m = index.model()
text = m.custom_columns[m.column_map[index.column()]]['display']['composite_template']
- editor = CommentsDialog(parent, text)
+ editor = TemplateDialog(parent, text)
editor.setWindowTitle(_("Edit template"))
editor.textbox.setTabChangesFocus(False)
editor.textbox.setTabStopWidth(20)
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 9da9a2f538..920753a77d 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -334,6 +334,8 @@ class BooksModel(QAbstractTableModel): # {{{
if key not in cf_to_display:
continue
name, val = mi.format_field(key)
+ if mi.metadata_for_field(key)['datatype'] == 'comments':
+ name += ':html'
if val:
data[name] = val
return data
From 1fa911c8c962642b611145292776e2c3af37becb Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Fri, 24 Dec 2010 22:46:42 +0000
Subject: [PATCH 2/7] Resize the comments delegate dialog box
---
src/calibre/gui2/dialogs/comments_dialog.ui | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/dialogs/comments_dialog.ui b/src/calibre/gui2/dialogs/comments_dialog.ui
index 9c6e6cb861..76b23e233d 100644
--- a/src/calibre/gui2/dialogs/comments_dialog.ui
+++ b/src/calibre/gui2/dialogs/comments_dialog.ui
@@ -6,8 +6,8 @@
0
0
- 336
- 235
+ 400
+ 400
From c0c4df77cab4ddddb009743546978e08e8490d69 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 25 Dec 2010 11:42:38 +0000
Subject: [PATCH 3/7] Improve search/replace handling of is_multiple fields
---
src/calibre/gui2/dialogs/metadata_bulk.py | 33 ++++++--
src/calibre/gui2/dialogs/metadata_bulk.ui | 99 +++++++++++++++++------
2 files changed, 101 insertions(+), 31 deletions(-)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.py b/src/calibre/gui2/dialogs/metadata_bulk.py
index bde5cae128..9dbc3dee5e 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.py
+++ b/src/calibre/gui2/dialogs/metadata_bulk.py
@@ -417,6 +417,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
self.multiple_separator.setFixedWidth(30)
self.multiple_separator.setText(' ::: ')
self.multiple_separator.textChanged.connect(self.s_r_separator_changed)
+ self.results_count.valueChanged[int].connect(self.s_r_display_bounds_changed)
+ self.starting_from.valueChanged[int].connect(self.s_r_display_bounds_changed)
def s_r_get_field(self, mi, field):
if field:
@@ -439,6 +441,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
val = []
return val
+ def s_r_display_bounds_changed(self, i):
+ self.s_r_search_field_changed(self.search_field.currentIndex())
+
def s_r_template_changed(self):
self.s_r_search_field_changed(self.search_field.currentIndex())
@@ -454,6 +459,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
mi = self.db.get_metadata(self.ids[i], index_is_id=True)
src = unicode(self.search_field.currentText())
t = self.s_r_get_field(mi, src)
+ if len(t) > 1:
+ t = t[self.starting_from.value()-1:
+ self.starting_from.value()-1 + self.results_count.value()]
w.setText(unicode(self.multiple_separator.text()).join(t))
if self.search_mode.currentIndex() == 0:
@@ -466,12 +474,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
txt = unicode(txt)
if not txt:
txt = unicode(self.search_field.currentText())
- self.comma_separated.setEnabled(True)
if txt and txt in self.writable_fields:
self.destination_field_fm = self.db.metadata_for_field(txt)
- if self.destination_field_fm['is_multiple']:
- self.comma_separated.setEnabled(False)
- self.comma_separated.setChecked(True)
self.s_r_paint_results(None)
def s_r_search_mode_changed(self, val):
@@ -542,6 +546,22 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
dest = src
dest_mode = self.replace_mode.currentIndex()
+ if self.destination_field_fm['is_multiple']:
+ if self.comma_separated.isChecked():
+ if dest == 'authors':
+ splitter = ' & '
+ else:
+ splitter = ','
+
+ res = []
+ for v in val:
+ for x in v.split(splitter):
+ if x.strip():
+ res.append(x.strip())
+ val = res
+ else:
+ val = [v.replace(',', '') for v in val]
+
if dest_mode != 0:
dest_val = mi.get(dest, '')
if dest_val is None:
@@ -602,8 +622,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
try:
result = self.s_r_do_regexp(mi)
t = self.s_r_do_destination(mi, result)
- if len(result) > 1 and self.destination_field_fm is not None and \
- self.destination_field_fm['is_multiple']:
+ if len(t) > 1 and self.destination_field_fm['is_multiple']:
+ t = t[self.starting_from.value()-1:
+ self.starting_from.value()-1 + self.results_count.value()]
t = unicode(self.multiple_separator.text()).join(t)
else:
t = self.s_r_replace_mode_separator().join(t)
diff --git a/src/calibre/gui2/dialogs/metadata_bulk.ui b/src/calibre/gui2/dialogs/metadata_bulk.ui
index d945909f96..41858b099b 100644
--- a/src/calibre/gui2/dialogs/metadata_bulk.ui
+++ b/src/calibre/gui2/dialogs/metadata_bulk.ui
@@ -658,11 +658,12 @@ If blank, the source field is used if the field is modifiable
-
- Specifies whether a comma should be put between values when copying from a
-multiple-valued field to a single-valued field
+ Specifies whether result items should be split into multiple values or
+left as single values. This option has the most effect when the source field is
+not multiple and the destination field is multiple
- &Use comma
+ Split &result
true
@@ -684,28 +685,8 @@ multiple-valued field to a single-valued field
- -
-
-
- Test text
-
-
- test_text
-
-
-
- -
+
-
-
-
-
-
- Test result
-
-
- test_result
-
-
-
-
@@ -719,10 +700,62 @@ multiple-valued field to a single-valued field
+ -
+
+
+ For multiple-valued fields, sho&w
+
+
+ results_count
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+ 999
+
+
+ 999
+
+
+
+ -
+
+
+ values starting a&t
+
+
+ starting_from
+
+
+
+ -
+
+
+ true
+
+
+ 1
+
+
+ 999
+
+
+ 1
+
+
+
-
- Multi&ple separator:
+ with values separated b&y
multiple_separator
@@ -756,6 +789,20 @@ multiple-valued field to a single-valued field
+
-
+
+
+ Test text
+
+
+
+ -
+
+
+ Test result
+
+
+
-
@@ -857,6 +904,8 @@ multiple-valued field to a single-valued field
destination_field
replace_mode
comma_separated
+ results_count
+ starting_from
multiple_separator
test_text
test_result
From 092982e057272e57819310dbb53cf2bb2ccce286 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 25 Dec 2010 12:40:55 +0000
Subject: [PATCH 4/7] More custom comments as html work
---
src/calibre/gui2/dialogs/book_info.py | 5 ++++-
src/calibre/gui2/library/delegates.py | 18 +++++++++---------
src/calibre/library/server/opds.py | 2 ++
3 files changed, 15 insertions(+), 10 deletions(-)
diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py
index 016f132c57..6cae27d926 100644
--- a/src/calibre/gui2/dialogs/book_info.py
+++ b/src/calibre/gui2/dialogs/book_info.py
@@ -12,6 +12,7 @@ from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
from calibre.gui2 import dynamic, open_local_file
from calibre import fit_image
from calibre.library.comments import comments_to_html
+from calibre.utils.icu import sort_key
class BookInfo(QDialog, Ui_BookInfo):
@@ -130,9 +131,11 @@ class BookInfo(QDialog, Ui_BookInfo):
for f in formats:
f = f.strip()
info[_('Formats')] += '%s, '%(f,f)
- for key in info.keys():
+ for key in sorted(info.keys(), key=sort_key):
if key == 'id': continue
txt = info[key]
+ if key.endswith(':html'):
+ key = key[:-5]
if key != _('Path'):
txt = u'
\n'.join(textwrap.wrap(txt, 120))
rows += u'%s: | %s |
'%(key, txt)
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py
index 2ae6cf2936..957828f93c 100644
--- a/src/calibre/gui2/library/delegates.py
+++ b/src/calibre/gui2/library/delegates.py
@@ -296,19 +296,19 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
Delegate for comments data.
'''
+ def __init__(self, parent):
+ QStyledItemDelegate.__init__(self, parent)
+ self.document = QTextDocument()
+
def paint(self, painter, option, index):
- document = QTextDocument()
- value = index.data(Qt.DisplayRole)
-# if value.isValid() and not value.isNull():
-# QString text("This is highlighted.");
- text = value.toString()
- document.setHtml(text);
+ self.document.setHtml(index.data(Qt.DisplayRole).toString())
painter.save()
+ if option.state & QStyle.State_Selected:
+ painter.fillRect(option.rect, option.palette.highlight())
painter.setClipRect(option.rect)
- painter.translate(option.rect.topLeft());
- document.drawContents(painter);
+ painter.translate(option.rect.topLeft())
+ self.document.drawContents(painter)
painter.restore()
-# painter.translate(-option.rect.topLeft());
def createEditor(self, parent, option, index):
m = index.model()
diff --git a/src/calibre/library/server/opds.py b/src/calibre/library/server/opds.py
index e447c6966c..fd8c50c594 100644
--- a/src/calibre/library/server/opds.py
+++ b/src/calibre/library/server/opds.py
@@ -173,6 +173,8 @@ def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
extra.append('%s: %s
'%(xml(name), xml(format_tag_string(val, ',',
ignore_max=True,
no_tag_count=True))))
+ elif datatype == 'comments':
+ extra.append('%s: %s
'%(xml(name), comments_to_html(unicode(val))))
else:
extra.append('%s: %s
'%(xml(name), xml(unicode(val))))
comments = item[FM['comments']]
From e770af4d47d72792068311e8244d7762b581822b Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 25 Dec 2010 18:08:11 +0000
Subject: [PATCH 5/7] New formatter function, fix problems with strcat and math
results, define basic formatter for usbms and formatter
---
src/calibre/devices/usbms/books.py | 21 +++-------------
src/calibre/utils/formatter.py | 40 +++++++++++++++++++++---------
2 files changed, 31 insertions(+), 30 deletions(-)
diff --git a/src/calibre/devices/usbms/books.py b/src/calibre/devices/usbms/books.py
index 73afd770c1..1e7d74480a 100644
--- a/src/calibre/devices/usbms/books.py
+++ b/src/calibre/devices/usbms/books.py
@@ -14,22 +14,7 @@ from calibre.constants import preferred_encoding
from calibre import isbytestring, force_unicode
from calibre.utils.config import prefs, tweaks
from calibre.utils.icu import strcmp
-from calibre.utils.formatter import TemplateFormatter
-
-class SafeFormat(TemplateFormatter):
- '''
- Provides a format function that substitutes '' for any missing value
- '''
-
- def get_value(self, key, args, kwargs):
- try:
- if key in kwargs:
- return kwargs[key]
- return key
- except:
- return key
-
-safe_formatter = SafeFormat()
+from calibre.utils.formatter import eval_formatter
class Book(Metadata):
def __init__(self, prefix, lpath, size=None, other=None):
@@ -131,10 +116,10 @@ class CollectionsBookList(BookList):
field_name = field_meta['name']
else:
field_name = ''
- cat_name = safe_formatter.safe_format(
+ cat_name = eval_formatter.safe_format(
fmt=tweaks['sony_collection_name_template'],
kwargs={'category':field_name, 'value':field_value},
- error_value='', book=None)
+ error_value='GET_CATEGORY', book=None)
return cat_name.strip()
def get_collections(self, collection_attributes):
diff --git a/src/calibre/utils/formatter.py b/src/calibre/utils/formatter.py
index 182aff5a7a..bb7e953d19 100644
--- a/src/calibre/utils/formatter.py
+++ b/src/calibre/utils/formatter.py
@@ -36,7 +36,7 @@ class _Parser(object):
return gt
def _assign(self, target, value):
- setattr(self, target, value)
+ self.variables[target] = value
return value
def _concat(self, *args):
@@ -55,18 +55,23 @@ class _Parser(object):
}
x = float(x if x else 0)
y = float(y if y else 0)
- return ops[op](x, y)
+ return str(ops[op](x, y))
def _template(self, template):
template = template.replace('[[', '{').replace(']]', '}')
return self.parent.safe_format(template, self.parent.kwargs, 'TEMPLATE',
self.parent.book)
+ def _eval(self, template):
+ template = template.replace('[[', '{').replace(']]', '}')
+ return eval_formatter.safe_format(template, self.variables, 'EVAL', None)
+
local_functions = {
'add' : (2, partial(_math, op='+')),
'assign' : (2, _assign),
'cmp' : (5, _cmp),
'divide' : (2, partial(_math, op='/')),
+ 'eval' : (1, _eval),
'field' : (1, lambda s, x: s.parent.get_value(x, [], s.parent.kwargs)),
'multiply' : (2, partial(_math, op='*')),
'strcat' : (-1, _concat),
@@ -82,7 +87,7 @@ class _Parser(object):
if prog[1] != '':
self.error(_('failed to scan program. Invalid input {0}').format(prog[1]))
self.parent = parent
- setattr(self, '$', val)
+ self.variables = {'$':val}
def error(self, message):
m = 'Formatter: ' + message + _(' near ')
@@ -144,7 +149,7 @@ class _Parser(object):
# We have an identifier. Determine if it is a function
id = self.token()
if not self.token_op_is_a('('):
- return getattr(self, id, _('unknown id ') + id)
+ return self.variables.get(id, _('unknown id ') + id)
# We have a function.
# Check if it is a known one. We do this here so error reporting is
# better, as it can identify the tokens near the problem.
@@ -417,15 +422,18 @@ class TemplateFormatter(string.Formatter):
self.kwargs = kwargs
self.book = book
self.composite_values = {}
- try:
- ans = self.vformat(fmt, [], kwargs).strip()
- except Exception, e:
- if DEBUG:
- traceback.print_exc()
- ans = error_value + ' ' + e.message
+ if fmt.startswith('program:'):
+ ans = self._eval_program(None, fmt[8:])
+ else:
+ try:
+ ans = self.vformat(fmt, [], kwargs).strip()
+ except Exception, e:
+ if DEBUG:
+ traceback.print_exc()
+ ans = error_value + ' ' + e.message
return ans
-class ValidateFormat(TemplateFormatter):
+class ValidateFormatter(TemplateFormatter):
'''
Provides a format function that substitutes '' for any missing value
'''
@@ -435,6 +443,14 @@ class ValidateFormat(TemplateFormatter):
def validate(self, x):
return self.vformat(x, [], {})
-validation_formatter = ValidateFormat()
+validation_formatter = ValidateFormatter()
+class EvalFormatter(TemplateFormatter):
+ '''
+ A template formatter that uses a simple dict instead of an mi instance
+ '''
+ def get_value(self, key, args, kwargs):
+ return kwargs.get(key, _('No such variable ') + key)
+
+eval_formatter = EvalFormatter()
From 6e80dca1bbd1b7925409571d034c8353179e5108 Mon Sep 17 00:00:00 2001
From: Kovid Goyal
Date: Sat, 25 Dec 2010 11:24:50 -0700
Subject: [PATCH 6/7] ...
---
src/calibre/gui2/library/delegates.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py
index fef1542737..b41fd78dc3 100644
--- a/src/calibre/gui2/library/delegates.py
+++ b/src/calibre/gui2/library/delegates.py
@@ -306,7 +306,7 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
painter.save()
if hasattr(QStyle, 'CE_ItemViewItem'):
style.drawControl(QStyle.CE_ItemViewItem, option,
- painter, self._parent)
+ painter, self.parent())
elif option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
painter.setClipRect(option.rect)
From 532749201a15611d262e903409ad5ea3352a6083 Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Sat, 25 Dec 2010 18:30:50 +0000
Subject: [PATCH 7/7] Make custom_column_widgets.py, book_info.py, and
book_details.py use comments_to_html. Fix _parent to parent()
---
src/calibre/gui2/book_details.py | 1 +
src/calibre/gui2/custom_column_widgets.py | 3 ++-
src/calibre/gui2/dialogs/book_info.py | 1 +
src/calibre/gui2/library/delegates.py | 2 +-
4 files changed, 5 insertions(+), 2 deletions(-)
diff --git a/src/calibre/gui2/book_details.py b/src/calibre/gui2/book_details.py
index 50ce72686a..dd12080d7f 100644
--- a/src/calibre/gui2/book_details.py
+++ b/src/calibre/gui2/book_details.py
@@ -46,6 +46,7 @@ def render_rows(data):
txt = txt.decode(preferred_encoding, 'replace')
if key.endswith(u':html'):
key = key[:-5]
+ txt = comments_to_html(txt)
elif '' not in txt:
txt = prepare_string_for_xml(txt)
if 'id' in data:
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index 40abb05f89..ec18675359 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -19,6 +19,7 @@ from calibre.gui2.comments_editor import Editor as CommentsEditor
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.utils.config import tweaks
from calibre.utils.icu import sort_key
+from calibre.library.comments import comments_to_html
class Base(object):
@@ -197,7 +198,7 @@ class Comments(Base):
def setter(self, val):
if val is None:
val = ''
- self._tb.html = val
+ self._tb.html = comments_to_html(val)
def getter(self):
val = unicode(self._tb.html).strip()
diff --git a/src/calibre/gui2/dialogs/book_info.py b/src/calibre/gui2/dialogs/book_info.py
index 6cae27d926..1384c27b8c 100644
--- a/src/calibre/gui2/dialogs/book_info.py
+++ b/src/calibre/gui2/dialogs/book_info.py
@@ -136,6 +136,7 @@ class BookInfo(QDialog, Ui_BookInfo):
txt = info[key]
if key.endswith(':html'):
key = key[:-5]
+ txt = comments_to_html(txt)
if key != _('Path'):
txt = u'
\n'.join(textwrap.wrap(txt, 120))
rows += u'%s: | %s |
'%(key, txt)
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py
index fef1542737..b41fd78dc3 100644
--- a/src/calibre/gui2/library/delegates.py
+++ b/src/calibre/gui2/library/delegates.py
@@ -306,7 +306,7 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
painter.save()
if hasattr(QStyle, 'CE_ItemViewItem'):
style.drawControl(QStyle.CE_ItemViewItem, option,
- painter, self._parent)
+ painter, self.parent())
elif option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
painter.setClipRect(option.rect)