KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > TextViewer


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 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  *******************************************************************************/

11 package org.eclipse.jface.text;
12
13 import java.util.ArrayList JavaDoc;
14 import java.util.Arrays JavaDoc;
15 import java.util.HashMap JavaDoc;
16 import java.util.HashSet JavaDoc;
17 import java.util.Iterator JavaDoc;
18 import java.util.List JavaDoc;
19 import java.util.Map JavaDoc;
20 import java.util.Set JavaDoc;
21 import java.util.regex.PatternSyntaxException JavaDoc;
22
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.custom.LineBackgroundEvent;
25 import org.eclipse.swt.custom.LineBackgroundListener;
26 import org.eclipse.swt.custom.MovementEvent;
27 import org.eclipse.swt.custom.MovementListener;
28 import org.eclipse.swt.custom.ST;
29 import org.eclipse.swt.custom.StyleRange;
30 import org.eclipse.swt.custom.StyledText;
31 import org.eclipse.swt.custom.VerifyKeyListener;
32 import org.eclipse.swt.events.ControlEvent;
33 import org.eclipse.swt.events.ControlListener;
34 import org.eclipse.swt.events.DisposeEvent;
35 import org.eclipse.swt.events.DisposeListener;
36 import org.eclipse.swt.events.KeyEvent;
37 import org.eclipse.swt.events.KeyListener;
38 import org.eclipse.swt.events.MouseAdapter;
39 import org.eclipse.swt.events.MouseEvent;
40 import org.eclipse.swt.events.MouseListener;
41 import org.eclipse.swt.events.SelectionEvent;
42 import org.eclipse.swt.events.SelectionListener;
43 import org.eclipse.swt.events.TraverseEvent;
44 import org.eclipse.swt.events.TraverseListener;
45 import org.eclipse.swt.events.VerifyEvent;
46 import org.eclipse.swt.events.VerifyListener;
47 import org.eclipse.swt.graphics.Color;
48 import org.eclipse.swt.graphics.GC;
49 import org.eclipse.swt.graphics.Point;
50 import org.eclipse.swt.graphics.Rectangle;
51 import org.eclipse.swt.printing.PrintDialog;
52 import org.eclipse.swt.printing.Printer;
53 import org.eclipse.swt.printing.PrinterData;
54 import org.eclipse.swt.widgets.Composite;
55 import org.eclipse.swt.widgets.Control;
56 import org.eclipse.swt.widgets.Display;
57 import org.eclipse.swt.widgets.Event;
58 import org.eclipse.swt.widgets.Listener;
59 import org.eclipse.swt.widgets.ScrollBar;
60
61 import org.eclipse.core.runtime.Assert;
62
63 import org.eclipse.jface.internal.text.NonDeletingPositionUpdater;
64 import org.eclipse.jface.viewers.IPostSelectionProvider;
65 import org.eclipse.jface.viewers.ISelection;
66 import org.eclipse.jface.viewers.ISelectionChangedListener;
67 import org.eclipse.jface.viewers.ISelectionProvider;
68 import org.eclipse.jface.viewers.SelectionChangedEvent;
69 import org.eclipse.jface.viewers.Viewer;
70
71 import org.eclipse.jface.text.hyperlink.HyperlinkManager;
72 import org.eclipse.jface.text.hyperlink.IHyperlinkDetector;
73 import org.eclipse.jface.text.hyperlink.IHyperlinkDetectorExtension;
74 import org.eclipse.jface.text.hyperlink.IHyperlinkPresenter;
75 import org.eclipse.jface.text.projection.ChildDocument;
76 import org.eclipse.jface.text.projection.ChildDocumentManager;
77
78
79 /**
80  * SWT based implementation of {@link ITextViewer} and its extension interfaces.
81  * Once the viewer and its SWT control have been created the viewer can only
82  * indirectly be disposed by disposing its SWT control.
83  * <p>
84  * Clients are supposed to instantiate a text viewer and subsequently to
85  * communicate with it exclusively using the
86  * {@link org.eclipse.jface.text.ITextViewer} interface or any of the
87  * implemented extension interfaces.
88  * <p>
89  * A text viewer serves as text operation target. It only partially supports the
90  * external control of the enable state of its text operations. A text viewer is
91  * also a widget token owner. Anything that wants to display an overlay window
92  * on top of a text viewer should implement the
93  * {@link org.eclipse.jface.text.IWidgetTokenKeeper} interface and participate
94  * in the widget token negotiation between the text viewer and all its potential
95  * widget token keepers.
96  * <p>
97  * This class is not intended to be subclassed outside the JFace Text component.</p>
98  */

99 public class TextViewer extends Viewer implements
100                     ITextViewer, ITextViewerExtension, ITextViewerExtension2, ITextViewerExtension4, ITextViewerExtension6, ITextViewerExtension7,
101                     IEditingSupportRegistry, ITextOperationTarget, ITextOperationTargetExtension,
102                     IWidgetTokenOwner, IWidgetTokenOwnerExtension, IPostSelectionProvider {
103
104     /** Internal flag to indicate the debug state. */
105     public static final boolean TRACE_ERRORS= false;
106     /** Internal flag to indicate the debug state. */
107     private static final boolean TRACE_DOUBLE_CLICK= false;
108
109     /**
110      * Represents a replace command that brings the text viewer's text widget
111      * back in synchronization with text viewer's document after the document
112      * has been changed.
113      */

114     protected class WidgetCommand {
115
116         /** The document event encapsulated by this command. */
117         public DocumentEvent event;
118         /** The start of the event. */
119         public int start;
120         /** The length of the event. */
121         public int length;
122         /** The inserted and replaced text segments of <code>event</code>. */
123         public String JavaDoc text;
124         /** The replaced text segments of <code>event</code>. */
125         public String JavaDoc preservedText;
126
127         /**
128          * Translates a document event into the presentation coordinates of this text viewer.
129          *
130          * @param e the event to be translated
131          */

132         public void setEvent(DocumentEvent e) {
133
134             event= e;
135
136             start= e.getOffset();
137             length= e.getLength();
138             text= e.getText();
139
140             if (length != 0) {
141                 try {
142
143                     if (e instanceof SlaveDocumentEvent) {
144                         SlaveDocumentEvent slave= (SlaveDocumentEvent) e;
145                         DocumentEvent master= slave.getMasterEvent();
146                         if (master != null)
147                             preservedText= master.getDocument().get(master.getOffset(), master.getLength());
148                     } else {
149                         preservedText= e.getDocument().get(e.getOffset(), e.getLength());
150                     }
151
152                 } catch (BadLocationException x) {
153                     preservedText= null;
154                     if (TRACE_ERRORS)
155                         System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.WidgetCommand.setEvent")); //$NON-NLS-1$
156
}
157             } else
158                 preservedText= null;
159         }
160     }
161
162
163     /**
164      * Connects a text double click strategy to this viewer's text widget.
165      * Calls the double click strategies when the mouse has
166      * been clicked inside the text editor.
167      */

168     class TextDoubleClickStrategyConnector extends MouseAdapter implements MovementListener {
169
170         /** Internal flag to remember the last double-click selection. */
171         private Point fDoubleClickSelection;
172
173         /*
174          * @see org.eclipse.swt.events.MouseAdapter#mouseUp(org.eclipse.swt.events.MouseEvent)
175          * @since 3.2
176          */

177         public void mouseUp(MouseEvent e) {
178             fDoubleClickSelection= null;
179         }
180
181         /*
182          * @see org.eclipse.swt.custom.MovementListener#getNextOffset(org.eclipse.swt.custom.MovementEvent)
183          * @since 3.3
184          */

185         public void getNextOffset(MovementEvent event) {
186             if (event.movement != SWT.MOVEMENT_WORD_END)
187                 return;
188             
189             if (TRACE_DOUBLE_CLICK) {
190                 System.out.println("\n+++"); //$NON-NLS-1$
191
print(event);
192             }
193             
194             if (fDoubleClickSelection != null) {
195                 if (fDoubleClickSelection.x <= event.offset && event.offset <= fDoubleClickSelection.y)
196                     event.newOffset= fDoubleClickSelection.y;
197             }
198         }
199
200         /*
201          * @see org.eclipse.swt.custom.MovementListener#getPreviousOffset(org.eclipse.swt.custom.MovementEvent)
202          * @since 3.3
203          */

204         public void getPreviousOffset(MovementEvent event) {
205             if (event.movement != SWT.MOVEMENT_WORD_START)
206                 return;
207             
208             if (TRACE_DOUBLE_CLICK) {
209                 System.out.println("\n---"); //$NON-NLS-1$
210
print(event);
211             }
212             if (fDoubleClickSelection == null) {
213                 ITextDoubleClickStrategy s= (ITextDoubleClickStrategy) selectContentTypePlugin(getSelectedRange().x, fDoubleClickStrategies);
214                 if (s != null) {
215                     StyledText textWidget= getTextWidget();
216                     s.doubleClicked(TextViewer.this);
217                     fDoubleClickSelection= textWidget.getSelection();
218                     event.newOffset= fDoubleClickSelection.x;
219                     if (TRACE_DOUBLE_CLICK)
220                         System.out.println("- setting selection: x= " + fDoubleClickSelection.x + ", y= " + fDoubleClickSelection.y); //$NON-NLS-1$ //$NON-NLS-2$
221
}
222             } else {
223                 if (fDoubleClickSelection.x <= event.offset && event.offset <= fDoubleClickSelection.y)
224                     event.newOffset= fDoubleClickSelection.x;
225             }
226         }
227     }
228
229     /**
230      * Print trace info about <code>MovementEvent</code>.
231      *
232      * @param e the event to print
233      * @since 3.3
234      */

235     private void print(MovementEvent e) {
236         System.out.println("line offset: " + e.lineOffset); //$NON-NLS-1$
237
System.out.println("line: " + e.lineText); //$NON-NLS-1$
238
System.out.println("type: " + e.movement); //$NON-NLS-1$
239
System.out.println("offset: " + e.offset); //$NON-NLS-1$
240
System.out.println("newOffset: " + e.newOffset); //$NON-NLS-1$
241
}
242
243     /**
244      * Monitors the area of the viewer's document that is visible in the viewer.
245      * If the area might have changed, it informs the text viewer about this
246      * potential change and its origin. The origin is internally used for optimization
247      * purposes.
248      */

249     class ViewportGuard extends MouseAdapter
250         implements ControlListener, KeyListener, SelectionListener {
251
252         /*
253          * @see ControlListener#controlResized(ControlEvent)
254          */

255         public void controlResized(ControlEvent e) {
256             updateViewportListeners(RESIZE);
257         }
258
259         /*
260          * @see ControlListener#controlMoved(ControlEvent)
261          */

262         public void controlMoved(ControlEvent e) {
263         }
264
265         /*
266          * @see KeyListener#keyReleased
267          */

268         public void keyReleased(KeyEvent e) {
269             updateViewportListeners(KEY);
270         }
271
272         /*
273          * @see KeyListener#keyPressed
274          */

275         public void keyPressed(KeyEvent e) {
276             updateViewportListeners(KEY);
277         }
278
279         /*
280          * @see MouseListener#mouseUp
281          */

282         public void mouseUp(MouseEvent e) {
283             if (fTextWidget != null)
284                 fTextWidget.removeSelectionListener(this);
285             updateViewportListeners(MOUSE_END);
286         }
287
288         /*
289          * @see MouseListener#mouseDown
290          */

291         public void mouseDown(MouseEvent e) {
292             if (fTextWidget != null)
293                 fTextWidget.addSelectionListener(this);
294         }
295
296         /*
297          * @see SelectionListener#widgetSelected
298          */

299         public void widgetSelected(SelectionEvent e) {
300             if (e.widget == fScroller)
301                 updateViewportListeners(SCROLLER);
302             else
303                 updateViewportListeners(MOUSE);
304         }
305
306         /*
307          * @see SelectionListener#widgetDefaultSelected
308          */

309         public void widgetDefaultSelected(SelectionEvent e) {}
310     }
311
312     /**
313      * This position updater is used to keep the selection during text shift operations.
314      */

315     static class ShiftPositionUpdater extends DefaultPositionUpdater {
316
317         /**
318          * Creates the position updater for the given category.
319          *
320          * @param category the category this updater takes care of
321          */

322         protected ShiftPositionUpdater(String JavaDoc category) {
323             super(category);
324         }
325
326         /**
327          * If an insertion happens at the selection's start offset,
328          * the position is extended rather than shifted.
329          */

330         protected void adaptToInsert() {
331
332             int myStart= fPosition.offset;
333             int myEnd= fPosition.offset + fPosition.length -1;
334             myEnd= Math.max(myStart, myEnd);
335
336             int yoursStart= fOffset;
337             int yoursEnd= fOffset + fReplaceLength -1;
338             yoursEnd= Math.max(yoursStart, yoursEnd);
339
340             if (myEnd < yoursStart)
341                 return;
342
343             if (myStart <= yoursStart) {
344                 fPosition.length += fReplaceLength;
345                 return;
346             }
347
348             if (myStart > yoursStart)
349                 fPosition.offset += fReplaceLength;
350         }
351     }
352
353     /**
354      * Internal document listener on the visible document.
355      */

356     class VisibleDocumentListener implements IDocumentListener {
357
358         /*
359          * @see IDocumentListener#documentAboutToBeChanged
360          */

361         public void documentAboutToBeChanged(DocumentEvent e) {
362             if (e.getDocument() == getVisibleDocument())
363                 fWidgetCommand.setEvent(e);
364             handleVisibleDocumentAboutToBeChanged(e);
365         }
366
367         /*
368          * @see IDocumentListener#documentChanged
369          */

370         public void documentChanged(DocumentEvent e) {
371             if (fWidgetCommand.event == e)
372                 updateTextListeners(fWidgetCommand);
373             fLastSentSelectionChange= null;
374             handleVisibleDocumentChanged(e);
375         }
376     }
377
378     /**
379      * Internal verify listener.
380      */

381     class TextVerifyListener implements VerifyListener {
382
383         /**
384          * Indicates whether verify events are forwarded or ignored.
385          * @since 2.0
386          */

387         private boolean fForward= true;
388
389         /**
390          * Tells the listener to forward received events.
391          *
392          * @param forward <code>true</code> if forwarding should be enabled.
393          * @since 2.0
394          */

395         public void forward(boolean forward) {
396             fForward= forward;
397         }
398
399         /*
400          * @see VerifyListener#verifyText(VerifyEvent)
401          */

402         public void verifyText(VerifyEvent e) {
403             if (fForward)
404                 handleVerifyEvent(e);
405         }
406     }
407
408     /**
409      * The viewer's manager responsible for registered verify key listeners.
410      * Uses batches rather than robust iterators because of performance issues.
411      * <p>
412      * The implementation is reentrant, i.e. installed listeners may trigger
413      * further <code>VerifyKeyEvent</code>s that may cause other listeners to be
414      * installed, but not thread safe.
415      * </p>
416      * @since 2.0
417      */

418     class VerifyKeyListenersManager implements VerifyKeyListener {
419
420         /**
421          * Represents a batched addListener/removeListener command.
422          */

423         class Batch {
424             /** The index at which to insert the listener. */
425             int index;
426             /** The listener to be inserted. */
427             VerifyKeyListener listener;
428
429             /**
430              * Creates a new batch containing the given listener for the given index.
431              *
432              * @param l the listener to be added
433              * @param i the index at which to insert the listener
434              */

435             public Batch(VerifyKeyListener l, int i) {
436                 listener= l;
437                 index= i;
438             }
439         }
440
441         /** List of registered verify key listeners. */
442         private List JavaDoc fListeners= new ArrayList JavaDoc();
443         /** List of pending batches. */
444         private List JavaDoc fBatched= new ArrayList JavaDoc();
445         /** The reentrance count. */
446         private int fReentranceCount= 0;
447
448         /*
449          * @see VerifyKeyListener#verifyKey(VerifyEvent)
450          */

451         public void verifyKey(VerifyEvent event) {
452             if (fListeners.isEmpty())
453                 return;
454
455             try {
456                 fReentranceCount++;
457                 Iterator JavaDoc iterator= fListeners.iterator();
458                 while (iterator.hasNext() && event.doit) {
459                     VerifyKeyListener listener= (VerifyKeyListener) iterator.next();
460                     listener.verifyKey(event); // we might trigger reentrant calls on GTK
461
}
462             } finally {
463                 fReentranceCount--;
464             }
465             if (fReentranceCount == 0)
466                 processBatchedRequests();
467         }
468
469         /**
470          * Processes the pending batched requests.
471          */

472         private void processBatchedRequests() {
473             if (!fBatched.isEmpty()) {
474                 Iterator JavaDoc e= fBatched.iterator();
475                 while (e.hasNext()) {
476                     Batch batch= (Batch) e.next();
477                     insertListener(batch.listener, batch.index);
478                 }
479                 fBatched.clear();
480             }
481         }
482
483         /**
484          * Returns the number of registered verify key listeners.
485          *
486          * @return the number of registered verify key listeners
487          */

488         public int numberOfListeners() {
489             return fListeners.size();
490         }
491
492         /**
493          * Inserts the given listener at the given index or moves it
494          * to that index.
495          *
496          * @param listener the listener to be inserted
497          * @param index the index of the listener or -1 for remove
498          */

499         public void insertListener(VerifyKeyListener listener, int index) {
500
501             if (index == -1) {
502                 removeListener(listener);
503             } else if (listener != null) {
504
505                 if (fReentranceCount > 0) {
506
507                     fBatched.add(new Batch(listener, index));
508
509                 } else {
510
511                     int idx= -1;
512
513                     // find index based on identity
514
int size= fListeners.size();
515                     for (int i= 0; i < size; i++) {
516                         if (listener == fListeners.get(i)) {
517                             idx= i;
518                             break;
519                         }
520                     }
521
522                     // move or add it
523
if (idx != index) {
524
525                         if (idx != -1)
526                             fListeners.remove(idx);
527
528                         if (index > fListeners.size())
529                             fListeners.add(listener);
530                         else
531                             fListeners.add(index, listener);
532                     }
533
534                     if (size == 0) // checking old size, i.e. current size == size + 1
535
install();
536                 }
537             }
538         }
539
540         /**
541          * Removes the given listener.
542          *
543          * @param listener the listener to be removed
544          */

545         public void removeListener(VerifyKeyListener listener) {
546             if (listener == null)
547                 return;
548
549             if (fReentranceCount > 0) {
550
551                 fBatched.add(new Batch(listener, -1));
552
553             } else {
554
555                 int size= fListeners.size();
556                 for (int i= 0; i < size; i++) {
557                     if (listener == fListeners.get(i)) {
558                         fListeners.remove(i);
559                         if (size == 1) // checking old size, i.e. current size == size - 1
560
uninstall();
561                         return;
562                     }
563                 }
564             }
565         }
566
567         /**
568          * Installs this manager.
569          */

570         private void install() {
571             StyledText textWidget= getTextWidget();
572             if (textWidget != null && !textWidget.isDisposed())
573                 textWidget.addVerifyKeyListener(this);
574         }
575
576         /**
577          * Uninstalls this manager.
578          */

579         private void uninstall() {
580             StyledText textWidget= getTextWidget();
581             if (textWidget != null && !textWidget.isDisposed())
582                 textWidget.removeVerifyKeyListener(this);
583         }
584     }
585
586
587     /**
588      * Reification of a range in which a find replace operation is performed. This range is visually
589      * highlighted in the viewer as long as the replace operation is in progress.
590      *
591      * @since 2.0
592      */

593     class FindReplaceRange implements LineBackgroundListener, ITextListener, IPositionUpdater {
594
595         /** Internal name for the position category used to update the range. */
596         private final static String JavaDoc RANGE_CATEGORY= "org.eclipse.jface.text.TextViewer.find.range"; //$NON-NLS-1$
597

598         /** The highlight color of this range. */
599         private Color fHighlightColor;
600         /** The position used to lively update this range's extent. */
601         private Position fPosition;
602
603         /** Creates a new find/replace range with the given extent.
604          *
605          * @param range the extent of this range
606          */

607         public FindReplaceRange(IRegion range) {
608             setRange(range);
609         }
610
611         /**
612          * Sets the extent of this range.
613          *
614          * @param range the extent of this range
615          */

616         public void setRange(IRegion range) {
617             fPosition= new Position(range.getOffset(), range.getLength());
618         }
619
620         /**
621          * Returns the extent of this range.
622          *
623          * @return the extent of this range
624          */

625         public IRegion getRange() {
626             return new Region(fPosition.getOffset(), fPosition.getLength());
627         }
628
629         /**
630          * Sets the highlight color of this range. Causes the range to be redrawn.
631          *
632          * @param color the highlight color
633          */

634         public void setHighlightColor(Color color) {
635             fHighlightColor= color;
636             paint();
637         }
638
639         /*
640          * @see LineBackgroundListener#lineGetBackground(LineBackgroundEvent)
641          * @since 2.0
642          */

643         public void lineGetBackground(LineBackgroundEvent event) {
644             /* Don't use cached line information because of patched redrawing events. */
645
646             if (fTextWidget != null) {
647                 int offset= widgetOffset2ModelOffset(event.lineOffset);
648                 if (fPosition.includes(offset))
649                     event.lineBackground= fHighlightColor;
650             }
651         }
652
653         /**
654          * Installs this range. The range registers itself as background
655          * line painter and text listener. Also, it creates a category with the
656          * viewer's document to maintain its own extent.
657          */

658         public void install() {
659             TextViewer.this.addTextListener(this);
660             fTextWidget.addLineBackgroundListener(this);
661
662             IDocument document= TextViewer.this.getDocument();
663             try {
664                 document.addPositionCategory(RANGE_CATEGORY);
665                 document.addPosition(RANGE_CATEGORY, fPosition);
666                 document.addPositionUpdater(this);
667             } catch (BadPositionCategoryException e) {
668                 // should not happen
669
} catch (BadLocationException e) {
670                 // should not happen
671
}
672
673             paint();
674         }
675
676         /**
677          * Uninstalls this range.
678          * @see #install()
679          */

680         public void uninstall() {
681
682             // http://bugs.eclipse.org/bugs/show_bug.cgi?id=19612
683

684             IDocument document= TextViewer.this.getDocument();
685             if (document != null) {
686                 document.removePositionUpdater(this);
687                 document.removePosition(fPosition);
688             }
689
690             if (fTextWidget != null && !fTextWidget.isDisposed())
691                 fTextWidget.removeLineBackgroundListener(this);
692
693             TextViewer.this.removeTextListener(this);
694
695             clear();
696         }
697
698         /**
699          * Clears the highlighting of this range.
700          */

701         private void clear() {
702             if (fTextWidget != null && !fTextWidget.isDisposed())
703                 fTextWidget.redraw();
704         }
705
706         /**
707          * Paints the highlighting of this range.
708          */

709         private void paint() {
710
711             IRegion widgetRegion= modelRange2WidgetRange(fPosition);
712             int offset= widgetRegion.getOffset();
713             int length= widgetRegion.getLength();
714
715             int count= fTextWidget.getCharCount();
716             if (offset + length >= count) {
717                 length= count - offset; // clip
718

719                 Point upperLeft= fTextWidget.getLocationAtOffset(offset);
720                 Point lowerRight= fTextWidget.getLocationAtOffset(offset + length);
721                 int width= fTextWidget.getClientArea().width;
722                 int height= fTextWidget.getLineHeight(offset + length) + lowerRight.y - upperLeft.y;
723                 fTextWidget.redraw(upperLeft.x, upperLeft.y, width, height, false);
724             }
725
726             fTextWidget.redrawRange(offset, length, true);
727         }
728
729         /*
730          * @see ITextListener#textChanged(TextEvent)
731          * @since 2.0
732          */

733         public void textChanged(TextEvent event) {
734             if (event.getViewerRedrawState())
735                 paint();
736         }
737
738         /*
739          * @see IPositionUpdater#update(DocumentEvent)
740          * @since 2.0
741          */

742         public void update(DocumentEvent event) {
743             int offset= event.getOffset();
744             int length= event.getLength();
745             int delta= event.getText().length() - length;
746
747             if (offset < fPosition.getOffset())
748                 fPosition.setOffset(fPosition.getOffset() + delta);
749             else if (offset < fPosition.getOffset() + fPosition.getLength())
750                 fPosition.setLength(fPosition.getLength() + delta);
751         }
752     }
753
754     /**
755      * This viewer's find/replace target.
756      */

757     class FindReplaceTarget implements IFindReplaceTarget, IFindReplaceTargetExtension, IFindReplaceTargetExtension3 {
758
759         /** The range for this target. */
760         private FindReplaceRange fRange;
761         /** The highlight color of the range of this target. */
762         private Color fScopeHighlightColor;
763         /** The document partitioner remembered in case of a "Replace All". */
764         private Map JavaDoc fRememberedPartitioners;
765         /**
766          * The active rewrite session.
767          * @since 3.1
768          */

769         private DocumentRewriteSession fRewriteSession;
770
771         /*
772          * @see IFindReplaceTarget#getSelectionText()
773          */

774         public String JavaDoc getSelectionText() {
775             Point s= TextViewer.this.getSelectedRange();
776             if (s.x > -1 && s.y > -1) {
777                 try {
778                     IDocument document= TextViewer.this.getDocument();
779                     return document.get(s.x, s.y);
780                 } catch (BadLocationException x) {
781                 }
782             }
783             return null;
784         }
785
786         /*
787          * @see IFindReplaceTarget#replaceSelection(String)
788          */

789         public void replaceSelection(String JavaDoc text) {
790             replaceSelection(text, false);
791         }
792
793         /*
794          * @see IFindReplaceTarget#replaceSelection(String)
795          */

796         public void replaceSelection(String JavaDoc text, boolean regExReplace) {
797             Point s= TextViewer.this.getSelectedRange();
798             if (s.x > -1 && s.y > -1) {
799                 try {
800                     IRegion matchRegion= TextViewer.this.getFindReplaceDocumentAdapter().replace(text, regExReplace);
801                     int length= -1;
802                     if (matchRegion != null)
803                         length= matchRegion.getLength();
804
805                     if (text != null && length > 0)
806                         TextViewer.this.setSelectedRange(s.x, length);
807                 } catch (BadLocationException x) {
808                 }
809             }
810         }
811
812         /*
813          * @see IFindReplaceTarget#isEditable()
814          */

815         public boolean isEditable() {
816             return TextViewer.this.isEditable();
817         }
818
819         /*
820          * @see IFindReplaceTarget#getSelection()
821          */

822         public Point getSelection() {
823             Point modelSelection= TextViewer.this.getSelectedRange();
824             return modelSelection2WidgetSelection(modelSelection);
825         }
826
827         /*
828          * @see IFindReplaceTarget#findAndSelect(int, String, boolean, boolean, boolean)
829          */

830         public int findAndSelect(int widgetOffset, String JavaDoc findString, boolean searchForward, boolean caseSensitive, boolean wholeWord) {
831             try {
832                 return findAndSelect(widgetOffset, findString, searchForward, caseSensitive, wholeWord, false);
833             } catch (PatternSyntaxException JavaDoc x) {
834                 return -1;
835             }
836         }
837
838         /*
839          * @see IFindReplaceTarget#findAndSelect(int, String, boolean, boolean, boolean)
840          */

841         public int findAndSelect(int widgetOffset, String JavaDoc findString, boolean searchForward, boolean caseSensitive, boolean wholeWord, boolean regExSearch) {
842
843             int modelOffset= widgetOffset == -1 ? -1 : widgetOffset2ModelOffset(widgetOffset);
844
845             if (fRange != null) {
846                 IRegion range= fRange.getRange();
847                 modelOffset= TextViewer.this.findAndSelectInRange(modelOffset, findString, searchForward, caseSensitive, wholeWord, range.getOffset(), range.getLength(), regExSearch);
848             } else {
849                 modelOffset= TextViewer.this.findAndSelect(modelOffset, findString, searchForward, caseSensitive, wholeWord, regExSearch);
850             }
851
852             widgetOffset= modelOffset == -1 ? -1 : modelOffset2WidgetOffset(modelOffset);
853             return widgetOffset;
854         }
855
856         /*
857          * @see IFindReplaceTarget#canPerformFind()
858          */

859         public boolean canPerformFind() {
860             return TextViewer.this.canPerformFind();
861         }
862
863         /*
864          * @see IFindReplaceTargetExtension#beginSession()
865          * @since 2.0
866          */

867         public void beginSession() {
868             fRange= null;
869         }
870
871         /*
872          * @see IFindReplaceTargetExtension#endSession()
873          * @since 2.0
874          */

875         public void endSession() {
876             if (fRange != null) {
877                 fRange.uninstall();
878                 fRange= null;
879             }
880         }
881
882         /*
883          * @see IFindReplaceTargetExtension#getScope()
884          * @since 2.0
885          */

886         public IRegion getScope() {
887             return fRange == null ? null : fRange.getRange();
888         }
889
890         /*
891          * @see IFindReplaceTargetExtension#getLineSelection()
892          * @since 2.0
893          */

894         public Point getLineSelection() {
895             Point point= TextViewer.this.getSelectedRange();
896
897             try {
898                 IDocument document= TextViewer.this.getDocument();
899
900                 // beginning of line
901
int line= document.getLineOfOffset(point.x);
902                 int offset= document.getLineOffset(line);
903
904                 // end of line
905
IRegion lastLineInfo= document.getLineInformationOfOffset(point.x + point.y);
906                 int lastLine= document.getLineOfOffset(point.x + point.y);
907                 int length;
908                 if (lastLineInfo.getOffset() == point.x + point.y && lastLine > 0)
909                     length= document.getLineOffset(lastLine - 1) + document.getLineLength(lastLine - 1) - offset;
910                 else
911                     length= lastLineInfo.getOffset() + lastLineInfo.getLength() - offset;
912
913                 return new Point(offset, length);
914
915             } catch (BadLocationException e) {
916                 // should not happen
917
return new Point(point.x, 0);
918             }
919         }
920
921         /*
922          * @see IFindReplaceTargetExtension#setSelection(int, int)
923          * @since 2.0
924          */

925         public void setSelection(int modelOffset, int modelLength) {
926             TextViewer.this.setSelectedRange(modelOffset, modelLength);
927         }
928
929         /*
930          * @see IFindReplaceTargetExtension#setScope(IRegion)
931          * @since 2.0
932          */

933         public void setScope(IRegion scope) {
934             if (fRange != null)
935                 fRange.uninstall();
936
937             if (scope == null) {
938                 fRange= null;
939                 return;
940             }
941
942             fRange= new FindReplaceRange(scope);
943             fRange.setHighlightColor(fScopeHighlightColor);
944             fRange.install();
945         }
946
947         /*
948          * @see IFindReplaceTargetExtension#setScopeHighlightColor(Color)
949          * @since 2.0
950          */

951         public void setScopeHighlightColor(Color color) {
952             if (fRange != null)
953                 fRange.setHighlightColor(color);
954             fScopeHighlightColor= color;
955         }
956
957         /*
958          * @see IFindReplaceTargetExtension#setReplaceAllMode(boolean)
959          * @since 2.0
960          */

961         public void setReplaceAllMode(boolean replaceAll) {
962
963             // http://bugs.eclipse.org/bugs/show_bug.cgi?id=18232
964

965             IDocument document= TextViewer.this.getDocument();
966
967             if (replaceAll) {
968
969                 if (document instanceof IDocumentExtension4) {
970                     IDocumentExtension4 extension= (IDocumentExtension4) document;
971                     fRewriteSession= extension.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
972                 } else {
973                     TextViewer.this.setRedraw(false);
974                     TextViewer.this.startSequentialRewriteMode(false);
975
976                     if (fUndoManager != null)
977                         fUndoManager.beginCompoundChange();
978
979                     fRememberedPartitioners= TextUtilities.removeDocumentPartitioners(document);
980                 }
981
982             } else {
983
984                 if (document instanceof IDocumentExtension4) {
985                     IDocumentExtension4 extension= (IDocumentExtension4) document;
986                     extension.stopRewriteSession(fRewriteSession);
987                 } else {
988                     TextViewer.this.setRedraw(true);
989                     TextViewer.this.stopSequentialRewriteMode();
990
991                     if (fUndoManager != null)
992                         fUndoManager.endCompoundChange();
993
994                     if (fRememberedPartitioners != null)
995                         TextUtilities.addDocumentPartitioners(document, fRememberedPartitioners);
996                 }
997             }
998         }
999     }
1000
1001
1002    /**
1003     * The viewer's rewrite target.
1004     * @since 2.0
1005     */

1006    class RewriteTarget implements IRewriteTarget {
1007
1008        /*
1009         * @see org.eclipse.jface.text.IRewriteTarget#beginCompoundChange()
1010         */

1011        public void beginCompoundChange() {
1012            if (fUndoManager != null)
1013                fUndoManager.beginCompoundChange();
1014        }
1015
1016        /*
1017         * @see org.eclipse.jface.text.IRewriteTarget#endCompoundChange()
1018         */

1019        public void endCompoundChange() {
1020            if (fUndoManager != null)
1021                fUndoManager.endCompoundChange();
1022        }
1023
1024        /*
1025         * @see org.eclipse.jface.text.IRewriteTarget#getDocument()
1026         */

1027        public IDocument getDocument() {
1028            return TextViewer.this.getDocument();
1029        }
1030
1031        /*
1032         * @see org.eclipse.jface.text.IRewriteTarget#setRedraw(boolean)
1033         */

1034        public void setRedraw(boolean redraw) {
1035            TextViewer.this.setRedraw(redraw);
1036        }
1037    }
1038
1039    /**
1040     * Value object used as key in the text hover configuration table. It is
1041     * modifiable only inside this compilation unit to allow the reuse of created
1042     * objects for efficiency reasons
1043     *
1044     * @since 2.1
1045     */

1046    protected class TextHoverKey {
1047
1048        /** The content type this key belongs to */
1049        private String JavaDoc fContentType;
1050        /** The state mask */
1051        private int fStateMask;
1052
1053        /**
1054         * Creates a new text hover key for the given content type and state mask.
1055         *
1056         * @param contentType the content type
1057         * @param stateMask the state mask
1058         */

1059        protected TextHoverKey(String JavaDoc contentType, int stateMask) {
1060            Assert.isNotNull(contentType);
1061            fContentType= contentType;
1062            fStateMask= stateMask;
1063        }
1064
1065        /*
1066         * @see java.lang.Object#equals(java.lang.Object)
1067         */

1068        public boolean equals(Object JavaDoc obj) {
1069            if (obj == null || obj.getClass() != getClass())
1070                return false;
1071            TextHoverKey textHoverKey= (TextHoverKey)obj;
1072            return textHoverKey.fContentType.equals(fContentType) && textHoverKey.fStateMask == fStateMask;
1073        }
1074
1075        /*
1076         * @see java.lang.Object#hashCode()
1077         */

1078        public int hashCode() {
1079            return fStateMask << 16 | fContentType.hashCode();
1080        }
1081
1082        /**
1083         * Sets the state mask of this text hover key.
1084         *
1085         * @param stateMask the state mask
1086         */

1087        private void setStateMask(int stateMask) {
1088            fStateMask= stateMask;
1089        }
1090    }
1091    
1092    /**
1093     * Captures and remembers the viewer state (selection and visual position). {@link TextViewer.ViewerState}
1094     * instances are normally used once and then discarded, similar to the following snippet:
1095     * <pre>
1096     * ViewerState state= new ViewerState(); // remember the state
1097     * doStuff(); // operation that may call setRedraw() and perform complex document modifications
1098     * state.restore(true); // restore the remembered state
1099     * </pre>
1100     *
1101     * @since 3.3
1102     */

1103    private final class ViewerState {
1104        /** The position tracking the selection. */
1105        private Position fSelection;
1106        /** <code>true</code> if {@link #fSelection} was originally backwards. */
1107        private boolean fReverseSelection;
1108        /** <code>true</code> if the selection has been updated while in redraw(off) mode. */
1109        private boolean fSelectionSet;
1110        /** The position tracking the visually stable line. */
1111        private Position fStableLine;
1112        /** The pixel offset of the stable line measured from the client area. */
1113        private int fStablePixel;
1114
1115        /** The position updater for {@link #fSelection} and {@link #fStableLine}. */
1116        private IPositionUpdater fUpdater;
1117        /** The document that the position updater and the positions are registered with. */
1118        private IDocument fUpdaterDocument;
1119        /** The position category used by {@link #fUpdater}. */
1120        private String JavaDoc fUpdaterCategory;
1121
1122        /**
1123         * Creates a new viewer state instance and connects it to the current document.
1124         */

1125        public ViewerState() {
1126            IDocument document= getDocument();
1127            if (document != null)
1128                connect(document);
1129        }
1130
1131        /**
1132         * Returns the normalized selection, i.e. the the selection length is always non-negative.
1133         *
1134         * @return the normalized selection
1135         */

1136        public Point getSelection() {
1137            if (fSelection == null)
1138                return new Point(-1, -1);
1139            return new Point(fSelection.getOffset(), fSelection.getLength());
1140        }
1141
1142        /**
1143         * Updates the selection.
1144         *
1145         * @param offset the new selection offset
1146         * @param length the new selection length
1147         */

1148        public void updateSelection(int offset, int length) {
1149            fSelectionSet= true;
1150            if (fSelection == null)
1151                fSelection= new Position(offset, length);
1152            else
1153                updatePosition(fSelection, offset, length);
1154        }
1155
1156        /**
1157         * Restores the state and disconnects it from the document. The selection is no longer
1158         * tracked after this call.
1159         *
1160         * @param restoreViewport <code>true</code> to restore both selection and viewport,
1161         * <code>false</code> to only restore the selection
1162         */

1163        public void restore(boolean restoreViewport) {
1164            if (isConnected())
1165                disconnect();
1166            if (fSelection != null) {
1167                int offset= fSelection.getOffset();
1168                int length= fSelection.getLength();
1169                if (fReverseSelection) {
1170                    offset-= length;
1171                    length= -length;
1172                }
1173                setSelectedRange(offset, length);
1174                if (restoreViewport)
1175                    updateViewport();
1176            }
1177        }
1178
1179        /**
1180         * Updates the viewport, trying to keep the
1181         * {@linkplain StyledText#getLinePixel(int) line pixel} of the caret line stable. If the
1182         * selection has been updated while in redraw(false) mode, the new selection is revealed.
1183         */

1184        private void updateViewport() {
1185            if (fSelectionSet) {
1186                revealRange(fSelection.getOffset(), fSelection.getLength());
1187            } else if (fStableLine != null) {
1188                int stableLine;
1189                try {
1190                    stableLine= fUpdaterDocument.getLineOfOffset(fStableLine.getOffset());
1191                } catch (BadLocationException x) {
1192                    // ignore and return silently
1193
return;
1194                }
1195                int stableWidgetLine= getClosestWidgetLineForModelLine(stableLine);
1196                if (stableWidgetLine == -1)
1197                    return;
1198                int linePixel= getTextWidget().getLinePixel(stableWidgetLine);
1199                int delta= fStablePixel - linePixel;
1200                int topPixel= getTextWidget().getTopPixel();
1201                getTextWidget().setTopPixel(topPixel - delta);
1202            }
1203        }
1204
1205        /**
1206         * Remembers the viewer state.
1207         *
1208         * @param document the document to remember the state of
1209         */

1210        private void connect(IDocument document) {
1211            Assert.isLegal(document != null);
1212            Assert.isLegal(!isConnected());
1213            fUpdaterDocument= document;
1214            try {
1215                fUpdaterCategory= SELECTION_POSITION_CATEGORY + hashCode();
1216                fUpdater= new NonDeletingPositionUpdater(fUpdaterCategory);
1217                fUpdaterDocument.addPositionCategory(fUpdaterCategory);
1218                fUpdaterDocument.addPositionUpdater(fUpdater);
1219
1220                Point selectionRange= getSelectedRange();
1221                fReverseSelection= selectionRange.y < 0;
1222                int offset, length;
1223                if (fReverseSelection) {
1224                    offset= selectionRange.x + selectionRange.y;
1225                    length= -selectionRange.y;
1226                } else {
1227                    offset= selectionRange.x;
1228                    length= selectionRange.y;
1229                }
1230
1231                fSelection= new Position(offset, length);
1232                fSelectionSet= false;
1233                fUpdaterDocument.addPosition(fUpdaterCategory, fSelection);
1234
1235                int stableLine= getStableLine();
1236                int stableWidgetLine= modelLine2WidgetLine(stableLine);
1237                fStablePixel= getTextWidget().getLinePixel(stableWidgetLine);
1238                IRegion stableLineInfo= fUpdaterDocument.getLineInformation(stableLine);
1239                fStableLine= new Position(stableLineInfo.getOffset(), stableLineInfo.getLength());
1240                fUpdaterDocument.addPosition(fUpdaterCategory, fStableLine);
1241            } catch (BadPositionCategoryException e) {
1242                // cannot happen
1243
Assert.isTrue(false);
1244            } catch (BadLocationException e) {
1245                // should not happen except on concurrent modification
1246
// ignore and disconnect
1247
disconnect();
1248            }
1249        }
1250
1251        /**
1252         * Updates a position with the given information and clears its deletion state.
1253         *
1254         * @param position the position to update
1255         * @param offset the new selection offset
1256         * @param length the new selection length
1257         */

1258        private void updatePosition(Position position, int offset, int length) {
1259            position.setOffset(offset);
1260            position.setLength(length);
1261            // http://bugs.eclipse.org/bugs/show_bug.cgi?id=32795
1262
position.isDeleted= false;
1263        }
1264
1265        /**
1266         * Returns the document line to keep visually stable. If the caret line is (partially)
1267         * visible, it is returned, otherwise the topmost (partially) visible line is returned.
1268         *
1269         * @return the visually stable line of this viewer state
1270         */

1271        private int getStableLine() {
1272            int stableLine; // the model line that we try to keep stable
1273
int caretLine= getTextWidget().getLineAtOffset(getTextWidget().getCaretOffset());
1274            if (caretLine < JFaceTextUtil.getPartialTopIndex(getTextWidget()) || caretLine > JFaceTextUtil.getPartialBottomIndex(getTextWidget())) {
1275                stableLine= JFaceTextUtil.getPartialTopIndex(TextViewer.this);
1276            } else {
1277                stableLine= widgetLine2ModelLine(caretLine);
1278            }
1279            return stableLine;
1280        }
1281
1282        /**
1283         * Returns <code>true</code> if the viewer state is being tracked, <code>false</code>
1284         * otherwise.
1285         *
1286         * @return the tracking state
1287         */

1288        private boolean isConnected() {
1289            return fUpdater != null;
1290        }
1291
1292        /**
1293         * Disconnects from the document.
1294         */

1295        private void disconnect() {
1296            Assert.isTrue(isConnected());
1297            try {
1298                fUpdaterDocument.removePosition(fUpdaterCategory, fSelection);
1299                fUpdaterDocument.removePosition(fUpdaterCategory, fStableLine);
1300                fUpdaterDocument.removePositionUpdater(fUpdater);
1301                fUpdater= null;
1302                fUpdaterDocument.removePositionCategory(fUpdaterCategory);
1303                fUpdaterCategory= null;
1304            } catch (BadPositionCategoryException x) {
1305                // cannot happen
1306
Assert.isTrue(false);
1307            }
1308        }
1309    }
1310
1311    /**
1312     * Internal cursor listener i.e. aggregation of mouse and key listener.
1313     *
1314     * @since 3.0
1315     */

1316    private class CursorListener implements KeyListener, MouseListener {
1317
1318        /**
1319         * Installs this cursor listener.
1320         */

1321        private void install() {
1322            if (fTextWidget != null && !fTextWidget.isDisposed()) {
1323                fTextWidget.addKeyListener(this);
1324                fTextWidget.addMouseListener(this);
1325            }
1326        }
1327
1328        /**
1329         * Uninstalls this cursor listener.
1330         */

1331        private void uninstall() {
1332            if (fTextWidget != null && !fTextWidget.isDisposed()) {
1333                fTextWidget.removeKeyListener(this);
1334                fTextWidget.removeMouseListener(this);
1335            }
1336        }
1337
1338        /*
1339         * @see KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
1340         */

1341        public void keyPressed(KeyEvent event) {
1342        }
1343
1344        /*
1345         * @see KeyListener#keyPressed(org.eclipse.swt.events.KeyEvent)
1346         */

1347        public void keyReleased(KeyEvent e) {
1348            if (fTextWidget.getSelectionCount() == 0) {
1349                fLastSentSelectionChange= null;
1350                queuePostSelectionChanged(e.character == SWT.DEL);
1351            }
1352        }
1353
1354        /*
1355         * @see MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
1356         */

1357        public void mouseDoubleClick(MouseEvent e) {
1358        }
1359
1360        /*
1361         * @see MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
1362         */

1363        public void mouseDown(MouseEvent e) {
1364        }
1365
1366        /*
1367         * @see MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
1368         */

1369        public void mouseUp(MouseEvent event) {
1370            if (fTextWidget.getSelectionCount() == 0)
1371                queuePostSelectionChanged(false);
1372        }
1373    }
1374
1375    /**
1376     * Internal listener to document rewrite session state changes.
1377     * @since 3.1
1378     */

1379    private class DocumentRewriteSessionListener implements IDocumentRewriteSessionListener {
1380
1381        /*
1382         * @see org.eclipse.jface.text.IDocumentRewriteSessionListener#documentRewriteSessionChanged(org.eclipse.jface.text.DocumentRewriteSessionEvent)
1383         */

1384        public void documentRewriteSessionChanged(DocumentRewriteSessionEvent event) {
1385            IRewriteTarget target= TextViewer.this.getRewriteTarget();
1386            // FIXME always use setRedraw to avoid flickering due to scrolling
1387
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=158746
1388
boolean toggleRedraw= true || event.getSession().getSessionType() != DocumentRewriteSessionType.UNRESTRICTED_SMALL;
1389            final boolean viewportStabilize= !toggleRedraw;
1390            if (DocumentRewriteSessionEvent.SESSION_START == event.getChangeType()) {
1391                if (toggleRedraw)
1392                    target.setRedraw(false);
1393                target.beginCompoundChange();
1394                if (viewportStabilize && fViewerState == null)
1395                    fViewerState= new ViewerState();
1396            } else if (DocumentRewriteSessionEvent.SESSION_STOP == event.getChangeType()) {
1397                if (viewportStabilize && fViewerState != null) {
1398                    fViewerState.restore(true);
1399                    fViewerState= null;
1400                }
1401                target.endCompoundChange();
1402                if (toggleRedraw)
1403                    target.setRedraw(true);
1404            }
1405        }
1406    }
1407    
1408    
1409    /**
1410     * Identifies the scrollbars as originators of a view port change.
1411     */

1412    protected static final int SCROLLER= 1;
1413    /**
1414     * Identifies mouse moves as originators of a view port change.
1415     */

1416    protected static final int MOUSE= 2;
1417    /**
1418     * Identifies mouse button up as originator of a view port change.
1419     */

1420    protected static final int MOUSE_END= 3;
1421    /**
1422     * Identifies key strokes as originators of a view port change.
1423     */

1424    protected static final int KEY= 4;
1425    /**
1426     * Identifies window resizing as originator of a view port change.
1427     */

1428    protected static final int RESIZE= 5;
1429    /**
1430     * Identifies internal reasons as originators of a view port change.
1431     */

1432    protected static final int INTERNAL= 6;
1433
1434    /** Internal name of the position category used selection preservation during shift. */
1435    protected static final String JavaDoc SHIFTING= "__TextViewer_shifting"; //$NON-NLS-1$
1436

1437    /**
1438     * Base position category name used by the selection updater
1439     * @since 3.1
1440     */

1441    private static final String JavaDoc SELECTION_POSITION_CATEGORY= "_textviewer_selection_category"; //$NON-NLS-1$
1442
/** The viewer's text widget */
1443    private StyledText fTextWidget;
1444    /** The viewer's input document */
1445    private IDocument fDocument;
1446    /** The viewer's visible document */
1447    private IDocument fVisibleDocument;
1448    /** The viewer's document adapter */
1449    private IDocumentAdapter fDocumentAdapter;
1450    /** The slave document manager */
1451    private ISlaveDocumentManager fSlaveDocumentManager;
1452    /** The text viewer's double click strategies connector */
1453    private TextDoubleClickStrategyConnector fDoubleClickStrategyConnector;
1454    /** The text viewer's view port guard */
1455    private ViewportGuard fViewportGuard;
1456    /** Caches the graphical coordinate of the first visible line */
1457    private int fTopInset= 0;
1458    /** The most recent document modification as widget command */
1459    private WidgetCommand fWidgetCommand= new WidgetCommand();
1460    /** The SWT control's scrollbars */
1461    private ScrollBar fScroller;
1462    /** Listener on the visible document */
1463    private VisibleDocumentListener fVisibleDocumentListener= new VisibleDocumentListener();
1464    /** Verify listener */
1465    private TextVerifyListener fVerifyListener= new TextVerifyListener();
1466    /** The most recent widget modification as document command */
1467    private DocumentCommand fDocumentCommand= new DocumentCommand();
1468    /** The viewer's find/replace target */
1469    private IFindReplaceTarget fFindReplaceTarget;
1470    /**
1471     * The text viewer's hovering controller
1472     * @since 2.0
1473     */

1474    private TextViewerHoverManager fTextHoverManager;
1475    /**
1476     * The viewer widget token keeper
1477     * @since 2.0
1478     */

1479    private IWidgetTokenKeeper fWidgetTokenKeeper;
1480    /**
1481     * The viewer's manager of verify key listeners
1482     * @since 2.0
1483     */

1484    private VerifyKeyListenersManager fVerifyKeyListenersManager= new VerifyKeyListenersManager();
1485    /**
1486     * The mark position.
1487     * @since 2.0
1488     */

1489    protected Position fMarkPosition;
1490    /**
1491     * The mark position category.
1492     * @since 2.0
1493     */

1494    private final String JavaDoc MARK_POSITION_CATEGORY="__mark_category_" + hashCode(); //$NON-NLS-1$
1495
/**
1496     * The mark position updater
1497     * @since 2.0
1498     */

1499    private final IPositionUpdater fMarkPositionUpdater= new DefaultPositionUpdater(MARK_POSITION_CATEGORY);
1500    /**
1501     * The flag indicating the redraw behavior
1502     * @since 2.0
1503     */

1504    private int fRedrawCounter= 0;
1505    /**
1506     * The viewer's rewrite target
1507     * @since 2.0
1508     */

1509    private IRewriteTarget fRewriteTarget;
1510    /**
1511     * The viewer's cursor listener.
1512     * @since 3.0
1513     */

1514    private CursorListener fCursorListener;
1515    /**
1516     * Last selection range sent to selection change listeners.
1517     * @since 3.0
1518     */

1519    private IRegion fLastSentSelectionChange;
1520    /**
1521     * The registered post selection changed listeners.
1522     * @since 3.0
1523     */

1524    private List JavaDoc fPostSelectionChangedListeners;
1525    /**
1526     * Queued post selection changed events count.
1527     * @since 3.0
1528     */

1529    private final int[] fNumberOfPostSelectionChangedEvents= new int[1];
1530    /**
1531     * Last selection range sent to post selection change listeners.
1532     * @since 3.0
1533     */

1534    private IRegion fLastSentPostSelectionChange;
1535    /**
1536     * The set of registered editor helpers.
1537     * @since 3.1
1538     */

1539    private Set JavaDoc fEditorHelpers= new HashSet JavaDoc();
1540    /**
1541     * The internal rewrite session listener.
1542     * @since 3.1
1543     */

1544    private DocumentRewriteSessionListener fDocumentRewriteSessionListener= new DocumentRewriteSessionListener();
1545
1546    /** Should the auto indent strategies ignore the next edit operation */
1547    protected boolean fIgnoreAutoIndent= false;
1548    /** The strings a line is prefixed with on SHIFT_RIGHT and removed from each line on SHIFT_LEFT */
1549    protected Map JavaDoc fIndentChars;
1550    /** The string a line is prefixed with on PREFIX and removed from each line on STRIP_PREFIX */
1551    protected Map JavaDoc fDefaultPrefixChars;
1552    /** The text viewer's text double click strategies */
1553    protected Map JavaDoc fDoubleClickStrategies;
1554    /** The text viewer's undo manager */
1555    protected IUndoManager fUndoManager;
1556    /** The text viewer's auto indent strategies */
1557    protected Map JavaDoc fAutoIndentStrategies;
1558    /** The text viewer's text hovers */
1559    protected Map JavaDoc fTextHovers;
1560    /** All registered view port listeners> */
1561    protected List JavaDoc fViewportListeners;
1562    /** The last visible vertical position of the top line */
1563    protected int fLastTopPixel;
1564    /** All registered text listeners */
1565    protected List JavaDoc fTextListeners;
1566    /** All registered text input listeners */
1567    protected List JavaDoc fTextInputListeners;
1568    /** The text viewer's event consumer */
1569    protected IEventConsumer fEventConsumer;
1570    /** Indicates whether the viewer's text presentation should be replaced are modified. */
1571    protected boolean fReplaceTextPresentation= false;
1572    /**
1573     * The creator of the text hover control
1574     * @since 2.0
1575     */

1576    protected IInformationControlCreator fHoverControlCreator;
1577    /**
1578     * The mapping between model and visible document.
1579     * @since 2.1
1580     */

1581    protected IDocumentInformationMapping fInformationMapping;
1582    /**
1583     * The viewer's paint manager.
1584     * @since 2.1
1585     */

1586    protected PaintManager fPaintManager;
1587    /**
1588     * The viewers partitioning. I.e. the partitioning name the viewer uses to access partitioning information of its input document.
1589     * @since 3.0
1590     */

1591    protected String JavaDoc fPartitioning;
1592    /**
1593     * All registered text presentation listeners.
1594     * since 3.0
1595     */

1596    protected List JavaDoc fTextPresentationListeners;
1597    /**
1598     * The find/replace document adapter.
1599     * @since 3.0
1600     */

1601    protected FindReplaceDocumentAdapter fFindReplaceDocumentAdapter;
1602
1603    /**
1604     * The text viewer's hyperlink detectors.
1605     * @since 3.1
1606     */

1607    protected IHyperlinkDetector[] fHyperlinkDetectors;
1608    /**
1609     * The text viewer's hyperlink presenter.
1610     * @since 3.1
1611     */

1612    protected IHyperlinkPresenter fHyperlinkPresenter;
1613    /**
1614     * The text viewer's hyperlink manager.
1615     * @since 3.1
1616     */

1617    protected HyperlinkManager fHyperlinkManager;
1618    /**
1619     * The SWT key modifier mask which in combination
1620     * with the left mouse button triggers the hyperlink mode.
1621     * @since 3.1
1622     */

1623    protected int fHyperlinkStateMask;
1624    /**
1625     * The viewer state when in non-redraw state, <code>null</code> otherwise.
1626     * @since 3.3
1627     */

1628    private ViewerState fViewerState;
1629    /**
1630     * The editor's tab converter.
1631     * @since 3.3
1632     */

1633    private IAutoEditStrategy fTabsToSpacesConverter;
1634
1635
1636    //---- Construction and disposal ------------------
1637

1638
1639    /**
1640     * Internal use only
1641     */

1642    protected TextViewer() {
1643    }
1644
1645    /**
1646     * Create a new text viewer with the given SWT style bits.
1647     * The viewer is ready to use but does not have any plug-in installed.
1648     *
1649     * @param parent the parent of the viewer's control
1650     * @param styles the SWT style bits for the viewer's control,
1651     * <em>if <code>SWT.WRAP</code> is set then a custom document adapter needs to be provided, see {@link #createDocumentAdapter()}
1652     */

1653    public TextViewer(Composite parent, int styles) {
1654        createControl(parent, styles);
1655    }
1656
1657    /**
1658     * Factory method to create the text widget to be used as the viewer's text widget.
1659     *
1660     * @param parent the parent of the styled text
1661     * @param styles the styles for the styled text
1662     * @return the text widget to be used
1663     */

1664    protected StyledText createTextWidget(Composite parent, int styles) {
1665        return new StyledText(parent, styles);
1666    }
1667
1668    /**
1669     * Factory method to create the document adapter to be used by this viewer.
1670     *
1671     * @return the document adapter to be used
1672     */

1673    protected IDocumentAdapter createDocumentAdapter() {
1674        return new DefaultDocumentAdapter();
1675    }
1676
1677    /**
1678     * Creates the viewer's SWT control. The viewer's text widget either is
1679     * the control or is a child of the control.
1680     *
1681     * @param parent the parent of the viewer's control
1682     * @param styles the SWT style bits for the viewer's control
1683     */

1684    protected void createControl(Composite parent, int styles) {
1685
1686        fTextWidget= createTextWidget(parent, styles);
1687        
1688        // Support scroll page upon MOD1+MouseWheel
1689
fTextWidget.addListener(SWT.MouseWheel, new Listener() {
1690            public void handleEvent(Event event) {
1691                if (((event.stateMask & SWT.MOD1) == 0))
1692                    return;
1693                
1694                int topIndex= fTextWidget.getTopIndex();
1695                int bottomIndex= JFaceTextUtil.getBottomIndex(fTextWidget);
1696                
1697                if (event.count > 0)
1698                    fTextWidget.setTopIndex(2 * topIndex - bottomIndex);
1699                else
1700                    fTextWidget.setTopIndex(bottomIndex);
1701                
1702                updateViewportListeners(INTERNAL);
1703            }
1704        });
1705        
1706        fTextWidget.addDisposeListener(
1707            new DisposeListener() {
1708                public void widgetDisposed(DisposeEvent e) {
1709                    handleDispose();
1710                }
1711            }
1712        );
1713
1714        fTextWidget.setFont(parent.getFont());
1715        fTextWidget.setDoubleClickEnabled(true);
1716
1717        /*
1718         * Disable SWT Shift+TAB traversal in this viewer
1719         * 1GIYQ9K: ITPUI:WINNT - StyledText swallows Shift+TAB
1720         */

1721        fTextWidget.addTraverseListener(new TraverseListener() {
1722            public void keyTraversed(TraverseEvent e) {
1723                if ((SWT.SHIFT == e.stateMask) && ('\t' == e.character))
1724                    e.doit= false;
1725            }
1726        });
1727
1728        // where does the first line start
1729
fTopInset= -fTextWidget.computeTrim(0, 0, 0, 0).y;
1730
1731        fVerifyListener.forward(true);
1732        fTextWidget.addVerifyListener(fVerifyListener);
1733
1734        fTextWidget.addSelectionListener(new SelectionListener() {
1735            public void widgetDefaultSelected(SelectionEvent event) {
1736                selectionChanged(event.x, event.y - event.x);
1737            }
1738            public void widgetSelected(SelectionEvent event) {
1739                selectionChanged(event.x, event.y - event.x);
1740            }
1741        });
1742
1743        fCursorListener= new CursorListener();
1744        fCursorListener.install();
1745
1746        initializeViewportUpdate();
1747    }
1748
1749    /*
1750     * @see Viewer#getControl()
1751     */

1752    public Control getControl() {
1753        return fTextWidget;
1754    }
1755
1756    /*
1757     * @see ITextViewer#activatePlugins()
1758     */

1759    public void activatePlugins() {
1760
1761        if (fDoubleClickStrategies != null && !fDoubleClickStrategies.isEmpty() && fDoubleClickStrategyConnector == null) {
1762            fDoubleClickStrategyConnector= new TextDoubleClickStrategyConnector();
1763            fTextWidget.addWordMovementListener(fDoubleClickStrategyConnector);
1764            fTextWidget.addMouseListener(fDoubleClickStrategyConnector);
1765        }
1766
1767        ensureHoverControlManagerInstalled();
1768        ensureHyperlinkManagerInstalled();
1769
1770        if (fUndoManager != null) {
1771            fUndoManager.connect(this);
1772            fUndoManager.reset();
1773        }
1774    }
1775
1776    /**
1777     * After this method has been executed the caller knows that any installed text hover has been installed.
1778     */

1779    private void ensureHoverControlManagerInstalled() {
1780        if (fTextHovers != null && !fTextHovers.isEmpty() && fHoverControlCreator != null && fTextHoverManager == null) {
1781            fTextHoverManager= new TextViewerHoverManager(this, fHoverControlCreator);
1782            fTextHoverManager.install(this.getTextWidget());
1783            fTextHoverManager.setSizeConstraints(60, 10, false, true);
1784        }
1785    }
1786
1787    /*
1788     * @see ITextViewer#resetPlugins()
1789     */

1790    public void resetPlugins() {
1791        if (fUndoManager != null)
1792            fUndoManager.reset();
1793    }
1794
1795    /**
1796     * Frees all resources allocated by this viewer. Internally called when the viewer's
1797     * control has been disposed.
1798     */

1799    protected void handleDispose() {
1800        
1801        setDocument(null);
1802
1803        if (fPaintManager != null) {
1804            fPaintManager.dispose();
1805            fPaintManager= null;
1806        }
1807
1808        removeViewPortUpdate();
1809        fViewportGuard= null;
1810
1811        if (fViewportListeners != null) {
1812            fViewportListeners.clear();
1813            fViewportListeners= null;
1814        }
1815
1816        if (fTextListeners != null) {
1817            fTextListeners.clear();
1818            fTextListeners= null;
1819        }
1820
1821        if (fTextInputListeners != null) {
1822            fTextInputListeners.clear();
1823            fTextInputListeners= null;
1824        }
1825
1826        if (fPostSelectionChangedListeners != null) {
1827            fPostSelectionChangedListeners.clear();
1828            fPostSelectionChangedListeners= null;
1829        }
1830
1831        if (fAutoIndentStrategies != null) {
1832            fAutoIndentStrategies.clear();
1833            fAutoIndentStrategies= null;
1834        }
1835
1836        if (fUndoManager != null) {
1837            fUndoManager.disconnect();
1838            fUndoManager= null;
1839        }
1840
1841        if (fDoubleClickStrategies != null) {
1842            fDoubleClickStrategies.clear();
1843            fDoubleClickStrategies= null;
1844        }
1845
1846        if (fTextHovers != null) {
1847            fTextHovers.clear();
1848            fTextHovers= null;
1849        }
1850
1851        fDoubleClickStrategyConnector= null;
1852
1853        if (fTextHoverManager != null) {
1854            fTextHoverManager.dispose();
1855            fTextHoverManager= null;
1856        }
1857
1858        if (fVisibleDocumentListener !=null) {
1859            if (fVisibleDocument != null)
1860                fVisibleDocument.removeDocumentListener(fVisibleDocumentListener);
1861            fVisibleDocumentListener= null;
1862        }
1863
1864        if (fDocumentAdapter != null) {
1865            fDocumentAdapter.setDocument(null);
1866            fDocumentAdapter= null;
1867        }
1868
1869        if (fSlaveDocumentManager != null) {
1870            if (fVisibleDocument != null)
1871                fSlaveDocumentManager.freeSlaveDocument(fVisibleDocument);
1872            fSlaveDocumentManager= null;
1873        }
1874
1875        if (fCursorListener != null) {
1876            fCursorListener.uninstall();
1877            fCursorListener= null;
1878        }
1879
1880        if (fHyperlinkManager != null) {
1881            fHyperlinkManager.uninstall();
1882            fHyperlinkManager= null;
1883        }
1884
1885        fHyperlinkDetectors= null;
1886        fVisibleDocument= null;
1887        fDocument= null;
1888        fScroller= null;
1889        
1890        fTextWidget= null;
1891    }
1892
1893
1894    //---- simple getters and setters
1895

1896    /*
1897     * @see org.eclipse.jface.text.ITextViewer#getTextWidget()
1898     */

1899    public StyledText getTextWidget() {
1900        return fTextWidget;
1901    }
1902
1903    /**
1904     * The delay in milliseconds before an empty selection
1905     * changed event is sent by the cursor listener.
1906     * <p>
1907     * Note: The return value is used to initialize the cursor
1908     * listener. To return a non-constant value has no effect.</p>
1909     * <p>
1910     * The same value (<code>500</code>) is used in <code>OpenStrategy.TIME</code>.</p>
1911     *
1912     * @return delay in milliseconds
1913     * @see org.eclipse.jface.util.OpenStrategy
1914     * @since 3.0
1915     */

1916    protected int getEmptySelectionChangedEventDelay() {
1917        return 500;
1918    }
1919
1920    /**
1921     * {@inheritDoc}
1922     * @deprecated since 3.1, use
1923     * {@link ITextViewerExtension2#prependAutoEditStrategy(IAutoEditStrategy, String)} and
1924     * {@link ITextViewerExtension2#removeAutoEditStrategy(IAutoEditStrategy, String)} instead
1925     */

1926    public void setAutoIndentStrategy(IAutoIndentStrategy strategy, String JavaDoc contentType) {
1927        setAutoEditStrategies(new IAutoEditStrategy[] { strategy }, contentType);
1928    }
1929
1930    /**
1931     * Sets the given edit strategy as the only strategy for the given content type.
1932     *
1933     * @param strategies the auto edit strategies
1934     * @param contentType the content type
1935     * @since 3.1
1936     */

1937    protected final void setAutoEditStrategies(IAutoEditStrategy[] strategies, String JavaDoc contentType) {
1938        if (fAutoIndentStrategies == null)
1939            fAutoIndentStrategies= new HashMap JavaDoc();
1940
1941        List JavaDoc autoEditStrategies= (List JavaDoc) fAutoIndentStrategies.get(contentType);
1942
1943        if (strategies == null) {
1944            if (autoEditStrategies == null)
1945                return;
1946
1947            fAutoIndentStrategies.put(contentType, null);
1948
1949        } else {
1950            if (autoEditStrategies == null) {
1951                autoEditStrategies= new ArrayList JavaDoc();
1952                fAutoIndentStrategies.put(contentType, autoEditStrategies);
1953            }
1954
1955            autoEditStrategies.clear();
1956            autoEditStrategies.addAll(Arrays.asList(strategies));
1957        }
1958    }
1959
1960    /*
1961     * @see org.eclipse.jface.text.ITextViewerExtension2#prependAutoEditStrategy(org.eclipse.jface.text.IAutoEditStrategy, java.lang.String)
1962     * @since 2.1
1963     */

1964    public void prependAutoEditStrategy(IAutoEditStrategy strategy, String JavaDoc contentType) {
1965
1966        if (strategy == null || contentType == null)
1967            throw new IllegalArgumentException JavaDoc();
1968
1969        if (fAutoIndentStrategies == null)
1970            fAutoIndentStrategies= new HashMap JavaDoc();
1971
1972        List JavaDoc autoEditStrategies= (List JavaDoc) fAutoIndentStrategies.get(contentType);
1973        if (autoEditStrategies == null) {
1974            autoEditStrategies= new ArrayList JavaDoc();
1975            fAutoIndentStrategies.put(contentType, autoEditStrategies);
1976        }
1977
1978        autoEditStrategies.add(0, strategy);
1979    }
1980
1981    /*
1982     * @see org.eclipse.jface.text.ITextViewerExtension2#removeAutoEditStrategy(org.eclipse.jface.text.IAutoEditStrategy, java.lang.String)
1983     * @since 2.1
1984     */

1985    public void removeAutoEditStrategy(IAutoEditStrategy strategy, String JavaDoc contentType) {
1986        if (fAutoIndentStrategies == null)
1987            return;
1988
1989        List JavaDoc autoEditStrategies= (List JavaDoc) fAutoIndentStrategies.get(contentType);
1990        if (autoEditStrategies == null)
1991            return;
1992
1993        for (final Iterator JavaDoc iterator= autoEditStrategies.iterator(); iterator.hasNext(); ) {
1994            if (iterator.next().equals(strategy)) {
1995                iterator.remove();
1996                break;
1997            }
1998        }
1999
2000        if (autoEditStrategies.isEmpty())
2001            fAutoIndentStrategies.put(contentType, null);
2002    }
2003
2004    /*
2005     * @see ITextViewer#setEventConsumer(IEventConsumer)
2006     */

2007    public void setEventConsumer(IEventConsumer consumer) {
2008        fEventConsumer= consumer;
2009    }
2010
2011    /*
2012     * @see ITextViewer#setIndentPrefixes(String[], String)
2013     */

2014    public void setIndentPrefixes(String JavaDoc[] indentPrefixes, String JavaDoc contentType) {
2015
2016        int i= -1;
2017        boolean ok= (indentPrefixes != null);
2018        while (ok && ++i < indentPrefixes.length)
2019            ok= (indentPrefixes[i] != null);
2020
2021        if (ok) {
2022
2023            if (fIndentChars == null)
2024                fIndentChars= new HashMap JavaDoc();
2025
2026            fIndentChars.put(contentType, indentPrefixes);
2027
2028        } else if (fIndentChars != null)
2029            fIndentChars.remove(contentType);
2030    }
2031
2032    /*
2033     * @see ITextViewer#getTopInset()
2034     */

2035    public int getTopInset() {
2036        return fTopInset;
2037    }
2038
2039    /*
2040     * @see ITextViewer#isEditable()
2041     */

2042    public boolean isEditable() {
2043        if (fTextWidget == null)
2044            return false;
2045        return fTextWidget.getEditable();
2046    }
2047
2048    /*
2049     * @see ITextViewer#setEditable(boolean)
2050     */

2051    public void setEditable(boolean editable) {
2052        if (fTextWidget != null)
2053            fTextWidget.setEditable(editable);
2054    }
2055
2056    /*
2057     * @see ITextViewer#setDefaultPrefixes
2058     * @since 2.0
2059     */

2060    public void setDefaultPrefixes(String JavaDoc[] defaultPrefixes, String JavaDoc contentType) {
2061
2062        if (defaultPrefixes != null && defaultPrefixes.length > 0) {
2063            if (fDefaultPrefixChars == null)
2064                fDefaultPrefixChars= new HashMap JavaDoc();
2065            fDefaultPrefixChars.put(contentType, defaultPrefixes);
2066        } else if (fDefaultPrefixChars != null)
2067            fDefaultPrefixChars.remove(contentType);
2068    }
2069
2070    /*
2071     * @see ITextViewer#setUndoManager(IUndoManager)
2072     */

2073    public void setUndoManager(IUndoManager undoManager) {
2074        fUndoManager= undoManager;
2075    }
2076
2077    /*
2078     * @see ITextViewerExtension6#getUndoManager()
2079     * @since 3.1
2080     */

2081    public IUndoManager getUndoManager() {
2082        return fUndoManager;
2083    }
2084
2085    /*
2086     * @see ITextViewer#setTextHover(ITextHover, String)
2087     */

2088    public void setTextHover(ITextHover hover, String JavaDoc contentType) {
2089        setTextHover(hover, contentType, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
2090    }
2091
2092    /*
2093     * @see ITextViewerExtension2#setTextHover(ITextHover, String, int)
2094     * @since 2.1
2095     */

2096    public void setTextHover(ITextHover hover, String JavaDoc contentType, int stateMask) {
2097        TextHoverKey key= new TextHoverKey(contentType, stateMask);
2098        if (hover != null) {
2099            if (fTextHovers == null) {
2100                fTextHovers= new HashMap JavaDoc();
2101            }
2102            fTextHovers.put(key, hover);
2103        } else if (fTextHovers != null)
2104            fTextHovers.remove(key);
2105
2106        ensureHoverControlManagerInstalled();
2107    }
2108
2109    /*
2110     * @see ITextViewerExtension2#removeTextHovers(String)
2111     * @since 2.1
2112     */

2113    public void removeTextHovers(String JavaDoc contentType) {
2114        if (fTextHovers == null)
2115            return;
2116
2117        Iterator JavaDoc iter= new HashSet JavaDoc(fTextHovers.keySet()).iterator();
2118        while (iter.hasNext()) {
2119            TextHoverKey key= (TextHoverKey)iter.next();
2120            if (key.fContentType.equals(contentType))
2121                fTextHovers.remove(key);
2122        }
2123    }
2124
2125    /**
2126     * Returns the text hover for a given offset.
2127     *
2128     * @param offset the offset for which to return the text hover
2129     * @return the text hover for the given offset
2130     */

2131    protected ITextHover getTextHover(int offset) {
2132        return getTextHover(offset, ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
2133    }
2134
2135    /**
2136     * Returns the text hover for a given offset and a given state mask.
2137     *
2138     * @param offset the offset for which to return the text hover
2139     * @param stateMask the SWT event state mask
2140     * @return the text hover for the given offset and state mask
2141     * @since 2.1
2142     */

2143    protected ITextHover getTextHover(int offset, int stateMask) {
2144        if (fTextHovers == null)
2145            return null;
2146
2147        IDocument document= getDocument();
2148        if (document == null)
2149            return null;
2150
2151        try {
2152            TextHoverKey key= new TextHoverKey(TextUtilities.getContentType(document, getDocumentPartitioning(), offset, true), stateMask);
2153            Object JavaDoc textHover= fTextHovers.get(key);
2154            if (textHover == null) {
2155                // Use default text hover
2156
key.setStateMask(ITextViewerExtension2.DEFAULT_HOVER_STATE_MASK);
2157                textHover= fTextHovers.get(key);
2158            }
2159            return (ITextHover) textHover;
2160        } catch (BadLocationException x) {
2161            if (TRACE_ERRORS)
2162                System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.selectContentTypePlugin")); //$NON-NLS-1$
2163
}
2164        return null;
2165    }
2166
2167    /**
2168     * Returns the text hovering controller of this viewer.
2169     *
2170     * @return the text hovering controller of this viewer
2171     * @since 2.0
2172     */

2173    protected AbstractInformationControlManager getTextHoveringController() {
2174        return fTextHoverManager;
2175    }
2176
2177    /**
2178     * Sets the creator for the hover controls.
2179     *
2180     * @param creator the hover control creator
2181     * @since 2.0
2182     */

2183    public void setHoverControlCreator(IInformationControlCreator creator) {
2184        fHoverControlCreator= creator;
2185    }
2186
2187    /*
2188     * @see IWidgetTokenOwner#requestWidgetToken(IWidgetTokenKeeper)
2189     * @since 2.0
2190     */

2191     public boolean requestWidgetToken(IWidgetTokenKeeper requester) {
2192         if (fTextWidget != null) {
2193             if (fWidgetTokenKeeper != null) {
2194                 if (fWidgetTokenKeeper == requester)
2195                     return true;
2196                 if (fWidgetTokenKeeper.requestWidgetToken(this)) {
2197                     fWidgetTokenKeeper= requester;
2198                     return true;
2199                 }
2200            } else {
2201                fWidgetTokenKeeper= requester;
2202                return true;
2203            }
2204        }
2205        return false;
2206    }
2207
2208    /*
2209     * @see org.eclipse.jface.text.IWidgetTokenOwnerExtension#requestWidgetToken(org.eclipse.jface.text.IWidgetTokenKeeper, int)
2210     * @since 3.0
2211     */

2212    public boolean requestWidgetToken(IWidgetTokenKeeper requester, int priority) {
2213        if (fTextWidget != null) {
2214            if (fWidgetTokenKeeper != null) {
2215
2216                if (fWidgetTokenKeeper == requester)
2217                    return true;
2218
2219                boolean accepted= false;
2220                if (fWidgetTokenKeeper instanceof IWidgetTokenKeeperExtension) {
2221                    IWidgetTokenKeeperExtension extension= (IWidgetTokenKeeperExtension) fWidgetTokenKeeper;
2222                    accepted= extension.requestWidgetToken(this, priority);
2223                } else {
2224                    accepted= fWidgetTokenKeeper.requestWidgetToken(this);
2225                }
2226
2227                if (accepted) {
2228                    fWidgetTokenKeeper= requester;
2229                    return true;
2230                }
2231
2232           } else {
2233               fWidgetTokenKeeper= requester;
2234               return true;
2235           }
2236       }
2237       return false;
2238   }
2239
2240    /*
2241     * @see IWidgetTokenOwner#releaseWidgetToken(IWidgetTokenKeeper)
2242     * @since 2.0
2243     */

2244    public void releaseWidgetToken(IWidgetTokenKeeper tokenKeeper) {
2245        if (fWidgetTokenKeeper == tokenKeeper)
2246            fWidgetTokenKeeper= null;
2247    }
2248
2249
2250    //---- Selection
2251

2252    /*
2253     * @see ITextViewer#getSelectedRange()
2254     */

2255    public Point getSelectedRange() {
2256
2257        if (!redraws() && fViewerState != null)
2258            return fViewerState.getSelection();
2259
2260        if (fTextWidget != null) {
2261            Point p= fTextWidget.getSelectionRange();
2262            p= widgetSelection2ModelSelection(p);
2263            if (p != null)
2264                return p;
2265        }
2266
2267        return new Point(-1, -1);
2268    }
2269
2270    /*
2271     * @see ITextViewer#setSelectedRange(int, int)
2272     */

2273    public void setSelectedRange(int selectionOffset, int selectionLength) {
2274
2275        if (!redraws()) {
2276            if (fViewerState != null)
2277                fViewerState.updateSelection(selectionOffset, selectionLength);
2278            return;
2279        }
2280
2281        if (fTextWidget == null)
2282            return;
2283
2284        IRegion widgetSelection= modelRange2ClosestWidgetRange(new Region(selectionOffset, selectionLength));
2285        if (widgetSelection != null) {
2286
2287            int[] selectionRange= new int[] { widgetSelection.getOffset(), widgetSelection.getLength() };
2288            validateSelectionRange(selectionRange);
2289            if (selectionRange[0] >= 0) {
2290                fTextWidget.setSelectionRange(selectionRange[0], selectionRange[1]);
2291                selectionChanged(selectionRange[0], selectionRange[1]);
2292            }
2293        }
2294    }
2295
2296    /**
2297     * Validates and adapts the given selection range if it is not a valid
2298     * widget selection. The widget selection is invalid if it starts or ends
2299     * inside a multi-character line delimiter. If so, the selection is adapted to
2300     * start <b>after</b> the divided line delimiter and to end <b>before</b>
2301     * the divided line delimiter. The parameter passed in is changed in-place
2302     * when being adapted. An adaptation to <code>[-1, -1]</code> indicates
2303     * that the selection range could not be validated.
2304     * Subclasses may reimplement this method.
2305     *
2306     * @param selectionRange selectionRange[0] is the offset, selectionRange[1]
2307     * the length of the selection to validate.
2308     * @since 2.0
2309     */

2310    protected void validateSelectionRange(int[] selectionRange) {
2311
2312        IDocument document= getVisibleDocument();
2313        if (document == null) {
2314            selectionRange[0]= -1;
2315            selectionRange[1]= -1;
2316            return;
2317        }
2318
2319        int documentLength= document.getLength();
2320        int offset= selectionRange[0];
2321        int length= selectionRange[1];
2322
2323        if (length < 0) {
2324            length= - length;
2325            offset -= length;
2326        }
2327
2328        if (offset <0)
2329            offset= 0;
2330
2331        if (offset > documentLength)
2332            offset= documentLength;
2333
2334        int delta= (offset + length) - documentLength;
2335        if (delta > 0)
2336            length -= delta;
2337
2338        try {
2339
2340            int lineNumber= document.getLineOfOffset(offset);
2341            IRegion lineInformation= document.getLineInformation(lineNumber);
2342
2343            int lineEnd= lineInformation.getOffset() + lineInformation.getLength();
2344            delta= offset - lineEnd;
2345            if (delta > 0) {
2346                // in the middle of a multi-character line delimiter
2347
offset= lineEnd;
2348                String JavaDoc delimiter= document.getLineDelimiter(lineNumber);
2349                if (delimiter != null)
2350                    offset += delimiter.length();
2351            }
2352
2353            int end= offset + length;
2354            lineInformation= document.getLineInformationOfOffset(end);
2355            lineEnd= lineInformation.getOffset() + lineInformation.getLength();
2356            delta= end - lineEnd;
2357            if (delta > 0) {
2358                // in the middle of a multi-character line delimiter
2359
length -= delta;
2360            }
2361
2362        } catch (BadLocationException x) {
2363            selectionRange[0]= -1;
2364            selectionRange[1]= -1;
2365            return;
2366        }
2367
2368        if (selectionRange[1] < 0) {
2369            selectionRange[0]= offset + length;
2370            selectionRange[1]= -length;
2371        } else {
2372            selectionRange[0]= offset;
2373            selectionRange[1]= length;
2374        }
2375    }
2376
2377    /*
2378     * @see Viewer#setSelection(ISelection)
2379     */

2380    public void setSelection(ISelection selection, boolean reveal) {
2381        if (selection instanceof ITextSelection) {
2382            ITextSelection s= (ITextSelection) selection;
2383            setSelectedRange(s.getOffset(), s.getLength());
2384            if (reveal)
2385                revealRange(s.getOffset(), s.getLength());
2386        }
2387    }
2388
2389    /*
2390     * @see Viewer#getSelection()
2391     */

2392    public ISelection getSelection() {
2393        Point p= getSelectedRange();
2394        if (p.x == -1 || p.y == -1)
2395            return TextSelection.emptySelection();
2396
2397        return new TextSelection(getDocument(), p.x, p.y);
2398    }
2399
2400    /*
2401     * @see ITextViewer#getSelectionProvider()
2402     */

2403    public ISelectionProvider getSelectionProvider() {
2404        return this;
2405    }
2406
2407    /*
2408     * @see org.eclipse.jface.text.IPostSelectionProvider#addPostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
2409     * @since 3.0
2410     */

2411    public void addPostSelectionChangedListener(ISelectionChangedListener listener) {
2412
2413        Assert.isNotNull(listener);
2414
2415        if (fPostSelectionChangedListeners == null)
2416            fPostSelectionChangedListeners= new ArrayList JavaDoc();
2417
2418        if (! fPostSelectionChangedListeners.contains(listener))
2419            fPostSelectionChangedListeners.add(listener);
2420    }
2421
2422    /*
2423     * @see org.eclipse.jface.text.IPostSelectionProvider#removePostSelectionChangedListener(org.eclipse.jface.viewers.ISelectionChangedListener)
2424     * @since 3.0
2425     */

2426    public void removePostSelectionChangedListener(ISelectionChangedListener listener) {
2427
2428        Assert.isNotNull(listener);
2429
2430        if (fPostSelectionChangedListeners != null) {
2431            fPostSelectionChangedListeners.remove(listener);
2432            if (fPostSelectionChangedListeners.size() == 0)
2433                fPostSelectionChangedListeners= null;
2434        }
2435    }
2436
2437    /**
2438     * Get the text widget's display.
2439     *
2440     * @return the display or <code>null</code> if the display cannot be retrieved or if the display is disposed
2441     * @since 3.0
2442     */

2443    private Display getDisplay() {
2444        if (fTextWidget == null || fTextWidget.isDisposed())
2445            return null;
2446
2447        Display display= fTextWidget.getDisplay();
2448        if (display != null && display.isDisposed())
2449            return null;
2450
2451        return display;
2452    }
2453
2454    /**
2455     * Starts a timer to send out a post selection changed event.
2456     *
2457     * @param fireEqualSelection <code>true</code> iff the event must be fired if the selection does not change
2458     * @since 3.0
2459     */

2460    private void queuePostSelectionChanged(final boolean fireEqualSelection) {
2461        Display display= getDisplay();
2462        if (display == null)
2463            return;
2464
2465        fNumberOfPostSelectionChangedEvents[0]++;
2466        display.timerExec(getEmptySelectionChangedEventDelay(), new Runnable JavaDoc() {
2467            final int id= fNumberOfPostSelectionChangedEvents[0];
2468            public void run() {
2469                if (id == fNumberOfPostSelectionChangedEvents[0]) {
2470                    // Check again because this is executed after the delay
2471
if (getDisplay() != null) {
2472                        Point selection= fTextWidget.getSelectionRange();
2473                        if (selection != null) {
2474                            IRegion r= widgetRange2ModelRange(new Region(selection.x, selection.y));
2475                            if (fireEqualSelection || (r != null && !r.equals(fLastSentPostSelectionChange)) || r == null) {
2476                                fLastSentPostSelectionChange= r;
2477                                firePostSelectionChanged(selection.x, selection.y);
2478                            }
2479                        }
2480                    }
2481                }
2482            }
2483        });
2484    }
2485
2486    /**
2487     * Sends out a text selection changed event to all registered post selection changed listeners.
2488     *
2489     * @param offset the offset of the newly selected range in the visible document
2490     * @param length the length of the newly selected range in the visible document
2491     * @since 3.0
2492     */

2493    protected void firePostSelectionChanged(int offset, int length) {
2494        if (redraws()) {
2495            IRegion r= widgetRange2ModelRange(new Region(offset, length));
2496            ISelection selection= r != null ? new TextSelection(getDocument(), r.getOffset(), r.getLength()) : TextSelection.emptySelection();
2497            SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
2498            firePostSelectionChanged(event);
2499        }
2500    }
2501
2502    /**
2503     * Sends out a text selection changed event to all registered listeners and
2504     * registers the selection changed event to be sent out to all post selection
2505     * listeners.
2506     *
2507     * @param offset the offset of the newly selected range in the visible document
2508     * @param length the length of the newly selected range in the visible document
2509     */

2510    protected void selectionChanged(int offset, int length) {
2511        queuePostSelectionChanged(true);
2512        fireSelectionChanged(offset, length);
2513    }
2514
2515    /**
2516     * Sends out a text selection changed event to all registered listeners.
2517     *
2518     * @param offset the offset of the newly selected range in the visible document
2519     * @param length the length of the newly selected range in the visible document
2520     * @since 3.0
2521     */

2522    protected void fireSelectionChanged(int offset, int length) {
2523        if (redraws()) {
2524            IRegion r= widgetRange2ModelRange(new Region(offset, length));
2525            if ((r != null && !r.equals(fLastSentSelectionChange)) || r == null) {
2526                fLastSentSelectionChange= r;
2527                ISelection selection= r != null ? new TextSelection(getDocument(), r.getOffset(), r.getLength()) : TextSelection.emptySelection();
2528                SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
2529                fireSelectionChanged(event);
2530            }
2531        }
2532    }
2533
2534    /**
2535     * Sends the given event to all registered post selection changed listeners.
2536     *
2537     * @param event the selection event
2538     * @since 3.0
2539     */

2540    private void firePostSelectionChanged(SelectionChangedEvent event) {
2541        List JavaDoc listeners= fPostSelectionChangedListeners;
2542        if (listeners != null) {
2543            listeners= new ArrayList JavaDoc(listeners);
2544            for (int i= 0; i < listeners.size(); i++) {
2545                ISelectionChangedListener l= (ISelectionChangedListener) listeners.get(i);
2546                l.selectionChanged(event);
2547            }
2548        }
2549    }
2550
2551    /**
2552     * Sends out a mark selection changed event to all registered listeners.
2553     *
2554     * @param offset the offset of the mark selection in the visible document, the offset is <code>-1</code> if the mark was cleared
2555     * @param length the length of the mark selection, may be negative if the caret is before the mark.
2556     * @since 2.0
2557     */

2558    protected void markChanged(int offset, int length) {
2559        if (redraws()) {
2560
2561            if (offset != -1) {
2562                IRegion r= widgetRange2ModelRange(new Region(offset, length));
2563                offset= r.getOffset();
2564                length= r.getLength();
2565            }
2566
2567            ISelection selection= new MarkSelection(getDocument(), offset, length);
2568            SelectionChangedEvent event= new SelectionChangedEvent(this, selection);
2569            fireSelectionChanged(event);
2570        }
2571    }
2572
2573
2574    //---- Text listeners
2575

2576    /*
2577     * @see ITextViewer#addTextListener(ITextListener)
2578     */

2579    public void addTextListener(ITextListener listener) {
2580
2581        Assert.isNotNull(listener);
2582
2583        if (fTextListeners == null)
2584            fTextListeners= new ArrayList JavaDoc();
2585
2586        if (!fTextListeners.contains(listener))
2587            fTextListeners.add(listener);
2588    }
2589
2590    /*
2591     * @see ITextViewer#removeTextListener(ITextListener)
2592     */

2593    public void removeTextListener(ITextListener listener) {
2594
2595        Assert.isNotNull(listener);
2596
2597        if (fTextListeners != null) {
2598            fTextListeners.remove(listener);
2599            if (fTextListeners.size() == 0)
2600                fTextListeners= null;
2601        }
2602    }
2603
2604    /**
2605     * Informs all registered text listeners about the change specified by the
2606     * widget command. This method does not use a robust iterator.
2607     *
2608     * @param cmd the widget command translated into a text event sent to all text listeners
2609     */

2610    protected void updateTextListeners(WidgetCommand cmd) {
2611
2612        if (fTextListeners != null) {
2613
2614            DocumentEvent event= cmd.event;
2615            if (event instanceof SlaveDocumentEvent)
2616                event= ((SlaveDocumentEvent) event).getMasterEvent();
2617
2618            TextEvent e= new TextEvent(cmd.start, cmd.length, cmd.text, cmd.preservedText, event, redraws());
2619            for (int i= 0; i < fTextListeners.size(); i++) {
2620                ITextListener l= (ITextListener) fTextListeners.get(i);
2621                l.textChanged(e);
2622            }
2623        }
2624    }
2625
2626    //---- Text input listeners
2627

2628    /*
2629     * @see ITextViewer#addTextInputListener(ITextInputListener)
2630     */

2631    public void addTextInputListener(ITextInputListener listener) {
2632
2633        Assert.isNotNull(listener);
2634
2635        if (fTextInputListeners == null)
2636            fTextInputListeners= new ArrayList JavaDoc();
2637
2638        if (!fTextInputListeners.contains(listener))
2639            fTextInputListeners.add(listener);
2640    }
2641
2642    /*
2643     * @see ITextViewer#removeTextInputListener(ITextInputListener)
2644     */

2645    public void removeTextInputListener(ITextInputListener listener) {
2646
2647        Assert.isNotNull(listener);
2648
2649        if (fTextInputListeners != null) {
2650            fTextInputListeners.remove(listener);
2651            if (fTextInputListeners.size() == 0)
2652                fTextInputListeners= null;
2653        }
2654    }
2655
2656    /**
2657     * Informs all registered text input listeners about the forthcoming input change,
2658     * This method does not use a robust iterator.
2659     *
2660     * @param oldInput the old input document
2661     * @param newInput the new input document
2662     */

2663    protected void fireInputDocumentAboutToBeChanged(IDocument oldInput, IDocument newInput) {
2664        List JavaDoc listener= fTextInputListeners;
2665        if (listener != null) {
2666            for (int i= 0; i < listener.size(); i++) {
2667                ITextInputListener l= (ITextInputListener) listener.get(i);
2668                l.inputDocumentAboutToBeChanged(oldInput, newInput);
2669            }
2670        }
2671    }
2672
2673    /**
2674     * Informs all registered text input listeners about the successful input change,
2675     * This method does not use a robust iterator.
2676     *
2677     * @param oldInput the old input document
2678     * @param newInput the new input document
2679     */

2680    protected void fireInputDocumentChanged(IDocument oldInput, IDocument newInput) {
2681        List JavaDoc listener= fTextInputListeners;
2682        if (listener != null) {
2683            for (int i= 0; i < listener.size(); i++) {
2684                ITextInputListener l= (ITextInputListener) listener.get(i);
2685                l.inputDocumentChanged(oldInput, newInput);
2686            }
2687        }
2688    }
2689
2690    //---- Document
2691

2692    /*
2693     * @see Viewer#getInput()
2694     */

2695    public Object JavaDoc getInput() {
2696        return getDocument();
2697    }
2698
2699    /*
2700     * @see ITextViewer#getDocument()
2701     */

2702    public IDocument getDocument() {
2703        return fDocument;
2704    }
2705
2706    /*
2707     * @see Viewer#setInput(Object)
2708     */

2709    public void setInput(Object JavaDoc input) {
2710
2711        IDocument document= null;
2712        if (input instanceof IDocument)
2713            document= (IDocument) input;
2714
2715        setDocument(document);
2716    }
2717
2718    /*
2719     * @see ITextViewer#setDocument(IDocument)
2720     */

2721    public void setDocument(IDocument document) {
2722
2723        fReplaceTextPresentation= true;
2724        fireInputDocumentAboutToBeChanged(fDocument, document);
2725
2726        IDocument oldDocument= fDocument;
2727        fDocument= document;
2728
2729        setVisibleDocument(fDocument);
2730
2731        resetPlugins();
2732        inputChanged(fDocument, oldDocument);
2733
2734        fireInputDocumentChanged(oldDocument, fDocument);
2735        fLastSentSelectionChange= null;
2736        fReplaceTextPresentation= false;
2737    }
2738
2739    /*
2740     * @see ITextViewer#setDocument(IDocument, int, int)
2741     */

2742    public void setDocument(IDocument document, int modelRangeOffset, int modelRangeLength) {
2743
2744        fReplaceTextPresentation= true;
2745        fireInputDocumentAboutToBeChanged(fDocument, document);
2746
2747        IDocument oldDocument= fDocument;
2748        fDocument= document;
2749
2750        try {
2751
2752            IDocument slaveDocument= createSlaveDocument(document);
2753            updateSlaveDocument(slaveDocument, modelRangeOffset, modelRangeLength);
2754            setVisibleDocument(slaveDocument);
2755
2756        } catch (BadLocationException x) {
2757            throw new IllegalArgumentException JavaDoc(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_1")); //$NON-NLS-1$
2758
}
2759
2760        resetPlugins();
2761        inputChanged(fDocument, oldDocument);
2762
2763        fireInputDocumentChanged(oldDocument, fDocument);
2764        fLastSentSelectionChange= null;
2765        fReplaceTextPresentation= false;
2766    }
2767
2768    /**
2769     * Creates a slave document for the given document if there is a slave document manager
2770     * associated with this viewer.
2771     *
2772     * @param document the master document
2773     * @return the newly created slave document
2774     * @since 2.1
2775     */

2776    protected IDocument createSlaveDocument(IDocument document) {
2777        ISlaveDocumentManager manager= getSlaveDocumentManager();
2778        if (manager != null) {
2779            if (manager.isSlaveDocument(document))
2780                return document;
2781            return manager.createSlaveDocument(document);
2782        }
2783        return document;
2784    }
2785
2786    /**
2787     * Sets the given slave document to the specified range of its master document.
2788     *
2789     * @param visibleDocument the slave document
2790     * @param visibleRegionOffset the offset of the master document range
2791     * @param visibleRegionLength the length of the master document range
2792     * @return <code>true</code> if the slave has been adapted successfully
2793     * @throws BadLocationException in case the specified range is not valid in the master document
2794     * @since 2.1
2795     * @deprecated use <code>updateSlaveDocument</code> instead
2796     */

2797    protected boolean updateVisibleDocument(IDocument visibleDocument, int visibleRegionOffset, int visibleRegionLength) throws BadLocationException {
2798        if (visibleDocument instanceof ChildDocument) {
2799            ChildDocument childDocument= (ChildDocument) visibleDocument;
2800
2801            IDocument document= childDocument.getParentDocument();
2802            int line= document.getLineOfOffset(visibleRegionOffset);
2803            int offset= document.getLineOffset(line);
2804            int length= (visibleRegionOffset - offset) + visibleRegionLength;
2805
2806            Position parentRange= childDocument.getParentDocumentRange();
2807            if (offset != parentRange.getOffset() || length != parentRange.getLength()) {
2808                childDocument.setParentDocumentRange(offset, length);
2809                return true;
2810            }
2811        }
2812        return false;
2813    }
2814
2815    /**
2816     * Updates the given slave document to show the specified range of its master document.
2817     *
2818     * @param slaveDocument the slave document
2819     * @param modelRangeOffset the offset of the master document range
2820     * @param modelRangeLength the length of the master document range
2821     * @return <code>true</code> if the slave has been adapted successfully
2822     * @throws BadLocationException in case the specified range is not valid in the master document
2823     * @since 3.0
2824     */

2825    protected boolean updateSlaveDocument(IDocument slaveDocument, int modelRangeOffset, int modelRangeLength) throws BadLocationException {
2826        return updateVisibleDocument(slaveDocument, modelRangeOffset, modelRangeLength);
2827    }
2828
2829
2830
2831    //---- View ports
2832

2833    /**
2834     * Initializes all listeners and structures required to set up view port listeners.
2835     */

2836    private void initializeViewportUpdate() {
2837
2838        if (fViewportGuard != null)
2839            return;
2840
2841        if (fTextWidget != null) {
2842
2843            fViewportGuard= new ViewportGuard();
2844            fLastTopPixel= -1;
2845
2846            fTextWidget.addKeyListener(fViewportGuard);
2847            fTextWidget.addMouseListener(fViewportGuard);
2848
2849            fScroller= fTextWidget.getVerticalBar();
2850            if (fScroller != null)
2851                fScroller.addSelectionListener(fViewportGuard);
2852        }
2853    }
2854
2855    /**
2856     * Removes all listeners and structures required to set up view port listeners.
2857     */

2858    private void removeViewPortUpdate() {
2859
2860        if (fTextWidget != null) {
2861
2862            fTextWidget.removeKeyListener(fViewportGuard);
2863            fTextWidget.removeMouseListener(fViewportGuard);
2864
2865            if (fScroller != null && !fScroller.isDisposed()) {
2866                fScroller.removeSelectionListener(fViewportGuard);
2867                fScroller= null;
2868            }
2869
2870            fViewportGuard= null;
2871        }
2872    }
2873
2874    /*
2875     * @see ITextViewer#addViewportListener(IViewportListener)
2876     */

2877    public void addViewportListener(IViewportListener listener) {
2878
2879        if (fViewportListeners == null) {
2880            fViewportListeners= new ArrayList JavaDoc();
2881            initializeViewportUpdate();
2882        }
2883
2884        if (!fViewportListeners.contains(listener))
2885            fViewportListeners.add(listener);
2886    }
2887
2888    /*
2889     * @see ITextViewer#removeViewportListener(IVewportListener)
2890     */

2891    public void removeViewportListener(IViewportListener listener) {
2892        if (fViewportListeners != null)
2893            fViewportListeners.remove(listener);
2894    }
2895
2896    /**
2897     * Checks whether the view port changed and if so informs all registered
2898     * listeners about the change.
2899     *
2900     * @param origin describes under which circumstances this method has been called.
2901     *
2902     * @see IViewportListener
2903     */

2904    protected void updateViewportListeners(int origin) {
2905
2906        if (redraws()) {
2907            int topPixel= fTextWidget.getTopPixel();
2908            if (topPixel >= 0 && topPixel != fLastTopPixel) {
2909                if (fViewportListeners != null) {
2910                    for (int i= 0; i < fViewportListeners.size(); i++) {
2911                        IViewportListener l= (IViewportListener) fViewportListeners.get(i);
2912                        l.viewportChanged(topPixel);
2913                    }
2914                }
2915                fLastTopPixel= topPixel;
2916            }
2917        }
2918    }
2919
2920    //---- scrolling and revealing
2921

2922    /*
2923     * @see ITextViewer#getTopIndex()
2924     */

2925    public int getTopIndex() {
2926
2927        if (fTextWidget != null) {
2928            int top= fTextWidget.getTopIndex();
2929            return widgetLine2ModelLine(top);
2930        }
2931
2932        return -1;
2933    }
2934
2935    /*
2936     * @see ITextViewer#setTopIndex(int)
2937     */

2938    public void setTopIndex(int index) {
2939
2940        if (fTextWidget != null) {
2941
2942            int widgetLine= modelLine2WidgetLine(index);
2943            if (widgetLine == -1)
2944                widgetLine= getClosestWidgetLineForModelLine(index);
2945
2946            if (widgetLine > -1) {
2947                fTextWidget.setTopIndex(widgetLine);
2948                    updateViewportListeners(INTERNAL);
2949            }
2950        }
2951    }
2952
2953    /**
2954     * Returns the number of lines that can fully fit into the viewport. This is computed by
2955     * dividing the widget's client area height by the widget's line height. The result is only
2956     * accurate if the widget does not use variable line heights - for that reason, clients should
2957     * not use this method any longer and use the client area height of the text widget to find out
2958     * how much content fits into it.
2959     *
2960     * @return the view port height in lines
2961     * @deprecated as of 3.2
2962     */

2963    protected int getVisibleLinesInViewport() {
2964        if (fTextWidget != null) {
2965            Rectangle clArea= fTextWidget.getClientArea();
2966            if (!clArea.isEmpty())
2967                return clArea.height / fTextWidget.getLineHeight();
2968        }
2969        return -1;
2970    }
2971
2972    /*
2973     * @see ITextViewer#getBottomIndex()
2974     */

2975    public int getBottomIndex() {
2976
2977        if (fTextWidget == null)
2978            return -1;
2979        
2980        int widgetBottom= JFaceTextUtil.getBottomIndex(fTextWidget);
2981        return widgetLine2ModelLine(widgetBottom);
2982    }
2983
2984    /*
2985     * @see ITextViewer#getTopIndexStartOffset()
2986     */

2987    public int getTopIndexStartOffset() {
2988
2989        if (fTextWidget != null) {
2990            int top= fTextWidget.getTopIndex();
2991            try {
2992                top= getVisibleDocument().getLineOffset(top);
2993                return widgetOffset2ModelOffset(top);
2994            } catch (BadLocationException ex) {
2995                if (TRACE_ERRORS)
2996                    System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getTopIndexStartOffset")); //$NON-NLS-1$
2997
}
2998        }
2999
3000        return -1;
3001    }
3002
3003    /*
3004     * @see ITextViewer#getBottomIndexEndOffset()
3005     */

3006    public int getBottomIndexEndOffset() {
3007        try {
3008
3009            IRegion line= getDocument().getLineInformation(getBottomIndex());
3010            int bottomEndOffset= line.getOffset() + line.getLength() - 1;
3011
3012            IRegion coverage= getModelCoverage();
3013            if (coverage == null)
3014                return -1;
3015
3016            int coverageEndOffset= coverage.getOffset() + coverage.getLength() - 1;
3017            return Math.min(coverageEndOffset, bottomEndOffset);
3018
3019        } catch (BadLocationException ex) {
3020            if (TRACE_ERRORS)
3021                System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getBottomIndexEndOffset")); //$NON-NLS-1$
3022
return getDocument().getLength() - 1;
3023        }
3024    }
3025
3026    /*
3027     * @see ITextViewer#revealRange(int, int)
3028     */

3029    public void revealRange(int start, int length) {
3030
3031        if (fTextWidget == null || !redraws())
3032            return;
3033
3034        IRegion modelRange= new Region(start, length);
3035        IRegion widgetRange= modelRange2ClosestWidgetRange(modelRange);
3036        if (widgetRange != null) {
3037
3038            int[] range= new int[] { widgetRange.getOffset(), widgetRange.getLength() };
3039            validateSelectionRange(range);
3040            if (range[0] >= 0)
3041                internalRevealRange(range[0], range[0] + range[1]);
3042
3043        } else {
3044
3045            IRegion coverage= getModelCoverage();
3046            int cursor= (coverage == null || start < coverage.getOffset()) ? 0 : getVisibleDocument().getLength();
3047            internalRevealRange(cursor, cursor);
3048        }
3049    }
3050
3051    /**
3052     * Reveals the given range of the visible document.
3053     *
3054     * @param start the start offset of the range
3055     * @param end the end offset of the range
3056     */

3057    protected void internalRevealRange(int start, int end) {
3058
3059        try {
3060
3061            IDocument doc= getVisibleDocument();
3062
3063            int startLine= doc.getLineOfOffset(start);
3064            int endLine= doc.getLineOfOffset(end);
3065
3066            int top= fTextWidget.getTopIndex();
3067            if (top > -1) {
3068
3069                // scroll vertically
3070
int bottom= JFaceTextUtil.getBottomIndex(fTextWidget);
3071                int lines= bottom - top;
3072                
3073                // if the widget is not scrollable as it is displaying the entire content
3074
// setTopIndex won't have any effect.
3075

3076                if (startLine >= top && startLine <= bottom && endLine >= top && endLine <= bottom ) {
3077
3078                    // do not scroll at all as it is already visible
3079

3080                } else {
3081
3082                    int delta= Math.max(0, lines - (endLine - startLine));
3083                    fTextWidget.setTopIndex(startLine - delta/3);
3084                    updateViewportListeners(INTERNAL);
3085                }
3086
3087                // scroll horizontally
3088

3089                if (endLine < startLine) {
3090                    endLine += startLine;
3091                    startLine= endLine - startLine;
3092                    endLine -= startLine;
3093                }
3094
3095                int startPixel= -1;
3096                int endPixel= -1;
3097
3098                if (endLine > startLine) {
3099                    // reveal the beginning of the range in the start line
3100
IRegion extent= getExtent(start, start);
3101                    startPixel= extent.getOffset() + fTextWidget.getHorizontalPixel();
3102                    endPixel= startPixel;
3103
3104                } else {
3105                    IRegion extent= getExtent(start, end);
3106                    startPixel= extent.getOffset() + fTextWidget.getHorizontalPixel();
3107                    endPixel= startPixel + extent.getLength();
3108                }
3109
3110                int visibleStart= fTextWidget.getHorizontalPixel();
3111                int visibleEnd= visibleStart + fTextWidget.getClientArea().width;
3112
3113                // scroll only if not yet visible
3114
if (startPixel < visibleStart || visibleEnd < endPixel) {
3115
3116                    // set buffer zone to 10 pixels
3117
int bufferZone= 10;
3118
3119                    int newOffset= visibleStart;
3120
3121                    int visibleWidth= visibleEnd - visibleStart;
3122                    int selectionPixelWidth= endPixel - startPixel;
3123
3124                    if (startPixel < visibleStart)
3125                        newOffset= startPixel;
3126                    else if (selectionPixelWidth + bufferZone < visibleWidth)
3127                        newOffset= endPixel + bufferZone - visibleWidth;
3128                    else
3129                        newOffset= startPixel;
3130
3131                    float index= ((float)newOffset) / ((float)getAverageCharWidth());
3132
3133                    fTextWidget.setHorizontalIndex(Math.round(index));
3134                }
3135
3136            }
3137        } catch (BadLocationException e) {
3138            throw new IllegalArgumentException JavaDoc(JFaceTextMessages.getString("TextViewer.error.invalid_range")); //$NON-NLS-1$
3139
}
3140    }
3141
3142    /**
3143     * Returns the width of the text when being drawn into this viewer's widget.
3144     *
3145     * @param text the string to measure
3146     * @return the width of the presentation of the given string
3147     * @deprecated use <code>getWidthInPixels(int, int)</code> instead
3148     */

3149    final protected int getWidthInPixels(String JavaDoc text) {
3150        GC gc= new GC(fTextWidget);
3151        gc.setFont(fTextWidget.getFont());
3152        Point extent= gc.textExtent(text);
3153        gc.dispose();
3154        return extent.x;
3155    }
3156
3157    /**
3158     * Returns the region covered by the given start and end offset.
3159     * The result is relative to the upper left corner of the widget
3160     * client area.
3161     *
3162     * @param start offset relative to the start of this viewer's view port
3163     * 0 <= offset <= getCharCount()
3164     * @param end offset relative to the start of this viewer's view port
3165     * 0 <= offset <= getCharCount()
3166     * @return the region covered by start and end offset
3167     */

3168    final protected IRegion getExtent(int start, int end) {
3169        if (end > 0 && start < end) {
3170            Rectangle bounds= fTextWidget.getTextBounds(start, end - 1);
3171            return new Region(bounds.x, bounds.width);
3172        }
3173        
3174        return new Region(fTextWidget.getLocationAtOffset(start).x, 0);
3175    }
3176
3177    /**
3178     * Returns the width of the representation of a text range in the
3179     * visible region of the viewer's document as drawn in this viewer's
3180     * widget.
3181     *
3182     * @param offset the offset of the text range in the visible region
3183     * @param length the length of the text range in the visible region
3184     * @return the width of the presentation of the specified text range
3185     * @since 2.0
3186     */

3187    final protected int getWidthInPixels(int offset, int length) {
3188        return getExtent(offset, offset + length).getLength();
3189    }
3190
3191    /**
3192     * Returns the average character width of this viewer's widget.
3193     *
3194     * @return the average character width of this viewer's widget
3195     */

3196    final protected int getAverageCharWidth() {
3197        GC gc= new GC(fTextWidget);
3198        gc.setFont(fTextWidget.getFont());
3199        int increment= gc.getFontMetrics().getAverageCharWidth();
3200        gc.dispose();
3201        return increment;
3202    }
3203
3204    /*
3205     * @see Viewer#refresh()
3206     */

3207    public void refresh() {
3208        setDocument(getDocument());
3209    }
3210
3211    //---- visible range support
3212

3213    /**
3214     * Returns the slave document manager
3215     *
3216     * @return the slave document manager
3217     * @since 2.1
3218     */

3219    protected ISlaveDocumentManager getSlaveDocumentManager() {
3220        if (fSlaveDocumentManager == null)
3221            fSlaveDocumentManager= createSlaveDocumentManager();
3222        return fSlaveDocumentManager;
3223    }
3224
3225    /**
3226     * Creates a new slave document manager. This implementation always
3227     * returns a <code>ChildDocumentManager</code>.
3228     *
3229     * @return ISlaveDocumentManager
3230     * @since 2.1
3231     */

3232    protected ISlaveDocumentManager createSlaveDocumentManager() {
3233        return new ChildDocumentManager();
3234    }
3235
3236    /*
3237     * @see org.eclipse.jface.text.ITextViewer#invalidateTextPresentation()
3238     */

3239    public final void invalidateTextPresentation() {
3240        if (fVisibleDocument != null) {
3241            fWidgetCommand.event= null;
3242            fWidgetCommand.start= 0;
3243            fWidgetCommand.length= fVisibleDocument.getLength();
3244            fWidgetCommand.text= fVisibleDocument.get();
3245            updateTextListeners(fWidgetCommand);
3246        }
3247    }
3248
3249    /**
3250     * Invalidates the given range of the text presentation.
3251     *
3252     * @param offset the offset of the range to be invalidated
3253     * @param length the length of the range to be invalidated
3254     * @since 2.1
3255     */

3256    public final void invalidateTextPresentation(int offset, int length) {
3257        if (fVisibleDocument != null) {
3258
3259            IRegion widgetRange= modelRange2WidgetRange(new Region(offset, length));
3260            if (widgetRange != null) {
3261
3262                fWidgetCommand.event= null;
3263                fWidgetCommand.start= widgetRange.getOffset();
3264                fWidgetCommand.length= widgetRange.getLength();
3265
3266                try {
3267                    fWidgetCommand.text= fVisibleDocument.get(widgetRange.getOffset(), widgetRange.getLength());
3268                    updateTextListeners(fWidgetCommand);
3269                } catch (BadLocationException x) {
3270                    // can not happen because of previous checking
3271
}
3272            }
3273        }
3274    }
3275
3276    /**
3277     * Initializes the text widget with the visual document and
3278     * invalidates the overall presentation.
3279     */

3280    private void initializeWidgetContents() {
3281
3282        if (fTextWidget != null && fVisibleDocument != null) {
3283
3284            // set widget content
3285
if (fDocumentAdapter == null)
3286                fDocumentAdapter= createDocumentAdapter();
3287
3288            fDocumentAdapter.setDocument(fVisibleDocument);
3289            fTextWidget.setContent(fDocumentAdapter);
3290
3291            // invalidate presentation
3292
invalidateTextPresentation();
3293        }
3294    }
3295
3296    /**
3297     * Frees the given document if it is a slave document.
3298     *
3299     * @param slave the potential slave document
3300     * @since 3.0
3301     */

3302    protected void freeSlaveDocument(IDocument slave) {
3303        ISlaveDocumentManager manager= getSlaveDocumentManager();
3304        if (manager != null && manager.isSlaveDocument(slave))
3305            manager.freeSlaveDocument(slave);
3306    }
3307
3308    /**
3309     * Sets this viewer's visible document. The visible document represents the
3310     * visible region of the viewer's input document.
3311     *
3312     * @param document the visible document
3313     */

3314    protected void setVisibleDocument(IDocument document) {
3315
3316        if (fVisibleDocument == document && fVisibleDocument instanceof ChildDocument) {
3317            // optimization for new child documents
3318
return;
3319        }
3320
3321        if (fVisibleDocument != null) {
3322            if (fVisibleDocumentListener != null)
3323                fVisibleDocument.removeDocumentListener(fVisibleDocumentListener);
3324            if (fVisibleDocument != document)
3325                freeSlaveDocument(fVisibleDocument);
3326        }
3327
3328        fVisibleDocument= document;
3329        initializeDocumentInformationMapping(fVisibleDocument);
3330
3331        initializeWidgetContents();
3332
3333        fFindReplaceDocumentAdapter= null;
3334        if (fVisibleDocument != null && fVisibleDocumentListener != null)
3335            fVisibleDocument.addDocumentListener(fVisibleDocumentListener);
3336    }
3337
3338    /**
3339     * Hook method called when the visible document is about to be changed.
3340     * <p>
3341     * Subclasses may override.
3342     *
3343     * @param event the document event
3344     * @since 3.0
3345     */

3346    protected void handleVisibleDocumentAboutToBeChanged(DocumentEvent event) {
3347    }
3348
3349    /**
3350     * Hook method called when the visible document has been changed.
3351     * <p>
3352     * Subclasses may override.
3353     *
3354     * @param event the document event
3355     * @since 3.0
3356     */

3357    protected void handleVisibleDocumentChanged(DocumentEvent event) {
3358    }
3359
3360    /**
3361     * Initializes the document information mapping between the given slave document and
3362     * its master document.
3363     *
3364     * @param visibleDocument the slave document
3365     * @since 2.1
3366     */

3367    protected void initializeDocumentInformationMapping(IDocument visibleDocument) {
3368        ISlaveDocumentManager manager= getSlaveDocumentManager();
3369        fInformationMapping= manager == null ? null : manager.createMasterSlaveMapping(visibleDocument);
3370    }
3371
3372    /**
3373     * Returns the viewer's visible document.
3374     *
3375     * @return the viewer's visible document
3376     */

3377    protected IDocument getVisibleDocument() {
3378        return fVisibleDocument;
3379    }
3380
3381    /**
3382     * Returns the offset of the visible region.
3383     *
3384     * @return the offset of the visible region
3385     */

3386    protected int _getVisibleRegionOffset() {
3387
3388        IDocument document= getVisibleDocument();
3389        if (document instanceof ChildDocument) {
3390            ChildDocument cdoc= (ChildDocument) document;
3391            return cdoc.getParentDocumentRange().getOffset();
3392        }
3393
3394        return 0;
3395    }
3396
3397    /*
3398     * @see ITextViewer#getVisibleRegion()
3399     */

3400    public IRegion getVisibleRegion() {
3401
3402        IDocument document= getVisibleDocument();
3403        if (document instanceof ChildDocument) {
3404            Position p= ((ChildDocument) document).getParentDocumentRange();
3405            return new Region(p.getOffset(), p.getLength());
3406        }
3407
3408        return new Region(0, document == null ? 0 : document.getLength());
3409    }
3410
3411    /*
3412     * @see ITextViewer#overlapsWithVisibleRegion(int, int)
3413     */

3414    public boolean overlapsWithVisibleRegion(int start, int length) {
3415        IDocument document= getVisibleDocument();
3416        if (document instanceof ChildDocument) {
3417            ChildDocument cdoc= (ChildDocument) document;
3418            return cdoc.getParentDocumentRange().overlapsWith(start, length);
3419        } else if (document != null) {
3420            int size= document.getLength();
3421            return (start >= 0 && length >= 0 && start + length <= size);
3422        }
3423        return false;
3424    }
3425
3426    /*
3427     * @see ITextViewer#setVisibleRegion(int, int)
3428     */

3429    public void setVisibleRegion(int start, int length) {
3430
3431        IRegion region= getVisibleRegion();
3432        if (start == region.getOffset() && length == region.getLength()) {
3433            // nothing to change
3434
return;
3435        }
3436
3437        setRedraw(false);
3438        try {
3439
3440            IDocument slaveDocument= createSlaveDocument(getVisibleDocument());
3441            if (updateSlaveDocument(slaveDocument, start, length))
3442                setVisibleDocument(slaveDocument);
3443
3444        } catch (BadLocationException x) {
3445            throw new IllegalArgumentException JavaDoc(JFaceTextMessages.getString("TextViewer.error.invalid_visible_region_2")); //$NON-NLS-1$
3446
} finally {
3447            setRedraw(true);
3448        }
3449    }
3450
3451    /*
3452     * @see ITextViewer#resetVisibleRegion()
3453     */

3454    public void resetVisibleRegion() {
3455        ISlaveDocumentManager manager= getSlaveDocumentManager();
3456        if (manager != null) {
3457            IDocument slave= getVisibleDocument();
3458            IDocument master= manager.getMasterDocument(slave);
3459            if (master != null) {
3460                setVisibleDocument(master);
3461                manager.freeSlaveDocument(slave);
3462            }
3463        }
3464    }
3465
3466
3467    //--------------------------------------
3468

3469    /*
3470     * @see ITextViewer#setTextDoubleClickStrategy(ITextDoubleClickStrategy, String)
3471     */

3472    public void setTextDoubleClickStrategy(ITextDoubleClickStrategy strategy, String JavaDoc contentType) {
3473
3474        if (strategy != null) {
3475            if (fDoubleClickStrategies == null)
3476                fDoubleClickStrategies= new HashMap JavaDoc();
3477            fDoubleClickStrategies.put(contentType, strategy);
3478        } else if (fDoubleClickStrategies != null)
3479            fDoubleClickStrategies.remove(contentType);
3480    }
3481
3482    /**
3483     * Selects from the given map the one which is registered under
3484     * the content type of the partition in which the given offset is located.
3485     *
3486     * @param plugins the map from which to choose
3487     * @param offset the offset for which to find the plug-in
3488     * @return the plug-in registered under the offset's content type
3489     */

3490    protected Object JavaDoc selectContentTypePlugin(int offset, Map JavaDoc plugins) {
3491        try {
3492            return selectContentTypePlugin(TextUtilities.getContentType(getDocument(), getDocumentPartitioning(), offset, true), plugins);
3493        } catch (BadLocationException x) {
3494            if (TRACE_ERRORS)
3495                System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.selectContentTypePlugin")); //$NON-NLS-1$
3496
}
3497        return null;
3498    }
3499
3500    /**
3501     * Selects from the given <code>plug-ins</code> this one which is
3502     * registered for the given content <code>type</code>.
3503     *
3504     * @param type the type to be used as lookup key
3505     * @param plugins the table to be searched
3506     * @return the plug-in in the map for the given content type
3507     */

3508    private Object JavaDoc selectContentTypePlugin(String JavaDoc type, Map JavaDoc plugins) {
3509
3510        if (plugins == null)
3511            return null;
3512
3513        return plugins.get(type);
3514    }
3515
3516    /**
3517     * Hook called on receipt of a <code>VerifyEvent</code>. The event has
3518     * been translated into a <code>DocumentCommand</code> which can now be
3519     * manipulated by interested parties. By default, the hook forwards the command
3520     * to the installed instances of <code>IAutoEditStrategy</code>.
3521     *
3522     * @param command the document command representing the verify event
3523     */

3524    protected void customizeDocumentCommand(DocumentCommand command) {
3525        if (isIgnoringAutoEditStrategies())
3526            return;
3527        
3528        IDocument document= getDocument();
3529        
3530        if (fTabsToSpacesConverter != null)
3531            fTabsToSpacesConverter.customizeDocumentCommand(document, command);
3532
3533        List JavaDoc strategies= (List JavaDoc) selectContentTypePlugin(command.offset, fAutoIndentStrategies);
3534        if (strategies == null)
3535            return;
3536
3537        switch (strategies.size()) {
3538        // optimization
3539
case 0:
3540            break;
3541
3542        case 1:
3543            ((IAutoEditStrategy) strategies.iterator().next()).customizeDocumentCommand(document, command);
3544            break;
3545
3546        // make iterator robust against adding/removing strategies from within strategies
3547
default:
3548            strategies= new ArrayList JavaDoc(strategies);
3549            for (final Iterator JavaDoc iterator= strategies.iterator(); iterator.hasNext(); )
3550                ((IAutoEditStrategy) iterator.next()).customizeDocumentCommand(document, command);
3551
3552            break;
3553        }
3554    }
3555
3556    /**
3557     * Handles the verify event issued by the viewer's text widget.
3558     *
3559     * @see VerifyListener#verifyText(VerifyEvent)
3560     * @param e the verify event
3561     */

3562    protected void handleVerifyEvent(VerifyEvent e) {
3563
3564        if (fEventConsumer != null) {
3565            fEventConsumer.processEvent(e);
3566            if (!e.doit)
3567                return;
3568        }
3569
3570        IRegion modelRange= event2ModelRange(e);
3571        fDocumentCommand.setEvent(e, modelRange);
3572        customizeDocumentCommand(fDocumentCommand);
3573        if (!fDocumentCommand.fillEvent(e, modelRange)) {
3574
3575            boolean compoundChange= fDocumentCommand.getCommandCount() > 1;
3576            try {
3577
3578                fVerifyListener.forward(false);
3579
3580                if (compoundChange && fUndoManager != null)
3581                    fUndoManager.beginCompoundChange();
3582
3583                fDocumentCommand.execute(getDocument());
3584
3585                if (fTextWidget != null) {
3586                    int documentCaret= fDocumentCommand.caretOffset;
3587                    if (documentCaret == -1) {
3588                        // old behavior of document command
3589
documentCaret= fDocumentCommand.offset + (fDocumentCommand.text == null ? 0 : fDocumentCommand.text.length());
3590                    }
3591
3592                    int widgetCaret= modelOffset2WidgetOffset(documentCaret);
3593                    if (widgetCaret == -1) {
3594                        // try to move it to the closest spot
3595
IRegion region= getModelCoverage();
3596                        if (region != null) {
3597                            if (documentCaret <= region.getOffset())
3598                                widgetCaret= 0;
3599                            else if (documentCaret >= region.getOffset() + region.getLength())
3600                                widgetCaret= getVisibleRegion().getLength();
3601                        }
3602                    }
3603
3604                    if (widgetCaret != -1) {
3605                        // there is a valid widget caret
3606
fTextWidget.setCaretOffset(widgetCaret);
3607                    }
3608
3609                    fTextWidget.showSelection();
3610                }
3611            } catch (BadLocationException x) {
3612
3613                if (TRACE_ERRORS)
3614                    System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.verifyText")); //$NON-NLS-1$
3615

3616            } finally {
3617
3618                if (compoundChange && fUndoManager != null)
3619                    fUndoManager.endCompoundChange();
3620
3621                fVerifyListener.forward(true);
3622
3623            }
3624        }
3625    }
3626
3627    //---- text manipulation
3628

3629    /**
3630     * Returns whether the marked region of this viewer is empty.
3631     *
3632     * @return <code>true</code> if the marked region of this viewer is empty, otherwise <code>false</code>
3633     * @since 2.0
3634     */

3635    private boolean isMarkedRegionEmpty() {
3636        return
3637            fTextWidget == null ||
3638            fMarkPosition == null ||
3639            fMarkPosition.isDeleted() ||
3640            modelRange2WidgetRange(fMarkPosition) == null;
3641    }
3642
3643    /*
3644     * @see ITextViewer#canDoOperation(int)
3645     */

3646    public boolean canDoOperation(int operation) {
3647
3648        if (fTextWidget == null || !redraws())
3649            return false;
3650
3651        switch (operation) {
3652            case CUT:
3653                return isEditable() &&(fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty());
3654            case COPY:
3655                return fTextWidget.getSelectionCount() > 0 || !isMarkedRegionEmpty();
3656            case DELETE:
3657            case PASTE:
3658                return isEditable();
3659            case SELECT_ALL:
3660                return true;
3661            case SHIFT_LEFT:
3662            case SHIFT_RIGHT:
3663                return isEditable() && fIndentChars != null && areMultipleLinesSelected();
3664            case PREFIX:
3665            case STRIP_PREFIX:
3666                return isEditable() && fDefaultPrefixChars != null;
3667            case UNDO:
3668                return fUndoManager != null && fUndoManager.undoable();
3669            case REDO:
3670                return fUndoManager != null && fUndoManager.redoable();
3671            case PRINT:
3672                return isPrintable();
3673        }
3674
3675        return false;
3676    }
3677
3678    /*
3679     * @see ITextViewer#doOperation(int)
3680     */

3681    public void doOperation(int operation) {
3682
3683        if (fTextWidget == null || !redraws())
3684            return;
3685
3686        Point selection= null;
3687
3688        switch (operation) {
3689
3690            case UNDO:
3691                if (fUndoManager != null) {
3692                    ignoreAutoEditStrategies(true);
3693                    fUndoManager.undo();
3694                    ignoreAutoEditStrategies(false);
3695                }
3696                break;
3697            case REDO:
3698                if (fUndoManager != null) {
3699                    ignoreAutoEditStrategies(true);
3700                    fUndoManager.redo();
3701                    ignoreAutoEditStrategies(false);
3702                }
3703                break;
3704            case CUT:
3705                if (fTextWidget.getSelectionCount() == 0)
3706                    copyMarkedRegion(true);
3707                else
3708                    fTextWidget.cut();
3709
3710                selection= fTextWidget.getSelectionRange();
3711                fireSelectionChanged(selection.x, selection.y);
3712
3713                break;
3714            case COPY:
3715                if (fTextWidget.getSelectionCount() == 0)
3716                    copyMarkedRegion(false);
3717                else
3718                    fTextWidget.copy();
3719                break;
3720            case PASTE:
3721// ignoreAutoEditStrategies(true);
3722
fTextWidget.paste();
3723                selection= fTextWidget.getSelectionRange();
3724                fireSelectionChanged(selection.x, selection.y);
3725// ignoreAutoEditStrategies(false);
3726
break;
3727            case DELETE:
3728                fTextWidget.invokeAction(ST.DELETE_NEXT);
3729                selection= fTextWidget.getSelectionRange();
3730                fireSelectionChanged(selection.x, selection.y);
3731                break;
3732            case SELECT_ALL: {
3733                if (getDocument() != null)
3734                    setSelectedRange(0, getDocument().getLength());
3735                break;
3736            }
3737            case SHIFT_RIGHT:
3738                shift(false, true, false);
3739                break;
3740            case SHIFT_LEFT:
3741                shift(false, false, false);
3742                break;
3743            case PREFIX:
3744                shift(true, true, true);
3745                break;
3746            case STRIP_PREFIX:
3747                shift(true, false, true);
3748                break;
3749            case PRINT:
3750                print();
3751                break;
3752        }
3753    }
3754
3755    /**
3756     * Tells this viewer whether the registered auto edit strategies should be ignored.
3757     *
3758     * @param ignore <code>true</code> if the strategies should be ignored.
3759     * @since 2.1
3760     */

3761    protected void ignoreAutoEditStrategies(boolean ignore) {
3762        if (fIgnoreAutoIndent == ignore)
3763            return;
3764
3765        fIgnoreAutoIndent= ignore;
3766
3767        IDocument document= getDocument();
3768        if (document instanceof IDocumentExtension2) {
3769            IDocumentExtension2 extension= (IDocumentExtension2) document;
3770            if (ignore)
3771                extension.ignorePostNotificationReplaces();
3772            else
3773                extension.acceptPostNotificationReplaces();
3774        }
3775    }
3776
3777    /**
3778     * Returns whether this viewer ignores the registered auto edit strategies.
3779     *
3780     * @return <code>true</code> if the strategies are ignored
3781     * @since 2.1
3782     */

3783    protected boolean isIgnoringAutoEditStrategies() {
3784        return fIgnoreAutoIndent;
3785    }
3786
3787    /*
3788     * @see ITextOperationTargetExtension#enableOperation(int, boolean)
3789     * @since 2.0
3790     */

3791    public void enableOperation(int operation, boolean enable) {
3792        /*
3793         * NO-OP by default.
3794         * Will be changed to regularly disable the known operations.
3795         */

3796    }
3797
3798    /**
3799     * Copies/cuts the marked region.
3800     *
3801     * @param delete <code>true</code> if the region should be deleted rather than copied.
3802     * @since 2.0
3803     */

3804    protected void copyMarkedRegion(boolean delete) {
3805
3806        if (fTextWidget == null)
3807            return;
3808
3809        if (fMarkPosition == null || fMarkPosition.isDeleted() || modelRange2WidgetRange(fMarkPosition) == null)
3810            return;
3811
3812        int widgetMarkOffset= modelOffset2WidgetOffset(fMarkPosition.offset);
3813        Point selection= fTextWidget.getSelection();
3814        if (selection.x <= widgetMarkOffset)
3815            fTextWidget.setSelection(selection.x, widgetMarkOffset);
3816        else
3817            fTextWidget.setSelection(widgetMarkOffset, selection.x);
3818
3819        if (delete) {
3820            fTextWidget.cut();
3821        } else {
3822            fTextWidget.copy();
3823            fTextWidget.setSelection(selection.x); // restore old cursor position
3824
}
3825    }
3826
3827    /**
3828     * Deletes the current selection. If the selection has the length 0
3829     * the selection is automatically extended to the right - either by 1
3830     * or by the length of line delimiter if at the end of a line.
3831     *
3832     * @deprecated use <code>StyledText.invokeAction</code> instead
3833     */

3834    protected void deleteText() {
3835        fTextWidget.invokeAction(ST.DELETE_NEXT);
3836    }
3837
3838    /**
3839     * A block is selected if the character preceding the start of the
3840     * selection is a new line character.
3841     *
3842     * @return <code>true</code> if a block is selected
3843     */

3844    protected boolean isBlockSelected() {
3845
3846        Point s= getSelectedRange();
3847        if (s.y == 0)
3848            return false;
3849
3850        try {
3851
3852            IDocument document= getDocument();
3853            int line= document.getLineOfOffset(s.x);
3854            int start= document.getLineOffset(line);
3855            return (s.x == start);
3856
3857        } catch (BadLocationException x) {
3858        }
3859
3860        return false;
3861    }
3862
3863    /**
3864     * Returns <code>true</code> if one line is completely selected or if multiple lines are selected.
3865     * Being completely selected means that all characters except the new line characters are
3866     * selected.
3867     *
3868     * @return <code>true</code> if one or multiple lines are selected
3869     * @since 2.0
3870     */

3871    protected boolean areMultipleLinesSelected() {
3872        Point s= getSelectedRange();
3873        if (s.y == 0)
3874            return false;
3875
3876        try {
3877
3878            IDocument document= getDocument();
3879            int startLine= document.getLineOfOffset(s.x);
3880            int endLine= document.getLineOfOffset(s.x + s.y);
3881            IRegion line= document.getLineInformation(startLine);
3882            return startLine != endLine || (s.x == line.getOffset() && s.y == line.getLength());
3883
3884        } catch (BadLocationException x) {
3885        }
3886
3887        return false;
3888    }
3889
3890    /**
3891     * Returns the index of the first line whose start offset is in the given text range.
3892     *
3893     * @param region the text range in characters where to find the line
3894     * @return the first line whose start index is in the given range, -1 if there is no such line
3895     */

3896    private int getFirstCompleteLineOfRegion(IRegion region) {
3897
3898        try {
3899
3900            IDocument d= getDocument();
3901
3902            int startLine= d.getLineOfOffset(region.getOffset());
3903
3904            int offset= d.getLineOffset(startLine);
3905            if (offset >= region.getOffset())
3906                return startLine;
3907
3908            offset= d.getLineOffset(startLine + 1);
3909            return (offset > region.getOffset() + region.getLength() ? -1 : startLine + 1);
3910
3911        } catch (BadLocationException x) {
3912            if (TRACE_ERRORS)
3913                System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.getFirstCompleteLineOfRegion")); //$NON-NLS-1$
3914
}
3915
3916        return -1;
3917    }
3918
3919
3920    /**
3921     * Creates a region describing the text block (something that starts at
3922     * the beginning of a line) completely containing the current selection.
3923     *
3924     * @param selection the selection to use
3925     * @return the region describing the text block comprising the given selection
3926     * @since 2.0
3927     */

3928    private IRegion getTextBlockFromSelection(Point selection) {
3929
3930        try {
3931            IDocument document= getDocument();
3932            IRegion line= document.getLineInformationOfOffset(selection.x);
3933            int length= selection.y == 0 ? line.getLength() : selection.y + (selection.x - line.getOffset());
3934            return new Region(line.getOffset(), length);
3935
3936        } catch (BadLocationException x) {
3937        }
3938
3939        return null;
3940    }
3941
3942    /**
3943     * Shifts a text block to the right or left using the specified set of prefix characters.
3944     * The prefixes must start at the beginning of the line.
3945     *
3946     * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
3947     * @param right says whether to shift to the right or the left
3948     *
3949     * @deprecated use shift(boolean, boolean, boolean) instead
3950     */

3951    protected void shift(boolean useDefaultPrefixes, boolean right) {
3952        shift(useDefaultPrefixes, right, false);
3953    }
3954
3955    /**
3956     * Shifts a text block to the right or left using the specified set of prefix characters.
3957     * If white space should be ignored the prefix characters must not be at the beginning of
3958     * the line when shifting to the left. There may be whitespace in front of the prefixes.
3959     *
3960     * @param useDefaultPrefixes says whether the configured default or indent prefixes should be used
3961     * @param right says whether to shift to the right or the left
3962     * @param ignoreWhitespace says whether whitespace in front of prefixes is allowed
3963     * @since 2.0
3964     */

3965    protected void shift(boolean useDefaultPrefixes, boolean right, boolean ignoreWhitespace) {
3966        if (fUndoManager != null)
3967            fUndoManager.beginCompoundChange();
3968        
3969        IDocument d= getDocument();
3970        Map JavaDoc partitioners= null;
3971        DocumentRewriteSession rewriteSession= null;
3972        try {
3973            Point selection= getSelectedRange();
3974            IRegion block= getTextBlockFromSelection(selection);
3975            ITypedRegion[] regions= TextUtilities.computePartitioning(d, getDocumentPartitioning(), block.getOffset(), block.getLength(), false);
3976
3977            int lineCount= 0;
3978            int[] lines= new int[regions.length * 2]; // [start line, end line, start line, end line, ...]
3979
for (int i= 0, j= 0; i < regions.length; i++, j+= 2) {
3980                // start line of region
3981
lines[j]= getFirstCompleteLineOfRegion(regions[i]);
3982                // end line of region
3983
int length= regions[i].getLength();
3984                int offset= regions[i].getOffset() + length;
3985                if (length > 0)
3986                    offset--;
3987                lines[j + 1]= (lines[j] == -1 ? -1 : d.getLineOfOffset(offset));
3988                lineCount += lines[j + 1] - lines[j] + 1;
3989            }
3990
3991            if (d instanceof IDocumentExtension4) {
3992                IDocumentExtension4 extension= (IDocumentExtension4) d;
3993                rewriteSession= extension.startRewriteSession(DocumentRewriteSessionType.SEQUENTIAL);
3994            } else {
3995                setRedraw(false);
3996                startSequentialRewriteMode(true);
3997            }
3998            if (lineCount >= 20)
3999                partitioners= TextUtilities.removeDocumentPartitioners(d);
4000
4001            // Perform the shift operation.
4002
Map JavaDoc map= (useDefaultPrefixes ? fDefaultPrefixChars : fIndentChars);
4003                for (int i= 0, j= 0; i < regions.length; i++, j += 2) {
4004                String JavaDoc[] prefixes= (String JavaDoc[]) selectContentTypePlugin(regions[i].getType(), map);
4005                if (prefixes != null && prefixes.length > 0 && lines[j] >= 0 && lines[j + 1] >= 0) {
4006                    if (right)
4007                        shiftRight(lines[j], lines[j + 1], prefixes[0]);
4008                    else
4009                        shiftLeft(lines[j], lines[j + 1], prefixes, ignoreWhitespace);
4010                }
4011            }
4012
4013        } catch (BadLocationException x) {
4014            if (TRACE_ERRORS)
4015                System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.shift_1")); //$NON-NLS-1$
4016

4017        } finally {
4018
4019            if (partitioners != null)
4020                TextUtilities.addDocumentPartitioners(d, partitioners);
4021            
4022            if (d instanceof IDocumentExtension4) {
4023                IDocumentExtension4 extension= (IDocumentExtension4) d;
4024                extension.stopRewriteSession(rewriteSession);
4025            } else {
4026                stopSequentialRewriteMode();
4027                setRedraw(true);
4028            }
4029
4030            if (fUndoManager != null)
4031                fUndoManager.endCompoundChange();
4032        }
4033    }
4034
4035    /**
4036     * Shifts the specified lines to the right inserting the given prefix
4037     * at the beginning of each line
4038     *
4039     * @param prefix the prefix to be inserted
4040     * @param startLine the first line to shift
4041     * @param endLine the last line to shift
4042     * @since 2.0
4043     */

4044    private void shiftRight(int startLine, int endLine, String JavaDoc prefix) {
4045
4046        try {
4047
4048            IDocument d= getDocument();
4049            while (startLine <= endLine) {
4050                d.replace(d.getLineOffset(startLine++), 0, prefix);
4051            }
4052
4053        } catch (BadLocationException x) {
4054            if (TRACE_ERRORS)
4055                System.out.println("TextViewer.shiftRight: BadLocationException"); //$NON-NLS-1$
4056
}
4057    }
4058
4059    /**
4060     * Shifts the specified lines to the right or to the left. On shifting to the right
4061     * it insert <code>prefixes[0]</code> at the beginning of each line. On shifting to the
4062     * left it tests whether each of the specified lines starts with one of the specified
4063     * prefixes and if so, removes the prefix.
4064     *
4065     * @param startLine the first line to shift
4066     * @param endLine the last line to shift
4067     * @param prefixes the prefixes to be used for shifting
4068     * @param ignoreWhitespace <code>true</code> if whitespace should be ignored, <code>false</code> otherwise
4069     * @since 2.0
4070     */

4071    private void shiftLeft(int startLine, int endLine, String JavaDoc[] prefixes, boolean ignoreWhitespace) {
4072
4073        IDocument d= getDocument();
4074
4075        try {
4076
4077            IRegion[] occurrences= new IRegion[endLine - startLine + 1];
4078
4079            // find all the first occurrences of prefix in the given lines
4080
for (int i= 0; i < occurrences.length; i++) {
4081
4082                IRegion line= d.getLineInformation(startLine + i);
4083                String JavaDoc text= d.get(line.getOffset(), line.getLength());
4084
4085                int index= -1;
4086                int[] found= TextUtilities.indexOf(prefixes, text, 0);
4087                if (found[0] != -1) {
4088                    if (ignoreWhitespace) {
4089                        String JavaDoc s= d.get(line.getOffset(), found[0]);
4090                        s= s.trim();
4091                        if (s.length() == 0)
4092                            index= line.getOffset() + found[0];
4093                    } else if (found[0] == 0)
4094                        index= line.getOffset();
4095                }
4096
4097                if (index > -1) {
4098                    // remember where prefix is in line, so that it can be removed
4099
int length= prefixes[found[1]].length();
4100                    if (length == 0 && !ignoreWhitespace && line.getLength() > 0) {
4101                        // found a non-empty line which cannot be shifted
4102
return;
4103                    }
4104                    occurrences[i]= new Region(index, length);
4105                } else {
4106                    // found a line which cannot be shifted
4107
return;
4108                }
4109            }
4110
4111            // OK - change the document
4112
int decrement= 0;
4113            for (int i= 0; i < occurrences.length; i++) {
4114                IRegion r= occurrences[i];
4115                d.replace(r.getOffset() - decrement, r.getLength(), ""); //$NON-NLS-1$
4116
decrement += r.getLength();
4117            }
4118
4119        } catch (BadLocationException x) {
4120            if (TRACE_ERRORS)
4121                System.out.println("TextViewer.shiftLeft: BadLocationException"); //$NON-NLS-1$
4122
}
4123    }
4124
4125    /**
4126     * Returns whether the shown text can be printed.
4127     *
4128     * @return the viewer's printable mode
4129     */

4130    protected boolean isPrintable() {
4131        /*
4132         * 1GK7Q10: ITPUI:WIN98 - internal error after invoking print at editor view
4133         * Changed from returning true to testing the length of the printer queue
4134         */

4135        PrinterData[] printerList= Printer.getPrinterList();
4136        return (printerList != null && printerList.length > 0);
4137    }
4138
4139    /**
4140     * Brings up a print dialog and calls <code>printContents(Printer)</code> which
4141     * performs the actual print.
4142     */

4143    protected void print() {
4144
4145        final PrintDialog dialog= new PrintDialog(fTextWidget.getShell(), SWT.PRIMARY_MODAL);
4146        final PrinterData data= dialog.open();
4147
4148        if (data != null) {
4149
4150            final Printer printer= new Printer(data);
4151            final Runnable JavaDoc styledTextPrinter= fTextWidget.print(printer);
4152
4153            Thread JavaDoc printingThread= new Thread JavaDoc("Printing") { //$NON-NLS-1$
4154
public void run() {
4155                    styledTextPrinter.run();
4156                    printer.dispose();
4157                }
4158            };
4159            printingThread.start();
4160        }
4161    }
4162
4163
4164    //------ find support
4165

4166    /**
4167     * Adheres to the contract of {@link IFindReplaceTarget#canPerformFind()}.
4168     *
4169     * @return <code>true</code> if find can be performed, <code>false</code> otherwise
4170     */

4171    protected boolean canPerformFind() {
4172        IDocument d= getVisibleDocument();
4173        return (fTextWidget != null && d != null && d.getLength() > 0);
4174    }
4175
4176    /**
4177     * Adheres to the contract of {@link IFindReplaceTarget#findAndSelect(int, String, boolean, boolean, boolean)}.
4178     *
4179     * @param startPosition the start position
4180     * @param findString the find string specification
4181     * @param forwardSearch the search direction
4182     * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
4183     * @param wholeWord <code>true</code> if match must be whole words, <code>false</code> otherwise
4184     * @return the model offset of the first match
4185     * @deprecated as of 3.0 use {@link #findAndSelect(int, String, boolean, boolean, boolean, boolean)}
4186     */

4187    protected int findAndSelect(int startPosition, String JavaDoc findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) {
4188        try {
4189            return findAndSelect(startPosition, findString, forwardSearch, caseSensitive, wholeWord, false);
4190        } catch (IllegalStateException JavaDoc ex) {
4191            return -1;
4192        } catch (PatternSyntaxException JavaDoc ex) {
4193            return -1;
4194        }
4195    }
4196
4197    /**
4198     * Adheres to the contract of
4199     * {@link org.eclipse.jface.text.IFindReplaceTargetExtension3#findAndSelect(int, String, boolean, boolean, boolean, boolean)}.
4200     *
4201     * @param startPosition the start position
4202     * @param findString the find string specification
4203     * @param forwardSearch the search direction
4204     * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
4205     * @param wholeWord <code>true</code> if matches must be whole words, <code>false</code> otherwise
4206     * @param regExSearch <code>true</code> if <code>findString</code> is a regular expression, <code>false</code> otherwise
4207     * @return the model offset of the first match
4208     *
4209     */

4210    protected int findAndSelect(int startPosition, String JavaDoc findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord, boolean regExSearch) {
4211        if (fTextWidget == null)
4212            return -1;
4213
4214        try {
4215
4216            int widgetOffset= (startPosition == -1 ? startPosition : modelOffset2WidgetOffset(startPosition));
4217            FindReplaceDocumentAdapter adapter= getFindReplaceDocumentAdapter();
4218            IRegion matchRegion= adapter.find(widgetOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
4219            if (matchRegion != null) {
4220                int widgetPos= matchRegion.getOffset();
4221                int length= matchRegion.getLength();
4222
4223                // Prevents setting of widget selection with line delimiters at beginning or end
4224
char startChar= adapter.charAt(widgetPos);
4225                char endChar= adapter.charAt(widgetPos+length-1);
4226                boolean borderHasLineDelimiter= startChar == '\n' || startChar == '\r' || endChar == '\n' || endChar == '\r';
4227                boolean redraws= redraws();
4228                if (borderHasLineDelimiter && redraws)
4229                    setRedraw(false);
4230
4231                if (redraws()) {
4232                    fTextWidget.setSelectionRange(widgetPos, length);
4233                    internalRevealRange(widgetPos, widgetPos + length);
4234                    selectionChanged(widgetPos, length);
4235                } else {
4236                    setSelectedRange(widgetOffset2ModelOffset(widgetPos), length);
4237                    if (redraws)
4238                        setRedraw(true);
4239                }
4240
4241                return widgetOffset2ModelOffset(widgetPos);
4242            }
4243
4244        } catch (BadLocationException x) {
4245            if (TRACE_ERRORS)
4246                System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
4247
}
4248
4249        return -1;
4250    }
4251
4252    /**
4253     * Adheres to the contract of {@link org.eclipse.jface.text.IFindReplaceTargetExtension3#findAndSelect(int, String, boolean, boolean, boolean, boolean)}.
4254     *
4255     * @param startPosition the start position
4256     * @param findString the find string specification
4257     * @param forwardSearch the search direction
4258     * @param caseSensitive <code>true</code> if case sensitive, <code>false</code> otherwise
4259     * @param wholeWord <code>true</code> if matches must be whole words, <code>false</code> otherwise
4260     * @param rangeOffset the search scope offset
4261     * @param rangeLength the search scope length
4262     * @param regExSearch <code>true</code> if <code>findString</code> is a regular expression, <code>false</code> otherwise
4263     * @return the model offset of the first match
4264     * @since 3.0
4265     */

4266    protected int findAndSelectInRange(int startPosition, String JavaDoc findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord, int rangeOffset, int rangeLength, boolean regExSearch) {
4267        if (fTextWidget == null)
4268            return -1;
4269
4270        try {
4271
4272            int modelOffset;
4273            if (forwardSearch && (startPosition == -1 || startPosition < rangeOffset)) {
4274                modelOffset= rangeOffset;
4275            } else if (!forwardSearch && (startPosition == -1 || startPosition > rangeOffset + rangeLength)) {
4276                modelOffset= rangeOffset + rangeLength;
4277            } else {
4278                modelOffset= startPosition;
4279            }
4280
4281            int widgetOffset= modelOffset2WidgetOffset(modelOffset);
4282            if (widgetOffset == -1)
4283                return -1;
4284
4285            FindReplaceDocumentAdapter adapter= getFindReplaceDocumentAdapter();
4286            IRegion matchRegion= adapter.find(widgetOffset, findString, forwardSearch, caseSensitive, wholeWord, regExSearch);
4287            int widgetPos= -1;
4288            int length= 0;
4289            if (matchRegion != null) {
4290                widgetPos= matchRegion.getOffset();
4291                length= matchRegion.getLength();
4292            }
4293            int modelPos= widgetPos == -1 ? -1 : widgetOffset2ModelOffset(widgetPos);
4294
4295            if (widgetPos != -1 && (modelPos < rangeOffset || modelPos + length > rangeOffset + rangeLength))
4296                widgetPos= -1;
4297
4298            if (widgetPos > -1) {
4299
4300                // Prevents setting of widget selection with line delimiters at beginning or end
4301
char startChar= adapter.charAt(widgetPos);
4302                char endChar= adapter.charAt(widgetPos+length-1);
4303                boolean borderHasLineDelimiter= startChar == '\n' || startChar == '\r' || endChar == '\n' || endChar == '\r';
4304                boolean redraws= redraws();
4305                if (borderHasLineDelimiter && redraws)
4306                    setRedraw(false);
4307
4308                if (redraws()) {
4309                    fTextWidget.setSelectionRange(widgetPos, length);
4310                    internalRevealRange(widgetPos, widgetPos + length);
4311                    selectionChanged(widgetPos, length);
4312                } else {
4313                    setSelectedRange(modelPos, length);
4314                    if (redraws)
4315                        setRedraw(true);
4316                }
4317
4318                return modelPos;
4319            }
4320
4321
4322        } catch (BadLocationException x) {
4323            if (TRACE_ERRORS)
4324                System.out.println(JFaceTextMessages.getString("TextViewer.error.bad_location.findAndSelect")); //$NON-NLS-1$
4325
}
4326
4327        return -1;
4328    }
4329
4330    //---------- text presentation support
4331

4332    /*
4333     * @see ITextViewer#setTextColor(Color)
4334     */

4335    public void setTextColor(Color color) {
4336        if (color != null)
4337            setTextColor(color, 0, getDocument().getLength(), true);
4338    }
4339
4340    /*
4341     * @see ITextViewer#setTextColor(Color, start, length, boolean)
4342     */

4343    public void setTextColor(Color color, int start, int length, boolean controlRedraw) {
4344        if (fTextWidget != null) {
4345
4346            StyleRange s= new StyleRange();
4347            s.foreground= color;
4348            s.start= start;
4349            s.length= length;
4350
4351            s= modelStyleRange2WidgetStyleRange(s);
4352            if (s != null) {
4353                if (controlRedraw)
4354                    fTextWidget.setRedraw(false);
4355                try {
4356                    fTextWidget.setStyleRange(s);
4357                } finally {
4358                    if (controlRedraw)
4359                        fTextWidget.setRedraw(true);
4360                }
4361            }
4362        }
4363    }
4364
4365    /**
4366     * Adds the given presentation to the viewer's style information.
4367     *
4368     * @param presentation the presentation to be added
4369     */

4370    private void addPresentation(TextPresentation presentation) {
4371
4372        StyleRange range= presentation.getDefaultStyleRange();
4373        if (range != null) {
4374
4375            range= modelStyleRange2WidgetStyleRange(range);
4376            if (range != null)
4377                fTextWidget.setStyleRange(range);
4378
4379            Iterator JavaDoc e= presentation.getNonDefaultStyleRangeIterator();
4380            while (e.hasNext()) {
4381                range= (StyleRange) e.next();
4382                range= modelStyleRange2WidgetStyleRange(range);
4383                if (range != null)
4384                    fTextWidget.setStyleRange(range);
4385            }
4386
4387        } else {
4388            IRegion region= modelRange2WidgetRange(presentation.getCoverage());
4389            if (region == null)
4390                return;
4391
4392            List JavaDoc list= new ArrayList JavaDoc(presentation.getDenumerableRanges());
4393            Iterator JavaDoc e= presentation.getAllStyleRangeIterator();
4394            while (e.hasNext()) {
4395                range= (StyleRange) e.next();
4396                range= modelStyleRange2WidgetStyleRange(range);
4397                if (range != null)
4398                    list.add(range);
4399            }
4400
4401            if (!list.isEmpty()) {
4402                StyleRange[] ranges= new StyleRange[list.size()];
4403                list.toArray(ranges);
4404                fTextWidget.replaceStyleRanges(region.getOffset(), region.getLength(), ranges);
4405            }
4406        }
4407    }
4408
4409    /**
4410     * Applies the given presentation to the given text widget. Helper method.
4411     *
4412     * @param presentation the style information
4413     * @since 2.1
4414     */

4415    private void applyTextPresentation(TextPresentation presentation) {
4416
4417        List JavaDoc list= new ArrayList JavaDoc(presentation.getDenumerableRanges());
4418        Iterator JavaDoc e= presentation.getAllStyleRangeIterator();
4419        while (e.hasNext()) {
4420            StyleRange range= (StyleRange) e.next();
4421            range= modelStyleRange2WidgetStyleRange(range);
4422            if (range != null)
4423                list.add(range);
4424        }
4425
4426        if (!list.isEmpty()) {
4427            StyleRange[] ranges= new StyleRange[list.size()];
4428            list.toArray(ranges);
4429            fTextWidget.setStyleRanges(ranges);
4430        }
4431    }
4432
4433    /**
4434     * Returns the visible region if it is not equal to the whole document.
4435     * Otherwise returns <code>null</code>.
4436     *
4437     * @return the viewer's visible region if smaller than input document, otherwise <code>null</code>
4438     */

4439    protected IRegion _internalGetVisibleRegion() {
4440
4441        IDocument document= getVisibleDocument();
4442        if (document instanceof ChildDocument) {
4443            Position p= ((ChildDocument) document).getParentDocumentRange();
4444            return new Region(p.getOffset(), p.getLength());
4445        }
4446
4447        return null;
4448    }
4449
4450    /*
4451     * @see ITextViewer#changeTextPresentation(TextPresentation, boolean)
4452     */

4453    public void changeTextPresentation(TextPresentation presentation, boolean controlRedraw) {
4454
4455        if (presentation == null || !redraws())
4456            return;
4457
4458        if (fTextWidget == null)
4459            return;
4460
4461
4462        /*
4463         * Call registered text presentation listeners
4464         * and let them apply their presentation.
4465         */

4466        if (fTextPresentationListeners != null) {
4467            ArrayList JavaDoc listeners= new ArrayList JavaDoc(fTextPresentationListeners);
4468            for (int i= 0, size= listeners.size(); i < size; i++) {
4469                ITextPresentationListener listener= (ITextPresentationListener)listeners.get(i);
4470                listener.applyTextPresentation(presentation);
4471            }
4472        }
4473
4474        if (presentation.isEmpty())
4475            return;
4476
4477        if (controlRedraw)
4478            fTextWidget.setRedraw(false);
4479
4480        if (fReplaceTextPresentation)
4481            applyTextPresentation(presentation);
4482        else
4483            addPresentation(presentation);
4484
4485        if (controlRedraw)
4486            fTextWidget.setRedraw(true);
4487    }
4488
4489    /*
4490     * @see ITextViewer#getFindReplaceTarget()
4491     */

4492    public IFindReplaceTarget getFindReplaceTarget() {
4493        if (fFindReplaceTarget == null)
4494            fFindReplaceTarget= new FindReplaceTarget();
4495        return fFindReplaceTarget;
4496    }
4497
4498    /**
4499     * Returns the find/replace document adapter.
4500     *
4501     * @return the find/replace document adapter.
4502     * @since 3.0
4503     */

4504    protected FindReplaceDocumentAdapter getFindReplaceDocumentAdapter() {
4505        if (fFindReplaceDocumentAdapter == null)
4506            fFindReplaceDocumentAdapter= new FindReplaceDocumentAdapter(getVisibleDocument());
4507        return fFindReplaceDocumentAdapter;
4508    }
4509
4510    /*
4511     * @see ITextViewer#getTextOperationTarget()
4512     */

4513    public ITextOperationTarget getTextOperationTarget() {
4514        return this;
4515    }
4516
4517    /*
4518     * @see ITextViewerExtension#appendVerifyKeyListener(VerifyKeyListener)
4519     * @since 2.0
4520     */

4521    public void appendVerifyKeyListener(VerifyKeyListener listener) {
4522        int index= fVerifyKeyListenersManager.numberOfListeners();
4523        fVerifyKeyListenersManager.insertListener(listener, index);
4524    }
4525
4526    /*
4527     * @see ITextViewerExtension#prependVerifyKeyListener(VerifyKeyListener)
4528     * @since 2.0
4529     */

4530    public void prependVerifyKeyListener(VerifyKeyListener listener) {
4531        fVerifyKeyListenersManager.insertListener(listener, 0);
4532
4533    }
4534
4535    /*
4536     * @see ITextViewerExtension#removeVerifyKeyListener(VerifyKeyListener)
4537     * @since 2.0
4538     */

4539    public void removeVerifyKeyListener(VerifyKeyListener listener) {
4540        fVerifyKeyListenersManager.removeListener(listener);
4541    }
4542
4543    /*
4544     * @see ITextViewerExtension#getMark()
4545     * @since 2.0
4546     */

4547    public int getMark() {
4548        return fMarkPosition == null || fMarkPosition.isDeleted() ? -1 : fMarkPosition.getOffset();
4549    }
4550
4551    /*
4552     * @see ITextViewerExtension#setMark(int)
4553     * @since 2.0
4554     */

4555    public void setMark(int offset) {
4556
4557        // clear
4558
if (offset == -1) {
4559            if (fMarkPosition != null && !fMarkPosition.isDeleted()) {
4560
4561                IDocument document= getDocument();
4562                if (document != null)
4563                    document.removePosition(fMarkPosition);
4564            }
4565
4566            fMarkPosition= null;
4567
4568            markChanged(-1, 0);
4569
4570        // set
4571
} else {
4572
4573            IDocument document= getDocument();
4574            if (document == null) {
4575                fMarkPosition= null;
4576                return;
4577            }
4578
4579            if (fMarkPosition != null)
4580                document.removePosition(fMarkPosition);
4581
4582            fMarkPosition= null;
4583
4584            try {
4585
4586                Position position= new Position(offset);
4587                document.addPosition(MARK_POSITION_CATEGORY, position);
4588                fMarkPosition= position;
4589
4590            } catch (BadLocationException e) {
4591                return;
4592            } catch (BadPositionCategoryException e) {
4593                return;
4594            }
4595
4596            markChanged(modelOffset2WidgetOffset(fMarkPosition.offset), 0);
4597        }
4598    }
4599
4600    /*
4601     * @see Viewer#inputChanged(Object, Object)
4602     * @since 2.0
4603     */

4604    protected void inputChanged(Object JavaDoc newInput, Object JavaDoc oldInput) {
4605
4606        IDocument oldDocument= (IDocument) oldInput;
4607        if (oldDocument != null) {
4608
4609            if (fMarkPosition != null && !fMarkPosition.isDeleted())
4610                oldDocument.removePosition(fMarkPosition);
4611
4612            try {
4613                oldDocument.removePositionUpdater(fMarkPositionUpdater);
4614                oldDocument.removePositionCategory(MARK_POSITION_CATEGORY);
4615
4616            } catch (BadPositionCategoryException e) {
4617            }
4618        }
4619
4620        fMarkPosition= null;
4621
4622        if (oldDocument instanceof IDocumentExtension4) {
4623            IDocumentExtension4 document= (IDocumentExtension4) oldDocument;
4624            document.removeDocumentRewriteSessionListener(fDocumentRewriteSessionListener);
4625        }
4626
4627        super.inputChanged(newInput, oldInput);
4628
4629        if (newInput instanceof IDocumentExtension4) {
4630            IDocumentExtension4 document= (IDocumentExtension4) newInput;
4631            document.addDocumentRewriteSessionListener(fDocumentRewriteSessionListener);
4632        }
4633
4634        IDocument newDocument= (IDocument) newInput;
4635        if (newDocument != null) {
4636            newDocument.addPositionCategory(MARK_POSITION_CATEGORY);
4637            newDocument.addPositionUpdater(fMarkPositionUpdater);
4638        }
4639    }
4640
4641    /**
4642     * Informs all text listeners about the change of the viewer's redraw state.
4643     * @since 2.0
4644     */

4645    private void fireRedrawChanged() {
4646        fWidgetCommand.start= 0;
4647        fWidgetCommand.length= 0;
4648        fWidgetCommand.text= null;
4649        fWidgetCommand.event= null;
4650        updateTextListeners(fWidgetCommand);
4651    }
4652
4653    /**
4654     * Enables the redrawing of this text viewer.
4655     * @since 2.0
4656     */

4657    protected void enabledRedrawing() {
4658        enabledRedrawing(-1);
4659    }
4660    /**
4661     * Enables the redrawing of this text viewer.
4662     *
4663     * @param topIndex the top index to be set or <code>-1</code>
4664     * @since 3.0
4665     */

4666    protected void enabledRedrawing(int topIndex) {
4667        if (fDocumentAdapter instanceof IDocumentAdapterExtension) {
4668            IDocumentAdapterExtension extension= (IDocumentAdapterExtension) fDocumentAdapter;
4669            StyledText textWidget= getTextWidget();
4670            if (textWidget != null && !textWidget.isDisposed()) {
4671                extension.resumeForwardingDocumentChanges();
4672                if (topIndex > -1) {
4673                    try {
4674                        setTopIndex(topIndex);
4675                    } catch (IllegalArgumentException JavaDoc x) {
4676                        // changes don't allow for the previous top pixel
4677
}
4678                }
4679            }
4680        }
4681
4682        if (fViewerState != null) {
4683            fViewerState.restore(topIndex == -1);
4684            fViewerState= null;
4685        }
4686
4687        if (fTextWidget != null && !fTextWidget.isDisposed())
4688            fTextWidget.setRedraw(true);
4689
4690        fireRedrawChanged();
4691    }
4692
4693    /**
4694     * Disables the redrawing of this text viewer. Subclasses may extend.
4695     * @since 2.0
4696     */

4697    protected void disableRedrawing() {
4698        if (fViewerState == null)
4699            fViewerState= new ViewerState();
4700
4701        if (fDocumentAdapter instanceof IDocumentAdapterExtension) {
4702            IDocumentAdapterExtension extension= (IDocumentAdapterExtension) fDocumentAdapter;
4703            extension.stopForwardingDocumentChanges();
4704        }
4705
4706        if (fTextWidget != null && !fTextWidget.isDisposed())
4707            fTextWidget.setRedraw(false);
4708
4709        fireRedrawChanged();
4710    }
4711
4712    /*
4713     * @see ITextViewerExtension#setRedraw(boolean)
4714     * @since 2.0
4715     */

4716    public final void setRedraw(boolean redraw) {
4717        setRedraw(redraw, -1);
4718    }
4719
4720    /**
4721     * Basically same functionality as
4722     * <code>ITextViewerExtension.setRedraw(boolean)</code>. Adds a way for
4723     * subclasses to pass in a desired top index that should be used when
4724     * <code>redraw</code> is <code>true</code>. If <code>topIndex</code>
4725     * is -1, this method is identical to
4726     * <code>ITextViewerExtension.setRedraw(boolean)</code>.
4727     *
4728     * @see ITextViewerExtension#setRedraw(boolean)
4729     *
4730     * @param redraw
4731     * @param topIndex
4732     * @since 3.0
4733     */

4734    protected final void setRedraw(boolean redraw, int topIndex) {
4735        if (!redraw) {
4736
4737            ++ fRedrawCounter;
4738            if (fRedrawCounter == 1)
4739                disableRedrawing();
4740
4741        } else {
4742            -- fRedrawCounter;
4743            if (fRedrawCounter == 0) {
4744                if (topIndex == -1)
4745                    enabledRedrawing();
4746                else
4747                    enabledRedrawing(topIndex);
4748            }
4749        }
4750    }
4751
4752    /**
4753     * Returns whether this viewer redraws itself.
4754     *
4755     * @return <code>true</code> if this viewer redraws itself
4756     * @since 2.0
4757     */

4758    protected final boolean redraws() {
4759        return fRedrawCounter <= 0;
4760    }
4761
4762    /**
4763     * Starts the sequential rewrite mode of the viewer's document.
4764     *
4765     * @param normalized <code>true</code> if the rewrite is performed from the start to the end of the document
4766     * @since 2.0
4767     * @deprecated since 3.1 use {@link IDocumentExtension4#startRewriteSession(DocumentRewriteSessionType)} instead
4768     */

4769    protected final void startSequentialRewriteMode(boolean normalized) {
4770        IDocument document= getDocument();
4771        if (document instanceof IDocumentExtension) {
4772            IDocumentExtension extension= (IDocumentExtension) document;
4773            extension.startSequentialRewrite(normalized);
4774        }
4775    }
4776
4777    /**
4778     * Sets the sequential rewrite mode of the viewer's document.
4779     *
4780     * @since 2.0
4781     * @deprecated since 3.1 use {@link IDocumentExtension4#stopRewriteSession(DocumentRewriteSession)} instead
4782     */

4783    protected final void stopSequentialRewriteMode() {
4784        IDocument document= getDocument();
4785        if (document instanceof IDocumentExtension) {
4786            IDocumentExtension extension= (IDocumentExtension) document;
4787            extension.stopSequentialRewrite();
4788        }
4789    }
4790
4791    /*
4792     * @see org.eclipse.jface.text.ITextViewerExtension#getRewriteTarget()
4793     * @since 2.0
4794     */

4795    public IRewriteTarget getRewriteTarget() {
4796        if (fRewriteTarget == null)
4797            fRewriteTarget= new RewriteTarget();
4798        return fRewriteTarget;
4799    }
4800
4801    /*
4802     * @see org.eclipse.jface.text.ITextViewerExtension2#getCurrentTextHover()
4803     */

4804    public ITextHover getCurrentTextHover() {
4805        if (fTextHoverManager == null)
4806            return null;
4807        return fTextHoverManager.getCurrentTextHover();
4808    }
4809
4810    /*
4811     * @see org.eclipse.jface.text.ITextViewerExtension2#getHoverEventLocation()
4812     */

4813    public Point getHoverEventLocation() {
4814        if (fTextHoverManager == null)
4815            return null;
4816        return fTextHoverManager.getHoverEventLocation();
4817    }
4818
4819    /**
4820     * Returns the paint manager of this viewer.
4821     *
4822     * @return the paint manager of this viewer
4823     * @since 2.1
4824     */

4825    protected PaintManager getPaintManager() {
4826        if (fPaintManager == null)
4827            fPaintManager= new PaintManager(this);
4828        return fPaintManager;
4829    }
4830
4831    /**
4832     * Adds the given painter to this viewer. If the painter is already registered
4833     * this method is without effect.
4834     *
4835     * @param painter the painter to be added
4836     * @since 2.1
4837     */

4838    public void addPainter(IPainter painter) {
4839        getPaintManager().addPainter(painter);
4840    }
4841
4842    /**
4843     * Removes the given painter from this viewer. If the painter has previously not been
4844     * added to this viewer this method is without effect.
4845     *
4846     * @param painter the painter to be removed
4847     * @since 2.1
4848     */

4849    public void removePainter(IPainter painter) {
4850        getPaintManager().removePainter(painter);
4851    }
4852
4853    // ----------------------------------- conversions -------------------------------------------------------
4854

4855    /**
4856     * Implements the contract of {@link ITextViewerExtension5#modelLine2WidgetLine(int)}.
4857     *
4858     * @param modelLine the model line
4859     * @return the corresponding widget line or <code>-1</code>
4860     * @since 2.1
4861     */

4862    public int modelLine2WidgetLine(int modelLine) {
4863        if (fInformationMapping == null)
4864            return modelLine;
4865
4866        try {
4867            return fInformationMapping.toImageLine(modelLine);
4868        } catch (BadLocationException x) {
4869    }
4870
4871        return -1;
4872    }
4873
4874    /**
4875     * Implements the contract of {@link ITextViewerExtension5#modelOffset2WidgetOffset(int)}.
4876     *
4877     * @param modelOffset the model offset
4878     * @return the corresponding widget offset or <code>-1</code>
4879     * @since 2.1
4880     */

4881    public int modelOffset2WidgetOffset(int modelOffset) {
4882        if (fInformationMapping == null)
4883            return modelOffset;
4884
4885        try {
4886            return fInformationMapping.toImageOffset(modelOffset);
4887        } catch (BadLocationException x) {
4888        }
4889
4890        return -1;
4891    }
4892
4893    /**
4894     * Implements the contract of {@link ITextViewerExtension5#modelRange2WidgetRange(IRegion)}.
4895     *
4896     * @param modelRange the model range
4897     * @return the corresponding widget range or <code>null</code>
4898     * @since 2.1
4899     */

4900    public IRegion modelRange2WidgetRange(IRegion modelRange) {
4901        if (fInformationMapping == null)
4902            return modelRange;
4903
4904        try {
4905
4906            if (modelRange.getLength() < 0) {
4907                Region reversed= new Region(modelRange.getOffset() + modelRange.getLength(), -modelRange.getLength());
4908                IRegion result= fInformationMapping.toImageRegion(reversed);
4909                if (result != null)
4910                    return new Region(result.getOffset() + result.getLength(), -result.getLength());
4911            }
4912            return fInformationMapping.toImageRegion(modelRange);
4913
4914        } catch (BadLocationException x) {
4915        }
4916
4917        return null;
4918    }
4919
4920    /**
4921     * Similar to {@link #modelRange2WidgetRange(IRegion)}, but more forgiving:
4922     * if <code>modelRange</code> describes a region entirely hidden in the
4923     * image, then this method returns the zero-length region at the offset of
4924     * the folded region.
4925     *
4926     * @param modelRange the model range
4927     * @return the corresponding widget range, or <code>null</code>
4928     * @since 3.1
4929     */

4930    protected IRegion modelRange2ClosestWidgetRange(IRegion modelRange) {
4931        if (!(fInformationMapping instanceof IDocumentInformationMappingExtension2))
4932            return modelRange2WidgetRange(modelRange);
4933
4934        try {
4935            if (modelRange.getLength() < 0) {
4936                Region reversed= new Region(modelRange.getOffset() + modelRange.getLength(), -modelRange.getLength());
4937                IRegion result= ((IDocumentInformationMappingExtension2) fInformationMapping).toClosestImageRegion(reversed);
4938                if (result != null)
4939                    return new Region(result.getOffset() + result.getLength(), -result.getLength());
4940            }
4941            return ((IDocumentInformationMappingExtension2) fInformationMapping).toClosestImageRegion(modelRange);
4942
4943        } catch (BadLocationException x) {
4944        }
4945
4946        return null;
4947    }
4948
4949    /**
4950     * Implements the contract of {@link ITextViewerExtension5#widgetLine2ModelLine(int)}.
4951     *
4952     * @param widgetLine the widget line
4953     * @return the corresponding model line
4954     * @since 2.1
4955     */

4956    public int widgetlLine2ModelLine(int widgetLine) {
4957        return widgetLine2ModelLine(widgetLine);
4958    }
4959
4960    /**
4961     * Implements the contract of {@link ITextViewerExtension5#widgetLine2ModelLine(int)}.
4962     *
4963     * @param widgetLine the widget line
4964     * @return the corresponding model line or <code>-1</code>
4965     * @since 3.0
4966     */

4967    public int widgetLine2ModelLine(int widgetLine) {
4968        if (fInformationMapping == null)
4969            return widgetLine;
4970
4971        try {
4972            return fInformationMapping.toOriginLine(widgetLine);
4973        } catch (BadLocationException x) {
4974        }
4975
4976        return -1;
4977    }
4978
4979    /**
4980     * Implements the contract of {@link ITextViewerExtension5#widgetOffset2ModelOffset(int)}.
4981     *
4982     * @param widgetOffset the widget offset
4983     * @return the corresponding model offset or <code>-1</code>
4984     * @since 2.1
4985     */

4986    public int widgetOffset2ModelOffset(int widgetOffset) {
4987        if (fInformationMapping == null)
4988            return widgetOffset;
4989
4990        try {
4991            return fInformationMapping.toOriginOffset(widgetOffset);
4992        } catch (BadLocationException x) {
4993            if (widgetOffset == getVisibleDocument().getLength()) {
4994                IRegion coverage= fInformationMapping.getCoverage();
4995                return coverage.getOffset() + coverage.getLength();
4996            }
4997        }
4998
4999        return -1;
5000    }
5001
5002    /**
5003     * Implements the contract of {@link ITextViewerExtension5#widgetRange2ModelRange(IRegion)}.
5004     *
5005     * @param widgetRange the widget range
5006     * @return the corresponding model range or <code>null</code>
5007     * @since 2.1
5008     */

5009    public IRegion widgetRange2ModelRange(IRegion widgetRange) {
5010        if (fInformationMapping == null)
5011            return widgetRange;
5012
5013        try {
5014
5015            if (widgetRange.getLength() < 0) {
5016                Region reveresed= new Region(widgetRange.getOffset() + widgetRange.getLength(), -widgetRange.getLength());
5017                IRegion result= fInformationMapping.toOriginRegion(reveresed);
5018                return new Region(result.getOffset() + result.getLength(), -result.getLength());
5019            }
5020
5021            return fInformationMapping.toOriginRegion(widgetRange);
5022
5023        } catch (BadLocationException x) {
5024            int modelOffset= widgetOffset2ModelOffset(widgetRange.getOffset());
5025            if (modelOffset > -1) {
5026                int modelEndOffset= widgetOffset2ModelOffset(widgetRange.getOffset() + widgetRange.getLength());
5027                if (modelEndOffset > -1)
5028                    return new Region(modelOffset, modelEndOffset - modelOffset);
5029            }
5030        }
5031
5032        return null;
5033    }
5034
5035    /**
5036     * Implements the contract of {@link ITextViewerExtension5#getModelCoverage()}.
5037     *
5038     * @return the model coverage
5039     * @since 2.1
5040     */

5041    public IRegion getModelCoverage() {
5042        if (fInformationMapping == null) {
5043            IDocument document= getDocument();
5044            if (document == null)
5045                return null;
5046            return new Region(0, document.getLength());
5047        }
5048
5049        return fInformationMapping.getCoverage();
5050    }
5051
5052    /**
5053     * Returns the line of the widget whose corresponding line in the viewer's document
5054     * is closest to the given line in the viewer's document or <code>-1</code>.
5055     *
5056     * @param modelLine the line in the viewer's document
5057     * @return the line in the widget that corresponds best to the given line in the viewer's document or <code>-1</code>
5058     * @since 2.1
5059     */

5060    protected int getClosestWidgetLineForModelLine(int modelLine) {
5061        if (fInformationMapping == null)
5062            return modelLine;
5063
5064        try {
5065            return fInformationMapping.toClosestImageLine(modelLine);
5066        } catch (BadLocationException x) {
5067        }
5068
5069        return -1;
5070    }
5071
5072    /**
5073     * Translates a style range given relative to the viewer's document into style
5074     * ranges relative to the viewer's widget or <code>null</code>.
5075     *
5076     * @param range the style range in the coordinates of the viewer's document
5077     * @return the style range in the coordinates of the viewer's widget or <code>null</code>
5078     * @since 2.1
5079     */

5080    protected StyleRange modelStyleRange2WidgetStyleRange(StyleRange range) {
5081        IRegion region= modelRange2WidgetRange(new Region(range.start, range.length));
5082        if (region != null) {
5083            StyleRange result= (StyleRange) range.clone();
5084            result.start= region.getOffset();
5085            result.length= region.getLength();
5086            return result;
5087        }
5088        return null;
5089    }
5090
5091    /**
5092     * Same as {@link #modelRange2WidgetRange(IRegion)} just for a {@link org.eclipse.jface.text.Position}.
5093     *
5094     * @param modelPosition the position describing a range in the viewer's document
5095     * @return a region describing a range in the viewer's widget
5096     * @since 2.1
5097     */

5098    protected IRegion modelRange2WidgetRange(Position modelPosition) {
5099        return modelRange2WidgetRange(new Region(modelPosition.getOffset(), modelPosition.getLength()));
5100    }
5101
5102    /**
5103     * Translates the widget region of the given verify event into
5104     * the corresponding region of the viewer's document.
5105     *
5106     * @param event the verify event
5107     * @return the region of the viewer's document corresponding to the verify event
5108     * @since 2.1
5109     */

5110    protected IRegion event2ModelRange(VerifyEvent event) {
5111
5112        Region region= null;
5113        if (event.start <= event.end)
5114            region= new Region(event.start, event.end - event.start);
5115        else
5116            region= new Region(event.end, event.start - event.end);
5117
5118        return widgetRange2ModelRange(region);
5119    }
5120
5121    /**
5122     * Translates the given widget selection into the corresponding region
5123     * of the viewer's document or returns <code>null</code> if this fails.
5124     *
5125     * @param widgetSelection the widget selection
5126     * @return the region of the viewer's document corresponding to the widget selection or <code>null</code>
5127     * @since 2.1
5128     */

5129    protected Point widgetSelection2ModelSelection(Point widgetSelection) {
5130        IRegion region= new Region(widgetSelection.x, widgetSelection.y);
5131        region= widgetRange2ModelRange(region);
5132        return region == null ? null : new Point(region.getOffset(), region.getLength());
5133    }
5134
5135    /**
5136     * Translates the given selection range of the viewer's document into
5137     * the corresponding widget range or returns <code>null</code> of this fails.
5138     *
5139     * @param modelSelection the selection range of the viewer's document
5140     * @return the widget range corresponding to the selection range or <code>null</code>
5141     * @since 2.1
5142     */

5143    protected Point modelSelection2WidgetSelection(Point modelSelection) {
5144        if (fInformationMapping == null)
5145            return modelSelection;
5146
5147        try {
5148            IRegion region= new Region(modelSelection.x, modelSelection.y);
5149            region= fInformationMapping.toImageRegion(region);
5150            if (region != null)
5151                return new Point(region.getOffset(), region.getLength());
5152        } catch (BadLocationException x) {
5153        }
5154
5155        return null;
5156    }
5157
5158    /**
5159     * Implements the contract of {@link ITextViewerExtension5#widgetLineOfWidgetOffset(int)}.
5160     *
5161     * @param widgetOffset the widget offset
5162     * @return the corresponding widget line or <code>-1</code>
5163     * @since 2.1
5164     */

5165    public int widgetLineOfWidgetOffset(int widgetOffset) {
5166        IDocument document= getVisibleDocument();
5167        if (document != null) {
5168            try {
5169                return document.getLineOfOffset(widgetOffset);
5170            } catch (BadLocationException e) {
5171            }
5172        }
5173        return -1;
5174    }
5175
5176    /*
5177     * @see org.eclipse.jface.text.ITextViewerExtension4#moveFocusToWidgetToken()
5178     * @since 3.0
5179     */

5180    public boolean moveFocusToWidgetToken() {
5181        if (fWidgetTokenKeeper instanceof IWidgetTokenKeeperExtension) {
5182            IWidgetTokenKeeperExtension extension= (IWidgetTokenKeeperExtension) fWidgetTokenKeeper;
5183            return extension.setFocus(this);
5184        }
5185        return false;
5186    }
5187
5188    /**
5189     * Sets the document partitioning of this viewer. The partitioning is used by this viewer to
5190     * access partitioning information of the viewers input document.
5191     *
5192     * @param partitioning the partitioning name
5193     * @since 3.0
5194     */

5195    public void setDocumentPartitioning(String JavaDoc partitioning) {
5196        fPartitioning= partitioning;
5197    }
5198
5199    /**
5200     * Returns the document partitioning for this viewer.
5201     *
5202     * @return the document partitioning for this viewer
5203     * @since 3.0
5204     */

5205    protected String JavaDoc getDocumentPartitioning() {
5206        return fPartitioning;
5207    }
5208
5209    //---- Text presentation listeners ----
5210

5211    /*
5212     * @see ITextViewerExtension4#addTextPresentationListener(ITextPresentationListener)
5213     * @since 3.0
5214     */

5215    public void addTextPresentationListener(ITextPresentationListener listener) {
5216
5217        Assert.isNotNull(listener);
5218
5219        if (fTextPresentationListeners == null)
5220            fTextPresentationListeners= new ArrayList JavaDoc();
5221
5222        if (!fTextPresentationListeners.contains(listener))
5223            fTextPresentationListeners.add(listener);
5224    }
5225
5226    /*
5227     * @see ITextViewerExtension4#removeTextPresentationListener(ITextPresentationListener)
5228     * @since 3.0
5229     */

5230    public void removeTextPresentationListener(ITextPresentationListener listener) {
5231
5232        Assert.isNotNull(listener);
5233
5234        if (fTextPresentationListeners != null) {
5235            fTextPresentationListeners.remove(listener);
5236            if (fTextPresentationListeners.size() == 0)
5237                fTextPresentationListeners= null;
5238        }
5239    }
5240
5241    /*
5242     * @see org.eclipse.jface.text.IEditingSupportRegistry#registerHelper(org.eclipse.jface.text.IEditingSupport)
5243     * @since 3.1
5244     */

5245    public void register(IEditingSupport helper) {
5246        Assert.isLegal(helper != null);
5247        fEditorHelpers.add(helper);
5248    }
5249
5250    /*
5251     * @see org.eclipse.jface.text.IEditingSupportRegistry#deregisterHelper(org.eclipse.jface.text.IEditingSupport)
5252     * @since 3.1
5253     */

5254    public void unregister(IEditingSupport helper) {
5255        fEditorHelpers.remove(helper);
5256    }
5257
5258    /*
5259     * @see org.eclipse.jface.text.IEditingSupportRegistry#getCurrentHelpers()
5260     * @since 3.1
5261     */

5262    public IEditingSupport[] getRegisteredSupports() {
5263        return (IEditingSupport[]) fEditorHelpers.toArray(new IEditingSupport[fEditorHelpers.size()]);
5264    }
5265
5266    /*
5267     * @see org.eclipse.jface.text.ITextViewerExtension6#setHyperlinkDetectors(org.eclipse.jface.text.hyperlink.IHyperlinkDetector[], int)
5268     * @since 3.1
5269     */

5270    public void setHyperlinkDetectors(IHyperlinkDetector[] hyperlinkDetectors, int eventStateMask) {
5271        if (fHyperlinkDetectors != null) {
5272            for (int i= 0; i < fHyperlinkDetectors.length; i++) {
5273                if (fHyperlinkDetectors[i] instanceof IHyperlinkDetectorExtension)
5274                    ((IHyperlinkDetectorExtension)fHyperlinkDetectors[i]).dispose();
5275            }
5276        }
5277        
5278        boolean enable= hyperlinkDetectors != null && hyperlinkDetectors.length > 0;
5279        fHyperlinkStateMask= eventStateMask;
5280        fHyperlinkDetectors= hyperlinkDetectors;
5281        if (enable) {
5282            if (fHyperlinkManager != null) {
5283                fHyperlinkManager.setHyperlinkDetectors(fHyperlinkDetectors);
5284                fHyperlinkManager.setHyperlinkStateMask(fHyperlinkStateMask);
5285            }
5286            ensureHyperlinkManagerInstalled();
5287        } else {
5288            if (fHyperlinkManager != null)
5289                fHyperlinkManager.uninstall();
5290            fHyperlinkManager= null;
5291        }
5292    }
5293
5294    /**
5295     * Sets the hyperlink presenter.
5296     * <p>
5297     * This is only valid as long as the hyperlink manager hasn't
5298     * been created yet.
5299     * </p>
5300     *
5301     * @param hyperlinkPresenter the hyperlink presenter
5302     * @throws IllegalStateException if the hyperlink manager has already been created
5303     * @since 3.1
5304     */

5305    public void setHyperlinkPresenter(IHyperlinkPresenter hyperlinkPresenter) throws IllegalStateException JavaDoc {
5306        if (fHyperlinkManager != null)
5307            throw new IllegalStateException JavaDoc();
5308
5309        fHyperlinkPresenter= hyperlinkPresenter;
5310        ensureHyperlinkManagerInstalled();
5311    }
5312
5313    /**
5314     * Ensures that the hyperlink manager has been
5315     * installed if a hyperlink detector is available.
5316     *
5317     * @since 3.1
5318     */

5319    private void ensureHyperlinkManagerInstalled() {
5320        if (fHyperlinkDetectors != null && fHyperlinkDetectors.length > 0 && fHyperlinkPresenter != null && fHyperlinkManager == null) {
5321            fHyperlinkManager= new HyperlinkManager(HyperlinkManager.FIRST);
5322            fHyperlinkManager.install(this, fHyperlinkPresenter, fHyperlinkDetectors, fHyperlinkStateMask);
5323        }
5324    }
5325
5326    /*
5327     * @see org.eclipse.jface.text.ITextViewerExtension7#setTabsToSpacesConverter(org.eclipse.jface.text.IAutoEditStrategy)
5328     * @since 3.3
5329     */

5330    public void setTabsToSpacesConverter(IAutoEditStrategy converter) {
5331        fTabsToSpacesConverter= converter;
5332    }
5333
5334}
5335
Popular Tags