No Description
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

MainWindow.java 32KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852
  1. package org.insa.graphics;
  2. import java.awt.BorderLayout;
  3. import java.awt.Color;
  4. import java.awt.Component;
  5. import java.awt.Dimension;
  6. import java.awt.GridBagConstraints;
  7. import java.awt.GridBagLayout;
  8. import java.awt.event.ActionEvent;
  9. import java.awt.event.ActionListener;
  10. import java.awt.event.KeyEvent;
  11. import java.awt.event.WindowAdapter;
  12. import java.awt.event.WindowEvent;
  13. import java.io.BufferedInputStream;
  14. import java.io.DataInputStream;
  15. import java.io.File;
  16. import java.io.FileInputStream;
  17. import java.io.IOException;
  18. import java.io.PrintStream;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. import javax.swing.BorderFactory;
  22. import javax.swing.Box;
  23. import javax.swing.BoxLayout;
  24. import javax.swing.JButton;
  25. import javax.swing.JFileChooser;
  26. import javax.swing.JFrame;
  27. import javax.swing.JLabel;
  28. import javax.swing.JMenu;
  29. import javax.swing.JMenuBar;
  30. import javax.swing.JMenuItem;
  31. import javax.swing.JOptionPane;
  32. import javax.swing.JPanel;
  33. import javax.swing.JScrollPane;
  34. import javax.swing.JSplitPane;
  35. import javax.swing.JTextArea;
  36. import javax.swing.KeyStroke;
  37. import javax.swing.SwingConstants;
  38. import javax.swing.Timer;
  39. import javax.swing.UIManager;
  40. import javax.swing.border.CompoundBorder;
  41. import javax.swing.border.EmptyBorder;
  42. import org.insa.algo.AbstractSolution;
  43. import org.insa.algo.AlgorithmFactory;
  44. import org.insa.algo.carpooling.CarPoolingAlgorithm;
  45. import org.insa.algo.packageswitch.PackageSwitchAlgorithm;
  46. import org.insa.algo.shortestpath.ShortestPathAlgorithm;
  47. import org.insa.algo.shortestpath.ShortestPathData;
  48. import org.insa.algo.shortestpath.ShortestPathGraphicObserver;
  49. import org.insa.algo.shortestpath.ShortestPathSolution;
  50. import org.insa.algo.shortestpath.ShortestPathTextObserver;
  51. import org.insa.algo.weakconnectivity.WeaklyConnectedComponentGraphicObserver;
  52. import org.insa.algo.weakconnectivity.WeaklyConnectedComponentTextObserver;
  53. import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsAlgorithm;
  54. import org.insa.algo.weakconnectivity.WeaklyConnectedComponentsData;
  55. import org.insa.graph.Graph;
  56. import org.insa.graph.Path;
  57. import org.insa.graph.io.BinaryGraphReader;
  58. import org.insa.graph.io.BinaryPathReader;
  59. import org.insa.graph.io.GraphReader;
  60. import org.insa.graph.io.MapMismatchException;
  61. import org.insa.graphics.AlgorithmPanel.StartActionEvent;
  62. import org.insa.graphics.drawing.BasicGraphPalette;
  63. import org.insa.graphics.drawing.BlackAndWhiteGraphPalette;
  64. import org.insa.graphics.drawing.Drawing;
  65. import org.insa.graphics.drawing.GraphPalette;
  66. import org.insa.graphics.drawing.components.BasicDrawing;
  67. import org.insa.graphics.drawing.components.MapViewDrawing;
  68. import org.insa.graphics.utils.FileUtils;
  69. import org.insa.graphics.utils.FileUtils.FolderType;
  70. public class MainWindow extends JFrame {
  71. /**
  72. *
  73. */
  74. private static final long serialVersionUID = 1L;
  75. /**
  76. *
  77. */
  78. private static final String WINDOW_TITLE = "BE Graphes INSA";
  79. /**
  80. *
  81. */
  82. private static final int THREAD_TIMER_DELAY = 1000; // in milliseconds
  83. // Current graph.
  84. protected Graph graph;
  85. // Path to the last opened graph file.
  86. private String graphFilePath;
  87. // Drawing and click adapter.
  88. protected Drawing drawing;
  89. private final MapViewDrawing mapViewDrawing;
  90. private final BasicDrawing basicDrawing;
  91. private final GraphPalette basicPalette, blackAndWhitePalette;
  92. private GraphPalette currentPalette;
  93. // Main panel.
  94. private final JSplitPane mainPanel;
  95. // Algorithm panels
  96. private final List<AlgorithmPanel> algoPanels = new ArrayList<>();
  97. private final AlgorithmPanel wccPanel, spPanel, cpPanel, psPanel;
  98. // Path panel
  99. private final PathsPanel pathPanel;
  100. // List of items that cannot be used without a graph
  101. private final ArrayList<JMenuItem> graphLockItems = new ArrayList<JMenuItem>();
  102. // Label containing the map ID of the current graph.
  103. private JLabel graphInfoPanel;
  104. // Thread information
  105. private Timer threadTimer;
  106. private JPanel threadPanel;
  107. // Log stream and print stream
  108. private StreamCapturer logStream;
  109. private PrintStream printStream;
  110. // Current running thread
  111. private ThreadWrapper currentThread;
  112. // Factory
  113. private BlockingActionFactory baf;
  114. // Observers
  115. private List<DrawingChangeListener> drawingChangeListeners = new ArrayList<>();
  116. private List<GraphChangeListener> graphChangeListeneres = new ArrayList<>();
  117. public MainWindow() {
  118. super(WINDOW_TITLE);
  119. setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
  120. setLayout(new BorderLayout());
  121. setMinimumSize(new Dimension(800, 600));
  122. // Create drawing and action listeners...
  123. this.basicDrawing = new BasicDrawing();
  124. this.mapViewDrawing = new MapViewDrawing();
  125. this.drawing = basicDrawing;
  126. // Createa palettes
  127. this.basicPalette = new BasicGraphPalette();
  128. this.blackAndWhitePalette = new BlackAndWhiteGraphPalette();
  129. this.currentPalette = this.basicPalette;
  130. wccPanel = new AlgorithmPanel(this, WeaklyConnectedComponentsAlgorithm.class,
  131. "Weakly-Connected Components", new String[] {}, false, false);
  132. wccPanel.addStartActionListener(new ActionListener() {
  133. @Override
  134. public void actionPerformed(ActionEvent e) {
  135. StartActionEvent evt = (StartActionEvent) e;
  136. WeaklyConnectedComponentsData data = new WeaklyConnectedComponentsData(graph);
  137. WeaklyConnectedComponentsAlgorithm wccAlgorithm = null;
  138. try {
  139. wccAlgorithm = (WeaklyConnectedComponentsAlgorithm) AlgorithmFactory
  140. .createAlgorithm(evt.getAlgorithmClass(), data);
  141. }
  142. catch (Exception e1) {
  143. JOptionPane.showMessageDialog(MainWindow.this,
  144. "An error occurred while creating the specified algorithm.",
  145. "Internal error: Algorithm instantiation failure",
  146. JOptionPane.ERROR_MESSAGE);
  147. e1.printStackTrace();
  148. return;
  149. }
  150. wccPanel.setEnabled(false);
  151. if (evt.isGraphicVisualizationEnabled()) {
  152. wccAlgorithm.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing));
  153. }
  154. if (evt.isTextualVisualizationEnabled()) {
  155. wccAlgorithm.addObserver(new WeaklyConnectedComponentTextObserver(printStream));
  156. }
  157. // We love Java...
  158. final WeaklyConnectedComponentsAlgorithm copyAlgorithm = wccAlgorithm;
  159. launchThread(new Runnable() {
  160. @Override
  161. public void run() {
  162. AbstractSolution solution = copyAlgorithm.run();
  163. wccPanel.solutionPanel.addSolution(solution, false);
  164. wccPanel.solutionPanel.setVisible(true);
  165. wccPanel.setEnabled(true);
  166. }
  167. });
  168. }
  169. });
  170. spPanel = new AlgorithmPanel(this, ShortestPathAlgorithm.class, "Shortest-Path",
  171. new String[] { "Origin", "Destination" }, true, true);
  172. spPanel.addStartActionListener(new ActionListener() {
  173. @Override
  174. public void actionPerformed(ActionEvent e) {
  175. StartActionEvent evt = (StartActionEvent) e;
  176. ShortestPathData data = new ShortestPathData(graph, evt.getNodes().get(0),
  177. evt.getNodes().get(1), evt.getMode(), evt.getArcFilter());
  178. ShortestPathAlgorithm spAlgorithm = null;
  179. try {
  180. spAlgorithm = (ShortestPathAlgorithm) AlgorithmFactory
  181. .createAlgorithm(evt.getAlgorithmClass(), data);
  182. }
  183. catch (Exception e1) {
  184. JOptionPane.showMessageDialog(MainWindow.this,
  185. "An error occurred while creating the specified algorithm.",
  186. "Internal error: Algorithm instantiation failure",
  187. JOptionPane.ERROR_MESSAGE);
  188. e1.printStackTrace();
  189. return;
  190. }
  191. spPanel.setEnabled(false);
  192. if (evt.isGraphicVisualizationEnabled()) {
  193. spAlgorithm.addObserver(new ShortestPathGraphicObserver(drawing));
  194. }
  195. if (evt.isTextualVisualizationEnabled()) {
  196. spAlgorithm.addObserver(new ShortestPathTextObserver(printStream));
  197. }
  198. final ShortestPathAlgorithm copyAlgorithm = spAlgorithm;
  199. launchThread(new Runnable() {
  200. @Override
  201. public void run() {
  202. // Run the algorithm.
  203. ShortestPathSolution solution = copyAlgorithm.run();
  204. // Add the solution to the solution panel (but do not display
  205. // overlay).
  206. spPanel.solutionPanel.addSolution(solution, false);
  207. // If the solution is feasible, add the path to the path panel.
  208. if (solution.isFeasible()) {
  209. pathPanel.addPath(solution.getPath());
  210. }
  211. // Show the solution panel and enable the shortest-path panel.
  212. spPanel.solutionPanel.setVisible(true);
  213. spPanel.setEnabled(true);
  214. }
  215. });
  216. }
  217. });
  218. cpPanel = new AlgorithmPanel(
  219. this, CarPoolingAlgorithm.class, "Car-Pooling", new String[] { "Origin Car",
  220. "Origin Pedestrian", "Destination Car", "Destination Pedestrian" },
  221. true, true);
  222. psPanel = new AlgorithmPanel(this, PackageSwitchAlgorithm.class, "Car-Pooling",
  223. new String[] { "Oribin A", "Origin B", "Destination A", "Destination B" }, true,
  224. true);
  225. // add algorithm panels
  226. algoPanels.add(wccPanel);
  227. algoPanels.add(spPanel);
  228. algoPanels.add(cpPanel);
  229. algoPanels.add(psPanel);
  230. this.pathPanel = new PathsPanel(this);
  231. // Add click listeners to both drawing.
  232. for (AlgorithmPanel panel: algoPanels) {
  233. this.basicDrawing.addDrawingClickListener(panel.nodesInputPanel);
  234. this.mapViewDrawing.addDrawingClickListener(panel.nodesInputPanel);
  235. this.graphChangeListeneres.add(panel.nodesInputPanel);
  236. this.graphChangeListeneres.add(panel.solutionPanel);
  237. this.drawingChangeListeners.add(panel.nodesInputPanel);
  238. this.drawingChangeListeners.add(panel.solutionPanel);
  239. this.drawingChangeListeners.add(panel);
  240. }
  241. this.graphChangeListeneres.add(pathPanel);
  242. this.drawingChangeListeners.add(pathPanel);
  243. // Create action factory.
  244. this.currentThread = new ThreadWrapper(this);
  245. this.baf = new BlockingActionFactory(this);
  246. this.baf.addAction(currentThread);
  247. // Click adapter
  248. ActionListener openMapActionListener = new ActionListener() {
  249. @Override
  250. public void actionPerformed(ActionEvent e) {
  251. JFileChooser chooser = FileUtils.createFileChooser(FolderType.Map);
  252. if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) {
  253. graphFilePath = chooser.getSelectedFile().getAbsolutePath();
  254. DataInputStream stream;
  255. try {
  256. stream = new DataInputStream(new BufferedInputStream(
  257. new FileInputStream(chooser.getSelectedFile())));
  258. }
  259. catch (IOException e1) {
  260. JOptionPane.showMessageDialog(MainWindow.this,
  261. "Cannot open the selected file.");
  262. return;
  263. }
  264. loadGraph(new BinaryGraphReader(stream));
  265. }
  266. }
  267. };
  268. setJMenuBar(createMenuBar(openMapActionListener));
  269. // Initial panel to show "Open Map... "
  270. JPanel openPanel = new JPanel();
  271. openPanel.setLayout(new BoxLayout(openPanel, BoxLayout.PAGE_AXIS));
  272. JButton openButton = new JButton("Open Map... ");
  273. openButton.setAlignmentX(Component.CENTER_ALIGNMENT);
  274. openButton.addActionListener(openMapActionListener);
  275. openButton.setFocusPainted(false);
  276. openPanel.add(Box.createVerticalGlue());
  277. openPanel.add(openButton);
  278. openPanel.add(Box.createVerticalGlue());
  279. addWindowListener(new WindowAdapter() {
  280. public void windowClosing(WindowEvent e) {
  281. int confirmed = JOptionPane.showConfirmDialog(MainWindow.this,
  282. "Are you sure you want to close the application?", "Exit Confirmation",
  283. JOptionPane.YES_NO_OPTION);
  284. if (confirmed == JOptionPane.YES_OPTION) {
  285. dispose();
  286. System.exit(0);
  287. }
  288. }
  289. });
  290. // Create graph area
  291. mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT);
  292. JTextArea infoPanel = new JTextArea();
  293. infoPanel.setMinimumSize(new Dimension(200, 50));
  294. infoPanel.setBackground(Color.WHITE);
  295. infoPanel.setLineWrap(true);
  296. infoPanel.setEditable(false);
  297. this.logStream = new StreamCapturer(infoPanel);
  298. this.printStream = new PrintStream(this.logStream);
  299. JPanel rightComponent = new JPanel();
  300. rightComponent.setLayout(new GridBagLayout());
  301. GridBagConstraints c = new GridBagConstraints();
  302. c.gridx = 0;
  303. c.gridy = 0;
  304. c.fill = GridBagConstraints.HORIZONTAL;
  305. rightComponent.add(pathPanel, c);
  306. c.gridy = 1;
  307. for (AlgorithmPanel panel: algoPanels) {
  308. panel.setVisible(false);
  309. rightComponent.add(panel, c);
  310. }
  311. c = new GridBagConstraints();
  312. c.gridx = 0;
  313. c.gridy = 2;
  314. c.weightx = 1;
  315. c.weighty = 1;
  316. c.fill = GridBagConstraints.BOTH;
  317. c.gridheight = GridBagConstraints.REMAINDER;
  318. rightComponent.add(new JScrollPane(infoPanel), c);
  319. mainPanel.setResizeWeight(0.8);
  320. mainPanel.setDividerSize(5);
  321. mainPanel.setBackground(Color.WHITE);
  322. mainPanel.setLeftComponent(openPanel);
  323. mainPanel.setRightComponent(rightComponent);
  324. this.add(mainPanel, BorderLayout.CENTER);
  325. // Top Panel
  326. this.add(createStatusBar(), BorderLayout.SOUTH);
  327. // Notify everythin
  328. notifyDrawingLoaded(null, drawing);
  329. }
  330. /**
  331. * @param runnable
  332. * @param canInterrupt
  333. */
  334. private void launchThread(Runnable runnable, boolean canInterrupt) {
  335. if (canInterrupt) {
  336. currentThread.setThread(new Thread(new Runnable() {
  337. @Override
  338. public void run() {
  339. threadTimer.restart();
  340. threadPanel.setVisible(true);
  341. runnable.run();
  342. clearCurrentThread();
  343. }
  344. }));
  345. }
  346. else {
  347. currentThread.setThread(new Thread(runnable));
  348. }
  349. currentThread.startThread();
  350. }
  351. private void launchThread(Runnable runnable) {
  352. launchThread(runnable, true);
  353. }
  354. protected void clearCurrentThread() {
  355. threadTimer.stop();
  356. threadPanel.setVisible(false);
  357. currentThread.setThread(null);
  358. if (spPanel.isVisible()) {
  359. spPanel.setEnabled(true);
  360. }
  361. }
  362. /**
  363. * Notify all listeners that a new graph has been loaded.
  364. */
  365. private void notifyNewGraphLoaded() {
  366. for (GraphChangeListener listener: graphChangeListeneres) {
  367. listener.newGraphLoaded(graph);
  368. }
  369. }
  370. /**
  371. * Notify all listeners that a new drawing has been set up.
  372. *
  373. * @param oldDrawing
  374. * @param newDrawing
  375. */
  376. private void notifyDrawingLoaded(Drawing oldDrawing, Drawing newDrawing) {
  377. for (DrawingChangeListener listener: drawingChangeListeners) {
  378. listener.onDrawingLoaded(oldDrawing, newDrawing);
  379. }
  380. }
  381. /**
  382. * Notify all listeners that a redraw request is emitted.
  383. */
  384. private void notifyRedrawRequest() {
  385. for (DrawingChangeListener listener: drawingChangeListeners) {
  386. listener.onRedrawRequest();
  387. }
  388. }
  389. /**
  390. * Draw the stored graph on the drawing.
  391. */
  392. private void drawGraph(Class<? extends Drawing> newClass, GraphPalette palette) {
  393. // Save old divider location
  394. int oldLocation = mainPanel.getDividerLocation();
  395. // Set drawing if not set
  396. if (!(mainPanel.getLeftComponent() instanceof Drawing)) {
  397. mainPanel.setLeftComponent((Component) this.drawing);
  398. mainPanel.setDividerLocation(oldLocation);
  399. }
  400. boolean isNewGraph = newClass == null;
  401. boolean isMapView = (isNewGraph && drawing == mapViewDrawing)
  402. || (!isNewGraph && newClass.equals(MapViewDrawing.class));
  403. // We need to draw MapView, we have to check if the file exists.
  404. File mfile = null;
  405. if (isMapView) {
  406. String mfpath = graphFilePath.substring(0, graphFilePath.lastIndexOf(".map"))
  407. + ".mapfg";
  408. mfile = new File(mfpath);
  409. if (!mfile.exists()) {
  410. if (JOptionPane.showConfirmDialog(this,
  411. "The associated mapsforge (.mapfg) file has not been found, do you want to specify it manually?",
  412. "File not found",
  413. JOptionPane.YES_NO_CANCEL_OPTION) == JOptionPane.YES_OPTION) {
  414. JFileChooser chooser = new JFileChooser(mfile.getParentFile());
  415. if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
  416. mfile = chooser.getSelectedFile();
  417. }
  418. else {
  419. mfile = null;
  420. }
  421. }
  422. else {
  423. mfile = null;
  424. }
  425. }
  426. }
  427. Runnable runnable = null;
  428. if (isMapView && mfile != null) {
  429. final File mfileFinal = mfile;
  430. // It is a mapview drawing and the file was found, so:
  431. // 1. We create the drawing if necessary.
  432. if (drawing != mapViewDrawing) {
  433. drawing.clear();
  434. drawing = mapViewDrawing;
  435. mainPanel.setLeftComponent(mapViewDrawing);
  436. mainPanel.setDividerLocation(oldLocation);
  437. notifyDrawingLoaded(basicDrawing, mapViewDrawing);
  438. drawing.clear();
  439. isNewGraph = true;
  440. }
  441. if (isNewGraph) {
  442. drawing.clear();
  443. runnable = new Runnable() {
  444. public void run() {
  445. ((MapViewDrawing) drawing).drawGraph(mfileFinal);
  446. notifyRedrawRequest();
  447. }
  448. };
  449. }
  450. }
  451. else if (!isMapView || (isMapView && mfile == null && isNewGraph)) {
  452. if (drawing == mapViewDrawing) {
  453. mapViewDrawing.clear();
  454. drawing = basicDrawing;
  455. mainPanel.setLeftComponent(basicDrawing);
  456. mainPanel.setDividerLocation(oldLocation);
  457. notifyDrawingLoaded(mapViewDrawing, basicDrawing);
  458. isNewGraph = true;
  459. }
  460. if (isNewGraph || palette != this.currentPalette) {
  461. this.currentPalette = palette;
  462. drawing.clear();
  463. runnable = new Runnable() {
  464. public void run() {
  465. drawing.drawGraph(graph, palette);
  466. notifyRedrawRequest();
  467. }
  468. };
  469. }
  470. }
  471. if (runnable != null) {
  472. launchThread(runnable, false);
  473. }
  474. else {
  475. drawing.clearOverlays();
  476. notifyRedrawRequest();
  477. }
  478. }
  479. /**
  480. * @param newClass
  481. */
  482. private void drawGraph(Class<? extends Drawing> newClass) {
  483. drawGraph(newClass, new BasicGraphPalette());
  484. }
  485. /**
  486. *
  487. */
  488. private void drawGraph() {
  489. drawGraph(null, this.currentPalette);
  490. }
  491. private void loadGraph(GraphReader reader) {
  492. launchThread(new Runnable() {
  493. @Override
  494. public void run() {
  495. GraphReaderProgressBar progressBar = new GraphReaderProgressBar(MainWindow.this);
  496. progressBar.setLocationRelativeTo(mainPanel.getLeftComponent());
  497. reader.addObserver(progressBar);
  498. try {
  499. graph = reader.read();
  500. System.out.flush();
  501. }
  502. catch (Exception exception) {
  503. progressBar.setVisible(false);
  504. progressBar = null;
  505. JOptionPane.showMessageDialog(MainWindow.this,
  506. "Unable to read graph from the selected file.");
  507. exception.printStackTrace(System.out);
  508. return;
  509. }
  510. String info = graph.getMapId();
  511. if (graph.getMapName() != null && !graph.getMapName().isEmpty()) {
  512. // The \u200e character is the left-to-right mark, we need to avoid issue with
  513. // name that are right-to-left (e.g. arabic names).
  514. info += " - " + graph.getMapName() + "\u200e";
  515. }
  516. info += ", " + graph.getNodes().size() + " nodes, "
  517. + graph.getGraphInformation().getArcCount() + " arcs.";
  518. graphInfoPanel.setText(info);
  519. drawGraph();
  520. notifyNewGraphLoaded();
  521. for (JMenuItem item: graphLockItems) {
  522. item.setEnabled(true);
  523. }
  524. }
  525. }, false);
  526. }
  527. /**
  528. * Show and enable the given AlgorithmPanel (and hide all others).
  529. *
  530. * @param algorithmPanel
  531. */
  532. private void enableAlgorithmPanel(AlgorithmPanel algorithmPanel) {
  533. int dividerLocation = mainPanel.getDividerLocation();
  534. for (AlgorithmPanel panel: algoPanels) {
  535. panel.setVisible(panel == algorithmPanel);
  536. }
  537. mainPanel.setDividerLocation(dividerLocation);
  538. }
  539. private JMenuBar createMenuBar(ActionListener openMapActionListener) {
  540. // Open Map item...
  541. JMenuItem openMapItem = new JMenuItem("Open Map... ", KeyEvent.VK_O);
  542. openMapItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK));
  543. openMapItem.addActionListener(baf.createBlockingAction(openMapActionListener));
  544. // Open Path item...
  545. JMenuItem openPathItem = new JMenuItem("Open Path... ", KeyEvent.VK_P);
  546. openPathItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.ALT_MASK));
  547. openPathItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  548. @Override
  549. public void actionPerformed(ActionEvent e) {
  550. JFileChooser chooser = FileUtils.createFileChooser(FolderType.PathInput);
  551. if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) {
  552. BinaryPathReader reader;
  553. try {
  554. reader = new BinaryPathReader(new DataInputStream(new BufferedInputStream(
  555. new FileInputStream(chooser.getSelectedFile()))));
  556. }
  557. catch (IOException e1) {
  558. JOptionPane.showMessageDialog(MainWindow.this,
  559. "Cannot open the selected file.");
  560. return;
  561. }
  562. try {
  563. Path path = reader.readPath(graph);
  564. pathPanel.addPath(path);
  565. }
  566. catch (MapMismatchException exception) {
  567. JOptionPane.showMessageDialog(MainWindow.this,
  568. "The selected file does not contain a path for the current graph.");
  569. return;
  570. }
  571. catch (Exception exception) {
  572. JOptionPane.showMessageDialog(MainWindow.this,
  573. "Unable to read path from the selected file.");
  574. return;
  575. }
  576. }
  577. }
  578. }));
  579. graphLockItems.add(openPathItem);
  580. // Close item
  581. JMenuItem closeItem = new JMenuItem("Quit", KeyEvent.VK_Q);
  582. closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK));
  583. closeItem.addActionListener(new ActionListener() {
  584. @Override
  585. public void actionPerformed(ActionEvent e) {
  586. MainWindow.this.dispatchEvent(
  587. new WindowEvent(MainWindow.this, WindowEvent.WINDOW_CLOSING));
  588. }
  589. });
  590. // Build the first menu.
  591. JMenu fileMenu = new JMenu("File");
  592. fileMenu.add(openMapItem);
  593. fileMenu.add(openPathItem);
  594. fileMenu.addSeparator();
  595. fileMenu.add(closeItem);
  596. // Second menu
  597. JMenuItem drawGraphItem = new JMenuItem("Redraw", KeyEvent.VK_R);
  598. drawGraphItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK));
  599. drawGraphItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  600. @Override
  601. public void actionPerformed(ActionEvent e) {
  602. drawGraph(BasicDrawing.class, basicPalette);
  603. }
  604. }));
  605. graphLockItems.add(drawGraphItem);
  606. JMenuItem drawGraphBWItem = new JMenuItem("Redraw (B&W)", KeyEvent.VK_B);
  607. drawGraphBWItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, ActionEvent.ALT_MASK));
  608. drawGraphBWItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  609. @Override
  610. public void actionPerformed(ActionEvent e) {
  611. drawGraph(BasicDrawing.class, blackAndWhitePalette);
  612. }
  613. }));
  614. graphLockItems.add(drawGraphBWItem);
  615. JMenuItem drawGraphMapsforgeItem = new JMenuItem("Redraw (Map)", KeyEvent.VK_M);
  616. drawGraphMapsforgeItem
  617. .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.ALT_MASK));
  618. drawGraphMapsforgeItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  619. @Override
  620. public void actionPerformed(ActionEvent e) {
  621. drawGraph(MapViewDrawing.class);
  622. }
  623. }));
  624. graphLockItems.add(drawGraphMapsforgeItem);
  625. JMenu graphMenu = new JMenu("Graph");
  626. graphMenu.add(drawGraphItem);
  627. graphMenu.add(drawGraphBWItem);
  628. graphMenu.addSeparator();
  629. graphMenu.add(drawGraphMapsforgeItem);
  630. // Algo menu
  631. JMenu algoMenu = new JMenu("Algorithms");
  632. // Weakly connected components
  633. JMenuItem wccItem = new JMenuItem("Weakly Connected Components");
  634. wccItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  635. @Override
  636. public void actionPerformed(ActionEvent e) {
  637. enableAlgorithmPanel(wccPanel);
  638. }
  639. }));
  640. // Shortest path
  641. JMenuItem spItem = new JMenuItem("Shortest-Path");
  642. spItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  643. @Override
  644. public void actionPerformed(ActionEvent e) {
  645. enableAlgorithmPanel(spPanel);
  646. }
  647. }));
  648. // Car pooling
  649. JMenuItem cpItem = new JMenuItem("Car Pooling");
  650. cpItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  651. @Override
  652. public void actionPerformed(ActionEvent e) {
  653. enableAlgorithmPanel(cpPanel);
  654. }
  655. }));
  656. // Car pooling
  657. JMenuItem psItem = new JMenuItem("Package Switch");
  658. psItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  659. @Override
  660. public void actionPerformed(ActionEvent e) {
  661. enableAlgorithmPanel(psPanel);
  662. }
  663. }));
  664. graphLockItems.add(wccItem);
  665. graphLockItems.add(spItem);
  666. graphLockItems.add(cpItem);
  667. graphLockItems.add(psItem);
  668. algoMenu.add(wccItem);
  669. algoMenu.addSeparator();
  670. algoMenu.add(spItem);
  671. algoMenu.add(cpItem);
  672. algoMenu.add(psItem);
  673. // Create the menu bar.
  674. JMenuBar menuBar = new JMenuBar();
  675. menuBar.add(fileMenu);
  676. menuBar.add(graphMenu);
  677. menuBar.add(algoMenu);
  678. for (JMenuItem item: graphLockItems) {
  679. item.setEnabled(false);
  680. }
  681. return menuBar;
  682. }
  683. private JPanel createStatusBar() {
  684. // create the status bar panel and shove it down the bottom of the frame
  685. JPanel statusPanel = new JPanel();
  686. statusPanel.setBorder(
  687. new CompoundBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY),
  688. new EmptyBorder(0, 15, 0, 15)));
  689. statusPanel.setPreferredSize(new Dimension(getWidth(), 38));
  690. statusPanel.setLayout(new BorderLayout());
  691. graphInfoPanel = new JLabel();
  692. graphInfoPanel.setHorizontalAlignment(SwingConstants.LEFT);
  693. statusPanel.add(graphInfoPanel, BorderLayout.WEST);
  694. JLabel threadInfo = new JLabel("Thread running... ");
  695. JLabel threadTimerLabel = new JLabel("00:00:00");
  696. JButton threadButton = new JButton("Stop");
  697. threadButton.addActionListener(new ActionListener() {
  698. @Override
  699. public void actionPerformed(ActionEvent e) {
  700. if (currentThread.isRunning()) {
  701. int confirmed = JOptionPane.showConfirmDialog(MainWindow.this,
  702. "Are you sure you want to kill the running thread?",
  703. "Kill Confirmation", JOptionPane.YES_NO_OPTION);
  704. if (confirmed == JOptionPane.YES_OPTION) {
  705. currentThread.interrupt();
  706. }
  707. }
  708. }
  709. });
  710. threadTimer = new Timer(THREAD_TIMER_DELAY, new ActionListener() {
  711. @Override
  712. public void actionPerformed(ActionEvent e) {
  713. long seconds = currentThread.getDuration().getSeconds();
  714. threadTimerLabel.setText(String.format("%02d:%02d:%02d", seconds / 3600,
  715. seconds / 60 % 60, seconds % 60));
  716. }
  717. });
  718. threadTimer.setInitialDelay(0);
  719. threadPanel = new JPanel();
  720. threadPanel.add(threadInfo);
  721. threadPanel.add(threadTimerLabel);
  722. threadPanel.add(threadButton);
  723. threadPanel.setVisible(false);
  724. statusPanel.add(threadPanel, BorderLayout.EAST);
  725. return statusPanel;
  726. }
  727. public static void main(final String[] args) {
  728. // Try to set system look and feel.
  729. try {
  730. UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  731. }
  732. catch (Exception e) {
  733. }
  734. MainWindow w = new MainWindow();
  735. w.setExtendedState(JFrame.MAXIMIZED_BOTH);
  736. w.setVisible(true);
  737. }
  738. }