/* * RapidMiner * * Copyright (C) 2001-2014 by RapidMiner and the contributors * * Complete list of developers available at our web site: * * http://rapidminer.com * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see http://www.gnu.org/licenses/. */ package com.rapidminer.gui.new_plotter.gui.popup; import java.awt.Component; import java.awt.Dimension; import java.awt.KeyboardFocusManager; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import javax.swing.JDialog; import javax.swing.JToggleButton; import javax.swing.SwingUtilities; import com.rapidminer.gui.RapidMinerGUI; import com.rapidminer.gui.tools.ResourceAction; /** * This action can be used to show a component as a popup on the screen. If the popup loses the * focus to another component in the containing window it will be hidden. * <p> * The action's settings are taken from a .properties file being part of the GUI Resource bundles of * RapidMiner. These might be accessed using the I18N class. * <p> * A resource action needs a key specifier, which will be used to build the complete keys of the * form: * <ul> * <li>gui.action.-key-.label = Which will be the caption</li> * <li>gui.action.-key-.icon = The icon of this action. For examples used in menus or buttons</li> * <li>gui.action.-key-.acc = The accelerator key used for menu entries</li> * <li>gui.action.-key-.tip = Which will be the tool tip</li> * <li>gui.action.-key-.mne = Which will give you access to the mnemonics key. Please make it the * same case as in the label</li> * </ul> * * @author Nils Woehler * */ public class PopupAction extends ResourceAction implements PopupComponentListener, ComponentListener { public enum PopupPosition { HORIZONTAL, VERTICAL } private class ContainerPopupDialog extends JDialog { private static final long serialVersionUID = 1L; public ContainerPopupDialog(Window owner, Component comp, Point point) { super(owner != null ? owner : RapidMinerGUI.getMainFrame()); this.add(comp); this.setLocation(point); this.setUndecorated(true); pack(); } } private static final long serialVersionUID = 1L; private final PopupPanel popupComponent; private Component actionSource = null; private ContainerPopupDialog popup = null; private PopupPosition position = PopupPosition.VERTICAL; private static final int BORDER_OFFSET = 9; private Window containingWindow; private long hideTime = 0; public PopupAction(boolean smallIcon, String i18nKey, Component component, Object... i18nArgs) { super(smallIcon, i18nKey, i18nArgs); this.popupComponent = new PopupPanel(component); popupComponent.addListener(this); } public PopupAction(boolean smallIcon, String i18nKey, Component component, PopupPosition position, Object... i18nArgs) { super(smallIcon, i18nKey, i18nArgs); this.position = position; this.popupComponent = new PopupPanel(component); popupComponent.addListener(this); } public PopupAction(String i18nKey, Component component, Object... i18nArgs) { super(i18nKey, i18nArgs); this.popupComponent = new PopupPanel(component); popupComponent.addListener(this); } public PopupAction(String i18nKey, Component component, PopupPosition position, Object... i18nArgs) { super(i18nKey, i18nArgs); this.position = position; this.popupComponent = new PopupPanel(component); popupComponent.addListener(this); } @Override public synchronized void actionPerformed(ActionEvent e) { if(System.currentTimeMillis()-hideTime > 150) { if (hidePopup()) { return; } showPopup((Component) e.getSource()); } else { Object source = e.getSource(); if(source instanceof JToggleButton) { ((JToggleButton) e.getSource()).setSelected(false); } } } private Point calculatePosition(Component source) { int xSource = source.getLocationOnScreen().x; int ySource = source.getLocationOnScreen().y; // get size of popup Dimension popupSize = ((Component) popupComponent).getSize(); if (popupSize.width == 0) { popupSize = ((Component) popupComponent).getPreferredSize(); } int xPopup = 0; int yPopup = 0; // get max x and y window positions Window focusedWindow = KeyboardFocusManager.getCurrentKeyboardFocusManager().getActiveWindow(); int maxX = focusedWindow.getLocationOnScreen().x + focusedWindow.getWidth(); int maxY = focusedWindow.getLocationOnScreen().y + focusedWindow.getHeight(); switch (position) { case VERTICAL: // place popup at sources' x position xPopup = xSource; // check if popup is outside active window if (xPopup + popupSize.width > maxX) { // move popup x position to the left // to fit inside the active window xPopup = maxX - popupSize.width - BORDER_OFFSET; } //place popup below source yPopup = ySource + source.getHeight(); // check if popup is outside active window if (yPopup + popupSize.height > maxY) { // move popup y position above source yPopup = ySource - popupSize.height; } break; case HORIZONTAL: //place popup on right side of source xPopup = xSource + source.getWidth(); //check if popup is outside active window if (xPopup + popupSize.width > maxX) { // move popup to left side of source xPopup = xSource - popupSize.width - BORDER_OFFSET; } yPopup = ySource; //check if popup is outside active window if (yPopup + popupSize.height > maxY) { // move popup upwards to fit into active window yPopup = maxY - popupSize.height - BORDER_OFFSET; } break; } return new Point(xPopup, yPopup); } /** * Creates the popup, calculates position and sets focus */ private void showPopup(Component source) { actionSource = source; actionSource.addComponentListener(this); if (actionSource instanceof JToggleButton) { JToggleButton toggleSource = (JToggleButton) actionSource; toggleSource.setSelected(true); } containingWindow = SwingUtilities.windowForComponent(actionSource); containingWindow.addComponentListener(this); Point position = calculatePosition(source); popupComponent.setLocation(position); popupComponent.setVisible(true); popup = new ContainerPopupDialog(containingWindow, popupComponent, position); popup.setVisible(true); popup.requestFocus(); popupComponent.startTracking(containingWindow, actionSource); } /** * Hides the popup component. */ private boolean hidePopup() { if (actionSource instanceof JToggleButton) { JToggleButton toggleSource = (JToggleButton) actionSource; toggleSource.setSelected(false); } if (containingWindow != null) { containingWindow.removeComponentListener(this); containingWindow = null; } if (actionSource != null) { actionSource.removeComponentListener(this); actionSource = null; } // Check if popup is visible if (popup != null) { popupComponent.setVisible(false); popupComponent.stopTracking(); // hide popup and reset popup.dispose(); popup = null; hideTime = System.currentTimeMillis(); return true; } return false; } public void focusLost() { hidePopup(); } @Override public void componentResized(ComponentEvent e) { hidePopup(); } @Override public void componentMoved(ComponentEvent e) { // Unnecessary to hide on move event. Causes trouble on Linux //hidePopup(); } @Override public void componentShown(ComponentEvent e) { return; // Nothing to be done } @Override public void componentHidden(ComponentEvent e) { hidePopup(); } }