Java tutorial
/******************************************************************************* * 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.editor.CEditorMessages; import org.eclipse.cdt.internal.ui.editor.ICEditorActionDefinitionIds; import org.eclipse.cdt.internal.ui.text.CCompositeReconcilingStrategy; 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.IConfigurationElement; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.Platform; 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.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.ContentAssistAction; import org.eclipse.ui.texteditor.DefaultRangeIndicator; import org.eclipse.ui.texteditor.IDocumentProvider; import org.eclipse.ui.texteditor.ITextEditorActionConstants; import org.eclipse.ui.texteditor.ITextEditorActionDefinitionIds; import org.eclipse.ui.texteditor.TextOperationAction; 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 final String SOURCE_VIEWER_CONFIG_EXTENSION_POINT_ID = "org.eclipse.photran.ui.sourceViewerConfig"; public 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); /////////////////////////////////////////////////////////////////////////////////////////////// // Public Fields - Custom Reconciler Task Support /////////////////////////////////////////////////////////////////////////////////////////////// /** @see org.eclipse.photran.internal.ui.editor_vpg.FortranVPGReconcilingStrategy */ public Object reconcilerTasks = null; /////////////////////////////////////////////////////////////////////////////////////////////// // 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(); } protected void createActions() { super.createActions(); // See CEditor#createActions IAction action = new TextOperationAction(CEditorMessages.getResourceBundle(), "Format.", this, //$NON-NLS-1$ ISourceViewer.FORMAT); action.setActionDefinitionId(ICEditorActionDefinitionIds.FORMAT); setAction("Format", action); //$NON-NLS-1$ markAsStateDependentAction("Format", true); //$NON-NLS-1$ action = new ContentAssistAction(CEditorMessages.getResourceBundle(), "ContentAssistProposal.", this); //$NON-NLS-1$ action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_PROPOSALS); setAction("ContentAssistProposal", action); //$NON-NLS-1$ markAsStateDependentAction("ContentAssistProposal", true); //$NON-NLS-1$ action = new TextOperationAction(CEditorMessages.getResourceBundle(), "ContentAssistContextInformation.", //$NON-NLS-1$ this, ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); action.setActionDefinitionId(ITextEditorActionDefinitionIds.CONTENT_ASSIST_CONTEXT_INFORMATION); setAction("ContentAssistContextInformation", action); //$NON-NLS-1$ markAsStateDependentAction("ContentAssistContextInformation", true); //$NON-NLS-1$ } /////////////////////////////////////////////////////////////////////////////////////////////// // Ctrl+/ Block Commenting Support /////////////////////////////////////////////////////////////////////////////////////////////// protected void initializeKeyBindingScopes() { setKeyBindingScopes(new String[] { "org.eclipse.ui.textEditorScope", FORTRAN_EDITOR_CONTEXT_ID }); } /////////////////////////////////////////////////////////////////////////////////////////////// // 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); } /////////////////////////////////////////////////////////////////////////////////////////////// // Extensible SourceViewerConfiguration (permits Declaration View, content assist, etc.) /////////////////////////////////////////////////////////////////////////////////////////////// protected SourceViewerConfiguration createSourceViewerConfiguration() { // If org.eclipse.photran.vpg.ui is contributing a SourceViewerConfiguration through the // extension point, load it IConfigurationElement[] configs = Platform.getExtensionRegistry() .getConfigurationElementsFor(SOURCE_VIEWER_CONFIG_EXTENSION_POINT_ID); if (configs.length > 0) { try { IFortranSourceViewerConfigurationFactory factory = (IFortranSourceViewerConfigurationFactory) configs[configs.length - 1].createExecutableExtension("factory"); return factory.create(AbstractFortranEditor.this); } catch (CoreException e) { // Fall through } } // Otherwise, default to CDT's reconciler return new FortranModelReconcilingSourceViewerConfiguration(this); } /////////////////////////////////////////////////////////////////////////////////////////////// // 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(); public static abstract class FortranSourceViewerConfiguration extends SourceViewerConfiguration { protected PresentationReconciler reconciler; /** * 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; } protected abstract ITokenScanner getTokenScanner(); } /////////////////////////////////////////////////////////////////////////////////////////////// // Outline View Support /////////////////////////////////////////////////////////////////////////////////////////////// public static class FortranModelReconcilingSourceViewerConfiguration extends FortranSourceViewerConfiguration { protected AbstractFortranEditor editor; public FortranModelReconcilingSourceViewerConfiguration(AbstractFortranEditor editor) { this.editor = editor; } /* * 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 r = new CReconciler(editor, new CReconcilingStrategy(editor)); MonoReconciler r = new CReconciler(editor, new CCompositeReconcilingStrategy(sourceViewer, editor, getConfiguredDocumentPartitioning(sourceViewer))); r.setIsIncrementalReconciler(false); r.setProgressMonitor(new NullProgressMonitor()); r.setDelay(500); return r; } protected ITokenScanner getTokenScanner() { return editor.getTokenScanner(); } } /** * @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 ISourceViewer getSourceViewerx() // Annoyingly, the superclass method is declared final { return super.getSourceViewer(); } public IReconciler getReconciler() { return getSourceViewerConfiguration().getReconciler(getSourceViewer()); } public void forceOutlineViewUpdate() { // // /// //// // // /// // // // // // // // ///// ////// ////// // //// /// // // // // // // // // // // // //// // // // IDocument doc = getIDocument(); if (doc == null) return; doc.set(" " + doc.get()); doSave(null); doc.set(doc.get().substring(1)); doSave(null); } }