Mercurial > projects > dwt-addons
annotate dwtx/jface/text/source/projection/ProjectionViewer.d @ 159:7926b636c282
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Wed, 27 Aug 2008 01:57:58 +0200 |
parents | 25f1f92fa3df |
children | 1a5b8f8129df |
rev | line source |
---|---|
129 | 1 /******************************************************************************* |
2 * Copyright (c) 2000, 2008 IBM Corporation and others. | |
3 * All rights reserved. This program and the accompanying materials | |
4 * are made available under the terms of the Eclipse Public License v1.0 | |
5 * which accompanies this distribution, and is available at | |
6 * http://www.eclipse.org/legal/epl-v10.html | |
7 * | |
8 * Contributors: | |
9 * IBM Corporation - initial API and implementation | |
10 * Port to the D programming language: | |
11 * Frank Benoit <benoit@tionex.de> | |
12 *******************************************************************************/ | |
13 module dwtx.jface.text.source.projection.ProjectionViewer; | |
14 | |
131 | 15 import dwtx.jface.text.source.projection.ProjectionSupport; // packageimport |
16 import dwtx.jface.text.source.projection.IProjectionPosition; // packageimport | |
17 import dwtx.jface.text.source.projection.AnnotationBag; // packageimport | |
18 import dwtx.jface.text.source.projection.ProjectionSummary; // packageimport | |
19 import dwtx.jface.text.source.projection.ProjectionAnnotationHover; // packageimport | |
20 import dwtx.jface.text.source.projection.ProjectionRulerColumn; // packageimport | |
21 import dwtx.jface.text.source.projection.ProjectionAnnotationModel; // packageimport | |
22 import dwtx.jface.text.source.projection.SourceViewerInformationControl; // packageimport | |
23 import dwtx.jface.text.source.projection.IProjectionListener; // packageimport | |
24 import dwtx.jface.text.source.projection.ProjectionAnnotation; // packageimport | |
25 | |
26 | |
129 | 27 import dwt.dwthelper.utils; |
28 | |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
147
diff
changeset
|
29 import dwtx.dwtxhelper.Collection; |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
147
diff
changeset
|
30 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
147
diff
changeset
|
31 |
129 | 32 |
33 import dwt.DWTError; | |
34 import dwt.custom.ST; | |
35 import dwt.custom.StyledText; | |
36 import dwt.dnd.Clipboard; | |
37 import dwt.dnd.DND; | |
38 import dwt.dnd.TextTransfer; | |
39 import dwt.dnd.Transfer; | |
40 import dwt.events.VerifyEvent; | |
41 import dwt.graphics.Point; | |
42 import dwt.widgets.Composite; | |
43 import dwt.widgets.Display; | |
44 import dwtx.core.runtime.Assert; | |
45 import dwtx.jface.text.BadLocationException; | |
46 import dwtx.jface.text.DocumentEvent; | |
47 import dwtx.jface.text.FindReplaceDocumentAdapter; | |
48 import dwtx.jface.text.IDocument; | |
49 import dwtx.jface.text.IDocumentInformationMappingExtension; | |
50 import dwtx.jface.text.IDocumentListener; | |
51 import dwtx.jface.text.IRegion; | |
52 import dwtx.jface.text.ISlaveDocumentManager; | |
53 import dwtx.jface.text.ITextViewerExtension5; | |
54 import dwtx.jface.text.Position; | |
55 import dwtx.jface.text.Region; | |
56 import dwtx.jface.text.TextUtilities; | |
57 import dwtx.jface.text.projection.ProjectionDocument; | |
58 import dwtx.jface.text.projection.ProjectionDocumentEvent; | |
59 import dwtx.jface.text.projection.ProjectionDocumentManager; | |
60 import dwtx.jface.text.source.Annotation; | |
61 import dwtx.jface.text.source.AnnotationModelEvent; | |
62 import dwtx.jface.text.source.CompositeRuler; | |
63 import dwtx.jface.text.source.IAnnotationModel; | |
64 import dwtx.jface.text.source.IAnnotationModelExtension; | |
65 import dwtx.jface.text.source.IAnnotationModelListener; | |
66 import dwtx.jface.text.source.IAnnotationModelListenerExtension; | |
67 import dwtx.jface.text.source.IOverviewRuler; | |
68 import dwtx.jface.text.source.IVerticalRuler; | |
69 import dwtx.jface.text.source.IVerticalRulerColumn; | |
70 import dwtx.jface.text.source.SourceViewer; | |
71 | |
72 | |
73 /** | |
74 * A projection source viewer is a source viewer which supports multiple visible | |
75 * regions which can dynamically be changed. | |
76 * <p> | |
77 * A projection source viewer uses a <code>ProjectionDocumentManager</code> | |
78 * for the management of the visible document.</p> | |
79 * <p> | |
80 * NOTE: The <code>ProjectionViewer</code> only supports projections that cover full lines. | |
81 * </p> | |
82 * <p> | |
83 * This class should not be subclassed.</p> | |
84 * | |
85 * @since 3.0 | |
86 * @noextend This class is not intended to be subclassed by clients. | |
87 */ | |
88 public class ProjectionViewer : SourceViewer , ITextViewerExtension5 { | |
89 | |
147 | 90 private static const int BASE= INFORMATION; // see ISourceViewer.INFORMATION |
129 | 91 |
92 /** Operation constant for the expand operation. */ | |
147 | 93 public static const int EXPAND= BASE + 1; |
129 | 94 /** Operation constant for the collapse operation. */ |
147 | 95 public static const int COLLAPSE= BASE + 2; |
129 | 96 /** Operation constant for the toggle projection operation. */ |
147 | 97 public static const int TOGGLE= BASE + 3; |
129 | 98 /** Operation constant for the expand all operation. */ |
147 | 99 public static const int EXPAND_ALL= BASE + 4; |
129 | 100 /** |
101 * Operation constant for the collapse all operation. | |
145 | 102 * |
129 | 103 * @since 3.2 |
104 */ | |
147 | 105 public static const int COLLAPSE_ALL= BASE + 5; |
129 | 106 |
107 /** | |
108 * Internal listener to changes of the annotation model. | |
109 */ | |
110 private class AnnotationModelListener : IAnnotationModelListener, IAnnotationModelListenerExtension { | |
111 | |
112 /* | |
113 * @see dwtx.jface.text.source.IAnnotationModelListener#modelChanged(dwtx.jface.text.source.IAnnotationModel) | |
114 */ | |
115 public void modelChanged(IAnnotationModel model) { | |
116 processModelChanged(model, null); | |
117 } | |
118 | |
119 /* | |
120 * @see dwtx.jface.text.source.IAnnotationModelListenerExtension#modelChanged(dwtx.jface.text.source.AnnotationModelEvent) | |
121 */ | |
122 public void modelChanged(AnnotationModelEvent event) { | |
123 processModelChanged(event.getAnnotationModel(), event); | |
124 } | |
125 | |
126 private void processModelChanged(IAnnotationModel model, AnnotationModelEvent event) { | |
127 if (model is fProjectionAnnotationModel) { | |
128 | |
129 if (fProjectionSummary !is null) | |
130 fProjectionSummary.updateSummaries(); | |
131 processCatchupRequest(event); | |
132 | |
133 } else if (model is getAnnotationModel() && fProjectionSummary !is null) | |
134 fProjectionSummary.updateSummaries(); | |
135 } | |
136 } | |
137 | |
138 /** | |
139 * Executes the 'replaceVisibleDocument' operation when called the first time. Self-destructs afterwards. | |
140 */ | |
141 private class ReplaceVisibleDocumentExecutor : IDocumentListener { | |
142 | |
143 private IDocument fSlaveDocument; | |
144 private IDocument fExecutionTrigger; | |
145 | |
146 /** | |
147 * Creates a new executor in order to free the given slave document. | |
148 * | |
149 * @param slaveDocument the slave document to free | |
150 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
151 public this(IDocument slaveDocument) { |
129 | 152 fSlaveDocument= slaveDocument; |
153 } | |
154 | |
155 /** | |
156 * Installs this executor on the given trigger document. | |
157 * | |
158 * @param executionTrigger the trigger document | |
159 */ | |
160 public void install(IDocument executionTrigger) { | |
161 if (executionTrigger !is null && fSlaveDocument !is null) { | |
162 fExecutionTrigger= executionTrigger; | |
163 fExecutionTrigger.addDocumentListener(this); | |
164 } | |
165 } | |
166 | |
167 /* | |
168 * @see dwtx.jface.text.IDocumentListener#documentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
169 */ | |
170 public void documentAboutToBeChanged(DocumentEvent event) { | |
171 } | |
172 | |
173 /* | |
174 * @see dwtx.jface.text.IDocumentListener#documentChanged(dwtx.jface.text.DocumentEvent) | |
175 */ | |
176 public void documentChanged(DocumentEvent event) { | |
177 fExecutionTrigger.removeDocumentListener(this); | |
178 executeReplaceVisibleDocument(fSlaveDocument); | |
179 } | |
180 } | |
181 | |
182 /** | |
183 * A command representing a change of the projection document. This can be either | |
184 * adding a master document range, removing a master document change, or invalidating | |
185 * the viewer text presentation. | |
186 */ | |
187 private static class ProjectionCommand { | |
188 | |
189 final static int ADD= 0; | |
190 final static int REMOVE= 1; | |
191 final static int INVALIDATE_PRESENTATION= 2; | |
192 | |
193 ProjectionDocument fProjection; | |
194 int fType; | |
195 int fOffset; | |
196 int fLength; | |
197 | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
198 this(ProjectionDocument projection, int type, int offset, int length) { |
129 | 199 fProjection= projection; |
200 fType= type; | |
201 fOffset= offset; | |
202 fLength= length; | |
203 } | |
204 | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
205 this(int offset, int length) { |
129 | 206 fType= INVALIDATE_PRESENTATION; |
207 fOffset= offset; | |
208 fLength= length; | |
209 } | |
210 | |
211 int computeExpectedCosts() { | |
212 | |
213 switch(fType) { | |
214 case ADD: { | |
215 try { | |
216 IRegion[] gaps= fProjection.computeUnprojectedMasterRegions(fOffset, fLength); | |
217 return gaps is null ? 0 : gaps.length; | |
218 } catch (BadLocationException x) { | |
219 } | |
220 break; | |
221 } | |
222 case REMOVE: { | |
223 try { | |
224 IRegion[] fragments= fProjection.computeProjectedMasterRegions(fOffset, fLength); | |
225 return fragments is null ? 0 : fragments.length; | |
226 } catch (BadLocationException x) { | |
227 } | |
228 break; | |
229 } | |
230 } | |
231 return 0; | |
232 } | |
233 } | |
234 | |
235 /** | |
236 * The queue of projection command objects. | |
237 */ | |
238 private static class ProjectionCommandQueue { | |
239 | |
240 final static int REDRAW_COSTS= 15; | |
241 final static int INVALIDATION_COSTS= 10; | |
242 | |
243 List fList= new ArrayList(15); | |
244 int fExpectedExecutionCosts= -1; | |
245 | |
246 | |
247 void add(ProjectionCommand command) { | |
248 fList.add(command); | |
249 } | |
250 | |
251 Iterator iterator() { | |
252 return fList.iterator(); | |
253 } | |
254 | |
255 void clear() { | |
256 fList.clear(); | |
257 fExpectedExecutionCosts= -1; | |
258 } | |
259 | |
260 bool passedRedrawCostsThreshold() { | |
261 if (fExpectedExecutionCosts is -1) | |
262 computeExpectedExecutionCosts(); | |
263 return fExpectedExecutionCosts > REDRAW_COSTS; | |
264 } | |
265 | |
266 bool passedInvalidationCostsThreshold() { | |
267 if (fExpectedExecutionCosts is -1) | |
268 computeExpectedExecutionCosts(); | |
269 return fExpectedExecutionCosts > INVALIDATION_COSTS; | |
270 } | |
271 | |
272 private void computeExpectedExecutionCosts() { | |
273 int max_costs= Math.max(REDRAW_COSTS, INVALIDATION_COSTS); | |
274 fExpectedExecutionCosts= fList.size(); | |
275 if (fExpectedExecutionCosts <= max_costs) { | |
276 ProjectionCommand command; | |
277 Iterator e= fList.iterator(); | |
278 while (e.hasNext()) { | |
134 | 279 command= cast(ProjectionCommand) e.next(); |
129 | 280 fExpectedExecutionCosts += command.computeExpectedCosts(); |
281 if (fExpectedExecutionCosts > max_costs) | |
282 break; | |
283 } | |
284 } | |
285 } | |
286 } | |
287 | |
288 /** The projection annotation model used by this viewer. */ | |
289 private ProjectionAnnotationModel fProjectionAnnotationModel; | |
290 /** The annotation model listener */ | |
159 | 291 private IAnnotationModelListener fAnnotationModelListener; |
129 | 292 /** The projection summary. */ |
293 private ProjectionSummary fProjectionSummary; | |
294 /** Indication that an annotation world change has not yet been processed. */ | |
295 private bool fPendingAnnotationWorldChange= false; | |
296 /** Indication whether projection changes in the visible document should be considered. */ | |
297 private bool fHandleProjectionChanges= true; | |
298 /** The list of projection listeners. */ | |
299 private List fProjectionListeners; | |
300 /** Internal lock for protecting the list of pending requests */ | |
159 | 301 private Object fLock; |
129 | 302 /** The list of pending requests */ |
159 | 303 private List fPendingRequests; |
129 | 304 /** The replace-visible-document execution trigger */ |
305 private IDocument fReplaceVisibleDocumentExecutionTrigger; | |
306 /** <code>true</code> if projection was on the last time we switched to segmented mode. */ | |
307 private bool fWasProjectionEnabled; | |
308 /** The queue of projection commands used to assess the costs of projection changes. */ | |
309 private ProjectionCommandQueue fCommandQueue; | |
310 /** | |
311 * The amount of lines deleted by the last document event issued by the | |
312 * visible document event. | |
313 * @since 3.1 | |
314 */ | |
315 private int fDeletedLines; | |
316 | |
317 | |
318 /** | |
319 * Creates a new projection source viewer. | |
320 * | |
321 * @param parent the DWT parent control | |
322 * @param ruler the vertical ruler | |
323 * @param overviewRuler the overview ruler | |
324 * @param showsAnnotationOverview <code>true</code> if the overview ruler should be shown | |
325 * @param styles the DWT style bits | |
326 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
327 public this(Composite parent, IVerticalRuler ruler, IOverviewRuler overviewRuler, bool showsAnnotationOverview, int styles) { |
159 | 328 |
329 fAnnotationModelListener= new AnnotationModelListener(); | |
330 fLock= new Object(); | |
331 fPendingRequests= new ArrayList(); | |
332 | |
129 | 333 super(parent, ruler, overviewRuler, showsAnnotationOverview, styles); |
334 } | |
335 | |
336 /** | |
337 * Sets the projection summary for this viewer. | |
338 * | |
339 * @param projectionSummary the projection summary. | |
340 */ | |
341 public void setProjectionSummary(ProjectionSummary projectionSummary) { | |
342 fProjectionSummary= projectionSummary; | |
343 } | |
344 | |
345 /** | |
346 * Adds the projection annotation model to the given annotation model. | |
347 * | |
348 * @param model the model to which the projection annotation model is added | |
349 */ | |
350 private void addProjectionAnnotationModel(IAnnotationModel model) { | |
138 | 351 if ( cast(IAnnotationModelExtension)model ) { |
134 | 352 IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model; |
129 | 353 extension.addAnnotationModel(ProjectionSupport.PROJECTION, fProjectionAnnotationModel); |
354 model.addAnnotationModelListener(fAnnotationModelListener); | |
355 } | |
356 } | |
357 | |
358 /** | |
359 * Removes the projection annotation model from the given annotation model. | |
360 * | |
361 * @param model the mode from which the projection annotation model is removed | |
362 * @return the removed projection annotation model or <code>null</code> if there was none | |
363 */ | |
364 private IAnnotationModel removeProjectionAnnotationModel(IAnnotationModel model) { | |
138 | 365 if ( cast(IAnnotationModelExtension)model ) { |
129 | 366 model.removeAnnotationModelListener(fAnnotationModelListener); |
134 | 367 IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model; |
129 | 368 return extension.removeAnnotationModel(ProjectionSupport.PROJECTION); |
369 } | |
370 return null; | |
371 } | |
372 | |
373 /* | |
374 * @see dwtx.jface.text.source.SourceViewer#setDocument(dwtx.jface.text.IDocument, dwtx.jface.text.source.IAnnotationModel, int, int) | |
375 */ | |
376 public void setDocument(IDocument document, IAnnotationModel annotationModel, int modelRangeOffset, int modelRangeLength) { | |
377 bool wasProjectionEnabled= false; | |
378 | |
379 synchronized (fLock) { | |
380 fPendingRequests.clear(); | |
381 } | |
382 | |
383 if (fProjectionAnnotationModel !is null) { | |
384 wasProjectionEnabled= removeProjectionAnnotationModel(getVisualAnnotationModel()) !is null; | |
385 fProjectionAnnotationModel= null; | |
386 } | |
387 | |
388 super.setDocument(document, annotationModel, modelRangeOffset, modelRangeLength); | |
389 | |
390 if (wasProjectionEnabled && document !is null) | |
391 enableProjection(); | |
392 } | |
393 | |
394 /* | |
395 * @see dwtx.jface.text.source.SourceViewer#createVisualAnnotationModel(dwtx.jface.text.source.IAnnotationModel) | |
396 */ | |
397 protected IAnnotationModel createVisualAnnotationModel(IAnnotationModel annotationModel) { | |
398 IAnnotationModel model= super.createVisualAnnotationModel(annotationModel); | |
399 fProjectionAnnotationModel= new ProjectionAnnotationModel(); | |
400 return model; | |
401 } | |
402 | |
403 /** | |
404 * Returns the projection annotation model. | |
405 * | |
406 * @return the projection annotation model | |
407 */ | |
408 public ProjectionAnnotationModel getProjectionAnnotationModel() { | |
409 IAnnotationModel model= getVisualAnnotationModel(); | |
138 | 410 if ( cast(IAnnotationModelExtension)model ) { |
134 | 411 IAnnotationModelExtension extension= cast(IAnnotationModelExtension) model; |
412 return cast(ProjectionAnnotationModel) extension.getAnnotationModel(ProjectionSupport.PROJECTION); | |
129 | 413 } |
414 return null; | |
415 } | |
416 | |
417 /* | |
418 * @see dwtx.jface.text.TextViewer#createSlaveDocumentManager() | |
419 */ | |
420 protected ISlaveDocumentManager createSlaveDocumentManager() { | |
421 return new ProjectionDocumentManager(); | |
422 } | |
423 | |
424 /* | |
425 * @see dwtx.jface.text.TextViewer#updateSlaveDocument(dwtx.jface.text.IDocument, int, int) | |
426 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
427 protected bool updateSlaveDocument(IDocument slaveDocument, int modelRangeOffset, int modelRangeLength) { |
138 | 428 if ( cast(ProjectionDocument)slaveDocument ) { |
134 | 429 ProjectionDocument projection= cast(ProjectionDocument) slaveDocument; |
129 | 430 |
431 int offset= modelRangeOffset; | |
432 int length= modelRangeLength; | |
433 | |
434 if (!isProjectionMode()) { | |
435 // mimic original TextViewer behavior | |
436 IDocument master= projection.getMasterDocument(); | |
437 int line= master.getLineOfOffset(modelRangeOffset); | |
438 offset= master.getLineOffset(line); | |
439 length= (modelRangeOffset - offset) + modelRangeLength; | |
440 | |
441 } | |
442 | |
443 try { | |
444 fHandleProjectionChanges= false; | |
445 projection.replaceMasterDocumentRanges(offset, length); | |
446 } finally { | |
447 fHandleProjectionChanges= true; | |
448 } | |
449 return true; | |
450 } | |
451 return false; | |
452 } | |
453 | |
454 /** | |
455 * Adds a projection annotation listener to this viewer. The listener may | |
456 * not be <code>null</code>. If the listener is already registered, this method | |
457 * does not have any effect. | |
458 * | |
459 * @param listener the listener to add | |
460 */ | |
461 public void addProjectionListener(IProjectionListener listener) { | |
462 | |
463 Assert.isNotNull(listener); | |
464 | |
465 if (fProjectionListeners is null) | |
466 fProjectionListeners= new ArrayList(); | |
467 | |
468 if (!fProjectionListeners.contains(listener)) | |
469 fProjectionListeners.add(listener); | |
470 } | |
471 | |
472 /** | |
473 * Removes the given listener from this viewer. The listener may not be | |
474 * <code>null</code>. If the listener is not registered with this viewer, | |
475 * this method is without effect. | |
476 * | |
477 * @param listener the listener to remove | |
478 */ | |
479 public void removeProjectionListener(IProjectionListener listener) { | |
480 | |
481 Assert.isNotNull(listener); | |
482 | |
483 if (fProjectionListeners !is null) { | |
484 fProjectionListeners.remove(listener); | |
485 if (fProjectionListeners.size() is 0) | |
486 fProjectionListeners= null; | |
487 } | |
488 } | |
489 | |
490 /** | |
491 * Notifies all registered projection listeners | |
492 * that projection mode has been enabled. | |
493 */ | |
494 protected void fireProjectionEnabled() { | |
495 if (fProjectionListeners !is null) { | |
145 | 496 Iterator e= (new ArrayList(fProjectionListeners)).iterator(); |
129 | 497 while (e.hasNext()) { |
134 | 498 IProjectionListener l= cast(IProjectionListener) e.next(); |
129 | 499 l.projectionEnabled(); |
500 } | |
501 } | |
502 } | |
503 | |
504 /** | |
505 * Notifies all registered projection listeners | |
506 * that projection mode has been disabled. | |
507 */ | |
508 protected void fireProjectionDisabled() { | |
509 if (fProjectionListeners !is null) { | |
145 | 510 Iterator e= (new ArrayList(fProjectionListeners)).iterator(); |
129 | 511 while (e.hasNext()) { |
134 | 512 IProjectionListener l= cast(IProjectionListener) e.next(); |
129 | 513 l.projectionDisabled(); |
514 } | |
515 } | |
516 } | |
517 | |
518 /** | |
519 * Returns whether this viewer is in projection mode. | |
520 * | |
521 * @return <code>true</code> if this viewer is in projection mode, | |
522 * <code>false</code> otherwise | |
523 */ | |
524 public final bool isProjectionMode() { | |
525 return getProjectionAnnotationModel() !is null; | |
526 } | |
527 | |
528 /** | |
529 * Disables the projection mode. | |
530 */ | |
531 public final void disableProjection() { | |
532 if (isProjectionMode()) { | |
533 removeProjectionAnnotationModel(getVisualAnnotationModel()); | |
534 fProjectionAnnotationModel.removeAllAnnotations(); | |
535 fFindReplaceDocumentAdapter= null; | |
536 fireProjectionDisabled(); | |
537 } | |
538 } | |
539 | |
540 /** | |
541 * Enables the projection mode. | |
542 */ | |
543 public final void enableProjection() { | |
544 if (!isProjectionMode()) { | |
545 addProjectionAnnotationModel(getVisualAnnotationModel()); | |
546 fFindReplaceDocumentAdapter= null; | |
547 fireProjectionEnabled(); | |
548 } | |
549 } | |
550 | |
551 private void expandAll() { | |
552 int offset= 0; | |
553 IDocument doc= getDocument(); | |
554 int length= doc is null ? 0 : doc.getLength(); | |
555 if (isProjectionMode()) { | |
556 fProjectionAnnotationModel.expandAll(offset, length); | |
557 } | |
558 } | |
559 | |
560 private void expand() { | |
561 if (isProjectionMode()) { | |
562 Position found= null; | |
563 Annotation bestMatch= null; | |
564 Point selection= getSelectedRange(); | |
565 for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) { | |
134 | 566 ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); |
129 | 567 if (annotation.isCollapsed()) { |
568 Position position= fProjectionAnnotationModel.getPosition(annotation); | |
569 // take the first most fine grained match | |
570 if (position !is null && touches(selection, position)) | |
571 if (found is null || position.includes(found.offset) && position.includes(found.offset + found.length)) { | |
572 found= position; | |
573 bestMatch= annotation; | |
574 } | |
575 } | |
576 } | |
577 | |
578 if (bestMatch !is null) { | |
579 fProjectionAnnotationModel.expand(bestMatch); | |
580 revealRange(selection.x, selection.y); | |
581 } | |
582 } | |
583 } | |
584 | |
585 private bool touches(Point selection, Position position) { | |
586 return position.overlapsWith(selection.x, selection.y) || selection.y is 0 && position.offset + position.length is selection.x + selection.y; | |
587 } | |
588 | |
589 private void collapse() { | |
590 if (isProjectionMode()) { | |
591 Position found= null; | |
592 Annotation bestMatch= null; | |
593 Point selection= getSelectedRange(); | |
594 for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) { | |
134 | 595 ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); |
129 | 596 if (!annotation.isCollapsed()) { |
597 Position position= fProjectionAnnotationModel.getPosition(annotation); | |
598 // take the first most fine grained match | |
599 if (position !is null && touches(selection, position)) | |
600 if (found is null || found.includes(position.offset) && found.includes(position.offset + position.length)) { | |
601 found= position; | |
602 bestMatch= annotation; | |
603 } | |
604 } | |
605 } | |
606 | |
607 if (bestMatch !is null) { | |
608 fProjectionAnnotationModel.collapse(bestMatch); | |
609 revealRange(selection.x, selection.y); | |
610 } | |
611 } | |
612 } | |
613 | |
614 /* | |
615 * @since 3.2 | |
616 */ | |
617 private void collapseAll() { | |
618 int offset= 0; | |
619 IDocument doc= getDocument(); | |
620 int length= doc is null ? 0 : doc.getLength(); | |
621 if (isProjectionMode()) { | |
622 fProjectionAnnotationModel.collapseAll(offset, length); | |
623 } | |
624 } | |
625 | |
626 /** | |
627 * Adds the given master range to the given projection document. While the | |
628 * modification is processed, the viewer no longer handles projection | |
629 * changes, as it is causing them. | |
630 * | |
631 * @param projection the projection document | |
632 * @param offset the offset in the master document | |
633 * @param length the length in the master document | |
634 * @throws BadLocationException in case the specified range is invalid | |
635 * | |
636 * @see ProjectionDocument#addMasterDocumentRange(int, int) | |
637 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
638 private void addMasterDocumentRange(ProjectionDocument projection, int offset, int length) { |
129 | 639 |
640 if (fCommandQueue !is null) { | |
641 fCommandQueue.add(new ProjectionCommand(projection, ProjectionCommand.ADD, offset, length)); | |
642 } else { | |
643 try { | |
644 fHandleProjectionChanges= false; | |
645 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=108258 | |
646 // make sure the document range is strictly line based | |
647 int end= offset + length; | |
648 offset= toLineStart(projection.getMasterDocument(), offset, false); | |
649 length= toLineStart(projection.getMasterDocument(), end, true) - offset; | |
650 projection.addMasterDocumentRange(offset, length); | |
651 } finally { | |
652 fHandleProjectionChanges= true; | |
653 } | |
654 } | |
655 } | |
656 | |
657 /** | |
658 * Removes the given master range from the given projection document. While the | |
659 * modification is processed, the viewer no longer handles projection | |
660 * changes, as it is causing them. | |
661 * | |
662 * @param projection the projection document | |
663 * @param offset the offset in the master document | |
664 * @param length the length in the master document | |
665 * @throws BadLocationException in case the specified range is invalid | |
666 * | |
667 * @see ProjectionDocument#removeMasterDocumentRange(int, int) | |
668 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
669 private void removeMasterDocumentRange(ProjectionDocument projection, int offset, int length) { |
129 | 670 if (fCommandQueue !is null) { |
671 fCommandQueue.add(new ProjectionCommand(projection, ProjectionCommand.REMOVE, offset, length)); | |
672 } else { | |
673 try { | |
674 fHandleProjectionChanges= false; | |
675 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=108258 | |
676 // make sure the document range is strictly line based | |
677 int end= offset + length; | |
678 offset= toLineStart(projection.getMasterDocument(), offset, false); | |
679 length= toLineStart(projection.getMasterDocument(), end, true) - offset; | |
680 projection.removeMasterDocumentRange(offset, length); | |
681 } finally { | |
682 fHandleProjectionChanges= true; | |
683 } | |
684 } | |
685 } | |
145 | 686 |
129 | 687 /** |
688 * Returns the first line offset <= <code>offset</code>. If <code>testLastLine</code> | |
689 * is <code>true</code> and the offset is on last line then <code>offset</code> is returned. | |
145 | 690 * |
129 | 691 * @param document the document |
692 * @param offset the master document offset | |
693 * @param testLastLine <code>true</code> if the test for the last line should be performed | |
694 * @return the closest line offset >= <code>offset</code> | |
695 * @throws BadLocationException if the offset is invalid | |
696 * @since 3.2 | |
697 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
698 private int toLineStart(IDocument document, int offset, bool testLastLine) { |
129 | 699 if (document is null) |
700 return offset; | |
145 | 701 |
129 | 702 if (testLastLine && offset >= document.getLineInformationOfOffset(document.getLength() - 1).getOffset()) |
703 return offset; | |
145 | 704 |
129 | 705 return document.getLineInformationOfOffset(offset).getOffset(); |
706 } | |
707 | |
708 /* | |
709 * @see dwtx.jface.text.TextViewer#setVisibleRegion(int, int) | |
710 */ | |
711 public void setVisibleRegion(int start, int length) { | |
712 fWasProjectionEnabled= isProjectionMode(); | |
713 disableProjection(); | |
714 super.setVisibleRegion(start, length); | |
715 } | |
716 | |
717 /* | |
718 * @see dwtx.jface.text.TextViewer#setVisibleDocument(dwtx.jface.text.IDocument) | |
719 */ | |
720 protected void setVisibleDocument(IDocument document) { | |
721 if (!isProjectionMode()) { | |
722 super.setVisibleDocument(document); | |
723 return; | |
724 } | |
725 | |
726 // In projection mode we don't want to throw away the find/replace document adapter | |
727 FindReplaceDocumentAdapter adapter= fFindReplaceDocumentAdapter; | |
728 super.setVisibleDocument(document); | |
729 fFindReplaceDocumentAdapter= adapter; | |
730 } | |
731 | |
732 /* | |
733 * @see dwtx.jface.text.TextViewer#resetVisibleRegion() | |
734 */ | |
735 public void resetVisibleRegion() { | |
736 super.resetVisibleRegion(); | |
737 if (fWasProjectionEnabled) | |
738 enableProjection(); | |
739 } | |
740 | |
741 /* | |
742 * @see dwtx.jface.text.ITextViewer#getVisibleRegion() | |
743 */ | |
744 public IRegion getVisibleRegion() { | |
745 disableProjection(); | |
746 IRegion visibleRegion= getModelCoverage(); | |
747 if (visibleRegion is null) | |
748 visibleRegion= new Region(0, 0); | |
749 | |
750 return visibleRegion; | |
751 } | |
752 | |
753 /* | |
754 * @see dwtx.jface.text.ITextViewer#overlapsWithVisibleRegion(int,int) | |
755 */ | |
756 public bool overlapsWithVisibleRegion(int offset, int length) { | |
757 disableProjection(); | |
758 IRegion coverage= getModelCoverage(); | |
759 if (coverage is null) | |
760 return false; | |
761 | |
762 bool appending= (offset is coverage.getOffset() + coverage.getLength()) && length is 0; | |
763 return appending || TextUtilities.overlaps(coverage, new Region(offset, length)); | |
764 } | |
765 | |
766 /** | |
767 * Replace the visible document with the given document. Maintains the | |
768 * scroll offset and the selection. | |
769 * | |
770 * @param slave the visible document | |
771 */ | |
772 private void replaceVisibleDocument(IDocument slave) { | |
773 if (fReplaceVisibleDocumentExecutionTrigger !is null) { | |
774 ReplaceVisibleDocumentExecutor executor= new ReplaceVisibleDocumentExecutor(slave); | |
775 executor.install(fReplaceVisibleDocumentExecutionTrigger); | |
776 } else | |
777 executeReplaceVisibleDocument(slave); | |
778 } | |
779 | |
780 | |
781 private void executeReplaceVisibleDocument(IDocument visibleDocument) { | |
782 StyledText textWidget= getTextWidget(); | |
783 try { | |
784 if (textWidget !is null && !textWidget.isDisposed()) | |
785 textWidget.setRedraw(false); | |
786 | |
787 int topIndex= getTopIndex(); | |
788 Point selection= getSelectedRange(); | |
789 setVisibleDocument(visibleDocument); | |
790 Point currentSelection= getSelectedRange(); | |
791 if (currentSelection.x !is selection.x || currentSelection.y !is selection.y) | |
792 setSelectedRange(selection.x, selection.y); | |
793 setTopIndex(topIndex); | |
794 | |
795 } finally { | |
796 if (textWidget !is null && !textWidget.isDisposed()) | |
797 textWidget.setRedraw(true); | |
798 } | |
799 } | |
800 | |
801 /** | |
802 * Hides the given range by collapsing it. If requested, a redraw request is issued. | |
803 * | |
804 * @param offset the offset of the range to hide | |
805 * @param length the length of the range to hide | |
806 * @param fireRedraw <code>true</code> if a redraw request should be issued, <code>false</code> otherwise | |
807 * @throws BadLocationException in case the range is invalid | |
808 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
809 private void collapse(int offset, int length, bool fireRedraw) { |
129 | 810 ProjectionDocument projection= null; |
811 | |
812 IDocument visibleDocument= getVisibleDocument(); | |
138 | 813 if ( cast(ProjectionDocument)visibleDocument ) |
134 | 814 projection= cast(ProjectionDocument) visibleDocument; |
129 | 815 else { |
816 IDocument master= getDocument(); | |
817 IDocument slave= createSlaveDocument(getDocument()); | |
138 | 818 if ( cast(ProjectionDocument)slave ) { |
134 | 819 projection= cast(ProjectionDocument) slave; |
129 | 820 addMasterDocumentRange(projection, 0, master.getLength()); |
821 replaceVisibleDocument(projection); | |
822 } | |
823 } | |
824 | |
825 if (projection !is null) | |
826 removeMasterDocumentRange(projection, offset, length); | |
827 | |
828 if (projection !is null && fireRedraw) { | |
829 // repaint line above to get the folding box | |
830 IDocument document= getDocument(); | |
831 int line= document.getLineOfOffset(offset); | |
832 if (line > 0) { | |
833 IRegion info= document.getLineInformation(line - 1); | |
834 internalInvalidateTextPresentation(info.getOffset(), info.getLength()); | |
835 } | |
836 } | |
837 } | |
838 | |
839 /** | |
840 * Makes the given range visible again while not changing the folding state of any contained | |
841 * ranges. If requested, a redraw request is issued. | |
145 | 842 * |
129 | 843 * @param offset the offset of the range to be expanded |
844 * @param length the length of the range to be expanded | |
845 * @param fireRedraw <code>true</code> if a redraw request should be issued, | |
846 * <code>false</code> otherwise | |
847 * @throws BadLocationException in case the range is invalid | |
848 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
849 private void expand(int offset, int length, bool fireRedraw) { |
129 | 850 IDocument slave= getVisibleDocument(); |
138 | 851 if ( cast(ProjectionDocument)slave ) { |
134 | 852 ProjectionDocument projection= cast(ProjectionDocument) slave; |
129 | 853 |
854 // expand | |
855 addMasterDocumentRange(projection, offset, length); | |
856 | |
857 // collapse contained regions | |
858 ProjectionAnnotation[] collapsed= computeCollapsedNestedAnnotations(offset, length); | |
859 if (collapsed !is null) { | |
860 for (int i= 0; i < collapsed.length; i++) { | |
861 IRegion[] regions= computeCollapsedRegions(fProjectionAnnotationModel.getPosition(collapsed[i])); | |
862 if (regions !is null) | |
863 for (int j= 0; j < regions.length; j++) | |
864 removeMasterDocumentRange(projection, regions[j].getOffset(), regions[j].getLength()); | |
865 } | |
866 } | |
867 | |
868 // redraw if requested | |
869 if (fireRedraw) | |
870 internalInvalidateTextPresentation(offset, length); | |
871 } | |
872 } | |
873 | |
874 /** | |
875 * Processes the request for catch up with the annotation model in the UI thread. If the current | |
876 * thread is not the UI thread or there are pending catch up requests, a new request is posted. | |
877 * | |
878 * @param event the annotation model event | |
879 */ | |
880 protected final void processCatchupRequest(AnnotationModelEvent event) { | |
881 if (Display.getCurrent() !is null) { | |
882 bool run= false; | |
883 synchronized (fLock) { | |
884 run= fPendingRequests.isEmpty(); | |
885 } | |
886 if (run) { | |
887 | |
888 try { | |
889 catchupWithProjectionAnnotationModel(event); | |
890 } catch (BadLocationException x) { | |
891 throw new IllegalArgumentException(); | |
892 } | |
893 | |
894 } else | |
895 postCatchupRequest(event); | |
896 } else { | |
897 postCatchupRequest(event); | |
898 } | |
899 } | |
900 | |
901 /** | |
902 * Posts the request for catch up with the annotation model into the UI thread. | |
903 * | |
904 * @param event the annotation model event | |
905 */ | |
158 | 906 protected final void postCatchupRequest(AnnotationModelEvent event) { |
129 | 907 synchronized (fLock) { |
908 fPendingRequests.add(event); | |
909 if (fPendingRequests.size() is 1) { | |
910 StyledText widget= getTextWidget(); | |
911 if (widget !is null) { | |
912 Display display= widget.getDisplay(); | |
913 if (display !is null) { | |
135 | 914 display.asyncExec(new class() Runnable { |
129 | 915 public void run() { |
916 try { | |
917 while (true) { | |
918 AnnotationModelEvent ame= null; | |
919 synchronized (fLock) { | |
920 if (fPendingRequests.size() is 0) | |
921 return; | |
134 | 922 ame= cast(AnnotationModelEvent) fPendingRequests.remove(0); |
129 | 923 } |
924 catchupWithProjectionAnnotationModel(ame); | |
925 } | |
926 } catch (BadLocationException x) { | |
927 try { | |
928 catchupWithProjectionAnnotationModel(null); | |
929 } catch (BadLocationException x1) { | |
930 throw new IllegalArgumentException(); | |
931 } finally { | |
932 synchronized (fLock) { | |
933 fPendingRequests.clear(); | |
934 } | |
935 } | |
936 } | |
937 } | |
938 }); | |
939 } | |
940 } | |
941 } | |
942 } | |
943 } | |
145 | 944 |
129 | 945 /** |
946 * Tests whether the visible document's master document | |
947 * is identical to this viewer's document. | |
145 | 948 * |
129 | 949 * @return <code>true</code> if the visible document's master is |
950 * identical to this viewer's document | |
951 * @since 3.1 | |
952 */ | |
953 private bool isVisibleMasterDocumentSameAsDocument() { | |
954 IDocument visibleDocument= getVisibleDocument(); | |
138 | 955 return ( cast(ProjectionDocument)visibleDocument ) && (cast(ProjectionDocument)visibleDocument).getMasterDocument() is getDocument(); |
129 | 956 } |
957 | |
958 /** | |
959 * Adapts the slave visual document of this viewer to the changes described | |
960 * in the annotation model event. When the event is <code>null</code>, | |
961 * this is identical to a world change event. | |
962 * | |
963 * @param event the annotation model event or <code>null</code> | |
964 * @exception BadLocationException in case the annotation model event is no longer in synchronization with the document | |
965 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
966 private void catchupWithProjectionAnnotationModel(AnnotationModelEvent event) { |
145 | 967 |
129 | 968 if (event is null || !isVisibleMasterDocumentSameAsDocument()) { |
969 | |
970 fPendingAnnotationWorldChange= false; | |
971 reinitializeProjection(); | |
972 | |
973 } else if (event.isWorldChange()) { | |
974 | |
975 if (event.isValid()) { | |
976 fPendingAnnotationWorldChange= false; | |
977 reinitializeProjection(); | |
978 } else | |
979 fPendingAnnotationWorldChange= true; | |
980 | |
981 } else if (fPendingAnnotationWorldChange) { | |
982 if (event.isValid()) { | |
983 fPendingAnnotationWorldChange= false; | |
984 reinitializeProjection(); | |
985 } | |
986 } else { | |
145 | 987 |
129 | 988 Annotation[] addedAnnotations= event.getAddedAnnotations(); |
989 Annotation[] changedAnnotation= event.getChangedAnnotations(); | |
990 Annotation[] removedAnnotations= event.getRemovedAnnotations(); | |
145 | 991 |
129 | 992 fCommandQueue= new ProjectionCommandQueue(); |
145 | 993 |
129 | 994 bool isRedrawing= redraws(); |
995 int topIndex= isRedrawing ? getTopIndex() : -1; | |
145 | 996 |
129 | 997 processDeletions(event, removedAnnotations, true); |
998 List coverage= new ArrayList(); | |
999 processChanges(addedAnnotations, true, coverage); | |
1000 processChanges(changedAnnotation, true, coverage); | |
145 | 1001 |
129 | 1002 ProjectionCommandQueue commandQueue= fCommandQueue; |
1003 fCommandQueue= null; | |
145 | 1004 |
129 | 1005 if (commandQueue.passedRedrawCostsThreshold()) { |
1006 setRedraw(false); | |
1007 try { | |
1008 executeProjectionCommands(commandQueue, false); | |
1009 } catch (IllegalArgumentException x) { | |
1010 reinitializeProjection(); | |
1011 } finally { | |
1012 setRedraw(true, topIndex); | |
1013 } | |
1014 } else { | |
1015 try { | |
1016 bool fireRedraw= !commandQueue.passedInvalidationCostsThreshold(); | |
1017 executeProjectionCommands(commandQueue, fireRedraw); | |
1018 if (!fireRedraw) | |
1019 invalidateTextPresentation(); | |
1020 } catch (IllegalArgumentException x) { | |
1021 reinitializeProjection(); | |
1022 } | |
1023 } | |
1024 } | |
1025 } | |
1026 | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
1027 private void executeProjectionCommands(ProjectionCommandQueue commandQueue, bool fireRedraw) { |
129 | 1028 |
1029 ProjectionCommand command; | |
1030 Iterator e= commandQueue.iterator(); | |
1031 while (e.hasNext()) { | |
134 | 1032 command= cast(ProjectionCommand) e.next(); |
129 | 1033 switch (command.fType) { |
1034 case ProjectionCommand.ADD: | |
1035 addMasterDocumentRange(command.fProjection, command.fOffset, command.fLength); | |
1036 break; | |
1037 case ProjectionCommand.REMOVE: | |
1038 removeMasterDocumentRange(command.fProjection, command.fOffset, command.fLength); | |
1039 break; | |
1040 case ProjectionCommand.INVALIDATE_PRESENTATION: | |
1041 if (fireRedraw) | |
1042 invalidateTextPresentation(command.fOffset, command.fLength); | |
1043 break; | |
1044 } | |
1045 } | |
1046 | |
1047 commandQueue.clear(); | |
1048 } | |
1049 | |
1050 private bool covers(int offset, int length, Position position) { | |
1051 if (!(position.offset is offset && position.length is length) && !position.isDeleted()) | |
1052 return offset <= position.getOffset() && position.getOffset() + position.getLength() <= offset + length; | |
1053 return false; | |
1054 } | |
1055 | |
1056 private ProjectionAnnotation[] computeCollapsedNestedAnnotations(int offset, int length) { | |
1057 List annotations= new ArrayList(5); | |
1058 Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); | |
1059 while (e.hasNext()) { | |
134 | 1060 ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); |
129 | 1061 if (annotation.isCollapsed()) { |
1062 Position position= fProjectionAnnotationModel.getPosition(annotation); | |
1063 if (position is null) { | |
1064 // annotation might already be deleted, we will be informed later on about this deletion | |
1065 continue; | |
1066 } | |
1067 if (covers(offset, length, position)) | |
1068 annotations.add(annotation); | |
1069 } | |
1070 } | |
1071 | |
1072 if (annotations.size() > 0) { | |
1073 ProjectionAnnotation[] result= new ProjectionAnnotation[annotations.size()]; | |
1074 annotations.toArray(result); | |
1075 return result; | |
1076 } | |
1077 | |
1078 return null; | |
1079 } | |
1080 | |
1081 private void internalInvalidateTextPresentation(int offset, int length) { | |
1082 if (fCommandQueue !is null) { | |
1083 fCommandQueue.add(new ProjectionCommand(offset, length)); | |
1084 } else { | |
1085 invalidateTextPresentation(offset, length); | |
1086 } | |
1087 } | |
1088 | |
1089 /* | |
1090 * We pass the removed annotation into this method for performance reasons only. Otherwise, they could be fetch from the event. | |
1091 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
1092 private void processDeletions(AnnotationModelEvent event, Annotation[] removedAnnotations, bool fireRedraw) { |
129 | 1093 for (int i= 0; i < removedAnnotations.length; i++) { |
134 | 1094 ProjectionAnnotation annotation= cast(ProjectionAnnotation) removedAnnotations[i]; |
129 | 1095 if (annotation.isCollapsed()) { |
1096 Position expanded= event.getPositionOfRemovedAnnotation(annotation); | |
1097 expand(expanded.getOffset(), expanded.getLength(), fireRedraw); | |
1098 } | |
1099 } | |
1100 } | |
1101 | |
1102 /** | |
1103 * Computes the region that must be collapsed when the given position is the | |
1104 * position of an expanded projection annotation. | |
1105 * | |
1106 * @param position the position | |
1107 * @return the range that must be collapsed | |
1108 */ | |
1109 public IRegion computeCollapsedRegion(Position position) { | |
1110 try { | |
1111 IDocument document= getDocument(); | |
1112 if (document is null) | |
1113 return null; | |
145 | 1114 |
129 | 1115 int line= document.getLineOfOffset(position.getOffset()); |
1116 int offset= document.getLineOffset(line + 1); | |
1117 | |
1118 int length= position.getLength() - (offset - position.getOffset()); | |
1119 if (length > 0) | |
1120 return new Region(offset, length); | |
1121 } catch (BadLocationException x) { | |
1122 } | |
1123 | |
1124 return null; | |
1125 } | |
1126 | |
1127 /** | |
1128 * Computes the regions that must be collapsed when the given position is | |
1129 * the position of an expanded projection annotation. | |
1130 * | |
1131 * @param position the position | |
1132 * @return the ranges that must be collapsed, or <code>null</code> if | |
1133 * there are none | |
1134 * @since 3.1 | |
1135 */ | |
1136 IRegion[] computeCollapsedRegions(Position position) { | |
1137 try { | |
1138 IDocument document= getDocument(); | |
1139 if (document is null) | |
1140 return null; | |
1141 | |
138 | 1142 if ( cast(IProjectionPosition)position ) { |
134 | 1143 IProjectionPosition projPosition= cast(IProjectionPosition) position; |
129 | 1144 return projPosition.computeProjectionRegions(document); |
1145 } | |
1146 | |
1147 int line= document.getLineOfOffset(position.getOffset()); | |
1148 int offset= document.getLineOffset(line + 1); | |
1149 | |
1150 int length= position.getLength() - (offset - position.getOffset()); | |
1151 if (length > 0) | |
145 | 1152 return [new Region(offset, length)]; |
129 | 1153 |
1154 return null; | |
1155 } catch (BadLocationException x) { | |
1156 return null; | |
1157 } | |
1158 } | |
1159 | |
1160 /** | |
1161 * Computes the collapsed region anchor for the given position. Assuming | |
1162 * that the position is the position of an expanded projection annotation, | |
1163 * the anchor is the region that is still visible after the projection | |
1164 * annotation has been collapsed. | |
1165 * | |
1166 * @param position the position | |
1167 * @return the collapsed region anchor | |
1168 */ | |
1169 public Position computeCollapsedRegionAnchor(Position position) { | |
1170 try { | |
1171 IDocument document= getDocument(); | |
1172 if (document is null) | |
1173 return null; | |
1174 | |
1175 int captionOffset= position.getOffset(); | |
138 | 1176 if ( cast(IProjectionPosition)position ) |
134 | 1177 captionOffset+= (cast(IProjectionPosition) position).computeCaptionOffset(document); |
129 | 1178 |
1179 IRegion lineInfo= document.getLineInformationOfOffset(captionOffset); | |
1180 return new Position(lineInfo.getOffset() + lineInfo.getLength(), 0); | |
1181 } catch (BadLocationException x) { | |
1182 } | |
1183 return null; | |
1184 } | |
1185 | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
1186 private void processChanges(Annotation[] annotations, bool fireRedraw, List coverage) { |
129 | 1187 for (int i= 0; i < annotations.length; i++) { |
134 | 1188 ProjectionAnnotation annotation= cast(ProjectionAnnotation) annotations[i]; |
129 | 1189 Position position= fProjectionAnnotationModel.getPosition(annotation); |
1190 | |
1191 if (position is null) | |
1192 continue; | |
1193 | |
1194 if (!covers(coverage, position)) { | |
1195 if (annotation.isCollapsed()) { | |
1196 coverage.add(position); | |
1197 IRegion[] regions= computeCollapsedRegions(position); | |
1198 if (regions !is null) | |
1199 for (int j= 0; j < regions.length; j++) | |
1200 collapse(regions[j].getOffset(), regions[j].getLength(), fireRedraw); | |
1201 } else { | |
1202 expand(position.getOffset(), position.getLength(), fireRedraw); | |
1203 } | |
1204 } | |
1205 } | |
1206 } | |
1207 | |
1208 private bool covers(List coverage, Position position) { | |
1209 Iterator e= coverage.iterator(); | |
1210 while (e.hasNext()) { | |
134 | 1211 Position p= cast(Position) e.next(); |
129 | 1212 if (p.getOffset() <= position.getOffset() && position.getOffset() + position.getLength() <= p.getOffset() + p.getLength()) |
1213 return true; | |
1214 } | |
1215 return false; | |
1216 } | |
1217 | |
1218 /** | |
1219 * Forces this viewer to throw away any old state and to initialize its content | |
1220 * from its projection annotation model. | |
1221 * | |
1222 * @throws BadLocationException in case something goes wrong during initialization | |
1223 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
1224 public final void reinitializeProjection() { |
129 | 1225 |
1226 ProjectionDocument projection= null; | |
1227 | |
1228 ISlaveDocumentManager manager= getSlaveDocumentManager(); | |
1229 if (manager !is null) { | |
1230 IDocument master= getDocument(); | |
1231 if (master !is null) { | |
1232 IDocument slave= manager.createSlaveDocument(master); | |
138 | 1233 if ( cast(ProjectionDocument)slave ) { |
134 | 1234 projection= cast(ProjectionDocument) slave; |
129 | 1235 addMasterDocumentRange(projection, 0, master.getLength()); |
1236 } | |
1237 } | |
1238 } | |
1239 | |
1240 if (projection !is null) { | |
1241 Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); | |
1242 while (e.hasNext()) { | |
134 | 1243 ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); |
129 | 1244 if (annotation.isCollapsed()) { |
1245 Position position= fProjectionAnnotationModel.getPosition(annotation); | |
1246 if (position !is null) { | |
1247 IRegion[] regions= computeCollapsedRegions(position); | |
1248 if (regions !is null) | |
1249 for (int i= 0; i < regions.length; i++) | |
1250 removeMasterDocumentRange(projection, regions[i].getOffset(), regions[i].getLength()); | |
1251 } | |
1252 } | |
1253 } | |
1254 | |
1255 } | |
1256 | |
1257 replaceVisibleDocument(projection); | |
1258 } | |
1259 | |
1260 /* | |
1261 * @see dwtx.jface.text.TextViewer#handleVerifyEvent(dwt.events.VerifyEvent) | |
1262 */ | |
1263 protected void handleVerifyEvent(VerifyEvent e) { | |
1264 IRegion modelRange= event2ModelRange(e); | |
1265 if (exposeModelRange(modelRange)) | |
1266 e.doit= false; | |
1267 else | |
1268 super.handleVerifyEvent(e); | |
1269 } | |
1270 | |
1271 /** | |
1272 * Adds the give column as last column to this viewer's vertical ruler. | |
1273 * | |
1274 * @param column the column to be added | |
1275 */ | |
1276 public void addVerticalRulerColumn(IVerticalRulerColumn column) { | |
1277 IVerticalRuler ruler= getVerticalRuler(); | |
138 | 1278 if ( cast(CompositeRuler)ruler ) { |
134 | 1279 CompositeRuler compositeRuler= cast(CompositeRuler) ruler; |
129 | 1280 compositeRuler.addDecorator(99, column); |
1281 } | |
1282 } | |
1283 | |
1284 /** | |
1285 * Removes the give column from this viewer's vertical ruler. | |
1286 * | |
1287 * @param column the column to be removed | |
1288 */ | |
1289 public void removeVerticalRulerColumn(IVerticalRulerColumn column) { | |
1290 IVerticalRuler ruler= getVerticalRuler(); | |
138 | 1291 if ( cast(CompositeRuler)ruler ) { |
134 | 1292 CompositeRuler compositeRuler= cast(CompositeRuler) ruler; |
129 | 1293 compositeRuler.removeDecorator(column); |
1294 } | |
1295 } | |
1296 | |
1297 /* | |
1298 * @see dwtx.jface.text.ITextViewerExtension5#exposeModelRange(dwtx.jface.text.IRegion) | |
1299 */ | |
1300 public bool exposeModelRange(IRegion modelRange) { | |
1301 if (isProjectionMode()) | |
1302 return fProjectionAnnotationModel.expandAll(modelRange.getOffset(), modelRange.getLength()); | |
1303 | |
1304 if (!overlapsWithVisibleRegion(modelRange.getOffset(), modelRange.getLength())) { | |
1305 resetVisibleRegion(); | |
1306 return true; | |
1307 } | |
1308 | |
1309 return false; | |
1310 } | |
1311 | |
1312 /* | |
1313 * @see dwtx.jface.text.source.SourceViewer#setRangeIndication(int, int, bool) | |
1314 */ | |
1315 public void setRangeIndication(int offset, int length, bool moveCursor) { | |
1316 IRegion rangeIndication= getRangeIndication(); | |
1317 if (moveCursor && fProjectionAnnotationModel !is null && (rangeIndication is null || offset !is rangeIndication.getOffset() || length !is rangeIndication.getLength())) { | |
1318 List expand= new ArrayList(2); | |
1319 // expand the immediate effected collapsed regions | |
1320 Iterator iterator= fProjectionAnnotationModel.getAnnotationIterator(); | |
1321 while (iterator.hasNext()) { | |
134 | 1322 ProjectionAnnotation annotation= cast(ProjectionAnnotation)iterator.next(); |
129 | 1323 if (annotation.isCollapsed() && willAutoExpand(fProjectionAnnotationModel.getPosition(annotation), offset, length)) |
1324 expand.add(annotation); | |
1325 } | |
1326 | |
1327 if (!expand.isEmpty()) { | |
1328 Iterator e= expand.iterator(); | |
1329 while (e.hasNext()) | |
134 | 1330 fProjectionAnnotationModel.expand(cast(Annotation)e.next()); |
129 | 1331 } |
1332 } | |
1333 super.setRangeIndication(offset, length, moveCursor); | |
1334 } | |
1335 | |
1336 private bool willAutoExpand(Position position, int offset, int length) { | |
1337 if (position is null || position.isDeleted()) | |
1338 return false; | |
1339 // right or left boundary | |
1340 if (position.getOffset() is offset || position.getOffset() + position.getLength() is offset + length) | |
1341 return true; | |
1342 // completely embedded in given position | |
1343 if (position.getOffset() < offset && offset + length < position.getOffset() + position.getLength()) | |
1344 return true; | |
1345 return false; | |
1346 } | |
1347 | |
1348 /* | |
1349 * @see dwtx.jface.text.source.SourceViewer#handleDispose() | |
1350 * @since 3.0 | |
1351 */ | |
1352 protected void handleDispose() { | |
1353 fWasProjectionEnabled= false; | |
1354 super.handleDispose(); | |
1355 } | |
1356 | |
1357 /* | |
1358 * @see dwtx.jface.text.TextViewer#handleVisibleDocumentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
1359 */ | |
1360 protected void handleVisibleDocumentChanged(DocumentEvent event) { | |
143 | 1361 if (fHandleProjectionChanges && cast(ProjectionDocumentEvent)event && isProjectionMode()) { |
134 | 1362 ProjectionDocumentEvent e= cast(ProjectionDocumentEvent) event; |
129 | 1363 |
1364 DocumentEvent master= e.getMasterEvent(); | |
1365 if (master !is null) | |
1366 fReplaceVisibleDocumentExecutionTrigger= master.getDocument(); | |
1367 | |
1368 try { | |
1369 | |
1370 int replaceLength= e.getText() is null ? 0 : e.getText().length(); | |
1371 if (ProjectionDocumentEvent.PROJECTION_CHANGE is e.getChangeType()) { | |
1372 if (e.getLength() is 0 && replaceLength !is 0) | |
1373 fProjectionAnnotationModel.expandAll(e.getMasterOffset(), e.getMasterLength()); | |
1374 } else if (master !is null && (replaceLength > 0 || fDeletedLines > 1)) { | |
1375 try { | |
1376 int numberOfLines= e.getDocument().getNumberOfLines(e.getOffset(), replaceLength); | |
1377 if (numberOfLines > 1 || fDeletedLines > 1) | |
1378 fProjectionAnnotationModel.expandAll(master.getOffset(), master.getLength()); | |
1379 } catch (BadLocationException x) { | |
1380 } | |
1381 } | |
1382 | |
1383 } finally { | |
1384 fReplaceVisibleDocumentExecutionTrigger= null; | |
1385 } | |
1386 | |
1387 } | |
1388 } | |
1389 | |
1390 /* | |
1391 * @see dwtx.jface.text.TextViewer#handleVisibleDocumentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
1392 * @since 3.1 | |
1393 */ | |
1394 protected void handleVisibleDocumentAboutToBeChanged(DocumentEvent event) { | |
143 | 1395 if (fHandleProjectionChanges && cast(ProjectionDocumentEvent)event && isProjectionMode()) { |
129 | 1396 int deletedLines; |
1397 try { | |
1398 deletedLines= event.getDocument().getNumberOfLines(event.getOffset(), event.getLength()); | |
1399 } catch (BadLocationException e1) { | |
1400 deletedLines= 0; | |
1401 } | |
1402 fDeletedLines= deletedLines; | |
1403 } | |
1404 } | |
1405 | |
1406 /* | |
1407 * @see dwtx.jface.text.ITextViewerExtension5#getCoveredModelRanges(dwtx.jface.text.IRegion) | |
1408 */ | |
1409 public IRegion[] getCoveredModelRanges(IRegion modelRange) { | |
1410 if (fInformationMapping is null) | |
145 | 1411 return [ new Region(modelRange.getOffset(), modelRange.getLength()) ]; |
129 | 1412 |
138 | 1413 if ( cast(IDocumentInformationMappingExtension)fInformationMapping ) { |
134 | 1414 IDocumentInformationMappingExtension extension= cast(IDocumentInformationMappingExtension) fInformationMapping; |
129 | 1415 try { |
1416 return extension.getExactCoverage(modelRange); | |
1417 } catch (BadLocationException x) { | |
1418 } | |
1419 } | |
1420 | |
1421 return null; | |
1422 } | |
1423 | |
1424 /* | |
1425 * @see dwtx.jface.text.ITextOperationTarget#doOperation(int) | |
1426 */ | |
1427 public void doOperation(int operation) { | |
1428 switch (operation) { | |
1429 case TOGGLE: | |
1430 if (canDoOperation(TOGGLE)) { | |
1431 if (!isProjectionMode()) { | |
1432 enableProjection(); | |
1433 } else { | |
1434 expandAll(); | |
1435 disableProjection(); | |
1436 } | |
1437 return; | |
1438 } | |
1439 } | |
1440 | |
1441 if (!isProjectionMode()) { | |
1442 super.doOperation(operation); | |
1443 return; | |
1444 } | |
1445 | |
1446 StyledText textWidget= getTextWidget(); | |
1447 if (textWidget is null) | |
1448 return; | |
1449 | |
1450 Point selection= null; | |
1451 switch (operation) { | |
1452 | |
1453 case CUT: | |
1454 | |
1455 if (redraws()) { | |
1456 selection= getSelectedRange(); | |
1457 if (exposeModelRange(new Region(selection.x, selection.y))) | |
1458 return; | |
145 | 1459 |
129 | 1460 if (selection.y is 0) |
1461 copyMarkedRegion(true); | |
1462 else | |
1463 copyToClipboard(selection.x, selection.y, true, textWidget); | |
1464 | |
1465 selection= textWidget.getSelectionRange(); | |
1466 fireSelectionChanged(selection.x, selection.y); | |
1467 } | |
1468 break; | |
1469 | |
1470 case COPY: | |
1471 | |
1472 if (redraws()) { | |
1473 selection= getSelectedRange(); | |
1474 if (selection.y is 0) | |
1475 copyMarkedRegion(false); | |
1476 else | |
1477 copyToClipboard(selection.x, selection.y, false, textWidget); | |
1478 } | |
1479 break; | |
1480 | |
1481 case DELETE: | |
1482 | |
1483 if (redraws()) { | |
1484 try { | |
1485 selection= getSelectedRange(); | |
1486 Point widgetSelection= textWidget.getSelectionRange(); | |
1487 if (selection.y is 0 || selection.y is widgetSelection.y) | |
1488 getTextWidget().invokeAction(ST.DELETE_NEXT); | |
1489 else | |
1490 deleteTextRange(selection.x, selection.y, textWidget); | |
1491 | |
1492 selection= textWidget.getSelectionRange(); | |
1493 fireSelectionChanged(selection.x, selection.y); | |
1494 | |
1495 } catch (BadLocationException x) { | |
1496 // ignore | |
1497 } | |
1498 } | |
1499 break; | |
1500 | |
1501 | |
1502 case EXPAND_ALL: | |
1503 if (redraws()) | |
1504 expandAll(); | |
1505 break; | |
1506 | |
1507 case EXPAND: | |
1508 if (redraws()) { | |
1509 expand(); | |
1510 } | |
1511 break; | |
1512 | |
1513 case COLLAPSE_ALL: | |
1514 if (redraws()) | |
1515 collapseAll(); | |
1516 break; | |
145 | 1517 |
129 | 1518 case COLLAPSE: |
1519 if (redraws()) { | |
1520 collapse(); | |
1521 } | |
1522 break; | |
1523 | |
1524 default: | |
1525 super.doOperation(operation); | |
1526 } | |
1527 } | |
1528 | |
1529 /* | |
1530 * @see dwtx.jface.text.source.SourceViewer#canDoOperation(int) | |
1531 */ | |
1532 public bool canDoOperation(int operation) { | |
1533 | |
1534 switch (operation) { | |
1535 case COLLAPSE: | |
1536 case COLLAPSE_ALL: | |
1537 case EXPAND: | |
1538 case EXPAND_ALL: | |
1539 return isProjectionMode(); | |
1540 case TOGGLE: | |
1541 return isProjectionMode() || !isSegmented(); | |
1542 } | |
1543 | |
1544 return super.canDoOperation(operation); | |
1545 } | |
1546 | |
1547 private bool isSegmented() { | |
1548 IDocument document= getDocument(); | |
1549 int length= document is null ? 0 : document.getLength(); | |
1550 IRegion visible= getModelCoverage(); | |
1551 bool isSegmented= visible !is null && !visible.equals(new Region(0, length)); | |
1552 return isSegmented; | |
1553 } | |
1554 | |
1555 private IRegion getMarkedRegion() { | |
1556 if (getTextWidget() is null) | |
1557 return null; | |
1558 | |
1559 if (fMarkPosition is null || fMarkPosition.isDeleted()) | |
1560 return null; | |
1561 | |
1562 int start= fMarkPosition.getOffset(); | |
1563 int end= getSelectedRange().x; | |
1564 | |
1565 return start > end ? new Region (end, start - end) : new Region(start, end - start); | |
1566 } | |
1567 | |
1568 /* | |
1569 * @see dwtx.jface.text.TextViewer#copyMarkedRegion(bool) | |
1570 */ | |
145 | 1571 protected void copyMarkedRegion(bool delete_) { |
129 | 1572 IRegion markedRegion= getMarkedRegion(); |
1573 if (markedRegion !is null) | |
145 | 1574 copyToClipboard(markedRegion.getOffset(), markedRegion.getLength(), delete_, getTextWidget()); |
129 | 1575 } |
1576 | |
145 | 1577 private void copyToClipboard(int offset, int length, bool delete_, StyledText textWidget) { |
129 | 1578 |
1579 String copyText= null; | |
1580 | |
1581 try { | |
1582 IDocument document= getDocument(); | |
1583 copyText= document.get(offset, length); | |
1584 } catch (BadLocationException ex) { | |
1585 // XXX: should log here, but JFace Text has no Log | |
1586 // As a fallback solution let the widget handle this | |
1587 textWidget.copy(); | |
1588 } | |
1589 | |
1590 if (copyText !is null && copyText.equals(textWidget.getSelectionText())) { | |
1591 /* | |
1592 * XXX: Reduce pain of https://bugs.eclipse.org/bugs/show_bug.cgi?id=64498 | |
1593 * by letting the widget handle the copy operation in this special case. | |
1594 */ | |
1595 textWidget.copy(); | |
1596 } else if (copyText !is null) { | |
1597 | |
1598 Clipboard clipboard= new Clipboard(textWidget.getDisplay()); | |
1599 | |
1600 try { | |
145 | 1601 Transfer[] dataTypes= [ TextTransfer.getInstance() ]; |
1602 Object[] data= [ stringcast(copyText) ]; | |
129 | 1603 try { |
1604 clipboard.setContents(data, dataTypes); | |
1605 } catch (DWTError e) { | |
1606 if (e.code !is DND.ERROR_CANNOT_SET_CLIPBOARD) | |
1607 throw e; | |
1608 /* | |
1609 * TODO see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59459 | |
1610 * we should either log and/or inform the user | |
1611 * silently fail for now. | |
1612 */ | |
1613 return; | |
1614 } | |
1615 | |
1616 } finally { | |
1617 clipboard.dispose(); | |
1618 } | |
1619 } | |
1620 | |
145 | 1621 if (delete_) { |
129 | 1622 try { |
1623 deleteTextRange(offset, length, textWidget); | |
1624 } catch (BadLocationException x) { | |
1625 // XXX: should log here, but JFace Text has no Log | |
1626 } | |
1627 } | |
1628 } | |
1629 | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
1630 private void deleteTextRange(int offset, int length, StyledText textWidget) { |
129 | 1631 getDocument().replace(offset, length, ""); //$NON-NLS-1$ |
1632 int widgetCaret= modelOffset2WidgetOffset(offset); | |
1633 if (widgetCaret > -1) | |
1634 textWidget.setSelection(widgetCaret); | |
1635 } | |
1636 | |
1637 /** | |
1638 * Adapts the behavior of the super class to respect line based folding. | |
1639 * | |
1640 * @param widgetSelection the widget selection | |
1641 * @return the model selection while respecting line based folding | |
1642 */ | |
1643 protected Point widgetSelection2ModelSelection(Point widgetSelection) { | |
1644 | |
1645 if (!isProjectionMode()) | |
1646 return super.widgetSelection2ModelSelection(widgetSelection); | |
1647 | |
1648 /* | |
1649 * There is one requirement that governs preservation of logical | |
1650 * positions: | |
1651 * | |
1652 * 1) a selection with widget_length is 0 should never expand to have | |
1653 * model_length > 0. | |
1654 * | |
1655 * There are a number of ambiguities to resolve with projection regions. | |
1656 * A projected region P has a widget-length of zero. Its widget offset | |
1657 * may interact with the selection S in various ways: | |
1658 * | |
1659 * A) P.widget_offset lies at the caret, S.widget_length is zero. | |
1660 * Requirement 1 applies. S is *behind* P (done so by widgetRange2ModelRange). | |
1661 * | |
1662 * B) P.widget_offset lies inside the widget selection. This case is | |
1663 * easy: P is included in S, which is automatically done so by | |
1664 * widgetRange2ModelRange. | |
1665 * | |
1666 * C) P.widget_offset lies at S.widget_end: This is | |
1667 * arguable - our policy is to include P if it belongs to a projection | |
1668 * annotation that overlaps with the widget selection. | |
1669 * | |
1670 * D) P.widget_offset lies at S.widget_offset: Arguable - our policy | |
1671 * is to include P if it belongs to a projection annotation that | |
1672 * overlaps with the widget selection | |
1673 */ | |
1674 IRegion modelSelection= widgetRange2ModelRange(new Region(widgetSelection.x, widgetSelection.y)); | |
1675 if (modelSelection is null) | |
1676 return null; | |
1677 | |
1678 int modelOffset= modelSelection.getOffset(); | |
1679 int modelEndOffset= modelOffset + modelSelection.getLength(); | |
1680 | |
1681 /* Case A: never expand a zero-length selection. S is *behind* P. */ | |
1682 if (widgetSelection.y is 0) | |
1683 return new Point(modelEndOffset, 0); | |
1684 | |
1685 int widgetSelectionExclusiveEnd= widgetSelection.x + widgetSelection.y; | |
1686 Position[] annotationPositions= computeOverlappingAnnotationPositions(modelSelection); | |
1687 for (int i= 0; i < annotationPositions.length; i++) { | |
1688 IRegion[] regions= computeCollapsedRegions(annotationPositions[i]); | |
1689 if (regions is null) | |
1690 continue; | |
1691 for (int j= 0; j < regions.length; j++) { | |
1692 IRegion modelRange= regions[j]; | |
1693 IRegion widgetRange= modelRange2ClosestWidgetRange(modelRange); | |
1694 // only take collapsed ranges, i.e. widget length is 0 | |
1695 if (widgetRange !is null && widgetRange.getLength() is 0) { | |
1696 int widgetOffset= widgetRange.getOffset(); | |
1697 // D) region is collapsed at S.widget_offset | |
1698 if (widgetOffset is widgetSelection.x) | |
1699 modelOffset= Math.min(modelOffset, modelRange.getOffset()); | |
1700 // C) region is collapsed at S.widget_end | |
1701 else if (widgetOffset is widgetSelectionExclusiveEnd) | |
1702 modelEndOffset= Math.max(modelEndOffset, modelRange.getOffset() + modelRange.getLength()); | |
1703 } | |
1704 } | |
1705 } | |
1706 return new Point(modelOffset, modelEndOffset - modelOffset); | |
1707 } | |
1708 | |
1709 /** | |
1710 * Returns the positions of all annotations that intersect with | |
1711 * <code>modelSelection</code> and that are at least partly visible. | |
1712 * @param modelSelection a model range | |
1713 * @return the positions of all annotations that intersect with | |
1714 * <code>modelSelection</code> | |
1715 * @since 3.1 | |
1716 */ | |
1717 private Position[] computeOverlappingAnnotationPositions(IRegion modelSelection) { | |
1718 List positions= new ArrayList(); | |
1719 for (Iterator e= fProjectionAnnotationModel.getAnnotationIterator(); e.hasNext();) { | |
134 | 1720 ProjectionAnnotation annotation= cast(ProjectionAnnotation) e.next(); |
129 | 1721 Position position= fProjectionAnnotationModel.getPosition(annotation); |
1722 if (position !is null && position.overlapsWith(modelSelection.getOffset(), modelSelection.getLength()) && modelRange2WidgetRange(position) !is null) | |
1723 positions.add(position); | |
1724 } | |
158 | 1725 return arraycast!(Position)( positions.toArray()); |
129 | 1726 } |
1727 | |
1728 /* | |
1729 * @see dwtx.jface.text.TextViewer#getFindReplaceDocumentAdapter() | |
1730 */ | |
1731 protected FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() { | |
1732 if (fFindReplaceDocumentAdapter is null) { | |
1733 IDocument document= isProjectionMode() ? getDocument() : getVisibleDocument(); | |
1734 fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(document); | |
1735 } | |
1736 return fFindReplaceDocumentAdapter; | |
1737 } | |
1738 | |
1739 /* | |
1740 * @see dwtx.jface.text.TextViewer#findAndSelect(int, java.lang.String, bool, bool, bool, bool) | |
1741 */ | |
1742 protected int findAndSelect(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) { | |
1743 | |
1744 if (!isProjectionMode()) | |
1745 return super.findAndSelect(startPosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); | |
1746 | |
1747 StyledText textWidget= getTextWidget(); | |
1748 if (textWidget is null) | |
1749 return -1; | |
1750 | |
1751 try { | |
1752 | |
1753 IRegion matchRegion= getFindReplaceDocumentAdapter().find(startPosition, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); | |
1754 if (matchRegion !is null) { | |
1755 exposeModelRange(matchRegion); | |
1756 revealRange(matchRegion.getOffset(), matchRegion.getLength()); | |
1757 setSelectedRange(matchRegion.getOffset(), matchRegion.getLength()); | |
1758 return matchRegion.getOffset(); | |
1759 } | |
1760 | |
1761 } catch (BadLocationException x) { | |
1762 } | |
1763 | |
1764 return -1; | |
1765 } | |
1766 | |
1767 /* | |
1768 * @see dwtx.jface.text.TextViewer#findAndSelectInRange(int, java.lang.String, bool, bool, bool, int, int, bool) | |
1769 */ | |
1770 protected int findAndSelectInRange(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, int rangeOffset, int rangeLength, bool regExSearch) { | |
1771 | |
1772 if (!isProjectionMode()) | |
1773 return super.findAndSelectInRange(startPosition, findString, forwardSearch, caseSensitive, wholeWord, rangeOffset, rangeLength, regExSearch); | |
1774 | |
1775 StyledText textWidget= getTextWidget(); | |
1776 if (textWidget is null) | |
1777 return -1; | |
1778 | |
1779 try { | |
1780 | |
1781 int modelOffset= startPosition; | |
1782 if (forwardSearch && (startPosition is -1 || startPosition < rangeOffset)) { | |
1783 modelOffset= rangeOffset; | |
1784 } else if (!forwardSearch && (startPosition is -1 || startPosition > rangeOffset + rangeLength)) { | |
1785 modelOffset= rangeOffset + rangeLength; | |
1786 } | |
1787 | |
1788 IRegion matchRegion= getFindReplaceDocumentAdapter().find(modelOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch); | |
1789 if (matchRegion !is null) { | |
1790 int offset= matchRegion.getOffset(); | |
1791 int length= matchRegion.getLength(); | |
1792 if (rangeOffset <= offset && offset + length <= rangeOffset + rangeLength) { | |
1793 exposeModelRange(matchRegion); | |
1794 revealRange(offset, length); | |
1795 setSelectedRange(offset, length); | |
1796 return offset; | |
1797 } | |
1798 } | |
1799 | |
1800 } catch (BadLocationException x) { | |
1801 } | |
1802 | |
1803 return -1; | |
1804 } | |
1805 } |