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 13KB

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