mirror of
https://github.com/ciromattia/kcc
synced 2026-06-02 04:33:18 +00:00
Add bulk volume editing with rich tooltip
* Add checkbox next to Volume field for enabling bulk volume editing (only in bulk mode) * Support single number (5), range (1-10), or comma list (1, 3, 5) input formats * Sort files alphabetically before volume assignment * Validate that volume count matches file count * Add rich HTML tooltip explaining the feature
This commit is contained in:
@@ -22,7 +22,7 @@ import itertools
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings)
|
from PySide6.QtCore import (QSize, QUrl, Qt, Signal, QIODeviceBase, QEvent, QThread, QSettings)
|
||||||
from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices)
|
from PySide6.QtGui import (QColor, QIcon, QPixmap, QDesktopServices)
|
||||||
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog)
|
from PySide6.QtWidgets import (QApplication, QLabel, QListWidgetItem, QMainWindow, QSystemTrayIcon, QFileDialog, QMessageBox, QDialog, QCheckBox)
|
||||||
from PySide6.QtNetwork import (QLocalSocket, QLocalServer)
|
from PySide6.QtNetwork import (QLocalSocket, QLocalServer)
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -1422,6 +1422,9 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
self.files = files if isinstance(files, list) else [files]
|
self.files = files if isinstance(files, list) else [files]
|
||||||
self.bulkMode = len(self.files) > 1
|
self.bulkMode = len(self.files) > 1
|
||||||
|
|
||||||
|
# Sort files by name for consistent volume assignment
|
||||||
|
self.files.sort()
|
||||||
|
|
||||||
if self.bulkMode:
|
if self.bulkMode:
|
||||||
firstFile = self.files[0]
|
firstFile = self.files[0]
|
||||||
self.parser = metadata.MetadataParser(firstFile)
|
self.parser = metadata.MetadataParser(firstFile)
|
||||||
@@ -1429,6 +1432,10 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
self.okButton.setEnabled(True)
|
self.okButton.setEnabled(True)
|
||||||
self.statusLabel.setText(f'Editing {len(self.files)} files.')
|
self.statusLabel.setText(f'Editing {len(self.files)} files.')
|
||||||
|
|
||||||
|
# Show bulk volume checkbox
|
||||||
|
self.bulkVolumeCheck.setVisible(True)
|
||||||
|
self.bulkVolumeCheck.setChecked(False)
|
||||||
|
|
||||||
for field in (self.volumeLine, self.numberLine, self.titleLine):
|
for field in (self.volumeLine, self.numberLine, self.titleLine):
|
||||||
field.setEnabled(False)
|
field.setEnabled(False)
|
||||||
field.setText('')
|
field.setText('')
|
||||||
@@ -1447,6 +1454,9 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
file = self.files[0]
|
file = self.files[0]
|
||||||
self.parser = metadata.MetadataParser(file)
|
self.parser = metadata.MetadataParser(file)
|
||||||
|
|
||||||
|
# Hide bulk volume checkbox in single file mode
|
||||||
|
self.bulkVolumeCheck.setVisible(False)
|
||||||
|
|
||||||
for field in (self.volumeLine, self.numberLine, self.titleLine, self.seriesLine,
|
for field in (self.volumeLine, self.numberLine, self.titleLine, self.seriesLine,
|
||||||
self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
self.writerLine, self.pencillerLine, self.inkerLine, self.coloristLine):
|
||||||
field.setEnabled(True)
|
field.setEnabled(True)
|
||||||
@@ -1486,7 +1496,16 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
if tmpData:
|
if tmpData:
|
||||||
bulkData[fieldName] = tmpData
|
bulkData[fieldName] = tmpData
|
||||||
|
|
||||||
if not bulkData:
|
# Handle bulk volume editing
|
||||||
|
volumes = None
|
||||||
|
if self.bulkVolumeCheck.isChecked():
|
||||||
|
volumeText = self.volumeLine.text()
|
||||||
|
volumes, error = self.parseVolumeInput(volumeText, len(self.files))
|
||||||
|
if error:
|
||||||
|
self.statusLabel.setText(error)
|
||||||
|
return
|
||||||
|
|
||||||
|
if not bulkData and volumes is None:
|
||||||
self.statusLabel.setText('No changes to apply.')
|
self.statusLabel.setText('No changes to apply.')
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -1506,6 +1525,9 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
continue
|
continue
|
||||||
for key, value in bulkData.items():
|
for key, value in bulkData.items():
|
||||||
parser.data[key] = value
|
parser.data[key] = value
|
||||||
|
# Set volume if bulk volume editing is enabled
|
||||||
|
if volumes is not None:
|
||||||
|
parser.data['Volume'] = str(volumes[i - 1])
|
||||||
parser.saveXML()
|
parser.saveXML()
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
errors.append(f'{os.path.basename(file)}: {str(err)}')
|
errors.append(f'{os.path.basename(file)}: {str(err)}')
|
||||||
@@ -1548,6 +1570,60 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
def cleanData(self, s):
|
def cleanData(self, s):
|
||||||
return escape(s.strip())
|
return escape(s.strip())
|
||||||
|
|
||||||
|
def parseVolumeInput(self, text, fileCount):
|
||||||
|
"""Parse volume input and return list of volume numbers.
|
||||||
|
Supports: single number (5), range (5-10), comma list (1,3,5)
|
||||||
|
Returns (volumes_list, error_message) tuple.
|
||||||
|
"""
|
||||||
|
text = text.strip()
|
||||||
|
if not text:
|
||||||
|
return None, None
|
||||||
|
|
||||||
|
volumes = []
|
||||||
|
|
||||||
|
# Check if it's a range (e.g., "5-10")
|
||||||
|
if '-' in text and ',' not in text:
|
||||||
|
parts = text.split('-')
|
||||||
|
if len(parts) == 2:
|
||||||
|
try:
|
||||||
|
start = int(parts[0].strip())
|
||||||
|
end = int(parts[1].strip())
|
||||||
|
if start <= end:
|
||||||
|
volumes = list(range(start, end + 1))
|
||||||
|
else:
|
||||||
|
return None, 'Invalid range: start > end'
|
||||||
|
except ValueError:
|
||||||
|
return None, 'Invalid range format'
|
||||||
|
# Check if it's a comma-separated list (e.g., "1,3,5")
|
||||||
|
elif ',' in text:
|
||||||
|
try:
|
||||||
|
volumes = [int(v.strip()) for v in text.split(',') if v.strip()]
|
||||||
|
except ValueError:
|
||||||
|
return None, 'Invalid list format'
|
||||||
|
# Single number - generate sequence starting from that number
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
start = int(text)
|
||||||
|
volumes = list(range(start, start + fileCount))
|
||||||
|
except ValueError:
|
||||||
|
return None, 'Invalid number'
|
||||||
|
|
||||||
|
# Validate count
|
||||||
|
if volumes and len(volumes) != fileCount:
|
||||||
|
return None, f'Volume count ({len(volumes)}) ≠ file count ({fileCount})'
|
||||||
|
|
||||||
|
return volumes, None
|
||||||
|
|
||||||
|
def toggleBulkVolume(self, checked):
|
||||||
|
"""Toggle volume field enabled state based on checkbox."""
|
||||||
|
self.volumeLine.setEnabled(checked)
|
||||||
|
if checked:
|
||||||
|
self.volumeLine.setText('')
|
||||||
|
self.volumeLine.setPlaceholderText('e.g., 5 or 1-10 or 1,3,5')
|
||||||
|
else:
|
||||||
|
self.volumeLine.setText('')
|
||||||
|
self.volumeLine.setPlaceholderText('(multiple files)')
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.ui = QDialog()
|
self.ui = QDialog()
|
||||||
self.parser = None
|
self.parser = None
|
||||||
@@ -1555,6 +1631,26 @@ class KCCGUI_MetaEditor(KCC_ui_editor.Ui_editorDialog):
|
|||||||
self.bulkMode = False
|
self.bulkMode = False
|
||||||
self.setupUi(self.ui)
|
self.setupUi(self.ui)
|
||||||
self.ui.setWindowFlags(self.ui.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
|
self.ui.setWindowFlags(self.ui.windowFlags() & ~Qt.WindowType.WindowContextHelpButtonHint)
|
||||||
|
|
||||||
|
# Create bulk volume editing widgets
|
||||||
|
self.bulkVolumeCheck = QCheckBox()
|
||||||
|
self.bulkVolumeCheck.setToolTip(
|
||||||
|
'<b>Bulk Volume Editing</b><br>'
|
||||||
|
'Check this box to assign volume numbers to multiple files.<br><br>'
|
||||||
|
'<b>Input formats:</b><br>'
|
||||||
|
'<code>5</code> → sequence starting from 5 (5, 6, 7...)<br>'
|
||||||
|
'<code>1-10</code> → range from 1 to 10<br>'
|
||||||
|
'<code>1, 3, 5</code> → specific values<br><br>'
|
||||||
|
'<i>Note: Files are sorted alphabetically before assignment.</i>'
|
||||||
|
)
|
||||||
|
self.bulkVolumeCheck.stateChanged.connect(self.toggleBulkVolume)
|
||||||
|
|
||||||
|
# Add widget to the grid layout at row 1 (Volume row), column 2
|
||||||
|
self.gridLayout.addWidget(self.bulkVolumeCheck, 1, 2, 1, 1)
|
||||||
|
|
||||||
|
# Hide by default (only shown in bulk mode)
|
||||||
|
self.bulkVolumeCheck.setVisible(False)
|
||||||
|
|
||||||
self.okButton.clicked.connect(self.saveData)
|
self.okButton.clicked.connect(self.saveData)
|
||||||
self.cancelButton.clicked.connect(self.ui.close)
|
self.cancelButton.clicked.connect(self.ui.close)
|
||||||
if sys.platform.startswith('linux'):
|
if sys.platform.startswith('linux'):
|
||||||
|
|||||||
Reference in New Issue
Block a user