Abilita selezione multipla nella coda

This commit is contained in:
2026-07-02 09:47:57 +02:00
parent 2d837008be
commit ffdbba2655
2 changed files with 34 additions and 16 deletions

View File

@@ -77,8 +77,8 @@ La GUI di configurazione deve permettere di:
- aprire i log giornalieri;
- eventualmente eseguire un test di raggiungibilita' del server;
- visualizzare l'elenco dei file in coda per il backup;
- rimuovere singoli file dalla coda;
- proporre, quando si rimuove un file dalla coda, se rimuovere solo il file o escludere dal backup la cartella che lo contiene.
- rimuovere singoli file o selezioni multiple dalla coda;
- proporre, quando si rimuovono file dalla coda, se rimuovere solo i file selezionati o escludere dal backup le cartelle che li contengono.
Se un computer viene spento senza usare `Backup e spegni`, i file gia' registrati come `pending` devono restare nel registro cambiamenti e accumularsi con quelli del giorno successivo, in modo da poter essere sincronizzati al backup successivo.

View File

@@ -155,7 +155,7 @@ class BakRestConfigApp(ctk.CTk):
refresh_button = ctk.CTkButton(toolbar, text="Aggiorna", command=self._refresh_queue)
refresh_button.grid(row=0, column=1, padx=4)
remove_button = ctk.CTkButton(toolbar, text="Rimuovi selezionato", command=self._remove_selected_pending)
remove_button = ctk.CTkButton(toolbar, text="Rimuovi selezionati", command=self._remove_selected_pending)
remove_button.grid(row=0, column=2, padx=4)
list_frame = ctk.CTkFrame(self.queue_tab)
@@ -163,7 +163,12 @@ class BakRestConfigApp(ctk.CTk):
list_frame.grid_columnconfigure(0, weight=1)
list_frame.grid_rowconfigure(0, weight=1)
self.queue_list = tk.Listbox(list_frame, activestyle="dotbox", exportselection=False)
self.queue_list = tk.Listbox(
list_frame,
activestyle="dotbox",
exportselection=False,
selectmode=tk.EXTENDED,
)
self.queue_list.grid(row=0, column=0, sticky="nsew", padx=(8, 0), pady=8)
scrollbar = ctk.CTkScrollbar(list_frame, command=self.queue_list.yview)
@@ -230,18 +235,21 @@ class BakRestConfigApp(ctk.CTk):
def _remove_selected_pending(self) -> None:
selection = self.queue_list.curselection()
if not selection:
messagebox.showinfo("Bak&Rest", "Seleziona un file dalla coda.")
messagebox.showinfo("Bak&Rest", "Seleziona uno o piu' file dalla coda.")
return
event = self.pending_events[selection[0]]
dialog = RemovePendingDialog(self, event.path)
selected_events = [self.pending_events[index] for index in selection]
dialog = RemovePendingDialog(self, [event.path for event in selected_events])
self.wait_window(dialog)
if dialog.choice == "file":
self.registry.mark_ignored(event.path)
self.registry.mark_status([event.path for event in selected_events], "ignored")
elif dialog.choice == "folder":
folder = str(Path(event.path).parent)
folders = sorted({str(Path(event.path).parent) for event in selected_events})
paths_to_ignore: set[str] = set()
for folder in folders:
self._add_exclude_dir(folder)
self.registry.mark_status(self._pending_paths_inside(folder), "ignored")
paths_to_ignore.update(self._pending_paths_inside(folder))
self.registry.mark_status(sorted(paths_to_ignore), "ignored")
save_config_data(self.data, self.config_path)
self.exclude_dirs.set_items(self.data.setdefault("watch", {}).get("exclude_dirs", []))
else:
@@ -268,25 +276,28 @@ class BakRestConfigApp(ctk.CTk):
class RemovePendingDialog(ctk.CTkToplevel):
def __init__(self, parent: ctk.CTk, file_path: str) -> None:
def __init__(self, parent: ctk.CTk, file_paths: list[str]) -> None:
super().__init__(parent)
self.choice: str | None = None
self.title("Rimuovi dalla coda")
self.geometry("520x190")
self.geometry("560x240")
self.resizable(False, False)
self.transient(parent)
self.grab_set()
self.grid_columnconfigure(0, weight=1)
count = len(file_paths)
subject = "il file selezionato" if count == 1 else f"i {count} file selezionati"
label = ctk.CTkLabel(
self,
text="Vuoi rimuovere solo il file o isolare dal backup la cartella che lo contiene?",
wraplength=470,
text=f"Vuoi rimuovere solo {subject} o isolare dal backup le cartelle che li contengono?",
wraplength=510,
justify="left",
)
label.grid(row=0, column=0, sticky="ew", padx=18, pady=(18, 8))
path_label = ctk.CTkLabel(self, text=file_path, wraplength=470, justify="left")
preview = _selection_preview(file_paths)
path_label = ctk.CTkLabel(self, text=preview, wraplength=510, justify="left")
path_label.grid(row=1, column=0, sticky="ew", padx=18, pady=(0, 16))
buttons = ctk.CTkFrame(self, fg_color="transparent")
@@ -301,6 +312,13 @@ class RemovePendingDialog(ctk.CTkToplevel):
self.destroy()
def _selection_preview(file_paths: list[str]) -> str:
if len(file_paths) <= 3:
return "\n".join(file_paths)
shown = "\n".join(file_paths[:3])
return f"{shown}\n... altri {len(file_paths) - 3} file"
class _ListEditor(ctk.CTkFrame):
def __init__(self, parent: ctk.CTkBaseClass, title: str, browse_dirs: bool = False) -> None:
super().__init__(parent)