Project

General

Profile

Download (17.5 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy 
4
* http://www.e-taxonomy.eu
5
* 
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9

    
10
package eu.etaxonomy.taxeditor.editor.name.container;
11

    
12
import java.util.Iterator;
13

    
14
import org.apache.log4j.Logger;
15
import org.eclipse.jface.text.BadLocationException;
16
import org.eclipse.jface.text.IDocument;
17
import org.eclipse.jface.text.IRegion;
18
import org.eclipse.jface.text.ITextListener;
19
import org.eclipse.jface.text.ITextViewer;
20
import org.eclipse.jface.text.ITextViewerExtension5;
21
import org.eclipse.jface.text.IViewportListener;
22
import org.eclipse.jface.text.JFaceTextUtil;
23
import org.eclipse.jface.text.Position;
24
import org.eclipse.jface.text.Region;
25
import org.eclipse.jface.text.TextEvent;
26
import org.eclipse.jface.text.source.Annotation;
27
import org.eclipse.jface.text.source.IAnnotationAccess;
28
import org.eclipse.jface.text.source.IAnnotationAccessExtension;
29
import org.eclipse.jface.text.source.IAnnotationModel;
30
import org.eclipse.jface.text.source.IAnnotationModelListener;
31
import org.eclipse.jface.text.source.IAnnotationPresentation;
32
import org.eclipse.jface.text.source.IVerticalRuler;
33
import org.eclipse.jface.text.source.IVerticalRulerExtension;
34
import org.eclipse.swt.SWT;
35
import org.eclipse.swt.custom.StyledText;
36
import org.eclipse.swt.events.DisposeEvent;
37
import org.eclipse.swt.events.DisposeListener;
38
import org.eclipse.swt.events.MouseEvent;
39
import org.eclipse.swt.events.MouseListener;
40
import org.eclipse.swt.events.PaintEvent;
41
import org.eclipse.swt.events.PaintListener;
42
import org.eclipse.swt.graphics.Font;
43
import org.eclipse.swt.graphics.GC;
44
import org.eclipse.swt.graphics.Image;
45
import org.eclipse.swt.graphics.Point;
46
import org.eclipse.swt.graphics.Rectangle;
47
import org.eclipse.swt.widgets.Canvas;
48
import org.eclipse.swt.widgets.Composite;
49
import org.eclipse.swt.widgets.Control;
50
import org.eclipse.swt.widgets.Display;
51
import org.eclipse.ui.forms.widgets.TableWrapData;
52

    
53
/**
54
 * 99% of the code in this class was copied from org.eclipse.jface.text.source.VerticalRuler,
55
 * which allows neither access to its paint methods nor subclassing to do same.
56
 * <p>
57
 * Changes made in method doubleBufferPaint(doubleBufferPaint(GC dest)).
58
 *
59
 * @see org.eclipse.jface.text.source.VerticalRuler
60
 * @author p.ciardelli
61
 * @created 27.01.2009
62
 * @version 1.0
63
 */
64
public class RulerWithIcon implements IVerticalRuler, IVerticalRulerExtension {
65
	private static final Logger logger = Logger.getLogger(RulerWithIcon.class);
66

    
67
	/**
68
	 * Internal listener class.
69
	 */
70
	class InternalListener implements IViewportListener, IAnnotationModelListener, ITextListener {
71

    
72
		/*
73
		 * @see IViewportListener#viewportChanged(int)
74
		 */
75
		public void viewportChanged(int verticalPosition) {
76
			if (verticalPosition != fScrollPos)
77
				redraw();
78
		}
79

    
80
		/*
81
		 * @see IAnnotationModelListener#modelChanged(IAnnotationModel)
82
		 */
83
		public void modelChanged(IAnnotationModel model) {
84
			update();
85
		}
86

    
87
		/*
88
		 * @see ITextListener#textChanged(TextEvent)
89
		 */
90
		public void textChanged(TextEvent e) {
91
			if (fTextViewer != null && e.getViewerRedrawState())
92
				redraw();
93
		}
94
	}
95

    
96
	/** The vertical ruler's text viewer */
97
	private ITextViewer fTextViewer;
98
	/** The ruler's canvas */
99
	private Canvas fCanvas;
100
	/** The vertical ruler's model */
101
	private IAnnotationModel fModel;
102
	/** Cache for the actual scroll position in pixels */
103
	private int fScrollPos;
104
	/** The buffer for double buffering */
105
	private Image fBuffer;
106
	/** The line of the last mouse button activity */
107
	private int fLastMouseButtonActivityLine= -1;
108
	/** The internal listener */
109
	private InternalListener fInternalListener= new InternalListener();
110
	/** The width of this vertical ruler */
111
	private int fWidth;
112
	/**
113
	 * The annotation access of this vertical ruler
114
	 * @since 3.0
115
	 */
116
	private IAnnotationAccess fAnnotationAccess;
117
	private Image icon;
118

    
119
	/**
120
	 * Constructs a vertical ruler with the given width.
121
	 *
122
	 * @param width the width of the vertical ruler
123
	 */
124
	public RulerWithIcon(int width) {
125
		this(width, null);
126
	}
127

    
128
	/**
129
	 * Constructs a vertical ruler with the given width and the given annotation
130
	 * access.
131
	 *
132
	 * @param width the width of the vertical ruler
133
	 * @param annotationAcccess the annotation access
134
	 * @since 3.0
135
	 */
136
	public RulerWithIcon(int width, IAnnotationAccess annotationAcccess) {
137
		fWidth= width;
138
		fAnnotationAccess= annotationAcccess;
139
	}
140

    
141
	/*
142
	 * @see IVerticalRuler#getControl()
143
	 */
144
	/**
145
	 * <p>getControl</p>
146
	 *
147
	 * @return a {@link org.eclipse.swt.widgets.Control} object.
148
	 */
149
	public Control getControl() {
150
		return fCanvas;
151
	}
152

    
153
	/*
154
	 * @see IVerticalRuler#createControl(Composite, ITextViewer)
155
	 */
156
	/** {@inheritDoc} */
157
	public Control createControl(Composite parent, ITextViewer textViewer) {
158

    
159
		fTextViewer= textViewer;
160

    
161
		fCanvas= new Canvas(parent, SWT.NO_BACKGROUND);
162

    
163
		TableWrapData layout = new TableWrapData(TableWrapData.LEFT);
164
		layout.heightHint = 20;
165
		layout.maxWidth = fWidth;
166
		fCanvas.setLayoutData(layout);		
167
		
168
		fCanvas.addPaintListener(new PaintListener() {
169
			public void paintControl(PaintEvent event) {
170
				if (fTextViewer != null)
171
					doubleBufferPaint(event.gc);
172
			}
173
		});
174

    
175
		fCanvas.addDisposeListener(new DisposeListener() {
176
			public void widgetDisposed(DisposeEvent e) {
177
				handleDispose();
178
				fTextViewer= null;
179
			}
180
		});
181

    
182
		fCanvas.addMouseListener(new MouseListener() {
183
			public void mouseUp(MouseEvent event) {
184
			}
185

    
186
			public void mouseDown(MouseEvent event) {
187
				fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
188
			}
189

    
190
			public void mouseDoubleClick(MouseEvent event) {
191
				fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
192
			}
193
		});
194

    
195
		if (fTextViewer != null) {
196
			fTextViewer.addViewportListener(fInternalListener);
197
			fTextViewer.addTextListener(fInternalListener);
198
		}
199

    
200
		return fCanvas;
201
	}
202

    
203
	/**
204
	 * Disposes the ruler's resources.
205
	 */
206
	private void handleDispose() {
207

    
208
		if (fTextViewer != null) {
209
			fTextViewer.removeViewportListener(fInternalListener);
210
			fTextViewer.removeTextListener(fInternalListener);
211
			fTextViewer= null;
212
		}
213

    
214
		if (fModel != null)
215
			fModel.removeAnnotationModelListener(fInternalListener);
216

    
217
		if (fBuffer != null) {
218
			fBuffer.dispose();
219
			fBuffer= null;
220
		}
221
	}
222

    
223

    
224
	/**
225
	 * Double buffer drawing.
226
	 *
227
	 * @param dest the GC to draw into
228
	 */
229
	private void doubleBufferPaint(GC dest) {
230

    
231
		Point size= fCanvas.getSize();
232

    
233
		if (size.x <= 0 || size.y <= 0)
234
			return;
235

    
236
		if (fBuffer != null) {
237
			Rectangle r= fBuffer.getBounds();
238
			if (r.width != size.x || r.height != size.y) {
239
				fBuffer.dispose();
240
				fBuffer= null;
241
			}
242
		}
243
		if (fBuffer == null)
244
			fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
245

    
246
		GC gc= new GC(fBuffer);
247
		gc.setFont(fTextViewer.getTextWidget().getFont());
248
		try {
249
			gc.setBackground(fCanvas.getBackground());
250
			gc.fillRectangle(0, 0, size.x, size.y);
251

    
252
			// Code added to VerticalRuler starts here
253
			if (icon != null) {
254
				
255
				Rectangle r = icon.getBounds();
256
				
257
				if (r.width > size.x || r.height > size.y) {
258
					logger.warn(r.width + "x" + r.height + " icon too big for " + size.x + "x" + size.y + " Canvas.");
259
				} else {
260

    
261
					// Set destination coordinates to center icon
262
					int x = (size.x - r.width) / 2;
263
					int y = (size.y - r.height) / 2;
264
					
265
					gc.drawImage(icon, 0, 0, r.width, r.height, x, y, r.width, r.height);
266
				}				
267
			}
268
			// Code added to VerticalRuler ends here
269
			
270
			if (fTextViewer instanceof ITextViewerExtension5)
271
				doPaint1(gc);
272
			else
273
				doPaint(gc);
274

    
275
		} finally {
276
			gc.dispose();
277
		}
278

    
279
		dest.drawImage(fBuffer, 0, 0);
280
	}
281

    
282
	/**
283
	 * Returns the document offset of the upper left corner of the
284
	 * widgets view port, possibly including partially visible lines.
285
	 *
286
	 * @return the document offset of the upper left corner including partially visible lines
287
	 * @since 2.0
288
	 */
289
	private int getInclusiveTopIndexStartOffset() {
290

    
291
		StyledText textWidget= fTextViewer.getTextWidget();
292
		if (textWidget != null && !textWidget.isDisposed()) {
293
			int top= JFaceTextUtil.getPartialTopIndex(fTextViewer);
294
			try {
295
				IDocument document= fTextViewer.getDocument();
296
				return document.getLineOffset(top);
297
			} catch (BadLocationException x) {
298
			}
299
		}
300

    
301
		return -1;
302
	}
303

    
304

    
305

    
306
	/**
307
	 * Draws the vertical ruler w/o drawing the Canvas background.
308
	 *
309
	 * @param gc  the GC to draw into
310
	 */
311
	protected void doPaint(GC gc) {
312

    
313
		if (fModel == null || fTextViewer == null)
314
			return;
315

    
316
		IAnnotationAccessExtension annotationAccessExtension= null;
317
		if (fAnnotationAccess instanceof IAnnotationAccessExtension)
318
			annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess;
319

    
320
		StyledText styledText= fTextViewer.getTextWidget();
321
		IDocument doc= fTextViewer.getDocument();
322

    
323
		int topLeft= getInclusiveTopIndexStartOffset();
324
		int bottomRight= fTextViewer.getBottomIndexEndOffset();
325
		int viewPort= bottomRight - topLeft;
326

    
327
		Point d= fCanvas.getSize();
328
		fScrollPos= styledText.getTopPixel();
329

    
330
		int topLine= -1, bottomLine= -1;
331
		try {
332
			IRegion region= fTextViewer.getVisibleRegion();
333
			topLine= doc.getLineOfOffset(region.getOffset());
334
			bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
335
		} catch (BadLocationException x) {
336
			return;
337
		}
338

    
339
		// draw Annotations
340
		Rectangle r= new Rectangle(0, 0, 0, 0);
341
		int maxLayer= 1;	// loop at least once though layers.
342

    
343
		for (int layer= 0; layer < maxLayer; layer++) {
344
			Iterator<?> iter= fModel.getAnnotationIterator();
345
			while (iter.hasNext()) {
346
				IAnnotationPresentation annotationPresentation= null;
347
				Annotation annotation= (Annotation) iter.next();
348

    
349
				int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
350
				if (annotationAccessExtension != null)
351
					lay= annotationAccessExtension.getLayer(annotation);
352
				else if (annotation instanceof IAnnotationPresentation) {
353
					annotationPresentation= (IAnnotationPresentation)annotation;
354
					lay= annotationPresentation.getLayer();
355
				}
356
				maxLayer= Math.max(maxLayer, lay+1);	// dynamically update layer maximum
357
				if (lay != layer)	// wrong layer: skip annotation
358
					continue;
359

    
360
				Position position= fModel.getPosition(annotation);
361
				if (position == null)
362
					continue;
363

    
364
				if (!position.overlapsWith(topLeft, viewPort))
365
					continue;
366

    
367
				try {
368

    
369
					int offset= position.getOffset();
370
					int length= position.getLength();
371

    
372
					int startLine= doc.getLineOfOffset(offset);
373
					if (startLine < topLine)
374
						startLine= topLine;
375

    
376
					int endLine= startLine;
377
					if (length > 0)
378
						endLine= doc.getLineOfOffset(offset + length - 1);
379
					if (endLine > bottomLine)
380
						endLine= bottomLine;
381

    
382
					startLine -= topLine;
383
					endLine -= topLine;
384

    
385
					r.x= 0;
386
					r.y= JFaceTextUtil.computeLineHeight(styledText, 0, startLine, startLine)  - fScrollPos;
387
					
388
					r.width= d.x;
389
					int lines= endLine - startLine;
390
					
391
					r.height= JFaceTextUtil.computeLineHeight(styledText, startLine, endLine + 1, (lines+1));
392

    
393
					if (r.y < d.y && annotationAccessExtension != null)  // annotation within visible area
394
						annotationAccessExtension.paint(annotation, gc, fCanvas, r);
395
					else if (annotationPresentation != null)
396
						annotationPresentation.paint(gc, fCanvas, r);
397

    
398
				} catch (BadLocationException e) {
399
				}
400
			}
401
		}
402
	}
403

    
404
	/**
405
	 * Draws the vertical ruler w/o drawing the Canvas background. Uses
406
	 * <code>ITextViewerExtension5</code> for its implementation. Will replace
407
	 * <code>doPaint(GC)</code>.
408
	 *
409
	 * @param gc  the GC to draw into
410
	 */
411
	protected void doPaint1(GC gc) {
412

    
413
		if (fModel == null || fTextViewer == null)
414
			return;
415

    
416
		IAnnotationAccessExtension annotationAccessExtension= null;
417
		if (fAnnotationAccess instanceof IAnnotationAccessExtension)
418
			annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess;
419

    
420
		ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
421
		StyledText textWidget= fTextViewer.getTextWidget();
422

    
423
		fScrollPos= textWidget.getTopPixel();
424
		Point dimension= fCanvas.getSize();
425

    
426
		// draw Annotations
427
		Rectangle r= new Rectangle(0, 0, 0, 0);
428
		int maxLayer= 1;	// loop at least once through layers.
429

    
430
		for (int layer= 0; layer < maxLayer; layer++) {
431
			Iterator<?> iter= fModel.getAnnotationIterator();
432
			while (iter.hasNext()) {
433
				IAnnotationPresentation annotationPresentation= null;
434
				Annotation annotation= (Annotation) iter.next();
435

    
436
				int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
437
				if (annotationAccessExtension != null)
438
					lay= annotationAccessExtension.getLayer(annotation);
439
				else if (annotation instanceof IAnnotationPresentation) {
440
					annotationPresentation= (IAnnotationPresentation)annotation;
441
					lay= annotationPresentation.getLayer();
442
				}
443
				maxLayer= Math.max(maxLayer, lay+1);	// dynamically update layer maximum
444
				if (lay != layer)	// wrong layer: skip annotation
445
					continue;
446

    
447
				Position position= fModel.getPosition(annotation);
448
				if (position == null)
449
					continue;
450

    
451
				IRegion widgetRegion= extension.modelRange2WidgetRange(new Region(position.getOffset(), position.getLength()));
452
				if (widgetRegion == null)
453
					continue;
454

    
455
				int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset());
456
				if (startLine == -1)
457
					continue;
458

    
459
				int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0));
460
				if (endLine == -1)
461
					continue;
462

    
463
				r.x= 0;
464
				r.y= JFaceTextUtil.computeLineHeight(textWidget, 0, startLine, startLine)  - fScrollPos;
465
				
466
				r.width= dimension.x;
467
				int lines= endLine - startLine;
468
				
469
				r.height= JFaceTextUtil.computeLineHeight(textWidget, startLine, endLine + 1, lines+1);
470

    
471
				if (r.y < dimension.y && annotationAccessExtension != null)  // annotation within visible area
472
					annotationAccessExtension.paint(annotation, gc, fCanvas, r);
473
				else if (annotationPresentation != null)
474
					annotationPresentation.paint(gc, fCanvas, r);
475
			}
476
		}
477
	}
478

    
479
	/**
480
	 * Thread-safe implementation.
481
	 * Can be called from any thread.
482
	 */
483
	/*
484
	 * @see IVerticalRuler#update()
485
	 */
486
	public void update() {
487
		if (fCanvas != null && !fCanvas.isDisposed()) {
488
			Display d= fCanvas.getDisplay();
489
			if (d != null) {
490
				d.asyncExec(new Runnable() {
491
					public void run() {
492
						redraw();
493
					}
494
				});
495
			}
496
		}
497
	}
498

    
499
	/**
500
	 * Redraws the vertical ruler.
501
	 */
502
	private void redraw() {
503
		if (fCanvas != null && !fCanvas.isDisposed()) {
504
			GC gc= new GC(fCanvas);
505
			doubleBufferPaint(gc);
506
			gc.dispose();
507
		}
508
	}
509

    
510
	/*
511
	 * @see IVerticalRuler#setModel(IAnnotationModel)
512
	 */
513
	/** {@inheritDoc} */
514
	public void setModel(IAnnotationModel model) {
515
		if (model != fModel) {
516

    
517
			if (fModel != null)
518
				fModel.removeAnnotationModelListener(fInternalListener);
519

    
520
			fModel= model;
521

    
522
			if (fModel != null)
523
				fModel.addAnnotationModelListener(fInternalListener);
524

    
525
			update();
526
		}
527
	}
528

    
529
	/*
530
	 * @see IVerticalRuler#getModel()
531
	 */
532
	/**
533
	 * <p>getModel</p>
534
	 *
535
	 * @return a {@link org.eclipse.jface.text.source.IAnnotationModel} object.
536
	 */
537
	public IAnnotationModel getModel() {
538
		return fModel;
539
	}
540

    
541
	/*
542
	 * @see IVerticalRulerInfo#getWidth()
543
	 */
544
	/**
545
	 * <p>getWidth</p>
546
	 *
547
	 * @return a int.
548
	 */
549
	public int getWidth() {
550
		return fWidth;
551
	}
552

    
553
	/*
554
	 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
555
	 */
556
	/**
557
	 * <p>getLineOfLastMouseButtonActivity</p>
558
	 *
559
	 * @return a int.
560
	 */
561
	public int getLineOfLastMouseButtonActivity() {
562
		return fLastMouseButtonActivityLine;
563
	}
564

    
565
	/*
566
	 * @see IVerticalRulerInfo#toDocumentLineNumber(int)
567
	 */
568
	/** {@inheritDoc} */
569
	public int toDocumentLineNumber(int y_coordinate) {
570
		if (fTextViewer == null  || y_coordinate == -1)
571
			return -1;
572

    
573
		StyledText text= fTextViewer.getTextWidget();
574
		int line= text.getLineIndex(y_coordinate);
575
		
576
		if (line == text.getLineCount() - 1) {
577
			// check whether y_coordinate exceeds last line
578
			if (y_coordinate > text.getLinePixel(line + 1))
579
				return -1;
580
		}
581
		
582
		return widgetLine2ModelLine(fTextViewer, line);
583
	}
584

    
585
	/**
586
	 * Returns the line of the viewer's document that corresponds to the given widget line.
587
	 *
588
	 * @param viewer the viewer
589
	 * @param widgetLine the widget line
590
	 * @return the corresponding line of the viewer's document
591
	 * @since 2.1
592
	 */
593
	protected final static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) {
594

    
595
		if (viewer instanceof ITextViewerExtension5) {
596
			ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
597
			return extension.widgetLine2ModelLine(widgetLine);
598
		}
599

    
600
		try {
601
			IRegion r= viewer.getVisibleRegion();
602
			IDocument d= viewer.getDocument();
603
			return widgetLine += d.getLineOfOffset(r.getOffset());
604
		} catch (BadLocationException x) {
605
		}
606
		return widgetLine;
607
	}
608

    
609
	/*
610
	 * @see IVerticalRulerExtension#setFont(Font)
611
	 * @since 2.0
612
	 */
613
	/** {@inheritDoc} */
614
	public void setFont(Font font) {
615
	}
616

    
617
	/*
618
	 * @see IVerticalRulerExtension#setLocationOfLastMouseButtonActivity(int, int)
619
	 * @since 2.0
620
	 */
621
	/** {@inheritDoc} */
622
	public void setLocationOfLastMouseButtonActivity(int x, int y) {
623
		fLastMouseButtonActivityLine= toDocumentLineNumber(y);
624
	}
625

    
626
	/**
627
	 * Adds the given mouse listener.
628
	 *
629
	 * @param listener the listener to be added
630
	 * @deprecated will be removed
631
	 * @since 2.0
632
	 */
633
	public void addMouseListener(MouseListener listener) {
634
		if (fCanvas != null && !fCanvas.isDisposed())
635
			fCanvas.addMouseListener(listener);
636
	}
637

    
638
	/**
639
	 * Removes the given mouse listener.
640
	 *
641
	 * @param listener the listener to be removed
642
	 * @deprecated will be removed
643
	 * @since 2.0
644
	 */
645
	public void removeMouseListener(MouseListener listener) {
646
		if (fCanvas != null && !fCanvas.isDisposed())
647
			fCanvas.removeMouseListener(listener);
648
	}
649

    
650
	/**
651
	 * <p>Setter for the field <code>icon</code>.</p>
652
	 *
653
	 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
654
	 */
655
	public void setIcon(Image icon) {
656
		this.icon = icon;
657
		redraw();
658
	}
659
}
660
	
(18-18/19)