# -*- coding: utf-8 -*- """ AzaOrdnerMixin – Ordner-Fenster (Ablage: KG, Briefe, Rezepte, KOGU, Diktat; Export/Import). """ import os import tkinter as tk from tkinter import ttk, 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, RoundedButton from aza_config import ABLAGE_SUBFOLDERS class AzaOrdnerMixin: """Mixin für das Ordner-/Ablage-Fenster (Speichern, Laden, Export/Import).""" def open_ordner_window(self): """Fenster für Ablage: KG, Briefe, Rezepte, Kostengutsprachen in Unterordnern; Export/Import. Bleibt sichtbar bis es geschlossen wird.""" ensure_ablage_dirs() base_path = _ablage_base_path() ORDNER_MIN_W, ORDNER_MIN_H = 750, 600 win = tk.Toplevel(self) win.title("Ordner – Ablage & Export/Import") win.minsize(ORDNER_MIN_W, ORDNER_MIN_H) win.configure(bg="#B9ECFA") win.attributes("-topmost", True) if hasattr(self, "_aza_windows"): self._aza_windows.add(win) self._register_window(win) # Fensterposition: gespeichert laden oder zentrieren saved_geom = load_ordner_geometry() if saved_geom: try: win.geometry(_clamp_geometry_str(saved_geom, ORDNER_MIN_W, ORDNER_MIN_H)) except Exception: win.geometry("800x650") center_window(win, 800, 650) else: # Keine gespeicherte Position → zentrieren win.geometry("640x500") center_window(win, 640, 500) def save_ordner_geom(): try: save_ordner_geometry(win.geometry()) except Exception: pass _ordner_geom_after_id = [None] def on_ordner_configure(e): if e.widget is win and _ordner_geom_after_id[0]: self.after_cancel(_ordner_geom_after_id[0]) if e.widget is win: _ordner_geom_after_id[0] = self.after(400, save_ordner_geom) win.bind("", on_ordner_configure) def on_ordner_close(): try: save_ordner_geometry(win.geometry()) except Exception: pass if hasattr(self, "_aza_windows"): self._aza_windows.discard(win) win.destroy() win.protocol("WM_DELETE_WINDOW", on_ordner_close) add_resize_grip(win, ORDNER_MIN_W, ORDNER_MIN_H) add_font_scale_control(win) main_f = ttk.Frame(win, padding=12) main_f.pack(fill="both", expand=True) ttk.Label(main_f, text=f"Ablage: {base_path}").pack(anchor="w") auto_delete_var = tk.BooleanVar( value=bool(getattr(self, "_autotext_data", {}).get("ablage_auto_delete_old", True)) ) cb_auto_delete = ttk.Checkbutton( main_f, text="Automatisch löschen nach 2 Wochen (mit Nachfrage)", variable=auto_delete_var, ) cb_auto_delete.pack(anchor="w", pady=(6, 2)) def _persist_auto_delete_pref(): 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 cb_auto_delete.configure(command=_persist_auto_delete_pref) # ─── Tab-Leiste (blaues Design) ─── _tab_style_active = {"bg": "#E8F4FA", "fg": "#1a4d6d", "font": ("Segoe UI", 10, "bold")} _tab_style_inactive = {"bg": "#A8D8E8", "fg": "#5A90B0", "font": ("Segoe UI", 10)} tab_bar = tk.Frame(main_f, bg="#A8D8E8") tab_bar.pack(fill="x", pady=(8, 0)) _ordner_pages = {} _ordner_tab_btns = {} _active_ordner_tab = [None] for cat in ABLAGE_SUBFOLDERS: page = tk.Frame(main_f, bg="#E8F4FA") _ordner_pages[cat] = page style = _tab_style_active if _active_ordner_tab[0] is None else _tab_style_inactive if _active_ordner_tab[0] is None: _active_ordner_tab[0] = cat btn = tk.Label(tab_bar, text=cat, cursor="hand2", padx=14, pady=5, **style) btn.pack(side="left") _ordner_tab_btns[cat] = btn def _switch_ordner_tab(tab_name): _active_ordner_tab[0] = tab_name for key, btn in _ordner_tab_btns.items(): btn.configure(**(_tab_style_active if key == tab_name else _tab_style_inactive)) for key, page in _ordner_pages.items(): page.pack_forget() _ordner_pages[tab_name].pack(fill="both", expand=True) for cat in ABLAGE_SUBFOLDERS: _ordner_tab_btns[cat].bind("", lambda e, c=cat: _switch_ordner_tab(c)) _ordner_pages[_active_ordner_tab[0]].pack(fill="both", expand=True) def refresh_list(listbox, category): listbox.delete(0, "end") for f in list_ablage_files(category): display_name = f[:-4] if (f and str(f).endswith(".txt")) else f listbox.insert("end", display_name) def save_current(category): if category == "KG": content = self.txt_output.get("1.0", "end").strip() if not content: messagebox.showinfo("Hinweis", "Keine Krankengeschichte zum Speichern.") return elif category == "Briefe": content = self._last_brief_text if not content: messagebox.showinfo("Hinweis", "Zuerst einen Brief erstellen (Button Brief).") return elif category == "Rezepte": content = self._last_rezept_text if not content: messagebox.showinfo("Hinweis", "Zuerst ein Rezept erstellen (Button Rezept).") return elif category == "Kostengutsprachen": content = self._last_kogu_text if not content: messagebox.showinfo("Hinweis", "Zuerst eine Kostengutsprache erstellen (Button KOGU).") return elif category == "Diktat": content = self.txt_transcript.get("1.0", "end").strip() if not content: messagebox.showinfo("Hinweis", "Kein Transkript zum Speichern vorhanden.") return elif category == "Transkript": content = self.txt_transcript.get("1.0", "end").strip() if not content: messagebox.showinfo("Hinweis", "Kein Transkript zum Speichern vorhanden.") return else: return try: path = save_to_ablage(category, content) if path: messagebox.showinfo("Gespeichert", f"Gespeichert unter:\n{path}") for lb in listboxes: refresh_list(lb["listbox"], lb["category"]) else: messagebox.showwarning("Hinweis", "Nichts gespeichert (Inhalt war leer).") except Exception as e: messagebox.showerror("Fehler", str(e)) def load_file_into_app(category, filename): content = get_ablage_content(category, filename) if not content: messagebox.showinfo("Hinweis", "Datei ist leer oder nicht gefunden.") return # Immer in neuem Fenster öffnen; Ordner-Fenster bleibt offen if category == "KG": self._show_text_window("KG (geladen)", content, buttons="kg") self.set_status("KG in neuem Fenster geöffnet.") elif category == "Briefe": self._last_brief_text = content self._show_text_window("Brief (geladen)", content, buttons="brief") self.set_status("Brief in neuem Fenster geöffnet.") elif category == "Rezepte": self._last_rezept_text = content self._show_text_window("Rezept (geladen)", content, buttons="rezept") self.set_status("Rezept in neuem Fenster geöffnet.") elif category == "Kostengutsprachen": self._last_kogu_text = content self._show_text_window("KOGU (geladen)", content, buttons="kogu") self.set_status("KOGU in neuem Fenster geöffnet.") elif category == "Diktat": self._show_text_window("Diktat (geladen)", content, buttons=None) self.set_status("Diktat in neuem Fenster geöffnet.") elif category == "Transkript": self._show_text_window("Transkript (geladen)", content, buttons=None) self.set_status("Transkript in neuem Fenster geöffnet.") listboxes = [] for cat in ABLAGE_SUBFOLDERS: page = _ordner_pages[cat] frame = tk.Frame(page, bg="#E8F4FA", padx=8, pady=4) frame.pack(fill="both", expand=True) tk.Label(frame, text="Aktuelles als neue Datei speichern (Nummer + Datum/Uhrzeit):", font=("Segoe UI", 9), bg="#E8F4FA", fg="#1a4d6d").pack(anchor="w") btn_row = tk.Frame(frame, bg="#E8F4FA") btn_row.pack(fill="x", pady=(0, 4)) RoundedButton( btn_row, "Aktuelles speichern", command=lambda c=cat: save_current(c), width=140, height=26, canvas_bg="#E8F4FA", ).pack(side="left") tk.Label(frame, text="Gespeicherte Dateien:", font=("Segoe UI", 9), bg="#E8F4FA", fg="#1a4d6d").pack(anchor="w", pady=(4, 0)) lb = tk.Listbox(frame, height=10, font=("Segoe UI", 10)) lb.pack(fill="both", expand=True, pady=(2, 4)) refresh_list(lb, cat) listboxes.append({"listbox": lb, "category": cat}) def on_select(evt, category=cat, listbox=lb): sel = listbox.curselection() if not sel: return idx = sel[0] files = list_ablage_files(category) if 0 <= idx < len(files): load_file_into_app(category, files[idx]) lb.bind("", on_select) def load_selected(lbx=lb, c=cat): sel = lbx.curselection() if not sel: messagebox.showinfo("Hinweis", "Bitte eine Datei auswählen.") return files = list_ablage_files(c) if 0 <= sel[0] < len(files): load_file_into_app(c, files[sel[0]]) RoundedButton( frame, "Ausgewählte Datei in App laden", command=load_selected, width=220, height=26, canvas_bg="#E8F4FA", ).pack(fill="x", pady=(0, 4)) def _maybe_cleanup_old_entries(): 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( "Auto-Löschen", "Sollen Ablage-Dateien älter als 2 Wochen jetzt gelöscht werden?" ): return total_deleted = 0 for cat in ABLAGE_SUBFOLDERS: try: total_deleted += int(delete_entries_older_than(cat, days=14)) except Exception: pass for lb in listboxes: refresh_list(lb["listbox"], lb["category"]) if total_deleted > 0: messagebox.showinfo("Auto-Löschen", f"{total_deleted} alte Einträge wurden gelöscht.") else: messagebox.showinfo("Auto-Löschen", "Keine Einträge älter als 2 Wochen gefunden.") # Nachfrage beim Öffnen des Fensters (nur wenn Checkbox aktiv). win.after(250, _maybe_cleanup_old_entries) btn_bottom = ttk.Frame(main_f) btn_bottom.pack(fill="x", pady=(8, 0)) def do_export(): from tkinter import filedialog import zipfile dest = filedialog.asksaveasfilename( title="Ablage exportieren (ZIP)", defaultextension=".zip", filetypes=[("ZIP", "*.zip"), ("Alle", "*.*")], ) if not dest: return try: with zipfile.ZipFile(dest, "w", zipfile.ZIP_DEFLATED) as zf: for root, dirs, files in os.walk(base_path): for f in files: if not f.endswith(".txt"): continue path = os.path.join(root, f) arcname = os.path.relpath(path, base_path) zf.write(path, arcname) messagebox.showinfo("Export", f"Exportiert nach:\n{dest}") except Exception as e: messagebox.showerror("Export fehlgeschlagen", str(e)) def do_import(): from tkinter import filedialog import zipfile src = filedialog.askopenfilename( title="ZIP importieren (Inhalt in Ablage entpacken)", filetypes=[("ZIP", "*.zip"), ("Alle", "*.*")], ) if not src: return try: with zipfile.ZipFile(src, "r") as zf: zf.extractall(base_path) messagebox.showinfo("Import", "Import abgeschlossen.") for lb in listboxes: refresh_list(lb["listbox"], lb["category"]) except Exception as e: messagebox.showerror("Import fehlgeschlagen", str(e)) RoundedButton(btn_bottom, "Export (ZIP)", command=do_export, width=120, height=26, canvas_bg="#B9ECFA").pack(side="left", padx=(0, 8)) RoundedButton(btn_bottom, "Import (ZIP)", command=do_import, width=120, height=26, canvas_bg="#B9ECFA").pack(side="left")