Add multi-select delete to Restore Session dialog

- Session list now supports extended selection (Shift+click for range,
  Ctrl+click for individual)
- "Delete Selected" button removes chosen sessions with confirmation
- List refreshes in-place after deletion so you can keep cleaning up
- Added delete_sessions() batch method to database (single transaction)
- Simplified delete_session() to rely on ON DELETE CASCADE

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-12 13:55:37 +01:00
parent 00e393f7b9
commit 893132f110
2 changed files with 76 additions and 18 deletions

View File

@@ -3285,7 +3285,10 @@ class SequenceLinkerUI(QWidget):
QMessageBox.information(self, "No Sessions", "No saved sessions found.")
return
from PyQt6.QtWidgets import QDialog, QDialogButtonBox, QVBoxLayout, QTreeWidget, QTreeWidgetItem, QHeaderView
from PyQt6.QtWidgets import (
QDialog, QDialogButtonBox, QVBoxLayout, QHBoxLayout,
QTreeWidget, QTreeWidgetItem, QHeaderView, QPushButton,
)
dlg = QDialog(self)
dlg.setWindowTitle("Restore Session")
@@ -3297,37 +3300,72 @@ class SequenceLinkerUI(QWidget):
tree.setHeaderLabels(["Date", "Destination", "Files"])
tree.setRootIsDecorated(False)
tree.setAlternatingRowColors(True)
tree.setSelectionMode(QAbstractItemView.SelectionMode.ExtendedSelection)
tree.header().setStretchLastSection(False)
tree.header().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
tree.header().setSectionResizeMode(1, QHeaderView.ResizeMode.Stretch)
tree.header().setSectionResizeMode(2, QHeaderView.ResizeMode.ResizeToContents)
layout.addWidget(tree)
for s in sessions:
date_str = s.created_at.strftime('%Y-%m-%d %H:%M')
dest_short = s.destination
# Shorten long paths
if len(dest_short) > 60:
dest_short = '...' + dest_short[-57:]
item = QTreeWidgetItem([date_str, dest_short, str(s.link_count)])
item.setData(0, Qt.ItemDataRole.UserRole, s)
item.setToolTip(1, s.destination)
tree.addTopLevelItem(item)
def populate_tree():
tree.clear()
for s in self.db.get_sessions():
date_str = s.created_at.strftime('%Y-%m-%d %H:%M')
dest_short = s.destination
if len(dest_short) > 60:
dest_short = '...' + dest_short[-57:]
item = QTreeWidgetItem([date_str, dest_short, str(s.link_count)])
item.setData(0, Qt.ItemDataRole.UserRole, s)
item.setToolTip(1, s.destination)
tree.addTopLevelItem(item)
if tree.topLevelItemCount() > 0:
tree.setCurrentItem(tree.topLevelItem(0))
def delete_selected():
selected_items = tree.selectedItems()
if not selected_items:
return
count = len(selected_items)
label = "session" if count == 1 else "sessions"
reply = QMessageBox.question(
dlg, "Delete Sessions",
f"Delete {count} {label}? This cannot be undone.",
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
)
if reply != QMessageBox.StandardButton.Yes:
return
ids = []
for item in selected_items:
s = item.data(0, Qt.ItemDataRole.UserRole)
if s is not None:
ids.append(s.id)
if ids:
try:
self.db.delete_sessions(ids)
except Exception as e:
QMessageBox.critical(dlg, "Error", f"Failed to delete:\n{e}")
return
populate_tree()
populate_tree()
btn_layout = QHBoxLayout()
delete_btn = QPushButton("Delete Selected")
delete_btn.clicked.connect(delete_selected)
btn_layout.addWidget(delete_btn)
btn_layout.addStretch()
buttons = QDialogButtonBox(
QDialogButtonBox.StandardButton.Ok | QDialogButtonBox.StandardButton.Cancel
)
buttons.accepted.connect(dlg.accept)
buttons.rejected.connect(dlg.reject)
layout.addWidget(buttons)
btn_layout.addWidget(buttons)
layout.addLayout(btn_layout)
# Double-click to accept
tree.itemDoubleClicked.connect(dlg.accept)
# Select first item
if tree.topLevelItemCount() > 0:
tree.setCurrentItem(tree.topLevelItem(0))
if dlg.exec() != QDialog.DialogCode.Accepted:
return