Sync to trunk.

This commit is contained in:
John Schember 2010-12-26 20:48:00 -05:00
commit e548421835
14 changed files with 301 additions and 91 deletions

View File

@ -14,22 +14,7 @@ from calibre.constants import preferred_encoding
from calibre import isbytestring, force_unicode from calibre import isbytestring, force_unicode
from calibre.utils.config import prefs, tweaks from calibre.utils.config import prefs, tweaks
from calibre.utils.icu import strcmp from calibre.utils.icu import strcmp
from calibre.utils.formatter import TemplateFormatter from calibre.utils.formatter import eval_formatter
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()
class Book(Metadata): class Book(Metadata):
def __init__(self, prefix, lpath, size=None, other=None): def __init__(self, prefix, lpath, size=None, other=None):
@ -131,10 +116,10 @@ class CollectionsBookList(BookList):
field_name = field_meta['name'] field_name = field_meta['name']
else: else:
field_name = '' field_name = ''
cat_name = safe_formatter.safe_format( cat_name = eval_formatter.safe_format(
fmt=tweaks['sony_collection_name_template'], fmt=tweaks['sony_collection_name_template'],
kwargs={'category':field_name, 'value':field_value}, kwargs={'category':field_name, 'value':field_value},
error_value='', book=None) error_value='GET_CATEGORY', book=None)
return cat_name.strip() return cat_name.strip()
def get_collections(self, collection_attributes): def get_collections(self, collection_attributes):

View File

@ -44,7 +44,10 @@ def render_rows(data):
key = key.decode(preferred_encoding, 'replace') key = key.decode(preferred_encoding, 'replace')
if isinstance(txt, str): if isinstance(txt, str):
txt = txt.decode(preferred_encoding, 'replace') txt = txt.decode(preferred_encoding, 'replace')
if '</font>' not in txt: if key.endswith(u':html'):
key = key[:-5]
txt = comments_to_html(txt)
elif '</font>' not in txt:
txt = prepare_string_for_xml(txt) txt = prepare_string_for_xml(txt)
if 'id' in data: if 'id' in data:
if key == _('Path'): if key == _('Path'):

View File

@ -9,15 +9,17 @@ import sys
from functools import partial from functools import partial
from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \
QDate, QGroupBox, QVBoxLayout, QPlainTextEdit, QSizePolicy, \ QDate, QGroupBox, QVBoxLayout, QSizePolicy, \
QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL, \ QSpacerItem, QIcon, QCheckBox, QWidget, QHBoxLayout, SIGNAL, \
QPushButton QPushButton
from calibre.utils.date import qt_to_dt, now from calibre.utils.date import qt_to_dt, now
from calibre.gui2.widgets import TagsLineEdit, EnComboBox 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.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.utils.config import tweaks from calibre.utils.config import tweaks
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
from calibre.library.comments import comments_to_html
class Base(object): class Base(object):
@ -186,9 +188,9 @@ class Comments(Base):
self._box = QGroupBox(parent) self._box = QGroupBox(parent)
self._box.setTitle('&'+self.col_metadata['name']) self._box.setTitle('&'+self.col_metadata['name'])
self._layout = QVBoxLayout() self._layout = QVBoxLayout()
self._tb = QPlainTextEdit(self._box) self._tb = CommentsEditor(self._box)
self._tb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum) self._tb.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Minimum)
self._tb.setTabChangesFocus(True) #self._tb.setTabChangesFocus(True)
self._layout.addWidget(self._tb) self._layout.addWidget(self._tb)
self._box.setLayout(self._layout) self._box.setLayout(self._layout)
self.widgets = [self._box] self.widgets = [self._box]
@ -196,10 +198,10 @@ class Comments(Base):
def setter(self, val): def setter(self, val):
if val is None: if val is None:
val = '' val = ''
self._tb.setPlainText(val) self._tb.html = comments_to_html(val)
def getter(self): def getter(self):
val = unicode(self._tb.toPlainText()).strip() val = unicode(self._tb.html).strip()
if not val: if not val:
val = None val = None
return val return val

View File

@ -12,6 +12,7 @@ from calibre.gui2.dialogs.book_info_ui import Ui_BookInfo
from calibre.gui2 import dynamic, open_local_file from calibre.gui2 import dynamic, open_local_file
from calibre import fit_image from calibre import fit_image
from calibre.library.comments import comments_to_html from calibre.library.comments import comments_to_html
from calibre.utils.icu import sort_key
class BookInfo(QDialog, Ui_BookInfo): class BookInfo(QDialog, Ui_BookInfo):
@ -130,9 +131,12 @@ class BookInfo(QDialog, Ui_BookInfo):
for f in formats: for f in formats:
f = f.strip() f = f.strip()
info[_('Formats')] += '<a href="%s">%s</a>, '%(f,f) info[_('Formats')] += '<a href="%s">%s</a>, '%(f,f)
for key in info.keys(): for key in sorted(info.keys(), key=sort_key):
if key == 'id': continue if key == 'id': continue
txt = info[key] txt = info[key]
if key.endswith(':html'):
key = key[:-5]
txt = comments_to_html(txt)
if key != _('Path'): if key != _('Path'):
txt = u'<br />\n'.join(textwrap.wrap(txt, 120)) txt = u'<br />\n'.join(textwrap.wrap(txt, 120))
rows += u'<tr><td><b>%s:</b></td><td>%s</td></tr>'%(key, txt) rows += u'<tr><td><b>%s:</b></td><td>%s</td></tr>'%(key, txt)

View File

@ -5,6 +5,7 @@ __license__ = 'GPL v3'
from PyQt4.Qt import Qt, QDialog, QDialogButtonBox from PyQt4.Qt import Qt, QDialog, QDialogButtonBox
from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog from calibre.gui2.dialogs.comments_dialog_ui import Ui_CommentsDialog
from calibre.library.comments import comments_to_html
class CommentsDialog(QDialog, Ui_CommentsDialog): class CommentsDialog(QDialog, Ui_CommentsDialog):
@ -18,8 +19,8 @@ class CommentsDialog(QDialog, Ui_CommentsDialog):
self.setWindowIcon(icon) self.setWindowIcon(icon)
if text is not None: if text is not None:
self.textbox.setPlainText(text) self.textbox.html = comments_to_html(text)
self.textbox.setTabChangesFocus(True) # self.textbox.setTabChangesFocus(True)
self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK')) self.buttonBox.button(QDialogButtonBox.Ok).setText(_('&OK'))
self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel')) self.buttonBox.button(QDialogButtonBox.Cancel).setText(_('&Cancel'))

View File

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>336</width> <width>400</width>
<height>235</height> <height>400</height>
</rect> </rect>
</property> </property>
<property name="sizePolicy"> <property name="sizePolicy">
@ -21,7 +21,7 @@
</property> </property>
<layout class="QVBoxLayout" name="verticalLayout"> <layout class="QVBoxLayout" name="verticalLayout">
<item> <item>
<widget class="QPlainTextEdit" name="textbox"/> <widget class="Editor" name="textbox" native="true"/>
</item> </item>
<item> <item>
<widget class="QDialogButtonBox" name="buttonBox"> <widget class="QDialogButtonBox" name="buttonBox">
@ -35,6 +35,13 @@
</item> </item>
</layout> </layout>
</widget> </widget>
<customwidgets>
<customwidget>
<class>Editor</class>
<extends>QWidget</extends>
<header>calibre/gui2/comments_editor.h</header>
</customwidget>
</customwidgets>
<resources/> <resources/>
<connections> <connections>
<connection> <connection>

View File

@ -417,6 +417,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
self.multiple_separator.setFixedWidth(30) self.multiple_separator.setFixedWidth(30)
self.multiple_separator.setText(' ::: ') self.multiple_separator.setText(' ::: ')
self.multiple_separator.textChanged.connect(self.s_r_separator_changed) 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): def s_r_get_field(self, mi, field):
if field: if field:
@ -439,6 +441,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
val = [] val = []
return 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): def s_r_template_changed(self):
self.s_r_search_field_changed(self.search_field.currentIndex()) 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) mi = self.db.get_metadata(self.ids[i], index_is_id=True)
src = unicode(self.search_field.currentText()) src = unicode(self.search_field.currentText())
t = self.s_r_get_field(mi, src) 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)) w.setText(unicode(self.multiple_separator.text()).join(t))
if self.search_mode.currentIndex() == 0: if self.search_mode.currentIndex() == 0:
@ -466,12 +474,8 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
txt = unicode(txt) txt = unicode(txt)
if not txt: if not txt:
txt = unicode(self.search_field.currentText()) txt = unicode(self.search_field.currentText())
self.comma_separated.setEnabled(True)
if txt and txt in self.writable_fields: if txt and txt in self.writable_fields:
self.destination_field_fm = self.db.metadata_for_field(txt) 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) self.s_r_paint_results(None)
def s_r_search_mode_changed(self, val): def s_r_search_mode_changed(self, val):
@ -542,6 +546,22 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
dest = src dest = src
dest_mode = self.replace_mode.currentIndex() 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: if dest_mode != 0:
dest_val = mi.get(dest, '') dest_val = mi.get(dest, '')
if dest_val is None: if dest_val is None:
@ -602,8 +622,9 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
try: try:
result = self.s_r_do_regexp(mi) result = self.s_r_do_regexp(mi)
t = self.s_r_do_destination(mi, result) t = self.s_r_do_destination(mi, result)
if len(result) > 1 and self.destination_field_fm is not None and \ if len(t) > 1 and self.destination_field_fm['is_multiple']:
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) t = unicode(self.multiple_separator.text()).join(t)
else: else:
t = self.s_r_replace_mode_separator().join(t) t = self.s_r_replace_mode_separator().join(t)

View File

@ -658,11 +658,12 @@ If blank, the source field is used if the field is modifiable</string>
<item> <item>
<widget class="QCheckBox" name="comma_separated"> <widget class="QCheckBox" name="comma_separated">
<property name="toolTip"> <property name="toolTip">
<string>Specifies whether a comma should be put between values when copying from a <string>Specifies whether result items should be split into multiple values or
multiple-valued field to a single-valued field</string> left as single values. This option has the most effect when the source field is
not multiple and the destination field is multiple</string>
</property> </property>
<property name="text"> <property name="text">
<string>&amp;Use comma</string> <string>Split &amp;result</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
@ -684,28 +685,8 @@ multiple-valued field to a single-valued field</string>
</item> </item>
</layout> </layout>
</item> </item>
<item row="8" column="1"> <item row="8" column="1" colspan="2">
<widget class="QLabel" name="xlabel_3">
<property name="text">
<string>Test text</string>
</property>
<property name="buddy">
<cstring>test_text</cstring>
</property>
</widget>
</item>
<item row="8" column="2">
<layout class="QHBoxLayout" name="horizontalLayout_21"> <layout class="QHBoxLayout" name="horizontalLayout_21">
<item>
<widget class="QLabel" name="label_51">
<property name="text">
<string>Test result</string>
</property>
<property name="buddy">
<cstring>test_result</cstring>
</property>
</widget>
</item>
<item> <item>
<spacer name="HSpacer_347"> <spacer name="HSpacer_347">
<property name="orientation"> <property name="orientation">
@ -719,10 +700,62 @@ multiple-valued field to a single-valued field</string>
</property> </property>
</spacer> </spacer>
</item> </item>
<item>
<widget class="QLabel" name="xlabel_412">
<property name="text">
<string>For multiple-valued fields, sho&amp;w</string>
</property>
<property name="buddy">
<cstring>results_count</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="results_count">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>999</number>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="xlabel_412">
<property name="text">
<string>values starting a&amp;t</string>
</property>
<property name="buddy">
<cstring>starting_from</cstring>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="starting_from">
<property name="enabled">
<bool>true</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>999</number>
</property>
<property name="value">
<number>1</number>
</property>
</widget>
</item>
<item> <item>
<widget class="QLabel" name="xlabel_41"> <widget class="QLabel" name="xlabel_41">
<property name="text"> <property name="text">
<string>Multi&amp;ple separator:</string> <string>with values separated b&amp;y</string>
</property> </property>
<property name="buddy"> <property name="buddy">
<cstring>multiple_separator</cstring> <cstring>multiple_separator</cstring>
@ -756,6 +789,20 @@ multiple-valued field to a single-valued field</string>
</rect> </rect>
</property> </property>
<layout class="QGridLayout" name="testgrid"> <layout class="QGridLayout" name="testgrid">
<item row="7" column="1">
<widget class="QLabel" name="xlabel_3">
<property name="text">
<string>Test text</string>
</property>
</widget>
</item>
<item row="7" column="2">
<widget class="QLabel" name="xlabel_3">
<property name="text">
<string>Test result</string>
</property>
</widget>
</item>
<item row="8" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_31"> <widget class="QLabel" name="label_31">
<property name="text"> <property name="text">
@ -857,6 +904,8 @@ multiple-valued field to a single-valued field</string>
<tabstop>destination_field</tabstop> <tabstop>destination_field</tabstop>
<tabstop>replace_mode</tabstop> <tabstop>replace_mode</tabstop>
<tabstop>comma_separated</tabstop> <tabstop>comma_separated</tabstop>
<tabstop>results_count</tabstop>
<tabstop>starting_from</tabstop>
<tabstop>multiple_separator</tabstop> <tabstop>multiple_separator</tabstop>
<tabstop>test_text</tabstop> <tabstop>test_text</tabstop>
<tabstop>test_result</tabstop> <tabstop>test_result</tabstop>

View File

@ -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'))

View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TemplateDialog</class>
<widget class="QDialog" name="TemplateDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>336</width>
<height>235</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Edit Comments</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QPlainTextEdit" name="textbox"/>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>TemplateDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>229</x>
<y>211</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>234</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>TemplateDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>297</x>
<y>217</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>234</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -13,7 +13,7 @@ from PyQt4.Qt import QColor, Qt, QModelIndex, QSize, \
QPen, QStyle, QPainter, QStyleOptionViewItemV4, \ QPen, QStyle, QPainter, QStyleOptionViewItemV4, \
QIcon, QDoubleSpinBox, QVariant, QSpinBox, \ QIcon, QDoubleSpinBox, QVariant, QSpinBox, \
QStyledItemDelegate, QCompleter, \ QStyledItemDelegate, QCompleter, \
QComboBox QComboBox, QTextDocument
from calibre.gui2 import UNDEFINED_QDATE, error_dialog from calibre.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.gui2.widgets import EnLineEdit, TagsLineEdit 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.formatter import validation_formatter
from calibre.utils.icu import sort_key from calibre.utils.icu import sort_key
from calibre.gui2.dialogs.comments_dialog import CommentsDialog from calibre.gui2.dialogs.comments_dialog import CommentsDialog
from calibre.gui2.dialogs.template_dialog import TemplateDialog
class RatingDelegate(QStyledItemDelegate): # {{{ class RatingDelegate(QStyledItemDelegate): # {{{
COLOR = QColor("blue") COLOR = QColor("blue")
@ -294,6 +296,24 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
Delegate for comments data. Delegate for comments data.
''' '''
def __init__(self, parent):
QStyledItemDelegate.__init__(self, parent)
self.document = QTextDocument()
def paint(self, painter, option, index):
style = self.parent().style()
self.document.setHtml(index.data(Qt.DisplayRole).toString())
painter.save()
if hasattr(QStyle, 'CE_ItemViewItem'):
style.drawControl(QStyle.CE_ItemViewItem, option,
painter, self.parent())
elif option.state & QStyle.State_Selected:
painter.fillRect(option.rect, option.palette.highlight())
painter.setClipRect(option.rect)
painter.translate(option.rect.topLeft())
self.document.drawContents(painter)
painter.restore()
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
m = index.model() m = index.model()
col = m.column_map[index.column()] col = m.column_map[index.column()]
@ -301,11 +321,11 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
editor = CommentsDialog(parent, text) editor = CommentsDialog(parent, text)
d = editor.exec_() d = editor.exec_()
if d: if d:
m.setData(index, QVariant(editor.textbox.toPlainText()), Qt.EditRole) m.setData(index, QVariant(editor.textbox.html), Qt.EditRole)
return None return None
def setModelData(self, editor, model, index): 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): # {{{ class CcBoolDelegate(QStyledItemDelegate): # {{{
@ -351,7 +371,7 @@ class CcTemplateDelegate(QStyledItemDelegate): # {{{
def createEditor(self, parent, option, index): def createEditor(self, parent, option, index):
m = index.model() m = index.model()
text = m.custom_columns[m.column_map[index.column()]]['display']['composite_template'] 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.setWindowTitle(_("Edit template"))
editor.textbox.setTabChangesFocus(False) editor.textbox.setTabChangesFocus(False)
editor.textbox.setTabStopWidth(20) editor.textbox.setTabStopWidth(20)

View File

@ -334,6 +334,8 @@ class BooksModel(QAbstractTableModel): # {{{
if key not in cf_to_display: if key not in cf_to_display:
continue continue
name, val = mi.format_field(key) name, val = mi.format_field(key)
if mi.metadata_for_field(key)['datatype'] == 'comments':
name += ':html'
if val: if val:
data[name] = val data[name] = val
return data return data

View File

@ -173,6 +173,8 @@ def ACQUISITION_ENTRY(item, version, db, updated, CFM, CKEYS, prefix):
extra.append('%s: %s<br />'%(xml(name), xml(format_tag_string(val, ',', extra.append('%s: %s<br />'%(xml(name), xml(format_tag_string(val, ',',
ignore_max=True, ignore_max=True,
no_tag_count=True)))) no_tag_count=True))))
elif datatype == 'comments':
extra.append('%s: %s<br />'%(xml(name), comments_to_html(unicode(val))))
else: else:
extra.append('%s: %s<br />'%(xml(name), xml(unicode(val)))) extra.append('%s: %s<br />'%(xml(name), xml(unicode(val))))
comments = item[FM['comments']] comments = item[FM['comments']]

View File

@ -36,7 +36,7 @@ class _Parser(object):
return gt return gt
def _assign(self, target, value): def _assign(self, target, value):
setattr(self, target, value) self.variables[target] = value
return value return value
def _concat(self, *args): def _concat(self, *args):
@ -55,18 +55,23 @@ class _Parser(object):
} }
x = float(x if x else 0) x = float(x if x else 0)
y = float(y if y else 0) y = float(y if y else 0)
return ops[op](x, y) return unicode(ops[op](x, y))
def _template(self, template): def _template(self, template):
template = template.replace('[[', '{').replace(']]', '}') template = template.replace('[[', '{').replace(']]', '}')
return self.parent.safe_format(template, self.parent.kwargs, 'TEMPLATE', return self.parent.safe_format(template, self.parent.kwargs, 'TEMPLATE',
self.parent.book) self.parent.book)
def _eval(self, template):
template = template.replace('[[', '{').replace(']]', '}')
return eval_formatter.safe_format(template, self.variables, 'EVAL', None)
local_functions = { local_functions = {
'add' : (2, partial(_math, op='+')), 'add' : (2, partial(_math, op='+')),
'assign' : (2, _assign), 'assign' : (2, _assign),
'cmp' : (5, _cmp), 'cmp' : (5, _cmp),
'divide' : (2, partial(_math, op='/')), 'divide' : (2, partial(_math, op='/')),
'eval' : (1, _eval),
'field' : (1, lambda s, x: s.parent.get_value(x, [], s.parent.kwargs)), 'field' : (1, lambda s, x: s.parent.get_value(x, [], s.parent.kwargs)),
'multiply' : (2, partial(_math, op='*')), 'multiply' : (2, partial(_math, op='*')),
'strcat' : (-1, _concat), 'strcat' : (-1, _concat),
@ -82,7 +87,7 @@ class _Parser(object):
if prog[1] != '': if prog[1] != '':
self.error(_('failed to scan program. Invalid input {0}').format(prog[1])) self.error(_('failed to scan program. Invalid input {0}').format(prog[1]))
self.parent = parent self.parent = parent
setattr(self, '$', val) self.variables = {'$':val}
def error(self, message): def error(self, message):
m = 'Formatter: ' + message + _(' near ') m = 'Formatter: ' + message + _(' near ')
@ -144,7 +149,7 @@ class _Parser(object):
# We have an identifier. Determine if it is a function # We have an identifier. Determine if it is a function
id = self.token() id = self.token()
if not self.token_op_is_a('('): 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. # We have a function.
# Check if it is a known one. We do this here so error reporting is # 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. # better, as it can identify the tokens near the problem.
@ -417,6 +422,9 @@ class TemplateFormatter(string.Formatter):
self.kwargs = kwargs self.kwargs = kwargs
self.book = book self.book = book
self.composite_values = {} self.composite_values = {}
if fmt.startswith('program:'):
ans = self._eval_program(None, fmt[8:])
else:
try: try:
ans = self.vformat(fmt, [], kwargs).strip() ans = self.vformat(fmt, [], kwargs).strip()
except Exception, e: except Exception, e:
@ -425,7 +433,7 @@ class TemplateFormatter(string.Formatter):
ans = error_value + ' ' + e.message ans = error_value + ' ' + e.message
return ans return ans
class ValidateFormat(TemplateFormatter): class ValidateFormatter(TemplateFormatter):
''' '''
Provides a format function that substitutes '' for any missing value Provides a format function that substitutes '' for any missing value
''' '''
@ -435,6 +443,14 @@ class ValidateFormat(TemplateFormatter):
def validate(self, x): def validate(self, x):
return self.vformat(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()