Mercurial > projects > dwt-addons
annotate dwtx/jface/text/link/LinkedModeUI.d @ 162:1a5b8f8129df
...
author | Frank Benoit <benoit@tionex.de> |
---|---|
date | Mon, 08 Sep 2008 00:51:37 +0200 |
parents | f70d9508c95c |
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.link.LinkedModeUI; | |
14 | |
131 | 15 import dwtx.jface.text.link.LinkedModeModel; // packageimport |
16 import dwtx.jface.text.link.LinkedPosition; // packageimport | |
17 import dwtx.jface.text.link.ILinkedModeListener; // packageimport | |
18 import dwtx.jface.text.link.TabStopIterator; // packageimport | |
19 import dwtx.jface.text.link.InclusivePositionUpdater; // packageimport | |
20 import dwtx.jface.text.link.LinkedPositionGroup; // packageimport | |
21 import dwtx.jface.text.link.LinkedModeManager; // packageimport | |
22 import dwtx.jface.text.link.LinkedPositionAnnotations; // packageimport | |
23 import dwtx.jface.text.link.ProposalPosition; // packageimport | |
24 | |
25 | |
129 | 26 import dwt.dwthelper.utils; |
27 | |
153
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
28 import dwtx.dwtxhelper.Collection; |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
29 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
30 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
31 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
32 |
f70d9508c95c
Fix java Collection imports
Frank Benoit <benoit@tionex.de>
parents:
150
diff
changeset
|
33 |
129 | 34 |
35 import dwt.DWT; | |
36 import dwt.custom.StyledText; | |
37 import dwt.custom.VerifyKeyListener; | |
38 import dwt.events.ShellEvent; | |
39 import dwt.events.ShellListener; | |
40 import dwt.events.VerifyEvent; | |
41 import dwt.graphics.Point; | |
42 import dwt.widgets.Display; | |
43 import dwt.widgets.Shell; | |
44 import dwtx.core.runtime.Assert; | |
45 import dwtx.jface.internal.text.link.contentassist.ContentAssistant2; | |
46 import dwtx.jface.internal.text.link.contentassist.IProposalListener; | |
47 import dwtx.jface.text.BadLocationException; | |
48 import dwtx.jface.text.BadPartitioningException; | |
49 import dwtx.jface.text.BadPositionCategoryException; | |
50 import dwtx.jface.text.DefaultPositionUpdater; | |
51 import dwtx.jface.text.DocumentCommand; | |
52 import dwtx.jface.text.DocumentEvent; | |
53 import dwtx.jface.text.IAutoEditStrategy; | |
54 import dwtx.jface.text.IDocument; | |
55 import dwtx.jface.text.IDocumentExtension3; | |
56 import dwtx.jface.text.IDocumentListener; | |
57 import dwtx.jface.text.IEditingSupport; | |
58 import dwtx.jface.text.IEditingSupportRegistry; | |
59 import dwtx.jface.text.IPositionUpdater; | |
60 import dwtx.jface.text.IRegion; | |
61 import dwtx.jface.text.IRewriteTarget; | |
62 import dwtx.jface.text.ITextInputListener; | |
63 import dwtx.jface.text.ITextOperationTarget; | |
64 import dwtx.jface.text.ITextSelection; | |
65 import dwtx.jface.text.ITextViewer; | |
66 import dwtx.jface.text.ITextViewerExtension; | |
67 import dwtx.jface.text.ITextViewerExtension2; | |
68 import dwtx.jface.text.ITextViewerExtension5; | |
69 import dwtx.jface.text.Position; | |
70 import dwtx.jface.text.Region; | |
71 import dwtx.jface.text.contentassist.ICompletionProposal; | |
72 import dwtx.jface.text.contentassist.ICompletionProposalExtension6; | |
73 import dwtx.jface.text.source.IAnnotationModel; | |
74 import dwtx.jface.text.source.IAnnotationModelExtension; | |
75 import dwtx.jface.text.source.ISourceViewer; | |
76 import dwtx.jface.viewers.IPostSelectionProvider; | |
77 import dwtx.jface.viewers.ISelection; | |
78 import dwtx.jface.viewers.ISelectionChangedListener; | |
79 import dwtx.jface.viewers.SelectionChangedEvent; | |
80 | |
81 /** | |
82 * The UI for linked mode. Detects events that influence behavior of the linked mode | |
83 * UI and acts upon them. | |
84 * <p> | |
85 * <code>LinkedModeUI</code> relies on all added | |
86 * <code>LinkedModeUITarget</code>s to provide implementations of | |
87 * <code>ITextViewer</code> that implement <code>ITextViewerExtension</code>, | |
88 * and the documents being edited to implement <code>IDocumentExtension3</code>. | |
89 * </p> | |
90 * <p> | |
91 * Clients may instantiate and extend this class. | |
92 * </p> | |
93 * | |
94 * @since 3.0 | |
95 */ | |
96 public class LinkedModeUI { | |
97 | |
98 /* cycle constants */ | |
99 /** | |
100 * Constant indicating that this UI should never cycle from the last | |
101 * position to the first and vice versa. | |
102 */ | |
162 | 103 public static Object CYCLE_NEVER_; |
104 public static Object CYCLE_NEVER(){ | |
105 if( CYCLE_NEVER_ is null ){ | |
106 synchronized( LinkedModeUI.classinfo ){ | |
107 if( CYCLE_NEVER_ is null ){ | |
108 CYCLE_NEVER_ = new Object(); | |
109 } | |
110 } | |
111 } | |
112 return CYCLE_NEVER_; | |
113 } | |
129 | 114 /** |
115 * Constant indicating that this UI should always cycle from the last | |
116 * position to the first and vice versa. | |
117 */ | |
162 | 118 public static Object CYCLE_ALWAYS_; |
119 public static Object CYCLE_ALWAYS(){ | |
120 if( CYCLE_ALWAYS_ is null ){ | |
121 synchronized( LinkedModeUI.classinfo ){ | |
122 if( CYCLE_ALWAYS_ is null ){ | |
123 CYCLE_ALWAYS_ = new Object(); | |
124 } | |
125 } | |
126 } | |
127 return CYCLE_ALWAYS_; | |
128 } | |
129 | 129 /** |
130 * Constant indicating that this UI should cycle from the last position to | |
131 * the first and vice versa if its model is not nested. | |
132 */ | |
162 | 133 public static Object CYCLE_WHEN_NO_PARENT_; |
134 public static Object CYCLE_WHEN_NO_PARENT(){ | |
135 if( CYCLE_WHEN_NO_PARENT_ is null ){ | |
136 synchronized( LinkedModeUI.classinfo ){ | |
137 if( CYCLE_WHEN_NO_PARENT_ is null ){ | |
138 CYCLE_WHEN_NO_PARENT_ = new Object(); | |
139 } | |
140 } | |
141 } | |
142 return CYCLE_WHEN_NO_PARENT_; | |
143 } | |
129 | 144 |
145 /** | |
146 * Listener that gets notified when the linked mode UI switches its focus position. | |
147 * <p> | |
148 * Clients may implement this interface. | |
149 * </p> | |
150 */ | |
151 public interface ILinkedModeUIFocusListener { | |
152 /** | |
153 * Called when the UI for the linked mode leaves a linked position. | |
154 * | |
155 * @param position the position being left | |
156 * @param target the target where <code>position</code> resides in | |
157 */ | |
158 void linkingFocusLost(LinkedPosition position, LinkedModeUITarget target); | |
159 /** | |
160 * Called when the UI for the linked mode gives focus to a linked position. | |
161 * | |
162 * @param position the position being entered | |
163 * @param target the target where <code>position</code> resides in | |
164 */ | |
165 void linkingFocusGained(LinkedPosition position, LinkedModeUITarget target); | |
166 } | |
167 | |
168 /** | |
169 * Null object implementation of focus listener. | |
170 */ | |
171 private static final class EmtpyFocusListener : ILinkedModeUIFocusListener { | |
172 | |
173 public void linkingFocusGained(LinkedPosition position, LinkedModeUITarget target) { | |
174 // ignore | |
175 } | |
176 | |
177 public void linkingFocusLost(LinkedPosition position, LinkedModeUITarget target) { | |
178 // ignore | |
179 } | |
180 } | |
181 | |
182 /** | |
183 * A link target consists of a viewer and gets notified if the linked mode UI on | |
184 * it is being shown. | |
185 * <p> | |
186 * Clients may extend this class. | |
187 * </p> | |
188 * @since 3.0 | |
189 */ | |
190 public static abstract class LinkedModeUITarget : ILinkedModeUIFocusListener { | |
191 /** | |
192 * Returns the viewer represented by this target, never <code>null</code>. | |
193 * | |
194 * @return the viewer associated with this target. | |
195 */ | |
196 public abstract ITextViewer getViewer(); | |
197 | |
198 /** | |
199 * The viewer's text widget is initialized when the UI first connects | |
200 * to the viewer and never changed thereafter. This is to keep the | |
201 * reference of the widget that we have registered our listeners with, | |
202 * as the viewer, when it gets disposed, does not remember it, resulting | |
203 * in a situation where we cannot uninstall the listeners and a memory leak. | |
204 */ | |
205 StyledText fWidget; | |
206 | |
207 /** The cached shell - same reason as fWidget. */ | |
208 Shell fShell; | |
209 | |
210 /** The registered listener, or <code>null</code>. */ | |
211 KeyListener fKeyListener; | |
212 | |
213 /** The cached custom annotation model. */ | |
214 LinkedPositionAnnotations fAnnotationModel; | |
215 } | |
216 | |
217 private static final class EmptyTarget : LinkedModeUITarget { | |
218 | |
219 private ITextViewer fTextViewer; | |
220 | |
221 /** | |
222 * @param viewer the viewer | |
223 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
224 public this(ITextViewer viewer) { |
162 | 225 Assert.isNotNull(cast(Object)viewer); |
129 | 226 fTextViewer= viewer; |
227 } | |
228 | |
229 /* | |
230 * @see dwtx.jdt.internal.ui.text.link2.LinkedModeUI.ILinkedUITarget#getViewer() | |
231 */ | |
232 public ITextViewer getViewer() { | |
233 return fTextViewer; | |
234 } | |
235 | |
236 /** | |
237 * {@inheritDoc} | |
238 */ | |
239 public void linkingFocusLost(LinkedPosition position, LinkedModeUITarget target) { | |
240 } | |
241 | |
242 /** | |
243 * {@inheritDoc} | |
244 */ | |
245 public void linkingFocusGained(LinkedPosition position, LinkedModeUITarget target) { | |
246 } | |
247 | |
248 } | |
249 | |
250 /** | |
251 * Listens for state changes in the model. | |
252 */ | |
253 private final class ExitListener : ILinkedModeListener { | |
254 public void left(LinkedModeModel model, int flags) { | |
255 leave(ILinkedModeListener.EXIT_ALL | flags); | |
256 } | |
257 | |
258 public void suspend(LinkedModeModel model) { | |
259 disconnect(); | |
260 redraw(); | |
261 } | |
262 | |
263 public void resume(LinkedModeModel model, int flags) { | |
264 if ((flags & ILinkedModeListener.EXIT_ALL) !is 0) { | |
265 leave(flags); | |
266 } else { | |
267 connect(); | |
268 if ((flags & ILinkedModeListener.SELECT) !is 0) | |
269 select(); | |
270 ensureAnnotationModelInstalled(); | |
271 redraw(); | |
272 } | |
273 } | |
274 } | |
275 | |
276 /** | |
277 * Exit flags returned if a custom exit policy wants to exit linked mode. | |
278 * <p> | |
279 * Clients may instantiate this class. | |
280 * </p> | |
281 */ | |
282 public static class ExitFlags { | |
283 /** The flags to return in the <code>leave</code> method. */ | |
284 public int flags; | |
285 /** The doit flag of the checked <code>VerifyKeyEvent</code>. */ | |
286 public bool doit; | |
287 /** | |
288 * Creates a new instance. | |
289 * | |
290 * @param flags the exit flags | |
291 * @param doit the doit flag for the verify event | |
292 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
293 public this(int flags, bool doit) { |
129 | 294 this.flags= flags; |
295 this.doit= doit; | |
296 } | |
297 } | |
298 | |
299 /** | |
300 * An exit policy can be registered by a caller to get custom exit | |
301 * behavior. | |
302 * <p> | |
303 * Clients may implement this interface. | |
304 * </p> | |
305 */ | |
306 public interface IExitPolicy { | |
307 /** | |
308 * Checks whether the linked mode should be left after receiving the | |
309 * given <code>VerifyEvent</code> and selection. Note that the event | |
310 * carries widget coordinates as opposed to <code>offset</code> and | |
311 * <code>length</code> which are document coordinates. | |
312 * | |
313 * @param model the linked mode model | |
314 * @param event the verify event | |
315 * @param offset the offset of the current selection | |
316 * @param length the length of the current selection | |
317 * @return valid exit flags or <code>null</code> if no special action | |
318 * should be taken | |
319 */ | |
320 ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length); | |
321 } | |
322 | |
323 /** | |
324 * A NullObject implementation of <code>IExitPolicy</code>. | |
325 */ | |
326 private static class NullExitPolicy : IExitPolicy { | |
327 /* | |
328 * @see dwtx.jdt.internal.ui.text.link2.LinkedModeUI.IExitPolicy#doExit(dwt.events.VerifyEvent, int, int) | |
329 */ | |
330 public ExitFlags doExit(LinkedModeModel model, VerifyEvent event, int offset, int length) { | |
331 return null; | |
332 } | |
333 } | |
334 | |
335 /** | |
336 * Listens for shell events and acts upon them. | |
337 */ | |
338 private class Closer : ShellListener, ITextInputListener { | |
339 | |
340 public void shellActivated(ShellEvent e) { | |
341 } | |
342 | |
343 public void shellClosed(ShellEvent e) { | |
344 leave(ILinkedModeListener.EXIT_ALL); | |
345 } | |
346 | |
347 public void shellDeactivated(ShellEvent e) { | |
348 // TODO re-enable after debugging | |
349 // if (true) return; | |
350 | |
351 // from LinkedPositionUI: | |
352 | |
353 // don't deactivate on focus lost, since the proposal popups may take focus | |
354 // plus: it doesn't hurt if you can check with another window without losing linked mode | |
355 // since there is no intrusive popup sticking out. | |
356 | |
357 // need to check first what happens on reentering based on an open action | |
358 // Seems to be no problem | |
359 | |
360 // Better: | |
361 // Check with content assistant and only leave if its not the proposal shell that took the | |
362 // focus away. | |
363 | |
364 StyledText text; | |
162 | 365 ITextViewer viewer; |
129 | 366 Display display; |
367 | |
368 if (fCurrentTarget is null || (text= fCurrentTarget.fWidget) is null | |
369 || text.isDisposed() || (display= text.getDisplay()) is null | |
370 || display.isDisposed() | |
371 || (viewer= fCurrentTarget.getViewer()) is null) | |
372 { | |
373 leave(ILinkedModeListener.EXIT_ALL); | |
374 } | |
375 else | |
376 { | |
377 // Post in UI thread since the assistant popup will only get the focus after we lose it. | |
162 | 378 display.asyncExec(dgRunnable( (ITextViewer viewer_) { |
379 if (fIsActive && cast(IEditingSupportRegistry)viewer_ ) { | |
380 IEditingSupport[] helpers= (cast(IEditingSupportRegistry) viewer_).getRegisteredSupports(); | |
381 for (int i= 0; i < helpers.length; i++) { | |
382 if (helpers[i].ownsFocusShell()) | |
383 return; | |
129 | 384 } |
162 | 385 } |
129 | 386 |
162 | 387 // else |
388 leave(ILinkedModeListener.EXIT_ALL); | |
129 | 389 |
162 | 390 }, viewer )); |
129 | 391 } |
392 } | |
393 | |
394 public void shellDeiconified(ShellEvent e) { | |
395 } | |
396 | |
397 public void shellIconified(ShellEvent e) { | |
398 leave(ILinkedModeListener.EXIT_ALL); | |
399 } | |
400 | |
401 /* | |
402 * @see dwtx.jface.text.ITextInputListener#inputDocumentAboutToBeChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
403 */ | |
404 public void inputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) { | |
405 leave(ILinkedModeListener.EXIT_ALL); | |
406 } | |
407 | |
408 /* | |
409 * @see dwtx.jface.text.ITextInputListener#inputDocumentChanged(dwtx.jface.text.IDocument, dwtx.jface.text.IDocument) | |
410 */ | |
411 public void inputDocumentChanged(IDocument oldInput, IDocument newInput) { | |
412 } | |
413 | |
414 } | |
415 | |
416 /** | |
417 * @since 3.1 | |
418 */ | |
419 private class DocumentListener : IDocumentListener { | |
420 /* | |
421 * @see dwtx.jface.text.IDocumentListener#documentAboutToBeChanged(dwtx.jface.text.DocumentEvent) | |
422 */ | |
423 public void documentAboutToBeChanged(DocumentEvent event) { | |
424 | |
425 // default behavior: any document change outside a linked position | |
426 // causes us to exit | |
427 int end= event.getOffset() + event.getLength(); | |
428 for (int offset= event.getOffset(); offset <= end; offset++) { | |
429 if (!fModel.anyPositionContains(offset)) { | |
430 ITextViewer viewer= fCurrentTarget.getViewer(); | |
143 | 431 if (fFramePosition !is null && cast(IEditingSupportRegistry)viewer ) { |
134 | 432 IEditingSupport[] helpers= (cast(IEditingSupportRegistry) viewer).getRegisteredSupports(); |
129 | 433 for (int i= 0; i < helpers.length; i++) { |
434 if (helpers[i].isOriginator(null, new Region(fFramePosition.getOffset(), fFramePosition.getLength()))) | |
435 return; | |
436 } | |
437 } | |
438 | |
439 leave(ILinkedModeListener.EXTERNAL_MODIFICATION); | |
440 return; | |
441 } | |
442 } | |
443 } | |
444 | |
445 /* | |
446 * @see dwtx.jface.text.IDocumentListener#documentChanged(dwtx.jface.text.DocumentEvent) | |
447 */ | |
448 public void documentChanged(DocumentEvent event) { | |
449 } | |
450 } | |
451 | |
452 /** | |
453 * Listens for key events, checks the exit policy for custom exit | |
454 * strategies but defaults to handling Tab, Enter, and Escape. | |
455 */ | |
456 private class KeyListener : VerifyKeyListener { | |
457 | |
458 private bool fIsEnabled= true; | |
459 | |
460 public void verifyKey(VerifyEvent event) { | |
461 | |
462 if (!event.doit || !fIsEnabled) | |
463 return; | |
464 | |
465 Point selection= fCurrentTarget.getViewer().getSelectedRange(); | |
466 int offset= selection.x; | |
467 int length= selection.y; | |
468 | |
469 // if the custom exit policy returns anything, use that | |
470 ExitFlags exitFlags= fExitPolicy.doExit(fModel, event, offset, length); | |
471 if (exitFlags !is null) { | |
472 leave(exitFlags.flags); | |
473 event.doit= exitFlags.doit; | |
474 return; | |
475 } | |
476 | |
477 // standard behavior: | |
478 // (Shift+)Tab: jumps from position to position, depending on cycle mode | |
479 // Enter: accepts all entries and leaves all (possibly stacked) environments, the last sets the caret | |
480 // Esc: accepts all entries and leaves all (possibly stacked) environments, the caret stays | |
481 // ? what do we do to leave one level of a cycling model that is stacked? | |
134 | 482 // -> This is only the case if the level was set up with forced cycling cast(CYCLE_ALWAYS), in which case |
129 | 483 // the caller is sure that one does not need by-level exiting. |
484 switch (event.character) { | |
485 // [SHIFT-]TAB = hop between edit boxes | |
486 case 0x09: | |
487 if (!(fExitPosition !is null && fExitPosition.includes(offset)) && !fModel.anyPositionContains(offset)) { | |
488 // outside any edit box -> leave (all? TODO should only leave the affected, level and forward to the next upper) | |
489 leave(ILinkedModeListener.EXIT_ALL); | |
490 break; | |
491 } | |
492 | |
493 if (event.stateMask is DWT.SHIFT) | |
494 previous(); | |
495 else | |
496 next(); | |
497 | |
498 event.doit= false; | |
499 break; | |
500 | |
501 // ENTER | |
502 case 0x0A: | |
503 // Ctrl+Enter on WinXP | |
504 case 0x0D: | |
505 // if ((fExitPosition !is null && fExitPosition.includes(offset)) || !fModel.anyPositionContains(offset)) { | |
506 if (!fModel.anyPositionContains(offset)) { | |
507 // if ((fExitPosition is null || !fExitPosition.includes(offset)) && !fModel.anyPositionContains(offset)) { | |
508 // outside any edit box or on exit position -> leave (all? TODO should only leave the affected, level and forward to the next upper) | |
509 leave(ILinkedModeListener.EXIT_ALL); | |
510 break; | |
511 } | |
512 | |
513 // normal case: exit entire stack and put caret to final position | |
514 leave(ILinkedModeListener.EXIT_ALL | ILinkedModeListener.UPDATE_CARET); | |
515 event.doit= false; | |
516 break; | |
517 | |
518 // ESC | |
519 case 0x1B: | |
520 // exit entire stack and leave caret | |
521 leave(ILinkedModeListener.EXIT_ALL); | |
522 event.doit= false; | |
523 break; | |
524 | |
525 default: | |
526 if (event.character !is 0) { | |
527 if (!controlUndoBehavior(offset, length)) { | |
528 leave(ILinkedModeListener.EXIT_ALL); | |
529 break; | |
530 } | |
531 } | |
532 } | |
533 } | |
534 | |
535 private bool controlUndoBehavior(int offset, int length) { | |
536 LinkedPosition position= fModel.findPosition(new LinkedPosition(fCurrentTarget.getViewer().getDocument(), offset, length, LinkedPositionGroup.NO_STOP)); | |
537 if (position !is null) { | |
538 | |
539 // if the last position is not the same and there is an open change: close it. | |
540 if (!position.equals(fPreviousPosition)) | |
541 endCompoundChange(); | |
542 | |
543 beginCompoundChange(); | |
544 } | |
545 | |
546 fPreviousPosition= position; | |
547 return fPreviousPosition !is null; | |
548 } | |
549 | |
550 /** | |
551 * @param enabled the new enabled state | |
552 */ | |
553 public void setEnabled(bool enabled) { | |
554 fIsEnabled= enabled; | |
555 } | |
556 | |
557 } | |
558 | |
559 /** | |
560 * Installed as post selection listener on the watched viewer. Updates the | |
561 * linked position after cursor movement, even to positions not in the | |
562 * iteration list. | |
563 */ | |
564 private class MySelectionListener : ISelectionChangedListener { | |
565 | |
566 /* | |
567 * @see dwtx.jface.viewers.ISelectionChangedListener#selectionChanged(dwtx.jface.viewers.SelectionChangedEvent) | |
568 */ | |
569 public void selectionChanged(SelectionChangedEvent event) { | |
570 ISelection selection= event.getSelection(); | |
138 | 571 if ( cast(ITextSelection)selection ) { |
134 | 572 ITextSelection textsel= cast(ITextSelection) selection; |
149 | 573 if ( cast(ITextViewer)event.getSelectionProvider() ) { |
134 | 574 IDocument doc= (cast(ITextViewer) event.getSelectionProvider()).getDocument(); |
129 | 575 if (doc !is null) { |
576 int offset= textsel.getOffset(); | |
577 int length= textsel.getLength(); | |
578 if (offset >= 0 && length >= 0) { | |
579 LinkedPosition find= new LinkedPosition(doc, offset, length, LinkedPositionGroup.NO_STOP); | |
580 LinkedPosition pos= fModel.findPosition(find); | |
581 if (pos is null && fExitPosition !is null && fExitPosition.includes(find)) | |
582 pos= fExitPosition; | |
583 | |
584 if (pos !is null) | |
585 switchPosition(pos, false, false); | |
586 } | |
587 } | |
588 } | |
589 } | |
590 } | |
591 | |
592 } | |
593 | |
594 private class ProposalListener : IProposalListener { | |
595 | |
596 /* | |
597 * @see dwtx.jface.internal.text.link.contentassist.IProposalListener#proposalChosen(dwtx.jface.text.contentassist.ICompletionProposal) | |
598 */ | |
599 public void proposalChosen(ICompletionProposal proposal) { | |
600 next(); | |
601 } | |
602 } | |
603 | |
604 /** The current viewer. */ | |
605 private LinkedModeUITarget fCurrentTarget; | |
606 /** | |
607 * The manager of the linked positions we provide a UI for. | |
608 * @since 3.1 | |
609 */ | |
610 private LinkedModeModel fModel; | |
611 /** The set of viewers we manage. */ | |
612 private LinkedModeUITarget[] fTargets; | |
613 /** The iterator over the tab stop positions. */ | |
614 private TabStopIterator fIterator; | |
615 | |
616 /* Our team of event listeners */ | |
617 /** The shell listener. */ | |
162 | 618 private Closer fCloser; |
129 | 619 /** The linked mode listener. */ |
162 | 620 private ILinkedModeListener fLinkedListener; |
129 | 621 /** The selection listener. */ |
162 | 622 private MySelectionListener fSelectionListener; |
129 | 623 /** The content assist listener. */ |
162 | 624 private ProposalListener fProposalListener; |
129 | 625 /** |
626 * The document listener. | |
627 * @since 3.1 | |
628 */ | |
162 | 629 private IDocumentListener fDocumentListener; |
129 | 630 |
631 /** The last caret position, used by fCaretListener. */ | |
162 | 632 private const Position fCaretPosition; |
129 | 633 /** The exit policy to control custom exit behavior */ |
162 | 634 private IExitPolicy fExitPolicy; |
129 | 635 /** The current frame position shown in the UI, or <code>null</code>. */ |
636 private LinkedPosition fFramePosition; | |
637 /** The last visited position, used for undo / redo. */ | |
638 private LinkedPosition fPreviousPosition; | |
639 /** The content assistant used to show proposals. */ | |
640 private ContentAssistant2 fAssistant; | |
641 /** The exit position. */ | |
642 private LinkedPosition fExitPosition; | |
643 /** State indicator to prevent multiple invocation of leave. */ | |
644 private bool fIsActive= false; | |
645 /** The position updater for the exit position. */ | |
162 | 646 private IPositionUpdater fPositionUpdater; |
129 | 647 /** Whether to show context info. */ |
648 private bool fDoContextInfo= false; | |
649 /** Whether we have begun a compound change, but not yet closed. */ | |
650 private bool fHasOpenCompoundChange= false; | |
651 /** The position listener. */ | |
162 | 652 private ILinkedModeUIFocusListener fPositionListener; |
653 private IAutoEditStrategy fAutoEditVetoer; | |
654 private void fAutoEditVetoer_init(){ | |
655 fAutoEditVetoer = new class() IAutoEditStrategy { | |
656 /* | |
657 * @see dwtx.jface.text.IAutoEditStrategy#customizeDocumentCommand(dwtx.jface.text.IDocument, dwtx.jface.text.DocumentCommand) | |
658 */ | |
659 public void customizeDocumentCommand(IDocument document, DocumentCommand command) { | |
660 // invalidate the change to ensure that the change is performed on the document only. | |
661 if (fModel.anyPositionContains(command.offset)) { | |
662 command.doit= false; | |
663 command.caretOffset= command.offset + command.length; | |
664 } | |
129 | 665 |
666 } | |
162 | 667 }; |
668 } | |
150 | 669 |
129 | 670 /** Whether this UI is in simple highlighting mode or not. */ |
671 private bool fSimple; | |
672 | |
673 /** | |
674 * Tells whether colored labels support is enabled. | |
675 * @since 3.4 | |
676 */ | |
677 private bool fIsColoredLabelsSupportEnabled= false; | |
678 | |
162 | 679 private this(){ |
680 fCloser= new Closer(); | |
681 fLinkedListener= new ExitListener(); | |
682 fSelectionListener= new MySelectionListener(); | |
683 fProposalListener= new ProposalListener(); | |
684 fDocumentListener= new DocumentListener(); | |
685 fCaretPosition= new Position(0, 0); | |
686 fExitPolicy= new NullExitPolicy(); | |
687 fPositionUpdater= new DefaultPositionUpdater(getCategory()); | |
688 fPositionListener= new EmtpyFocusListener(); | |
689 fAutoEditVetoer_init(); | |
690 } | |
691 | |
129 | 692 /** |
693 * Creates a new UI on the given model and the set of viewers. The model | |
694 * must provide a tab stop sequence with a non-empty list of tab stops. | |
695 * | |
696 * @param model the linked mode model | |
697 * @param targets the non-empty list of targets upon which the linked mode | |
698 * UI should act | |
699 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
700 public this(LinkedModeModel model, LinkedModeUITarget[] targets) { |
162 | 701 this(); |
129 | 702 constructor(model, targets); |
703 } | |
704 | |
705 /** | |
706 * Convenience constructor for just one viewer. | |
707 * | |
708 * @param model the linked mode model | |
709 * @param viewer the viewer upon which the linked mode UI should act | |
710 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
711 public this(LinkedModeModel model, ITextViewer viewer) { |
162 | 712 this(); |
150 | 713 constructor(model, [new EmptyTarget(viewer)]); |
129 | 714 } |
715 | |
716 /** | |
717 * Convenience constructor for multiple viewers. | |
718 * | |
719 * @param model the linked mode model | |
720 * @param viewers the non-empty list of viewers upon which the linked mode | |
721 * UI should act | |
722 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
723 public this(LinkedModeModel model, ITextViewer[] viewers) { |
162 | 724 this(); |
129 | 725 LinkedModeUITarget[] array= new LinkedModeUITarget[viewers.length]; |
726 for (int i= 0; i < array.length; i++) { | |
727 array[i]= new EmptyTarget(viewers[i]); | |
728 } | |
729 constructor(model, array); | |
730 } | |
731 | |
732 /** | |
733 * Convenience constructor for one target. | |
734 * | |
735 * @param model the linked mode model | |
736 * @param target the target upon which the linked mode UI should act | |
737 */ | |
133
7d818bd32d63
Fix ctors to this with gvim regexp
Frank Benoit <benoit@tionex.de>
parents:
131
diff
changeset
|
738 public this(LinkedModeModel model, LinkedModeUITarget target) { |
162 | 739 this(); |
150 | 740 constructor(model, [target]); |
129 | 741 } |
742 | |
743 /** | |
744 * This does the actual constructor work. | |
745 * | |
746 * @param model the linked mode model | |
747 * @param targets the non-empty array of targets upon which the linked mode UI | |
748 * should act | |
749 */ | |
750 private void constructor(LinkedModeModel model, LinkedModeUITarget[] targets) { | |
751 Assert.isNotNull(model); | |
162 | 752 //Assert.isNotNull(targets); |
129 | 753 Assert.isTrue(targets.length > 0); |
754 Assert.isTrue(model.getTabStopSequence().size() > 0); | |
755 | |
756 fModel= model; | |
757 fTargets= targets; | |
758 fCurrentTarget= targets[0]; | |
759 fIterator= new TabStopIterator(fModel.getTabStopSequence()); | |
760 fIterator.setCycling(!fModel.isNested()); | |
761 fModel.addLinkingListener(fLinkedListener); | |
762 | |
763 fAssistant= new ContentAssistant2(); | |
764 fAssistant.addProposalListener(fProposalListener); | |
765 // TODO find a way to set up content assistant. | |
766 // fAssistant.setDocumentPartitioning(IJavaPartitions.JAVA_PARTITIONING); | |
767 fAssistant.enableColoredLabels(fIsColoredLabelsSupportEnabled); | |
140
26688fec6d23
Following dsss compile errors
Frank Benoit <benoit@tionex.de>
parents:
138
diff
changeset
|
768 fCaretPosition.delete_(); |
129 | 769 } |
770 | |
771 /** | |
772 * Starts this UI on the first position. | |
773 */ | |
774 public void enter() { | |
775 fIsActive= true; | |
776 connect(); | |
777 next(); | |
778 } | |
779 | |
780 /** | |
781 * Sets an <code>IExitPolicy</code> to customize the exit behavior of | |
782 * this linked mode UI. | |
783 * | |
784 * @param policy the exit policy to use. | |
785 */ | |
786 public void setExitPolicy(IExitPolicy policy) { | |
787 fExitPolicy= policy; | |
788 } | |
789 | |
790 /** | |
791 * Sets the exit position to move the caret to when linked mode mode is | |
792 * exited. | |
793 * | |
794 * @param target the target where the exit position is located | |
795 * @param offset the offset of the exit position | |
796 * @param length the length of the exit position (in case there should be a | |
797 * selection) | |
798 * @param sequence set to the tab stop position of the exit position, or | |
799 * <code>LinkedPositionGroup.NO_STOP</code> if there should be no | |
800 * tab stop. | |
801 * @throws BadLocationException if the position is not valid in the viewer's | |
802 * document | |
803 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
804 public void setExitPosition(LinkedModeUITarget target, int offset, int length, int sequence) { |
129 | 805 // remove any existing exit position |
806 if (fExitPosition !is null) { | |
807 fExitPosition.getDocument().removePosition(fExitPosition); | |
808 fIterator.removePosition(fExitPosition); | |
809 fExitPosition= null; | |
810 } | |
811 | |
812 IDocument doc= target.getViewer().getDocument(); | |
813 if (doc is null) | |
814 return; | |
815 | |
816 fExitPosition= new LinkedPosition(doc, offset, length, sequence); | |
817 doc.addPosition(fExitPosition); // gets removed in leave() | |
818 if (sequence !is LinkedPositionGroup.NO_STOP) | |
819 fIterator.addPosition(fExitPosition); | |
820 | |
821 } | |
822 | |
823 /** | |
824 * Sets the exit position to move the caret to when linked mode is exited. | |
825 * | |
826 * @param viewer the viewer where the exit position is located | |
827 * @param offset the offset of the exit position | |
828 * @param length the length of the exit position (in case there should be a | |
829 * selection) | |
830 * @param sequence set to the tab stop position of the exit position, or | |
831 * <code>LinkedPositionGroup.NO_STOP</code> if there should be no tab stop. | |
832 * @throws BadLocationException if the position is not valid in the | |
833 * viewer's document | |
834 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
835 public void setExitPosition(ITextViewer viewer, int offset, int length, int sequence) { |
129 | 836 setExitPosition(new EmptyTarget(viewer), offset, length, sequence); |
837 } | |
838 | |
839 /** | |
840 * Sets the cycling mode to either of <code>CYCLING_ALWAYS</code>, | |
841 * <code>CYCLING_NEVER</code>, or <code>CYCLING_WHEN_NO_PARENT</code>, | |
842 * which is the default. | |
843 * | |
844 * @param mode the new cycling mode. | |
845 */ | |
846 public void setCyclingMode(Object mode) { | |
847 if (mode !is CYCLE_ALWAYS && mode !is CYCLE_NEVER && mode !is CYCLE_WHEN_NO_PARENT) | |
162 | 848 throw new IllegalArgumentException(null); |
129 | 849 |
850 if (mode is CYCLE_ALWAYS || mode is CYCLE_WHEN_NO_PARENT && !fModel.isNested()) | |
851 fIterator.setCycling(true); | |
852 else | |
853 fIterator.setCycling(false); | |
854 } | |
855 | |
856 void next() { | |
857 if (fIterator.hasNext(fFramePosition)) { | |
858 switchPosition(fIterator.next(fFramePosition), true, true); | |
859 return; | |
860 } | |
861 leave(ILinkedModeListener.UPDATE_CARET); | |
862 } | |
863 | |
864 void previous() { | |
865 if (fIterator.hasPrevious(fFramePosition)) { | |
866 switchPosition(fIterator.previous(fFramePosition), true, true); | |
867 } else | |
868 // dont't update caret, but rather select the current frame | |
869 leave(ILinkedModeListener.SELECT); | |
870 } | |
871 | |
872 private void triggerContextInfo() { | |
873 ITextOperationTarget target= fCurrentTarget.getViewer().getTextOperationTarget(); | |
874 if (target !is null) { | |
875 if (target.canDoOperation(ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION)) | |
876 target.doOperation(ISourceViewer.CONTENTASSIST_CONTEXT_INFORMATION); | |
877 } | |
878 } | |
879 | |
880 /** Trigger content assist on choice positions */ | |
881 private void triggerContentAssist() { | |
138 | 882 if ( cast(ProposalPosition)fFramePosition ) { |
134 | 883 ProposalPosition pp= cast(ProposalPosition) fFramePosition; |
129 | 884 ICompletionProposal[] choices= pp.getChoices(); |
885 if (choices !is null && choices.length > 0) { | |
886 fAssistant.setCompletions(choices); | |
887 fAssistant.showPossibleCompletions(); | |
888 return; | |
889 } | |
890 } | |
891 | |
892 fAssistant.setCompletions(new ICompletionProposal[0]); | |
893 fAssistant.hidePossibleCompletions(); | |
894 } | |
895 | |
162 | 896 private void switchPosition(LinkedPosition pos, bool select_, bool showProposals) { |
129 | 897 Assert.isNotNull(pos); |
898 if (pos.equals(fFramePosition)) | |
899 return; | |
900 | |
901 if (fFramePosition !is null && fCurrentTarget !is null) | |
902 fPositionListener.linkingFocusLost(fFramePosition, fCurrentTarget); | |
903 | |
904 // undo | |
905 endCompoundChange(); | |
906 | |
907 redraw(); // redraw current position being left - usually not needed | |
908 IDocument oldDoc= fFramePosition is null ? null : fFramePosition.getDocument(); | |
909 IDocument newDoc= pos.getDocument(); | |
910 | |
911 switchViewer(oldDoc, newDoc, pos); | |
912 fFramePosition= pos; | |
913 | |
162 | 914 if (select_) |
129 | 915 select(); |
916 if (fFramePosition is fExitPosition && !fIterator.isCycling()) | |
917 leave(ILinkedModeListener.NONE); | |
918 else { | |
919 redraw(); // redraw new position | |
920 ensureAnnotationModelInstalled(); | |
921 } | |
922 if (showProposals) | |
923 triggerContentAssist(); | |
924 if (fFramePosition !is fExitPosition && fDoContextInfo) | |
925 triggerContextInfo(); | |
926 | |
927 if (fFramePosition !is null && fCurrentTarget !is null) | |
928 fPositionListener.linkingFocusGained(fFramePosition, fCurrentTarget); | |
929 | |
930 } | |
931 | |
932 private void ensureAnnotationModelInstalled() { | |
933 LinkedPositionAnnotations lpa= fCurrentTarget.fAnnotationModel; | |
934 if (lpa !is null) { | |
935 ITextViewer viewer= fCurrentTarget.getViewer(); | |
138 | 936 if ( cast(ISourceViewer)viewer ) { |
134 | 937 ISourceViewer sv= cast(ISourceViewer) viewer; |
129 | 938 IAnnotationModel model= sv.getAnnotationModel(); |
138 | 939 if ( cast(IAnnotationModelExtension)model ) { |
134 | 940 IAnnotationModelExtension ext= cast(IAnnotationModelExtension) model; |
162 | 941 IAnnotationModel ourModel= ext.getAnnotationModel(stringcast(getUniqueKey())); |
129 | 942 if (ourModel is null) { |
162 | 943 ext.addAnnotationModel(stringcast(getUniqueKey()), lpa); |
129 | 944 } |
945 } | |
946 } | |
947 } | |
948 } | |
949 | |
950 private void uninstallAnnotationModel(LinkedModeUITarget target) { | |
951 ITextViewer viewer= target.getViewer(); | |
138 | 952 if ( cast(ISourceViewer)viewer ) { |
134 | 953 ISourceViewer sv= cast(ISourceViewer) viewer; |
129 | 954 IAnnotationModel model= sv.getAnnotationModel(); |
138 | 955 if ( cast(IAnnotationModelExtension)model ) { |
134 | 956 IAnnotationModelExtension ext= cast(IAnnotationModelExtension) model; |
162 | 957 ext.removeAnnotationModel(stringcast(getUniqueKey())); |
129 | 958 } |
959 } | |
960 } | |
961 | |
962 private void switchViewer(IDocument oldDoc, IDocument newDoc, LinkedPosition pos) { | |
963 if (oldDoc !is newDoc) { | |
964 | |
965 // redraw current document with new position before switching viewer | |
966 if (fCurrentTarget.fAnnotationModel !is null) | |
967 fCurrentTarget.fAnnotationModel.switchToPosition(fModel, pos); | |
968 | |
969 LinkedModeUITarget target= null; | |
970 for (int i= 0; i < fTargets.length; i++) { | |
971 if (fTargets[i].getViewer().getDocument() is newDoc) { | |
972 target= fTargets[i]; | |
973 break; | |
974 } | |
975 } | |
976 if (target !is fCurrentTarget) { | |
977 disconnect(); | |
978 fCurrentTarget= target; | |
979 target.linkingFocusLost(fFramePosition, target); | |
980 connect(); | |
981 ensureAnnotationModelInstalled(); | |
982 if (fCurrentTarget !is null) | |
983 fCurrentTarget.linkingFocusGained(pos, fCurrentTarget); | |
984 } | |
985 } | |
986 } | |
987 | |
988 private void select() { | |
989 ITextViewer viewer= fCurrentTarget.getViewer(); | |
138 | 990 if ( cast(ITextViewerExtension5)viewer ) { |
134 | 991 ITextViewerExtension5 extension5= cast(ITextViewerExtension5) viewer; |
129 | 992 extension5.exposeModelRange(new Region(fFramePosition.offset, fFramePosition.length)); |
993 } else if (!viewer.overlapsWithVisibleRegion(fFramePosition.offset, fFramePosition.length)) { | |
994 viewer.resetVisibleRegion(); | |
995 } | |
996 viewer.revealRange(fFramePosition.offset, fFramePosition.length); | |
997 viewer.setSelectedRange(fFramePosition.offset, fFramePosition.length); | |
998 } | |
999 | |
1000 private void redraw() { | |
1001 if (fCurrentTarget.fAnnotationModel !is null) | |
1002 fCurrentTarget.fAnnotationModel.switchToPosition(fModel, fFramePosition); | |
1003 } | |
1004 | |
1005 private void connect() { | |
1006 Assert.isNotNull(fCurrentTarget); | |
1007 ITextViewer viewer= fCurrentTarget.getViewer(); | |
162 | 1008 Assert.isNotNull(cast(Object)viewer); |
129 | 1009 fCurrentTarget.fWidget= viewer.getTextWidget(); |
1010 if (fCurrentTarget.fWidget is null) | |
1011 leave(ILinkedModeListener.EXIT_ALL); | |
1012 | |
1013 if (fCurrentTarget.fKeyListener is null) { | |
1014 fCurrentTarget.fKeyListener= new KeyListener(); | |
134 | 1015 (cast(ITextViewerExtension) viewer).prependVerifyKeyListener(fCurrentTarget.fKeyListener); |
129 | 1016 } else |
1017 fCurrentTarget.fKeyListener.setEnabled(true); | |
1018 | |
1019 registerAutoEditVetoer(viewer); | |
1020 | |
134 | 1021 (cast(IPostSelectionProvider) viewer).addPostSelectionChangedListener(fSelectionListener); |
129 | 1022 |
1023 createAnnotationModel(); | |
1024 | |
1025 showSelection(); | |
1026 | |
1027 fCurrentTarget.fShell= fCurrentTarget.fWidget.getShell(); | |
1028 if (fCurrentTarget.fShell is null) | |
1029 leave(ILinkedModeListener.EXIT_ALL); | |
1030 fCurrentTarget.fShell.addShellListener(fCloser); | |
1031 | |
1032 fAssistant.install(viewer); | |
1033 | |
1034 viewer.addTextInputListener(fCloser); | |
1035 | |
1036 viewer.getDocument().addDocumentListener(fDocumentListener); | |
1037 } | |
1038 | |
1039 /** | |
1040 * Reveals the selection on the current target's widget, if it is valid. | |
1041 */ | |
1042 private void showSelection() { | |
1043 final StyledText widget= fCurrentTarget.fWidget; | |
1044 if (widget is null || widget.isDisposed()) | |
1045 return; | |
150 | 1046 |
129 | 1047 // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=132263 |
135 | 1048 widget.getDisplay().asyncExec(new class() Runnable { |
129 | 1049 public void run() { |
1050 if (!widget.isDisposed()) | |
1051 try { | |
1052 widget.showSelection(); | |
1053 } catch (IllegalArgumentException e) { | |
1054 /* | |
1055 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=66914 | |
1056 * if the StyledText is in setRedraw(false) mode, its | |
1057 * selection may not be up2date and calling showSelection | |
1058 * will throw an IAE. | |
1059 * We don't have means to find out whether the selection is valid | |
1060 * or whether the widget is redrawing or not therefore we try | |
1061 * and ignore an IAE. | |
1062 */ | |
1063 } | |
1064 } | |
1065 }); | |
1066 } | |
1067 | |
1068 /** | |
1069 * Registers our auto edit vetoer with the viewer. | |
1070 * | |
1071 * @param viewer the viewer we want to veto ui-triggered changes within | |
1072 * linked positions | |
1073 */ | |
1074 private void registerAutoEditVetoer(ITextViewer viewer) { | |
1075 try { | |
1076 String[] contentTypes= getContentTypes(viewer.getDocument()); | |
138 | 1077 if ( cast(ITextViewerExtension2)viewer ) { |
134 | 1078 ITextViewerExtension2 vExtension= (cast(ITextViewerExtension2) viewer); |
129 | 1079 for (int i= 0; i < contentTypes.length; i++) { |
1080 vExtension.prependAutoEditStrategy(fAutoEditVetoer, contentTypes[i]); | |
1081 } | |
1082 } else { | |
1083 Assert.isTrue(false); | |
1084 } | |
1085 | |
1086 } catch (BadPartitioningException e) { | |
1087 leave(ILinkedModeListener.EXIT_ALL); | |
1088 } | |
1089 } | |
1090 | |
1091 private void unregisterAutoEditVetoer(ITextViewer viewer) { | |
1092 try { | |
1093 String[] contentTypes= getContentTypes(viewer.getDocument()); | |
138 | 1094 if ( cast(ITextViewerExtension2)viewer ) { |
134 | 1095 ITextViewerExtension2 vExtension= (cast(ITextViewerExtension2) viewer); |
129 | 1096 for (int i= 0; i < contentTypes.length; i++) { |
1097 vExtension.removeAutoEditStrategy(fAutoEditVetoer, contentTypes[i]); | |
1098 } | |
1099 } else { | |
1100 Assert.isTrue(false); | |
1101 } | |
1102 } catch (BadPartitioningException e) { | |
1103 leave(ILinkedModeListener.EXIT_ALL); | |
1104 } | |
1105 } | |
1106 | |
1107 /** | |
1108 * Returns all possible content types of <code>document</code>. | |
150 | 1109 * |
129 | 1110 * @param document the document |
1111 * @return all possible content types of <code>document</code> | |
1112 * @throws BadPartitioningException | |
1113 * @since 3.1 | |
1114 */ | |
136
6dcb0baaa031
Regex removal of throws decls, some instanceof
Frank Benoit <benoit@tionex.de>
parents:
135
diff
changeset
|
1115 private String[] getContentTypes(IDocument document) { |
138 | 1116 if ( cast(IDocumentExtension3)document ) { |
134 | 1117 IDocumentExtension3 ext= cast(IDocumentExtension3) document; |
129 | 1118 String[] partitionings= ext.getPartitionings(); |
1119 Set contentTypes= new HashSet(20); | |
1120 for (int i= 0; i < partitionings.length; i++) { | |
162 | 1121 contentTypes.addAll(Arrays.asList(stringcast(ext.getLegalContentTypes(partitionings[i])))); |
129 | 1122 } |
1123 contentTypes.add(IDocument.DEFAULT_CONTENT_TYPE); | |
162 | 1124 return stringcast( contentTypes.toArray()); |
129 | 1125 } |
1126 return document.getLegalContentTypes(); | |
1127 } | |
150 | 1128 |
129 | 1129 private void createAnnotationModel() { |
1130 if (fCurrentTarget.fAnnotationModel is null) { | |
1131 LinkedPositionAnnotations lpa= new LinkedPositionAnnotations(); | |
1132 if (fSimple) { | |
1133 lpa.markExitTarget(true); | |
1134 lpa.markFocus(false); | |
1135 lpa.markSlaves(false); | |
1136 lpa.markTargets(false); | |
1137 } | |
1138 lpa.setTargets(fIterator.getPositions()); | |
1139 lpa.setExitTarget(fExitPosition); | |
1140 lpa.connect(fCurrentTarget.getViewer().getDocument()); | |
1141 fCurrentTarget.fAnnotationModel= lpa; | |
1142 } | |
1143 } | |
1144 | |
1145 private String getUniqueKey() { | |
162 | 1146 return "linked.annotationmodelkey."~toString(); //$NON-NLS-1$ |
129 | 1147 } |
1148 | |
1149 private void disconnect() { | |
1150 Assert.isNotNull(fCurrentTarget); | |
1151 ITextViewer viewer= fCurrentTarget.getViewer(); | |
162 | 1152 Assert.isNotNull(cast(Object)viewer); |
129 | 1153 |
1154 viewer.getDocument().removeDocumentListener(fDocumentListener); | |
1155 | |
1156 fAssistant.uninstall(); | |
1157 fAssistant.removeProposalListener(fProposalListener); | |
1158 | |
1159 fCurrentTarget.fWidget= null; | |
1160 | |
1161 Shell shell= fCurrentTarget.fShell; | |
1162 fCurrentTarget.fShell= null; | |
1163 | |
1164 if (shell !is null && !shell.isDisposed()) | |
1165 shell.removeShellListener(fCloser); | |
1166 | |
1167 // this one is asymmetric: we don't install the model in | |
1168 // connect, but leave it to its callers to ensure they | |
1169 // have the model installed if they need it | |
1170 uninstallAnnotationModel(fCurrentTarget); | |
1171 | |
1172 unregisterAutoEditVetoer(viewer); | |
1173 | |
1174 // don't remove the verify key listener to let it keep its position | |
1175 // in the listener queue | |
1176 if (fCurrentTarget.fKeyListener !is null) | |
1177 fCurrentTarget.fKeyListener.setEnabled(false); | |
1178 | |
134 | 1179 (cast(IPostSelectionProvider) viewer).removePostSelectionChangedListener(fSelectionListener); |
129 | 1180 |
1181 redraw(); | |
1182 } | |
1183 | |
162 | 1184 void leave(int flags) { |
129 | 1185 if (!fIsActive) |
1186 return; | |
1187 fIsActive= false; | |
1188 | |
1189 endCompoundChange(); | |
1190 | |
1191 Display display= null; | |
1192 if (fCurrentTarget.fWidget !is null && !fCurrentTarget.fWidget.isDisposed()) | |
1193 display= fCurrentTarget.fWidget.getDisplay(); | |
1194 | |
1195 if (fCurrentTarget.fAnnotationModel !is null) | |
1196 fCurrentTarget.fAnnotationModel.removeAllAnnotations(); | |
1197 disconnect(); | |
1198 | |
1199 for (int i= 0; i < fTargets.length; i++) { | |
1200 LinkedModeUITarget target= fTargets[i]; | |
1201 ITextViewer viewer= target.getViewer(); | |
1202 if (target.fKeyListener !is null) { | |
134 | 1203 (cast(ITextViewerExtension) viewer).removeVerifyKeyListener(target.fKeyListener); |
129 | 1204 target.fKeyListener= null; |
1205 } | |
1206 | |
1207 viewer.removeTextInputListener(fCloser); | |
1208 } | |
1209 | |
1210 for (int i= 0; i < fTargets.length; i++) { | |
1211 | |
1212 if (fTargets[i].fAnnotationModel !is null) { | |
1213 fTargets[i].fAnnotationModel.removeAllAnnotations(); | |
1214 fTargets[i].fAnnotationModel.disconnect(fTargets[i].getViewer().getDocument()); | |
1215 fTargets[i].fAnnotationModel= null; | |
1216 } | |
1217 | |
1218 uninstallAnnotationModel(fTargets[i]); | |
1219 } | |
1220 | |
1221 | |
1222 if ((flags & ILinkedModeListener.UPDATE_CARET) !is 0 && fExitPosition !is null && fFramePosition !is fExitPosition && !fExitPosition.isDeleted()) | |
1223 switchPosition(fExitPosition, true, false); | |
1224 | |
1225 final List docs= new ArrayList(); | |
1226 for (int i= 0; i < fTargets.length; i++) { | |
1227 IDocument doc= fTargets[i].getViewer().getDocument(); | |
1228 if (doc !is null) | |
162 | 1229 docs.add(cast(Object)doc); |
129 | 1230 } |
1231 | |
1232 fModel.stopForwarding(flags); | |
1233 | |
162 | 1234 Runnable runnable= dgRunnable( (int flags_){ |
1235 if (fExitPosition !is null) | |
1236 fExitPosition.getDocument().removePosition(fExitPosition); | |
129 | 1237 |
162 | 1238 for (Iterator iter = docs.iterator(); iter.hasNext(); ) { |
1239 IDocument doc= cast(IDocument) iter.next(); | |
1240 doc.removePositionUpdater(fPositionUpdater); | |
1241 bool uninstallCat= false; | |
1242 String[] cats= doc.getPositionCategories(); | |
1243 for (int j= 0; j < cats.length; j++) { | |
1244 if (getCategory().equals(cats[j])) { | |
1245 uninstallCat= true; | |
1246 break; | |
129 | 1247 } |
1248 } | |
162 | 1249 if (uninstallCat) |
1250 try { | |
1251 doc.removePositionCategory(getCategory()); | |
1252 } catch (BadPositionCategoryException e) { | |
1253 // ignore | |
1254 } | |
129 | 1255 } |
162 | 1256 fModel.exit(flags_); |
1257 }, flags ); | |
129 | 1258 |
1259 // remove positions (both exit positions AND linked positions in the | |
1260 // model) asynchronously to make sure that the annotation painter | |
1261 // gets correct document offsets. | |
1262 if (display !is null) | |
1263 display.asyncExec(runnable); | |
1264 else | |
1265 runnable.run(); | |
1266 } | |
1267 | |
1268 private void endCompoundChange() { | |
1269 if (fHasOpenCompoundChange) { | |
134 | 1270 ITextViewerExtension extension= cast(ITextViewerExtension) fCurrentTarget.getViewer(); |
129 | 1271 IRewriteTarget target= extension.getRewriteTarget(); |
1272 target.endCompoundChange(); | |
1273 fHasOpenCompoundChange= false; | |
1274 } | |
1275 } | |
1276 | |
1277 private void beginCompoundChange() { | |
1278 if (!fHasOpenCompoundChange) { | |
134 | 1279 ITextViewerExtension extension= cast(ITextViewerExtension) fCurrentTarget.getViewer(); |
129 | 1280 IRewriteTarget target= extension.getRewriteTarget(); |
1281 target.beginCompoundChange(); | |
1282 fHasOpenCompoundChange= true; | |
1283 } | |
1284 } | |
1285 | |
1286 /** | |
1287 * Returns the currently selected region or <code>null</code>. | |
1288 * | |
1289 * @return the currently selected region or <code>null</code> | |
1290 */ | |
1291 public IRegion getSelectedRegion() { | |
1292 if (fFramePosition !is null) | |
1293 return new Region(fFramePosition.getOffset(), fFramePosition.getLength()); | |
1294 if (fExitPosition !is null) | |
1295 return new Region(fExitPosition.getOffset(), fExitPosition.getLength()); | |
1296 return null; | |
1297 } | |
1298 | |
1299 private String getCategory() { | |
1300 return toString(); | |
1301 } | |
1302 | |
1303 /** | |
1304 * Sets the context info property. If set to <code>true</code>, context | |
1305 * info will be invoked on the current target's viewer whenever a position | |
1306 * is switched. | |
1307 * | |
1308 * @param doContextInfo <code>true</code> if context information should be | |
1309 * displayed | |
1310 */ | |
1311 public void setDoContextInfo(bool doContextInfo) { | |
1312 fDoContextInfo= doContextInfo; | |
1313 } | |
1314 | |
1315 /** | |
1316 * Sets the focus callback which will get informed when the focus of the | |
1317 * linked mode UI changes. | |
1318 * <p> | |
1319 * If there is a listener installed already, it will be replaced. | |
1320 * </p> | |
1321 * | |
1322 * @param listener the new listener, never <code>null</code>. | |
1323 */ | |
1324 protected void setPositionListener(ILinkedModeUIFocusListener listener) { | |
162 | 1325 Assert.isNotNull(cast(Object)listener); |
129 | 1326 fPositionListener= listener; |
1327 } | |
1328 | |
1329 /** | |
1330 * Sets the "simple" mode of the receiver. A linked mode UI in simple mode | |
1331 * merely draws the exit position, but not the target, focus, and slave | |
1332 * positions. Default is <code>false</code>. This method must be called | |
1333 * before it is entered. | |
1334 * | |
1335 * @param simple <code>true</code> if the UI should be in simple mode. | |
1336 */ | |
1337 public void setSimpleMode(bool simple) { | |
1338 fSimple= simple; | |
1339 } | |
1340 | |
1341 /** | |
1342 * Enables the support for colored labels in the proposal popup. | |
1343 * <p>Completion proposals can implement {@link ICompletionProposalExtension6} | |
1344 * to provide colored proposal labels.</p> | |
150 | 1345 * |
129 | 1346 * @param isEnabled if <code>true</code> the support for colored labels is enabled in the proposal popup |
1347 * @since 3.4 | |
1348 */ | |
1349 public void enableColoredLabels(bool isEnabled) { | |
1350 fIsColoredLabelsSupportEnabled= isEnabled; | |
1351 } | |
1352 | |
1353 } |