/********************************************************* begin of preamble
**
** Copyright (C) 2003-2010 Software- und Organisations-Service GmbH.
** All rights reserved.
**
** This file may be used under the terms of either the
**
** GNU General Public License version 2.0 (GPL)
**
** as published by the Free Software Foundation
** http://www.gnu.org/licenses/gpl-2.0.txt and appearing in the file
** LICENSE.GPL included in the packaging of this file.
**
** or the
**
** Agreement for Purchase and Licensing
**
** as offered by Software- und Organisations-Service GmbH
** in the respective terms of supply that ship with this file.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
** IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
** THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
** POSSIBILITY OF SUCH DAMAGE.
********************************************************** end of preamble*/
package net.cwroethel.swt.popupcalendar;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.Shell;
/**
* A class to manage the popup windows. To create a popup window with arbitrary
* contents simply:
*
* <pre>
* ...
* PopupShellManager popupShell =
* new PopupShellManager(contentsPane.getShell());
* Shell shell = popupShell.getShell();
* ... // treat shell just as you would any other shell
*
* // create a button to open the shell. Pretty much anything else
* // could be used to do that.
* Button button = new Button(...);
* select.addSelectionListener(new SelectionAdapter() {
* public void widgetSelected(SelectionEvent event) {
* Control comp = (Control)event.getSource();
*
* // align the shell with the right edge of the control that
* // opened it if there is enough space.
* popupShell.openShell(comp, SWT.RIGHT);
* }
* });
* ...
* </pre>
*
* Even though the popup shell disappears it still needs to be disposed when
* it's not needed anymore. This can either be done using the dispose() method
* of PopupShellManager or by calling dispose on the managed shell itself.
* (PopupShellManager itself is not a swt component and does not need to be
* disposed). PopupShellManager can be subclassed safely.
*
* @author Will
*/
public class PopupShellManager {
// store the default style definitions passed on in the constructor.
private int defaultStyle = 0;
private Shell shell = null;
// Don't create an empty PopupShellManager.
private PopupShellManager() {
}
/**
* Create a PopupShellManager that manages a shell with the default style.
* (SWT.LEFT_TO_RIGHT).
*
* @param parent
*/
public PopupShellManager(Shell parent) {
this(parent, SWT.LEFT_TO_RIGHT);
}
/**
* Create a PopupShellManager with the provided style. Pretty much any style
* is allowed that is supported by Shell.
*
* @param parent
* @param style
*/
public PopupShellManager(Shell parent, int style) {
shell = new Shell(parent, style);
// close dialog if user selects outside of the shell
// from PopupList
shell.addListener(SWT.Deactivate, new Listener() {
public void handleEvent(Event e) {
shell.setVisible(false);
}
});
}
/**
* Return the managed shell.
*
* @return
*/
public Shell getShell() {
return shell;
}
/**
* Draw the popup window at origin x, y measured in the same reference frame
* as control (i.e. control's parent frame). If the popup window does not
* fully fit in the screen, attempts will be made to reposition it to make
* it fit.
*
* @param control Control
* @param x int
* @param y int
*/
public void openShell(Control control, int x, int y) {
// calculate the position of the popup window
Point controlOrig = getAbsoluteLocation(control);
int xAbs = x + controlOrig.x;
int yAbs = y + controlOrig.y;
if (xAbs < 0) {
xAbs = 0;
}
if (yAbs < 0) {
yAbs = 0;
}
shell.pack();
Point size = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
// Point size = datePane.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
Point pDraw = checkBounds(new Rectangle(xAbs, yAbs, size.x, size.y), controlOrig);
// set the bounds
// setBounds(pDraw.x, pDraw.y, size.x, size.y);
shell.setLocation(pDraw.x, pDraw.y);
shell.open();
shell.setFocus();
Display display = shell.getDisplay();
while (!shell.isDisposed() && shell.isVisible()) {
if (!display.readAndDispatch())
display.sleep();
}
}
/**
* Open the popup window relative to a given control. The popup window will
* be positioned below the control if it fits on the screen and above the
* control otherwise, and aligned either with the left edge (LEFT), right
* edge (RIGHT), or centered (CENTER) below or above the control.
*
* @param control Control
* @param style int
*/
public void openShell(Control control, int style) {
// calculate the position of the popup window
shell.pack();
Point size = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, false);
Point controlOrig = getAbsoluteLocation(control);
Point controlSize = control.getSize();
int yAbs = controlOrig.y + controlSize.y;
int xAbs = 0;
switch (style) {
case SWT.LEFT:
xAbs = controlOrig.x;
break;
case SWT.RIGHT:
xAbs = controlOrig.x + controlSize.x - size.x;
break;
case SWT.CENTER:
xAbs = controlOrig.x + controlSize.x / 2 - size.x / 2;
break;
default:
xAbs = controlOrig.x;
break;
}
// check boundaries
if (xAbs < 0) {
xAbs = 0;
}
if (yAbs < 0) {
yAbs = 0;
}
// check the screen size
Point pDraw = checkBounds(new Rectangle(xAbs, yAbs, size.x, size.y), controlOrig);
// set the bounds
// setBounds(pDraw.x, pDraw.y, size.x, size.y);
shell.setLocation(pDraw.x, pDraw.y);
shell.open();
shell.setFocus();
// datePane.getTodaySelector().setFocus();
Display display = shell.getDisplay();
while (!shell.isDisposed() && shell.isVisible()) {
if (!display.readAndDispatch())
display.sleep();
}
}
/**
* Open the popup window using the default style defined in the constructor.
*
* @param control
*/
public void openShell(Control control) {
openShell(control, defaultStyle);
}
/**
* Check if the popup window fits on the screen.
*
* @param rect Rectangle
* @param orig Point
* @return Point
*/
private Point checkBounds(Rectangle rect, Point orig) {
int x = rect.x;
int y = rect.y;
int xMax = x + rect.width;
int yMax = y + rect.height;
// get the screen size
Rectangle screenSize = shell.getDisplay().getBounds();
// make window popup above control if the
// bottom of the popup window is off-screen and fits on top.
if ((yMax > (screenSize.height - 30)) && ((orig.y - rect.height) > 30)) {
y = orig.y - rect.height;
yMax = y + rect.height;
}
if ((xMax > (screenSize.width - 30)) && ((screenSize.width - rect.width) > 30)) {
x = screenSize.width - 30 - rect.width;
xMax = x + rect.width;
}
return new Point(x, y);
}
/**
* Calculate the absolute screen position of the origin of the control.
*
* @param control Control
* @return Point
*/
public Point getAbsoluteLocation(Control control) {
Point orig = control.getLocation();
while (control.getParent() != null) {
control = control.getParent();
orig.x = orig.x + control.getLocation().x;
orig.y = orig.y + control.getLocation().y;
// if shell then we have to add the client area
if (Shell.class.isInstance(control)) {
int frame = (control.getSize().x - ((Shell) control).getClientArea().width) / 2;
orig.x = orig.x + frame;
orig.y = orig.y + control.getSize().y - ((Shell) control).getClientArea().height - frame;
break;
}
}
return orig;
}
/**
* Dispose the popup shell and all it's children.
*/
public void dispose() {
shell.dispose();
}
}