Gui.java 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447
  1. package holeg.ui.view.main;
  2. import holeg.model.GroupNode;
  3. import holeg.model.Model;
  4. import holeg.preferences.ImagePreference;
  5. import holeg.preferences.PreferenceKeys;
  6. import holeg.ui.controller.Control;
  7. import holeg.ui.model.GuiSettings;
  8. import holeg.ui.view.canvas.Canvas;
  9. import holeg.ui.view.canvas.CanvasCollectionPanel;
  10. import holeg.ui.view.category.CategoryPanel;
  11. import holeg.ui.view.dialog.CanvasResizePopUp;
  12. import holeg.ui.view.dialog.EditEdgesPopUp;
  13. import holeg.ui.view.image.Import;
  14. import holeg.ui.view.information.HolonInformationPanel;
  15. import holeg.ui.view.inspector.Inspector;
  16. import holeg.ui.view.window.AddOnWindow;
  17. import holeg.ui.view.window.FlexWindow;
  18. import holeg.ui.view.window.Outliner;
  19. import holeg.utility.listener.WindowClosingListener;
  20. import holeg.utility.math.vector.Vec2i;
  21. import java.awt.BorderLayout;
  22. import java.awt.Container;
  23. import java.awt.Dimension;
  24. import java.awt.Rectangle;
  25. import java.awt.Toolkit;
  26. import java.awt.event.InputEvent;
  27. import java.awt.event.KeyEvent;
  28. import java.io.File;
  29. import java.net.URI;
  30. import java.util.Optional;
  31. import java.util.logging.Logger;
  32. import java.util.prefs.Preferences;
  33. import java.util.stream.Collectors;
  34. import javax.swing.ImageIcon;
  35. import javax.swing.JCheckBoxMenuItem;
  36. import javax.swing.JFileChooser;
  37. import javax.swing.JFrame;
  38. import javax.swing.JMenu;
  39. import javax.swing.JMenuBar;
  40. import javax.swing.JMenuItem;
  41. import javax.swing.JOptionPane;
  42. import javax.swing.JScrollPane;
  43. import javax.swing.JSplitPane;
  44. import javax.swing.KeyStroke;
  45. import javax.swing.filechooser.FileNameExtensionFilter;
  46. import javax.swing.filechooser.FileSystemView;
  47. /**
  48. * The main frame of this program.
  49. */
  50. public class Gui extends JFrame {
  51. private static final Logger log = Logger.getLogger(Model.class.getName());
  52. private static final Preferences prefs = Preferences.userNodeForPackage(Gui.class);
  53. public final CanvasCollectionPanel canvasCollection;
  54. private final Control control;
  55. private final CategoryPanel categoryPanel;
  56. private final Inspector inspector;
  57. private final HolonInformationPanel informationPanel;
  58. private final TimePanel timePanel;
  59. /**
  60. * Create the application.
  61. *
  62. * @param control the Controller
  63. */
  64. public Gui(Control control) {
  65. super("HOLEG Simulator");
  66. this.control = control;
  67. this.informationPanel = new HolonInformationPanel(control);
  68. this.inspector = new Inspector(control);
  69. this.categoryPanel = new CategoryPanel(control, this);
  70. this.canvasCollection = new CanvasCollectionPanel(control);
  71. this.timePanel = new TimePanel(control);
  72. init();
  73. }
  74. /**
  75. * Initialize the gui.
  76. */
  77. private void init() {
  78. initFrame();
  79. initLayout();
  80. }
  81. private void initFrame() {
  82. this.setIconImage(Import.loadImage(ImagePreference.Logo, 30, 30));
  83. this.setBounds(new Rectangle(prefs.getInt(PreferenceKeys.Gui.Width, 1200),
  84. prefs.getInt(PreferenceKeys.Gui.Height, 800)));
  85. Appearance.loadPrefs();
  86. if (prefs.get(PreferenceKeys.Gui.Width, null) != null) {
  87. this.setLocation(prefs.getInt(PreferenceKeys.Gui.XPos, 1200),
  88. prefs.getInt(PreferenceKeys.Gui.YPos, 800));
  89. } else {
  90. this.setLocationRelativeTo(null);
  91. }
  92. this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
  93. this.addWindowListener((WindowClosingListener) e -> {
  94. Rectangle bounds = this.getBounds();
  95. prefs.putInt(PreferenceKeys.Gui.XPos, bounds.x);
  96. prefs.putInt(PreferenceKeys.Gui.YPos, bounds.y);
  97. prefs.putInt(PreferenceKeys.Gui.Width, bounds.width);
  98. prefs.putInt(PreferenceKeys.Gui.Height, bounds.height);
  99. Appearance.savePrefs();
  100. control.saveCategories();
  101. if (JOptionPane.showConfirmDialog(this, "Are you sure you want to exit?", "HOLEG",
  102. JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) {
  103. System.exit(0);
  104. }
  105. });
  106. }
  107. private void initLayout() {
  108. this.setJMenuBar(new GuiMenuBar());
  109. final JSplitPane categorySplit = new JSplitPane();
  110. final JSplitPane canvasSplit = new JSplitPane();
  111. final JSplitPane elementSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
  112. final JScrollPane informationPanelScrollPane = new JScrollPane();
  113. Container contentPanel = this.getContentPane();
  114. contentPanel.setLayout(new BorderLayout(0, 0));
  115. contentPanel.add(categorySplit);
  116. categorySplit.setLeftComponent(categoryPanel);
  117. categorySplit.setRightComponent(canvasSplit);
  118. categorySplit.setDividerLocation(200);
  119. categorySplit.setBorder(null);
  120. canvasSplit.setMinimumSize(new Dimension(0, 25));
  121. canvasSplit.setDividerLocation(500);
  122. canvasSplit.setLeftComponent(canvasCollection);
  123. canvasSplit.setRightComponent(elementSplit);
  124. canvasSplit.setResizeWeight(0.9);
  125. canvasSplit.setBorder(null);
  126. elementSplit.setDividerLocation(700);
  127. elementSplit.setTopComponent(inspector);
  128. elementSplit.setBottomComponent(informationPanelScrollPane);
  129. elementSplit.setBorder(null);
  130. informationPanelScrollPane.setViewportView(this.informationPanel);
  131. informationPanelScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
  132. informationPanelScrollPane.getVerticalScrollBar().setUnitIncrement(16);
  133. informationPanelScrollPane.setBorder(null);
  134. contentPanel.add(timePanel, BorderLayout.SOUTH);
  135. }
  136. public Canvas getActualCanvas() {
  137. return this.canvasCollection.getActualCanvas();
  138. }
  139. /**
  140. * This class handles all Menu of the gui.
  141. */
  142. private class GuiMenuBar extends JMenuBar {
  143. private final static int IconSize = 15;
  144. private final static Vec2i DefaultOffset = new Vec2i(20, 20);
  145. //Menus
  146. private final JMenu fileMenu = new JMenu("File");
  147. private final JMenu editMenu = new JMenu("Edit");
  148. private final JMenu viewMenu = new JMenu("View");
  149. private final JMenu windowMenu = new JMenu("Window");
  150. private final JMenu helpMenu = new JMenu("Help");
  151. // FileMenu
  152. private final JMenuItem newMenuButton = new JMenuItem("New");
  153. private final JMenuItem openMenuButton = new JMenuItem("Open..");
  154. private final JMenuItem saveMenuButton = new JMenuItem("Save");
  155. private final JMenuItem saveAsMenuButton = new JMenuItem("Save..");
  156. // EditMenu
  157. private final JMenuItem undoButton = new JMenuItem("Undo");
  158. private final JMenuItem redoButton = new JMenuItem("Redo");
  159. private final JMenuItem copyButton = new JMenuItem("Copy");
  160. private final JMenuItem pasteButton = new JMenuItem("Paste");
  161. private final JMenuItem cutButton = new JMenuItem("Cut");
  162. private final JMenu selectionMenu = new JMenu("Selection");
  163. private final JMenuItem selectAllButton = new JMenuItem("All");
  164. private final JMenuItem clearSelectionButton = new JMenuItem("Clear");
  165. private final JMenuItem invertSelectionButton = new JMenuItem("Invert");
  166. private final JMenuItem groupButton = new JMenuItem("Group");
  167. private final JMenuItem ungroupButton = new JMenuItem("Ungroup");
  168. private final JMenuItem removeButton = new JMenuItem("Remove");
  169. private final JMenuItem edgePropertiesButton = new JMenuItem("Edge Properties");
  170. private final JMenuItem alignAllButton = new JMenuItem("Align All");
  171. private final JMenu resetMenu = new JMenu("Reset");
  172. private final JMenuItem resetCategoryButton = new JMenuItem("Categories");
  173. //HelpMenu
  174. private final JMenuItem introductionButton = new JMenuItem("Introduction");
  175. private final JMenuItem userManualButton = new JMenuItem("User Manual");
  176. private final JMenuItem algorithmHelpButton = new JMenuItem("Algorithm Introduction");
  177. private final JMenuItem codeDocumentationButton = new JMenuItem("Code Documentation");
  178. //ViewMenu
  179. private final JMenu appearanceMenu = new JMenu("Appearance");
  180. private final JMenuItem canvasSizeButton = new JMenuItem("Set View Size");
  181. private final JCheckBoxMenuItem showSupplyBarsCheckBox = new JCheckBoxMenuItem("Supply bars",
  182. Appearance.supplyBarVisible);
  183. private final JCheckBoxMenuItem edgeCapacityVisibleCheckBox = new JCheckBoxMenuItem(
  184. "Edge capacity", Appearance.edgeCapacityVisible);
  185. private final JCheckBoxMenuItem canvasObjectEnergyVisibleCheckBox = new JCheckBoxMenuItem(
  186. "HolonObject energy", Appearance.canvasObjectEnergyVisible);
  187. private final JFileChooser fileChooser = initFileChooser();
  188. //WindowMenu
  189. JMenuItem algorithmButton = new JMenuItem("Algorithm Panel", new ImageIcon(Import
  190. .loadImage(ImagePreference.Button.Menu.Algo)
  191. .getScaledInstance(IconSize, IconSize, java.awt.Image.SCALE_SMOOTH)));
  192. JMenuItem outlinerButton = new JMenuItem("Outliner", new ImageIcon(Import
  193. .loadImage(ImagePreference.Button.Menu.Outliner)
  194. .getScaledInstance(IconSize, IconSize, java.awt.Image.SCALE_SMOOTH)));
  195. JMenuItem flexMenuButton = new JMenuItem("Flexibility Panel", new ImageIcon(Import
  196. .loadImage(ImagePreference.Button.Menu.Algo)
  197. .getScaledInstance(IconSize, IconSize, java.awt.Image.SCALE_SMOOTH)));
  198. GuiMenuBar() {
  199. initMenuLayout();
  200. initButtonActions();
  201. initButtonShortCuts();
  202. }
  203. private static JFileChooser initFileChooser() {
  204. JFileChooser safeLoadFileChooser = new JFileChooser(
  205. prefs.get(PreferenceKeys.Gui.DefaultFolder,
  206. FileSystemView.getFileSystemView().getDefaultDirectory().getPath()));
  207. safeLoadFileChooser.setFileFilter(new FileNameExtensionFilter("Holeg json files", "json"));
  208. safeLoadFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
  209. safeLoadFileChooser.setAcceptAllFileFilterUsed(false);
  210. return safeLoadFileChooser;
  211. }
  212. private void initMenuLayout() {
  213. add(fileMenu);
  214. add(editMenu);
  215. add(viewMenu);
  216. add(windowMenu);
  217. add(helpMenu);
  218. fileMenu.add(newMenuButton);
  219. fileMenu.add(openMenuButton);
  220. fileMenu.addSeparator();
  221. fileMenu.add(saveMenuButton);
  222. fileMenu.add(saveAsMenuButton);
  223. editMenu.add(undoButton);
  224. editMenu.add(redoButton);
  225. editMenu.addSeparator();
  226. editMenu.add(copyButton);
  227. editMenu.add(pasteButton);
  228. editMenu.add(cutButton);
  229. editMenu.addSeparator();
  230. editMenu.add(selectionMenu);
  231. selectionMenu.add(selectAllButton);
  232. selectionMenu.add(clearSelectionButton);
  233. selectionMenu.add(invertSelectionButton);
  234. editMenu.addSeparator();
  235. editMenu.add(groupButton);
  236. editMenu.add(ungroupButton);
  237. editMenu.addSeparator();
  238. editMenu.add(removeButton);
  239. editMenu.addSeparator();
  240. editMenu.add(edgePropertiesButton);
  241. editMenu.add(alignAllButton);
  242. editMenu.add(resetMenu);
  243. resetMenu.add(resetCategoryButton);
  244. helpMenu.add(introductionButton);
  245. helpMenu.add(userManualButton);
  246. helpMenu.add(algorithmHelpButton);
  247. helpMenu.add(codeDocumentationButton);
  248. viewMenu.add(appearanceMenu);
  249. appearanceMenu.add(showSupplyBarsCheckBox);
  250. appearanceMenu.add(edgeCapacityVisibleCheckBox);
  251. appearanceMenu.add(canvasObjectEnergyVisibleCheckBox);
  252. viewMenu.add(canvasSizeButton);
  253. windowMenu.add(algorithmButton);
  254. windowMenu.add(outlinerButton);
  255. windowMenu.add(flexMenuButton);
  256. }
  257. private void initButtonActions() {
  258. newMenuButton.addActionListener(clicked -> newFile());
  259. openMenuButton.addActionListener(clicked -> openFile());
  260. saveMenuButton.addActionListener(clicked -> saveFile());
  261. saveAsMenuButton.addActionListener(clicked -> saveNewFile());
  262. groupButton.addActionListener(clicked -> control.group());
  263. ungroupButton.addActionListener(clicked -> control.ungroup());
  264. edgePropertiesButton.addActionListener(actionEvent -> new EditEdgesPopUp(Gui.this, control));
  265. alignAllButton.addActionListener(clicked -> {
  266. getActualCanvas().getGroupNode()
  267. .getObjectsInThisLayer().forEach(obj -> {
  268. int distance = GuiSettings.getPictureScaleDiv2() * 3;
  269. Vec2i pos = obj.getPosition();
  270. // offset relative to a grid with lines every distance pixels
  271. Vec2i offset = new Vec2i(pos.getX() % distance, pos.getY() % distance);
  272. //align to the other Line, if it is nearer
  273. if (offset.getX() > distance / 2) {
  274. offset.setX(offset.getX() - distance);
  275. }
  276. if (offset.getY() > distance / 2) {
  277. offset.setY(offset.getY() - distance);
  278. }
  279. obj.setPosition(obj.getPosition().subtract(offset));
  280. });
  281. control.OnCanvasUpdate.broadcast();
  282. });
  283. resetCategoryButton.addActionListener(clicked -> control.resetCategories());
  284. showSupplyBarsCheckBox.addActionListener(clicked -> {
  285. Appearance.supplyBarVisible = showSupplyBarsCheckBox.isSelected();
  286. control.OnCanvasUpdate.broadcast();
  287. });
  288. edgeCapacityVisibleCheckBox.addActionListener(clicked -> {
  289. Appearance.edgeCapacityVisible = edgeCapacityVisibleCheckBox.isSelected();
  290. control.OnCanvasUpdate.broadcast();
  291. });
  292. canvasObjectEnergyVisibleCheckBox.addActionListener(clicked -> {
  293. Appearance.canvasObjectEnergyVisible = canvasObjectEnergyVisibleCheckBox.isSelected();
  294. control.OnCanvasUpdate.broadcast();
  295. });
  296. canvasSizeButton.addActionListener(clicked -> new CanvasResizePopUp(control, Gui.this));
  297. algorithmButton.addActionListener(clicked -> new AddOnWindow(Gui.this, control));
  298. outlinerButton.addActionListener(clicked -> new Outliner(Gui.this, control));
  299. flexMenuButton.addActionListener(clicked -> new FlexWindow(Gui.this, control));
  300. selectAllButton.addActionListener(clicked -> selectAll());
  301. clearSelectionButton.addActionListener(clicked -> control.clearSelection());
  302. invertSelectionButton.addActionListener(clicked -> invertSelection());
  303. copyButton.addActionListener(clicked -> control.copy());
  304. cutButton.addActionListener(clicked -> control.cut());
  305. pasteButton.addActionListener(clicked -> paste());
  306. undoButton.addActionListener(clicked -> control.undo());
  307. redoButton.addActionListener(clicked -> control.redo());
  308. removeButton.addActionListener(clicked -> removeSelectedObjects());
  309. String tkWikiWebpage = "https://git.tk.informatik.tu-darmstadt.de/carlos.garcia/praktikum-holons/wiki/";
  310. introductionButton.addActionListener(
  311. clicked -> openWebpage(tkWikiWebpage + "Introduction+V2.1"));
  312. userManualButton.addActionListener(
  313. clicked -> openWebpage(tkWikiWebpage + "User+Manual+V2.1"));
  314. algorithmHelpButton.addActionListener(
  315. clicked -> openWebpage(tkWikiWebpage + "Algorithms+V2.1"));
  316. codeDocumentationButton.addActionListener(
  317. clicked -> openWebpage(tkWikiWebpage + "Code+documentation+V2.1"));
  318. }
  319. private void openWebpage(String URL) {
  320. try {
  321. java.awt.Desktop.getDesktop().browse(new URI(URL));
  322. } catch (Exception e) {
  323. e.printStackTrace();
  324. }
  325. }
  326. /**
  327. * Initialize all shortcuts of the program.
  328. */
  329. private void initButtonShortCuts() {
  330. int defaultModifier = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx();
  331. int defaultShiftModifier = defaultModifier + InputEvent.SHIFT_DOWN_MASK;
  332. saveMenuButton.setAccelerator(KeyStroke.getKeyStroke('S', defaultModifier));
  333. saveAsMenuButton.setAccelerator(KeyStroke.getKeyStroke('S', defaultShiftModifier));
  334. openMenuButton.setAccelerator(KeyStroke.getKeyStroke('O', defaultModifier));
  335. newMenuButton.setAccelerator(KeyStroke.getKeyStroke('N', defaultModifier));
  336. undoButton.setAccelerator(KeyStroke.getKeyStroke('Z', defaultModifier));
  337. redoButton.setAccelerator(KeyStroke.getKeyStroke('Y', defaultModifier));
  338. copyButton.setAccelerator(KeyStroke.getKeyStroke('C', defaultModifier));
  339. pasteButton.setAccelerator(KeyStroke.getKeyStroke('V', defaultModifier));
  340. cutButton.setAccelerator(KeyStroke.getKeyStroke('X', defaultModifier));
  341. selectAllButton.setAccelerator(KeyStroke.getKeyStroke('A', defaultModifier));
  342. clearSelectionButton.setAccelerator(KeyStroke.getKeyStroke('A', defaultShiftModifier));
  343. invertSelectionButton.setAccelerator(KeyStroke.getKeyStroke('I', defaultModifier));
  344. groupButton.setAccelerator(KeyStroke.getKeyStroke('G', defaultModifier));
  345. ungroupButton.setAccelerator(KeyStroke.getKeyStroke('U', defaultModifier));
  346. alignAllButton.setAccelerator(KeyStroke.getKeyStroke('L', defaultModifier));
  347. removeButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0));
  348. algorithmButton.setAccelerator(KeyStroke.getKeyStroke('A', InputEvent.ALT_DOWN_MASK));
  349. outlinerButton.setAccelerator(KeyStroke.getKeyStroke('O', InputEvent.ALT_DOWN_MASK));
  350. flexMenuButton.setAccelerator(KeyStroke.getKeyStroke('F', InputEvent.ALT_DOWN_MASK));
  351. }
  352. /**
  353. * Save if save file is selected else open the dialog.
  354. */
  355. private void saveFile() {
  356. GuiSettings.getActualSaveFile().ifPresentOrElse(control::saveFile, this::saveNewFile);
  357. }
  358. private void saveNewFile() {
  359. if (fileChooser.showSaveDialog(Gui.this) == JFileChooser.APPROVE_OPTION) {
  360. String path = fileChooser.getSelectedFile().getPath();
  361. if (!path.endsWith(".json")) {
  362. path += ".json";
  363. }
  364. prefs.put(PreferenceKeys.Gui.DefaultFolder, fileChooser.getCurrentDirectory().getPath());
  365. control.saveFile(new File(path));
  366. }
  367. }
  368. private void openFile() {
  369. if (fileChooser.showOpenDialog(Gui.this) == JFileChooser.APPROVE_OPTION) {
  370. prefs.put(PreferenceKeys.Gui.DefaultFolder, fileChooser.getCurrentDirectory().getPath());
  371. control.loadFile(fileChooser.getSelectedFile());
  372. }
  373. }
  374. private void newFile() {
  375. if (control.getModel().getCanvas().getObjectsInThisLayer().findAny().isPresent()) {
  376. int selectedOption = JOptionPane.showConfirmDialog(Gui.this,
  377. "Do you want to save your current model?",
  378. "Warning", JOptionPane.YES_NO_OPTION);
  379. if (selectedOption == JOptionPane.YES_OPTION) {
  380. saveNewFile();
  381. }
  382. }
  383. control.clearModel();
  384. }
  385. private void selectAll() {
  386. control.setSelection(getActualCanvas().getGroupNode()
  387. .getObjectsInThisLayer().collect(Collectors.toSet()));
  388. }
  389. private void invertSelection() {
  390. control.toggleSelectedObjects(getActualCanvas().getGroupNode()
  391. .getObjectsInThisLayer().collect(Collectors.toSet()));
  392. }
  393. private void removeSelectedObjects() {
  394. control.deleteCanvasObjects(GuiSettings.getSelectedObjects());
  395. control.clearSelection();
  396. }
  397. private void paste() {
  398. Vec2i middlePosition = GroupNode.calculateMiddlePosition(GuiSettings.getClipboardObjects());
  399. Optional<Vec2i> offset = Optional.ofNullable(getActualCanvas().getMousePosition())
  400. .map(point -> new Vec2i(point).subtract(middlePosition));
  401. control.paste(getActualCanvas().getGroupNode(), offset.orElse(DefaultOffset));
  402. }
  403. }
  404. }