This commit is contained in:
2026-05-28 18:58:38 +02:00
parent 641bb10479
commit 28f429885a
4950 changed files with 933414 additions and 666 deletions

View File

@@ -0,0 +1,357 @@
# -*- coding: utf-8 -*-
"""
AzaOrdnerMixin AzA-Ordner-Fenster (modernes AzA-Design).
Zeigt gespeicherte KG, Briefe, Rezepte, Kostengutsprachen, Diktate, Transkripte.
Doppelklick oeffnet Datei. Fenster bleibt immer offen.
"""
import os
import tkinter as tk
from tkinter import messagebox
from aza_persistence import (
ensure_ablage_dirs,
_ablage_base_path,
load_ordner_geometry,
save_ordner_geometry,
list_ablage_files,
get_ablage_content,
save_to_ablage,
count_entries_older_than,
delete_entries_older_than,
save_autotext,
_clamp_geometry_str,
)
from aza_ui_helpers import center_window, add_resize_grip, add_font_scale_control
from aza_config import ABLAGE_SUBFOLDERS
# ── Design ────────────────────────────────────────────────────────────────────
_WIN_BG = "#EEF4F8"
_HDR_BG = "#1A4D6D"
_HDR_FG = "#FFFFFF"
_HDR_SUB = "#A0C0DC"
_CARD_BG = "#FFFFFF"
_CARD_BD = "#C8D8E8"
_TEXT = "#1A3D55"
_TEXT_SUB = "#607890"
_TAB_ACT = "#1A8ACC"
_TAB_INACT = "#D4E7F5"
_TAB_FG_A = "#FFFFFF"
_TAB_FG_I = "#1A4D6D"
_LB_SEL = "#1A8ACC"
_FF = "Segoe UI"
class AzaOrdnerMixin:
"""Mixin fuer den AzA-Ordner (modernes Design, Doppelklick zum Laden)."""
def open_ordner_window(self):
"""Oeffnet den AzA-Ordner in modernem Design. Bleibt offen bis Benutzer schliesst."""
ensure_ablage_dirs()
base_path = _ablage_base_path()
ORDNER_MIN_W, ORDNER_MIN_H = 660, 560
win = tk.Toplevel(self)
win.title("AzA-Ordner")
win.transient(self)
win.minsize(ORDNER_MIN_W, ORDNER_MIN_H)
win.configure(bg=_WIN_BG)
try:
win.attributes("-alpha", 0.0)
except Exception:
pass
if hasattr(self, "_aza_windows"):
self._aza_windows.add(win)
self._register_window(win)
# Groesse setzen — Position wird nach kurzer Verzoegerung gesetzt,
# damit Windows/transient die Positionierung nicht ueberschreibt
win.geometry(f"{ORDNER_MIN_W}x{ORDNER_MIN_H}")
def _place_next_to_main():
try:
win.update_idletasks()
sw = win.winfo_screenwidth()
sh = win.winfo_screenheight()
px = self.winfo_rootx()
py = self.winfo_rooty()
pw = self.winfo_width()
ww, wh = ORDNER_MIN_W, ORDNER_MIN_H
x = px + pw + 8
if x + ww > sw:
x = max(0, px - ww - 8)
y = max(0, min(py, sh - wh))
win.geometry(f"{ww}x{wh}+{x}+{y}")
except Exception:
pass
win.after(50, _place_next_to_main)
# Geometrie persistieren
_after_id = [None]
def _save_geom():
try:
save_ordner_geometry(win.geometry())
except Exception:
pass
def _on_configure(e):
if e.widget is not win:
return
if _after_id[0]:
try:
self.after_cancel(_after_id[0])
except Exception:
pass
_after_id[0] = self.after(400, _save_geom)
win.bind("<Configure>", _on_configure)
def _on_close():
_save_geom()
if hasattr(self, "_aza_windows"):
self._aza_windows.discard(win)
win.destroy()
win.protocol("WM_DELETE_WINDOW", _on_close)
# ── Header ────────────────────────────────────────────────────────────
hdr = tk.Frame(win, bg=_HDR_BG)
hdr.pack(fill="x")
hdr_inner = tk.Frame(hdr, bg=_HDR_BG, padx=20, pady=14)
hdr_inner.pack(fill="x")
tk.Label(hdr_inner, text="AzA-Ordner", bg=_HDR_BG, fg=_HDR_FG,
font=(_FF, 13, "bold")).pack(anchor="w")
tk.Label(hdr_inner,
text="Gespeicherte Krankengeschichten, Briefe, Rezepte, Kostengutsprachen, Diktate und Transkripte",
bg=_HDR_BG, fg=_HDR_SUB, font=(_FF, 8),
wraplength=580, justify="left").pack(anchor="w", pady=(2, 0))
tk.Label(hdr_inner, text=base_path, bg=_HDR_BG, fg="#6090B8",
font=(_FF, 7)).pack(anchor="w", pady=(2, 0))
tk.Frame(win, bg=_CARD_BD, height=1).pack(fill="x")
# ── Auto-delete card ──────────────────────────────────────────────────
cb_card = tk.Frame(win, bg=_CARD_BG,
highlightbackground=_CARD_BD, highlightthickness=1,
padx=14, pady=8)
cb_card.pack(fill="x", padx=14, pady=(10, 4))
auto_delete_var = tk.BooleanVar(
value=bool(getattr(self, "_autotext_data", {}).get("ablage_auto_delete_old", True))
)
def _persist_auto_delete():
try:
if hasattr(self, "_autotext_data"):
self._autotext_data["ablage_auto_delete_old"] = bool(auto_delete_var.get())
save_autotext(self._autotext_data)
except Exception:
pass
tk.Checkbutton(
cb_card,
text="Lokale Eintraege nach 2 Wochen automatisch loeschen",
variable=auto_delete_var,
command=_persist_auto_delete,
bg=_CARD_BG, fg=_TEXT, activebackground=_CARD_BG,
activeforeground=_TEXT, selectcolor=_CARD_BG,
font=(_FF, 9), anchor="w",
).pack(anchor="w")
tk.Label(cb_card,
text="(Beim Oeffnen des Ordners wird eine Bestaetigung abgefragt)",
bg=_CARD_BG, fg=_TEXT_SUB, font=(_FF, 8)).pack(anchor="w")
# ── Tab-Leiste ────────────────────────────────────────────────────────
tab_outer = tk.Frame(win, bg=_WIN_BG, padx=14)
tab_outer.pack(fill="x", pady=(6, 0))
tab_bar = tk.Frame(tab_outer, bg=_WIN_BG)
tab_bar.pack(fill="x")
_pages: dict = {}
_tab_btns: dict = {}
_active: list = [None]
# Inhaltsbereich (Karte fuer Listbox)
content_outer = tk.Frame(win, bg=_WIN_BG, padx=14)
content_outer.pack(fill="both", expand=True, pady=(0, 14))
content_card = tk.Frame(content_outer, bg=_CARD_BG,
highlightbackground=_CARD_BD, highlightthickness=1)
content_card.pack(fill="both", expand=True)
for cat in ABLAGE_SUBFOLDERS:
page = tk.Frame(content_card, bg=_CARD_BG, padx=10, pady=8)
_pages[cat] = page
is_first = _active[0] is None
if is_first:
_active[0] = cat
btn = tk.Label(
tab_bar, text=cat, cursor="hand2",
bg=_TAB_ACT if is_first else _TAB_INACT,
fg=_TAB_FG_A if is_first else _TAB_FG_I,
font=(_FF, 9, "bold") if is_first else (_FF, 9),
padx=14, pady=5,
)
btn.pack(side="left", padx=(0, 2))
_tab_btns[cat] = btn
def _switch_tab(name: str):
_active[0] = name
for k, b in _tab_btns.items():
active = k == name
b.configure(
bg=_TAB_ACT if active else _TAB_INACT,
fg=_TAB_FG_A if active else _TAB_FG_I,
font=(_FF, 9, "bold") if active else (_FF, 9),
)
for k, p in _pages.items():
if k == name:
p.pack(fill="both", expand=True)
else:
p.pack_forget()
for cat in ABLAGE_SUBFOLDERS:
_tab_btns[cat].bind("<Button-1>", lambda e, c=cat: _switch_tab(c))
_tab_btns[cat].bind("<Enter>", lambda e, b=_tab_btns[cat], c=cat: (
b.configure(bg=_TAB_ACT) if _active[0] != c else None
))
_tab_btns[cat].bind("<Leave>", lambda e, b=_tab_btns[cat], c=cat: (
b.configure(bg=_TAB_INACT) if _active[0] != c else None
))
# Erste Tab-Seite sichtbar
_pages[_active[0]].pack(fill="both", expand=True)
# ── Pro-Tab: Listbox + Hilfstext ──────────────────────────────────────
listboxes: list = []
def _refresh(lb: tk.Listbox, category: str):
lb.delete(0, "end")
for f in list_ablage_files(category):
disp = f[:-4] if f.endswith(".txt") else f
lb.insert("end", disp)
def _load_file(category: str, filename: str):
"""Laedt Datei in neuem Fenster. Ordner-Fenster bleibt offen."""
content = get_ablage_content(category, filename)
if not content:
messagebox.showinfo("Hinweis", "Datei ist leer oder nicht gefunden.",
parent=win)
return
if category == "KG":
self._show_text_window("KG (geladen)", content, buttons="kg")
self.set_status("KG in neuem Fenster geoeffnet.")
elif category == "Briefe":
self._last_brief_text = content
self._show_text_window("Brief (geladen)", content, buttons="brief")
self.set_status("Brief in neuem Fenster geoeffnet.")
elif category == "Rezepte":
self._last_rezept_text = content
self._show_text_window("Rezept (geladen)", content, buttons="rezept")
self.set_status("Rezept in neuem Fenster geoeffnet.")
elif category == "Kostengutsprachen":
self._last_kogu_text = content
self._show_text_window("KOGU (geladen)", content, buttons="kogu")
self.set_status("KOGU in neuem Fenster geoeffnet.")
elif category in ("Diktat", "Transkript"):
self._show_text_window(f"{category} (geladen)", content, buttons=None)
self.set_status(f"{category} in neuem Fenster geoeffnet.")
for cat in ABLAGE_SUBFOLDERS:
page = _pages[cat]
hint = tk.Label(page,
text="Doppelklick: Datei in neuem Fenster oeffnen",
bg=_CARD_BG, fg=_TEXT_SUB, font=(_FF, 8))
hint.pack(anchor="w", pady=(0, 4))
lb = tk.Listbox(
page, font=(_FF, 10),
bg=_CARD_BG, fg=_TEXT,
selectbackground=_LB_SEL, selectforeground=_HDR_FG,
activestyle="none",
highlightbackground=_CARD_BD, highlightthickness=1,
relief="flat", bd=0,
)
lb.pack(fill="both", expand=True)
_refresh(lb, cat)
listboxes.append({"listbox": lb, "category": cat})
def _on_dblclick(evt, c=cat, lbx=lb):
sel = lbx.curselection()
if not sel:
return
files = list_ablage_files(c)
if 0 <= sel[0] < len(files):
_load_file(c, files[sel[0]])
lb.bind("<Double-Button-1>", _on_dblclick)
# ── Internes Speichern (kein Button, aber Funktion erhalten) ──────────
def _save_current_internal(category: str):
"""Intern erreichbar, kein sichtbarer Button."""
if category == "KG":
content = self.txt_output.get("1.0", "end").strip()
elif category == "Briefe":
content = getattr(self, "_last_brief_text", "")
elif category == "Rezepte":
content = getattr(self, "_last_rezept_text", "")
elif category == "Kostengutsprachen":
content = getattr(self, "_last_kogu_text", "")
elif category in ("Diktat", "Transkript"):
content = self.txt_transcript.get("1.0", "end").strip()
else:
return
if not content:
return
try:
save_to_ablage(category, content)
for lb_info in listboxes:
_refresh(lb_info["listbox"], lb_info["category"])
except Exception:
pass
# ── Auto-Cleanup beim Oeffnen ─────────────────────────────────────────
def _maybe_cleanup():
if not bool(auto_delete_var.get()):
return
total_old = 0
for cat in ABLAGE_SUBFOLDERS:
try:
total_old += int(count_entries_older_than(cat, days=14))
except Exception:
pass
if total_old <= 0:
return
if not messagebox.askyesno(
"Automatisch loeschen",
f"{total_old} Eintraege aelter als 2 Wochen gefunden.\n"
"Jetzt loeschen?",
parent=win,
):
return
deleted = 0
for cat in ABLAGE_SUBFOLDERS:
try:
deleted += int(delete_entries_older_than(cat, days=14))
except Exception:
pass
for lb_info in listboxes:
_refresh(lb_info["listbox"], lb_info["category"])
if deleted > 0:
messagebox.showinfo("Geloescht",
f"{deleted} alte Eintraege wurden geloescht.",
parent=win)
win.after(300, _maybe_cleanup)
# ── Fenster sichtbar machen ───────────────────────────────────────────
try:
win.attributes("-alpha", 1.0)
except Exception:
pass
try:
win.lift()
win.focus_force()
win.after(600, lambda: win.attributes("-topmost", False))
win.attributes("-topmost", True)
except Exception:
pass
add_font_scale_control(win)

File diff suppressed because it is too large Load Diff