diff --git a/src/calibre/gui2/dialogs/enum_values_edit.py b/src/calibre/gui2/dialogs/enum_values_edit.py index b5ae615a29..503679f2ac 100644 --- a/src/calibre/gui2/dialogs/enum_values_edit.py +++ b/src/calibre/gui2/dialogs/enum_values_edit.py @@ -17,7 +17,8 @@ from qt.core import ( QVBoxLayout, ) -from calibre.gui2 import error_dialog, gprefs +from calibre.gui2 import error_dialog, gprefs, question_dialog +from calibre.utils.icu import lower from calibre.utils.localization import ngettext @@ -35,6 +36,11 @@ class CountTableWidgetItem(QTableWidgetItem): class EnumValuesEdit(QDialog): + VALUE_COLUMN = 0 + WAS_COLUMN = 1 + COLOR_COLUMN = 2 + COUNT_COLUMN = 3 + def __init__(self, parent, db, key): QDialog.__init__(self, parent) @@ -46,8 +52,8 @@ class EnumValuesEdit(QDialog): bbox.addStretch(10) self.del_button = QToolButton() self.del_button.setIcon(QIcon.ic('trash.png')) - self.del_button.setToolTip(_('Remove the currently selected value. Only ' - 'values with a count of zero can be removed')) + self.del_button.setToolTip(_('Remove the currently selected value. The ' + 'value will be removed from all books.')) self.ins_button = QToolButton() self.ins_button.setIcon(QIcon.ic('plus.png')) self.ins_button.setToolTip(_('Add a new permissible value')) @@ -65,6 +71,7 @@ class EnumValuesEdit(QDialog): bbox.addStretch(10) l.addItem(bbox, 0, 0) + self.deleted_values = {} self.del_button.clicked.connect(self.del_line) self.all_colors = {str(s) for s in list(QColor.colorNames())} @@ -72,14 +79,15 @@ class EnumValuesEdit(QDialog): tl = QVBoxLayout() l.addItem(tl, 0, 1) self.table = t = QTableWidget(parent) - t.setColumnCount(3) + t.setColumnCount(4) t.setRowCount(1) - t.setHorizontalHeaderLabels([_('Value'), _('Color'), _('Count')]) + t.setHorizontalHeaderLabels([_('Value'), _('Was'), _('Color'), _('Count')]) t.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection) tl.addWidget(t) counts = self.db.new_api.get_usage_count_by_id(key) - self.name_to_count = {self.db.new_api.get_item_name(key, item_id):count for item_id,count in counts.items()} + self.name_to_count = {lower(self.db.new_api.get_item_name(key, item_id)):count + for item_id,count in counts.items()} self.key = key self.fm = fm = db.field_metadata[key] @@ -93,6 +101,7 @@ class EnumValuesEdit(QDialog): c.setCurrentIndex(c.findText(colors[i])) else: c.setCurrentIndex(0) + self.make_was_item(i) self.make_count_item(i, v) t.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch) @@ -112,10 +121,21 @@ class EnumValuesEdit(QDialog): def cell_changed(self, row, col): if col == 0: - item = self.table.item(row, 2) - if item is not None and self.table.item(row, 0) is not None: - count = self.name_to_count.get(self.table.item(row, 0).text()) + val_item = self.table.item(row, self.VALUE_COLUMN) + if val_item is None: + return + item = self.table.item(row, self.COUNT_COLUMN) + if item is not None: + count = self.name_to_count.get(lower(self.table.item(row, self.VALUE_COLUMN).text())) item.set_count(count) + txt = val_item.text() + orig_txt = str(val_item.data(Qt.ItemDataRole.UserRole)) + was_item = self.table.item(row, self.WAS_COLUMN) + if was_item is not None: + if txt != orig_txt: + was_item.setText(orig_txt) + else: + was_item.setText('') def sizeHint(self): sz = QDialog.sizeHint(self) @@ -126,9 +146,8 @@ class EnumValuesEdit(QDialog): def make_name_item(self, row, txt): it = QTableWidgetItem(txt) it.setData(Qt.ItemDataRole.UserRole, txt) - it.setCheckState(Qt.CheckState.Unchecked) - it.setToolTip('

' + _('Check the box if you change the value and want it renamed in books where it is used') + '

') - self.table.setItem(row, 0, it) + it.setToolTip(_('Changing the value will rename it in all books')) + self.table.setItem(row, self.VALUE_COLUMN, it) def make_color_combobox(self, row, dex): c = QComboBox(self) @@ -136,14 +155,18 @@ class EnumValuesEdit(QDialog): c.addItems(QColor.colorNames()) c.setToolTip('

' + _('Selects the color of the text when displayed in the book list. ' 'Either all rows must have a color or no rows have a color') + '

') - self.table.setCellWidget(row, 1, c) + self.table.setCellWidget(row, self.COLOR_COLUMN, c) if dex >= 0: c.setCurrentIndex(dex) return c + def make_was_item(self, row): + it = QTableWidgetItem('') + self.table.setItem(row, self.WAS_COLUMN, it) + def make_count_item(self, row, txt): - it = CountTableWidgetItem(self.name_to_count.get(txt)) - self.table.setItem(row, 2, it) + it = CountTableWidgetItem(self.name_to_count.get(lower(txt))) + self.table.setItem(row, self.COUNT_COLUMN, it) def move_up_clicked(self): row = self.table.currentRow() @@ -156,15 +179,17 @@ class EnumValuesEdit(QDialog): self.move_row(row, -1) def move_row(self, row, direction): - t = self.table.takeItem(row, 0) - c = self.table.cellWidget(row, 1).currentIndex() - count = self.table.takeItem(row, 2) + t = self.table.takeItem(row, self.VALUE_COLUMN) + c = self.table.cellWidget(row, self.COLOR_COLUMN).currentIndex() + was = self.table.takeItem(row, self.WAS_COLUMN) + count = self.table.takeItem(row, self.COUNT_COLUMN) self.table.removeRow(row) row += direction self.table.insertRow(row) - self.table.setItem(row, 0, t) + self.table.setItem(row, self.VALUE_COLUMN, t) self.make_color_combobox(row, c) - self.table.setItem(row, 2, count) + self.table.setItem(row, self.WAS_COLUMN, was) + self.table.setItem(row, self.COUNT_COLUMN, count) self.table.setCurrentCell(row, 0) def move_down_clicked(self): @@ -180,16 +205,17 @@ class EnumValuesEdit(QDialog): def del_line(self): row = self.table.currentRow() if row >= 0: - txt = self.table.item(row, 0).text() - count = self.name_to_count.get(txt, 0) + txt = self.table.item(row, self.VALUE_COLUMN).text() + count = self.name_to_count.get(lower(txt), 0) if count > 0: - error_dialog(self, - _('Cannot remove value "{}"').format(txt), - ngettext('The value "{0}" is used in {1} book and cannot be removed.', - 'The value "{0}" is used in {1} books and cannot be removed.', - count).format(txt, count), - show=True) - return + r = question_dialog(self, + _('Value "{}" is used').format(txt), + ngettext('The value "{0}" is used in {1} book. Do you really want to remove it?', + 'The value "{0}" is used in {1} books. Do you really want to remove it?', + count).format(txt, count)) + if r != QDialog.DialogCode.Accepted: + return + self.deleted_values[lower(txt)] = txt self.table.removeRow(self.table.currentRow()) def ins_button_clicked(self): @@ -201,6 +227,7 @@ class EnumValuesEdit(QDialog): self.table.insertRow(row) self.make_name_item(row, '') self.make_color_combobox(row, -1) + self.make_was_item(row) self.make_count_item(row, '') def save_geometry(self): @@ -212,21 +239,20 @@ class EnumValuesEdit(QDialog): colors = [] id_map = {} for i in range(0, self.table.rowCount()): - it = self.table.item(i, 0) + it = self.table.item(i, self.VALUE_COLUMN) v = str(it.text()) if not v: error_dialog(self, _('Empty value'), _('Empty values are not allowed'), show=True) return ov = str(it.data(Qt.ItemDataRole.UserRole)) - if v != ov and it.checkState() == Qt.CheckState.Checked: + if v != ov: fid = self.db.new_api.get_item_id(self.key, ov) id_map[fid] = v values.append(v) - c = str(self.table.cellWidget(i, 1).currentText()) + c = str(self.table.cellWidget(i, self.COLOR_COLUMN).currentText()) if c: colors.append(c) - l_lower = [v.lower() for v in values] for i,v in enumerate(l_lower): if v in l_lower[i+1:]: @@ -241,6 +267,22 @@ class EnumValuesEdit(QDialog): 'Either all values or no values must have colors'), show=True) return + # Process deleted values. It is possible that a value was deleted then + # added back, possibly with a different case. If the case is the same then + # don't delete it. If the case is different then add it to the rename dict. + for v in values: + dv = self.deleted_values.get(lower(v)) + if dv is None: + continue + self.deleted_values.pop(lower(v)) + if v != dv: + fid = self.db.new_api.get_item_id(self.key, dv) + id_map[fid] = v + + ids_to_delete = (self.db.new_api.get_item_id(self.key, v) for v in self.deleted_values.values()) + if ids_to_delete: + self.db.new_api.remove_items(self.key, ids_to_delete) + disp['enum_values'] = values disp['enum_colors'] = colors self.db.set_custom_column_metadata(self.fm['colnum'], display=disp,