package holeg.ui.view.main; import holeg.model.AbstractCanvasObject; import holeg.model.GroupNode; import holeg.model.Model; import holeg.preferences.ImagePreference; import holeg.preferences.PreferenceKeys; import holeg.ui.controller.Control; import holeg.ui.model.GuiSettings; import holeg.ui.view.canvas.Canvas; import holeg.ui.view.canvas.CanvasCollectionPanel; import holeg.ui.view.category.CategoryPanel; import holeg.ui.view.dialog.AboutUsPopUp; import holeg.ui.view.dialog.CanvasResizePopUp; import holeg.ui.view.dialog.EditEdgesPopUp; import holeg.ui.view.image.Import; import holeg.ui.view.information.HolonInformationPanel; import holeg.ui.view.inspector.Inspector; import holeg.ui.view.window.AddOnWindow; import holeg.ui.view.window.FlexWindow; import holeg.ui.view.window.Outliner; import holeg.utility.listener.WindowClosingListener; import holeg.utility.math.vector.Vec2i; import javax.swing.*; import javax.swing.filechooser.FileNameExtensionFilter; import javax.swing.filechooser.FileSystemView; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.io.File; import java.net.URI; import java.util.Optional; import java.util.Set; import java.util.logging.Logger; import java.util.prefs.Preferences; import java.util.stream.Collectors; public class Gui extends JFrame { private static final Logger log = Logger.getLogger(Model.class.getName()); private static final Preferences prefs = Preferences.userNodeForPackage(Gui.class); private final Control control; private final CategoryPanel categoryPanel; private final CanvasCollectionPanel canvasCollection; private final Inspector inspector; private final HolonInformationPanel informationPanel; private final TimePanel timePanel; /** * Create the application. * * @param control the Controller */ public Gui(Control control) { super("HOLEG Simulator"); this.control = control; this.informationPanel = new HolonInformationPanel(control); this.inspector = new Inspector(control); this.categoryPanel = new CategoryPanel(control, this); this.canvasCollection = new CanvasCollectionPanel(control); this.timePanel = new TimePanel(control); init(); } private void init() { initFrame(); initLayout(); } private void initFrame() { this.setIconImage(Import.loadImage(ImagePreference.Logo, 30, 30)); this.setBounds(new Rectangle(prefs.getInt(PreferenceKeys.Gui.Width, 1200), prefs.getInt(PreferenceKeys.Gui.Height, 800))); if (prefs.get(PreferenceKeys.Gui.Width, null) != null) { this.setLocation(prefs.getInt(PreferenceKeys.Gui.XPos, 1200), prefs.getInt(PreferenceKeys.Gui.YPos, 800)); } else { this.setLocationRelativeTo(null); } this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); this.addWindowListener((WindowClosingListener) e -> { Rectangle bounds = this.getBounds(); prefs.putInt(PreferenceKeys.Gui.XPos, bounds.x); prefs.putInt(PreferenceKeys.Gui.YPos, bounds.y); prefs.putInt(PreferenceKeys.Gui.Width, bounds.width); prefs.putInt(PreferenceKeys.Gui.Height, bounds.height); if (JOptionPane.showConfirmDialog(this, "Are you sure you want to exit?", "HOLEG", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.YES_OPTION) { System.exit(0); } }); } private void initLayout() { this.setJMenuBar(new GuiMenuBar()); final JSplitPane categorySplit = new JSplitPane(); final JSplitPane canvasSplit = new JSplitPane(); final JSplitPane elementSplit = new JSplitPane(JSplitPane.VERTICAL_SPLIT); final JScrollPane informationPanelScrollPane = new JScrollPane(); Container contentPanel = this.getContentPane(); contentPanel.setLayout(new BorderLayout(0, 0)); contentPanel.add(categorySplit); categorySplit.setLeftComponent(categoryPanel); categorySplit.setRightComponent(canvasSplit); categorySplit.setDividerLocation(200); categorySplit.setBorder(null); canvasSplit.setMinimumSize(new Dimension(0, 25)); canvasSplit.setDividerLocation(500); canvasSplit.setLeftComponent(canvasCollection); canvasSplit.setRightComponent(elementSplit); canvasSplit.setResizeWeight(0.9); canvasSplit.setBorder(null); elementSplit.setDividerLocation(700); elementSplit.setTopComponent(inspector); elementSplit.setBottomComponent(informationPanelScrollPane); elementSplit.setBorder(null); informationPanelScrollPane.setViewportView(this.informationPanel); informationPanelScrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); informationPanelScrollPane.getVerticalScrollBar().setUnitIncrement(16); informationPanelScrollPane.setBorder(null); contentPanel.add(timePanel, BorderLayout.SOUTH); } public Canvas getActualCanvas() { return this.canvasCollection.getActualCanvas(); } private class GuiMenuBar extends JMenuBar { //Menus private final JMenu fileMenu = new JMenu("File"); private final JMenu editMenu = new JMenu("Edit"); private final JMenu viewMenu = new JMenu("View"); private final JMenu windowMenu = new JMenu("Window"); private final JMenu helpMenu = new JMenu("Help"); // FileMenu private final JMenuItem newMenuButton = new JMenuItem("New"); private final JMenuItem openMenuButton = new JMenuItem("Open.."); private final JMenuItem saveMenuButton = new JMenuItem("Save"); private final JMenuItem saveAsMenuButton = new JMenuItem("Save.."); // EditMenu private final JMenuItem undoButton = new JMenuItem("Undo"); private final JMenuItem redoButton = new JMenuItem("Redo"); private final JMenuItem copyButton = new JMenuItem("Copy"); private final JMenuItem pasteButton = new JMenuItem("Paste"); private final JMenuItem cutButton = new JMenuItem("Cut"); private final JMenu selectionMenu = new JMenu("Selection"); private final JMenuItem selectAllButton = new JMenuItem("All"); private final JMenuItem clearSelectionButton = new JMenuItem("Clear"); private final JMenuItem invertSelectionButton = new JMenuItem("Invert"); private final JMenuItem groupButton = new JMenuItem("Group"); private final JMenuItem ungroupButton = new JMenuItem("Ungroup"); private final JMenuItem removeButton = new JMenuItem("Remove"); private final JMenuItem edgePropertiesButton = new JMenuItem("Edge Properties"); private final JMenuItem alignAllButton = new JMenuItem("Align All"); private final JMenu resetMenu = new JMenu("Reset"); private final JMenuItem resetCategoryButton = new JMenuItem("Categories"); //HelpMenu private final JMenuItem introductionButton = new JMenuItem("Introduction"); private final JMenuItem userManualButton = new JMenuItem("User Manual"); private final JMenuItem algorithmHelpButton = new JMenuItem("Algorithm Introduction"); private final JMenuItem codeDocumentationButton = new JMenuItem("Code Documentation"); private final JMenuItem aboutUsButton = new JMenuItem("About Us"); //ViewMenu private final JMenu appearanceMenu = new JMenu("Appearance"); private final JMenuItem canvasSizeButton = new JMenuItem("Set View Size"); private final JCheckBoxMenuItem showSupplyBarsCheckBox = new JCheckBoxMenuItem("Show supply bars.", true); private final JFileChooser fileChooser = initFileChooser(); private final static int IconSize = 15; //WindowMenu JMenuItem algorithmButton = new JMenuItem("Algorithm Panel", new ImageIcon(Import .loadImage(ImagePreference.Button.Menu.Algo).getScaledInstance(IconSize, IconSize, java.awt.Image.SCALE_SMOOTH))); JMenuItem outlinerButton = new JMenuItem("Outliner", new ImageIcon(Import .loadImage(ImagePreference.Button.Menu.Outliner).getScaledInstance(IconSize, IconSize, java.awt.Image.SCALE_SMOOTH))); JMenuItem flexMenuButton = new JMenuItem("Flexibility Panel", new ImageIcon(Import .loadImage(ImagePreference.Button.Menu.Algo).getScaledInstance(IconSize, IconSize, java.awt.Image.SCALE_SMOOTH))); private final static Vec2i DefaultOffset = new Vec2i(20,20); GuiMenuBar() { initMenuLayout(); initButtonActions(); initButtonShortCuts(); } private static JFileChooser initFileChooser() { JFileChooser safeLoadFileChooser = new JFileChooser(prefs.get(PreferenceKeys.Gui.DefaultFolder, FileSystemView.getFileSystemView().getDefaultDirectory().getPath())); safeLoadFileChooser.setFileFilter(new FileNameExtensionFilter("Holeg json files", "json")); safeLoadFileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY); safeLoadFileChooser.setAcceptAllFileFilterUsed(false); return safeLoadFileChooser; } private void initMenuLayout() { add(fileMenu); add(editMenu); add(viewMenu); add(windowMenu); add(helpMenu); fileMenu.add(newMenuButton); fileMenu.add(openMenuButton); fileMenu.addSeparator(); fileMenu.add(saveMenuButton); fileMenu.add(saveAsMenuButton); editMenu.add(undoButton); editMenu.add(redoButton); editMenu.addSeparator(); editMenu.add(copyButton); editMenu.add(pasteButton); editMenu.add(cutButton); editMenu.addSeparator(); editMenu.add(selectionMenu); selectionMenu.add(selectAllButton); selectionMenu.add(clearSelectionButton); selectionMenu.add(invertSelectionButton); editMenu.addSeparator(); editMenu.add(groupButton); editMenu.add(ungroupButton); editMenu.addSeparator(); editMenu.add(removeButton); editMenu.addSeparator(); editMenu.add(edgePropertiesButton); editMenu.add(alignAllButton); editMenu.add(resetMenu); resetMenu.add(resetCategoryButton); helpMenu.add(introductionButton); helpMenu.add(userManualButton); helpMenu.add(algorithmHelpButton); helpMenu.add(codeDocumentationButton); helpMenu.add(aboutUsButton); viewMenu.add(appearanceMenu); appearanceMenu.add(showSupplyBarsCheckBox); viewMenu.add(canvasSizeButton); windowMenu.add(algorithmButton); windowMenu.add(outlinerButton); windowMenu.add(flexMenuButton); } private void initButtonActions() { newMenuButton.addActionListener(clicked -> newFile()); openMenuButton.addActionListener(clicked -> openFile()); saveMenuButton.addActionListener(clicked -> saveFile()); saveAsMenuButton.addActionListener(clicked -> saveNewFile()); groupButton.addActionListener(clicked -> control.group()); ungroupButton.addActionListener(clicked -> control.ungroup()); edgePropertiesButton.addActionListener(actionEvent -> new EditEdgesPopUp(Gui.this, control)); alignAllButton.addActionListener(clicked -> { //TODO(Tom2022-01-14): recreateTryToAlignObjects log.info("Not implemented yet."); }); resetCategoryButton.addActionListener(clicked -> control.resetCategories()); showSupplyBarsCheckBox.addActionListener(clicked -> toggleSupplyBarAppearance()); canvasSizeButton.addActionListener(clicked -> new CanvasResizePopUp(control, Gui.this)); algorithmButton.addActionListener(clicked -> new AddOnWindow(Gui.this, control)); outlinerButton.addActionListener(clicked -> new Outliner(Gui.this, control)); flexMenuButton.addActionListener(clicked -> new FlexWindow(Gui.this, control)); selectAllButton.addActionListener(clicked -> selectAll()); clearSelectionButton.addActionListener(clicked -> control.clearSelection()); invertSelectionButton.addActionListener(clicked -> invertSelection()); copyButton.addActionListener(clicked -> control.copy()); cutButton.addActionListener(clicked -> control.cut()); pasteButton.addActionListener(clicked -> paste()); removeButton.addActionListener(clicked -> removeSelectedObjects()); String tkWikiWebpage = "https://git.tk.informatik.tu-darmstadt.de/carlos.garcia/praktikum-holons/wiki/"; introductionButton.addActionListener(clicked -> openWebpage(tkWikiWebpage + "Introduction+V2.1")); userManualButton.addActionListener(clicked -> openWebpage(tkWikiWebpage + "User+Manual+V2.1")); algorithmHelpButton.addActionListener(clicked -> openWebpage(tkWikiWebpage + "Algorithms+V2.1")); codeDocumentationButton.addActionListener(clicked -> openWebpage(tkWikiWebpage + "Code+documentation+V2.1")); aboutUsButton.addActionListener(clicked -> new AboutUsPopUp(Gui.this)); } private void openWebpage(String URL) { try { java.awt.Desktop.getDesktop().browse(new URI(URL)); } catch (Exception e) { e.printStackTrace(); } } private void initButtonShortCuts() { int defaultModifier = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx(); int defaultShiftModifier = defaultModifier + InputEvent.SHIFT_DOWN_MASK; saveMenuButton.setAccelerator(KeyStroke.getKeyStroke('S', defaultModifier)); saveAsMenuButton.setAccelerator(KeyStroke.getKeyStroke('S', defaultShiftModifier)); openMenuButton.setAccelerator(KeyStroke.getKeyStroke('O', defaultModifier)); newMenuButton.setAccelerator(KeyStroke.getKeyStroke('N', defaultModifier)); undoButton.setAccelerator(KeyStroke.getKeyStroke('Z', defaultModifier)); redoButton.setAccelerator(KeyStroke.getKeyStroke('Y', defaultModifier)); copyButton.setAccelerator(KeyStroke.getKeyStroke('C', defaultModifier)); pasteButton.setAccelerator(KeyStroke.getKeyStroke('V', defaultModifier)); cutButton.setAccelerator(KeyStroke.getKeyStroke('X', defaultModifier)); selectAllButton.setAccelerator(KeyStroke.getKeyStroke('A', defaultModifier)); clearSelectionButton.setAccelerator(KeyStroke.getKeyStroke('A', defaultShiftModifier)); invertSelectionButton.setAccelerator(KeyStroke.getKeyStroke('I', defaultModifier)); groupButton.setAccelerator(KeyStroke.getKeyStroke('G', defaultModifier)); ungroupButton.setAccelerator(KeyStroke.getKeyStroke('U', defaultModifier)); removeButton.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0)); } private void toggleSupplyBarAppearance() { GuiSettings.showSupplyBars = showSupplyBarsCheckBox.isSelected(); } private void saveFile() { GuiSettings.getActualSaveFile().ifPresentOrElse(control::saveFile, this::saveNewFile); } private void saveNewFile() { if (fileChooser.showSaveDialog(Gui.this) == JFileChooser.APPROVE_OPTION) { String path = fileChooser.getSelectedFile().getPath(); if (!path.endsWith(".json")) { path += ".json"; } prefs.put(PreferenceKeys.Gui.DefaultFolder, fileChooser.getCurrentDirectory().getPath()); control.saveFile(new File(path)); } } private void openFile() { if (fileChooser.showOpenDialog(Gui.this) == JFileChooser.APPROVE_OPTION) { prefs.put(PreferenceKeys.Gui.DefaultFolder, fileChooser.getCurrentDirectory().getPath()); control.loadFile(fileChooser.getSelectedFile()); } } private void newFile() { if (control.getModel().getCanvas().getObjectsInThisLayer().findAny().isPresent()) { int selectedOption = JOptionPane.showConfirmDialog(Gui.this, "Do you want to save your current model?", "Warning", JOptionPane.YES_NO_OPTION); if (selectedOption == JOptionPane.YES_OPTION) { saveNewFile(); } } control.clearModel(); } private void selectAll(){ control.setSelection(getActualCanvas().getGroupNode() .getObjectsInThisLayer().collect(Collectors.toSet())); } private void invertSelection() { control.toggleSelectedObjects(getActualCanvas().getGroupNode() .getObjectsInThisLayer().collect(Collectors.toSet())); } private void removeSelectedObjects() { control.deleteCanvasObjects(GuiSettings.getSelectedObjects()); control.clearSelection(); } private void paste() { Vec2i middlePosition = GroupNode.calculateMiddlePosition(GuiSettings.getClipboardObjects()); Optional offset = Optional.ofNullable(getActualCanvas().getMousePosition()) .map(point -> new Vec2i(point).subtract(middlePosition)); control.paste(getActualCanvas().getGroupNode(), offset.orElse(DefaultOffset)); } } }