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.TextViewer;
|
|
14
|
|
15 import dwt.dwthelper.utils;
|
|
16
|
|
17 import java.util.ArrayList;
|
|
18 import java.util.Arrays;
|
|
19 import java.util.HashMap;
|
|
20 import java.util.HashSet;
|
|
21 import java.util.Iterator;
|
|
22 import java.util.List;
|
|
23 import java.util.Map;
|
|
24 import java.util.Set;
|
|
25 import java.util.regex.PatternSyntaxException;
|
|
26
|
|
27 import dwt.DWT;
|
|
28 import dwt.custom.LineBackgroundEvent;
|
|
29 import dwt.custom.LineBackgroundListener;
|
|
30 import dwt.custom.MovementEvent;
|
|
31 import dwt.custom.MovementListener;
|
|
32 import dwt.custom.ST;
|
|
33 import dwt.custom.StyleRange;
|
|
34 import dwt.custom.StyledText;
|
|
35 import dwt.custom.StyledTextPrintOptions;
|
|
36 import dwt.custom.VerifyKeyListener;
|
|
37 import dwt.events.ControlEvent;
|
|
38 import dwt.events.ControlListener;
|
|
39 import dwt.events.DisposeEvent;
|
|
40 import dwt.events.DisposeListener;
|
|
41 import dwt.events.KeyEvent;
|
|
42 import dwt.events.KeyListener;
|
|
43 import dwt.events.MouseAdapter;
|
|
44 import dwt.events.MouseEvent;
|
|
45 import dwt.events.MouseListener;
|
|
46 import dwt.events.SelectionEvent;
|
|
47 import dwt.events.SelectionListener;
|
|
48 import dwt.events.TraverseEvent;
|
|
49 import dwt.events.TraverseListener;
|
|
50 import dwt.events.VerifyEvent;
|
|
51 import dwt.events.VerifyListener;
|
|
52 import dwt.graphics.Color;
|
|
53 import dwt.graphics.GC;
|
|
54 import dwt.graphics.Point;
|
|
55 import dwt.graphics.Rectangle;
|
|
56 import dwt.printing.PrintDialog;
|
|
57 import dwt.printing.Printer;
|
|
58 import dwt.printing.PrinterData;
|
|
59 import dwt.widgets.Composite;
|
|
60 import dwt.widgets.Control;
|
|
61 import dwt.widgets.Display;
|
|
62 import dwt.widgets.Event;
|
|
63 import dwt.widgets.Listener;
|
|
64 import dwt.widgets.ScrollBar;
|
|
65 import dwtx.core.runtime.Assert;
|
|
66 import dwtx.jface.internal.text.NonDeletingPositionUpdater;
|
|
67 import dwtx.jface.internal.text.StickyHoverManager;
|
|
68 import dwtx.jface.text.hyperlink.HyperlinkManager;
|
|
69 import dwtx.jface.text.hyperlink.IHyperlinkDetector;
|
|
70 import dwtx.jface.text.hyperlink.IHyperlinkDetectorExtension;
|
|
71 import dwtx.jface.text.hyperlink.IHyperlinkPresenter;
|
|
72 import dwtx.jface.text.hyperlink.HyperlinkManager.DETECTION_STRATEGY;
|
|
73 import dwtx.jface.text.projection.ChildDocument;
|
|
74 import dwtx.jface.text.projection.ChildDocumentManager;
|
|
75 import dwtx.jface.viewers.IPostSelectionProvider;
|
|
76 import dwtx.jface.viewers.ISelection;
|
|
77 import dwtx.jface.viewers.ISelectionChangedListener;
|
|
78 import dwtx.jface.viewers.ISelectionProvider;
|
|
79 import dwtx.jface.viewers.SelectionChangedEvent;
|
|
80 import dwtx.jface.viewers.Viewer;
|
|
81
|
|
82
|
|
83 /**
|
|
84 * DWT based implementation of {@link ITextViewer} and its extension interfaces.
|
|
85 * Once the viewer and its DWT control have been created the viewer can only
|
|
86 * indirectly be disposed by disposing its DWT control.
|
|
87 * <p>
|
|
88 * Clients are supposed to instantiate a text viewer and subsequently to
|
|
89 * communicate with it exclusively using the
|
|
90 * {@link dwtx.jface.text.ITextViewer} interface or any of the
|
|
91 * implemented extension interfaces.
|
|
92 * <p>
|
|
93 * A text viewer serves as text operation target. It only partially supports the
|
|
94 * external control of the enable state of its text operations. A text viewer is
|
|
95 * also a widget token owner. Anything that wants to display an overlay window
|
|
96 * on top of a text viewer should implement the
|
|
97 * {@link dwtx.jface.text.IWidgetTokenKeeper} interface and participate
|
|
98 * in the widget token negotiation between the text viewer and all its potential
|
|
99 * widget token keepers.
|
|
100 * <p>
|
|
101 * This class is not intended to be subclassed outside the JFace Text component.</p>
|
|
102 * @noextend This class is not intended to be subclassed by clients.
|
|
103 */
|
|
104 public class TextViewer : Viewer ,
|
|
105 ITextViewer, ITextViewerExtension, ITextViewerExtension2, ITextViewerExtension4, ITextViewerExtension6, ITextViewerExtension7, ITextViewerExtension8,
|
|
106 IEditingSupportRegistry, ITextOperationTarget, ITextOperationTargetExtension,
|
|
107 IWidgetTokenOwner, IWidgetTokenOwnerExtension, IPostSelectionProvider {
|
|
108
|
|
109 /** Internal flag to indicate the debug state. */
|
|
110 public static final bool TRACE_ERRORS= false;
|
|
111 /** Internal flag to indicate the debug state. */
|
|
112 private static final bool TRACE_DOUBLE_CLICK= false;
|
|
113
|
|
114 /**
|
|
115 * Width constraint for text hovers (in characters).
|
|
116 * @since 3.4
|
|
117 */
|
|
118 private static final int TEXT_HOVER_WIDTH_CHARS= 100; //used to be 60 (text font)
|
|
119 /**
|
|
120 * Height constraint for text hovers (in characters).
|
|
121 * @since 3.4
|
|
122 */
|
|
123 private static final int TEXT_HOVER_HEIGHT_CHARS= 12; //used to be 10 (text font)
|
|
124
|
|
125 /**
|
|
126 * Represents a replace command that brings the text viewer's text widget
|
|
127 * back in synchronization with text viewer's document after the document
|
|
128 * has been changed.
|
|
129 */
|
|
130 protected class WidgetCommand {
|
|
131
|
|
132 /** The document event encapsulated by this command. */
|
|
133 public DocumentEvent event;
|
|
134 /** The start of the event. */
|
|
135 public int start;
|
|
136 /** The length of the event. */
|
|
137 public int length;
|
|
138 /** The inserted and replaced text segments of <code>event</code>. */
|
|
139 public String text;
|
|
140 /** The replaced text segments of <code>event</code>. */
|
|
141 public String preservedText;
|
|
142
|
|
143 /**
|
|
144 * Translates a document event into the presentation coordinates of this text viewer.
|
|
145 *
|
|
146 * @param e the event to be translated
|
|
147 */
|
|
148 public void setEvent(DocumentEvent e) {
|
|
149
|
|
150 event= e;
|
|
151
|
|
152 start= e.getOffset();
|
|
153 length= e.getLength();
|
|
154 text= e.getText();
|
|
155
|
|
156 if (length !is 0) {
|
|
157 try {
|
|
158
|
|
159 if (e instanceof SlaveDocumentEvent) {
|
|
160 SlaveDocumentEvent slave= (SlaveDocumentEvent) e;
|
|
161 DocumentEvent master= slave.getMasterEvent();
|
|
162 if (master !is null)
|
|
163 preservedText= master.getDocument().get(master.getOffset(), master.getLength());
|
|
164 } else {
|
|
165 preservedText= e.getDocument().get(e.getOffset(), e.getLength());
|
|
166 }
|
|
167
|
|
168 } catch (BadLocationException x) {
|
|
169 preservedText= null;
|
|
170 if (TRACE_ERRORS)
|
|
171 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.WidgetCommand.setEvent")); //$NON-NLS-1$
|
|
172 }
|
|
173 } else
|
|
174 preservedText= null;
|
|
175 }
|
|
176 }
|
|
177
|
|
178
|
|
179 /**
|
|
180 * Connects a text double click strategy to this viewer's text widget.
|
|
181 * Calls the double click strategies when the mouse has
|
|
182 * been clicked inside the text editor.
|
|
183 */
|
|
184 class TextDoubleClickStrategyConnector : MouseAdapter , MovementListener {
|
|
185
|
|
186 /** Internal flag to remember the last double-click selection. */
|
|
187 private Point fDoubleClickSelection;
|
|
188
|
|
189 /*
|
|
190 * @see dwt.events.MouseAdapter#mouseUp(dwt.events.MouseEvent)
|
|
191 * @since 3.2
|
|
192 */
|
|
193 public void mouseUp(MouseEvent e) {
|
|
194 fDoubleClickSelection= null;
|
|
195 }
|
|
196
|
|
197 /*
|
|
198 * @see dwt.custom.MovementListener#getNextOffset(dwt.custom.MovementEvent)
|
|
199 * @since 3.3
|
|
200 */
|
|
201 public void getNextOffset(MovementEvent event) {
|
|
202 if (event.movement !is DWT.MOVEMENT_WORD_END)
|
|
203 return;
|
|
204
|
|
205 if (TRACE_DOUBLE_CLICK) {
|
|
206 System.out.println("\n+++"); //$NON-NLS-1$
|
|
207 print(event);
|
|
208 }
|
|
209
|
|
210 if (fDoubleClickSelection !is null) {
|
|
211 if (fDoubleClickSelection.x <= event.offset && event.offset <= fDoubleClickSelection.y)
|
|
212 event.newOffset= fDoubleClickSelection.y;
|
|
213 }
|
|
214 }
|
|
215
|
|
216 /*
|
|
217 * @see dwt.custom.MovementListener#getPreviousOffset(dwt.custom.MovementEvent)
|
|
218 * @since 3.3
|
|
219 */
|
|
220 public void getPreviousOffset(MovementEvent event) {
|
|
221 if (event.movement !is DWT.MOVEMENT_WORD_START)
|
|
222 return;
|
|
223
|
|
224 if (TRACE_DOUBLE_CLICK) {
|
|
225 System.out.println("\n---"); //$NON-NLS-1$
|
|
226 print(event);
|
|
227 }
|
|
228 if (fDoubleClickSelection is null) {
|
|
229 ITextDoubleClickStrategy s= (ITextDoubleClickStrategy) selectContentTypePlugin(getSelectedRange().x, fDoubleClickStrategies);
|
|
230 if (s !is null) {
|
|
231 StyledText textWidget= getTextWidget();
|
|
232 s.doubleClicked(TextViewer.this);
|
|
233 fDoubleClickSelection= textWidget.getSelection();
|
|
234 event.newOffset= fDoubleClickSelection.x;
|
|
235 if (TRACE_DOUBLE_CLICK)
|
|
236 System.out.println("- setting selection: x= " + fDoubleClickSelection.x + ", y= " + fDoubleClickSelection.y); //$NON-NLS-1$ //$NON-NLS-2$
|
|
237 }
|
|
238 } else {
|
|
239 if (fDoubleClickSelection.x <= event.offset && event.offset <= fDoubleClickSelection.y)
|
|
240 event.newOffset= fDoubleClickSelection.x;
|
|
241 }
|
|
242 }
|
|
243 }
|
|
244
|
|
245 /**
|
|
246 * Print trace info about <code>MovementEvent</code>.
|
|
247 *
|
|
248 * @param e the event to print
|
|
249 * @since 3.3
|
|
250 */
|
|
251 private void print(MovementEvent e) {
|
|
252 System.out.println("line offset: " + e.lineOffset); //$NON-NLS-1$
|
|
253 System.out.println("line: " + e.lineText); //$NON-NLS-1$
|
|
254 System.out.println("type: " + e.movement); //$NON-NLS-1$
|
|
255 System.out.println("offset: " + e.offset); //$NON-NLS-1$
|
|
256 System.out.println("newOffset: " + e.newOffset); //$NON-NLS-1$
|
|
257 }
|
|
258
|
|
259 /**
|
|
260 * Monitors the area of the viewer's document that is visible in the viewer.
|
|
261 * If the area might have changed, it informs the text viewer about this
|
|
262 * potential change and its origin. The origin is internally used for optimization
|
|
263 * purposes.
|
|
264 */
|
|
265 class ViewportGuard : MouseAdapter
|
|
266 , ControlListener, KeyListener, SelectionListener {
|
|
267
|
|
268 /*
|
|
269 * @see ControlListener#controlResized(ControlEvent)
|
|
270 */
|
|
271 public void controlResized(ControlEvent e) {
|
|
272 updateViewportListeners(RESIZE);
|
|
273 }
|
|
274
|
|
275 /*
|
|
276 * @see ControlListener#controlMoved(ControlEvent)
|
|
277 */
|
|
278 public void controlMoved(ControlEvent e) {
|
|
279 }
|
|
280
|
|
281 /*
|
|
282 * @see KeyListener#keyReleased
|
|
283 */
|
|
284 public void keyReleased(KeyEvent e) {
|
|
285 updateViewportListeners(KEY);
|
|
286 }
|
|
287
|
|
288 /*
|
|
289 * @see KeyListener#keyPressed
|
|
290 */
|
|
291 public void keyPressed(KeyEvent e) {
|
|
292 updateViewportListeners(KEY);
|
|
293 }
|
|
294
|
|
295 /*
|
|
296 * @see MouseListener#mouseUp
|
|
297 */
|
|
298 public void mouseUp(MouseEvent e) {
|
|
299 if (fTextWidget !is null)
|
|
300 fTextWidget.removeSelectionListener(this);
|
|
301 updateViewportListeners(MOUSE_END);
|
|
302 }
|
|
303
|
|
304 /*
|
|
305 * @see MouseListener#mouseDown
|
|
306 */
|
|
307 public void mouseDown(MouseEvent e) {
|
|
308 if (fTextWidget !is null)
|
|
309 fTextWidget.addSelectionListener(this);
|
|
310 }
|
|
311
|
|
312 /*
|
|
313 * @see SelectionListener#widgetSelected
|
|
314 */
|
|
315 public void widgetSelected(SelectionEvent e) {
|
|
316 if (e.widget is fScroller)
|
|
317 updateViewportListeners(SCROLLER);
|
|
318 else
|
|
319 updateViewportListeners(MOUSE);
|
|
320 }
|
|
321
|
|
322 /*
|
|
323 * @see SelectionListener#widgetDefaultSelected
|
|
324 */
|
|
325 public void widgetDefaultSelected(SelectionEvent e) {}
|
|
326 }
|
|
327
|
|
328 /**
|
|
329 * This position updater is used to keep the selection during text shift operations.
|
|
330 */
|
|
331 static class ShiftPositionUpdater : DefaultPositionUpdater {
|
|
332
|
|
333 /**
|
|
334 * Creates the position updater for the given category.
|
|
335 *
|
|
336 * @param category the category this updater takes care of
|
|
337 */
|
|
338 protected ShiftPositionUpdater(String category) {
|
|
339 super(category);
|
|
340 }
|
|
341
|
|
342 /**
|
|
343 * If an insertion happens at the selection's start offset,
|
|
344 * the position is extended rather than shifted.
|
|
345 */
|
|
346 protected void adaptToInsert() {
|
|
347
|
|
348 int myStart= fPosition.offset;
|
|
349 int myEnd= fPosition.offset + fPosition.length -1;
|
|
350 myEnd= Math.max(myStart, myEnd);
|
|
351
|
|
352 int yoursStart= fOffset;
|
|
353 int yoursEnd= fOffset + fReplaceLength -1;
|
|
354 yoursEnd= Math.max(yoursStart, yoursEnd);
|
|
355
|
|
356 if (myEnd < yoursStart)
|
|
357 return;
|
|
358
|
|
359 if (myStart <= yoursStart) {
|
|
360 fPosition.length += fReplaceLength;
|
|
361 return;
|
|
362 }
|
|
363
|
|
364 if (myStart > yoursStart)
|
|
365 fPosition.offset += fReplaceLength;
|
|
366 }
|
|
367 }
|
|
368
|
|
369 /**
|
|
370 * Internal document listener on the visible document.
|
|
371 */
|
|
372 class VisibleDocumentListener : IDocumentListener {
|
|
373
|
|
374 /*
|
|
375 * @see IDocumentListener#documentAboutToBeChanged
|
|
376 */
|
|
377 public void documentAboutToBeChanged(DocumentEvent e) {
|
|
378 if (e.getDocument() is getVisibleDocument())
|
|
379 fWidgetCommand.setEvent(e);
|
|
380 handleVisibleDocumentAboutToBeChanged(e);
|
|
381 }
|
|
382
|
|
383 /*
|
|
384 * @see IDocumentListener#documentChanged
|
|
385 */
|
|
386 public void documentChanged(DocumentEvent e) {
|
|
387 if (fWidgetCommand.event is e)
|
|
388 updateTextListeners(fWidgetCommand);
|
|
389 fLastSentSelectionChange= null;
|
|
390 handleVisibleDocumentChanged(e);
|
|
391 }
|
|
392 }
|
|
393
|
|
394 /**
|
|
395 * Internal verify listener.
|
|
396 */
|
|
397 class TextVerifyListener : VerifyListener {
|
|
398
|
|
399 /**
|
|
400 * Indicates whether verify events are forwarded or ignored.
|
|
401 * @since 2.0
|
|
402 */
|
|
403 private bool fForward= true;
|
|
404
|
|
405 /**
|
|
406 * Tells the listener to forward received events.
|
|
407 *
|
|
408 * @param forward <code>true</code> if forwarding should be enabled.
|
|
409 * @since 2.0
|
|
410 */
|
|
411 public void forward(bool forward) {
|
|
412 fForward= forward;
|
|
413 }
|
|
414
|
|
415 /*
|
|
416 * @see VerifyListener#verifyText(VerifyEvent)
|
|
417 */
|
|
418 public void verifyText(VerifyEvent e) {
|
|
419 if (fForward)
|
|
420 handleVerifyEvent(e);
|
|
421 }
|
|
422 }
|
|
423
|
|
424 /**
|
|
425 * The viewer's manager responsible for registered verify key listeners.
|
|
426 * Uses batches rather than robust iterators because of performance issues.
|
|
427 * <p>
|
|
428 * The implementation is reentrant, i.e. installed listeners may trigger
|
|
429 * further <code>VerifyKeyEvent</code>s that may cause other listeners to be
|
|
430 * installed, but not thread safe.
|
|
431 * </p>
|
|
432 * @since 2.0
|
|
433 */
|
|
434 class VerifyKeyListenersManager : VerifyKeyListener {
|
|
435
|
|
436 /**
|
|
437 * Represents a batched addListener/removeListener command.
|
|
438 */
|
|
439 class Batch {
|
|
440 /** The index at which to insert the listener. */
|
|
441 int index;
|
|
442 /** The listener to be inserted. */
|
|
443 VerifyKeyListener listener;
|
|
444
|
|
445 /**
|
|
446 * Creates a new batch containing the given listener for the given index.
|
|
447 *
|
|
448 * @param l the listener to be added
|
|
449 * @param i the index at which to insert the listener
|
|
450 */
|
|
451 public Batch(VerifyKeyListener l, int i) {
|
|
452 listener= l;
|
|
453 index= i;
|
|
454 }
|
|
455 }
|
|
456
|
|
457 /** List of registered verify key listeners. */
|
|
458 private List fListeners= new ArrayList();
|
|
459 /** List of pending batches. */
|
|
460 private List fBatched= new ArrayList();
|
|
461 /** The reentrance count. */
|
|
462 private int fReentranceCount= 0;
|
|
463
|
|
464 /*
|
|
465 * @see VerifyKeyListener#verifyKey(VerifyEvent)
|
|
466 */
|
|
467 public void verifyKey(VerifyEvent event) {
|
|
468 if (fListeners.isEmpty())
|
|
469 return;
|
|
470
|
|
471 try {
|
|
472 fReentranceCount++;
|
|
473 Iterator iterator= fListeners.iterator();
|
|
474 while (iterator.hasNext() && event.doit) {
|
|
475 VerifyKeyListener listener= (VerifyKeyListener) iterator.next();
|
|
476 listener.verifyKey(event); // we might trigger reentrant calls on GTK
|
|
477 }
|
|
478 } finally {
|
|
479 fReentranceCount--;
|
|
480 }
|
|
481 if (fReentranceCount is 0)
|
|
482 processBatchedRequests();
|
|
483 }
|
|
484
|
|
485 /**
|
|
486 * Processes the pending batched requests.
|
|
487 */
|
|
488 private void processBatchedRequests() {
|
|
489 if (!fBatched.isEmpty()) {
|
|
490 Iterator e= fBatched.iterator();
|
|
491 while (e.hasNext()) {
|
|
492 Batch batch= (Batch) e.next();
|
|
493 insertListener(batch.listener, batch.index);
|
|
494 }
|
|
495 fBatched.clear();
|
|
496 }
|
|
497 }
|
|
498
|
|
499 /**
|
|
500 * Returns the number of registered verify key listeners.
|
|
501 *
|
|
502 * @return the number of registered verify key listeners
|
|
503 */
|
|
504 public int numberOfListeners() {
|
|
505 return fListeners.size();
|
|
506 }
|
|
507
|
|
508 /**
|
|
509 * Inserts the given listener at the given index or moves it
|
|
510 * to that index.
|
|
511 *
|
|
512 * @param listener the listener to be inserted
|
|
513 * @param index the index of the listener or -1 for remove
|
|
514 */
|
|
515 public void insertListener(VerifyKeyListener listener, int index) {
|
|
516
|
|
517 if (index is -1) {
|
|
518 removeListener(listener);
|
|
519 } else if (listener !is null) {
|
|
520
|
|
521 if (fReentranceCount > 0) {
|
|
522
|
|
523 fBatched.add(new Batch(listener, index));
|
|
524
|
|
525 } else {
|
|
526
|
|
527 int idx= -1;
|
|
528
|
|
529 // find index based on identity
|
|
530 int size= fListeners.size();
|
|
531 for (int i= 0; i < size; i++) {
|
|
532 if (listener is fListeners.get(i)) {
|
|
533 idx= i;
|
|
534 break;
|
|
535 }
|
|
536 }
|
|
537
|
|
538 // move or add it
|
|
539 if (idx !is index) {
|
|
540
|
|
541 if (idx !is -1)
|
|
542 fListeners.remove(idx);
|
|
543
|
|
544 if (index > fListeners.size())
|
|
545 fListeners.add(listener);
|
|
546 else
|
|
547 fListeners.add(index, listener);
|
|
548 }
|
|
549
|
|
550 if (size is 0) // checking old size, i.e. current size is size + 1
|
|
551 install();
|
|
552 }
|
|
553 }
|
|
554 }
|
|
555
|
|
556 /**
|
|
557 * Removes the given listener.
|
|
558 *
|
|
559 * @param listener the listener to be removed
|
|
560 */
|
|
561 public void removeListener(VerifyKeyListener listener) {
|
|
562 if (listener is null)
|
|
563 return;
|
|
564
|
|
565 if (fReentranceCount > 0) {
|
|
566
|
|
567 fBatched.add(new Batch(listener, -1));
|
|
568
|
|
569 } else {
|
|
570
|
|
571 int size= fListeners.size();
|
|
572 for (int i= 0; i < size; i++) {
|
|
573 if (listener is fListeners.get(i)) {
|
|
574 fListeners.remove(i);
|
|
575 if (size is 1) // checking old size, i.e. current size is size - 1
|
|
576 uninstall();
|
|
577 return;
|
|
578 }
|
|
579 }
|
|
580 }
|
|
581 }
|
|
582
|
|
583 /**
|
|
584 * Installs this manager.
|
|
585 */
|
|
586 private void install() {
|
|
587 StyledText textWidget= getTextWidget();
|
|
588 if (textWidget !is null && !textWidget.isDisposed())
|
|
589 textWidget.addVerifyKeyListener(this);
|
|
590 }
|
|
591
|
|
592 /**
|
|
593 * Uninstalls this manager.
|
|
594 */
|
|
595 private void uninstall() {
|
|
596 StyledText textWidget= getTextWidget();
|
|
597 if (textWidget !is null && !textWidget.isDisposed())
|
|
598 textWidget.removeVerifyKeyListener(this);
|
|
599 }
|
|
600 }
|
|
601
|
|
602
|
|
603 /**
|
|
604 * Reification of a range in which a find replace operation is performed. This range is visually
|
|
605 * highlighted in the viewer as long as the replace operation is in progress.
|
|
606 *
|
|
607 * @since 2.0
|
|
608 */
|
|
609 class FindReplaceRange : LineBackgroundListener, ITextListener, IPositionUpdater {
|
|
610
|
|
611 /** Internal name for the position category used to update the range. */
|
|
612 private final static String RANGE_CATEGORY= "dwtx.jface.text.TextViewer.find.range"; //$NON-NLS-1$
|
|
613
|
|
614 /** The highlight color of this range. */
|
|
615 private Color fHighlightColor;
|
|
616 /** The position used to lively update this range's extent. */
|
|
617 private Position fPosition;
|
|
618
|
|
619 /** Creates a new find/replace range with the given extent.
|
|
620 *
|
|
621 * @param range the extent of this range
|
|
622 */
|
|
623 public FindReplaceRange(IRegion range) {
|
|
624 setRange(range);
|
|
625 }
|
|
626
|
|
627 /**
|
|
628 * Sets the extent of this range.
|
|
629 *
|
|
630 * @param range the extent of this range
|
|
631 */
|
|
632 public void setRange(IRegion range) {
|
|
633 fPosition= new Position(range.getOffset(), range.getLength());
|
|
634 }
|
|
635
|
|
636 /**
|
|
637 * Returns the extent of this range.
|
|
638 *
|
|
639 * @return the extent of this range
|
|
640 */
|
|
641 public IRegion getRange() {
|
|
642 return new Region(fPosition.getOffset(), fPosition.getLength());
|
|
643 }
|
|
644
|
|
645 /**
|
|
646 * Sets the highlight color of this range. Causes the range to be redrawn.
|
|
647 *
|
|
648 * @param color the highlight color
|
|
649 */
|
|
650 public void setHighlightColor(Color color) {
|
|
651 fHighlightColor= color;
|
|
652 paint();
|
|
653 }
|
|
654
|
|
655 /*
|
|
656 * @see LineBackgroundListener#lineGetBackground(LineBackgroundEvent)
|
|
657 * @since 2.0
|
|
658 */
|
|
659 public void lineGetBackground(LineBackgroundEvent event) {
|
|
660 /* Don't use cached line information because of patched redrawing events. */
|
|
661
|
|
662 if (fTextWidget !is null) {
|
|
663 int offset= widgetOffset2ModelOffset(event.lineOffset);
|
|
664 if (fPosition.includes(offset))
|
|
665 event.lineBackground= fHighlightColor;
|
|
666 }
|
|
667 }
|
|
668
|
|
669 /**
|
|
670 * Installs this range. The range registers itself as background
|
|
671 * line painter and text listener. Also, it creates a category with the
|
|
672 * viewer's document to maintain its own extent.
|
|
673 */
|
|
674 public void install() {
|
|
675 TextViewer.this.addTextListener(this);
|
|
676 fTextWidget.addLineBackgroundListener(this);
|
|
677
|
|
678 IDocument document= TextViewer.this.getDocument();
|
|
679 try {
|
|
680 document.addPositionCategory(RANGE_CATEGORY);
|
|
681 document.addPosition(RANGE_CATEGORY, fPosition);
|
|
682 document.addPositionUpdater(this);
|
|
683 } catch (BadPositionCategoryException e) {
|
|
684 // should not happen
|
|
685 } catch (BadLocationException e) {
|
|
686 // should not happen
|
|
687 }
|
|
688
|
|
689 paint();
|
|
690 }
|
|
691
|
|
692 /**
|
|
693 * Uninstalls this range.
|
|
694 * @see #install()
|
|
695 */
|
|
696 public void uninstall() {
|
|
697
|
|
698 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19612
|
|
699
|
|
700 IDocument document= TextViewer.this.getDocument();
|
|
701 if (document !is null) {
|
|
702 document.removePositionUpdater(this);
|
|
703 document.removePosition(fPosition);
|
|
704 }
|
|
705
|
|
706 if (fTextWidget !is null && !fTextWidget.isDisposed())
|
|
707 fTextWidget.removeLineBackgroundListener(this);
|
|
708
|
|
709 TextViewer.this.removeTextListener(this);
|
|
710
|
|
711 clear();
|
|
712 }
|
|
713
|
|
714 /**
|
|
715 * Clears the highlighting of this range.
|
|
716 */
|
|
717 private void clear() {
|
|
718 if (fTextWidget !is null && !fTextWidget.isDisposed())
|
|
719 fTextWidget.redraw();
|
|
720 }
|
|
721
|
|
722 /**
|
|
723 * Paints the highlighting of this range.
|
|
724 */
|
|
725 private void paint() {
|
|
726
|
|
727 IRegion widgetRegion= modelRange2WidgetRange(fPosition);
|
|
728 int offset= widgetRegion.getOffset();
|
|
729 int length= widgetRegion.getLength();
|
|
730
|
|
731 int count= fTextWidget.getCharCount();
|
|
732 if (offset + length >= count) {
|
|
733 length= count - offset; // clip
|
|
734
|
|
735 Point upperLeft= fTextWidget.getLocationAtOffset(offset);
|
|
736 Point lowerRight= fTextWidget.getLocationAtOffset(offset + length);
|
|
737 int width= fTextWidget.getClientArea().width;
|
|
738 int height= fTextWidget.getLineHeight(offset + length) + lowerRight.y - upperLeft.y;
|
|
739 fTextWidget.redraw(upperLeft.x, upperLeft.y, width, height, false);
|
|
740 }
|
|
741
|
|
742 fTextWidget.redrawRange(offset, length, true);
|
|
743 }
|
|
744
|
|
745 /*
|
|
746 * @see ITextListener#textChanged(TextEvent)
|
|
747 * @since 2.0
|
|
748 */
|
|
749 public void textChanged(TextEvent event) {
|
|
750 if (event.getViewerRedrawState())
|
|
751 paint();
|
|
752 }
|
|
753
|
|
754 /*
|
|
755 * @see IPositionUpdater#update(DocumentEvent)
|
|
756 * @since 2.0
|
|
757 */
|
|
758 public void update(DocumentEvent event) {
|
|
759 int offset= event.getOffset();
|
|
760 int length= event.getLength();
|
|
761 int delta= event.getText().length() - length;
|
|
762
|
|
763 if (offset < fPosition.getOffset())
|
|
764 fPosition.setOffset(fPosition.getOffset() + delta);
|
|
765 else if (offset < fPosition.getOffset() + fPosition.getLength())
|
|
766 fPosition.setLength(fPosition.getLength() + delta);
|
|
767 }
|
|
768 }
|
|
769
|
|
770 /**
|
|
771 * This viewer's find/replace target.
|
|
772 */
|
|
773 class FindReplaceTarget : IFindReplaceTarget, IFindReplaceTargetExtension, IFindReplaceTargetExtension3 {
|
|
774
|
|
775 /** The range for this target. */
|
|
776 private FindReplaceRange fRange;
|
|
777 /** The highlight color of the range of this target. */
|
|
778 private Color fScopeHighlightColor;
|
|
779 /** The document partitioner remembered in case of a "Replace All". */
|
|
780 private Map fRememberedPartitioners;
|
|
781 /**
|
|
782 * The active rewrite session.
|
|
783 * @since 3.1
|
|
784 */
|
|
785 private DocumentRewriteSession fRewriteSession;
|
|
786
|
|
787 /*
|
|
788 * @see IFindReplaceTarget#getSelectionText()
|
|
789 */
|
|
790 public String getSelectionText() {
|
|
791 Point s= TextViewer.this.getSelectedRange();
|
|
792 if (s.x > -1 && s.y > -1) {
|
|
793 try {
|
|
794 IDocument document= TextViewer.this.getDocument();
|
|
795 return document.get(s.x, s.y);
|
|
796 } catch (BadLocationException x) {
|
|
797 }
|
|
798 }
|
|
799 return ""; //$NON-NLS-1$
|
|
800 }
|
|
801
|
|
802 /*
|
|
803 * @see IFindReplaceTarget#replaceSelection(String)
|
|
804 */
|
|
805 public void replaceSelection(String text) {
|
|
806 replaceSelection(text, false);
|
|
807 }
|
|
808
|
|
809 /*
|
|
810 * @see IFindReplaceTarget#replaceSelection(String)
|
|
811 */
|
|
812 public void replaceSelection(String text, bool regExReplace) {
|
|
813 Point s= TextViewer.this.getSelectedRange();
|
|
814 if (s.x > -1 && s.y > -1) {
|
|
815 try {
|
|
816 IRegion matchRegion= TextViewer.this.getFindReplaceDocumentAdapter().replace(text, regExReplace);
|
|
817 int length= -1;
|
|
818 if (matchRegion !is null)
|
|
819 length= matchRegion.getLength();
|
|
820
|
|
821 if (text !is null && length > 0)
|
|
822 TextViewer.this.setSelectedRange(s.x, length);
|
|
823 } catch (BadLocationException x) {
|
|
824 }
|
|
825 }
|
|
826 }
|
|
827
|
|
828 /*
|
|
829 * @see IFindReplaceTarget#isEditable()
|
|
830 */
|
|
831 public bool isEditable() {
|
|
832 return TextViewer.this.isEditable();
|
|
833 }
|
|
834
|
|
835 /*
|
|
836 * @see IFindReplaceTarget#getSelection()
|
|
837 */
|
|
838 public Point getSelection() {
|
|
839 Point modelSelection= TextViewer.this.getSelectedRange();
|
|
840 Point widgetSelection= modelSelection2WidgetSelection(modelSelection);
|
|
841 return widgetSelection !is null ? widgetSelection : new Point(-1, -1);
|
|
842 }
|
|
843
|
|
844 /*
|
|
845 * @see IFindReplaceTarget#findAndSelect(int, String, bool, bool, bool)
|
|
846 */
|
|
847 public int findAndSelect(int widgetOffset, String findString, bool searchForward, bool caseSensitive, bool wholeWord) {
|
|
848 try {
|
|
849 return findAndSelect(widgetOffset, findString, searchForward, caseSensitive, wholeWord, false);
|
|
850 } catch (PatternSyntaxException x) {
|
|
851 return -1;
|
|
852 }
|
|
853 }
|
|
854
|
|
855 /*
|
|
856 * @see IFindReplaceTarget#findAndSelect(int, String, bool, bool, bool)
|
|
857 */
|
|
858 public int findAndSelect(int widgetOffset, String findString, bool searchForward, bool caseSensitive, bool wholeWord, bool regExSearch) {
|
|
859
|
|
860 int modelOffset= widgetOffset is -1 ? -1 : widgetOffset2ModelOffset(widgetOffset);
|
|
861
|
|
862 if (fRange !is null) {
|
|
863 IRegion range= fRange.getRange();
|
|
864 modelOffset= TextViewer.this.findAndSelectInRange(modelOffset, findString, searchForward, caseSensitive, wholeWord, range.getOffset(), range.getLength(), regExSearch);
|
|
865 } else {
|
|
866 modelOffset= TextViewer.this.findAndSelect(modelOffset, findString, searchForward, caseSensitive, wholeWord, regExSearch);
|
|
867 }
|
|
868
|
|
869 widgetOffset= modelOffset is -1 ? -1 : modelOffset2WidgetOffset(modelOffset);
|
|
870 return widgetOffset;
|
|
871 }
|
|
872
|
|
873 /*
|
|
874 * @see IFindReplaceTarget#canPerformFind()
|
|
875 */
|
|
876 public bool canPerformFind() {
|
|
877 return TextViewer.this.canPerformFind();
|
|
878 }
|
|
879
|
|
880 /*
|
|
881 * @see IFindReplaceTargetExtension#beginSession()
|
|
882 * @since 2.0
|
|
883 */
|
|
884 public void beginSession() {
|
|
885 fRange= null;
|
|
886 }
|
|
887
|
|
888 /*
|
|
889 * @see IFindReplaceTargetExtension#endSession()
|
|
890 * @since 2.0
|
|
891 */
|
|
892 public void endSession() {
|
|
893 if (fRange !is null) {
|
|
894 fRange.uninstall();
|
|
895 fRange= null;
|
|
896 }
|
|
897 }
|
|
898
|
|
899 /*
|
|
900 * @see IFindReplaceTargetExtension#getScope()
|
|
901 * @since 2.0
|
|
902 */
|
|
903 public IRegion getScope() {
|
|
904 return fRange is null ? null : fRange.getRange();
|
|
905 }
|
|
906
|
|
907 /*
|
|
908 * @see IFindReplaceTargetExtension#getLineSelection()
|
|
909 * @since 2.0
|
|
910 */
|
|
911 public Point getLineSelection() {
|
|
912 Point point= TextViewer.this.getSelectedRange();
|
|
913
|
|
914 try {
|
|
915 IDocument document= TextViewer.this.getDocument();
|
|
916
|
|
917 // beginning of line
|
|
918 int line= document.getLineOfOffset(point.x);
|
|
919 int offset= document.getLineOffset(line);
|
|
920
|
|
921 // end of line
|
|
922 IRegion lastLineInfo= document.getLineInformationOfOffset(point.x + point.y);
|
|
923 int lastLine= document.getLineOfOffset(point.x + point.y);
|
|
924 int length;
|
|
925 if (lastLineInfo.getOffset() is point.x + point.y && lastLine > 0)
|
|
926 length= document.getLineOffset(lastLine - 1) + document.getLineLength(lastLine - 1) - offset;
|
|
927 else
|
|
928 length= lastLineInfo.getOffset() + lastLineInfo.getLength() - offset;
|
|
929
|
|
930 return new Point(offset, length);
|
|
931
|
|
932 } catch (BadLocationException e) {
|
|
933 // should not happen
|
|
934 return new Point(point.x, 0);
|
|
935 }
|
|
936 }
|
|
937
|
|
938 /*
|
|
939 * @see IFindReplaceTargetExtension#setSelection(int, int)
|
|
940 * @since 2.0
|
|
941 */
|
|
942 public void setSelection(int modelOffset, int modelLength) {
|
|
943 TextViewer.this.setSelectedRange(modelOffset, modelLength);
|
|
944 }
|
|
945
|
|
946 /*
|
|
947 * @see IFindReplaceTargetExtension#setScope(IRegion)
|
|
948 * @since 2.0
|
|
949 */
|
|
950 public void setScope(IRegion scope) {
|
|
951 if (fRange !is null)
|
|
952 fRange.uninstall();
|
|
953
|
|
954 if (scope is null) {
|
|
955 fRange= null;
|
|
956 return;
|
|
957 }
|
|
958
|
|
959 fRange= new FindReplaceRange(scope);
|
|
960 fRange.setHighlightColor(fScopeHighlightColor);
|
|
961 fRange.install();
|
|
962 }
|
|
963
|
|
964 /*
|
|
965 * @see IFindReplaceTargetExtension#setScopeHighlightColor(Color)
|
|
966 * @since 2.0
|
|
967 */
|
|
968 public void setScopeHighlightColor(Color color) {
|
|
969 if (fRange !is null)
|
|
970 fRange.setHighlightColor(color);
|
|
971 fScopeHighlightColor= color;
|
|
972 }
|
|
973
|
|
974 /*
|
|
975 * @see IFindReplaceTargetExtension#setReplaceAllMode(bool)
|
|
976 * @since 2.0
|
|
977 */
|
|
978 public void setReplaceAllMode(bool replaceAll) {
|
|
979
|
|
980 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18232
|
|
981
|
|
982 IDocument document= TextViewer.this.getDocument();
|
|
983
|
|
984 if (replaceAll) {
|
|
985
|
|
986 if (document instanceof IDocumentExtension4) {
|
|
987 IDocumentExtension4 extension= (IDocumentExtension4) document;
|
|
988 fRewriteSession= extension.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
|
|
989 } else {
|
|
990 TextViewer.this.setRedraw(false);
|
|
991 TextViewer.this.startSequentialRewriteMode(false);
|
|
992
|
|
993 if (fUndoManager !is null)
|
|
994 fUndoManager.beginCompoundChange();
|
|
995
|
|
996 fRememberedPartitioners= TextUtilities.removeDocumentPartitioners(document);
|
|
997 }
|
|
998
|
|
999 } else {
|
|
1000
|
|
1001 if (document instanceof IDocumentExtension4) {
|
|
1002 IDocumentExtension4 extension= (IDocumentExtension4) document;
|
|
1003 extension.stopRewriteSession(fRewriteSession);
|
|
1004 } else {
|
|
1005 TextViewer.this.setRedraw(true);
|
|
1006 TextViewer.this.stopSequentialRewriteMode();
|
|
1007
|
|
1008 if (fUndoManager !is null)
|
|
1009 fUndoManager.endCompoundChange();
|
|
1010
|
|
1011 if (fRememberedPartitioners !is null)
|
|
1012 TextUtilities.addDocumentPartitioners(document, fRememberedPartitioners);
|
|
1013 }
|
|
1014 }
|
|
1015 }
|
|
1016 }
|
|
1017
|
|
1018
|
|
1019 /**
|
|
1020 * The viewer's rewrite target.
|
|
1021 * @since 2.0
|
|
1022 */
|
|
1023 class RewriteTarget : IRewriteTarget {
|
|
1024
|
|
1025 /*
|
|
1026 * @see dwtx.jface.text.IRewriteTarget#beginCompoundChange()
|
|
1027 */
|
|
1028 public void beginCompoundChange() {
|
|
1029 if (fUndoManager !is null)
|
|
1030 fUndoManager.beginCompoundChange();
|
|
1031 }
|
|
1032
|
|
1033 /*
|
|
1034 * @see dwtx.jface.text.IRewriteTarget#endCompoundChange()
|
|
1035 */
|
|
1036 public void endCompoundChange() {
|
|
1037 if (fUndoManager !is null)
|
|
1038 fUndoManager.endCompoundChange();
|
|
1039 }
|
|
1040
|
|
1041 /*
|
|
1042 * @see dwtx.jface.text.IRewriteTarget#getDocument()
|
|
1043 */
|
|
1044 public IDocument getDocument() {
|
|
1045 return TextViewer.this.getDocument();
|
|
1046 }
|
|
1047
|
|
1048 /*
|
|
1049 * @see dwtx.jface.text.IRewriteTarget#setRedraw(bool)
|
|
1050 */
|
|
1051 public void setRedraw(bool redraw) {
|
|
1052 TextViewer.this.setRedraw(redraw);
|
|
1053 }
|
|
1054 }
|
|
1055
|
|
1056 /**
|
|
1057 * Value object used as key in the text hover configuration table. It is
|
|
1058 * modifiable only inside this compilation unit to allow the reuse of created
|
|
1059 * objects for efficiency reasons
|
|
1060 *
|
|
1061 * @since 2.1
|
|
1062 */
|
|
1063 protected class TextHoverKey {
|
|
1064
|
|
1065 /** The content type this key belongs to */
|
|
1066 private String fContentType;
|
|
1067 /** The state mask */
|
|
1068 private int fStateMask;
|
|
1069
|
|
1070 /**
|
|
1071 * Creates a new text hover key for the given content type and state mask.
|
|
1072 *
|
|
1073 * @param contentType the content type
|
|
1074 * @param stateMask the state mask
|
|
1075 */
|
|
1076 protected TextHoverKey(String contentType, int stateMask) {
|
|
1077 Assert.isNotNull(contentType);
|
|
1078 fContentType= contentType;
|
|
1079 fStateMask= stateMask;
|
|
1080 }
|
|
1081
|
|
1082 /*
|
|
1083 * @see java.lang.Object#equals(java.lang.Object)
|
|
1084 */
|
|
1085 public bool equals(Object obj) {
|
|
1086 if (obj is null || obj.getClass() !is getClass())
|
|
1087 return false;
|
|
1088 TextHoverKey textHoverKey= (TextHoverKey)obj;
|
|
1089 return textHoverKey.fContentType.equals(fContentType) && textHoverKey.fStateMask is fStateMask;
|
|
1090 }
|
|
1091
|
|
1092 /*
|
|
1093 * @see java.lang.Object#hashCode()
|
|
1094 */
|
|
1095 public int hashCode() {
|
|
1096 return fStateMask << 16 | fContentType.hashCode();
|
|
1097 }
|
|
1098
|
|
1099 /**
|
|
1100 * Sets the state mask of this text hover key.
|
|
1101 *
|
|
1102 * @param stateMask the state mask
|
|
1103 */
|
|
1104 private void setStateMask(int stateMask) {
|
|
1105 fStateMask= stateMask;
|
|
1106 }
|
|
1107 }
|
|
1108
|
|
1109 /**
|
|
1110 * Captures and remembers the viewer state (selection and visual position). {@link TextViewer.ViewerState}
|
|
1111 * instances are normally used once and then discarded, similar to the following snippet:
|
|
1112 * <pre>
|
|
1113 * ViewerState state= new ViewerState(); // remember the state
|
|
1114 * doStuff(); // operation that may call setRedraw() and perform complex document modifications
|
|
1115 * state.restore(true); // restore the remembered state
|
|
1116 * </pre>
|
|
1117 *
|
|
1118 * @since 3.3
|
|
1119 */
|
|
1120 private final class ViewerState {
|
|
1121 /** The position tracking the selection. */
|
|
1122 private Position fSelection;
|
|
1123 /** <code>true</code> if {@link #fSelection} was originally backwards. */
|
|
1124 private bool fReverseSelection;
|
|
1125 /** <code>true</code> if the selection has been updated while in redraw(off) mode. */
|
|
1126 private bool fSelectionSet;
|
|
1127 /** The position tracking the visually stable line. */
|
|
1128 private Position fStableLine;
|
|
1129 /** The pixel offset of the stable line measured from the client area. */
|
|
1130 private int fStablePixel;
|
|
1131
|
|
1132 /** The position updater for {@link #fSelection} and {@link #fStableLine}. */
|
|
1133 private IPositionUpdater fUpdater;
|
|
1134 /** The document that the position updater and the positions are registered with. */
|
|
1135 private IDocument fUpdaterDocument;
|
|
1136 /** The position category used by {@link #fUpdater}. */
|
|
1137 private String fUpdaterCategory;
|
|
1138
|
|
1139 /**
|
|
1140 * Creates a new viewer state instance and connects it to the current document.
|
|
1141 */
|
|
1142 public ViewerState() {
|
|
1143 IDocument document= getDocument();
|
|
1144 if (document !is null)
|
|
1145 connect(document);
|
|
1146 }
|
|
1147
|
|
1148 /**
|
|
1149 * Returns the normalized selection, i.e. the the selection length is always non-negative.
|
|
1150 *
|
|
1151 * @return the normalized selection
|
|
1152 */
|
|
1153 public Point getSelection() {
|
|
1154 if (fSelection is null)
|
|
1155 return new Point(-1, -1);
|
|
1156 return new Point(fSelection.getOffset(), fSelection.getLength());
|
|
1157 }
|
|
1158
|
|
1159 /**
|
|
1160 * Updates the selection.
|
|
1161 *
|
|
1162 * @param offset the new selection offset
|
|
1163 * @param length the new selection length
|
|
1164 */
|
|
1165 public void updateSelection(int offset, int length) {
|
|
1166 fSelectionSet= true;
|
|
1167 if (fSelection is null)
|
|
1168 fSelection= new Position(offset, length);
|
|
1169 else
|
|
1170 updatePosition(fSelection, offset, length);
|
|
1171 }
|
|
1172
|
|
1173 /**
|
|
1174 * Restores the state and disconnects it from the document. The selection is no longer
|
|
1175 * tracked after this call.
|
|
1176 *
|
|
1177 * @param restoreViewport <code>true</code> to restore both selection and viewport,
|
|
1178 * <code>false</code> to only restore the selection
|
|
1179 */
|
|
1180 public void restore(bool restoreViewport) {
|
|
1181 if (isConnected())
|
|
1182 disconnect();
|
|
1183 if (fSelection !is null) {
|
|
1184 int offset= fSelection.getOffset();
|
|
1185 int length= fSelection.getLength();
|
|
1186 if (fReverseSelection) {
|
|
1187 offset-= length;
|
|
1188 length= -length;
|
|
1189 }
|
|
1190 setSelectedRange(offset, length);
|
|
1191 if (restoreViewport)
|
|
1192 updateViewport();
|
|
1193 }
|
|
1194 }
|
|
1195
|
|
1196 /**
|
|
1197 * Updates the viewport, trying to keep the
|
|
1198 * {@linkplain StyledText#getLinePixel(int) line pixel} of the caret line stable. If the
|
|
1199 * selection has been updated while in redraw(false) mode, the new selection is revealed.
|
|
1200 */
|
|
1201 private void updateViewport() {
|
|
1202 if (fSelectionSet) {
|
|
1203 revealRange(fSelection.getOffset(), fSelection.getLength());
|
|
1204 } else if (fStableLine !is null) {
|
|
1205 int stableLine;
|
|
1206 try {
|
|
1207 stableLine= fUpdaterDocument.getLineOfOffset(fStableLine.getOffset());
|
|
1208 } catch (BadLocationException x) {
|
|
1209 // ignore and return silently
|
|
1210 return;
|
|
1211 }
|
|
1212 int stableWidgetLine= getClosestWidgetLineForModelLine(stableLine);
|
|
1213 if (stableWidgetLine is -1)
|
|
1214 return;
|
|
1215 int linePixel= getTextWidget().getLinePixel(stableWidgetLine);
|
|
1216 int delta= fStablePixel - linePixel;
|
|
1217 int topPixel= getTextWidget().getTopPixel();
|
|
1218 getTextWidget().setTopPixel(topPixel - delta);
|
|
1219 }
|
|
1220 }
|
|
1221
|
|
1222 /**
|
|
1223 * Remembers the viewer state.
|
|
1224 *
|
|
1225 * @param document the document to remember the state of
|
|
1226 */
|
|
1227 private void connect(IDocument document) {
|
|
1228 Assert.isLegal(document !is null);
|
|
1229 Assert.isLegal(!isConnected());
|
|
1230 fUpdaterDocument= document;
|
|
1231 try {
|
|
1232 fUpdaterCategory= SELECTION_POSITION_CATEGORY + hashCode();
|
|
1233 fUpdater= new NonDeletingPositionUpdater(fUpdaterCategory);
|
|
1234 fUpdaterDocument.addPositionCategory(fUpdaterCategory);
|
|
1235 fUpdaterDocument.addPositionUpdater(fUpdater);
|
|
1236
|
|
1237 Point selectionRange= getSelectedRange();
|
|
1238 fReverseSelection= selectionRange.y < 0;
|
|
1239 int offset, length;
|
|
1240 if (fReverseSelection) {
|
|
1241 offset= selectionRange.x + selectionRange.y;
|
|
1242 length= -selectionRange.y;
|
|
1243 } else {
|
|
1244 offset= selectionRange.x;
|
|
1245 length= selectionRange.y;
|
|
1246 }
|
|
1247
|
|
1248 fSelection= new Position(offset, length);
|
|
1249 fSelectionSet= false;
|
|
1250 fUpdaterDocument.addPosition(fUpdaterCategory, fSelection);
|
|
1251
|
|
1252 int stableLine= getStableLine();
|
|
1253 int stableWidgetLine= modelLine2WidgetLine(stableLine);
|
|
1254 fStablePixel= getTextWidget().getLinePixel(stableWidgetLine);
|
|
1255 IRegion stableLineInfo= fUpdaterDocument.getLineInformation(stableLine);
|
|
1256 fStableLine= new Position(stableLineInfo.getOffset(), stableLineInfo.getLength());
|
|
1257 fUpdaterDocument.addPosition(fUpdaterCategory, fStableLine);
|
|
1258 } catch (BadPositionCategoryException e) {
|
|
1259 // cannot happen
|
|
1260 Assert.isTrue(false);
|
|
1261 } catch (BadLocationException e) {
|
|
1262 // should not happen except on concurrent modification
|
|
1263 // ignore and disconnect
|
|
1264 disconnect();
|
|
1265 }
|
|
1266 }
|
|
1267
|
|
1268 /**
|
|
1269 * Updates a position with the given information and clears its deletion state.
|
|
1270 *
|
|
1271 * @param position the position to update
|
|
1272 * @param offset the new selection offset
|
|
1273 * @param length the new selection length
|
|
1274 */
|
|
1275 private void updatePosition(Position position, int offset, int length) {
|
|
1276 position.setOffset(offset);
|
|
1277 position.setLength(length);
|
|
1278 // http://bugs.eclipse.org/bugs/show_bug.cgi?id=32795
|
|
1279 position.isDeleted= false;
|
|
1280 }
|
|
1281
|
|
1282 /**
|
|
1283 * Returns the document line to keep visually stable. If the caret line is (partially)
|
|
1284 * visible, it is returned, otherwise the topmost (partially) visible line is returned.
|
|
1285 *
|
|
1286 * @return the visually stable line of this viewer state
|
|
1287 */
|
|
1288 private int getStableLine() {
|
|
1289 int stableLine; // the model line that we try to keep stable
|
|
1290 int caretLine= getTextWidget().getLineAtOffset(getTextWidget().getCaretOffset());
|
|
1291 if (caretLine < JFaceTextUtil.getPartialTopIndex(getTextWidget()) || caretLine > JFaceTextUtil.getPartialBottomIndex(getTextWidget())) {
|
|
1292 stableLine= JFaceTextUtil.getPartialTopIndex(TextViewer.this);
|
|
1293 } else {
|
|
1294 stableLine= widgetLine2ModelLine(caretLine);
|
|
1295 }
|
|
1296 return stableLine;
|
|
1297 }
|
|
1298
|
|
1299 /**
|
|
1300 * Returns <code>true</code> if the viewer state is being tracked, <code>false</code>
|
|
1301 * otherwise.
|
|
1302 *
|
|
1303 * @return the tracking state
|
|
1304 */
|
|
1305 private bool isConnected() {
|
|
1306 return fUpdater !is null;
|
|
1307 }
|
|
1308
|
|
1309 /**
|
|
1310 * Disconnects from the document.
|
|
1311 */
|
|
1312 private void disconnect() {
|
|
1313 Assert.isTrue(isConnected());
|
|
1314 try {
|
|
1315 fUpdaterDocument.removePosition(fUpdaterCategory, fSelection);
|
|
1316 fUpdaterDocument.removePosition(fUpdaterCategory, fStableLine);
|
|
1317 fUpdaterDocument.removePositionUpdater(fUpdater);
|
|
1318 fUpdater= null;
|
|
1319 fUpdaterDocument.removePositionCategory(fUpdaterCategory);
|
|
1320 fUpdaterCategory= null;
|
|
1321 } catch (BadPositionCategoryException x) {
|
|
1322 // cannot happen
|
|
1323 Assert.isTrue(false);
|
|
1324 }
|
|
1325 }
|
|
1326 }
|
|
1327
|
|
1328 /**
|
|
1329 * Internal cursor listener i.e. aggregation of mouse and key listener.
|
|
1330 *
|
|
1331 * @since 3.0
|
|
1332 */
|
|
1333 private class CursorListener : KeyListener, MouseListener {
|
|
1334
|
|
1335 /**
|
|
1336 * Installs this cursor listener.
|
|
1337 */
|
|
1338 private void install() {
|
|
1339 if (fTextWidget !is null && !fTextWidget.isDisposed()) {
|
|
1340 fTextWidget.addKeyListener(this);
|
|
1341 fTextWidget.addMouseListener(this);
|
|
1342 }
|
|
1343 }
|
|
1344
|
|
1345 /**
|
|
1346 * Uninstalls this cursor listener.
|
|
1347 */
|
|
1348 private void uninstall() {
|
|
1349 if (fTextWidget !is null && !fTextWidget.isDisposed()) {
|
|
1350 fTextWidget.removeKeyListener(this);
|
|
1351 fTextWidget.removeMouseListener(this);
|
|
1352 }
|
|
1353 }
|
|
1354
|
|
1355 /*
|
|
1356 * @see KeyListener#keyPressed(dwt.events.KeyEvent)
|
|
1357 */
|
|
1358 public void keyPressed(KeyEvent event) {
|
|
1359 }
|
|
1360
|
|
1361 /*
|
|
1362 * @see KeyListener#keyPressed(dwt.events.KeyEvent)
|
|
1363 */
|
|
1364 public void keyReleased(KeyEvent e) {
|
|
1365 if (fTextWidget.getSelectionCount() is 0) {
|
|
1366 fLastSentSelectionChange= null;
|
|
1367 queuePostSelectionChanged(e.character is DWT.DEL);
|
|
1368 }
|
|
1369 }
|
|
1370
|
|
1371 /*
|
|
1372 * @see MouseListener#mouseDoubleClick(dwt.events.MouseEvent)
|
|
1373 */
|
|
1374 public void mouseDoubleClick(MouseEvent e) {
|
|
1375 }
|
|
1376
|
|
1377 /*
|
|
1378 * @see MouseListener#mouseDown(dwt.events.MouseEvent)
|
|
1379 */
|
|
1380 public void mouseDown(MouseEvent e) {
|
|
1381 }
|
|
1382
|
|
1383 /*
|
|
1384 * @see MouseListener#mouseUp(dwt.events.MouseEvent)
|
|
1385 */
|
|
1386 public void mouseUp(MouseEvent event) {
|
|
1387 if (fTextWidget.getSelectionCount() is 0)
|
|
1388 queuePostSelectionChanged(false);
|
|
1389 }
|
|
1390 }
|
|
1391
|
|
1392 /**
|
|
1393 * Internal listener to document rewrite session state changes.
|
|
1394 * @since 3.1
|
|
1395 */
|
|
1396 private class DocumentRewriteSessionListener : IDocumentRewriteSessionListener {
|
|
1397
|
|
1398 /*
|
|
1399 * @see dwtx.jface.text.IDocumentRewriteSessionListener#documentRewriteSessionChanged(dwtx.jface.text.DocumentRewriteSessionEvent)
|
|
1400 */
|
|
1401 public void documentRewriteSessionChanged(DocumentRewriteSessionEvent event) {
|
|
1402 IRewriteTarget target= TextViewer.this.getRewriteTarget();
|
|
1403 // FIXME always use setRedraw to avoid flickering due to scrolling
|
|
1404 // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=158746
|
|
1405 bool toggleRedraw= true || event.getSession().getSessionType() !is DocumentRewriteSessionType.UNRESTRICTED_SMALL;
|
|
1406 final bool viewportStabilize= !toggleRedraw;
|
|
1407 if (DocumentRewriteSessionEvent.SESSION_START is event.getChangeType()) {
|
|
1408 if (toggleRedraw)
|
|
1409 target.setRedraw(false);
|
|
1410 target.beginCompoundChange();
|
|
1411 if (viewportStabilize && fViewerState is null)
|
|
1412 fViewerState= new ViewerState();
|
|
1413 } else if (DocumentRewriteSessionEvent.SESSION_STOP is event.getChangeType()) {
|
|
1414 if (viewportStabilize && fViewerState !is null) {
|
|
1415 fViewerState.restore(true);
|
|
1416 fViewerState= null;
|
|
1417 }
|
|
1418 target.endCompoundChange();
|
|
1419 if (toggleRedraw)
|
|
1420 target.setRedraw(true);
|
|
1421 }
|
|
1422 }
|
|
1423 }
|
|
1424
|
|
1425
|
|
1426 /**
|
|
1427 * Identifies the scrollbars as originators of a view port change.
|
|
1428 */
|
|
1429 protected static final int SCROLLER= 1;
|
|
1430 /**
|
|
1431 * Identifies mouse moves as originators of a view port change.
|
|
1432 */
|
|
1433 protected static final int MOUSE= 2;
|
|
1434 /**
|
|
1435 * Identifies mouse button up as originator of a view port change.
|
|
1436 */
|
|
1437 protected static final int MOUSE_END= 3;
|
|
1438 /**
|
|
1439 * Identifies key strokes as originators of a view port change.
|
|
1440 */
|
|
1441 protected static final int KEY= 4;
|
|
1442 /**
|
|
1443 * Identifies window resizing as originator of a view port change.
|
|
1444 */
|
|
1445 protected static final int RESIZE= 5;
|
|
1446 /**
|
|
1447 * Identifies internal reasons as originators of a view port change.
|
|
1448 */
|
|
1449 protected static final int INTERNAL= 6;
|
|
1450
|
|
1451 /** Internal name of the position category used selection preservation during shift. */
|
|
1452 protected static final String SHIFTING= "__TextViewer_shifting"; //$NON-NLS-1$
|
|
1453
|
|
1454 /**
|
|
1455 * Base position category name used by the selection updater
|
|
1456 * @since 3.1
|
|
1457 */
|
|
1458 private static final String SELECTION_POSITION_CATEGORY= "_textviewer_selection_category"; //$NON-NLS-1$
|
|
1459 /** The viewer's text widget */
|
|
1460 private StyledText fTextWidget;
|
|
1461 /** The viewer's input document */
|
|
1462 private IDocument fDocument;
|
|
1463 /** The viewer's visible document */
|
|
1464 private IDocument fVisibleDocument;
|
|
1465 /** The viewer's document adapter */
|
|
1466 private IDocumentAdapter fDocumentAdapter;
|
|
1467 /** The slave document manager */
|
|
1468 private ISlaveDocumentManager fSlaveDocumentManager;
|
|
1469 /** The text viewer's double click strategies connector */
|
|
1470 private TextDoubleClickStrategyConnector fDoubleClickStrategyConnector;
|
|
1471 /** The text viewer's view port guard */
|
|
1472 private ViewportGuard fViewportGuard;
|
|
1473 /** Caches the graphical coordinate of the first visible line */
|
|
1474 private int fTopInset= 0;
|
|
1475 /** The most recent document modification as widget command */
|
|
1476 private WidgetCommand fWidgetCommand= new WidgetCommand();
|
|
1477 /** The DWT control's scrollbars */
|
|
1478 private ScrollBar fScroller;
|
|
1479 /** Listener on the visible document */
|
|
1480 private VisibleDocumentListener fVisibleDocumentListener= new VisibleDocumentListener();
|
|
1481 /** Verify listener */
|
|
1482 private TextVerifyListener fVerifyListener= new TextVerifyListener();
|
|
1483 /** The most recent widget modification as document command */
|
|
1484 private DocumentCommand fDocumentCommand= new DocumentCommand();
|
|
1485 /** The viewer's find/replace target */
|
|
1486 private IFindReplaceTarget fFindReplaceTarget;
|
|
1487 /**
|
|
1488 * The text viewer's hovering controller
|
|
1489 * @since 2.0
|
|
1490 */
|
|
1491 private TextViewerHoverManager fTextHoverManager;
|
|
1492 /**
|
|
1493 * The viewer widget token keeper
|
|
1494 * @since 2.0
|
|
1495 */
|
|
1496 private IWidgetTokenKeeper fWidgetTokenKeeper;
|
|
1497 /**
|
|
1498 * The viewer's manager of verify key listeners
|
|
1499 * @since 2.0
|
|
1500 */
|
|
1501 private VerifyKeyListenersManager fVerifyKeyListenersManager= new VerifyKeyListenersManager();
|
|
1502 /**
|
|
1503 * The mark position.
|
|
1504 * @since 2.0
|
|
1505 */
|
|
1506 protected Position fMarkPosition;
|
|
1507 /**
|
|
1508 * The mark position category.
|
|
1509 * @since 2.0
|
|
1510 */
|
|
1511 private final String MARK_POSITION_CATEGORY="__mark_category_" + hashCode(); //$NON-NLS-1$
|
|
1512 /**
|
|
1513 * The mark position updater
|
|
1514 * @since 2.0
|
|
1515 */
|
|
1516 private final IPositionUpdater fMarkPositionUpdater= new DefaultPositionUpdater(MARK_POSITION_CATEGORY);
|
|
1517 /**
|
|
1518 * The flag indicating the redraw behavior
|
|
1519 * @since 2.0
|
|
1520 */
|
|
1521 private int fRedrawCounter= 0;
|
|
1522 /**
|
|
1523 * The viewer's rewrite target
|
|
1524 * @since 2.0
|
|
1525 */
|
|
1526 private IRewriteTarget fRewriteTarget;
|
|
1527 /**
|
|
1528 * The viewer's cursor listener.
|
|
1529 * @since 3.0
|
|
1530 */
|
|
1531 private CursorListener fCursorListener;
|
|
1532 /**
|
|
1533 * Last selection range sent to selection change listeners.
|
|
1534 * @since 3.0
|
|
1535 */
|
|
1536 private IRegion fLastSentSelectionChange;
|
|
1537 /**
|
|
1538 * The registered post selection changed listeners.
|
|
1539 * @since 3.0
|
|
1540 */
|
|
1541 private List fPostSelectionChangedListeners;
|
|
1542 /**
|
|
1543 * Queued post selection changed events count.
|
|
1544 * @since 3.0
|
|
1545 */
|
|
1546 private final int[] fNumberOfPostSelectionChangedEvents= new int[1];
|
|
1547 /**
|
|
1548 * Last selection range sent to post selection change listeners.
|
|
1549 * @since 3.0
|
|
1550 */
|
|
1551 private IRegion fLastSentPostSelectionChange;
|
|
1552 /**
|
|
1553 * The set of registered editor helpers.
|
|
1554 * @since 3.1
|
|
1555 */
|
|
1556 private Set fEditorHelpers= new HashSet();
|
|
1557 /**
|
|
1558 * The internal rewrite session listener.
|
|
1559 * @since 3.1
|
|
1560 */
|
|
1561 private DocumentRewriteSessionListener fDocumentRewriteSessionListener= new DocumentRewriteSessionListener();
|
|
1562
|
|
1563 /** Should the auto indent strategies ignore the next edit operation */
|
|
1564 protected bool fIgnoreAutoIndent= false;
|
|
1565 /** The strings a line is prefixed with on SHIFT_RIGHT and removed from each line on SHIFT_LEFT */
|
|
1566 protected Map fIndentChars;
|
|
1567 /** The string a line is prefixed with on PREFIX and removed from each line on STRIP_PREFIX */
|
|
1568 protected Map fDefaultPrefixChars;
|
|
1569 /** The text viewer's text double click strategies */
|
|
1570 protected Map fDoubleClickStrategies;
|
|
1571 /** The text viewer's undo manager */
|
|
1572 protected IUndoManager fUndoManager;
|
|
1573 /** The text viewer's auto indent strategies */
|
|
1574 protected Map fAutoIndentStrategies;
|
|
1575 /** The text viewer's text hovers */
|
|
1576 protected Map fTextHovers;
|
|
1577 /** All registered view port listeners> */
|
|
1578 protected List fViewportListeners;
|
|
1579 /** The last visible vertical position of the top line */
|
|
1580 protected int fLastTopPixel;
|
|
1581 /** All registered text listeners */
|
|
1582 protected List fTextListeners;
|
|
1583 /** All registered text input listeners */
|
|
1584 protected List fTextInputListeners;
|
|
1585 /** The text viewer's event consumer */
|
|
1586 protected IEventConsumer fEventConsumer;
|
|
1587 /** Indicates whether the viewer's text presentation should be replaced are modified. */
|
|
1588 protected bool fReplaceTextPresentation= false;
|
|
1589 /**
|
|
1590 * The creator of the text hover control
|
|
1591 * @since 2.0
|
|
1592 */
|
|
1593 protected IInformationControlCreator fHoverControlCreator;
|
|
1594 /**
|
|
1595 * The mapping between model and visible document.
|
|
1596 * @since 2.1
|
|
1597 */
|
|
1598 protected IDocumentInformationMapping fInformationMapping;
|
|
1599 /**
|
|
1600 * The viewer's paint manager.
|
|
1601 * @since 2.1
|
|
1602 */
|
|
1603 protected PaintManager fPaintManager;
|
|
1604 /**
|
|
1605 * The viewers partitioning. I.e. the partitioning name the viewer uses to access partitioning information of its input document.
|
|
1606 * @since 3.0
|
|
1607 */
|
|
1608 protected String fPartitioning;
|
|
1609 /**
|
|
1610 * All registered text presentation listeners.
|
|
1611 * since 3.0
|
|
1612 */
|
|
1613 protected List fTextPresentationListeners;
|
|
1614 /**
|
|
1615 * The find/replace document adapter.
|
|
1616 * @since 3.0
|
|
1617 */
|
|
1618 protected FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
|
|
1619
|
|
1620 /**
|
|
1621 * The text viewer's hyperlink detectors.
|
|
1622 * @since 3.1
|
|
1623 */
|
|
1624 protected IHyperlinkDetector[] fHyperlinkDetectors;
|
|
1625 /**
|
|
1626 * The text viewer's hyperlink presenter.
|
|
1627 * @since 3.1
|
|
1628 */
|
|
1629 protected IHyperlinkPresenter fHyperlinkPresenter;
|
|
1630 /**
|
|
1631 * The text viewer's hyperlink manager.
|
|
1632 * @since 3.1
|
|
1633 */
|
|
1634 protected HyperlinkManager fHyperlinkManager;
|
|
1635 /**
|
|
1636 * The DWT key modifier mask which in combination
|
|
1637 * with the left mouse button triggers the hyperlink mode.
|
|
1638 * @since 3.1
|
|
1639 */
|
|
1640 protected int fHyperlinkStateMask;
|
|
1641 /**
|
|
1642 * The viewer state when in non-redraw state, <code>null</code> otherwise.
|
|
1643 * @since 3.3
|
|
1644 */
|
|
1645 private ViewerState fViewerState;
|
|
1646 /**
|
|
1647 * The editor's tab converter.
|
|
1648 * @since 3.3
|
|
1649 */
|
|
1650 private IAutoEditStrategy fTabsToSpacesConverter;
|
|
1651
|
|
1652
|
|
1653 //---- Construction and disposal ------------------
|
|
1654
|
|
1655
|
|
1656 /**
|
|
1657 * Internal use only
|
|
1658 */
|
|
1659 protected TextViewer() {
|
|
1660 }
|
|
1661
|
|
1662 /**
|
|
1663 * Create a new text viewer with the given DWT style bits.
|
|
1664 * The viewer is ready to use but does not have any plug-in installed.
|
|
1665 *
|
|
1666 * @param parent the parent of the viewer's control
|
|
1667 * @param styles the DWT style bits for the viewer's control,
|
|
1668 * <em>if <code>DWT.WRAP</code> is set then a custom document adapter needs to be provided, see {@link #createDocumentAdapter()}
|
|
1669 */
|
|
1670 public TextViewer(Composite parent, int styles) {
|
|
1671 createControl(parent, styles);
|
|
1672 }
|
|
1673
|
|
1674 /**
|
|
1675 * Factory method to create the text widget to be used as the viewer's text widget.
|
|
1676 *
|
|
1677 * @param parent the parent of the styled text
|
|
1678 * @param styles the styles for the styled text
|
|
1679 * @return the text widget to be used
|
|
1680 */
|
|
1681 protected StyledText createTextWidget(Composite parent, int styles) {
|
|
1682 return new StyledText(parent, styles);
|
|
1683 }
|
|
1684
|
|
1685 /**
|
|
1686 * Factory method to create the document adapter to be used by this viewer.
|
|
1687 *
|
|
1688 * @return the document adapter to be used
|
|
1689 */
|
|
1690 protected IDocumentAdapter createDocumentAdapter() {
|
|
1691 return new DefaultDocumentAdapter();
|
|
1692 }
|
|
1693
|
|
1694 /**
|
|
1695 * Creates the viewer's DWT control. The viewer's text widget either is
|
|
1696 * the control or is a child of the control.
|
|
1697 *
|
|
1698 * @param parent the parent of the viewer's control
|
|
1699 * @param styles the DWT style bits for the viewer's control
|
|
1700 */
|
|
1701 protected void createControl(Composite parent, int styles) {
|
|
1702
|
|
1703 fTextWidget= createTextWidget(parent, styles);
|
|
1704
|
|
1705 // Support scroll page upon MOD1+MouseWheel
|
|
1706 fTextWidget.addListener(DWT.MouseWheel, new Listener() {
|
|
1707
|
|
1708 public void handleEvent(Event event) {
|
|
1709 if (((event.stateMask & DWT.MOD1) is 0))
|
|
1710 return;
|
|
1711
|
|
1712 int topIndex= fTextWidget.getTopIndex();
|
|
1713 int bottomIndex= JFaceTextUtil.getBottomIndex(fTextWidget);
|
|
1714
|
|
1715 if (event.count > 0)
|
|
1716 fTextWidget.setTopIndex(2 * topIndex - bottomIndex);
|
|
1717 else
|
|
1718 fTextWidget.setTopIndex(bottomIndex);
|
|
1719
|
|
1720 updateViewportListeners(INTERNAL);
|
|
1721 }
|
|
1722 });
|
|
1723
|
|
1724 fTextWidget.addDisposeListener(
|
|
1725 new DisposeListener() {
|
|
1726 public void widgetDisposed(DisposeEvent e) {
|
|
1727 handleDispose();
|
|
1728 }
|
|
1729 }
|
|
1730 );
|
|
1731
|
|
1732 fTextWidget.setFont(parent.getFont());
|
|
1733 fTextWidget.setDoubleClickEnabled(true);
|
|
1734
|
|
1735 /*
|
|
1736 * Disable DWT Shift+TAB traversal in this viewer
|
|
1737 * 1GIYQ9K: ITPUI:WINNT - StyledText swallows Shift+TAB
|
|
1738 */
|
|
1739 fTextWidget.addTraverseListener(new TraverseListener() {
|
|
1740 public void keyTraversed(TraverseEvent e) {
|
|
1741 if ((DWT.SHIFT is e.stateMask) && ('\t' is e.character))
|
|
1742 e.doit= false;
|
|
1743 }
|
|
1744 });
|
|
1745
|
|
1746 // where does the first line start
|
|
1747 fTopInset= -fTextWidget.computeTrim(0, 0, 0, 0).y;
|
|
1748
|
|
1749 fVerifyListener.forward(true);
|
|
1750 fTextWidget.addVerifyListener(fVerifyListener);
|
|
1751
|
|
1752 fTextWidget.addSelectionListener(new SelectionListener() {
|
|
1753 public void widgetDefaultSelected(SelectionEvent event) {
|
|
1754 selectionChanged(event.x, event.y - event.x);
|
|
1755 }
|
|
1756 public void widgetSelected(SelectionEvent event) {
|
|
1757 selectionChanged(event.x, event.y - event.x);
|
|
1758 }
|
|
1759 });
|
|
1760
|
|
1761 fCursorListener= new CursorListener();
|
|
1762 fCursorListener.install();
|
|
1763
|
|
1764 initializeViewportUpdate();
|
|
1765 }
|
|
1766
|
|
1767 /*
|
|
1768 * @see Viewer#getControl()
|
|
1769 */
|
|
1770 public Control getControl() {
|
|
1771 return fTextWidget;
|
|
1772 }
|
|
1773
|
|
1774 /*
|
|
1775 * @see ITextViewer#activatePlugins()
|
|
1776 */
|
|
1777 public void activatePlugins() {
|
|
1778
|
|
1779 if (fDoubleClickStrategies !is null && !fDoubleClickStrategies.isEmpty() && fDoubleClickStrategyConnector is null) {
|
|
1780 fDoubleClickStrategyConnector= new TextDoubleClickStrategyConnector();
|
|
1781 fTextWidget.addWordMovementListener(fDoubleClickStrategyConnector);
|
|
1782 fTextWidget.addMouseListener(fDoubleClickStrategyConnector);
|
|
1783 }
|
|
1784
|
|
1785 ensureHoverControlManagerInstalled();
|
|
1786 ensureHyperlinkManagerInstalled();
|
|
1787
|
|
1788 if (fUndoManager !is null) {
|
|
1789 fUndoManager.connect(this);
|
|
1790 fUndoManager.reset();
|
|
1791 }
|
|
1792 }
|
|
1793
|
|
1794 /**
|
|
1795 * After this method has been executed the caller knows that any installed text hover has been installed.
|
|
1796 */
|
|
1797 private void ensureHoverControlManagerInstalled() {
|
|
1798 if (fTextHovers !is null && !fTextHovers.isEmpty() && fHoverControlCreator !is null && fTextHoverManager is null) {
|
|
1799 fTextHoverManager= new TextViewerHoverManager(this, fHoverControlCreator);
|
|
1800 fTextHoverManager.install(this.getTextWidget());
|
|
1801 fTextHoverManager.setSizeConstraints(TEXT_HOVER_WIDTH_CHARS, TEXT_HOVER_HEIGHT_CHARS, false, true);
|
|
1802 fTextHoverManager.setInformationControlReplacer(new StickyHoverManager(this));
|
|
1803 }
|
|
1804 }
|
|
1805
|
|
1806 /*
|
|
1807 * @see ITextViewer#resetPlugins()
|
|
1808 */
|
|
1809 public void resetPlugins() {
|
|
1810 if (fUndoManager !is null)
|
|
1811 fUndoManager.reset();
|
|
1812 }
|
|
1813
|
|
1814 /**
|
|
1815 * Frees all resources allocated by this viewer. Internally called when the viewer's
|
|
1816 * control has been disposed.
|
|
1817 */
|
|
1818 protected void handleDispose() {
|
|
1819
|
|
1820 setDocument(null);
|
|
1821
|
|
1822 if (fPaintManager !is null) {
|
|
1823 fPaintManager.dispose();
|
|
1824 fPaintManager= null;
|
|
1825 }
|
|
1826
|
|
1827 removeViewPortUpdate();
|
|
1828 fViewportGuard= null;
|
|
1829
|
|
1830 if (fViewportListeners !is null) {
|
|
1831 fViewportListeners.clear();
|
|
1832 fViewportListeners= null;
|
|
1833 }
|
|
1834
|
|
1835 if (fTextListeners !is null) {
|
|
1836 fTextListeners.clear();
|
|
1837 fTextListeners= null;
|
|
1838 }
|
|
1839
|
|
1840 if (fTextInputListeners !is null) {
|
|
1841 fTextInputListeners.clear();
|
|
1842 fTextInputListeners= null;
|
|
1843 }
|
|
1844
|
|
1845 if (fPostSelectionChangedListeners !is null) {
|
|
1846 fPostSelectionChangedListeners.clear();
|
|
1847 fPostSelectionChangedListeners= null;
|
|
1848 }
|
|
1849
|
|
1850 if (fAutoIndentStrategies !is null) {
|
|
1851 fAutoIndentStrategies.clear();
|
|
1852 fAutoIndentStrategies= null;
|
|
1853 }
|
|
1854
|
|
1855 if (fUndoManager !is null) {
|
|
1856 fUndoManager.disconnect();
|
|
1857 fUndoManager= null;
|
|
1858 }
|
|
1859
|
|
1860 if (fDoubleClickStrategies !is null) {
|
|
1861 fDoubleClickStrategies.clear();
|
|
1862 fDoubleClickStrategies= null;
|
|
1863 }
|
|
1864
|
|
1865 if (fTextHovers !is null) {
|
|
1866 fTextHovers.clear();
|
|
1867 fTextHovers= null;
|
|
1868 }
|
|
1869
|
|
1870 fDoubleClickStrategyConnector= null;
|
|
1871
|
|
1872 if (fTextHoverManager !is null) {
|
|
1873 fTextHoverManager.dispose();
|
|
1874 fTextHoverManager= null;
|
|
1875 }
|
|
1876
|
|
1877 if (fVisibleDocumentListener !is null) {
|
|
1878 if (fVisibleDocument !is null)
|
|
1879 fVisibleDocument.removeDocumentListener(fVisibleDocumentListener);
|
|
1880 fVisibleDocumentListener= null;
|
|
1881 }
|
|
1882
|
|
1883 if (fDocumentAdapter !is null) {
|
|
1884 fDocumentAdapter.setDocument(null);
|
|
1885 fDocumentAdapter= null;
|
|
1886 }
|
|
1887
|
|
1888 if (fSlaveDocumentManager !is null) {
|
|
1889 if (fVisibleDocument !is null)
|
|
1890 fSlaveDocumentManager.freeSlaveDocument(fVisibleDocument);
|
|
1891 fSlaveDocumentManager= null;
|
|
1892 }
|
|
1893
|
|
1894 if (fCursorListener !is null) {
|
|
1895 fCursorListener.uninstall();
|
|
1896 fCursorListener= null;
|
|
1897 }
|
|
1898
|
|
1899 if (fHyperlinkManager !is null) {
|
|
1900 fHyperlinkManager.uninstall();
|
|
1901 fHyperlinkManager= null;
|
|
1902 }
|
|
1903
|
|
1904 fHyperlinkDetectors= null;
|
|
1905 fVisibleDocument= null;
|
|
1906 fDocument= null;
|
|
1907 fScroller= null;
|
|
1908
|
|
1909 fTextWidget= null;
|
|
1910 }
|
|
1911
|
|
1912
|
|
1913 //---- simple getters and setters
|
|
1914
|
|
1915 /*
|
|
1916 * @see dwtx.jface.text.ITextViewer#getTextWidget()
|
|
1917 */
|
|
1918 public StyledText getTextWidget() {
|
|
1919 return fTextWidget;
|
|
1920 }
|
|
1921
|
|
1922 /**
|
|
1923 * The delay in milliseconds before an empty selection
|
|
1924 * changed event is sent by the cursor listener.
|
|
1925 * <p>
|
|
1926 * Note: The return value is used to initialize the cursor
|
|
1927 * listener. To return a non-constant value has no effect.</p>
|
|
1928 * <p>
|
|
1929 * The same value (<code>500</code>) is used in <code>OpenStrategy.TIME</code>.</p>
|
|
1930 *
|
|
1931 * @return delay in milliseconds
|
|
1932 * @see dwtx.jface.util.OpenStrategy
|
|
1933 * @since 3.0
|
|
1934 */
|
|
1935 protected int getEmptySelectionChangedEventDelay() {
|
|
1936 return 500;
|
|
1937 }
|
|
1938
|
|
1939 /**
|
|
1940 * {@inheritDoc}
|
|
1941 * @deprecated since 3.1, use
|
|
1942 * {@link ITextViewerExtension2#prependAutoEditStrategy(IAutoEditStrategy, String)} and
|
|
1943 * {@link ITextViewerExtension2#removeAutoEditStrategy(IAutoEditStrategy, String)} instead
|
|
1944 */
|
|
1945 public void setAutoIndentStrategy(IAutoIndentStrategy strategy, String contentType) {
|
|
1946 setAutoEditStrategies(new IAutoEditStrategy[] { strategy }, contentType);
|
|
1947 }
|
|
1948
|
|
1949 /**
|
|
1950 * Sets the given edit strategy as the only strategy for the given content type.
|
|
1951 *
|
|
1952 * @param strategies the auto edit strategies
|
|
1953 * @param contentType the content type
|
|
1954 * @since 3.1
|
|
1955 */
|
|
1956 protected final void setAutoEditStrategies(IAutoEditStrategy[] strategies, String contentType) {
|
|
1957 if (fAutoIndentStrategies is null)
|
|
1958 fAutoIndentStrategies= new HashMap();
|
|
1959
|
|
1960 List autoEditStrategies= (List) fAutoIndentStrategies.get(contentType);
|
|
1961
|
|
1962 if (strategies is null) {
|
|
1963 if (autoEditStrategies is null)
|
|
1964 return;
|
|
1965
|
|
1966 fAutoIndentStrategies.put(contentType, null);
|
|
1967
|
|
1968 } else {
|
|
1969 if (autoEditStrategies is null) {
|
|
1970 autoEditStrategies= new ArrayList();
|
|
1971 fAutoIndentStrategies.put(contentType, autoEditStrategies);
|
|
1972 }
|
|
1973
|
|
1974 autoEditStrategies.clear();
|
|
1975 autoEditStrategies.addAll(Arrays.asList(strategies));
|
|
1976 }
|
|
1977 }
|
|
1978
|
|
1979 /*
|
|
1980 * @see dwtx.jface.text.ITextViewerExtension2#prependAutoEditStrategy(dwtx.jface.text.IAutoEditStrategy, java.lang.String)
|
|
1981 * @since 2.1
|
|
1982 */
|
|
1983 public void prependAutoEditStrategy(IAutoEditStrategy strategy, String contentType) {
|
|
1984
|
|
1985 if (strategy is null || contentType is null)
|
|
1986 throw new IllegalArgumentException();
|
|
1987
|
|
1988 if (fAutoIndentStrategies is null)
|
|
1989 fAutoIndentStrategies= new HashMap();
|
|
1990
|
|
1991 List autoEditStrategies= (List) fAutoIndentStrategies.get(contentType);
|
|
1992 if (autoEditStrategies is null) {
|
|
1993 autoEditStrategies= new ArrayList();
|
|
1994 fAutoIndentStrategies.put(contentType, autoEditStrategies);
|
|
1995 }
|
|
1996
|
|
1997 autoEditStrategies.add(0, strategy);
|
|
1998 }
|
|
1999
|
|
2000 /*
|
|
2001 * @see dwtx.jface.text.ITextViewerExtension2#removeAutoEditStrategy(dwtx.jface.text.IAutoEditStrategy, java.lang.String)
|
|
2002 * @since 2.1
|
|
2003 */
|
|
2004 public void removeAutoEditStrategy(IAutoEditStrategy strategy, String contentType) {
|
|
2005 if (fAutoIndentStrategies is null)
|
|
2006 return;
|
|
2007
|
|
2008 List autoEditStrategies= (List) fAutoIndentStrategies.get(contentType);
|
|
2009 if (autoEditStrategies is null)
|
|
2010 return;
|
|
2011
|
|
2012 for (final Iterator iterator= autoEditStrategies.iterator(); iterator.hasNext(); ) {
|
|
2013 if (iterator.next().equals(strategy)) {
|
|
2014 iterator.remove();
|
|
2015 break;
|
|
2016 }
|
|
2017 }
|
|
2018
|
|
2019 if (autoEditStrategies.isEmpty())
|
|
2020 fAutoIndentStrategies.put(contentType, null);
|
|
2021 }
|
|
2022
|
|
2023 /*
|
|
2024 * @see ITextViewer#setEventConsumer(IEventConsumer)
|
|
2025 */
|
|
2026 public void setEventConsumer(IEventConsumer consumer) {
|
|
2027 fEventConsumer= consumer;
|
|
2028 }
|
|
2029
|
|
2030 /*
|
|
2031 * @see ITextViewer#setIndentPrefixes(String[], String)
|
|
2032 */
|
|
2033 public void setIndentPrefixes(String[] indentPrefixes, String contentType) {
|
|
2034
|
|
2035 int i= -1;
|
|
2036 bool ok= (indentPrefixes !is null);
|
|
2037 while (ok && ++i < indentPrefixes.length)
|
|
2038 ok= (indentPrefixes[i] !is null);
|
|
2039
|
|
2040 if (ok) {
|
|
2041
|
|
2042 if (fIndentChars is null)
|
|
2043 fIndentChars= new HashMap();
|
|
2044
|
|
2045 fIndentChars.put(contentType, indentPrefixes);
|
|
2046
|
|
2047 } else if (fIndentChars !is null)
|
|
2048 fIndentChars.remove(contentType);
|
|
2049 }
|
|
2050
|
|
2051 /*
|
|
2052 * @see ITextViewer#getTopInset()
|
|
2053 */
|
|
2054 public int getTopInset() {
|
|
2055 return fTopInset;
|
|
2056 }
|
|
2057
|
|
2058 /*
|
|
2059 * @see ITextViewer#isEditable()
|
|
2060 */
|
|
2061 public bool isEditable() {
|
|
2062 if (fTextWidget is null)
|
|
2063 return false;
|
|
2064 return fTextWidget.getEditable();
|
|
2065 }
|
|
2066
|
|
2067 /*
|
|
2068 * @see ITextViewer#setEditable(bool)
|
|
2069 */
|
|
2070 public void setEditable(bool editable) {
|
|
2071 if (fTextWidget !is null)
|
|
2072 fTextWidget.setEditable(editable);
|
|
2073 }
|
|
2074
|
|
2075 /*
|
|
2076 * @see ITextViewer#setDefaultPrefixes
|
|
2077 * @since 2.0
|
|
2078 */
|
|
2079 public void setDefaultPrefixes(String[] defaultPrefixes, String contentType) {
|
|
2080
|
|
2081 if (defaultPrefixes !is null && defaultPrefixes.length > 0) {
|
|
2082 if (fDefaultPrefixChars is null)
|
|
2083 fDefaultPrefixChars= new HashMap();
|
|
2084 fDefaultPrefixChars.put(contentType, defaultPrefixes);
|
|
2085 } else if (fDefaultPrefixChars !is null)
|
|
2086 fDefaultPrefixChars.remove(contentType);
|
|
2087 }
|
|
2088
|
|
2089 /*
|
|
2090 * @see ITextViewer#setUndoManager(IUndoManager)
|
|
2091 */
|
|
2092 public void setUndoManager(IUndoManager undoManager) {
|
|
2093 fUndoManager= undoManager;
|
|
2094 }
|
|
2095
|
|
2096 /*
|
|
2097 * @see ITextViewerExtension6#getUndoManager()
|
|
2098 * @since 3.1
|
|
2099 */
|
|
2100 public IUndoManager getUndoManager() {
|
|
2101 return fUndoManager;
|
|
2102 }
|
|
2103
|
|
2104 /*
|
|
2105 * @see ITextViewer#setTextHover(ITextHover, String)
|
|
2106 */
|
|
2107 public void setTextHover(ITextHover hover, String contentType) {
|
|
2108 setTextHover(hover, contentType, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
|
|
2109 }
|
|
2110
|
|
2111 /*
|
|
2112 * @see ITextViewerExtension2#setTextHover(ITextHover, String, int)
|
|
2113 * @since 2.1
|
|
2114 */
|
|
2115 public void setTextHover(ITextHover hover, String contentType, int stateMask) {
|
|
2116 TextHoverKey key= new TextHoverKey(contentType, stateMask);
|
|
2117 if (hover !is null) {
|
|
2118 if (fTextHovers is null) {
|
|
2119 fTextHovers= new HashMap();
|
|
2120 }
|
|
2121 fTextHovers.put(key, hover);
|
|
2122 } else if (fTextHovers !is null)
|
|
2123 fTextHovers.remove(key);
|
|
2124
|
|
2125 ensureHoverControlManagerInstalled();
|
|
2126 }
|
|
2127
|
|
2128 /*
|
|
2129 * @see ITextViewerExtension2#removeTextHovers(String)
|
|
2130 * @since 2.1
|
|
2131 */
|
|
2132 public void removeTextHovers(String contentType) {
|
|
2133 if (fTextHovers is null)
|
|
2134 return;
|
|
2135
|
|
2136 Iterator iter= new HashSet(fTextHovers.keySet()).iterator();
|
|
2137 while (iter.hasNext()) {
|
|
2138 TextHoverKey key= (TextHoverKey)iter.next();
|
|
2139 if (key.fContentType.equals(contentType))
|
|
2140 fTextHovers.remove(key);
|
|
2141 }
|
|
2142 }
|
|
2143
|
|
2144 /**
|
|
2145 * Returns the text hover for a given offset.
|
|
2146 *
|
|
2147 * @param offset the offset for which to return the text hover
|
|
2148 * @return the text hover for the given offset
|
|
2149 */
|
|
2150 protected ITextHover getTextHover(int offset) {
|
|
2151 return getTextHover(offset, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
|
|
2152 }
|
|
2153
|
|
2154 /**
|
|
2155 * Returns the text hover for a given offset and a given state mask.
|
|
2156 *
|
|
2157 * @param offset the offset for which to return the text hover
|
|
2158 * @param stateMask the DWT event state mask
|
|
2159 * @return the text hover for the given offset and state mask
|
|
2160 * @since 2.1
|
|
2161 */
|
|
2162 protected ITextHover getTextHover(int offset, int stateMask) {
|
|
2163 if (fTextHovers is null)
|
|
2164 return null;
|
|
2165
|
|
2166 IDocument document= getDocument();
|
|
2167 if (document is null)
|
|
2168 return null;
|
|
2169
|
|
2170 try {
|
|
2171 TextHoverKey key= new TextHoverKey(TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true), stateMask);
|
|
2172 Object textHover= fTextHovers.get(key);
|
|
2173 if (textHover is null) {
|
|
2174 // Use default text hover
|
|
2175 key.setStateMask(ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
|
|
2176 textHover= fTextHovers.get(key);
|
|
2177 }
|
|
2178 return (ITextHover) textHover;
|
|
2179 } catch (BadLocationException x) {
|
|
2180 if (TRACE_ERRORS)
|
|
2181 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.selectContentTypePlugin")); //$NON-NLS-1$
|
|
2182 }
|
|
2183 return null;
|
|
2184 }
|
|
2185
|
|
2186 /**
|
|
2187 * Returns the text hovering controller of this viewer.
|
|
2188 *
|
|
2189 * @return the text hovering controller of this viewer
|
|
2190 * @since 2.0
|
|
2191 */
|
|
2192 protected AbstractInformationControlManager getTextHoveringController() {
|
|
2193 return fTextHoverManager;
|
|
2194 }
|
|
2195
|
|
2196 /**
|
|
2197 * Sets the creator for the hover controls.
|
|
2198 *
|
|
2199 * @param creator the hover control creator
|
|
2200 * @since 2.0
|
|
2201 */
|
|
2202 public void setHoverControlCreator(IInformationControlCreator creator) {
|
|
2203 fHoverControlCreator= creator;
|
|
2204 }
|
|
2205
|
|
2206 /**
|
|
2207 * {@inheritDoc}
|
|
2208 *
|
|
2209 * @since 3.4
|
|
2210 */
|
|
2211 public void setHoverEnrichMode(ITextViewerExtension8.EnrichMode mode) {
|
|
2212 if (fTextHoverManager is null)
|
|
2213 return;
|
|
2214 fTextHoverManager.setHoverEnrichMode(mode);
|
|
2215 }
|
|
2216
|
|
2217 /*
|
|
2218 * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
|
|
2219 * @since 2.0
|
|
2220 */
|
|
2221 public bool requestWidgetToken(IWidgetTokenKeeper requester) {
|
|
2222 if (fTextWidget !is null) {
|
|
2223 if (fWidgetTokenKeeper !is null) {
|
|
2224 if (fWidgetTokenKeeper is requester)
|
|
2225 return true;
|
|
2226 if (fWidgetTokenKeeper.requestWidgetToken(this)) {
|
|
2227 fWidgetTokenKeeper= requester;
|
|
2228 return true;
|
|
2229 }
|
|
2230 } else {
|
|
2231 fWidgetTokenKeeper= requester;
|
|
2232 return true;
|
|
2233 }
|
|
2234 }
|
|
2235 return false;
|
|
2236 }
|
|
2237
|
|
2238 /*
|
|
2239 * @see dwtx.jface.text.IWidgetTokenOwnerExtension#requestWidgetToken(dwtx.jface.text.IWidgetTokenKeeper, int)
|
|
2240 * @since 3.0
|
|
2241 */
|
|
2242 public bool requestWidgetToken(IWidgetTokenKeeper requester, int priority) {
|
|
2243 if (fTextWidget !is null) {
|
|
2244 if (fWidgetTokenKeeper !is null) {
|
|
2245
|
|
2246 if (fWidgetTokenKeeper is requester)
|
|
2247 return true;
|
|
2248
|
|
2249 bool accepted= false;
|
|
2250 if (fWidgetTokenKeeper instanceof IWidgetTokenKeeperExtension) {
|
|
2251 IWidgetTokenKeeperExtension extension= (IWidgetTokenKeeperExtension) fWidgetTokenKeeper;
|
|
2252 accepted= extension.requestWidgetToken(this, priority);
|
|
2253 } else {
|
|
2254 accepted= fWidgetTokenKeeper.requestWidgetToken(this);
|
|
2255 }
|
|
2256
|
|
2257 if (accepted) {
|
|
2258 fWidgetTokenKeeper= requester;
|
|
2259 return true;
|
|
2260 }
|
|
2261
|
|
2262 } else {
|
|
2263 fWidgetTokenKeeper= requester;
|
|
2264 return true;
|
|
2265 }
|
|
2266 }
|
|
2267 return false;
|
|
2268 }
|
|
2269
|
|
2270 /*
|
|
2271 * @see IWidgetTokenOwner#releaseWidgetToken(IWidgetTokenKeeper)
|
|
2272 * @since 2.0
|
|
2273 */
|
|
2274 public void releaseWidgetToken(IWidgetTokenKeeper tokenKeeper) {
|
|
2275 if (fWidgetTokenKeeper is tokenKeeper)
|
|
2276 fWidgetTokenKeeper= null;
|
|
2277 }
|
|
2278
|
|
2279
|
|
2280 //---- Selection
|
|
2281
|
|
2282 /*
|
|
2283 * @see ITextViewer#getSelectedRange()
|
|
2284 */
|
|
2285 public Point getSelectedRange() {
|
|
2286
|
|
2287 if (!redraws() && fViewerState !is null)
|
|
2288 return fViewerState.getSelection();
|
|
2289
|
|
2290 if (fTextWidget !is null) {
|
|
2291 Point p= fTextWidget.getSelectionRange();
|
|
2292 p= widgetSelection2ModelSelection(p);
|
|
2293 if (p !is null)
|
|
2294 return p;
|
|
2295 }
|
|
2296
|
|
2297 return new Point(-1, -1);
|
|
2298 }
|
|
2299
|
|
2300 /*
|
|
2301 * @see ITextViewer#setSelectedRange(int, int)
|
|
2302 */
|
|
2303 public void setSelectedRange(int selectionOffset, int selectionLength) {
|
|
2304
|
|
2305 if (!redraws()) {
|
|
2306 if (fViewerState !is null)
|
|
2307 fViewerState.updateSelection(selectionOffset, selectionLength);
|
|
2308 return;
|
|
2309 }
|
|
2310
|
|
2311 if (fTextWidget is null)
|
|
2312 return;
|
|
2313
|
|
2314 IRegion widgetSelection= modelRange2ClosestWidgetRange(new Region(selectionOffset, selectionLength));
|
|
2315 if (widgetSelection !is null) {
|
|
2316
|
|
2317 int[] selectionRange= new int[] { widgetSelection.getOffset(), widgetSelection.getLength() };
|
|
2318 validateSelectionRange(selectionRange);
|
|
2319 if (selectionRange[0] >= 0) {
|
|
2320 fTextWidget.setSelectionRange(selectionRange[0], selectionRange[1]);
|
|
2321 selectionChanged(selectionRange[0], selectionRange[1]);
|
|
2322 }
|
|
2323 }
|
|
2324 }
|
|
2325
|
|
2326 /**
|
|
2327 * Validates and adapts the given selection range if it is not a valid
|
|
2328 * widget selection. The widget selection is invalid if it starts or ends
|
|
2329 * inside a multi-character line delimiter. If so, the selection is adapted to
|
|
2330 * start <b>after</b> the divided line delimiter and to end <b>before</b>
|
|
2331 * the divided line delimiter. The parameter passed in is changed in-place
|
|
2332 * when being adapted. An adaptation to <code>[-1, -1]</code> indicates
|
|
2333 * that the selection range could not be validated.
|
|
2334 * Subclasses may reimplement this method.
|
|
2335 *
|
|
2336 * @param selectionRange selectionRange[0] is the offset, selectionRange[1]
|
|
2337 * the length of the selection to validate.
|
|
2338 * @since 2.0
|
|
2339 */
|
|
2340 protected void validateSelectionRange(int[] selectionRange) {
|
|
2341
|
|
2342 IDocument document= getVisibleDocument();
|
|
2343 if (document is null) {
|
|
2344 selectionRange[0]= -1;
|
|
2345 selectionRange[1]= -1;
|
|
2346 return;
|
|
2347 }
|
|
2348
|
|
2349 int documentLength= document.getLength();
|
|
2350 int offset= selectionRange[0];
|
|
2351 int length= selectionRange[1];
|
|
2352
|
|
2353 if (length < 0) {
|
|
2354 length= - length;
|
|
2355 offset -= length;
|
|
2356 }
|
|
2357
|
|
2358 if (offset <0)
|
|
2359 offset= 0;
|
|
2360
|
|
2361 if (offset > documentLength)
|
|
2362 offset= documentLength;
|
|
2363
|
|
2364 int delta= (offset + length) - documentLength;
|
|
2365 if (delta > 0)
|
|
2366 length -= delta;
|
|
2367
|
|
2368 try {
|
|
2369
|
|
2370 int lineNumber= document.getLineOfOffset(offset);
|
|
2371 IRegion lineInformation= document.getLineInformation(lineNumber);
|
|
2372
|
|
2373 int lineEnd= lineInformation.getOffset() + lineInformation.getLength();
|
|
2374 delta= offset - lineEnd;
|
|
2375 if (delta > 0) {
|
|
2376 // in the middle of a multi-character line delimiter
|
|
2377 offset= lineEnd;
|
|
2378 String delimiter= document.getLineDelimiter(lineNumber);
|
|
2379 if (delimiter !is null)
|
|
2380 offset += delimiter.length();
|
|
2381 }
|
|
2382
|
|
2383 int end= offset + length;
|
|
2384 lineInformation= document.getLineInformationOfOffset(end);
|
|
2385 lineEnd= lineInformation.getOffset() + lineInformation.getLength();
|
|
2386 delta= end - lineEnd;
|
|
2387 if (delta > 0) {
|
|
2388 // in the middle of a multi-character line delimiter
|
|
2389 length -= delta;
|
|
2390 }
|
|
2391
|
|
2392 } catch (BadLocationException x) {
|
|
2393 selectionRange[0]= -1;
|
|
2394 selectionRange[1]= -1;
|
|
2395 return;
|
|
2396 }
|
|
2397
|
|
2398 if (selectionRange[1] < 0) {
|
|
2399 selectionRange[0]= offset + length;
|
|
2400 selectionRange[1]= -length;
|
|
2401 } else {
|
|
2402 selectionRange[0]= offset;
|
|
2403 selectionRange[1]= length;
|
|
2404 }
|
|
2405 }
|
|
2406
|
|
2407 /*
|
|
2408 * @see Viewer#setSelection(ISelection)
|
|
2409 */
|
|
2410 public void setSelection(ISelection selection, bool reveal) {
|
|
2411 if (selection instanceof ITextSelection) {
|
|
2412 ITextSelection s= (ITextSelection) selection;
|
|
2413 setSelectedRange(s.getOffset(), s.getLength());
|
|
2414 if (reveal)
|
|
2415 revealRange(s.getOffset(), s.getLength());
|
|
2416 }
|
|
2417 }
|
|
2418
|
|
2419 /*
|
|
2420 * @see Viewer#getSelection()
|
|
2421 */
|
|
2422 public ISelection getSelection() {
|
|
2423 Point p= getSelectedRange();
|
|
2424 if (p.x is -1 || p.y is -1)
|
|
2425 return TextSelection.emptySelection();
|
|
2426
|
|
2427 return new TextSelection(getDocument(), p.x, p.y);
|
|
2428 }
|
|
2429
|
|
2430 /*
|
|
2431 * @see ITextViewer#getSelectionProvider()
|
|
2432 */
|
|
2433 public ISelectionProvider getSelectionProvider() {
|
|
2434 return this;
|
|
2435 }
|
|
2436
|
|
2437 /*
|
|
2438 * @see dwtx.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(dwtx.jface.viewers.ISelectionChangedListener)
|
|
2439 * @since 3.0
|
|
2440 */
|
|
2441 public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
|
|
2442
|
|
2443 Assert.isNotNull(listener);
|
|
2444
|
|
2445 if (fPostSelectionChangedListeners is null)
|
|
2446 fPostSelectionChangedListeners= new ArrayList();
|
|
2447
|
|
2448 if (!fPostSelectionChangedListeners.contains(listener))
|
|
2449 fPostSelectionChangedListeners.add(listener);
|
|
2450 }
|
|
2451
|
|
2452 /*
|
|
2453 * @see dwtx.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(dwtx.jface.viewers.ISelectionChangedListener)
|
|
2454 * @since 3.0
|
|
2455 */
|
|
2456 public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
|
|
2457
|
|
2458 Assert.isNotNull(listener);
|
|
2459
|
|
2460 if (fPostSelectionChangedListeners !is null) {
|
|
2461 fPostSelectionChangedListeners.remove(listener);
|
|
2462 if (fPostSelectionChangedListeners.size() is 0)
|
|
2463 fPostSelectionChangedListeners= null;
|
|
2464 }
|
|
2465 }
|
|
2466
|
|
2467 /**
|
|
2468 * Get the text widget's display.
|
|
2469 *
|
|
2470 * @return the display or <code>null</code> if the display cannot be retrieved or if the display is disposed
|
|
2471 * @since 3.0
|
|
2472 */
|
|
2473 private Display getDisplay() {
|
|
2474 if (fTextWidget is null || fTextWidget.isDisposed())
|
|
2475 return null;
|
|
2476
|
|
2477 Display display= fTextWidget.getDisplay();
|
|
2478 if (display !is null && display.isDisposed())
|
|
2479 return null;
|
|
2480
|
|
2481 return display;
|
|
2482 }
|
|
2483
|
|
2484 /**
|
|
2485 * Starts a timer to send out a post selection changed event.
|
|
2486 *
|
|
2487 * @param fireEqualSelection <code>true</code> iff the event must be fired if the selection does not change
|
|
2488 * @since 3.0
|
|
2489 */
|
|
2490 private void queuePostSelectionChanged(final bool fireEqualSelection) {
|
|
2491 Display display= getDisplay();
|
|
2492 if (display is null)
|
|
2493 return;
|
|
2494
|
|
2495 fNumberOfPostSelectionChangedEvents[0]++;
|
|
2496 display.timerExec(getEmptySelectionChangedEventDelay(), new Runnable() {
|
|
2497 final int id= fNumberOfPostSelectionChangedEvents[0];
|
|
2498 public void run() {
|
|
2499 if (id is fNumberOfPostSelectionChangedEvents[0]) {
|
|
2500 // Check again because this is executed after the delay
|
|
2501 if (getDisplay() !is null) {
|
|
2502 Point selection= fTextWidget.getSelectionRange();
|
|
2503 if (selection !is null) {
|
|
2504 IRegion r= widgetRange2ModelRange(new Region(selection.x, selection.y));
|
|
2505 if (fireEqualSelection || (r !is null && !r.equals(fLastSentPostSelectionChange)) || r is null) {
|
|
2506 fLastSentPostSelectionChange= r;
|
|
2507 firePostSelectionChanged(selection.x, selection.y);
|
|
2508 }
|
|
2509 }
|
|
2510 }
|
|
2511 }
|
|
2512 }
|
|
2513 });
|
|
2514 }
|
|
2515
|
|
2516 /**
|
|
2517 * Sends out a text selection changed event to all registered post selection changed listeners.
|
|
2518 *
|
|
2519 * @param offset the offset of the newly selected range in the visible document
|
|
2520 * @param length the length of the newly selected range in the visible document
|
|
2521 * @since 3.0
|
|
2522 */
|
|
2523 protected void firePostSelectionChanged(int offset, int length) {
|
|
2524 if (redraws()) {
|
|
2525 IRegion r= widgetRange2ModelRange(new Region(offset, length));
|
|
2526 ISelection selection= r !is null ? new TextSelection(getDocument(), r.getOffset(), r.getLength()) : TextSelection.emptySelection();
|
|
2527 SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
|
|
2528 firePostSelectionChanged(event);
|
|
2529 }
|
|
2530 }
|
|
2531
|
|
2532 /**
|
|
2533 * Sends out a text selection changed event to all registered listeners and
|
|
2534 * registers the selection changed event to be sent out to all post selection
|
|
2535 * listeners.
|
|
2536 *
|
|
2537 * @param offset the offset of the newly selected range in the visible document
|
|
2538 * @param length the length of the newly selected range in the visible document
|
|
2539 */
|
|
2540 protected void selectionChanged(int offset, int length) {
|
|
2541 queuePostSelectionChanged(true);
|
|
2542 fireSelectionChanged(offset, length);
|
|
2543 }
|
|
2544
|
|
2545 /**
|
|
2546 * Sends out a text selection changed event to all registered listeners.
|
|
2547 *
|
|
2548 * @param offset the offset of the newly selected range in the visible document
|
|
2549 * @param length the length of the newly selected range in the visible document
|
|
2550 * @since 3.0
|
|
2551 */
|
|
2552 protected void fireSelectionChanged(int offset, int length) {
|
|
2553 if (redraws()) {
|
|
2554 IRegion r= widgetRange2ModelRange(new Region(offset, length));
|
|
2555 if ((r !is null && !r.equals(fLastSentSelectionChange)) || r is null) {
|
|
2556 fLastSentSelectionChange= r;
|
|
2557 ISelection selection= r !is null ? new TextSelection(getDocument(), r.getOffset(), r.getLength()) : TextSelection.emptySelection();
|
|
2558 SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
|
|
2559 fireSelectionChanged(event);
|
|
2560 }
|
|
2561 }
|
|
2562 }
|
|
2563
|
|
2564 /**
|
|
2565 * Sends the given event to all registered post selection changed listeners.
|
|
2566 *
|
|
2567 * @param event the selection event
|
|
2568 * @since 3.0
|
|
2569 */
|
|
2570 private void firePostSelectionChanged(SelectionChangedEvent event) {
|
|
2571 List listeners= fPostSelectionChangedListeners;
|
|
2572 if (listeners !is null) {
|
|
2573 listeners= new ArrayList(listeners);
|
|
2574 for (int i= 0; i < listeners.size(); i++) {
|
|
2575 ISelectionChangedListener l= (ISelectionChangedListener) listeners.get(i);
|
|
2576 l.selectionChanged(event);
|
|
2577 }
|
|
2578 }
|
|
2579 }
|
|
2580
|
|
2581 /**
|
|
2582 * Sends out a mark selection changed event to all registered listeners.
|
|
2583 *
|
|
2584 * @param offset the offset of the mark selection in the visible document, the offset is <code>-1</code> if the mark was cleared
|
|
2585 * @param length the length of the mark selection, may be negative if the caret is before the mark.
|
|
2586 * @since 2.0
|
|
2587 */
|
|
2588 protected void markChanged(int offset, int length) {
|
|
2589 if (redraws()) {
|
|
2590
|
|
2591 if (offset !is -1) {
|
|
2592 IRegion r= widgetRange2ModelRange(new Region(offset, length));
|
|
2593 offset= r.getOffset();
|
|
2594 length= r.getLength();
|
|
2595 }
|
|
2596
|
|
2597 ISelection selection= new MarkSelection(getDocument(), offset, length);
|
|
2598 SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
|
|
2599 fireSelectionChanged(event);
|
|
2600 }
|
|
2601 }
|
|
2602
|
|
2603
|
|
2604 //---- Text listeners
|
|
2605
|
|
2606 /*
|
|
2607 * @see ITextViewer#addTextListener(ITextListener)
|
|
2608 */
|
|
2609 public void addTextListener(ITextListener listener) {
|
|
2610
|
|
2611 Assert.isNotNull(listener);
|
|
2612
|
|
2613 if (fTextListeners is null)
|
|
2614 fTextListeners= new ArrayList();
|
|
2615
|
|
2616 if (!fTextListeners.contains(listener))
|
|
2617 fTextListeners.add(listener);
|
|
2618 }
|
|
2619
|
|
2620 /*
|
|
2621 * @see ITextViewer#removeTextListener(ITextListener)
|
|
2622 */
|
|
2623 public void removeTextListener(ITextListener listener) {
|
|
2624
|
|
2625 Assert.isNotNull(listener);
|
|
2626
|
|
2627 if (fTextListeners !is null) {
|
|
2628 fTextListeners.remove(listener);
|
|
2629 if (fTextListeners.size() is 0)
|
|
2630 fTextListeners= null;
|
|
2631 }
|
|
2632 }
|
|
2633
|
|
2634 /**
|
|
2635 * Informs all registered text listeners about the change specified by the
|
|
2636 * widget command. This method does not use a robust iterator.
|
|
2637 *
|
|
2638 * @param cmd the widget command translated into a text event sent to all text listeners
|
|
2639 */
|
|
2640 protected void updateTextListeners(WidgetCommand cmd) {
|
|
2641 List textListeners= fTextListeners;
|
|
2642 if (textListeners !is null) {
|
|
2643 textListeners= new ArrayList(textListeners);
|
|
2644 DocumentEvent event= cmd.event;
|
|
2645 if (event instanceof SlaveDocumentEvent)
|
|
2646 event= ((SlaveDocumentEvent) event).getMasterEvent();
|
|
2647
|
|
2648 TextEvent e= new TextEvent(cmd.start, cmd.length, cmd.text, cmd.preservedText, event, redraws());
|
|
2649 for (int i= 0; i < textListeners.size(); i++) {
|
|
2650 ITextListener l= (ITextListener) textListeners.get(i);
|
|
2651 l.textChanged(e);
|
|
2652 }
|
|
2653 }
|
|
2654 }
|
|
2655
|
|
2656 //---- Text input listeners
|
|
2657
|
|
2658 /*
|
|
2659 * @see ITextViewer#addTextInputListener(ITextInputListener)
|
|
2660 */
|
|
2661 public void addTextInputListener(ITextInputListener listener) {
|
|
2662
|
|
2663 Assert.isNotNull(listener);
|
|
2664
|
|
2665 if (fTextInputListeners is null)
|
|
2666 fTextInputListeners= new ArrayList();
|
|
2667
|
|
2668 if (!fTextInputListeners.contains(listener))
|
|
2669 fTextInputListeners.add(listener);
|
|
2670 }
|
|
2671
|
|
2672 /*
|
|
2673 * @see ITextViewer#removeTextInputListener(ITextInputListener)
|
|
2674 */
|
|
2675 public void removeTextInputListener(ITextInputListener listener) {
|
|
2676
|
|
2677 Assert.isNotNull(listener);
|
|
2678
|
|
2679 if (fTextInputListeners !is null) {
|
|
2680 fTextInputListeners.remove(listener);
|
|
2681 if (fTextInputListeners.size() is 0)
|
|
2682 fTextInputListeners= null;
|
|
2683 }
|
|
2684 }
|
|
2685
|
|
2686 /**
|
|
2687 * Informs all registered text input listeners about the forthcoming input change,
|
|
2688 * This method does not use a robust iterator.
|
|
2689 *
|
|
2690 * @param oldInput the old input document
|
|
2691 * @param newInput the new input document
|
|
2692 */
|
|
2693 protected void fireInputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
|
|
2694 List listener= fTextInputListeners;
|
|
2695 if (listener !is null) {
|
|
2696 for (int i= 0; i < listener.size(); i++) {
|
|
2697 ITextInputListener l= (ITextInputListener) listener.get(i);
|
|
2698 l.inputDocumentAboutToBeChanged(oldInput, newInput);
|
|
2699 }
|
|
2700 }
|
|
2701 }
|
|
2702
|
|
2703 /**
|
|
2704 * Informs all registered text input listeners about the successful input change,
|
|
2705 * This method does not use a robust iterator.
|
|
2706 *
|
|
2707 * @param oldInput the old input document
|
|
2708 * @param newInput the new input document
|
|
2709 */
|
|
2710 protected void fireInputDocumentChanged(IDocument oldInput, IDocument newInput) {
|
|
2711 List listener= fTextInputListeners;
|
|
2712 if (listener !is null) {
|
|
2713 for (int i= 0; i < listener.size(); i++) {
|
|
2714 ITextInputListener l= (ITextInputListener) listener.get(i);
|
|
2715 l.inputDocumentChanged(oldInput, newInput);
|
|
2716 }
|
|
2717 }
|
|
2718 }
|
|
2719
|
|
2720 //---- Document
|
|
2721
|
|
2722 /*
|
|
2723 * @see Viewer#getInput()
|
|
2724 */
|
|
2725 public Object getInput() {
|
|
2726 return getDocument();
|
|
2727 }
|
|
2728
|
|
2729 /*
|
|
2730 * @see ITextViewer#getDocument()
|
|
2731 */
|
|
2732 public IDocument getDocument() {
|
|
2733 return fDocument;
|
|
2734 }
|
|
2735
|
|
2736 /*
|
|
2737 * @see Viewer#setInput(Object)
|
|
2738 */
|
|
2739 public void setInput(Object input) {
|
|
2740
|
|
2741 IDocument document= null;
|
|
2742 if (input instanceof IDocument)
|
|
2743 document= (IDocument) input;
|
|
2744
|
|
2745 setDocument(document);
|
|
2746 }
|
|
2747
|
|
2748 /*
|
|
2749 * @see ITextViewer#setDocument(IDocument)
|
|
2750 */
|
|
2751 public void setDocument(IDocument document) {
|
|
2752
|
|
2753 fReplaceTextPresentation= true;
|
|
2754 fireInputDocumentAboutToBeChanged(fDocument, document);
|
|
2755
|
|
2756 IDocument oldDocument= fDocument;
|
|
2757 fDocument= document;
|
|
2758
|
|
2759 setVisibleDocument(fDocument);
|
|
2760
|
|
2761 resetPlugins();
|
|
2762 inputChanged(fDocument, oldDocument);
|
|
2763
|
|
2764 fireInputDocumentChanged(oldDocument, fDocument);
|
|
2765 fLastSentSelectionChange= null;
|
|
2766 fReplaceTextPresentation= false;
|
|
2767 }
|
|
2768
|
|
2769 /*
|
|
2770 * @see ITextViewer#setDocument(IDocument, int, int)
|
|
2771 */
|
|
2772 public void setDocument(IDocument document, int modelRangeOffset, int modelRangeLength) {
|
|
2773
|
|
2774 fReplaceTextPresentation= true;
|
|
2775 fireInputDocumentAboutToBeChanged(fDocument, document);
|
|
2776
|
|
2777 IDocument oldDocument= fDocument;
|
|
2778 fDocument= document;
|
|
2779
|
|
2780 try {
|
|
2781
|
|
2782 IDocument slaveDocument= createSlaveDocument(document);
|
|
2783 updateSlaveDocument(slaveDocument, modelRangeOffset, modelRangeLength);
|
|
2784 setVisibleDocument(slaveDocument);
|
|
2785
|
|
2786 } catch (BadLocationException x) {
|
|
2787 throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_1")); //$NON-NLS-1$
|
|
2788 }
|
|
2789
|
|
2790 resetPlugins();
|
|
2791 inputChanged(fDocument, oldDocument);
|
|
2792
|
|
2793 fireInputDocumentChanged(oldDocument, fDocument);
|
|
2794 fLastSentSelectionChange= null;
|
|
2795 fReplaceTextPresentation= false;
|
|
2796 }
|
|
2797
|
|
2798 /**
|
|
2799 * Creates a slave document for the given document if there is a slave document manager
|
|
2800 * associated with this viewer.
|
|
2801 *
|
|
2802 * @param document the master document
|
|
2803 * @return the newly created slave document
|
|
2804 * @since 2.1
|
|
2805 */
|
|
2806 protected IDocument createSlaveDocument(IDocument document) {
|
|
2807 ISlaveDocumentManager manager= getSlaveDocumentManager();
|
|
2808 if (manager !is null) {
|
|
2809 if (manager.isSlaveDocument(document))
|
|
2810 return document;
|
|
2811 return manager.createSlaveDocument(document);
|
|
2812 }
|
|
2813 return document;
|
|
2814 }
|
|
2815
|
|
2816 /**
|
|
2817 * Sets the given slave document to the specified range of its master document.
|
|
2818 *
|
|
2819 * @param visibleDocument the slave document
|
|
2820 * @param visibleRegionOffset the offset of the master document range
|
|
2821 * @param visibleRegionLength the length of the master document range
|
|
2822 * @return <code>true</code> if the slave has been adapted successfully
|
|
2823 * @throws BadLocationException in case the specified range is not valid in the master document
|
|
2824 * @since 2.1
|
|
2825 * @deprecated use <code>updateSlaveDocument</code> instead
|
|
2826 */
|
|
2827 protected bool updateVisibleDocument(IDocument visibleDocument, int visibleRegionOffset, int visibleRegionLength) throws BadLocationException {
|
|
2828 if (visibleDocument instanceof ChildDocument) {
|
|
2829 ChildDocument childDocument= (ChildDocument) visibleDocument;
|
|
2830
|
|
2831 IDocument document= childDocument.getParentDocument();
|
|
2832 int line= document.getLineOfOffset(visibleRegionOffset);
|
|
2833 int offset= document.getLineOffset(line);
|
|
2834 int length= (visibleRegionOffset - offset) + visibleRegionLength;
|
|
2835
|
|
2836 Position parentRange= childDocument.getParentDocumentRange();
|
|
2837 if (offset !is parentRange.getOffset() || length !is parentRange.getLength()) {
|
|
2838 childDocument.setParentDocumentRange(offset, length);
|
|
2839 return true;
|
|
2840 }
|
|
2841 }
|
|
2842 return false;
|
|
2843 }
|
|
2844
|
|
2845 /**
|
|
2846 * Updates the given slave document to show the specified range of its master document.
|
|
2847 *
|
|
2848 * @param slaveDocument the slave document
|
|
2849 * @param modelRangeOffset the offset of the master document range
|
|
2850 * @param modelRangeLength the length of the master document range
|
|
2851 * @return <code>true</code> if the slave has been adapted successfully
|
|
2852 * @throws BadLocationException in case the specified range is not valid in the master document
|
|
2853 * @since 3.0
|
|
2854 */
|
|
2855 protected bool updateSlaveDocument(IDocument slaveDocument, int modelRangeOffset, int modelRangeLength) throws BadLocationException {
|
|
2856 return updateVisibleDocument(slaveDocument, modelRangeOffset, modelRangeLength);
|
|
2857 }
|
|
2858
|
|
2859
|
|
2860
|
|
2861 //---- View ports
|
|
2862
|
|
2863 /**
|
|
2864 * Initializes all listeners and structures required to set up view port listeners.
|
|
2865 */
|
|
2866 private void initializeViewportUpdate() {
|
|
2867
|
|
2868 if (fViewportGuard !is null)
|
|
2869 return;
|
|
2870
|
|
2871 if (fTextWidget !is null) {
|
|
2872
|
|
2873 fViewportGuard= new ViewportGuard();
|
|
2874 fLastTopPixel= -1;
|
|
2875
|
|
2876 fTextWidget.addKeyListener(fViewportGuard);
|
|
2877 fTextWidget.addMouseListener(fViewportGuard);
|
|
2878
|
|
2879 fScroller= fTextWidget.getVerticalBar();
|
|
2880 if (fScroller !is null)
|
|
2881 fScroller.addSelectionListener(fViewportGuard);
|
|
2882 }
|
|
2883 }
|
|
2884
|
|
2885 /**
|
|
2886 * Removes all listeners and structures required to set up view port listeners.
|
|
2887 */
|
|
2888 private void removeViewPortUpdate() {
|
|
2889
|
|
2890 if (fTextWidget !is null) {
|
|
2891
|
|
2892 fTextWidget.removeKeyListener(fViewportGuard);
|
|
2893 fTextWidget.removeMouseListener(fViewportGuard);
|
|
2894
|
|
2895 if (fScroller !is null && !fScroller.isDisposed()) {
|
|
2896 fScroller.removeSelectionListener(fViewportGuard);
|
|
2897 fScroller= null;
|
|
2898 }
|
|
2899
|
|
2900 fViewportGuard= null;
|
|
2901 }
|
|
2902 }
|
|
2903
|
|
2904 /*
|
|
2905 * @see ITextViewer#addViewportListener(IViewportListener)
|
|
2906 */
|
|
2907 public void addViewportListener(IViewportListener listener) {
|
|
2908
|
|
2909 if (fViewportListeners is null) {
|
|
2910 fViewportListeners= new ArrayList();
|
|
2911 initializeViewportUpdate();
|
|
2912 }
|
|
2913
|
|
2914 if (!fViewportListeners.contains(listener))
|
|
2915 fViewportListeners.add(listener);
|
|
2916 }
|
|
2917
|
|
2918 /*
|
|
2919 * @see ITextViewer#removeViewportListener(IVewportListener)
|
|
2920 */
|
|
2921 public void removeViewportListener(IViewportListener listener) {
|
|
2922 if (fViewportListeners !is null)
|
|
2923 fViewportListeners.remove(listener);
|
|
2924 }
|
|
2925
|
|
2926 /**
|
|
2927 * Checks whether the view port changed and if so informs all registered
|
|
2928 * listeners about the change.
|
|
2929 *
|
|
2930 * @param origin describes under which circumstances this method has been called.
|
|
2931 *
|
|
2932 * @see IViewportListener
|
|
2933 */
|
|
2934 protected void updateViewportListeners(int origin) {
|
|
2935
|
|
2936 if (redraws()) {
|
|
2937 int topPixel= fTextWidget.getTopPixel();
|
|
2938 if (topPixel >= 0 && topPixel !is fLastTopPixel) {
|
|
2939 if (fViewportListeners !is null) {
|
|
2940 for (int i= 0; i < fViewportListeners.size(); i++) {
|
|
2941 IViewportListener l= (IViewportListener) fViewportListeners.get(i);
|
|
2942 l.viewportChanged(topPixel);
|
|
2943 }
|
|
2944 }
|
|
2945 fLastTopPixel= topPixel;
|
|
2946 }
|
|
2947 }
|
|
2948 }
|
|
2949
|
|
2950 //---- scrolling and revealing
|
|
2951
|
|
2952 /*
|
|
2953 * @see ITextViewer#getTopIndex()
|
|
2954 */
|
|
2955 public int getTopIndex() {
|
|
2956
|
|
2957 if (fTextWidget !is null) {
|
|
2958 int top= fTextWidget.getTopIndex();
|
|
2959 return widgetLine2ModelLine(top);
|
|
2960 }
|
|
2961
|
|
2962 return -1;
|
|
2963 }
|
|
2964
|
|
2965 /*
|
|
2966 * @see ITextViewer#setTopIndex(int)
|
|
2967 */
|
|
2968 public void setTopIndex(int index) {
|
|
2969
|
|
2970 if (fTextWidget !is null) {
|
|
2971
|
|
2972 int widgetLine= modelLine2WidgetLine(index);
|
|
2973 if (widgetLine is -1)
|
|
2974 widgetLine= getClosestWidgetLineForModelLine(index);
|
|
2975
|
|
2976 if (widgetLine > -1) {
|
|
2977 fTextWidget.setTopIndex(widgetLine);
|
|
2978 updateViewportListeners(INTERNAL);
|
|
2979 }
|
|
2980 }
|
|
2981 }
|
|
2982
|
|
2983 /**
|
|
2984 * Returns the number of lines that can fully fit into the viewport. This is computed by
|
|
2985 * dividing the widget's client area height by the widget's line height. The result is only
|
|
2986 * accurate if the widget does not use variable line heights - for that reason, clients should
|
|
2987 * not use this method any longer and use the client area height of the text widget to find out
|
|
2988 * how much content fits into it.
|
|
2989 *
|
|
2990 * @return the view port height in lines
|
|
2991 * @deprecated as of 3.2
|
|
2992 */
|
|
2993 protected int getVisibleLinesInViewport() {
|
|
2994 if (fTextWidget !is null) {
|
|
2995 Rectangle clArea= fTextWidget.getClientArea();
|
|
2996 if (!clArea.isEmpty())
|
|
2997 return clArea.height / fTextWidget.getLineHeight();
|
|
2998 }
|
|
2999 return -1;
|
|
3000 }
|
|
3001
|
|
3002 /*
|
|
3003 * @see ITextViewer#getBottomIndex()
|
|
3004 */
|
|
3005 public int getBottomIndex() {
|
|
3006
|
|
3007 if (fTextWidget is null)
|
|
3008 return -1;
|
|
3009
|
|
3010 int widgetBottom= JFaceTextUtil.getBottomIndex(fTextWidget);
|
|
3011 return widgetLine2ModelLine(widgetBottom);
|
|
3012 }
|
|
3013
|
|
3014 /*
|
|
3015 * @see ITextViewer#getTopIndexStartOffset()
|
|
3016 */
|
|
3017 public int getTopIndexStartOffset() {
|
|
3018
|
|
3019 if (fTextWidget !is null) {
|
|
3020 int top= fTextWidget.getTopIndex();
|
|
3021 try {
|
|
3022 top= getVisibleDocument().getLineOffset(top);
|
|
3023 return widgetOffset2ModelOffset(top);
|
|
3024 } catch (BadLocationException ex) {
|
|
3025 if (TRACE_ERRORS)
|
|
3026 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getTopIndexStartOffset")); //$NON-NLS-1$
|
|
3027 }
|
|
3028 }
|
|
3029
|
|
3030 return -1;
|
|
3031 }
|
|
3032
|
|
3033 /*
|
|
3034 * @see ITextViewer#getBottomIndexEndOffset()
|
|
3035 */
|
|
3036 public int getBottomIndexEndOffset() {
|
|
3037 try {
|
|
3038
|
|
3039 IRegion line= getDocument().getLineInformation(getBottomIndex());
|
|
3040 int bottomEndOffset= line.getOffset() + line.getLength() - 1;
|
|
3041
|
|
3042 IRegion coverage= getModelCoverage();
|
|
3043 if (coverage is null)
|
|
3044 return -1;
|
|
3045
|
|
3046 int coverageEndOffset= coverage.getOffset() + coverage.getLength() - 1;
|
|
3047 return Math.min(coverageEndOffset, bottomEndOffset);
|
|
3048
|
|
3049 } catch (BadLocationException ex) {
|
|
3050 if (TRACE_ERRORS)
|
|
3051 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getBottomIndexEndOffset")); //$NON-NLS-1$
|
|
3052 return getDocument().getLength() - 1;
|
|
3053 }
|
|
3054 }
|
|
3055
|
|
3056 /*
|
|
3057 * @see ITextViewer#revealRange(int, int)
|
|
3058 */
|
|
3059 public void revealRange(int start, int length) {
|
|
3060
|
|
3061 if (fTextWidget is null || !redraws())
|
|
3062 return;
|
|
3063
|
|
3064 IRegion modelRange= new Region(start, length);
|
|
3065 IRegion widgetRange= modelRange2ClosestWidgetRange(modelRange);
|
|
3066 if (widgetRange !is null) {
|
|
3067
|
|
3068 int[] range= new int[] { widgetRange.getOffset(), widgetRange.getLength() };
|
|
3069 validateSelectionRange(range);
|
|
3070 if (range[0] >= 0)
|
|
3071 internalRevealRange(range[0], range[0] + range[1]);
|
|
3072
|
|
3073 } else {
|
|
3074
|
|
3075 IRegion coverage= getModelCoverage();
|
|
3076 int cursor= (coverage is null || start < coverage.getOffset()) ? 0 : getVisibleDocument().getLength();
|
|
3077 internalRevealRange(cursor, cursor);
|
|
3078 }
|
|
3079 }
|
|
3080
|
|
3081 /**
|
|
3082 * Reveals the given range of the visible document.
|
|
3083 *
|
|
3084 * @param start the start offset of the range
|
|
3085 * @param end the end offset of the range
|
|
3086 */
|
|
3087 protected void internalRevealRange(int start, int end) {
|
|
3088
|
|
3089 try {
|
|
3090
|
|
3091 IDocument doc= getVisibleDocument();
|
|
3092
|
|
3093 int startLine= doc.getLineOfOffset(start);
|
|
3094 int endLine= doc.getLineOfOffset(end);
|
|
3095
|
|
3096 int top= fTextWidget.getTopIndex();
|
|
3097 if (top > -1) {
|
|
3098
|
|
3099 // scroll vertically
|
|
3100 int bottom= JFaceTextUtil.getBottomIndex(fTextWidget);
|
|
3101 int lines= bottom - top;
|
|
3102
|
|
3103 // if the widget is not scrollable as it is displaying the entire content
|
|
3104 // setTopIndex won't have any effect.
|
|
3105
|
|
3106 if (startLine >= top && startLine <= bottom && endLine >= top && endLine <= bottom ) {
|
|
3107
|
|
3108 // do not scroll at all as it is already visible
|
|
3109
|
|
3110 } else {
|
|
3111
|
|
3112 int delta= Math.max(0, lines - (endLine - startLine));
|
|
3113 fTextWidget.setTopIndex(startLine - delta/3);
|
|
3114 updateViewportListeners(INTERNAL);
|
|
3115 }
|
|
3116
|
|
3117 // scroll horizontally
|
|
3118
|
|
3119 if (endLine < startLine) {
|
|
3120 endLine += startLine;
|
|
3121 startLine= endLine - startLine;
|
|
3122 endLine -= startLine;
|
|
3123 }
|
|
3124
|
|
3125 int startPixel= -1;
|
|
3126 int endPixel= -1;
|
|
3127
|
|
3128 if (endLine > startLine) {
|
|
3129 // reveal the beginning of the range in the start line
|
|
3130 IRegion extent= getExtent(start, start);
|
|
3131 startPixel= extent.getOffset() + fTextWidget.getHorizontalPixel();
|
|
3132 endPixel= startPixel;
|
|
3133
|
|
3134 } else {
|
|
3135 IRegion extent= getExtent(start, end);
|
|
3136 startPixel= extent.getOffset() + fTextWidget.getHorizontalPixel();
|
|
3137 endPixel= startPixel + extent.getLength();
|
|
3138 }
|
|
3139
|
|
3140 int visibleStart= fTextWidget.getHorizontalPixel();
|
|
3141 int visibleEnd= visibleStart + fTextWidget.getClientArea().width;
|
|
3142
|
|
3143 // scroll only if not yet visible
|
|
3144 if (startPixel < visibleStart || visibleEnd < endPixel) {
|
|
3145
|
|
3146 // set buffer zone to 10 pixels
|
|
3147 int bufferZone= 10;
|
|
3148
|
|
3149 int newOffset= visibleStart;
|
|
3150
|
|
3151 int visibleWidth= visibleEnd - visibleStart;
|
|
3152 int selectionPixelWidth= endPixel - startPixel;
|
|
3153
|
|
3154 if (startPixel < visibleStart)
|
|
3155 newOffset= startPixel;
|
|
3156 else if (selectionPixelWidth + bufferZone < visibleWidth)
|
|
3157 newOffset= endPixel + bufferZone - visibleWidth;
|
|
3158 else
|
|
3159 newOffset= startPixel;
|
|
3160
|
|
3161 float index= ((float)newOffset) / ((float)getAverageCharWidth());
|
|
3162
|
|
3163 fTextWidget.setHorizontalIndex(Math.round(index));
|
|
3164 }
|
|
3165
|
|
3166 }
|
|
3167 } catch (BadLocationException e) {
|
|
3168 throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_range")); //$NON-NLS-1$
|
|
3169 }
|
|
3170 }
|
|
3171
|
|
3172 /**
|
|
3173 * Returns the width of the text when being drawn into this viewer's widget.
|
|
3174 *
|
|
3175 * @param text the string to measure
|
|
3176 * @return the width of the presentation of the given string
|
|
3177 * @deprecated use <code>getWidthInPixels(int, int)</code> instead
|
|
3178 */
|
|
3179 final protected int getWidthInPixels(String text) {
|
|
3180 GC gc= new GC(fTextWidget);
|
|
3181 gc.setFont(fTextWidget.getFont());
|
|
3182 Point extent= gc.textExtent(text);
|
|
3183 gc.dispose();
|
|
3184 return extent.x;
|
|
3185 }
|
|
3186
|
|
3187 /**
|
|
3188 * Returns the region covered by the given start and end offset.
|
|
3189 * The result is relative to the upper left corner of the widget
|
|
3190 * client area.
|
|
3191 *
|
|
3192 * @param start offset relative to the start of this viewer's view port
|
|
3193 * 0 <= offset <= getCharCount()
|
|
3194 * @param end offset relative to the start of this viewer's view port
|
|
3195 * 0 <= offset <= getCharCount()
|
|
3196 * @return the region covered by start and end offset
|
|
3197 */
|
|
3198 final protected IRegion getExtent(int start, int end) {
|
|
3199 if (end > 0 && start < end) {
|
|
3200 Rectangle bounds= fTextWidget.getTextBounds(start, end - 1);
|
|
3201 return new Region(bounds.x, bounds.width);
|
|
3202 }
|
|
3203
|
|
3204 return new Region(fTextWidget.getLocationAtOffset(start).x, 0);
|
|
3205 }
|
|
3206
|
|
3207 /**
|
|
3208 * Returns the width of the representation of a text range in the
|
|
3209 * visible region of the viewer's document as drawn in this viewer's
|
|
3210 * widget.
|
|
3211 *
|
|
3212 * @param offset the offset of the text range in the visible region
|
|
3213 * @param length the length of the text range in the visible region
|
|
3214 * @return the width of the presentation of the specified text range
|
|
3215 * @since 2.0
|
|
3216 */
|
|
3217 final protected int getWidthInPixels(int offset, int length) {
|
|
3218 return getExtent(offset, offset + length).getLength();
|
|
3219 }
|
|
3220
|
|
3221 /**
|
|
3222 * Returns the average character width of this viewer's widget.
|
|
3223 *
|
|
3224 * @return the average character width of this viewer's widget
|
|
3225 */
|
|
3226 final protected int getAverageCharWidth() {
|
|
3227 return JFaceTextUtil.getAverageCharWidth(getTextWidget());
|
|
3228 }
|
|
3229
|
|
3230 /*
|
|
3231 * @see Viewer#refresh()
|
|
3232 */
|
|
3233 public void refresh() {
|
|
3234 setDocument(getDocument());
|
|
3235 }
|
|
3236
|
|
3237 //---- visible range support
|
|
3238
|
|
3239 /**
|
|
3240 * Returns the slave document manager
|
|
3241 *
|
|
3242 * @return the slave document manager
|
|
3243 * @since 2.1
|
|
3244 */
|
|
3245 protected ISlaveDocumentManager getSlaveDocumentManager() {
|
|
3246 if (fSlaveDocumentManager is null)
|
|
3247 fSlaveDocumentManager= createSlaveDocumentManager();
|
|
3248 return fSlaveDocumentManager;
|
|
3249 }
|
|
3250
|
|
3251 /**
|
|
3252 * Creates a new slave document manager. This implementation always
|
|
3253 * returns a <code>ChildDocumentManager</code>.
|
|
3254 *
|
|
3255 * @return ISlaveDocumentManager
|
|
3256 * @since 2.1
|
|
3257 */
|
|
3258 protected ISlaveDocumentManager createSlaveDocumentManager() {
|
|
3259 return new ChildDocumentManager();
|
|
3260 }
|
|
3261
|
|
3262 /*
|
|
3263 * @see dwtx.jface.text.ITextViewer#invalidateTextPresentation()
|
|
3264 */
|
|
3265 public final void invalidateTextPresentation() {
|
|
3266 if (fVisibleDocument !is null) {
|
|
3267 fWidgetCommand.event= null;
|
|
3268 fWidgetCommand.start= 0;
|
|
3269 fWidgetCommand.length= fVisibleDocument.getLength();
|
|
3270 fWidgetCommand.text= fVisibleDocument.get();
|
|
3271 updateTextListeners(fWidgetCommand);
|
|
3272 }
|
|
3273 }
|
|
3274
|
|
3275 /**
|
|
3276 * Invalidates the given range of the text presentation.
|
|
3277 *
|
|
3278 * @param offset the offset of the range to be invalidated
|
|
3279 * @param length the length of the range to be invalidated
|
|
3280 * @since 2.1
|
|
3281 */
|
|
3282 public final void invalidateTextPresentation(int offset, int length) {
|
|
3283 if (fVisibleDocument !is null) {
|
|
3284
|
|
3285 IRegion widgetRange= modelRange2WidgetRange(new Region(offset, length));
|
|
3286 if (widgetRange !is null) {
|
|
3287
|
|
3288 fWidgetCommand.event= null;
|
|
3289 fWidgetCommand.start= widgetRange.getOffset();
|
|
3290 fWidgetCommand.length= widgetRange.getLength();
|
|
3291
|
|
3292 try {
|
|
3293 fWidgetCommand.text= fVisibleDocument.get(widgetRange.getOffset(), widgetRange.getLength());
|
|
3294 updateTextListeners(fWidgetCommand);
|
|
3295 } catch (BadLocationException x) {
|
|
3296 // can not happen because of previous checking
|
|
3297 }
|
|
3298 }
|
|
3299 }
|
|
3300 }
|
|
3301
|
|
3302 /**
|
|
3303 * Initializes the text widget with the visual document and
|
|
3304 * invalidates the overall presentation.
|
|
3305 */
|
|
3306 private void initializeWidgetContents() {
|
|
3307
|
|
3308 if (fTextWidget !is null && fVisibleDocument !is null) {
|
|
3309
|
|
3310 // set widget content
|
|
3311 if (fDocumentAdapter is null)
|
|
3312 fDocumentAdapter= createDocumentAdapter();
|
|
3313
|
|
3314 fDocumentAdapter.setDocument(fVisibleDocument);
|
|
3315 fTextWidget.setContent(fDocumentAdapter);
|
|
3316
|
|
3317 // invalidate presentation
|
|
3318 invalidateTextPresentation();
|
|
3319 }
|
|
3320 }
|
|
3321
|
|
3322 /**
|
|
3323 * Frees the given document if it is a slave document.
|
|
3324 *
|
|
3325 * @param slave the potential slave document
|
|
3326 * @since 3.0
|
|
3327 */
|
|
3328 protected void freeSlaveDocument(IDocument slave) {
|
|
3329 ISlaveDocumentManager manager= getSlaveDocumentManager();
|
|
3330 if (manager !is null && manager.isSlaveDocument(slave))
|
|
3331 manager.freeSlaveDocument(slave);
|
|
3332 }
|
|
3333
|
|
3334 /**
|
|
3335 * Sets this viewer's visible document. The visible document represents the
|
|
3336 * visible region of the viewer's input document.
|
|
3337 *
|
|
3338 * @param document the visible document
|
|
3339 */
|
|
3340 protected void setVisibleDocument(IDocument document) {
|
|
3341
|
|
3342 if (fVisibleDocument is document && fVisibleDocument instanceof ChildDocument) {
|
|
3343 // optimization for new child documents
|
|
3344 return;
|
|
3345 }
|
|
3346
|
|
3347 if (fVisibleDocument !is null) {
|
|
3348 if (fVisibleDocumentListener !is null)
|
|
3349 fVisibleDocument.removeDocumentListener(fVisibleDocumentListener);
|
|
3350 if (fVisibleDocument !is document)
|
|
3351 freeSlaveDocument(fVisibleDocument);
|
|
3352 }
|
|
3353
|
|
3354 fVisibleDocument= document;
|
|
3355 initializeDocumentInformationMapping(fVisibleDocument);
|
|
3356
|
|
3357 initializeWidgetContents();
|
|
3358
|
|
3359 fFindReplaceDocumentAdapter= null;
|
|
3360 if (fVisibleDocument !is null && fVisibleDocumentListener !is null)
|
|
3361 fVisibleDocument.addDocumentListener(fVisibleDocumentListener);
|
|
3362 }
|
|
3363
|
|
3364 /**
|
|
3365 * Hook method called when the visible document is about to be changed.
|
|
3366 * <p>
|
|
3367 * Subclasses may override.
|
|
3368 *
|
|
3369 * @param event the document event
|
|
3370 * @since 3.0
|
|
3371 */
|
|
3372 protected void handleVisibleDocumentAboutToBeChanged(DocumentEvent event) {
|
|
3373 }
|
|
3374
|
|
3375 /**
|
|
3376 * Hook method called when the visible document has been changed.
|
|
3377 * <p>
|
|
3378 * Subclasses may override.
|
|
3379 *
|
|
3380 * @param event the document event
|
|
3381 * @since 3.0
|
|
3382 */
|
|
3383 protected void handleVisibleDocumentChanged(DocumentEvent event) {
|
|
3384 }
|
|
3385
|
|
3386 /**
|
|
3387 * Initializes the document information mapping between the given slave document and
|
|
3388 * its master document.
|
|
3389 *
|
|
3390 * @param visibleDocument the slave document
|
|
3391 * @since 2.1
|
|
3392 */
|
|
3393 protected void initializeDocumentInformationMapping(IDocument visibleDocument) {
|
|
3394 ISlaveDocumentManager manager= getSlaveDocumentManager();
|
|
3395 fInformationMapping= manager is null ? null : manager.createMasterSlaveMapping(visibleDocument);
|
|
3396 }
|
|
3397
|
|
3398 /**
|
|
3399 * Returns the viewer's visible document.
|
|
3400 *
|
|
3401 * @return the viewer's visible document
|
|
3402 */
|
|
3403 protected IDocument getVisibleDocument() {
|
|
3404 return fVisibleDocument;
|
|
3405 }
|
|
3406
|
|
3407 /**
|
|
3408 * Returns the offset of the visible region.
|
|
3409 *
|
|
3410 * @return the offset of the visible region
|
|
3411 */
|
|
3412 protected int _getVisibleRegionOffset() {
|
|
3413
|
|
3414 IDocument document= getVisibleDocument();
|
|
3415 if (document instanceof ChildDocument) {
|
|
3416 ChildDocument cdoc= (ChildDocument) document;
|
|
3417 return cdoc.getParentDocumentRange().getOffset();
|
|
3418 }
|
|
3419
|
|
3420 return 0;
|
|
3421 }
|
|
3422
|
|
3423 /*
|
|
3424 * @see ITextViewer#getVisibleRegion()
|
|
3425 */
|
|
3426 public IRegion getVisibleRegion() {
|
|
3427
|
|
3428 IDocument document= getVisibleDocument();
|
|
3429 if (document instanceof ChildDocument) {
|
|
3430 Position p= ((ChildDocument) document).getParentDocumentRange();
|
|
3431 return new Region(p.getOffset(), p.getLength());
|
|
3432 }
|
|
3433
|
|
3434 return new Region(0, document is null ? 0 : document.getLength());
|
|
3435 }
|
|
3436
|
|
3437 /*
|
|
3438 * @see ITextViewer#overlapsWithVisibleRegion(int, int)
|
|
3439 */
|
|
3440 public bool overlapsWithVisibleRegion(int start, int length) {
|
|
3441 IDocument document= getVisibleDocument();
|
|
3442 if (document instanceof ChildDocument) {
|
|
3443 ChildDocument cdoc= (ChildDocument) document;
|
|
3444 return cdoc.getParentDocumentRange().overlapsWith(start, length);
|
|
3445 } else if (document !is null) {
|
|
3446 int size= document.getLength();
|
|
3447 return (start >= 0 && length >= 0 && start + length <= size);
|
|
3448 }
|
|
3449 return false;
|
|
3450 }
|
|
3451
|
|
3452 /*
|
|
3453 * @see ITextViewer#setVisibleRegion(int, int)
|
|
3454 */
|
|
3455 public void setVisibleRegion(int start, int length) {
|
|
3456
|
|
3457 IRegion region= getVisibleRegion();
|
|
3458 if (start is region.getOffset() && length is region.getLength()) {
|
|
3459 // nothing to change
|
|
3460 return;
|
|
3461 }
|
|
3462
|
|
3463 setRedraw(false);
|
|
3464 try {
|
|
3465
|
|
3466 IDocument slaveDocument= createSlaveDocument(getVisibleDocument());
|
|
3467 if (updateSlaveDocument(slaveDocument, start, length))
|
|
3468 setVisibleDocument(slaveDocument);
|
|
3469
|
|
3470 } catch (BadLocationException x) {
|
|
3471 throw new IllegalArgumentException(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_2")); //$NON-NLS-1$
|
|
3472 } finally {
|
|
3473 setRedraw(true);
|
|
3474 }
|
|
3475 }
|
|
3476
|
|
3477 /*
|
|
3478 * @see ITextViewer#resetVisibleRegion()
|
|
3479 */
|
|
3480 public void resetVisibleRegion() {
|
|
3481 ISlaveDocumentManager manager= getSlaveDocumentManager();
|
|
3482 if (manager !is null) {
|
|
3483 IDocument slave= getVisibleDocument();
|
|
3484 IDocument master= manager.getMasterDocument(slave);
|
|
3485 if (master !is null) {
|
|
3486 setVisibleDocument(master);
|
|
3487 manager.freeSlaveDocument(slave);
|
|
3488 }
|
|
3489 }
|
|
3490 }
|
|
3491
|
|
3492
|
|
3493 //--------------------------------------
|
|
3494
|
|
3495 /*
|
|
3496 * @see ITextViewer#setTextDoubleClickStrategy(ITextDoubleClickStrategy, String)
|
|
3497 */
|
|
3498 public void setTextDoubleClickStrategy(ITextDoubleClickStrategy strategy, String contentType) {
|
|
3499
|
|
3500 if (strategy !is null) {
|
|
3501 if (fDoubleClickStrategies is null)
|
|
3502 fDoubleClickStrategies= new HashMap();
|
|
3503 fDoubleClickStrategies.put(contentType, strategy);
|
|
3504 } else if (fDoubleClickStrategies !is null)
|
|
3505 fDoubleClickStrategies.remove(contentType);
|
|
3506 }
|
|
3507
|
|
3508 /**
|
|
3509 * Selects from the given map the one which is registered under
|
|
3510 * the content type of the partition in which the given offset is located.
|
|
3511 *
|
|
3512 * @param plugins the map from which to choose
|
|
3513 * @param offset the offset for which to find the plug-in
|
|
3514 * @return the plug-in registered under the offset's content type
|
|
3515 */
|
|
3516 protected Object selectContentTypePlugin(int offset, Map plugins) {
|
|
3517 try {
|
|
3518 return selectContentTypePlugin(TextUtilities.getContentType(getDocument(), getDocumentPartitioning(), offset, true), plugins);
|
|
3519 } catch (BadLocationException x) {
|
|
3520 if (TRACE_ERRORS)
|
|
3521 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.selectContentTypePlugin")); //$NON-NLS-1$
|
|
3522 }
|
|
3523 return null;
|
|
3524 }
|
|
3525
|
|
3526 /**
|
|
3527 * Selects from the given <code>plug-ins</code> this one which is
|
|
3528 * registered for the given content <code>type</code>.
|
|
3529 *
|
|
3530 * @param type the type to be used as lookup key
|
|
3531 * @param plugins the table to be searched
|
|
3532 * @return the plug-in in the map for the given content type
|
|
3533 */
|
|
3534 private Object selectContentTypePlugin(String type, Map plugins) {
|
|
3535
|
|
3536 if (plugins is null)
|
|
3537 return null;
|
|
3538
|
|
3539 return plugins.get(type);
|
|
3540 }
|
|
3541
|
|
3542 /**
|
|
3543 * Hook called on receipt of a <code>VerifyEvent</code>. The event has
|
|
3544 * been translated into a <code>DocumentCommand</code> which can now be
|
|
3545 * manipulated by interested parties. By default, the hook forwards the command
|
|
3546 * to the installed instances of <code>IAutoEditStrategy</code>.
|
|
3547 *
|
|
3548 * @param command the document command representing the verify event
|
|
3549 */
|
|
3550 protected void customizeDocumentCommand(DocumentCommand command) {
|
|
3551 if (isIgnoringAutoEditStrategies())
|
|
3552 return;
|
|
3553
|
|
3554 IDocument document= getDocument();
|
|
3555
|
|
3556 if (fTabsToSpacesConverter !is null)
|
|
3557 fTabsToSpacesConverter.customizeDocumentCommand(document, command);
|
|
3558
|
|
3559 List strategies= (List) selectContentTypePlugin(command.offset, fAutoIndentStrategies);
|
|
3560 if (strategies is null)
|
|
3561 return;
|
|
3562
|
|
3563 switch (strategies.size()) {
|
|
3564 // optimization
|
|
3565 case 0:
|
|
3566 break;
|
|
3567
|
|
3568 case 1:
|
|
3569 ((IAutoEditStrategy) strategies.iterator().next()).customizeDocumentCommand(document, command);
|
|
3570 break;
|
|
3571
|
|
3572 // make iterator robust against adding/removing strategies from within strategies
|
|
3573 default:
|
|
3574 strategies= new ArrayList(strategies);
|
|
3575 for (final Iterator iterator= strategies.iterator(); iterator.hasNext(); )
|
|
3576 ((IAutoEditStrategy) iterator.next()).customizeDocumentCommand(document, command);
|
|
3577
|
|
3578 break;
|
|
3579 }
|
|
3580 }
|
|
3581
|
|
3582 /**
|
|
3583 * Handles the verify event issued by the viewer's text widget.
|
|
3584 *
|
|
3585 * @see VerifyListener#verifyText(VerifyEvent)
|
|
3586 * @param e the verify event
|
|
3587 */
|
|
3588 protected void handleVerifyEvent(VerifyEvent e) {
|
|
3589
|
|
3590 if (fEventConsumer !is null) {
|
|
3591 fEventConsumer.processEvent(e);
|
|
3592 if (!e.doit)
|
|
3593 return;
|
|
3594 }
|
|
3595
|
|
3596 IRegion modelRange= event2ModelRange(e);
|
|
3597 fDocumentCommand.setEvent(e, modelRange);
|
|
3598 customizeDocumentCommand(fDocumentCommand);
|
|
3599 if (!fDocumentCommand.fillEvent(e, modelRange)) {
|
|
3600
|
|
3601 bool compoundChange= fDocumentCommand.getCommandCount() > 1;
|
|
3602 try {
|
|
3603
|
|
3604 fVerifyListener.forward(false);
|
|
3605
|
|
3606 if (compoundChange && fUndoManager !is null)
|
|
3607 fUndoManager.beginCompoundChange();
|
|
3608
|
|
3609 fDocumentCommand.execute(getDocument());
|
|
3610
|
|
3611 if (fTextWidget !is null) {
|
|
3612 int documentCaret= fDocumentCommand.caretOffset;
|
|
3613 if (documentCaret is -1) {
|
|
3614 // old behavior of document command
|
|
3615 documentCaret= fDocumentCommand.offset + (fDocumentCommand.text is null ? 0 : fDocumentCommand.text.length());
|
|
3616 }
|
|
3617
|
|
3618 int widgetCaret= modelOffset2WidgetOffset(documentCaret);
|
|
3619 if (widgetCaret is -1) {
|
|
3620 // try to move it to the closest spot
|
|
3621 IRegion region= getModelCoverage();
|
|
3622 if (region !is null) {
|
|
3623 if (documentCaret <= region.getOffset())
|
|
3624 widgetCaret= 0;
|
|
3625 else if (documentCaret >= region.getOffset() + region.getLength())
|
|
3626 widgetCaret= getVisibleRegion().getLength();
|
|
3627 }
|
|
3628 }
|
|
3629
|
|
3630 if (widgetCaret !is -1) {
|
|
3631 // there is a valid widget caret
|
|
3632 fTextWidget.setCaretOffset(widgetCaret);
|
|
3633 }
|
|
3634
|
|
3635 fTextWidget.showSelection();
|
|
3636 }
|
|
3637 } catch (BadLocationException x) {
|
|
3638
|
|
3639 if (TRACE_ERRORS)
|
|
3640 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.verifyText")); //$NON-NLS-1$
|
|
3641
|
|
3642 } finally {
|
|
3643
|
|
3644 if (compoundChange && fUndoManager !is null)
|
|
3645 fUndoManager.endCompoundChange();
|
|
3646
|
|
3647 fVerifyListener.forward(true);
|
|
3648
|
|
3649 }
|
|
3650 }
|
|
3651 }
|
|
3652
|
|
3653 //---- text manipulation
|
|
3654
|
|
3655 /**
|
|
3656 * Returns whether the marked region of this viewer is empty.
|
|
3657 *
|
|
3658 * @return <code>true</code> if the marked region of this viewer is empty, otherwise <code>false</code>
|
|
3659 * @since 2.0
|
|
3660 */
|
|
3661 private bool isMarkedRegionEmpty() {
|
|
3662 return
|
|
3663 fTextWidget is null ||
|
|
3664 fMarkPosition is null ||
|
|
3665 fMarkPosition.isDeleted() ||
|
|
3666 modelRange2WidgetRange(fMarkPosition) is null;
|
|
3667 }
|
|
3668
|
|
3669 /*
|
|
3670 * @see ITextViewer#canDoOperation(int)
|
|
3671 */
|
|
3672 public bool canDoOperation(int operation) {
|
|
3673
|
|
3674 if (fTextWidget is null || !redraws())
|
|
3675 return false;
|
|
3676
|
|
3677 switch (operation) {
|
|
3678 case CUT:
|
|
3679 return isEditable() &&(fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty());
|
|
3680 case COPY:
|
|
3681 return fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty();
|
|
3682 case DELETE:
|
|
3683 case PASTE:
|
|
3684 return isEditable();
|
|
3685 case SELECT_ALL:
|
|
3686 return true;
|
|
3687 case SHIFT_LEFT:
|
|
3688 case SHIFT_RIGHT:
|
|
3689 return isEditable() && fIndentChars !is null && areMultipleLinesSelected();
|
|
3690 case PREFIX:
|
|
3691 case STRIP_PREFIX:
|
|
3692 return isEditable() && fDefaultPrefixChars !is null;
|
|
3693 case UNDO:
|
|
3694 return fUndoManager !is null && fUndoManager.undoable();
|
|
3695 case REDO:
|
|
3696 return fUndoManager !is null && fUndoManager.redoable();
|
|
3697 case PRINT:
|
|
3698 return isPrintable();
|
|
3699 }
|
|
3700
|
|
3701 return false;
|
|
3702 }
|
|
3703
|
|
3704 /*
|
|
3705 * @see ITextViewer#doOperation(int)
|
|
3706 */
|
|
3707 public void doOperation(int operation) {
|
|
3708
|
|
3709 if (fTextWidget is null || !redraws())
|
|
3710 return;
|
|
3711
|
|
3712 Point selection= null;
|
|
3713
|
|
3714 switch (operation) {
|
|
3715
|
|
3716 case UNDO:
|
|
3717 if (fUndoManager !is null) {
|
|
3718 ignoreAutoEditStrategies(true);
|
|
3719 fUndoManager.undo();
|
|
3720 ignoreAutoEditStrategies(false);
|
|
3721 }
|
|
3722 break;
|
|
3723 case REDO:
|
|
3724 if (fUndoManager !is null) {
|
|
3725 ignoreAutoEditStrategies(true);
|
|
3726 fUndoManager.redo();
|
|
3727 ignoreAutoEditStrategies(false);
|
|
3728 }
|
|
3729 break;
|
|
3730 case CUT:
|
|
3731 if (fTextWidget.getSelectionCount() is 0)
|
|
3732 copyMarkedRegion(true);
|
|
3733 else
|
|
3734 fTextWidget.cut();
|
|
3735
|
|
3736 selection= fTextWidget.getSelectionRange();
|
|
3737 fireSelectionChanged(selection.x, selection.y);
|
|
3738
|
|
3739 break;
|
|
3740 case COPY:
|
|
3741 if (fTextWidget.getSelectionCount() is 0)
|
|
3742 copyMarkedRegion(false);
|
|
3743 else
|
|
3744 fTextWidget.copy();
|
|
3745 break;
|
|
3746 case PASTE:
|
|
3747 // ignoreAutoEditStrategies(true);
|
|
3748 fTextWidget.paste();
|
|
3749 selection= fTextWidget.getSelectionRange();
|
|
3750 fireSelectionChanged(selection.x, selection.y);
|
|
3751 // ignoreAutoEditStrategies(false);
|
|
3752 break;
|
|
3753 case DELETE:
|
|
3754 fTextWidget.invokeAction(ST.DELETE_NEXT);
|
|
3755 selection= fTextWidget.getSelectionRange();
|
|
3756 fireSelectionChanged(selection.x, selection.y);
|
|
3757 break;
|
|
3758 case SELECT_ALL: {
|
|
3759 if (getDocument() !is null)
|
|
3760 setSelectedRange(0, getDocument().getLength());
|
|
3761 break;
|
|
3762 }
|
|
3763 case SHIFT_RIGHT:
|
|
3764 shift(false, true, false);
|
|
3765 break;
|
|
3766 case SHIFT_LEFT:
|
|
3767 shift(false, false, false);
|
|
3768 break;
|
|
3769 case PREFIX:
|
|
3770 shift(true, true, true);
|
|
3771 break;
|
|
3772 case STRIP_PREFIX:
|
|
3773 shift(true, false, true);
|
|
3774 break;
|
|
3775 case PRINT:
|
|
3776 print();
|
|
3777 break;
|
|
3778 }
|
|
3779 }
|
|
3780
|
|
3781 /**
|
|
3782 * Tells this viewer whether the registered auto edit strategies should be ignored.
|
|
3783 *
|
|
3784 * @param ignore <code>true</code> if the strategies should be ignored.
|
|
3785 * @since 2.1
|
|
3786 */
|
|
3787 protected void ignoreAutoEditStrategies(bool ignore) {
|
|
3788 if (fIgnoreAutoIndent is ignore)
|
|
3789 return;
|
|
3790
|
|
3791 fIgnoreAutoIndent= ignore;
|
|
3792
|
|
3793 IDocument document= getDocument();
|
|
3794 if (document instanceof IDocumentExtension2) {
|
|
3795 IDocumentExtension2 extension= (IDocumentExtension2) document;
|
|
3796 if (ignore)
|
|
3797 extension.ignorePostNotificationReplaces();
|
|
3798 else
|
|
3799 extension.acceptPostNotificationReplaces();
|
|
3800 }
|
|
3801 }
|
|
3802
|
|
3803 /**
|
|
3804 * Returns whether this viewer ignores the registered auto edit strategies.
|
|
3805 *
|
|
3806 * @return <code>true</code> if the strategies are ignored
|
|
3807 * @since 2.1
|
|
3808 */
|
|
3809 protected bool isIgnoringAutoEditStrategies() {
|
|
3810 return fIgnoreAutoIndent;
|
|
3811 }
|
|
3812
|
|
3813 /*
|
|
3814 * @see ITextOperationTargetExtension#enableOperation(int, bool)
|
|
3815 * @since 2.0
|
|
3816 */
|
|
3817 public void enableOperation(int operation, bool enable) {
|
|
3818 /*
|
|
3819 * NO-OP by default.
|
|
3820 * Will be changed to regularly disable the known operations.
|
|
3821 */
|
|
3822 }
|
|
3823
|
|
3824 /**
|
|
3825 * Copies/cuts the marked region.
|
|
3826 *
|
|
3827 * @param delete <code>true</code> if the region should be deleted rather than copied.
|
|
3828 * @since 2.0
|
|
3829 */
|
|
3830 protected void copyMarkedRegion(bool delete) {
|
|
3831
|
|
3832 if (fTextWidget is null)
|
|
3833 return;
|
|
3834
|
|
3835 if (fMarkPosition is null || fMarkPosition.isDeleted() || modelRange2WidgetRange(fMarkPosition) is null)
|
|
3836 return;
|
|
3837
|
|
3838 int widgetMarkOffset= modelOffset2WidgetOffset(fMarkPosition.offset);
|
|
3839 Point selection= fTextWidget.getSelection();
|
|
3840 if (selection.x <= widgetMarkOffset)
|
|
3841 fTextWidget.setSelection(selection.x, widgetMarkOffset);
|
|
3842 else
|
|
3843 fTextWidget.setSelection(widgetMarkOffset, selection.x);
|
|
3844
|
|
3845 if (delete) {
|
|
3846 fTextWidget.cut();
|
|
3847 } else {
|
|
3848 fTextWidget.copy();
|
|
3849 fTextWidget.setSelection(selection.x); // restore old cursor position
|
|
3850 }
|
|
3851 }
|
|
3852
|
|
3853 /**
|
|
3854 * Deletes the current selection. If the selection has the length 0
|
|
3855 * the selection is automatically extended to the right - either by 1
|
|
3856 * or by the length of line delimiter if at the end of a line.
|
|
3857 *
|
|
3858 * @deprecated use <code>StyledText.invokeAction</code> instead
|
|
3859 */
|
|
3860 protected void deleteText() {
|
|
3861 fTextWidget.invokeAction(ST.DELETE_NEXT);
|
|
3862 }
|
|
3863
|
|
3864 /**
|
|
3865 * A block is selected if the character preceding the start of the
|
|
3866 * selection is a new line character.
|
|
3867 *
|
|
3868 * @return <code>true</code> if a block is selected
|
|
3869 */
|
|
3870 protected bool isBlockSelected() {
|
|
3871
|
|
3872 Point s= getSelectedRange();
|
|
3873 if (s.y is 0)
|
|
3874 return false;
|
|
3875
|
|
3876 try {
|
|
3877
|
|
3878 IDocument document= getDocument();
|
|
3879 int line= document.getLineOfOffset(s.x);
|
|
3880 int start= document.getLineOffset(line);
|
|
3881 return (s.x is start);
|
|
3882
|
|
3883 } catch (BadLocationException x) {
|
|
3884 }
|
|
3885
|
|
3886 return false;
|
|
3887 }
|
|
3888
|
|
3889 /**
|
|
3890 * Returns <code>true</code> if one line is completely selected or if multiple lines are selected.
|
|
3891 * Being completely selected means that all characters except the new line characters are
|
|
3892 * selected.
|
|
3893 *
|
|
3894 * @return <code>true</code> if one or multiple lines are selected
|
|
3895 * @since 2.0
|
|
3896 */
|
|
3897 protected bool areMultipleLinesSelected() {
|
|
3898 Point s= getSelectedRange();
|
|
3899 if (s.y is 0)
|
|
3900 return false;
|
|
3901
|
|
3902 try {
|
|
3903
|
|
3904 IDocument document= getDocument();
|
|
3905 int startLine= document.getLineOfOffset(s.x);
|
|
3906 int endLine= document.getLineOfOffset(s.x + s.y);
|
|
3907 IRegion line= document.getLineInformation(startLine);
|
|
3908 return startLine !is endLine || (s.x is line.getOffset() && s.y is line.getLength());
|
|
3909
|
|
3910 } catch (BadLocationException x) {
|
|
3911 }
|
|
3912
|
|
3913 return false;
|
|
3914 }
|
|
3915
|
|
3916 /**
|
|
3917 * Returns the index of the first line whose start offset is in the given text range.
|
|
3918 *
|
|
3919 * @param region the text range in characters where to find the line
|
|
3920 * @return the first line whose start index is in the given range, -1 if there is no such line
|
|
3921 */
|
|
3922 private int getFirstCompleteLineOfRegion(IRegion region) {
|
|
3923
|
|
3924 try {
|
|
3925
|
|
3926 IDocument d= getDocument();
|
|
3927
|
|
3928 int startLine= d.getLineOfOffset(region.getOffset());
|
|
3929
|
|
3930 int offset= d.getLineOffset(startLine);
|
|
3931 if (offset >= region.getOffset())
|
|
3932 return startLine;
|
|
3933
|
|
3934 offset= d.getLineOffset(startLine + 1);
|
|
3935 return (offset > region.getOffset() + region.getLength() ? -1 : startLine + 1);
|
|
3936
|
|
3937 } catch (BadLocationException x) {
|
|
3938 if (TRACE_ERRORS)
|
|
3939 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getFirstCompleteLineOfRegion")); //$NON-NLS-1$
|
|
3940 }
|
|
3941
|
|
3942 return -1;
|
|
3943 }
|
|
3944
|
|
3945
|
|
3946 /**
|
|
3947 * Creates a region describing the text block (something that starts at
|
|
3948 * the beginning of a line) completely containing the current selection.
|
|
3949 *
|
|
3950 * @param selection the selection to use
|
|
3951 * @return the region describing the text block comprising the given selection
|
|
3952 * @since 2.0
|
|
3953 */
|
|
3954 private IRegion getTextBlockFromSelection(Point selection) {
|
|
3955
|
|
3956 try {
|
|
3957 IDocument document= getDocument();
|
|
3958 IRegion line= document.getLineInformationOfOffset(selection.x);
|
|
3959 int length= selection.y is 0 ? line.getLength() : selection.y + (selection.x - line.getOffset());
|
|
3960 return new Region(line.getOffset(), length);
|
|
3961
|
|
3962 } catch (BadLocationException x) {
|
|
3963 }
|
|
3964
|
|
3965 return null;
|
|
3966 }
|
|
3967
|
|
3968 /**
|
|
3969 * Shifts a text block to the right or left using the specified set of prefix characters.
|
|
3970 * The prefixes must start at the beginning of the line.
|
|
3971 *
|
|
3972 * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
|
|
3973 * @param right says whether to shift to the right or the left
|
|
3974 *
|
|
3975 * @deprecated use shift(bool, bool, bool) instead
|
|
3976 */
|
|
3977 protected void shift(bool useDefaultPrefixes, bool right) {
|
|
3978 shift(useDefaultPrefixes, right, false);
|
|
3979 }
|
|
3980
|
|
3981 /**
|
|
3982 * Shifts a text block to the right or left using the specified set of prefix characters.
|
|
3983 * If white space should be ignored the prefix characters must not be at the beginning of
|
|
3984 * the line when shifting to the left. There may be whitespace in front of the prefixes.
|
|
3985 *
|
|
3986 * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
|
|
3987 * @param right says whether to shift to the right or the left
|
|
3988 * @param ignoreWhitespace says whether whitespace in front of prefixes is allowed
|
|
3989 * @since 2.0
|
|
3990 */
|
|
3991 protected void shift(bool useDefaultPrefixes, bool right, bool ignoreWhitespace) {
|
|
3992 if (fUndoManager !is null)
|
|
3993 fUndoManager.beginCompoundChange();
|
|
3994
|
|
3995 IDocument d= getDocument();
|
|
3996 Map partitioners= null;
|
|
3997 DocumentRewriteSession rewriteSession= null;
|
|
3998 try {
|
|
3999 Point selection= getSelectedRange();
|
|
4000 IRegion block= getTextBlockFromSelection(selection);
|
|
4001 ITypedRegion[] regions= TextUtilities.computePartitioning(d, getDocumentPartitioning(), block.getOffset(), block.getLength(), false);
|
|
4002
|
|
4003 int lineCount= 0;
|
|
4004 int[] lines= new int[regions.length * 2]; // [start line, end line, start line, end line, ...]
|
|
4005 for (int i= 0, j= 0; i < regions.length; i++, j+= 2) {
|
|
4006 // start line of region
|
|
4007 lines[j]= getFirstCompleteLineOfRegion(regions[i]);
|
|
4008 // end line of region
|
|
4009 int length= regions[i].getLength();
|
|
4010 int offset= regions[i].getOffset() + length;
|
|
4011 if (length > 0)
|
|
4012 offset--;
|
|
4013 lines[j + 1]= (lines[j] is -1 ? -1 : d.getLineOfOffset(offset));
|
|
4014 lineCount += lines[j + 1] - lines[j] + 1;
|
|
4015 }
|
|
4016
|
|
4017 if (d instanceof IDocumentExtension4) {
|
|
4018 IDocumentExtension4 extension= (IDocumentExtension4) d;
|
|
4019 rewriteSession= extension.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
|
|
4020 } else {
|
|
4021 setRedraw(false);
|
|
4022 startSequentialRewriteMode(true);
|
|
4023 }
|
|
4024 if (lineCount >= 20)
|
|
4025 partitioners= TextUtilities.removeDocumentPartitioners(d);
|
|
4026
|
|
4027 // Perform the shift operation.
|
|
4028 Map map= (useDefaultPrefixes ? fDefaultPrefixChars : fIndentChars);
|
|
4029 for (int i= 0, j= 0; i < regions.length; i++, j += 2) {
|
|
4030 String[] prefixes= (String[]) selectContentTypePlugin(regions[i].getType(), map);
|
|
4031 if (prefixes !is null && prefixes.length > 0 && lines[j] >= 0 && lines[j + 1] >= 0) {
|
|
4032 if (right)
|
|
4033 shiftRight(lines[j], lines[j + 1], prefixes[0]);
|
|
4034 else
|
|
4035 shiftLeft(lines[j], lines[j + 1], prefixes, ignoreWhitespace);
|
|
4036 }
|
|
4037 }
|
|
4038
|
|
4039 } catch (BadLocationException x) {
|
|
4040 if (TRACE_ERRORS)
|
|
4041 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.shift_1")); //$NON-NLS-1$
|
|
4042
|
|
4043 } finally {
|
|
4044
|
|
4045 if (partitioners !is null)
|
|
4046 TextUtilities.addDocumentPartitioners(d, partitioners);
|
|
4047
|
|
4048 if (d instanceof IDocumentExtension4) {
|
|
4049 IDocumentExtension4 extension= (IDocumentExtension4) d;
|
|
4050 extension.stopRewriteSession(rewriteSession);
|
|
4051 } else {
|
|
4052 stopSequentialRewriteMode();
|
|
4053 setRedraw(true);
|
|
4054 }
|
|
4055
|
|
4056 if (fUndoManager !is null)
|
|
4057 fUndoManager.endCompoundChange();
|
|
4058 }
|
|
4059 }
|
|
4060
|
|
4061 /**
|
|
4062 * Shifts the specified lines to the right inserting the given prefix
|
|
4063 * at the beginning of each line
|
|
4064 *
|
|
4065 * @param prefix the prefix to be inserted
|
|
4066 * @param startLine the first line to shift
|
|
4067 * @param endLine the last line to shift
|
|
4068 * @since 2.0
|
|
4069 */
|
|
4070 private void shiftRight(int startLine, int endLine, String prefix) {
|
|
4071
|
|
4072 try {
|
|
4073
|
|
4074 IDocument d= getDocument();
|
|
4075 while (startLine <= endLine) {
|
|
4076 d.replace(d.getLineOffset(startLine++), 0, prefix);
|
|
4077 }
|
|
4078
|
|
4079 } catch (BadLocationException x) {
|
|
4080 if (TRACE_ERRORS)
|
|
4081 System.out.println("TextViewer.shiftRight: BadLocationException"); //$NON-NLS-1$
|
|
4082 }
|
|
4083 }
|
|
4084
|
|
4085 /**
|
|
4086 * Shifts the specified lines to the right or to the left. On shifting to the right
|
|
4087 * it insert <code>prefixes[0]</code> at the beginning of each line. On shifting to the
|
|
4088 * left it tests whether each of the specified lines starts with one of the specified
|
|
4089 * prefixes and if so, removes the prefix.
|
|
4090 *
|
|
4091 * @param startLine the first line to shift
|
|
4092 * @param endLine the last line to shift
|
|
4093 * @param prefixes the prefixes to be used for shifting
|
|
4094 * @param ignoreWhitespace <code>true</code> if whitespace should be ignored, <code>false</code> otherwise
|
|
4095 * @since 2.0
|
|
4096 */
|
|
4097 private void shiftLeft(int startLine, int endLine, String[] prefixes, bool ignoreWhitespace) {
|
|
4098
|
|
4099 IDocument d= getDocument();
|
|
4100
|
|
4101 try {
|
|
4102
|
|
4103 IRegion[] occurrences= new IRegion[endLine - startLine + 1];
|
|
4104
|
|
4105 // find all the first occurrences of prefix in the given lines
|
|
4106 for (int i= 0; i < occurrences.length; i++) {
|
|
4107
|
|
4108 IRegion line= d.getLineInformation(startLine + i);
|
|
4109 String text= d.get(line.getOffset(), line.getLength());
|
|
4110
|
|
4111 int index= -1;
|
|
4112 int[] found= TextUtilities.indexOf(prefixes, text, 0);
|
|
4113 if (found[0] !is -1) {
|
|
4114 if (ignoreWhitespace) {
|
|
4115 String s= d.get(line.getOffset(), found[0]);
|
|
4116 s= s.trim();
|
|
4117 if (s.length() is 0)
|
|
4118 index= line.getOffset() + found[0];
|
|
4119 } else if (found[0] is 0)
|
|
4120 index= line.getOffset();
|
|
4121 }
|
|
4122
|
|
4123 if (index > -1) {
|
|
4124 // remember where prefix is in line, so that it can be removed
|
|
4125 int length= prefixes[found[1]].length();
|
|
4126 if (length is 0 && !ignoreWhitespace && line.getLength() > 0) {
|
|
4127 // found a non-empty line which cannot be shifted
|
|
4128 return;
|
|
4129 }
|
|
4130 occurrences[i]= new Region(index, length);
|
|
4131 } else {
|
|
4132 // found a line which cannot be shifted
|
|
4133 return;
|
|
4134 }
|
|
4135 }
|
|
4136
|
|
4137 // OK - change the document
|
|
4138 int decrement= 0;
|
|
4139 for (int i= 0; i < occurrences.length; i++) {
|
|
4140 IRegion r= occurrences[i];
|
|
4141 d.replace(r.getOffset() - decrement, r.getLength(), ""); //$NON-NLS-1$
|
|
4142 decrement += r.getLength();
|
|
4143 }
|
|
4144
|
|
4145 } catch (BadLocationException x) {
|
|
4146 if (TRACE_ERRORS)
|
|
4147 System.out.println("TextViewer.shiftLeft: BadLocationException"); //$NON-NLS-1$
|
|
4148 }
|
|
4149 }
|
|
4150
|
|
4151 /**
|
|
4152 * Returns whether the shown text can be printed.
|
|
4153 *
|
|
4154 * @return the viewer's printable mode
|
|
4155 */
|
|
4156 protected bool isPrintable() {
|
|
4157 /*
|
|
4158 * 1GK7Q10: ITPUI:WIN98 - internal error after invoking print at editor view
|
|
4159 * Changed from returning true to testing the length of the printer queue
|
|
4160 */
|
|
4161 PrinterData[] printerList= Printer.getPrinterList();
|
|
4162 return (printerList !is null && printerList.length > 0);
|
|
4163 }
|
|
4164
|
|
4165 /**
|
|
4166 * {@inheritDoc}
|
|
4167 *
|
|
4168 * @since 3.4
|
|
4169 */
|
|
4170 public void print(StyledTextPrintOptions options) {
|
|
4171 final PrintDialog dialog= new PrintDialog(fTextWidget.getShell(), DWT.PRIMARY_MODAL);
|
|
4172 final PrinterData data= dialog.open();
|
|
4173
|
|
4174 if (data !is null) {
|
|
4175 final Printer printer= new Printer(data);
|
|
4176 final Runnable styledTextPrinter= fTextWidget.print(printer, options);
|
|
4177
|
|
4178 Thread printingThread= new Thread("Printing") { //$NON-NLS-1$
|
|
4179 public void run() {
|
|
4180 styledTextPrinter.run();
|
|
4181 printer.dispose();
|
|
4182 }
|
|
4183 };
|
|
4184 printingThread.start();
|
|
4185 }
|
|
4186 }
|
|
4187
|
|
4188 /**
|
|
4189 * Brings up a print dialog and calls <code>printContents(Printer)</code>
|
|
4190 * which performs the actual print.
|
|
4191 */
|
|
4192 protected void print() {
|
|
4193 StyledTextPrintOptions options= new StyledTextPrintOptions();
|
|
4194 options.printTextFontStyle= true;
|
|
4195 options.printTextForeground= true;
|
|
4196 print(options);
|
|
4197 }
|
|
4198
|
|
4199 //------ find support
|
|
4200
|
|
4201 /**
|
|
4202 * Adheres to the contract of {@link IFindReplaceTarget#canPerformFind()}.
|
|
4203 *
|
|
4204 * @return <code>true</code> if find can be performed, <code>false</code> otherwise
|
|
4205 */
|
|
4206 protected bool canPerformFind() {
|
|
4207 IDocument d= getVisibleDocument();
|
|
4208 return (fTextWidget !is null && d !is null && d.getLength() > 0);
|
|
4209 }
|
|
4210
|
|
4211 /**
|
|
4212 * Adheres to the contract of {@link IFindReplaceTarget#findAndSelect(int, String, bool, bool, bool)}.
|
|
4213 *
|
|
4214 * @param startPosition the start position
|
|
4215 * @param findString the find string specification
|
|
4216 * @param forwardSearch the search direction
|
|
4217 * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
|
|
4218 * @param wholeWord <code>true</code> if match must be whole words, <code>false</code> otherwise
|
|
4219 * @return the model offset of the first match
|
|
4220 * @deprecated as of 3.0 use {@link #findAndSelect(int, String, bool, bool, bool, bool)}
|
|
4221 */
|
|
4222 protected int findAndSelect(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord) {
|
|
4223 try {
|
|
4224 return findAndSelect(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
|
|
4225 } catch (IllegalStateException ex) {
|
|
4226 return -1;
|
|
4227 } catch (PatternSyntaxException ex) {
|
|
4228 return -1;
|
|
4229 }
|
|
4230 }
|
|
4231
|
|
4232 /**
|
|
4233 * Adheres to the contract of
|
|
4234 * {@link dwtx.jface.text.IFindReplaceTargetExtension3#findAndSelect(int, String, bool, bool, bool, bool)}.
|
|
4235 *
|
|
4236 * @param startPosition the start position
|
|
4237 * @param findString the find string specification
|
|
4238 * @param forwardSearch the search direction
|
|
4239 * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
|
|
4240 * @param wholeWord <code>true</code> if matches must be whole words, <code>false</code> otherwise
|
|
4241 * @param regExSearch <code>true</code> if <code>findString</code> is a regular expression, <code>false</code> otherwise
|
|
4242 * @return the model offset of the first match
|
|
4243 *
|
|
4244 */
|
|
4245 protected int findAndSelect(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, bool regExSearch) {
|
|
4246 if (fTextWidget is null)
|
|
4247 return -1;
|
|
4248
|
|
4249 try {
|
|
4250
|
|
4251 int widgetOffset= (startPosition is -1 ? startPosition : modelOffset2WidgetOffset(startPosition));
|
|
4252 FindReplaceDocumentAdapter adapter= getFindReplaceDocumentAdapter();
|
|
4253 IRegion matchRegion= adapter.find(widgetOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
|
|
4254 if (matchRegion !is null) {
|
|
4255 int widgetPos= matchRegion.getOffset();
|
|
4256 int length= matchRegion.getLength();
|
|
4257
|
|
4258 // Prevents setting of widget selection with line delimiters at beginning or end
|
|
4259 char startChar= adapter.charAt(widgetPos);
|
|
4260 char endChar= adapter.charAt(widgetPos+length-1);
|
|
4261 bool borderHasLineDelimiter= startChar is '\n' || startChar is '\r' || endChar is '\n' || endChar is '\r';
|
|
4262 bool redraws= redraws();
|
|
4263 if (borderHasLineDelimiter && redraws)
|
|
4264 setRedraw(false);
|
|
4265
|
|
4266 if (redraws()) {
|
|
4267 fTextWidget.setSelectionRange(widgetPos, length);
|
|
4268 internalRevealRange(widgetPos, widgetPos + length);
|
|
4269 selectionChanged(widgetPos, length);
|
|
4270 } else {
|
|
4271 setSelectedRange(widgetOffset2ModelOffset(widgetPos), length);
|
|
4272 if (redraws)
|
|
4273 setRedraw(true);
|
|
4274 }
|
|
4275
|
|
4276 return widgetOffset2ModelOffset(widgetPos);
|
|
4277 }
|
|
4278
|
|
4279 } catch (BadLocationException x) {
|
|
4280 if (TRACE_ERRORS)
|
|
4281 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
|
|
4282 }
|
|
4283
|
|
4284 return -1;
|
|
4285 }
|
|
4286
|
|
4287 /**
|
|
4288 * Adheres to the contract of {@link dwtx.jface.text.IFindReplaceTargetExtension3#findAndSelect(int, String, bool, bool, bool, bool)}.
|
|
4289 *
|
|
4290 * @param startPosition the start position
|
|
4291 * @param findString the find string specification
|
|
4292 * @param forwardSearch the search direction
|
|
4293 * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
|
|
4294 * @param wholeWord <code>true</code> if matches must be whole words, <code>false</code> otherwise
|
|
4295 * @param rangeOffset the search scope offset
|
|
4296 * @param rangeLength the search scope length
|
|
4297 * @param regExSearch <code>true</code> if <code>findString</code> is a regular expression, <code>false</code> otherwise
|
|
4298 * @return the model offset of the first match
|
|
4299 * @since 3.0
|
|
4300 */
|
|
4301 protected int findAndSelectInRange(int startPosition, String findString, bool forwardSearch, bool caseSensitive, bool wholeWord, int rangeOffset, int rangeLength, bool regExSearch) {
|
|
4302 if (fTextWidget is null)
|
|
4303 return -1;
|
|
4304
|
|
4305 try {
|
|
4306
|
|
4307 int modelOffset;
|
|
4308 if (forwardSearch && (startPosition is -1 || startPosition < rangeOffset)) {
|
|
4309 modelOffset= rangeOffset;
|
|
4310 } else if (!forwardSearch && (startPosition is -1 || startPosition > rangeOffset + rangeLength)) {
|
|
4311 modelOffset= rangeOffset + rangeLength;
|
|
4312 } else {
|
|
4313 modelOffset= startPosition;
|
|
4314 }
|
|
4315
|
|
4316 int widgetOffset= modelOffset2WidgetOffset(modelOffset);
|
|
4317 if (widgetOffset is -1)
|
|
4318 return -1;
|
|
4319
|
|
4320 FindReplaceDocumentAdapter adapter= getFindReplaceDocumentAdapter();
|
|
4321 IRegion matchRegion= adapter.find(widgetOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
|
|
4322 int widgetPos= -1;
|
|
4323 int length= 0;
|
|
4324 if (matchRegion !is null) {
|
|
4325 widgetPos= matchRegion.getOffset();
|
|
4326 length= matchRegion.getLength();
|
|
4327 }
|
|
4328 int modelPos= widgetPos is -1 ? -1 : widgetOffset2ModelOffset(widgetPos);
|
|
4329
|
|
4330 if (widgetPos !is -1 && (modelPos < rangeOffset || modelPos + length > rangeOffset + rangeLength))
|
|
4331 widgetPos= -1;
|
|
4332
|
|
4333 if (widgetPos > -1) {
|
|
4334
|
|
4335 // Prevents setting of widget selection with line delimiters at beginning or end
|
|
4336 char startChar= adapter.charAt(widgetPos);
|
|
4337 char endChar= adapter.charAt(widgetPos+length-1);
|
|
4338 bool borderHasLineDelimiter= startChar is '\n' || startChar is '\r' || endChar is '\n' || endChar is '\r';
|
|
4339 bool redraws= redraws();
|
|
4340 if (borderHasLineDelimiter && redraws)
|
|
4341 setRedraw(false);
|
|
4342
|
|
4343 if (redraws()) {
|
|
4344 fTextWidget.setSelectionRange(widgetPos, length);
|
|
4345 internalRevealRange(widgetPos, widgetPos + length);
|
|
4346 selectionChanged(widgetPos, length);
|
|
4347 } else {
|
|
4348 setSelectedRange(modelPos, length);
|
|
4349 if (redraws)
|
|
4350 setRedraw(true);
|
|
4351 }
|
|
4352
|
|
4353 return modelPos;
|
|
4354 }
|
|
4355
|
|
4356
|
|
4357 } catch (BadLocationException x) {
|
|
4358 if (TRACE_ERRORS)
|
|
4359 System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
|
|
4360 }
|
|
4361
|
|
4362 return -1;
|
|
4363 }
|
|
4364
|
|
4365 //---------- text presentation support
|
|
4366
|
|
4367 /*
|
|
4368 * @see ITextViewer#setTextColor(Color)
|
|
4369 */
|
|
4370 public void setTextColor(Color color) {
|
|
4371 if (color !is null)
|
|
4372 setTextColor(color, 0, getDocument().getLength(), true);
|
|
4373 }
|
|
4374
|
|
4375 /*
|
|
4376 * @see ITextViewer#setTextColor(Color, start, length, bool)
|
|
4377 */
|
|
4378 public void setTextColor(Color color, int start, int length, bool controlRedraw) {
|
|
4379 if (fTextWidget !is null) {
|
|
4380
|
|
4381 StyleRange s= new StyleRange();
|
|
4382 s.foreground= color;
|
|
4383 s.start= start;
|
|
4384 s.length= length;
|
|
4385
|
|
4386 s= modelStyleRange2WidgetStyleRange(s);
|
|
4387 if (s !is null) {
|
|
4388 if (controlRedraw)
|
|
4389 fTextWidget.setRedraw(false);
|
|
4390 try {
|
|
4391 fTextWidget.setStyleRange(s);
|
|
4392 } finally {
|
|
4393 if (controlRedraw)
|
|
4394 fTextWidget.setRedraw(true);
|
|
4395 }
|
|
4396 }
|
|
4397 }
|
|
4398 }
|
|
4399
|
|
4400 /**
|
|
4401 * Adds the given presentation to the viewer's style information.
|
|
4402 *
|
|
4403 * @param presentation the presentation to be added
|
|
4404 */
|
|
4405 private void addPresentation(TextPresentation presentation) {
|
|
4406
|
|
4407 StyleRange range= presentation.getDefaultStyleRange();
|
|
4408 if (range !is null) {
|
|
4409
|
|
4410 range= modelStyleRange2WidgetStyleRange(range);
|
|
4411 if (range !is null)
|
|
4412 fTextWidget.setStyleRange(range);
|
|
4413
|
|
4414 ArrayList ranges= new ArrayList(presentation.getDenumerableRanges());
|
|
4415 Iterator e= presentation.getNonDefaultStyleRangeIterator();
|
|
4416 while (e.hasNext()) {
|
|
4417 range= (StyleRange) e.next();
|
|
4418 range= modelStyleRange2WidgetStyleRange(range);
|
|
4419 if (range !is null)
|
|
4420 ranges.add(range);
|
|
4421 }
|
|
4422
|
|
4423 if (!ranges.isEmpty())
|
|
4424 fTextWidget.replaceStyleRanges(0, 0, (StyleRange[])ranges.toArray(new StyleRange[ranges.size()]));
|
|
4425
|
|
4426 } else {
|
|
4427 IRegion region= modelRange2WidgetRange(presentation.getCoverage());
|
|
4428 if (region is null)
|
|
4429 return;
|
|
4430
|
|
4431 List list= new ArrayList(presentation.getDenumerableRanges());
|
|
4432 Iterator e= presentation.getAllStyleRangeIterator();
|
|
4433 while (e.hasNext()) {
|
|
4434 range= (StyleRange) e.next();
|
|
4435 range= modelStyleRange2WidgetStyleRange(range);
|
|
4436 if (range !is null)
|
|
4437 list.add(range);
|
|
4438 }
|
|
4439
|
|
4440 if (!list.isEmpty()) {
|
|
4441 StyleRange[] ranges= new StyleRange[list.size()];
|
|
4442 list.toArray(ranges);
|
|
4443 fTextWidget.replaceStyleRanges(region.getOffset(), region.getLength(), ranges);
|
|
4444 }
|
|
4445 }
|
|
4446 }
|
|
4447
|
|
4448 /**
|
|
4449 * Applies the given presentation to the given text widget. Helper method.
|
|
4450 *
|
|
4451 * @param presentation the style information
|
|
4452 * @since 2.1
|
|
4453 */
|
|
4454 private void applyTextPresentation(TextPresentation presentation) {
|
|
4455
|
|
4456 List list= new ArrayList(presentation.getDenumerableRanges());
|
|
4457 Iterator e= presentation.getAllStyleRangeIterator();
|
|
4458 while (e.hasNext()) {
|
|
4459 StyleRange range= (StyleRange) e.next();
|
|
4460 range= modelStyleRange2WidgetStyleRange(range);
|
|
4461 if (range !is null)
|
|
4462 list.add(range);
|
|
4463 }
|
|
4464
|
|
4465 if (!list.isEmpty()) {
|
|
4466 StyleRange[] ranges= new StyleRange[list.size()];
|
|
4467 list.toArray(ranges);
|
|
4468 fTextWidget.setStyleRanges(ranges);
|
|
4469 }
|
|
4470 }
|
|
4471
|
|
4472 /**
|
|
4473 * Returns the visible region if it is not equal to the whole document.
|
|
4474 * Otherwise returns <code>null</code>.
|
|
4475 *
|
|
4476 * @return the viewer's visible region if smaller than input document, otherwise <code>null</code>
|
|
4477 */
|
|
4478 protected IRegion _internalGetVisibleRegion() {
|
|
4479
|
|
4480 IDocument document= getVisibleDocument();
|
|
4481 if (document instanceof ChildDocument) {
|
|
4482 Position p= ((ChildDocument) document).getParentDocumentRange();
|
|
4483 return new Region(p.getOffset(), p.getLength());
|
|
4484 }
|
|
4485
|
|
4486 return null;
|
|
4487 }
|
|
4488
|
|
4489 /*
|
|
4490 * @see ITextViewer#changeTextPresentation(TextPresentation, bool)
|
|
4491 */
|
|
4492 public void changeTextPresentation(TextPresentation presentation, bool controlRedraw) {
|
|
4493
|
|
4494 if (presentation is null || !redraws())
|
|
4495 return;
|
|
4496
|
|
4497 if (fTextWidget is null)
|
|
4498 return;
|
|
4499
|
|
4500
|
|
4501 /*
|
|
4502 * Call registered text presentation listeners
|
|
4503 * and let them apply their presentation.
|
|
4504 */
|
|
4505 if (fTextPresentationListeners !is null) {
|
|
4506 ArrayList listeners= new ArrayList(fTextPresentationListeners);
|
|
4507 for (int i= 0, size= listeners.size(); i < size; i++) {
|
|
4508 ITextPresentationListener listener= (ITextPresentationListener)listeners.get(i);
|
|
4509 listener.applyTextPresentation(presentation);
|
|
4510 }
|
|
4511 }
|
|
4512
|
|
4513 if (presentation.isEmpty())
|
|
4514 return;
|
|
4515
|
|
4516 if (controlRedraw)
|
|
4517 fTextWidget.setRedraw(false);
|
|
4518
|
|
4519 if (fReplaceTextPresentation)
|
|
4520 applyTextPresentation(presentation);
|
|
4521 else
|
|
4522 addPresentation(presentation);
|
|
4523
|
|
4524 if (controlRedraw)
|
|
4525 fTextWidget.setRedraw(true);
|
|
4526 }
|
|
4527
|
|
4528 /*
|
|
4529 * @see ITextViewer#getFindReplaceTarget()
|
|
4530 */
|
|
4531 public IFindReplaceTarget getFindReplaceTarget() {
|
|
4532 if (fFindReplaceTarget is null)
|
|
4533 fFindReplaceTarget= new FindReplaceTarget();
|
|
4534 return fFindReplaceTarget;
|
|
4535 }
|
|
4536
|
|
4537 /**
|
|
4538 * Returns the find/replace document adapter.
|
|
4539 *
|
|
4540 * @return the find/replace document adapter.
|
|
4541 * @since 3.0
|
|
4542 */
|
|
4543 protected FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
|
|
4544 if (fFindReplaceDocumentAdapter is null)
|
|
4545 fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(getVisibleDocument());
|
|
4546 return fFindReplaceDocumentAdapter;
|
|
4547 }
|
|
4548
|
|
4549 /*
|
|
4550 * @see ITextViewer#getTextOperationTarget()
|
|
4551 */
|
|
4552 public ITextOperationTarget getTextOperationTarget() {
|
|
4553 return this;
|
|
4554 }
|
|
4555
|
|
4556 /*
|
|
4557 * @see ITextViewerExtension#appendVerifyKeyListener(VerifyKeyListener)
|
|
4558 * @since 2.0
|
|
4559 */
|
|
4560 public void appendVerifyKeyListener(VerifyKeyListener listener) {
|
|
4561 int index= fVerifyKeyListenersManager.numberOfListeners();
|
|
4562 fVerifyKeyListenersManager.insertListener(listener, index);
|
|
4563 }
|
|
4564
|
|
4565 /*
|
|
4566 * @see ITextViewerExtension#prependVerifyKeyListener(VerifyKeyListener)
|
|
4567 * @since 2.0
|
|
4568 */
|
|
4569 public void prependVerifyKeyListener(VerifyKeyListener listener) {
|
|
4570 fVerifyKeyListenersManager.insertListener(listener, 0);
|
|
4571
|
|
4572 }
|
|
4573
|
|
4574 /*
|
|
4575 * @see ITextViewerExtension#removeVerifyKeyListener(VerifyKeyListener)
|
|
4576 * @since 2.0
|
|
4577 */
|
|
4578 public void removeVerifyKeyListener(VerifyKeyListener listener) {
|
|
4579 fVerifyKeyListenersManager.removeListener(listener);
|
|
4580 }
|
|
4581
|
|
4582 /*
|
|
4583 * @see ITextViewerExtension#getMark()
|
|
4584 * @since 2.0
|
|
4585 */
|
|
4586 public int getMark() {
|
|
4587 return fMarkPosition is null || fMarkPosition.isDeleted() ? -1 : fMarkPosition.getOffset();
|
|
4588 }
|
|
4589
|
|
4590 /*
|
|
4591 * @see ITextViewerExtension#setMark(int)
|
|
4592 * @since 2.0
|
|
4593 */
|
|
4594 public void setMark(int offset) {
|
|
4595
|
|
4596 // clear
|
|
4597 if (offset is -1) {
|
|
4598 if (fMarkPosition !is null && !fMarkPosition.isDeleted()) {
|
|
4599
|
|
4600 IDocument document= getDocument();
|
|
4601 if (document !is null)
|
|
4602 document.removePosition(fMarkPosition);
|
|
4603 }
|
|
4604
|
|
4605 fMarkPosition= null;
|
|
4606
|
|
4607 markChanged(-1, 0);
|
|
4608
|
|
4609 // set
|
|
4610 } else {
|
|
4611
|
|
4612 IDocument document= getDocument();
|
|
4613 if (document is null) {
|
|
4614 fMarkPosition= null;
|
|
4615 return;
|
|
4616 }
|
|
4617
|
|
4618 if (fMarkPosition !is null)
|
|
4619 document.removePosition(fMarkPosition);
|
|
4620
|
|
4621 fMarkPosition= null;
|
|
4622
|
|
4623 try {
|
|
4624
|
|
4625 Position position= new Position(offset);
|
|
4626 document.addPosition(MARK_POSITION_CATEGORY, position);
|
|
4627 fMarkPosition= position;
|
|
4628
|
|
4629 } catch (BadLocationException e) {
|
|
4630 return;
|
|
4631 } catch (BadPositionCategoryException e) {
|
|
4632 return;
|
|
4633 }
|
|
4634
|
|
4635 markChanged(modelOffset2WidgetOffset(fMarkPosition.offset), 0);
|
|
4636 }
|
|
4637 }
|
|
4638
|
|
4639 /*
|
|
4640 * @see Viewer#inputChanged(Object, Object)
|
|
4641 * @since 2.0
|
|
4642 */
|
|
4643 protected void inputChanged(Object newInput, Object oldInput) {
|
|
4644
|
|
4645 IDocument oldDocument= (IDocument) oldInput;
|
|
4646 if (oldDocument !is null) {
|
|
4647
|
|
4648 if (fMarkPosition !is null && !fMarkPosition.isDeleted())
|
|
4649 oldDocument.removePosition(fMarkPosition);
|
|
4650
|
|
4651 try {
|
|
4652 oldDocument.removePositionUpdater(fMarkPositionUpdater);
|
|
4653 oldDocument.removePositionCategory(MARK_POSITION_CATEGORY);
|
|
4654
|
|
4655 } catch (BadPositionCategoryException e) {
|
|
4656 }
|
|
4657 }
|
|
4658
|
|
4659 fMarkPosition= null;
|
|
4660
|
|
4661 if (oldDocument instanceof IDocumentExtension4) {
|
|
4662 IDocumentExtension4 document= (IDocumentExtension4) oldDocument;
|
|
4663 document.removeDocumentRewriteSessionListener(fDocumentRewriteSessionListener);
|
|
4664 }
|
|
4665
|
|
4666 super.inputChanged(newInput, oldInput);
|
|
4667
|
|
4668 if (newInput instanceof IDocumentExtension4) {
|
|
4669 IDocumentExtension4 document= (IDocumentExtension4) newInput;
|
|
4670 document.addDocumentRewriteSessionListener(fDocumentRewriteSessionListener);
|
|
4671 }
|
|
4672
|
|
4673 IDocument newDocument= (IDocument) newInput;
|
|
4674 if (newDocument !is null) {
|
|
4675 newDocument.addPositionCategory(MARK_POSITION_CATEGORY);
|
|
4676 newDocument.addPositionUpdater(fMarkPositionUpdater);
|
|
4677 }
|
|
4678 }
|
|
4679
|
|
4680 /**
|
|
4681 * Informs all text listeners about the change of the viewer's redraw state.
|
|
4682 * @since 2.0
|
|
4683 */
|
|
4684 private void fireRedrawChanged() {
|
|
4685 fWidgetCommand.start= 0;
|
|
4686 fWidgetCommand.length= 0;
|
|
4687 fWidgetCommand.text= null;
|
|
4688 fWidgetCommand.event= null;
|
|
4689 updateTextListeners(fWidgetCommand);
|
|
4690 }
|
|
4691
|
|
4692 /**
|
|
4693 * Enables the redrawing of this text viewer.
|
|
4694 * @since 2.0
|
|
4695 */
|
|
4696 protected void enabledRedrawing() {
|
|
4697 enabledRedrawing(-1);
|
|
4698 }
|
|
4699 /**
|
|
4700 * Enables the redrawing of this text viewer.
|
|
4701 *
|
|
4702 * @param topIndex the top index to be set or <code>-1</code>
|
|
4703 * @since 3.0
|
|
4704 */
|
|
4705 protected void enabledRedrawing(int topIndex) {
|
|
4706 if (fDocumentAdapter instanceof IDocumentAdapterExtension) {
|
|
4707 IDocumentAdapterExtension extension= (IDocumentAdapterExtension) fDocumentAdapter;
|
|
4708 StyledText textWidget= getTextWidget();
|
|
4709 if (textWidget !is null && !textWidget.isDisposed()) {
|
|
4710 extension.resumeForwardingDocumentChanges();
|
|
4711 if (topIndex > -1) {
|
|
4712 try {
|
|
4713 setTopIndex(topIndex);
|
|
4714 } catch (IllegalArgumentException x) {
|
|
4715 // changes don't allow for the previous top pixel
|
|
4716 }
|
|
4717 }
|
|
4718 }
|
|
4719 }
|
|
4720
|
|
4721 if (fViewerState !is null) {
|
|
4722 fViewerState.restore(topIndex is -1);
|
|
4723 fViewerState= null;
|
|
4724 }
|
|
4725
|
|
4726 if (fTextWidget !is null && !fTextWidget.isDisposed())
|
|
4727 fTextWidget.setRedraw(true);
|
|
4728
|
|
4729 fireRedrawChanged();
|
|
4730 }
|
|
4731
|
|
4732 /**
|
|
4733 * Disables the redrawing of this text viewer. Subclasses may extend.
|
|
4734 * @since 2.0
|
|
4735 */
|
|
4736 protected void disableRedrawing() {
|
|
4737 if (fViewerState is null)
|
|
4738 fViewerState= new ViewerState();
|
|
4739
|
|
4740 if (fDocumentAdapter instanceof IDocumentAdapterExtension) {
|
|
4741 IDocumentAdapterExtension extension= (IDocumentAdapterExtension) fDocumentAdapter;
|
|
4742 extension.stopForwardingDocumentChanges();
|
|
4743 }
|
|
4744
|
|
4745 if (fTextWidget !is null && !fTextWidget.isDisposed())
|
|
4746 fTextWidget.setRedraw(false);
|
|
4747
|
|
4748 fireRedrawChanged();
|
|
4749 }
|
|
4750
|
|
4751 /*
|
|
4752 * @see ITextViewerExtension#setRedraw(bool)
|
|
4753 * @since 2.0
|
|
4754 */
|
|
4755 public final void setRedraw(bool redraw) {
|
|
4756 setRedraw(redraw, -1);
|
|
4757 }
|
|
4758
|
|
4759 /**
|
|
4760 * Basically same functionality as
|
|
4761 * <code>ITextViewerExtension.setRedraw(bool)</code>. Adds a way for
|
|
4762 * subclasses to pass in a desired top index that should be used when
|
|
4763 * <code>redraw</code> is <code>true</code>. If <code>topIndex</code>
|
|
4764 * is -1, this method is identical to
|
|
4765 * <code>ITextViewerExtension.setRedraw(bool)</code>.
|
|
4766 *
|
|
4767 * @see ITextViewerExtension#setRedraw(bool)
|
|
4768 *
|
|
4769 * @param redraw
|
|
4770 * @param topIndex
|
|
4771 * @since 3.0
|
|
4772 */
|
|
4773 protected final void setRedraw(bool redraw, int topIndex) {
|
|
4774 if (!redraw) {
|
|
4775
|
|
4776 ++ fRedrawCounter;
|
|
4777 if (fRedrawCounter is 1)
|
|
4778 disableRedrawing();
|
|
4779
|
|
4780 } else {
|
|
4781 -- fRedrawCounter;
|
|
4782 if (fRedrawCounter is 0) {
|
|
4783 if (topIndex is -1)
|
|
4784 enabledRedrawing();
|
|
4785 else
|
|
4786 enabledRedrawing(topIndex);
|
|
4787 }
|
|
4788 }
|
|
4789 }
|
|
4790
|
|
4791 /**
|
|
4792 * Returns whether this viewer redraws itself.
|
|
4793 *
|
|
4794 * @return <code>true</code> if this viewer redraws itself
|
|
4795 * @since 2.0
|
|
4796 */
|
|
4797 protected final bool redraws() {
|
|
4798 return fRedrawCounter <= 0;
|
|
4799 }
|
|
4800
|
|
4801 /**
|
|
4802 * Starts the sequential rewrite mode of the viewer's document.
|
|
4803 *
|
|
4804 * @param normalized <code>true</code> if the rewrite is performed from the start to the end of the document
|
|
4805 * @since 2.0
|
|
4806 * @deprecated since 3.1 use {@link IDocumentExtension4#startRewriteSession(DocumentRewriteSessionType)} instead
|
|
4807 */
|
|
4808 protected final void startSequentialRewriteMode(bool normalized) {
|
|
4809 IDocument document= getDocument();
|
|
4810 if (document instanceof IDocumentExtension) {
|
|
4811 IDocumentExtension extension= (IDocumentExtension) document;
|
|
4812 extension.startSequentialRewrite(normalized);
|
|
4813 }
|
|
4814 }
|
|
4815
|
|
4816 /**
|
|
4817 * Sets the sequential rewrite mode of the viewer's document.
|
|
4818 *
|
|
4819 * @since 2.0
|
|
4820 * @deprecated since 3.1 use {@link IDocumentExtension4#stopRewriteSession(DocumentRewriteSession)} instead
|
|
4821 */
|
|
4822 protected final void stopSequentialRewriteMode() {
|
|
4823 IDocument document= getDocument();
|
|
4824 if (document instanceof IDocumentExtension) {
|
|
4825 IDocumentExtension extension= (IDocumentExtension) document;
|
|
4826 extension.stopSequentialRewrite();
|
|
4827 }
|
|
4828 }
|
|
4829
|
|
4830 /*
|
|
4831 * @see dwtx.jface.text.ITextViewerExtension#getRewriteTarget()
|
|
4832 * @since 2.0
|
|
4833 */
|
|
4834 public IRewriteTarget getRewriteTarget() {
|
|
4835 if (fRewriteTarget is null)
|
|
4836 fRewriteTarget= new RewriteTarget();
|
|
4837 return fRewriteTarget;
|
|
4838 }
|
|
4839
|
|
4840 /*
|
|
4841 * @see dwtx.jface.text.ITextViewerExtension2#getCurrentTextHover()
|
|
4842 */
|
|
4843 public ITextHover getCurrentTextHover() {
|
|
4844 if (fTextHoverManager is null)
|
|
4845 return null;
|
|
4846 return fTextHoverManager.getCurrentTextHover();
|
|
4847 }
|
|
4848
|
|
4849 /*
|
|
4850 * @see dwtx.jface.text.ITextViewerExtension2#getHoverEventLocation()
|
|
4851 */
|
|
4852 public Point getHoverEventLocation() {
|
|
4853 if (fTextHoverManager is null)
|
|
4854 return null;
|
|
4855 return fTextHoverManager.getHoverEventLocation();
|
|
4856 }
|
|
4857
|
|
4858 /**
|
|
4859 * Returns the paint manager of this viewer.
|
|
4860 *
|
|
4861 * @return the paint manager of this viewer
|
|
4862 * @since 2.1
|
|
4863 */
|
|
4864 protected PaintManager getPaintManager() {
|
|
4865 if (fPaintManager is null)
|
|
4866 fPaintManager= new PaintManager(this);
|
|
4867 return fPaintManager;
|
|
4868 }
|
|
4869
|
|
4870 /**
|
|
4871 * Adds the given painter to this viewer. If the painter is already registered
|
|
4872 * this method is without effect.
|
|
4873 *
|
|
4874 * @param painter the painter to be added
|
|
4875 * @since 2.1
|
|
4876 */
|
|
4877 public void addPainter(IPainter painter) {
|
|
4878 getPaintManager().addPainter(painter);
|
|
4879 }
|
|
4880
|
|
4881 /**
|
|
4882 * Removes the given painter from this viewer. If the painter has previously not been
|
|
4883 * added to this viewer this method is without effect.
|
|
4884 *
|
|
4885 * @param painter the painter to be removed
|
|
4886 * @since 2.1
|
|
4887 */
|
|
4888 public void removePainter(IPainter painter) {
|
|
4889 getPaintManager().removePainter(painter);
|
|
4890 }
|
|
4891
|
|
4892 // ----------------------------------- conversions -------------------------------------------------------
|
|
4893
|
|
4894 /**
|
|
4895 * Implements the contract of {@link ITextViewerExtension5#modelLine2WidgetLine(int)}.
|
|
4896 *
|
|
4897 * @param modelLine the model line
|
|
4898 * @return the corresponding widget line or <code>-1</code>
|
|
4899 * @since 2.1
|
|
4900 */
|
|
4901 public int modelLine2WidgetLine(int modelLine) {
|
|
4902 if (fInformationMapping is null)
|
|
4903 return modelLine;
|
|
4904
|
|
4905 try {
|
|
4906 return fInformationMapping.toImageLine(modelLine);
|
|
4907 } catch (BadLocationException x) {
|
|
4908 }
|
|
4909
|
|
4910 return -1;
|
|
4911 }
|
|
4912
|
|
4913 /**
|
|
4914 * Implements the contract of {@link ITextViewerExtension5#modelOffset2WidgetOffset(int)}.
|
|
4915 *
|
|
4916 * @param modelOffset the model offset
|
|
4917 * @return the corresponding widget offset or <code>-1</code>
|
|
4918 * @since 2.1
|
|
4919 */
|
|
4920 public int modelOffset2WidgetOffset(int modelOffset) {
|
|
4921 if (fInformationMapping is null)
|
|
4922 return modelOffset;
|
|
4923
|
|
4924 try {
|
|
4925 return fInformationMapping.toImageOffset(modelOffset);
|
|
4926 } catch (BadLocationException x) {
|
|
4927 }
|
|
4928
|
|
4929 return -1;
|
|
4930 }
|
|
4931
|
|
4932 /**
|
|
4933 * Implements the contract of {@link ITextViewerExtension5#modelRange2WidgetRange(IRegion)}.
|
|
4934 *
|
|
4935 * @param modelRange the model range
|
|
4936 * @return the corresponding widget range or <code>null</code>
|
|
4937 * @since 2.1
|
|
4938 */
|
|
4939 public IRegion modelRange2WidgetRange(IRegion modelRange) {
|
|
4940 if (fInformationMapping is null)
|
|
4941 return modelRange;
|
|
4942
|
|
4943 try {
|
|
4944
|
|
4945 if (modelRange.getLength() < 0) {
|
|
4946 Region reversed= new Region(modelRange.getOffset() + modelRange.getLength(), -modelRange.getLength());
|
|
4947 IRegion result= fInformationMapping.toImageRegion(reversed);
|
|
4948 if (result !is null)
|
|
4949 return new Region(result.getOffset() + result.getLength(), -result.getLength());
|
|
4950 }
|
|
4951 return fInformationMapping.toImageRegion(modelRange);
|
|
4952
|
|
4953 } catch (BadLocationException x) {
|
|
4954 }
|
|
4955
|
|
4956 return null;
|
|
4957 }
|
|
4958
|
|
4959 /**
|
|
4960 * Similar to {@link #modelRange2WidgetRange(IRegion)}, but more forgiving:
|
|
4961 * if <code>modelRange</code> describes a region entirely hidden in the
|
|
4962 * image, then this method returns the zero-length region at the offset of
|
|
4963 * the folded region.
|
|
4964 *
|
|
4965 * @param modelRange the model range
|
|
4966 * @return the corresponding widget range, or <code>null</code>
|
|
4967 * @since 3.1
|
|
4968 */
|
|
4969 protected IRegion modelRange2ClosestWidgetRange(IRegion modelRange) {
|
|
4970 if (!(fInformationMapping instanceof IDocumentInformationMappingExtension2))
|
|
4971 return modelRange2WidgetRange(modelRange);
|
|
4972
|
|
4973 try {
|
|
4974 if (modelRange.getLength() < 0) {
|
|
4975 Region reversed= new Region(modelRange.getOffset() + modelRange.getLength(), -modelRange.getLength());
|
|
4976 IRegion result= ((IDocumentInformationMappingExtension2) fInformationMapping).toClosestImageRegion(reversed);
|
|
4977 if (result !is null)
|
|
4978 return new Region(result.getOffset() + result.getLength(), -result.getLength());
|
|
4979 }
|
|
4980 return ((IDocumentInformationMappingExtension2) fInformationMapping).toClosestImageRegion(modelRange);
|
|
4981
|
|
4982 } catch (BadLocationException x) {
|
|
4983 }
|
|
4984
|
|
4985 return null;
|
|
4986 }
|
|
4987
|
|
4988 /**
|
|
4989 * Implements the contract of {@link ITextViewerExtension5#widgetLine2ModelLine(int)}.
|
|
4990 *
|
|
4991 * @param widgetLine the widget line
|
|
4992 * @return the corresponding model line
|
|
4993 * @since 2.1
|
|
4994 */
|
|
4995 public int widgetlLine2ModelLine(int widgetLine) {
|
|
4996 return widgetLine2ModelLine(widgetLine);
|
|
4997 }
|
|
4998
|
|
4999 /**
|
|
5000 * Implements the contract of {@link ITextViewerExtension5#widgetLine2ModelLine(int)}.
|
|
5001 *
|
|
5002 * @param widgetLine the widget line
|
|
5003 * @return the corresponding model line or <code>-1</code>
|
|
5004 * @since 3.0
|
|
5005 */
|
|
5006 public int widgetLine2ModelLine(int widgetLine) {
|
|
5007 if (fInformationMapping is null)
|
|
5008 return widgetLine;
|
|
5009
|
|
5010 try {
|
|
5011 return fInformationMapping.toOriginLine(widgetLine);
|
|
5012 } catch (BadLocationException x) {
|
|
5013 }
|
|
5014
|
|
5015 return -1;
|
|
5016 }
|
|
5017
|
|
5018 /**
|
|
5019 * Implements the contract of {@link ITextViewerExtension5#widgetOffset2ModelOffset(int)}.
|
|
5020 *
|
|
5021 * @param widgetOffset the widget offset
|
|
5022 * @return the corresponding model offset or <code>-1</code>
|
|
5023 * @since 2.1
|
|
5024 */
|
|
5025 public int widgetOffset2ModelOffset(int widgetOffset) {
|
|
5026 if (fInformationMapping is null)
|
|
5027 return widgetOffset;
|
|
5028
|
|
5029 try {
|
|
5030 return fInformationMapping.toOriginOffset(widgetOffset);
|
|
5031 } catch (BadLocationException x) {
|
|
5032 if (widgetOffset is getVisibleDocument().getLength()) {
|
|
5033 IRegion coverage= fInformationMapping.getCoverage();
|
|
5034 return coverage.getOffset() + coverage.getLength();
|
|
5035 }
|
|
5036 }
|
|
5037
|
|
5038 return -1;
|
|
5039 }
|
|
5040
|
|
5041 /**
|
|
5042 * Implements the contract of {@link ITextViewerExtension5#widgetRange2ModelRange(IRegion)}.
|
|
5043 *
|
|
5044 * @param widgetRange the widget range
|
|
5045 * @return the corresponding model range or <code>null</code>
|
|
5046 * @since 2.1
|
|
5047 */
|
|
5048 public IRegion widgetRange2ModelRange(IRegion widgetRange) {
|
|
5049 if (fInformationMapping is null)
|
|
5050 return widgetRange;
|
|
5051
|
|
5052 try {
|
|
5053
|
|
5054 if (widgetRange.getLength() < 0) {
|
|
5055 Region reveresed= new Region(widgetRange.getOffset() + widgetRange.getLength(), -widgetRange.getLength());
|
|
5056 IRegion result= fInformationMapping.toOriginRegion(reveresed);
|
|
5057 return new Region(result.getOffset() + result.getLength(), -result.getLength());
|
|
5058 }
|
|
5059
|
|
5060 return fInformationMapping.toOriginRegion(widgetRange);
|
|
5061
|
|
5062 } catch (BadLocationException x) {
|
|
5063 int modelOffset= widgetOffset2ModelOffset(widgetRange.getOffset());
|
|
5064 if (modelOffset > -1) {
|
|
5065 int modelEndOffset= widgetOffset2ModelOffset(widgetRange.getOffset() + widgetRange.getLength());
|
|
5066 if (modelEndOffset > -1)
|
|
5067 return new Region(modelOffset, modelEndOffset - modelOffset);
|
|
5068 }
|
|
5069 }
|
|
5070
|
|
5071 return null;
|
|
5072 }
|
|
5073
|
|
5074 /**
|
|
5075 * Implements the contract of {@link ITextViewerExtension5#getModelCoverage()}.
|
|
5076 *
|
|
5077 * @return the model coverage
|
|
5078 * @since 2.1
|
|
5079 */
|
|
5080 public IRegion getModelCoverage() {
|
|
5081 if (fInformationMapping is null) {
|
|
5082 IDocument document= getDocument();
|
|
5083 if (document is null)
|
|
5084 return null;
|
|
5085 return new Region(0, document.getLength());
|
|
5086 }
|
|
5087
|
|
5088 return fInformationMapping.getCoverage();
|
|
5089 }
|
|
5090
|
|
5091 /**
|
|
5092 * Returns the line of the widget whose corresponding line in the viewer's document
|
|
5093 * is closest to the given line in the viewer's document or <code>-1</code>.
|
|
5094 *
|
|
5095 * @param modelLine the line in the viewer's document
|
|
5096 * @return the line in the widget that corresponds best to the given line in the viewer's document or <code>-1</code>
|
|
5097 * @since 2.1
|
|
5098 */
|
|
5099 protected int getClosestWidgetLineForModelLine(int modelLine) {
|
|
5100 if (fInformationMapping is null)
|
|
5101 return modelLine;
|
|
5102
|
|
5103 try {
|
|
5104 return fInformationMapping.toClosestImageLine(modelLine);
|
|
5105 } catch (BadLocationException x) {
|
|
5106 }
|
|
5107
|
|
5108 return -1;
|
|
5109 }
|
|
5110
|
|
5111 /**
|
|
5112 * Translates a style range given relative to the viewer's document into style
|
|
5113 * ranges relative to the viewer's widget or <code>null</code>.
|
|
5114 *
|
|
5115 * @param range the style range in the coordinates of the viewer's document
|
|
5116 * @return the style range in the coordinates of the viewer's widget or <code>null</code>
|
|
5117 * @since 2.1
|
|
5118 */
|
|
5119 protected StyleRange modelStyleRange2WidgetStyleRange(StyleRange range) {
|
|
5120 IRegion region= modelRange2WidgetRange(new Region(range.start, range.length));
|
|
5121 if (region !is null) {
|
|
5122 StyleRange result= (StyleRange) range.clone();
|
|
5123 result.start= region.getOffset();
|
|
5124 result.length= region.getLength();
|
|
5125 return result;
|
|
5126 }
|
|
5127 return null;
|
|
5128 }
|
|
5129
|
|
5130 /**
|
|
5131 * Same as {@link #modelRange2WidgetRange(IRegion)} just for a {@link dwtx.jface.text.Position}.
|
|
5132 *
|
|
5133 * @param modelPosition the position describing a range in the viewer's document
|
|
5134 * @return a region describing a range in the viewer's widget
|
|
5135 * @since 2.1
|
|
5136 */
|
|
5137 protected IRegion modelRange2WidgetRange(Position modelPosition) {
|
|
5138 return modelRange2WidgetRange(new Region(modelPosition.getOffset(), modelPosition.getLength()));
|
|
5139 }
|
|
5140
|
|
5141 /**
|
|
5142 * Translates the widget region of the given verify event into
|
|
5143 * the corresponding region of the viewer's document.
|
|
5144 *
|
|
5145 * @param event the verify event
|
|
5146 * @return the region of the viewer's document corresponding to the verify event
|
|
5147 * @since 2.1
|
|
5148 */
|
|
5149 protected IRegion event2ModelRange(VerifyEvent event) {
|
|
5150
|
|
5151 Region region= null;
|
|
5152 if (event.start <= event.end)
|
|
5153 region= new Region(event.start, event.end - event.start);
|
|
5154 else
|
|
5155 region= new Region(event.end, event.start - event.end);
|
|
5156
|
|
5157 return widgetRange2ModelRange(region);
|
|
5158 }
|
|
5159
|
|
5160 /**
|
|
5161 * Translates the given widget selection into the corresponding region
|
|
5162 * of the viewer's document or returns <code>null</code> if this fails.
|
|
5163 *
|
|
5164 * @param widgetSelection the widget selection
|
|
5165 * @return the region of the viewer's document corresponding to the widget selection or <code>null</code>
|
|
5166 * @since 2.1
|
|
5167 */
|
|
5168 protected Point widgetSelection2ModelSelection(Point widgetSelection) {
|
|
5169 IRegion region= new Region(widgetSelection.x, widgetSelection.y);
|
|
5170 region= widgetRange2ModelRange(region);
|
|
5171 return region is null ? null : new Point(region.getOffset(), region.getLength());
|
|
5172 }
|
|
5173
|
|
5174 /**
|
|
5175 * Translates the given selection range of the viewer's document into
|
|
5176 * the corresponding widget range or returns <code>null</code> of this fails.
|
|
5177 *
|
|
5178 * @param modelSelection the selection range of the viewer's document
|
|
5179 * @return the widget range corresponding to the selection range or <code>null</code>
|
|
5180 * @since 2.1
|
|
5181 */
|
|
5182 protected Point modelSelection2WidgetSelection(Point modelSelection) {
|
|
5183 if (fInformationMapping is null)
|
|
5184 return modelSelection;
|
|
5185
|
|
5186 try {
|
|
5187 IRegion region= new Region(modelSelection.x, modelSelection.y);
|
|
5188 region= fInformationMapping.toImageRegion(region);
|
|
5189 if (region !is null)
|
|
5190 return new Point(region.getOffset(), region.getLength());
|
|
5191 } catch (BadLocationException x) {
|
|
5192 }
|
|
5193
|
|
5194 return null;
|
|
5195 }
|
|
5196
|
|
5197 /**
|
|
5198 * Implements the contract of {@link ITextViewerExtension5#widgetLineOfWidgetOffset(int)}.
|
|
5199 *
|
|
5200 * @param widgetOffset the widget offset
|
|
5201 * @return the corresponding widget line or <code>-1</code>
|
|
5202 * @since 2.1
|
|
5203 */
|
|
5204 public int widgetLineOfWidgetOffset(int widgetOffset) {
|
|
5205 IDocument document= getVisibleDocument();
|
|
5206 if (document !is null) {
|
|
5207 try {
|
|
5208 return document.getLineOfOffset(widgetOffset);
|
|
5209 } catch (BadLocationException e) {
|
|
5210 }
|
|
5211 }
|
|
5212 return -1;
|
|
5213 }
|
|
5214
|
|
5215 /*
|
|
5216 * @see dwtx.jface.text.ITextViewerExtension4#moveFocusToWidgetToken()
|
|
5217 * @since 3.0
|
|
5218 */
|
|
5219 public bool moveFocusToWidgetToken() {
|
|
5220 if (fWidgetTokenKeeper instanceof IWidgetTokenKeeperExtension) {
|
|
5221 IWidgetTokenKeeperExtension extension= (IWidgetTokenKeeperExtension) fWidgetTokenKeeper;
|
|
5222 return extension.setFocus(this);
|
|
5223 }
|
|
5224 return false;
|
|
5225 }
|
|
5226
|
|
5227 /**
|
|
5228 * Sets the document partitioning of this viewer. The partitioning is used by this viewer to
|
|
5229 * access partitioning information of the viewers input document.
|
|
5230 *
|
|
5231 * @param partitioning the partitioning name
|
|
5232 * @since 3.0
|
|
5233 */
|
|
5234 public void setDocumentPartitioning(String partitioning) {
|
|
5235 fPartitioning= partitioning;
|
|
5236 }
|
|
5237
|
|
5238 /**
|
|
5239 * Returns the document partitioning for this viewer.
|
|
5240 *
|
|
5241 * @return the document partitioning for this viewer
|
|
5242 * @since 3.0
|
|
5243 */
|
|
5244 protected String getDocumentPartitioning() {
|
|
5245 return fPartitioning;
|
|
5246 }
|
|
5247
|
|
5248 //---- Text presentation listeners ----
|
|
5249
|
|
5250 /*
|
|
5251 * @see ITextViewerExtension4#addTextPresentationListener(ITextPresentationListener)
|
|
5252 * @since 3.0
|
|
5253 */
|
|
5254 public void addTextPresentationListener(ITextPresentationListener listener) {
|
|
5255
|
|
5256 Assert.isNotNull(listener);
|
|
5257
|
|
5258 if (fTextPresentationListeners is null)
|
|
5259 fTextPresentationListeners= new ArrayList();
|
|
5260
|
|
5261 if (!fTextPresentationListeners.contains(listener))
|
|
5262 fTextPresentationListeners.add(listener);
|
|
5263 }
|
|
5264
|
|
5265 /*
|
|
5266 * @see ITextViewerExtension4#removeTextPresentationListener(ITextPresentationListener)
|
|
5267 * @since 3.0
|
|
5268 */
|
|
5269 public void removeTextPresentationListener(ITextPresentationListener listener) {
|
|
5270
|
|
5271 Assert.isNotNull(listener);
|
|
5272
|
|
5273 if (fTextPresentationListeners !is null) {
|
|
5274 fTextPresentationListeners.remove(listener);
|
|
5275 if (fTextPresentationListeners.size() is 0)
|
|
5276 fTextPresentationListeners= null;
|
|
5277 }
|
|
5278 }
|
|
5279
|
|
5280 /*
|
|
5281 * @see dwtx.jface.text.IEditingSupportRegistry#registerHelper(dwtx.jface.text.IEditingSupport)
|
|
5282 * @since 3.1
|
|
5283 */
|
|
5284 public void register(IEditingSupport helper) {
|
|
5285 Assert.isLegal(helper !is null);
|
|
5286 fEditorHelpers.add(helper);
|
|
5287 }
|
|
5288
|
|
5289 /*
|
|
5290 * @see dwtx.jface.text.IEditingSupportRegistry#deregisterHelper(dwtx.jface.text.IEditingSupport)
|
|
5291 * @since 3.1
|
|
5292 */
|
|
5293 public void unregister(IEditingSupport helper) {
|
|
5294 fEditorHelpers.remove(helper);
|
|
5295 }
|
|
5296
|
|
5297 /*
|
|
5298 * @see dwtx.jface.text.IEditingSupportRegistry#getCurrentHelpers()
|
|
5299 * @since 3.1
|
|
5300 */
|
|
5301 public IEditingSupport[] getRegisteredSupports() {
|
|
5302 return (IEditingSupport[]) fEditorHelpers.toArray(new IEditingSupport[fEditorHelpers.size()]);
|
|
5303 }
|
|
5304
|
|
5305 /*
|
|
5306 * @see dwtx.jface.text.ITextViewerExtension6#setHyperlinkDetectors(dwtx.jface.text.hyperlink.IHyperlinkDetector[], int)
|
|
5307 * @since 3.1
|
|
5308 */
|
|
5309 public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors, int eventStateMask) {
|
|
5310 if (fHyperlinkDetectors !is null) {
|
|
5311 for (int i= 0; i < fHyperlinkDetectors.length; i++) {
|
|
5312 if (fHyperlinkDetectors[i] instanceof IHyperlinkDetectorExtension)
|
|
5313 ((IHyperlinkDetectorExtension)fHyperlinkDetectors[i]).dispose();
|
|
5314 }
|
|
5315 }
|
|
5316
|
|
5317 bool enable= hyperlinkDetectors !is null && hyperlinkDetectors.length > 0;
|
|
5318 fHyperlinkStateMask= eventStateMask;
|
|
5319 fHyperlinkDetectors= hyperlinkDetectors;
|
|
5320 if (enable) {
|
|
5321 if (fHyperlinkManager !is null) {
|
|
5322 fHyperlinkManager.setHyperlinkDetectors(fHyperlinkDetectors);
|
|
5323 fHyperlinkManager.setHyperlinkStateMask(fHyperlinkStateMask);
|
|
5324 }
|
|
5325 ensureHyperlinkManagerInstalled();
|
|
5326 } else {
|
|
5327 if (fHyperlinkManager !is null)
|
|
5328 fHyperlinkManager.uninstall();
|
|
5329 fHyperlinkManager= null;
|
|
5330 }
|
|
5331 }
|
|
5332
|
|
5333 /**
|
|
5334 * Sets the hyperlink presenter.
|
|
5335 * <p>
|
|
5336 * This is only valid as long as the hyperlink manager hasn't
|
|
5337 * been created yet.
|
|
5338 * </p>
|
|
5339 *
|
|
5340 * @param hyperlinkPresenter the hyperlink presenter
|
|
5341 * @throws IllegalStateException if the hyperlink manager has already been created
|
|
5342 * @since 3.1
|
|
5343 */
|
|
5344 public void setHyperlinkPresenter(IHyperlinkPresenter hyperlinkPresenter) throws IllegalStateException {
|
|
5345 if (fHyperlinkManager !is null)
|
|
5346 throw new IllegalStateException();
|
|
5347
|
|
5348 fHyperlinkPresenter= hyperlinkPresenter;
|
|
5349 ensureHyperlinkManagerInstalled();
|
|
5350 }
|
|
5351
|
|
5352 /**
|
|
5353 * Ensures that the hyperlink manager has been
|
|
5354 * installed if a hyperlink detector is available.
|
|
5355 *
|
|
5356 * @since 3.1
|
|
5357 */
|
|
5358 private void ensureHyperlinkManagerInstalled() {
|
|
5359 if (fHyperlinkDetectors !is null && fHyperlinkDetectors.length > 0 && fHyperlinkPresenter !is null && fHyperlinkManager is null) {
|
|
5360 DETECTION_STRATEGY strategy= fHyperlinkPresenter.canShowMultipleHyperlinks() ? HyperlinkManager.ALL : HyperlinkManager.FIRST;
|
|
5361 fHyperlinkManager= new HyperlinkManager(strategy);
|
|
5362 fHyperlinkManager.install(this, fHyperlinkPresenter, fHyperlinkDetectors, fHyperlinkStateMask);
|
|
5363 }
|
|
5364 }
|
|
5365
|
|
5366 /*
|
|
5367 * @see dwtx.jface.text.ITextViewerExtension7#setTabsToSpacesConverter(dwtx.jface.text.IAutoEditStrategy)
|
|
5368 * @since 3.3
|
|
5369 */
|
|
5370 public void setTabsToSpacesConverter(IAutoEditStrategy converter) {
|
|
5371 fTabsToSpacesConverter= converter;
|
|
5372 }
|
|
5373
|
|
5374 }
|