blob: eaa395c23d5516ffdf3cb52ad0880baedc3a67b8 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2012 Oracle. All rights reserved.
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v1.0, which accompanies this distribution
* and is available at http://www.eclipse.org/legal/epl-v10.html.
*
* Contributors:
* Oracle - initial API and implementation
******************************************************************************/
package org.eclipse.jpt.common.ui.internal.util;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.util.OpenStrategy;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.FocusListener;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.widgets.ScrolledForm;
/**
* A suite of utility methods related to the user interface.
*
* @version 2.0
* @since 1.0
*/
public class SWTUtil {
/**
* @see Display#asyncExec(Runnable)
* @see #syncExec(Runnable)
* @see #timerExec(int, Runnable)
* @see #execute(Runnable)
*/
public static void asyncExec(Runnable runnable) {
getDisplay().asyncExec(runnable);
}
/**
* @see Display#syncExec(Runnable)
* @see #asyncExec(Runnable)
* @see #timerExec(int, Runnable)
* @see #execute(Runnable)
*/
public static void syncExec(Runnable runnable) {
getDisplay().syncExec(runnable);
}
/**
* Use the standard delay.
* @see OpenStrategy#getPostSelectionDelay()
* @see #timerExec(int, Runnable)
*/
public static void delayedExec(Runnable runnable) {
timerExec(OpenStrategy.getPostSelectionDelay(), runnable);
}
/**
* @see Display#timerExec(int, Runnable)
* @see #asyncExec(Runnable)
* @see #syncExec(Runnable)
* @see #execute(Runnable)
*/
public static void timerExec(int milliseconds, Runnable runnable) {
getDisplay().timerExec(milliseconds, runnable);
}
/**
* Execute the specified runnable if the current thread is the UI thread;
* otherwise asynchrounously dispatch the runnable to the UI thread,
* returning immediately. This is useful for event handlers when it is not
* obviously whether the events are fired on the UI thread.
*
* @see Display#asyncExec(Runnable)
* @see #asyncExec(Runnable)
* @see #syncExec(Runnable)
* @see #timerExec(int, Runnable)
*/
public static void execute(Runnable runnable) {
Display display = Display.getCurrent();
if (display != null) {
// the current thread is the UI thread
runnable.run();
} else {
Display.getDefault().asyncExec(runnable);
}
}
/**
* Execute the specified runnable if the current thread is the specified
* viewer's thread;
* otherwise asynchrounously dispatch the runnable to the viewer's thread,
* returning immediately. This is useful for event handlers when it is not
* obviously whether the events are fired on the viewer's thread.
*
* @see #execute(Runnable)
* @see Display#asyncExec(Runnable)
* @see #asyncExec(Runnable)
* @see #syncExec(Runnable)
* @see #timerExec(int, Runnable)
*/
public static void execute(Viewer viewer, Runnable runnable) {
execute(viewer.getControl(), runnable);
}
/**
* Execute the specified runnable if the current thread is the specified
* control's thread;
* otherwise asynchrounously dispatch the runnable to the control's thread,
* returning immediately. This is useful for event handlers when it is not
* obviously whether the events are fired on the control's thread.
*
* @see #execute(Runnable)
* @see Display#asyncExec(Runnable)
* @see #asyncExec(Runnable)
* @see #syncExec(Runnable)
* @see #timerExec(int, Runnable)
*/
public static void execute(Control control, Runnable runnable) {
execute(control.getDisplay(), runnable);
}
/**
* Execute the specified runnable if the current thread is the specified
* display's thread;
* otherwise asynchrounously dispatch the runnable to the display's thread,
* returning immediately. This is useful for event handlers when it is not
* obviously whether the events are fired on the display's thread.
*
* @see #execute(Runnable)
* @see Display#asyncExec(Runnable)
* @see #asyncExec(Runnable)
* @see #syncExec(Runnable)
* @see #timerExec(int, Runnable)
*/
public static void execute(Display display, Runnable runnable) {
if (display.getThread() == Thread.currentThread()) {
// the current thread is the display's thread
runnable.run();
} else {
display.asyncExec(runnable);
}
}
/**
* Convenience method for getting the current shell. If the current thread is
* not the UI thread, then an invalid thread access exception will be thrown.
*
* @return The shell, never <code>null</code>
*/
public static Shell getShell() {
// Retrieve the active shell, which can be the shell from any window
Shell shell = getDisplay().getActiveShell();
if (shell != null) {
return shell;
}
// No shell could be found, revert back to the active workbench window
shell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell();
return (shell != null) ? shell : new Shell();
}
/**
* Return the most appropriate {@link Display display}; i.e. return the
* {@link Display#getCurrent() display associated with the current thread}
* if it is present; otherwise return the
* {@link Display#getDefault() default display}.
*/
public static Display getDisplay() {
Display display = Display.getCurrent();
return (display != null) ? display : Display.getDefault();
}
/**
* Center the specified shell within its display.
* <p>
* <strong>NB:</strong> This will not look too good on a dual monitor system.
*/
public static void center(Shell shell) {
shell.setBounds(calculateCenteredBounds(shell.getBounds(), shell.getDisplay().getBounds()));
}
/**
* Center the specified shell within the specified parent shell.
*/
public static void center(Shell shell, Shell parentShell) {
shell.setBounds(calculateCenteredBounds(shell.getBounds(), parentShell.getBounds()));
}
/**
* Calculate the bounds (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Rectangle calculateCenteredBounds(Rectangle rectangle1, Rectangle rectangle2) {
return calculateCenteredBounds(rectangle1.width, rectangle1.height, rectangle2.x, rectangle2.y, rectangle2.width, rectangle2.height);
}
/**
* Calculate the bounds (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Rectangle calculateCenteredBounds(Point size1, Point size2) {
return calculateCenteredBounds(size1.x, size1.y, size2.x, size2.y);
}
/**
* Calculate the point (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Rectangle calculateCenteredBounds(int width1, int height1, int width2, int height2) {
return calculateCenteredBounds(width1, height1, 0, 0, width2, height2);
}
/**
* Calculate the point (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Rectangle calculateCenteredBounds(int width1, int height1, int x2, int y2, int width2, int height2) {
return new Rectangle(x2 + ((width2 - width1) / 2), y2 + ((height2 - height1) / 2), width1, height1);
}
/**
* Calculate the point (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Point calculateCenteredPosition(Rectangle rectangle1, Rectangle rectangle2) {
return calculateCenteredPosition(rectangle1.width, rectangle1.height, rectangle2.x, rectangle2.y, rectangle2.width, rectangle2.height);
}
/**
* Calculate the point (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Point calculateCenteredPosition(Point size1, Point size2) {
return calculateCenteredPosition(size1.x, size1.y, size2.x, size2.y);
}
/**
* Calculate the point (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Point calculateCenteredPosition(int width1, int height1, int width2, int height2) {
return calculateCenteredPosition(width1, height1, 0, 0, width2, height2);
}
/**
* Calculate the point (within the second specified rectangle's coordinate
* system) that would center the first specified rectangle
* with respect to the second specified rectangle.
*/
public static Point calculateCenteredPosition(int width1, int height1, int x2, int y2, int width2, int height2) {
return new Point(x2 + ((width2 - width1) / 2), y2 + ((height2 - height1) / 2));
}
/**
* Tweaks the given <code>Combo</code> to remove the default value when the
* widget receives the focus and to show the default when the widget loses
* the focus.
*
* @param combo The widget having a default value that is always at the
* beginning of the list
*/
public static void attachDefaultValueHandler(Combo combo) {
ComboHandler handler = new ComboHandler();
combo.addFocusListener(handler);
combo.addModifyListener(handler);
}
public static int getTableHeightHint(Table table, int rows) {
if (table.getFont().equals(JFaceResources.getDefaultFont()))
table.setFont(JFaceResources.getDialogFont());
int result= table.getItemHeight() * rows + table.getHeaderHeight();
if (table.getLinesVisible())
result+= table.getGridLineWidth() * (rows - 1);
return result;
}
/**
* Relays out the parents of the <code>Control</code>. This was taken from
* the widget <code>Section</code>.
*
* @param pane The pane to revalidate as well as its parents
*/
public static void reflow(Composite pane) {
for (Composite composite = pane; composite != null; ) {
composite.setRedraw(false);
composite = composite.getParent();
if (composite instanceof ScrolledForm || composite instanceof Shell) {
break;
}
}
for (Composite composite = pane; composite != null; ) {
composite.layout(true);
composite = composite.getParent();
if (composite instanceof ScrolledForm) {
((ScrolledForm) composite).reflow(true);
break;
}
}
for (Composite composite = pane; composite != null; ) {
composite.setRedraw(true);
composite = composite.getParent();
if (composite instanceof ScrolledForm || composite instanceof Shell) {
break;
}
}
}
/**
* Determines if the current thread is the UI event thread.
*
* @return <code>true</code> if it's the UI event thread, <code>false</code>
* otherwise
*/
public static boolean uiThread() {
return Display.getCurrent() != null;
}
/**
* Determines if the current thread is the UI event thread by using the
* thread from which the given viewer's display was instantiated.
*
* @param viewer The viewer used to determine if the current thread
* is the UI event thread
* @return <code>true</code> if the current thread is the UI event thread;
* <code>false</code> otherwise
*/
public static boolean uiThread(Viewer viewer) {
return uiThread(viewer.getControl());
}
/**
* Determines if the current thread is the UI event thread by using the
* thread from which the given widget's display was instantiated.
*
* @param widget The widget used to determine if the current thread
* is the UI event thread
* @return <code>true</code> if the current thread is the UI event thread;
* <code>false</code> otherwise
*/
public static boolean uiThread(Widget widget) {
return widget.getDisplay().getThread() == Thread.currentThread();
}
/**
* This handler is responsible for removing the default value when the combo
* has the focus or when the selected item is the default value and to select
* it when the combo loses the focus.
*/
private static class ComboHandler implements ModifyListener,
FocusListener {
public void focusGained(FocusEvent e) {
Combo combo = (Combo) e.widget;
if (combo.getSelectionIndex() == 0) {
// The text selection has to be changed outside of the context of this
// listener otherwise the combo won't update because it's currently
// notifying its listeners
asyncExec(new SelectText(combo));
}
}
public void focusLost(FocusEvent e) {
//do nothing
}
public void modifyText(ModifyEvent e) {
Combo combo = (Combo) e.widget;
if (combo.isFocusControl() &&
combo.getSelectionIndex() == 0) {
// The text has to be changed outside of the context of this
// listener otherwise the combo won't update because it's currently
// notifying its listeners
asyncExec(new ModifyText(combo));
}
}
private class ModifyText implements Runnable {
private final Combo combo;
public ModifyText(Combo combo) {
super();
this.combo = combo;
}
public void run() {
if (this.combo.isDisposed()) {
return;
}
String text = this.combo.getText();
if (text.length() == 0) {
text = this.combo.getItem(0);
this.combo.setText(text);
}
this.combo.setSelection(new Point(0, text.length()));
}
}
private class SelectText implements Runnable {
private final Combo combo;
public SelectText(Combo combo) {
super();
this.combo = combo;
}
public void run() {
if (this.combo.isDisposed()) {
return;
}
String text = this.combo.getText();
this.combo.setSelection(new Point(0, text.length()));
}
}
}
}