mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Allow creating a custom title-like (short text) column (Preferences->Add your own columns->Short text)
Also, allow using Markdown for formatting in custom comments like columns.
This commit is contained in:
parent
0b3125e4d8
commit
a3bb5e2cae
@ -14,7 +14,7 @@ from calibre import prepare_string_for_xml, force_unicode
|
|||||||
from calibre.ebooks.metadata import fmt_sidx
|
from calibre.ebooks.metadata import fmt_sidx
|
||||||
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
|
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
|
||||||
from calibre.constants import filesystem_encoding
|
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.icu import sort_key
|
||||||
from calibre.utils.formatter import EvalFormatter
|
from calibre.utils.formatter import EvalFormatter
|
||||||
from calibre.utils.date import is_date_undefined
|
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:
|
if not name:
|
||||||
name = field
|
name = field
|
||||||
name += ':'
|
name += ':'
|
||||||
|
disp = metadata['display']
|
||||||
if metadata['datatype'] == 'comments' or field == 'comments':
|
if metadata['datatype'] == 'comments' or field == 'comments':
|
||||||
val = getattr(mi, field)
|
val = getattr(mi, field)
|
||||||
if val:
|
if val:
|
||||||
val = comments_to_html(force_unicode(val))
|
ctype = disp.get('interpret_as') or 'html'
|
||||||
if metadata['display'].get('show_heading'):
|
val = force_unicode(val)
|
||||||
val = '<h3 class="comments-heading">%s</h3>%s' % (p(name), val)
|
if ctype == 'short-text':
|
||||||
comment_fields.append('<div id="%s" class="comments">%s</div>' % (field.replace('#', '_'), val))
|
ans.append((field, row % (name, p(val))))
|
||||||
|
else:
|
||||||
|
if ctype == 'long-text':
|
||||||
|
val = '<pre>%s</pre>' % p(val)
|
||||||
|
elif ctype == 'markdown':
|
||||||
|
val = markdown(val)
|
||||||
|
else:
|
||||||
|
val = comments_to_html(val)
|
||||||
|
if disp.get('show_heading'):
|
||||||
|
val = '<h3 class="comments-heading">%s</h3>%s' % (p(name), val)
|
||||||
|
comment_fields.append('<div id="%s" class="comments">%s</div>' % (field.replace('#', '_'), val))
|
||||||
elif metadata['datatype'] == 'rating':
|
elif metadata['datatype'] == 'rating':
|
||||||
val = getattr(mi, field)
|
val = getattr(mi, field)
|
||||||
if val:
|
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)
|
val = getattr(mi, field)
|
||||||
if val:
|
if val:
|
||||||
val = force_unicode(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))))
|
ans.append((field, row % (name, comments_to_html(val))))
|
||||||
else:
|
else:
|
||||||
if not metadata['is_multiple']:
|
if not metadata['is_multiple']:
|
||||||
|
@ -82,6 +82,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
'is_multiple':True
|
'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):
|
def __init__(self, parent, current_row, current_key, standard_colheads, standard_colnames):
|
||||||
QDialog.__init__(self, parent)
|
QDialog.__init__(self, parent)
|
||||||
@ -165,6 +166,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
self.format_box.setText(c['display'].get('number_format', ''))
|
self.format_box.setText(c['display'].get('number_format', ''))
|
||||||
elif ct == 'comments':
|
elif ct == 'comments':
|
||||||
self.show_comments_heading.setChecked(c['display'].get('show_heading', False))
|
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()
|
self.datatype_changed()
|
||||||
if ct in ['text', 'composite', 'enumeration']:
|
if ct in ['text', 'composite', 'enumeration']:
|
||||||
self.use_decorations.setChecked(c['display'].get('use_decorations', False))
|
self.use_decorations.setChecked(c['display'].get('use_decorations', False))
|
||||||
@ -177,12 +180,13 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
def shortcut_activated(self, url): # {{{
|
def shortcut_activated(self, url): # {{{
|
||||||
which = unicode(url).split(':')[-1]
|
which = unicode(url).split(':')[-1]
|
||||||
self.column_type_box.setCurrentIndex({
|
self.column_type_box.setCurrentIndex({
|
||||||
'yesno': 9,
|
'yesno': self.column_types_map['bool'],
|
||||||
'tags' : 1,
|
'tags' : self.column_types_map['*text'],
|
||||||
'series': 3,
|
'series': self.column_types_map['series'],
|
||||||
'rating': 8,
|
'rating': self.column_types_map['rating'],
|
||||||
'people': 1,
|
'people': self.column_types_map['*text'],
|
||||||
}.get(which, 10))
|
'text': self.column_types_map['comments'],
|
||||||
|
}.get(which, self.column_types_map['composite']))
|
||||||
self.column_name_box.setText(which)
|
self.column_name_box.setText(which)
|
||||||
self.column_heading_box.setText({
|
self.column_heading_box.setText({
|
||||||
'isbn':'ISBN',
|
'isbn':'ISBN',
|
||||||
@ -191,7 +195,9 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
'tags': _('My Tags'),
|
'tags': _('My Tags'),
|
||||||
'series': _('My Series'),
|
'series': _('My Series'),
|
||||||
'rating': _('My Rating'),
|
'rating': _('My Rating'),
|
||||||
'people': _('People')}[which])
|
'people': _('People'),
|
||||||
|
'text': _('My Title'),
|
||||||
|
}[which])
|
||||||
self.is_names.setChecked(which == 'people')
|
self.is_names.setChecked(which == 'people')
|
||||||
if self.composite_box.isVisible():
|
if self.composite_box.isVisible():
|
||||||
self.composite_box.setText(
|
self.composite_box.setText(
|
||||||
@ -200,6 +206,9 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
'formats': "{:'approximate_formats()'}",
|
'formats': "{:'approximate_formats()'}",
|
||||||
}[which])
|
}[which])
|
||||||
self.composite_sort_by.setCurrentIndex(0)
|
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): # {{{
|
def setup_ui(self): # {{{
|
||||||
@ -215,7 +224,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
for col, name in [('isbn', _('ISBN')), ('formats', _('Formats')),
|
for col, name in [('isbn', _('ISBN')), ('formats', _('Formats')),
|
||||||
('yesno', _('Yes/No')),
|
('yesno', _('Yes/No')),
|
||||||
('tags', _('Tags')), ('series', _('Series')), ('rating',
|
('tags', _('Tags')), ('series', _('Series')), ('rating',
|
||||||
_('Rating')), ('people', _("People's names"))]:
|
_('Rating')), ('people', _("Names")), ('text', _('Short text'))]:
|
||||||
text += ' <a href="col:%s">%s</a>,'%(col, name)
|
text += ' <a href="col:%s">%s</a>,'%(col, name)
|
||||||
text = text[:-1]
|
text = text[:-1]
|
||||||
s.setText(text)
|
s.setText(text)
|
||||||
@ -303,6 +312,18 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
sch.setToolTip(_(
|
sch.setToolTip(_(
|
||||||
'Choose whether to show the heading for this column in the Book Details Panel'))
|
'Choose whether to show the heading for this column in the Book Details Panel'))
|
||||||
add_row(None, sch)
|
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
|
# Values for enum type
|
||||||
l = QGridLayout()
|
l = QGridLayout()
|
||||||
@ -394,7 +415,10 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration')
|
getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration')
|
||||||
self.use_decorations.setVisible(col_type in ['text', 'composite', 'enumeration'])
|
self.use_decorations.setVisible(col_type in ['text', 'composite', 'enumeration'])
|
||||||
self.is_names.setVisible(col_type == '*text')
|
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):
|
def accept(self):
|
||||||
col = unicode(self.column_name_box.text()).strip()
|
col = unicode(self.column_name_box.text()).strip()
|
||||||
@ -491,6 +515,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
display_dict = {'number_format': None}
|
display_dict = {'number_format': None}
|
||||||
elif col_type == 'comments':
|
elif col_type == 'comments':
|
||||||
display_dict['show_heading'] = bool(self.show_comments_heading.isChecked())
|
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:
|
if col_type in ['text', 'composite', 'enumeration'] and not is_multiple:
|
||||||
display_dict['use_decorations'] = self.use_decorations.checkState()
|
display_dict['use_decorations'] = self.use_decorations.checkState()
|
||||||
|
@ -130,6 +130,14 @@ def comments_to_html(comments):
|
|||||||
|
|
||||||
return result.renderContents(encoding=None)
|
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):
|
def merge_comments(one, two):
|
||||||
return comments_to_html(one) + '\n\n' + comments_to_html(two)
|
return comments_to_html(one) + '\n\n' + comments_to_html(two)
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ from functools import partial
|
|||||||
from threading import Lock
|
from threading import Lock
|
||||||
from urllib import quote
|
from urllib import quote
|
||||||
|
|
||||||
|
from calibre import prepare_string_for_xml
|
||||||
from calibre.constants import config_dir
|
from calibre.constants import config_dir
|
||||||
from calibre.db.categories import Tag
|
from calibre.db.categories import Tag
|
||||||
from calibre.ebooks.metadata.sources.identify import urls_from_identifiers
|
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.file_type_icons import EXT_MAP
|
||||||
from calibre.utils.icu import collation_order
|
from calibre.utils.icu import collation_order
|
||||||
from calibre.utils.localization import calibre_langcode_to_name
|
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
|
from calibre.library.field_metadata import category_icon_map
|
||||||
|
|
||||||
IGNORED_FIELDS = frozenset('cover ondevice path marked au_map size'.split())
|
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:
|
if val is None:
|
||||||
return
|
return
|
||||||
elif datatype == 'comments' or field == 'comments':
|
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 = '<pre>%s</pre>' % prepare_string_for_xml(val)
|
||||||
|
else:
|
||||||
|
val = comments_to_html(val)
|
||||||
elif datatype == 'composite' and field_metadata['display'].get('contains_html'):
|
elif datatype == 'composite' and field_metadata['display'].get('contains_html'):
|
||||||
val = comments_to_html(val)
|
val = comments_to_html(val)
|
||||||
ans[field] = val
|
ans[field] = val
|
||||||
|
@ -201,7 +201,10 @@ def render_metadata(mi, interface_data, table, field_list=None):
|
|||||||
datatype = fm.datatype
|
datatype = fm.datatype
|
||||||
val = mi[field]
|
val = mi[field]
|
||||||
if field is 'comments' or datatype is 'comments':
|
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
|
return
|
||||||
func = None
|
func = None
|
||||||
if datatype is 'composite':
|
if datatype is 'composite':
|
||||||
@ -249,9 +252,13 @@ def render_metadata(mi, interface_data, table, field_list=None):
|
|||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
for i, field in enumerate(sorted(comments)):
|
for i, field in enumerate(sorted(comments)):
|
||||||
|
fm = interface_data.field_metadata[field]
|
||||||
comment = comments[field]
|
comment = comments[field]
|
||||||
div = E.div()
|
div = E.div()
|
||||||
div.innerHTML = comment
|
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)
|
table.parentNode.appendChild(div)
|
||||||
if i is 0:
|
if i is 0:
|
||||||
div.style.marginTop = '2ex'
|
div.style.marginTop = '2ex'
|
||||||
|
Loading…
x
Reference in New Issue
Block a user