diff --git a/src/calibre/ebooks/metadata/book/render.py b/src/calibre/ebooks/metadata/book/render.py
index 26ca442890..e0750a301a 100644
--- a/src/calibre/ebooks/metadata/book/render.py
+++ b/src/calibre/ebooks/metadata/book/render.py
@@ -14,7 +14,7 @@ from calibre import prepare_string_for_xml, force_unicode
from calibre.ebooks.metadata import fmt_sidx
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
from calibre.constants import filesystem_encoding
-from calibre.library.comments import comments_to_html
+from calibre.library.comments import comments_to_html, markdown
from calibre.utils.icu import sort_key
from calibre.utils.formatter import EvalFormatter
from calibre.utils.date import is_date_undefined
@@ -83,13 +83,24 @@ def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=
if not name:
name = field
name += ':'
+ disp = metadata['display']
if metadata['datatype'] == 'comments' or field == 'comments':
val = getattr(mi, field)
if val:
- val = comments_to_html(force_unicode(val))
- if metadata['display'].get('show_heading'):
- val = '
%s' % (p(name), val)
- comment_fields.append('' % (field.replace('#', '_'), val))
+ ctype = disp.get('interpret_as') or 'html'
+ val = force_unicode(val)
+ if ctype == 'short-text':
+ ans.append((field, row % (name, p(val))))
+ else:
+ if ctype == 'long-text':
+ val = '%s
' % p(val)
+ elif ctype == 'markdown':
+ val = markdown(val)
+ else:
+ val = comments_to_html(val)
+ if disp.get('show_heading'):
+ val = '%s' % (p(name), val)
+ comment_fields.append('' % (field.replace('#', '_'), val))
elif metadata['datatype'] == 'rating':
val = getattr(mi, field)
if val:
@@ -102,7 +113,7 @@ def mi_to_html(mi, field_list=None, default_author_link=None, use_roman_numbers=
val = getattr(mi, field)
if val:
val = force_unicode(val)
- if metadata['display'].get('contains_html', False):
+ if disp.get('contains_html', False):
ans.append((field, row % (name, comments_to_html(val))))
else:
if not metadata['is_multiple']:
diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py
index 2f51bad9a2..9a5bfd1a77 100644
--- a/src/calibre/gui2/preferences/create_custom_column.py
+++ b/src/calibre/gui2/preferences/create_custom_column.py
@@ -82,6 +82,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
'is_multiple':True
},
)))
+ column_types_map = {k['datatype']:idx for idx, k in column_types.iteritems()}
def __init__(self, parent, current_row, current_key, standard_colheads, standard_colnames):
QDialog.__init__(self, parent)
@@ -165,6 +166,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
self.format_box.setText(c['display'].get('number_format', ''))
elif ct == 'comments':
self.show_comments_heading.setChecked(c['display'].get('show_heading', False))
+ idx = max(0, self.comments_type.findData(c['display'].get('interpret_as', 'html')))
+ self.comments_type.setCurrentIndex(idx)
self.datatype_changed()
if ct in ['text', 'composite', 'enumeration']:
self.use_decorations.setChecked(c['display'].get('use_decorations', False))
@@ -177,12 +180,13 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
def shortcut_activated(self, url): # {{{
which = unicode(url).split(':')[-1]
self.column_type_box.setCurrentIndex({
- 'yesno': 9,
- 'tags' : 1,
- 'series': 3,
- 'rating': 8,
- 'people': 1,
- }.get(which, 10))
+ 'yesno': self.column_types_map['bool'],
+ 'tags' : self.column_types_map['*text'],
+ 'series': self.column_types_map['series'],
+ 'rating': self.column_types_map['rating'],
+ 'people': self.column_types_map['*text'],
+ 'text': self.column_types_map['comments'],
+ }.get(which, self.column_types_map['composite']))
self.column_name_box.setText(which)
self.column_heading_box.setText({
'isbn':'ISBN',
@@ -191,7 +195,9 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
'tags': _('My Tags'),
'series': _('My Series'),
'rating': _('My Rating'),
- 'people': _('People')}[which])
+ 'people': _('People'),
+ 'text': _('My Title'),
+ }[which])
self.is_names.setChecked(which == 'people')
if self.composite_box.isVisible():
self.composite_box.setText(
@@ -200,6 +206,9 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
'formats': "{:'approximate_formats()'}",
}[which])
self.composite_sort_by.setCurrentIndex(0)
+ if which == 'text':
+ self.show_comments_heading.setChecked(True)
+ self.comments_type.setCurrentIndex(self.comments_type.findData('short-text'))
# }}}
def setup_ui(self): # {{{
@@ -215,7 +224,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
for col, name in [('isbn', _('ISBN')), ('formats', _('Formats')),
('yesno', _('Yes/No')),
('tags', _('Tags')), ('series', _('Series')), ('rating',
- _('Rating')), ('people', _("People's names"))]:
+ _('Rating')), ('people', _("Names")), ('text', _('Short text'))]:
text += ' %s,'%(col, name)
text = text[:-1]
s.setText(text)
@@ -303,6 +312,18 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
sch.setToolTip(_(
'Choose whether to show the heading for this column in the Book Details Panel'))
add_row(None, sch)
+ self.comments_type = ct = QComboBox(self)
+ for k, text in (
+ ('html', 'HTML'),
+ ('short-text', _('Short text, like a title')),
+ ('long-text', _('Plain text')),
+ ('markdown', _('Plain text formatted using markdown'))
+ ):
+ ct.addItem(text, k)
+ ct.setToolTip(_('Choose how the data in this column is interpreted.\n'
+ 'This control how the data is displayed in the Book Details panel\n'
+ 'and how it is edited.'))
+ self.comments_type_label = add_row(_('Interpret this column as:') + ' ', ct)
# Values for enum type
l = QGridLayout()
@@ -394,7 +415,10 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
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')
- self.show_comments_heading.setVisible(col_type == 'comments')
+ is_comments = col_type == 'comments'
+ self.show_comments_heading.setVisible(is_comments)
+ self.comments_type.setVisible(is_comments)
+ self.comments_type_label.setVisible(is_comments)
def accept(self):
col = unicode(self.column_name_box.text()).strip()
@@ -491,6 +515,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
display_dict = {'number_format': None}
elif col_type == 'comments':
display_dict['show_heading'] = bool(self.show_comments_heading.isChecked())
+ display_dict['interpret_as'] = type(u'')(self.comments_type.currentData())
if col_type in ['text', 'composite', 'enumeration'] and not is_multiple:
display_dict['use_decorations'] = self.use_decorations.checkState()
diff --git a/src/calibre/library/comments.py b/src/calibre/library/comments.py
index e9a4a50410..b3e26472a4 100644
--- a/src/calibre/library/comments.py
+++ b/src/calibre/library/comments.py
@@ -130,6 +130,14 @@ def comments_to_html(comments):
return result.renderContents(encoding=None)
+def markdown(val):
+ try:
+ md = markdown.Markdown
+ except AttributeError:
+ from calibre.ebooks.markdown import Markdown
+ md = markdown.Markdown = Markdown()
+ return md.convert(val)
+
def merge_comments(one, two):
return comments_to_html(one) + '\n\n' + comments_to_html(two)
diff --git a/src/calibre/srv/metadata.py b/src/calibre/srv/metadata.py
index 8d5ad7f87f..e1c411a582 100644
--- a/src/calibre/srv/metadata.py
+++ b/src/calibre/srv/metadata.py
@@ -12,6 +12,7 @@ from functools import partial
from threading import Lock
from urllib import quote
+from calibre import prepare_string_for_xml
from calibre.constants import config_dir
from calibre.db.categories import Tag
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
@@ -21,7 +22,7 @@ from calibre.utils.formatter import EvalFormatter
from calibre.utils.file_type_icons import EXT_MAP
from calibre.utils.icu import collation_order
from calibre.utils.localization import calibre_langcode_to_name
-from calibre.library.comments import comments_to_html
+from calibre.library.comments import comments_to_html, markdown
from calibre.library.field_metadata import category_icon_map
IGNORED_FIELDS = frozenset('cover ondevice path marked au_map size'.split())
@@ -49,7 +50,15 @@ def add_field(field, db, book_id, ans, field_metadata):
if val is None:
return
elif datatype == 'comments' or field == 'comments':
- val = comments_to_html(val)
+ ctype = field_metadata.get('display', {}).get('interpret_as', 'html')
+ if ctype == 'markdown':
+ val = markdown(val)
+ elif ctype == 'short-text':
+ pass
+ elif ctype == 'long-text':
+ val = '%s
' % prepare_string_for_xml(val)
+ else:
+ val = comments_to_html(val)
elif datatype == 'composite' and field_metadata['display'].get('contains_html'):
val = comments_to_html(val)
ans[field] = val
diff --git a/src/pyj/book_list/book_details.pyj b/src/pyj/book_list/book_details.pyj
index dce565d437..cc8225e833 100644
--- a/src/pyj/book_list/book_details.pyj
+++ b/src/pyj/book_list/book_details.pyj
@@ -201,7 +201,10 @@ def render_metadata(mi, interface_data, table, field_list=None):
datatype = fm.datatype
val = mi[field]
if field is 'comments' or datatype is 'comments':
- comments[field] = val
+ if fm.display?.interpret_as is 'short-text':
+ add_row(name, val)
+ else:
+ comments[field] = val
return
func = None
if datatype is 'composite':
@@ -249,9 +252,13 @@ def render_metadata(mi, interface_data, table, field_list=None):
traceback.print_exc()
for i, field in enumerate(sorted(comments)):
+ fm = interface_data.field_metadata[field]
comment = comments[field]
div = E.div()
div.innerHTML = comment
+ if fm.display?.show_heading:
+ name = fm.name or field
+ div.insertBefore(E.h3(name), div.firstChild or None)
table.parentNode.appendChild(div)
if i is 0:
div.style.marginTop = '2ex'