Abilita selezione multipla nella coda
This commit is contained in:
@@ -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.
|
||||
|
||||
|
||||
@@ -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)
|
||||
self._add_exclude_dir(folder)
|
||||
self.registry.mark_status(self._pending_paths_inside(folder), "ignored")
|
||||
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)
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user