blob: 72b313be9b985e01705a68b1f8ef33645d265895 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007 University of Illinois at Urbana-Champaign 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:
* UIUC - Initial API and implementation
*******************************************************************************/
package org.eclipse.photran.internal.ui.editor;
import org.eclipse.cdt.core.model.CModelException;
import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ISourceRange;
import org.eclipse.cdt.core.model.ISourceReference;
import org.eclipse.cdt.core.model.IWorkingCopy;
import org.eclipse.cdt.internal.ui.actions.SelectionConverter;
import org.eclipse.cdt.internal.ui.editor.CContentOutlinePage;
import org.eclipse.cdt.internal.ui.text.CReconciler;
import org.eclipse.cdt.internal.ui.text.CReconcilingStrategy;
import org.eclipse.cdt.ui.CUIPlugin;
import org.eclipse.cdt.ui.IWorkingCopyManager;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jface.action.IAction;
import org.eclipse.jface.preference.IPreferenceStore;
import org.eclipse.jface.resource.JFaceResources;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IDocumentPartitioner;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextSelection;
import org.eclipse.jface.text.ITextViewerExtension2;
import org.eclipse.jface.text.MarginPainter;
import org.eclipse.jface.text.presentation.IPresentationReconciler;
import org.eclipse.jface.text.presentation.PresentationReconciler;
import org.eclipse.jface.text.reconciler.IReconciler;
import org.eclipse.jface.text.reconciler.MonoReconciler;
import org.eclipse.jface.text.rules.DefaultDamagerRepairer;
import org.eclipse.jface.text.rules.FastPartitioner;
import org.eclipse.jface.text.rules.ITokenScanner;
import org.eclipse.jface.text.rules.RuleBasedPartitionScanner;
import org.eclipse.jface.text.source.ISourceViewer;
import org.eclipse.jface.text.source.SourceViewerConfiguration;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.ISelectionProvider;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.photran.internal.core.preferences.FortranPreferences;
import org.eclipse.photran.internal.ui.actions.FortranBlockCommentActionDelegate;
import org.eclipse.photran.ui.FortranUIPlugin;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.graphics.RGB;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.ui.IEditorInput;
import org.eclipse.ui.IFileEditorInput;
import org.eclipse.ui.IPageLayout;
import org.eclipse.ui.IPartService;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.editors.text.EditorsUI;
import org.eclipse.ui.editors.text.TextEditor;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTargetList;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.texteditor.ChainedPreferenceStore;
import org.eclipse.ui.texteditor.DefaultRangeIndicator;
import org.eclipse.ui.texteditor.IDocumentProvider;
import org.eclipse.ui.texteditor.ITextEditorActionConstants;
import org.eclipse.ui.texteditor.WorkbenchChainedTextFontFieldEditor;
import org.eclipse.ui.views.contentoutline.IContentOutlinePage;
/**
* Base class for the fixed and free-form Fortran editors
*
* @author Jeff Overbey
*/
public abstract class AbstractFortranEditor extends TextEditor implements ISelectionChangedListener
{
///////////////////////////////////////////////////////////////////////////////////////////////
// Constants
///////////////////////////////////////////////////////////////////////////////////////////////
protected static String[] PARTITION_TYPES = new String[] { IDocument.DEFAULT_CONTENT_TYPE };
protected static String FORTRAN_EDITOR_CONTEXT_ID = "org.eclipse.photran.ui.FortranEditorContext";
protected static String CONTEXT_MENU_ID = "#FortranEditorContextMenu";
protected static String BLOCK_COMMENT_COMMAND_ID = "org.eclipse.photran.ui.CommentCommand";
protected static final RGB VERTICAL_LINE_COLOR = new RGB(176, 180, 185);
///////////////////////////////////////////////////////////////////////////////////////////////
// Fields
///////////////////////////////////////////////////////////////////////////////////////////////
protected IPreferenceStore fCombinedPreferenceStore;
protected Composite fMainComposite;
protected CContentOutlinePage fOutlinePage;
protected FortranHorizontalRuler fHRuler;
protected Color verticalLineColor;
///////////////////////////////////////////////////////////////////////////////////////////////
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////////
public AbstractFortranEditor()
{
super();
setSourceViewerConfiguration(createSourceViewerConfiguration());
setRangeIndicator(new DefaultRangeIndicator());
// We must use the CUIPlugin's document provider in order for the
// working copy manager in setOutlinePageInput (below) to function correctly.
setDocumentProvider(CUIPlugin.getDefault().getDocumentProvider());
// This has to be set to be notified of changes to preferences
// Without this, the editor will not auto-update
IPreferenceStore store = FortranUIPlugin.getDefault().getPreferenceStore();
IPreferenceStore generalTextStore = EditorsUI.getPreferenceStore();
fCombinedPreferenceStore = new ChainedPreferenceStore(new IPreferenceStore[] { store, generalTextStore, getPreferenceStore()});
setPreferenceStore(fCombinedPreferenceStore);
// This enables any global changes to editor e.g. font type and size to take effect
WorkbenchChainedTextFontFieldEditor.startPropagate(store, JFaceResources.TEXT_FONT);
// JO: This gives you a "Toggle Breakpoint" action (and others)
// when you right-click the Fortran editor's ruler
setRulerContextMenuId("#CEditorRulerContext"); //$NON-NLS-1$
setEditorContextMenuId(CONTEXT_MENU_ID);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// JFace Text Overrides
///////////////////////////////////////////////////////////////////////////////////////////////
protected void doSetInput(IEditorInput input) throws CoreException
{
super.doSetInput(input);
IDocument document = this.getDocumentProvider().getDocument(input);
if (document == null) return;
configurePartitionScanner(document);
}
public void createPartControl(Composite parent)
{
super.createPartControl(parent);
Composite childComp = (Composite)((Composite) parent.getChildren()[0]).getChildren()[0];
GridLayout layout = new GridLayout();
layout.marginHeight = 0;
layout.marginWidth = 0;
layout.verticalSpacing = 2;
childComp.setLayout(layout);
GridData data = new GridData(GridData.FILL_BOTH);
childComp.getChildren()[0].setLayoutData(data);
fMainComposite = childComp;
createHorizontalRuler(fMainComposite);
createLightGrayLines();
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Ctrl+/ Block Commenting Support
///////////////////////////////////////////////////////////////////////////////////////////////
protected void initializeKeyBindingScopes()
{
setKeyBindingScopes(new String[] { "org.eclipse.ui.textEditorScope", FORTRAN_EDITOR_CONTEXT_ID });
}
// /**
// * Create actions that will be registered with the editor.
// */
// protected void createActions()
// {
// super.createActions();
// createAction(new FortranBlockCommentActionDelegate(this), BLOCK_COMMENT_COMMAND_ID);
// //createAction(new FortranOpenDeclarationActionDelegate(this), OPEN_DECLARATION_COMMAND_ID);
// }
protected void createAction(IAction action, String id)
{
action.setActionDefinitionId(id);
setAction(id, action);
markAsStateDependentAction(id, true);
markAsSelectionDependentAction(id, true);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Custom Ruler
///////////////////////////////////////////////////////////////////////////////////////////////
/**
* @param mainComposite
* This creates the horizontal ruler and adds it to the top of the editor
*/
protected void createHorizontalRuler(Composite mainComposite)
{
GC gc = new GC(getSourceViewer().getTextWidget());
GridData data = new GridData(GridData.FILL_HORIZONTAL);
data.heightHint = gc.getFontMetrics().getHeight();
gc.dispose();
fHRuler = getFortranHorizontalRuler(mainComposite);
fHRuler.setFont(getSourceViewer().getTextWidget().getFont());
fHRuler.setSourceViewer(getSourceViewer());
fHRuler.setLayoutData(data);
fHRuler.moveAbove(null);
}
protected abstract FortranHorizontalRuler getFortranHorizontalRuler(Composite mainComposite);
///////////////////////////////////////////////////////////////////////////////////////////////
// Gray Vertical Lines
///////////////////////////////////////////////////////////////////////////////////////////////
/**
* Display a light gray line between columns 6/7 and 72/73
*/
protected void createLightGrayLines()
{
verticalLineColor = new Color(null, VERTICAL_LINE_COLOR);
ISourceViewer sourceViewer = getSourceViewer();
if (sourceViewer instanceof ITextViewerExtension2)
{
ITextViewerExtension2 painter = (ITextViewerExtension2)sourceViewer;
int[] columns = getColumnsToDrawVerticalLinesOn();
for (int i = 0; i < columns.length; i++)
{
MarginPainter p = new MarginPainter(getSourceViewer());
p.setMarginRulerColumn(columns[i]);
p.setMarginRulerColor(verticalLineColor);
painter.addPainter(p);
}
}
}
protected abstract int[] getColumnsToDrawVerticalLinesOn();
///////////////////////////////////////////////////////////////////////////////////////////////
// Preference Page Support
///////////////////////////////////////////////////////////////////////////////////////////////
/**
* Returns true if the event will require us to perform a damage and repair
* e.g. a color preference change
*/
protected boolean affectsTextPresentation(PropertyChangeEvent event)
{
return FortranPreferences.respondToPreferenceChange(event.getProperty())
|| super.affectsTextPresentation(event);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Syntax Highlighting
///////////////////////////////////////////////////////////////////////////////////////////////
protected void configurePartitionScanner(IDocument document)
{
IDocumentPartitioner partitioner = new FastPartitioner(new RuleBasedPartitionScanner(),
PARTITION_TYPES);
partitioner.connect(document);
document.setDocumentPartitioner(partitioner);
}
protected abstract ITokenScanner getTokenScanner();
protected SourceViewerConfiguration createSourceViewerConfiguration()
{
return new FortranSourceViewerConfiguration();
}
protected class FortranSourceViewerConfiguration extends SourceViewerConfiguration
{
protected PresentationReconciler reconciler = null;
/**
* Returns a list of the possible partitions' content types.
* @see org.eclipse.jface.text.source.SourceViewerConfiguration#getConfiguredContentTypes(org.eclipse.jface.text.source.ISourceViewer)
*/
public String[] getConfiguredContentTypes(ISourceViewer sourceViewer)
{
return PARTITION_TYPES;
}
/**
* Sets up rules for syntax highlighting.
* @see org.eclipse.jface.text.source.SourceViewerConfiguration#getPresentationReconciler(org.eclipse.jface.text.source.ISourceViewer)
*/
public IPresentationReconciler getPresentationReconciler(ISourceViewer sourceViewer)
{
if (reconciler == null)
{
reconciler = new PresentationReconciler();
// Set up a damager-repairer for each content type
DefaultDamagerRepairer dr = new DefaultDamagerRepairer(getTokenScanner());
reconciler.setDamager(dr, IDocument.DEFAULT_CONTENT_TYPE);
reconciler.setRepairer(dr, IDocument.DEFAULT_CONTENT_TYPE);
}
return reconciler;
}
/*
* The CReconciler is used to ensure that an ElementChangedEvent is fired.
* Without this, the Outline view says "Pending..." but never populates.
*
* From Anton Leherbaurer (cdt-dev, 8/16/07):
* The outline view waits for the initial reconciler to run and it requires
* an ElementChangedEvent when it is done to populate the view.
* See CContentOutlinerProvider$ElementChangedListener#elementChanged().
* The event should usually be issued from the
* ReconcileWorkingCopyOperation.
*/
public IReconciler getReconciler(ISourceViewer sourceViewer)
{
MonoReconciler reconciler = new CReconciler(AbstractFortranEditor.this, new CReconcilingStrategy(AbstractFortranEditor.this));
reconciler.setIsIncrementalReconciler(false);
reconciler.setProgressMonitor(new NullProgressMonitor());
reconciler.setDelay(500);
return reconciler;
}
}
/**
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
*/
public Object getAdapter(Class required) {
if (IContentOutlinePage.class.equals(required)) {
return getOutlinePage();
}
if (required == IShowInTargetList.class) {
return new IShowInTargetList() {
public String[] getShowInTargetIds() {
return new String[] { CUIPlugin.CVIEW_ID, IPageLayout.ID_OUTLINE, IPageLayout.ID_RES_NAV };
}
};
}
if (required == IShowInSource.class) {
ICElement ce= null;
try {
ce= SelectionConverter.getElementAtOffset(this);
} catch (CModelException ex) {
ce= null;
}
if (ce != null) {
final ISelection selection= new StructuredSelection(ce);
return new IShowInSource() {
public ShowInContext getShowInContext() {
return new ShowInContext(getEditorInput(), selection);
}
};
}
}
return super.getAdapter(required);
}
/**
* Gets the outline page of the c-editor.
* @return Outline page.
*/
public CContentOutlinePage getOutlinePage() {
if (fOutlinePage == null) {
fOutlinePage = new CContentOutlinePage(null);
fOutlinePage.addSelectionChangedListener(this);
}
setOutlinePageInput(fOutlinePage, getEditorInput());
return fOutlinePage;
}
/**
* Sets an input for the outline page.
* @param page Page to set the input.
* @param input Input to set.
*/
public static void setOutlinePageInput(CContentOutlinePage page, IEditorInput input) {
if (page != null) {
IWorkingCopyManager manager = CUIPlugin.getDefault().getWorkingCopyManager();
IWorkingCopy workingCopy = manager.getWorkingCopy(input);
page.setInput(workingCopy);
}
}
// /**
// * Gets the outline page of the c-editor.
// *
// * @return Outline page.
// */
// public CContentOutlinePage getOutlinePage() {
// if (fOutlinePage == null) {
// // CContentOutlinePage currently does nothing with its editor
// // parameter,
// // so we can pass in null rather than trying to convince it to use
// // our
// // editor (e.g., by subclassing CEditor).
// fOutlinePage = new CContentOutlinePage(null);
// fOutlinePage.addSelectionChangedListener(this);
// }
// setOutlinePageInput(fOutlinePage, getEditorInput());
// return fOutlinePage;
// }
//
// /**
// * Sets an input for the outline page.
// *
// * @param page
// * Page to set the input.
// * @param input
// * Input to set.
// */
// public static void setOutlinePageInput(CContentOutlinePage page,
// IEditorInput input) {
// if (page != null) {
// IWorkingCopyManager manager = CUIPlugin.getDefault()
// .getWorkingCopyManager();
// page.setInput(manager.getWorkingCopy(input));
// }
// }
// ISelectionChangedListener Implementation ///////////////////////////////////////////////////
// (for updating editor when Outline clicked)
/**
* React to changed selection in the outline view.
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
ISelection sel = event.getSelection();
if (sel instanceof IStructuredSelection) {
IStructuredSelection selection = (IStructuredSelection) sel;
Object obj = selection.getFirstElement();
if (obj instanceof ISourceReference) {
try {
ISourceRange range = ((ISourceReference) obj).getSourceRange();
if (range != null) {
setSelection(range, !isActivePart());
}
} catch (CModelException e) {
// Selection change not applied.
}
}
}
}
/**
* Sets the current editor selection to the source range. Optionally
* sets the current editor position.
*
* @param element the source range to be shown in the editor, can be null.
* @param moveCursor if true the editor is scrolled to show the range.
*/
public void setSelection(ISourceRange element, boolean moveCursor) {
if (element == null) {
return;
}
try {
IRegion alternateRegion = null;
int start = element.getStartPos();
int length = element.getLength();
// Sanity check sometimes the parser may throw wrong numbers.
if (start < 0 || length < 0) {
start = 0;
length = 0;
}
// 0 length and start and non-zero start line says we know
// the line for some reason, but not the offset.
if (length == 0 && start == 0 && element.getStartLine() > 0) {
// We have the information in term of lines, we can work it out.
// Binary elements return the first executable statement so we have to substract -1
start = getDocumentProvider().getDocument(getEditorInput()).getLineOffset(element.getStartLine() - 1);
if (element.getEndLine() > 0) {
length = getDocumentProvider().getDocument(getEditorInput()).getLineOffset(element.getEndLine()) - start;
} else {
length = start;
}
// create an alternate region for the keyword highlight.
alternateRegion = getDocumentProvider().getDocument(getEditorInput()).getLineInformation(element.getStartLine() - 1);
if (start == length || length < 0) {
if (alternateRegion != null) {
start = alternateRegion.getOffset();
length = alternateRegion.getLength();
}
}
}
setHighlightRange(start, length, moveCursor);
if (moveCursor) {
start = element.getIdStartPos();
length = element.getIdLength();
if (start == 0 && length == 0 && alternateRegion != null) {
start = alternateRegion.getOffset();
length = alternateRegion.getLength();
}
if (start > -1 && getSourceViewer() != null) {
getSourceViewer().revealRange(start, length);
getSourceViewer().setSelectedRange(start, length);
}
updateStatusField(ITextEditorActionConstants.STATUS_CATEGORY_INPUT_POSITION);
}
return;
} catch (IllegalArgumentException x) {
// No information to the user
} catch (BadLocationException e) {
// No information to the user
}
if (moveCursor)
resetHighlightRange();
}
/**
* Checks is the editor active part.
* @return <code>true</code> if editor is the active part of the workbench.
*/
private boolean isActivePart() {
IWorkbenchWindow window = getSite().getWorkbenchWindow();
IPartService service = window.getPartService();
return (this == service.getActivePart());
}
///////////////////////////////////////////////////////////////////////////////////////////////
// Utility Methods
///////////////////////////////////////////////////////////////////////////////////////////////
public abstract boolean isFixedForm();
public IFile getIFile()
{
IEditorInput input = getEditorInput();
return (input != null && input instanceof IFileEditorInput ? ((IFileEditorInput)input).getFile() : null);
}
public IDocument getIDocument()
{
IEditorInput input = getEditorInput();
if (input == null) return null;
IDocumentProvider dp = getDocumentProvider();
if (dp == null) return null;
return dp.getDocument(input);
}
public ITextSelection getSelection()
{
ISelectionProvider provider = getSelectionProvider();
if (provider == null) return null;
ISelection sel = provider.getSelection();
if (!(sel instanceof ITextSelection)) return null;
return (ITextSelection)sel;
}
public Shell getShell()
{
return getSite().getShell();
}
public void forceOutlineViewUpdate()
{
// // /// //// // // ///
// // // // // // // /////
////// ////// // //// ///
// // // // // // //
// // // // //// // // //
IDocument doc = getIDocument();
if (doc == null) return;
doc.set(" " + doc.get());
doSave(null);
doc.set(doc.get().substring(1));
doSave(null);
}
}