SXXXXXXX_RepoSync/reposync/gui/profile_dialog.py
2025-07-07 11:09:32 +02:00

200 lines
6.9 KiB
Python

# RepoSync/gui/profile_dialog.py
"""
A Toplevel dialog window for managing server connection profiles.
"""
import tkinter as tk
from tkinter import ttk, messagebox
from typing import Dict
from ..config.profile_manager import ProfileManager
class ProfileDialog(tk.Toplevel):
"""
A modal dialog for adding, editing, and deleting server profiles.
"""
def __init__(self, parent, profile_manager: ProfileManager):
super().__init__(parent)
self.parent = parent
self.profile_manager = profile_manager
self.title("Manage Server Profiles")
self.geometry("800x400")
# Make the window modal
self.transient(parent)
self.grab_set()
# Data variables
self.profile_name = tk.StringVar()
self.server_url = tk.StringVar()
self.token = tk.StringVar()
self.service_type = tk.StringVar()
self._create_widgets()
self._load_profiles_to_listbox()
self.protocol("WM_DELETE_WINDOW", self._on_close)
def _create_widgets(self):
main_frame = ttk.Frame(self, padding="10")
main_frame.pack(expand=True, fill="both")
main_frame.columnconfigure(0, weight=1)
main_frame.columnconfigure(1, weight=2)
main_frame.rowconfigure(0, weight=1)
# --- Left Pane: Profile List ---
list_frame = ttk.LabelFrame(main_frame, text="Profiles", padding="10")
list_frame.grid(row=0, column=0, sticky="nsew", padx=(0, 5))
list_frame.rowconfigure(0, weight=1)
list_frame.columnconfigure(0, weight=1)
self.profile_listbox = tk.Listbox(list_frame, exportselection=False)
self.profile_listbox.grid(row=0, column=0, sticky="nsew")
self.profile_listbox.bind("<<ListboxSelect>>", self._on_profile_select)
scrollbar = ttk.Scrollbar(
list_frame, orient="vertical", command=self.profile_listbox.yview
)
scrollbar.grid(row=0, column=1, sticky="ns")
self.profile_listbox.config(yscrollcommand=scrollbar.set)
# --- Right Pane: Profile Details ---
details_frame = ttk.LabelFrame(main_frame, text="Profile Details", padding="10")
details_frame.grid(row=0, column=1, sticky="nsew", padx=(5, 0))
details_frame.columnconfigure(1, weight=1)
ttk.Label(details_frame, text="Profile Name:").grid(
row=0, column=0, sticky="w", pady=2
)
ttk.Entry(details_frame, textvariable=self.profile_name).grid(
row=0, column=1, sticky="ew", pady=2
)
ttk.Label(details_frame, text="Server URL:").grid(
row=1, column=0, sticky="w", pady=2
)
ttk.Entry(details_frame, textvariable=self.server_url).grid(
row=1, column=1, sticky="ew", pady=2
)
ttk.Label(details_frame, text="Access Token:").grid(
row=2, column=0, sticky="w", pady=2
)
ttk.Entry(details_frame, textvariable=self.token, show="*").grid(
row=2, column=1, sticky="ew", pady=2
)
ttk.Label(details_frame, text="Service Type:").grid(
row=3, column=0, sticky="w", pady=2
)
ttk.Combobox(
details_frame,
textvariable=self.service_type,
values=["gitea", "github"], # Future-proof
state="readonly",
).grid(row=3, column=1, sticky="ew", pady=2)
# --- Buttons ---
button_frame = ttk.Frame(details_frame)
button_frame.grid(row=4, column=0, columnspan=2, pady=10, sticky="e")
ttk.Button(button_frame, text="New", command=self._clear_fields).pack(
side="left", padx=5
)
ttk.Button(button_frame, text="Save", command=self._save_profile).pack(
side="left", padx=5
)
ttk.Button(button_frame, text="Delete", command=self._delete_profile).pack(
side="left", padx=5
)
def _load_profiles_to_listbox(self):
"""Clears and reloads profile names into the listbox."""
self.profile_listbox.delete(0, tk.END)
for name in sorted(self.profile_manager.get_profile_names()):
self.profile_listbox.insert(tk.END, name)
def _on_profile_select(self, event=None):
"""Handles selection of a profile in the listbox."""
selected_indices = self.profile_listbox.curselection()
if not selected_indices:
return
selected_name = self.profile_listbox.get(selected_indices[0])
profile_data = self.profile_manager.get_profile(selected_name)
if profile_data:
self.profile_name.set(selected_name)
self.server_url.set(profile_data.get("url", ""))
self.token.set(profile_data.get("token", ""))
self.service_type.set(profile_data.get("type", "gitea"))
def _clear_fields(self):
"""Clears all entry fields."""
self.profile_name.set("")
self.server_url.set("")
self.token.set("")
self.service_type.set("gitea")
self.profile_listbox.selection_clear(0, tk.END)
def _save_profile(self):
"""Saves the current data as a new or updated profile."""
name = self.profile_name.get().strip()
if not name:
messagebox.showerror(
"Validation Error", "Profile Name cannot be empty.", parent=self
)
return
profile_data = {
"url": self.server_url.get().strip(),
"token": self.token.get().strip(),
"type": self.service_type.get(),
}
self.profile_manager.add_or_update_profile(name, profile_data)
self._load_profiles_to_listbox()
# Reselect the saved profile
for i, item_name in enumerate(self.profile_listbox.get(0, tk.END)):
if item_name == name:
self.profile_listbox.selection_set(i)
self.profile_listbox.see(i)
break
messagebox.showinfo(
"Success", f"Profile '{name}' saved successfully.", parent=self
)
def _delete_profile(self):
"""Deletes the selected profile."""
selected_indices = self.profile_listbox.curselection()
if not selected_indices:
messagebox.showwarning(
"No Selection", "Please select a profile to delete.", parent=self
)
return
name_to_delete = self.profile_listbox.get(selected_indices[0])
if messagebox.askyesno(
"Confirm Delete",
f"Are you sure you want to delete the profile '{name_to_delete}'?",
parent=self,
):
self.profile_manager.delete_profile(name_to_delete)
self._clear_fields()
self._load_profiles_to_listbox()
messagebox.showinfo(
"Success", f"Profile '{name_to_delete}' deleted.", parent=self
)
def _on_close(self):
"""Ensures grab_release is called when the window is closed."""
self.grab_release()
self.destroy()