mirror of
https://git.hiabuto.net/C3MA/CCMA.git
synced 2026-08-24 22:45:18 +02:00
136 lines
4.7 KiB
Python
136 lines
4.7 KiB
Python
from __future__ import annotations
|
|
|
|
import tkinter as tk
|
|
from pathlib import Path
|
|
from tkinter import filedialog, messagebox
|
|
|
|
from ccma import __version__
|
|
from ccma.config import load_config
|
|
from ccma.domain.dates import setup_system_locale
|
|
from ccma.storage.repository import MemberRepository
|
|
from ccma.ui.main_window import MainWindow
|
|
from ccma.ui.monitors import (
|
|
centered_geometry,
|
|
ensure_visible_geometry,
|
|
monitor_for_geometry,
|
|
preferred_monitor,
|
|
)
|
|
from ccma.ui.splash import SplashScreen, StartupResult
|
|
from ccma.ui.theme import load_theme
|
|
from ccma.ui.window_state import is_maximized, maximize
|
|
|
|
|
|
class CCMAApp(tk.Tk):
|
|
def __init__(self):
|
|
super().__init__()
|
|
setup_system_locale()
|
|
self.config_obj = load_config()
|
|
self.title(f"CCMA · v{__version__}")
|
|
self.minsize(1050, 650)
|
|
load_theme(self, self.config_obj.theme_mode)
|
|
self.startup_monitor = preferred_monitor(self, self.config_obj.monitor_bounds)
|
|
initial_geometry = self.config_obj.window_geometry or centered_geometry(
|
|
1500, 860, self.startup_monitor
|
|
)
|
|
self.geometry(ensure_visible_geometry(initial_geometry, self.startup_monitor))
|
|
self._last_normal_geometry = self.geometry()
|
|
self._geometry_capture_job: str | None = None
|
|
self.bind("<Configure>", self._remember_window_geometry)
|
|
self.protocol("WM_DELETE_WINDOW", self.close)
|
|
self.withdraw()
|
|
self.after_idle(self._start)
|
|
|
|
def _start(self) -> None:
|
|
store_path = self._resolve_store()
|
|
if not store_path:
|
|
self.destroy()
|
|
return
|
|
repository = MemberRepository(store_path)
|
|
try:
|
|
SplashScreen(
|
|
self,
|
|
repository,
|
|
self._startup_complete,
|
|
self._startup_failed,
|
|
run_housekeeper=self.config_obj.run_housekeeper_on_startup,
|
|
monitor=self.startup_monitor,
|
|
housekeeper_settings=self.config_obj.housekeeper_settings(),
|
|
minimum_display_seconds=self.config_obj.splash_minimum_seconds,
|
|
)
|
|
except Exception as exc:
|
|
self._startup_failed(exc)
|
|
|
|
def _resolve_store(self) -> Path | None:
|
|
configured = self.config_obj.store_path.strip()
|
|
if configured:
|
|
return Path(configured).expanduser()
|
|
selected = filedialog.askdirectory(
|
|
parent=self,
|
|
title="Zentrales CCMA-Mitgliederverzeichnis auswählen oder anlegen",
|
|
initialdir=str(Path.home()),
|
|
mustexist=False,
|
|
)
|
|
if not selected:
|
|
return None
|
|
self.config_obj.store_path = selected
|
|
self.config_obj.save()
|
|
return Path(selected)
|
|
|
|
def _startup_complete(self, result: StartupResult) -> None:
|
|
self.deiconify()
|
|
main = MainWindow(
|
|
self,
|
|
result.repository,
|
|
self.config_obj,
|
|
result.findings,
|
|
result.validation_errors,
|
|
)
|
|
main.pack(fill="both", expand=True)
|
|
if self.config_obj.window_state in {"maximized", "zoomed"}:
|
|
self.after_idle(lambda: maximize(self))
|
|
|
|
def _startup_failed(self, error: Exception) -> None:
|
|
self.deiconify()
|
|
messagebox.showerror("CCMA konnte nicht gestartet werden", str(error), parent=self)
|
|
self.destroy()
|
|
|
|
def _remember_window_geometry(self, _event: tk.Event | None = None) -> None:
|
|
if self._geometry_capture_job:
|
|
try:
|
|
self.after_cancel(self._geometry_capture_job)
|
|
except tk.TclError:
|
|
pass
|
|
self._geometry_capture_job = self.after(150, self._capture_normal_geometry)
|
|
|
|
def _capture_normal_geometry(self) -> None:
|
|
self._geometry_capture_job = None
|
|
try:
|
|
if self.state() == "normal" and not is_maximized(self) and self.winfo_viewable():
|
|
self._last_normal_geometry = self.winfo_geometry()
|
|
except tk.TclError:
|
|
return
|
|
|
|
def close(self) -> None:
|
|
try:
|
|
maximized = is_maximized(self)
|
|
current_geometry = self.winfo_geometry()
|
|
if not maximized:
|
|
self._last_normal_geometry = current_geometry
|
|
self.config_obj.window_geometry = self._last_normal_geometry
|
|
self.config_obj.window_state = "maximized" if maximized else "normal"
|
|
monitor = monitor_for_geometry(self, current_geometry)
|
|
self.config_obj.monitor_bounds = monitor.as_tuple()
|
|
self.config_obj.save()
|
|
except (OSError, tk.TclError):
|
|
pass
|
|
self.destroy()
|
|
|
|
|
|
def main() -> None:
|
|
app = CCMAApp()
|
|
app.mainloop()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|