Enhancement #2091139: Reordering Columns in Custom Column Editor

This commit is contained in:
Charles Haley 2024-12-07 12:11:54 +00:00
parent 485e1a2d20
commit f2c1274824

View File

@ -19,6 +19,16 @@ from calibre.gui2.preferences.create_custom_column import CreateCustomColumn
class ConfigWidget(ConfigWidgetBase, Ui_Form):
ORDER_COLUMN = 0
HEADER_COLUMN = 1
KEY_COLUMN = 2
TYPE_COLUMN = 3
DESCRIPTION_COLUMN = 4
STATUS_COLUMN = 5
column_headings = (_('Order'), _('Column header'), _('Lookup name'),
_('Type'), _('Description'), _('Status'))
restart_critical = True
def genesis(self, gui):
@ -46,6 +56,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
signal.connect(self.columns_changed)
self.show_all_button.clicked.connect(self.show_all)
self.hide_all_button.clicked.connect(self.hide_all)
self.column_positions = None
def initialize(self):
ConfigWidgetBase.initialize(self)
@ -57,10 +68,6 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.changed_signal.emit()
def commit(self):
widths = []
for i in range(0, self.opt_columns.columnCount()):
widths.append(self.opt_columns.columnWidth(i))
gprefs.set('custcol-prefs-table-geometry', widths)
rr = ConfigWidgetBase.commit(self)
return self.apply_custom_column_changes() or rr
@ -79,13 +86,24 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.field_metadata = db.field_metadata
self.opt_columns.setColumnCount(6)
self.opt_columns.setHorizontalHeaderItem(0, QTableWidgetItem(_('Order')))
self.opt_columns.setHorizontalHeaderItem(1, QTableWidgetItem(_('Column header')))
self.opt_columns.setHorizontalHeaderItem(2, QTableWidgetItem(_('Lookup name')))
self.opt_columns.setHorizontalHeaderItem(3, QTableWidgetItem(_('Type')))
self.opt_columns.setHorizontalHeaderItem(4, QTableWidgetItem(_('Description')))
self.opt_columns.setHorizontalHeaderItem(5, QTableWidgetItem(_('Status')))
self.opt_columns.horizontalHeader().sectionClicked.connect(self.table_sorted)
# Set up the columns in logical index order
for p in range(0, len(self.column_headings)):
self.opt_columns.setHorizontalHeaderItem(p, QTableWidgetItem(self.column_headings[p]))
# Now reorder the columns into the desired visual order. Note: ignore
# visual order when looking at items. Qt automatically maps the visual
# order onto the logical order.
self.column_positions = gprefs.get('custcol-prefs-column_order', [0, 1, 2, 3, 4, 5])
header = self.opt_columns.horizontalHeader()
for dvi,li in enumerate(self.column_positions):
cvi = header.visualIndex(li)
if cvi != dvi:
header.moveSection(cvi, dvi)
header.sectionClicked.connect(self.table_sorted)
header.setSectionsMovable(True)
header.setFirstSectionMovable(False)
header.sectionMoved.connect(self.header_moved)
header.sectionResized.connect(self.save_geometry)
self.opt_columns.verticalHeader().hide()
self.opt_columns.setRowCount(len(colmap))
@ -104,6 +122,12 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
self.set_up_down_enabled(self.opt_columns.currentItem(), None)
self.opt_columns.blockSignals(False)
def header_moved(self, log_index, old_v_index, new_v_index):
self.column_positions = []
for vi in range(0, self.opt_columns.columnCount()):
self.column_positions.append(self.opt_columns.horizontalHeader().logicalIndex(vi))
self.save_geometry()
def set_up_down_enabled(self, current_item, _):
h = self.opt_columns.horizontalHeader()
row = current_item.row()
@ -130,7 +154,16 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def row_double_clicked(self, r, c):
self.edit_custcol()
def save_geometry(self):
# Save both the column widths and the column order
widths = []
for i in range(0, self.opt_columns.columnCount()):
widths.append(self.opt_columns.columnWidth(i))
gprefs.set('custcol-prefs-table-geometry', widths)
gprefs.set('custcol-prefs-column_order', self.column_positions)
def restore_geometry(self):
# restore the column widths. Order is done when the table is created.
geom = gprefs.get('custcol-prefs-table-geometry', None)
if geom is not None and len(geom) == self.opt_columns.columnCount():
with suppress(Exception):
@ -141,14 +174,14 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
def hide_all(self):
for row in range(self.opt_columns.rowCount()):
item = self.opt_columns.item(row, 0)
item = self.opt_columns.item(row, self.ORDER_COLUMN)
if item.checkState() != Qt.CheckState.PartiallyChecked:
item.setCheckState(Qt.CheckState.Unchecked)
self.changed_signal.emit()
def show_all(self):
for row in range(self.opt_columns.rowCount()):
item = self.opt_columns.item(row, 0)
item = self.opt_columns.item(row, self.ORDER_COLUMN)
if item.checkState() != Qt.CheckState.PartiallyChecked:
item.setCheckState(Qt.CheckState.Checked)
self.changed_signal.emit()
@ -169,7 +202,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
item.setToolTip(str(order))
item.setData(Qt.ItemDataRole.UserRole, key)
item.setFlags(flags)
self.opt_columns.setItem(row, 0, item)
self.opt_columns.setItem(row, self.ORDER_COLUMN, item)
flags |= Qt.ItemFlag.ItemIsUserCheckable
if key == 'ondevice':
@ -182,17 +215,20 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
else:
item.setCheckState(force_checked_to)
# The columns are added in logical index order, not visual index order,
# so we can process them without a loop
item = QTableWidgetItem(cc['name'])
item.setToolTip(cc['name'])
item.setFlags(flags)
if self.is_custom_key(key):
item.setData(Qt.ItemDataRole.DecorationRole, (QIcon.ic('column.png')))
self.opt_columns.setItem(row, 1, item)
self.opt_columns.setItem(row, self.HEADER_COLUMN, item)
item = QTableWidgetItem(key)
item.setToolTip(key)
item.setFlags(flags)
self.opt_columns.setItem(row, 2, item)
self.opt_columns.setItem(row, self.KEY_COLUMN, item)
if key == 'title':
coltype = _('Text')
@ -210,13 +246,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
item = QTableWidgetItem(coltype)
item.setToolTip(coltype)
item.setFlags(flags)
self.opt_columns.setItem(row, 3, item)
self.opt_columns.setItem(row, self.TYPE_COLUMN, item)
desc = cc['display'].get('description', "")
item = QTableWidgetItem(desc)
item.setToolTip(desc)
item.setFlags(flags)
self.opt_columns.setItem(row, 4, item)
self.opt_columns.setItem(row, self.DESCRIPTION_COLUMN, item)
if '*deleted' in cc:
col_status = _('Deleted column. Double-click to undelete it')
@ -231,13 +267,15 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
item = QTableWidgetItem(col_status)
item.setToolTip(col_status)
item.setFlags(flags)
self.opt_columns.setItem(row, 5, item)
self.opt_columns.setItem(row, self.STATUS_COLUMN, item)
self.opt_columns.setSortingEnabled(True)
def recreate_row(self, row):
checked = self.opt_columns.item(row, 0).checkState()
title = self.opt_columns.item(row, 2).text()
self.setup_row(row, title, row, force_checked_to=checked)
checked = self.opt_columns.item(row, self.ORDER_COLUMN).checkState()
# Again, use the logical index, not the visual index
key = self.opt_columns.item(row, self.KEY_COLUMN).text()
self.setup_row(row, key, row, force_checked_to=checked)
def get_move_count(self):
mods = QApplication.keyboardModifiers()
@ -297,7 +335,7 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
if row < 0:
return error_dialog(self, '', _('You must select a column to delete it'),
show=True)
key = str(self.opt_columns.item(row, 0).data(Qt.ItemDataRole.UserRole) or '')
key = str(self.opt_columns.item(row, self.ORDER_COLUMN).data(Qt.ItemDataRole.UserRole) or '')
if key not in self.custcols:
return error_dialog(self, '',
_('The selected column is not a custom column'), show=True)
@ -340,13 +378,13 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
return key.startswith('#')
def column_order_val(self, row):
return int(self.opt_columns.item(row, 0).text())
return int(self.opt_columns.item(row, self.ORDER_COLUMN).text())
def edit_custcol(self):
model = self.gui.library_view.model()
row = self.opt_columns.currentRow()
try:
key = str(self.opt_columns.item(row, 0).data(Qt.ItemDataRole.UserRole))
key = str(self.opt_columns.item(row, self.ORDER_COLUMN).data(Qt.ItemDataRole.UserRole))
if key not in self.custcols:
return error_dialog(self, '',
_('The selected column is not a user-defined column'),
@ -382,14 +420,14 @@ class ConfigWidget(ConfigWidgetBase, Ui_Form):
model = self.gui.library_view.model()
db = model.db
self.opt_columns.sortItems(0, Qt.SortOrder.AscendingOrder)
config_cols = [str(self.opt_columns.item(i, 0).data(Qt.ItemDataRole.UserRole) or '')
config_cols = [str(self.opt_columns.item(i, self.ORDER_COLUMN).data(Qt.ItemDataRole.UserRole) or '')
for i in range(self.opt_columns.rowCount())]
if not config_cols:
config_cols = ['title']
removed_cols = set(model.column_map) - set(config_cols)
hidden_cols = {str(self.opt_columns.item(i, 0).data(Qt.ItemDataRole.UserRole) or '')
hidden_cols = {str(self.opt_columns.item(i, self.ORDER_COLUMN).data(Qt.ItemDataRole.UserRole) or '')
for i in range(self.opt_columns.rowCount())
if self.opt_columns.item(i, 0).checkState()==Qt.CheckState.Unchecked}
if self.opt_columns.item(i, self.ORDER_COLUMN).checkState()==Qt.CheckState.Unchecked}
hidden_cols = hidden_cols.union(removed_cols) # Hide removed cols
hidden_cols = list(hidden_cols.intersection(set(model.column_map)))
if 'ondevice' in hidden_cols: