KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > ui > console > TextConsoleViewer


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
12 package org.eclipse.ui.console;
13
14 import java.util.ArrayList JavaDoc;
15 import java.util.List JavaDoc;
16
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.core.runtime.Status;
20 import org.eclipse.jface.preference.JFacePreferences;
21 import org.eclipse.jface.resource.ColorRegistry;
22 import org.eclipse.jface.resource.JFaceColors;
23 import org.eclipse.jface.resource.JFaceResources;
24 import org.eclipse.jface.text.BadPositionCategoryException;
25 import org.eclipse.jface.text.DocumentEvent;
26 import org.eclipse.jface.text.IDocument;
27 import org.eclipse.jface.text.IDocumentAdapter;
28 import org.eclipse.jface.text.IDocumentListener;
29 import org.eclipse.jface.text.IPositionUpdater;
30 import org.eclipse.jface.text.IRegion;
31 import org.eclipse.jface.text.Position;
32 import org.eclipse.jface.text.source.SourceViewer;
33 import org.eclipse.jface.util.IPropertyChangeListener;
34 import org.eclipse.jface.util.PropertyChangeEvent;
35 import org.eclipse.swt.SWT;
36 import org.eclipse.swt.custom.LineBackgroundEvent;
37 import org.eclipse.swt.custom.LineBackgroundListener;
38 import org.eclipse.swt.custom.LineStyleEvent;
39 import org.eclipse.swt.custom.LineStyleListener;
40 import org.eclipse.swt.custom.StyleRange;
41 import org.eclipse.swt.custom.StyledText;
42 import org.eclipse.swt.events.MouseEvent;
43 import org.eclipse.swt.events.MouseListener;
44 import org.eclipse.swt.events.MouseMoveListener;
45 import org.eclipse.swt.events.MouseTrackListener;
46 import org.eclipse.swt.graphics.Color;
47 import org.eclipse.swt.graphics.Cursor;
48 import org.eclipse.swt.graphics.Font;
49 import org.eclipse.swt.graphics.Point;
50 import org.eclipse.swt.widgets.Composite;
51 import org.eclipse.swt.widgets.Control;
52 import org.eclipse.swt.widgets.Display;
53 import org.eclipse.swt.widgets.Event;
54 import org.eclipse.swt.widgets.Listener;
55 import org.eclipse.ui.internal.console.ConsoleDocumentAdapter;
56 import org.eclipse.ui.internal.console.ConsoleHyperlinkPosition;
57 import org.eclipse.ui.progress.WorkbenchJob;
58
59 /**
60  * Default viewer used to display a <code>TextConsole</code>.
61  * <p>
62  * Clients may subclass this class.
63  * </p>
64  *
65  * @since 3.1
66  */

67 public class TextConsoleViewer extends SourceViewer implements LineStyleListener, LineBackgroundListener, MouseTrackListener, MouseMoveListener, MouseListener {
68     /**
69      * Adapts document to the text widget.
70      */

71     private ConsoleDocumentAdapter documentAdapter;
72
73     private IHyperlink hyperlink;
74
75     private Cursor handCursor;
76
77     private Cursor textCursor;
78
79     private int consoleWidth = -1;
80
81     private TextConsole console;
82
83     private IPropertyChangeListener propertyChangeListener;
84
85     private IDocumentListener documentListener = new IDocumentListener() {
86         public void documentAboutToBeChanged(DocumentEvent event) {
87         }
88
89         public void documentChanged(DocumentEvent event) {
90             updateLinks(event.fOffset);
91         }
92     };
93     // event listener used to send event to hyperlink for IHyperlink2
94
private Listener mouseUpListener = new Listener() {
95         public void handleEvent(Event event) {
96             if (hyperlink != null) {
97                 String JavaDoc selection = getTextWidget().getSelectionText();
98                 if (selection.length() <= 0) {
99                     if (event.button == 1) {
100                         if (hyperlink instanceof IHyperlink2) {
101                             ((IHyperlink2) hyperlink).linkActivated(event);
102                         } else {
103                             hyperlink.linkActivated();
104                         }
105                     }
106                 }
107             }
108         }
109     };
110
111     WorkbenchJob revealJob = new WorkbenchJob("Reveal End of Document") {//$NON-NLS-1$
112
public IStatus runInUIThread(IProgressMonitor monitor) {
113             StyledText textWidget = getTextWidget();
114             if (textWidget != null && !textWidget.isDisposed()) {
115                 int lineCount = textWidget.getLineCount();
116                 textWidget.setTopIndex(lineCount - 1);
117             }
118             return Status.OK_STATUS;
119         }
120     };
121     
122     private IPositionUpdater positionUpdater = new IPositionUpdater() {
123         public void update(DocumentEvent event) {
124             try {
125                 IDocument document = getDocument();
126                 if (document != null) {
127                     Position[] positions = document.getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY);
128                     for (int i = 0; i < positions.length; i++) {
129                         Position position = positions[i];
130                         if (position.offset == event.fOffset && position.length<=event.fLength) {
131                             position.delete();
132                         }
133                         if (position.isDeleted) {
134                             document.removePosition(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY, position);
135                         }
136                     }
137                 }
138             } catch (BadPositionCategoryException e) {
139             }
140         }
141     };
142
143     /**
144      * Constructs a new viewer in the given parent for the specified console.
145      *
146      * @param parent
147      * containing widget
148      * @param console
149      * text console
150      */

151     public TextConsoleViewer(Composite parent, TextConsole console) {
152         super(parent, null, SWT.V_SCROLL | SWT.H_SCROLL);
153         this.console = console;
154
155         IDocument document = console.getDocument();
156         setDocument(document);
157
158         StyledText styledText = getTextWidget();
159         styledText.setDoubleClickEnabled(true);
160         styledText.addLineStyleListener(this);
161         styledText.addLineBackgroundListener(this);
162         styledText.setEditable(true);
163         setFont(console.getFont());
164         styledText.addMouseTrackListener(this);
165         styledText.addListener(SWT.MouseUp, mouseUpListener);
166
167         ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
168         propertyChangeListener = new HyperlinkColorChangeListener();
169         colorRegistry.addListener(propertyChangeListener);
170
171         revealJob.setSystem(true);
172         document.addDocumentListener(documentListener);
173         document.addPositionUpdater(positionUpdater);
174     }
175
176     /**
177      * Sets the tab width used by this viewer.
178      *
179      * @param tabWidth
180      * the tab width used by this viewer
181      */

182     public void setTabWidth(int tabWidth) {
183         StyledText styledText = getTextWidget();
184         int oldWidth = styledText.getTabs();
185         if (tabWidth != oldWidth) {
186             styledText.setTabs(tabWidth);
187         }
188     }
189
190     /**
191      * Sets the font used by this viewer.
192      *
193      * @param font
194      * the font used by this viewer
195      */

196     public void setFont(Font font) {
197         StyledText styledText = getTextWidget();
198         Font oldFont = styledText.getFont();
199         if (oldFont == font) {
200             return;
201         }
202         if (font == null || !(font.equals(oldFont))) {
203             styledText.setFont(font);
204         }
205     }
206
207     /**
208      * Positions the cursor at the end of the document.
209      */

210     protected void revealEndOfDocument() {
211         revealJob.schedule(50);
212     }
213
214     /*
215      * (non-Javadoc)
216      *
217      * @see org.eclipse.swt.custom.LineStyleListener#lineGetStyle(org.eclipse.swt.custom.LineStyleEvent)
218      */

219     public void lineGetStyle(LineStyleEvent event) {
220         IDocument document = getDocument();
221         if (document != null && document.getLength() > 0) {
222             ArrayList JavaDoc ranges = new ArrayList JavaDoc();
223             int offset = event.lineOffset;
224             int length = event.lineText.length();
225
226             StyleRange[] partitionerStyles = ((IConsoleDocumentPartitioner) document.getDocumentPartitioner()).getStyleRanges(event.lineOffset, event.lineText.length());
227             if (partitionerStyles != null) {
228                 for (int i = 0; i < partitionerStyles.length; i++) {
229                     ranges.add(partitionerStyles[i]);
230                 }
231             } else {
232                 ranges.add(new StyleRange(offset, length, null, null));
233             }
234
235             try {
236                 Position[] positions = getDocument().getPositions(ConsoleHyperlinkPosition.HYPER_LINK_CATEGORY);
237                 Position[] overlap = findPosition(offset, length, positions);
238                 Color color = JFaceColors.getHyperlinkText(Display.getCurrent());
239                 if (overlap != null) {
240                     for (int i = 0; i < overlap.length; i++) {
241                         Position position = overlap[i];
242                         StyleRange linkRange = new StyleRange(position.offset, position.length, color, null);
243                         linkRange.underline = true;
244                         override(ranges, linkRange);
245                     }
246                 }
247             } catch (BadPositionCategoryException e) {
248             }
249
250             if (ranges.size() > 0) {
251                 event.styles = (StyleRange[]) ranges.toArray(new StyleRange[ranges.size()]);
252             }
253         }
254     }
255
256     private void override(List JavaDoc ranges, StyleRange newRange) {
257         if (ranges.isEmpty()) {
258             ranges.add(newRange);
259             return;
260         }
261
262         int start = newRange.start;
263         int end = start + newRange.length;
264         for (int i = 0; i < ranges.size(); i++) {
265             StyleRange existingRange = (StyleRange) ranges.get(i);
266             int rEnd = existingRange.start + existingRange.length;
267             if (end <= existingRange.start || start >= rEnd) {
268                 continue;
269             }
270
271             if (start < existingRange.start && end > existingRange.start) {
272                 start = existingRange.start;
273             }
274
275             if (start >= existingRange.start && end <= rEnd) {
276                 existingRange.length = start - existingRange.start;
277                 ranges.add(++i, newRange);
278                 if (end != rEnd) {
279                     ranges.add(++i, new StyleRange(end, rEnd - end - 1, existingRange.foreground, existingRange.background));
280                 }
281                 return;
282             } else if (start >= existingRange.start && start < rEnd) {
283                 existingRange.length = start - existingRange.start;
284                 ranges.add(++i, newRange);
285             } else if (end >= rEnd) {
286                 ranges.remove(i);
287             } else {
288                 ranges.add(++i, new StyleRange(end + 1, rEnd - end + 1, existingRange.foreground, existingRange.background));
289             }
290         }
291     }
292
293     /**
294      * Binary search for the positions overlapping the given range
295      *
296      * @param offset
297      * the offset of the range
298      * @param length
299      * the length of the range
300      * @param positions
301      * the positions to search
302      * @return the positions overlapping the given range, or <code>null</code>
303      */

304     private Position[] findPosition(int offset, int length, Position[] positions) {
305
306         if (positions.length == 0)
307             return null;
308
309         int rangeEnd = offset + length;
310         int left = 0;
311         int right = positions.length - 1;
312         int mid = 0;
313         Position position = null;
314
315         while (left < right) {
316
317             mid = (left + right) / 2;
318
319             position = positions[mid];
320             if (rangeEnd < position.getOffset()) {
321                 if (left == mid)
322                     right = left;
323                 else
324                     right = mid - 1;
325             } else if (offset > (position.getOffset() + position.getLength() - 1)) {
326                 if (right == mid)
327                     left = right;
328                 else
329                     left = mid + 1;
330             } else {
331                 left = right = mid;
332             }
333         }
334
335         List JavaDoc list = new ArrayList JavaDoc();
336         int index = left - 1;
337         if (index >= 0) {
338             position = positions[index];
339             while (index >= 0 && (position.getOffset() + position.getLength()) > offset) {
340                 index--;
341                 if (index > 0) {
342                     position = positions[index];
343                 }
344             }
345         }
346         index++;
347         position = positions[index];
348         while (index < positions.length && (position.getOffset() < rangeEnd)) {
349             list.add(position);
350             index++;
351             if (index < positions.length) {
352                 position = positions[index];
353             }
354         }
355
356         if (list.isEmpty()) {
357             return null;
358         }
359         return (Position[]) list.toArray(new Position[list.size()]);
360     }
361
362     /*
363      * (non-Javadoc)
364      *
365      * @see org.eclipse.swt.custom.LineBackgroundListener#lineGetBackground(org.eclipse.swt.custom.LineBackgroundEvent)
366      */

367     public void lineGetBackground(LineBackgroundEvent event) {
368         event.lineBackground = null;
369     }
370
371     /**
372      * Returns the hand cursor.
373      *
374      * @return the hand cursor
375      */

376     protected Cursor getHandCursor() {
377         if (handCursor == null) {
378             handCursor = new Cursor(ConsolePlugin.getStandardDisplay(), SWT.CURSOR_HAND);
379         }
380         return handCursor;
381     }
382
383     /**
384      * Returns the text cursor.
385      *
386      * @return the text cursor
387      */

388     protected Cursor getTextCursor() {
389         if (textCursor == null) {
390             textCursor = new Cursor(ConsolePlugin.getStandardDisplay(), SWT.CURSOR_IBEAM);
391         }
392         return textCursor;
393     }
394
395     /**
396      * Notification a hyperlink has been entered.
397      *
398      * @param link
399      * the link that was entered
400      */

401     protected void linkEntered(IHyperlink link) {
402         Control control = getTextWidget();
403         if (hyperlink != null) {
404             linkExited(hyperlink);
405         }
406         hyperlink = link;
407         hyperlink.linkEntered();
408         control.setCursor(getHandCursor());
409         control.redraw();
410         control.addMouseListener(this);
411     }
412
413     /**
414      * Notification a link was exited.
415      *
416      * @param link
417      * the link that was exited
418      */

419     protected void linkExited(IHyperlink link) {
420         link.linkExited();
421         hyperlink = null;
422         Control control = getTextWidget();
423         control.setCursor(getTextCursor());
424         control.redraw();
425         control.removeMouseListener(this);
426     }
427
428     /*
429      * (non-Javadoc)
430      *
431      * @see org.eclipse.swt.events.MouseTrackListener#mouseEnter(org.eclipse.swt.events.MouseEvent)
432      */

433     public void mouseEnter(MouseEvent e) {
434         getTextWidget().addMouseMoveListener(this);
435     }
436
437     /*
438      * (non-Javadoc)
439      *
440      * @see org.eclipse.swt.events.MouseTrackListener#mouseExit(org.eclipse.swt.events.MouseEvent)
441      */

442     public void mouseExit(MouseEvent e) {
443         getTextWidget().removeMouseMoveListener(this);
444         if (hyperlink != null) {
445             linkExited(hyperlink);
446         }
447     }
448
449     /*
450      * (non-Javadoc)
451      *
452      * @see org.eclipse.swt.events.MouseTrackListener#mouseHover(org.eclipse.swt.events.MouseEvent)
453      */

454     public void mouseHover(MouseEvent e) {
455     }
456
457     /*
458      * (non-Javadoc)
459      *
460      * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
461      */

462     public void mouseMove(MouseEvent e) {
463         int offset = -1;
464         try {
465             Point p = new Point(e.x, e.y);
466             offset = getTextWidget().getOffsetAtLocation(p);
467         } catch (IllegalArgumentException JavaDoc ex) {
468             // out of the document range
469
}
470         updateLinks(offset);
471     }
472
473     /**
474      * The cursor has just be moved to the given offset, the mouse has hovered
475      * over the given offset. Update link rendering.
476      *
477      * @param offset
478      */

479     protected void updateLinks(int offset) {
480         if (offset >= 0) {
481             IHyperlink link = getHyperlink(offset);
482             if (link != null) {
483                 if (link.equals(hyperlink)) {
484                     return;
485                 }
486                 linkEntered(link);
487                 return;
488             }
489         }
490         if (hyperlink != null) {
491             linkExited(hyperlink);
492         }
493     }
494
495     /**
496      * Returns the currently active hyperlink or <code>null</code> if none.
497      *
498      * @return the currently active hyperlink or <code>null</code> if none
499      */

500     public IHyperlink getHyperlink() {
501         return hyperlink;
502     }
503
504     /**
505      * Returns the hyperlink at the specified offset, or <code>null</code> if
506      * none.
507      *
508      * @param offset
509      * offset at which a hyperlink has been requested
510      * @return hyperlink at the specified offset, or <code>null</code> if none
511      */

512     public IHyperlink getHyperlink(int offset) {
513         if (offset >= 0 && console != null) {
514             return console.getHyperlink(offset);
515         }
516         return null;
517     }
518
519     /*
520      * (non-Javadoc)
521      *
522      * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
523      */

524     public void mouseDoubleClick(MouseEvent e) {
525     }
526
527     /*
528      * (non-Javadoc)
529      *
530      * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
531      */

532     public void mouseDown(MouseEvent e) {
533     }
534
535     /*
536      * (non-Javadoc)
537      *
538      * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
539      */

540     public void mouseUp(MouseEvent e) {
541     }
542
543     /*
544      * (non-Javadoc)
545      *
546      * @see org.eclipse.jface.text.TextViewer#createDocumentAdapter()
547      */

548     protected IDocumentAdapter createDocumentAdapter() {
549         if (documentAdapter == null) {
550             documentAdapter = new ConsoleDocumentAdapter(consoleWidth = -1);
551         }
552         return documentAdapter;
553     }
554
555     /**
556      * Sets the console to have a fixed character width. Use -1 to indicate that
557      * a fixed width should not be used.
558      *
559      * @param width
560      * fixed character width of the console, or -1
561      */

562     public void setConsoleWidth(int width) {
563         if (consoleWidth != width) {
564             consoleWidth = width;
565             ConsolePlugin.getStandardDisplay().asyncExec(new Runnable JavaDoc() {
566                 public void run() {
567                     if (documentAdapter != null) {
568                         documentAdapter.setWidth(consoleWidth);
569                     }
570                 }
571             });
572         }
573     }
574
575     /*
576      * (non-Javadoc)
577      *
578      * @see org.eclipse.jface.text.TextViewer#handleDispose()
579      */

580     protected void handleDispose() {
581         IDocument document = getDocument();
582         if (document != null) {
583             document.removeDocumentListener(documentListener);
584             document.removePositionUpdater(positionUpdater);
585         }
586
587         StyledText styledText = getTextWidget();
588         styledText.removeLineStyleListener(this);
589         styledText.removeLineBackgroundListener(this);
590         styledText.removeMouseTrackListener(this);
591
592         handCursor = null;
593         textCursor = null;
594         hyperlink = null;
595         console = null;
596
597         ColorRegistry colorRegistry = JFaceResources.getColorRegistry();
598         colorRegistry.removeListener(propertyChangeListener);
599         
600         super.handleDispose();
601     }
602
603     class HyperlinkColorChangeListener implements IPropertyChangeListener {
604         public void propertyChange(PropertyChangeEvent event) {
605             if (event.getProperty().equals(JFacePreferences.ACTIVE_HYPERLINK_COLOR) || event.getProperty().equals(JFacePreferences.HYPERLINK_COLOR)) {
606                 getTextWidget().redraw();
607             }
608         }
609
610     }
611
612     /*
613      * work around to memory leak in TextViewer$WidgetCommand
614      */

615     protected void updateTextListeners(WidgetCommand cmd) {
616         super.updateTextListeners(cmd);
617         cmd.preservedText = null;
618         cmd.event = null;
619         cmd.text = null;
620     }
621
622     protected void internalRevealRange(int start, int end) {
623         StyledText textWidget = getTextWidget();
624         int startLine = documentAdapter.getLineAtOffset(start);
625         int endLine = documentAdapter.getLineAtOffset(end);
626
627         int top = textWidget.getTopIndex();
628         if (top > -1) {
629             // scroll vertically
630
int lines = getVisibleLinesInViewport();
631             int bottom = top + lines;
632
633             // two lines at the top and the bottom should always be left
634
// if window is smaller than 5 lines, always center position is
635
// chosen
636
int bufferZone = 2;
637             if (startLine >= top + bufferZone && startLine <= bottom - bufferZone && endLine >= top + bufferZone && endLine <= bottom - bufferZone) {
638
639                 // do not scroll at all as it is already visible
640
} else {
641                 int delta = Math.max(0, lines - (endLine - startLine));
642                 textWidget.setTopIndex(startLine - delta / 3);
643                 updateViewportListeners(INTERNAL);
644             }
645
646             // scroll horizontally
647
if (endLine < startLine) {
648                 endLine += startLine;
649                 startLine = endLine - startLine;
650                 endLine -= startLine;
651             }
652
653             int startPixel = -1;
654             int endPixel = -1;
655
656             if (endLine > startLine) {
657                 // reveal the beginning of the range in the start line
658
IRegion extent = getExtent(start, start);
659                 startPixel = extent.getOffset() + textWidget.getHorizontalPixel();
660                 endPixel = startPixel;
661             } else {
662                 IRegion extent = getExtent(start, end);
663                 startPixel = extent.getOffset() + textWidget.getHorizontalPixel();
664                 endPixel = startPixel + extent.getLength();
665             }
666
667             int visibleStart = textWidget.getHorizontalPixel();
668             int visibleEnd = visibleStart + textWidget.getClientArea().width;
669
670             // scroll only if not yet visible
671
if (startPixel < visibleStart || visibleEnd < endPixel) {
672                 // set buffer zone to 10 pixels
673
bufferZone = 10;
674                 int newOffset = visibleStart;
675                 int visibleWidth = visibleEnd - visibleStart;
676                 int selectionPixelWidth = endPixel - startPixel;
677
678                 if (startPixel < visibleStart)
679                     newOffset = startPixel;
680                 else if (selectionPixelWidth + bufferZone < visibleWidth)
681                     newOffset = endPixel + bufferZone - visibleWidth;
682                 else
683                     newOffset = startPixel;
684
685                 float index = ((float) newOffset) / ((float) getAverageCharWidth());
686
687                 textWidget.setHorizontalIndex(Math.round(index));
688             }
689
690         }
691     }
692
693 }
694
Popular Tags