/*******************************************************************************
* CogTool Copyright Notice and Distribution Terms
* CogTool 1.3, Copyright (c) 2005-2013 Carnegie Mellon University
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* CogTool is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* CogTool 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CogTool; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* CogTool makes use of several third-party components, with the
* following notices:
*
* Eclipse SWT version 3.448
* Eclipse GEF Draw2D version 3.2.1
*
* Unless otherwise indicated, all Content made available by the Eclipse
* Foundation is provided to you under the terms and conditions of the Eclipse
* Public License Version 1.0 ("EPL"). A copy of the EPL is provided with this
* Content and is also available at http://www.eclipse.org/legal/epl-v10.html.
*
* CLISP version 2.38
*
* Copyright (c) Sam Steingold, Bruno Haible 2001-2006
* This software is distributed under the terms of the FSF Gnu Public License.
* See COPYRIGHT file in clisp installation folder for more information.
*
* ACT-R 6.0
*
* Copyright (c) 1998-2007 Dan Bothell, Mike Byrne, Christian Lebiere &
* John R Anderson.
* This software is distributed under the terms of the FSF Lesser
* Gnu Public License (see LGPL.txt).
*
* Apache Jakarta Commons-Lang 2.1
*
* This product contains software developed by the Apache Software Foundation
* (http://www.apache.org/)
*
* jopt-simple version 1.0
*
* Copyright (c) 2004-2013 Paul R. Holser, Jr.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* Mozilla XULRunner 1.9.0.5
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/.
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The J2SE(TM) Java Runtime Environment version 5.0
*
* Copyright 2009 Sun Microsystems, Inc., 4150
* Network Circle, Santa Clara, California 95054, U.S.A. All
* rights reserved. U.S.
* See the LICENSE file in the jre folder for more information.
******************************************************************************/
package edu.cmu.cs.hcii.cogtool.util;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.swt.SWT;
import org.eclipse.swt.SWTException;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Cursor;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.ImageData;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Event;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Layout;
import org.eclipse.swt.widgets.Listener;
import org.eclipse.swt.widgets.MessageBox;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.swt.widgets.Widget;
/**
* Support for creating application and pop-up windows and interacting
* with the constructed user interface.
* <p>
* It is not necessary (and therefore not allowed) to create an instance
* of <code>WindowUtil</code>.
* <p>
* In order to support multiple (non-pop-up) windows for an application, the
* <code>Display</code> object's user data is used to reference-count
* the number of windows; interaction terminates when the count reaches zero.
*/
public class WindowUtil
{
/*
* Constants for cursors
*/
public static final int SELECT_CURSOR = SWT.CURSOR_ARROW;
public static final int DRAW_CURSOR = SWT.CURSOR_CROSS;
public static final int TEXT_CURSOR = SWT.CURSOR_IBEAM;
public static final int LINK_CURSOR = SWT.CURSOR_HAND;
public static final int HELP_CURSOR = SWT.CURSOR_HELP;
public static final int NOT_ALLOWED_CURSOR = SWT.CURSOR_NO;
public static final int BUSY_CURSOR = SWT.CURSOR_WAIT;
public static final int RESIZE_NS_CURSOR = SWT.CURSOR_SIZENS;
public static final int RESIZE_WE_CURSOR = SWT.CURSOR_SIZEWE;
public static final int RESIZE_NW_SE_CURSOR = SWT.CURSOR_SIZENWSE;
public static final int RESIZE_NE_SW_CURSOR = SWT.CURSOR_SIZENESW;
public static final int RESIZE_ALL_CURSOR = SWT.CURSOR_SIZEALL;
public static final int MAC_MENU_HEIGHT = 24;
protected static final String DEFERRED_DISPOSALS_KEY =
"edu.cmu.hcii.cogtool.deferredDisposals";
public static Map<Integer, Cursor> cursorRegistry =
new HashMap<Integer, Cursor>();
public static Cursor getCursor(int cursorIndex)
{
Integer cursorKey = new Integer(cursorIndex);
Cursor cachedCursor = cursorRegistry.get(cursorKey);
if (cachedCursor == null) {
cachedCursor = new Cursor(GLOBAL_DISPLAY, cursorIndex);
cursorRegistry.put(cursorKey, cachedCursor);
}
return cachedCursor;
}
public static Cursor OPEN_HAND_CURSOR = null;
public static Cursor CLOSED_HAND_CURSOR = null;
/**
* Returns a hand cursor.
*
* IMPORTANT: this cursor must be disposed when its creator is disposed,
* or we will have a memory link.
*
* @return a hew hand cursor
*/
public static Cursor getOpenHandCursor()
{
if (OPEN_HAND_CURSOR == null) {
ImageData curData =
GraphicsUtil.getImageDataFromResource("edu/cmu/cs/hcii/cogtool/resources/open_hand.gif");
if (curData == null) {
return null;
}
OPEN_HAND_CURSOR = new Cursor(GLOBAL_DISPLAY, curData, 8, 8);
}
return OPEN_HAND_CURSOR;
}
public static Cursor getClosedHandCursor()
{
if (CLOSED_HAND_CURSOR == null) {
ImageData curData =
GraphicsUtil.getImageDataFromResource("edu/cmu/cs/hcii/cogtool/resources/closed_hand.gif");
if (curData == null) {
return null;
}
CLOSED_HAND_CURSOR = new Cursor(GLOBAL_DISPLAY, curData, 8, 8);
}
return CLOSED_HAND_CURSOR;
}
/**
* The global SWT Display object
*/
public static final Display GLOBAL_DISPLAY;
static {
Display.setAppName("CogTool");
System.setProperty("dock:name", "CogTool");
GLOBAL_DISPLAY = Display.getDefault();
GLOBAL_DISPLAY.disposeExec(new Runnable() {
public void run() {
processDeferredDisposals();
GLOBAL_DISPLAY.setData(DEFERRED_DISPOSALS_KEY, null);
}
});
}
public static final Cursor BUSY_CURSOR_INSTANCE = getCursor(BUSY_CURSOR);
// Prevent instantiation
private WindowUtil() { }
/**
* Gets the Text control with the keyboard focus, or null if no Text has
* the focus.
*
* @return a Text or null
*/
public static Text getFocusedText()
{
Control focused = GLOBAL_DISPLAY.getFocusControl();
if (focused instanceof Text) {
return (Text) focused;
}
return null;
}
/**
* Gets the Combo control with the keyboard focus, or null if no Combo has
* the focus.
*
* @return a Combo or null
*/
public static Combo getFocusedCombo()
{
Control focused = GLOBAL_DISPLAY.getFocusControl();
if (focused instanceof Combo) {
return (Combo) focused;
}
return null;
}
// Record that a new application window has been created.
private static void incrementReferenceCount(Display display)
{
Integer refs = (Integer) display.getData();
display.setData(new Integer((refs == null) ? 1
: refs.intValue() + 1));
}
// Record that an application window has been closed.
// Returns true iff the decrement results in zero; at that point,
// the user data is reset to <code>null</code>.
// On the Mac, the application must be explicitly closed with a call to
// Display.setData(null). This behavior can be overridden by setting the
// system property "edu.cmu.cs.hcii.cogtool.util.QuitAfterLastWindowClosed"
// to a value of "true".
private static void decrementReferenceCount(Display display)
{
Integer refs = (Integer) display.getData();
if (refs != null) {
if (refs.intValue() == 1) {
if ((! OSUtils.MACOSX) ||
Boolean.getBoolean("edu.cmu.cs.hcii.cogtool.util.QuitAfterLastWindowClosed"))
{
display.setData(null);
}
else {
display.setData(new Integer(0));
}
}
else {
display.setData(new Integer(refs.intValue() - 1));
}
}
}
// Attached to each application window to decrement the display's
// reference count when the window is closed (on the SWT.Dispose event).
private static Listener WindowClosed =
new Listener()
{
public void handleEvent(Event evt)
{
decrementReferenceCount(evt.display);
}
};
/**
* This listener forces termination of the main interaction loop
* by ignoring the reference count of application windows and resetting
* the user data to null.
*
* @see #interact(Display display)
*/
public static Listener ExitApplication =
new Listener()
{
public void handleEvent(Event evt)
{
if (evt.doit) {
evt.display.setData(null);
}
}
};
/**
* This listener closes the specified application window upon request.
* <p>
* This listener should use a window created by
* <code>createNormalWindow</code> and attached
* (using <code>addListener</code>) to the user interface
* component that represents a request to close that window
* (e.g., a <code>MenuItem</code>).
* <p>
* <b>Implementation note</b>: The <code>close</code> method causes a
* <code>ShellEvent</code> to the appropriate listener on the
* <code>Shell</code> object. If you wish to "prevent" closing,
* you can provide such a listener that sets <code>doit</code> false.
* <p>
* <b>Implementation note</b>: If the <code>close</code> succeeds,
* or the associated application window is closed in any other way,
* a <code>SWT.Dispose</code> event is sent to registered listeners,
* one of which will be <code>WindowClosed</code>.
*
* @see #createNormalWindow
* @see org.eclipse.swt.events.ShellEvent
*/
public static class CloseListener implements Listener
{
Shell window;
/**
* Constructor for the event listener that closes the given
* application window upon request.
*
* @param win the application window
* @author mlh
*/
public CloseListener(Shell win)
{
window = win;
}
/**
* Handle the request to close an application window.
*
* @param evt the event representing the request
* @author mlh
*/
public void handleEvent(Event evt)
{
// Calls dispose; causes ShellEvent to be sent to:
// DefaultUI.closeListener
// AView.shell (in ctor)
if (evt.doit) {
window.close();
}
}
}
/**
* Create a normal application window.
* <p>
* The window is created with application "trim" (i.e., close/resize
* buttons, etc.). The created window participates in the reference
* counting that controls when the main interaction loop exits.
*
* @param display the device that represents the display screen
* @param windowTitle the string to be used as the window's title
* @param bounds the desired position with respect to the display,
* and size of the window
* @param setPosition if true, the position information in <code>bounds</code>
* is used; otherwise a platform dependent default
* position is used; note that the size information in
* <code>bounds</code> is always used.
* @param layout the SWT object expressing layout rules for the
* window's contents
* @return the SWT object representing the application window
*/
public static Shell createNormalWindow(Display display,
String windowTitle,
Rectangle bounds,
boolean setPosition,
Layout layout)
{
Shell window = new Shell(display, SWT.SHELL_TRIM);
window.setLayout(layout);
window.setText(windowTitle);
if (setPosition) {
window.setBounds(bounds);
}
else {
window.setSize(bounds.width, bounds.height);
// Try to ensure that the window is in the client area of the display;
// in particular, it shouldn't be under the Windows taskbar. Note that if
// the window's bigger than the client area there's no way we can
// succeed, and we'll just leave it in the upper left corner.
Rectangle ca = display.getClientArea();
Rectangle wb = window.getBounds();
// There appears to be an SWT bug where, when there is more than one
// monitor, it includes the Mac Menus in the client area, which is Not
// Good for window positioning. This works around that problem.
int minY = (OSUtils.MACOSX ? MAC_MENU_HEIGHT : 0);
// if it's too low, move it up, but no higher than y = 0 (suitably
// adjusted for the bug alluded to above).
int newY = wb.y;
int diff = ca.height - (wb.y + wb.height);
if (diff < 0) {
newY += diff;
}
if (newY < minY) {
newY = minY;
}
// it's too far to the right, move it left, but not past x = 0
int newX = wb.x;
diff = ca.width - (wb.x + wb.width);
if (diff < 0) {
newX += diff;
}
if (newX < 0) {
newX = 0;
}
window.setLocation(newX, newY);
}
incrementReferenceCount(display);
window.addListener(SWT.Dispose, WindowClosed);
return window;
}
/**
* Display the given application window on its associated device.
* <p>
* Packing the contents of a window may adjust the window's size
* to be different from that requested.
*
* @param shell the window to display
* @param packContents whether or not to "minimize" area displayed
* @author mlh
* @see org.eclipse.swt.widgets.Control#pack
*/
public static void display(Shell shell, boolean packContents)
{
if (packContents) {
shell.pack();
}
shell.open();
}
/**
* Display the given application window on its associated device.
* <p>
* Packing the contents of a window may adjust the window's size
* to be different from that requested.
*
* @param shell the window to display
* @param packContents whether or not to "minimize" area displayed
* @param changed allows the caller to request that layout caches
* be flushed
* @author mlh
* @see org.eclipse.swt.widgets.Control#pack
*/
public static void display(Shell shell,
boolean packContents,
boolean changed)
{
if (packContents) {
shell.pack(changed);
}
shell.open();
}
/**
* The main interaction loop.
* <p>
* Continue to process events while an open window exists.
* <p>
* <b>Implementation note</b>: On Macintosh systems, the convention
* is to keep the application running even if there are no open windows.
*
* @see decrementReferenceCount(Display)
* @author mlh
*/
public static void interact(boolean returnWhenDone)
{
try {
while (GLOBAL_DISPLAY.getData() != null) {
processDeferredDisposals();
if (! GLOBAL_DISPLAY.readAndDispatch()) {
if (returnWhenDone) {
return;
}
GLOBAL_DISPLAY.sleep();
}
}
}
catch (SWTException e) {
// In the case where the display is already disposed.
// This happens on the Mac if you use the QUIT function in the SWT
// menu or the red 'x' to close the window.
// Does not happen if you exit using the file -> exit menu item,
// or on Windows.
// Do not display error.
// Hides some error reports that are important... print out trace.
if (! e.getMessage().equals("Device is disposed")) {
System.err.println("SWTException caught in main CogTool loop.");
e.printStackTrace();
System.err.println();
// then let the Exception catching code in main() try to
// report it to the user
throw e;
}
}
}
public static void interact() {
interact(false);
}
/**
* Arrange for the disposal of a widget to be deferred until the
* next iteration of the main event loop. If there is no open window
* go ahead and dispose it right away.
*/
@SuppressWarnings("unchecked")
public static void deferDisposal(Widget w)
{
if (GLOBAL_DISPLAY.getData() == null) {
w.dispose();
return;
}
List<Widget> deferrals =
(List<Widget>) GLOBAL_DISPLAY.getData(DEFERRED_DISPOSALS_KEY);
if (deferrals == null) {
deferrals = new ArrayList<Widget>(1);
GLOBAL_DISPLAY.setData(DEFERRED_DISPOSALS_KEY, deferrals);
}
deferrals.add(w);
}
@SuppressWarnings("unchecked")
protected static void processDeferredDisposals()
{
List<Widget> deferred =
(List<Widget>) GLOBAL_DISPLAY.getData(DEFERRED_DISPOSALS_KEY);
if (deferred != null) {
for (Iterator<Widget> it = deferred.iterator(); it.hasNext(); ) {
it.next().dispose();
it.remove();
}
}
}
/**
* Nested interaction for pop-up (modal) windows.
* <p>
* This allows one to fetch values from the user and maintain
* the place in the execution thread that wishes to use those values.
*
* @param shell the pop-up window
* @author mlh
*/
public static void interact(Shell shell)
{
Display display = shell.getDisplay();
while (! shell.isDisposed()) {
if (! display.readAndDispatch()) {
display.sleep();
}
}
}
/**
* Both displays and provides nested interaction for a pop-up (modal)
* window.
*
* @param shell the pop-up window
* @author mlh
*/
public static void displayAndInteract(Shell shell)
{
displayAndInteract(shell, true);
}
/**
* Both displays and provides nested interaction for a pop-up (modal)
* window.
*
* @param shell the pop-up window
* @param packContents whether or not to "minimize" area displayed
* @author mlh
*/
public static void displayAndInteract(Shell shell, boolean packContents)
{
display(shell, packContents);
interact(shell);
}
/**
* Creates a nested interaction pop-up (modal) window.
*
* For the "flag" parameters, use the following values:
* <code>
* buttons: one of the following combinations
* SWT.OK
* SWT.OK | SWT.CANCEL
* SWT.YES | SWT.NO
* SWT.YES | SWT.NO | SWT.CANCEL
* SWT.RETRY | SWT.CANCEL
* SWT.ABORT | SWT.RETRY | SWT.IGNORE
*
* icon: one of
* SWT.ICON_ERROR, SWT.ICON_INFORMATION, SWT.ICON_QUESTION,
* SWT.ICON_WARNING, SWT.ICON_WORKING, SWT.NONE
*
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @param buttons which buttons to display in the pop-up
* @param icon the icon to display, indicating the message's nature
* @param mode whether the pop-up is modal relative to
* the parent window, the application as a whole,
* or the entire user's system/computer
* @return the created MessageBox
* @author mlh
* @see org.eclipse.swt.widgets.MessageBox
* @see org.eclipse.swt.SWT
*/
public static MessageBox createMessageDialog(Shell parent,
String title,
String message,
int buttons,
int icon,
int mode)
{
MessageBox msgBox = new MessageBox(parent, buttons | icon | mode);
msgBox.setText(title);
msgBox.setMessage(message);
return msgBox;
}
/**
* Both displays and provides nested interaction for a pop-up (modal)
* window built using SWT's <code>MessageBox</code>.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @param buttons which buttons to display in the pop-up
* @param icon the icon to display, indicating the message's nature
* @param mode whether the pop-up is modal relative to
* the parent window, the application as a whole,
* or the entire user's system/computer
* @return which button was pushed to dismiss the pop-up
* (see the <code>SWT</code> class for code constants)
* @author mlh
* @see createMessageDialog
*/
public static int presentMessageDialog(Shell parent,
String title,
String message,
int buttons,
int icon,
int mode)
{
if (message.length() > MAX_BUFFER) {
ScrollableDialog d =
new ScrollableDialog(parent,
title,
buttons | icon | mode,
message);
Object response = d.open();
return ((Integer) response).intValue();
}
MessageBox msgBox = createMessageDialog(parent, title, message,
buttons, icon, mode);
if (parent != null) {
// TODO: it would be nice to center this on the parent window!
// Point parentLoc = parent.getLocation();
// Point parentSize = parent.getSize();
// Point size = msgBox.getSize();
// int x = parentLoc.x + parentSize.x / 2 - size.x / 2;
// int y = parentLoc.y + parentSize.y / 2 - size.y / 2;
// msgBox.setLocation(x, y);
}
// else don't bother trying to center it, since no parent
return msgBox.open(); // no need to dispose; not a Widget!
}
/**
* Simple modal error message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return the created MessageBox; use open() to interact, which
* will return SWT.OK
* @author mlh
*/
public static MessageBox createErrorDialog(Shell parent,
String title,
String message)
{
return createMessageDialog(parent,
title,
message,
SWT.OK,
SWT.ICON_ERROR,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal error message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return <code>SWT.OK</code>, which must be the button pushed
* to dismiss the pop-up
* @author mlh
*/
public static int presentErrorDialog(Shell parent,
String title,
String message)
{
return presentMessageDialog(parent,
title,
message,
SWT.OK,
SWT.ICON_ERROR,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal warning message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return the created MessageBox; use open() to interact, which
* will return SWT.OK
* @author mlh
*/
public static MessageBox createWarningDialog(Shell parent,
String title,
String message)
{
return createMessageDialog(parent,
title,
message,
SWT.OK,
SWT.ICON_WARNING,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal warning message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return <code>SWT.OK</code>, which must be the button pushed
* to dismiss the pop-up
* @author mlh
*/
public static int presentWarningDialog(Shell parent,
String title,
String message)
{
return presentMessageDialog(parent,
title,
message,
SWT.OK,
SWT.ICON_WARNING,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal informational message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return the created MessageBox; use open() to interact, which
* will return SWT.OK
* @author mlh
*/
public static MessageBox createInformationDialog(Shell parent,
String title,
String message)
{
return createMessageDialog(parent,
title,
message,
SWT.OK,
SWT.ICON_INFORMATION,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal informational message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return <code>SWT.OK</code>, which must be the button pushed
* to dismiss the pop-up
* @author mlh
*/
public static int presentInformationDialog(Shell parent,
String title,
String message)
{
return presentMessageDialog(parent,
title,
message,
SWT.OK,
SWT.ICON_INFORMATION,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal error message with options to retry or cancel.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return the created MessageBox; use open() to interact, which
* will return one of SWT.RETRY or SWT.CANCEL
* @author mlh
*/
public static MessageBox createErrorAbortDialog(Shell parent,
String title,
String message)
{
return createMessageDialog(parent,
title,
message,
SWT.RETRY | SWT.CANCEL,
SWT.ICON_ERROR,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal error message with options to retry or cancel.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return <code>SWT.RETRY</code> or <code>SWT.CANCEL</code>
* @author mlh
*/
public static int presentErrorAbortDialog(Shell parent,
String title,
String message)
{
return presentMessageDialog(parent,
title,
message,
SWT.RETRY | SWT.CANCEL,
SWT.ICON_ERROR,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal confirm message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return the created Message; use open() to interact, which
* will return one of SWT.OK or SWT.CANCEL
* @author mlh
*/
public static MessageBox createConfirmDialog(Shell parent,
String title,
String message)
{
return createMessageDialog(parent,
title,
message,
SWT.OK | SWT.CANCEL,
SWT.ICON_QUESTION,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal confirm message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return <code>SWT.OK</code> or <code>SWT.CANCEL</code>
* @author mlh
*/
public static int presentConfirmDialog(Shell parent,
String title,
String message)
{
return presentMessageDialog(parent,
title,
message,
SWT.OK | SWT.CANCEL,
SWT.ICON_QUESTION,
SWT.PRIMARY_MODAL);
}
/**
* The maximum number of items <code>presentConfirmItemsDialog</code> will show
* without truncating and adding "and <i>N</i> more." An exception is that if
* there are one more items than this constant, all will be shown, since it
* would be silly to simply replace the last with "and 1 more."
*/
public static final int CONFIRM_ITEMS_LIMIT = 12;
/**
* This displays a modal confirm dialog, that also lists an array of items.
* A prototypical use of this would be "Really delete the following?" and a
* list of the things to be deleted. The items being listed must implement
* the interface INamedObject.
*
* @param parent the window the alert dialog adheres to
* @param title the title for the alert dialog window
* @param message the message to display
* @param items an array of items, whose names are to be be enumerated
* @return <code>SWT.OK</code> or <code>SWT.CANCEL</code>
*/
public static int presentConfirmItemsDialog(Shell parent,
String title,
String message,
NamedObject[] items)
{
String[] itemNames = new String[items.length];
for (int i = 0; i < items.length; i++) {
itemNames[i] = items[i].getName();
}
return presentConfirmItemsDialog(parent, title, message, itemNames);
}
/**
* This displays a modal confirm dialog, that also lists an array of items,
* but just passing in the <code>String</code> names of the items rather
* than the items themselves.
*
* @param parent the window the alert dialog adheres to
* @param title the title for the alert dialog window
* @param message the message to display
* @param items an array of <code>String</code> names of the items
* @return <code>SWT.OK</code> or <code>SWT.CANCEL</code>
*/
public static int presentConfirmItemsDialog(Shell parent,
String title,
String message,
String[] itemNames)
{
String indent = "\n ";
StringBuilder formattedMessage = new StringBuilder(message);
int limit = itemNames.length;
for (int i = 0; i < limit; ++i) {
formattedMessage.append(indent);
formattedMessage.append(itemNames[i]);
}
return presentMessageDialog(parent,
title,
formattedMessage.toString(),
SWT.OK | SWT.CANCEL,
SWT.ICON_QUESTION,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal yes-no question message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return the created MessageBox; use open() to interact, which
* will return one of SWT.YES or SWT.NO
* @author mlh
*/
public static MessageBox createYesNoQuestionDialog(Shell parent,
String title,
String message)
{
return createMessageDialog(parent,
title,
message,
SWT.YES | SWT.NO,
SWT.ICON_QUESTION,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal yes-no question message.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return <code>SWT.YES</code> or <code>SWT.NO</code>
* @author mlh
*/
public static int presentYesNoQuestionDialog(Shell parent,
String title,
String message)
{
return presentMessageDialog(parent,
title,
message,
SWT.YES | SWT.NO,
SWT.ICON_QUESTION,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal yes-no question with option to cancel.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return the created MessageBox; use open() to interact, which
* will return one of SWT.YES, SWT.NO, or SWT.CANCEL
* @author mlh
*/
public static MessageBox createYesNoCancelDialog(Shell parent,
String title,
String message)
{
return createMessageDialog(parent,
title,
message,
SWT.YES | SWT.NO | SWT.CANCEL,
SWT.ICON_QUESTION,
SWT.PRIMARY_MODAL);
}
/**
* Simple modal yes-no question with option to cancel.
*
* @param parent the window the pop-up "adheres" to
* @param title the title for the pop-up window
* @param message the message to display to the user
* @return <code>SWT.YES</code> or <code>SWT.NO</code>
* or <code>SWT.CANCEL</code>
* @author mlh
*/
public static int presentYesNoCancelDialog(Shell parent,
String title,
String message)
{
return presentMessageDialog(parent,
title,
message,
SWT.YES | SWT.NO | SWT.CANCEL,
SWT.ICON_QUESTION,
SWT.PRIMARY_MODAL);
}
/**
* A class to support the construction of more interesting pop-up
* dialog boxes.
* <p>
* A subclass need only override <code>buildDialog</code> to build
* the contents of the dialog's window. Other methods may be provided
* by subclasses as needed, such as to provide access to the values
* collected from the user.
*
* @author mlh
*/
public static abstract class SimpleDialog
{
protected String dialogTitle;
protected int popupMode;
protected int style;
protected Font buttonFont;
protected Font textFont;
protected Object userResponse = null; // how user dismisses the dialog
protected Shell dialog;
protected Shell parent;
/**
* Initialize the dialog for later pop-up.
* <p>
* mode: one of (in increasing order of restriction)
* <code>
* SWT.MODELESS, SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL,
* SWT.SYSTEM_MODAL
* </code>
* <p>
* <b>Implementation note</b>: The values pertinent for the
* <code>styleFlags</code> parameter are those defined in SWT's
* documentation the Shell constructors' style parameter that are not
* used for the <code>mode</code> parameter. Thus, we separate two
* sets of things, mode and style, which are combined into one by SWT;
* for the purposes of this class, values of style should be the
* set theoretic difference of SWT's style values and the modality
* values.)
*
* @param parentWin the window the pop-up "adheres" to
* @param title the string to be used as the dialog window's title
* @param mode whether the pop-up is modal relative to
* the parent window, the application as a whole,
* or the entire user's system/computer
* @param styleFlags flags passed through to SWT's <code>Dialog</code>
* @author mlh
* @see org.eclipse.swt.widgets.Dialog
*/
public SimpleDialog(Shell parentWin,
String title,
int mode,
int styleFlags)
{
parent = parentWin;
dialogTitle = title;
popupMode = mode;
style = styleFlags;
userResponse = null;
dialog = null;
if (OSUtils.MACOSX) {
buttonFont = new Font(WindowUtil.GLOBAL_DISPLAY,
"Lucida Grande", 13, SWT.NORMAL);
textFont = buttonFont;
}
}
/**
* Initialize the dialog for later pop-up.
* <p>
* mode: one of (in increasing order of restriction)
* <code>
* SWT.MODELESS, SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL,
* SWT.SYSTEM_MODAL
* </code>
*
* @param parentWin the window the pop-up "adheres" to
* @param title the string to be used as the dialog window's title
* @param mode whether the pop-up is modal relative to
* the parent window, the application as a whole,
* or the entire user's system/computer
* @author mlh
* @see org.eclipse.swt.widgets.Dialog
*/
public SimpleDialog(Shell parentWin, String title, int mode)
{
this(parentWin, title, mode, SWT.DIALOG_TRIM);
}
public SimpleDialog(String title, int mode, int styleFlags)
{
this(null, title, mode, styleFlags);
}
public SimpleDialog(String title, int mode)
{
this(null, title, mode, SWT.DIALOG_TRIM);
}
/**
* Construct the contents of the dialog window.
* <p>
* Subclasses should override this method; it is responsible for
* creating contents by adding to <code>this.dialog</code>.
* <p>
* Listeners based on contents are responsible for setting
* <code>this.userResponse</code>
* and for closing or disposing the <code>this.dialog</code> shell!
*
* @author mlh
*/
protected abstract void buildDialog();
protected static final int MIN_X = 0;
protected static final int MIN_Y = (OSUtils.MACOSX ? 24 : 0);
/**
* Create the pop-up dialog window, populate it (using
* <code>buildDialog</code>), and interact with the user.
* <p>
* As noted in <code>buildDialog</code>, the subclass' listeners are
* responsible for closing or disposing the dialog window and
* for setting which user's response was used to dismiss the window.
*
* @return the user's response that dismissed the window
* @author mlh
*/
public Object open()
{
if (parent != null) {
dialog =
new Shell(parent, style | popupMode);
}
else {
dialog = new Shell(WindowUtil.GLOBAL_DISPLAY,
style | popupMode);
}
dialog.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent arg0)
{
if (buttonFont != null) {
buttonFont.dispose();
}
}
});
dialog.setText(dialogTitle);
buildDialog();
display(dialog, true);
if (parent != null) {
Point parentLoc = parent.getLocation();
Point parentSize = parent.getSize();
Point size = dialog.getSize();
int x = parentLoc.x + parentSize.x / 2 - size.x / 2;
if (x < MIN_X) {
x = MIN_X;
}
int y = parentLoc.y + parentSize.y / 2 - size.y / 2;
if (y < MIN_Y) {
y = MIN_Y;
}
dialog.setLocation(x, y);
}
// else don't bother trying to center it, since no parent
interact(dialog);
return userResponse;
}
}
/**
* A simple class representing a dialog pop-up that prompts for a single
* <code>String</code> value
* <p>
* The dialog has two buttons: "OK" and "Cancel" (localized)
* <p>
* The return value of <code>open</code> on an instance of
* <code>PromptDialog</code> will indicate which button was selected;
* see the constants <code>OK</code> and <code>CANCEL</code>.
*
* @author mlh
*/
public static class PromptDialog extends CustomDialog
{
protected static int NO_WIDTH_HINT = -1;
protected String responseLabel;
protected String request;
protected String promptResponse;
// Needed for listeners
protected Text responseBox;
// By default, OK is disabled unless the prompt response is not empty
protected boolean disableOK = true;
protected int widthHint = NO_WIDTH_HINT;
/**
* Initialize the string prompting dialog for later pop-up.
* <p>
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
* <p>
* <p>
* <b>Implementation note</b>: The values pertinent for the
* <code>styleFlags</code> parameter are those defined in SWT's
* documentation the Shell constructors' style parameter that are not
* used for the <code>mode</code> parameter. Thus, we separate two
* sets of things, mode and style, which are combined into one by SWT;
* for the purposes of this class, values of style should be the
* set theoretic difference of SWT's style values and the modality
* values.)
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @param widHint the width hint for the response box, or
* NO_WIDTH_HINT
* @param responseLbl label of the text input field
* @param requestQuestion question or instructions
* @param defaultResponse initial value for the text input field
* @param styleFlags flags passed to SWT's <code>Dialog</code>
* @author mlh
* @see org.eclipse.swt.widgets.Dialog
*/
public PromptDialog(Shell parentWin,
String title,
int mode,
int widHint,
String responseLbl,
String requestQuestion,
String defaultResponse,
int styleFlags)
{
super(parentWin, title, mode, styleFlags);
responseLabel = responseLbl;
request = requestQuestion;
promptResponse = defaultResponse;
widthHint = widHint;
}
/**
* Initialize the string prompting dialog for later pop-up.
* <p>
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
* <p>
* <p>
* <b>Implementation note</b>: The values pertinent for the
* <code>styleFlags</code> parameter are those defined in SWT's
* documentation the Shell constructors' style parameter that are not
* used for the <code>mode</code> parameter. Thus, we separate two
* sets of things, mode and style, which are combined into one by SWT;
* for the purposes of this class, values of style should be the
* set theoretic difference of SWT's style values and the modality
* values.)
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @param responseLbl label of the text input field
* @param requestQuestion question or instructions
* @param defaultResponse initial value for the text input field
* @param styleFlags flags passed to SWT's <code>Dialog</code>
* @author mlh
* @see org.eclipse.swt.widgets.Dialog
*/
public PromptDialog(Shell parentWin,
String title,
int mode,
String responseLbl,
String requestQuestion,
String defaultResponse,
int styleFlags)
{
this(parentWin, title, mode, NO_WIDTH_HINT,
responseLbl, requestQuestion, defaultResponse,
SWT.DIALOG_TRIM);
}
/**
* Initialize the string prompting dialog for later pop-up.
* <p>
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @param widHint the width hint for the response box, or
* NO_WIDTH_HINT
* @param responseLbl label of the text input field
* @param requestQuestion question or instructions
* @param defaultResponse initial value for the text input field
* @author mlh
*/
public PromptDialog(Shell parentWin,
String title,
int mode,
int widHint,
String responseLbl,
String requestQuestion,
String defaultResponse)
{
this(parentWin, title, mode, widHint,
responseLbl, requestQuestion, defaultResponse,
SWT.DIALOG_TRIM);
}
/**
* Initialize the string prompting dialog for later pop-up.
* <p>
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @param responseLbl label of the text input field
* @param requestQuestion question or instructions
* @param defaultResponse initial value for the text input field
* @author mlh
*/
public PromptDialog(Shell parentWin,
String title,
int mode,
String responseLbl,
String requestQuestion,
String defaultResponse)
{
this(parentWin, title, mode,
responseLbl, requestQuestion, defaultResponse,
SWT.DIALOG_TRIM);
}
/**
* Initialize the string prompting dialog for later pop-up.
* <p>
* Assumes an empty default value ("").
* <p>
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @param responseLbl label of the text input field
* @param requestQuestion question or instructions
* @author mlh
*/
public PromptDialog(Shell parentWin,
String title,
int mode,
String responseLbl,
String requestQuestion)
{
this(parentWin, title, mode,
responseLbl, requestQuestion, "", SWT.DIALOG_TRIM);
}
/**
* Initialize the string prompting dialog for later pop-up.
* <p>
* Assumes an empty default value ("") and an empty question.
* <p>
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @param responseLbl label of the text input field
* @author mlh
*/
public PromptDialog(Shell parentWin,
String title,
int mode,
String responseLbl)
{
this(parentWin, title, mode, responseLbl, "", "", SWT.DIALOG_TRIM);
}
/**
* What to do when the OK button is selected by the user.
*
* @author mlh
*/
@Override
protected void onOK()
{
promptResponse = responseBox.getText();
super.onOK();
}
/**
* Fetch the <code>String</code> value of the prompt specified by
* the user.
*
* @return the <code>String</code> value of the prompt
* specified by the user.
* @author mlh
*/
public String getPromptResponse()
{
return promptResponse;
}
protected void addResponseBox()
{
responseBox =
new ManagedText(dialog,
SWT.SINGLE | SWT.BORDER,
Keypad.FULL_KEYPAD)
{
@Override
protected void onModify()
{
if ((okButton != null) && disableOK) {
okButton.setEnabled(getText().length() > 0);
}
}
};
}
/**
* Allows subclasses to add more dialog "fields" between the question
* and response "field" and the OK/CANCEL buttons.
* <p>
* Subclasses should know that the dialog uses a
* <code>GridLayout</code> instance of four columns, and thus should
* attach <code>GridData</code> instances to each "field", using
* <code>setLayoutData</code>.
*
* @author mlh
*/
@Override
protected void addMoreFields()
{
//<<<<<<< .mine
// // Four columns
// GridLayout layout = new GridLayout(4, false);
//
// if (OSUtils.MACOSX) {
// layout.marginLeft = 19;
// layout.marginRight = 13;
// layout.marginTop = 7;
// layout.marginBottom = 12;
//
// layout.horizontalSpacing = 0;
// layout.verticalSpacing = 4;
// }
//
// this.dialog.setLayout(layout);
//
//=======
//>>>>>>> .r832
// If given a request question, center, filling width
if ((request != null) && (request != "")) {
Label requestLabel = new Label(dialog, SWT.NONE);
if (textFont != null) {
requestLabel.setFont(textFont);
}
requestLabel.setText(request);
GridData reqLayout = new GridData();
reqLayout.grabExcessHorizontalSpace = true;
reqLayout.horizontalSpan = 4;
requestLabel.setLayoutData(reqLayout);
}
// If given a label, left-most column, right-justified
if ((responseLabel != null) && (responseLabel != "")) {
Label lbl = new Label(dialog, SWT.NONE);
if (textFont != null) {
lbl.setFont(textFont);
}
lbl.setText(responseLabel);
GridData lblLayout =
new GridData(GridData.HORIZONTAL_ALIGN_END);
lbl.setLayoutData(lblLayout);
}
// Text box to collect response, three columns, align left
addResponseBox();
if ((promptResponse != null) && (promptResponse != "")) {
if (textFont != null) {
responseBox.setFont(textFont);
}
responseBox.setText(promptResponse);
responseBox.selectAll();
}
GridData responseLayout =
new GridData(GridData.HORIZONTAL_ALIGN_FILL);
responseLayout.grabExcessHorizontalSpace = true;
if (widthHint > 0) {
responseLayout.widthHint = widthHint;
}
responseLayout.horizontalSpan =
((responseLabel != null) && ! responseLabel.equals(""))
? 3
: 4; // use entire width if no label!
responseBox.setLayoutData(responseLayout);
responseBox.setFocus();
// If needed, subclasses should extend this method.
}
/**
* Allows subclasses to add more dialog "buttons" after the
* OK/CANCEL buttons.
* <p>
* Subclasses should know that the dialog uses a
* <code>GridLayout</code> instance of four columns, and thus should
* attach <code>GridData</code> instances to each "button", using
* <code>setLayoutData</code>.
*
* @author mlh
*/
@Override
protected void addMoreButtons()
{
if (disableOK) {
okButton.setEnabled(promptResponse.length() > 0);
}
}
} // PromptDialog
/**
* A simple class representing a dialog pop-up.
* <p>
* The dialog has two buttons: "OK" and "Cancel" (localized)
* <p>
* The return value of <code>open</code> on an instance of
* <code>PromptDialog</code> will indicate which button was selected;
* see the constants <code>OK</code> and <code>CANCEL</code>.
*
* @author mlh
*/
public static class CustomDialog extends SimpleDialog
{
/**
* The return value of <code>open</code> when the user selects "OK".
*/
public static final String OK = "OK";
/**
* The return value of <code>open</code> when the user selects
* "Cancel".
*/
public static final String CANCEL = "Cancel";
// Available for alignment
protected Button okButton;
protected Button cancelButton;
protected Label rightOfButtons;
protected Listener okListener;
/**
* <p>
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
* <p>
* <p>
* <b>Implementation note</b>: The values pertinent for the
* <code>styleFlags</code> parameter are those defined in SWT's
* documentation the Shell constructors' style parameter that are not
* used for the <code>mode</code> parameter. Thus, we separate two
* sets of things, mode and style, which are combined into one by SWT;
* for the purposes of this class, values of style should be the
* set theoretic difference of SWT's style values and the modality
* values.)
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @param styleFlags flags passed to SWT's <code>Dialog</code>
* @param actionOnOK what to do when the OK button is picked
* @author alexeiser
* @see org.eclipse.swt.widgets.Dialog
*/
public CustomDialog(Shell parentWin,
String title,
int mode,
int styleFlags)
{
super(parentWin, title, mode, SWT.DIALOG_TRIM | styleFlags);
okListener = new Listener() {
public void handleEvent(Event evt)
{
onOK();
}
};
}
/**
* <code>
* mode: one of (in increasing order of restriction)
* SWT.PRIMARY_MODAL, SWT.APPLICATION_MODAL, SWT.SYSTEM_MODAL
* </code>
*
* @param parentWin the window the pop-up "adheres" to
* @param title the dialog window's title
* @param mode whether the pop-up is modal relative to the
* parent window, the application as a whole,
* or the entire user's system/computer
* @author alexeiser
*/
public CustomDialog(Shell parentWin, String title, int mode)
{
this(parentWin, title, mode, SWT.NONE);
}
/**
*
*/
public Button getOK()
{
return okButton;
}
/**
* What to do when the OK button is selected by the user.
*
* @author mlh
*/
protected void onOK()
{
userResponse = OK; // success!
dialog.close();
}
/**
* Construct the contents of the dialog window.
* <p>
* First, the question/instructions, if specified.<br>
* Next, the label (if specified) and a text input field,
* on the same line.<br>
* Finally, an "OK" and a "Cancel" button.
*
* @author mlh
*/
@Override
protected void buildDialog()
{
// Four columns
GridLayout layout = new GridLayout(4, false);
if (OSUtils.MACOSX) {
layout.marginLeft = 19;
layout.marginRight = 13;
layout.marginTop = 7;
layout.marginBottom = 12;
}
dialog.setLayout(layout);
addMoreFields();
// Buttons (OK and Cancel), centered in the 2nd and 3rd columns
Label emptyCell = new Label(dialog, SWT.NONE);
GridData emptyCellLayout = new GridData();
emptyCellLayout.grabExcessHorizontalSpace = true;
emptyCell.setLayoutData(emptyCellLayout);
okButton = new Button(dialog, SWT.PUSH);
cancelButton = new Button(dialog, SWT.PUSH);
if (OSUtils.MACOSX) {
rightOfButtons =
new Label(dialog, SWT.NONE); // 2nd column
emptyCellLayout = new GridData();
emptyCellLayout.grabExcessHorizontalSpace = true;
rightOfButtons.setLayoutData(emptyCellLayout);
// See Apple HIGs:
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/TP30000359-TPXREF186
// this.cancelButton.setSize(68, 20);
// this.okButton.setSize(68, 20);
}
if (buttonFont != null) {
okButton.setFont(buttonFont);
cancelButton.setFont(buttonFont);
}
okButton.setText(L10N.get("B.OK", "OK"));
cancelButton.setText(L10N.get("B.CANCEL", "Cancel"));
dialog.setDefaultButton(okButton);
GridData okLayout = new GridData(GridData.HORIZONTAL_ALIGN_END);
GridData cancelLayout =
new GridData(GridData.HORIZONTAL_ALIGN_END);
okButton.setLayoutData(okLayout);
cancelButton.setLayoutData(cancelLayout);
if (OSUtils.MACOSX) {
// See Apple HIGs:
// http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/XHIGControls/chapter_18_section_2.html#//apple_ref/doc/uid/TP30000359-TPXREF186
okLayout.widthHint = 82;
cancelLayout.widthHint = 82;
}
else {
rightOfButtons =
new Label(dialog, SWT.NONE); // 4th column
emptyCellLayout = new GridData();
emptyCellLayout.grabExcessHorizontalSpace = true;
rightOfButtons.setLayoutData(emptyCellLayout);
}
// Dismiss the dialog box when OK button selected, with success
okButton.addListener(SWT.Selection, okListener);
// Dismiss the dialog box when Cancel button selected, with failure
cancelButton.addListener(SWT.Selection,
new Listener() {
public void handleEvent(Event e)
{
userResponse = CANCEL;
dialog.close();
}
});
addMoreButtons();
} // buildDialog
/**
* Allows subclasses to add more dialog "fields" between the question
* and response "field" and the OK/CANCEL buttons.
* <p>
* Subclasses should know that the dialog uses a
* <code>GridLayout</code> instance of four columns, and thus should
* attach <code>GridData</code> instances to each "field", using
* <code>setLayoutData</code>.
*
* @author mlh
*/
protected void addMoreFields()
{
// If needed, subclasses should override this method.
}
/**
* Allows subclasses to add more dialog "buttons" after the
* OK/CANCEL buttons.
* <p>
* Subclasses should know that the dialog uses a
* <code>GridLayout</code> instance of four columns, and thus should
* attach <code>GridData</code> instances to each "button", using
* <code>setLayoutData</code>.
*
* @author mlh
*/
protected void addMoreButtons()
{
// If needed, subclasses should override this method.
}
} // CustomDialog
/**
* Utility a child thread may use to schedule a Runnable on the
* main UI thread asynchronously.
*
* @param r runnable to be scheduled asynchronously
*/
public static void scheduleAsynchronously(Runnable r)
{
// Note that the f.run() throws an uncaught Exception, it will be
// wrapped in an SWTException and caught not in main, but in
// WindowUtil.interact().
if (! WindowUtil.GLOBAL_DISPLAY.isDisposed()) {
WindowUtil.GLOBAL_DISPLAY.asyncExec(r);
}
}
public static abstract class CustomToolTip
{
protected Shell toolTipWindow = null;
protected Label toolTipLabel = null;
protected Point evtLocation = new Point(0, 0);
protected StringBuilder toolTipText = new StringBuilder();
protected Control tipOwner;
protected Runnable visibleTimer =
new Runnable() {
public void run()
{
// It's possible to close the window before the
// timer goes off.
if ((toolTipWindow != null) && ! toolTipWindow.isDisposed())
{
toolTipWindow.setVisible(false);
}
}
};
public CustomToolTip(Control owningWidget)
{
Listener tipListener =
new Listener() {
public void handleEvent(Event evt)
{
switch (evt.type) {
case SWT.Dispose: {
if (toolTipWindow != null) {
toolTipWindow.dispose();
toolTipWindow = null;
toolTipLabel = null;
}
break;
}
case SWT.KeyDown:
case SWT.MouseMove: {
if (toolTipWindow != null) {
toolTipWindow.setVisible(false);
}
break;
}
case SWT.MouseHover: {
setToolTip(evt.x, evt.y);
break;
}
}
}
};
tipOwner = owningWidget;
tipOwner.addListener(SWT.Dispose, tipListener);
tipOwner.addListener(SWT.KeyDown, tipListener);
tipOwner.addListener(SWT.MouseMove, tipListener);
tipOwner.addListener(SWT.MouseHover, tipListener);
}
protected abstract void setTipContents(int x, int y);
protected void setToolTip(int x, int y)
{
evtLocation.x = x;
evtLocation.y = y;
if (toolTipWindow == null) {
createToolTip();
}
setTipContents(x, y);
Point size =
toolTipWindow.computeSize(SWT.DEFAULT, SWT.DEFAULT);
Point pt = tipOwner.toDisplay(x, y);
toolTipWindow.setBounds(pt.x + 2, pt.y + 2, size.x, size.y);
//TODO: when fixed this.toolTipWindow.setVisible(true);
WindowUtil.GLOBAL_DISPLAY.timerExec(5000, visibleTimer);
}
protected abstract boolean handleClick(Event evt);
protected void createToolTip()
{
toolTipWindow =
new Shell(tipOwner.getShell(), SWT.ON_TOP | SWT.TOOL);
toolTipWindow.setVisible(false);
toolTipWindow.setLayout(new FillLayout());
toolTipLabel = new Label(toolTipWindow, SWT.NONE);
Color labelFG =
GLOBAL_DISPLAY.getSystemColor(SWT.COLOR_INFO_FOREGROUND);
Color labelBG =
GLOBAL_DISPLAY.getSystemColor(SWT.COLOR_INFO_BACKGROUND);
toolTipLabel.setForeground(labelFG);
toolTipLabel.setBackground(labelBG);
Listener labelListener =
new Listener() {
public void handleEvent(Event evt)
{
switch (evt.type) {
case SWT.MouseDown: {
if (! handleClick(evt)) {
break;
}
// Fall through!
}
case SWT.MouseExit: {
toolTipWindow.setVisible(false);
break;
}
}
}
};
toolTipLabel.addListener(SWT.MouseExit, labelListener);
toolTipLabel.addListener(SWT.MouseDown, labelListener);
}
}
public static final int MAX_BUFFER = 1000;
public static class ScrollableDialog extends SimpleDialog
{
protected static final int OK_INDEX = 0;
protected static final int CANCEL_INDEX = 1;
protected static final int YES_INDEX = 2;
protected static final int NO_INDEX = 3;
protected static final int RETRY_INDEX = 4;
protected static final int ABORT_INDEX = 5;
protected static final int IGNORE_INDEX = 6;
protected static final Integer OK = new Integer(SWT.OK);
protected static final Integer CANCEL = new Integer(SWT.CANCEL);
protected static final Integer YES = new Integer(SWT.YES);
protected static final Integer NO = new Integer(SWT.NO);
protected static final Integer RETRY = new Integer(SWT.RETRY);
protected static final Integer ABORT = new Integer(SWT.ABORT);
protected static final Integer IGNORE = new Integer(SWT.IGNORE);
// Values are in the same order as the index constants above
protected static final Integer[] VALUES =
{ OK, CANCEL, YES, NO, RETRY, ABORT, IGNORE };
protected static final String[] LABELS =
{
L10N.get("B.OK", "OK"),
L10N.get("B.Cancel", "Cancel"),
L10N.get("B.Yes", "Yes"),
L10N.get("B.No", "No"),
L10N.get("B.Retry", "Retry"),
L10N.get("B.Abort", "Abort"),
L10N.get("B.Ignore", "Ignore")
};
protected final SelectionListener OK_LISTENER =
new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
userResponse = OK;
dialog.close();
}
};
protected final SelectionListener CANCEL_LISTENER =
new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
userResponse = CANCEL;
dialog.close();
}
};
protected final SelectionListener YES_LISTENER =
new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
userResponse = YES;
dialog.close();
}
};
protected final SelectionListener NO_LISTENER =
new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
userResponse = NO;
dialog.close();
}
};
protected final SelectionListener RETRY_LISTENER =
new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
userResponse = RETRY;
dialog.close();
}
};
protected final SelectionListener ABORT_LISTENER =
new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
userResponse = ABORT;
dialog.close();
}
};
protected final SelectionListener IGNORE_LISTENER =
new SelectionAdapter()
{
@Override
public void widgetSelected(SelectionEvent e)
{
userResponse = IGNORE;
dialog.close();
}
};
protected final SelectionListener[] LISTENERS =
{ OK_LISTENER, CANCEL_LISTENER, YES_LISTENER, NO_LISTENER,
RETRY_LISTENER, ABORT_LISTENER, IGNORE_LISTENER };
protected String message;
// saved to determine which buttons should be added
// TODO: add icons?
protected int mode;
public ScrollableDialog(Shell parentWin,
String title,
int flags,
String msg)
{
super(parentWin, title, flags);
message = msg;
mode = flags;
}
protected void addEmptyCell()
{
Label emptyCell = new Label(dialog, SWT.NONE);
GridData emptyCellLayout = new GridData();
emptyCellLayout.grabExcessHorizontalSpace = true;
emptyCell.setLayoutData(emptyCellLayout);
}
protected Button buildButton(int index, boolean isDefault)
{
Button newButton = new Button(dialog, SWT.PUSH);
newButton.setText(LABELS[index]);
newButton.addSelectionListener(LISTENERS[index]);
GridData buttonLayout = new GridData(GridData.HORIZONTAL_ALIGN_FILL);
buttonLayout.widthHint = 100;
newButton.setLayoutData(buttonLayout);
if (isDefault && (dialog.getDefaultButton() == null)) {
newButton.setFocus();
dialog.setDefaultButton(newButton);
}
return newButton;
}
@Override
public void buildDialog()
{
Layout layout = new GridLayout(5, false);
dialog.setLayout(layout);
GridData textLayout = new GridData();
textLayout.horizontalAlignment = GridData.FILL;
textLayout.grabExcessHorizontalSpace = true;
textLayout.widthHint = 600;
textLayout.heightHint = 500;
textLayout.horizontalSpan = 5;
Text t = new Text(dialog,
SWT.MULTI | SWT.READ_ONLY | SWT.WRAP |
SWT.V_SCROLL);
t.setText(message);
t.setLayoutData(textLayout);
int numButtons = 0;
boolean[] setButtons = new boolean[VALUES.length];
for (int i = 0; i < VALUES.length; i++) {
if ((mode & VALUES[i].intValue()) != 0) {
setButtons[i] = true;
numButtons++;
}
}
if ((numButtons == 1) || (numButtons > 3)) {
addEmptyCell();
addEmptyCell();
addEmptyCell();
addEmptyCell();
buildButton(OK_INDEX, true);
}
else if (numButtons == 2) {
addEmptyCell();
addEmptyCell();
addEmptyCell();
if (setButtons[YES_INDEX]) {
if (OSUtils.MACOSX) {
buildButton(NO_INDEX, false);
buildButton(YES_INDEX, true);
}
else {
buildButton(YES_INDEX, true);
buildButton(NO_INDEX, false);
}
}
else if (setButtons[RETRY_INDEX]) {
if (OSUtils.MACOSX) {
buildButton(CANCEL_INDEX, false);
buildButton(RETRY_INDEX, true);
}
else {
buildButton(RETRY_INDEX, true);
buildButton(CANCEL_INDEX, false);
}
}
else {
if (OSUtils.MACOSX) {
buildButton(CANCEL_INDEX, false);
buildButton(OK_INDEX, true);
}
else {
buildButton(OK_INDEX, true);
buildButton(CANCEL_INDEX, false);
}
}
}
else if (numButtons == 3) {
if (OSUtils.MACOSX) {
addEmptyCell();
if (setButtons[ABORT_INDEX]) {
buildButton(RETRY_INDEX, false);
addEmptyCell();
buildButton(IGNORE_INDEX, false);
buildButton(ABORT_INDEX, true);
}
else {
buildButton(NO_INDEX, false);
addEmptyCell();
buildButton(CANCEL_INDEX, false);
buildButton(YES_INDEX, true);
}
}
else {
addEmptyCell();
addEmptyCell();
if (setButtons[ABORT_INDEX]) {
buildButton(ABORT_INDEX, true);
buildButton(RETRY_INDEX, false);
buildButton(IGNORE_INDEX, false);
}
else {
buildButton(YES_INDEX, true);
buildButton(NO_INDEX, false);
buildButton(CANCEL_INDEX, false);
}
}
}
} // buildDialog
} // ScrollableDialog
} // WindowUtil