package org.geogebra.desktop.gui.layout; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.GraphicsEnvironment; import java.awt.Rectangle; import java.awt.SystemColor; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.WindowEvent; import java.awt.event.WindowListener; import java.util.Comparator; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JDialog; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JMenuBar; import javax.swing.JPanel; import javax.swing.JSplitPane; import javax.swing.border.Border; import org.geogebra.common.gui.layout.DockComponent; import org.geogebra.common.gui.layout.DockPanel; import org.geogebra.common.gui.toolbar.ToolBar; import org.geogebra.common.io.layout.DockPanelData; import org.geogebra.common.main.App; import org.geogebra.common.util.debug.Log; import org.geogebra.desktop.awt.GRectangleD; import org.geogebra.desktop.gui.GuiManagerD; import org.geogebra.desktop.gui.app.GeoGebraFrame; import org.geogebra.desktop.gui.layout.panels.EuclidianDockPanelAbstract; import org.geogebra.desktop.gui.toolbar.ToolbarContainer; import org.geogebra.desktop.gui.toolbar.ToolbarD; import org.geogebra.desktop.gui.util.LayoutUtil; import org.geogebra.desktop.main.AppD; import org.geogebra.desktop.main.LocalizationD; import org.geogebra.desktop.util.GuiResourcesD; /** * Every object which should be dragged needs to be of type DockPanel. A * DockPanel will wrap around the component with the real contents (e.g. the * EuclidianView) and will add a title bar if the user is not in the * "layout fixed" mode. The user can move the DockPanel by dragging the title * bar. * * To add a new dock panel one has to subclass DockPanel, implement the abstract * method DockPanel::loadComponent() and maybe replace DockPanel::getIcon() and * DockPanel::getStyleBar(). * * One can add a panel using Layout::registerPanel(), the GuiManager also * provides GuiManager()::initLayoutPanels() as an easy access point to add new * panels. This is also important because it matters at which point of execution * a panel is added, see Layout::registerPanel() for further information. * * @author Florian Sonner */ public abstract class DockPanelD extends JPanel implements ActionListener, WindowListener, MouseListener, DockPanel, DockComponent { private static final long serialVersionUID = 1L; protected DockManagerD dockManager; protected AppD app; protected LocalizationD loc; /** * The ID of this dock panel. */ protected int id; /** * The title of this dock panel. */ private String title; /** * If this panel is visible. */ protected boolean visible = false; /** * If this panel has focus. */ protected boolean hasFocus = false; /** * The dimensions of the external window of this panel. */ protected Rectangle frameBounds = new Rectangle(50, 50, 500, 500); /** * If this panel should be opened in a frame the next time it's visible. */ protected boolean openInFrame = false; /** * If there is a style bar associated with this panel. */ private boolean hasStyleBar = false; /** * Style bar component. */ protected JComponent styleBar; /** * Panel to contain a toggle button within the stylebar panel. */ private JPanel styleBarButtonPanel; /** * If the style bar is visible. */ protected boolean showStyleBar = false; /** * String which stores the position of the panel in the layout. */ protected String embeddedDef = "1"; /** * The size of the panel in the layout, may be either the width or height * depending upon embeddedDef. */ protected int embeddedSize = 150; /** * The panel at the top where the title and the close button is displayed * normally. */ protected JPanel titlePanel; /** * The label with the view title. */ protected JLabel titleLabel; /** * The panel which holds all buttons. */ protected JPanel buttonPanel; /** * The close button. */ protected JButton closeButton; /** * Button which opens the panel in a new window. */ private JButton windowButton; /** * A button which brings the panel back to the main window. */ private JButton unwindowButton, unwindowButton2; /** * Button used to show / hide the style bar in the titlePanel. */ private JButton toggleStyleBarButton; /** * Button used to show / hide the style bar when title panel is invisible. */ private JButton toggleStyleBarButton2; /** * Button to maximize/unmaximize a panel. */ private JButton maximizeButton; /** * Panel for the styling bar if one is available. */ private JPanel styleBarPanel; /** * Panel used for the toolbar if this dock panel has one. */ private JPanel toolbarPanel; /** * Toolbar container which is used if this dock panel is opened in its own * frame. */ private ToolbarContainer toolbarContainer; /** * Toolbar associated with this dock panel or null if this panel has no * toolbar. */ private ToolbarD toolbar; /** * Toolbar definition string associated with this panel or null if this * panel has no toolbar. Always contains the string of the perspective * loaded last. */ protected String toolbarString; /** * Default toolbar definition string associated with this panel or null if * this panel has no toolbar. This string is specified in the constructor * and won't change. */ private String defaultToolbarString; /** * The window which holds this DockPanel if the DockPanel is opened in an * additional window. The window may become either a JFrame or JDialog. */ protected Window frame = null; /** * The component used for this view. */ protected JComponent component; /** * The location of this panel in the view menu. If -1 this panel won't * appear there at all. */ private int menuOrder; /** * Shortcut to show this panel, SHIFT is automatically used as modifier, * \u0000 is the default value. */ private char menuShortcut; /** * Indicator whether this panel is the last one in the main frame. In this * case no title bar will be visible, but just the stylebar. */ private boolean isAlone; /** * Indicator whether this panel is hidden. A hidden panel is not visible, * but it's View component is still attached to the kernel. */ private boolean isHidden; /** * Flag to determine if a dialog is newly created */ private boolean isNewDialog = true; /** * Flag to determine if the frame field will be created as a JDialog (true) * or as a JFram (false). Default is false. */ private boolean isDialog = false; /** * If the view needs a menu bar when undocked, its is kept here */ private JMenuBar menubar; /** * @return true if this dock panel frame will be created as a JDialog. If * false then it will be created as a JFrame * */ public boolean isDialog() { return isDialog; } /** * Sets the isDialog flag. * * @param isDialog * true if this dock panel frame will be created as a JDialog. If * false then it will be created as a JFrame */ public void setDialog(boolean isDialog) { this.isDialog = isDialog; } /** * Prepare dock panel. DockPanel::register() has to be called to make this * panel fully functional! No shortcut is assigned to the view in this * construtor. * * @param id * The id of the panel * @param title * The title phrase of the view located in plain.properties * @param toolbar * The default toolbar string (or null if this view has none) * @param hasStyleBar * If a style bar exists * @param menuOrder * The location of this view in the view menu, -1 if the view * should not appear at all */ public DockPanelD(int id, String title, String toolbar, boolean hasStyleBar, int menuOrder) { this(id, title, toolbar, hasStyleBar, menuOrder, '\u0000'); } /** * Prepare dock panel. DockPanel::register() has to be called to make this * panel fully functional! * * @param id * The id of the panel * @param title * The title phrase of the view located in plain.properties * @param toolbar * The default toolbar string (or null if this view has none) * @param hasStyleBar * If a style bar exists * @param menuOrder * The location of this view in the view menu, -1 if the view * should not appear at all * @param menuShortcut * The shortcut character which can be used to make this view * visible */ public DockPanelD(int id, String title, String toolbar, boolean hasStyleBar, int menuOrder, char menuShortcut) { this.id = id; this.title = title; this.defaultToolbarString = toolbar; this.menuOrder = menuOrder; this.menuShortcut = menuShortcut; this.hasStyleBar = hasStyleBar; this.isAlone = false; this.setMinimumSize(new Dimension(100, 100)); setLayout(new BorderLayout()); } /** * @param app * application */ protected void setApp(AppD app) { this.app = app; this.loc = app.getLocalization(); } /** * @return The icon of the menu item, if this method was not overwritten it * will return the empty icon or null for Win Vista / 7 to prevent * the "checkbox bug" */ public ImageIcon getIcon() { if (AppD.WINDOWS_VISTA_OR_LATER) { return null; } return app.getEmptyIcon(); } /** * @return The style bar. Note: Unless this method is overridden a dummy * stylebar is returned. */ protected JComponent loadStyleBar() { return new JPanel(); } /** * @return The main panel of this view. */ protected abstract JComponent loadComponent(); /** * @return The main panel of this view (null if none was loaded yet). */ public JComponent getComponent() { return component; } /** * Method which is called if this dock panel gained focus. This happens if * setFocus(true) was called and this panel had no focus before. * * @remark If GeoGebra is running as unsigned applet focus is just changed * between euclidian views (even if other views were selected in the * meantime). */ protected void focusGained() { // by default do nothing } /** * Method which is called if this dock panel lost focus. This happens if * setFocus(false) was called and this panel had focus before. * * @remark If GeoGebra is running as unsigned applet focus is just changed * between euclidian views (even if other views were selected in the * meantime). */ protected void focusLost() { // by default do nothing } /** * create the focus panel (composed of titleLabel, and, for * EuclidianDockPanels, focus icon) * * @return the focus panel */ protected JComponent createFocusPanel() { titleLabel = new JLabel(loc.getPlain(title)); titleLabel.setFont(app.getPlainFont()); titleLabel.setForeground(Color.darkGray); JPanel p = new JPanel(new FlowLayout(app.flowLeft(), 2, 1)); if (app.getLocalization().isRightToLeftReadingOrder()) { p.add(titleLabel); p.add(Box.createHorizontalStrut(2)); if (this.hasStyleBar) { p.add(this.toggleStyleBarButton); } } else { if (this.hasStyleBar) { p.add(this.toggleStyleBarButton); } p.add(Box.createHorizontalStrut(2)); p.add(titleLabel); } return p; } /** * Bind this view to a dock manager. Also initializes the whole GUI as just * at this point the application is available. * * @param dockManager1 * dock manager */ public void register(DockManagerD dockManager1) { this.dockManager = dockManager1; setApp(dockManager1.getLayout().getApplication()); // create buttons for the panels createButtons(); // create button panel buttonPanel = new JPanel(); buttonPanel.setLayout(new FlowLayout(app.flowRight(), 0, 1)); if (app.getLocalization().isRightToLeftReadingOrder()) { buttonPanel.add(closeButton); buttonPanel.add(Box.createHorizontalStrut(4)); buttonPanel.add(windowButton); buttonPanel.add(unwindowButton); buttonPanel.add(Box.createHorizontalStrut(4)); buttonPanel.add(maximizeButton); } else { buttonPanel.add(maximizeButton); buttonPanel.add(Box.createHorizontalStrut(4)); buttonPanel.add(unwindowButton); buttonPanel.add(windowButton); buttonPanel.add(Box.createHorizontalStrut(4)); buttonPanel.add(closeButton); } // Custom border for the major panels (title, stylebar and toolbar) Border panelBorder = BorderFactory.createCompoundBorder( BorderFactory.createMatteBorder(0, 0, 1, 0, SystemColor.controlShadow), BorderFactory.createEmptyBorder(0, 2, 0, 2)); // create style bar panel styleBarPanel = new JPanel(new BorderLayout(1, 2)); styleBarPanel.setBorder(panelBorder); styleBarPanel.addMouseListener(this); styleBarButtonPanel = new JPanel(new BorderLayout()); JPanel p = new JPanel(new FlowLayout(0, 0, app.flowLeft())); if (this.hasStyleBar) { p.add(toggleStyleBarButton2); } p.add(Box.createHorizontalStrut(4)); styleBarButtonPanel.add(p, BorderLayout.NORTH); styleBarPanel.add(styleBarButtonPanel, loc.borderWest()); styleBarPanel.add(LayoutUtil.flowPanelRight(0, 0, 4, unwindowButton2), loc.borderEast()); // construct the title panel and add all elements titlePanel = new JPanel(); titlePanel.setBorder(panelBorder); titlePanel.setLayout(new BorderLayout()); titlePanel.add(createFocusPanel(), loc.borderWest()); titlePanel.add(buttonPanel, loc.borderEast()); titlePanel.addMouseListener(this); // dragging to reconfigure titlePanel.addMouseListener(new MyButtonHider()); // create toolbar panel if (hasToolbar()) { toolbarPanel = new JPanel(new BorderLayout()); toolbarPanel.setBorder(panelBorder); } // construct a meta panel to hold the title, tool bar and style bar // panels JPanel titleBar = new JPanel(new BorderLayout()); titleBar.add(styleBarPanel, BorderLayout.SOUTH); titleBar.add(titlePanel, BorderLayout.NORTH); JPanel metaPanel = new JPanel(new BorderLayout()); metaPanel.add(titleBar, BorderLayout.SOUTH); if (hasToolbar()) { metaPanel.add(toolbarPanel, BorderLayout.CENTER); } // make titlebar visible if necessary updatePanel(); add(metaPanel, BorderLayout.NORTH); } private void createButtons() { int iconSize = 16;// app.getScaledIconSize(); int toggleSize = (int) Math.round(app.getScaledIconSize() * 0.75); // button to show/hide styling bar and the title panel buttons toggleStyleBarButton = new JButton(); toggleStyleBarButton.addActionListener(this); toggleStyleBarButton.setFocusPainted(false); toggleStyleBarButton.setBorderPainted(false); toggleStyleBarButton.setContentAreaFilled(false); toggleStyleBarButton .setPreferredSize(new Dimension(toggleSize, toggleSize)); toggleStyleBarButton.setRolloverEnabled(true); // button to show/hide styling bar if the title panel is invisible toggleStyleBarButton2 = new JButton(); toggleStyleBarButton2.setFocusPainted(false); toggleStyleBarButton2.setBorderPainted(false); toggleStyleBarButton2.setContentAreaFilled(false); toggleStyleBarButton2 .setPreferredSize(new Dimension(toggleSize, toggleSize)); toggleStyleBarButton2.addActionListener(this); toggleStyleBarButton2.setRolloverEnabled(true); updateToggleStyleBarButtons(); // button to insert the view in the main window unwindowButton = new JButton( app.getScaledIcon(GuiResourcesD.VIEW_UNWINDOW)); unwindowButton.addActionListener(this); unwindowButton.setFocusPainted(false); unwindowButton.setContentAreaFilled(false); unwindowButton.setBorderPainted(false); unwindowButton.setPreferredSize(new Dimension(iconSize, iconSize)); // button to insert the view in the main window unwindowButton2 = new JButton( app.getScaledIcon(GuiResourcesD.VIEW_UNWINDOW)); unwindowButton2.addActionListener(this); unwindowButton2.setFocusPainted(false); unwindowButton2.setContentAreaFilled(false); unwindowButton2.setBorderPainted(false); unwindowButton2.setPreferredSize(new Dimension(iconSize, iconSize)); // button to display the view in a separate window windowButton = new JButton( app.getScaledIcon(GuiResourcesD.VIEW_WINDOW)); windowButton.addActionListener(this); windowButton.setFocusPainted(false); windowButton.setContentAreaFilled(false); windowButton.setBorderPainted(false); windowButton.setPreferredSize(new Dimension(iconSize, iconSize)); // button to close the view closeButton = new JButton(app.getScaledIcon(GuiResourcesD.VIEW_CLOSE)); closeButton.addActionListener(this); closeButton.setFocusPainted(false); closeButton.setPreferredSize(new Dimension(iconSize, iconSize)); // button to toggle maximize/normal state maximizeButton = new JButton( app.getScaledIcon(GuiResourcesD.VIEW_MAXIMIZE)); maximizeButton.addActionListener(this); maximizeButton.setFocusPainted(false); maximizeButton.setPreferredSize(new Dimension(iconSize, iconSize)); } /** * * @return title in plain style */ protected String getPlainTitle() { return loc.getPlain(title); } /** * Create a frame for this DockPanel. The frame will either be a JFrame or a * JDialog depending on the isDialog flag. */ public void createFrame() { if (isDialog) { frame = new JDialog(app.getFrame(), false) { private static final long serialVersionUID = 1L; // Send window closing event when dialog is set invisible. // This allows a dock panel view to close properly. @Override public void setVisible(boolean isVisible) { if (!isVisible && frame != null) { windowClosing(new WindowEvent(frame, WindowEvent.WINDOW_CLOSING)); } super.setVisible(isVisible); } }; } else { frame = new JFrame(getPlainTitle()); // needs the higher res as used by Windows 7 for the Toolbar ((JFrame) frame).setIconImage( app.getInternalImage(GuiResourcesD.GEOGEBRA64)); } frame.addWindowListener(this); frame.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent event) { setFrameBounds(event.getComponent().getBounds()); } @Override public void componentMoved(ComponentEvent event) { setFrameBounds(event.getComponent().getBounds()); } }); if (isDialog) { (((JDialog) frame).getContentPane()).add(this); } else { (((JFrame) frame).getContentPane()).add(this); menubar = loadMenuBar(); if (menubar != null) { ((JFrame) frame).setJMenuBar(menubar); } } // TODO multimonitor supported? Rectangle screenSize = GraphicsEnvironment.getLocalGraphicsEnvironment() .getMaximumWindowBounds(); // Use the previous dimension of this view Rectangle windowBounds = getFrameBounds(); // resize window if necessary if (windowBounds.width > screenSize.width) { windowBounds.width = screenSize.width - 50; } if (windowBounds.height > screenSize.height) { windowBounds.height = windowBounds.height - 50; } // center window if necessary if (isNewDialog) { // frame.pack(); frame.setSize(windowBounds.getSize()); frame.setLocationRelativeTo(app.getMainComponent()); isNewDialog = false; } else if (windowBounds.x + windowBounds.width > screenSize.width || windowBounds.y + windowBounds.height > screenSize.height) { frame.setLocationRelativeTo(null); } else { frame.setLocation(windowBounds.getLocation()); } setOpenInFrame(true); frame.setSize(windowBounds.getSize()); frame.setVisible(true); // make titlebar visible if necessary updatePanel(); frame.repaint(); } /** * Remove the frame. */ public void removeFrame() { frame.removeAll(); frame.setVisible(false); frame = null; } /** * Update all elements in the title bar. */ public void updateTitleBar() { // The view is in the main window if (frame == null) { closeButton.setVisible(!isMaximized()); windowButton.setVisible(false); // !isMaximized()); unwindowButton.setVisible(false); unwindowButton2.setVisible(false); maximizeButton.setVisible(isMaximized()); titleLabel.setVisible(true); } else { closeButton.setVisible(false); unwindowButton.setVisible(true); unwindowButton2.setVisible(true); windowButton.setVisible(false); maximizeButton.setVisible(false); titleLabel.setVisible(false); } if (isMaximized()) { maximizeButton .setIcon(app.getScaledIcon(GuiResourcesD.VIEW_UNMAXIMIZE)); } else { maximizeButton .setIcon(app.getScaledIcon(GuiResourcesD.VIEW_MAXIMIZE)); } updateLabels(); } /** * A panel is 'alone' if no other panel is visible in the main frame. In * this case no title bar is displayed, but just the style bar. Changing the * value of the 'alone' state will cause the GUI to update automatically if * this panel is visible. * * @param isAlone * whether is only panel in the main frame */ public void setAlone(boolean isAlone) { if (this.isAlone == isAlone) { return; } this.isAlone = isAlone; if (isVisible()) { updatePanel(); } } /** * @return If this panel thinks it's the last visible one in the main frame. */ public boolean isAlone() { return isAlone; } /** * @return If this panel is hidden but not permanently removed. */ public boolean isHidden() { return isHidden; } /** * Sets the the isHidden flag (no other action) * * @param isHidden * true for hidden dock panel */ public void setHidden(boolean isHidden) { this.isHidden = isHidden; } /** * Update the panel. */ public void updatePanel() { // load content if panel was hidden till now if (component == null && isVisible()) { component = loadComponent(); add(component, BorderLayout.CENTER); if (isStyleBarVisible()) { setStyleBar(); } // load toolbar if this panel has one if (hasToolbar()) { toolbar = new ToolbarD(app, this); if (isOpenInFrame()) { toolbarContainer = new ToolbarContainer(app, false); toolbarContainer.addToolbar(toolbar); toolbarContainer.buildGui(); toolbarContainer.setActiveToolbar(getViewId()); toolbarPanel.add(toolbarContainer, BorderLayout.CENTER); } } // euclidian view uses the general toolbar if (this instanceof EuclidianDockPanelAbstract) { // TODO implement.. } } // make panels visible if necessary if (isVisible()) { if (isStyleBarVisible()) { setStyleBar(); } // display toolbar panel if the dock panel is open in a frame if (hasToolbar()) { toolbarPanel.setVisible(frame != null); } } // if this is the last dock panel don't display the title bar, otherwise // take the user's configuration into consideration titlePanel.setVisible(app.getSettings().getLayout().showTitleBar() && !(isAlone && !isMaximized()) && !app.isApplet() && (!isOpenInFrame())); // update stylebar visibility setShowStyleBar(isStyleBarVisible()); updateStyleBarVisibility(); // update the title bar if necessary updateTitleBarIfNecessary(); } /** * */ protected void updateTitleBarIfNecessary() { if (titlePanel.isVisible()) { updateTitleBar(); } } protected JMenuBar loadMenuBar() { return null; } /** * Update the toolbar of this dock panel if it's open in its own toolbar * container. */ public void updateToolbar() { if (isVisible() && isOpenInFrame() && hasToolbar()) { toolbarContainer.updateToolbarPanel(); } } /** * Change the toolbar mode for panels open in a separate frame. * * @param mode */ public void setToolbarMode(int mode) { if (toolbarContainer != null && isVisible() && isOpenInFrame() && hasToolbar()) { toolbarContainer.setMode(mode); } } /** * Update the toolbar GUI. */ public void buildToolbarGui() { if (toolbarContainer != null) { toolbarContainer.buildGui(); toolbarContainer.updateHelpText(); if (isVisible() && isOpenInFrame()) { frame.validate(); } } } /** * Update all labels of this DockPanel. Called while initializing and if the * language was changed. */ public void updateLabels() { closeButton.setToolTipText(loc.getMenuTooltip("Close")); windowButton.setToolTipText(loc.getPlainTooltip("ViewOpenExtraWindow")); unwindowButton .setToolTipText(loc.getPlainTooltip("ViewCloseExtraWindow")); unwindowButton2 .setToolTipText(loc.getPlainTooltip("ViewCloseExtraWindow")); toggleStyleBarButton .setToolTipText(loc.getPlainTooltip("ToggleStyleBar")); toggleStyleBarButton2 .setToolTipText(loc.getPlainTooltip("ToggleStyleBar")); if (frame == null) { titleLabel.setText(getPlainTitle()); } else { updateTitle(); } } /** * Update fonts. */ public void updateFonts() { if (hasFocus && dockManager.hasFullFocusSystem()) { titleLabel.setFont(app.getBoldFont()); } else { titleLabel.setFont(app.getPlainFont()); } updateIcons(); } /** * Update the title of the frame. This is necessary if the language changed * or if the title of the main window changed (e.g. because the file was * saved under a different name). */ public void updateTitle() { if (isOpenInFrame()) { StringBuilder windowTitle = new StringBuilder(); windowTitle.append(getPlainTitle()); if (app.getCurrentFile() != null) { windowTitle.append(" - "); windowTitle.append(app.getCurrentFile().getName()); } else { if (GeoGebraFrame.getInstanceCount() > 1) { int nr = ((GeoGebraFrame) app.getFrame()) .getInstanceNumber(); windowTitle.append(" - ("); windowTitle.append(nr + 1); windowTitle.append(")"); } } if (isDialog) { ((JDialog) frame).setTitle(windowTitle.toString()); } else { ((JFrame) frame).setTitle(windowTitle.toString()); } } } /** * Close this panel permanently. */ @Override public void closePanel() { closePanel(true); } /** * Close this panel. * * @param isPermanent * true for permanent closing (also detach the view) */ protected void closePanel(boolean isPermanent) { dockManager.closePanel(this, isPermanent); } /** * Display this panel in an external window. */ public void windowPanel() { // try to hide the panel if (dockManager.hide(this, false)) { // move the toolbar from the main window to the panel if (hasToolbar()) { if (toolbarContainer == null) { toolbarContainer = new ToolbarContainer(app, false); } toolbarContainer.addToolbar(toolbar); toolbarContainer.buildGui(); toolbarContainer.setActiveToolbar(getViewId()); toolbarPanel.add(toolbarContainer, BorderLayout.CENTER); ToolbarContainer mainContainer = ((GuiManagerD) app .getGuiManager()).getToolbarPanel(); mainContainer.removeToolbar(toolbar); mainContainer.updateToolbarPanel(); } setVisible(true); createFrame(); } } /** * Display this panel in the main window. */ public void unwindowPanel() { // hide the frame dockManager.hide(this, false); // don't display this panel in a frame the next time setOpenInFrame(false); // show the panel in the main window dockManager.show(this); // as this view already *had* focus and will retain focus // DockManager::show() // won't be able to update the active toolbar if (hasToolbar()) { ((GuiManagerD) app.getGuiManager()).getToolbarPanel() .setActiveToolbar(toolbar); } } /** loads the styleBar and puts it into the stylBarPanel */ protected void setStyleBar() { if (styleBar == null) { styleBar = loadStyleBar(); styleBarPanel.add(styleBar, BorderLayout.CENTER); } } /** * Toggle the style bar. */ public void toggleStyleBar() { setShowStyleBar(!showStyleBar); updateStyleBarVisibility(); } /** * Update the style bar visibility. */ public void updateStyleBarVisibility() { if (!isVisible()) { return; } styleBarPanel.setVisible(isStyleBarVisible()); updateToggleStyleBarButtons(); updateTitleBar(); if (isStyleBarVisible()) { setStyleBar(); styleBar.setVisible(showStyleBar); styleBarButtonPanel.setVisible(!titlePanel.isVisible()); } } /** * One of the buttons was pressed. */ @Override public void actionPerformed(ActionEvent e) { if (e.getSource() == closeButton) { closePanel(false); } else if (e.getSource() == windowButton) { windowPanel(); } else if (e.getSource() == unwindowButton || e.getSource() == unwindowButton2) { unwindowPanel(); } else if (e.getSource() == toggleStyleBarButton || e.getSource() == toggleStyleBarButton2) { toggleStyleBar(); } else if (e.getSource() == maximizeButton) { toggleMaximize(); } } /** * Hide the view if the window was closed or if the close button was * pressed. */ @Override public void windowClosing(WindowEvent e) { closePanel(false); } /** * Start dragging if the mouse was pressed while it was on the title panel. * Or toggle the stylebar on double-click. */ @Override public void mousePressed(MouseEvent arg0) { // double-click opens the stylebar and shows the button panel if (arg0.getClickCount() == 2) { // toggleStyleBar(); toggleMaximize(); } // otherwise start drag if the view is in the main window else { if (frame == null) { dockManager.drag(this); } } } /** * @return The parent DockSplitPane or null. */ public DockSplitPane getParentSplitPane() { if (isOpenInFrame()) { return null; } Container parent = getParent(); if (parent == null || !(parent instanceof DockSplitPane)) { return null; } return (DockSplitPane) parent; } /** * @return The embedded def string for this DockPanel. */ public String calculateEmbeddedDef() { StringBuilder def = new StringBuilder(); Component current = this; Component parent = this.getParent(); DockSplitPane parentDSP; while (parent instanceof DockSplitPane) { int defType = -1; parentDSP = (DockSplitPane) parent; if (parentDSP.getOrientation() == JSplitPane.HORIZONTAL_SPLIT) { if (current == parentDSP.getLeftComponent()) { defType = 3; } else { // right defType = 1; } } else { if (current == parentDSP.getLeftComponent()) { defType = 0; } else { // bottom defType = 2; } } if (def.length() == 0) { def.append(defType); } else { def.append("," + defType); } current = parent; parent = current.getParent(); } return def.reverse().toString(); } /** * @return The XML container which stores all relevant information for this * panel. */ public DockPanelData createInfo() { return new DockPanelData(id, toolbarString, visible, openInFrame, showStyleBar, new GRectangleD(frameBounds), embeddedDef, embeddedSize); } /** * @return If this DockPanel is in an extra frame / window. */ public boolean isInFrame() { return frame != null; } /** * If this view should open in a frame. Has no immediate effect. * * @param openInFrame */ public void setOpenInFrame(boolean openInFrame) { this.openInFrame = openInFrame; } /** * @return Whether this view should open in frame. */ @Override public boolean isOpenInFrame() { return openInFrame; } /** * If the stylebar of this view should be visible. Has no immediate effect. * * @param showStyleBar */ public void setShowStyleBar(boolean showStyleBar) { this.showStyleBar = showStyleBar; } private void updateToggleStyleBarButtons() { if (toggleStyleBarButton != null) { if (showStyleBar) { toggleStyleBarButton.setIcon( app.getScaledIcon(GuiResourcesD.TRIANGLE_DOWN)); } else { toggleStyleBarButton.setIcon( app.getScaledIcon(GuiResourcesD.TRIANGLE_RIGHT)); } } if (toggleStyleBarButton2 != null) { toggleStyleBarButton2.setIcon(toggleStyleBarButton.getIcon()); } } /** * @return If the style bar should be visible. */ protected boolean isStyleBarVisible() { if (id == App.VIEW_EUCLIDIAN || id == App.VIEW_EUCLIDIAN2 || id == App.VIEW_ALGEBRA) { if (!app.getSettings().getLayout().isAllowingStyleBar()) { return false; } } return (showStyleBar || !titlePanel.isVisible()); } public void setFrameBounds(Rectangle frameBounds) { this.frameBounds = frameBounds; } public Rectangle getFrameBounds() { return this.frameBounds; } /** * @return return the Window */ public Window getFrame() { return frame; } /** * @param embeddedDef * the embeddedDef to set */ public void setEmbeddedDef(String embeddedDef) { this.embeddedDef = embeddedDef; } public String getEmbeddedDef() { return embeddedDef; } /** * @param embeddedSize * the embeddedSize to set */ public void setEmbeddedSize(int embeddedSize) { this.embeddedSize = embeddedSize; } /** * @return the embeddedSize */ public int getEmbeddedSize() { return embeddedSize; } /** * @return If this DockPanel is visible. */ @Override public boolean isVisible() { return visible; } @Override public void setVisible(boolean visible) { if (this.visible != visible) { this.visible = visible; if (app.getGuiManager() != null) { app.getGuiManager().updatePropertiesViewStylebar(); } } } @Override public boolean hasFocus() { return hasFocus; } /** * Mark this panel as focused. When gaining focus the panel will * automatically request focus for its parent frame. * * @remark The focus system implemented here has nothing to do with swings * focus system, therefore Swings focus methods won't work. * * @param hasFocus * has the focus * @param updatePropertiesView * update properties view */ public void setFocus(boolean hasFocus, boolean updatePropertiesView) { if (hasFocus && updatePropertiesView) { ((GuiManagerD) app.getGuiManager()).updatePropertiesView(); } setFocus(hasFocus); } /** * Mark this panel as focused. When gaining focus the panel will * automatically request focus for its parent frame. * * @remark The focus system implemented here has nothing to do with swings * focus system, therefore Swings focus methods won't work. * * @param hasFocus * has the focus */ protected void setFocus(boolean hasFocus) { // don't change anything if it's not necessary if (this.hasFocus == hasFocus) { return; } this.hasFocus = hasFocus; if (hasFocus) { // request focus and change toolbar if necessary if (openInFrame) { frame.requestFocus(); } else { if (!app.isApplet()) { JFrame frame1 = app.getFrame(); if (frame1 != null) { frame1.toFront(); } } setActiveToolBar(); } } else { } // call callback methods for focus changes if (hasFocus) { focusGained(); } else { focusLost(); } /* * Mark the focused view in bold if the focus system is available. If * this isn't the case we always stick with the normal font as it would * confuse the users that the focus "indicator" just changes if we * switch between EVs. */ setTitleLabelFocus(); } /** * sets the active toolbar */ protected void setActiveToolBar() { int currentMode; if (hasToolbar()) { currentMode = ((GuiManagerD) app.getGuiManager()).getToolbarPanel() .setActiveToolbar(toolbar); } else { currentMode = ((GuiManagerD) app.getGuiManager()).getToolbarPanel() .setActiveToolbar(-1); } // switching the view may cause shrinking of help panel, // we need an update here ((GuiManagerD) app.getGuiManager()).getToolbarPanel().validate(); ((GuiManagerD) app.getGuiManager()).getToolbarPanel() .updateHelpText(currentMode); } /** * sets the title label when this has not the focus */ protected void setTitleLabelFocus() { if (dockManager.hasFullFocusSystem()) { if (titleIsBold()) { titleLabel.setFont(app.getBoldFont()); } else { titleLabel.setFont(app.getPlainFont()); } } } /** * * @return true if title has to be in bold */ protected boolean titleIsBold() { return hasFocus; } /** * @return An unique ID for this DockPanel. */ @Override public int getViewId() { return id; } /** * @return The title of this view. The String returned has to be the key of * a value in plain.properties */ public String getViewTitle() { return title; } /** * @return The order of this panel in the view menu, with 0 being "highest". * Will be -1 if this view does not appear in the menu at all. */ public int getMenuOrder() { return menuOrder; } /** * @return Whether the current view has a menu shortcut to toggle its * visibility. */ public boolean hasMenuShortcut() { return menuShortcut != '\u0000'; } /** * @return The menu shortcut of this view. */ public char getMenuShortcut() { return menuShortcut; } /** * @return The toolbar associated with this panel. */ public ToolbarD getToolbar() { return toolbar; } /** * @return If this panel has a toolbar. */ public boolean hasToolbar() { return defaultToolbarString != null; } /** * @return If this panel can customize its toolbar. */ public boolean canCustomizeToolbar() { return hasToolbar(); } /** * @return The definition string associated with this toolbar. */ @Override public String getToolbarString() { if (toolbarString == null) { Log.warn("Toolbar not initialized"); return defaultToolbarString; } return toolbarString; } /** * Set the toolbar string of this view. If the toolbar string is null but * this panel has a panel normally the default toolbar string is used. This * is used for backward compability. Has no visible effect. * * @param toolbarString */ public void setToolbarString(String toolbarString) { if (toolbarString == null && hasToolbar()) { this.toolbarString = defaultToolbarString; } else { this.toolbarString = toolbarString; } } /** * @return The default toolbar string of this panel (or null). */ @Override public String getDefaultToolbarString() { return defaultToolbarString; } /** * @return dock panel information as string for debugging. */ @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("[DockPanel,id="); sb.append(getViewId()); sb.append(",toolbar="); sb.append(getToolbarString()); sb.append(",visible="); sb.append(isVisible()); sb.append(",inframe="); sb.append(isOpenInFrame()); sb.append("]"); return sb.toString(); } /** * Helper class to compare dock panels for sorting in the menu. * * @author Florian Sonner */ public static class MenuOrderComparator implements Comparator<DockPanelD> { @Override public int compare(DockPanelD a, DockPanelD b) { return a.getMenuOrder() - b.getMenuOrder(); } } @Override public final void windowClosed(WindowEvent e) { // only handle windowClosing } @Override public final void windowActivated(WindowEvent e) { // only handle windowClosing } @Override public final void windowDeactivated(WindowEvent e) { // only handle windowClosing } @Override public final void windowDeiconified(WindowEvent e) { // only handle windowClosing } @Override public final void windowIconified(WindowEvent e) { // only handle windowClosing } @Override public final void windowOpened(WindowEvent e) { // only handle windowClosing } @Override public final void mouseClicked(MouseEvent e) { // only handle mousePressed } @Override public final void mouseEntered(MouseEvent e) { // only handle mousePressed } @Override public final void mouseExited(MouseEvent e) { // only handle mousePressed } @Override public final void mouseReleased(MouseEvent e) { // only handle mousePressed } public class MyButtonHider extends MouseAdapter { @Override public void mouseEntered(MouseEvent e) { // System.out.println("entered, not jpanel"); if (e.getSource() != titlePanel) { e.consume(); } else if (!windowButton.isVisible() && (!isAlone() && !isInFrame() && !isMaximized())) { windowButton.setVisible(true); } // make sure tooltips from Tool Bar don't get in the way setToolTipText(""); } @Override public void mouseExited(MouseEvent e) { // System.out.println("exited:"); if (!titlePanel.getVisibleRect().contains(e.getPoint())) { windowButton.setVisible(false); } } } /** * @return true if the layout has been maximized */ public boolean isMaximized() { return dockManager.isMaximized(); } /** * Toggles the panel between maximized and normal state */ public void toggleMaximize() { if (isMaximized()) { dockManager.undoMaximize(true); } else { dockManager.maximize(this); } updatePanel(); } @Override public String toString(String prefix) { return "\n" + prefix + this.toString(); } @Override public boolean updateResizeWeight() { return false; } @Override public void saveDividerLocation() { // no divider here } @Override public void updateDividerLocation(int size, int orientation1) { // no divider here } @Override public void setDockPanelsVisible(boolean visible) { setVisible(visible); } public boolean isEuclidianDockPanel3D() { return false; } @Override public void deferredOnResize() { // used in Web only } public void addToToolbar(int mode) { this.toolbarString = ToolBar.addMode(toolbarString, mode); } public void updateIcons() { if (toggleStyleBarButton == null) { return; } int iconSize = app.getScaledIconSize(); int toggleSize = (int) Math.round(app.getScaledIconSize() * 0.75); // button to show/hide styling bar and the title panel buttons toggleStyleBarButton .setPreferredSize(new Dimension(toggleSize, toggleSize)); // button to show/hide styling bar if the title panel is invisible toggleStyleBarButton2 .setPreferredSize(new Dimension(toggleSize, toggleSize)); // button to insert the view in the main window unwindowButton.setIcon(app.getScaledIcon(GuiResourcesD.VIEW_UNWINDOW)); unwindowButton.setPreferredSize(new Dimension(iconSize, iconSize)); // button to insert the view in the main window unwindowButton2.setIcon(app.getScaledIcon(GuiResourcesD.VIEW_UNWINDOW)); unwindowButton2.setPreferredSize(new Dimension(iconSize, iconSize)); // button to display the view in a separate window windowButton.setIcon(app.getScaledIcon(GuiResourcesD.VIEW_WINDOW)); windowButton.setPreferredSize(new Dimension(iconSize, iconSize)); // button to close the view closeButton.setIcon(app.getScaledIcon(GuiResourcesD.VIEW_CLOSE)); closeButton.setPreferredSize(new Dimension(iconSize, iconSize)); // button to toggle maximize/normal state maximizeButton.setPreferredSize(new Dimension(iconSize, iconSize)); if (isMaximized()) { maximizeButton .setIcon(app.getScaledIcon(GuiResourcesD.VIEW_UNMAXIMIZE)); } else { maximizeButton .setIcon(app.getScaledIcon(GuiResourcesD.VIEW_MAXIMIZE)); } updateToggleStyleBarButtons(); } @Override public void updateNavigationBar() { // not needed in desktop } public boolean hasPlane() { return false; } }