diff --git a/src/calibre/gui2/custom_column_widgets.py b/src/calibre/gui2/custom_column_widgets.py index c873d1ed94..360a5bcd0a 100644 --- a/src/calibre/gui2/custom_column_widgets.py +++ b/src/calibre/gui2/custom_column_widgets.py @@ -151,12 +151,27 @@ class DateEdit(QDateEdit): def set_to_today(self): self.setDate(now()) + def set_to_clear(self): + self.setDate(UNDEFINED_QDATE) + class DateTime(Base): def setup_ui(self, parent): cm = self.col_metadata - self.widgets = [QLabel('&'+cm['name']+':', parent), DateEdit(parent), - QLabel(''), QPushButton(_('Set \'%s\' to today')%cm['name'], parent)] + self.widgets = [QLabel('&'+cm['name']+':', parent), DateEdit(parent)] + self.widgets.append(QLabel('')) + w = QWidget(parent) + self.widgets.append(w) + l = QHBoxLayout() + l.setContentsMargins(0, 0, 0, 0) + w.setLayout(l) + l.addStretch(1) + self.today_button = QPushButton(_('Set \'%s\' to today')%cm['name'], parent) + l.addWidget(self.today_button) + self.clear_button = QPushButton(_('Clear \'%s\'')%cm['name'], parent) + l.addWidget(self.clear_button) + l.addStretch(2) + w = self.widgets[1] format = cm['display'].get('date_format','') if not format: @@ -165,7 +180,8 @@ class DateTime(Base): w.setCalendarPopup(True) w.setMinimumDate(UNDEFINED_QDATE) w.setSpecialValueText(_('Undefined')) - self.widgets[3].clicked.connect(w.set_to_today) + self.today_button.clicked.connect(w.set_to_today) + self.clear_button.clicked.connect(w.set_to_clear) def setter(self, val): if val is None: @@ -470,11 +486,48 @@ class BulkBase(Base): self.setter(val) def commit(self, book_ids, notify=False): + if not self.a_c_checkbox.isChecked(): + return val = self.gui_val val = self.normalize_ui_val(val) if val != self.initial_val: self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify) + def make_widgets(self, parent, main_widget_class, extra_label_text=''): + w = QWidget(parent) + self.widgets = [QLabel('&'+self.col_metadata['name']+':', w), w] + l = QHBoxLayout() + l.setContentsMargins(0, 0, 0, 0) + w.setLayout(l) + self.main_widget = main_widget_class(w) + l.addWidget(self.main_widget) + l.setStretchFactor(self.main_widget, 10) + self.a_c_checkbox = QCheckBox( _('Apply changes'), w) + l.addWidget(self.a_c_checkbox) + self.ignore_change_signals = True + + # connect to the various changed signals so we can auto-update the + # apply changes checkbox + if hasattr(self.main_widget, 'editTextChanged'): + # editable combobox widgets + self.main_widget.editTextChanged.connect(self.a_c_checkbox_changed) + if hasattr(self.main_widget, 'textChanged'): + # lineEdit widgets + self.main_widget.textChanged.connect(self.a_c_checkbox_changed) + if hasattr(self.main_widget, 'currentIndexChanged'): + # combobox widgets + self.main_widget.currentIndexChanged[int].connect(self.a_c_checkbox_changed) + if hasattr(self.main_widget, 'valueChanged'): + # spinbox widgets + self.main_widget.valueChanged.connect(self.a_c_checkbox_changed) + if hasattr(self.main_widget, 'dateChanged'): + # dateEdit widgets + self.main_widget.dateChanged.connect(self.a_c_checkbox_changed) + + def a_c_checkbox_changed(self): + if not self.ignore_change_signals: + self.a_c_checkbox.setChecked(True) + class BulkBool(BulkBase, Bool): def get_initial_value(self, book_ids): @@ -484,58 +537,144 @@ class BulkBool(BulkBase, Bool): if tweaks['bool_custom_columns_are_tristate'] == 'no' and val is None: val = False if value is not None and value != val: - return 'nochange' + return None value = val return value def setup_ui(self, parent): - self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), - QComboBox(parent)] - w = self.widgets[1] - items = [_('Yes'), _('No'), _('Undefined'), _('Do not change')] - icons = [I('ok.png'), I('list_remove.png'), I('blank.png'), I('blank.png')] + self.make_widgets(parent, QComboBox) + items = [_('Yes'), _('No'), _('Undefined')] + icons = [I('ok.png'), I('list_remove.png'), I('blank.png')] + self.main_widget.blockSignals(True) for icon, text in zip(icons, items): - w.addItem(QIcon(icon), text) + self.main_widget.addItem(QIcon(icon), text) + self.main_widget.blockSignals(False) def getter(self): - val = self.widgets[1].currentIndex() - return {3: 'nochange', 2: None, 1: False, 0: True}[val] + val = self.main_widget.currentIndex() + return {2: None, 1: False, 0: True}[val] def setter(self, val): - val = {'nochange': 3, None: 2, False: 1, True: 0}[val] - self.widgets[1].setCurrentIndex(val) + val = {None: 2, False: 1, True: 0}[val] + self.main_widget.setCurrentIndex(val) + self.ignore_change_signals = False def commit(self, book_ids, notify=False): + if not self.a_c_checkbox.isChecked(): + return val = self.gui_val val = self.normalize_ui_val(val) - if val != self.initial_val and val != 'nochange': - if tweaks['bool_custom_columns_are_tristate'] == 'no' and val is None: - val = False - self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify) + if tweaks['bool_custom_columns_are_tristate'] == 'no' and val is None: + val = False + self.db.set_custom_bulk(book_ids, val, num=self.col_id, notify=notify) -class BulkInt(BulkBase, Int): - pass +class BulkInt(BulkBase): -class BulkFloat(BulkBase, Float): - pass + def setup_ui(self, parent): + self.make_widgets(parent, QSpinBox) + self.main_widget.setRange(-100, sys.maxint) + self.main_widget.setSpecialValueText(_('Undefined')) + self.main_widget.setSingleStep(1) -class BulkRating(BulkBase, Rating): - pass + def setter(self, val): + if val is None: + val = self.main_widget.minimum() + else: + val = int(val) + self.main_widget.setValue(val) + self.ignore_change_signals = False -class BulkDateTime(BulkBase, DateTime): - pass + def getter(self): + val = self.main_widget.value() + if val == self.main_widget.minimum(): + val = None + return val + +class BulkFloat(BulkInt): + + def setup_ui(self, parent): + self.make_widgets(parent, QDoubleSpinBox) + self.main_widget.setRange(-100., float(sys.maxint)) + self.main_widget.setDecimals(2) + self.main_widget.setSpecialValueText(_('Undefined')) + self.main_widget.setSingleStep(1) + +class BulkRating(BulkBase): + + def setup_ui(self, parent): + self.make_widgets(parent, QSpinBox) + self.main_widget.setRange(0, 5) + self.main_widget.setSuffix(' '+_('star(s)')) + self.main_widget.setSpecialValueText(_('Unrated')) + self.main_widget.setSingleStep(1) + + def setter(self, val): + if val is None: + val = 0 + self.main_widget.setValue(int(round(val/2.))) + self.ignore_change_signals = False + + def getter(self): + val = self.main_widget.value() + if val == 0: + val = None + else: + val *= 2 + return val + +class BulkDateTime(BulkBase): + + def setup_ui(self, parent): + cm = self.col_metadata + self.make_widgets(parent, DateEdit) + self.widgets.append(QLabel('')) + w = QWidget(parent) + self.widgets.append(w) + l = QHBoxLayout() + l.setContentsMargins(0, 0, 0, 0) + w.setLayout(l) + l.addStretch(1) + self.today_button = QPushButton(_('Set \'%s\' to today')%cm['name'], parent) + l.addWidget(self.today_button) + self.clear_button = QPushButton(_('Clear \'%s\'')%cm['name'], parent) + l.addWidget(self.clear_button) + l.addStretch(2) + + w = self.main_widget + format = cm['display'].get('date_format','') + if not format: + format = 'dd MMM yyyy' + w.setDisplayFormat(format) + w.setCalendarPopup(True) + w.setMinimumDate(UNDEFINED_QDATE) + w.setSpecialValueText(_('Undefined')) + self.today_button.clicked.connect(w.set_to_today) + self.clear_button.clicked.connect(w.set_to_clear) + + def setter(self, val): + if val is None: + val = self.main_widget.minimumDate() + else: + val = QDate(val.year, val.month, val.day) + self.main_widget.setDate(val) + self.ignore_change_signals = False + + def getter(self): + val = self.main_widget.date() + if val == UNDEFINED_QDATE: + val = None + else: + val = qt_to_dt(val) + return val class BulkSeries(BulkBase): def setup_ui(self, parent): + self.make_widgets(parent, EnComboBox) values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) - w = EnComboBox(parent) - w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) - w.setMinimumContentsLength(25) - self.name_widget = w - self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w] - + self.main_widget.setSizeAdjustPolicy(self.main_widget.AdjustToMinimumContentsLengthWithIcon) + self.main_widget.setMinimumContentsLength(25) self.widgets.append(QLabel('', parent)) w = QWidget(parent) layout = QHBoxLayout(w) @@ -555,15 +694,24 @@ class BulkSeries(BulkBase): layout.addWidget(self.series_start_number) layout.addItem(QSpacerItem(20, 10, QSizePolicy.Expanding, QSizePolicy.Minimum)) self.widgets.append(w) + self.idx_widget.stateChanged.connect(self.check_changed_checkbox) + self.force_number.stateChanged.connect(self.check_changed_checkbox) + self.series_start_number.valueChanged.connect(self.check_changed_checkbox) + self.remove_series.stateChanged.connect(self.check_changed_checkbox) + self.ignore_change_signals = False + + def check_changed_checkbox(self): + self.a_c_checkbox.setChecked(True) def initialize(self, book_id): self.idx_widget.setChecked(False) for c in self.all_values: - self.name_widget.addItem(c) - self.name_widget.setEditText('') + self.main_widget.addItem(c) + self.main_widget.setEditText('') + self.a_c_checkbox.setChecked(False) def getter(self): - n = unicode(self.name_widget.currentText()).strip() + n = unicode(self.main_widget.currentText()).strip() i = self.idx_widget.checkState() f = self.force_number.checkState() s = self.series_start_number.value() @@ -571,6 +719,8 @@ class BulkSeries(BulkBase): return n, i, f, s, r def commit(self, book_ids, notify=False): + if not self.a_c_checkbox.isChecked(): + return val, update_indices, force_start, at_value, clear = self.gui_val val = None if clear else self.normalize_ui_val(val) if clear or val != '': @@ -598,9 +748,9 @@ class BulkEnumeration(BulkBase, Enumeration): def get_initial_value(self, book_ids): value = None - ret_value = None + first = True dialog_shown = False - for i,book_id in enumerate(book_ids): + for book_id in book_ids: val = self.db.get_custom(book_id, num=self.col_id, index_is_id=True) if val and val not in self.col_metadata['display']['enum_values']: if not dialog_shown: @@ -610,44 +760,32 @@ class BulkEnumeration(BulkBase, Enumeration): self.col_metadata['name']), show=True, show_copy_button=False) dialog_shown = True - ret_value = ' nochange ' - elif (value is not None and value != val) or (val and i != 0): - ret_value = ' nochange ' - value = val - if ret_value is None: - return value - return ret_value + if first: + value = val + first = False + elif value != val: + value = None + if not value: + self.ignore_change_signals = False + return value def setup_ui(self, parent): - self.parent = parent - self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), - QComboBox(parent)] - w = self.widgets[1] + self.make_widgets(parent, QComboBox) vals = self.col_metadata['display']['enum_values'] - w.addItem('Do Not Change') - w.addItem('') - for v in vals: - w.addItem(v) + self.main_widget.blockSignals(True) + self.main_widget.addItem('') + self.main_widget.addItems(vals) + self.main_widget.blockSignals(False) def getter(self): - if self.widgets[1].currentIndex() == 0: - return ' nochange ' - return unicode(self.widgets[1].currentText()) + return unicode(self.main_widget.currentText()) def setter(self, val): - if val == ' nochange ': - self.widgets[1].setCurrentIndex(0) + if val is None: + self.main_widget.setCurrentIndex(0) else: - if val is None: - self.widgets[1].setCurrentIndex(1) - 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) + self.main_widget.setCurrentIndex(self.main_widget.findText(val)) + self.ignore_change_signals = False class RemoveTags(QWidget): @@ -658,11 +796,10 @@ class RemoveTags(QWidget): layout.setContentsMargins(0, 0, 0, 0) self.tags_box = CompleteLineEdit(parent, values) - layout.addWidget(self.tags_box, stretch = 1) - # self.tags_box.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) - + layout.addWidget(self.tags_box, stretch=3) self.checkbox = QCheckBox(_('Remove all tags'), parent) layout.addWidget(self.checkbox) + layout.addStretch(1) self.setLayout(layout) self.connect(self.checkbox, SIGNAL('stateChanged(int)'), self.box_touched) @@ -679,39 +816,45 @@ class BulkText(BulkBase): values = self.all_values = list(self.db.all_custom(num=self.col_id)) values.sort(key=sort_key) if self.col_metadata['is_multiple']: - w = CompleteLineEdit(parent, values) - w.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) - self.widgets = [QLabel('&'+self.col_metadata['name']+': ' + - _('tags to add'), parent), w] - self.adding_widget = w + self.make_widgets(parent, CompleteLineEdit, + extra_label_text=_('tags to add')) + self.main_widget.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Preferred) + self.adding_widget = self.main_widget w = RemoveTags(parent, values) self.widgets.append(QLabel('&'+self.col_metadata['name']+': ' + _('tags to remove'), parent)) self.widgets.append(w) self.removing_widget = w + w.tags_box.textChanged.connect(self.a_c_checkbox_changed) + w.checkbox.stateChanged.connect(self.a_c_checkbox_changed) else: - w = EnComboBox(parent) - w.setSizeAdjustPolicy(w.AdjustToMinimumContentsLengthWithIcon) - w.setMinimumContentsLength(25) - self.widgets = [QLabel('&'+self.col_metadata['name']+':', parent), w] + self.make_widgets(parent, EnComboBox) + self.main_widget.setSizeAdjustPolicy( + self.main_widget.AdjustToMinimumContentsLengthWithIcon) + self.main_widget.setMinimumContentsLength(25) + self.ignore_change_signals = False def initialize(self, book_ids): if self.col_metadata['is_multiple']: - self.widgets[1].update_items_cache(self.all_values) + self.main_widget.update_items_cache(self.all_values) else: val = self.get_initial_value(book_ids) self.initial_val = val = self.normalize_db_val(val) idx = None + self.main_widget.blockSignals(True) for i, c in enumerate(self.all_values): if c == val: idx = i - self.widgets[1].addItem(c) - self.widgets[1].setEditText('') + self.main_widget.addItem(c) + self.main_widget.setEditText('') if idx is not None: - self.widgets[1].setCurrentIndex(idx) + self.main_widget.setCurrentIndex(idx) + self.main_widget.blockSignals(False) def commit(self, book_ids, notify=False): + if not self.a_c_checkbox.isChecked(): + return if self.col_metadata['is_multiple']: remove_all, adding, rtext = self.gui_val remove = set() @@ -740,7 +883,7 @@ class BulkText(BulkBase): unicode(self.adding_widget.text()), \ unicode(self.removing_widget.tags_box.text()) - val = unicode(self.widgets[1].currentText()).strip() + val = unicode(self.main_widget.currentText()).strip() if not val: val = None return val