From 7704ca4a0534885e66e4a0d371a46aff76d9159b Mon Sep 17 00:00:00 2001
From: Charles Haley <>
Date: Wed, 1 Dec 2010 19:07:29 +0000
Subject: [PATCH] First pass at enum custom type
---
src/calibre/gui2/custom_column_widgets.py | 82 +++++++++++++++++++
src/calibre/gui2/library/delegates.py | 33 ++++++++
src/calibre/gui2/library/models.py | 4 +-
src/calibre/gui2/library/views.py | 6 +-
.../gui2/preferences/create_custom_column.py | 17 +++-
.../gui2/preferences/create_custom_column.ui | 44 ++++++++--
src/calibre/library/custom_columns.py | 7 +-
src/calibre/library/field_metadata.py | 2 +-
8 files changed, 176 insertions(+), 19 deletions(-)
diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py
index 053dd7a743..7ea4fa679f 100644
--- a/src/calibre/gui2/custom_column_widgets.py
+++ b/src/calibre/gui2/custom_column_widgets.py
@@ -310,6 +310,37 @@ class Series(Base):
self.db.set_custom(book_id, val, extra=s_index,
num=self.col_id, notify=notify, commit=False)
+class Enumeration(Base):
+
+ def setup_ui(self, parent):
+ self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
+ QComboBox(parent)]
+ w = self.widgets[1]
+ print self.col_metadata['display']
+ vals = self.col_metadata['display']['enum_values']
+ w.addItem('')
+ 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:
+ val = ''
+ self.initial_val = val
+ val = self.normalize_db_val(val)
+ idx = self.widgets[1].findText(val)
+ if idx < 0:
+ 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 = {
'bool' : Bool,
'rating' : Rating,
@@ -319,6 +350,7 @@ widgets = {
'text' : Text,
'comments': Comments,
'series': Series,
+ 'enumeration': Enumeration
}
def field_sort(y, z, x=None):
@@ -551,6 +583,55 @@ class BulkSeries(BulkBase):
self.db.set_custom_bulk(book_ids, val, extras=extras,
num=self.col_id, notify=notify)
+class BulkEnumeration(BulkBase, Enumeration):
+
+ def get_initial_value(self, book_ids):
+ value = None
+ for book_id in book_ids:
+ val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True)
+ if value is not None and value != val:
+ return ' nochange '
+ value = val
+ return value
+
+ def setup_ui(self, parent):
+ self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent),
+ QComboBox(parent)]
+ w = self.widgets[1]
+ print self.col_metadata['display']
+ vals = self.col_metadata['display']['enum_values']
+ w.addItem('Do Not Change')
+ w.addItem('')
+ 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_ui_val(self, val):
+ if val == '':
+ return None
+ return val
+
+ def normalize_db_val(self, val):
+ if val is None:
+ return ''
+ return val
+
class RemoveTags(QWidget):
def __init__(self, parent, values):
@@ -656,4 +737,5 @@ bulk_widgets = {
'datetime': BulkDateTime,
'text' : BulkText,
'series': BulkSeries,
+ 'enumeration': BulkEnumeration,
}
diff --git a/src/calibre/gui2/library/delegates.py b/src/calibre/gui2/library/delegates.py
index f9ba612bc9..f5610bf849 100644
--- a/src/calibre/gui2/library/delegates.py
+++ b/src/calibre/gui2/library/delegates.py
@@ -254,6 +254,39 @@ 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()]
+ print 'delegate'
+ editor = QComboBox(parent)
+ editor.addItem('')
+ 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())
+ if val == '':
+ val = None
+ 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:
+ val = ''
+ idx = editor.findText(val)
+ if idx < 0:
+ editor.setCurrentIndex(0)
+ else:
+ editor.setCurrentIndex(idx)
+# }}}
+
class CcCommentsDelegate(QStyledItemDelegate): # {{{
'''
Delegate for comments data.
diff --git a/src/calibre/gui2/library/models.py b/src/calibre/gui2/library/models.py
index 4ef2c34c03..99639ed0a7 100644
--- a/src/calibre/gui2/library/models.py
+++ b/src/calibre/gui2/library/models.py
@@ -634,7 +634,7 @@ class BooksModel(QAbstractTableModel): # {{{
for col in self.custom_columns:
idx = self.custom_columns[col]['rec_index']
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,
mult=self.custom_columns[col]['is_multiple'])
elif datatype in ('int', 'float'):
@@ -719,7 +719,7 @@ class BooksModel(QAbstractTableModel): # {{{
typ = cc['datatype']
label=self.db.field_metadata.key_to_label(colhead)
s_index = None
- if typ in ('text', 'comments'):
+ if typ in ('text', 'comments', 'enumeration'):
val = unicode(value.toString()).strip()
val = val if val else None
if typ == 'bool':
diff --git a/src/calibre/gui2/library/views.py b/src/calibre/gui2/library/views.py
index fadb8314e3..f724ca7b58 100644
--- a/src/calibre/gui2/library/views.py
+++ b/src/calibre/gui2/library/views.py
@@ -14,7 +14,8 @@ from PyQt4.Qt import QTableView, Qt, QAbstractItemView, QMenu, pyqtSignal, \
from calibre.gui2.library.delegates import RatingDelegate, PubDateDelegate, \
TextDelegate, DateDelegate, TagsDelegate, CcTextDelegate, \
- CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate
+ CcBoolDelegate, CcCommentsDelegate, CcDateDelegate, CcTemplateDelegate, \
+ CcEnumDelegate
from calibre.gui2.library.models import BooksModel, DeviceBooksModel
from calibre.utils.config import tweaks, prefs
from calibre.gui2 import error_dialog, gprefs
@@ -76,6 +77,7 @@ class BooksView(QTableView): # {{{
self.publisher_delegate = TextDelegate(self)
self.text_delegate = TextDelegate(self)
self.cc_text_delegate = CcTextDelegate(self)
+ self.cc_enum_delegate = CcEnumDelegate(self)
self.cc_bool_delegate = CcBoolDelegate(self)
self.cc_comments_delegate = CcCommentsDelegate(self)
self.cc_template_delegate = CcTemplateDelegate(self)
@@ -427,6 +429,8 @@ class BooksView(QTableView): # {{{
self.setItemDelegateForColumn(cm.index(colhead), self.rating_delegate)
elif cc['datatype'] == 'composite':
self.setItemDelegateForColumn(cm.index(colhead), self.cc_template_delegate)
+ elif cc['datatype'] == 'enumeration':
+ self.setItemDelegateForColumn(cm.index(colhead), self.cc_enum_delegate)
else:
dattr = colhead+'_delegate'
delegate = colhead if hasattr(self, dattr) else 'text'
diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py
index ebf33784d4..d4d2b2678c 100644
--- a/src/calibre/gui2/preferences/create_custom_column.py
+++ b/src/calibre/gui2/preferences/create_custom_column.py
@@ -40,6 +40,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
'text':_('Yes/No'), 'is_multiple':False},
9:{'datatype':'composite',
'text':_('Column built from other columns'), 'is_multiple':False},
+ 10:{'datatype':'enumeration',
+ 'text':_('Fixed set of text values'), 'is_multiple':False},
}
def __init__(self, parent, editing, standard_colheads, standard_colnames):
@@ -90,7 +92,7 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
if c['display'].get('date_format', None):
self.date_format_box.setText(c['display'].get('date_format', ''))
elif ct == 'composite':
- self.composite_box.setText(c['display'].get('composite_template', ''))
+ self.composite_box.setText(c['display'].get('enum_values', ''))
self.datatype_changed()
self.exec_()
@@ -103,7 +105,8 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
getattr(self, 'date_format_'+x).setVisible(col_type == 'datetime')
for x in ('box', 'default_label', 'label'):
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):
col = unicode(self.column_name_box.text())
@@ -145,17 +148,23 @@ class CreateCustomColumn(QDialog, Ui_QCreateCustomColumn):
return self.simple_error('', _('The heading %s is already used')%col_heading)
display_dict = {}
+
if col_type == 'datetime':
if self.date_format_box.text():
display_dict = {'date_format':unicode(self.date_format_box.text())}
else:
display_dict = {'date_format': None}
-
- if col_type == 'composite':
+ elif col_type == 'composite':
if not self.composite_box.text():
return self.simple_error('', _('You must enter a template for'
' composite columns'))
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'))
+ display_dict = {'enum_values':
+ [v.strip() for v in unicode(self.enum_box.text()).split(',')]}
db = self.parent.gui.library_view.model().db
key = db.field_metadata.custom_field_prefix+col
diff --git a/src/calibre/gui2/preferences/create_custom_column.ui b/src/calibre/gui2/preferences/create_custom_column.ui
index 640becca8c..03c191d34e 100644
--- a/src/calibre/gui2/preferences/create_custom_column.ui
+++ b/src/calibre/gui2/preferences/create_custom_column.ui
@@ -10,7 +10,7 @@
0
0
528
- 199
+ 212
@@ -24,7 +24,7 @@
-
-
+
QLayout::SetDefaultConstraint
@@ -56,7 +56,7 @@
- -
+
-
@@ -69,7 +69,7 @@
- -
+
-
Column heading in the library view and category name in the tag browser
@@ -86,7 +86,7 @@
- -
+
-
@@ -105,7 +105,7 @@
- -
+
-
-
@@ -147,7 +147,7 @@
- -
+
-
-
@@ -184,7 +184,7 @@
- -
+
-
Qt::Vertical
@@ -197,6 +197,34 @@
+ -
+
+
+ Values
+
+
+
+ -
+
+
-
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Default: (nothing)
+
+
+
+
+
-
diff --git a/src/calibre/library/custom_columns.py b/src/calibre/library/custom_columns.py
index fdd78e89f8..b477c89fe5 100644
--- a/src/calibre/library/custom_columns.py
+++ b/src/calibre/library/custom_columns.py
@@ -18,7 +18,7 @@ from calibre.utils.date import parse_date
class CustomColumns(object):
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):
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}),
'datetime' : adapt_datetime,
'text':adapt_text,
- 'series':adapt_text
+ 'series':adapt_text,
+ 'enumeration': adapt_text
}
# Create Tag Browser categories for custom columns
@@ -558,7 +559,7 @@ class CustomColumns(object):
if datatype in ('rating', 'int'):
dt = 'INT'
- elif datatype in ('text', 'comments', 'series', 'composite'):
+ elif datatype in ('text', 'comments', 'series', 'composite', 'enumeration'):
dt = 'TEXT'
elif datatype in ('float',):
dt = 'REAL'
diff --git a/src/calibre/library/field_metadata.py b/src/calibre/library/field_metadata.py
index d10dc5da71..f5a156d3a7 100644
--- a/src/calibre/library/field_metadata.py
+++ b/src/calibre/library/field_metadata.py
@@ -83,7 +83,7 @@ class FieldMetadata(dict):
'''
VALID_DATA_TYPES = frozenset([None, 'rating', 'text', 'comments', 'datetime',
- 'int', 'float', 'bool', 'series', 'composite'])
+ 'int', 'float', 'bool', 'series', 'composite', 'enumeration'])
# Builtin metadata {{{