SXXXXXXX_GitUtility/gitutility/gui/tabs/submodules_tab.py

222 lines
9.5 KiB
Python

# --- FILE: gitsync_tool/gui/tabs/submodules_tab.py ---
import tkinter as tk
from tkinter import ttk
from typing import Callable, List, Dict, Optional, Any
from gitutility.gui.tooltip import Tooltip
from gitutility.logging_setup import log_handler
class SubmodulesTab(ttk.Frame):
"""
The 'Submodules' tab in the main application notebook.
This tab provides an interface for viewing, adding, updating, and removing
Git submodules within the current repository.
"""
def __init__(self, master: tk.Misc, **kwargs):
"""
Initializes the Submodules tab.
Args:
master: The parent widget (the ttk.Notebook).
**kwargs: Dictionary of callbacks from the main controller.
"""
super().__init__(master, padding=(10, 10))
# Store callbacks by getting them from kwargs
self.refresh_submodules_callback = kwargs.get('refresh_submodules_cb')
self.add_submodule_callback = kwargs.get('add_submodule_cb')
self.init_missing_submodules_callback = kwargs.get('init_missing_submodules_cb')
self.sync_all_submodules_callback = kwargs.get('sync_all_submodules_cb')
self.remove_submodule_callback = kwargs.get('remove_submodule_cb')
self.view_submodule_changes_callback = kwargs.get('view_submodule_changes_cb')
# Get a reference to the main frame for shared components
self.main_frame = self.master.master
# Configure layout
self.columnconfigure(0, weight=1)
self.rowconfigure(1, weight=1)
# Create widgets
self._create_widgets()
def _create_widgets(self) -> None:
"""Creates and arranges all widgets for this tab."""
# --- Actions Frame ---
actions_frame = ttk.LabelFrame(self, text="Submodule Actions", padding=(10, 5))
actions_frame.grid(row=0, column=0, sticky="ew", padx=5, pady=5)
self.refresh_submodules_button = ttk.Button(
actions_frame, text="Refresh List", command=self.refresh_submodules_callback, state=tk.DISABLED
)
self.refresh_submodules_button.pack(side=tk.LEFT, padx=5, pady=5)
Tooltip(self.refresh_submodules_button, "Reload the status of all submodules.")
self.add_submodule_button = ttk.Button(
actions_frame, text="Add Submodule...", command=self.add_submodule_callback, state=tk.DISABLED
)
self.add_submodule_button.pack(side=tk.LEFT, padx=5, pady=5)
Tooltip(self.add_submodule_button, "Add a new submodule to this repository.")
self.init_submodules_button = ttk.Button(
actions_frame, text="Initialize Missing", command=self.init_missing_submodules_callback, state=tk.DISABLED
)
self.init_submodules_button.pack(side=tk.LEFT, padx=5, pady=5)
Tooltip(self.init_submodules_button, "Clone any submodules that are registered but not yet downloaded.")
self.sync_submodules_button = ttk.Button(
actions_frame, text="Sync/Update All", command=self.sync_all_submodules_callback, state=tk.DISABLED
)
self.sync_submodules_button.pack(side=tk.LEFT, padx=5, pady=5)
Tooltip(self.sync_submodules_button, "Update all initialized submodules to the latest commit on their tracked branch and commit the change.")
# --- List Frame ---
list_frame = ttk.LabelFrame(self, text="Repository Submodules", padding=(10, 5))
list_frame.grid(row=1, column=0, sticky="nsew", padx=5, pady=5)
list_frame.rowconfigure(0, weight=1)
list_frame.columnconfigure(0, weight=1)
columns = ("path", "commit", "status")
self.submodules_tree = ttk.Treeview(
list_frame, columns=columns, show="headings", selectmode="browse"
)
self.submodules_tree.heading("path", text="Path", anchor="w")
self.submodules_tree.heading("commit", text="Current Commit", anchor="w")
self.submodules_tree.heading("status", text="Status", anchor="w")
self.submodules_tree.column("path", width=300, stretch=tk.YES, anchor="w")
self.submodules_tree.column("commit", width=120, stretch=tk.NO, anchor="w")
self.submodules_tree.column("status", width=220, stretch=tk.NO, anchor="w")
scrollbar = ttk.Scrollbar(list_frame, orient=tk.VERTICAL, command=self.submodules_tree.yview)
self.submodules_tree.configure(yscrollcommand=scrollbar.set)
self.submodules_tree.grid(row=0, column=0, sticky="nsew")
scrollbar.grid(row=0, column=1, sticky="ns")
self.submodules_tree.bind("<Button-3>", self._show_submodule_context_menu)
Tooltip(self.submodules_tree, "List of registered submodules. Right-click on an item for more options.")
# Tag configuration for status colors
self.submodules_tree.tag_configure("status_ok", foreground="green")
self.submodules_tree.tag_configure("status_warning", foreground="orange")
self.submodules_tree.tag_configure("status_error", foreground="red")
def update_submodules_list(self, submodules_data: Optional[List[Dict[str, str]]]) -> None:
"""Populates the submodules Treeview with the latest status."""
# ... (il codice esistente di questa funzione va cancellato e sostituito)
func_name = "update_submodules_list (GUI)"
for item in self.submodules_tree.get_children():
self.submodules_tree.delete(item)
if submodules_data is None:
self.submodules_tree.insert("", "end", values=("(Error)", "", "Could not retrieve status"), tags=("status_error",))
return
if not submodules_data:
self.submodules_tree.insert("", "end", values=("(No submodules found)", "", ""))
return
for i, sub in enumerate(submodules_data):
path = sub.get("path", "N/A")
commit = sub.get("commit", "N/A")
status_char = sub.get("status_char", "?")
description = sub.get("description", "N/A")
dirty_details = sub.get("dirty_details", "") # <-- Nuovo campo
status_text = "OK"
tag = "status_ok"
if status_char == "-":
status_text = "Not Initialized"
tag = "status_warning"
elif status_char == "U":
status_text = "Merge Conflict"
tag = "status_error"
elif status_char == "+":
status_text = "Ahead (New Commits)"
tag = "status_warning"
# --- NUOVA LOGICA: Controlla le modifiche locali ---
if dirty_details == "modified content":
# Se c'è già uno stato (es. Ahead), aggiungi l'informazione
if status_text != "OK":
status_text += " + Local Changes"
else: # Altrimenti, è lo stato principale
status_text = "Modified (Local Changes)"
tag = "status_warning" # Le modifiche locali sono un avviso
# ------------------------------------------------
# Se lo stato è ancora OK, usiamo la descrizione per più dettagli
if status_text == "OK":
status_text = f"OK ({description})"
self.submodules_tree.insert(
"", "end", iid=i, values=(path, commit[:10], status_text), tags=(tag,)
)
def get_selected_submodule_path(self) -> Optional[str]:
"""Returns the path of the selected submodule in the treeview."""
selection = self.submodules_tree.selection()
if not selection:
return None
item_data = self.submodules_tree.item(selection[0])
values = item_data.get("values")
if values and len(values) > 0:
return values[0]
return None
def _show_submodule_context_menu(self, event: tk.Event) -> None:
"""Shows a context menu for the selected submodule."""
# Select the item under the cursor
iid = self.submodules_tree.identify_row(event.y)
if iid:
self.submodules_tree.selection_set(iid)
else:
return
path = self.get_selected_submodule_path()
if not path:
return
menu = self.main_frame.submodule_context_menu
if not menu:
return
menu.delete(0, tk.END)
# --- NUOVA OPZIONE "VIEW CHANGES" ---
menu.add_command(
label=f"View Local Changes in '{path}'",
command=lambda p=path: self.view_submodule_changes_callback(p) if callable(self.view_submodule_changes_callback) else None
)
menu.add_separator()
# --------------------------------
menu.add_command(
label=f"Remove Submodule '{path}'...",
command=lambda p=path: self.remove_submodule_callback(p) if callable(self.remove_submodule_callback) else None
)
menu.add_separator()
menu.add_command(label="Cancel")
try:
menu.tk_popup(event.x_root, event.y_root)
finally:
menu.grab_release()
def set_action_widgets_state(self, state: str) -> None:
"""Sets the state of all action widgets in this tab."""
widgets_to_toggle = [
self.refresh_submodules_button,
self.add_submodule_button,
self.init_submodules_button,
self.sync_submodules_button,
]
for widget in widgets_to_toggle:
if widget and widget.winfo_exists():
widget.config(state=state)