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