/* * This file is part of WebLookAndFeel library. * * WebLookAndFeel library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * WebLookAndFeel library 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with WebLookAndFeel library. If not, see . */ package com.alee.managers.popup; import com.alee.extended.layout.TableLayout; import com.alee.global.StyleConstants; import com.alee.laf.button.WebButton; import com.alee.laf.panel.WebPanel; import com.alee.managers.style.skin.web.WebPanelPainter; import com.alee.utils.LafUtils; import com.alee.utils.SwingUtils; import com.alee.utils.swing.AncestorAdapter; import javax.swing.*; import javax.swing.event.AncestorEvent; import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.Arrays; import java.util.List; /** * User: mgarin Date: 26.03.12 Time: 17:32 */ public class WebButtonPopup extends WebPopup { private static final List BUTTON_PROPERTIES = Arrays.asList ( AbstractButton.ICON_CHANGED_PROPERTY, AbstractButton.TEXT_CHANGED_PROPERTY, AbstractButton.HORIZONTAL_ALIGNMENT_CHANGED_PROPERTY, AbstractButton.VERTICAL_ALIGNMENT_CHANGED_PROPERTY, AbstractButton.HORIZONTAL_TEXT_POSITION_CHANGED_PROPERTY, AbstractButton.VERTICAL_TEXT_POSITION_CHANGED_PROPERTY, "iconTextGap", "border" ); private PopupWay popupWay; private final WebButton button; private final WebButton copiedButton; private final WebPanel container; public WebButtonPopup ( final WebButton button, final PopupWay popupWay ) { super (); this.popupWay = popupWay; this.button = button; // Initial popup settings setCloseOnFocusLoss ( true ); setFocusCycleRoot ( false ); setPainter ( new WebButtonPopupPainter () ); // Button copy for popup copiedButton = copy ( button ); copiedButton.addActionListener ( new ActionListener () { @Override public void actionPerformed ( final ActionEvent e ) { hidePopup ( true ); } } ); // Button listeners button.addActionListener ( new ActionListener () { @Override public void actionPerformed ( final ActionEvent e ) { // Displaying popup when button is pressed showPopup (); } } ); button.addPropertyChangeListener ( new PropertyChangeListener () { @Override public void propertyChange ( final PropertyChangeEvent evt ) { // Updating button copy on property changes if ( BUTTON_PROPERTIES.contains ( evt.getPropertyName () ) ) { copySettings ( button, copiedButton ); } } } ); button.addHierarchyListener ( new HierarchyListener () { @Override public void hierarchyChanged ( final HierarchyEvent e ) { // Hiding popup properly when popup button parent has changed if ( e.getID () == HierarchyEvent.PARENT_CHANGED ) { hidePopup ( false ); } } } ); button.addAncestorListener ( new AncestorAdapter () { @Override public void ancestorRemoved ( final AncestorEvent event ) { // Hiding popup properly when button is removed from visible container somehow hidePopup ( false ); } @Override public void ancestorMoved ( final AncestorEvent event ) { // Placing popup properly when button has moved updateBounds (); } } ); button.addComponentListener ( new ComponentAdapter () { @Override public void componentHidden ( final ComponentEvent e ) { // Hiding popup properly when button is hidden hidePopup ( false ); } @Override public void componentResized ( final ComponentEvent e ) { updateBounds (); } @Override public void componentMoved ( final ComponentEvent e ) { updateBounds (); } } ); // Popup content panel container = new WebPanel (); container.setOpaque ( false ); container.setFocusCycleRoot ( true ); // Initial components composition updateContent (); // Bounds update listeners addAncestorListener ( new AncestorAdapter () { @Override public void ancestorAdded ( final AncestorEvent event ) { updateBounds (); } @Override public void ancestorMoved ( final AncestorEvent event ) { updateBounds (); } } ); } private void updateContent () { removeAll (); // Bottom popup if ( popupWay.equals ( PopupWay.downLeft ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.FILL, TableLayout.PREFERRED }, { TableLayout.PREFERRED, TableLayout.PREFERRED } } ) ); add ( copiedButton, "1,0" ); add ( container, "0,1,1,1" ); } else if ( popupWay.equals ( PopupWay.downCenter ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.FILL, TableLayout.PREFERRED, TableLayout.FILL }, { TableLayout.PREFERRED, TableLayout.PREFERRED } } ) ); add ( copiedButton, "1,0" ); add ( container, "0,1,2,1" ); } else if ( popupWay.equals ( PopupWay.downRight ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.FILL }, { TableLayout.PREFERRED, TableLayout.PREFERRED } } ) ); add ( copiedButton, "0,0" ); add ( container, "0,1,1,1" ); } // Top popup if ( popupWay.equals ( PopupWay.upLeft ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.FILL, TableLayout.PREFERRED }, { TableLayout.PREFERRED, TableLayout.PREFERRED } } ) ); add ( copiedButton, "1,1" ); add ( container, "0,0,1,0" ); } else if ( popupWay.equals ( PopupWay.upCenter ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.FILL, TableLayout.PREFERRED, TableLayout.FILL }, { TableLayout.PREFERRED, TableLayout.PREFERRED } } ) ); add ( copiedButton, "1,1" ); add ( container, "0,0,2,0" ); } else if ( popupWay.equals ( PopupWay.upRight ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.FILL }, { TableLayout.PREFERRED, TableLayout.PREFERRED } } ) ); add ( copiedButton, "0,1" ); add ( container, "0,0,1,0" ); } // Left popup if ( popupWay.equals ( PopupWay.leftUp ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.PREFERRED }, { TableLayout.FILL, TableLayout.PREFERRED } } ) ); add ( copiedButton, "1,1" ); add ( container, "0,0,0,1" ); } else if ( popupWay.equals ( PopupWay.leftCenter ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.PREFERRED }, { TableLayout.FILL, TableLayout.PREFERRED, TableLayout.FILL } } ) ); add ( copiedButton, "1,1" ); add ( container, "0,0,0,2" ); } else if ( popupWay.equals ( PopupWay.leftDown ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.PREFERRED }, { TableLayout.PREFERRED, TableLayout.FILL } } ) ); add ( copiedButton, "1,0" ); add ( container, "0,0,0,1" ); } // Right popup if ( popupWay.equals ( PopupWay.rightUp ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.PREFERRED }, { TableLayout.FILL, TableLayout.PREFERRED } } ) ); add ( copiedButton, "0,1" ); add ( container, "1,0,1,1" ); } else if ( popupWay.equals ( PopupWay.rightCenter ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.PREFERRED }, { TableLayout.FILL, TableLayout.PREFERRED, TableLayout.FILL } } ) ); add ( copiedButton, "0,1" ); add ( container, "1,0,1,2" ); } else if ( popupWay.equals ( PopupWay.rightDown ) ) { setLayout ( new TableLayout ( new double[][]{ { TableLayout.PREFERRED, TableLayout.PREFERRED }, { TableLayout.PREFERRED, TableLayout.FILL } } ) ); add ( copiedButton, "0,0" ); add ( container, "1,0,1,1" ); } final int margin = button.getShadeWidth () + 1; if ( isDown () ) { container.setMargin ( new Insets ( 0, margin, margin, margin ) ); } else if ( isUp () ) { container.setMargin ( new Insets ( margin, margin, 0, margin ) ); } else if ( isLeft () ) { container.setMargin ( new Insets ( margin, margin, margin, 0 ) ); } else if ( isRight () ) { container.setMargin ( new Insets ( margin, 0, margin, margin ) ); } revalidate (); } /** * {@inheritDoc} */ @Override public void focusChanged ( final boolean focused ) { super.focusChanged ( focused ); WebButtonPopup.this.repaint (); } public void setContent ( final Component component ) { container.removeAll (); container.add ( component ); container.revalidate (); } public PopupWay getPopupWay () { return popupWay; } public void setPopupWay ( final PopupWay popupWay ) { this.popupWay = popupWay; updateContent (); updateBounds (); } private WebButton copy ( final WebButton button ) { final WebButton copy = new WebButton () { @Override public Dimension getPreferredSize () { return button.getSize (); } }; copySettings ( button, copy ); copy.setFocusable ( true ); copy.setUndecorated ( true ); copy.setCursor ( button.getCursor () ); return copy; } private void copySettings ( final WebButton button, final WebButton copy ) { copy.setFont ( button.getFont () ); copy.setIcon ( button.getIcon () ); copy.setText ( button.getText () ); copy.setIconTextGap ( button.getIconTextGap () ); copy.setHorizontalAlignment ( button.getHorizontalAlignment () ); copy.setVerticalAlignment ( button.getVerticalAlignment () ); copy.setHorizontalTextPosition ( button.getHorizontalTextPosition () ); copy.setVerticalTextPosition ( button.getVerticalTextPosition () ); copy.setBorder ( button.getBorder () ); } @Override public void updateBounds () { if ( isShowing () && button.isShowing () ) { final Point rl = SwingUtils.getRelativeLocation ( button, getParent () ); final Dimension ps = getPreferredSize (); // Bottom popup if ( popupWay.equals ( PopupWay.downLeft ) ) { setBounds ( new Rectangle ( new Point ( rl.x + button.getWidth () - ps.width, rl.y ), ps ) ); } else if ( popupWay.equals ( PopupWay.downCenter ) ) { setBounds ( new Rectangle ( new Point ( rl.x + button.getWidth () / 2 - ps.width / 2, rl.y ), ps ) ); } else if ( popupWay.equals ( PopupWay.downRight ) ) { setBounds ( new Rectangle ( rl, ps ) ); } // Top popup else if ( popupWay.equals ( PopupWay.upLeft ) ) { setBounds ( new Rectangle ( new Point ( rl.x + button.getWidth () - ps.width, rl.y + button.getHeight () - ps.height ), ps ) ); } else if ( popupWay.equals ( PopupWay.upCenter ) ) { setBounds ( new Rectangle ( new Point ( rl.x + button.getWidth () / 2 - ps.width / 2, rl.y + button.getHeight () - ps.height ), ps ) ); } else if ( popupWay.equals ( PopupWay.upRight ) ) { setBounds ( new Rectangle ( new Point ( rl.x, rl.y + button.getHeight () - ps.height ), ps ) ); } // Left popup else if ( popupWay.equals ( PopupWay.leftUp ) ) { setBounds ( new Rectangle ( new Point ( rl.x + button.getWidth () - ps.width, rl.y + button.getHeight () - ps.height ), ps ) ); } else if ( popupWay.equals ( PopupWay.leftCenter ) ) { setBounds ( new Rectangle ( new Point ( rl.x + button.getWidth () - ps.width, rl.y + button.getHeight () / 2 - ps.height / 2 ), ps ) ); } else if ( popupWay.equals ( PopupWay.leftDown ) ) { setBounds ( new Rectangle ( new Point ( rl.x + button.getWidth () - ps.width, rl.y ), ps ) ); } // Right popup else if ( popupWay.equals ( PopupWay.rightUp ) ) { setBounds ( new Rectangle ( new Point ( rl.x, rl.y + button.getHeight () - ps.height ), ps ) ); } else if ( popupWay.equals ( PopupWay.rightCenter ) ) { setBounds ( new Rectangle ( new Point ( rl.x, rl.y + button.getHeight () / 2 - ps.height / 2 ), ps ) ); } else if ( popupWay.equals ( PopupWay.rightDown ) ) { setBounds ( new Rectangle ( new Point ( rl.x, rl.y ), ps ) ); } revalidate (); } } @Override public boolean contains ( final Point p ) { return getPopupShape ( this ).contains ( p ); } public Shape getPopupShape ( final WebButtonPopup c ) { final int shadeWidth = button.getShadeWidth (); final int round = button.getRound (); final int bh = button.getHeight () - 1; final int bw = button.getWidth () - 1; final int cw = c.getWidth () - 1; final int ch = c.getHeight () - 1; Shape shape = null; // Simplified shape if ( isUpDown () && cw == bw || isLeftRight () && ch == bh ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ) ); } // Bottom popup else if ( popupWay.equals ( PopupWay.downLeft ) ) { shape = LafUtils.createRoundedShape ( round, p ( cw - bw + shadeWidth, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ), p ( shadeWidth, bh ), p ( cw - bw + shadeWidth, bh ) ); } else if ( popupWay.equals ( PopupWay.downCenter ) ) { final int shear = bw % 2; shape = LafUtils.createRoundedShape ( round, p ( cw / 2 - bw / 2 - shear + shadeWidth, shadeWidth ), p ( cw / 2 + bw / 2 - shadeWidth, shadeWidth ), p ( cw / 2 + bw / 2 - shadeWidth, bh ), p ( cw - shadeWidth, bh ), p ( cw - shadeWidth, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ), p ( shadeWidth, bh ), p ( cw / 2 - bw / 2 - shear + shadeWidth, bh ) ); } else if ( popupWay.equals ( PopupWay.downRight ) ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( bw - shadeWidth, shadeWidth ), p ( bw - shadeWidth, bh ), p ( cw - shadeWidth, bh ), p ( cw - shadeWidth, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ) ); } // Top popup else if ( popupWay.equals ( PopupWay.upLeft ) ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - shadeWidth ), p ( cw - bw + shadeWidth, ch - shadeWidth ), p ( cw - bw + shadeWidth, ch - bh ), p ( shadeWidth, ch - bh ) ); } else if ( popupWay.equals ( PopupWay.upCenter ) ) { final int shear = bw % 2; shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - bh ), p ( cw / 2 + bw / 2 - shadeWidth, ch - bh ), p ( cw / 2 + bw / 2 - shadeWidth, ch - shadeWidth ), p ( cw / 2 - bw / 2 - shear + shadeWidth, ch - shadeWidth ), p ( cw / 2 - bw / 2 - shear + shadeWidth, ch - bh ), p ( shadeWidth, ch - bh ) ); } else if ( popupWay.equals ( PopupWay.upRight ) ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - bh ), p ( bw - shadeWidth, ch - bh ), p ( bw - shadeWidth, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ) ); } // Left popup else if ( popupWay.equals ( PopupWay.leftUp ) ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - bw, shadeWidth ), p ( cw - bw, ch - bh + shadeWidth ), p ( cw - shadeWidth, ch - bh + shadeWidth ), p ( cw - shadeWidth, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ) ); } else if ( popupWay.equals ( PopupWay.leftCenter ) ) { final int shear = bh % 2; shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - bw, shadeWidth ), p ( cw - bw, ch / 2 - bh / 2 + shadeWidth ), p ( cw - shadeWidth, ch / 2 - bh / 2 + shadeWidth ), p ( cw - shadeWidth, ch / 2 + bh / 2 + shear - shadeWidth ), p ( cw - bw, ch / 2 + bh / 2 + shear - shadeWidth ), p ( cw - bw, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ) ); } else if ( popupWay.equals ( PopupWay.leftDown ) ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, bh - shadeWidth ), p ( cw - bw, bh - shadeWidth ), p ( cw - bw, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ) ); } // Right popup else if ( popupWay.equals ( PopupWay.rightUp ) ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, ch - bh + shadeWidth ), p ( bw, ch - bh + shadeWidth ), p ( bw, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - shadeWidth ), p ( shadeWidth, ch - shadeWidth ) ); } else if ( popupWay.equals ( PopupWay.rightCenter ) ) { final int shear = bh % 2; shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, ch / 2 - bh / 2 + shadeWidth ), p ( bw, ch / 2 - bh / 2 + shadeWidth ), p ( bw, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - shadeWidth ), p ( bw, ch - shadeWidth ), p ( bw, ch / 2 + bh / 2 + shear - shadeWidth ), p ( shadeWidth, ch / 2 + bh / 2 + shear - shadeWidth ) ); } else if ( popupWay.equals ( PopupWay.rightDown ) ) { shape = LafUtils.createRoundedShape ( round, p ( shadeWidth, shadeWidth ), p ( cw - shadeWidth, shadeWidth ), p ( cw - shadeWidth, ch - shadeWidth ), p ( bw, ch - shadeWidth ), p ( bw, bh - shadeWidth ), p ( shadeWidth, bh - shadeWidth ) ); } return shape; } private boolean isUpDown () { return isUp () || isDown (); } private boolean isDown () { return popupWay.equals ( PopupWay.downLeft ) || popupWay.equals ( PopupWay.downCenter ) || popupWay.equals ( PopupWay.downRight ); } private boolean isUp () { return popupWay.equals ( PopupWay.upLeft ) || popupWay.equals ( PopupWay.upCenter ) || popupWay.equals ( PopupWay.upRight ); } private boolean isLeftRight () { return isLeft () || isRight (); } private boolean isRight () { return popupWay.equals ( PopupWay.rightUp ) || popupWay.equals ( PopupWay.rightCenter ) || popupWay.equals ( PopupWay.rightDown ); } private boolean isLeft () { return popupWay.equals ( PopupWay.leftUp ) || popupWay.equals ( PopupWay.leftCenter ) || popupWay.equals ( PopupWay.leftDown ); } private Point p ( final int x, final int y ) { return new Point ( x, y ); } @Override public Shape provideShape () { return getPopupShape ( this ); } public void showPopup () { PopupManager.showPopup ( button, this, false ); // todo Some problems with focus transfer // if ( SwingUtils.hasFocusableComponent ( container ) ) // { // container.transferFocus (); // } } @Override public void hidePopup () { hidePopup ( false ); } public void hidePopup ( final boolean requestFocus ) { if ( isCloseOnFocusLoss () && button.isFocusable () && requestFocus ) { button.requestFocusInWindow (); return; } super.hidePopup (); // Transfering focus back to component if ( button.isFocusable () && requestFocus ) { button.requestFocusInWindow (); } } /** * Custom button popup painter. */ protected class WebButtonPopupPainter extends WebPanelPainter { /** * {@inheritDoc} */ @Override public void paint ( final Graphics2D g2d, final Rectangle bounds, final WebButtonPopup c ) { LafUtils.drawCustomWebBorder ( g2d, c, getPopupShape ( c ), button.isFocusable () && button.isDrawFocus () && focused ? StyleConstants.fieldFocusColor : StyleConstants.shadeColor, button.getShadeWidth (), true, isWebColoredBackground () ); } } }