diff --git a/game/ato/flight.py b/game/ato/flight.py index 3c7d71ce..5efaee7c 100644 --- a/game/ato/flight.py +++ b/game/ato/flight.py @@ -276,3 +276,15 @@ class Flight(SidcDescribable): self._flight_plan_builder.regenerate() EventStream.put_nowait(GameUpdateEvents().update_flight(self)) + + @staticmethod + def clone_flight(flight: Flight) -> Flight: + return Flight( + flight.package, + flight.country, + flight.squadron, + flight.count, + flight.flight_type, + flight.start_type, + flight.divert, + ) diff --git a/game/ato/package.py b/game/ato/package.py index 68b82daa..e80d23f8 100644 --- a/game/ato/package.py +++ b/game/ato/package.py @@ -214,3 +214,12 @@ class Package: if flight.departure == airfield: return airfield raise RuntimeError("Could not find any airfield assigned to this package") + + @staticmethod + def clone_package(package: Package) -> Package: + clone = Package(package.target, package._db, package.auto_asap) + clone.time_over_target = package.time_over_target + for f in package.flights: + cf = Flight.clone_flight(f) + clone.add_flight(cf) + return clone diff --git a/qt_ui/widgets/ato.py b/qt_ui/widgets/ato.py index 638cf758..61ce06b5 100644 --- a/qt_ui/widgets/ato.py +++ b/qt_ui/widgets/ato.py @@ -23,6 +23,7 @@ from PySide2.QtWidgets import ( QPushButton, QSplitter, QVBoxLayout, + QMessageBox, ) from game.ato.flight import Flight @@ -128,6 +129,19 @@ class QFlightList(QListView): parent=self.window(), ) + def clone_flight(self, index: QModelIndex) -> None: + flight = self.package_model.flight_at_index(index) + try: + clone = Flight.clone_flight(flight) + except ValueError as ve: + QMessageBox.critical( + None, + "Can't clone flight!", + f"Insufficient aircraft to clone flight:\n\n{ve}", + ) + return + self.package_model.add_flight(clone) + def cancel_or_abort_flight(self, index: QModelIndex) -> None: self.package_model.cancel_or_abort_flight_at_index(index) @@ -180,8 +194,12 @@ class QFlightPanel(QGroupBox): self.edit_button.clicked.connect(self.on_edit) self.button_row.addWidget(self.edit_button) + self.clone_button = QPushButton("Clone") + self.clone_button.setProperty("style", "btn-success") + self.clone_button.clicked.connect(self.on_clone) + self.button_row.addWidget(self.clone_button) + self.delete_button = QPushButton("Cancel") - # noinspection PyTypeChecker self.delete_button.setProperty("style", "btn-danger") self.delete_button.clicked.connect(self.on_cancel_flight) self.button_row.addWidget(self.delete_button) @@ -206,6 +224,7 @@ class QFlightPanel(QGroupBox): index = self.flight_list.currentIndex() enabled = index.isValid() self.edit_button.setEnabled(enabled) + self.clone_button.setEnabled(enabled) self.delete_button.setEnabled(enabled) self.change_map_flight_selection(index) delete_text = "Cancel" @@ -230,6 +249,13 @@ class QFlightPanel(QGroupBox): return self.flight_list.edit_flight(index) + def on_clone(self) -> None: + index = self.flight_list.currentIndex() + if not index.isValid(): + logging.error(f"Cannot clone flight when no flight is selected.") + return + self.flight_list.clone_flight(index) + def on_cancel_flight(self) -> None: """Removes the selected flight from the package.""" index = self.flight_list.currentIndex() @@ -306,6 +332,19 @@ class QPackageList(QListView): Dialog.open_edit_package_dialog(self.ato_model.get_package_model(index)) + def clone_package(self, index: QModelIndex) -> None: + package_model = self.ato_model.get_package_model(index) + try: + clone = Package.clone_package(package_model.package) + except ValueError as ve: + QMessageBox.critical( + None, + "Can't clone package!", + f"Insufficient aircraft to clone package:\n\n{ve}", + ) + return + self.ato_model.add_package(clone) + def delete_package(self, index: QModelIndex) -> None: self.ato_model.cancel_or_abort_package_at_index(index) @@ -370,8 +409,12 @@ class QPackagePanel(QGroupBox): self.edit_button.clicked.connect(self.on_edit) self.button_row.addWidget(self.edit_button) + self.clone_button = QPushButton("Clone") + self.clone_button.setProperty("style", "btn-success") + self.clone_button.clicked.connect(self.on_clone) + self.button_row.addWidget(self.clone_button) + self.delete_button = QPushButton("Cancel/abort") - # noinspection PyTypeChecker self.delete_button.setProperty("style", "btn-danger") self.delete_button.clicked.connect(self.on_delete) self.button_row.addWidget(self.delete_button) @@ -389,6 +432,7 @@ class QPackagePanel(QGroupBox): index = self.package_list.currentIndex() enabled = index.isValid() self.edit_button.setEnabled(enabled) + self.clone_button.setEnabled(enabled) self.delete_button.setEnabled(enabled) self.change_map_package_selection(index) @@ -415,6 +459,13 @@ class QPackagePanel(QGroupBox): return self.package_list.edit_package(index) + def on_clone(self) -> None: + index = self.package_list.currentIndex() + if not index.isValid(): + logging.error(f"Cannot clone package when no package is selected.") + return + self.package_list.clone_package(index) + def on_delete(self) -> None: """Removes the package from the ATO.""" index = self.package_list.currentIndex() diff --git a/resources/stylesheets/style-dcs.css b/resources/stylesheets/style-dcs.css index 79edf3e4..b3726b2b 100644 --- a/resources/stylesheets/style-dcs.css +++ b/resources/stylesheets/style-dcs.css @@ -135,12 +135,12 @@ QPushButton[style="btn-success"] , QPushButton[style="start-button"]{ background-color:#82A466; color: white; border-radius:2px; - font-weight:bold; - text-transform:uppercase; } QPushButton[style="start-button"]{ padding: 8px 30px; + font-weight:bold; + text-transform:uppercase; } QPushButton[style="btn-success"]:hover , QPushButton[style="start-button"]:hover{