# 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("<>", 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()