from pathlib import Path from PyQt6.QtWidgets import QMessageBox, QDialog from warchron.model.model import Model from warchron.view.view import View from warchron.view.view import PlayerDialog, WarDialog, CampaignDialog class Controller: def __init__(self, model: Model, view: View): self.model = model self.view = view self.current_file: Path | None = None self.selected_war_id = None self.selected_campaign_id = None self.selected_round_id = None self.view.on_close_callback = self.on_app_close self.is_dirty = False self.__connect() self.refresh_players_view() self.refresh_wars_view() self.update_window_title() self.update_actions_state() self.view.on_tree_selection_changed = self.on_tree_selection_changed self.view.on_add_campaign = self.add_campaign self.view.on_add_round = self.add_round def __connect(self): 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.addPlayerBtn.clicked.connect(self.add_player) self.view.addWarBtn.clicked.connect(self.add_war) 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 def new(self): 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.refresh_players_view() self.refresh_wars_view() self.update_window_title() def open_file(self): 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.refresh_players_view() self.refresh_wars_view() self.update_window_title() def save(self): 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): 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 update_window_title(self): base = "WarChron" if self.current_file: base += f" - {self.current_file.name}" else: base += f" - New file" if self.is_dirty: base = base + " *" self.view.setWindowTitle(base) def refresh_players_view(self): players = self.model.get_all_players() self.view.display_players(players) def refresh_wars_view(self): wars = self.model.get_all_wars() self.view.display_wars(wars) def on_tree_selection_changed(self, selection): self.selected_war_id = None self.selected_campaign_id = None self.selected_round_id = None if selection: if selection["type"] == "war": self.selected_war_id = selection["id"] elif selection["type"] == "campaign": self.selected_campaign_id = selection["id"] elif selection["type"] == "round": self.selected_round_id = selection["id"] self.update_actions_state() def update_actions_state(self): self.view.set_add_campaign_enabled(self.selected_war_id is not None) self.view.set_add_round_enabled(self.selected_campaign_id is not None) def add_player(self): dialog = PlayerDialog(self.view) result = dialog.exec() # modal blocking dialog if result == QDialog.DialogCode.Accepted: name = dialog.get_player_name() if not name: QMessageBox.warning( self.view, "Invalid name", "Player name cannot be empty." ) return self.model.add_player(name) self.is_dirty = True self.refresh_players_view() self.update_window_title() def add_war(self): dialog = WarDialog(self.view) result = dialog.exec() # modal blocking dialog if result == QDialog.DialogCode.Accepted: name = dialog.get_war_name() if not name: QMessageBox.warning( self.view, "Invalid name", "War name cannot be empty." ) return self.model.add_war(name) self.is_dirty = True self.refresh_wars_view() self.update_window_title() def add_campaign(self): if not self.selected_war_id: return dialog = CampaignDialog(self.view) if dialog.exec() != QDialog.DialogCode.Accepted: return name = dialog.get_campaign_name() if not name: return self.model.add_campaign(self.selected_war_id, name) self.is_dirty = True self.refresh_wars_view() self.update_window_title() def add_round(self): if not self.selected_campaign_id: return self.model.add_round(self.selected_campaign_id) self.is_dirty = True self.refresh_wars_view() self.update_window_title()