mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
A new custom column type: Enumeration. This column can take one of a user defined set of values
This commit is contained in:
commit
8c2697c90b
@ -15,7 +15,7 @@ from PyQt4.Qt import QComboBox, QLabel, QSpinBox, QDoubleSpinBox, QDateEdit, \
|
|||||||
|
|
||||||
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 import UNDEFINED_QDATE
|
from calibre.gui2 import UNDEFINED_QDATE, error_dialog
|
||||||
from calibre.utils.config import tweaks
|
from calibre.utils.config import tweaks
|
||||||
|
|
||||||
class Base(object):
|
class Base(object):
|
||||||
@ -310,6 +310,43 @@ class Series(Base):
|
|||||||
self.db.set_custom(book_id, val, extra=s_index,
|
self.db.set_custom(book_id, val, extra=s_index,
|
||||||
num=self.col_id, notify=notify, commit=False)
|
num=self.col_id, notify=notify, commit=False)
|
||||||
|
|
||||||
|
class Enumeration(Base):
|
||||||
|
|
||||||
|
def setup_ui(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
|
||||||
|
QComboBox(parent)]
|
||||||
|
w = self.widgets[1]
|
||||||
|
vals = self.col_metadata['display']['enum_values']
|
||||||
|
for v in vals:
|
||||||
|
w.addItem(v)
|
||||||
|
|
||||||
|
def initialize(self, book_id):
|
||||||
|
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
||||||
|
if val is None:
|
||||||
|
# This really shouldn't happen
|
||||||
|
val = self.col_metadata['display']['enum_values'][0]
|
||||||
|
self.initial_val = val
|
||||||
|
val = self.normalize_db_val(val)
|
||||||
|
idx = self.widgets[1].findText(val)
|
||||||
|
if idx < 0:
|
||||||
|
error_dialog(self.parent, '',
|
||||||
|
_('The enumeration "{0}" contains an invalid value '
|
||||||
|
'that will be set to the default').format(
|
||||||
|
self.col_metadata['name']),
|
||||||
|
show=True, show_copy_button=False)
|
||||||
|
|
||||||
|
idx = 0
|
||||||
|
self.widgets[1].setCurrentIndex(idx)
|
||||||
|
|
||||||
|
def setter(self, val):
|
||||||
|
if val is None:
|
||||||
|
val = ''
|
||||||
|
self.widgets[1].setCurrentIndex(self.widgets[1].findText(val))
|
||||||
|
|
||||||
|
def getter(self):
|
||||||
|
return unicode(self.widgets[1].currentText())
|
||||||
|
|
||||||
widgets = {
|
widgets = {
|
||||||
'bool' : Bool,
|
'bool' : Bool,
|
||||||
'rating' : Rating,
|
'rating' : Rating,
|
||||||
@ -319,6 +356,7 @@ widgets = {
|
|||||||
'text' : Text,
|
'text' : Text,
|
||||||
'comments': Comments,
|
'comments': Comments,
|
||||||
'series': Series,
|
'series': Series,
|
||||||
|
'enumeration': Enumeration
|
||||||
}
|
}
|
||||||
|
|
||||||
def field_sort(y, z, x=None):
|
def field_sort(y, z, x=None):
|
||||||
@ -551,6 +589,63 @@ class BulkSeries(BulkBase):
|
|||||||
self.db.set_custom_bulk(book_ids, val, extras=extras,
|
self.db.set_custom_bulk(book_ids, val, extras=extras,
|
||||||
num=self.col_id, notify=notify)
|
num=self.col_id, notify=notify)
|
||||||
|
|
||||||
|
class BulkEnumeration(BulkBase, Enumeration):
|
||||||
|
|
||||||
|
def get_initial_value(self, book_ids):
|
||||||
|
value = None
|
||||||
|
ret_value = None
|
||||||
|
dialog_shown = False
|
||||||
|
for book_id in book_ids:
|
||||||
|
val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
|
||||||
|
if val not in self.col_metadata['display']['enum_values']:
|
||||||
|
if not dialog_shown:
|
||||||
|
error_dialog(self.parent, '',
|
||||||
|
_('The enumeration "{0}" contains invalid values '
|
||||||
|
'that will not appear in the list').format(
|
||||||
|
self.col_metadata['name']),
|
||||||
|
show=True, show_copy_button=False)
|
||||||
|
dialog_shown = True
|
||||||
|
ret_value = ' nochange '
|
||||||
|
elif value is not None and value != val:
|
||||||
|
ret_value = ' nochange '
|
||||||
|
value = val
|
||||||
|
if ret_value is None:
|
||||||
|
return value
|
||||||
|
return ret_value
|
||||||
|
|
||||||
|
def setup_ui(self, parent):
|
||||||
|
self.parent = parent
|
||||||
|
self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
|
||||||
|
QComboBox(parent)]
|
||||||
|
w = self.widgets[1]
|
||||||
|
vals = self.col_metadata['display']['enum_values']
|
||||||
|
w.addItem('Do Not Change')
|
||||||
|
for v in vals:
|
||||||
|
w.addItem(v)
|
||||||
|
|
||||||
|
def getter(self):
|
||||||
|
if self.widgets[1].currentIndex() == 0:
|
||||||
|
return ' nochange '
|
||||||
|
return unicode(self.widgets[1].currentText())
|
||||||
|
|
||||||
|
def setter(self, val):
|
||||||
|
if val == ' nochange ':
|
||||||
|
self.widgets[1].setCurrentIndex(0)
|
||||||
|
else:
|
||||||
|
self.widgets[1].setCurrentIndex(self.widgets[1].findText(val))
|
||||||
|
|
||||||
|
def commit(self, book_ids, notify=False):
|
||||||
|
val = self.gui_val
|
||||||
|
val = self.normalize_ui_val(val)
|
||||||
|
if val != self.initial_val and val != ' nochange ':
|
||||||
|
self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify)
|
||||||
|
|
||||||
|
def normalize_db_val(self, val):
|
||||||
|
if val is None:
|
||||||
|
# this really shouldn't happen
|
||||||
|
val = self.col_metadata['display']['enum_values'][0]
|
||||||
|
return val
|
||||||
|
|
||||||
class RemoveTags(QWidget):
|
class RemoveTags(QWidget):
|
||||||
|
|
||||||
def __init__(self, parent, values):
|
def __init__(self, parent, values):
|
||||||
@ -656,4 +751,5 @@ bulk_widgets = {
|
|||||||
'datetime': BulkDateTime,
|
'datetime': BulkDateTime,
|
||||||
'text' : BulkText,
|
'text' : BulkText,
|
||||||
'series': BulkSeries,
|
'series': BulkSeries,
|
||||||
|
'enumeration': BulkEnumeration,
|
||||||
}
|
}
|
||||||
|
@ -255,7 +255,7 @@ class MetadataBulkDialog(QDialog, Ui_MetadataBulkDialog):
|
|||||||
fm = self.db.field_metadata
|
fm = self.db.field_metadata
|
||||||
for f in fm:
|
for f in fm:
|
||||||
if (f in ['author_sort'] or
|
if (f in ['author_sort'] or
|
||||||
(fm[f]['datatype'] in ['text', 'series']
|
(fm[f]['datatype'] in ['text', 'series', 'enumeration']
|
||||||
and fm[f].get('search_terms', None)
|
and fm[f].get('search_terms', None)
|
||||||
and f not in ['formats', 'ondevice', 'sort'])):
|
and f not in ['formats', 'ondevice', 'sort'])):
|
||||||
self.all_fields.append(f)
|
self.all_fields.append(f)
|
||||||
|
@ -254,6 +254,44 @@ class CcTextDelegate(QStyledItemDelegate): # {{{
|
|||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
|
class CcEnumDelegate(QStyledItemDelegate): # {{{
|
||||||
|
'''
|
||||||
|
Delegate for text/int/float data.
|
||||||
|
'''
|
||||||
|
|
||||||
|
def createEditor(self, parent, option, index):
|
||||||
|
m = index.model()
|
||||||
|
col = m.column_map[index.column()]
|
||||||
|
editor = QComboBox(parent)
|
||||||
|
for v in m.custom_columns[col]['display']['enum_values']:
|
||||||
|
editor.addItem(v)
|
||||||
|
return editor
|
||||||
|
|
||||||
|
def setModelData(self, editor, model, index):
|
||||||
|
val = unicode(editor.currentText())
|
||||||
|
m = index.model()
|
||||||
|
col = m.column_map[index.column()]
|
||||||
|
if val not in m.custom_columns[col]['display']['enum_values']:
|
||||||
|
# This shouldn't happen ...
|
||||||
|
print 'shouldnt happen'
|
||||||
|
val = m.custom_columns[col]['display']['enum_values'][0]
|
||||||
|
model.setData(index, QVariant(val), Qt.EditRole)
|
||||||
|
|
||||||
|
def setEditorData(self, editor, index):
|
||||||
|
m = index.model()
|
||||||
|
val = m.db.data[index.row()][m.custom_columns[m.column_map[index.column()]]['rec_index']]
|
||||||
|
if val is None:
|
||||||
|
# This shouldn't happen
|
||||||
|
m = index.model()
|
||||||
|
col = m.column_map[index.column()]
|
||||||
|
val = m.custom_columns[col]['display']['enum_values'][0]
|
||||||
|
idx = editor.findText(val)
|
||||||
|
if idx < 0:
|
||||||
|
editor.setCurrentIndex(0)
|
||||||
|
else:
|
||||||
|
editor.setCurrentIndex(idx)
|
||||||
|
# }}}
|
||||||
|
|
||||||
class CcCommentsDelegate(QStyledItemDelegate): # {{{
|
class CcCommentsDelegate(QStyledItemDelegate): # {{{
|
||||||
'''
|
'''
|
||||||
Delegate for comments data.
|
Delegate for comments data.
|
||||||
|
@ -634,7 +634,7 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
for col in self.custom_columns:
|
for col in self.custom_columns:
|
||||||
idx = self.custom_columns[col]['rec_index']
|
idx = self.custom_columns[col]['rec_index']
|
||||||
datatype = self.custom_columns[col]['datatype']
|
datatype = self.custom_columns[col]['datatype']
|
||||||
if datatype in ('text', 'comments', 'composite'):
|
if datatype in ('text', 'comments', 'composite', 'enumeration'):
|
||||||
self.dc[col] = functools.partial(text_type, idx=idx,
|
self.dc[col] = functools.partial(text_type, idx=idx,
|
||||||
mult=self.custom_columns[col]['is_multiple'])
|
mult=self.custom_columns[col]['is_multiple'])
|
||||||
elif datatype in ('int', 'float'):
|
elif datatype in ('int', 'float'):
|
||||||
@ -722,7 +722,9 @@ class BooksModel(QAbstractTableModel): # {{{
|
|||||||
if typ in ('text', 'comments'):
|
if typ in ('text', 'comments'):
|
||||||
val = unicode(value.toString()).strip()
|
val = unicode(value.toString()).strip()
|
||||||
val = val if val else None
|
val = val if val else None
|
||||||
if typ == 'bool':
|
elif typ == 'enumeration':
|
||||||
|
val = unicode(value.toString()).strip()
|
||||||
|
elif typ == 'bool':
|
||||||
val = value.toPyObject()
|
val = value.toPyObject()
|
||||||
elif typ == 'rating':
|
elif typ == 'rating':
|
||||||
val = value.toInt()[0]
|
val = value.toInt()[0]
|
||||||
|
@ -14,7 +14,8 @@ from PyQt4.Qt import QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, \
|
|||||||
|
|
||||||
from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, \
|
from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, \
|
||||||
TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, \
|
TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, \
|
||||||
CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate
|
CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate, \
|
||||||
|
CcEnumDelegate
|
||||||
from calibre.gui2.library.models import BooksModel, DeviceBooksModel
|
from calibre.gui2.library.models import BooksModel, DeviceBooksModel
|
||||||
from calibre.utils.config import tweaks, prefs
|
from calibre.utils.config import tweaks, prefs
|
||||||
from calibre.gui2 import error_dialog, gprefs
|
from calibre.gui2 import error_dialog, gprefs
|
||||||
@ -76,6 +77,7 @@ class BooksView(QTableView): # {{{
|
|||||||
self.publisher_delegate = TextDelegate(self)
|
self.publisher_delegate = TextDelegate(self)
|
||||||
self.text_delegate = TextDelegate(self)
|
self.text_delegate = TextDelegate(self)
|
||||||
self.cc_text_delegate = CcTextDelegate(self)
|
self.cc_text_delegate = CcTextDelegate(self)
|
||||||
|
self.cc_enum_delegate = CcEnumDelegate(self)
|
||||||
self.cc_bool_delegate = CcBoolDelegate(self)
|
self.cc_bool_delegate = CcBoolDelegate(self)
|
||||||
self.cc_comments_delegate = CcCommentsDelegate(self)
|
self.cc_comments_delegate = CcCommentsDelegate(self)
|
||||||
self.cc_template_delegate = CcTemplateDelegate(self)
|
self.cc_template_delegate = CcTemplateDelegate(self)
|
||||||
@ -427,6 +429,8 @@ class BooksView(QTableView): # {{{
|
|||||||
self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate)
|
self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate)
|
||||||
elif cc['datatype'] == 'composite':
|
elif cc['datatype'] == 'composite':
|
||||||
self.setItemDelegateForColumn(cm.index(colhead), self.cc_template_delegate)
|
self.setItemDelegateForColumn(cm.index(colhead), self.cc_template_delegate)
|
||||||
|
elif cc['datatype'] == 'enumeration':
|
||||||
|
self.setItemDelegateForColumn(cm.index(colhead), self.cc_enum_delegate)
|
||||||
else:
|
else:
|
||||||
dattr = colhead+'_delegate'
|
dattr = colhead+'_delegate'
|
||||||
delegate = colhead if hasattr(self, dattr) else 'text'
|
delegate = colhead if hasattr(self, dattr) else 'text'
|
||||||
|
@ -27,18 +27,20 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
3:{'datatype':'series',
|
3:{'datatype':'series',
|
||||||
'text':_('Text column for keeping series-like information'),
|
'text':_('Text column for keeping series-like information'),
|
||||||
'is_multiple':False},
|
'is_multiple':False},
|
||||||
4:{'datatype':'datetime',
|
4:{'datatype':'enumeration',
|
||||||
|
'text':_('Text, but with a fixed set of permitted values'), 'is_multiple':False},
|
||||||
|
5:{'datatype':'datetime',
|
||||||
'text':_('Date'), 'is_multiple':False},
|
'text':_('Date'), 'is_multiple':False},
|
||||||
5:{'datatype':'float',
|
6:{'datatype':'float',
|
||||||
'text':_('Floating point numbers'), 'is_multiple':False},
|
'text':_('Floating point numbers'), 'is_multiple':False},
|
||||||
6:{'datatype':'int',
|
7:{'datatype':'int',
|
||||||
'text':_('Integers'), 'is_multiple':False},
|
'text':_('Integers'), 'is_multiple':False},
|
||||||
7:{'datatype':'rating',
|
8:{'datatype':'rating',
|
||||||
'text':_('Ratings, shown with stars'),
|
'text':_('Ratings, shown with stars'),
|
||||||
'is_multiple':False},
|
'is_multiple':False},
|
||||||
8:{'datatype':'bool',
|
9:{'datatype':'bool',
|
||||||
'text':_('Yes/No'), 'is_multiple':False},
|
'text':_('Yes/No'), 'is_multiple':False},
|
||||||
9:{'datatype':'composite',
|
10:{'datatype':'composite',
|
||||||
'text':_('Column built from other columns'), 'is_multiple':False},
|
'text':_('Column built from other columns'), 'is_multiple':False},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -59,6 +61,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
self.editing_col = editing
|
self.editing_col = editing
|
||||||
self.standard_colheads = standard_colheads
|
self.standard_colheads = standard_colheads
|
||||||
self.standard_colnames = standard_colnames
|
self.standard_colnames = standard_colnames
|
||||||
|
self.column_type_box.setMaxVisibleItems(len(self.column_types))
|
||||||
for t in self.column_types:
|
for t in self.column_types:
|
||||||
self.column_type_box.addItem(self.column_types[t]['text'])
|
self.column_type_box.addItem(self.column_types[t]['text'])
|
||||||
self.column_type_box.currentIndexChanged.connect(self.datatype_changed)
|
self.column_type_box.currentIndexChanged.connect(self.datatype_changed)
|
||||||
@ -91,6 +94,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
self.date_format_box.setText(c['display'].get('date_format', ''))
|
self.date_format_box.setText(c['display'].get('date_format', ''))
|
||||||
elif ct == 'composite':
|
elif ct == 'composite':
|
||||||
self.composite_box.setText(c['display'].get('composite_template', ''))
|
self.composite_box.setText(c['display'].get('composite_template', ''))
|
||||||
|
elif ct == 'enumeration':
|
||||||
|
self.enum_box.setText(','.join(c['display'].get('enum_values', [])))
|
||||||
self.datatype_changed()
|
self.datatype_changed()
|
||||||
self.exec_()
|
self.exec_()
|
||||||
|
|
||||||
@ -103,7 +108,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
getattr(self, 'date_format_'+x).setVisible(col_type == 'datetime')
|
getattr(self, 'date_format_'+x).setVisible(col_type == 'datetime')
|
||||||
for x in ('box', 'default_label', 'label'):
|
for x in ('box', 'default_label', 'label'):
|
||||||
getattr(self, 'composite_'+x).setVisible(col_type == 'composite')
|
getattr(self, 'composite_'+x).setVisible(col_type == 'composite')
|
||||||
|
for x in ('box', 'default_label', 'label'):
|
||||||
|
getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration')
|
||||||
|
|
||||||
def accept(self):
|
def accept(self):
|
||||||
col = unicode(self.column_name_box.text())
|
col = unicode(self.column_name_box.text())
|
||||||
@ -145,17 +151,27 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
|
|||||||
return self.simple_error('', _('The heading %s is already used')%col_heading)
|
return self.simple_error('', _('The heading %s is already used')%col_heading)
|
||||||
|
|
||||||
display_dict = {}
|
display_dict = {}
|
||||||
|
|
||||||
if col_type == 'datetime':
|
if col_type == 'datetime':
|
||||||
if self.date_format_box.text():
|
if self.date_format_box.text():
|
||||||
display_dict = {'date_format':unicode(self.date_format_box.text())}
|
display_dict = {'date_format':unicode(self.date_format_box.text())}
|
||||||
else:
|
else:
|
||||||
display_dict = {'date_format': None}
|
display_dict = {'date_format': None}
|
||||||
|
elif col_type == 'composite':
|
||||||
if col_type == 'composite':
|
|
||||||
if not self.composite_box.text():
|
if not self.composite_box.text():
|
||||||
return self.simple_error('', _('You must enter a template for'
|
return self.simple_error('', _('You must enter a template for'
|
||||||
' composite columns'))
|
' composite columns'))
|
||||||
display_dict = {'composite_template':unicode(self.composite_box.text())}
|
display_dict = {'composite_template':unicode(self.composite_box.text())}
|
||||||
|
elif col_type == 'enumeration':
|
||||||
|
if not self.enum_box.text():
|
||||||
|
return self.simple_error('', _('You must enter at least one'
|
||||||
|
' value for enumeration columns'))
|
||||||
|
l = [v.strip() for v in unicode(self.enum_box.text()).split(',')]
|
||||||
|
for i in range(0, len(l)-1):
|
||||||
|
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}
|
||||||
|
|
||||||
db = self.parent.gui.library_view.model().db
|
db = self.parent.gui.library_view.model().db
|
||||||
key = db.field_metadata.custom_field_prefix+col
|
key = db.field_metadata.custom_field_prefix+col
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>528</width>
|
<width>528</width>
|
||||||
<height>199</height>
|
<height>212</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@ -24,7 +24,7 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<item>
|
<item>
|
||||||
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,0,0">
|
<layout class="QGridLayout" name="gridLayout_2" rowstretch="0,0,0,0,0,0,0,0,0,0,0,0">
|
||||||
<property name="sizeConstraint">
|
<property name="sizeConstraint">
|
||||||
<enum>QLayout::SetDefaultConstraint</enum>
|
<enum>QLayout::SetDefaultConstraint</enum>
|
||||||
</property>
|
</property>
|
||||||
@ -56,7 +56,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="1">
|
<item row="0" column="2">
|
||||||
<widget class="QLineEdit" name="column_name_box">
|
<widget class="QLineEdit" name="column_name_box">
|
||||||
<property name="minimumSize">
|
<property name="minimumSize">
|
||||||
<size>
|
<size>
|
||||||
@ -69,7 +69,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="1" column="1">
|
<item row="1" column="2">
|
||||||
<widget class="QLineEdit" name="column_heading_box">
|
<widget class="QLineEdit" name="column_heading_box">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Column heading in the library view and category name in the tag browser</string>
|
<string>Column heading in the library view and category name in the tag browser</string>
|
||||||
@ -86,7 +86,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="1">
|
<item row="2" column="2">
|
||||||
<widget class="QComboBox" name="column_type_box">
|
<widget class="QComboBox" name="column_type_box">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
@ -105,7 +105,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="4" column="1">
|
<item row="4" column="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="date_format_box">
|
<widget class="QLineEdit" name="date_format_box">
|
||||||
@ -147,7 +147,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="5" column="1">
|
<item row="5" column="2">
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QLineEdit" name="composite_box">
|
<widget class="QLineEdit" name="composite_box">
|
||||||
@ -158,7 +158,7 @@
|
|||||||
</sizepolicy>
|
</sizepolicy>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string><p>Field template. Uses the same syntax as save templates.</string>
|
<string>Field template. Uses the same syntax as save templates.</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
@ -184,7 +184,7 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="10" column="0" colspan="3">
|
<item row="11" column="0" colspan="4">
|
||||||
<spacer name="verticalSpacer_2">
|
<spacer name="verticalSpacer_2">
|
||||||
<property name="orientation">
|
<property name="orientation">
|
||||||
<enum>Qt::Vertical</enum>
|
<enum>Qt::Vertical</enum>
|
||||||
@ -197,6 +197,46 @@
|
|||||||
</property>
|
</property>
|
||||||
</spacer>
|
</spacer>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="6" column="0">
|
||||||
|
<widget class="QLabel" name="enum_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Values</string>
|
||||||
|
</property>
|
||||||
|
<property name="buddy">
|
||||||
|
<cstring>enum_box</cstring>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="6" column="2">
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
|
<item>
|
||||||
|
<widget class="QLineEdit" name="enum_box">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>A comma-separated list of permitted values. You can specify
|
||||||
|
empty values by entering only the comma. For example, the list
|
||||||
|
',one,two,three' has 4 valid values, one of them empty. The first
|
||||||
|
value in the list is the default.</string>
|
||||||
|
</property>
|
||||||
|
<property name="sizePolicy">
|
||||||
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
|
<horstretch>0</horstretch>
|
||||||
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="enum_default_label">
|
||||||
|
<property name="text">
|
||||||
|
<string>Default: (nothing)</string>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>The first value entered will be the default value for this enumeration</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</item>
|
</item>
|
||||||
<item row="11" column="0">
|
<item row="11" column="0">
|
||||||
|
@ -254,6 +254,12 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
if field_metadata[key]['datatype'] == 'composite':
|
if field_metadata[key]['datatype'] == 'composite':
|
||||||
self.composites.append((key, field_metadata[key]['rec_index']))
|
self.composites.append((key, field_metadata[key]['rec_index']))
|
||||||
|
|
||||||
|
self.enumerations = []
|
||||||
|
for key in field_metadata:
|
||||||
|
if field_metadata[key]['datatype'] == 'enumeration':
|
||||||
|
self.enumerations.append((field_metadata[key]['display']['enum_values'][0],
|
||||||
|
field_metadata[key]['rec_index']))
|
||||||
|
|
||||||
def __getitem__(self, row):
|
def __getitem__(self, row):
|
||||||
return self._data[self._map_filtered[row]]
|
return self._data[self._map_filtered[row]]
|
||||||
|
|
||||||
@ -520,7 +526,7 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
if len(self.field_metadata[x]['search_terms']):
|
if len(self.field_metadata[x]['search_terms']):
|
||||||
db_col[x] = self.field_metadata[x]['rec_index']
|
db_col[x] = self.field_metadata[x]['rec_index']
|
||||||
if self.field_metadata[x]['datatype'] not in \
|
if self.field_metadata[x]['datatype'] not in \
|
||||||
['composite', 'text', 'comments', 'series']:
|
['composite', 'text', 'comments', 'series', 'enumeration']:
|
||||||
exclude_fields.append(db_col[x])
|
exclude_fields.append(db_col[x])
|
||||||
col_datatype[db_col[x]] = self.field_metadata[x]['datatype']
|
col_datatype[db_col[x]] = self.field_metadata[x]['datatype']
|
||||||
is_multiple_cols[db_col[x]] = self.field_metadata[x]['is_multiple']
|
is_multiple_cols[db_col[x]] = self.field_metadata[x]['is_multiple']
|
||||||
@ -691,6 +697,10 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
mi = db.get_metadata(id, index_is_id=True)
|
mi = db.get_metadata(id, index_is_id=True)
|
||||||
for k,c in self.composites:
|
for k,c in self.composites:
|
||||||
self._data[id][c] = mi.get(k, None)
|
self._data[id][c] = mi.get(k, None)
|
||||||
|
if len(self.enumerations) > 0:
|
||||||
|
for v,c in self.enumerations:
|
||||||
|
if self._data[id][c] is None:
|
||||||
|
self._data[id][c] = v
|
||||||
except IndexError:
|
except IndexError:
|
||||||
return None
|
return None
|
||||||
try:
|
try:
|
||||||
@ -711,6 +721,10 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
mi = db.get_metadata(id, index_is_id=True)
|
mi = db.get_metadata(id, index_is_id=True)
|
||||||
for k,c in self.composites:
|
for k,c in self.composites:
|
||||||
self._data[id][c] = mi.get(k)
|
self._data[id][c] = mi.get(k)
|
||||||
|
if len(self.enumerations) > 0:
|
||||||
|
for v,c in self.self._data[id][c]:
|
||||||
|
if self._data[id][c] is None:
|
||||||
|
self._data[id][c] = v
|
||||||
self._map[0:0] = ids
|
self._map[0:0] = ids
|
||||||
self._map_filtered[0:0] = ids
|
self._map_filtered[0:0] = ids
|
||||||
|
|
||||||
@ -740,6 +754,10 @@ class ResultCache(SearchQueryParser): # {{{
|
|||||||
mi = db.get_metadata(item[0], index_is_id=True)
|
mi = db.get_metadata(item[0], index_is_id=True)
|
||||||
for k,c in self.composites:
|
for k,c in self.composites:
|
||||||
item[c] = mi.get(k)
|
item[c] = mi.get(k)
|
||||||
|
if len(self.enumerations) > 0:
|
||||||
|
for v,c in self.enumerations:
|
||||||
|
if item[c] is None:
|
||||||
|
item[c] = v
|
||||||
|
|
||||||
self._map = [i[0] for i in self._data if i is not None]
|
self._map = [i[0] for i in self._data if i is not None]
|
||||||
if field is not None:
|
if field is not None:
|
||||||
@ -828,7 +846,7 @@ class SortKeyGenerator(object):
|
|||||||
sidx = record[sidx_fm['rec_index']]
|
sidx = record[sidx_fm['rec_index']]
|
||||||
val = (val, sidx)
|
val = (val, sidx)
|
||||||
|
|
||||||
elif dt in ('text', 'comments', 'composite'):
|
elif dt in ('text', 'comments', 'composite', 'enumeration'):
|
||||||
if val is None:
|
if val is None:
|
||||||
val = ''
|
val = ''
|
||||||
val = val.lower()
|
val = val.lower()
|
||||||
|
@ -18,7 +18,7 @@ from calibre.utils.date import parse_date
|
|||||||
class CustomColumns(object):
|
class CustomColumns(object):
|
||||||
|
|
||||||
CUSTOM_DATA_TYPES = frozenset(['rating', 'text', 'comments', 'datetime',
|
CUSTOM_DATA_TYPES = frozenset(['rating', 'text', 'comments', 'datetime',
|
||||||
'int', 'float', 'bool', 'series', 'composite'])
|
'int', 'float', 'bool', 'series', 'composite', 'enumeration'])
|
||||||
|
|
||||||
def custom_table_names(self, num):
|
def custom_table_names(self, num):
|
||||||
return 'custom_column_%d'%num, 'books_custom_column_%d_link'%num
|
return 'custom_column_%d'%num, 'books_custom_column_%d_link'%num
|
||||||
@ -144,7 +144,8 @@ class CustomColumns(object):
|
|||||||
'comments': lambda x,d: adapt_text(x, {'is_multiple':False}),
|
'comments': lambda x,d: adapt_text(x, {'is_multiple':False}),
|
||||||
'datetime' : adapt_datetime,
|
'datetime' : adapt_datetime,
|
||||||
'text':adapt_text,
|
'text':adapt_text,
|
||||||
'series':adapt_text
|
'series':adapt_text,
|
||||||
|
'enumeration': adapt_text
|
||||||
}
|
}
|
||||||
|
|
||||||
# Create Tag Browser categories for custom columns
|
# Create Tag Browser categories for custom columns
|
||||||
@ -176,6 +177,8 @@ class CustomColumns(object):
|
|||||||
ans = ans.split('|') if ans else []
|
ans = ans.split('|') if ans else []
|
||||||
if data['display'].get('sort_alpha', False):
|
if data['display'].get('sort_alpha', False):
|
||||||
ans.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
|
ans.sort(cmp=lambda x,y:cmp(x.lower(), y.lower()))
|
||||||
|
elif data['datatype'] == 'enumeration' and ans is None:
|
||||||
|
ans = data['display']['enum_values'][0]
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
def get_custom_extra(self, idx, label=None, num=None, index_is_id=False):
|
def get_custom_extra(self, idx, label=None, num=None, index_is_id=False):
|
||||||
@ -439,6 +442,9 @@ class CustomColumns(object):
|
|||||||
val = self.custom_data_adapters[data['datatype']](val, data)
|
val = self.custom_data_adapters[data['datatype']](val, data)
|
||||||
|
|
||||||
if data['normalized']:
|
if data['normalized']:
|
||||||
|
if data['datatype'] == 'enumeration' and \
|
||||||
|
val not in data['display']['enum_values']:
|
||||||
|
return None
|
||||||
if not append or not data['is_multiple']:
|
if not append or not data['is_multiple']:
|
||||||
self.conn.execute('DELETE FROM %s WHERE book=?'%lt, (id_,))
|
self.conn.execute('DELETE FROM %s WHERE book=?'%lt, (id_,))
|
||||||
self.conn.execute(
|
self.conn.execute(
|
||||||
@ -558,7 +564,7 @@ class CustomColumns(object):
|
|||||||
|
|
||||||
if datatype in ('rating', 'int'):
|
if datatype in ('rating', 'int'):
|
||||||
dt = 'INT'
|
dt = 'INT'
|
||||||
elif datatype in ('text', 'comments', 'series', 'composite'):
|
elif datatype in ('text', 'comments', 'series', 'composite', 'enumeration'):
|
||||||
dt = 'TEXT'
|
dt = 'TEXT'
|
||||||
elif datatype in ('float',):
|
elif datatype in ('float',):
|
||||||
dt = 'REAL'
|
dt = 'REAL'
|
||||||
|
@ -83,7 +83,7 @@ class FieldMetadata(dict):
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime',
|
VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime',
|
||||||
'int', 'float', 'bool', 'series', 'composite'])
|
'int', 'float', 'bool', 'series', 'composite', 'enumeration'])
|
||||||
|
|
||||||
# Builtin metadata {{{
|
# Builtin metadata {{{
|
||||||
|
|
||||||
|
@ -115,7 +115,6 @@ def pynocase(one, two, encoding='utf-8'):
|
|||||||
pass
|
pass
|
||||||
return cmp(one.lower(), two.lower())
|
return cmp(one.lower(), two.lower())
|
||||||
|
|
||||||
|
|
||||||
def load_c_extensions(conn, debug=DEBUG):
|
def load_c_extensions(conn, debug=DEBUG):
|
||||||
try:
|
try:
|
||||||
conn.enable_load_extension(True)
|
conn.enable_load_extension(True)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user