View Javadoc

1   /*
2    *  File: DefaultHierarchyRenderer.java 
3    *  Copyright (c) 2004-2007  Peter Kliem (Peter.Kliem@jaret.de)
4    *  A commercial license is available, see http://www.jaret.de.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation; either version 2 of the License, or
9    *  (at your option) any later version.
10   *
11   *  This program is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   *  GNU General Public License for more details.
15   *
16   *  You should have received a copy of the GNU General Public License
17   *  along with this program; if not, write to the Free Software
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package de.jaret.util.ui.timebars.swt.renderer;
21  
22  import java.util.HashMap;
23  import java.util.Map;
24  
25  import org.eclipse.jface.resource.ImageDescriptor;
26  import org.eclipse.jface.resource.ImageRegistry;
27  import org.eclipse.jface.viewers.ILabelProvider;
28  import org.eclipse.swt.SWT;
29  import org.eclipse.swt.graphics.Color;
30  import org.eclipse.swt.graphics.GC;
31  import org.eclipse.swt.graphics.Image;
32  import org.eclipse.swt.graphics.Rectangle;
33  import org.eclipse.swt.printing.Printer;
34  import org.eclipse.swt.widgets.Display;
35  
36  import de.jaret.util.ui.ResourceImageDescriptor;
37  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
38  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
39  import de.jaret.util.ui.timebars.model.StdHierarchicalTimeBarModel;
40  import de.jaret.util.ui.timebars.model.TimeBarNode;
41  import de.jaret.util.ui.timebars.model.TimeBarRow;
42  import de.jaret.util.ui.timebars.swt.TimeBarViewer;
43  
44  /**
45   * Default implementation of a HierarchyRenderer. Can draw +/-/o for nodes or any configured image. Uses label providers
46   * (regsiters for row implementing class) for labels and icons (icons replace standard node symbol).
47   * 
48   * @author Peter Kliem
49   * @version $Id: DefaultHierarchyRenderer.java 800 2008-12-27 22:27:33Z kliem $
50   */
51  public class DefaultHierarchyRenderer extends RendererBase implements HierarchyRenderer {
52      /** size of the plus/minus signs. */
53      protected static final int DEFAULT_SIZE = 12;
54  
55      /** insets for painting the +/. symbol. */
56      protected static final int SYMBOLINSETS = 3;
57  
58      /** defaut level width. */
59      protected static final int DEFAULT_LEVEL_WIDTH = 16;
60  
61      /** gap between tree and the label/icon provided by a label provider. */
62      protected static final int LABELGAP = 4;
63  
64      /** actual size of the symbols. */
65      protected int _size = DEFAULT_SIZE;
66  
67      /** actual value for the signinstes. */
68      protected int _signInsets = SYMBOLINSETS;
69  
70      /** if true, tree lines will be drawn. */
71      protected boolean _drawTreeLines = true;
72  
73      /** level width for fixed level width drawing. */
74      protected int _levelWidth = DEFAULT_LEVEL_WIDTH;
75  
76      /** if true the level indentation is fixed. */
77      protected boolean _fixedLevelWidth = false;
78  
79      /** true for enabling drawing of icons. */
80      protected boolean _drawIcons = false;
81  
82      /** true for enabling label drawing. */
83      protected boolean _drawLabels = false;
84  
85      /** label providers for labels and icons. */
86      protected Map<Class<? extends TimeBarRow>, ILabelProvider> _labelProviderMap = new HashMap<Class<? extends TimeBarRow>, ILabelProvider>();
87  
88      /** key for image registry. */
89      protected static final String PLUS = "plus";
90      /** key for image registry. */
91      protected static final String MINUS = "minus";
92      /** key for image registry. */
93      protected static final String LEAF = "leaf";
94  
95      /** image registry for holding the images. */
96      private ImageRegistry _imageRegistry;
97  
98      /** path of the plus image ressource. */
99      protected String _plusRscName;
100     /** path of the minus image ressource. */
101     protected String _minusRscName;
102     /** path of the leaf image ressource. */
103     protected String _leafRscName;
104 
105     /**
106      * Create for a printer.
107      * 
108      * @param printer printer device
109      */
110     public DefaultHierarchyRenderer(Printer printer) {
111         super(printer);
112         _size = scaleX(DEFAULT_SIZE);
113         _signInsets = scaleX(_signInsets);
114         _levelWidth = scaleX(_levelWidth);
115     }
116 
117     /**
118      * Default constructor.
119      */
120     public DefaultHierarchyRenderer() {
121         super(null);
122     }
123 
124     /**
125      * {@inheritDoc}
126      */
127     public void draw(GC gc, Rectangle drawingArea, TimeBarViewerDelegate tbv, TimeBarRow row, boolean selected,
128             boolean expanded, boolean leaf, int level, int depth, boolean printing) {
129 
130         int offx;
131         if (!_fixedLevelWidth && depth > 0) {
132             offx = (drawingArea.width - _size) / (depth + 1);
133         } else {
134             offx = scaleX(_levelWidth);
135         }
136 
137         int x = drawingArea.x + offx * level + _size / 2;
138 
139         int y = drawingArea.y + (drawingArea.height - _size) / 2;
140 
141         if (leaf && !_drawIcons) {
142             drawLeafSymbol(gc, _size, x, y);
143         } else if (expanded && !leaf) {
144             drawExpanded(gc, _size, x, y);
145         } else if (!leaf) {
146             drawCollapsed(gc, _size, x, y);
147         }
148         x += _size + LABELGAP;
149 
150         ILabelProvider labelProvider = getLabelProvider(row.getClass());
151 
152         if (labelProvider != null && (_drawIcons || _drawLabels)) {
153             int labelx = x;
154             if (_drawIcons) {
155                 Image img = labelProvider.getImage(row);
156                 if (img != null) {
157                     if (!printing) {
158                         gc.drawImage(img, x, y);
159                         labelx += img.getBounds().width;
160                     } else {
161                         gc.drawImage(img, 0, 0, img.getBounds().width, img.getBounds().height, x, y, scaleX(img
162                                 .getBounds().width), scaleY(img.getBounds().height));
163                         labelx += scaleX(img.getBounds().width);
164                     }
165                 }
166             }
167             if (_drawLabels) {
168                 String label = labelProvider.getText(row);
169                 if (label != null) {
170                     if (!printing && selected) {
171                         Color bg = gc.getBackground();
172                         gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLUE));
173                         gc.drawString(label, labelx, y);
174                         gc.setBackground(bg);
175                     } else {
176                         gc.drawString(label, labelx, y);
177                     }
178                 }
179             }
180 
181         }
182 
183         // draw tree connections
184         if (_drawTreeLines) {
185             TimeBarNode node = (TimeBarNode) row;
186             if (printing) {
187                 gc.setLineWidth(getDefaultLineWidth());
188             }
189             gc.setLineStyle(SWT.LINE_DOT);
190             int midy = drawingArea.y + ((drawingArea.height - _size) / 2) + _size / 2;
191             int icoy = drawingArea.y + ((drawingArea.height - _size) / 2) + _size;
192             int icox = drawingArea.x + offx * (level) + _size - _size / 2;
193             int midx = drawingArea.x + +offx * (level) + _size;
194             int beginx = drawingArea.x + offx * (level - 1) + _size;
195             // int endx = drawingArea.x + offx * (level + 1) + _size;
196 
197             // connection
198             gc.drawLine(beginx, midy, icox, midy);
199 
200             // uplink
201             gc.drawLine(beginx, drawingArea.y, beginx, midy);
202 
203             // downlink
204             if ((!leaf && expanded)) {
205                 gc.drawLine(midx, icoy, midx, drawingArea.y + drawingArea.height);
206             }
207 
208             // level lines
209             if (tbv.getModel() instanceof StdHierarchicalTimeBarModel) {
210                 StdHierarchicalTimeBarModel model = (StdHierarchicalTimeBarModel) tbv.getModel();
211                 for (int i = 0; i < level; i++) {
212                     if (model.moreSiblings(node, i)) {
213                         x = drawingArea.x + offx * i + _size;
214                         gc.drawLine(x, drawingArea.y, x, drawingArea.y + drawingArea.height);
215                     }
216                 }
217             }
218 
219             gc.setLineStyle(SWT.LINE_SOLID);
220             gc.setLineWidth(1);
221         }
222     }
223 
224     /**
225      * Draw the collapsed symbol.
226      * 
227      * @param gc GC
228      * @param size size
229      * @param x left x
230      * @param y upper y
231      */
232     protected void drawCollapsed(GC gc, int size, int x, int y) {
233         if (_plusRscName != null) {
234             ImageRegistry registry = getImageRegistry();
235             Image img = registry.get(PLUS);
236             if (img != null) {
237                 gc.drawImage(img, 0, 0, img.getBounds().width, img.getBounds().height, x, y, size, size);
238             }
239         } else {
240             drawPlus(gc, size, x, y);
241         }
242     }
243 
244     /**
245      * Draw the expanded symbol.
246      * 
247      * @param gc GC
248      * @param size size
249      * @param x left x
250      * @param y upper y
251      */
252     protected void drawExpanded(GC gc, int size, int x, int y) {
253         if (_minusRscName != null) {
254             ImageRegistry registry = getImageRegistry();
255             Image img = registry.get(MINUS);
256             if (img != null) {
257                 gc.drawImage(img, 0, 0, img.getBounds().width, img.getBounds().height, x, y, size, size);
258             }
259         } else {
260             drawMinus(gc, size, x, y);
261         }
262     }
263 
264     /**
265      * Draw the leaf symbol.
266      * 
267      * @param gc GC
268      * @param size size
269      * @param x left x
270      * @param y upper y
271      */
272     protected void drawLeafSymbol(GC gc, int size, int x, int y) {
273         if (_leafRscName != null) {
274             ImageRegistry registry = getImageRegistry();
275             Image img = registry.get(LEAF);
276             if (img != null) {
277                 gc.drawImage(img, 0, 0, img.getBounds().width, img.getBounds().height, x, y, size, size);
278             }
279         } else {
280             drawLeaf(gc, size, x, y);
281         }
282     }
283 
284     /**
285      * Draw the plus sign.
286      * 
287      * @param gc GC
288      * @param size size
289      * @param x left x
290      * @param y upper y
291      */
292     protected void drawPlus(GC gc, int size, int x, int y) {
293         gc.drawLine(x + _signInsets, y + size / 2, x + size - _signInsets, y + size / 2);
294         gc.drawLine(x + size / 2, y + _signInsets, x + size / 2, y + size - _signInsets);
295         gc.drawRectangle(x, y, size, size);
296     }
297 
298     /**
299      * Draw the minus sign.
300      * 
301      * @param gc GC
302      * @param size size
303      * @param x left x
304      * @param y upper y
305      */
306     protected void drawMinus(GC gc, int size, int x, int y) {
307         gc.drawLine(x + _signInsets, y + size / 2, x + size - _signInsets, y + size / 2);
308         gc.drawRectangle(x, y, size, size);
309     }
310 
311     /**
312      * Draw the leaf symbol.
313      * 
314      * @param gc GC
315      * @param size size
316      * @param x left x
317      * @param y upper y
318      */
319     protected void drawLeaf(GC gc, int size, int x, int y) {
320         Color bg = gc.getBackground();
321         gc.setBackground(Display.getCurrent().getSystemColor(SWT.COLOR_BLACK));
322         gc.fillOval(x + size / 2, y + size / 2, size / 2, size / 2);
323         gc.setBackground(bg);
324     }
325 
326     /**
327      * {@inheritDoc}
328      */
329     public String getToolTipText(TimeBarNode node, Rectangle drawingArea, int x, int y) {
330         if (node != null) {
331             return node.getRowHeader().getLabel();
332         }
333         return null;
334     }
335 
336     /**
337      * {@inheritDoc}
338      */
339     public boolean isInToggleArea(TimeBarViewerInterface tbv, TimeBarNode node, Rectangle drawingArea, int xx, int yy) {
340         int depth = tbv.getHierarchicalModel().getDepth();
341         int offx;
342         if (!_fixedLevelWidth && depth > 0) {
343             offx = (drawingArea.width - _size) / (depth + 1);
344         } else {
345             offx = scaleX(_levelWidth);
346         }
347         int level = node.getLevel();
348 
349         int x = drawingArea.x + offx * level + _size / 2;
350         int y = drawingArea.y + (drawingArea.height - _size) / 2;
351 
352         return x <= xx && xx <= x + _size && y <= yy && yy <= y + _size;
353     }
354 
355     /**
356      * {@inheritDoc}
357      */
358     public boolean isInHierarchySelectionArea(TimeBarViewer tbv, TimeBarNode node, Rectangle drawingArea, int xx, int yy) {
359         int depth = tbv.getHierarchicalModel().getDepth();
360         int offx;
361         if (!_fixedLevelWidth && depth > 0) {
362             offx = (drawingArea.width - _size) / (depth + 1);
363         } else {
364             offx = scaleX(_levelWidth);
365         }
366         int level = node.getLevel();
367 
368         int x = drawingArea.x + offx * level + _size / 2;
369         int y = drawingArea.y + (drawingArea.height - _size) / 2;
370 
371         x += _size + LABELGAP;
372 
373         ILabelProvider labelProvider = getLabelProvider(node.getClass());
374 
375         if (labelProvider != null && (_drawIcons || _drawLabels)) {
376             Rectangle selRect = new Rectangle(x, y, 0, 0);
377             if (_drawIcons) {
378                 Image img = labelProvider.getImage(node);
379                 if (img != null) {
380                     selRect.width += img.getBounds().width;
381                     selRect.height += img.getBounds().height;
382                 }
383             }
384             if (_drawLabels) {
385                 String label = labelProvider.getText(node);
386                 if (label != null) {
387                     selRect.width += drawingArea.width - selRect.x; // selection
388                     // to end of
389                     // hierarchy
390                     // rendering
391                     selRect.height = Math.max(selRect.height, 16); // 16 pixel
392                     // minimium
393                     // height
394                 }
395             }
396             return selRect.contains(xx, yy);
397 
398         }
399         return false;
400 
401     }
402 
403     /**
404      * {@inheritDoc}
405      */
406     public int getPreferredWidth() {
407         return scaleX(_size + LABELGAP);
408     }
409 
410     /**
411      * {@inheritDoc}
412      */
413     public void dispose() {
414         if (_imageRegistry != null) {
415             _imageRegistry.dispose();
416         }
417     }
418 
419     /**
420      * Retrieve whether level indentation is done with a fixed width or variable.
421      * 
422      * @return true if the renderer uses a fixed width for the level indentation
423      */
424     public boolean getFixedLevelWidth() {
425         return _fixedLevelWidth;
426     }
427 
428     /**
429      * Set method of level indentation.
430      * 
431      * @param fixedLevelWidth true for the use of a fixed indent for the levels
432      */
433     public void setFixedLevelWidth(boolean fixedLevelWidth) {
434         _fixedLevelWidth = fixedLevelWidth;
435     }
436 
437     /**
438      * Get the default label provider to use for label texts and icons (TimeBarRow.class).
439      * 
440      * @return Returns the labelProvider.
441      */
442     public ILabelProvider getLabelProvider() {
443         return getLabelProvider(TimeBarRow.class);
444     }
445 
446     /**
447      * Set the default label provider to use for label texts and icons (TimeBarRow.class).
448      * 
449      * @param labelProvider The labelProvider to set.
450      */
451     public void setLabelProvider(ILabelProvider labelProvider) {
452         registerLabelProvider(TimeBarRow.class, labelProvider);
453     }
454 
455     /**
456      * Register a label provider for a class.
457      * 
458      * @param clazz time bar row implementing clas or extendin ginterface
459      * @param labelProvider label provider
460      */
461     public void registerLabelProvider(Class<? extends TimeBarRow> clazz, ILabelProvider labelProvider) {
462         _labelProviderMap.put(clazz, labelProvider);
463     }
464 
465     /**
466      * Set the complete label provider map (internal use).
467      * 
468      * @param map map of the label providers
469      */
470     protected void setLabelProviderMap(Map<Class<? extends TimeBarRow>, ILabelProvider> map) {
471         _labelProviderMap = map;
472     }
473 
474     /**
475      * Retrieve a label provider for a given class. Checks all interfaces and all superclasses.
476      * 
477      * @param clazz class in question
478      * @return label provider or null
479      */
480     protected ILabelProvider getLabelProvider(Class<? extends TimeBarRow> clazz) {
481         ILabelProvider result = null;
482         result = _labelProviderMap.get(clazz);
483         if (result != null) {
484             return result;
485         }
486 
487         // direct interfaces
488         Class<?>[] interfaces = clazz.getInterfaces();
489         for (Class<?> c : interfaces) {
490             result = _labelProviderMap.get(c);
491             if (result != null) {
492                 return result;
493             }
494         }
495 
496         // superclasses
497         Class<?> sc = clazz.getSuperclass();
498 
499         while (sc != null) {
500             result = _labelProviderMap.get(sc);
501             if (result != null) {
502                 return result;
503             }
504             // interfaces of the superclass
505             Class<?>[] scinterfaces = sc.getInterfaces();
506             for (Class<?> c : scinterfaces) {
507                 result = _labelProviderMap.get(c);
508                 if (result != null) {
509                     return result;
510                 }
511             }
512             sc = sc.getSuperclass();
513         }
514 
515         return result;
516     }
517 
518     /**
519      * Retrieve the width for each level (only applicable if fixed level width is used).
520      * 
521      * @return Returns the levelWidth.
522      */
523     public int getLevelWidth() {
524         return _levelWidth;
525     }
526 
527     /**
528      * @param levelWidth The levelWidth to set.
529      */
530     public void setLevelWidth(int levelWidth) {
531         _levelWidth = levelWidth;
532     }
533 
534     /**
535      * @return Returns the drawIcons.
536      */
537     public boolean getDrawIcons() {
538         return _drawIcons;
539     }
540 
541     /**
542      * @param drawIcons The drawIcons to set.
543      */
544     public void setDrawIcons(boolean drawIcons) {
545         this._drawIcons = drawIcons;
546     }
547 
548     /**
549      * @return Returns the drawLabels.
550      */
551     public boolean getDrawLabels() {
552         return _drawLabels;
553     }
554 
555     /**
556      * @param drawLabels The drawLabels to set.
557      */
558     public void setDrawLabels(boolean drawLabels) {
559         this._drawLabels = drawLabels;
560     }
561 
562     /**
563      * {@inheritDoc}
564      */
565     public HierarchyRenderer createPrintRenderer(Printer printer) {
566         DefaultHierarchyRenderer r = new DefaultHierarchyRenderer(printer);
567         r.setDrawIcons(_drawIcons);
568         r.setDrawLabels(_drawLabels);
569         r.setFixedLevelWidth(_fixedLevelWidth);
570         r.setLevelWidth(_levelWidth);
571         r.setLabelProviderMap(_labelProviderMap);
572         r.setRscNames(_plusRscName, _minusRscName, _leafRscName);
573         return r;
574     }
575 
576     /**
577      * Set the paths for images to use a symbols. If set the will be used instead of the generic +/-/o symbols. Please
578      * note that the resources are loaded bay getResourceAsStream whicj can be problematic when using it in a multi
579      * class loader environment like eclipse rcp. Use the setImageDescriptors method instead.
580      * 
581      * @param plusRscPath ressource path for the collapsed symbol
582      * @param minusRscPath ressource path for the expanded symbol
583      * @param leafRscPath ressource path for the leaf symbol
584      */
585     public void setRscNames(String plusRscPath, String minusRscPath, String leafRscPath) {
586         if (_imageRegistry != null) {
587             _imageRegistry.dispose();
588             _imageRegistry = null;
589         }
590         _plusRscName = plusRscPath;
591         _minusRscName = minusRscPath;
592         _leafRscName = leafRscPath;
593     }
594 
595     /**
596      * Set image descriptors for the symbols.
597      * 
598      * @param plus image descriptor for the plus sign
599      * @param minus image descriptor for the minus sign
600      * @param leaf image descriptor for the leaf sign
601      */
602     public void setImageDescriptors(ImageDescriptor plus, ImageDescriptor minus, ImageDescriptor leaf) {
603         if (_imageRegistry != null) {
604             _imageRegistry.dispose();
605         }
606         _imageRegistry = new ImageRegistry();
607         _imageRegistry.put(PLUS, plus);
608         _imageRegistry.put(MINUS, minus);
609         _imageRegistry.put(LEAF, leaf);
610         _plusRscName = "plusRscPath";
611         _minusRscName = "minusRscPath";
612         _leafRscName = "leafRscPath";
613     }
614 
615     /**
616      * Retrieve initialized image registry.
617      * 
618      * @return initialized registry
619      */
620     private ImageRegistry getImageRegistry() {
621         if (_imageRegistry == null) {
622             _imageRegistry = new ImageRegistry();
623             ImageDescriptor imgDesc = new ResourceImageDescriptor(_plusRscName, this.getClass());
624             _imageRegistry.put(PLUS, imgDesc.createImage());
625             imgDesc = new ResourceImageDescriptor(_minusRscName, this.getClass());
626             _imageRegistry.put(MINUS, imgDesc.createImage());
627             imgDesc = new ResourceImageDescriptor(_leafRscName, this.getClass());
628             _imageRegistry.put(LEAF, imgDesc.createImage());
629         }
630         return _imageRegistry;
631     }
632 
633     /**
634      * Retrieve the squae size of the symbols. Images will be scaled.
635      * 
636      * @return size
637      */
638     public int getSize() {
639         return _size;
640     }
641 
642     /**
643      * Set the size of the symbols.
644      * 
645      * @param size size in pixel
646      */
647     public void setSize(int size) {
648         _size = size;
649     }
650 
651     /**
652      * Retrieve whether tree lines shoul dbe drawn.
653      * 
654      * @return true if the tree lines should be drawn
655      */
656     public boolean getDrawTreeLines() {
657         return _drawTreeLines;
658     }
659 
660     /**
661      * Set whether connecting lines in the tree should be drawn.
662      * 
663      * @param drawTreeLines true if tree lines should be drawn
664      */
665     public void setDrawTreeLines(boolean drawTreeLines) {
666         _drawTreeLines = drawTreeLines;
667     }
668 
669 }