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