/******************************************************************************* * 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; } /*-------------------------------------------------------------------------------------*/ }