/*******************************************************************************
* Copyright (c) 2012 Arapiki Solutions 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:
* "Peter Smith <psmith@arapiki.com>" - initial API and
* implementation and/or initial documentation
*******************************************************************************/
package com.buildml.eclipse.utils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Iterator;
import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.filesystem.EFS;
import org.eclipse.core.filesystem.IFileStore;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.resource.ImageRegistry;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorReference;
import org.eclipse.ui.IEditorRegistry;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchPartSite;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.console.ConsolePlugin;
import org.eclipse.ui.console.IConsole;
import org.eclipse.ui.console.IConsoleConstants;
import org.eclipse.ui.console.IConsoleManager;
import org.eclipse.ui.console.IConsoleView;
import org.eclipse.ui.console.MessageConsole;
import org.eclipse.ui.handlers.HandlerUtil;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.progress.UIJob;
import com.buildml.eclipse.Activator;
import com.buildml.eclipse.ISubEditor;
import com.buildml.eclipse.MainEditor;
import com.buildml.eclipse.actions.ActionsEditor;
import com.buildml.eclipse.bobj.UIAction;
import com.buildml.eclipse.bobj.UIDirectory;
import com.buildml.eclipse.bobj.UIFile;
import com.buildml.eclipse.bobj.UIInteger;
import com.buildml.eclipse.files.FilesEditor;
import com.buildml.eclipse.packages.PackageDiagramEditor;
import com.buildml.model.BuildStoreFactory;
import com.buildml.model.BuildStoreVersionException;
import com.buildml.model.IActionMgr;
import com.buildml.model.IBuildStore;
import com.buildml.model.IFileMgr;
import com.buildml.model.types.FileSet;
import com.buildml.model.types.ActionSet;
/**
* @author "Peter Smith <psmith@arapiki.com>"
*
*/
public class EclipsePartUtils {
/** The MainEditor that currently has the focus */
private static MainEditor activeMainEditor = null;
/*=====================================================================================*
* PUBLIC METHODS
*=====================================================================================*/
/**
* General use method for retrieving the currently selected object in the Eclipse UI.
* This method does not required any input arguments (such as an "event"), so it's usable
* in a wider range of scenarios.
*
* @return The selected objects in the Eclipse UI, or null if nothing is selected.
*/
public static IStructuredSelection getSelection() {
IWorkbenchWindow window = activeMainEditor.getEditorSite().getWorkbenchWindow();
if (window == null) {
return null;
}
return (IStructuredSelection)(window.getSelectionService().getSelection());
}
/*-------------------------------------------------------------------------------------*/
/**
* Given an Eclipse command handler's selection, such as when a user selects a bunch of UIInteger
* nodes from a TreeViewer, convert the selection into a FileSet. Selected items that are not of type
* UIFile or UIDirectory are ignored.
* @param buildStore The BuildStore that stores the selected objects.
* @param selection The Eclipse command handler's selection.
* @return The equivalent FileSet.
*/
public static FileSet getFileSetFromSelection(IBuildStore buildStore, TreeSelection selection) {
IFileMgr fileMgr = buildStore.getFileMgr();
FileSet fs = new FileSet(fileMgr);
Iterator<?> iter = selection.iterator();
while (iter.hasNext()) {
Object item = iter.next();
if ((item instanceof UIFile) || (item instanceof UIDirectory)) {
UIInteger uiInt = (UIInteger)item;
fs.add(uiInt.getId());
}
}
return fs;
}
/*-------------------------------------------------------------------------------------*/
/**
* Given an Eclipse command handler's selection, such as when a user selects a bunch of UIAction
* nodes from a TreeViewer, convert the selection into an ActionSet. Selected items that are not of type
* UIAction are ignored.
* @param buildStore The BuildStore that stores the selected objects.
* @param selection The Eclipse command handler's selection.
* @return The equivalent ActionSet.
*/
public static ActionSet getActionSetFromSelection(IBuildStore buildStore, TreeSelection selection) {
IActionMgr actionMgr = buildStore.getActionMgr();
ActionSet acts = new ActionSet(actionMgr);
Iterator<?> iter = selection.iterator();
while (iter.hasNext()) {
Object item = iter.next();
if (item instanceof UIAction) {
acts.add(((UIAction)item).getId());
}
}
return acts;
}
/*-------------------------------------------------------------------------------------*/
/**
* Given a UI event handler's selection, return the one (and only) UIInteger
* object that was selected. Otherwise return null;
*
* @param event The UI event, as passed into the UI handler code.
* @return The selected UIDirectory, or null if something else (or nothing at all)
* was selected.
*/
public static UIDirectory getSingleSelectedPathDir(ExecutionEvent event) {
TreeSelection selection = (TreeSelection)HandlerUtil.getCurrentSelection(event);
if ((selection == null) || (selection.size() != 1)) {
return null;
}
Object nodeObject = selection.getFirstElement();
if (!(nodeObject instanceof UIDirectory)) {
return null;
}
return (UIDirectory)nodeObject;
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns the currently active MainEditor. If the current editor is not a
* MainEditor, return null;
* @return The currently active MainEditor instance, or null;
*/
public static MainEditor getActiveMainEditor() {
return activeMainEditor;
}
/*-------------------------------------------------------------------------------------*/
/**
* Record the currently-focused MainEditor, so that we can query its features whenever
* necessary.
*
* @param mainEditor The MainEditor that currently has the focus.
*/
public static void setActiveMainEditor(MainEditor mainEditor) {
activeMainEditor = mainEditor;
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns the currently active SubEditor.
* @return The currently active SubEditor instance, or null;
*/
public static ISubEditor getActiveSubEditor() {
MainEditor mainEditor = getActiveMainEditor();
if (mainEditor == null) {
return null;
}
return mainEditor.getActiveSubEditor();
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns the currently active FilesEditor. If the current editor is not a
* FilesEditor, return null;
* @return The currently active FilesEditor instance, or null;
*/
public static FilesEditor getActiveFilesEditor() {
ISubEditor subEditor = EclipsePartUtils.getActiveSubEditor();
if (subEditor instanceof FilesEditor) {
return (FilesEditor)subEditor;
}
return null;
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns the currently active ActionsEditor. If the current editor is not an
* ActionsEditor, return null;
* @return The currently active ActionsEditor instance, or null;
*/
public static ActionsEditor getActiveActionsEditor() {
ISubEditor subEditor = EclipsePartUtils.getActiveSubEditor();
if (subEditor instanceof ActionsEditor) {
return (ActionsEditor)subEditor;
}
return null;
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns the currently active PackageDiagramEditor. If the current editor is not an
* PackageDiagramEditor, return null;
* @return The currently active PackageDiagramEditor instance, or null;
*/
public static PackageDiagramEditor getActivePackageDiagramEditor() {
ISubEditor subEditor = EclipsePartUtils.getActiveSubEditor();
if (subEditor instanceof PackageDiagramEditor) {
return (PackageDiagramEditor)subEditor;
}
return null;
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns the BuildStore for the currently active editor.
* @return The currently active BuildStore instance, or null;
*/
public static IBuildStore getActiveBuildStore() {
MainEditor mainEditor = getActiveMainEditor();
if (mainEditor == null) {
return null;
}
return mainEditor.getBuildStore();
}
/*-------------------------------------------------------------------------------------*/
/**
* Returns true or false, to specify whether the currently active sub editor supports
* the specified feature.
* @param feature A textual name for an editor feature.
* @return true if the feature is supported, or false. If the active editor is invalid
* for some reason, also return false.
*/
public static boolean activeSubEditorHasFeature(String feature) {
ISubEditor subEditor = getActiveSubEditor();
if (subEditor == null) {
return false;
}
return subEditor.hasFeature(feature);
}
/*-------------------------------------------------------------------------------------*/
/**
* @return An array of the currently open BuildML editors, or null if there was an
* error retrieving the editor list.
*/
public static MainEditor[] getOpenBmlEditors()
{
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
return null;
}
IWorkbenchPage page = window.getActivePage();
if (page == null) {
return null;
}
IEditorReference editors[] = page.getEditorReferences();
ArrayList<MainEditor> bmlEditors = new ArrayList<MainEditor>();
int foundMainEditors = 0;
for (int i = 0; i < editors.length; i++) {
IEditorPart editor = editors[i].getEditor(true);
if (editor instanceof MainEditor) {
foundMainEditors++;
bmlEditors.add((MainEditor)editor);
}
}
return bmlEditors.toArray(new MainEditor[foundMainEditors]);
}
/*-------------------------------------------------------------------------------------*/
/**
* Given a file (on the file system), determine whether there are any BML editors
* currently editing that file.
* @param fileBeingEdited The file that an editor might be editing.
* @return The matching editor, or null if no open editor is editing this file.
*/
public static MainEditor getOpenBmlEditor(File fileBeingEdited)
{
MainEditor[] editors = getOpenBmlEditors();
for (int i = 0; i < editors.length; i++) {
if (editors[i].getFile().equals(fileBeingEdited)) {
return editors[i];
}
}
return null;
}
/*-------------------------------------------------------------------------------------*/
/**
* @return The currently active workbench page, or null if there's no active page.
*/
public static IWorkbenchPage getActiveWorkbenchPage()
{
IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow();
if (window == null) {
return null;
}
return window.getActivePage();
}
/*-------------------------------------------------------------------------------------*/
/**
* Given a path relative to the Eclipse workspace root, return the equivalent absolute
* path.
* @param relativePath The workspace-relative path.
* @return The equivalent absolute path.
*/
public static String workspaceRelativeToAbsolutePath(String relativePath)
{
return ResourcesPlugin.getWorkspace().getRoot().getLocation().toString() +
relativePath;
}
/*-------------------------------------------------------------------------------------*/
/**
* Given an absolute file path, return it as a workspace-relative path (or null if
* it's not within the workspace).
*
* @param file The absolute file path.
* @return The workspace-relative version of this path, or null if it's outside the
* workspace.
*/
public static String absoluteToWorkspaceRelativePath(String file) {
String root = workspaceRelativeToAbsolutePath("");
if (file.startsWith(root)) {
return file.substring(root.length());
}
return null;
}
/*-------------------------------------------------------------------------------------*/
/**
* Open an Eclipse console view, ready for textual output to be displayed. This involves
* either finding an existing console (with the specified name), or creating a new console.
* In either case, ensure that the console is visible so the user can see the output.
*
* @param consoleName Name of the console view to open.
* @return An OutputStream, for writing to the console.
*/
public static PrintStream getConsolePrintStream(String consoleName) {
/*
* Look through the existing consoles, to see if we've already created it.
*/
ConsolePlugin plugin = ConsolePlugin.getDefault();
IConsoleManager conMan = plugin.getConsoleManager();
IConsole[] existing = conMan.getConsoles();
MessageConsole messageConsole = null;
for (int i = 0; i < existing.length; i++) {
if (existing[i].getName().equals(consoleName)) {
messageConsole = (MessageConsole)existing[i];
}
}
/* no, it didn't exist so create a new one */
if (messageConsole == null) {
messageConsole = new MessageConsole(consoleName, null);
conMan.addConsoles(new IConsole[]{messageConsole});
}
/*
* Ensure that the console is visible to the end user. This involves
* starting a UI thread that's able to open the view.
*/
final MessageConsole console = messageConsole;
Job showConsoleJob = new UIJob("show console") {
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
IWorkbenchPage page = EclipsePartUtils.getActiveWorkbenchPage();
if (page == null) {
return Status.CANCEL_STATUS;
}
IConsoleView view = null;
try {
view = (IConsoleView) page.showView(IConsoleConstants.ID_CONSOLE_VIEW);
} catch (PartInitException e1) {
/* nothing we can do - just don't show the console */
}
view.display(console);
return Status.OK_STATUS;
}
};
showConsoleJob.schedule();
/* return a new PrintStream, so we can use all the standard output functionality */
return new PrintStream(messageConsole.newMessageStream());
}
/*-------------------------------------------------------------------------------------*/
/**
* Create a new BuildStore object by opening a *.bml database file.
* @param bmlFileName Name of the .bml file to be opened. This is a UI centric method
* that will display dialog boxes to explain if anything goes wrong.
* @return The corresponding BuildStore object, or null if there was a problem
* opening the database.
*/
public static IBuildStore getNewBuildStore(String bmlFileName) {
IBuildStore buildStore = null;
File fileInput = new File(bmlFileName);
try {
/*
* Test that file exists before opening it, otherwise it'll be created
* automatically, which isn't what we want. Ideally Eclipse wouldn't ask
* us to open a file that doesn't exist, so this is an extra safety
* check.
*/
if (!fileInput.exists()) {
String message = fileInput + " does not exist, or is not writable.";
throw new FileNotFoundException(message);
}
/*
* Open the existing BuildStore database, with the requirement that changes
* must be explicitly saved before they're written back to this original
* file.
*/
buildStore = BuildStoreFactory.openBuildStore(fileInput.toString(), true);
} catch (BuildStoreVersionException e) {
AlertDialog.displayErrorDialog("BuildML database has the wrong version.", e.getMessage());
} catch (FileNotFoundException e) {
AlertDialog.displayErrorDialog("Can't open the BuildML database.", e.getMessage());
} catch (IOException e) {
AlertDialog.displayErrorDialog("An I/O error occurred in the BuildML database.", e.getMessage());
}
return buildStore;
}
/*-------------------------------------------------------------------------------------*/
/**
* Open a .bml file in a new BML editor window. This must be executed by the UI thread.
*
* @param fileToOpen The file to be opened.
* @return true on success, or false on failure. On failure, a suitable dialog box
* will display the reason for the failure.
*/
public static boolean openNewEditor(String fileToOpen) {
File file = new File(fileToOpen);
if (file.isFile()) {
IFileStore fileStore = EFS.getLocalFileSystem().getStore(file.toURI());
IWorkbenchPage page = getActiveWorkbenchPage();
if (page == null) {
AlertDialog.displayErrorDialog("Editor can't be opened",
"For some unknown reason, a new editor couldn't be opened.");
return false;
}
try {
IDE.openEditorOnFileStore(page, fileStore);
} catch (PartInitException e) {
AlertDialog.displayErrorDialog("File can't be opened",
"For some unknown reason, the file couldn't be opened.");
return false;
}
} else {
AlertDialog.displayErrorDialog("File not found",
"The file couldn't be opened because it's not currently on your file system.");
return false;
}
return true;
}
/*-------------------------------------------------------------------------------------*/
/**
* Create an SWT Image, given the plug-in relative path of the image file (such as a .gif).
* If possible, the image is retrieved from a cache of existing images.
* @param plugInPath The image file's path, relative to the plug-in root.
* @return An Image object, or null if the image couldn't be created.
*/
public static Image getImage(String plugInPath)
{
/* first, check if the image is already in the registry (a cache of images) */
ImageRegistry pluginImageRegistry = Activator.getDefault().getImageRegistry();
ImageDescriptor imageDescr = Activator.getImageDescriptor(plugInPath);
Image iconImage = pluginImageRegistry.get(imageDescr.toString());
/*
* If not, proceed to create the image (as a new object) and store it in the
* registry for future use.
*/
if (iconImage == null) {
iconImage = imageDescr.createImage();
if (iconImage == null) {
return null;
}
pluginImageRegistry.put(imageDescr.toString(), iconImage);
}
return iconImage;
}
/*-------------------------------------------------------------------------------------*/
/**
* Choose a suitable icon, based on a file name's extension. For example, a foo.c file
* will have a suitable graph image indicating that it's C language.
*
* @param name The name of the file.
* @return An Image that relates to this file.
*/
public static Image getImageFromFileType(String name) {
IEditorRegistry editorImageRegistry = PlatformUI.getWorkbench().getEditorRegistry();
ImageDescriptor imageDescr = editorImageRegistry.getImageDescriptor(name);
/* can we get this image from the plugin's cache? */
ImageRegistry pluginImageRegistry = Activator.getDefault().getImageRegistry();
Image iconImage = pluginImageRegistry.get(imageDescr.toString());
if (iconImage == null) {
iconImage = imageDescr.createImage();
pluginImageRegistry.put(imageDescr.toString(), iconImage);
}
return iconImage;
}
/*-------------------------------------------------------------------------------------*/
/**
* @return The width of the current display (in pixels).
*/
public static int getScreenWidth() {
Rectangle bounds = Display.getCurrent().getBounds();
return bounds.width;
}
/*-------------------------------------------------------------------------------------*/
/**
* @return The height of the current display (in pixels).
*/
public static int getScreenHeight() {
Rectangle bounds = Display.getCurrent().getBounds();
return bounds.height;
}
/*-------------------------------------------------------------------------------------*/
/**
* Return the height (in pixels) required by the font used by the specified Control.
*
* @param control The control that uses the font.
* @return The height of the control's font, in pixels.
*/
public static int getFontHeight(Control control) {
int pointHeight = control.getFont().getFontData()[0].getHeight();
int dpi = Display.getDefault().getDPI().y;
int pixelHeight = (int) Math.round((pointHeight * dpi) / 72.0);
return pixelHeight;
}
/*-------------------------------------------------------------------------------------*/
/**
* Return the Eclipse handler for the specific service.
* @param serviceType The class of the service to locate.
* @return The service, or null if it couldn't be found.
*/
public static Object getService(Class<?> serviceType) {
MainEditor editor = EclipsePartUtils.getActiveMainEditor();
if (editor != null) {
IWorkbenchPartSite site = editor.getSite();
if (site != null) {
return site.getService(serviceType);
}
}
return null;
}
/*-------------------------------------------------------------------------------------*/
}