package org.rubypeople.rdt.internal.ui.rubyeditor;
import java.lang.reflect.InvocationTargetException;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.swt.SWT;
import org.eclipse.ui.IEditorDescriptor;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IEditorPart;
import org.eclipse.ui.IEditorSite;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.WorkspaceModifyOperation;
import org.eclipse.ui.ide.IDE;
import org.eclipse.ui.ide.IGotoMarker;
import org.eclipse.ui.part.FileEditorInput;
import org.eclipse.ui.texteditor.ITextEditor;
import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds;
import org.eclipse.ui.texteditor.TextEditorAction;
import org.rubypeople.rdt.core.IMember;
import org.rubypeople.rdt.core.IRubyElement;
import org.rubypeople.rdt.core.IRubyProject;
import org.rubypeople.rdt.core.IRubyScript;
import org.rubypeople.rdt.core.ISourceRange;
import org.rubypeople.rdt.core.ISourceReference;
import org.rubypeople.rdt.core.LocalFileStorage;
import org.rubypeople.rdt.core.RubyCore;
import org.rubypeople.rdt.core.RubyModelException;
import org.rubypeople.rdt.internal.core.ExternalRubyScript;
import org.rubypeople.rdt.internal.core.util.Messages;
import org.rubypeople.rdt.internal.corext.util.RubyModelUtil;
import org.rubypeople.rdt.internal.ui.RubyPlugin;
import org.rubypeople.rdt.ui.PreferenceConstants;
import org.rubypeople.rdt.ui.RubyUI;
public class EditorUtility {
/**
* Opens a Ruby editor for an element such as <code>IRubyElement</code>, <code>IFile</code>, or <code>IStorage</code>.
* The editor is activated by default.
* @return the IEditorPart or null if wrong element type or opening failed
*/
public static IEditorPart openInEditor(Object inputElement) throws RubyModelException, PartInitException {
return openInEditor(inputElement, true);
}
/**
* Opens a Ruby editor for an element (IRubyElement, IFile, IStorage...)
* @return the IEditorPart or null if wrong element type or opening failed
*/
public static IEditorPart openInEditor(Object inputElement, boolean activate) throws RubyModelException, PartInitException {
if (inputElement instanceof IFile)
return openInEditor((IFile) inputElement, activate);
IEditorInput input= getEditorInput(inputElement);
if (input instanceof IFileEditorInput) {
IFileEditorInput fileInput= (IFileEditorInput) input;
return openInEditor(fileInput.getFile(), activate);
}
if (input != null)
return openInEditor(input, getEditorID(input, inputElement), activate);
return null;
}
private static IEditorPart openInEditor(IFile file, boolean activate) throws PartInitException {
if (file != null) {
IWorkbenchPage p= RubyPlugin.getActivePage();
if (p != null) {
IEditorPart editorPart= IDE.openEditor(p, file, activate);
initializeHighlightRange(editorPart);
return editorPart;
}
}
return null;
}
private static IEditorPart openInEditor(IEditorInput input, String editorID, boolean activate) throws PartInitException {
if (input != null) {
IWorkbenchPage p= RubyPlugin.getActivePage();
if (p != null) {
IEditorPart editorPart= p.openEditor(input, editorID, activate);
initializeHighlightRange(editorPart);
return editorPart;
}
}
return null;
}
private static void initializeHighlightRange(IEditorPart editorPart) {
if (editorPart instanceof ITextEditor) {
IAction toggleAction= editorPart.getEditorSite().getActionBars().getGlobalActionHandler(ITextEditorActionDefinitionIds.TOGGLE_SHOW_SELECTED_ELEMENT_ONLY);
boolean enable= toggleAction != null;
if (enable && editorPart instanceof RubyEditor)
enable= RubyPlugin.getDefault().getPreferenceStore().getBoolean(PreferenceConstants.EDITOR_SHOW_SEGMENTS);
else
enable= enable && toggleAction.isEnabled() && toggleAction.isChecked();
if (enable) {
if (toggleAction instanceof TextEditorAction) {
// Reset the action
((TextEditorAction)toggleAction).setEditor(null);
// Restore the action
((TextEditorAction)toggleAction).setEditor((ITextEditor)editorPart);
} else {
// Un-check
toggleAction.run();
// Check
toggleAction.run();
}
}
}
}
private static String getEditorID(IEditorInput input, Object inputObject) {
IEditorDescriptor editorDescriptor;
try {
editorDescriptor= IDE.getEditorDescriptor(input.getName());
} catch (PartInitException e) {
return null;
}
if (editorDescriptor != null)
return editorDescriptor.getId();
return null;
}
public static IEditorInput getEditorInput(Object input) throws RubyModelException {
if (input instanceof IRubyElement)
return getEditorInput((IRubyElement) input);
if (input instanceof IFile)
return new FileEditorInput((IFile) input);
if (input instanceof LocalFileStorage) {
return new ExternalRubyFileEditorInput((LocalFileStorage)input);
}
return null;
}
private static IEditorInput getEditorInput(IRubyElement element) throws RubyModelException {
while (element != null) {
if (element instanceof IRubyScript) {
IRubyScript unit= RubyModelUtil.toOriginal((IRubyScript) element);
IResource resource= unit.getResource();
if (resource instanceof IFile)
return new FileEditorInput((IFile) resource);
}
if (element instanceof ExternalRubyScript)
return new RubyScriptEditorInput(((ExternalRubyScript) element));
element= element.getParent();
}
return null;
}
/**
* Selects a Java Element in an editor
*/
public static void revealInEditor(IEditorPart part, IRubyElement element) {
if (element == null)
return;
if (part instanceof RubyEditor) {
((RubyEditor) part).setSelection(element);
return;
}
// Support for non-Ruby editor
try {
ISourceRange range= null;
if (element instanceof IRubyScript)
range= null;
// else if (element instanceof ILocalVariable)
// range= ((ILocalVariable)element).getNameRange();
else if (element instanceof IMember)
range= ((IMember)element).getNameRange();
else if (element instanceof ISourceReference)
range= ((ISourceReference)element).getSourceRange();
if (range != null)
revealInEditor(part, range.getOffset(), range.getLength());
} catch (RubyModelException e) {
// don't reveal
}
}
/**
* Selects and reveals the given offset and length in the given editor part.
*/
public static void revealInEditor(IEditorPart editor, final int offset, final int length) {
if (editor instanceof ITextEditor) {
((ITextEditor)editor).selectAndReveal(offset, length);
return;
}
// Support for non-text editor - try IGotoMarker interface
if (editor instanceof IGotoMarker) {
final IEditorInput input= editor.getEditorInput();
if (input instanceof IFileEditorInput) {
final IGotoMarker gotoMarkerTarget= (IGotoMarker)editor;
WorkspaceModifyOperation op = new WorkspaceModifyOperation() {
protected void execute(IProgressMonitor monitor) throws CoreException {
IMarker marker= null;
try {
marker= ((IFileEditorInput)input).getFile().createMarker(IMarker.TEXT);
marker.setAttribute(IMarker.CHAR_START, offset);
marker.setAttribute(IMarker.CHAR_END, offset + length);
gotoMarkerTarget.gotoMarker(marker);
} finally {
if (marker != null)
marker.delete();
}
}
};
try {
op.run(null);
} catch (InvocationTargetException ex) {
// reveal failed
} catch (InterruptedException e) {
Assert.isTrue(false, "this operation can not be canceled"); //$NON-NLS-1$
}
}
return;
}
/*
* Workaround: send out a text selection
* XXX: Needs to be improved, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=32214
*/
if (editor != null && editor.getEditorSite().getSelectionProvider() != null) {
IEditorSite site= editor.getEditorSite();
if (site == null)
return;
ISelectionProvider provider= editor.getEditorSite().getSelectionProvider();
if (provider == null)
return;
provider.setSelection(new TextSelection(offset, length));
}
}
/**
* Returns the Ruby project for a given editor input or <code>null</code> if no corresponding
* Ruby project exists.
*
* @param input the editor input
* @return the corresponding Ruby project
*
* @since 0.9.0
*/
public static IRubyProject getRubyProject(IEditorInput input) {
IRubyProject rProject= null;
if (input instanceof IFileEditorInput) {
IProject project= ((IFileEditorInput)input).getFile().getProject();
if (project != null) {
rProject= RubyCore.create(project);
if (!rProject.exists())
rProject= null;
}
}
return rProject;
}
/**
* Tests if a CU is currently shown in an editor
* @return the IEditorPart if shown, null if element is not open in an editor
*/
public static IEditorPart isOpenInEditor(Object inputElement) {
IEditorInput input= null;
try {
input= getEditorInput(inputElement);
} catch (RubyModelException x) {
RubyPlugin.log(x.getStatus());
}
if (input != null) {
IWorkbenchPage p= RubyPlugin.getActivePage();
if (p != null) {
return p.findEditor(input);
}
}
return null;
}
/**
* Maps the localized modifier name to a code in the same
* manner as #findModifier.
*
* @param modifierName the modifier name
* @return the SWT modifier bit, or <code>0</code> if no match was found
* @since 2.1.1
*/
public static int findLocalizedModifier(String modifierName) {
if (modifierName == null)
return 0;
if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.CTRL)))
return SWT.CTRL;
if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.SHIFT)))
return SWT.SHIFT;
if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.ALT)))
return SWT.ALT;
if (modifierName.equalsIgnoreCase(Action.findModifierString(SWT.COMMAND)))
return SWT.COMMAND;
return 0;
}
/**
* Returns the modifier string for the given SWT modifier
* modifier bits.
*
* @param stateMask the SWT modifier bits
* @return the modifier string
* @since 2.1.1
*/
public static String getModifierString(int stateMask) {
String modifierString= ""; //$NON-NLS-1$
if ((stateMask & SWT.CTRL) == SWT.CTRL)
modifierString= appendModifierString(modifierString, SWT.CTRL);
if ((stateMask & SWT.ALT) == SWT.ALT)
modifierString= appendModifierString(modifierString, SWT.ALT);
if ((stateMask & SWT.SHIFT) == SWT.SHIFT)
modifierString= appendModifierString(modifierString, SWT.SHIFT);
if ((stateMask & SWT.COMMAND) == SWT.COMMAND)
modifierString= appendModifierString(modifierString, SWT.COMMAND);
return modifierString;
}
/**
* Appends to modifier string of the given SWT modifier bit
* to the given modifierString.
*
* @param modifierString the modifier string
* @param modifier an int with SWT modifier bit
* @return the concatenated modifier string
* @since 2.1.1
*/
private static String appendModifierString(String modifierString, int modifier) {
if (modifierString == null)
modifierString= ""; //$NON-NLS-1$
String newModifierString= Action.findModifierString(modifier);
if (modifierString.length() == 0)
return newModifierString;
return Messages.format(RubyEditorMessages.EditorUtility_concatModifierStrings, new String[] {modifierString, newModifierString});
}
/**
* Returns the given editor's input as Ruby element.
*
* @param editor the editor
* @param primaryOnly if <code>true</code> only primary working copies will be returned
* @return the given editor's input as Ruby element or <code>null</code> if none
* @since 1.0
*/
public static IRubyElement getEditorInputRubyElement(IEditorPart editor, boolean primaryOnly) {
Assert.isNotNull(editor);
IEditorInput editorInput= editor.getEditorInput();
if (editorInput == null)
return null;
IRubyElement je= RubyUI.getEditorInputRubyElement(editorInput);
if (je != null || primaryOnly)
return je;
return RubyPlugin.getDefault().getWorkingCopyManager().getWorkingCopy(editorInput, false);
}
}