View Javadoc

1   /*
2    *  File: TimeBarViewer.java 
3    *  Copyright (c) 2004-2009  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.swing;
21  
22  import java.awt.AlphaComposite;
23  import java.awt.BorderLayout;
24  import java.awt.Color;
25  import java.awt.Component;
26  import java.awt.Cursor;
27  import java.awt.Dimension;
28  import java.awt.Graphics;
29  import java.awt.Graphics2D;
30  import java.awt.Point;
31  import java.awt.Rectangle;
32  import java.awt.event.ComponentEvent;
33  import java.awt.event.ComponentListener;
34  import java.awt.event.MouseEvent;
35  import java.awt.event.MouseListener;
36  import java.awt.event.MouseMotionListener;
37  import java.awt.event.MouseWheelEvent;
38  import java.awt.event.MouseWheelListener;
39  import java.util.ArrayList;
40  import java.util.HashMap;
41  import java.util.List;
42  import java.util.Map;
43  
44  import javax.swing.BoundedRangeModel;
45  import javax.swing.JComponent;
46  import javax.swing.JPanel;
47  import javax.swing.JPopupMenu;
48  import javax.swing.JScrollBar;
49  import javax.swing.SwingUtilities;
50  import javax.swing.event.ChangeEvent;
51  import javax.swing.event.ChangeListener;
52  
53  import de.jaret.util.date.Interval;
54  import de.jaret.util.date.JaretDate;
55  import de.jaret.util.misc.Pair;
56  import de.jaret.util.ui.timebars.TimeBarIntervalFilter;
57  import de.jaret.util.ui.timebars.TimeBarMarker;
58  import de.jaret.util.ui.timebars.TimeBarRowFilter;
59  import de.jaret.util.ui.timebars.TimeBarRowSorter;
60  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
61  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
62  import de.jaret.util.ui.timebars.mod.IntervalModificator;
63  import de.jaret.util.ui.timebars.model.FocussedIntervalListener;
64  import de.jaret.util.ui.timebars.model.HierarchicalTimeBarModel;
65  import de.jaret.util.ui.timebars.model.HierarchicalViewState;
66  import de.jaret.util.ui.timebars.model.IIntervalRelation;
67  import de.jaret.util.ui.timebars.model.ISelectionRectListener;
68  import de.jaret.util.ui.timebars.model.ITimeBarChangeListener;
69  import de.jaret.util.ui.timebars.model.ITimeBarViewState;
70  import de.jaret.util.ui.timebars.model.TBRect;
71  import de.jaret.util.ui.timebars.model.TimeBarModel;
72  import de.jaret.util.ui.timebars.model.TimeBarNode;
73  import de.jaret.util.ui.timebars.model.TimeBarRow;
74  import de.jaret.util.ui.timebars.model.TimeBarRowHeader;
75  import de.jaret.util.ui.timebars.model.TimeBarSelectionModel;
76  import de.jaret.util.ui.timebars.strategy.IOverlapStrategy;
77  import de.jaret.util.ui.timebars.strategy.ITickProvider;
78  import de.jaret.util.ui.timebars.strategy.OverlapInfo;
79  import de.jaret.util.ui.timebars.swing.renderer.DefaultGridRenderer;
80  import de.jaret.util.ui.timebars.swing.renderer.DefaultHeaderRenderer;
81  import de.jaret.util.ui.timebars.swing.renderer.DefaultMarkerRenderer;
82  import de.jaret.util.ui.timebars.swing.renderer.DefaultMiscRenderer;
83  import de.jaret.util.ui.timebars.swing.renderer.DefaultTimeBarRenderer;
84  import de.jaret.util.ui.timebars.swing.renderer.DefaultTimeScaleRenderer;
85  import de.jaret.util.ui.timebars.swing.renderer.GridRenderer;
86  import de.jaret.util.ui.timebars.swing.renderer.HeaderRenderer;
87  import de.jaret.util.ui.timebars.swing.renderer.HierarchyRenderer;
88  import de.jaret.util.ui.timebars.swing.renderer.IGlobalAssistantRenderer;
89  import de.jaret.util.ui.timebars.swing.renderer.IMarkerRenderer;
90  import de.jaret.util.ui.timebars.swing.renderer.IMiscRenderer;
91  import de.jaret.util.ui.timebars.swing.renderer.IRelationRenderer;
92  import de.jaret.util.ui.timebars.swing.renderer.ITitleRenderer;
93  import de.jaret.util.ui.timebars.swing.renderer.TimeBarGapRenderer;
94  import de.jaret.util.ui.timebars.swing.renderer.TimeBarRenderer;
95  import de.jaret.util.ui.timebars.swing.renderer.TimeScaleRenderer;
96  
97  /**
98   * Viewer for a TimeBarModel (Swing version). Displays the intervals using a renderer. Supports sorting and/or filtering
99   * of the rows in the model without affecting the model itself.
100  * <p>
101  * <b>NOTE: The Swing version is not as complete as the swt version!</b>
102  * </p>
103  * <p>
104  * The implementation depends on the <code>TimeBarViewerDelegate</code> for the operations and calculations that is
105  * shared between the Swing and the SWT implementation of the viewer. The delegate is accesible via the method
106  * <code>getDelegate()</code>. It supplies additional functionality not delegated by methods in this class (will be done
107  * in future releases).
108  * </p>
109  * <p>
110  * 
111  * @author Peter Kliem
112  * @version $Id: TimeBarViewer.java 1090 2011-10-04 19:41:10Z kliem $
113  */
114 @SuppressWarnings("serial")
115 public class TimeBarViewer extends JPanel implements TimeBarViewerInterface, ChangeListener, ComponentListener {
116     /** DEBUGGING OPTION: if set to true the actual paint times will be printed to stdout. */
117     private static final boolean SHOWPAINTTIME = false;
118 
119     /** the preferred height. */
120     private static final int PREFHEIGHT = 300;
121     /** the preferred width. */
122     private static final int PREFWIDTH = 500;
123 
124     /**
125      * The delegate is the heart of the joined implementation between Swing and SWT version of the viewer.
126      */
127     protected TimeBarViewerDelegate _delegate;
128 
129     /** renderer used for the stimescale. */
130     protected transient TimeScaleRenderer _timeScaleRenderer = new DefaultTimeScaleRenderer();
131     /** used to cache the time scale component for static use (tooltips). */
132     protected JComponent _timeScaleRendererComponent;
133     /** renderer used for rendering the grid/background. */
134     protected transient GridRenderer _gridRenderer = new DefaultGridRenderer();
135     /** used to cache the grid renderer component. */
136     protected JComponent _gridRendererComponent;
137     /** gap renderer. */
138     protected TimeBarGapRenderer _gapRenderer = null;
139     /** renderer for the row headers. */
140     protected HeaderRenderer _headerRenderer;
141     /** renderer for the hierarchy section. */
142     protected HierarchyRenderer _hierarchyRenderer;
143     /** Renderer for various elements. */
144     protected transient IMiscRenderer _miscRenderer = new DefaultMiscRenderer();
145     /** Renderer used to render the title area. null indicates no renderer. */
146     protected transient ITitleRenderer _titleRenderer = null;
147     /** Relation render. */
148     protected transient IRelationRenderer _relationRenderer = null;
149     /** marker renderer. */
150     protected transient IMarkerRenderer _markerRenderer = new DefaultMarkerRenderer();
151     /** gloab assistant renderer. */
152     protected transient IGlobalAssistantRenderer _globalAssistantRenderer;
153 
154     /** horizontal scrollbar if existing. */
155     protected JScrollBar _xScrollBar;
156 
157     /** vertical scrollbar if existing. */
158     protected JScrollBar _yScrollBar;
159 
160     /** the diagram pane itself. */
161     public Diagram _diagram;
162 
163     /** mapping between interval classes and renderers. */
164     protected Map<Class<? extends Interval>, TimeBarRenderer> _rendererMap = new HashMap<Class<? extends Interval>, TimeBarRenderer>();
165 
166     /** map of registered popup menus for intervals. */
167     protected Map<Class<? extends Interval>, JPopupMenu> _registeredPopupMenues;
168 
169     /** context menu for the body of the viewer. */
170     protected JPopupMenu _bodyContextMenu;
171     /** context menu for the time scale. */
172     protected JPopupMenu _timeScaleContextMenu;
173     /** context menu for the header. */
174     protected JPopupMenu _headerContextMenu;
175     /** context menu for the hierarchy area. */
176     protected JPopupMenu _hierarchyContextMenu;
177     /** context menu for the title area. */
178     protected JPopupMenu _titleContextMenu;
179 
180     /** flag indicating popupTrigger is not set for MouseReleased event. */     
181     protected boolean _requiresPopupTriggerCheck; 
182 
183     /** panel the horizontal scrollbar is placed on. */
184     protected JPanel _horizontalScrollPanel;
185     /** panel the vertical scrollbar is placed on. */
186     protected JPanel _verticalScrollPanel;
187 
188     protected boolean _useTitleRendererComponentInPlace = false;
189 
190     /**
191      * Constructs a timebar viewer.
192      * 
193      * @param model TimeBarModel to be used. The model may be <code>null</code>.
194      * @param suppressXScroll if true the x scrollbar will not be displayed
195      * @param suppressYScroll if true the y scrollbar will not be displayed
196      */
197     public TimeBarViewer(TimeBarModel model, boolean suppressXScroll, boolean suppressYScroll) {
198         _delegate = new TimeBarViewerDelegate(this);
199         // store delegate for access from outside (special cases)
200         putClientProperty("delegate", _delegate);
201 
202         setLayout(new BorderLayout());
203         // horizontal scrollbar
204         _xScrollBar = new JScrollBar(JScrollBar.HORIZONTAL);
205         BoundedRangeModel brModelX = _xScrollBar.getModel();
206         brModelX.addChangeListener(this);
207         _xScrollBar.setUnitIncrement(1);
208         if (!suppressXScroll) {
209             _horizontalScrollPanel = new JPanel(new BorderLayout());
210             add(_horizontalScrollPanel, BorderLayout.SOUTH);
211             _horizontalScrollPanel.add(_xScrollBar, BorderLayout.CENTER);
212         }
213         // vertical scrollbar
214         _yScrollBar = new JScrollBar(JScrollBar.VERTICAL);
215         BoundedRangeModel brModelY = _yScrollBar.getModel();
216         brModelY.addChangeListener(this);
217         _yScrollBar.setUnitIncrement(1);
218         if (!suppressYScroll) {
219             _verticalScrollPanel = new JPanel(new BorderLayout());
220             add(_verticalScrollPanel, BorderLayout.EAST);
221             _verticalScrollPanel.add(_yScrollBar, BorderLayout.CENTER);
222         }
223 
224         // diagram
225         _diagram = new Diagram();
226         _diagram._timeBarViewer = this; // parent reference
227         add(_diagram, BorderLayout.CENTER);
228         // resize listener
229         this.addComponentListener(this);
230         // set the model if given
231         if (model != null) {
232             _delegate.setModel(model);
233         }
234         // default header renderer
235         setHeaderRenderer(new DefaultHeaderRenderer());
236 
237         // register the default renderer for intervals
238         registerTimeBarRenderer(Interval.class, new DefaultTimeBarRenderer());
239 
240         // set the timescale renderer as the tick provider for the defaultgrid renderer
241         // heavy knowledge of the default setup here ...
242         _gridRenderer.setTickProvider((DefaultTimeScaleRenderer) _timeScaleRenderer);
243 
244         // check macos
245         String osname = System.getProperty("os.name");
246         // check if we need to manually check for popup trigger
247         if (osname != null) {
248             _requiresPopupTriggerCheck = osname.startsWith("Mac") || osname.startsWith("Linux");
249         }
250 
251     }
252 
253     /**
254      * Constructs a timebarviewer with both y and x scrollbars.
255      * 
256      * @param model timebarmodel to be displayed. The model may be <code>null</code>.
257      */
258     public TimeBarViewer(TimeBarModel model) {
259         this(model, false, false);
260     }
261 
262     /**
263      * Constructs a timebarviewer without a model.
264      * 
265      */
266     public TimeBarViewer() {
267         this(null);
268     }
269 
270     /**
271      * Retrieve the x scroll bar.
272      * 
273      * @return the x scroll bar or <code>null</code> if the scroll bar has been suppressed
274      */
275     public JScrollBar getXScrollBar() {
276         return _xScrollBar;
277     }
278 
279     /**
280      * Retrieve the y scroll bar.
281      * 
282      * @return the y scroll bar or <code>null</code> if the scroll bar has been suppressed
283      */
284     public JScrollBar getYScrollBar() {
285         return _yScrollBar;
286     }
287 
288     /**
289      * {@inheritDoc}
290      */
291     public synchronized void addMouseListener(MouseListener l) {
292         // TODO remove, handling of the other possible listeners
293         super.addMouseListener(l);
294         _diagram.addMouseListener(l);
295     }
296 
297     /**
298      * {@inheritDoc}
299      */
300     public void setModel(TimeBarModel model) {
301         _delegate.setModel(model);
302     }
303 
304     /**
305      * {@inheritDoc}
306      */
307     public TimeBarModel getModel() {
308         return _delegate.getModel();
309     }
310 
311     /**
312      * {@inheritDoc}
313      */
314     public void setRowFilter(TimeBarRowFilter rowFilter) {
315         _delegate.setRowFilter(rowFilter);
316     }
317 
318     /**
319      * {@inheritDoc}
320      */
321     public TimeBarRowFilter getRowFilter() {
322         return _delegate.getRowFilter();
323     }
324 
325     /**
326      * {@inheritDoc}
327      */
328     public void setRowSorter(TimeBarRowSorter rowSorter) {
329         _delegate.setRowSorter(rowSorter);
330     }
331 
332     /**
333      * {@inheritDoc}
334      */
335     public TimeBarRowSorter getRowSorter() {
336         return _delegate.getRowSorter();
337     }
338 
339     /**
340      * {@inheritDoc}
341      */
342     public void setIntervalFilter(TimeBarIntervalFilter intervalFilter) {
343         _delegate.setIntervalFilter(intervalFilter);
344     }
345 
346     /**
347      * Set the default renderer to be used for rendering the timebars.
348      * 
349      * @param renderer the renderer to be used if no other registered renderer is appropriate
350      */
351     public void setTimeBarRenderer(TimeBarRenderer renderer) {
352         registerTimeBarRenderer(Interval.class, renderer);
353     }
354 
355     /**
356      * Retrieve the default renderer currently used for rendering intervals (regsitered for Interval.class).
357      * 
358      * @return the renderer
359      */
360     public TimeBarRenderer getTimeBarRenderer() {
361         return _rendererMap.get(Interval.class);
362     }
363 
364     /**
365      * Register a renderer for an interval class or interface. The renderer registered for Interval.class is the default
366      * renderer if no other renderer can be found (obviously).
367      * 
368      * @param intervalClass class of the intervals
369      * @param renderer renderer for the given class
370      */
371     public void registerTimeBarRenderer(Class<? extends Interval> intervalClass, TimeBarRenderer renderer) {
372         _rendererMap.put(intervalClass, renderer);
373         repaint();
374     }
375 
376     /**
377      * Retrieve the complete renderer map. This method's purpose is mainly to feed the TimeBarPrinter.
378      * 
379      * @return the renderer map
380      */
381     public Map<Class<? extends Interval>, TimeBarRenderer> getRendererMapping() {
382         return _rendererMap;
383     }
384 
385     /**
386      * Retrieve a renderer for a given class. Checks all interfaces and all superclasses.
387      * 
388      * @param clazz class in question
389      * @return renderer or null
390      */
391     protected TimeBarRenderer getRenderer(Class<? extends Interval> clazz) {
392         TimeBarRenderer result = null;
393         result = _rendererMap.get(clazz);
394         if (result != null) {
395             return result;
396         }
397 
398         // direct interfaces
399         Class<?>[] interfaces = clazz.getInterfaces();
400         for (Class<?> c : interfaces) {
401             result = _rendererMap.get(c);
402             if (result != null) {
403                 return result;
404             }
405         }
406 
407         // superclasses
408         Class<?> sc = clazz.getSuperclass();
409 
410         while (sc != null) {
411             result = _rendererMap.get(sc);
412             if (result != null) {
413                 return result;
414             }
415             // interfaces of the superclass
416             Class<?>[] scinterfaces = sc.getInterfaces();
417             for (Class<?> c : scinterfaces) {
418                 result = _rendererMap.get(c);
419                 if (result != null) {
420                     return result;
421                 }
422             }
423             sc = sc.getSuperclass();
424         }
425 
426         return result;
427     }
428 
429     /**
430      * Set the renderer to be used for rendering the row headers.
431      * 
432      * @param renderer HeaderRenderer to be used or <code>null</code> indicating headers should not be rendered
433      */
434     public void setHeaderRenderer(HeaderRenderer renderer) {
435         _headerRenderer = renderer;
436         if (_headerRenderer != null) {
437             _delegate.setYAxisWidth(renderer.getWidth());
438         } else {
439             _delegate.setYAxisWidth(0);
440         }
441         _diagram.safeRepaint();
442     }
443 
444     /**
445      * Set the renderer to be used for rendering the hierarchy area.
446      * 
447      * @param renderer HierarchyRenderer to be used or <code>null</code> indicating headers should not be rendered
448      */
449     public void setHierarchyRenderer(HierarchyRenderer renderer) {
450         _hierarchyRenderer = renderer;
451         if (_hierarchyRenderer != null) {
452             _delegate.setHierarchyWidth(renderer.getWidth());
453         } else {
454             _delegate.setHierarchyWidth(0);
455         }
456         _diagram.safeRepaint();
457     }
458 
459     /**
460      * Sets the scale of the x axis as pixel per second, thus a value of 1000.0 / (24.0 * 60 * 60) will result in
461      * displaying one day over 1000 pixel. The property is a bound property and can be listened to by a
462      * PropertyChangeListener
463      * 
464      * @param pixelPerSecond pixel per second
465      */
466     public void setPixelPerSecond(double pixelPerSecond) {
467         _delegate.setPixelPerSecond(pixelPerSecond);
468     }
469 
470     /**
471      * {@inheritDoc}
472      */
473     public double getPixelPerSecond() {
474         return _delegate.getPixelPerSecond();
475     }
476 
477     /**
478      * {@inheritDoc}
479      */
480     public void setRowHeight(int rowHeight) {
481         _delegate.setRowHeight(rowHeight);
482     }
483 
484     /**
485      * {@inheritDoc}
486      */
487     public int getRowHeight() {
488         return _delegate.getTimeBarViewState().getDefaultRowHeight();
489     }
490 
491     /**
492      * {@inheritDoc}
493      */
494     public void updateXScrollBar(int max, int pos, int secondsDisplayed) {
495         BoundedRangeModel brModel = _xScrollBar.getModel();
496         if (brModel != null) {
497             // remove changelistener to avoid cycling
498             brModel.removeChangeListener(this);
499             brModel.setMinimum(0);
500             brModel.setMaximum(max);
501             brModel.setExtent(secondsDisplayed);
502             brModel.setValue(pos);
503             brModel.addChangeListener(this);
504             _xScrollBar.setBlockIncrement(secondsDisplayed  * 9 / 10); // TOOD check configurabilty
505             _xScrollBar.setUnitIncrement(secondsDisplayed / 10);
506         }
507     }
508 
509     /**
510      * {@inheritDoc}
511      */
512     public void updateYScrollBar(int max, int pos, int rowsDisplayed) {
513         BoundedRangeModel brModel = _yScrollBar.getModel();
514         // remove changelistener to avoid cycling
515         brModel.removeChangeListener(this);
516         brModel.setMinimum(0);
517         brModel.setMaximum(max);
518         brModel.setExtent(rowsDisplayed);
519         brModel.setValue(pos);
520         brModel.addChangeListener(this);
521         _yScrollBar.setBlockIncrement(rowsDisplayed * 9 / 10); // TODO check configurabilty
522         _yScrollBar.setUnitIncrement(getRowHeight() * 3);
523 //        _yScrollBar.setBlockIncrement(rowsDisplayed / 10 + 1);
524 //        _yScrollBar.setUnitIncrement(rowsDisplayed / 20 + 1);
525     }
526 
527     /**
528      * Invoked when one of the scrollbars was moved.
529      * 
530      * @param e event
531      * @see javax.swing.event.ChangeListener#stateChanged(javax.swing.event.ChangeEvent)
532      */
533     public void stateChanged(ChangeEvent e) {
534         if (e.getSource() == _xScrollBar.getModel()) {
535             BoundedRangeModel brModel = _xScrollBar.getModel();
536             int value = brModel.getValue();
537             _delegate.handleHorizontalScroll(value, true);
538         } else if (e.getSource() == _yScrollBar.getModel()) {
539             BoundedRangeModel brModel = _yScrollBar.getModel();
540             int value = brModel.getValue();
541             _delegate.handleVerticalScroll(value, true);
542             // _diagram.safeRepaint();
543         } else {
544             throw new RuntimeException("Unknown sender");
545         }
546     }
547 
548     // *** ComponentListener for registering resizes
549     /**
550      * {@inheritDoc}
551      */
552     public void componentHidden(ComponentEvent e) {
553     }
554 
555     /**
556      * {@inheritDoc}
557      */
558     public void componentMoved(ComponentEvent e) {
559     }
560 
561     /**
562      * {@inheritDoc}
563      */
564     public void componentResized(ComponentEvent e) {
565         // _delegate.updateScrollBars();
566         _delegate.componentResized();
567     }
568 
569     /**
570      * {@inheritDoc}
571      */
572     public void componentShown(ComponentEvent e) {
573     }
574 
575     // *** End of ComponentListener
576 
577     /**
578      * Retrieve the JComponent for a given row and interval.
579      * 
580      * @param row row
581      * @param interval interval
582      * @return JComponent set up for rendering or other tasks (bounds are set)
583      */
584     protected JComponent getIntervalComponent(TimeBarRow row, Interval interval) {
585         Rectangle intervalRect = _delegate.getIntervalBounds(row, interval);
586 
587         return getIntervalComponent(interval, intervalRect);
588     }
589 
590     /**
591      * Retrieve the JComponent for a given row and interval.
592      * 
593      * @param interval interval
594      * @param intervalRect rectangle denoting the area of the interval
595      * @return JComponent set up for rendering or other tasks (bounds are set)
596      */
597     protected JComponent getIntervalComponent(Interval interval, Rectangle intervalRect) {
598         boolean overlapping = false;
599         OverlapInfo oi = null;
600         TimeBarRow row = _delegate.getModel().getRowForInterval(interval);
601         if (!_delegate.getTimeBarViewState().getDrawOverlapping(row)) {
602             oi = _delegate.getOverlapStrategy().getOverlapInfo(row, interval);
603             if (oi != null && (oi.maxOverlapping > 0 || _delegate.getUseUniformHeight())) {
604                 overlapping = true;
605             }
606         }
607         return getIntervalComponent(interval, intervalRect, overlapping);
608     }
609 
610     /**
611      * Retrieve the JComponent for an interval.
612      * 
613      * @param interval the interval
614      * @param intervalRect rectangle marking the drawing bounds
615      * @param overlapping if the interval is overlapping
616      * @return the configured JComponent
617      */
618     protected JComponent getIntervalComponent(Interval interval, Rectangle intervalRect, boolean overlapping) {
619         TimeBarRenderer renderer = getRenderer(interval.getClass());
620         if (renderer == null) {
621             throw new RuntimeException("no suitable renderer registered");
622         }
623 
624         JComponent component = renderer.getTimeBarRendererComponent(this, interval, false, overlapping);
625         component.setBounds(intervalRect);
626         return component;
627     }
628 
629     /**
630      * The component drawing the viewer itself.
631      * 
632      * @author Peter Kliem
633      * @version $Id: TimeBarViewer.java 1090 2011-10-04 19:41:10Z kliem $
634      */
635     private class Diagram extends JComponent implements MouseListener, MouseMotionListener, MouseWheelListener {
636         /** surrounding timebar viewer. */
637         protected TimeBarViewer _timeBarViewer;
638 
639         /**
640          * Default constructor.
641          */
642         public Diagram() {
643             // setDoubleBuffered(false);
644             // setOpaque(false);
645             setBackground(Color.WHITE);
646             setToolTipText(""); // set a tooltip since otherwise swing wont ask
647             addMouseListener(this);
648             addMouseMotionListener(this);
649             addMouseWheelListener(this);
650         }
651 
652         /**
653          * {@inheritDoc}
654          */
655         public String getToolTipText(MouseEvent event) {
656             int x = event.getX();
657             int y = event.getY();
658             return _delegate.getToolTipText(x, y);
659         }
660 
661         /**
662          * Thread safe repaint (mybe the repaint method is thread safe by itself ... won't hurt)
663          */
664         public void safeRepaint() {
665             Runnable doRepaint = new Runnable() {
666                 public void run() {
667                     repaint();
668                 }
669             };
670             SwingUtilities.invokeLater(doRepaint);
671         }
672 
673         /**
674          * {@inheritDoc} Paint the component. The clipping bounds of the graphics are used to reduce painting to the
675          * region that needs update.
676          */
677         public void paintComponent(Graphics g) {
678             long time = System.currentTimeMillis();
679             long nanoTime = System.nanoTime();
680 
681             _delegate.preparePaint(getWidth(), getHeight()); // prepare the geometry
682 
683             g.setColor(Color.WHITE);
684             g.fillRect(g.getClipBounds().x, g.getClipBounds().y, g.getClipBounds().width, g.getClipBounds().height);
685             g.setColor(Color.BLACK);
686 
687             // draw x axis if enabled
688             if (_delegate.getTimeScalePosition() != TIMESCALE_POSITION_NONE) {
689                 drawXAxis(g);
690             }
691             drawGrid(g); // draw the grid if enabled
692 
693             // kick in the global assistant renderer
694             if (_globalAssistantRenderer != null) {
695                 _globalAssistantRenderer.doRenderingBeforeIntervals(_delegate, g);
696             }
697 
698             // draw rows (including gaps)
699             drawRows(g);
700             // draw markers
701             drawMarkers(g);
702 
703             // draw selection rectangle
704             Rectangle clipSave = g.getClipBounds();
705             g.setClip(clipSave.intersection(_delegate.getDiagramRect()));
706             drawSelectionRect(g);
707             g.setClip(clipSave);
708 
709             // draw region rect
710             clipSave = g.getClipBounds();
711             g.setClip(clipSave.intersection(_delegate.getDiagramRect()));
712             _miscRenderer.renderRegionRect(g, this._timeBarViewer, _delegate);
713             g.setClip(clipSave);
714 
715             // draw the title if a title renderer has been set
716             if (_titleRenderer != null) {
717                 JComponent titleComponent = _titleRenderer.getTitleRendererComponent(this._timeBarViewer);
718                 if (!_useTitleRendererComponentInPlace) {
719                     titleComponent.setBounds(_delegate.getTitleRect());
720                     titleComponent.paint(g);
721                 } else {
722                     if (titleComponent.getParent() == null) {
723                         add(titleComponent);
724                     }
725                     titleComponent.setBounds(_delegate.getTitleRect());
726                     titleComponent.doLayout();
727                     // no double painting for the title renderer
728                     //titleComponent.paintAll(g); 
729                 }
730             }
731 
732             // kick in the global assistant renderer
733             if (_globalAssistantRenderer != null) {
734                 _globalAssistantRenderer.doRenderingLast(_delegate, g);
735             }
736 
737             // primitive debug for monitoring paint time
738             if (SHOWPAINTTIME) {
739                 System.out.println(_delegate.getName() + " : paintTime " + (System.currentTimeMillis() - time) + " ms "
740                         + (System.nanoTime() - nanoTime) + " ns");
741             }
742         }
743 
744         /**
745          * Draws the selection rectangle if present.
746          * 
747          * @param g graphics
748          */
749         private void drawSelectionRect(Graphics g) {
750             if (_delegate.getSelectionRect() != null) {
751                 // normalize and remember
752                 _delegate.setLastSelRect(normalizeRectangle(_delegate.getSelectionRect()));
753                 Rectangle lastSelRect = _delegate.getLastSelRect();
754                 _miscRenderer.renderSelectionRect(g, this._timeBarViewer, lastSelRect);
755             }
756 
757         }
758 
759         /**
760          * Normalize a rectangle.
761          * 
762          * @param rect rectangle to normalize
763          * @return normalized (pos width nd heigth) rectangle
764          */
765         private Rectangle normalizeRectangle(Rectangle rect) {
766             int x = Math.min(rect.x, rect.x + rect.width);
767             int y = Math.min(rect.y, rect.y + rect.height);
768             int width = Math.abs(rect.width);
769             int height = Math.abs(rect.height);
770             return new Rectangle(x, y, width, height);
771         }
772 
773         /**
774          * Draws all markers for the diagram. If a marker is not currently displayed it will not be painted.
775          * 
776          * @param g Graphics to use
777          */
778         private void drawMarkers(Graphics g) {
779             if (_delegate.getMarkers() != null) {
780                 for (TimeBarMarker marker : _delegate.getMarkers()) {
781                     if (_delegate.isDisplayed(marker.getDate())) {
782                         drawMarker(g, marker);
783                     }
784                 }
785             }
786         }
787 
788         /**
789          * Draw a single marker.
790          * 
791          * @param g Graphics to be used
792          * @param marker marker to draw
793          */
794         private void drawMarker(Graphics g, TimeBarMarker marker) {
795             if (_markerRenderer != null) {
796                 int x = _delegate.xForDate(marker.getDate());
797                 boolean dragged = _delegate.getDraggedMarker() == marker;
798                 _markerRenderer.renderMarker(_delegate, g, marker, x, dragged);
799             }
800         }
801 
802         /**
803          * Draws the x axis using the time scale renderer.
804          * 
805          * @param g Graphics context to use
806          */
807         private void drawXAxis(Graphics g) {
808             if (_timeScaleRenderer != null) {
809                 _timeScaleRendererComponent = _timeScaleRenderer.getRendererComponent(_timeBarViewer, _delegate
810 
811                 .getTimeScalePosition() == TimeBarViewerInterface.TIMESCALE_POSITION_TOP);
812                 _timeScaleRendererComponent.setBounds(_delegate.getXAxisRect());
813                 Graphics gg = g.create(_delegate.getXAxisRect().x, _delegate.getXAxisRect().y,
814                         _delegate.getXAxisRect().width, _delegate.getXAxisRect().height);
815                 _timeScaleRendererComponent.paint(gg);
816                 gg.dispose();
817             }
818         }
819 
820         /**
821          * Draws the grid/background using the gridRenderer.
822          * 
823          * @param g graphics
824          */
825         private void drawGrid(Graphics g) {
826             if (_gridRenderer != null) {
827                 _gridRendererComponent = _gridRenderer.getRendererComponent(_timeBarViewer);
828                 _gridRendererComponent.setBounds(_delegate.getDiagramRect());
829                 Graphics gg = g.create(_delegate.getDiagramRect().x, _delegate.getDiagramRect().y,
830                         _delegate.getDiagramRect().width, _delegate.getDiagramRect().height);
831                 _gridRendererComponent.paint(gg);
832                 gg.dispose();
833             }
834         }
835 
836         /**
837          * Draw all rows. Rows outside the clipping bounds of the graphics context are not painted.
838          * 
839          * @param g Graphics to use
840          */
841         private void drawRows(Graphics g) {
842             if (_delegate.getOrientation() == Orientation.HORIZONTAL) {
843                 drawRowsHorizontal(g);
844             } else {
845                 drawRowsVertical(g);
846             }
847             // relation rendering
848             if (_relationRenderer != null) {
849                 _relationRenderer.renderRelations(_delegate, g);
850             }
851             drawGhostIntervals(g);
852         }
853 
854         /**
855          * Draw rows for horizontal orientation.
856          * 
857          * @param g Graphics to use
858          */
859         private void drawRowsHorizontal(Graphics g) {
860             g.setColor(Color.BLACK);
861 
862             // set the clipping to include only the heigth of the diagram rect
863             Rectangle clipSave = g.getClipBounds();
864             g.setClip(0, _delegate.getDiagramRect().y, getWidth(), _delegate.getDiagramRect().height);
865 
866             // separating line to the header
867             // MAYBE color configurable
868             g.drawLine(_delegate.getDiagramRect().x - 1, 0, _delegate.getDiagramRect().x - 1, getHeight());
869             g.drawLine(_delegate.getHierarchyRect().x + _delegate.getHierarchyWidth() - 1, 0,
870                     _delegate.getHierarchyRect().x + _delegate.getHierarchyWidth() - 1, getHeight());
871 
872             int upperYBound = _delegate.getDiagramRect().y;
873             int lowerYBound = upperYBound + _delegate.getDiagramRect().height;
874             if (g.getClipBounds() != null) {
875                 upperYBound = g.getClipBounds().y;
876                 lowerYBound = upperYBound + g.getClipBounds().height;
877             }
878             for (int r = _delegate.getFirstRow(); r <= _delegate.getFirstRow() + _delegate.getRowsDisplayed()
879                     && r < _delegate.getRowCount(); r++) {
880                 TimeBarRow row = _delegate.getRow(r);
881                 int rowHeight = _delegate.getTimeBarViewState().getRowHeight(row);
882                 // int y = (r - _delegate.getFirstRow()) * rowHeight + _delegate.getDiagramRect().y
883                 // - _delegate.getFirstRowOffset();
884                 int y = _delegate.yForRow(row);
885                 if (y + rowHeight < 0) {
886                     // Bypass rows with the bottom pixel above the drawing area
887                     continue;
888                 }
889 
890                 // row is drawn if either the beginning or the end is inside the
891                 // clipping rect
892                 // or if the upperBound is inside the row rect (clipping rect is
893                 // inside the row rect
894                 if ((y >= upperYBound && y <= lowerYBound)
895                         || (y + rowHeight >= upperYBound && y + rowHeight <= lowerYBound)
896                         || (upperYBound > y && upperYBound < y + rowHeight)) {
897                     drawRow(g, y, rowHeight, _delegate.getRow(r),
898                             _delegate.getSelectionModel().isSelected(_delegate.getRow(r)));
899                     // draw gaps if a renderer is set
900                     if (_gapRenderer != null) {
901                         drawRowGaps(g, y, rowHeight, _delegate.getRow(r),
902                                 _delegate.getSelectionModel().isSelected(_delegate.getRow(r)));
903                     }
904                 }
905                 if (y + rowHeight > lowerYBound) {
906                     // Omit all further checks if the row would be drawn off the bottom of the screen
907                     break;
908                 }
909             }
910             g.setClip(clipSave);
911         }
912 
913         /**
914          * Draw rows for vertical orientation.
915          * 
916          * @param g Graphics to use
917          */
918         private void drawRowsVertical(Graphics g) {
919             g.setColor(Color.BLACK);
920 
921             // set the clipping to include only the heigth of the diagram rect
922             Rectangle clipSave = g.getClipBounds();
923             g.setClip(0, _delegate.getDiagramRect().y, getWidth(), _delegate.getDiagramRect().height);
924 
925             // separating line to the header
926             // MAYBE coloro configurable
927             g.drawLine(0, _delegate.getDiagramRect().y - 1, getWidth(), _delegate.getDiagramRect().y - 1);
928             g.drawLine(0, _delegate.getHierarchyRect().y + _delegate.getHierarchyWidth() - 1, getWidth(),
929                     _delegate.getHierarchyRect().y + _delegate.getHierarchyWidth() - 1);
930 
931             int leftXBound = _delegate.getDiagramRect().x;
932             int rightXBound = leftXBound + _delegate.getDiagramRect().width;
933             if (g.getClipBounds() != null) {
934                 leftXBound = g.getClipBounds().x;
935                 rightXBound = leftXBound + g.getClipBounds().width;
936             }
937 
938             for (int r = _delegate.getFirstRow(); r <= _delegate.getFirstRow() + _delegate.getRowsDisplayed()
939                     && r < _delegate.getRowCount(); r++) {
940                 TimeBarRow row = _delegate.getRow(r);
941                 int rowWidth = _delegate.getTimeBarViewState().getRowHeight(row);
942                 // int x = (r - _delegate.getFirstRow()) * rowWidth + _delegate.getDiagramRect().x
943                 // - _delegate.getFirstRowOffset();
944 
945                 int x = _delegate.yForRow(row);
946                 if (x + rowWidth < 0) {
947                     // Bypass columns with the right pixel to the left of the drawing area
948                     continue;
949                 }
950 
951                 // row is drawn if either the beginning or the end is inside the
952                 // clipping rect
953                 // or if the upperBound is inside the row rect (clipping rect is
954                 // inside the row rect
955                 if ((x >= leftXBound && x <= rightXBound)
956                         || (x + rowWidth >= leftXBound && x + rowWidth <= rightXBound)
957                         || (leftXBound > x && leftXBound < x + rowWidth)) {
958                     drawRowVertical(g, x, rowWidth, _delegate.getRow(r),
959                             _delegate.getSelectionModel().isSelected(_delegate.getRow(r)));
960                     // draw gaps if a renderer is set
961                     if (_gapRenderer != null) {
962                         drawRowGapsVertical(g, x, rowWidth, _delegate.getRow(r), _delegate.getSelectionModel()
963                                 .isSelected(_delegate.getRow(r)));
964                     }
965                 }
966                 if (x + rowWidth > rightXBound) {
967                     // Omit all further checks if the column would be drawn off the right of the screen
968                     break;
969                 }
970             }
971             g.setClip(clipSave);
972         }
973 
974         /**
975          * Draw a single row (horizontal).
976          * 
977          * @param g graphics
978          * @param y starting pos y
979          * @param height height
980          * @param row row to draw
981          * @param selected true if the row is selected
982          */
983         private void drawRow(Graphics g, int y, int height, TimeBarRow row, boolean selected) {
984             // draw the row header
985             drawRowHeader(g, y, height, row.getRowHeader(), selected);
986             // draw the hierarchy area
987             drawHierarchy(g, y, height, row, selected);
988             // draw a line at the bottom of the row if enabled
989             Rectangle diagramRect = _delegate.getDiagramRect();
990             if (_delegate.getDrawRowGrid()) {
991                 _miscRenderer.drawRowGridLine(g, diagramRect.x, y + height - 1, diagramRect.x + diagramRect.width, y
992                         + height - 1);
993             }
994 
995             // draw a background if selected or highlighted
996             // highlighting is at higher priority than selection
997             if (selected || _delegate.getHighlightedRow() == row) {
998                 boolean highlighted = _delegate.getHighlightedRow() == row;
999                 int markerHeight = height;
1000                 // calculate height for clipping
1001                 if (y + markerHeight > diagramRect.y + diagramRect.height) {
1002                     markerHeight = markerHeight - (y + markerHeight - (diagramRect.y + diagramRect.height));
1003                 }
1004                 _miscRenderer.drawRowBackground(g, diagramRect.x, y,
1005                         getWidth() - _delegate.getYAxisWidth() - _delegate.getHierarchyWidth(), markerHeight, selected,
1006                         highlighted);
1007 
1008             }
1009             // use the clip bounds (if given) to shorten the region to be
1010             // painted
1011             JaretDate start = _delegate.getStartDate();
1012             JaretDate end = _delegate.getEndDate();
1013             if (g.getClipBounds() != null) {
1014                 start = _delegate.dateForCoord(g.getClipBounds().x);
1015                 end = _delegate.dateForCoord(g.getClipBounds().x + g.getClipBounds().width);
1016             }
1017             // List intervals = row.getIntervals(_startDate, _endDate);
1018             List<Interval> intervals = row.getIntervals(start, end);
1019             for (Interval i : intervals) {
1020                 // apply filter on intervals if set
1021                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(i)) {
1022                     if (_delegate.getTimeBarViewState().getDrawOverlapping(row)) {
1023                         drawInterval(g, y, height, i, _delegate.getSelectionModel().isSelected(i), null, row);
1024                     } else {
1025                         drawInterval(g, y, height, i, _delegate.getSelectionModel().isSelected(i), _delegate
1026                                 .getOverlapStrategy().getOverlapInfo(row, i), row);
1027                     }
1028                 }
1029             }
1030         }
1031 
1032         /**
1033          * Draw a single row (vertical).
1034          * 
1035          * @param g graphics
1036          * @param x starting pos x
1037          * @param width width
1038          * @param row row to draw
1039          * @param selected true if the row is selected
1040          */
1041         private void drawRowVertical(Graphics g, int x, int width, TimeBarRow row, boolean selected) {
1042             // draw the row header
1043             drawRowHeaderVertical(g, x, width, row.getRowHeader(), selected);
1044             // draw the hierarchy area
1045             drawHierarchyVertical(g, x, width, row, selected);
1046             // draw a line at the bottom of the row if enabled
1047             Rectangle diagramRect = _delegate.getDiagramRect();
1048             if (_delegate.getDrawRowGrid()) {
1049                 _miscRenderer.drawRowGridLine(g, x + width - 1, diagramRect.y, x + width - 1, diagramRect.y
1050                         + diagramRect.height);
1051             }
1052 
1053             // draw a background if selected or highlighted
1054             // highlighting is at higher priority than selection
1055             if (selected || _delegate.getHighlightedRow() == row) {
1056                 boolean highlighted = _delegate.getHighlightedRow() == row;
1057                 int markerWidth = width;
1058                 // calculate width for clipping
1059                 if (x + markerWidth > diagramRect.x + diagramRect.width) {
1060                     markerWidth = markerWidth - (x + markerWidth - (diagramRect.x + diagramRect.width));
1061                 }
1062                 _miscRenderer.drawRowBackground(g, x, diagramRect.y, markerWidth,
1063                         getHeight() - _delegate.getYAxisWidth() - _delegate.getHierarchyWidth(), selected, highlighted);
1064             }
1065             // use the clip bounds (if given) to shorten the region to be
1066             // painted
1067             JaretDate start = _delegate.getStartDate();
1068             JaretDate end = _delegate.getEndDate();
1069             if (g.getClipBounds() != null) {
1070                 start = _delegate.dateForCoord(g.getClipBounds().y);
1071                 end = _delegate.dateForCoord(g.getClipBounds().y + g.getClipBounds().height);
1072             }
1073             List<Interval> intervals = row.getIntervals(start, end);
1074             for (Interval i : intervals) {
1075                 // apply filter on intervals if set
1076                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(i)) {
1077                     if (_delegate.getTimeBarViewState().getDrawOverlapping(row)) {
1078                         drawIntervalVertical(g, x, width, i, _delegate.getSelectionModel().isSelected(i), null, row);
1079                     } else {
1080                         drawIntervalVertical(g, x, width, i, _delegate.getSelectionModel().isSelected(i), _delegate
1081                                 .getOverlapStrategy().getOverlapInfo(row, i), row);
1082                     }
1083                 }
1084             }
1085         }
1086 
1087         /**
1088          * Draws the gaps beetwenn intervals (horizontal). The selection of the bordering interval sis done inside this
1089          * method. TODO maybe move the selection routine out TODO call for edges i.e. one interval is null
1090          * 
1091          * @param g Graphics for painting
1092          * @param y y coordinate
1093          * @param height rowHeight
1094          * @param row TimeBarRow to be painted
1095          * @param selected selection status of the row (not used)
1096          */
1097         private void drawRowGaps(Graphics g, int y, int height, TimeBarRow row, boolean selected) {
1098             // use the clip bounds (if given) to shorten the region to be
1099             // painted
1100             JaretDate start = _delegate.getStartDate();
1101             JaretDate end = _delegate.getEndDate();
1102             if (g.getClipBounds() != null) {
1103                 start = _delegate.dateForCoord(g.getClipBounds().x);
1104                 end = _delegate.dateForCoord(g.getClipBounds().x + g.getClipBounds().width);
1105             }
1106             // for the gaps we need a minimum of two intervals
1107             // those has to be in the displayed intervals, so we apply the
1108             // filter first (if set) and do the selection
1109             // ourself. The alogorithm is highly dependet on the ordering of the
1110             // list! This should be guaranteed by the model
1111             List<Interval> intervals = new ArrayList<Interval>();
1112             Interval firstInterval = null;
1113             for (Interval interval : row.getIntervals()) {
1114                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(interval)) {
1115                     if (interval.getEnd().compareTo(start) < 0) {
1116                         // before the starting date: remember the nearest
1117                         // interval
1118                         if (firstInterval == null
1119                                 || start.diffSeconds(interval.getEnd()) < start.diffSeconds(firstInterval.getEnd())) {
1120                             firstInterval = interval;
1121                         }
1122                     } else if (interval.contains(start)) {
1123                         // direct hit
1124                         firstInterval = interval;
1125                     } else {
1126                         // in or after the starting date: copy the intervals
1127                         // until we have one behind the end date or
1128                         // a direct hit
1129                         // First of all, add the firstInterval to the resulting
1130                         // list once
1131                         if (firstInterval != null) {
1132                             intervals.add(firstInterval);
1133                             firstInterval = null; // we don't need the
1134                             // reference
1135                             // and by setting it to null
1136                             // we do not add it twice
1137                         }
1138                         if (interval.contains(end)) {
1139                             // direct hit
1140                             intervals.add(interval);
1141                             break; // finished
1142                         }
1143                         if (interval.getBegin().compareTo(end) > 0) {
1144                             intervals.add(interval);
1145                             break; // found an interval beginning behind the
1146                             // end
1147                             // date
1148                         } else {
1149                             // all between: add
1150                             intervals.add(interval);
1151                         }
1152                     }
1153                 }
1154             }
1155             Interval lastInterval = null;
1156             for (Interval i : intervals) {
1157                 // draw gap if there is lastInterval
1158                 if (lastInterval != null) {
1159                     drawGap(g, row, y, height, lastInterval, i);
1160                 }
1161                 // remember last interval for next turn
1162                 lastInterval = i;
1163             }
1164         }
1165 
1166         /**
1167          * Draws the gaps beetwenn intervals (horizontal). The selection of the bordering interval sis done inside this
1168          * method. TODO maybe move the selection routine out TODO call for edges i.e. one interval is null
1169          * 
1170          * @param g Graphics for painting
1171          * @param x x coordinate
1172          * @param width rowHeight
1173          * @param row TimeBarRow to be painted
1174          * @param selected selection status of the row (not used)
1175          */
1176         private void drawRowGapsVertical(Graphics g, int x, int width, TimeBarRow row, boolean selected) {
1177             // use the clip bounds (if given) to shorten the region to be
1178             // painted
1179             JaretDate start = _delegate.getStartDate();
1180             JaretDate end = _delegate.getEndDate();
1181             if (g.getClipBounds() != null) {
1182                 start = _delegate.dateForCoord(g.getClipBounds().y);
1183                 end = _delegate.dateForCoord(g.getClipBounds().y + g.getClipBounds().height);
1184             }
1185             // for the gaps we need a minimum of two intervals
1186             // those has to be in the displayed intervals, so we apply the
1187             // filter first (if set) and do the selection
1188             // ourself. The alogorithm is highly dependet on the ordering of the
1189             // list! This should be guaranteed by the model
1190             List<Interval> intervals = new ArrayList<Interval>();
1191             Interval firstInterval = null;
1192             for (Interval interval : row.getIntervals()) {
1193                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(interval)) {
1194                     if (interval.getEnd().compareTo(start) < 0) {
1195                         // before the starting date: remember the nearest
1196                         // interval
1197                         if (firstInterval == null
1198                                 || start.diffSeconds(interval.getEnd()) < start.diffSeconds(firstInterval.getEnd())) {
1199                             firstInterval = interval;
1200                         }
1201                     } else if (interval.contains(start)) {
1202                         // direct hit
1203                         firstInterval = interval;
1204                     } else {
1205                         // in or after the starting date: copy the intervals
1206                         // until we have one behind the end date or
1207                         // a direct hit
1208                         // First of all, add the firstInterval to the resulting
1209                         // list once
1210                         if (firstInterval != null) {
1211                             intervals.add(firstInterval);
1212                             firstInterval = null; // we don't need the
1213                             // reference
1214                             // and by setting it to null
1215                             // we do not add it twice
1216                         }
1217                         if (interval.contains(end)) {
1218                             // direct hit
1219                             intervals.add(interval);
1220                             break; // finished
1221                         }
1222                         if (interval.getBegin().compareTo(end) > 0) {
1223                             intervals.add(interval);
1224                             break; // found an interval beginning behind the
1225                             // end
1226                             // date
1227                         } else {
1228                             // all between: add
1229                             intervals.add(interval);
1230                         }
1231                     }
1232                 }
1233             }
1234             Interval lastInterval = null;
1235             for (Interval i : intervals) {
1236                 // draw gap if there is lastInterval
1237                 if (lastInterval != null) {
1238                     drawGapVertical(g, row, x, width, lastInterval, i);
1239                 }
1240                 // remember last interval for next turn
1241                 lastInterval = i;
1242             }
1243         }
1244 
1245         /**
1246          * Draw a single Interval (horizontal).
1247          * 
1248          * @param g Graphics to paint with
1249          * @param y starting y
1250          * @param height height for painting
1251          * @param i interval to be painted
1252          * @param selected true if the interval should be drawn selecetd
1253          * @param oiInfo overlap information or <code>null</code> if intervals are drawn overlapping
1254          * @param row row of the interval
1255          */
1256         private void drawInterval(Graphics g, int y, int height, Interval i, boolean selected, OverlapInfo oiInfo,
1257                 TimeBarRow row) {
1258             TimeBarRenderer renderer = getRenderer(i.getClass());
1259             if (renderer == null) {
1260                 throw new RuntimeException("no suitable renderer registered for " + i.getClass().getName());
1261             }
1262 
1263             boolean overlapping = oiInfo != null && oiInfo.maxOverlapping > 0;
1264 
1265                         
1266             if (oiInfo != null) {
1267                 float exactHeight;
1268                 // correct the bounds when overlapping
1269                 if (!_delegate.getUseUniformHeight()) {
1270                     exactHeight = 1.0f * _delegate.getTimeBarViewState().getRowHeight(row)
1271                         / (oiInfo.maxOverlapping + 1);
1272                 } else {
1273                     exactHeight = 1.0f * _delegate.getTimeBarViewState().getRowHeight(row)
1274                         / (_delegate.getOverlapStrategy().getMaxOverlapCount(row));
1275                 }
1276                 int yOffset = (int)(oiInfo.pos * exactHeight);
1277                 y += yOffset;
1278                 height = (int)((oiInfo.pos + 1) * exactHeight) - yOffset;
1279             }
1280             
1281 // old int code 
1282             // TODO take the fix (above) to SWT
1283 //            if (oiInfo != null) {
1284 //                // correct the bounds when overlapping
1285 //                if (!_delegate.getUseUniformHeight()) {
1286 //                    height = _delegate.getTimeBarViewState().getRowHeight(row) / (oiInfo.maxOverlapping + 1);
1287 //                } else {
1288 //                    height = _delegate.getTimeBarViewState().getRowHeight(row)
1289 //                            / (_delegate.getOverlapStrategy().getMaxOverlapCount(row));
1290 //                }
1291 //                y = y + oiInfo.pos * height;
1292 //            }
1293 
1294             Component component = renderer.getTimeBarRendererComponent(_timeBarViewer, i, selected, overlapping);
1295             int x = _delegate.xForDate(i.getBegin());
1296             int width = _delegate.xForDate(i.getEnd()) - x;
1297 
1298             // check preferred drawing bounds
1299             Rectangle intervalDrawingArea = new Rectangle(x, y, width, height);
1300             Rectangle drawingArea = renderer.getPreferredDrawingBounds(intervalDrawingArea, _delegate, i, selected,
1301                     overlapping);
1302             x = drawingArea.x;
1303             width = drawingArea.width;
1304             y = drawingArea.y;
1305             height = drawingArea.height;
1306 
1307             component.setBounds(x, y, width, height);
1308             Graphics gg = g.create(x, y, width, height);
1309             // calculate height for clipping
1310             Rectangle diagramRect = _delegate.getDiagramRect();
1311             if (y + height > diagramRect.y + diagramRect.height) {
1312                 height = height - (y + height - (diagramRect.y + diagramRect.height));
1313             }
1314             int upperClipBound = 0;
1315             if (y < diagramRect.y) {
1316                 upperClipBound = diagramRect.y - y;
1317             }
1318 
1319             if (x + width > diagramRect.x + diagramRect.width) {
1320                 width = width - (x + width - (diagramRect.x + diagramRect.width));
1321             }
1322             // calc x clipping and set clipping rect
1323 
1324 //            Rectangle cr = new Rectangle(x < diagramRect.x ? diagramRect.x - x : 0, upperClipBound, diagramRect.width,
1325 //                    height);
1326 //
1327 //            gg.setClip(cr);
1328 
1329             int clipX = x < diagramRect.x ? diagramRect.x - x : 0;
1330             int clipWidth = x < diagramRect.x ? Math.max(0, width - (diagramRect.x - x)) : width;
1331             gg.setClip(clipX, upperClipBound, clipWidth, height);
1332             
1333             component.paint(gg);
1334             gg.dispose();
1335         }
1336 
1337         /**
1338          * Draw a single Interval (vertical).
1339          * 
1340          * @param g Graphics to paint with
1341          * @param x starting x
1342          * @param width width for painting
1343          * @param i interval to be painted
1344          * @param selected true if the interval should be drawn selecetd
1345          * @param oiInfo overlap information or <code>null</code> if intervals are drawn overlapping
1346          * @param row row of the interval
1347          */
1348         private void drawIntervalVertical(Graphics g, int x, int width, Interval i, boolean selected,
1349                 OverlapInfo oiInfo, TimeBarRow row) {
1350             TimeBarRenderer renderer = getRenderer(i.getClass());
1351             if (renderer == null) {
1352                 throw new RuntimeException("no suitable renderer registered");
1353             }
1354 
1355             boolean overlapping = oiInfo != null && oiInfo.maxOverlapping > 0;
1356 
1357             if (oiInfo != null) {
1358                 // correct the bounds when overlapping
1359                 if (!_delegate.getUseUniformHeight()) {
1360                     width = _delegate.getTimeBarViewState().getRowHeight(row) / (oiInfo.maxOverlapping + 1);
1361                 } else {
1362                     width = _delegate.getTimeBarViewState().getRowHeight(row)
1363                             / (_delegate.getOverlapStrategy().getMaxOverlapCount(row));
1364                 }
1365                 x = x + oiInfo.pos * width;
1366             }
1367 
1368             Component component = renderer.getTimeBarRendererComponent(_timeBarViewer, i, selected, overlapping);
1369             int y = _delegate.xForDate(i.getBegin());
1370             int height = _delegate.xForDate(i.getEnd()) - y;
1371 
1372             // check preferred drawing bounds
1373             Rectangle intervalDrawingArea = new Rectangle(x, y, width, height);
1374             Rectangle drawingArea = renderer.getPreferredDrawingBounds(intervalDrawingArea, _delegate, i, selected,
1375                     overlapping);
1376             x = drawingArea.x;
1377             width = drawingArea.width;
1378             y = drawingArea.y;
1379             height = drawingArea.height;
1380 
1381             component.setBounds(x, y, width, height);
1382             Graphics gg = g.create(x, y, width, height);
1383             // calculate height for clipping
1384             Rectangle diagramRect = _delegate.getDiagramRect();
1385             if (x + width > diagramRect.x + diagramRect.width) {
1386                 width = width - (x + width - (diagramRect.x + diagramRect.width));
1387             }
1388             int upperClipBound = 0;
1389             if (x < diagramRect.x) {
1390                 upperClipBound = diagramRect.x - x;
1391             }
1392 
1393             // calculate height for clipping
1394             if (y + height > diagramRect.y + diagramRect.height) {
1395                 height = height - (y + height - (diagramRect.y + diagramRect.height));
1396             }
1397             // calc x clipping and set clipping rect
1398 //            Rectangle cr = new Rectangle(upperClipBound, y < diagramRect.y ? diagramRect.y - y : 0, width,
1399 //                    diagramRect.height);
1400 //
1401 //            gg.setClip(cr);
1402 
1403             int clipY = y < diagramRect.y ? diagramRect.y - y : 0;
1404             int clipHeight = y < diagramRect.y ? Math.max(0, height - (diagramRect.y - y)) : height;
1405             gg.setClip(upperClipBound, clipY, width, clipHeight);
1406             
1407             
1408             
1409             component.paint(gg);
1410             gg.dispose();
1411         }
1412 
1413         /**
1414          * Draw the gap beetween to intervals using a GapRenderer (horizontal).
1415          * 
1416          * @param g Graphics for painting
1417          * @param row TimeBarRow the gap "belongs to"
1418          * @param y starting ccordinate y
1419          * @param height height for painting
1420          * @param i1 earlier interval
1421          * @param i2 later interval
1422          */
1423         private void drawGap(Graphics g, TimeBarRow row, int y, int height, Interval i1, Interval i2) {
1424             Component component = _gapRenderer.getTimeBarGapRendererComponent(_timeBarViewer, row, i1, i2);
1425             int x = _delegate.xForDate(i1.getEnd());
1426             int width = _delegate.xForDate(i2.getBegin()) - x;
1427             // handle minimum width requirements of the gap renderer
1428             if (_gapRenderer.getMinimumWidth() > 0) {
1429                 if (width < _gapRenderer.getMinimumWidth()) {
1430                     int diff = _gapRenderer.getMinimumWidth() - width;
1431                     width += diff;
1432                     x -= diff / 2;
1433                 }
1434             }
1435             // TODO clipping width
1436             component.setBounds(x, y, width, height);
1437             Graphics gg = g.create(x, y, width, height);
1438             // calculate height for clipping
1439             Rectangle diagramRect = _delegate.getDiagramRect();
1440             if (y + height > diagramRect.y + diagramRect.height) {
1441                 height = height - (y + height - (diagramRect.y + diagramRect.height));
1442             }
1443             int upperClipBound = 0;
1444             if (y < diagramRect.y) {
1445                 upperClipBound = diagramRect.y - y;
1446             }
1447 
1448             // calc x clipping and set clipping rect
1449             gg.setClip(x < diagramRect.x ? diagramRect.x - x : 0, upperClipBound, width, height);
1450             component.paint(gg);
1451             gg.dispose();
1452         }
1453 
1454         /**
1455          * Draw the gap beetween to intervals using a GapRenderer (vertical).
1456          * 
1457          * @param g Graphics for painting
1458          * @param row TimeBarRow the gap "belongs to"
1459          * @param x starting cordinate x
1460          * @param width width for painting
1461          * @param i1 earlier interval
1462          * @param i2 later interval
1463          */
1464         private void drawGapVertical(Graphics g, TimeBarRow row, int x, int width, Interval i1, Interval i2) {
1465             Component component = _gapRenderer.getTimeBarGapRendererComponent(_timeBarViewer, row, i1, i2);
1466             int y = _delegate.xForDate(i1.getEnd());
1467             int height = _delegate.xForDate(i2.getBegin()) - y;
1468             // handle minimum width requirements of the gap renderer
1469             if (_gapRenderer.getMinimumWidth() > 0) {
1470                 if (height < _gapRenderer.getMinimumWidth()) {
1471                     int diff = _gapRenderer.getMinimumWidth() - height;
1472                     height += diff;
1473                     y -= diff / 2;
1474                 }
1475             }
1476             // TODO clipping width
1477             component.setBounds(x, y, width, height);
1478             Graphics gg = g.create(x, y, width, height);
1479             // calculate height for clipping
1480             Rectangle diagramRect = _delegate.getDiagramRect();
1481             if (x + width > diagramRect.x + diagramRect.width) {
1482                 width = width - (x + width - (diagramRect.x + diagramRect.width));
1483             }
1484             int upperClipBound = 0;
1485             if (x < diagramRect.x) {
1486                 upperClipBound = diagramRect.x - x;
1487             }
1488 
1489             // calc x clipping and set clipping rect
1490             gg.setClip(upperClipBound, y < diagramRect.y ? diagramRect.y - y : 0, width, height);
1491             component.paint(gg);
1492             gg.dispose();
1493         }
1494 
1495         /**
1496          * Draw a row header (horizontal). If the header claims a smaller preferred size than the rowheight it is
1497          * vertically centered.
1498          * 
1499          * @param g Graphics
1500          * @param y upper bound
1501          * @param height row height
1502          * @param header the header object
1503          * @param selected true if the row is selected
1504          */
1505         private void drawRowHeader(Graphics g, int y, int height, TimeBarRowHeader header, boolean selected) {
1506             // draw only if a header renderer is set and the width is >0
1507             if (_headerRenderer != null && _delegate.getYAxisWidth() > 0) {
1508                 JComponent component = _headerRenderer.getHeaderRendererComponent(_timeBarViewer, header, selected);
1509                 int x = _delegate.getYAxisRect().x;
1510                 int width = _delegate.getYAxisWidth() - 1;
1511                 Rectangle diagramRect = _delegate.getDiagramRect();
1512                 Graphics gg;
1513                 int prefHeight = component.getPreferredSize().height;
1514                 if (prefHeight < height) {
1515                     int hCor = (height - prefHeight) / 2;
1516                     gg = g.create(x, y + hCor, width, prefHeight);
1517                     component.setBounds(x, y + hCor, width, prefHeight);
1518                     int upperClipBound = 0;
1519                     if (y + hCor < diagramRect.y) {
1520                         upperClipBound = diagramRect.y - y - hCor;
1521                     }
1522                     if (y + hCor + height > diagramRect.y + diagramRect.height) {
1523                         prefHeight = prefHeight - (y + hCor + prefHeight - (diagramRect.y + diagramRect.height));
1524                     }
1525                     // calc x clipping and set clipping rect
1526                     gg.setClip(0, upperClipBound, width, prefHeight);
1527                 } else {
1528                     gg = g.create(x, y, width, height);
1529                     component.setBounds(x, y, width, height);
1530                     int upperClipBound = 0;
1531                     if (y < diagramRect.y) {
1532                         upperClipBound = diagramRect.y - y;
1533                     }
1534 
1535                     if (y + height > diagramRect.y + diagramRect.height) {
1536                         height = height - (y + height - (diagramRect.y + diagramRect.height));
1537                     }
1538                     // calc x clipping and set clipping rect
1539                     gg.setClip(x, upperClipBound, width, height);
1540                 }
1541                 component.paint(gg);
1542                 gg.dispose();
1543             }
1544         }
1545 
1546         /**
1547          * Draw a row header (vertical). If the hedare claims a smaller preferred size than the rowheight it is
1548          * vertically centered.
1549          * 
1550          * @param g Graphics
1551          * @param x left bound
1552          * @param width width
1553          * @param header the header object
1554          * @param selected true if the row is selected
1555          */
1556         private void drawRowHeaderVertical(Graphics g, int x, int width, TimeBarRowHeader header, boolean selected) {
1557             // draw only if a header renderer is set and the width is >0
1558             if (_headerRenderer != null && _delegate.getYAxisWidth() > 0) {
1559                 JComponent component = _headerRenderer.getHeaderRendererComponent(_timeBarViewer, header, selected);
1560                 int y = _delegate.getYAxisRect().y;
1561                 int height = _delegate.getYAxisWidth() - 1;
1562                 Rectangle diagramRect = _delegate.getDiagramRect();
1563                 Graphics gg;
1564                 int prefWidth = component.getPreferredSize().width;
1565                 if (prefWidth < width) {
1566                     int wCor = (width - prefWidth) / 2;
1567                     gg = g.create(x + wCor, y, prefWidth, height);
1568                     component.setBounds(x + wCor, y, prefWidth, height);
1569                     int upperClipBound = 0;
1570                     if (x + wCor < diagramRect.x) {
1571                         upperClipBound = diagramRect.x - x - wCor;
1572                     }
1573                     if (x + wCor + width > diagramRect.x + diagramRect.width) {
1574                         prefWidth = prefWidth - (x + wCor + prefWidth - (diagramRect.x + diagramRect.width));
1575                     }
1576                     // calc y clipping and set clipping rect
1577                     gg.setClip(upperClipBound, 0, prefWidth, height);
1578                 } else {
1579                     gg = g.create(x, y, width, height);
1580                     component.setBounds(x, y, width, height);
1581                     int upperClipBound = 0;
1582                     if (x < diagramRect.x) {
1583                         upperClipBound = diagramRect.x - x;
1584                     }
1585 
1586                     if (x + width > diagramRect.x + diagramRect.width) {
1587                         width = width - (x + width - (diagramRect.x + diagramRect.width));
1588                     }
1589                     // calc y clipping and set clipping rect
1590                     gg.setClip(upperClipBound, 0, width, height);
1591                 }
1592                 component.paint(gg);
1593                 gg.dispose();
1594             }
1595         }
1596 
1597         /**
1598          * Draw the hierachy section for a row/node (horizontal).
1599          * 
1600          * @param g graphics
1601          * @param y begin y
1602          * @param height height of a row
1603          * @param row the row
1604          * @param selected true if the row is selected
1605          */
1606         private void drawHierarchy(Graphics g, int y, int height, TimeBarRow row, boolean selected) {
1607             // draw only if a hierarchy renderer is set and the width is >0
1608             if (_hierarchyRenderer != null && _delegate.getHierarchyWidth() > 0) {
1609                 int level = 0;
1610                 int depth = 0;
1611                 boolean expanded = false;
1612                 boolean leaf = true;
1613                 if (row instanceof TimeBarNode) {
1614                     TimeBarNode node = (TimeBarNode) row;
1615                     if (_delegate.getHierarchicalViewState().isExpanded(node)) {
1616                         expanded = true;
1617                     }
1618                     leaf = node.getChildren().size() == 0;
1619                     level = node.getLevel();
1620                     depth = _delegate.getHierarchicalModel().getDepth();
1621                 }
1622 
1623                 int x = _delegate.getHierarchyRect().x;
1624                 int width = _delegate.getHierarchyWidth() - 1;
1625 
1626                 JComponent component = _hierarchyRenderer.getHierarchyRendererComponent(_timeBarViewer, row, selected,
1627                         expanded, leaf, level, depth);
1628                 Rectangle diagramRect = _delegate.getDiagramRect();
1629                 Graphics gg;
1630 
1631                 gg = g.create(x, y, width, height);
1632                 component.setBounds(x, y, width, height);
1633                 if (y + height > diagramRect.y + diagramRect.height) {
1634                     height = height - (y + height - (diagramRect.y + diagramRect.height));
1635                 }
1636                 int upperClipBound = 0;
1637                 if (y < diagramRect.y) {
1638                     upperClipBound = diagramRect.y - y;
1639                 }
1640                 // calc x clipping and set clipping rect
1641                 gg.setClip(x, upperClipBound, width, height);
1642 
1643                 component.paint(gg);
1644                 gg.dispose();
1645             }
1646 
1647         }
1648 
1649         /**
1650          * Draw the hierachy section for a row/node (vertical).
1651          * 
1652          * @param g graphics
1653          * @param x begin x
1654          * @param width width of a col
1655          * @param row the row
1656          * @param selected true if the row is selected
1657          */
1658         private void drawHierarchyVertical(Graphics g, int x, int width, TimeBarRow row, boolean selected) {
1659             // draw only if a hierarchy renderer is set and the width is >0
1660             if (_hierarchyRenderer != null && _delegate.getHierarchyWidth() > 0) {
1661                 int level = 0;
1662                 int depth = 0;
1663                 boolean expanded = false;
1664                 boolean leaf = true;
1665                 if (row instanceof TimeBarNode) {
1666                     TimeBarNode node = (TimeBarNode) row;
1667                     if (_delegate.getHierarchicalViewState().isExpanded(node)) {
1668                         expanded = true;
1669                     }
1670                     leaf = node.getChildren().size() == 0;
1671                     level = node.getLevel();
1672                     depth = _delegate.getHierarchicalModel().getDepth();
1673                 }
1674 
1675                 int y = _delegate.getHierarchyRect().y;
1676                 int height = _delegate.getHierarchyWidth() - 1;
1677 
1678                 JComponent component = _hierarchyRenderer.getHierarchyRendererComponent(_timeBarViewer, row, selected,
1679                         expanded, leaf, level, depth);
1680                 Rectangle diagramRect = _delegate.getDiagramRect();
1681                 Graphics gg;
1682 
1683                 gg = g.create(x, y, width, height);
1684                 component.setBounds(x, y, width, height);
1685 
1686                 if (x + width > diagramRect.x + diagramRect.width) {
1687                     width = width - (x + width - (diagramRect.x + diagramRect.width));
1688                 }
1689                 int upperClipBound = 0;
1690                 if (x < diagramRect.x) {
1691                     upperClipBound = diagramRect.x - x;
1692                 }
1693                 // calc y clipping and set clipping rect
1694                 gg.setClip(upperClipBound, y, width, height);
1695 
1696                 component.paint(gg);
1697                 gg.dispose();
1698             }
1699 
1700         }
1701 
1702         /**
1703          * {@inheritDoc}
1704          */
1705         public Dimension getPreferredSize() {
1706             return new Dimension(PREFWIDTH, PREFHEIGHT);
1707         }
1708 
1709         // *** MouseListener
1710         /**
1711          * {@inheritDoc}
1712          */
1713         public void mouseEntered(MouseEvent e) {
1714         }
1715 
1716         /**
1717          * {@inheritDoc}
1718          */
1719         public void mouseExited(MouseEvent e) {
1720         }
1721 
1722         /**
1723          * {@inheritDoc}
1724          */
1725         public void mousePressed(MouseEvent e) {
1726             _delegate.mousePressed(e.getX(), e.getY(), e.isPopupTrigger(), e.getModifiersEx());
1727         }
1728 
1729         /**
1730          * {@inheritDoc}
1731          */
1732         public void mouseClicked(MouseEvent e) {
1733         }
1734 
1735         /**
1736          * {@inheritDoc}
1737          */
1738         public void mouseReleased(MouseEvent e) {
1739             boolean popupTrigger = e.isPopupTrigger();
1740             if (!popupTrigger && _requiresPopupTriggerCheck) {
1741                 popupTrigger = e.getButton() == MouseEvent.BUTTON3;
1742             }
1743 
1744             _delegate.mouseReleased(e.getX(), e.getY(), popupTrigger, e.getModifiersEx());
1745         }
1746 
1747         // *** End of MouseListener
1748 
1749         // *** MouseMotionListener
1750         /**
1751          * {@inheritDoc}
1752          */
1753         public void mouseDragged(MouseEvent e) {
1754             _delegate.mouseDragged(e.getX(), e.getY(), e.getModifiersEx());
1755         }
1756 
1757         /**
1758          * {@inheritDoc}
1759          */
1760         public void mouseMoved(MouseEvent e) {
1761             _delegate.mouseMoved(e.getX(), e.getY());
1762         }
1763 
1764         // *** End of MouseMotionListener
1765         // *** MouseWheelListener
1766         /**
1767          * {@inheritDoc}
1768          */
1769         public void mouseWheelMoved(MouseWheelEvent e) {
1770             if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
1771                 int val = e.getUnitsToScroll();
1772                 if (_delegate.getXAxisRect() != null && e.getY() > _delegate.getXAxisRect().y
1773                         && e.getY() < _delegate.getXAxisRect().y + _delegate.getXAxisRect().height) {
1774                     // on the xaxis
1775                     _xScrollBar.setValue(_xScrollBar.getValue() + val * _xScrollBar.getModel().getExtent() / 5);
1776                 } else {
1777                     // y axis
1778                     _yScrollBar.getModel().setValue(_yScrollBar.getModel().getValue() + val * getRowHeight()); // TODO check configurabilty
1779                 }
1780             }
1781         }
1782         // *** End of MouseWheelListener
1783     }
1784 
1785     /**
1786      * @return Returns the timeScaleRenderer.
1787      */
1788     public TimeScaleRenderer getTimeScaleRenderer() {
1789         return _timeScaleRenderer;
1790     }
1791 
1792     /**
1793      * Set a renderer for the x axis. The Height for the x axis will be set to the preferred height of the renderer if
1794      * the renderer supplies one.
1795      * 
1796      * @param timeScaleRenderer The timeScaleRenderer to set.
1797      */
1798     public void setTimeScaleRenderer(TimeScaleRenderer timeScaleRenderer) {
1799         _timeScaleRenderer = timeScaleRenderer;
1800         if (_timeScaleRenderer != null && _timeScaleRenderer.getHeight() != -1) {
1801             // if the renderer announces a preferred height, get it and set it
1802             _delegate.setXAxisHeight(_timeScaleRenderer.getHeight());
1803         }
1804         // maybe the new time scale renderer is not a TickProvider ... tell the grid rederer
1805         if (_gridRenderer != null) {
1806             _gridRenderer.setTickProvider(null);
1807         }
1808         if (_timeScaleRenderer != null && _gridRenderer != null && _timeScaleRenderer instanceof ITickProvider
1809                 && _delegate.getTimeScalePosition() != TimeBarViewerInterface.TIMESCALE_POSITION_NONE) {
1810             _gridRenderer.setTickProvider((ITickProvider) _timeScaleRenderer);
1811         }
1812         _diagram.safeRepaint();
1813     }
1814 
1815     /**
1816      * Get the current misc renderer.
1817      * 
1818      * @return the misc renderer
1819      */
1820     public IMiscRenderer getMiscRenderer() {
1821         return _miscRenderer;
1822     }
1823 
1824     /**
1825      * Set the misc renderer to be used for rendering some parts/elements in the viewer.
1826      * 
1827      * @param miscRenderer the renderer to use
1828      */
1829     public void setMiscRenderer(IMiscRenderer miscRenderer) {
1830         _miscRenderer = miscRenderer;
1831         _diagram.safeRepaint();
1832     }
1833 
1834     /**
1835      * Retrieve the renderer that is currently used to render the title area.
1836      * 
1837      * @return the renderer or <code>null</code> if no renderer is set.
1838      */
1839     public ITitleRenderer getTitleRenderer() {
1840         return _titleRenderer;
1841     }
1842 
1843     /**
1844      * Set the title renderer.
1845      * 
1846      * @param titleRenderer the new renderer or <code>null</code> to disable titel rendering.
1847      */
1848     public void setTitleRenderer(ITitleRenderer titleRenderer) {
1849         _titleRenderer = titleRenderer;
1850         _diagram.safeRepaint();
1851     }
1852 
1853     /**
1854      * Retrieve the relation renderer currently set.
1855      * 
1856      * @return the relation renderer or <code>null</code> if none is set.
1857      */
1858     public IRelationRenderer getRelationRenderer() {
1859         return _relationRenderer;
1860     }
1861 
1862     /**
1863      * Set the relation renderer to use.
1864      * 
1865      * @param relationRenderer the renderer or <code>null</code> to disable relation rendering.
1866      */
1867     public void setRelationRenderer(IRelationRenderer relationRenderer) {
1868         _relationRenderer = relationRenderer;
1869         _diagram.safeRepaint();
1870     }
1871 
1872     /**
1873      * Retrieve the configured marker renderer.
1874      * 
1875      * @return the marker renderer
1876      */
1877     public IMarkerRenderer getMarkerRenderer() {
1878         return _markerRenderer;
1879     }
1880 
1881     /**
1882      * Set the marker renderer to be used.
1883      * 
1884      * @param markerRenderer the marker renderer to be used.
1885      */
1886     public void setMarkerRenderer(IMarkerRenderer markerRenderer) {
1887         _markerRenderer = markerRenderer;
1888         _diagram.safeRepaint();
1889     }
1890 
1891     /**
1892      * Get the current global assistant renderer.
1893      * 
1894      * @return the renderer currently in use
1895      */
1896     public IGlobalAssistantRenderer getGlobalAssistantRenderer() {
1897         return _globalAssistantRenderer;
1898     }
1899 
1900     /**
1901      * Set a gloabl assistant renederer.
1902      * 
1903      * @param globalAssistantRenderer the renderer
1904      */
1905     public void setGlobalAssistantRenderer(IGlobalAssistantRenderer globalAssistantRenderer) {
1906         _globalAssistantRenderer = globalAssistantRenderer;
1907         _diagram.safeRepaint();
1908     }
1909 
1910     /**
1911      * @return Returns the gridRenderer.
1912      */
1913     public GridRenderer getGridRenderer() {
1914         return _gridRenderer;
1915     }
1916 
1917     /**
1918      * @param gridRenderer The gridRenderer to set.
1919      */
1920     public void setGridRenderer(GridRenderer gridRenderer) {
1921         _gridRenderer = gridRenderer;
1922         // set the tick provider on the grid renderer
1923         if (_gridRenderer != null && _timeScaleRenderer != null && _timeScaleRenderer instanceof ITickProvider) {
1924             _gridRenderer.setTickProvider((ITickProvider) _timeScaleRenderer);
1925         }
1926         _diagram.safeRepaint();
1927     }
1928 
1929     /**
1930      * @return Returns the gapRenderer.
1931      */
1932     public TimeBarGapRenderer getGapRenderer() {
1933         return _gapRenderer;
1934     }
1935 
1936     /**
1937      * @param gapRenderer The gapRenderer to set.
1938      */
1939     public void setGapRenderer(TimeBarGapRenderer gapRenderer) {
1940         _gapRenderer = gapRenderer;
1941         _diagram.safeRepaint();
1942     }
1943 
1944     /**
1945      * Register a popup menu for a given interval class.
1946      * 
1947      * @param clazz class that the menu is for
1948      * @param popup popup menu to show
1949      */
1950     public void registerPopupMenu(Class<? extends Interval> clazz, JPopupMenu popup) {
1951         if (_registeredPopupMenues == null) {
1952             _registeredPopupMenues = new HashMap<Class<? extends Interval>, JPopupMenu>();
1953         }
1954         _registeredPopupMenues.put(clazz, popup);
1955     }
1956 
1957     /**
1958      * Retrieve the popup menu registered for a given interval class.
1959      * 
1960      * @param clazz class in question
1961      * @return menu or <code>null</code>
1962      */
1963     public JPopupMenu getPopupMenu(Class<? extends Interval> clazz) {
1964         if (_registeredPopupMenues == null) {
1965             return null;
1966         }
1967         JPopupMenu result = null;
1968         result = _registeredPopupMenues.get(clazz);
1969         if (result != null) {
1970             return result;
1971         }
1972 
1973         // direct interfaces
1974         Class<?>[] interfaces = clazz.getInterfaces();
1975         for (Class<?> c : interfaces) {
1976             result = _registeredPopupMenues.get(c);
1977             if (result != null) {
1978                 return result;
1979             }
1980         }
1981 
1982         // superclasses
1983         Class<?> sc = clazz.getSuperclass();
1984 
1985         while (sc != null) {
1986             result = _registeredPopupMenues.get(sc);
1987             if (result != null) {
1988                 return result;
1989             }
1990             // interfaces of the superclass
1991             Class<?>[] scinterfaces = sc.getInterfaces();
1992             for (Class<?> c : scinterfaces) {
1993                 result = _registeredPopupMenues.get(c);
1994                 if (result != null) {
1995                     return result;
1996                 }
1997             }
1998             sc = sc.getSuperclass();
1999         }
2000 
2001         return result;
2002     }
2003 
2004     /**
2005      * {@inheritDoc}
2006      */
2007     public void firePropertyChangeX(String propName, Object oldVal, Object newVal) {
2008         firePropertyChange(propName, oldVal, newVal);
2009     }
2010 
2011     /**
2012      * {@inheritDoc}
2013      */
2014     public boolean timeBarContains(Interval interval, Rectangle intervalRect, int x, int y, boolean overlapping) {
2015         JComponent component = getIntervalComponent(interval, intervalRect, overlapping);
2016         return component.contains(x, y);
2017     }
2018 
2019     /**
2020      * {@inheritDoc}
2021      */
2022     public Rectangle timeBarContainingRect(Interval interval, Rectangle intervalRect, boolean overlapping) {
2023         JComponent component = getIntervalComponent(interval, intervalRect);
2024         // get the containing rect or - if not set by the renderer -
2025         // use the component bounds
2026         Rectangle containingRect = (Rectangle) component.getClientProperty(TimeBarRenderer.CONTAINING_RECTANGLE);
2027         return containingRect;
2028     }
2029 
2030     /**
2031      * {@inheritDoc}
2032      */
2033     public void setCursor(int cursorType) {
2034         setCursor(Cursor.getPredefinedCursor(cursorType));
2035     }
2036 
2037     /**
2038      * {@inheritDoc}
2039      */
2040     public String getIntervalToolTipText(Interval interval, Rectangle intervalRect, int x, int y) {
2041         JComponent component = getIntervalComponent(interval, intervalRect);
2042         // String tooltip = component.getToolTipText(new MouseEvent(this, 0, 0,
2043         // 0, x - component.getX(), y - component.getY(), 0, false));
2044         String tooltip = component.getToolTipText(new MouseEvent(this, 0, 0, 0, x, y, 0, false));
2045         return tooltip;
2046     }
2047 
2048     /**
2049      * {@inheritDoc}
2050      */
2051     public JaretDate getStartDate() {
2052         return _delegate.getStartDate();
2053     }
2054 
2055     /**
2056      * {@inheritDoc}
2057      */
2058     public void setStartDate(JaretDate startDate) {
2059         _delegate.setStartDate(startDate);
2060     }
2061 
2062     /**
2063      * {@inheritDoc}
2064      */
2065     public JaretDate getMinDate() {
2066         return _delegate.getMinDate();
2067     }
2068 
2069     /**
2070      * {@inheritDoc}
2071      */
2072     public void setMinDate(JaretDate minDate) {
2073         _delegate.setMinDate(minDate);
2074     }
2075 
2076     /**
2077      * {@inheritDoc}
2078      */
2079     public JaretDate getMaxDate() {
2080         return _delegate.getMaxDate();
2081     }
2082 
2083     /**
2084      * {@inheritDoc}
2085      */
2086     public void setMaxDate(JaretDate maxDate) {
2087         _delegate.setMaxDate(maxDate);
2088     }
2089 
2090     /**
2091      * {@inheritDoc}
2092      */
2093     public TimeBarSelectionModel getSelectionModel() {
2094         return _delegate.getSelectionModel();
2095     }
2096 
2097     /**
2098      * {@inheritDoc}
2099      */
2100     public void setSelectionModel(TimeBarSelectionModel selectionModel) {
2101         _delegate.setSelectionModel(selectionModel);
2102     }
2103 
2104     /**
2105      * {@inheritDoc}
2106      */
2107     public int getFirstRowDisplayed() {
2108         return _delegate.getFirstRow();
2109     }
2110 
2111     /**
2112      * {@inheritDoc}
2113      */
2114     public void setFirstRowDisplayed(int rowIdx) {
2115         _delegate.setFirstRow(rowIdx);
2116     }
2117 
2118     /**
2119      * {@inheritDoc}
2120      */
2121     public void setFirstRowDisplayed(TimeBarRow row) {
2122         _delegate.setFirstRow(row);
2123     }
2124 
2125     /**
2126      * {@inheritDoc}
2127      */
2128     public void setFirstRow(int firstRow, int pixOffset) {
2129         _delegate.setFirstRow(firstRow, pixOffset);
2130     }
2131 
2132     /**
2133      * {@inheritDoc}
2134      */
2135     public void setLastRow(int index) {
2136         _delegate.setLastRow(index);
2137     }
2138 
2139     /**
2140      * {@inheritDoc}
2141      */
2142     public void setLastRow(TimeBarRow row) {
2143         _delegate.setLastRow(row);
2144     }
2145 
2146     /**
2147      * {@inheritDoc}
2148      */
2149     public JaretDate getEndDate() {
2150         return _delegate.getEndDate();
2151     }
2152 
2153     /**
2154      * {@inheritDoc}
2155      */
2156     public int getFirstRowOffset() {
2157         return _delegate.getFirstRowOffset();
2158     }
2159 
2160     /**
2161      * {@inheritDoc}
2162      */
2163     public void setFirstRowOffset(int offset) {
2164         _delegate.setFirstRowOffset(offset);
2165     }
2166 
2167     /**
2168      * {@inheritDoc}
2169      */
2170     public void setTimeScalePosition(int timeScalePosition) {
2171         if (timeScalePosition == TimeBarViewerInterface.TIMESCALE_POSITION_NONE) {
2172             if (_gridRenderer != null) {
2173                 _gridRenderer.setTickProvider(null);
2174             }
2175         } else {
2176             if (_gridRenderer != null && _timeScaleRenderer instanceof ITickProvider) {
2177                 _gridRenderer.setTickProvider((ITickProvider) _timeScaleRenderer);
2178             }
2179         }
2180         _delegate.setTimeScalePosition(timeScalePosition);
2181     }
2182 
2183     /**
2184      * {@inheritDoc}
2185      */
2186     public void setAdjustMinMaxDatesByModel(boolean adjust) {
2187         _delegate.setAdjustMinMaxDatesByModel(adjust);
2188     }
2189 
2190     /**
2191      * {@inheritDoc}
2192      */
2193     public boolean getAdjustMinMaxDatesByModel() {
2194         return _delegate.getAdjustMinMaxDatesByModel();
2195     }
2196 
2197     /**
2198      * {@inheritDoc}
2199      */
2200     public TimeBarRow rowForY(int y) {
2201         return _delegate.rowForY(y);
2202     }
2203 
2204     /**
2205      * {@inheritDoc}
2206      */
2207     public JaretDate dateForX(int x) {
2208         return _delegate.dateForCoord(x);
2209     }
2210 
2211     /**
2212      * {@inheritDoc}
2213      */
2214     public JaretDate dateForXY(int x, int y) {
2215         return _delegate.dateForCoord(x, y);
2216     }
2217 
2218     /**
2219      * {@inheritDoc}
2220      */
2221     public int xForDate(JaretDate date) {
2222         return _delegate.xForDate(date);
2223     }
2224 
2225     /**
2226      * {@inheritDoc}
2227      */
2228     public void highlightRow(int y) {
2229         _delegate.highlightRow(y);
2230     }
2231 
2232     /**
2233      * {@inheritDoc}
2234      */
2235     public void highlightRow(TimeBarRow timeBarRow) {
2236         _delegate.highlightRow(timeBarRow);
2237     }
2238 
2239     /**
2240      * {@inheritDoc}
2241      */
2242     public void deHighlightRow() {
2243         _delegate.deHighlightRow();
2244     }
2245 
2246     /**
2247      * {@inheritDoc}
2248      */
2249     public void setDrawRowGrid(boolean drawRowGrid) {
2250         _delegate.setDrawRowGrid(drawRowGrid);
2251     }
2252 
2253     /**
2254      * {@inheritDoc}
2255      */
2256     public boolean getDrawRowGrid() {
2257         return _delegate.getDrawRowGrid();
2258     }
2259 
2260     /**
2261      * {@inheritDoc}
2262      */
2263     public void setYAxisWidth(int width) {
2264         _delegate.setYAxisWidth(width);
2265     }
2266 
2267     /**
2268      * {@inheritDoc}
2269      */
2270     public int getYAxisWidth() {
2271         return _delegate.getYAxisWidth();
2272     }
2273 
2274     /**
2275      * {@inheritDoc}
2276      */
2277     public void setHierarchyWidth(int width) {
2278         _delegate.setHierarchyWidth(width);
2279     }
2280 
2281     /**
2282      * {@inheritDoc}
2283      */
2284     public int getHierarchyWidth() {
2285         return _delegate.getHierarchyWidth();
2286     }
2287 
2288     /**
2289      * {@inheritDoc}
2290      */
2291     public void addIntervalModificator(IntervalModificator intervalModificator) {
2292         _delegate.addIntervalModificator(intervalModificator);
2293     }
2294 
2295     /**
2296      * {@inheritDoc}
2297      */
2298     public void remIntervalModificator(IntervalModificator intervalModificator) {
2299         _delegate.remIntervalModificator(intervalModificator);
2300     }
2301 
2302     /**
2303      * {@inheritDoc}
2304      */
2305     public void setAutoscrollEnabled(boolean enableAutoscroll) {
2306         _delegate.setAutoscrollEnabled(enableAutoscroll);
2307     }
2308 
2309     /**
2310      * {@inheritDoc}
2311      */
2312     public boolean isAutoscrollEnabled() {
2313         return _delegate.isAutoscrollEnabled();
2314     }
2315 
2316     /**
2317      * {@inheritDoc}
2318      */
2319     public String getTimeScaleToolTipText(int x, int y) {
2320         if (_timeScaleRendererComponent != null) {
2321             return _timeScaleRendererComponent.getToolTipText(new MouseEvent(this, 0, 0, 0, x
2322                     - _delegate.getDiagramRect().x, y - _delegate.getXAxisRect().y, 0, false));
2323         }
2324         return null;
2325     }
2326 
2327     /**
2328      * {@inheritDoc}
2329      */
2330     public String getHeaderToolTipText(TimeBarRow row, int x, int y) {
2331         if (_headerRenderer != null && row != null) {
2332             JComponent component = _headerRenderer.getHeaderRendererComponent(this, row.getRowHeader(), false);
2333             if (component != null) {
2334                 component.setBounds(_delegate.getHeaderRect(row));
2335                 return component.getToolTipText(new MouseEvent(this, 0, 0, 0, x - _delegate.getYAxisRect().x, y
2336                         - _delegate.getYAxisRect().y, 0, false));
2337             }
2338         }
2339         return null;
2340     }
2341 
2342     /**
2343      * {@inheritDoc}
2344      */
2345     public String getHierarchyToolTipText(TimeBarNode node, int x, int y) {
2346         if (_hierarchyRenderer != null && node != null) {
2347             // TODO check additional parameters to be corrrect - maybe these are
2348             // useful for generating the tooltip
2349             JComponent component = _hierarchyRenderer.getHierarchyRendererComponent(this, node, false, false, false, 0,
2350                     1);
2351             component.setBounds(_delegate.getHierarchyRect(node));
2352             return component.getToolTipText(new MouseEvent(this, 0, 0, 0, x - _delegate.getHierarchyRect().x, y
2353                     - _delegate.getHierarchyRect().y, 0, false));
2354         }
2355         return null;
2356     }
2357 
2358     /**
2359      * {@inheritDoc}
2360      */
2361     public HierarchicalViewState getHierarchicalViewState() {
2362         return _delegate.getHierarchicalViewState();
2363     }
2364 
2365     /**
2366      * {@inheritDoc}
2367      */
2368     public void setHierarchicalViewState(HierarchicalViewState hierarchicalViewState) {
2369         _delegate.setHierarchicalViewState(hierarchicalViewState);
2370     }
2371 
2372     /**
2373      * {@inheritDoc}
2374      */
2375     public void setModel(HierarchicalTimeBarModel hModel) {
2376         _delegate.setModel(hModel);
2377     }
2378 
2379     /**
2380      * {@inheritDoc}
2381      */
2382     public HierarchicalTimeBarModel getHierarchicalModel() {
2383         return _delegate.getHierarchicalModel();
2384     }
2385 
2386     /**
2387      * {@inheritDoc}
2388      */
2389     public int getMarkerWidth(TimeBarMarker marker) {
2390         if (_markerRenderer != null) {
2391             return _markerRenderer.getMarkerWidth(marker);
2392         }
2393         return 0;
2394     }
2395 
2396     /**
2397      * {@inheritDoc}
2398      */
2399     public void setTitle(String title) {
2400         _delegate.setTitle(title);
2401     }
2402 
2403     /**
2404      * {@inheritDoc}
2405      */
2406     public String getTitle() {
2407         return _delegate.getTitle();
2408     }
2409 
2410     /**
2411      * Retrieve the context menu set for the body.
2412      * 
2413      * @return the context menu or <code>null</code>
2414      */
2415     public JPopupMenu getBodyContextMenu() {
2416         return _bodyContextMenu;
2417     }
2418 
2419     /**
2420      * Set the context menu to be used for the body area.
2421      * 
2422      * @param bodyContextMenu context menu or <code>null</code> for no context menu
2423      */
2424     public void setBodyContextMenu(JPopupMenu bodyContextMenu) {
2425         _bodyContextMenu = bodyContextMenu;
2426     }
2427 
2428     /**
2429      * Retrieve the context menu set for the time scale.
2430      * 
2431      * @return the context menu or <code>null</code>
2432      */
2433     public JPopupMenu getTimeScaleContextMenu() {
2434         return _timeScaleContextMenu;
2435     }
2436 
2437     /**
2438      * Set the context menu to be used for the time scale area.
2439      * 
2440      * @param timeScaleContextMenu context menu or <code>null</code> for no context menu
2441      */
2442     public void setTimeScaleContextMenu(JPopupMenu timeScaleContextMenu) {
2443         _timeScaleContextMenu = timeScaleContextMenu;
2444     }
2445 
2446     /**
2447      * Retrieve the context menu set for the header.
2448      * 
2449      * @return the context menu or <code>null</code>
2450      */
2451 
2452     public JPopupMenu getHeaderContextMenu() {
2453         return _headerContextMenu;
2454     }
2455 
2456     /**
2457      * Set the context menu to be used for the header area.
2458      * 
2459      * @param headerContextMenu context menu or <code>null</code> for no context menu
2460      */
2461     public void setHeaderContextMenu(JPopupMenu headerContextMenu) {
2462         _headerContextMenu = headerContextMenu;
2463     }
2464 
2465     /**
2466      * Retrieve the context menu set for the hierrarchy.
2467      * 
2468      * @return the context menu or <code>null</code>
2469      */
2470     public JPopupMenu getHierarchyContextMenu() {
2471         return _hierarchyContextMenu;
2472     }
2473 
2474     /**
2475      * Set the context menu to be used for the hierarchy area.
2476      * 
2477      * @param hierarchyContextMenu context menu or <code>null</code> for no context menu
2478      */
2479     public void setHierarchyContextMenu(JPopupMenu hierarchyContextMenu) {
2480         _hierarchyContextMenu = hierarchyContextMenu;
2481     }
2482 
2483     /**
2484      * Retrieve the context menu set for the title area.
2485      * 
2486      * @return the context menu or <code>null</code>
2487      */
2488     public JPopupMenu getTitleContextMenu() {
2489         return _titleContextMenu;
2490     }
2491 
2492     /**
2493      * Set the context menu to be used for the titel area.
2494      * 
2495      * @param titleContextMenu context menu or <code>null</code> for no context menu
2496      */
2497     public void setTitleContextMenu(JPopupMenu titleContextMenu) {
2498         _titleContextMenu = titleContextMenu;
2499     }
2500 
2501     /**
2502      * {@inheritDoc}
2503      */
2504     public void displayBodyContextMenu(int x, int y) {
2505         if (_bodyContextMenu != null) {
2506             _bodyContextMenu.show(_diagram, x, y);
2507         }
2508     }
2509 
2510     /**
2511      * {@inheritDoc}
2512      */
2513     public void displayTimeScaleContextMenu(int x, int y) {
2514         if (_timeScaleContextMenu != null) {
2515             _timeScaleContextMenu.show(_diagram, x, y);
2516         }
2517     }
2518 
2519     /**
2520      * {@inheritDoc}
2521      */
2522     public void displayIntervalContextMenu(Interval interval, int x, int y) {
2523         JPopupMenu menu = getPopupMenu(interval.getClass());
2524         if (menu != null) {
2525             menu.show(_diagram, x, y);
2526         }
2527     }
2528 
2529     /**
2530      * {@inheritDoc}
2531      */
2532     public void displayHeaderContextMenu(TimeBarRow row, int x, int y) {
2533         if (_headerContextMenu != null) {
2534             _headerContextMenu.show(_diagram, x, y);
2535         }
2536     }
2537 
2538     /**
2539      * {@inheritDoc}
2540      */
2541     public void displayHierarchyContextMenu(TimeBarRow row, int x, int y) {
2542         if (_hierarchyContextMenu != null) {
2543             _hierarchyContextMenu.show(_diagram, x, y);
2544         }
2545     }
2546 
2547     /**
2548      * {@inheritDoc}
2549      */
2550     public void displayTitleContextMenu(int x, int y) {
2551         if (_titleContextMenu != null) {
2552             _titleContextMenu.show(_diagram, x, y);
2553         }
2554     }
2555 
2556     /**
2557      * {@inheritDoc}
2558      */
2559     public boolean isInToggleArea(TimeBarNode node, int x, int y) {
2560         return true;
2561     }
2562 
2563     /**
2564      * {@inheritDoc}
2565      */
2566     public boolean isInHierarchySelectionArea(TimeBarNode node, int x, int y) {
2567         return false;
2568     }
2569 
2570     /**
2571      * Set the drawing mode.
2572      * 
2573      * @param drawOverlapping if set to true intervals will be painted on another. If set to false, every interval will
2574      * only get a fraction of the space corresponding to the count of overlapping intervals.
2575      */
2576     public void setDrawOverlapping(boolean drawOverlapping) {
2577         _delegate.setDrawOverlapping(drawOverlapping);
2578     }
2579 
2580     /**
2581      * Retrieve the drawing mode.
2582      * 
2583      * @return the drawing mode
2584      */
2585     public boolean getDrawOverlapping() {
2586         return _delegate.isDrawOverlapping();
2587     }
2588 
2589     /**
2590      * {@inheritDoc}
2591      */
2592     public void addMarker(TimeBarMarker marker) {
2593         _delegate.addMarker(marker);
2594     }
2595 
2596     /**
2597      * {@inheritDoc}
2598      */
2599     public void remMarker(TimeBarMarker marker) {
2600         _delegate.remMarker(marker);
2601     }
2602 
2603     /**
2604      * {@inheritDoc}
2605      */
2606     public List<TimeBarMarker> getMarkers() {
2607         return _delegate.getMarkers();
2608     }
2609 
2610     /**
2611      * {@inheritDoc}
2612      */
2613     public void addMarkers(List<TimeBarMarker> markers) {
2614         _delegate.addMarkers(markers);
2615     }
2616 
2617     /**
2618      * {@inheritDoc}
2619      */
2620     public int getSelectionDelta() {
2621         return _delegate.getSelectionDelta();
2622     }
2623 
2624     /**
2625      * {@inheritDoc}
2626      */
2627     public void setSelectionDelta(int selectionDelta) {
2628         _delegate.setSelectionDelta(selectionDelta);
2629     }
2630 
2631     /**
2632      * {@inheritDoc}
2633      */
2634     public boolean isLineDraggingAllowed() {
2635         return _delegate.isLineDraggingAllowed();
2636     }
2637 
2638     /**
2639      * {@inheritDoc}
2640      */
2641     public void setLineDraggingAllowed(boolean lineDraggingAllowed) {
2642         _delegate.setLineDraggingAllowed(lineDraggingAllowed);
2643     }
2644 
2645     /**
2646      * {@inheritDoc}
2647      */
2648     public int getYForRow(TimeBarRow row) {
2649         return _delegate.yForRow(row);
2650     }
2651 
2652     /**
2653      * {@inheritDoc}. This is the same as rowForY.
2654      */
2655     public TimeBarRow getRowForY(int y) {
2656         return _delegate.rowForY(y);
2657     }
2658 
2659     /**
2660      * {@inheritDoc}
2661      */
2662     public TimeBarRow getRowForXY(int x, int y) {
2663         return _delegate.rowForXY(x, y);
2664     }
2665 
2666     /**
2667      * {@inheritDoc}
2668      */
2669     public boolean isMilliAccuracy() {
2670         return _delegate.isMilliAccuracy();
2671     }
2672 
2673     /**
2674      * {@inheritDoc}
2675      */
2676     public void setMilliAccuracy(boolean milliAccuracy) {
2677         _delegate.setMilliAccuracy(milliAccuracy);
2678     }
2679 
2680     /**
2681      * {@inheritDoc}
2682      */
2683     public TimeBarNode getPpsRow() {
2684         return _delegate.getPpsRow();
2685     }
2686 
2687     /**
2688      * {@inheritDoc}
2689      */
2690     public boolean hasVariableXScale() {
2691         return _delegate.hasVariableXScale();
2692     }
2693 
2694     /**
2695      * {@inheritDoc}
2696      */
2697     public void setVariableXScale(boolean state) {
2698         _delegate.setVariableXScale(state);
2699     }
2700 
2701     /**
2702      * {@inheritDoc} Includes the timescale if present.
2703      */
2704     public void doScrollHorizontal(int diff) {
2705         Graphics g = _diagram.getGraphics();
2706         if (g == null) {
2707             return;
2708         }
2709         if (_delegate.getDiagramRect() == null) {
2710             return;
2711         }
2712 
2713         Rectangle d = new Rectangle(_delegate.getDiagramRect());
2714         if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
2715             if (_delegate.getTimeScalePosition() != TIMESCALE_POSITION_NONE) {
2716                 if (_delegate.getTimeScalePosition() == TIMESCALE_POSITION_TOP) {
2717                     d.y = _delegate.getXAxisRect().y;
2718                 }
2719                 d.height += _delegate.getXAxisRect().height;
2720             }
2721         } else {
2722             d.y = 0;
2723             d.height = d.height + _delegate.getHierarchyWidth() + _delegate.getYAxisWidth();
2724         }
2725 
2726         if (diff > 0) {
2727             // to the right
2728             g.copyArea(d.x + diff, d.y, d.width - diff, d.height, -diff, 0);
2729             _diagram.repaint(d.x + d.width - diff, d.y, diff, d.height);
2730         } else {
2731             diff = -diff;
2732             g.copyArea(d.x, d.y, d.width - diff, d.height, diff, 0);
2733             _diagram.repaint(d.x, d.y, diff, d.height);
2734         }
2735         g.dispose();
2736     }
2737 
2738     /**
2739      * {@inheritDoc} Includes header and/or hierarchy if present.
2740      */
2741     public void doScrollVertical(int diff) {
2742         Graphics g = _diagram.getGraphics();
2743         if (g == null) {
2744             return;
2745         }
2746 
2747         if (_delegate.getDiagramRect() == null) {
2748             return;
2749         }
2750 
2751         Rectangle d = new Rectangle(_delegate.getDiagramRect());
2752         if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
2753             d.x = d.x - _delegate.getHierarchyWidth() - _delegate.getYAxisWidth();
2754             d.width = d.width + _delegate.getHierarchyWidth() + _delegate.getYAxisWidth();
2755         } else {
2756             if (_delegate.getTimeScalePosition() == TIMESCALE_POSITION_TOP) {
2757                 d.x = d.x - _delegate.getXAxisHeight();
2758             }
2759             d.width = d.width + _delegate.getXAxisHeight();
2760         }
2761         if (diff > 0) {
2762             // downwards
2763             g.copyArea(d.x, d.y + diff, d.width, d.height - diff, 0, -diff);
2764             _diagram.repaint(d.x, d.y + d.height - diff, d.width, diff);
2765         } else {
2766             diff = -diff;
2767             g.copyArea(d.x, d.y, d.width, d.height - diff, 0, diff);
2768             _diagram.repaint(d.x, d.y, d.width, diff);
2769         }
2770         g.dispose();
2771     }
2772 
2773     /**
2774      * {@inheritDoc}
2775      */
2776     public boolean getOptimizeScrolling() {
2777         return _delegate.getOptimizeScrolling();
2778     }
2779 
2780     /**
2781      * {@inheritDoc}
2782      */
2783     public void setOptimizeScrolling(boolean optimizeScrolling) {
2784         _delegate.setOptimizeScrolling(optimizeScrolling);
2785     }
2786 
2787     /**
2788      * {@inheritDoc}
2789      */
2790     public Orientation getTBOrientation() {
2791         return _delegate.getOrientation();
2792     }
2793 
2794     /**
2795      * {@inheritDoc}
2796      */
2797     public void setTBOrientation(Orientation orientation) {
2798         _delegate.setOrientation(orientation);
2799     }
2800 
2801     /**
2802      * {@inheritDoc}
2803      */
2804     public int getAutoScaleRows() {
2805         return _delegate.getAutoScaleRows();
2806     }
2807 
2808     /**
2809      * {@inheritDoc}
2810      */
2811     public void setAutoScaleRows(int rows) {
2812         _delegate.setAutoScaleRows(rows);
2813     }
2814 
2815     /**
2816      * {@inheritDoc}
2817      */
2818     public int getXAxisHeight() {
2819         return _delegate.getXAxisHeight();
2820     }
2821 
2822     /**
2823      * {@inheritDoc}
2824      */
2825     public void setXAxisHeight(int height) {
2826         _delegate.setXAxisHeight(height);
2827     }
2828 
2829     /**
2830      * {@inheritDoc}
2831      */
2832     public void fireSelectionChanged() {
2833         // nothing to do in the swing version
2834     }
2835 
2836     /**
2837      * {@inheritDoc}
2838      */
2839     public void addTimeBarChangeListener(ITimeBarChangeListener listener) {
2840         _delegate.addTimeBarChangeListener(listener);
2841     }
2842 
2843     /**
2844      * {@inheritDoc}
2845      */
2846     public void removeTimeBarChangeListener(ITimeBarChangeListener listener) {
2847         _delegate.removeTimeBarChangeListener(listener);
2848     }
2849 
2850     /**
2851      * {@inheritDoc}
2852      */
2853     public void addFocussedIntervalListener(FocussedIntervalListener listener) {
2854         _delegate.addFocussedIntervalListener(listener);
2855     }
2856 
2857     /**
2858      * {@inheritDoc}
2859      */
2860     public void remFocussedIntervalListener(FocussedIntervalListener listener) {
2861         _delegate.remFocussedIntervalListener(listener);
2862     }
2863 
2864     /**
2865      * {@inheritDoc}
2866      */
2867     public ITimeBarViewState getTimeBarViewState() {
2868         return _delegate.getTimeBarViewState();
2869     }
2870 
2871     /**
2872      * {@inheritDoc}
2873      */
2874     public boolean isRowHeightDragginAllowed() {
2875         return _delegate.isRowHeightDraggingAllowed();
2876     }
2877 
2878     /**
2879      * {@inheritDoc}
2880      */
2881     public void setRowHeightDraggingAllowed(boolean rowHeightDraggingAllowed) {
2882         _delegate.setRowHeightDraggingAllowed(rowHeightDraggingAllowed);
2883     }
2884 
2885     /**
2886      * {@inheritDoc}
2887      */
2888     public boolean rowLineHit(int x, int y) {
2889         return _delegate.rowLineHit(x, y);
2890     }
2891 
2892     /**
2893      * {@inheritDoc}
2894      */
2895     public boolean isInRowAxis(int x, int y) {
2896         return _delegate.isInRowAxis(x, y);
2897     }
2898 
2899     /**
2900      * {@inheritDoc}
2901      */
2902     public boolean isInDiagram(int x, int y) {
2903         return _delegate.isInDiagram(x, y);
2904     }
2905 
2906     /**
2907      * {@inheritDoc}
2908      */
2909     public boolean getStrictClipTimeCheck() {
2910         return _delegate.getStrictClipTimeCheck();
2911     }
2912 
2913     /**
2914      * {@inheritDoc}
2915      */
2916     public void setStrictClipTimeCheck(boolean strictClipTimeCheck) {
2917         _delegate.setStrictClipTimeCheck(strictClipTimeCheck);
2918     }
2919 
2920     /**
2921      * {@inheritDoc}
2922      */
2923     public int getSecondsDisplayed() {
2924         return _delegate.getSecondsDisplayed();
2925     }
2926 
2927     /**
2928      * {@inheritDoc}
2929      */
2930     public IOverlapStrategy getOverlapStrategy() {
2931         return _delegate.getOverlapStrategy();
2932     }
2933 
2934     /**
2935      * {@inheritDoc}
2936      */
2937     public void setOverlapStrategy(IOverlapStrategy overlapStrategy) {
2938         _delegate.setOverlapStrategy(overlapStrategy);
2939     }
2940 
2941     /**
2942      * {@inheritDoc}
2943      */
2944     public int getScrollLookBackMinutes() {
2945         return _delegate.getScrollLookBackMinutes();
2946     }
2947 
2948     /**
2949      * {@inheritDoc}
2950      */
2951     public void setScrollLookBackMinutes(int scrollLookBackMinutes) {
2952         _delegate.setScrollLookBackMinutes(scrollLookBackMinutes);
2953     }
2954 
2955     /**
2956      * {@inheritDoc}
2957      */
2958     public void setScrollLookForwardMinutes(int scrollLookForwardMinutes) {
2959         _delegate.setScrollLookForwardMinutes(scrollLookForwardMinutes);
2960     }
2961 
2962     /**
2963      * {@inheritDoc}
2964      */
2965     public int getScrollLookForwardMinutes() {
2966         return _delegate.getScrollLookForwardMinutes();
2967     }
2968 
2969     /**
2970      * {@inheritDoc}
2971      */
2972     public String getName() {
2973         // null check becuase of the gtk plaf that calls getName before the component is fully initialized
2974         if (_delegate != null) {
2975             return _delegate.getName();
2976         } else {
2977             return null;
2978         }
2979     }
2980 
2981     /**
2982      * {@inheritDoc}
2983      */
2984     public void setName(String name) {
2985         _delegate.setName(name);
2986     }
2987 
2988     /**
2989      * {@inheritDoc}
2990      */
2991     public TimeBarViewerDelegate getDelegate() {
2992         return _delegate;
2993     }
2994 
2995     /**
2996      * {@inheritDoc}
2997      */
2998     public int getAutoscrollDelta() {
2999         return _delegate.getAutoscrollDelta();
3000     }
3001 
3002     /**
3003      * {@inheritDoc}
3004      */
3005     public void setAutoscrollDelta(int autoscrollDelta) {
3006         _delegate.setAutoscrollDelta(autoscrollDelta);
3007     }
3008 
3009     /**
3010      * {@inheritDoc}
3011      */
3012     public boolean getDragAllSelectedIntervals() {
3013         return _delegate.getDragAllSelectedIntervals();
3014     }
3015 
3016     /**
3017      * {@inheritDoc}
3018      */
3019     public void setDragAllSelectedIntervals(boolean dragAllSelectedIntervals) {
3020         _delegate.setDragAllSelectedIntervals(dragAllSelectedIntervals);
3021     }
3022 
3023     /**
3024      * {@inheritDoc}
3025      */
3026     public List<IIntervalRelation> getRelationsForCoord(int x, int y) {
3027         if (_relationRenderer != null) {
3028             return _relationRenderer.getRelationsForCoord(x, y);
3029         } else {
3030             return null;
3031         }
3032     }
3033 
3034     /**
3035      * {@inheritDoc}
3036      */
3037     public String getRelationTooltip(int x, int y) {
3038         if (_relationRenderer != null) {
3039             return _relationRenderer.getTooltip(x, y);
3040         }
3041         return null;
3042     }
3043 
3044     /**
3045      * {@inheritDoc}
3046      */
3047     public boolean getScrollOnFocus() {
3048         return _delegate.getScrollOnFocus();
3049     }
3050 
3051     /**
3052      * {@inheritDoc}
3053      */
3054     public void setScrollOnFocus(boolean scrollOnFocus) {
3055         _delegate.setScrollOnFocus(scrollOnFocus);
3056     }
3057 
3058     /**
3059      * {@inheritDoc}
3060      */
3061     public void addSelectionRectListener(ISelectionRectListener listener) {
3062         _delegate.addSelectionRectListener(listener);
3063     }
3064 
3065     /**
3066      * {@inheritDoc}
3067      */
3068     public void remSelectionRectListener(ISelectionRectListener listener) {
3069         _delegate.remSelectionRectListener(listener);
3070     }
3071 
3072     /**
3073      * {@inheritDoc}
3074      */
3075     public boolean getHideRoot() {
3076         return _delegate.getHideRoot();
3077     }
3078 
3079     /**
3080      * {@inheritDoc}
3081      */
3082     public void setHideRoot(boolean hideRoot) {
3083         _delegate.setHideRoot(hideRoot);
3084     }
3085 
3086     /**
3087      * {@inheritDoc}
3088      */
3089     public boolean getMarkerDraggingInDiagramArea() {
3090         return _delegate.getMarkerDraggingInDiagramArea();
3091     }
3092 
3093     /**
3094      * {@inheritDoc}
3095      */
3096     public void setMarkerDraggingInDiagramArea(boolean allowed) {
3097         _delegate.setMarkerDraggingInDiagramArea(allowed);
3098     }
3099 
3100     /**
3101      * {@inheritDoc}
3102      */
3103     public void clearRegionRect() {
3104         _delegate.clearRegionRect();
3105     }
3106 
3107     /**
3108      * {@inheritDoc}
3109      */
3110     public TBRect getRegionRect() {
3111         return _delegate.getRegionRect();
3112     }
3113 
3114     /**
3115      * {@inheritDoc}
3116      */
3117     public boolean getRegionRectEnable() {
3118         return _delegate.getRegionRectEnable();
3119     }
3120 
3121     /**
3122      * {@inheritDoc}
3123      */
3124     public void setRegionRectEnable(boolean enabled) {
3125         _delegate.setRegionRectEnable(enabled);
3126     }
3127 
3128     /**
3129      * {@inheritDoc} Repaint a rectangle. Overridden and extended widht and height by 1.
3130      */
3131     public void repaint(Rectangle r) {
3132         super.repaint(r.x, r.y, r.width + 1, r.height + 1);
3133     }
3134 
3135     /**
3136      * {@inheritDoc}
3137      */
3138     public boolean getUseUniformHeight() {
3139         return _delegate.getUseUniformHeight();
3140     }
3141 
3142     /**
3143      * {@inheritDoc}
3144      */
3145     public void setUseUniformHeight(boolean useUniformHeight) {
3146         _delegate.setUseUniformHeight(useUniformHeight);
3147     }
3148 
3149     /**
3150      * {@inheritDoc}
3151      */
3152     public void setSecondsDisplayed(int seconds, boolean center) {
3153         _delegate.setSecondsDisplayed(seconds, center);
3154     }
3155 
3156     /**
3157      * {@inheritDoc}
3158      */
3159     public void setSecondsDisplayed(int seconds, JaretDate centerDate) {
3160         _delegate.setSecondsDisplayed(seconds, centerDate);
3161     }
3162 
3163     /**
3164      * {@inheritDoc}
3165      */
3166     public boolean isDisplayed(JaretDate date) {
3167         return _delegate.isDisplayed(date);
3168     }
3169 
3170     /**
3171      * {@inheritDoc}
3172      */
3173     public void setInitialDisplayRange(JaretDate startDate, int secondsDisplayed) {
3174         _delegate.setInitialDisplayRange(startDate, secondsDisplayed);
3175     }
3176 
3177     /**
3178      * Retrieve the panel that the horizontal scroll bar is plcaed on (BorderLayout, CENETER). This can be used to add
3179      * special extensions in the scroll bar area.
3180      * 
3181      * @return th panel or <code>null</code> if the scroll bar has been suppressed
3182      */
3183     public JPanel getHorizontalScrollPanel() {
3184         return _horizontalScrollPanel;
3185     }
3186 
3187     /**
3188      * Retrieve the panel that the vertical scroll bar is plcaed on (BorderLayout, CENETER). This can be used to add
3189      * special extensions in the scroll bar area.
3190      * 
3191      * @return th panel or <code>null</code> if the scroll bar has been suppressed
3192      */
3193     public JPanel getVerticalScrollPanel() {
3194         return _verticalScrollPanel;
3195     }
3196 
3197     /**
3198      * {@inheritDoc}
3199      */
3200     public Pair<TimeBarRow, JaretDate> getPopUpInformation() {
3201         return _delegate.getPopUpInformation();
3202     }
3203 
3204     /** list of ghost intervals to be painted. */
3205     protected List<? extends Interval> _ghostIntervals;
3206     /** y offsets for the ghost intervals. */
3207     protected List<Integer> _ghostIntervalYCoordinates;
3208     /**
3209      * the origin for painting the ghost intervals/rows. The ghosted elements will paintetd relative to the y
3210      * coordinate.
3211      */
3212     protected Point _ghostOrigin;
3213     /** list of ghost rows to paint. */
3214     protected List<TimeBarRow> _ghostRows;
3215     /** y offsets for the ghost rows. */
3216     protected List<Integer> _ghostRowYCoordinates;
3217 
3218     /**
3219      * Set the list of ghost intervals to be drawn.
3220      * 
3221      * @param intervals list of intervals or <code>null</code> to delete ghosted intervals.
3222      * @param yCoordinates list of y offsets for the ghost intervals (maybe <code>null</code> when deleting ghost
3223      * intervals
3224      */
3225     public void setGhostIntervals(List<? extends Interval> intervals, List<Integer> yCoordinates) {
3226         _ghostIntervals = intervals;
3227         _ghostIntervalYCoordinates = yCoordinates;
3228         repaint();
3229     }
3230 
3231     /**
3232      * Draw ghost intervals and rows.
3233      * 
3234      * @param gc GC
3235      */
3236     private void drawGhosts(Graphics gc) {
3237         drawGhostIntervals(gc);
3238         drawGhostRows(gc);
3239     }
3240 
3241     /**
3242      * Draw ghost intervals. Y positions are dependant from the ghost y offsets and the ghost origin. (Always uses the
3243      * defaultRowHeight.)
3244      * 
3245      * @param gc GC
3246      */
3247     private void drawGhostIntervals(Graphics gc) {
3248         if (_ghostOrigin != null && _ghostIntervals != null) {
3249             for (int i = 0; i < _ghostIntervals.size(); i++) {
3250                 Interval interval = _ghostIntervals.get(i);
3251                 int yoff = _ghostIntervalYCoordinates.get(i);
3252 
3253                 Rectangle drawingArea = _delegate.getIntervalBounds(-1, interval);
3254                 if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
3255                     drawingArea.y = _ghostOrigin.y + yoff;
3256                     drawingArea.height = _delegate.getTimeBarViewState().getDefaultRowHeight();
3257                 } else {
3258                     drawingArea.x = _ghostOrigin.x + yoff;
3259                     drawingArea.width = _delegate.getTimeBarViewState().getDefaultRowHeight();
3260                 }
3261                 Graphics2D g2 = (Graphics2D) gc;
3262                 Color color = g2.getColor();
3263                 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
3264 
3265                 TimeBarRenderer renderer = getRenderer(interval.getClass());
3266                 if (renderer == null) {
3267                     throw new RuntimeException("no suitable renderer");
3268                 }
3269 
3270                 Component component = renderer.getTimeBarRendererComponent(this, interval, false, false);
3271 
3272                 component.setBounds(drawingArea);//
3273 
3274                 Graphics2D g22 = (Graphics2D) g2.create(drawingArea.x, drawingArea.y - _delegate.getRowHeight() / 2,
3275                         drawingArea.width, drawingArea.height);
3276                 component.paint(g22);
3277 
3278                 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
3279                 g2.setColor(color);
3280 
3281             }
3282         }
3283     }
3284 
3285     /**
3286      * Draw ghost rows. Y positions are dependent from the ghost y offsets and the ghost origin.
3287      * 
3288      * @param gc GC
3289      */
3290     private void drawGhostRows(Graphics gc) {
3291         if (_ghostOrigin != null && _ghostRows != null) {
3292 
3293             Graphics2D g2 = (Graphics2D) gc;
3294             Color color = g2.getColor();
3295             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
3296 
3297             for (int i = 0; i < _ghostRows.size(); i++) {
3298                 TimeBarRow row = _ghostRows.get(i);
3299                 int yoff = _ghostRowYCoordinates.get(i);
3300 
3301                 if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
3302                     int y = _ghostOrigin.y + yoff;
3303                     int height = _delegate.getRowHeight(); // default height
3304                     _diagram.drawRow(g2, y, height, row, false);
3305                 } else {
3306                     int x = _ghostOrigin.x + yoff;
3307                     int width = _delegate.getRowHeight(); // default height
3308                     _diagram.drawRowVertical(g2, x, width, row, false);
3309                 }
3310             }
3311             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
3312             g2.setColor(color);
3313         }
3314     }
3315 
3316     /**
3317      * Set the origin (current drag position) to shift the ghost elements.
3318      * 
3319      * @param x x coordinate
3320      * @param y y coordniate
3321      */
3322     public void setGhostOrigin(int x, int y) {
3323         _ghostOrigin = new Point(x, y);
3324         if (_ghostIntervals != null || _ghostRows != null) {
3325             repaint();
3326         }
3327     }
3328 
3329     /**
3330      * If set to true this will cause the title renderer component to be used directly instead of beeing just used to
3331      * paint.
3332      * 
3333      * @param useTitleRendererComponentInPlace true for direct use of the component
3334      */
3335     public void setUseTitleRendererComponentInPlace(boolean useTitleRendererComponentInPlace) {
3336         _useTitleRendererComponentInPlace = useTitleRendererComponentInPlace;
3337     }
3338 
3339     /**
3340      * {@inheritDoc}
3341      */
3342     public int scrollDateToVisible(JaretDate date) {
3343         return _delegate.scrollDateToVisible(date);
3344     }
3345 
3346     /**
3347      * {@inheritDoc}
3348      */
3349     public void scrollRowToVisible(TimeBarRow row) {
3350         _delegate.scrollRowToVisible(row);
3351     }
3352 
3353     /**
3354      * {@inheritDoc}
3355      */
3356     public void scrollIntervalToVisible(TimeBarRow row, Interval interval) {
3357         _delegate.scrollIntervalToVisible(row, interval);
3358     }
3359 
3360 }