KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jface > text > source > AbstractRulerColumn


1 /*******************************************************************************
2  * Copyright (c) 2006, 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.source;
12
13 import org.eclipse.swt.SWT;
14 import org.eclipse.swt.custom.StyledText;
15 import org.eclipse.swt.events.MouseEvent;
16 import org.eclipse.swt.events.MouseListener;
17 import org.eclipse.swt.events.MouseMoveListener;
18 import org.eclipse.swt.events.PaintEvent;
19 import org.eclipse.swt.events.PaintListener;
20 import org.eclipse.swt.graphics.Color;
21 import org.eclipse.swt.graphics.Font;
22 import org.eclipse.swt.graphics.GC;
23 import org.eclipse.swt.widgets.Canvas;
24 import org.eclipse.swt.widgets.Composite;
25 import org.eclipse.swt.widgets.Control;
26 import org.eclipse.swt.widgets.Display;
27
28 import org.eclipse.core.runtime.Assert;
29
30 import org.eclipse.jface.resource.JFaceResources;
31
32 import org.eclipse.jface.text.ITextListener;
33 import org.eclipse.jface.text.ITextViewer;
34 import org.eclipse.jface.text.IViewportListener;
35 import org.eclipse.jface.text.JFaceTextUtil;
36 import org.eclipse.jface.text.TextEvent;
37
38
39 /**
40  * Abstract implementation of a {@link IVerticalRulerColumn} that
41  * uses a {@link Canvas} to draw the ruler contents and which
42  * handles scrolling and mouse selection.
43  *
44  * <h3>Painting</h3>
45  * Subclasses can hook into the paint loop at three levels:
46  * <ul>
47  * <li>Override <strong>{@link #paint(GC, ILineRange)}</strong> to control the entire painting of
48  * the ruler.</li>
49  * <li>Override <strong>{@link #paintLine(GC, int, int, int, int)}</strong> to control the
50  * painting of a line.</li>
51  * <li>Leave the painting to the default implementation, but override <strong>{@link #computeBackground(int)}</strong>,
52  * <strong>{@link #computeForeground(int)}</strong> and <strong>{@link #computeText(int)}</strong>
53  * to specify the ruler appearance for a line.</li>
54  * </ul>
55  *
56  * <h3>Invalidation</h3>
57  * Subclasses may call {@link #redraw()} to mark the entire ruler as needing to be redrawn.
58  * Alternatively, use {@link #redraw(ILineRange)} to only invalidate a certain line range, for
59  * example due to changes to the display model.
60  *
61  * <h3>Configuration</h3>
62  * Subclasses can set the following properties. Setting them may trigger redrawing.
63  * <ul>
64  * <li>The {@link #setFont(Font) font} used to draw text in {@link #paintLine(GC, int, int, int, int)}.</li>
65  * <li>The horizontal {@link #setTextInset(int) text inset} for text drawn.</li>
66  * <li>The {@link #setDefaultBackground(Color) default background color} of the ruler.</li>
67  * <li>The {@link #setWidth(int) width} of the ruler.</li>
68  * </ul>
69  *
70  * @since 3.3
71  */

72 public abstract class AbstractRulerColumn implements IVerticalRulerColumn, IVerticalRulerInfo, IVerticalRulerInfoExtension {
73     private static final int DEFAULT_WIDTH= 12;
74     private static final int DEFAULT_TEXT_INSET= 2;
75
76     /**
77      * Handles all the mouse interaction in this line number ruler column.
78      */

79     private final class MouseHandler implements MouseListener, MouseMoveListener {
80
81         /*
82          * @see org.eclipse.swt.events.MouseListener#mouseUp(org.eclipse.swt.events.MouseEvent)
83          */

84         public void mouseUp(MouseEvent event) {
85         }
86
87         /*
88          * @see org.eclipse.swt.events.MouseListener#mouseDown(org.eclipse.swt.events.MouseEvent)
89          */

90         public void mouseDown(MouseEvent event) {
91             fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
92         }
93
94         /*
95          * @see org.eclipse.swt.events.MouseListener#mouseDoubleClick(org.eclipse.swt.events.MouseEvent)
96          */

97         public void mouseDoubleClick(MouseEvent event) {
98             fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
99         }
100
101         /*
102          * @see org.eclipse.swt.events.MouseMoveListener#mouseMove(org.eclipse.swt.events.MouseEvent)
103          */

104         public void mouseMove(MouseEvent event) {
105             fParentRuler.setLocationOfLastMouseButtonActivity(event.x, event.y);
106         }
107     }
108
109     /**
110      * Internal listener class that updates the ruler upon scrolling and text modifications.
111      */

112     private final class InternalListener implements IViewportListener, ITextListener {
113
114         /*
115          * @see IViewportListener#viewportChanged(int)
116          */

117         public void viewportChanged(int topPixel) {
118             int delta= topPixel - fLastTopPixel;
119             if (scrollVertical(delta))
120                 fCanvas.update(); // force update the invalidated regions
121
}
122
123         /*
124          * @see ITextListener#textChanged(TextEvent)
125          */

126         public void textChanged(TextEvent event) {
127             /*
128              * Redraw: - when the viewer is drawing, and any of the following - the widget was not
129              * full before the change - the widget is not full after the change - the document event
130              * was a visual modification (no document event attached) - for example when the
131              * projection changes.
132              */

133             if (!event.getViewerRedrawState())
134                 return;
135
136             if (fWasShowingEntireContents || event.getDocumentEvent() == null || JFaceTextUtil.isShowingEntireContents(fStyledText))
137                 redraw();
138         }
139     }
140
141     /* Listeners */
142
143     /** The viewport listener. */
144     private final InternalListener fInternalListener= new InternalListener();
145     /** The mouse handler. */
146     private final MouseHandler fMouseHandler= new MouseHandler();
147
148     /*
149      * Implementation and context of this ruler - created and set in createControl(), disposed of in
150      * columnRemoved().
151      */

152
153     /** The parent ruler, possibly <code>null</code>. */
154     private CompositeRuler fParentRuler;
155     /** The canvas, the only widget used to draw this ruler, possibly <code>null</code>. */
156     private Canvas fCanvas;
157     /** The text viewer, possibly <code>null</code>. */
158     private ITextViewer fTextViewer;
159     /** The text viewer's widget, possibly <code>null</code>. */
160     private StyledText fStyledText;
161
162     /* State when the canvas was last painted. */
163
164     /** The text widget's top pixel when the ruler was last painted. */
165     private int fLastTopPixel= -1;
166     /** Whether the text widget was showing the entire contents when the ruler was last painted. */
167     private boolean fWasShowingEntireContents= false;
168
169     /* Configuration */
170
171     /** The width of this ruler. */
172     private int fWidth= DEFAULT_WIDTH;
173     /** The text inset. */
174     private int fTextInset= DEFAULT_TEXT_INSET;
175     /** The default background color, <code>null</code> to use the text viewer's background color. */
176     private Color fBackground;
177     /** The font, <code>null</code> to use the default font. */
178     private Font fFont;
179     /** The annotation model, possibly <code>null</code>. */
180     private IAnnotationModel fModel;
181     /** The annotation hover, possibly <code>null</code>. */
182     private IAnnotationHover fHover;
183
184     /**
185      * Creates a new ruler.
186      */

187     protected AbstractRulerColumn() {
188     }
189
190     /*
191      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#createControl(org.eclipse.jface.text.source.CompositeRuler,
192      * org.eclipse.swt.widgets.Composite)
193      */

194     public Control createControl(CompositeRuler parentRuler, Composite parentControl) {
195         Assert.isLegal(parentControl != null);
196         Assert.isLegal(parentRuler != null);
197         Assert.isLegal(fParentRuler == null); // only call when not yet initialized!
198

199         fParentRuler= parentRuler;
200
201         fTextViewer= getParentRuler().getTextViewer();
202         fTextViewer.addViewportListener(fInternalListener);
203         fTextViewer.addTextListener(fInternalListener);
204
205         fStyledText= fTextViewer.getTextWidget();
206
207         fCanvas= new Canvas(parentControl, getCanvasStyle());
208
209         fCanvas.setBackground(getDefaultBackground());
210         fCanvas.setFont(getFont());
211
212         fCanvas.addPaintListener(new PaintListener() {
213             public void paintControl(PaintEvent event) {
214                 AbstractRulerColumn.this.paintControl(event);
215             }
216         });
217
218         fCanvas.addMouseListener(fMouseHandler);
219         fCanvas.addMouseMoveListener(fMouseHandler);
220
221         return fCanvas;
222     }
223
224     /**
225      * Returns the SWT style bits used when creating the ruler canvas.
226      * <p>
227      * The default implementation returns <code>SWT.NO_BACKGROUND</code>.</p>
228      * <p>
229      * Clients may reimplement this method to create a canvas with their
230      * desired style bits.</p>
231      *
232      * @return the SWT style bits, or <code>SWT.NONE</code> if none
233      */

234     protected int getCanvasStyle() {
235         return SWT.NO_BACKGROUND;
236     }
237
238     /*
239      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#getControl()
240      */

241     public final Control getControl() {
242         return fCanvas;
243     }
244
245     /**
246      * The new width in pixels. The <code>DEFAULT_WIDTH</code> constant
247      * specifies the default width.
248      *
249      * @param width the new width
250      */

251     protected final void setWidth(int width) {
252         Assert.isLegal(width >= 0);
253         if (fWidth != width) {
254             fWidth= width;
255             CompositeRuler composite= getParentRuler();
256             if (composite != null)
257                 composite.relayout();
258         }
259     }
260
261     /*
262      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#getWidth()
263      */

264     public final int getWidth() {
265         return fWidth;
266     }
267
268     /**
269      * Returns the parent ruler, <code>null</code> before
270      * {@link #createControl(CompositeRuler, Composite)} has been called.
271      *
272      * @return the parent ruler or <code>null</code>
273      */

274     protected final CompositeRuler getParentRuler() {
275         return fParentRuler;
276     }
277
278     /**
279      * {@inheritDoc}
280      *
281      * @param font the font or <code>null</code> to use the default font
282      */

283     public final void setFont(Font font) {
284         if (fFont != font) {
285             fFont= font;
286             redraw();
287         }
288     }
289
290     /**
291      * Returns the current font. If a font has not been explicitly set, the widget's font is
292      * returned.
293      *
294      * @return the font used to render text on the ruler.
295      */

296     protected final Font getFont() {
297         if (fFont != null)
298             return fFont;
299         if (fStyledText != null && !fStyledText.isDisposed())
300             return fStyledText.getFont();
301         return JFaceResources.getTextFont();
302     }
303
304     /**
305      * Sets the text inset (padding) used to draw text in {@link #paintLine(GC, int, int, int, int)}.
306      *
307      * @param textInset the new text inset
308      */

309     protected final void setTextInset(int textInset) {
310         if (textInset != fTextInset) {
311             fTextInset= textInset;
312             redraw();
313         }
314     }
315
316     /**
317      * Returns the text inset for text drawn by {@link #paintLine(GC, int, int, int, int)}. The
318      * <code>DEFAULT_TEXT_INSET</code> constant specifies the default inset in pixels.
319      *
320      * @return the text inset for text
321      */

322     protected final int getTextInset() {
323         return fTextInset;
324     }
325
326     /*
327      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#setModel(org.eclipse.jface.text.source.IAnnotationModel)
328      */

329     public void setModel(IAnnotationModel model) {
330         if (fModel != model) {
331             fModel= model;
332             redraw();
333         }
334     }
335
336     /*
337      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getModel()
338      */

339     public final IAnnotationModel getModel() {
340         return fModel;
341     }
342
343     /**
344      * Sets the default background color for this column. The default background is used as default
345      * implementation of {@link #computeBackground(int)} and also to paint the area of the ruler
346      * that does not correspond to any lines (when the viewport is not entirely filled with lines).
347      *
348      * @param background the default background color, <code>null</code> to use the text widget's
349      * background
350      */

351     protected final void setDefaultBackground(Color background) {
352         if (fBackground != background) {
353             fBackground= background;
354             if (fCanvas != null && !fCanvas.isDisposed())
355                 fCanvas.setBackground(getDefaultBackground());
356             redraw();
357         }
358     }
359
360     /**
361      * Returns the background color. May return <code>null</code> if the system is shutting down.
362      *
363      * @return the background color
364      */

365     protected final Color getDefaultBackground() {
366         if (fBackground != null)
367             return fBackground;
368         if (fStyledText != null && !fStyledText.isDisposed())
369             return fStyledText.getBackground();
370         Display display;
371         if (fCanvas != null && !fCanvas.isDisposed())
372             display= fCanvas.getDisplay();
373         else
374             display= Display.getCurrent();
375         if (display != null)
376             return display.getSystemColor(SWT.COLOR_LIST_BACKGROUND);
377         return null;
378     }
379
380     /**
381      * Sets the annotation hover.
382      *
383      * @param hover the annotation hover, <code>null</code> for no hover
384      */

385     protected final void setHover(IAnnotationHover hover) {
386         if (fHover != hover)
387             fHover= hover;
388     }
389
390     /*
391      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#getHover()
392      */

393     public IAnnotationHover getHover() {
394         return fHover;
395     }
396
397     /**
398      * Disposes this ruler column.
399      * <p>
400      * Subclasses may extend this method.</p>
401      * <p>
402      * Clients who created this column are responsible to call this method
403      * once the column is no longer used.</p>
404      */

405     public void dispose() {
406         if (fTextViewer != null) {
407             fTextViewer.removeViewportListener(fInternalListener);
408             fTextViewer.removeTextListener(fInternalListener);
409             fTextViewer= null;
410         }
411
412         if (fStyledText != null)
413             fStyledText= null;
414
415         if (fCanvas != null) {
416             fCanvas.dispose();
417             fCanvas= null;
418         }
419     }
420
421     /*
422      * @see org.eclipse.jface.text.source.IVerticalRulerColumn#redraw()
423      */

424     public final void redraw() {
425         if (fCanvas != null && !fCanvas.isDisposed())
426             fCanvas.redraw();
427     }
428
429     /**
430      * Marks the region covered by <code>lines</code> as needing to be redrawn.
431      *
432      * @param lines the lines to be redrawn in document coordinates
433      */

434     protected final void redraw(ILineRange lines) {
435         if (fCanvas == null || fCanvas.isDisposed())
436             return;
437         int firstModelLine= lines.getStartLine();
438         int lastModelLine= firstModelLine + lines.getNumberOfLines();
439         int firstWidgetLine= JFaceTextUtil.modelLineToWidgetLine(fTextViewer, firstModelLine);
440         int lastWidgetLine= JFaceTextUtil.modelLineToWidgetLine(fTextViewer, lastModelLine);
441
442         int from= Math.max(0, fStyledText.getLinePixel(firstWidgetLine));
443         // getLinePixel will return the last pixel of the last line if line == lineCount
444
int to= Math.min(fCanvas.getSize().y, fStyledText.getLinePixel(lastWidgetLine + 1));
445         fCanvas.redraw(0, from, fWidth, to - from, false);
446     }
447
448     /**
449      * Paints the ruler column.
450      *
451      * @param event the paint event
452      */

453     private void paintControl(PaintEvent event) {
454         if (fTextViewer == null)
455             return;
456         fWasShowingEntireContents= JFaceTextUtil.isShowingEntireContents(fStyledText);
457         fLastTopPixel= fStyledText.getTopPixel();
458
459         ILineRange lines= computeDirtyWidgetLines(event);
460         GC gc= event.gc;
461         paint(gc, lines);
462
463         if ((fCanvas.getStyle() & SWT.NO_BACKGROUND) != 0) {
464             // fill empty area below any lines
465
int firstEmpty= Math.max(event.y, fStyledText.getLinePixel(fStyledText.getLineCount()));
466             int lastEmpty= event.y + event.height;
467             if (lastEmpty > firstEmpty) {
468                 gc.setBackground(getDefaultBackground());
469                 gc.fillRectangle(0, firstEmpty, getWidth(), lastEmpty - firstEmpty);
470             }
471         }
472     }
473
474     /**
475      * Computes the widget lines that need repainting given the clipping region of a paint event.
476      *
477      * @param event the paint event
478      * @return the lines in widget coordinates that need repainting
479      */

480     private ILineRange computeDirtyWidgetLines(PaintEvent event) {
481         int firstLine= fStyledText.getLineIndex(event.y);
482         int lastLine= fStyledText.getLineIndex(event.y + event.height - 1);
483         return new LineRange(firstLine, lastLine - firstLine + 1);
484     }
485
486     /**
487      * Paints the ruler. Note that <code>lines</code> reference widget line indices, and that
488      * <code>lines</code> may not cover the entire viewport, but only the lines that need to be
489      * painted. The lines may not be entirely visible.
490      * <p>
491      * Subclasses may replace or extend. The default implementation calls
492      * {@link #paintLine(GC, int, int, int, int)} for every visible line.
493      * </p>
494      *
495      * @param gc the graphics context to paint on
496      * @param lines the lines to paint in widget coordinates
497      */

498     protected void paint(GC gc, ILineRange lines) {
499         final int firstLine= lines.getStartLine();
500         final int lastLine= firstLine + lines.getNumberOfLines();
501         for (int line= firstLine; line < lastLine; line++) {
502             int modelLine= JFaceTextUtil.widgetLine2ModelLine(fTextViewer, line);
503             if (modelLine == -1)
504                 continue;
505             int linePixel= fStyledText.getLinePixel(line);
506             int lineHeight= fStyledText.getLineHeight(fStyledText.getOffsetAtLine(line));
507             paintLine(gc, modelLine, line, linePixel, lineHeight);
508         }
509     }
510
511     /**
512      * Paints the ruler representation of a single line.
513      * <p>
514      * Subclasses may replace or extend. The default implementation draws the text obtained by
515      * {@link #computeText(int)} in the {@link #computeForeground(int) foreground color} and fills
516      * the entire width using the {@link #computeBackground(int) background color}. The text is
517      * drawn {@link #getTextInset()} pixels to the right of the left border.
518      * </p>
519      *
520      * @param gc the graphics context to paint on
521      * @param modelLine the model line (based on document coordinates)
522      * @param widgetLine the line in the text widget corresponding to <code>modelLine</code>
523      * @param linePixel the first y-pixel of the widget line
524      * @param lineHeight the line height in pixels
525      */

526     protected void paintLine(GC gc, int modelLine, int widgetLine, int linePixel, int lineHeight) {
527         gc.setBackground(computeBackground(modelLine));
528         gc.fillRectangle(0, linePixel, getWidth(), lineHeight);
529         String JavaDoc text= computeText(modelLine);
530         if (text != null) {
531             gc.setForeground(computeForeground(modelLine));
532             gc.drawString(text, getTextInset(), linePixel, true);
533         }
534     }
535
536     /**
537      * Returns the text to be drawn for a certain line by {@link #paintLine(GC, int, int, int, int)},
538      * <code>null</code> for no text. The default implementation returns <code>null</code>.
539      * <p>
540      * Subclasses may replace or extend.
541      * </p>
542      *
543      * @param line the document line number
544      * @return the text to be drawn for the given line, <code>null</code> for no text
545      */

546     protected String JavaDoc computeText(int line) {
547         return null;
548     }
549
550     /**
551      * Returns the background color drawn for a certain line by
552      * {@link #paintLine(GC, int, int, int, int)}. The default implementation returns
553      * {@link #getDefaultBackground()}.
554      * <p>
555      * Subclasses may replace or extend.
556      * </p>
557      *
558      * @param line the document line number
559      * @return the background color for drawn for the given line
560      */

561     protected Color computeBackground(int line) {
562         return getDefaultBackground();
563     }
564
565     /**
566      * Returns the foreground color drawn for a certain line by
567      * {@link #paintLine(GC, int, int, int, int)}. The default implementation returns a
568      * {@link SWT#COLOR_DARK_GRAY} color.
569      * <p>
570      * Subclasses may replace or extend.
571      * </p>
572      *
573      * @param line the document line number
574      * @return the foreground color for drawn for the given line
575      */

576     protected Color computeForeground(int line) {
577         return fStyledText.getDisplay().getSystemColor(SWT.COLOR_DARK_GRAY);
578     }
579
580     /*
581      * @see org.eclipse.jface.text.source.IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
582      */

583     public final int getLineOfLastMouseButtonActivity() {
584         return getParentRuler().getLineOfLastMouseButtonActivity();
585     }
586
587     /*
588      * @see org.eclipse.jface.text.source.IVerticalRulerInfo#toDocumentLineNumber(int)
589      */

590     public final int toDocumentLineNumber(int y_coordinate) {
591         return getParentRuler().toDocumentLineNumber(y_coordinate);
592     }
593
594     /*
595      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#addVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
596      */

597     public void addVerticalRulerListener(IVerticalRulerListener listener) {
598         throw new UnsupportedOperationException JavaDoc();
599     }
600
601     /*
602      * @see org.eclipse.jface.text.source.IVerticalRulerInfoExtension#removeVerticalRulerListener(org.eclipse.jface.text.source.IVerticalRulerListener)
603      */

604     public void removeVerticalRulerListener(IVerticalRulerListener listener) {
605         throw new UnsupportedOperationException JavaDoc();
606     }
607
608     /**
609      * Scrolls the canvas vertically (adapted from
610      * {@linkplain StyledText StyledText.scrollVertical()}).
611      *
612      * @param pixels the number of pixels to scroll (negative to scroll upwards)
613      * @return <code>true</code> if the widget was scrolled, <code>false</code> if the widget
614      * was not scrolled
615      */

616     private boolean scrollVertical(int pixels) {
617         if (pixels == 0 || fCanvas == null || fCanvas.isDisposed())
618             return false;
619
620         final int width= getWidth();
621         final int clientAreaHeight= fStyledText.getClientArea().height;
622         final int topMargin= 0;
623         final int leftMargin= 0;
624         final int bottomMargin= 0;
625
626         if (pixels > 0) {
627             // downwards scrolling - content moves upwards
628
int sourceY= topMargin + pixels;
629             int scrollHeight= clientAreaHeight - sourceY - bottomMargin;
630             if (scrollHeight > 0)
631                 // scroll recycled area
632
fCanvas.scroll(leftMargin, topMargin, leftMargin, sourceY, width, scrollHeight, true);
633             if (sourceY > scrollHeight) {
634                 // redraw in-between area
635
int redrawY= Math.max(0, topMargin + scrollHeight);
636                 int redrawHeight= Math.min(clientAreaHeight, pixels - scrollHeight);
637                 fCanvas.redraw(leftMargin, redrawY, width, redrawHeight, true);
638             }
639         } else {
640             // upwards scrolling - content moves downwards
641
int destinationY= topMargin - pixels;
642             int scrollHeight= clientAreaHeight - destinationY - bottomMargin;
643             if (scrollHeight > 0)
644                 // scroll recycled area
645
fCanvas.scroll(leftMargin, destinationY, leftMargin, topMargin, width, scrollHeight, true);
646             if (destinationY > scrollHeight) {
647                 // redraw in-between area
648
int redrawY= Math.max(0, topMargin + scrollHeight);
649                 int redrawHeight= Math.min(clientAreaHeight, -pixels - scrollHeight);
650                 fCanvas.redraw(leftMargin, redrawY, width, redrawHeight, true);
651             }
652         }
653         return true;
654     }
655 }
656
Popular Tags