Add Rename to the component right-click menu

Rename is offered from both the canvas and the navigation tree. Names are
checked against Sedona's Component.checkName rules (1-7 ASCII chars, a
letter first, then letters, digits or '_') and must be unique among
siblings. In file mode link paths through the old name are rewritten and
the rename is one undo step; in live mode it goes to the device via Sox
and warns if the device rejects it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-02 20:22:49 +03:00
co-authored by Claude Opus 5.5
parent a2d4be6ea8
commit 088fe69964
4 changed files with 180 additions and 1 deletions
+71
View File
@@ -1387,6 +1387,71 @@ def open_link_dialog(source_element, target_element):
ttk.Button(btn_frame, text="Cancel", width=10, command=dialog.destroy).pack(side=tk.LEFT, padx=5)
def open_rename_dialog(comp):
"""Asks for a new name, checking Sedona's naming rules as it is typed."""
window = app_state.window
root = app_state.xml_root_element
if root is None or comp.tag != "comp":
return
parent = find_parent_element(root, comp)
old_name = comp.get("name", "")
dialog = tk.Toplevel(window)
dialog.title("Rename Component")
dialog.resizable(False, False)
dialog.transient(window)
dialog.grab_set()
dialog.geometry(f"+{window.winfo_x() + 100}+{window.winfo_y() + 60}")
main_frame = tk.Frame(dialog, padx=10, pady=10)
main_frame.pack(fill=tk.BOTH, expand=True)
tk.Label(main_frame, text=f"New name for '{old_name}' [id: {comp.get('id', '?')}]:",
anchor="w").grid(row=0, column=0, sticky="w")
name_var = tk.StringVar(value=old_name)
entry = ttk.Entry(main_frame, textvariable=name_var, width=24)
entry.grid(row=1, column=0, sticky="we", pady=(4, 0))
tk.Label(main_frame, text="1-7 characters: a letter, then letters, digits or '_'.",
anchor="w", fg="#666666").grid(row=2, column=0, sticky="w", pady=(4, 0))
error_var = tk.StringVar(value="")
tk.Label(main_frame, textvariable=error_var, anchor="w", fg="#b00020",
wraplength=320, justify="left").grid(row=3, column=0, sticky="w")
btn_frame = tk.Frame(main_frame)
btn_frame.grid(row=4, column=0, pady=(10, 0))
ok_button = ttk.Button(btn_frame, text="OK", width=10)
ok_button.pack(side=tk.LEFT, padx=5)
ttk.Button(btn_frame, text="Cancel", width=10,
command=dialog.destroy).pack(side=tk.LEFT, padx=5)
def current_error():
name = name_var.get()
if name == old_name:
return None
return sax_file.check_component_name(name, parent, comp)
def on_change(*_):
err = current_error()
error_var.set(err or "")
ok_button.state(["disabled"] if err else ["!disabled"])
def on_ok(*_):
if current_error() is not None:
return
new_name = name_var.get()
dialog.destroy()
# An open panel may show the old name, so it is refreshed along with the sheet.
if new_name != old_name and sax_file.rename_component(comp, new_name):
refresh_all_views()
ok_button.configure(command=on_ok)
name_var.trace_add("write", on_change)
entry.bind("<Return>", on_ok)
dialog.bind("<Escape>", lambda _: dialog.destroy())
entry.focus_set()
entry.select_range(0, tk.END)
def fill_component_popup(menu, comp):
"""Fills a popup with the actions that apply to one component.
@@ -1415,6 +1480,12 @@ def fill_component_popup(menu, comp):
command=lambda: show_user_service(comp),
)
menu.add_separator()
if comp.tag == "comp":
# The app itself is named by its appName property, not by a component name.
menu.add_command(
label="Rename...",
command=lambda: open_rename_dialog(comp),
)
menu.add_command(
label="Delete Component",
command=lambda: delete_component(comp),