blob: b813d2737f5e543e95d7f7da77a9803c995da407 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2014 Christian Pontesegger 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:
* Christian Pontesegger - initial API and implementation
*******************************************************************************/
package org.eclipse.ease.modules.platform;
import java.io.IOException;
import org.eclipse.core.resources.IFile;
import org.eclipse.ease.modules.AbstractScriptModule;
import org.eclipse.ease.modules.ScriptParameter;
import org.eclipse.ease.modules.WrapToScript;
import org.eclipse.ease.tools.ResourceTools;
import org.eclipse.ease.tools.RunnableWithResult;
import org.eclipse.ease.ui.console.ScriptConsole;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.dnd.Clipboard;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.ISelectionService;
import org.eclipse.ui.IViewPart;
import org.eclipse.ui.IViewReference;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPart;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.actions.ActionFactory;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.views.IViewDescriptor;
import org.eclipse.ui.views.IViewRegistry;
/**
* Methods providing access to UI components. Allows to show dialogs, execute code in the UI thread, access views and editors.
*/
public class UIModule extends AbstractScriptModule {
/** Module identifier. */
public static final String MODULE_ID = "/System/UI";
/**
* Run code in UI thread. Needed to interact with SWT elements. Might not be supported by some engines. Might be disabled by the user. Code will be executed
* synchronously and stall UI updates while executed.
*
* @param code
* code/object to execute
* @return execution result
*/
@WrapToScript
public Object executeUI(final Object code) {
return getEnvironment().getScriptEngine().injectUI(code);
}
/**
* Returns <code>true</code> when executed in the UI thread.
*
* @return <code>true</code> in UI thread
*/
@WrapToScript
public static boolean isUIThread() {
return Thread.currentThread().equals(Display.getDefault().getThread());
}
/**
* Displays an info dialog. Uses the engine output stream in headless mode.
*
* @param message
* dialog message
* @param title
* dialog title
*/
@WrapToScript(alias = "showMessageDialog")
public void showInfoDialog(final String message, @ScriptParameter(defaultValue = "Info") final String title) {
if (isHeadless())
getEnvironment().print("INFO: " + message, true);
else
Display.getDefault().syncExec(() -> MessageDialog.openInformation(Display.getDefault().getActiveShell(), title, message));
}
/**
* Displays a question dialog. Contains yes/no buttons. Uses the engine I/O streams in headless mode.
*
* @param message
* dialog message
* @param title
* dialog title
* @return <code>true</code> when 'yes' was pressed, <code>false</code> otherwise
*/
@WrapToScript
public boolean showQuestionDialog(final String message, @ScriptParameter(defaultValue = "Question") final String title) {
if (isHeadless()) {
try {
getEnvironment().print(message + " [Y/n])", false);
final int character = getScriptEngine().getInputStream().read();
if (Character.toLowerCase(character) == 'n')
return false;
return true;
} catch (final IOException e) {
// could not read from input
return false;
}
} else {
final RunnableWithResult<Boolean> runnable = new RunnableWithResult<Boolean>() {
@Override
public void run() {
setResult(MessageDialog.openQuestion(Display.getDefault().getActiveShell(), title, message));
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
}
/**
* Displays an input dialog. Uses the engine I/O streams in headless mode.
*
* @param message
* dialog message
* @param initialValue
* default value used to populate input box
* @param title
* dialog title
* @return user input or <code>null</code> in case the user aborted/closed the dialog
*/
@WrapToScript
public String showInputDialog(final String message, @ScriptParameter(defaultValue = "") final String initialValue,
@ScriptParameter(defaultValue = "Information request") final String title) {
if (isHeadless()) {
try {
getEnvironment().print(message, (initialValue == null));
if (initialValue != null)
getEnvironment().print("[" + initialValue + "]", true);
final StringBuilder result = new StringBuilder();
while (true) {
final int character = getScriptEngine().getInputStream().read();
if (character == -1)
// EOF reached
return null;
if (Character.toLowerCase(character) == '\n')
return result.toString();
result.append((char) character);
}
} catch (final IOException e) {
// could not read from input
return null;
}
} else {
final RunnableWithResult<String> runnable = new RunnableWithResult<String>() {
@Override
public void run() {
final InputDialog dialog = new InputDialog(Display.getDefault().getActiveShell(), title, message, initialValue, null);
if (dialog.open() == Window.OK)
setResult(dialog.getValue());
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
}
/**
* Displays a confirmation dialog. Uses the engine I/O streams in headless mode.
*
* @param message
* dialog message
* @param title
* dialog title
* @return <code>true</code> when accepted
*/
@WrapToScript
public boolean showConfirmDialog(final String message, @ScriptParameter(defaultValue = "Confirmation") final String title) {
if (isHeadless())
return showQuestionDialog(message, title);
else {
final RunnableWithResult<Boolean> runnable = new RunnableWithResult<Boolean>() {
@Override
public void run() {
setResult(MessageDialog.openConfirm(Display.getDefault().getActiveShell(), title, message));
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
}
/**
* Displays a warning dialog. Uses the engine output stream in headless mode.
*
* @param message
* dialog message
* @param title
* dialog title
*/
@WrapToScript
public void showWarningDialog(final String message, @ScriptParameter(defaultValue = "Warning") final String title) {
if (isHeadless())
getEnvironment().print("WARNING: " + message, true);
else
Display.getDefault().syncExec(() -> MessageDialog.openWarning(Display.getDefault().getActiveShell(), title, message));
}
/**
* Displays an error dialog. Uses the engine output stream in headless mode.
*
* @param message
* dialog message
* @param title
* dialog title
*/
@WrapToScript
public void showErrorDialog(final String message, @ScriptParameter(defaultValue = "Error") final String title) {
if (isHeadless())
getEnvironment().print("ERROR: " + message, true);
else
Display.getDefault().syncExec(() -> MessageDialog.openError(Display.getDefault().getActiveShell(), title, message));
}
/**
* Close the application. On unsaved editors user will be asked to save before closing.
*/
@WrapToScript
public static void exitApplication() {
Display.getDefault().asyncExec(() -> PlatformUI.getWorkbench().close());
}
/**
* Opens a view by given Name or id. When <i>name</i> does not match any known view id we try to match it with a view title. When found the view is opened.
* If the view is already visible it will be given focus.
*
* @param name
* name or id of view to open
* @return view instance or <code>null</code>
* @throws Throwable
* when view cannot be created
*/
public static IViewPart showView(final String name) throws Throwable {
return showView(name, null, IWorkbenchPage.VIEW_ACTIVATE);
}
/**
* Shows a view in this page with the given id and secondary id. The Behavior of this method varies based on the supplied mode. If
* <code>VIEW_ACTIVATE</code> is supplied, the view is given focus. If <code>VIEW_VISIBLE</code> is supplied, then it is made visible but not given focus.
* Finally, if <code>VIEW_CREATE</code> is supplied the view is created and will only be made visible if it is not created in a folder that already contains
* visible views.
* <p>
* This allows multiple instances of a particular view to be created. They are disambiguated using the secondary id. If a secondary id is given, the view
* must allow multiple instances by having specified allowMultiple="true" in its extension.
* </p>
*
* @param name
* either the id of the view extension to use or the visible name of the view (tab title)
* @param secondaryId
* the secondary id to use, or <code>null</code> for no secondary id
* @param mode
* the activation mode. Must be {@link #VIEW_ACTIVATE}, {@link #VIEW_VISIBLE} or {@link #VIEW_CREATE}, Default is {@link #VIEW_ACTIVATE}
* @return a view
* @throws Throwable
* when the view cannot be opened
* @throws IllegalArgumentException
* when the supplied mode is not valid
* @since 3.0
*/
@WrapToScript(alias = "openView")
public static IViewPart showView(final String name, @ScriptParameter(defaultValue = ScriptParameter.NULL) final String secondaryId,
@ScriptParameter(defaultValue = "" + IWorkbenchPage.VIEW_ACTIVATE) final int mode) throws Throwable {
// find view ID
final String viewID = getIDForName(name);
if (viewID != null) {
final RunnableWithResult<IViewPart> runnable = new RunnableWithResult<IViewPart>() {
@Override
public void runWithTry() throws Throwable {
try {
setResult(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().showView(viewID, secondaryId, mode));
} catch (final NullPointerException e) {
if (PlatformUI.getWorkbench().getWorkbenchWindowCount() > 0)
setResult(PlatformUI.getWorkbench().getWorkbenchWindows()[0].getActivePage().showView(viewID, secondaryId, mode));
}
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResultFromTry();
}
// maybe this view is already open, search for view titles
for (final IViewReference part : PlatformUI.getWorkbench().getWorkbenchWindows()[0].getPages()[0].getViewReferences()) {
if (part.getTitle().equals(name))
return part.getView(false);
}
return null;
}
/**
* Opens a file in an editor.
*
* @param location
* file location to open, either {@link IFile} or workspace file location
* @return editor instance or <code>null</code>
* @throws Throwable
* when we cannot open the editor
*/
@WrapToScript(alias = "openEditor")
public IEditorPart showEditor(final Object location) throws Throwable {
final Object file = ResourceTools.resolve(location, getScriptEngine().getExecutedFile());
if (file instanceof IFile)
return showEditor((IFile) file);
return null;
}
/**
* Opens a file in an editor.
*
* @param file
* file location to open
*
* @return editor instance or <code>null</code>
* @throws Throwable
* when we cannot open the editor
*/
public static IEditorPart showEditor(final IFile file) throws Throwable {
IEditorDescriptor descriptor = PlatformUI.getWorkbench().getEditorRegistry().getDefaultEditor(file.getName());
if (descriptor == null)
descriptor = PlatformUI.getWorkbench().getEditorRegistry().findEditor(EditorsUI.DEFAULT_TEXT_EDITOR_ID);
if (descriptor != null) {
final IEditorDescriptor editorDescriptor = descriptor;
final RunnableWithResult<IEditorPart> runnable = new RunnableWithResult<IEditorPart>() {
@Override
public void runWithTry() throws Throwable {
try {
setResult(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().openEditor(new FileEditorInput(file),
editorDescriptor.getId()));
} catch (final NullPointerException e) {
if (PlatformUI.getWorkbench().getWorkbenchWindowCount() > 0)
setResult(PlatformUI.getWorkbench().getWorkbenchWindows()[0].getActivePage().openEditor(new FileEditorInput(file),
editorDescriptor.getId()));
}
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResultFromTry();
}
return null;
}
/**
* Get the current selection. If <i>partID</i> is provided, the selection of the given part is returned. Otherwise the selection of the current active part
* is returned.
*
* @param name
* name or ID of part to get selection from
* @return current selection
*/
@WrapToScript
public static ISelection getSelection(@ScriptParameter(defaultValue = ScriptParameter.NULL) final String name) {
final ISelectionService selectionService = PlatformUI.getWorkbench().getWorkbenchWindows()[0].getSelectionService();
if ((name != null) && (!name.isEmpty())) {
final String partID = getIDForName(name);
if (partID != null)
return selectionService.getSelection(partID);
return null;
}
// current selection needs to be accessed from Display thread
final RunnableWithResult<ISelection> runnable = new RunnableWithResult<ISelection>() {
@Override
public void run() {
setResult(selectionService.getSelection());
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
/**
* Converts selection to a consumable form. Table/Tree selections are transformed into Object[], Text selections into the selected String.
*
* @param selection
* selection to convert
* @return converted elements
*/
@WrapToScript
public static Object convertSelection(final ISelection selection) {
if (selection instanceof IStructuredSelection)
return ((IStructuredSelection) selection).toArray();
if (selection instanceof ITextSelection)
return ((ITextSelection) selection).getText();
return null;
}
/**
* Find ID for a given view name. If <i>name</i> already contains a valid id, it will be returned.
*
* @param name
* name of view
* @return view ID or <code>null</code>
*/
private static String getIDForName(final String name) {
String id = null;
final IViewRegistry viewRegistry = PlatformUI.getWorkbench().getViewRegistry();
for (final IViewDescriptor descriptor : viewRegistry.getViews()) {
if (descriptor.getId().equals(name)) {
// this is a valid view ID
return name;
} else if (descriptor.getLabel().equals(name)) {
id = descriptor.getId();
// continue as we might have a match with an ID later
}
}
return id;
}
/**
* Show a generic dialog.
*
* @param dialog
* dialog to display
* @return result of dialog.open() method
*/
@WrapToScript
public static int openDialog(final Window dialog) {
final RunnableWithResult<Integer> run = new RunnableWithResult<Integer>() {
@Override
public void run() {
setResult(dialog.open());
}
};
Display.getDefault().syncExec(run);
return run.getResult();
}
/**
* Get the workbench shell instance.
*
* @return shell
*/
@WrapToScript
public static Shell getShell() {
final RunnableWithResult<Shell> runnable = new RunnableWithResult<Shell>() {
@Override
public void run() {
setResult(Display.getCurrent().getActiveShell());
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
/**
* Get the active view instance.
*
* @return active view
*/
@WrapToScript
public static IWorkbenchPart getActiveView() {
final RunnableWithResult<IWorkbenchPart> runnable = new RunnableWithResult<IWorkbenchPart>() {
@Override
public void run() {
setResult(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActivePart());
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
/**
* Get the active editor instance.
*
* @return active editor
*/
@WrapToScript
public static IEditorPart getActiveEditor() {
final RunnableWithResult<IEditorPart> runnable = new RunnableWithResult<IEditorPart>() {
@Override
public void run() {
setResult(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().getActiveEditor());
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
/**
* Write text data to the clipboard.
*
* @param data
* data to write to the clipboard
*/
@WrapToScript
public static void setClipboard(final String data) {
final Runnable runnable = () -> {
final Clipboard clipboard = new Clipboard(Display.getDefault());
clipboard.setContents(new Object[] { data }, new Transfer[] { TextTransfer.getInstance() });
};
Display.getDefault().syncExec(runnable);
}
/**
* Get text data from the clipboard.
*
* @return clipboard text
*/
@WrapToScript
public static Object getClipboard() {
final RunnableWithResult<Object> runnable = new RunnableWithResult<Object>() {
@Override
public void run() {
final Clipboard clipboard = new Clipboard(Display.getDefault());
setResult(clipboard.getContents(TextTransfer.getInstance()));
}
};
Display.getDefault().syncExec(runnable);
return runnable.getResult();
}
/**
* Clear the script console.
*/
@WrapToScript
public void clearConsole() {
final ScriptConsole console = getConsole();
if (console != null)
console.clearConsole();
}
/**
* Get the script console for the current engine.
*
* @return script console or <code>null</code>
*/
@WrapToScript
public ScriptConsole getConsole() {
final IConsole[] consoles = ConsolePlugin.getDefault().getConsoleManager().getConsoles();
for (final IConsole console : consoles) {
if (console instanceof ScriptConsole) {
if (getScriptEngine().equals(((ScriptConsole) console).getScriptEngine()))
return (ScriptConsole) console;
}
}
return null;
}
/**
* Maximize a dedicated view. If the view is not opened yet, it will be opened by this call. A second call will restore the original size of the view.
*
* @param name
* visible name or id of view to maximize
* @throws Throwable
* when view cannot be opened
*/
@WrapToScript
public static void maximizeView(final String name) throws Throwable {
final IViewPart view = showView(name);
if (view != null)
ActionFactory.MAXIMIZE.create(view.getViewSite().getWorkbenchWindow()).run();
}
/**
* Minimize a dedicated view. If the view is not opened yet, it will be opened by this call. A second call will restore view on a floating dock.
*
* @param name
* name or id of view to minimize
* @throws Throwable
* when view cannot be opened
*/
@WrapToScript
public static void minimizeView(final String name) throws Throwable {
final IViewPart view = showView(name);
if (view != null)
ActionFactory.MINIMIZE.create(view.getViewSite().getWorkbenchWindow()).run();
}
/**
* Close a dedicated view.
*
* @param name
* visible name or id of view to close
* @param secondaryID
* secondary ID of view to close
*/
@WrapToScript
public static void closeView(final String name, @ScriptParameter(defaultValue = ScriptParameter.NULL) final String secondaryID) {
// find view ID
final String viewID = getIDForName(name);
final Runnable runnable = () -> {
final IWorkbenchPage activePage = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
for (final IViewReference part : activePage.getViewReferences()) {
if (part.getId().equals(viewID)) {
if ((secondaryID == null) || (secondaryID.equals(part.getSecondaryId()))) {
activePage.hideView(part);
return;
}
}
}
};
Display.getDefault().syncExec(runnable);
}
/**
* Shut down the application.
*/
@WrapToScript
public static void shutdown() {
Display.getDefault().asyncExec(() -> PlatformUI.getWorkbench().close());
}
/**
* Verify if we are running in headless mode.
*
* @return <code>true</code> if we are running without UI
*/
@WrapToScript
public static boolean isHeadless() {
return !PlatformUI.isWorkbenchRunning();
}
/**
* Constructs a new color given the desired red, green and blue values expressed as ints in the range 0 to 255 (where 0 is black and 255 is full
* brightness).
* <p>
* You must dispose the color when it is no longer required.
* </p>
*
* @param red
* the amount of red in the color
* @param green
* the amount of green in the color
* @param blue
* the amount of blue in the color
*
* @exception IllegalArgumentException
* <ul>
* <li>ERROR_NULL_ARGUMENT - if device is null and there is no current device</li>
* <li>ERROR_INVALID_ARGUMENT - if the red, green or blue argument is not between 0 and 255</li>
* </ul>
* @return color instance
*/
@WrapToScript
public Color createColor(int red, int green, int blue) {
return new Color(Display.getDefault(), red, green, blue);
}
}