001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (C) Alkacon Software (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.gwt.client.ui;
029
030import org.opencms.ade.galleries.client.ui.CmsGalleryDialog;
031import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle;
032import org.opencms.gwt.client.util.CmsPositionBean;
033
034import com.google.gwt.core.client.Scheduler;
035import com.google.gwt.dom.client.Element;
036import com.google.gwt.dom.client.Style.Unit;
037import com.google.gwt.user.client.DOM;
038import com.google.gwt.user.client.Window;
039import com.google.gwt.user.client.ui.ButtonBase;
040
041/**
042 * A popup which can be displayed below buttons in a toolbar.
043 */
044public class CmsToolbarPopup extends CmsPopup {
045
046    /** The minimum distance of the popup to the window border. */
047    private static final int MIN_MARGIN = 8;
048
049    /** The 'arrow-shaped' connector element above the popup. */
050    protected Element m_arrow = DOM.createDiv();
051
052    /** The toolbar button to which this popup belongs. */
053    protected ButtonBase m_button;
054
055    /** The toolbar width. */
056    protected int m_toolbarWidth;
057
058    /** The 'toolbar mode' flag. */
059    protected boolean m_isToolbarMode;
060
061    /** The base element of the toolbar button. */
062    protected Element m_baseElement;
063
064    /**
065     * Creates a new toolbar popup.<p>
066     *
067     * @param button the toolbar button to which this popup belongs
068     * @param toolbarMode the toolbar mode flag
069     * @param baseElement the base element of the toolbar button
070     */
071    public CmsToolbarPopup(ButtonBase button, boolean toolbarMode, Element baseElement) {
072
073        super();
074        m_button = button;
075        m_baseElement = baseElement;
076        m_isToolbarMode = toolbarMode;
077        setModal(false);
078        setAutoHideEnabled(true);
079        setWidth(DEFAULT_WIDTH);
080        removePadding();
081    }
082
083    /**
084     * Calculates the popup height to use.<p>
085     *
086     * @return the height
087     */
088    public static int getAvailableHeight() {
089
090        int height = CmsGalleryDialog.DEFAULT_DIALOG_HEIGHT;
091        if (Window.getClientHeight() > 590) {
092            height = (int)Math.ceil((Window.getClientHeight() - 50) * 0.9);
093        }
094        return height;
095    }
096
097    /**
098     * Calculates the popup width to use.<p>
099     *
100     * @return the width
101     */
102    public static int getAvailableWidth() {
103
104        int width = CmsGalleryDialog.DEFAULT_DIALOG_WIDTH;
105        if (Window.getClientWidth() > 1100) {
106            width = 1000;
107        }
108        return width;
109    }
110
111    /**
112     * Positions the popup below the toolbar button.<p>
113     */
114    public void position() {
115
116        // get the window client width
117        int windowWidth = Window.getClientWidth();
118        // get the min left position
119        int minLeft = MIN_MARGIN;
120
121        // get the max right position
122        int maxRight = windowWidth - MIN_MARGIN;
123        // get the middle button position
124        CmsPositionBean buttonPosition = CmsPositionBean.generatePositionInfo(m_button.getElement());
125        int buttonMiddle = (buttonPosition.getLeft() - Window.getScrollLeft()) + (buttonPosition.getWidth() / 2);
126        // get the content width
127        int contentWidth = getOffsetWidth();
128
129        // the optimum left position is in the middle of the button minus the half content width
130        // assume that the optimum fits into the space
131        int contentLeft = buttonMiddle - (contentWidth / 2);
132
133        if (minLeft > contentLeft) {
134            // if the optimum left position of the popup is outside the min left position:
135            // move the popup to the right (take the min left position as left)
136            contentLeft = minLeft;
137        } else if ((contentLeft + contentWidth) > maxRight) {
138            // if the left position plus the content width is outside the max right position:
139            // move the popup to the left (take the max right position minus the content width)
140            contentLeft = maxRight - contentWidth;
141        }
142
143        // limit the right position if the popup is right outside the window
144        if ((contentLeft + contentWidth + MIN_MARGIN) > windowWidth) {
145            contentLeft = windowWidth - contentWidth - MIN_MARGIN;
146        }
147
148        // limit the left position if the popup is left outside the window
149        if (contentLeft < MIN_MARGIN) {
150            contentLeft = MIN_MARGIN;
151        }
152
153        int arrowSpace = 10;
154        int arrowWidth = 40;
155        int arrowHeight = 12;
156
157        // the optimum position for the arrow is in the middle of the button
158        int arrowLeft = buttonMiddle - contentLeft - (arrowWidth / 2);
159        if ((arrowLeft + arrowWidth + arrowSpace) > contentWidth) {
160            // limit the arrow position if the maximum is reached (content width 'minus x')
161            arrowLeft = contentWidth - arrowWidth - arrowSpace;
162        } else if ((arrowLeft - arrowSpace) < 0) {
163            // limit the arrow position if the minimum is reached ('plus x')
164            arrowLeft = arrowWidth + arrowSpace;
165        }
166
167        int arrowTop = -(arrowHeight - 2);
168        String arrowClass = I_CmsLayoutBundle.INSTANCE.dialogCss().menuArrowTop();
169
170        int contentTop = (((buttonPosition.getTop() + buttonPosition.getHeight()) - Window.getScrollTop())
171            + arrowHeight) - 2;
172        if (!m_isToolbarMode) {
173            contentTop = (buttonPosition.getTop() + buttonPosition.getHeight() + arrowHeight) - 2;
174            int contentHeight = getOffsetHeight();
175            int windowHeight = Window.getClientHeight();
176
177            if (((contentHeight + MIN_MARGIN) < windowHeight)
178                && ((buttonPosition.getTop() - Window.getScrollTop()) > contentHeight)
179                && (((contentHeight + MIN_MARGIN + contentTop) - Window.getScrollTop()) > windowHeight)) {
180                // content fits into the window height,
181                // there is enough space above the button
182                // and there is to little space below the button
183                // so show above
184                contentTop = ((buttonPosition.getTop() - arrowHeight) + 2) - contentHeight;
185                arrowTop = contentHeight - 1;
186                arrowClass = I_CmsLayoutBundle.INSTANCE.dialogCss().menuArrowBottom();
187            }
188        } else {
189            contentLeft = contentLeft - Window.getScrollLeft();
190            setPositionFixed();
191        }
192
193        m_arrow.setClassName(arrowClass);
194        m_arrow.getStyle().setLeft(arrowLeft, Unit.PX);
195        m_arrow.getStyle().setTop(arrowTop, Unit.PX);
196
197        showArrow(m_arrow);
198        setPopupPosition(contentLeft + Window.getScrollLeft(), contentTop);
199    }
200
201    /**
202     * Defers execution of the position() method.<p>
203     */
204    public void positionDeferred() {
205
206        Scheduler.get().scheduleDeferred(() -> position());
207    }
208
209    /**
210     * Sets the isToolbarMode.<p>
211     *
212     * @param isToolbarMode the isToolbarMode to set
213     */
214    public void setToolbarMode(boolean isToolbarMode) {
215
216        m_isToolbarMode = isToolbarMode;
217        if (m_isToolbarMode) {
218            // important, so a click on the button won't trigger the auto-close
219            addAutoHidePartner(m_baseElement);
220        } else {
221            removeAutoHidePartner(m_baseElement);
222        }
223    }
224}