diff options
Diffstat (limited to 'propertysheet/src/org/eclipse/wb/internal/core/utils')
20 files changed, 3234 insertions, 0 deletions
diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java new file mode 100644 index 0000000..3df0e9b --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/Pair.java @@ -0,0 +1,81 @@ +/*******************************************************************************
+ * Copyright (c) 2011 Google, Inc.
+ * 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:
+ * Google, Inc. - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.wb.internal.core.utils;
+
+import com.google.common.base.Objects;
+
+/**
+ * Pair of two objects.
+ *
+ * @author scheglov_ke
+ * @coverage core.util
+ */
+public final class Pair<L, R> {
+ private final L left;
+ private final R right;
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Constructor
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public Pair(L left, R right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Object
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if (!(o instanceof Pair<?, ?>)) {
+ return false;
+ }
+ Pair<?, ?> other = (Pair<?, ?>) o;
+ return Objects.equal(getLeft(), other.getLeft())
+ && Objects.equal(getRight(), other.getRight());
+ }
+
+ @Override
+ public int hashCode() {
+ int hLeft = getLeft() == null ? 0 : getLeft().hashCode();
+ int hRight = getRight() == null ? 0 : getRight().hashCode();
+ return hLeft + 37 * hRight;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Access
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public L getLeft() {
+ return left;
+ }
+
+ public R getRight() {
+ return right;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ //
+ // Factory
+ //
+ ////////////////////////////////////////////////////////////////////////////
+ public static <L, R> Pair<L, R> create(L left, R right) {
+ return new Pair<L, R>(left, right);
+ }
+}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java new file mode 100644 index 0000000..155b55c --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/AbstractControlActionsManager.java @@ -0,0 +1,174 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.binding.editors.controls; + +import com.google.common.collect.Lists; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.core.expressions.EvaluationResult; +import org.eclipse.core.expressions.Expression; +import org.eclipse.core.expressions.ExpressionInfo; +import org.eclipse.core.expressions.IEvaluationContext; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.ISources; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.handlers.IHandlerActivation; +import org.eclipse.ui.handlers.IHandlerService; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; + +import java.util.List; + +/** + * Manager for installing/unistalling global handlers for {@link Control} actions commands. + * + * @author sablin_aa + * @author mitin_aa + */ +public abstract class AbstractControlActionsManager { + @SuppressWarnings("deprecation") + protected final Object[] COMMAND_HANDLER_IDS = new Object[]{ + IWorkbenchActionDefinitionIds.COPY, + IWorkbenchActionDefinitionIds.CUT, + IWorkbenchActionDefinitionIds.PASTE, + IWorkbenchActionDefinitionIds.DELETE, + IWorkbenchActionDefinitionIds.SELECT_ALL, + IWorkbenchActionDefinitionIds.UNDO, + IWorkbenchActionDefinitionIds.REDO}; + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + protected final Control m_control; + private boolean m_active = false; + + public AbstractControlActionsManager(final Control control) { + m_control = control; + m_control.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + activateHandlers(); + m_active = true; + } + + @Override + public void focusLost(FocusEvent e) { + deactivateHandlers(); + m_active = false; + } + }); + m_control.addDisposeListener(new DisposeListener() { + @Override + public void widgetDisposed(DisposeEvent e) { + if (m_active) { + // deactivate on dispose + deactivateHandlers(); + } + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Handlers + // + //////////////////////////////////////////////////////////////////////////// + protected static final IHandler EMPTY_HANDLER = new AbstractHandler() { + @Override + public Object execute(ExecutionEvent event) throws ExecutionException { + // do nothing + return null; + } + + @Override + public boolean isEnabled() { + // of course, it is enabled ;) + return true; + } + + @Override + public boolean isHandled() { + // we do not handle the actions; since action not handled, its' underlying SWT event + // would not be filtered by workbench, so it get a chance to be handled by SWT code + // or to be passed to the OS; see WorkbenchKeyboard.press() method. + return false; + } + }; + + /** + * @returns the global handler service. + */ + public static IHandlerService getHandlerService() { + return (IHandlerService) PlatformUI.getWorkbench().getService(IHandlerService.class); + } + + /** + * Activates the handlers for list of commands (see COMMAND_HANDLERS) with:<br> + * 1. The empty handler (except 'selectAll'), so underlying SWT event wouldn't be filtered by the + * workbench;<br> + * 2. Highest priority {@link Expression}, so this handler has a chance to be set. + */ + protected void activateHandlers() { + IHandlerService service = getHandlerService(); + for (int i = 0; i < COMMAND_HANDLER_IDS.length; ++i) { + String actionName = (String) COMMAND_HANDLER_IDS[i]; + IHandler handler = getHandlerFor(actionName); + activateHandler(actionName, service, handler, new Expression() { + @Override + public EvaluationResult evaluate(IEvaluationContext context) throws CoreException { + return EvaluationResult.TRUE; + } + + @Override + public void collectExpressionInfo(ExpressionInfo info) { + // get the highest priority + // note, if someone else has such priority, there will be key-binding conflicts logged. + info.markSystemPropertyAccessed(); + info.markDefaultVariableAccessed(); + info.addVariableNameAccess(ISources.ACTIVE_MENU_NAME); + } + }); + } + } + + protected IHandler getHandlerFor(String actionName) { + return EMPTY_HANDLER; + } + + /** + * Activates handler and stores it into a collection for further deactivation. + */ + private final List<IHandlerActivation> m_activations = Lists.newLinkedList(); + + private void activateHandler(String actionName, + IHandlerService service, + IHandler handler, + Expression highPriorityExpression) { + // activate handler and store it into a collection for further deactivation + m_activations.add(service.activateHandler(actionName, handler, highPriorityExpression)); + } + + /** + * Deactivates all handlers and clears handlers collection. + */ + protected void deactivateHandlers() { + getHandlerService().deactivateHandlers(m_activations); + m_activations.clear(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java new file mode 100644 index 0000000..ff048bc --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/binding/editors/controls/DefaultControlActionsManager.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.binding.editors.controls; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.texteditor.IWorkbenchActionDefinitionIds; + +/** + * Default manager for installing/unistalling global handlers for {@link Control} actions commands. + * + * @author sablin_aa + */ +public class DefaultControlActionsManager extends AbstractControlActionsManager { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public DefaultControlActionsManager(final Control control) { + super(control); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Handlers + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected IHandler getHandlerFor(String actionName) { + if (actionName.equalsIgnoreCase(IWorkbenchActionDefinitionIds.SELECT_ALL)) { + return SELECTALL_HANDLER; + } + return super.getHandlerFor(actionName); + } + + /** + * Handler for process "Select all" action. + */ + private final IHandler SELECTALL_HANDLER = new AbstractHandler() { + public Object execute(ExecutionEvent event) throws ExecutionException { + selectAllExecuted(); + return null; + } + + @Override + public boolean isEnabled() { + return true; + } + + @Override + public boolean isHandled() { + return true; + } + }; + + protected void selectAllExecuted() { + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java new file mode 100644 index 0000000..28de0b6 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/Assert.java @@ -0,0 +1,361 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.check; + +import java.text.MessageFormat; + +/** + * <code>Assert</code> is useful for for embedding runtime sanity checks in code. The predicate + * methods all test a condition and throw some type of unchecked exception if the condition does not + * hold. + * <p> + * Assertion failure exceptions, like most runtime exceptions, are thrown when something is + * misbehaving. Assertion failures are invariably unspecified behavior; consequently, clients should + * never rely on these being thrown (and certainly should not being catching them specifically). + * + * @author scheglov_ke + * @coverage core.util + */ +public final class Assert { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private Assert() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // "legal" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that an argument is legal. If the given boolean is not <code>true</code>, an + * <code>IllegalArgumentException</code> is thrown. + * + * @param expression + * the boolean expression of the check + * @return <code>true</code> if the check passes (does not return if the check fails) + * @exception IllegalArgumentException + * if the legality test failed + */ + public static boolean isLegal(boolean expression) { + return isLegal(expression, ""); //$NON-NLS-1$ + } + + /** + * Asserts that an argument is legal. If the given boolean is not <code>true</code>, an + * <code>IllegalArgumentException</code> is thrown. The given message is included in that + * exception, to aid debugging. + * + * @param expression + * the boolean expression of the check + * @param message + * the message to include in the exception + * @return <code>true</code> if the check passes (does not return if the check fails) + * @exception IllegalArgumentException + * if the legality test failed + */ + public static boolean isLegal(boolean expression, String message) { + if (!expression) { + throw new IllegalArgumentException(message); + } + return expression; + } + + //////////////////////////////////////////////////////////////////////////// + // + // "null" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that the given object is <code>null</code>. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param object + * the value to test + */ + public static void isNull(Object object) { + isNull(object, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given object is <code>null</code>. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param message + * the message to include in the exception + */ + public static void isNull(Object object, String message) { + if (object != null) { + throw new AssertionFailedException("null argument expected: " + message); //$NON-NLS-1$ + } + } + + /** + * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link String#format(String, Object...)}. For example + * <code>"Execution flow problem. %s expected, but %s found."</code>. + * @param args + * the arguments for {@code errorFormat} + */ + public static void isNull(Object object, String errorFormat, Object... args) { + if (object != null) { + fail("null argument expected: " + String.format(errorFormat, args)); //$NON-NLS-1$ + } + } + + /** + * @param errorFormat + * the format of error message suitable for {@link MessageFormat}. + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example + * <code>"Execution flow problem. {0} expected, but {1} found."</code>. + */ + public static void isNull2(Object object, String errorFormat, Object... args) { + if (object != null) { + String message = "null argument expected: " + MessageFormat.format(errorFormat, args); //$NON-NLS-1$ + fail(message); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // not "null" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param object + * the value to test + */ + public static void isNotNull(Object object) { + isNotNull(object, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param message + * the message to include in the exception + */ + public static void isNotNull(Object object, String message) { + if (object == null) { + fail("null argument: " + message); //$NON-NLS-1$ + } + } + + /** + * Asserts that the given object is not <code>null</code>. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param object + * the value to test + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link String#format(String, Object...)}. For example + * <code>"Execution flow problem. %s expected, but %s found."</code>. + * @param args + * the arguments for {@code errorFormat} + */ + public static void isNotNull(Object object, String errorFormat, Object... args) { + if (object == null) { + fail("null argument: " + String.format(errorFormat, args)); //$NON-NLS-1$ + } + } + + /** + * @param errorFormat + * the format of error message suitable for {@link MessageFormat}. + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example + * <code>"Execution flow problem. {0} expected, but {1} found."</code>. + */ + public static void isNotNull2(Object object, String errorFormat, Object... args) { + if (object == null) { + String message = "null argument: " + MessageFormat.format(errorFormat, args); //$NON-NLS-1$ + fail(message); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Fail + // + //////////////////////////////////////////////////////////////////////////// + /** + * Fails with given message. + * + * @param message + * the message to include in the exception + */ + public static void fail(String message) { + throw new AssertionFailedException(message); + } + + /** + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example <code>"{0} expected, but {1} found."</code>. + */ + public static void fail(String errorFormat, Object... args) { + String message = MessageFormat.format(errorFormat, args); + throw new AssertionFailedException(message); + } + + //////////////////////////////////////////////////////////////////////////// + // + // "true" + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param expression + * the boolean expression of the check + * @return <code>true</code> if the check passes (does not return if the check fails) + */ + public static boolean isTrue(boolean expression) { + return isTrue(expression, ""); //$NON-NLS-1$ + } + + /** + * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the boolean expression of the check + * @param message + * the message to include in the exception + * @return <code>true</code> if the check passes (does not return if the check fails) + */ + public static boolean isTrue(boolean expression, String message) { + if (!expression) { + fail("assertion failed: " + message); //$NON-NLS-1$ + } + return expression; + } + + /** + * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the boolean expression of the check + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link String#format(String, Object...)}. For example + * <code>"Execution flow problem. %s expected, but %s found."</code>. + * @param args + * the arguments for {@code errorFormat} + * @return <code>true</code> if the check passes (does not return if the check fails) + */ + public static boolean isTrue(boolean expression, String errorFormat, Object... args) { + if (!expression) { + fail("assertion failed: " + String.format(errorFormat, args)); //$NON-NLS-1$ + } + return expression; + } + + /** + * Asserts that the given boolean is <code>true</code>. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expression + * the boolean expression to check. + * @param errorFormat + * the format of error message to produce if the check fails, as expected by + * {@link MessageFormat}. For example <code>"{0} expected, but {1} found."</code>. + */ + public static boolean isTrue2(boolean expression, String errorFormat, Object... args) { + if (!expression) { + fail(errorFormat, args); + } + return expression; + } + + //////////////////////////////////////////////////////////////////////////// + // + // equals + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that given actual value equals expected value. If this is not the case, some kind of + * unchecked exception is thrown. + * + * @param expected + * the expected value + * @param the + * actual value to check + */ + public static void equals(int expected, int actual) { + equals(expected, actual, expected + " expected, but " + actual + " found"); + } + + /** + * Asserts that given actual value equals expected value. If this is not the case, some kind of + * unchecked exception is thrown. The given message is included in that exception, to aid + * debugging. + * + * @param expected + * the expected value + * @param the + * actual value to check + * @param message + * the message to include in the exception + */ + public static void equals(int expected, int actual, String message) { + if (expected != actual) { + fail("assertation failed: " + message); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // instanceOf + // + //////////////////////////////////////////////////////////////////////////// + /** + * Asserts that given object is not <code>null</code> and has class compatible with given. + */ + public static void instanceOf(Class<?> expectedClass, Object o) { + if (o == null) { + fail(expectedClass.getName() + " expected, but 'null' found."); + } + if (!expectedClass.isAssignableFrom(o.getClass())) { + fail(expectedClass.getName() + " expected, but " + o.getClass().getName() + " found."); + } + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java new file mode 100644 index 0000000..d0c9794 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/check/AssertionFailedException.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.check; + +/** + * <code>AssertionFailedException</code> is a runtime exception thrown by some of the methods in + * <code>Assert</code>. + * + * @author scheglov_ke + * @coverage core.util + */ +public final class AssertionFailedException extends RuntimeException { + private static final long serialVersionUID = 0L; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + /** + * Constructs a new exception with the given message. + * + * @param detail + * the message + */ + public AssertionFailedException(String detail) { + super(detail); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java new file mode 100644 index 0000000..d80ff94 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/ExecutionUtils.java @@ -0,0 +1,292 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.execution; + +import org.eclipse.swt.widgets.Display; +import org.eclipse.wb.internal.core.DesignerPlugin; +import org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils; + +import java.beans.Beans; + +/** + * Utilities for executing actions, such as {@link RunnableEx}. + * + * @author scheglov_ke + * @coverage core.util + */ +public class ExecutionUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private ExecutionUtils() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Sleep + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sleeps given number of milliseconds, ignoring exceptions. + */ + public static void sleep(final int millis) { + runIgnore(new RunnableEx() { + @Override + public void run() throws Exception { + Thread.sleep(millis); + } + }); + } + + /** + * Waits given number of milliseconds and runs events loop every 1 millisecond. At least one + * events loop will be executed. If current thread is not UI thread, then this method works just + * as {@link #sleep(int)}. + */ + public static void waitEventLoop(int millis) { + Display display = Display.getCurrent(); + if (display != null) { + long nanos = millis * 1000000L; + long start = System.nanoTime(); + do { + sleep(0); + while (display.readAndDispatch()) { + // do nothing + } + } while (System.nanoTime() - start < nanos); + } else { + sleep(millis); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // void + // + //////////////////////////////////////////////////////////////////////////// + /** + * Runs given {@link RunnableEx} and ignores exceptions. + * + * @return <code>true</code> if execution was finished without exception. + */ + public static boolean runIgnore(RunnableEx runnable) { + try { + runnable.run(); + return true; + } catch (Throwable e) { + return false; + } + } + + /** + * Runs given {@link RunnableEx} and logs exceptions using {@link DesignerPlugin#log(Throwable)}. + * + * @return <code>true</code> if execution was finished without exception. + */ + public static boolean runLog(RunnableEx runnable) { + try { + runnable.run(); + return true; + } catch (Throwable e) { + DesignerPlugin.log(e); + return false; + } + } + + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + */ + public static void runRethrow(RunnableEx runnable) { + try { + runnable.run(); + } catch (Throwable e) { + throw ReflectionUtils.propagate(e); + } + } + + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + */ + public static void runRethrow(RunnableEx runnable, String format, Object... args) { + try { + runnable.run(); + } catch (Throwable e) { + String message = String.format(format, args); + throw new RuntimeException(message, e); + } + } + + /** + * Ensures that {@link Beans#isDesignTime()} returns <code>true</code> and runs given + * {@link RunnableEx}. + */ + public static void runDesignTime(RunnableEx runnable) throws Exception { + boolean old_designTime = Beans.isDesignTime(); + try { + Beans.setDesignTime(true); + runnable.run(); + } finally { + Beans.setDesignTime(old_designTime); + } + } + + /** + * Ensures that {@link Beans#isDesignTime()} returns <code>true</code> and runs given + * {@link RunnableEx}. + */ + public static <T> T runDesignTime(RunnableObjectEx<T> runnable) throws Exception { + boolean old_designTime = Beans.isDesignTime(); + try { + Beans.setDesignTime(true); + return runnable.runObject(); + } finally { + Beans.setDesignTime(old_designTime); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // UI + // + //////////////////////////////////////////////////////////////////////////// + /** + * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}. + * + * @return <code>true</code> if {@link RunnableEx} was executed without any {@link Exception}. + */ + public static boolean runLogUI(final RunnableEx runnable) { + final boolean[] success = new boolean[1]; + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + success[0] = ExecutionUtils.runLog(runnable); + } + }); + return success[0]; + } + + /** + * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}. + */ + public static void runRethrowUI(final RunnableEx runnable) { + Display.getDefault().syncExec(new Runnable() { + @Override + public void run() { + ExecutionUtils.runRethrow(runnable); + } + }); + } + + /** + * Runs given {@link RunnableEx} within UI thread using {@link Display#asyncExec(Runnable)}. Logs + * a {@link Throwable} which may occur. + */ + public static void runAsync(final RunnableEx runnable) { + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + ExecutionUtils.runLog(runnable); + } + }); + } + + /** + * Runs given {@link RunnableEx} inside of UI thread, using {@link Display#syncExec(Runnable)}. + */ + @SuppressWarnings("unchecked") + public static <T> T runObjectUI(final RunnableObjectEx<T> runnable) { + final Object[] result = new Object[1]; + runRethrowUI(new RunnableEx() { + @Override + public void run() throws Exception { + result[0] = runObject(runnable); + } + }); + return (T) result[0]; + } + + /** + * Runs given {@link RunnableEx} as {@link #runLog(RunnableEx)}, but using + * {@link Display#asyncExec(Runnable)}. + */ + public static void runLogLater(final RunnableEx runnable) { + Display.getDefault().asyncExec(new Runnable() { + @Override + public void run() { + ExecutionUtils.runLog(runnable); + } + }); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Object + // + //////////////////////////////////////////////////////////////////////////// + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + * + * @return the {@link Object} returned by {@link RunnableEx#run()}. + */ + public static <T> T runObject(RunnableObjectEx<T> runnable) { + try { + return runnable.runObject(); + } catch (Throwable e) { + throw ReflectionUtils.propagate(e); + } + } + + /** + * Runs given {@link RunnableEx} and re-throws exceptions using {@link RuntimeException}. + * + * @return the {@link Object} returned by {@link RunnableEx#run()}. + */ + public static <T> T runObject(RunnableObjectEx<T> runnable, String format, Object... args) { + try { + return runnable.runObject(); + } catch (Throwable e) { + String message = String.format(format, args); + throw new Error(message, e); + } + } + + /** + * Runs given {@link RunnableEx} and ignores exceptions. + * + * @return the {@link Object} returned by {@link RunnableEx#run()} or <code>defaultValue</code> if + * exception happened. + */ + public static <T> T runObjectIgnore(RunnableObjectEx<T> runnable, T defaultValue) { + try { + return runnable.runObject(); + } catch (Throwable e) { + return defaultValue; + } + } + + /** + * Runs given {@link RunnableEx} and logs exceptions using {@link DesignerPlugin#log(Throwable)}. + * + * @return the {@link Object} returned by {@link RunnableEx#run()} or <code>defaultValue</code> if + * exception was logged. + */ + public static <T> T runObjectLog(RunnableObjectEx<T> runnable, T defaultValue) { + try { + return runnable.runObject(); + } catch (Throwable e) { + DesignerPlugin.log(e); + return defaultValue; + } + } + +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java new file mode 100644 index 0000000..18c141f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableEx.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.execution; + +/** + * Analog of {@link Runnable} where method <code>run</code> can throw {@link Exception}. + * + * @author scheglov_ke + * @coverage core.util + */ +public interface RunnableEx { + /** + * Executes operation that can cause {@link Exception}. + */ + void run() throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java new file mode 100644 index 0000000..02a2865 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/execution/RunnableObjectEx.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.execution; + +/** + * Analog of {@link Runnable} where method <code>run</code> can throw {@link Exception}. + * + * @author scheglov_ke + * @coverage core.util + */ +public interface RunnableObjectEx<T> { + /** + * Executes operation that can cause {@link Exception}. + * + * @return some {@link Object} result for caller. + */ + T runObject() throws Exception; +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java new file mode 100644 index 0000000..b74ba88 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassLoaderLocalMap.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.reflect; + +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * Helper for setting properties for {@link ClassLoader}. + * <p> + * http://java.dzone.com/articles/classloaderlocal-how-avoid + * + * @author Jevgeni Kabanov + * @author scheglov_ke + * @coverage core.util + */ +@SuppressWarnings("unchecked") +public class ClassLoaderLocalMap implements Opcodes { + private static final String NAME = "GEN$$ClassLoaderProperties"; + private static final Map<Object, Object> globalMap = + Collections.synchronizedMap(new WeakHashMap<Object, Object>()); + private static Method defineMethod; + private static Method findLoadedClass; + static { + try { + defineMethod = + ClassLoader.class.getDeclaredMethod("defineClass", new Class[]{ + String.class, + byte[].class, + int.class, + int.class}); + defineMethod.setAccessible(true); + findLoadedClass = + ClassLoader.class.getDeclaredMethod("findLoadedClass", new Class[]{String.class}); + findLoadedClass.setAccessible(true); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Map + // + //////////////////////////////////////////////////////////////////////////// + public static boolean containsKey(ClassLoader cl, Object key) { + if (cl == null) { + return globalMap.containsKey(key); + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + if (!hasHolder(cl)) { + return false; + } + return getLocalMap(cl).containsKey(key); + } + } + + public static void put(ClassLoader cl, Object key, Object value) { + if (cl == null) { + globalMap.put(key, value); + return; + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + getLocalMap(cl).put(key, value); + } + } + + public static Object get(ClassLoader cl, Object key) { + if (cl == null) { + return globalMap.get(key); + } + // synchronizing over ClassLoader is usually safest + synchronized (cl) { + return getLocalMap(cl).get(key); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation + // + //////////////////////////////////////////////////////////////////////////// + private static boolean hasHolder(ClassLoader cl) { + String propertiesClassName = NAME; + try { + Class<?> clazz = (Class<?>) findLoadedClass.invoke(cl, new Object[]{propertiesClassName}); + if (clazz == null) { + return false; + } + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getTargetException()); + } + return true; + } + + private static Map<Object, Object> getLocalMap(ClassLoader cl) { + String holderClassName = NAME; + Class<?> holderClass; + try { + holderClass = (Class<?>) findLoadedClass.invoke(cl, new Object[]{holderClassName}); + } catch (IllegalArgumentException e) { + throw new RuntimeException(e); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getTargetException()); + } + if (holderClass == null) { + byte[] classBytes = buildHolderByteCode(holderClassName); + try { + holderClass = + (Class<?>) defineMethod.invoke( + cl, + new Object[]{ + holderClassName, + classBytes, + Integer.valueOf(0), + Integer.valueOf(classBytes.length)}); + } catch (InvocationTargetException e1) { + throw new RuntimeException(e1.getTargetException()); + } catch (Throwable e1) { + throw new RuntimeException(e1); + } + } + try { + return (Map<Object, Object>) holderClass.getDeclaredField("localMap").get(null); + } catch (Throwable e1) { + throw new RuntimeException(e1); + } + } + + private static byte[] buildHolderByteCode(String holderClassName) { + ClassWriter cw = new ClassWriter(0); + FieldVisitor fv; + MethodVisitor mv; + cw.visit(V1_2, ACC_PUBLIC + ACC_SUPER, holderClassName, null, "java/lang/Object", null); + { + fv = + cw.visitField( + ACC_PUBLIC + ACC_FINAL + ACC_STATIC, + "localMap", + "Ljava/util/Map;", + null, + null); + fv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null); + mv.visitCode(); + mv.visitTypeInsn(NEW, "java/util/WeakHashMap"); + mv.visitInsn(DUP); + mv.visitMethodInsn(INVOKESPECIAL, "java/util/WeakHashMap", "<init>", "()V"); + mv.visitFieldInsn(PUTSTATIC, holderClassName, "localMap", "Ljava/util/Map;"); + mv.visitInsn(RETURN); + mv.visitMaxs(2, 0); + mv.visitEnd(); + } + { + mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); + mv.visitCode(); + mv.visitVarInsn(ALOAD, 0); + mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); + mv.visitInsn(RETURN); + mv.visitMaxs(1, 1); + mv.visitEnd(); + } + cw.visitEnd(); + return cw.toByteArray(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java new file mode 100644 index 0000000..ce0d339 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ClassMap.java @@ -0,0 +1,75 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.reflect; + +import java.util.HashMap; +import java.util.Map; + +/** + * {@link Map}-like interface for mapping {@link Class} to value. + * + * @author scheglov_ke + * @coverage core.util + */ +public final class ClassMap<V> { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + /** + * Creates new instance of {@link ClassMap}. + */ + public static <V> ClassMap<V> create() { + return new ClassMap<V>(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Map + // + //////////////////////////////////////////////////////////////////////////// + public void put(Class<?> key, V value) { + getMap(key).put(key, value); + } + + public V get(Class<?> key) { + return getMap(key).get(key); + } + + public void remove(Class<?> key) { + getMap(key).remove(key); + } + + public void clear(ClassLoader classLoader) { + getMap(classLoader).clear(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Implementation + // + //////////////////////////////////////////////////////////////////////////// + private Map<Class<?>, V> getMap(Class<?> key) { + ClassLoader classLoader = key.getClassLoader(); + return getMap(classLoader); + } + + @SuppressWarnings("unchecked") + private Map<Class<?>, V> getMap(ClassLoader classLoader) { + Object map = ClassLoaderLocalMap.get(classLoader, this); + if (map == null) { + map = new HashMap<Class<?>, V>(); + ClassLoaderLocalMap.put(classLoader, this, map); + } + return (Map<Class<?>, V>) map; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java new file mode 100644 index 0000000..b362211 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/reflect/ReflectionUtils.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.reflect; + +import com.google.common.collect.Maps; + +import org.eclipse.wb.internal.core.utils.check.Assert; + +import java.lang.reflect.Field; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Map; + +/** + * Contains different Java reflection utilities. + * + * @author scheglov_ke + * @coverage core.util + */ +public class ReflectionUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private ReflectionUtils() { + } + + //////////////////////////////////////////////////////////////////////////// + // + // Signature + // + //////////////////////////////////////////////////////////////////////////// + /** + * @param runtime + * is <code>true</code> if we need name for class loading, <code>false</code> if we need + * name for source generation. + * + * @return the fully qualified name of given {@link Type}. + */ + public static String getFullyQualifiedName(Type type, boolean runtime) { + Assert.isNotNull(type); + // Class + if (type instanceof Class<?>) { + Class<?> clazz = (Class<?>) type; + // array + if (clazz.isArray()) { + return getFullyQualifiedName(clazz.getComponentType(), runtime) + "[]"; + } + // object + String name = clazz.getName(); + if (!runtime) { + name = name.replace('$', '.'); + } + return name; + } + // GenericArrayType + if (type instanceof GenericArrayType) { + GenericArrayType genericArrayType = (GenericArrayType) type; + return getFullyQualifiedName(genericArrayType.getGenericComponentType(), runtime) + "[]"; + } + // ParameterizedType + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType) type; + Type rawType = parameterizedType.getRawType(); + // raw type + StringBuilder sb = new StringBuilder(); + sb.append(getFullyQualifiedName(rawType, runtime)); + // type arguments + sb.append("<"); + boolean firstTypeArgument = true; + for (Type typeArgument : parameterizedType.getActualTypeArguments()) { + if (!firstTypeArgument) { + sb.append(","); + } + firstTypeArgument = false; + sb.append(getFullyQualifiedName(typeArgument, runtime)); + } + sb.append(">"); + // done + return sb.toString(); + } + // WildcardType + if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + return "? extends " + getFullyQualifiedName(wildcardType.getUpperBounds()[0], runtime); + } + // TypeVariable + TypeVariable<?> typeVariable = (TypeVariable<?>) type; + return typeVariable.getName(); + } + + /** + * Appends fully qualified names of given parameter types (appends also <code>"()"</code>). + */ + private static void appendParameterTypes(StringBuilder buffer, Type[] parameterTypes) { + buffer.append('('); + boolean firstParameter = true; + for (Type parameterType : parameterTypes) { + if (firstParameter) { + firstParameter = false; + } else { + buffer.append(','); + } + buffer.append(getFullyQualifiedName(parameterType, false)); + } + buffer.append(')'); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Method + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return all declared {@link Method}'s, including protected and private. + */ + public static Map<String, Method> getMethods(Class<?> clazz) { + Map<String, Method> methods = Maps.newHashMap(); + // process classes + for (Class<?> c = clazz; c != null; c = c.getSuperclass()) { + for (Method method : c.getDeclaredMethods()) { + String signature = getMethodSignature(method); + if (!methods.containsKey(signature)) { + method.setAccessible(true); + methods.put(signature, method); + } + } + } + // process interfaces + for (Class<?> interfaceClass : clazz.getInterfaces()) { + for (Method method : interfaceClass.getDeclaredMethods()) { + String signature = getMethodSignature(method); + if (!methods.containsKey(signature)) { + method.setAccessible(true); + methods.put(signature, method); + } + } + } + // done + return methods; + } + + /** + * @return signature for given {@link Method}. This signature is not same signature as in JVM or + * JDT, just some string that unique identifies method in its {@link Class}. + */ + public static String getMethodSignature(Method method) { + Assert.isNotNull(method); + return getMethodSignature(method.getName(), method.getParameterTypes()); + } + + /** + * Returns the signature of {@link Method} with given combination of name and parameter types. + * This signature is not same signature as in JVM or JDT, just some string that unique identifies + * method in its {@link Class}. + * + * @param name + * the name of {@link Method}. + * @param parameterTypes + * the types of {@link Method} parameters. + * + * @return signature of {@link Method}. + */ + public static String getMethodSignature(String name, Type... parameterTypes) { + Assert.isNotNull(name); + Assert.isNotNull(parameterTypes); + // + StringBuilder buffer = new StringBuilder(); + buffer.append(name); + appendParameterTypes(buffer, parameterTypes); + return buffer.toString(); + } + + private static final ClassMap<Map<String, Method>> m_getMethodBySignature = ClassMap.create(); + + /** + * Returns the {@link Method} defined in {@link Class}. This method can have any visibility, i.e. + * we can find even protected/private methods. Can return <code>null</code> if no method with + * given signature found. + * + * @param clazz + * the {@link Class} to get method from it, or its superclass. + * @param signature + * the signature of method in same format as {@link #getMethodSignature(Method)}. + * + * @return the {@link Method} for given signature, or <code>null</code> if no such method found. + */ + public static Method getMethodBySignature(Class<?> clazz, String signature) { + Assert.isNotNull(clazz); + Assert.isNotNull(signature); + // prepare cache + Map<String, Method> cache = m_getMethodBySignature.get(clazz); + if (cache == null) { + cache = getMethods(clazz); + m_getMethodBySignature.put(clazz, cache); + } + // use cache + return cache.get(signature); + } + + /** + * @return the {@link Object} result of invoking method with given signature. + */ + public static Object invokeMethod(Object object, String signature, Object... arguments) + throws Exception { + Assert.isNotNull(object); + Assert.isNotNull(arguments); + // prepare class/object + Class<?> refClass = getRefClass(object); + Object refObject = getRefObject(object); + // prepare method + Method method = getMethodBySignature(refClass, signature); + Assert.isNotNull(method, "Can not find method " + signature + " in " + refClass); + // do invoke + try { + return method.invoke(refObject, arguments); + } catch (InvocationTargetException e) { + throw propagate(e.getCause()); + } + } + + /** + * Invokes method by name and parameter types. + * + * @param object + * the object to call, may be {@link Class} for invoking static method. + * @param name + * the name of method. + * @param parameterTypes + * the types of parameters. + * @param arguments + * the values of argument for invocation. + * + * @return the {@link Object} result of invoking method. + */ + public static Object invokeMethod2(Object object, + String name, + Class<?>[] parameterTypes, + Object[] arguments) throws Exception { + Assert.equals(parameterTypes.length, arguments.length); + String signature = getMethodSignature(name, parameterTypes); + return invokeMethod(object, signature, arguments); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Utils + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Class} of given {@link Object} or casted object, if it is {@link Class} + * itself. + */ + private static Class<?> getRefClass(Object object) { + return object instanceof Class<?> ? (Class<?>) object : object.getClass(); + } + + /** + * @return the {@link Object} that should be used as argument for {@link Field#get(Object)} and + * {@link Method#invoke(Object, Object[])}. + */ + private static Object getRefObject(Object object) { + return object instanceof Class<?> ? null : object; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Throwable propagation + // + //////////////////////////////////////////////////////////////////////////// + /** + * Helper class used in {@link #propagate(Throwable)}. + */ + private static class ExceptionThrower { + private static Throwable throwable; + + private ExceptionThrower() throws Throwable { + if (System.getProperty("wbp.ReflectionUtils.propagate().InstantiationException") != null) { + throw new InstantiationException(); + } + if (System.getProperty("wbp.ReflectionUtils.propagate().IllegalAccessException") != null) { + throw new IllegalAccessException(); + } + throw throwable; + } + + public static synchronized void spit(Throwable t) { + if (System.getProperty("wbp.ReflectionUtils.propagate().dontThrow") == null) { + ExceptionThrower.throwable = t; + try { + ExceptionThrower.class.newInstance(); + } catch (InstantiationException e) { + } catch (IllegalAccessException e) { + } finally { + ExceptionThrower.throwable = null; + } + } + } + } + + /** + * Propagates {@code throwable} as-is without any wrapping. This is trick. + * + * @return nothing will ever be returned; this return type is only for your convenience, to use + * this method in "throw" statement. + */ + public static RuntimeException propagate(Throwable throwable) { + if (System.getProperty("wbp.ReflectionUtils.propagate().forceReturn") == null) { + ExceptionThrower.spit(throwable); + } + return null; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java new file mode 100644 index 0000000..f7cc09d --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/DrawUtils.java @@ -0,0 +1,337 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import com.google.common.io.Closeables; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Display; +import org.eclipse.wb.draw2d.IColorConstants; + +import java.io.InputStream; +import java.net.URL; + +/** + * Utilities for drawing on {@link GC}. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class DrawUtils { + private static final String DOTS = "..."; + + //////////////////////////////////////////////////////////////////////////// + // + // Drawing + // + //////////////////////////////////////////////////////////////////////////// + /** + * Draws given text clipped horizontally and centered vertically. + */ + public static final void drawStringCV(GC gc, String text, int x, int y, int width, int height) { + Rectangle oldClipping = gc.getClipping(); + try { + gc.setClipping(new Rectangle(x, y, width, height)); + // + int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2; + gc.drawString(clipString(gc, text, width), x, textStartY, true); + } finally { + gc.setClipping(oldClipping); + } + } + + /** + * Draws given text clipped or centered horizontally and centered vertically. + */ + public static final void drawStringCHCV(GC gc, String text, int x, int y, int width, int height) { + int textStartY = y + (height - gc.getFontMetrics().getHeight()) / 2; + Point textSize = gc.stringExtent(text); + // + if (textSize.x > width) { + gc.drawString(clipString(gc, text, width), x, textStartY); + } else { + gc.drawString(text, x + (width - textSize.x) / 2, textStartY); + } + } + + /** + * Draws image at given <code>x</code> and centered vertically. + */ + public static final void drawImageCV(GC gc, Image image, int x, int y, int height) { + if (image != null) { + Rectangle imageBounds = image.getBounds(); + gc.drawImage(image, x, y + (height - imageBounds.height) / 2); + } + } + + /** + * Draws image at given <code>x</code> and centered vertically. + */ + public static final void drawImageCHCV(GC gc, Image image, int x, int y, int width, int height) { + if (image != null) { + Rectangle imageBounds = image.getBounds(); + int centerX = (width - imageBounds.width) / 2; + int centerY = y + (height - imageBounds.height) / 2; + gc.drawImage(image, x + centerX, centerY); + } + } + + /** + * Draws {@link Image} on {@link GC} centered in given {@link Rectangle}. If {@link Image} is + * bigger that {@link Rectangle}, {@link Image} will be scaled down as needed with keeping + * proportions. + */ + public static void drawScaledImage(GC gc, Image image, Rectangle targetRectangle) { + int imageWidth = image.getBounds().width; + int imageHeight = image.getBounds().height; + // prepare scaled image size + int newImageWidth; + int newImageHeight; + if (imageWidth <= targetRectangle.width && imageHeight <= targetRectangle.height) { + newImageWidth = imageWidth; + newImageHeight = imageHeight; + } else { + // prepare minimal scale + double k; + { + double k_w = targetRectangle.width / (double) imageWidth; + double k_h = targetRectangle.height / (double) imageHeight; + k = Math.min(k_w, k_h); + } + // calculate scaled image size + newImageWidth = (int) (imageWidth * k); + newImageHeight = (int) (imageHeight * k); + } + // draw image centered in target rectangle + int destX = targetRectangle.x + (targetRectangle.width - newImageWidth) / 2; + int destY = targetRectangle.y + (targetRectangle.height - newImageHeight) / 2; + gc.drawImage(image, 0, 0, imageWidth, imageHeight, destX, destY, newImageWidth, newImageHeight); + } + + /** + * @return the string clipped to have width less than given. Clipping is done as trailing "...". + */ + public static String clipString(GC gc, String text, int width) { + if (width <= 0) { + return ""; + } + // check if text already fits in given width + if (gc.stringExtent(text).x <= width) { + return text; + } + // use average count of characters as base + int count = Math.min(width / gc.getFontMetrics().getAverageCharWidth(), text.length()); + if (gc.stringExtent(text.substring(0, count) + DOTS).x > width) { + while (count > 0 && gc.stringExtent(text.substring(0, count) + DOTS).x > width) { + count--; + } + } else { + while (count < text.length() - 1 + && gc.stringExtent(text.substring(0, count + 1) + DOTS).x < width) { + count++; + } + } + return text.substring(0, count) + DOTS; + } + + /** + * Draws {@link String} in rectangle, wraps at any character (not by words). + */ + public static void drawTextWrap(GC gc, String text, int x, int y, int width, int height) { + int y_ = y; + int x_ = x; + int lineHeight = 0; + for (int i = 0; i < text.length(); i++) { + String c = text.substring(i, i + 1); + Point extent = gc.stringExtent(c); + if (x_ + extent.x > x + width) { + y_ += lineHeight; + if (y_ > y + height) { + return; + } + x_ = x; + } + gc.drawText(c, x_, y_); + x_ += extent.x; + lineHeight = Math.max(lineHeight, extent.y); + } + } + + /** + * Draws 3D highlight rectangle. + */ + public static void drawHighlightRectangle(GC gc, int x, int y, int width, int height) { + int right = x + width - 1; + int bottom = y + height - 1; + // + Color oldForeground = gc.getForeground(); + try { + gc.setForeground(IColorConstants.buttonLightest); + gc.drawLine(x, y, right, y); + gc.drawLine(x, y, x, bottom); + // + gc.setForeground(IColorConstants.buttonDarker); + gc.drawLine(right, y, right, bottom); + gc.drawLine(x, bottom, right, bottom); + } finally { + gc.setForeground(oldForeground); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Images + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the {@link Image} loaded relative to given {@link Class}. + */ + public static Image loadImage(Class<?> clazz, String path) { + try { + URL resource = clazz.getResource(path); + if (resource != null) { + InputStream stream = resource.openStream(); + try { + return new Image(null, stream); + } finally { + Closeables.closeQuietly(stream); + } + } + } catch (Throwable e) { + } + return null; + } + + /** + * @return the thumbnail {@link Image} of required size for given "big" {@link Image}, centered or + * scaled down. + */ + public static Image getThubmnail(Image image, + int minWidth, + int minHeight, + int maxWidth, + int maxHeight) { + Rectangle imageBounds = image.getBounds(); + int imageWidth = imageBounds.width; + int imageHeight = imageBounds.height; + if (imageWidth < minWidth && imageHeight < minHeight) { + // create "thumbnail" Image with required size + Image thumbnail = new Image(null, minWidth, minHeight); + GC gc = new GC(thumbnail); + try { + drawImageCHCV(gc, image, 0, 0, minWidth, minHeight); + } finally { + gc.dispose(); + } + // recreate "thumbnail" Image with transparent pixel + try { + ImageData thumbnailData = thumbnail.getImageData(); + thumbnailData.transparentPixel = thumbnailData.getPixel(0, 0); + return new Image(null, thumbnailData); + } finally { + thumbnail.dispose(); + } + } else if (imageWidth <= maxWidth && imageHeight <= maxHeight) { + return new Image(null, image, SWT.IMAGE_COPY); + } else { + double kX = (double) maxWidth / imageWidth; + double kY = (double) maxHeight / imageHeight; + double k = Math.max(kX, kY); + int dWidth = (int) (imageWidth * k); + int dHeight = (int) (imageHeight * k); + ImageData scaledImageData = image.getImageData().scaledTo(dWidth, dHeight); + return new Image(null, scaledImageData); + } + } + + //////////////////////////////////////////////////////////////////////////// + // + // Colors + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return new {@link Color} based on given {@link Color} and shifted on given value to make it + * darker or lighter. + */ + public static Color getShiftedColor(Color color, int delta) { + int r = Math.max(0, Math.min(color.getRed() + delta, 255)); + int g = Math.max(0, Math.min(color.getGreen() + delta, 255)); + int b = Math.max(0, Math.min(color.getBlue() + delta, 255)); + return new Color(color.getDevice(), r, g, b); + } + + /** + * @return <code>true</code> if the given <code>color</code> is dark. + */ + public static boolean isDarkColor(Color c) { + int value = + (int) Math.sqrt(c.getRed() + * c.getRed() + * .241 + + c.getGreen() + * c.getGreen() + * .691 + + c.getBlue() + * c.getBlue() + * .068); + return value < 130; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Fonts + // + //////////////////////////////////////////////////////////////////////////// + /** + * @return the bold version of given {@link Font}. + */ + public static Font getBoldFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the italic version of given {@link Font}. + */ + public static Font getBoldItalicFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.BOLD | SWT.ITALIC); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the italic version of given {@link Font}. + */ + public static Font getItalicFont(Font baseFont) { + FontData[] boldData = getModifiedFontData(baseFont, SWT.ITALIC); + return new Font(Display.getCurrent(), boldData); + } + + /** + * @return the array of {@link FontData} with the specified style. + */ + private static FontData[] getModifiedFontData(Font baseFont, int style) { + FontData[] baseData = baseFont.getFontData(); + FontData[] styleData = new FontData[baseData.length]; + for (int i = 0; i < styleData.length; i++) { + FontData base = baseData[i]; + styleData[i] = new FontData(base.getName(), base.getHeight(), base.getStyle() | style); + } + return styleData; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java new file mode 100644 index 0000000..4ccda40 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridDataFactory.java @@ -0,0 +1,541 @@ +/******************************************************************************* + * Copyright (c) 2005, 2011 IBM Corporation and others. + * 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: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Control; + +/** + * This class provides a convienient shorthand for creating and initializing GridData. This offers + * several benefits over creating GridData normal way: + * + * <ul> + * <li>The same factory can be used many times to create several GridData instances</li> + * <li>The setters on GridDataFactory all return "this", allowing them to be chained</li> + * <li>GridDataFactory uses vector setters (it accepts Points), making it easy to set X and Y values + * together</li> + * </ul> + * + * <p> + * GridDataFactory instances are created using one of the static methods on this class. + * </p> + * + * <p> + * Example usage: + * </p> + * <code> + * + * //////////////////////////////////////////////////////////// + * // Example 1: Typical grid data for a non-wrapping label + * + * // GridDataFactory version + * GridDataFactory.fillDefaults().applyTo(myLabel); + * + * // Equivalent SWT version + * GridData labelData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_FILL); + * myLabel.setLayoutData(labelData); + * + * /////////////////////////////////////////////////////////// + * // Example 2: Typical grid data for a wrapping label + * + * // GridDataFactory version + * GridDataFactory.fillDefaults() + * .align(SWT.FILL, SWT.CENTER) + * .hint(150, SWT.DEFAULT) + * .grab(true, false) + * .applyTo(wrappingLabel); + * + * // Equivalent SWT version + * GridData wrappingLabelData = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_CENTER); + * wrappingLabelData.minimumWidth = 1; + * wrappingLabelData.widthHint = 150; + * wrappingLabel.setLayoutData(wrappingLabelData); + * + * ////////////////////////////////////////////////////////////// + * // Example 3: Typical grid data for a scrollable control (a list box, tree, table, etc.) + * + * // GridDataFactory version + * GridDataFactory.fillDefaults().grab(true, true).hint(150, 150).applyTo(listBox); + * + * // Equivalent SWT version + * GridData listBoxData = new GridData(GridData.FILL_BOTH); + * listBoxData.widthHint = 150; + * listBoxData.heightHint = 150; + * listBoxData.minimumWidth = 1; + * listBoxData.minimumHeight = 1; + * listBox.setLayoutData(listBoxData); + * + * ///////////////////////////////////////////////////////////// + * // Example 4: Typical grid data for a button + * + * // GridDataFactory version + * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize); + * GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).hint(hint).applyTo(button); + * + * // Equivalent SWT version + * Point preferredSize = button.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + * Point hint = Geometry.max(LayoutConstants.getMinButtonSize(), preferredSize); + * GridData buttonData = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.VERTICAL_ALIGN_CENTER); + * buttonData.widthHint = hint.x; + * buttonData.heightHint = hint.y; + * button.setLayoutData(buttonData); + * </code> + * + * <p> + * IMPORTANT: WHEN ASSIGNING LAYOUT DATA TO A CONTROL, BE SURE TO USE + * gridDataFactory.applyTo(control) AND NEVER control.setLayoutData(gridDataFactory). + * </p> + * + * @since 3.2 + */ +public final class GridDataFactory { + private final Control m_control; + private final PixelConverter m_pixelConverter; + private final GridData m_data; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private GridDataFactory(Control control, GridData gridData) { + m_control = control; + m_pixelConverter = new PixelConverter(m_control); + // + m_data = gridData; + if (m_control.getLayoutData() != m_data) { + m_control.setLayoutData(m_data); + } + } + + /** + * Creates new {@link GridDataFactory} with new {@link GridData}. + */ + public static GridDataFactory create(Control control) { + return new GridDataFactory(control, new GridData()); + } + + /** + * Creates new {@link GridDataFactory} for modifying {@link GridData} already installed in + * control. + */ + public static GridDataFactory modify(Control control) { + GridData gridData; + { + Object existingLayoutData = control.getLayoutData(); + if (existingLayoutData instanceof GridData) { + gridData = (GridData) existingLayoutData; + } else { + gridData = new GridData(); + } + } + return new GridDataFactory(control, gridData); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Span + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param hSpan + * number of columns spanned by the control + * @param vSpan + * number of rows spanned by the control + * @return this + */ + public GridDataFactory span(int hSpan, int vSpan) { + m_data.horizontalSpan = hSpan; + m_data.verticalSpan = vSpan; + return this; + } + + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param hSpan + * number of columns spanned by the control + * @return this + */ + public GridDataFactory spanH(int hSpan) { + m_data.horizontalSpan = hSpan; + return this; + } + + /** + * Sets the GridData span. The span controls how many cells are filled by the control. + * + * @param vSpan + * number of rows spanned by the control + * @return this + */ + public GridDataFactory spanV(int vSpan) { + m_data.verticalSpan = vSpan; + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Hint + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the width and height hints. The width and height hints override the control's preferred + * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used. + * + * @param xHint + * horizontal hint (pixels), or SWT.DEFAULT to use the control's preferred size + * @param yHint + * vertical hint (pixels), or SWT.DEFAULT to use the control's preferred size + * @return this + */ + public GridDataFactory hint(int xHint, int yHint) { + m_data.widthHint = xHint; + m_data.heightHint = yHint; + return this; + } + + /** + * Sets hint in chars. + */ + public GridDataFactory hintC(int xHintInChars, int yHintInChars) { + hintHC(xHintInChars); + hintVC(yHintInChars); + return this; + } + + /** + * Sets the width hint. + * + * @return this + */ + public GridDataFactory hintH(int xHint) { + m_data.widthHint = xHint; + return this; + } + + /** + * Sets the width hint to the minimum of current hint and given <code>otherHint</code>. + * + * @return this + */ + public GridDataFactory hintHMin(int otherHint) { + m_data.widthHint = Math.min(m_data.widthHint, otherHint); + return this; + } + + /** + * Sets the width hint in chars. + * + * @return this + */ + public GridDataFactory hintHC(int hintInChars) { + return hintH(m_pixelConverter.convertWidthInCharsToPixels(hintInChars)); + } + + /** + * Sets the width hint. + * + * @return this + */ + public GridDataFactory hintHU(int hintInDLU) { + return hintH(m_pixelConverter.convertHorizontalDLUsToPixels(hintInDLU)); + } + + /** + * Sets the height hint. + * + * @return this + */ + public GridDataFactory hintV(int yHint) { + m_data.heightHint = yHint; + return this; + } + + /** + * Sets the height hint in chars. + * + * @return this + */ + public GridDataFactory hintVC(int hintInChars) { + return hintV(m_pixelConverter.convertHeightInCharsToPixels(hintInChars)); + } + + /** + * Increments horizontal hint on given value. + * + * @return this + */ + public GridDataFactory hintHAdd(int increment) { + return hintV(m_data.widthHint + increment); + } + + /** + * Increments vertical hint on given value. + * + * @return this + */ + public GridDataFactory hintVAdd(int increment) { + return hintV(m_data.heightHint + increment); + } + + /** + * Sets the width and height hints. The width and height hints override the control's preferred + * size. If either hint is set to SWT.DEFAULT, the control's preferred size is used. + * + * @param hint + * size (pixels) to be used instead of the control's preferred size. If the x or y values + * are set to SWT.DEFAULT, the control's computeSize() method will be used to obtain that + * dimension of the preferred size. + * @return this + */ + public GridDataFactory hint(Point hint) { + m_data.widthHint = hint.x; + m_data.heightHint = hint.y; + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Minimum size + // + //////////////////////////////////////////////////////////////////////////// + public GridDataFactory minH(int minimumWidth) { + m_data.minimumWidth = minimumWidth; + return this; + } + + public GridDataFactory minHC(int widthInChars) { + return minH(m_pixelConverter.convertWidthInCharsToPixels(widthInChars)); + } + + public GridDataFactory minV(int minimumHeight) { + m_data.minimumHeight = minimumHeight; + return this; + } + + public GridDataFactory minVC(int heightInChars) { + return minV(m_pixelConverter.convertHeightInCharsToPixels(heightInChars)); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Alignment + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the alignment of the control within its cell. + * + * @param hAlign + * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @param vAlign + * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory align(int hAlign, int vAlign) { + m_data.horizontalAlignment = hAlign; + m_data.verticalAlignment = vAlign; + return this; + } + + /** + * Sets the horizontal and vertical alignment to GridData.FILL. + */ + public GridDataFactory fill() { + return align(GridData.FILL, GridData.FILL); + } + + /** + * Sets the horizontal alignment of the control within its cell. + * + * @param hAlign + * horizontal alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory alignH(int hAlign) { + m_data.horizontalAlignment = hAlign; + return this; + } + + /** + * Sets the horizontal alignment of the control to GridData.BEGINNING + * + * @return this + */ + public GridDataFactory alignHL() { + return alignH(GridData.BEGINNING); + } + + /** + * Sets the horizontal alignment of the control to GridData.CENTER + * + * @return this + */ + public GridDataFactory alignHC() { + return alignH(GridData.CENTER); + } + + /** + * Sets the horizontal alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory alignHF() { + return alignH(GridData.FILL); + } + + /** + * Sets the horizontal alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory fillH() { + return alignHF(); + } + + /** + * Sets the horizontal alignment of the control to GridData.END + * + * @return this + */ + public GridDataFactory alignHR() { + return alignH(GridData.END); + } + + /** + * Sets the vertical alignment of the control within its cell. + * + * @param vAlign + * vertical alignment. One of SWT.BEGINNING, SWT.CENTER, SWT.END, or SWT.FILL. + * @return this + */ + public GridDataFactory alignV(int vAlign) { + m_data.verticalAlignment = vAlign; + return this; + } + + /** + * Sets the vertical alignment of the control to GridData.BEGINNING + * + * @return this + */ + public GridDataFactory alignVT() { + return alignV(GridData.BEGINNING); + } + + /** + * Sets the vertical alignment of the control to GridData.CENTER + * + * @return this + */ + public GridDataFactory alignVM() { + return alignV(GridData.CENTER); + } + + /** + * Sets the vertical alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory alignVF() { + return alignV(GridData.FILL); + } + + /** + * Sets the vertical alignment of the control to GridData.FILL + * + * @return this + */ + public GridDataFactory fillV() { + return alignVF(); + } + + /** + * Sets the vertical alignment of the control to GridData.END + * + * @return this + */ + public GridDataFactory alignVB() { + return alignV(GridData.END); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Indent + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the indent of the control within the cell in pixels. + */ + public GridDataFactory indentH(int hIndent) { + m_data.horizontalIndent = hIndent; + return this; + } + + /** + * Sets the indent of the control within the cell in characters. + */ + public GridDataFactory indentHC(int hIndent) { + m_data.horizontalIndent = m_pixelConverter.convertWidthInCharsToPixels(hIndent); + return this; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Grab + // + //////////////////////////////////////////////////////////////////////////// + /** + * Determines whether extra horizontal or vertical space should be allocated to this control's + * column when the layout resizes. If any control in the column is set to grab horizontal then the + * whole column will grab horizontal space. If any control in the row is set to grab vertical then + * the whole row will grab vertical space. + * + * @param horizontal + * true if the control's column should grow horizontally + * @param vertical + * true if the control's row should grow vertically + * @return this + */ + public GridDataFactory grab(boolean horizontal, boolean vertical) { + m_data.grabExcessHorizontalSpace = horizontal; + m_data.grabExcessVerticalSpace = vertical; + return this; + } + + public GridDataFactory grabH() { + m_data.grabExcessHorizontalSpace = true; + return this; + } + + public GridDataFactory grabV() { + m_data.grabExcessVerticalSpace = true; + return this; + } + + public GridDataFactory grab() { + return grab(true, true); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Exclude + // + //////////////////////////////////////////////////////////////////////////// + public GridDataFactory exclude(boolean value) { + m_data.exclude = value; + return this; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java new file mode 100644 index 0000000..f6dc58e --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/GridLayoutFactory.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Layout; + +/** + * GridLayoutFactory provides a convenient shorthand for creating and initializing GridLayout. + * + * @author scheglov_ke + */ +public final class GridLayoutFactory { + private final GridLayout m_layout; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + private GridLayoutFactory(Composite composite, GridLayout layout) { + m_layout = layout; + composite.setLayout(m_layout); + } + + public static GridLayoutFactory create(Composite composite) { + return new GridLayoutFactory(composite, new GridLayout()); + } + + public static GridLayoutFactory modify(Composite composite) { + Layout layout = composite.getLayout(); + if (layout instanceof GridLayout) { + return new GridLayoutFactory(composite, (GridLayout) layout); + } + return create(composite); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Access + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets number of columns in {@link GridLayout}. + */ + public GridLayoutFactory columns(int numColumns) { + m_layout.numColumns = numColumns; + return this; + } + + /** + * Specifies whether all columns in the layout will be forced to have the same width. + */ + public GridLayoutFactory equalColumns() { + m_layout.makeColumnsEqualWidth = true; + return this; + } + + /** + * Sets the horizontal margins. + */ + public GridLayoutFactory marginsH(int margins) { + m_layout.marginWidth = margins; + return this; + } + + /** + * Sets the vertical margins. + */ + public GridLayoutFactory marginsV(int margins) { + m_layout.marginHeight = margins; + return this; + } + + /** + * Sets the horizontal/vertical margins. + */ + public GridLayoutFactory margins(int margins) { + m_layout.marginWidth = m_layout.marginHeight = margins; + return this; + } + + /** + * Sets zero horizontal and vertical margins. + */ + public GridLayoutFactory noMargins() { + m_layout.marginWidth = m_layout.marginHeight = 0; + return this; + } + + /** + * Sets zero horizontal and vertical spacing. + */ + public GridLayoutFactory noSpacing() { + m_layout.horizontalSpacing = m_layout.verticalSpacing = 0; + return this; + } + + /** + * Sets horizontal spacing. + */ + public GridLayoutFactory spacingH(int spacing) { + m_layout.horizontalSpacing = spacing; + return this; + } + + /** + * Sets vertical spacing. + */ + public GridLayoutFactory spacingV(int spacing) { + m_layout.verticalSpacing = spacing; + return this; + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java new file mode 100644 index 0000000..d5fc39f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/ImageImageDescriptor.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * Implementation of {@link ImageDescriptor} for {@link Image} instance. + * + * @author scheglov_ke + * @coverage core.ui + */ +public final class ImageImageDescriptor extends ImageDescriptor { + private final Image m_Image; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ImageImageDescriptor(Image image) { + m_Image = image; + } + + //////////////////////////////////////////////////////////////////////////// + // + // ImageDescriptor + // + //////////////////////////////////////////////////////////////////////////// + @Override + public ImageData getImageData() { + return m_Image == null ? null : m_Image.getImageData(); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java new file mode 100644 index 0000000..1e3699f --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/PixelConverter.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.GC; +import org.eclipse.swt.widgets.Control; + +/** + * Helper class for converting DLU and char size into pixels. + * + * Based on code from JDT UI. + * + * @author scheglov_ke + */ +public class PixelConverter { + private final FontMetrics fFontMetrics; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructors + // + //////////////////////////////////////////////////////////////////////////// + public PixelConverter(Control control) { + GC gc = new GC(control); + gc.setFont(control.getFont()); + fFontMetrics = gc.getFontMetrics(); + gc.dispose(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Conversions + // + //////////////////////////////////////////////////////////////////////////// + /** + * see org.eclipse.jface.dialogs.DialogPage#convertHeightInCharsToPixels(int) + */ + public int convertHeightInCharsToPixels(int chars) { + return Dialog.convertHeightInCharsToPixels(fFontMetrics, chars); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertHorizontalDLUsToPixels(int) + */ + public int convertHorizontalDLUsToPixels(int dlus) { + return Dialog.convertHorizontalDLUsToPixels(fFontMetrics, dlus); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertVerticalDLUsToPixels(int) + */ + public int convertVerticalDLUsToPixels(int dlus) { + return Dialog.convertVerticalDLUsToPixels(fFontMetrics, dlus); + } + + /** + * see org.eclipse.jface.dialogs.DialogPage#convertWidthInCharsToPixels(int) + */ + public int convertWidthInCharsToPixels(int chars) { + return Dialog.convertWidthInCharsToPixels(fFontMetrics, chars); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java new file mode 100644 index 0000000..126efba --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/UiUtils.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui; + +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.swt.widgets.Shell; + +/** + * Utilities for UI. + * + * @author scheglov_ke + */ +public class UiUtils { + //////////////////////////////////////////////////////////////////////////// + // + // Message dialogs + // + //////////////////////////////////////////////////////////////////////////// + /** + * Opens standard warning dialog. + */ + public static void openWarning(Shell parent, String title, String message) { + MessageDialog dialog = + new MessageDialog(parent, + title, + null, + message, + MessageDialog.WARNING, + new String[]{IDialogConstants.OK_LABEL}, + 0); + dialog.open(); + } +}
\ No newline at end of file diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java new file mode 100644 index 0000000..307c2f6 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/ResizableDialog.java @@ -0,0 +1,221 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui.dialogs; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * {@link Dialog} that remembers location/size between usage sessions. + * + * @author scheglov_ke + * @coverage core.ui + */ +public abstract class ResizableDialog extends Dialog { + /** + * Key for accessing {@link Dialog} from its {@link Shell}. + */ + public static final String KEY_DIALOG = "KEY_DIALOG"; + //////////////////////////////////////////////////////////////////////////// + // + // Internal constants + // + //////////////////////////////////////////////////////////////////////////// + private static final String X = "x"; + private static final String Y = "y"; + private static final String WIDTH = "width"; + private static final String HEIGHT = "height"; + //////////////////////////////////////////////////////////////////////////// + // + // Instance fields + // + //////////////////////////////////////////////////////////////////////////// + private final AbstractUIPlugin m_plugin; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public ResizableDialog(Shell parentShell, AbstractUIPlugin plugin) { + super(parentShell); + m_plugin = plugin; + setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Size + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected Point getInitialSize() { + // track the current dialog bounds + installDialogBoundsTracker(); + // answer the size from the previous incarnation + Point defaultSize = getDefaultSize(); + if ((getShellStyle() & SWT.RESIZE) != 0) { + Rectangle oldBounds = loadBounds(); + if (oldBounds != null) { + Rectangle displayBounds = getShell().getDisplay().getBounds(); + int width = Math.min(displayBounds.width, Math.max(oldBounds.width, defaultSize.x)); + int height = Math.min(displayBounds.height, Math.max(oldBounds.height, defaultSize.y)); + return new Point(width, height); + } + } + // use default size + return defaultSize; + } + + /** + * @return the default size of dialog. + */ + protected Point getDefaultSize() { + return super.getInitialSize(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Location + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected Point getInitialLocation(Point initialSize) { + Rectangle windowBounds; + { + Shell windowShell = m_plugin.getWorkbench().getActiveWorkbenchWindow().getShell(); + windowBounds = windowShell.getBounds(); + } + // answer the location from the previous incarnation + Rectangle bounds = loadBounds(); + if (bounds != null) { + int x = bounds.x; + int y = bounds.y; + int maxX = windowBounds.x + windowBounds.width - initialSize.x; + int maxY = windowBounds.y + windowBounds.height - initialSize.y; + if (x > maxX) { + x = maxX; + } + if (y > maxY) { + y = maxY; + } + if (x < windowBounds.x) { + x = windowBounds.x; + } + if (y < windowBounds.y) { + y = windowBounds.y; + } + return new Point(x, y); + } + // default location - centered on workbench window + int x = windowBounds.x + (windowBounds.width - initialSize.x) / 2; + int y = windowBounds.y + (windowBounds.height - initialSize.y) / 2; + return new Point(x, y); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Bounds + // + //////////////////////////////////////////////////////////////////////////// + /** + * Loads bounds from {@link IDialogSettings}. + */ + private Rectangle loadBounds() { + IDialogSettings settings = getDialogSettings(); + try { + return new Rectangle(settings.getInt(X), + settings.getInt(Y), + settings.getInt(WIDTH), + settings.getInt(HEIGHT)); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Saves bounds to {@link IDialogSettings}. + */ + private void saveBounds(Rectangle bounds) { + IDialogSettings settings = getDialogSettings(); + settings.put(X, bounds.x); + settings.put(Y, bounds.y); + settings.put(WIDTH, bounds.width); + settings.put(HEIGHT, bounds.height); + } + + /** + * @return the {@link IDialogSettings} for this dialog with this type. + */ + protected IDialogSettings getDialogSettings() { + IDialogSettings settings = m_plugin.getDialogSettings(); + String sectionName = getDialogSettingsSectionName(); + if (settings.getSection(sectionName) == null) { + return settings.addNewSection(sectionName); + } + return settings.getSection(sectionName); + } + + /** + * @return the name of section for dialog specific bounds. By default uses name of {@link Class}, + * but if same dialog is used for displaying different content, then may be overridden. + */ + protected String getDialogSettingsSectionName() { + return getClass().getName(); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Size tracking + // + //////////////////////////////////////////////////////////////////////////// + protected Rectangle cachedBounds; + + private void installDialogBoundsTracker() { + getShell().addControlListener(new ControlListener() { + public void controlMoved(ControlEvent e) { + cachedBounds = getShell().getBounds(); + } + + public void controlResized(ControlEvent e) { + cachedBounds = getShell().getBounds(); + } + }); + } + + @Override + public boolean close() { + boolean shellMaximized = getShell().getMaximized(); + boolean closed = super.close(); + if (closed && !shellMaximized && cachedBounds != null) { + saveBounds(cachedBounds); + } + return closed; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Shell + // + //////////////////////////////////////////////////////////////////////////// + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setData(KEY_DIALOG, this); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java new file mode 100644 index 0000000..745bcc3 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/StringsDialog.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui.dialogs; + +import com.google.common.base.Joiner; +import com.google.common.collect.Lists; + +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.wb.internal.core.utils.execution.ExecutionUtils; +import org.eclipse.wb.internal.core.utils.execution.RunnableObjectEx; + +import java.io.BufferedReader; +import java.io.StringReader; +import java.util.List; + +/** + * The dialog for editing array of {@link String}'s. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class StringsDialog extends TextDialog { + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public StringsDialog(Shell parentShell, + AbstractUIPlugin plugin, + String titleText, + String headerText, + String footerText) { + super(parentShell, plugin, titleText, headerText, footerText); + } + + //////////////////////////////////////////////////////////////////////////// + // + // Items + // + //////////////////////////////////////////////////////////////////////////// + /** + * Sets the items to edit. + */ + public void setItems(String[] items) { + setText(Joiner.on('\n').join(items)); + } + + /** + * @return the edited items. + */ + public String[] getItems() { + return ExecutionUtils.runObjectLog(new RunnableObjectEx<String[]>() { + @Override + public String[] runObject() throws Exception { + List<String> strings = Lists.newArrayList(); + BufferedReader br = new BufferedReader(new StringReader(getText())); + while (true) { + String s = br.readLine(); + if (s == null) { + break; + } + strings.add(s); + } + return strings.toArray(new String[strings.size()]); + } + }, new String[0]); + } +} diff --git a/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java new file mode 100644 index 0000000..320fa32 --- /dev/null +++ b/propertysheet/src/org/eclipse/wb/internal/core/utils/ui/dialogs/TextDialog.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2011 Google, Inc. + * 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: + * Google, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.wb.internal.core.utils.ui.dialogs; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.eclipse.wb.internal.core.utils.ui.GridDataFactory; +import org.eclipse.wb.internal.core.utils.ui.GridLayoutFactory; + +/** + * The dialog for editing multiline text. + * + * @author scheglov_ke + * @coverage core.ui + */ +public class TextDialog extends ResizableDialog { + private final String m_titleText; + private final String m_headerText; + private final String m_footerText; + + //////////////////////////////////////////////////////////////////////////// + // + // Constructor + // + //////////////////////////////////////////////////////////////////////////// + public TextDialog(Shell parentShell, + AbstractUIPlugin plugin, + String titleText, + String headerText, + String footerText) { + super(parentShell, plugin); + m_titleText = titleText; + m_headerText = headerText; + m_footerText = footerText; + } + + //////////////////////////////////////////////////////////////////////////// + // + // Text + // + //////////////////////////////////////////////////////////////////////////// + private String m_text; + + /** + * Sets the text to edit. + */ + public final void setText(String text) { + m_text = text; + } + + /** + * @return the edited text. + */ + public final String getText() { + return m_text; + } + + //////////////////////////////////////////////////////////////////////////// + // + // GUI + // + //////////////////////////////////////////////////////////////////////////// + protected Text m_textWidget; + + @Override + protected Control createDialogArea(Composite parent) { + Composite area = (Composite) super.createDialogArea(parent); + GridLayoutFactory.create(area); + // header + new Label(area, SWT.NONE).setText(m_headerText); + // Text widget + { + m_textWidget = new Text(area, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); + GridDataFactory.create(m_textWidget).grab().fill().hintVC(10); + m_textWidget.setText(m_text); + // handle Ctrl+Enter as OK + m_textWidget.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(KeyEvent e) { + if (e.stateMask == SWT.CTRL && e.keyCode == SWT.CR) { + okPressed(); + } + } + }); + } + // footer + new Label(area, SWT.NONE).setText(m_footerText); + // + return area; + } + + @Override + protected void configureShell(Shell newShell) { + super.configureShell(newShell); + newShell.setText(m_titleText); + } + + @Override + protected void okPressed() { + m_text = m_textWidget.getText(); + super.okPressed(); + } +} |