diff --git a/src/calibre/db/cache.py b/src/calibre/db/cache.py index 8352a9d0dd..4bf00000e0 100644 --- a/src/calibre/db/cache.py +++ b/src/calibre/db/cache.py @@ -92,6 +92,22 @@ def _add_newbook_tag(mi): mi.tags.append(tag) +def _add_default_custom_column_values(mi, fm): + cols = fm.custom_field_metadata(include_composites=False) + for cc,col in iteritems(cols): + dv = col['display'].get('default_value', '') + try: + if dv: + if not mi.get_user_metadata(cc, make_copy=False): + mi.set_user_metadata(cc, col) + dt = col['datatype'] + if dt == 'datetime' and icu_lower(dv) == 'now': + dv = nowf() + mi.set(cc, dv) + except: + traceback.print_exc() + + dynamic_category_preferences = frozenset({'grouped_search_make_user_categories', 'grouped_search_terms', 'user_categories'}) @@ -1571,6 +1587,7 @@ class Cache(object): mi.tags = list(mi.tags) if apply_import_tags: _add_newbook_tag(mi) + _add_default_custom_column_values(mi, self.field_metadata) if not add_duplicates and self._has_book(mi): return series_index = (self._get_next_series_num_for(mi.series) if mi.series_index is None else mi.series_index) diff --git a/src/calibre/gui2/preferences/create_custom_column.py b/src/calibre/gui2/preferences/create_custom_column.py index 6310568b67..150276d0f7 100644 --- a/src/calibre/gui2/preferences/create_custom_column.py +++ b/src/calibre/gui2/preferences/create_custom_column.py @@ -17,6 +17,7 @@ from PyQt5.Qt import ( ) from calibre.gui2 import error_dialog +from calibre.utils.date import parse_date, UNDEFINED_DATE from polyglot.builtins import iteritems, unicode_type, range, map @@ -127,8 +128,7 @@ class CreateCustomColumn(QDialog): self.shortcuts.setVisible(False) idx = current_row if idx < 0: - self.simple_error(_('No column selected'), - _('No column has been selected')) + self.simple_error(_('No column selected'), _('No column has been selected')) return col = current_key if col not in parent.custcols: @@ -177,6 +177,24 @@ class CreateCustomColumn(QDialog): self.comments_type.setCurrentIndex(idx) elif ct == 'rating': self.allow_half_stars.setChecked(bool(c['display'].get('allow_half_stars', False))) + + # Default values + dv = c['display'].get('default_value', None) + if dv is not None: + if ct == 'bool': + self.default_value.setText(_('Yes') if dv else _('No')) + elif ct == 'datetime': + self.default_value.setText(_('Now') if dv == 'now' else dv) + elif ct == 'rating': + if self.allow_half_stars.isChecked(): + self.default_value.setText(unicode_type(dv/2)) + else: + self.default_value.setText(unicode_type(dv//2)) + elif ct in ('int', 'float'): + self.default_value.setText(unicode_type(dv)) + elif ct not in ('composite', '*composite'): + self.default_value.setText(dv) + self.datatype_changed() if ct in ['text', 'composite', 'enumeration']: self.use_decorations.setChecked(c['display'].get('use_decorations', False)) @@ -390,6 +408,15 @@ class CreateCustomColumn(QDialog): l.addWidget(cch) add_row(None, l) + # Default value + self.default_value = dv = QLineEdit(self) + dv.setToolTip('
' + _('Default value when a new book is added to the ' + 'library. For Date columns enter the word "Now", or the date as ' + 'yyyy-mm-dd. For Yes/No columns enter "Yes" "No". For Text with ' + 'a fixed set of values enter one of the permitted values. For ' + 'Rating columns enter a number between 0 and 5.') + '
') + self.default_value_label = add_row(_('Default value'), dv) + self.resize(self.sizeHint()) # }}} @@ -456,6 +483,8 @@ class CreateCustomColumn(QDialog): getattr(self, 'composite_'+x).setVisible(col_type in ['composite', '*composite']) for x in ('box', 'default_label', 'label', 'colors', 'colors_label'): getattr(self, 'enum_'+x).setVisible(col_type == 'enumeration') + for x in ('value_label', 'value'): + getattr(self, 'default_'+x).setVisible(col_type not in ['composite', '*composite']) self.use_decorations.setVisible(col_type in ['text', 'composite', 'enumeration']) self.is_names.setVisible(col_type == '*text') is_comments = col_type == 'comments' @@ -512,15 +541,30 @@ class CreateCustomColumn(QDialog): display_dict = {} + default_val = (unicode_type(self.default_value.text()).strip() + if col_type != 'composite' else None) + if col_type == 'datetime': if unicode_type(self.format_box.text()).strip(): display_dict = {'date_format':unicode_type(self.format_box.text()).strip()} else: display_dict = {'date_format': None} + if default_val: + if default_val == _('Now'): + display_dict['default_value'] = 'now' + else: + try: + tv = parse_date(default_val) + except: + tv = UNDEFINED_DATE + if tv == UNDEFINED_DATE: + return self.simple_error(_('Invalid default value'), + _('The default value must be "Now" or a date')) + display_dict['default_value'] = default_val elif col_type == 'composite': if not unicode_type(self.composite_box.text()).strip(): - return self.simple_error('', _('You must enter a template for' - ' composite columns')) + return self.simple_error('', _('You must enter a template for ' + 'composite columns')) display_dict = {'composite_template':unicode_type(self.composite_box.text()).strip(), 'composite_sort': ['text', 'number', 'date', 'bool'] [self.composite_sort_by.currentIndex()], @@ -529,8 +573,8 @@ class CreateCustomColumn(QDialog): } elif col_type == 'enumeration': if not unicode_type(self.enum_box.text()).strip(): - return self.simple_error('', _('You must enter at least one' - ' value for enumeration columns')) + return self.simple_error('', _('You must enter at least one ' + 'value for enumeration columns')) l = [v.strip() for v in unicode_type(self.enum_box.text()).split(',') if v.strip()] l_lower = [v.lower() for v in l] for i,v in enumerate(l_lower): @@ -544,13 +588,16 @@ class CreateCustomColumn(QDialog): 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')) + 'contain the same number of items as the value box')) for tc in c: if tc not in QColor.colorNames() and not re.match("#(?:[0-9a-f]{3}){1,4}",tc,re.I): - return self.simple_error('', - _('The color {0} is unknown').format(tc)) - + return self.simple_error('', _('The color {0} is unknown').format(tc)) display_dict = {'enum_values': l, 'enum_colors': c} + if default_val: + if default_val not in l: + return self.simple_error(_('Invalid default value'), + _('The default value must be one of the permitted values')) + display_dict['default_value'] = default_val elif col_type == 'text' and is_multiple: display_dict = {'is_names': self.is_names.isChecked()} elif col_type in ['int', 'float']: @@ -558,14 +605,51 @@ class CreateCustomColumn(QDialog): display_dict = {'number_format':unicode_type(self.format_box.text()).strip()} else: display_dict = {'number_format': None} + if default_val: + try: + if col_type == 'int': + msg = _('The default value must be an integer') + tv = int(default_val) + display_dict['default_value'] = tv + else: + msg = _('The default value must be a real number') + tv = float(default_val) + display_dict['default_value'] = tv + except: + return self.simple_error(_('Invalid default value'), msg) elif col_type == 'comments': display_dict['heading_position'] = unicode_type(self.comments_heading_position.currentData()) display_dict['interpret_as'] = unicode_type(self.comments_type.currentData()) elif col_type == 'rating': - display_dict['allow_half_stars'] = bool(self.allow_half_stars.isChecked()) + half_stars = bool(self.allow_half_stars.isChecked()) + display_dict['allow_half_stars'] = half_stars + if default_val: + try: + tv = int((float(default_val) if half_stars else int(default_val)) * 2) + except: + tv = -1 + if tv < 0 or tv > 10: + if half_stars: + return self.simple_error(_('Invalid default value'), + _('The default value must be a real number between 0 and 5.0')) + else: + return self.simple_error(_('Invalid default value'), + _('The default value must be an integer between 0 and 5')) + display_dict['default_value'] = tv + elif col_type == 'bool': + if default_val: + tv = {_('Yes'): True, _('No'): False}.get(default_val, None) + if tv is None: + return self.simple_error(_('Invalid default value'), + _('The default value must be "Yes" or "No"')) + display_dict['default_value'] = tv if col_type in ['text', 'composite', 'enumeration'] and not is_multiple: display_dict['use_decorations'] = self.use_decorations.checkState() + + if default_val and 'default_value' not in display_dict: + display_dict['default_value'] = default_val + display_dict['description'] = self.description_box.text().strip() if not self.editing_col: