diff --git a/src/calibre/gui2/dialogs/template_line_editor.py b/src/calibre/gui2/dialogs/template_line_editor.py
new file mode 100644
index 0000000000..c3598a8abb
--- /dev/null
+++ b/src/calibre/gui2/dialogs/template_line_editor.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+# vim:fileencoding=UTF-8:ts=4:sw=4:sta:et:sts=4:ai
+
+__license__ = 'GPL v3'
+__copyright__ = '2010, Kovid Goyal '
+__docformat__ = 'restructuredtext en'
+
+from PyQt4.Qt import QLineEdit
+from calibre.gui2.dialogs.template_dialog import TemplateDialog
+
+class TemplateLineEditor(QLineEdit):
+
+ '''
+ Extend the context menu of a QLineEdit to include more actions.
+ '''
+
+ def contextMenuEvent(self, event):
+ menu = self.createStandardContextMenu()
+ menu.addSeparator()
+
+ action_open_editor = menu.addAction(_('Open Template Editor'))
+
+ action_open_editor.triggered.connect(self.open_editor)
+ menu.exec_(event.globalPos())
+
+ def open_editor(self):
+ t = TemplateDialog(self, self.text())
+ t.setWindowTitle(_('Edit template'))
+ if t.exec_():
+ self.setText(t.textbox.toPlainText())
+
+
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py
index 7265be89c8..50c411aaa4 100644
--- a/src/calibre/gui2/library/delegates.py
+++ b/src/calibre/gui2/library/delegates.py
@@ -7,11 +7,12 @@ __docformat__ = 'restructuredtext en'
from math import cos, sin, pi
-from PyQt4.Qt import (QColor, Qt, QModelIndex, QSize,
+from PyQt4.Qt import (QColor, Qt, QModelIndex, QSize, QApplication,
QPainterPath, QLinearGradient, QBrush,
QPen, QStyle, QPainter, QStyleOptionViewItemV4,
QIcon, QDoubleSpinBox, QVariant, QSpinBox,
- QStyledItemDelegate, QComboBox, QTextDocument)
+ QStyledItemDelegate, QComboBox, QTextDocument,
+ QAbstractTextDocumentLayout)
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
from calibre.gui2.widgets import EnLineEdit
@@ -62,12 +63,14 @@ class RatingDelegate(QStyledItemDelegate): # {{{
painter.restore()
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())
+ else:
+ painter.fillRect(option.rect, option.backgroundBrush)
+
try:
painter.setRenderHint(QPainter.Antialiasing)
painter.setClipRect(option.rect)
@@ -316,18 +319,22 @@ class CcCommentsDelegate(QStyledItemDelegate): # {{{
self.document = QTextDocument()
def paint(self, painter, option, index):
- style = self.parent().style()
- self.document.setHtml(index.data(Qt.DisplayRole).toString())
- painter.save()
+ self.initStyleOption(option, index)
+ style = QApplication.style() if option.widget is None \
+ else option.widget.style()
+ self.document.setHtml(option.text)
+ option.text = u''
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()
+ style.drawControl(QStyle.CE_ItemViewItem, option, painter)
+ ctx = QAbstractTextDocumentLayout.PaintContext()
+ ctx.palette = option.palette #.setColor(QPalette.Text, QColor("red"));
+ if hasattr(QStyle, 'SE_ItemViewItemText'):
+ textRect = style.subElementRect(QStyle.SE_ItemViewItemText, option)
+ painter.save()
+ painter.translate(textRect.topLeft())
+ painter.setClipRect(textRect.translated(-textRect.topLeft()))
+ self.document.documentLayout().draw(painter, ctx)
+ painter.restore()
def createEditor(self, parent, option, index):
m = index.model()
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 0baf98ecdd..d698655746 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -14,6 +14,7 @@ from PyQt4.Qt import (QAbstractTableModel, Qt, pyqtSignal, QIcon, QImage,
from calibre.gui2 import NONE, UNDEFINED_QDATE
from calibre.utils.pyparsing import ParseException
from calibre.ebooks.metadata import fmt_sidx, authors_to_string, string_to_authors
+from calibre.ebooks.metadata.book.base import composite_formatter
from calibre.ptempfile import PersistentTemporaryFile
from calibre.utils.config import tweaks, prefs
from calibre.utils.date import dt_factory, qt_to_dt
@@ -96,6 +97,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.ids_to_highlight_set = set()
self.current_highlighted_idx = None
self.highlight_only = False
+ self.column_color_map = {}
self.read_config()
def change_alignment(self, colname, alignment):
@@ -151,6 +153,7 @@ class BooksModel(QAbstractTableModel): # {{{
self.headers[col] = self.custom_columns[col]['name']
self.build_data_convertors()
+ self.set_color_templates(reset=False)
self.reset()
self.database_changed.emit(db)
self.stop_metadata_backup()
@@ -532,6 +535,15 @@ class BooksModel(QAbstractTableModel): # {{{
img = self.default_image
return img
+ def set_color_templates(self, reset=True):
+ self.column_color_map = {}
+ for i in range(1,self.db.column_color_count+1):
+ name = self.db.prefs.get('column_color_name_'+str(i))
+ if name:
+ self.column_color_map[name] = \
+ self.db.prefs.get('column_color_template_'+str(i))
+ if reset:
+ self.reset()
def build_data_convertors(self):
def authors(r, idx=-1):
@@ -696,6 +708,31 @@ class BooksModel(QAbstractTableModel): # {{{
elif role == Qt.BackgroundRole:
if self.id(index) in self.ids_to_highlight_set:
return QVariant(QColor('lightgreen'))
+ elif role == Qt.ForegroundRole:
+ key = self.column_map[col]
+ if key in self.column_color_map:
+ mi = self.db.get_metadata(self.id(index), index_is_id=True)
+ fmt = self.column_color_map[key]
+ try:
+ color = QColor(composite_formatter.safe_format(fmt, mi, '', mi))
+ if color.isValid():
+ return QVariant(color)
+ except:
+ return NONE
+ elif self.is_custom_column(key) and \
+ self.custom_columns[key]['datatype'] == 'enumeration':
+ cc = self.custom_columns[self.column_map[col]]['display']
+ colors = cc.get('enum_colors', [])
+ values = cc.get('enum_values', [])
+ txt = unicode(index.data(Qt.DisplayRole).toString())
+ if len(colors) > 0 and txt in values:
+ try:
+ color = QColor(colors[values.index(txt)])
+ if color.isValid():
+ return QVariant(color)
+ except:
+ pass
+ return NONE
elif role == Qt.DecorationRole:
if self.column_to_dc_decorator_map[col] is not None:
return self.column_to_dc_decorator_map[index.column()](index.row())
diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py
index 7b891b782c..3a245580dd 100644
--- a/src/calibre/gui2/preferences/create_custom_column.py
+++ b/src/calibre/gui2/preferences/create_custom_column.py
@@ -6,7 +6,7 @@ __copyright__ = '2010, Kovid Goyal '
import re
from functools import partial
-from PyQt4.Qt import QDialog, Qt, QListWidgetItem, QVariant
+from PyQt4.Qt import QDialog, Qt, QListWidgetItem, QVariant, QColor
from calibre.gui2.preferences.create_custom_column_ui import Ui_QCreateCustomColumn
from calibre.gui2 import error_dialog
@@ -126,11 +126,15 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
c['display'].get('make_category', False))
elif ct == 'enumeration':
self.enum_box.setText(','.join(c['display'].get('enum_values', [])))
+ self.enum_colors.setText(','.join(c['display'].get('enum_colors', [])))
self.datatype_changed()
if ct in ['text', 'composite', 'enumeration']:
self.use_decorations.setChecked(c['display'].get('use_decorations', False))
elif ct == '*text':
self.is_names.setChecked(c['display'].get('is_names', False))
+
+ all_colors = [unicode(s) for s in list(QColor.colorNames())]
+ self.enum_colors_label.setToolTip('' + ', '.join(all_colors) + '
')
self.exec_()
def shortcut_activated(self, url):
@@ -170,7 +174,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
for x in ('box', 'default_label', 'label', 'sort_by', 'sort_by_label',
'make_category'):
getattr(self, 'composite_'+x).setVisible(col_type in ['composite', '*composite'])
- for x in ('box', 'default_label', 'label'):
+ for x in ('box', 'default_label', 'label', 'colors', 'colors_label'):
getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration')
self.use_decorations.setVisible(col_type in ['text', 'composite', 'enumeration'])
self.is_names.setVisible(col_type == '*text')
@@ -247,7 +251,20 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
if l[i] in l[i+1:]:
return self.simple_error('', _('The value "{0}" is in the '
'list more than once').format(l[i]))
- display_dict = {'enum_values': l}
+ c = unicode(self.enum_colors.text())
+ if c:
+ c = [v.strip() for v in unicode(self.enum_colors.text()).split(',')]
+ else:
+ c = []
+ if len(c) != 0 and len(c) != len(l):
+ return self.simple_error('', _('The colors box must be empty or '
+ 'contain the same number of items as the value box'))
+ for tc in c:
+ if tc not in QColor.colorNames():
+ return self.simple_error('',
+ _('The color {0} is unknown').format(tc))
+
+ display_dict = {'enum_values': l, 'enum_colors': c}
elif col_type == 'text' and is_multiple:
display_dict = {'is_names': self.is_names.isChecked()}
diff --git a/src/calibre/gui2/preferences/create_custom_column.ui b/src/calibre/gui2/preferences/create_custom_column.ui
index 619b0c6212..2bdadd4b9d 100644
--- a/src/calibre/gui2/preferences/create_custom_column.ui
+++ b/src/calibre/gui2/preferences/create_custom_column.ui
@@ -304,8 +304,8 @@ Everything else will show nothing.
-
-
-
-
+
+
-
@@ -320,13 +320,34 @@ four values, the first of them being the empty value.
- -
+
-
The empty string is always the first value
- Default: (nothing)
+ Values
+
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+ A list of color names to use when displaying an item. The
+list must be empty or contain a color for each value.
+
+
+
+ -
+
+
+ Colors
diff --git a/src/calibre/gui2/preferences/look_feel.py b/src/calibre/gui2/preferences/look_feel.py
index ee2d7a5428..1c9d4abfce 100644
--- a/src/calibre/gui2/preferences/look_feel.py
+++ b/src/calibre/gui2/preferences/look_feel.py
@@ -6,7 +6,7 @@ __copyright__ = '2010, Kovid Goyal '
__docformat__ = 'restructuredtext en'
from PyQt4.Qt import (QApplication, QFont, QFontInfo, QFontDialog,
- QAbstractListModel, Qt)
+ QAbstractListModel, Qt, QColor)
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, CommaSeparatedList
from calibre.gui2.preferences.look_feel_ui import Ui_Form
@@ -159,6 +159,51 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.df_up_button.clicked.connect(self.move_df_up)
self.df_down_button.clicked.connect(self.move_df_down)
+ self.color_help_text.setText('' +
+ _('Here you can specify coloring rules for columns shown in the '
+ 'library view. Choose the column you wish to color, then '
+ 'supply a template that specifies the color to use based on '
+ 'the values in the column. There is a '
+ ''
+ 'tutorial on using templates.') +
+ '
' +
+ _('The template must evaluate to one of the color names shown '
+ 'below. You can use any legal template expression. '
+ 'For example, you can set the title to always display in '
+ 'green using the template "green" (without the quotes). '
+ 'To show the title in the color named in the custom column '
+ '#column, use "{#column}". To show the title in blue if the '
+ 'custom column #column contains the value "foo", in red if the '
+ 'column contains the value "bar", otherwise in black, use '
+ '
{#column:switch(foo,blue,bar,red,black)}
'
+ 'To show the title in blue if the book has the exact tag '
+ '"Science Fiction", red if the book has the exact tag '
+ '"Mystery", or black if the book has neither tag, use'
+ "program: \n"
+ " t = field('tags'); \n"
+ " first_non_empty(\n"
+ " in_list(t, ',', '^Science Fiction$', 'blue', ''), \n"
+ " in_list(t, ',', '^Mystery$', 'red', 'black'))
"
+ 'To show the title in green if it has one format, blue if it '
+ 'two formats, and red if more, use'
+ "program:cmp(count(field('formats'),','), 2, 'green', 'blue', 'red')
") +
+ '
' +
+ _('You can access a multi-line template editor from the '
+ 'context menu (right-click).') + '
' +
+ _('Note: if you want to color a "custom column with a fixed set '
+ 'of values", it is often easier to specify the '
+ 'colors in the column definition dialog. There you can '
+ 'provide a color for each value without using a template.')+ '
')
+ choices = db.field_metadata.displayable_field_keys()
+ choices.sort(key=sort_key)
+ choices.insert(0, '')
+ self.column_color_count = db.column_color_count+1
+ for i in range(1, self.column_color_count):
+ r('column_color_name_'+str(i), db.prefs, choices=choices)
+ r('column_color_template_'+str(i), db.prefs)
+ all_colors = [unicode(s) for s in list(QColor.colorNames())]
+ self.colors_box.setText(', '.join(all_colors))
+
def initialize(self):
ConfigWidgetBase.initialize(self)
font = gprefs['font']
@@ -226,6 +271,11 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.changed_signal.emit()
def commit(self, *args):
+ for i in range(1, self.column_color_count):
+ col = getattr(self, 'opt_column_color_name_'+str(i))
+ if not col.currentText():
+ temp = getattr(self, 'opt_column_color_template_'+str(i))
+ temp.setText('')
rr = ConfigWidgetBase.commit(self, *args)
if self.current_font != self.initial_font:
gprefs['font'] = (self.current_font[:4] if self.current_font else
@@ -238,6 +288,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
return rr
def refresh_gui(self, gui):
+ gui.library_view.model().set_color_templates()
self.update_font_display()
gui.tags_view.reread_collapse_parameters()
gui.library_view.refresh_book_details()
diff --git a/src/calibre/gui2/preferences/look_feel.ui b/src/calibre/gui2/preferences/look_feel.ui
index 244b811cbd..970052ad2e 100644
--- a/src/calibre/gui2/preferences/look_feel.ui
+++ b/src/calibre/gui2/preferences/look_feel.ui
@@ -7,7 +7,7 @@
0
0
717
- 390
+ 519
@@ -407,6 +407,125 @@ then the tags will be displayed each on their own line.
+
+
+
+ :/images/format-fill-color.png:/images/format-fill-color.png
+
+
+ Column Coloring
+
+
+ -
+
+
+ Column name
+
+
+
+ -
+
+
+ Selection template
+
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ Color names
+
+
+
+ -
+
+
+
+ 0
+ 1
+
+
+
+
+ 16777215
+ 100
+
+
+
+ true
+
+
+
+ -
+
+
+
+ 0
+ 200
+
+
+
+ true
+
+
+ Qt::AlignCenter
+
+
+
+
+ 0
+ 0
+ 687
+ 194
+
+
+
+
-
+
+
+ true
+
+
+ true
+
+
+
+
+
+
+
+
+
@@ -417,6 +536,11 @@ then the tags will be displayed each on their own line.
QLineEdit
+
+ TemplateLineEditor
+ QLineEdit
+ calibre/gui2/dialogs/template_line_editor.h
+
diff --git a/src/calibre/gui2/preferences/plugboard.py b/src/calibre/gui2/preferences/plugboard.py
index b4b1d4e08e..cf632c04c0 100644
--- a/src/calibre/gui2/preferences/plugboard.py
+++ b/src/calibre/gui2/preferences/plugboard.py
@@ -7,12 +7,12 @@ __docformat__ = 'restructuredtext en'
import copy
-from PyQt4.Qt import Qt, QLineEdit, QComboBox, SIGNAL, QListWidgetItem
+from PyQt4.Qt import Qt, QComboBox, QListWidgetItem
from calibre.customize.ui import is_disabled
from calibre.gui2 import error_dialog, question_dialog
from calibre.gui2.device import device_name_for_plugboards
-from calibre.gui2.dialogs.template_dialog import TemplateDialog
+from calibre.gui2.dialogs.template_line_editor import TemplateLineEditor
from calibre.gui2.preferences import ConfigWidgetBase, test_widget
from calibre.gui2.preferences.plugboard_ui import Ui_Form
from calibre.customize.ui import metadata_writers, device_plugins
@@ -24,26 +24,6 @@ from calibre.library.server.content import plugboard_content_server_value, \
from calibre.utils.formatter import validation_formatter
-class LineEditWithTextBox(QLineEdit):
-
- '''
- Extend the context menu of a QLineEdit to include more actions.
- '''
-
- def contextMenuEvent(self, event):
- menu = self.createStandardContextMenu()
- menu.addSeparator()
-
- action_open_editor = menu.addAction(_('Open Editor'))
-
- self.connect(action_open_editor, SIGNAL('triggered()'), self.open_editor)
- menu.exec_(event.globalPos())
-
- def open_editor(self):
- t = TemplateDialog(self, self.text())
- if t.exec_():
- self.setText(t.textbox.toPlainText())
-
class ConfigWidget(ConfigWidgetBase, Ui_Form):
def genesis(self, gui):
@@ -107,7 +87,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.source_widgets = []
self.dest_widgets = []
for i in range(0, len(self.dest_fields)-1):
- w = LineEditWithTextBox(self)
+ w = TemplateLineEditor(self)
self.source_widgets.append(w)
self.fields_layout.addWidget(w, 5+i, 0, 1, 1)
w = QComboBox(self)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 9a740a08b7..819ac2cd24 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -211,6 +211,10 @@ class LibraryDatabase2(LibraryDatabase, SchemaUpgrade, CustomColumns):
defs = self.prefs.defaults
defs['gui_restriction'] = defs['cs_restriction'] = ''
defs['categories_using_hierarchy'] = []
+ self.column_color_count = 5
+ for i in range(1,self.column_color_count+1):
+ defs['column_color_name_'+str(i)] = ''
+ defs['column_color_template_'+str(i)] = ''
# Migrate the bool tristate tweak
defs['bools_are_tristate'] = \
diff --git a/src/calibre/manual/template_lang.rst b/src/calibre/manual/template_lang.rst
index 0fd396fb64..69c77e5bfd 100644
--- a/src/calibre/manual/template_lang.rst
+++ b/src/calibre/manual/template_lang.rst
@@ -123,7 +123,8 @@ The functions available are:
* ``contains(pattern, text if match, text if not match`` -- checks if field contains matches for the regular expression `pattern`. Returns `text if match` if matches are found, otherwise it returns `text if no match`.
* ``count(separator)`` -- interprets the value as a list of items separated by `separator`, returning the number of items in the list. Most lists use a comma as the separator, but authors uses an ampersand. Examples: `{tags:count(,)}`, `{authors:count(&)}`
* ``ifempty(text)`` -- if the field is not empty, return the value of the field. Otherwise return `text`.
- * ``list_item(index, separator)`` -- interpret the value as a list of items separated by `separator`, returning the `index`th item. The first item is number zero. The last item can be returned using `list_item(-1,separator)`. If the item is not in the list, then the empty value is returned. The separator has the same meaning as in the `count` function.
+ * ``in_list(separator, pattern, found_val, not_found_val)`` -- interpret the field as a list of items separated by `separator`, comparing the `pattern` against each value in the list. If the pattern matches a value, return `found_val`, otherwise return `not_found_val`.
+ * ``list_item(index, separator)`` -- interpret the field as a list of items separated by `separator`, returning the `index`th item. The first item is number zero. The last item can be returned using `list_item(-1,separator)`. If the item is not in the list, then the empty value is returned. The separator has the same meaning as in the `count` function.
* ``re(pattern, replacement)`` -- return the field after applying the regular expression. All instances of `pattern` are replaced with `replacement`. As in all of |app|, these are python-compatible regular expressions.
* ``shorten(left chars, middle text, right chars)`` -- Return a shortened version of the field, consisting of `left chars` characters from the beginning of the field, followed by `middle text`, followed by `right chars` characters from the end of the string. `Left chars` and `right chars` must be integers. For example, assume the title of the book is `Ancient English Laws in the Times of Ivanhoe`, and you want it to fit in a space of at most 15 characters. If you use ``{title:shorten(9,-,5)}``, the result will be `Ancient E-nhoe`. If the field's length is less than ``left chars`` + ``right chars`` + the length of ``middle text``, then the field will be used intact. For example, the title `The Dome` would not be changed.
* ``switch(pattern, value, pattern, value, ..., else_value)`` -- for each ``pattern, value`` pair, checks if the field matches the regular expression ``pattern`` and if so, returns that ``value``. If no ``pattern`` matches, then ``else_value`` is returned. You can have as many ``pattern, value`` pairs as you want.
@@ -234,6 +235,7 @@ The following functions are available in addition to those described in single-f
* ``cmp(x, y, lt, eq, gt)`` -- compares x and y after converting both to numbers. Returns ``lt`` if x < y. Returns ``eq`` if x == y. Otherwise returns ``gt``.
* ``divide(x, y)`` -- returns x / y. Throws an exception if either x or y are not numbers.
* ``field(name)`` -- returns the metadata field named by ``name``.
+ * ``first_non_empty(value, value, ...) -- returns the first value that is not empty. If all values are empty, then the empty value is returned. You can have as many values as you want.
* ``format_date(x, date_format)`` -- format_date(val, format_string) -- format the value, which must be a date field, using the format_string, returning a string. The formatting codes are::
d : the day as number without a leading zero (1 to 31)
diff --git a/src/calibre/utils/formatter_functions.py b/src/calibre/utils/formatter_functions.py
index aa8e4fb3a3..c53277f3ce 100644
--- a/src/calibre/utils/formatter_functions.py
+++ b/src/calibre/utils/formatter_functions.py
@@ -327,6 +327,22 @@ class BuiltinSwitch(BuiltinFormatterFunction):
return args[i+1]
i += 2
+class BuiltinInList(BuiltinFormatterFunction):
+ name = 'in_list'
+ arg_count = 5
+ doc = _('in_list(val, separator, pattern, found_val, not_found_val) -- '
+ 'treat val as a list of items separated by separator, '
+ 'comparing the pattern against each value in the list. If the '
+ 'pattern matches a value, return found_val, otherwise return '
+ 'not_found_val.')
+
+ def evaluate(self, formatter, kwargs, mi, locals, val, sep, pat, fv, nfv):
+ l = [v.strip() for v in val.split(sep) if v.strip()]
+ for v in l:
+ if re.search(pat, v):
+ return fv
+ return nfv
+
class BuiltinRe(BuiltinFormatterFunction):
name = 're'
arg_count = 3
@@ -562,6 +578,22 @@ class BuiltinBooksize(BuiltinFormatterFunction):
pass
return ''
+class BuiltinFirstNonEmpty(BuiltinFormatterFunction):
+ name = 'first_non_empty'
+ arg_count = -1
+ doc = _('first_non_empty(value, value, ...) -- '
+ 'returns the first value that is not empty. If all values are '
+ 'empty, then the empty value is returned.'
+ 'You can have as many values as you want.')
+
+ def evaluate(self, formatter, kwargs, mi, locals, *args):
+ i = 0
+ while i < len(args):
+ if args[i]:
+ return args[i]
+ i += 1
+ return ''
+
builtin_add = BuiltinAdd()
builtin_assign = BuiltinAssign()
builtin_booksize = BuiltinBooksize()
@@ -571,9 +603,11 @@ builtin_contains = BuiltinContains()
builtin_count = BuiltinCount()
builtin_divide = BuiltinDivide()
builtin_eval = BuiltinEval()
-builtin_format_date = BuiltinFormat_date()
+builtin_first_non_empty = BuiltinFirstNonEmpty()
builtin_field = BuiltinField()
+builtin_format_date = BuiltinFormat_date()
builtin_ifempty = BuiltinIfempty()
+builtin_in_list = BuiltinInList()
builtin_list_item = BuiltinListitem()
builtin_lookup = BuiltinLookup()
builtin_lowercase = BuiltinLowercase()