from pathlib import Path from PyQt6.QtWidgets import QMessageBox from warchron.model.model import Model from warchron.model.exception import DomainError, RequiresConfirmation from warchron.view.view import View from warchron.constants import ItemType, RefreshScope from warchron.controller.navigation_controller import NavigationController from warchron.controller.player_controller import PlayerController from warchron.controller.war_controller import WarController from warchron.controller.campaign_controller import CampaignController from warchron.controller.round_controller import RoundController class AppController: def __init__(self, model: Model, view: View, version: str) -> None: self.model: Model = model self.view: View = view self.app_version = version self.navigation = NavigationController(self) self.players = PlayerController(self) self.wars = WarController(self) self.campaigns = CampaignController(self) self.rounds = RoundController(self) self.current_file: Path | None = None self.view.on_close_callback = self.on_app_close self.is_dirty: bool = False self.__connect() self.navigation.refresh_players_view() self.navigation.refresh_wars_view() self.update_window_title() self.view.on_tree_selection_changed = self.navigation.on_tree_selection_changed self.view.on_add_campaign = self.campaigns.add_campaign self.view.on_add_round = self.rounds.add_round def __connect(self) -> None: self.view.actionExit.triggered.connect(self.view.close) self.view.actionNew.triggered.connect(self.new) self.view.actionOpen.triggered.connect(self.open_file) self.view.actionSave.triggered.connect(self.save) self.view.actionSave_as.triggered.connect(self.save_as) self.view.actionAbout.triggered.connect(self.show_about) self.view.addPlayerBtn.clicked.connect(self.players.add_player) self.view.addWarBtn.clicked.connect(self.wars.add_war) self.view.majorValue.valueChanged.connect(self.wars.set_major_value) self.view.minorValue.valueChanged.connect(self.wars.set_minor_value) self.view.influenceToken.toggled.connect(self.wars.set_influence_token) self.view.addObjectiveBtn.clicked.connect(self.wars.add_objective) self.view.addWarParticipantBtn.clicked.connect(self.wars.add_war_participant) self.view.endWarBtn.clicked.connect(self.wars.close_war) self.view.addSectorBtn.clicked.connect(self.campaigns.add_sector) self.view.addCampaignParticipantBtn.clicked.connect( self.campaigns.add_campaign_participant ) self.view.endCampaignBtn.clicked.connect(self.campaigns.close_campaign) self.view.endRoundBtn.clicked.connect(self.rounds.close_round) self.view.on_edit_item = self.edit_item self.view.on_delete_item = self.delete_item def on_app_close(self) -> bool: if self.is_dirty: reply = QMessageBox.question( self.view, "Unsaved changes", "You have unsaved changes. Do you want to save before quitting?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No | QMessageBox.StandardButton.Cancel, ) if reply == QMessageBox.StandardButton.Yes: self.save() elif reply == QMessageBox.StandardButton.Cancel: return False return True # Menu bar methods def new(self) -> None: if self.is_dirty: reply = QMessageBox.question( self.view, "Unsaved changes", "Discard current campaign?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, ) if reply != QMessageBox.StandardButton.Yes: return self.model.new() self.current_file = None self.is_dirty = False self.navigation.refresh_players_view() self.navigation.refresh_wars_view() self.update_window_title() def open_file(self) -> None: if self.is_dirty: reply = QMessageBox.question( self.view, "Unsaved changes", "Discard current campaign?", QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No, ) if reply != QMessageBox.StandardButton.Yes: return path = self.view.ask_open_file() if not path: return self.model.load(path) self.current_file = path self.is_dirty = False self.navigation.refresh_players_view() self.navigation.refresh_wars_view() self.update_window_title() def save(self) -> None: if not self.current_file: self.save_as() return self.model.save(self.current_file) self.is_dirty = False self.update_window_title() def save_as(self) -> None: path = self.view.ask_save_file() if not path: return self.current_file = path self.model.save(path) self.is_dirty = False self.update_window_title() def show_about(self) -> None: QMessageBox.about( self.view, "About WarChron", f"""
Version: {self.app_version}
Campaign & War management tool
© 2026 Your Name
Licensed under GNU GPL v3
Icons from Fugue Icons 3.5.6
© Yusuke Kamiyamane
Licensed under Creative Commons Attribution 3.0