Files
BEdit/src/bedit_gui/controllers/document_controller.py
2026-07-26 14:46:18 +02:00

125 lines
4.2 KiB
Python

from __future__ import annotations
from pathlib import Path
from typing import Protocol
from PySide6.QtCore import QEvent, QObject
from bedit_gui.documents import Document
from bedit_gui.services.application_logging import get_logger
from bedit_gui.views.dialogs.document_dialogs import (
DocumentDialogs,
SaveChangesChoice,
)
from bedit_gui.views.main_window import MainWindow
logger = get_logger(__name__)
class DocumentDialogProvider(Protocol):
def choose_open_path(self, current_path: Path | None) -> Path | None: ...
def choose_save_path(self, current_path: Path | None) -> Path | None: ...
def ask_save_changes(self) -> SaveChangesChoice: ...
def show_file_error(self, title: str, error: Exception) -> None: ...
class DocumentController(QObject):
"""Coordinates the single-document workflow with the main window."""
def __init__(
self,
document: Document,
window: MainWindow,
dialogs: DocumentDialogProvider | None = None,
) -> None:
super().__init__(window)
self.document = document
self.window = window
self.dialogs = dialogs or DocumentDialogs(window)
window.ui.actionNew_File.triggered.connect(self.new_document)
window.ui.actionOpen_File.triggered.connect(self.open_document)
window.ui.actionSave_File.triggered.connect(self.save_document)
window.ui.actionSave_File_As.triggered.connect(self.save_document_as)
window.ui.actionClose.triggered.connect(window.close)
document.path_changed.connect(self.update_window_title)
document.modified_changed.connect(self.update_window_title)
window.installEventFilter(self)
self.update_window_title()
def new_document(self) -> None:
if self.maybe_save_changes():
self.document.new()
logger.info("Created new document")
def open_document(self) -> None:
if not self.maybe_save_changes():
return
path = self.dialogs.choose_open_path(self.document.path)
if path is None:
return
try:
self.document.open(path)
except (OSError, TypeError, ValueError) as exc:
logger.exception("Could not open document %s", path)
self.dialogs.show_file_error("Could not open document", exc)
else:
logger.info("Opened document: %s", path)
def save_document(self) -> bool:
if self.document.path is None:
return self.save_document_as()
try:
self.document.save()
except (OSError, TypeError, ValueError) as exc:
logger.exception("Could not save document %s", self.document.path)
self.dialogs.show_file_error("Could not save document", exc)
return False
logger.info("Saved document: %s", self.document.path)
return True
def save_document_as(self) -> bool:
path = self.dialogs.choose_save_path(self.document.path)
if path is None:
return False
try:
self.document.save_as(path)
except (OSError, TypeError, ValueError) as exc:
logger.exception("Could not save document as %s", path)
self.dialogs.show_file_error("Could not save document", exc)
return False
logger.info("Saved document as: %s", path)
return True
def maybe_save_changes(self) -> bool:
if not self.document.modified:
return True
choice = self.dialogs.ask_save_changes()
if choice is SaveChangesChoice.SAVE:
return self.save_document()
return choice is SaveChangesChoice.DISCARD
def update_window_title(self, *_args: object) -> None:
name = self.document.path.name if self.document.path else "Untitled"
marker = "*" if self.document.modified else ""
self.window.setWindowTitle(f"{name}{marker} — BEdit")
def eventFilter(self, watched: QObject, event: QEvent) -> bool:
window = getattr(self, "window", None)
if (
watched is window
and event.type() == QEvent.Type.Close
and not self.maybe_save_changes()
):
event.ignore()
return True
return super().eventFilter(watched, event)