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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862
  1. package org.insa.graphs.gui;
  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.SwingUtilities;
  39. import javax.swing.Timer;
  40. import javax.swing.UIManager;
  41. import javax.swing.border.CompoundBorder;
  42. import javax.swing.border.EmptyBorder;
  43. import org.insa.graphs.algorithm.AbstractSolution;
  44. import org.insa.graphs.algorithm.AlgorithmFactory;
  45. import org.insa.graphs.algorithm.carpooling.CarPoolingAlgorithm;
  46. import org.insa.graphs.algorithm.packageswitch.PackageSwitchAlgorithm;
  47. import org.insa.graphs.algorithm.shortestpath.ShortestPathAlgorithm;
  48. import org.insa.graphs.algorithm.shortestpath.ShortestPathData;
  49. import org.insa.graphs.algorithm.shortestpath.ShortestPathSolution;
  50. import org.insa.graphs.algorithm.shortestpath.ShortestPathTextObserver;
  51. import org.insa.graphs.algorithm.weakconnectivity.WeaklyConnectedComponentTextObserver;
  52. import org.insa.graphs.algorithm.weakconnectivity.WeaklyConnectedComponentsAlgorithm;
  53. import org.insa.graphs.algorithm.weakconnectivity.WeaklyConnectedComponentsData;
  54. import org.insa.graphs.gui.AlgorithmPanel.StartActionEvent;
  55. import org.insa.graphs.gui.drawing.BasicGraphPalette;
  56. import org.insa.graphs.gui.drawing.BlackAndWhiteGraphPalette;
  57. import org.insa.graphs.gui.drawing.Drawing;
  58. import org.insa.graphs.gui.drawing.GraphPalette;
  59. import org.insa.graphs.gui.drawing.components.BasicDrawing;
  60. import org.insa.graphs.gui.drawing.components.MapViewDrawing;
  61. import org.insa.graphs.gui.observers.ShortestPathGraphicObserver;
  62. import org.insa.graphs.gui.observers.WeaklyConnectedComponentGraphicObserver;
  63. import org.insa.graphs.gui.utils.FileUtils;
  64. import org.insa.graphs.gui.utils.FileUtils.FolderType;
  65. import org.insa.graphs.model.Graph;
  66. import org.insa.graphs.model.Path;
  67. import org.insa.graphs.model.io.BinaryGraphReader;
  68. import org.insa.graphs.model.io.BinaryPathReader;
  69. import org.insa.graphs.model.io.GraphReader;
  70. import org.insa.graphs.model.io.MapMismatchException;
  71. public class MainWindow extends JFrame {
  72. /**
  73. *
  74. */
  75. private static final long serialVersionUID = 1L;
  76. /**
  77. *
  78. */
  79. private static final String WINDOW_TITLE = "BE Graphes INSA";
  80. /**
  81. *
  82. */
  83. private static final int THREAD_TIMER_DELAY = 1000; // in milliseconds
  84. // Current graph.
  85. protected Graph graph;
  86. // Path to the last opened graph file.
  87. private String graphFilePath;
  88. // Drawing and click adapter.
  89. protected Drawing drawing;
  90. private final MapViewDrawing mapViewDrawing;
  91. private final BasicDrawing basicDrawing;
  92. private final GraphPalette basicPalette, blackAndWhitePalette;
  93. private GraphPalette currentPalette;
  94. // Main panel.
  95. private final JSplitPane mainPanel;
  96. // Algorithm panels
  97. private final List<AlgorithmPanel> algoPanels = new ArrayList<>();
  98. private final AlgorithmPanel wccPanel, spPanel, cpPanel, psPanel;
  99. // Path panel
  100. private final PathsPanel pathPanel;
  101. // List of items that cannot be used without a graph
  102. private final ArrayList<JMenuItem> graphLockItems = new ArrayList<JMenuItem>();
  103. // Label containing the map ID of the current graph.
  104. private JLabel graphInfoPanel;
  105. // Thread information
  106. private Timer threadTimer;
  107. private JPanel threadPanel;
  108. // Log stream and print stream
  109. private StreamCapturer logStream;
  110. private PrintStream printStream;
  111. // Current running thread
  112. private ThreadWrapper currentThread;
  113. // Factory
  114. private BlockingActionFactory baf;
  115. // Observers
  116. private List<DrawingChangeListener> drawingChangeListeners = new ArrayList<>();
  117. private List<GraphChangeListener> graphChangeListeneres = new ArrayList<>();
  118. public MainWindow() {
  119. super(WINDOW_TITLE);
  120. setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
  121. setLayout(new BorderLayout());
  122. setMinimumSize(new Dimension(800, 600));
  123. // Create drawing and action listeners...
  124. this.basicDrawing = new BasicDrawing();
  125. this.mapViewDrawing = new MapViewDrawing();
  126. this.drawing = basicDrawing;
  127. // Createa palettes
  128. this.basicPalette = new BasicGraphPalette();
  129. this.blackAndWhitePalette = new BlackAndWhiteGraphPalette();
  130. this.currentPalette = this.basicPalette;
  131. wccPanel = new AlgorithmPanel(this, WeaklyConnectedComponentsAlgorithm.class,
  132. "Weakly-Connected Components", new String[] {}, false);
  133. wccPanel.addStartActionListener(new ActionListener() {
  134. @Override
  135. public void actionPerformed(ActionEvent e) {
  136. StartActionEvent evt = (StartActionEvent) e;
  137. WeaklyConnectedComponentsData data = new WeaklyConnectedComponentsData(graph);
  138. WeaklyConnectedComponentsAlgorithm wccAlgorithm = null;
  139. try {
  140. wccAlgorithm = (WeaklyConnectedComponentsAlgorithm) AlgorithmFactory
  141. .createAlgorithm(evt.getAlgorithmClass(), data);
  142. }
  143. catch (Exception e1) {
  144. JOptionPane.showMessageDialog(MainWindow.this,
  145. "An error occurred while creating the specified algorithm.",
  146. "Internal error: Algorithm instantiation failure",
  147. JOptionPane.ERROR_MESSAGE);
  148. e1.printStackTrace();
  149. return;
  150. }
  151. wccPanel.setEnabled(false);
  152. if (evt.isGraphicVisualizationEnabled()) {
  153. wccAlgorithm.addObserver(new WeaklyConnectedComponentGraphicObserver(drawing));
  154. }
  155. if (evt.isTextualVisualizationEnabled()) {
  156. wccAlgorithm.addObserver(new WeaklyConnectedComponentTextObserver(printStream));
  157. }
  158. // We love Java...
  159. final WeaklyConnectedComponentsAlgorithm copyAlgorithm = wccAlgorithm;
  160. launchThread(new Runnable() {
  161. @Override
  162. public void run() {
  163. AbstractSolution solution = copyAlgorithm.run();
  164. wccPanel.solutionPanel.addSolution(solution, false);
  165. wccPanel.solutionPanel.setVisible(true);
  166. wccPanel.setEnabled(true);
  167. }
  168. });
  169. }
  170. });
  171. spPanel = new AlgorithmPanel(this, ShortestPathAlgorithm.class, "Shortest-Path",
  172. new String[] { "Origin", "Destination" }, true);
  173. spPanel.addStartActionListener(new ActionListener() {
  174. @Override
  175. public void actionPerformed(ActionEvent e) {
  176. StartActionEvent evt = (StartActionEvent) e;
  177. ShortestPathData data = new ShortestPathData(graph, evt.getNodes().get(0),
  178. evt.getNodes().get(1), evt.getArcFilter());
  179. ShortestPathAlgorithm spAlgorithm = null;
  180. try {
  181. spAlgorithm = (ShortestPathAlgorithm) AlgorithmFactory
  182. .createAlgorithm(evt.getAlgorithmClass(), data);
  183. }
  184. catch (Exception e1) {
  185. JOptionPane.showMessageDialog(MainWindow.this,
  186. "An error occurred while creating the specified algorithm.",
  187. "Internal error: Algorithm instantiation failure",
  188. JOptionPane.ERROR_MESSAGE);
  189. e1.printStackTrace();
  190. return;
  191. }
  192. spPanel.setEnabled(false);
  193. if (evt.isGraphicVisualizationEnabled()) {
  194. spAlgorithm.addObserver(new ShortestPathGraphicObserver(drawing));
  195. }
  196. if (evt.isTextualVisualizationEnabled()) {
  197. spAlgorithm.addObserver(new ShortestPathTextObserver(printStream));
  198. }
  199. final ShortestPathAlgorithm copyAlgorithm = spAlgorithm;
  200. launchThread(new Runnable() {
  201. @Override
  202. public void run() {
  203. // Run the algorithm.
  204. ShortestPathSolution solution = copyAlgorithm.run();
  205. // Add the solution to the solution panel (but do not display
  206. // overlay).
  207. spPanel.solutionPanel.addSolution(solution, false);
  208. // If the solution is feasible, add the path to the path panel.
  209. if (solution.isFeasible()) {
  210. pathPanel.addPath(solution.getPath());
  211. }
  212. // Show the solution panel and enable the shortest-path panel.
  213. spPanel.solutionPanel.setVisible(true);
  214. spPanel.setEnabled(true);
  215. }
  216. });
  217. }
  218. });
  219. cpPanel = new AlgorithmPanel(this, CarPoolingAlgorithm.class, "Car-Pooling", new String[] {
  220. "Origin Car", "Origin Pedestrian", "Destination Car", "Destination Pedestrian" },
  221. true);
  222. psPanel = new AlgorithmPanel(this, PackageSwitchAlgorithm.class, "Car-Pooling",
  223. new String[] { "Oribin A", "Origin B", "Destination A", "Destination B" }, true);
  224. // add algorithm panels
  225. algoPanels.add(wccPanel);
  226. algoPanels.add(spPanel);
  227. algoPanels.add(cpPanel);
  228. algoPanels.add(psPanel);
  229. this.pathPanel = new PathsPanel(this);
  230. // Add click listeners to both drawing.
  231. for (AlgorithmPanel panel: algoPanels) {
  232. this.basicDrawing.addDrawingClickListener(panel.nodesInputPanel);
  233. this.mapViewDrawing.addDrawingClickListener(panel.nodesInputPanel);
  234. this.graphChangeListeneres.add(panel.nodesInputPanel);
  235. this.graphChangeListeneres.add(panel.solutionPanel);
  236. this.drawingChangeListeners.add(panel.nodesInputPanel);
  237. this.drawingChangeListeners.add(panel.solutionPanel);
  238. this.drawingChangeListeners.add(panel);
  239. }
  240. this.graphChangeListeneres.add(pathPanel);
  241. this.drawingChangeListeners.add(pathPanel);
  242. // Create action factory.
  243. this.currentThread = new ThreadWrapper(this);
  244. this.baf = new BlockingActionFactory(this);
  245. this.baf.addAction(currentThread);
  246. // Click adapter
  247. ActionListener openMapActionListener = new ActionListener() {
  248. @Override
  249. public void actionPerformed(ActionEvent e) {
  250. JFileChooser chooser = FileUtils.createFileChooser(FolderType.Map);
  251. if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) {
  252. graphFilePath = chooser.getSelectedFile().getAbsolutePath();
  253. // Note: Don't use a try-resources block since loadGraph is asynchronous.
  254. final 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. // Need to re-validate or the drawing will not have the
  400. // correct size prior to drawing, which can cause issue.
  401. this.revalidate();
  402. }
  403. boolean isNewGraph = newClass == null;
  404. boolean isMapView = (isNewGraph && drawing == mapViewDrawing)
  405. || (!isNewGraph && newClass.equals(MapViewDrawing.class));
  406. // We need to draw MapView, we have to check if the file exists.
  407. File mfile = null;
  408. if (isMapView) {
  409. String mfpath = graphFilePath.substring(0, graphFilePath.lastIndexOf(".map"))
  410. + ".mapfg";
  411. mfile = new File(mfpath);
  412. if (!mfile.exists()) {
  413. if (JOptionPane.showConfirmDialog(this,
  414. "The associated mapsforge (.mapfg) file has not been found, do you want to specify it manually?",
  415. "File not found",
  416. JOptionPane.YES_NO_CANCEL_OPTION) == JOptionPane.YES_OPTION) {
  417. JFileChooser chooser = new JFileChooser(mfile.getParentFile());
  418. if (chooser.showOpenDialog(this) == JFileChooser.APPROVE_OPTION) {
  419. mfile = chooser.getSelectedFile();
  420. }
  421. else {
  422. mfile = null;
  423. }
  424. }
  425. else {
  426. mfile = null;
  427. }
  428. }
  429. }
  430. Runnable runnable = null;
  431. if (isMapView && mfile != null) {
  432. final File mfileFinal = mfile;
  433. // It is a mapview drawing and the file was found, so:
  434. // 1. We create the drawing if necessary.
  435. if (drawing != mapViewDrawing) {
  436. drawing.clear();
  437. drawing = mapViewDrawing;
  438. mainPanel.setLeftComponent(mapViewDrawing);
  439. mainPanel.setDividerLocation(oldLocation);
  440. notifyDrawingLoaded(basicDrawing, mapViewDrawing);
  441. drawing.clear();
  442. isNewGraph = true;
  443. mainPanel.revalidate();
  444. }
  445. if (isNewGraph) {
  446. drawing.clear();
  447. runnable = new Runnable() {
  448. public void run() {
  449. ((MapViewDrawing) drawing).drawGraph(mfileFinal);
  450. notifyRedrawRequest();
  451. }
  452. };
  453. }
  454. }
  455. else if (!isMapView || (isMapView && mfile == null && isNewGraph)) {
  456. if (drawing == mapViewDrawing) {
  457. mapViewDrawing.clear();
  458. drawing = basicDrawing;
  459. mainPanel.setLeftComponent(basicDrawing);
  460. mainPanel.setDividerLocation(oldLocation);
  461. notifyDrawingLoaded(mapViewDrawing, basicDrawing);
  462. isNewGraph = true;
  463. }
  464. if (isNewGraph || palette != this.currentPalette) {
  465. this.currentPalette = palette;
  466. drawing.clear();
  467. runnable = new Runnable() {
  468. public void run() {
  469. drawing.drawGraph(graph, palette);
  470. notifyRedrawRequest();
  471. }
  472. };
  473. }
  474. }
  475. if (runnable != null) {
  476. launchThread(runnable, false);
  477. }
  478. else {
  479. drawing.clearOverlays();
  480. notifyRedrawRequest();
  481. }
  482. }
  483. /**
  484. * @param newClass
  485. */
  486. private void drawGraph(Class<? extends Drawing> newClass) {
  487. drawGraph(newClass, new BasicGraphPalette());
  488. }
  489. /**
  490. *
  491. */
  492. private void drawGraph() {
  493. drawGraph(null, this.currentPalette);
  494. }
  495. private void loadGraph(GraphReader reader) {
  496. launchThread(new Runnable() {
  497. @Override
  498. public void run() {
  499. GraphReaderProgressBar progressBar = new GraphReaderProgressBar(MainWindow.this);
  500. progressBar.setLocationRelativeTo(mainPanel.getLeftComponent());
  501. reader.addObserver(progressBar);
  502. try {
  503. graph = reader.read();
  504. reader.close();
  505. }
  506. catch (Exception exception) {
  507. progressBar.setVisible(false);
  508. progressBar.dispose();
  509. progressBar = null;
  510. JOptionPane.showMessageDialog(MainWindow.this,
  511. "<html><p>Unable to read graph from the selected file:</p><p>"
  512. + exception.getMessage() + "</p>");
  513. exception.printStackTrace(System.out);
  514. return;
  515. }
  516. // In case of....
  517. progressBar.setVisible(false);
  518. progressBar.dispose();
  519. progressBar = null;
  520. String info = graph.getMapId();
  521. if (graph.getMapName() != null && !graph.getMapName().isEmpty()) {
  522. // The \u200e character is the left-to-right mark, we need to avoid issue with
  523. // name that are right-to-left (e.g. arabic names).
  524. info += " - " + graph.getMapName() + "\u200e";
  525. }
  526. info += ", " + graph.size() + " nodes, " + graph.getGraphInformation().getArcCount()
  527. + " arcs.";
  528. graphInfoPanel.setText(info);
  529. drawGraph();
  530. notifyNewGraphLoaded();
  531. for (JMenuItem item: graphLockItems) {
  532. item.setEnabled(true);
  533. }
  534. }
  535. }, false);
  536. }
  537. /**
  538. * Show and enable the given AlgorithmPanel (and hide all others).
  539. *
  540. * @param algorithmPanel
  541. */
  542. private void enableAlgorithmPanel(AlgorithmPanel algorithmPanel) {
  543. int dividerLocation = mainPanel.getDividerLocation();
  544. for (AlgorithmPanel panel: algoPanels) {
  545. panel.setVisible(panel == algorithmPanel);
  546. }
  547. mainPanel.setDividerLocation(dividerLocation);
  548. }
  549. private JMenuBar createMenuBar(ActionListener openMapActionListener) {
  550. // Open Map item...
  551. JMenuItem openMapItem = new JMenuItem("Open Map... ", KeyEvent.VK_O);
  552. openMapItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, ActionEvent.ALT_MASK));
  553. openMapItem.addActionListener(baf.createBlockingAction(openMapActionListener));
  554. // Open Path item...
  555. JMenuItem openPathItem = new JMenuItem("Open Path... ", KeyEvent.VK_P);
  556. openPathItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_P, ActionEvent.ALT_MASK));
  557. openPathItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  558. @Override
  559. public void actionPerformed(ActionEvent e) {
  560. JFileChooser chooser = FileUtils.createFileChooser(FolderType.PathInput);
  561. if (chooser.showOpenDialog(MainWindow.this) == JFileChooser.APPROVE_OPTION) {
  562. try (BinaryPathReader reader = new BinaryPathReader(new DataInputStream(new BufferedInputStream(
  563. new FileInputStream(chooser.getSelectedFile()))))){
  564. Path path = reader.readPath(graph);
  565. pathPanel.addPath(path);
  566. }
  567. catch (MapMismatchException exception) {
  568. JOptionPane.showMessageDialog(MainWindow.this,
  569. "The selected file does not contain a path for the current graph.");
  570. }
  571. catch (IOException e1) {
  572. JOptionPane.showMessageDialog(MainWindow.this,
  573. "Cannot open the selected file.");
  574. }
  575. catch (Exception exception) {
  576. JOptionPane.showMessageDialog(MainWindow.this,
  577. "Unable to read path from the selected file.");
  578. }
  579. }
  580. }
  581. }));
  582. graphLockItems.add(openPathItem);
  583. // Close item
  584. JMenuItem closeItem = new JMenuItem("Quit", KeyEvent.VK_Q);
  585. closeItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_Q, ActionEvent.ALT_MASK));
  586. closeItem.addActionListener(new ActionListener() {
  587. @Override
  588. public void actionPerformed(ActionEvent e) {
  589. MainWindow.this.dispatchEvent(
  590. new WindowEvent(MainWindow.this, WindowEvent.WINDOW_CLOSING));
  591. }
  592. });
  593. // Build the first menu.
  594. JMenu fileMenu = new JMenu("File");
  595. fileMenu.add(openMapItem);
  596. fileMenu.add(openPathItem);
  597. fileMenu.addSeparator();
  598. fileMenu.add(closeItem);
  599. // Second menu
  600. JMenuItem drawGraphItem = new JMenuItem("Redraw", KeyEvent.VK_R);
  601. drawGraphItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, ActionEvent.ALT_MASK));
  602. drawGraphItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  603. @Override
  604. public void actionPerformed(ActionEvent e) {
  605. drawGraph(BasicDrawing.class, basicPalette);
  606. }
  607. }));
  608. graphLockItems.add(drawGraphItem);
  609. JMenuItem drawGraphBWItem = new JMenuItem("Redraw (B&W)", KeyEvent.VK_B);
  610. drawGraphBWItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_B, ActionEvent.ALT_MASK));
  611. drawGraphBWItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  612. @Override
  613. public void actionPerformed(ActionEvent e) {
  614. drawGraph(BasicDrawing.class, blackAndWhitePalette);
  615. }
  616. }));
  617. graphLockItems.add(drawGraphBWItem);
  618. JMenuItem drawGraphMapsforgeItem = new JMenuItem("Redraw (Map)", KeyEvent.VK_M);
  619. drawGraphMapsforgeItem
  620. .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M, ActionEvent.ALT_MASK));
  621. drawGraphMapsforgeItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  622. @Override
  623. public void actionPerformed(ActionEvent e) {
  624. drawGraph(MapViewDrawing.class);
  625. }
  626. }));
  627. graphLockItems.add(drawGraphMapsforgeItem);
  628. JMenu graphMenu = new JMenu("Graph");
  629. graphMenu.add(drawGraphItem);
  630. graphMenu.add(drawGraphBWItem);
  631. graphMenu.addSeparator();
  632. graphMenu.add(drawGraphMapsforgeItem);
  633. // Algo menu
  634. JMenu algoMenu = new JMenu("Algorithms");
  635. // Weakly connected components
  636. JMenuItem wccItem = new JMenuItem("Weakly Connected Components");
  637. wccItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  638. @Override
  639. public void actionPerformed(ActionEvent e) {
  640. enableAlgorithmPanel(wccPanel);
  641. }
  642. }));
  643. // Shortest path
  644. JMenuItem spItem = new JMenuItem("Shortest-Path");
  645. spItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  646. @Override
  647. public void actionPerformed(ActionEvent e) {
  648. enableAlgorithmPanel(spPanel);
  649. }
  650. }));
  651. // Car pooling
  652. JMenuItem cpItem = new JMenuItem("Car Pooling");
  653. cpItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  654. @Override
  655. public void actionPerformed(ActionEvent e) {
  656. enableAlgorithmPanel(cpPanel);
  657. }
  658. }));
  659. // Car pooling
  660. JMenuItem psItem = new JMenuItem("Package Switch");
  661. psItem.addActionListener(baf.createBlockingAction(new ActionListener() {
  662. @Override
  663. public void actionPerformed(ActionEvent e) {
  664. enableAlgorithmPanel(psPanel);
  665. }
  666. }));
  667. graphLockItems.add(wccItem);
  668. graphLockItems.add(spItem);
  669. graphLockItems.add(cpItem);
  670. graphLockItems.add(psItem);
  671. algoMenu.add(wccItem);
  672. algoMenu.addSeparator();
  673. algoMenu.add(spItem);
  674. algoMenu.add(cpItem);
  675. algoMenu.add(psItem);
  676. // Create the menu bar.
  677. JMenuBar menuBar = new JMenuBar();
  678. menuBar.add(fileMenu);
  679. menuBar.add(graphMenu);
  680. menuBar.add(algoMenu);
  681. for (JMenuItem item: graphLockItems) {
  682. item.setEnabled(false);
  683. }
  684. return menuBar;
  685. }
  686. private JPanel createStatusBar() {
  687. // create the status bar panel and shove it down the bottom of the frame
  688. JPanel statusPanel = new JPanel();
  689. statusPanel.setBorder(
  690. new CompoundBorder(BorderFactory.createMatteBorder(1, 0, 0, 0, Color.GRAY),
  691. new EmptyBorder(0, 15, 0, 15)));
  692. statusPanel.setPreferredSize(new Dimension(getWidth(), 38));
  693. statusPanel.setLayout(new BorderLayout());
  694. graphInfoPanel = new JLabel();
  695. graphInfoPanel.setHorizontalAlignment(SwingConstants.LEFT);
  696. statusPanel.add(graphInfoPanel, BorderLayout.WEST);
  697. JLabel threadInfo = new JLabel("Thread running... ");
  698. JLabel threadTimerLabel = new JLabel("00:00:00");
  699. JButton threadButton = new JButton("Stop");
  700. threadButton.addActionListener(new ActionListener() {
  701. @Override
  702. public void actionPerformed(ActionEvent e) {
  703. if (currentThread.isRunning()) {
  704. int confirmed = JOptionPane.showConfirmDialog(MainWindow.this,
  705. "Are you sure you want to kill the running thread?",
  706. "Kill Confirmation", JOptionPane.YES_NO_OPTION);
  707. if (confirmed == JOptionPane.YES_OPTION) {
  708. currentThread.interrupt();
  709. }
  710. }
  711. }
  712. });
  713. threadTimer = new Timer(THREAD_TIMER_DELAY, new ActionListener() {
  714. @Override
  715. public void actionPerformed(ActionEvent e) {
  716. long seconds = currentThread.getDuration().getSeconds();
  717. threadTimerLabel.setText(String.format("%02d:%02d:%02d", seconds / 3600,
  718. seconds / 60 % 60, seconds % 60));
  719. }
  720. });
  721. threadTimer.setInitialDelay(0);
  722. threadPanel = new JPanel();
  723. threadPanel.add(threadInfo);
  724. threadPanel.add(threadTimerLabel);
  725. threadPanel.add(threadButton);
  726. threadPanel.setVisible(false);
  727. statusPanel.add(threadPanel, BorderLayout.EAST);
  728. return statusPanel;
  729. }
  730. public static void main(final String[] args) {
  731. // Try to set system look and feel.
  732. try {
  733. UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
  734. }
  735. catch (Exception e) {
  736. }
  737. SwingUtilities.invokeLater(new Runnable() {
  738. @Override
  739. public void run() {
  740. MainWindow w = new MainWindow();
  741. w.setExtendedState(JFrame.MAXIMIZED_BOTH);
  742. w.setVisible(true);
  743. }
  744. });
  745. }
  746. }