Project

General

Profile

Download (18.3 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
 */
63
public class RulerWithIcon implements IVerticalRuler, IVerticalRulerExtension {
64
	private static final Logger logger = Logger.getLogger(RulerWithIcon.class);
65

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

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

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

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

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

    
123
	/**
124
	 * Constructs a vertical ruler with the given width.
125
	 *
126
	 * @param width the width of the vertical ruler
127
	 */
128
	public RulerWithIcon(int width) {
129
		this(width, null);
130
	}
131

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

    
145
	/*
146
	 * @see IVerticalRuler#getControl()
147
	 */
148
	/**
149
	 * <p>getControl</p>
150
	 *
151
	 * @return a {@link org.eclipse.swt.widgets.Control} object.
152
	 */
153
	@Override
154
    public Control getControl() {
155
		return fCanvas;
156
	}
157

    
158
	@Override
159
    public Control createControl(Composite parent, ITextViewer textViewer) {
160

    
161
		fTextViewer= textViewer;
162

    
163
		fCanvas= new Canvas(parent, SWT.NO_BACKGROUND);
164

    
165
		TableWrapData layout = new TableWrapData(TableWrapData.LEFT);
166
		layout.heightHint = 20;
167
		layout.maxWidth = fWidth;
168
		fCanvas.setLayoutData(layout);
169

    
170
		fCanvas.addPaintListener(new PaintListener() {
171
			@Override
172
            public void paintControl(PaintEvent event) {
173
				if (fTextViewer != null) {
174
                    doubleBufferPaint(event.gc);
175
                }
176
			}
177
		});
178

    
179
		fCanvas.addDisposeListener(new DisposeListener() {
180
			@Override
181
            public void widgetDisposed(DisposeEvent e) {
182
				handleDispose();
183
				fTextViewer= null;
184
			}
185
		});
186

    
187
		fCanvas.addMouseListener(new MouseListener() {
188
			@Override
189
            public void mouseUp(MouseEvent event) {
190
			}
191

    
192
			@Override
193
            public void mouseDown(MouseEvent event) {
194
				fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
195
			}
196

    
197
			@Override
198
            public void mouseDoubleClick(MouseEvent event) {
199
				fLastMouseButtonActivityLine= toDocumentLineNumber(event.y);
200
			}
201
		});
202

    
203
		if (fTextViewer != null) {
204
			fTextViewer.addViewportListener(fInternalListener);
205
			fTextViewer.addTextListener(fInternalListener);
206
		}
207

    
208
		return fCanvas;
209
	}
210

    
211
	/**
212
	 * Disposes the ruler's resources.
213
	 */
214
	private void handleDispose() {
215

    
216
		if (fTextViewer != null) {
217
			fTextViewer.removeViewportListener(fInternalListener);
218
			fTextViewer.removeTextListener(fInternalListener);
219
			fTextViewer= null;
220
		}
221

    
222
		if (fModel != null) {
223
            fModel.removeAnnotationModelListener(fInternalListener);
224
        }
225

    
226
		if (fBuffer != null) {
227
			fBuffer.dispose();
228
			fBuffer= null;
229
		}
230
	}
231

    
232

    
233
	/**
234
	 * Double buffer drawing.
235
	 *
236
	 * @param dest the GC to draw into
237
	 */
238
	private void doubleBufferPaint(GC dest) {
239

    
240
		Point size= fCanvas.getSize();
241

    
242
		if (size.x <= 0 || size.y <= 0) {
243
            return;
244
        }
245

    
246
		if (fBuffer != null) {
247
			Rectangle r= fBuffer.getBounds();
248
			if (r.width != size.x || r.height != size.y) {
249
				fBuffer.dispose();
250
				fBuffer= null;
251
			}
252
		}
253
		if (fBuffer == null) {
254
            fBuffer= new Image(fCanvas.getDisplay(), size.x, size.y);
255
        }
256

    
257
		GC gc= new GC(fBuffer);
258
		gc.setFont(fTextViewer.getTextWidget().getFont());
259
		try {
260
			gc.setBackground(fCanvas.getBackground());
261
			gc.fillRectangle(0, 0, size.x, size.y);
262

    
263
			// Code added to VerticalRuler starts here
264
			if (icon != null) {
265

    
266
				Rectangle r = icon.getBounds();
267

    
268
				if (r.width > size.x || r.height > size.y) {
269
					logger.warn(r.width + "x" + r.height + " icon too big for " + size.x + "x" + size.y + " Canvas."); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
270
				} else {
271

    
272
					// Set destination coordinates to center icon
273
					int x = (size.x - r.width) / 2;
274
					int y = (size.y - r.height) / 2;
275

    
276
					gc.drawImage(icon, 0, 0, r.width, r.height, x, y, r.width, r.height);
277
				}
278
			}
279
			// Code added to VerticalRuler ends here
280

    
281
			if (fTextViewer instanceof ITextViewerExtension5) {
282
                doPaint1(gc);
283
            } else {
284
                doPaint(gc);
285
            }
286

    
287
		} finally {
288
			gc.dispose();
289
		}
290

    
291
		dest.drawImage(fBuffer, 0, 0);
292
	}
293

    
294
	/**
295
	 * Returns the document offset of the upper left corner of the
296
	 * widgets view port, possibly including partially visible lines.
297
	 *
298
	 * @return the document offset of the upper left corner including partially visible lines
299
	 * @since 2.0
300
	 */
301
	private int getInclusiveTopIndexStartOffset() {
302

    
303
		StyledText textWidget= fTextViewer.getTextWidget();
304
		if (textWidget != null && !textWidget.isDisposed()) {
305
			int top= JFaceTextUtil.getPartialTopIndex(fTextViewer);
306
			try {
307
				IDocument document= fTextViewer.getDocument();
308
				return document.getLineOffset(top);
309
			} catch (BadLocationException x) {
310
			}
311
		}
312

    
313
		return -1;
314
	}
315

    
316

    
317

    
318
	/**
319
	 * Draws the vertical ruler w/o drawing the Canvas background.
320
	 *
321
	 * @param gc  the GC to draw into
322
	 */
323
	protected void doPaint(GC gc) {
324

    
325
		if (fModel == null || fTextViewer == null) {
326
            return;
327
        }
328

    
329
		IAnnotationAccessExtension annotationAccessExtension= null;
330
		if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
331
            annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess;
332
        }
333

    
334
		StyledText styledText= fTextViewer.getTextWidget();
335
		IDocument doc= fTextViewer.getDocument();
336

    
337
		int topLeft= getInclusiveTopIndexStartOffset();
338
		int bottomRight= fTextViewer.getBottomIndexEndOffset();
339
		int viewPort= bottomRight - topLeft;
340

    
341
		Point d= fCanvas.getSize();
342
		fScrollPos= styledText.getTopPixel();
343

    
344
		int topLine= -1, bottomLine= -1;
345
		try {
346
			IRegion region= fTextViewer.getVisibleRegion();
347
			topLine= doc.getLineOfOffset(region.getOffset());
348
			bottomLine= doc.getLineOfOffset(region.getOffset() + region.getLength());
349
		} catch (BadLocationException x) {
350
			return;
351
		}
352

    
353
		// draw Annotations
354
		Rectangle r= new Rectangle(0, 0, 0, 0);
355
		int maxLayer= 1;	// loop at least once though layers.
356

    
357
		for (int layer= 0; layer < maxLayer; layer++) {
358
			Iterator<?> iter= fModel.getAnnotationIterator();
359
			while (iter.hasNext()) {
360
				IAnnotationPresentation annotationPresentation= null;
361
				Annotation annotation= (Annotation) iter.next();
362

    
363
				int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
364
				if (annotationAccessExtension != null) {
365
                    lay= annotationAccessExtension.getLayer(annotation);
366
                } else if (annotation instanceof IAnnotationPresentation) {
367
					annotationPresentation= (IAnnotationPresentation)annotation;
368
					lay= annotationPresentation.getLayer();
369
				}
370
				maxLayer= Math.max(maxLayer, lay+1);	// dynamically update layer maximum
371
				if (lay != layer) {
372
                    continue;
373
                }
374

    
375
				Position position= fModel.getPosition(annotation);
376
				if (position == null) {
377
                    continue;
378
                }
379

    
380
				if (!position.overlapsWith(topLeft, viewPort)) {
381
                    continue;
382
                }
383

    
384
				try {
385

    
386
					int offset= position.getOffset();
387
					int length= position.getLength();
388

    
389
					int startLine= doc.getLineOfOffset(offset);
390
					if (startLine < topLine) {
391
                        startLine= topLine;
392
                    }
393

    
394
					int endLine= startLine;
395
					if (length > 0) {
396
                        endLine= doc.getLineOfOffset(offset + length - 1);
397
                    }
398
					if (endLine > bottomLine) {
399
                        endLine= bottomLine;
400
                    }
401

    
402
					startLine -= topLine;
403
					endLine -= topLine;
404

    
405
					r.x= 0;
406
					r.y= JFaceTextUtil.computeLineHeight(styledText, 0, startLine, startLine)  - fScrollPos;
407

    
408
					r.width= d.x;
409
					int lines= endLine - startLine;
410

    
411
					r.height= JFaceTextUtil.computeLineHeight(styledText, startLine, endLine + 1, (lines+1));
412

    
413
					if (r.y < d.y && annotationAccessExtension != null) {
414
                        annotationAccessExtension.paint(annotation, gc, fCanvas, r);
415
                    } else if (annotationPresentation != null) {
416
                        annotationPresentation.paint(gc, fCanvas, r);
417
                    }
418

    
419
				} catch (BadLocationException e) {
420
				}
421
			}
422
		}
423
	}
424

    
425
	/**
426
	 * Draws the vertical ruler w/o drawing the Canvas background. Uses
427
	 * <code>ITextViewerExtension5</code> for its implementation. Will replace
428
	 * <code>doPaint(GC)</code>.
429
	 *
430
	 * @param gc  the GC to draw into
431
	 */
432
	protected void doPaint1(GC gc) {
433

    
434
		if (fModel == null || fTextViewer == null) {
435
            return;
436
        }
437

    
438
		IAnnotationAccessExtension annotationAccessExtension= null;
439
		if (fAnnotationAccess instanceof IAnnotationAccessExtension) {
440
            annotationAccessExtension= (IAnnotationAccessExtension) fAnnotationAccess;
441
        }
442

    
443
		ITextViewerExtension5 extension= (ITextViewerExtension5) fTextViewer;
444
		StyledText textWidget= fTextViewer.getTextWidget();
445

    
446
		fScrollPos= textWidget.getTopPixel();
447
		Point dimension= fCanvas.getSize();
448

    
449
		// draw Annotations
450
		Rectangle r= new Rectangle(0, 0, 0, 0);
451
		int maxLayer= 1;	// loop at least once through layers.
452

    
453
		for (int layer= 0; layer < maxLayer; layer++) {
454
			Iterator<?> iter= fModel.getAnnotationIterator();
455
			while (iter.hasNext()) {
456
				IAnnotationPresentation annotationPresentation= null;
457
				Annotation annotation= (Annotation) iter.next();
458

    
459
				int lay= IAnnotationAccessExtension.DEFAULT_LAYER;
460
				if (annotationAccessExtension != null) {
461
                    lay= annotationAccessExtension.getLayer(annotation);
462
                } else if (annotation instanceof IAnnotationPresentation) {
463
					annotationPresentation= (IAnnotationPresentation)annotation;
464
					lay= annotationPresentation.getLayer();
465
				}
466
				maxLayer= Math.max(maxLayer, lay+1);	// dynamically update layer maximum
467
				if (lay != layer) {
468
                    continue;
469
                }
470

    
471
				Position position= fModel.getPosition(annotation);
472
				if (position == null) {
473
                    continue;
474
                }
475

    
476
				IRegion widgetRegion= extension.modelRange2WidgetRange(new Region(position.getOffset(), position.getLength()));
477
				if (widgetRegion == null) {
478
                    continue;
479
                }
480

    
481
				int startLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset());
482
				if (startLine == -1) {
483
                    continue;
484
                }
485

    
486
				int endLine= extension.widgetLineOfWidgetOffset(widgetRegion.getOffset() + Math.max(widgetRegion.getLength() -1, 0));
487
				if (endLine == -1) {
488
                    continue;
489
                }
490

    
491
				r.x= 0;
492
				r.y= JFaceTextUtil.computeLineHeight(textWidget, 0, startLine, startLine)  - fScrollPos;
493

    
494
				r.width= dimension.x;
495
				int lines= endLine - startLine;
496

    
497
				r.height= JFaceTextUtil.computeLineHeight(textWidget, startLine, endLine + 1, lines+1);
498

    
499
				if (r.y < dimension.y && annotationAccessExtension != null) {
500
                    annotationAccessExtension.paint(annotation, gc, fCanvas, r);
501
                } else if (annotationPresentation != null) {
502
                    annotationPresentation.paint(gc, fCanvas, r);
503
                }
504
			}
505
		}
506
	}
507

    
508
	/**
509
	 * Thread-safe implementation.
510
	 * Can be called from any thread.
511
	 */
512
	/*
513
	 * @see IVerticalRuler#update()
514
	 */
515
	@Override
516
    public void update() {
517
		if (fCanvas != null && !fCanvas.isDisposed()) {
518
			Display d= fCanvas.getDisplay();
519
			if (d != null) {
520
				d.asyncExec(new Runnable() {
521
					@Override
522
                    public void run() {
523
						redraw();
524
					}
525
				});
526
			}
527
		}
528
	}
529

    
530
	/**
531
	 * Redraws the vertical ruler.
532
	 */
533
	private void redraw() {
534
		if (fCanvas != null && !fCanvas.isDisposed()) {
535
			GC gc= new GC(fCanvas);
536
			doubleBufferPaint(gc);
537
			gc.dispose();
538
		}
539
	}
540

    
541
	@Override
542
    public void setModel(IAnnotationModel model) {
543
		if (model != fModel) {
544

    
545
			if (fModel != null) {
546
                fModel.removeAnnotationModelListener(fInternalListener);
547
            }
548

    
549
			fModel= model;
550

    
551
			if (fModel != null) {
552
                fModel.addAnnotationModelListener(fInternalListener);
553
            }
554

    
555
			update();
556
		}
557
	}
558

    
559
	/*
560
	 * @see IVerticalRuler#getModel()
561
	 */
562
	/**
563
	 * <p>getModel</p>
564
	 *
565
	 * @return a {@link org.eclipse.jface.text.source.IAnnotationModel} object.
566
	 */
567
	@Override
568
    public IAnnotationModel getModel() {
569
		return fModel;
570
	}
571

    
572
	/*
573
	 * @see IVerticalRulerInfo#getWidth()
574
	 */
575
	/**
576
	 * <p>getWidth</p>
577
	 *
578
	 * @return a int.
579
	 */
580
	@Override
581
    public int getWidth() {
582
		return fWidth;
583
	}
584

    
585
	/*
586
	 * @see IVerticalRulerInfo#getLineOfLastMouseButtonActivity()
587
	 */
588
	/**
589
	 * <p>getLineOfLastMouseButtonActivity</p>
590
	 *
591
	 * @return a int.
592
	 */
593
	@Override
594
    public int getLineOfLastMouseButtonActivity() {
595
		return fLastMouseButtonActivityLine;
596
	}
597

    
598
	@Override
599
    public int toDocumentLineNumber(int y_coordinate) {
600
		if (fTextViewer == null  || y_coordinate == -1) {
601
            return -1;
602
        }
603

    
604
		StyledText text= fTextViewer.getTextWidget();
605
		int line= text.getLineIndex(y_coordinate);
606

    
607
		if (line == text.getLineCount() - 1) {
608
			// check whether y_coordinate exceeds last line
609
			if (y_coordinate > text.getLinePixel(line + 1)) {
610
                return -1;
611
            }
612
		}
613

    
614
		return widgetLine2ModelLine(fTextViewer, line);
615
	}
616

    
617
	/**
618
	 * Returns the line of the viewer's document that corresponds to the given widget line.
619
	 *
620
	 * @param viewer the viewer
621
	 * @param widgetLine the widget line
622
	 * @return the corresponding line of the viewer's document
623
	 * @since 2.1
624
	 */
625
	protected final static int widgetLine2ModelLine(ITextViewer viewer, int widgetLine) {
626

    
627
		if (viewer instanceof ITextViewerExtension5) {
628
			ITextViewerExtension5 extension= (ITextViewerExtension5) viewer;
629
			return extension.widgetLine2ModelLine(widgetLine);
630
		}
631

    
632
		try {
633
			IRegion r= viewer.getVisibleRegion();
634
			IDocument d= viewer.getDocument();
635
			return widgetLine += d.getLineOfOffset(r.getOffset());
636
		} catch (BadLocationException x) {
637
		}
638
		return widgetLine;
639
	}
640

    
641
	@Override
642
    public void setFont(Font font) {
643
	}
644

    
645
	@Override
646
    public void setLocationOfLastMouseButtonActivity(int x, int y) {
647
		fLastMouseButtonActivityLine= toDocumentLineNumber(y);
648
	}
649

    
650
	/**
651
	 * Adds the given mouse listener.
652
	 *
653
	 * @param listener the listener to be added
654
	 * @deprecated will be removed
655
	 * @since 2.0
656
	 */
657
	@Deprecated
658
    public void addMouseListener(MouseListener listener) {
659
		if (fCanvas != null && !fCanvas.isDisposed()) {
660
            fCanvas.addMouseListener(listener);
661
        }
662
	}
663

    
664
	/**
665
	 * Removes the given mouse listener.
666
	 *
667
	 * @param listener the listener to be removed
668
	 * @deprecated will be removed
669
	 * @since 2.0
670
	 */
671
	@Deprecated
672
    public void removeMouseListener(MouseListener listener) {
673
		if (fCanvas != null && !fCanvas.isDisposed()) {
674
            fCanvas.removeMouseListener(listener);
675
        }
676
	}
677

    
678
	/**
679
	 * <p>Setter for the field <code>icon</code>.</p>
680
	 *
681
	 * @param icon a {@link org.eclipse.swt.graphics.Image} object.
682
	 */
683
	public void setIcon(Image icon) {
684
		this.icon = icon;
685
		redraw();
686
	}
687
}
688

    
(6-6/6)