blob: a6166e1d2f28fdb4994a107aa3cc20544dbfc51d [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008 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.ui.internal.util;
import java.util.Locale;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.AssertionFailedException;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jpt.ui.internal.widgets.NullPostExecution;
import org.eclipse.jpt.ui.internal.widgets.PostExecution;
import org.eclipse.jpt.utility.internal.ClassTools;
import org.eclipse.swt.custom.CCombo;
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.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
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.IWorkbench;
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
*/
@SuppressWarnings("nls")
public class SWTUtil {
/**
* Causes the <code>run()</code> method of the given runnable to be invoked
* by the user-interface thread at the next reasonable opportunity. The caller
* of this method continues to run in parallel, and is not notified when the
* runnable has completed.
*
* @param runnable Code to run on the user-interface thread
* @exception org.eclipse.swt.SWTException
* <ul>
* <li>ERROR_DEVICE_DISPOSED - if the receiver has been disposed</li>
* </ul>
* @see #syncExec
*/
public static void asyncExec(Runnable runnable) {
getStandardDisplay().asyncExec(runnable);
}
/**
* Tweaks the given <code>CCombo</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(CCombo combo) {
CComboHandler handler = new CComboHandler();
combo.addFocusListener(handler);
combo.addModifyListener(handler);
}
/**
* 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);
}
/**
* Retrieves the localized string from the given NLS class by creating the
* key. That key is the concatenation of the composite's short class name
* with the toString() of the given value separated by an underscore.
*
* @param nlsClass The NLS class used to retrieve the localized text
* @param compositeClass The class used for creating the key, its short class
* name is the beginning of the key
* @param value The value used to append its toString() to the generated key
* @return The localized text associated with the value
*/
public static String buildDisplayString(Class<?> nlsClass,
Class<?> compositeClass,
Object value) {
StringBuilder sb = new StringBuilder();
sb.append(ClassTools.shortNameFor(compositeClass));
sb.append("_");
sb.append(value.toString().toLowerCase(Locale.ENGLISH));//bug 234953
//TODO in a future release we should not be converting the key using toLowerCase()
return (String) ClassTools.staticFieldValue(nlsClass, sb.toString());
}
/**
* Retrieves the localized string from the given NLS class by creating the
* key. That key is the concatenation of the composite's short class name
* with the toString() of the given value separated by an underscore.
*
* @param nlsClass The NLS class used to retrieve the localized text
* @param composite The object used to retrieve the short class name that is
* the beginning of the key
* @param value The value used to append its toString() to the generated key
* @return The localized text associated with the value
*/
public static final String buildDisplayString(Class<?> nlsClass,
Object composite,
Object value) {
return buildDisplayString(nlsClass, composite.getClass(), value);
}
/**
* Creates the <code>Runnable</code> that will invoke the given
* <code>PostExecution</code> in order to its execution to be done in the
* UI thread.
*
* @param dialog The dialog that was just diposed
* @param postExecution The post execution once the dialog is disposed
* @return The <code>Runnable</code> that will invoke
* {@link PostExecution#execute(Dialog)}
*/
@SuppressWarnings("unchecked")
private static <D1 extends Dialog, D2 extends D1>
Runnable buildPostExecutionRunnable(
final D1 dialog,
final PostExecution<D2> postExecution) {
return new Runnable() {
public void run() {
setUserInterfaceActive(false);
try {
postExecution.execute((D2) dialog);
}
finally {
setUserInterfaceActive(true);
}
}
};
}
/**
* 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 = getStandardDisplay().getActiveShell();
// No shell could be found, revert back to the active workbench window
if (shell == null) {
shell = getWorkbench().getActiveWorkbenchWindow().getShell();
}
// Make sure it's never null
if (shell == null) {
shell = new Shell(getStandardDisplay().getActiveShell());
}
return shell;
}
/**
* Returns the standard display to be used. The method first checks, if the
* thread calling this method has an associated display. If so, this display
* is returned. Otherwise the method returns the default display.
*
* @return The current display if not <code>null</code> otherwise the default
* display is returned
*/
public static Display getStandardDisplay()
{
Display display = Display.getCurrent();
if (display == null) {
display = Display.getDefault();
}
return display;
}
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;
}
/**
* Returns the Platform UI workbench.
*
* @return The workbench for this plug-in
*/
public static IWorkbench getWorkbench() {
return PlatformUI.getWorkbench();
}
/**
* 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;
}
}
}
/**
* Sets whether the entire shell and its widgets should be enabled or
* everything should be unaccessible.
*
* @param active <code>true</code> to make all the UI active otherwise
* <code>false</code> to deactivate it
*/
public static void setUserInterfaceActive(boolean active) {
Shell[] shells = getStandardDisplay().getShells();
for (Shell shell : shells) {
shell.setEnabled(active);
}
}
/**
* Asynchronously launches the specified dialog in the UI thread.
*
* @param dialog The dialog to show on screen
* @param postExecution This interface let the caller to invoke a piece of
* code once the dialog is disposed
*/
public static <D1 extends Dialog, D2 extends D1>
void show(final D1 dialog, final PostExecution<D2> postExecution) {
try {
Assert.isNotNull(dialog, "The dialog cannot be null");
Assert.isNotNull(postExecution, "The PostExecution cannot be null");
}
catch (AssertionFailedException e) {
// Make sure the UI is interactive
setUserInterfaceActive(true);
throw e;
}
new Thread() {
@Override
public void run() {
asyncExec(
new Runnable() { public void run() {
showImp(dialog, postExecution);
}
}
);
}}.start();
}
/**
* Asynchronously launches the specified dialog in the UI thread.
*
* @param dialog The dialog to show on screen
*/
public static void show(Dialog dialog) {
show(dialog, NullPostExecution.<Dialog>instance());
}
/**
* Asynchronously launches the specified dialog in the UI thread.
*
* @param dialog The dialog to show on screen
* @param postExecution This interface let the caller to invoke a piece of
* code once the dialog is disposed
*/
private static <D1 extends Dialog, D2 extends D1>
void showImp(D1 dialog, PostExecution<D2> postExecution) {
setUserInterfaceActive(true);
dialog.open();
if (postExecution != NullPostExecution.<D2>instance()) {
asyncExec(buildPostExecutionRunnable(dialog, postExecution));
}
}
/**
* Causes the <code>run()</code> method of the given runnable to be invoked
* by the user-interface thread at the next reasonable opportunity. The
* thread which calls this method is suspended until the runnable completes.
*
* @param runnable code to run on the user-interface thread.
* @see #asyncExec
*/
public static void syncExec(Runnable runnable) {
getStandardDisplay().syncExec(runnable);
}
/**
* 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 getStandardDisplay().getThread() == Thread.currentThread();
}
/**
* 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 CComboHandler implements ModifyListener,
FocusListener {
public void focusGained(FocusEvent e) {
CCombo combo = (CCombo) 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) {
CCombo combo = (CCombo) e.widget;
if (combo.isFocusControl() &&
combo.getSelectionIndex() <= 0) {
// Make sure the current text is the default value
String currentValue = combo.getText();
if (currentValue.length() > 0 &&
combo.getItemCount() > 0 &&
!currentValue.equals(combo.getItem(0))) {
return;
}
// 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 SelectText implements Runnable {
private final CCombo combo;
public SelectText(CCombo 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()));
}
}
private class ModifyText implements Runnable {
private final CCombo combo;
public ModifyText(CCombo 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()));
}
}
}
/**
* 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()));
}
}
}
}