@@ -12,15 +12,20 @@ package cz.vutbr.fit.interlockSim.gui
1212
1313import cz.vutbr.fit.interlockSim.context.EditingContext
1414import cz.vutbr.fit.interlockSim.context.JvmEditingContextFactory
15+ import cz.vutbr.fit.interlockSim.context.SimulationContext
16+ import cz.vutbr.fit.interlockSim.context.SimulationContextFactory
1517import cz.vutbr.fit.interlockSim.xml.XMLContextFactory
1618import org.koin.mp.KoinPlatform.getKoin
1719import java.awt.event.ActionEvent
20+ import java.awt.event.KeyEvent
1821import java.io.File
1922import javax.swing.AbstractAction
2023import javax.swing.JFileChooser
2124import javax.swing.JMenu
2225import javax.swing.JMenuBar
26+ import javax.swing.JMenuItem
2327import javax.swing.JOptionPane
28+ import javax.swing.KeyStroke
2429
2530/* *
2631 * Application menu bar with File and Help menus
@@ -265,6 +270,57 @@ class MenuBar : JMenuBar() {
265270 }
266271 }
267272
273+ /* *
274+ * Shows a file chooser, loads the selected XML as a [SimulationContext], sets it on the
275+ * [Frame] and immediately starts the simulation.
276+ */
277+ private inner class StartSimulationAction : AbstractAction (" Start..." ) {
278+ override fun actionPerformed (e : ActionEvent ) {
279+ val fileChooser = JFileChooser (System .getProperty(" user.dir" ))
280+ fileChooser.dialogTitle = " Start Simulation"
281+
282+ val returnValue = fileChooser.showOpenDialog(this @MenuBar)
283+ if (returnValue != JFileChooser .APPROVE_OPTION ) return
284+
285+ val selectedFile: File = fileChooser.selectedFile
286+
287+ try {
288+ val simulationContextFactory = getKoin().get<SimulationContextFactory >()
289+ val simContext = simulationContextFactory.createContext(selectedFile) as SimulationContext
290+ val frame = getKoin().get<Frame >()
291+ frame.setContext(simContext)
292+ frame.startSimulation()
293+ } catch (exception: Exception ) {
294+ JOptionPane .showMessageDialog(
295+ this @MenuBar,
296+ " Failed to start simulation: ${exception.message} \n\n " +
297+ " Ensure the file is a valid railway network XML." ,
298+ " Cannot Start Simulation" ,
299+ JOptionPane .ERROR_MESSAGE
300+ )
301+ }
302+ }
303+ }
304+
305+ /* * Terminates the currently running simulation via [Frame.stopSimulation]. */
306+ private inner class StopSimulationAction : AbstractAction (" Stop" ) {
307+ override fun actionPerformed (e : ActionEvent ) {
308+ val frame = getKoin().get<Frame >()
309+ frame.stopSimulation()
310+ }
311+ }
312+
313+ /* * Sets the simulation speed multiplier via [SimulationController.setSpeed]. */
314+ private inner class SetSpeedAction (
315+ private val label : String ,
316+ private val multiplier : Double ,
317+ ) : AbstractAction(label) {
318+ override fun actionPerformed (e : ActionEvent ) {
319+ val frame = getKoin().get<Frame >()
320+ frame.simulationController.setSpeed(multiplier)
321+ }
322+ }
323+
268324 private inner class InfoAction (
269325 private val infoName : String ,
270326 private val text : String
@@ -276,6 +332,7 @@ class MenuBar : JMenuBar() {
276332
277333 init {
278334 add(fileMenu())
335+ add(simulationMenu())
279336 add(helpMenu())
280337 }
281338
@@ -289,6 +346,37 @@ class MenuBar : JMenuBar() {
289346 return menu
290347 }
291348
349+ /* *
350+ * Builds the "Simulation" menu with Start/Stop actions and a Speed submenu.
351+ *
352+ * Speed presets (0.1x, 0.5x, 1x, 2x, 10x) have keyboard accelerators (keys 1–5)
353+ * so that the user can change the speed without reaching for the mouse during a run.
354+ */
355+ private fun simulationMenu (): JMenu {
356+ val menu = JMenu (" Simulation" )
357+ menu.add(StartSimulationAction ())
358+ menu.add(StopSimulationAction ())
359+ menu.addSeparator()
360+
361+ val speedMenu = JMenu (" Speed" )
362+ val speedPresets =
363+ listOf (
364+ Triple (" 0.1x" , 0.1 , KeyEvent .VK_1 ),
365+ Triple (" 0.5x" , 0.5 , KeyEvent .VK_2 ),
366+ Triple (" 1x" , 1.0 , KeyEvent .VK_3 ),
367+ Triple (" 2x" , 2.0 , KeyEvent .VK_4 ),
368+ Triple (" 10x" , 10.0 , KeyEvent .VK_5 ),
369+ )
370+ for ((label, multiplier, keyCode) in speedPresets) {
371+ val item = JMenuItem (SetSpeedAction (label, multiplier))
372+ item.accelerator = KeyStroke .getKeyStroke(keyCode, 0 )
373+ speedMenu.add(item)
374+ }
375+ menu.add(speedMenu)
376+
377+ return menu
378+ }
379+
292380 private fun helpMenu (): JMenu {
293381 val menu = JMenu (" Help" )
294382 menu.add(
@@ -300,7 +388,16 @@ class MenuBar : JMenuBar() {
300388 " <br><b>Editing:</b><br>" +
301389 " - Left mouse: Insert nodes and join them<br>" +
302390 " - Middle mouse: Delete nodes<br>" +
303- " - Right mouse: Popup menu</html>"
391+ " - Right mouse: Popup menu<br>" +
392+ " <br><b>Simulation:</b><br>" +
393+ " - Simulation > Start...: Load XML and start simulation<br>" +
394+ " - Simulation > Stop: Terminate running simulation<br>" +
395+ " <br><b>Simulation Speed (keyboard shortcuts):</b><br>" +
396+ " - Key 1: 0.1x speed<br>" +
397+ " - Key 2: 0.5x speed<br>" +
398+ " - Key 3: 1x speed (real-time)<br>" +
399+ " - Key 4: 2x speed<br>" +
400+ " - Key 5: 10x speed</html>"
304401 )
305402 )
306403 menu.add(
0 commit comments