Mercurial > projects > dwt-addons
view dwtx/jface/text/source/projection/ProjectionViewer.d @ 158:25f1f92fa3df
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Tue, 26 Aug 2008 02:46:34 +0200 |
parents | f70d9508c95c |
children | 7926b636c282 |
line wrap: on
line source
/******************************************************************************* * Copyright (c) 2000, 2008 IBM Corporation 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: * IBM Corporation - initial API and implementation * Port to the D programming language: * Frank Benoit <benoit@tionex.de> *******************************************************************************/ module dwtx.jface.text.source.projection.ProjectionViewer; import dwtx.jface.text.source.projection.ProjectionSupport; // packageimport import dwtx.jface.text.source.projection.IProjectionPosition; // packageimport import dwtx.jface.text.source.projection.AnnotationBag; // packageimport import dwtx.jface.text.source.projection.ProjectionSummary; // packageimport import dwtx.jface.text.source.projection.ProjectionAnnotationHover; // packageimport import dwtx.jface.text.source.projection.ProjectionRulerColumn; // packageimport import dwtx.jface.text.source.projection.ProjectionAnnotationModel; // packageimport import dwtx.jface.text.source.projection.SourceViewerInformationControl; // packageimport import dwtx.jface.text.source.projection.IProjectionListener; // packageimport import dwtx.jface.text.source.projection.ProjectionAnnotation; // packageimport import dwt.dwthelper.utils; import dwtx.dwtxhelper.Collection; import dwt.DWTError; import dwt.custom.ST; import dwt.custom.StyledText; import dwt.dnd.Clipboard; import dwt.dnd.DND; import dwt.dnd.TextTransfer; import dwt.dnd.Transfer; import dwt.events.VerifyEvent; import dwt.graphics.Point; import dwt.widgets.Composite; import dwt.widgets.Display; import dwtx.core.runtime.Assert; import dwtx.jface.text.BadLocationException; import dwtx.jface.text.DocumentEvent; import dwtx.jface.text.FindReplaceDocumentAdapter; import dwtx.jface.text.IDocument; import dwtx.jface.text.IDocumentInformationMappingExtension; import dwtx.jface.text.IDocumentListener; import dwtx.jface.text.IRegion; import dwtx.jface.text.ISlaveDocumentManager; import dwtx.jface.text.ITextViewerExtension5; import dwtx.jface.text.Position; import dwtx.jface.text.Region; import dwtx.jface.text.TextUtilities; import dwtx.jface.text.projection.ProjectionDocument; import dwtx.jface.text.projection.ProjectionDocumentEvent; import dwtx.jface.text.projection.ProjectionDocumentManager; import dwtx.jface.text.source.Annotation; import dwtx.jface.text.source.AnnotationModelEvent; import dwtx.jface.text.source.CompositeRuler; import dwtx.jface.text.source.IAnnotationModel; import dwtx.jface.text.source.IAnnotationModelExtension; import dwtx.jface.text.source.IAnnotationModelListener; import dwtx.jface.text.source.IAnnotationModelListenerExtension; import dwtx.jface.text.source.IOverviewRuler; import dwtx.jface.text.source.IVerticalRuler; import dwtx.jface.text.source.IVerticalRulerColumn; import dwtx.jface.text.source.SourceViewer; /** * A projection source viewer is a source viewer which supports multiple visible * regions which can dynamically be changed. * <p> * A projection source viewer uses a <code>ProjectionDocumentManager</code> * for the management of the visible document.</p> * <p> * NOTE: The <code>ProjectionViewer</code> only supports projections that cover full lines. * </p> * <p> * This class should not be subclassed.</p> * * @since 3.0 * @noextend This class is not intended to be subclassed by clients. */ public class ProjectionViewer : SourceViewer , ITextViewerExtension5 { private static const int BASE= INFORMATION; // see ISourceViewer.INFORMATION /** Operation constant for the expand operation. */ public static const int EXPAND= BASE + 1; /** Operation constant for the collapse operation. */ public static const int COLLAPSE= BASE + 2; /** Operation constant for the toggle projection operation. */ public static const int TOGGLE= BASE + 3; /** Operation constant for the expand all operation. */ public static const int EXPAND_ALL= BASE + 4; /** * Operation constant for the collapse all operation. * * @since 3.2 */ public static const int COLLAPSE_ALL= BASE + 5; /** * Internal listener to changes of the annotation model. */ private class AnnotationModelListener : IAnnotationModelListener, IAnnotationModelListenerExtension { /* * @see dwtx.jface.text.source.IAnnotationModelListener#modelChanged(dwtx.jface.text.source.IAnnotationModel) */ public void modelChanged(IAnnotationModel model) { processModelChanged(model, null); } /* * @see dwtx.jface.text.source.IAnnotationModelListenerExtension#modelChanged(dwtx.jface.text.source.AnnotationModelEvent) */ public void modelChanged(AnnotationModelEvent event) { processModelChanged(event.getAnnotationModel(), event); } private void processModelChanged(IAnnotationModel model, AnnotationModelEvent event) { if (model is fProjectionAnnotationModel) { if (fProjectionSummary !is null) fProjectionSummary.updateSummaries(); processCatchupRequest(event); } else if (model is getAnnotationModel() && fProjectionSummary !is null) fProjectionSummary.updateSummaries(); } } /** * Executes the 'replaceVisibleDocument' operation when called the first time. Self-destructs afterwards. */ private class ReplaceVisibleDocumentExecutor : IDocumentListener { private IDocument fSlaveDocument; private IDocument fExecutionTrigger; /** * Creates a new executor in order to free the given slave document. * * @param slaveDocument the slave document to free */ public this(IDocument slaveDocument) { fSlaveDocument= slaveDocument; } /** * Installs this executor on the given trigger document. * * @param executionTrigger the trigger document */ public void install(IDocument executionTrigger) { if (executionTrigger !is null && fSlaveDocument !is null) { fExecutionTrigger= executionTrigger; fExecutionTrigger.addDocumentListener(this); } } /* * @see dwtx.jface.text.IDocumentListener#documentAboutToBeChanged(dwtx.jface.text.DocumentEvent) */ public void documentAboutToBeChanged(DocumentEvent event) { } /* * @see dwtx.jface.text.IDocumentListener#documentChanged(dwtx.jface.text.DocumentEvent) */ public void documentChanged(DocumentEvent event) { fExecutionTrigger.removeDocumentListener(this); executeReplaceVisibleDocument(fSlaveDocument); } } /** * A command representing a change of the projection document. This can be either * adding a master document range, removing a master document change, or invalidating * the viewer text presentation. */ private static class ProjectionCommand { final static int ADD= 0; final static int REMOVE= 1; final static int INVALIDATE_PRESENTATION= 2; ProjectionDocument fProjection; int fType; int fOffset; int fLength; this(ProjectionDocument projection, int type, int offset, int length) { fProjection= projection; fType= type; fOffset= offset; fLength= length; } this(int offset, int length) { fType= INVALIDATE_PRESENTATION; fOffset= offset; fLength= length; } int computeExpectedCosts() { switch(fType) { case ADD: { try { IRegion[] gaps= fProjection.computeUnprojectedMasterRegions(fOffset, fLength); return gaps is null ? 0 : gaps.length; } catch (BadLocationException x) { } break; } case REMOVE: { try { IRegion[] fragments= fProjection.computeProjectedMasterRegions(fOffset, fLength); return fragments is null ? 0 : fragments.length; } catch (BadLocationException x) { } break; } } return 0; } } /** * The queue of projection command objects. */ private static class ProjectionCommandQueue { final static int REDRAW_COSTS= 15; final static int INVALIDATION_COSTS= 10; List fList= new ArrayList(15); int fExpectedExecutionCosts= -1; void add(ProjectionCommand command) { fList.add(command); } Iterator iterator() { return fList.iterator(); } void clear() { fList.clear(); fExpectedExecutionCosts= -1; } bool passedRedrawCostsThreshold() { if (fExpectedExecutionCosts is -1) computeExpectedExecutionCosts(); return fExpectedExecutionCosts > REDRAW_COSTS; } bool passedInvalidationCostsThreshold() { if (fExpectedExecutionCosts is -1) computeExpectedExecutionCosts(); return fExpectedExecutionCosts > INVALIDATION_COSTS; } private void computeExpectedExecutionCosts() { int max_costs= Math.max(REDRAW_COSTS, INVALIDATION_COSTS); fExpectedExecutionCosts= fList.size(); if (fExpectedExecutionCosts <= max_costs) { ProjectionCommand command; Iterator e= fList.iterator(); while (e.hasNext()) { command= cast(ProjectionCommand) e.next(); fExpectedExecutionCosts += command.computeExpectedCosts(); if (fExpectedExecutionCosts > max_costs) break; } } } } /** The projection annotation model used by this viewer. */ private ProjectionAnnotationModel fProjectionAnnotationModel; /** The annotation model listener */ private IAnnotationModelListener fAnnotationModelListener= new AnnotationModelListener(); /** The projection summary. */ private ProjectionSummary fProjectionSummary; /** Indication that an annotation world change has not yet been processed. */ private bool fPendingAnnotationWorldChange= false; /** Indication whether projection changes in the visible document should be considered. */ private bool fHandleProjectionChanges= true; /** The list of projection listeners. */ private List fProjectionListeners; /** Internal lock for protecting the list of pending requests */ private Object fLock= new Object(); /** The list of pending requests */ private List fPendingRequests= new ArrayList(); /** The replace-visible-document execution trigger */ private IDocument fReplaceVisibleDocumentExecutionTrigger; /** <code>true</code> if projection was on the last time we switched to segmented mode. */ private bool fWasProjectionEnabled; /** The queue of projection commands used to assess the costs of projection changes. */ private ProjectionCommandQueue fCommandQueue; /** * The amount of lines deleted by the last document event issued by the * visible document event. * @since 3.1 */ private int fDeletedLines; /** * Creates a new projection source viewer. * * @param parent the DWT parent control * @param ruler the vertical ruler * @param overviewRuler the overview ruler * @param showsAnnotationOverview <code>true</code> if the overview ruler should be shown * @param styles the DWT style bits */ public this(Composite parent, IVerticalRuler ruler, IOverviewRuler overviewRuler, bool showsAnnotationOverview, int styles) { super(parent, ruler, overviewRuler, showsAnnotationOverview, styles); } /** * Sets the projection summary for this viewer. * * @param projectionSummary the projection summary. */ public void setProjectionSummary(ProjectionSummary projectionSummary) { fProjectionSummary= projectionSummary; } /** * Adds the projection annotation model to the given annotation model. * * @param model the model to which the projection annotation model is added */ private void addProjectionAnnotationModel(IAnnotationModel model) { if ( cast(IAnnotationModelExtension)model ) { IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model; extension.addAnnotationModel(ProjectionSupport.PROJECTION, fProjectionAnnotationModel); model.addAnnotationModelListener(fAnnotationModelListener); } } /** * Removes the projection annotation model from the given annotation model. * * @param model the mode from which the projection annotation model is removed * @return the removed projection annotation model or <code>null</code> if there was none */ private IAnnotationModel removeProjectionAnnotationModel(IAnnotationModel model) { if ( cast(IAnnotationModelExtension)model ) { model.removeAnnotationModelListener(fAnnotationModelListener); IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model; return extension.removeAnnotationModel(ProjectionSupport.PROJECTION); } return null; } /* * @see dwtx.jface.text.source.SourceViewer#setDocument(dwtx.jface.text.IDocument, dwtx.jface.text.source.IAnnotationModel, int, int) */ public void setDocument(IDocument document, IAnnotationModel annotationModel, int modelRangeOffset, int modelRangeLength) { bool wasProjectionEnabled= false; synchronized (fLock) { fPendingRequests.clear(); } if (fProjectionAnnotationModel !is null) { wasProjectionEnabled= removeProjectionAnnotationModel(getVisualAnnotationModel()) !is null; fProjectionAnnotationModel= null; } super.setDocument(document, annotationModel, modelRangeOffset, modelRangeLength); if (wasProjectionEnabled && document !is null) enableProjection(); } /* * @see dwtx.jface.text.source.SourceViewer#createVisualAnnotationModel(dwtx.jface.text.source.IAnnotationModel) */ protected IAnnotationModel createVisualAnnotationModel(IAnnotationModel annotationModel) { IAnnotationModel model= super.createVisualAnnotationModel(annotationModel); fProjectionAnnotationModel= new ProjectionAnnotationModel(); return model; } /** * Returns the projection annotation model. * * @return the projection annotation model */ public ProjectionAnnotationModel getProjectionAnnotationModel() { IAnnotationModel model= getVisualAnnotationModel(); if ( cast(IAnnotationModelExtension)model ) { IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model; return cast(ProjectionAnnotationModel) extension.getAnnotationModel(ProjectionSupport.PROJECTION); } return null; } /* * @see dwtx.jface.text.TextViewer#createSlaveDocumentManager() */ protected ISlaveDocumentManager createSlaveDocumentManager() { return new ProjectionDocumentManager(); } /* * @see dwtx.jface.text.TextViewer#updateSlaveDocument(dwtx.jface.text.IDocument, int, int) */ protected bool updateSlaveDocument(IDocument slaveDocument, int modelRangeOffset, int modelRangeLength) { if ( cast(ProjectionDocument)slaveDocument ) { ProjectionDocument projection= cast(ProjectionDocument) slaveDocument; int offset= modelRangeOffset; int length= modelRangeLength; if (!isProjectionMode()) { // mimic original TextViewer behavior IDocument master= projection.getMasterDocument(); int line= master.getLineOfOffset(modelRangeOffset); offset= master.getLineOffset(line); length= (modelRangeOffset - offset) + modelRangeLength; } try { fHandleProjectionChanges= false; projection.replaceMasterDocumentRanges(offset, length); } finally { fHandleProjectionChanges= true; } return true; } return false; } /** * Adds a projection annotation listener to this viewer. The listener may * not be <code>null</code>. If the listener is already registered, this method * does not have any effect. * * @param listener the listener to add */ public void addProjectionListener(IProjectionListener listener) { Assert.isNotNull(listener); if (fProjectionListeners is null) fProjectionListeners= new ArrayList(); if (!fProjectionListeners.contains(listener)) fProjectionListeners.add(listener); } /** * Removes the given listener from this viewer. The listener may not be * <code>null</code>. If the listener is not registered with this viewer, * this method is without effect. * * @param listener the listener to remove */ public void removeProjectionListener(IProjectionListener listener) { Assert.isNotNull(listener); if (fProjectionListeners !is null) { fProjectionListeners.remove(listener); if (fProjectionListeners.size() is 0) fProjectionListeners= null; } } /** * Notifies all registered projection listeners * that projection mode has been enabled. */ protected void fireProjectionEnabled() { if (fProjectionListeners !is null) { Iterator e= (new ArrayList(fProjectionListeners)).iterator(); while (e.hasNext()) { IProjectionListener l= cast(IProjectionListener) e.next(); l.projectionEnabled(); } } } /** * Notifies all registered projection listeners * that projection mode has been disabled. */ protected void fireProjectionDisabled() { if (fProjectionListeners !is null) { Iterator e= (new ArrayList(fProjectionListeners)).iterator(); while (e.hasNext()) { IProjectionListener l= cast(IProjectionListener) e.next(); l.projectionDisabled(); } } } /** * Returns whether this viewer is in projection mode. * * @return <code>true</code> if this viewer is in projection mode, * <code>false</code> otherwise */ public final bool isProjectionMode() { return getProjectionAnnotationModel() !is null; } /** * Disables the projection mode. */ public final void disableProjection() { if (isProjectionMode()) { removeProjectionAnnotationModel(getVisualAnnotationModel()); fProjectionAnnotationModel.removeAllAnnotations(); fFindReplaceDocumentAdapter= null; fireProjectionDisabled(); } } /** * Enables the projection mode. */ public final void enableProjection() { if (!isProjectionMode()) { addProjectionAnnotationModel(getVisualAnnotationModel()); fFindReplaceDocumentAdapter= null; fireProjectionEnabled(); } } private void expandAll() { int offset= 0; IDocument doc= getDocument(); int length= doc is null ? 0 : doc.getLength(); if (isProjectionMode()) { fProjectionAnnotationModel.expandAll(offset, length); } } private void expand() { if (isProjectionMode()) { Position found= null; Annotation bestMatch= null; Point selection= getSelectedRange(); for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) { ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); if (annotation.isCollapsed()) { Position position= fProjectionAnnotationModel.getPosition(annotation); // take the first most fine grained match if (position !is null && touches(selection, position)) if (found is null || position.includes(found.offset) && position.includes(found.offset + found.length)) { found= position; bestMatch= annotation; } } } if (bestMatch !is null) { fProjectionAnnotationModel.expand(bestMatch); revealRange(selection.x, selection.y); } } } private bool touches(Point selection, Position position) { return position.overlapsWith(selection.x, selection.y) || selection.y is 0 && position.offset + position.length is selection.x + selection.y; } private void collapse() { if (isProjectionMode()) { Position found= null; Annotation bestMatch= null; Point selection= getSelectedRange(); for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) { ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); if (!annotation.isCollapsed()) { Position position= fProjectionAnnotationModel.getPosition(annotation); // take the first most fine grained match if (position !is null && touches(selection, position)) if (found is null || found.includes(position.offset) && found.includes(position.offset + position.length)) { found= position; bestMatch= annotation; } } } if (bestMatch !is null) { fProjectionAnnotationModel.collapse(bestMatch); revealRange(selection.x, selection.y); } } } /* * @since 3.2 */ private void collapseAll() { int offset= 0; IDocument doc= getDocument(); int length= doc is null ? 0 : doc.getLength(); if (isProjectionMode()) { fProjectionAnnotationModel.collapseAll(offset, length); } } /** * Adds the given master range to the given projection document. While the * modification is processed, the viewer no longer handles projection * changes, as it is causing them. * * @param projection the projection document * @param offset the offset in the master document * @param length the length in the master document * @throws BadLocationException in case the specified range is invalid * * @see ProjectionDocument#addMasterDocumentRange(int, int) */ private void addMasterDocumentRange(ProjectionDocument projection, int offset, int length) { if (fCommandQueue !is null) { fCommandQueue.add(new ProjectionCommand(projection, ProjectionCommand.ADD, offset, length)); } else { try { fHandleProjectionChanges= false; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=108258 // make sure the document range is strictly line based int end= offset + length; offset= toLineStart(projection.getMasterDocument(), offset, false); length= toLineStart(projection.getMasterDocument(), end, true) - offset; projection.addMasterDocumentRange(offset, length); } finally { fHandleProjectionChanges= true; } } } /** * Removes the given master range from the given projection document. While the * modification is processed, the viewer no longer handles projection * changes, as it is causing them. * * @param projection the projection document * @param offset the offset in the master document * @param length the length in the master document * @throws BadLocationException in case the specified range is invalid * * @see ProjectionDocument#removeMasterDocumentRange(int, int) */ private void removeMasterDocumentRange(ProjectionDocument projection, int offset, int length) { if (fCommandQueue !is null) { fCommandQueue.add(new ProjectionCommand(projection, ProjectionCommand.REMOVE, offset, length)); } else { try { fHandleProjectionChanges= false; // https://bugs.eclipse.org/bugs/show_bug.cgi?id=108258 // make sure the document range is strictly line based int end= offset + length; offset= toLineStart(projection.getMasterDocument(), offset, false); length= toLineStart(projection.getMasterDocument(), end, true) - offset; projection.removeMasterDocumentRange(offset, length); } finally { fHandleProjectionChanges= true; } } } /** * Returns the first line offset <= <code>offset</code>. If <code>testLastLine</code> * is <code>true</code> and the offset is on last line then <code>offset</code> is returned. * * @param document the document * @param offset the master document offset * @param testLastLine <code>true</code> if the test for the last line should be performed * @return the closest line offset >= <code>offset</code> * @throws BadLocationException if the offset is invalid * @since 3.2 */ private int toLineStart(IDocument document, int offset, bool testLastLine) { if (document is null) return offset; if (testLastLine && offset >= document.getLineInformationOfOffset(document.getLength() - 1).getOffset()) return offset; return document.getLineInformationOfOffset(offset).getOffset(); } /* * @see dwtx.jface.text.TextViewer#setVisibleRegion(int, int) */ public void setVisibleRegion(int start, int length) { fWasProjectionEnabled= isProjectionMode(); disableProjection(); super.setVisibleRegion(start, length); } /* * @see dwtx.jface.text.TextViewer#setVisibleDocument(dwtx.jface.text.IDocument) */ protected void setVisibleDocument(IDocument document) { if (!isProjectionMode()) { super.setVisibleDocument(document); return; } // In projection mode we don't want to throw away the find/replace document adapter FindReplaceDocumentAdapter adapter= fFindReplaceDocumentAdapter; super.setVisibleDocument(document); fFindReplaceDocumentAdapter= adapter; } /* * @see dwtx.jface.text.TextViewer#resetVisibleRegion() */ public void resetVisibleRegion() { super.resetVisibleRegion(); if (fWasProjectionEnabled) enableProjection(); } /* * @see dwtx.jface.text.ITextViewer#getVisibleRegion() */ public IRegion getVisibleRegion() { disableProjection(); IRegion visibleRegion= getModelCoverage(); if (visibleRegion is null) visibleRegion= new Region(0, 0); return visibleRegion; } /* * @see dwtx.jface.text.ITextViewer#overlapsWithVisibleRegion(int,int) */ public bool overlapsWithVisibleRegion(int offset, int length) { disableProjection(); IRegion coverage= getModelCoverage(); if (coverage is null) return false; bool appending= (offset is coverage.getOffset() + coverage.getLength()) && length is 0; return appending || TextUtilities.overlaps(coverage, new Region(offset, length)); } /** * Replace the visible document with the given document. Maintains the * scroll offset and the selection. * * @param slave the visible document */ private void replaceVisibleDocument(IDocument slave) { if (fReplaceVisibleDocumentExecutionTrigger !is null) { ReplaceVisibleDocumentExecutor executor= new ReplaceVisibleDocumentExecutor(slave); executor.install(fReplaceVisibleDocumentExecutionTrigger); } else executeReplaceVisibleDocument(slave); } private void executeReplaceVisibleDocument(IDocument visibleDocument) { StyledText textWidget= getTextWidget(); try { if (textWidget !is null && !textWidget.isDisposed()) textWidget.setRedraw(false); int topIndex= getTopIndex(); Point selection= getSelectedRange(); setVisibleDocument(visibleDocument); Point currentSelection= getSelectedRange(); if (currentSelection.x !is selection.x || currentSelection.y !is selection.y) setSelectedRange(selection.x, selection.y); setTopIndex(topIndex); } finally { if (textWidget !is null && !textWidget.isDisposed()) textWidget.setRedraw(true); } } /** * Hides the given range by collapsing it. If requested, a redraw request is issued. * * @param offset the offset of the range to hide * @param length the length of the range to hide * @param fireRedraw <code>true</code> if a redraw request should be issued, <code>false</code> otherwise * @throws BadLocationException in case the range is invalid */ private void collapse(int offset, int length, bool fireRedraw) { ProjectionDocument projection= null; IDocument visibleDocument= getVisibleDocument(); if ( cast(ProjectionDocument)visibleDocument ) projection= cast(ProjectionDocument) visibleDocument; else { IDocument master= getDocument(); IDocument slave= createSlaveDocument(getDocument()); if ( cast(ProjectionDocument)slave ) { projection= cast(ProjectionDocument) slave; addMasterDocumentRange(projection, 0, master.getLength()); replaceVisibleDocument(projection); } } if (projection !is null) removeMasterDocumentRange(projection, offset, length); if (projection !is null && fireRedraw) { // repaint line above to get the folding box IDocument document= getDocument(); int line= document.getLineOfOffset(offset); if (line > 0) { IRegion info= document.getLineInformation(line - 1); internalInvalidateTextPresentation(info.getOffset(), info.getLength()); } } } /** * Makes the given range visible again while not changing the folding state of any contained * ranges. If requested, a redraw request is issued. * * @param offset the offset of the range to be expanded * @param length the length of the range to be expanded * @param fireRedraw <code>true</code> if a redraw request should be issued, * <code>false</code> otherwise * @throws BadLocationException in case the range is invalid */ private void expand(int offset, int length, bool fireRedraw) { IDocument slave= getVisibleDocument(); if ( cast(ProjectionDocument)slave ) { ProjectionDocument projection= cast(ProjectionDocument) slave; // expand addMasterDocumentRange(projection, offset, length); // collapse contained regions ProjectionAnnotation[] collapsed= computeCollapsedNestedAnnotations(offset, length); if (collapsed !is null) { for (int i= 0; i < collapsed.length; i++) { IRegion[] regions= computeCollapsedRegions(fProjectionAnnotationModel.getPosition(collapsed[i])); if (regions !is null) for (int j= 0; j < regions.length; j++) removeMasterDocumentRange(projection, regions[j].getOffset(), regions[j].getLength()); } } // redraw if requested if (fireRedraw) internalInvalidateTextPresentation(offset, length); } } /** * Processes the request for catch up with the annotation model in the UI thread. If the current * thread is not the UI thread or there are pending catch up requests, a new request is posted. * * @param event the annotation model event */ protected final void processCatchupRequest(AnnotationModelEvent event) { if (Display.getCurrent() !is null) { bool run= false; synchronized (fLock) { run= fPendingRequests.isEmpty(); } if (run) { try { catchupWithProjectionAnnotationModel(event); } catch (BadLocationException x) { throw new IllegalArgumentException(); } } else postCatchupRequest(event); } else { postCatchupRequest(event); } } /** * Posts the request for catch up with the annotation model into the UI thread. * * @param event the annotation model event */ protected final void postCatchupRequest(AnnotationModelEvent event) { synchronized (fLock) { fPendingRequests.add(event); if (fPendingRequests.size() is 1) { StyledText widget= getTextWidget(); if (widget !is null) { Display display= widget.getDisplay(); if (display !is null) { display.asyncExec(new class() Runnable { public void run() { try { while (true) { AnnotationModelEvent ame= null; synchronized (fLock) { if (fPendingRequests.size() is 0) return; ame= cast(AnnotationModelEvent) fPendingRequests.remove(0); } catchupWithProjectionAnnotationModel(ame); } } catch (BadLocationException x) { try { catchupWithProjectionAnnotationModel(null); } catch (BadLocationException x1) { throw new IllegalArgumentException(); } finally { synchronized (fLock) { fPendingRequests.clear(); } } } } }); } } } } } /** * Tests whether the visible document's master document * is identical to this viewer's document. * * @return <code>true</code> if the visible document's master is * identical to this viewer's document * @since 3.1 */ private bool isVisibleMasterDocumentSameAsDocument() { IDocument visibleDocument= getVisibleDocument(); return ( cast(ProjectionDocument)visibleDocument ) && (cast(ProjectionDocument)visibleDocument).getMasterDocument() is getDocument(); } /** * Adapts the slave visual document of this viewer to the changes described * in the annotation model event. When the event is <code>null</code>, * this is identical to a world change event. * * @param event the annotation model event or <code>null</code> * @exception BadLocationException in case the annotation model event is no longer in synchronization with the document */ private void catchupWithProjectionAnnotationModel(AnnotationModelEvent event) { if (event is null || !isVisibleMasterDocumentSameAsDocument()) { fPendingAnnotationWorldChange= false; reinitializeProjection(); } else if (event.isWorldChange()) { if (event.isValid()) { fPendingAnnotationWorldChange= false; reinitializeProjection(); } else fPendingAnnotationWorldChange= true; } else if (fPendingAnnotationWorldChange) { if (event.isValid()) { fPendingAnnotationWorldChange= false; reinitializeProjection(); } } else { Annotation[] addedAnnotations= event.getAddedAnnotations(); Annotation[] changedAnnotation= event.getChangedAnnotations(); Annotation[] removedAnnotations= event.getRemovedAnnotations(); fCommandQueue= new ProjectionCommandQueue(); bool isRedrawing= redraws(); int topIndex= isRedrawing ? getTopIndex() : -1; processDeletions(event, removedAnnotations, true); List coverage= new ArrayList(); processChanges(addedAnnotations, true, coverage); processChanges(changedAnnotation, true, coverage); ProjectionCommandQueue commandQueue= fCommandQueue; fCommandQueue= null; if (commandQueue.passedRedrawCostsThreshold()) { setRedraw(false); try { executeProjectionCommands(commandQueue, false); } catch (IllegalArgumentException x) { reinitializeProjection(); } finally { setRedraw(true, topIndex); } } else { try { bool fireRedraw= !commandQueue.passedInvalidationCostsThreshold(); executeProjectionCommands(commandQueue, fireRedraw); if (!fireRedraw) invalidateTextPresentation(); } catch (IllegalArgumentException x) { reinitializeProjection(); } } } } private void executeProjectionCommands(ProjectionCommandQueue commandQueue, bool fireRedraw) { ProjectionCommand command; Iterator e= commandQueue.iterator(); while (e.hasNext()) { command= cast(ProjectionCommand) e.next(); switch (command.fType) { case ProjectionCommand.ADD: addMasterDocumentRange(command.fProjection, command.fOffset, command.fLength); break; case ProjectionCommand.REMOVE: removeMasterDocumentRange(command.fProjection, command.fOffset, command.fLength); break; case ProjectionCommand.INVALIDATE_PRESENTATION: if (fireRedraw) invalidateTextPresentation(command.fOffset, command.fLength); break; } } commandQueue.clear(); } private bool covers(int offset, int length, Position position) { if (!(position.offset is offset && position.length is length) && !position.isDeleted()) return offset <= position.getOffset() && position.getOffset() + position.getLength() <= offset + length; return false; } private ProjectionAnnotation[] computeCollapsedNestedAnnotations(int offset, int length) { List annotations= new ArrayList(5); Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); while (e.hasNext()) { ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); if (annotation.isCollapsed()) { Position position= fProjectionAnnotationModel.getPosition(annotation); if (position is null) { // annotation might already be deleted, we will be informed later on about this deletion continue; } if (covers(offset, length, position)) annotations.add(annotation); } } if (annotations.size() > 0) { ProjectionAnnotation[] result= new ProjectionAnnotation[annotations.size()]; annotations.toArray(result); return result; } return null; } private void internalInvalidateTextPresentation(int offset, int length) { if (fCommandQueue !is null) { fCommandQueue.add(new ProjectionCommand(offset, length)); } else { invalidateTextPresentation(offset, length); } } /* * We pass the removed annotation into this method for performance reasons only. Otherwise, they could be fetch from the event. */ private void processDeletions(AnnotationModelEvent event, Annotation[] removedAnnotations, bool fireRedraw) { for (int i= 0; i < removedAnnotations.length; i++) { ProjectionAnnotation annotation= cast(ProjectionAnnotation) removedAnnotations[i]; if (annotation.isCollapsed()) { Position expanded= event.getPositionOfRemovedAnnotation(annotation); expand(expanded.getOffset(), expanded.getLength(), fireRedraw); } } } /** * Computes the region that must be collapsed when the given position is the * position of an expanded projection annotation. * * @param position the position * @return the range that must be collapsed */ public IRegion computeCollapsedRegion(Position position) { try { IDocument document= getDocument(); if (document is null) return null; int line= document.getLineOfOffset(position.getOffset()); int offset= document.getLineOffset(line + 1); int length= position.getLength() - (offset - position.getOffset()); if (length > 0) return new Region(offset, length); } catch (BadLocationException x) { } return null; } /** * Computes the regions that must be collapsed when the given position is * the position of an expanded projection annotation. * * @param position the position * @return the ranges that must be collapsed, or <code>null</code> if * there are none * @since 3.1 */ IRegion[] computeCollapsedRegions(Position position) { try { IDocument document= getDocument(); if (document is null) return null; if ( cast(IProjectionPosition)position ) { IProjectionPosition projPosition= cast(IProjectionPosition) position; return projPosition.computeProjectionRegions(document); } int line= document.getLineOfOffset(position.getOffset()); int offset= document.getLineOffset(line + 1); int length= position.getLength() - (offset - position.getOffset()); if (length > 0) return [new Region(offset, length)]; return null; } catch (BadLocationException x) { return null; } } /** * Computes the collapsed region anchor for the given position. Assuming * that the position is the position of an expanded projection annotation, * the anchor is the region that is still visible after the projection * annotation has been collapsed. * * @param position the position * @return the collapsed region anchor */ public Position computeCollapsedRegionAnchor(Position position) { try { IDocument document= getDocument(); if (document is null) return null; int captionOffset= position.getOffset(); if ( cast(IProjectionPosition)position ) captionOffset+= (cast(IProjectionPosition) position).computeCaptionOffset(document); IRegion lineInfo= document.getLineInformationOfOffset(captionOffset); return new Position(lineInfo.getOffset() + lineInfo.getLength(), 0); } catch (BadLocationException x) { } return null; } private void processChanges(Annotation[] annotations, bool fireRedraw, List coverage) { for (int i= 0; i < annotations.length; i++) { ProjectionAnnotation annotation= cast(ProjectionAnnotation) annotations[i]; Position position= fProjectionAnnotationModel.getPosition(annotation); if (position is null) continue; if (!covers(coverage, position)) { if (annotation.isCollapsed()) { coverage.add(position); IRegion[] regions= computeCollapsedRegions(position); if (regions !is null) for (int j= 0; j < regions.length; j++) collapse(regions[j].getOffset(), regions[j].getLength(), fireRedraw); } else { expand(position.getOffset(), position.getLength(), fireRedraw); } } } } private bool covers(List coverage, Position position) { Iterator e= coverage.iterator(); while (e.hasNext()) { Position p= cast(Position) e.next(); if (p.getOffset() <= position.getOffset() && position.getOffset() + position.getLength() <= p.getOffset() + p.getLength()) return true; } return false; } /** * Forces this viewer to throw away any old state and to initialize its content * from its projection annotation model. * * @throws BadLocationException in case something goes wrong during initialization */ public final void reinitializeProjection() { ProjectionDocument projection= null; ISlaveDocumentManager manager= getSlaveDocumentManager(); if (manager !is null) { IDocument master= getDocument(); if (master !is null) { IDocument slave= manager.createSlaveDocument(master); if ( cast(ProjectionDocument)slave ) { projection= cast(ProjectionDocument) slave; addMasterDocumentRange(projection, 0, master.getLength()); } } } if (projection !is null) { Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); while (e.hasNext()) { ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); if (annotation.isCollapsed()) { Position position= fProjectionAnnotationModel.getPosition(annotation); if (position !is null) { IRegion[] regions= computeCollapsedRegions(position); if (regions !is null) for (int i= 0; i < regions.length; i++) removeMasterDocumentRange(projection, regions[i].getOffset(), regions[i].getLength()); } } } } replaceVisibleDocument(projection); } /* * @see dwtx.jface.text.TextViewer#handleVerifyEvent(dwt.events.VerifyEvent) */ protected void handleVerifyEvent(VerifyEvent e) { IRegion modelRange= event2ModelRange(e); if (exposeModelRange(modelRange)) e.doit= false; else super.handleVerifyEvent(e); } /** * Adds the give column as last column to this viewer's vertical ruler. * * @param column the column to be added */ public void addVerticalRulerColumn(IVerticalRulerColumn column) { IVerticalRuler ruler= getVerticalRuler(); if ( cast(CompositeRuler)ruler ) { CompositeRuler compositeRuler= cast(CompositeRuler) ruler; compositeRuler.addDecorator(99, column); } } /** * Removes the give column from this viewer's vertical ruler. * * @param column the column to be removed */ public void removeVerticalRulerColumn(IVerticalRulerColumn column) { IVerticalRuler ruler= getVerticalRuler(); if ( cast(CompositeRuler)ruler ) { CompositeRuler compositeRuler= cast(CompositeRuler) ruler; compositeRuler.removeDecorator(column); } } /* * @see dwtx.jface.text.ITextViewerExtension5#exposeModelRange(dwtx.jface.text.IRegion) */ public bool exposeModelRange(IRegion modelRange) { if (isProjectionMode()) return fProjectionAnnotationModel.expandAll(modelRange.getOffset(), modelRange.getLength()); if (!overlapsWithVisibleRegion(modelRange.getOffset(), modelRange.getLength())) { resetVisibleRegion(); return true; } return false; } /* * @see dwtx.jface.text.source.SourceViewer#setRangeIndication(int, int, bool) */ public void setRangeIndication(int offset, int length, bool moveCursor) { IRegion rangeIndication= getRangeIndication(); if (moveCursor && fProjectionAnnotationModel !is null && (rangeIndication is null || offset !is rangeIndication.getOffset() || length !is rangeIndication.getLength())) { List expand= new ArrayList(2); // expand the immediate effected collapsed regions Iterator iterator= fProjectionAnnotationModel.getAnnotationIterator(); while (iterator.hasNext()) { ProjectionAnnotation annotation= cast(ProjectionAnnotation)iterator.next(); if (annotation.isCollapsed() && willAutoExpand(fProjectionAnnotationModel.getPosition(annotation), offset, length)) expand.add(annotation); } if (!expand.isEmpty()) { Iterator e= expand.iterator(); while (e.hasNext()) fProjectionAnnotationModel.expand(cast(Annotation)e.next()); } } super.setRangeIndication(offset, length, moveCursor); } private bool willAutoExpand(Position position, int offset, int length) { if (position is null || position.isDeleted()) return false; // right or left boundary if (position.getOffset() is offset || position.getOffset() + position.getLength() is offset + length) return true; // completely embedded in given position if (position.getOffset() < offset && offset + length < position.getOffset() + position.getLength()) return true; return false; } /* * @see dwtx.jface.text.source.SourceViewer#handleDispose() * @since 3.0 */ protected void handleDispose() { fWasProjectionEnabled= false; super.handleDispose(); } /* * @see dwtx.jface.text.TextViewer#handleVisibleDocumentAboutToBeChanged(dwtx.jface.text.DocumentEvent) */ protected void handleVisibleDocumentChanged(DocumentEvent event) { if (fHandleProjectionChanges && cast(ProjectionDocumentEvent)event && isProjectionMode()) { ProjectionDocumentEvent e= cast(ProjectionDocumentEvent) event; DocumentEvent master= e.getMasterEvent(); if (master !is null) fReplaceVisibleDocumentExecutionTrigger= master.getDocument(); try { int replaceLength= e.getText() is null ? 0 : e.getText().length(); if (ProjectionDocumentEvent.PROJECTION_CHANGE is e.getChangeType()) { if (e.getLength() is 0 && replaceLength !is 0) fProjectionAnnotationModel.expandAll(e.getMasterOffset(), e.getMasterLength()); } else if (master !is null && (replaceLength > 0 || fDeletedLines > 1)) { try { int numberOfLines= e.getDocument().getNumberOfLines(e.getOffset(), replaceLength); if (numberOfLines > 1 || fDeletedLines > 1) fProjectionAnnotationModel.expandAll(master.getOffset(), master.getLength()); } catch (BadLocationException x) { } } } finally { fReplaceVisibleDocumentExecutionTrigger= null; } } } /* * @see dwtx.jface.text.TextViewer#handleVisibleDocumentAboutToBeChanged(dwtx.jface.text.DocumentEvent) * @since 3.1 */ protected void handleVisibleDocumentAboutToBeChanged(DocumentEvent event) { if (fHandleProjectionChanges && cast(ProjectionDocumentEvent)event && isProjectionMode()) { int deletedLines; try { deletedLines= event.getDocument().getNumberOfLines(event.getOffset(), event.getLength()); } catch (BadLocationException e1) { deletedLines= 0; } fDeletedLines= deletedLines; } } /* * @see dwtx.jface.text.ITextViewerExtension5#getCoveredModelRanges(dwtx.jface.text.IRegion) */ public IRegion[] getCoveredModelRanges(IRegion modelRange) { if (fInformationMapping is null) return [ new Region(modelRange.getOffset(), modelRange.getLength()) ]; if ( cast(IDocumentInformationMappingExtension)fInformationMapping ) { IDocumentInformationMappingExtension extension= cast(IDocumentInformationMappingExtension) fInformationMapping; try { return extension.getExactCoverage(modelRange); } catch (BadLocationException x) { } } return null; } /* * @see dwtx.jface.text.ITextOperationTarget#doOperation(int) */ public void doOperation(int operation) { switch (operation) { case TOGGLE: if (canDoOperation(TOGGLE)) { if (!isProjectionMode()) { enableProjection(); } else { expandAll(); disableProjection(); } return; } } if (!isProjectionMode()) { super.doOperation(operation); return; } StyledText textWidget= getTextWidget(); if (textWidget is null) return; Point selection= null; switch (operation) { case CUT: if (redraws()) { selection= getSelectedRange(); if (exposeModelRange(new Region(selection.x, selection.y))) return; if (selection.y is 0) copyMarkedRegion(true); else copyToClipboard(selection.x, selection.y, true, textWidget); selection= textWidget.getSelectionRange(); fireSelectionChanged(selection.x, selection.y); } break; case COPY: if (redraws()) { selection= getSelectedRange(); if (selection.y is 0) copyMarkedRegion(false); else copyToClipboard(selection.x, selection.y, false, textWidget); } break; case DELETE: if (redraws()) { try { selection= getSelectedRange(); Point widgetSelection= textWidget.getSelectionRange(); if (selection.y is 0 || selection.y is widgetSelection.y) getTextWidget().invokeAction(ST.DELETE_NEXT); else deleteTextRange(selection.x, selection.y, textWidget); selection= textWidget.getSelectionRange(); fireSelectionChanged(selection.x, selection.y); } catch (BadLocationException x) { // ignore } } break; case EXPAND_ALL: if (redraws()) expandAll(); break; case EXPAND: if (redraws()) { expand(); } break; case COLLAPSE_ALL: if (redraws()) collapseAll(); break; case COLLAPSE: if (redraws()) { collapse(); } break; default: super.doOperation(operation); } } /* * @see dwtx.jface.text.source.SourceViewer#canDoOperation(int) */ public bool canDoOperation(int operation) { switch (operation) { case COLLAPSE: case COLLAPSE_ALL: case EXPAND: case EXPAND_ALL: return isProjectionMode(); case TOGGLE: return isProjectionMode() || !isSegmented(); } return super.canDoOperation(operation); } private bool isSegmented() { IDocument document= getDocument(); int length= document is null ? 0 : document.getLength(); IRegion visible= getModelCoverage(); bool isSegmented= visible !is null && !visible.equals(new Region(0, length)); return isSegmented; } private IRegion getMarkedRegion() { if (getTextWidget() is null) return null; if (fMarkPosition is null || fMarkPosition.isDeleted()) return null; int start= fMarkPosition.getOffset(); int end= getSelectedRange().x; return start > end ? new Region (end, start - end) : new Region(start, end - start); } /* * @see dwtx.jface.text.TextViewer#copyMarkedRegion(bool) */ protected void copyMarkedRegion(bool delete_) { IRegion markedRegion= getMarkedRegion(); if (markedRegion !is null) copyToClipboard(markedRegion.getOffset(), markedRegion.getLength(), delete_, getTextWidget()); } private void copyToClipboard(int offset, int length, bool delete_, StyledText textWidget) { String copyText= null; try { IDocument document= getDocument(); copyText= document.get(offset, length); } catch (BadLocationException ex) { // XXX: should log here, but JFace Text has no Log // As a fallback solution let the widget handle this textWidget.copy(); } if (copyText !is null && copyText.equals(textWidget.getSelectionText())) { /* * XXX: Reduce pain of https://bugs.eclipse.org/bugs/show_bug.cgi?id=64498 * by letting the widget handle the copy operation in this special case. */ textWidget.copy(); } else if (copyText !is null) { Clipboard clipboard= new Clipboard(textWidget.getDisplay()); try { Transfer[] dataTypes= [ TextTransfer.getInstance() ]; Object[] data= [ stringcast(copyText) ]; try { clipboard.setContents(data, dataTypes); } catch (DWTError e) { if (e.code !is DND.ERROR_CANNOT_SET_CLIPBOARD) throw e; /* * TODO see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59459 * we should either log and/or inform the user * silently fail for now. */ return; } } finally { clipboard.dispose(); } } if (delete_) { try { deleteTextRange(offset, length, textWidget); } catch (BadLocationException x) { // XXX: should log here, but JFace Text has no Log } } } private void deleteTextRange(int offset, int length, StyledText textWidget) { getDocument().replace(offset, length, ""); //$NON-NLS-1$ int widgetCaret= modelOffset2WidgetOffset(offset); if (widgetCaret > -1) textWidget.setSelection(widgetCaret); } /** * Adapts the behavior of the super class to respect line based folding. * * @param widgetSelection the widget selection * @return the model selection while respecting line based folding */ protected Point widgetSelection2ModelSelection(Point widgetSelection) { if (!isProjectionMode()) return super.widgetSelection2ModelSelection(widgetSelection); /* * There is one requirement that governs preservation of logical * positions: * * 1) a selection with widget_length is 0 should never expand to have * model_length > 0. * * There are a number of ambiguities to resolve with projection regions. * A projected region P has a widget-length of zero. Its widget offset * may interact with the selection S in various ways: * * A) P.widget_offset lies at the caret, S.widget_length is zero. * Requirement 1 applies. S is *behind* P (done so by widgetRange2ModelRange). * * B) P.widget_offset lies inside the widget selection. This case is * easy: P is included in S, which is automatically done so by * widgetRange2ModelRange. * * C) P.widget_offset lies at S.widget_end: This is * arguable - our policy is to include P if it belongs to a projection * annotation that overlaps with the widget selection. * * D) P.widget_offset lies at S.widget_offset: Arguable - our policy * is to include P if it belongs to a projection annotation that * overlaps with the widget selection */ IRegion modelSelection= widgetRange2ModelRange(new Region(widgetSelection.x, widgetSelection.y)); if (modelSelection is null) return null; int modelOffset= modelSelection.getOffset(); int modelEndOffset= modelOffset + modelSelection.getLength(); /* Case A: never expand a zero-length selection. S is *behind* P. */ if (widgetSelection.y is 0) return new Point(modelEndOffset, 0); int widgetSelectionExclusiveEnd= widgetSelection.x + widgetSelection.y; Position[] annotationPositions= computeOverlappingAnnotationPositions(modelSelection); for (int i= 0; i < annotationPositions.length; i++) { IRegion[] regions= computeCollapsedRegions(annotationPositions[i]); if (regions is null) continue; for (int j= 0; j < regions.length; j++) { IRegion modelRange= regions[j]; IRegion widgetRange= modelRange2ClosestWidgetRange(modelRange); // only take collapsed ranges, i.e. widget length is 0 if (widgetRange !is null && widgetRange.getLength() is 0) { int widgetOffset= widgetRange.getOffset(); // D) region is collapsed at S.widget_offset if (widgetOffset is widgetSelection.x) modelOffset= Math.min(modelOffset, modelRange.getOffset()); // C) region is collapsed at S.widget_end else if (widgetOffset is widgetSelectionExclusiveEnd) modelEndOffset= Math.max(modelEndOffset, modelRange.getOffset() + modelRange.getLength()); } } } return new Point(modelOffset, modelEndOffset - modelOffset); } /** * Returns the positions of all annotations that intersect with * <code>modelSelection</code> and that are at least partly visible. * @param modelSelection a model range * @return the positions of all annotations that intersect with * <code>modelSelection</code> * @since 3.1 */ private Position[] computeOverlappingAnnotationPositions(IRegion modelSelection) { List positions= new ArrayList(); for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) { ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); Position position= fProjectionAnnotationModel.getPosition(annotation); if (position !is null && position.overlapsWith(modelSelection.getOffset(), modelSelection.getLength()) && modelRange2WidgetRange(position) !is null) positions.add(position); } return arraycast!(Position)( positions.toArray()); } /* * @see dwtx.jface.text.TextViewer#getFindReplaceDocumentAdapter() */ protected FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() { if (fFindReplaceDocumentAdapter is null) { IDocument document= isProjectionMode() ? getDocument() : getVisibleDocument(); fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(document); } return fFindReplaceDocumentAdapter; } /* * @see dwtx.jface.text.TextViewer#findAndSelect(int, java.lang.String, bool, bool, bool, bool) */ protected int findAndSelect(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) { if (!isProjectionMode()) return super.findAndSelect(startPosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); StyledText textWidget= getTextWidget(); if (textWidget is null) return -1; try { IRegion matchRegion= getFindReplaceDocumentAdapter().find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); if (matchRegion !is null) { exposeModelRange(matchRegion); revealRange(matchRegion.getOffset(), matchRegion.getLength()); setSelectedRange(matchRegion.getOffset(), matchRegion.getLength()); return matchRegion.getOffset(); } } catch (BadLocationException x) { } return -1; } /* * @see dwtx.jface.text.TextViewer#findAndSelectInRange(int, java.lang.String, bool, bool, bool, int, int, bool) */ protected int findAndSelectInRange(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, int rangeOffset, int rangeLength, bool regExSearch) { if (!isProjectionMode()) return super.findAndSelectInRange(startPosition, findString, forwardSearch, caseSensitive, wholeWord, rangeOffset, rangeLength, regExSearch); StyledText textWidget= getTextWidget(); if (textWidget is null) return -1; try { int modelOffset= startPosition; if (forwardSearch && (startPosition is -1 || startPosition < rangeOffset)) { modelOffset= rangeOffset; } else if (!forwardSearch && (startPosition is -1 || startPosition > rangeOffset + rangeLength)) { modelOffset= rangeOffset + rangeLength; } IRegion matchRegion= getFindReplaceDocumentAdapter().find(modelOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); if (matchRegion !is null) { int offset= matchRegion.getOffset(); int length= matchRegion.getLength(); if (rangeOffset <= offset && offset + length <= rangeOffset + rangeLength) { exposeModelRange(matchRegion); revealRange(offset, length); setSelectedRange(offset, length); return offset; } } } catch (BadLocationException x) { } return -1; } }