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.

PathsPanel.java 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352
  1. package org.insa.graphics;
  2. import java.awt.Color;
  3. import java.awt.Component;
  4. import java.awt.Dimension;
  5. import java.awt.Graphics;
  6. import java.awt.Image;
  7. import java.awt.event.ActionEvent;
  8. import java.awt.event.ActionListener;
  9. import java.awt.event.MouseAdapter;
  10. import java.awt.event.MouseEvent;
  11. import java.io.BufferedOutputStream;
  12. import java.io.DataOutputStream;
  13. import java.io.File;
  14. import java.io.FileOutputStream;
  15. import java.io.IOException;
  16. import javax.swing.BorderFactory;
  17. import javax.swing.Box;
  18. import javax.swing.BoxLayout;
  19. import javax.swing.Icon;
  20. import javax.swing.ImageIcon;
  21. import javax.swing.JButton;
  22. import javax.swing.JCheckBox;
  23. import javax.swing.JColorChooser;
  24. import javax.swing.JFileChooser;
  25. import javax.swing.JLabel;
  26. import javax.swing.JOptionPane;
  27. import javax.swing.JPanel;
  28. import javax.swing.UIManager;
  29. import javax.swing.border.EmptyBorder;
  30. import javax.swing.event.ChangeEvent;
  31. import javax.swing.event.ChangeListener;
  32. import org.insa.graph.Graph;
  33. import org.insa.graph.Path;
  34. import org.insa.graph.io.BinaryPathWriter;
  35. import org.insa.graphics.drawing.Drawing;
  36. import org.insa.graphics.drawing.overlays.PathOverlay;
  37. import org.insa.graphics.utils.FileUtils;
  38. import org.insa.graphics.utils.FileUtils.FolderType;
  39. public class PathsPanel extends JPanel implements DrawingChangeListener, GraphChangeListener {
  40. /**
  41. *
  42. */
  43. private static final long serialVersionUID = 1L;
  44. private class PathPanel extends JPanel {
  45. /**
  46. *
  47. */
  48. private static final long serialVersionUID = 1L;
  49. /**
  50. * Simple icon that represents a unicolor rectangle.
  51. *
  52. */
  53. protected class ColorIcon implements Icon {
  54. private Color color;
  55. private int width, height;
  56. public ColorIcon(Color color, int width, int height) {
  57. this.width = width;
  58. this.height = height;
  59. this.color = color;
  60. }
  61. public void setColor(Color color) {
  62. this.color = color;
  63. }
  64. @Override
  65. public void paintIcon(Component c, Graphics g, int x, int y) {
  66. g.setColor(this.color);
  67. g.fillRect(x, y, getIconWidth(), getIconHeight());
  68. }
  69. @Override
  70. public int getIconWidth() {
  71. return this.width;
  72. }
  73. @Override
  74. public int getIconHeight() {
  75. return this.height;
  76. }
  77. }
  78. // Solution
  79. private final Path path;
  80. // Path Overlay (not final due to redraw)
  81. private PathOverlay overlay;
  82. // Color button
  83. private final JButton colorButton;
  84. /**
  85. * Create a new bundle with the given path and create a new overlay
  86. * corresponding to the path.
  87. *
  88. * @param path Path for this bundle, must not be null.
  89. *
  90. */
  91. public PathPanel(Path path) {
  92. super();
  93. setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
  94. setBorder(BorderFactory.createCompoundBorder(
  95. BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY),
  96. new EmptyBorder(5, 0, 5, 0)));
  97. this.path = path;
  98. this.overlay = drawing.drawPath(this.path);
  99. JCheckBox checkbox = new JCheckBox();
  100. checkbox.setSelected(true);
  101. checkbox.addActionListener(new ActionListener() {
  102. @Override
  103. public void actionPerformed(ActionEvent e) {
  104. overlay.setVisible(checkbox.isSelected());
  105. }
  106. });
  107. JLabel infoPanel = new JLabel();
  108. String info = "";
  109. // Display length
  110. float length = path.getLength();
  111. if (length < 2000) {
  112. info += String.format("Length = %.1f meters", length);
  113. }
  114. else {
  115. info += String.format("Length = %.3f kilometers", length / 1000.);
  116. }
  117. // Display time
  118. info += ", Duration=";
  119. double time = path.getMinimumTravelTime();
  120. int hours = (int) (time / 3600);
  121. int minutes = (int) (time / 60) % 60;
  122. int seconds = ((int) time) % 60;
  123. if (hours > 0) {
  124. info += String.format("%d hours, ", hours);
  125. }
  126. if (minutes > 0) {
  127. info += String.format("%d minutes, ", minutes);
  128. }
  129. info += String.format("%d seconds.", seconds);
  130. infoPanel.setText("<html>" + toString() + "<br/>" + info + "</html>");
  131. infoPanel.addMouseListener(new MouseAdapter() {
  132. @Override
  133. public void mouseClicked(MouseEvent e) {
  134. checkbox.setSelected(!checkbox.isSelected());
  135. overlay.setVisible(checkbox.isSelected());
  136. }
  137. });
  138. Dimension size = new Dimension(24, 24);
  139. ColorIcon icon = new ColorIcon(overlay.getColor(), 14, 14);
  140. colorButton = new JButton(icon);
  141. colorButton.setFocusable(false);
  142. colorButton.setFocusPainted(false);
  143. colorButton.setMinimumSize(size);
  144. colorButton.setPreferredSize(size);
  145. colorButton.setMaximumSize(size);
  146. colorButton.setToolTipText("Pick a color");
  147. colorButton.addActionListener(new ActionListener() {
  148. @Override
  149. public void actionPerformed(ActionEvent e) {
  150. final Color originalColor = overlay.getColor();
  151. JColorChooser chooser = new JColorChooser(overlay.getColor());
  152. chooser.getSelectionModel().addChangeListener(new ChangeListener() {
  153. @Override
  154. public void stateChanged(ChangeEvent e) {
  155. icon.setColor(chooser.getSelectionModel().getSelectedColor());
  156. colorButton.repaint();
  157. overlay.setColor(chooser.getSelectionModel().getSelectedColor());
  158. overlay.redraw();
  159. }
  160. });
  161. JColorChooser.createDialog(getTopLevelAncestor(), "Pick a new color", true,
  162. chooser, new ActionListener() {
  163. @Override
  164. public void actionPerformed(ActionEvent e) {
  165. icon.setColor(chooser.getSelectionModel().getSelectedColor());
  166. colorButton.repaint();
  167. overlay.setColor(
  168. chooser.getSelectionModel().getSelectedColor());
  169. overlay.redraw();
  170. }
  171. }, new ActionListener() {
  172. @Override
  173. public void actionPerformed(ActionEvent e) {
  174. icon.setColor(originalColor);
  175. colorButton.repaint();
  176. overlay.setColor(originalColor);
  177. overlay.redraw();
  178. }
  179. }).setVisible(true);
  180. ;
  181. }
  182. });
  183. JButton saveButton = new JButton(UIManager.getIcon("FileView.floppyDriveIcon"));
  184. saveButton.setFocusPainted(false);
  185. saveButton.setFocusable(false);
  186. saveButton.setMinimumSize(size);
  187. saveButton.setPreferredSize(size);
  188. saveButton.setMaximumSize(size);
  189. saveButton.addActionListener(new ActionListener() {
  190. @Override
  191. public void actionPerformed(ActionEvent e) {
  192. String filepath = String.format("path_%s_%d_%d.path",
  193. path.getGraph().getMapId().toLowerCase().replaceAll("[^a-z0-9_]", ""),
  194. path.getOrigin().getId(), path.getDestination().getId());
  195. JFileChooser chooser = FileUtils.createFileChooser(FolderType.PathOutput,
  196. filepath);
  197. if (chooser
  198. .showSaveDialog(getTopLevelAncestor()) == JFileChooser.APPROVE_OPTION) {
  199. File file = chooser.getSelectedFile();
  200. try {
  201. BinaryPathWriter writer = new BinaryPathWriter(new DataOutputStream(
  202. new BufferedOutputStream(new FileOutputStream(file))));
  203. writer.writePath(path);
  204. }
  205. catch (IOException e1) {
  206. JOptionPane.showMessageDialog(getTopLevelAncestor(),
  207. "Unable to write path to the selected file.");
  208. e1.printStackTrace();
  209. }
  210. }
  211. }
  212. });
  213. Image newimg = new ImageIcon("res/cross_mark.png").getImage().getScaledInstance(14, 14,
  214. java.awt.Image.SCALE_SMOOTH);
  215. JButton deleteButton = new JButton(new ImageIcon(newimg));
  216. deleteButton.setFocusPainted(false);
  217. deleteButton.setFocusable(false);
  218. deleteButton.setMinimumSize(size);
  219. deleteButton.setPreferredSize(size);
  220. deleteButton.setMaximumSize(size);
  221. deleteButton.addActionListener(new ActionListener() {
  222. @Override
  223. public void actionPerformed(ActionEvent e) {
  224. overlay.delete();
  225. PathsPanel.this.removePath(PathPanel.this);
  226. }
  227. });
  228. add(checkbox);
  229. add(Box.createHorizontalStrut(5));
  230. add(infoPanel);
  231. add(Box.createHorizontalGlue());
  232. add(colorButton);
  233. add(saveButton);
  234. add(deleteButton);
  235. }
  236. /**
  237. * Re-draw the current overlay (if any) on the new drawing.
  238. *
  239. */
  240. public void updateOverlay() {
  241. PathOverlay oldOverlay = this.overlay;
  242. this.overlay = drawing.drawPath(path);
  243. this.overlay.setColor(oldOverlay.getColor());
  244. ((ColorIcon) this.colorButton.getIcon()).setColor(this.overlay.getColor());
  245. this.colorButton.repaint();
  246. this.overlay.setVisible(oldOverlay.isVisible());
  247. oldOverlay.delete();
  248. }
  249. /*
  250. * (non-Javadoc)
  251. *
  252. * @see java.lang.Object#toString()
  253. */
  254. public String toString() {
  255. return "Path from #" + path.getOrigin().getId() + " to #"
  256. + path.getDestination().getId();
  257. }
  258. }
  259. // Solution
  260. private Drawing drawing;
  261. public PathsPanel(Component parent) {
  262. super();
  263. setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
  264. setBorder(new EmptyBorder(15, 15, 15, 15));
  265. // Default hidden
  266. this.setVisible(false);
  267. }
  268. public void addPath(Path path) {
  269. this.add(new PathPanel(path));
  270. this.setVisible(true);
  271. this.revalidate();
  272. this.repaint();
  273. }
  274. protected void removePath(PathPanel panel) {
  275. PathsPanel.this.remove(panel);
  276. PathsPanel.this.revalidate();
  277. PathsPanel.this.repaint();
  278. if (this.getComponentCount() == 0) {
  279. this.setVisible(false);
  280. }
  281. }
  282. @Override
  283. public void newGraphLoaded(Graph graph) {
  284. for (Component c: this.getComponents()) {
  285. if (c instanceof PathPanel) {
  286. ((PathPanel) c).overlay.delete();
  287. }
  288. }
  289. this.removeAll();
  290. this.setVisible(false);
  291. }
  292. @Override
  293. public void onDrawingLoaded(Drawing oldDrawing, Drawing newDrawing) {
  294. if (newDrawing != drawing) {
  295. drawing = newDrawing;
  296. }
  297. }
  298. @Override
  299. public void onRedrawRequest() {
  300. for (Component c: this.getComponents()) {
  301. if (c instanceof PathPanel) {
  302. ((PathPanel) c).updateOverlay();
  303. }
  304. }
  305. }
  306. }