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 1107 2012-02-09 22:31:29Z 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 1107 2012-02-09 22:31:29Z 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             
682             _delegate.preparePaint(getWidth(), getHeight()); // prepare the geometry
683 
684             g.setColor(Color.WHITE);
685             g.fillRect(g.getClipBounds().x, g.getClipBounds().y, g.getClipBounds().width, g.getClipBounds().height);
686             g.setColor(Color.BLACK);
687 
688             // draw x axis if enabled
689             if (_delegate.getTimeScalePosition() != TIMESCALE_POSITION_NONE) {
690                 drawXAxis(g);
691             }
692             drawGrid(g); // draw the grid if enabled
693 
694             // kick in the global assistant renderer
695             if (_globalAssistantRenderer != null) {
696                 _globalAssistantRenderer.doRenderingBeforeIntervals(_delegate, g);
697             }
698 
699             // draw rows (including gaps)
700             drawRows(g);
701             // draw markers
702             drawMarkers(g);
703 
704             // draw selection rectangle
705             Rectangle clipSave = g.getClipBounds();
706             g.setClip(clipSave.intersection(_delegate.getDiagramRect()));
707             drawSelectionRect(g);
708             g.setClip(clipSave);
709 
710             // draw region rect
711             clipSave = g.getClipBounds();
712             g.setClip(clipSave.intersection(_delegate.getDiagramRect()));
713             _miscRenderer.renderRegionRect(g, this._timeBarViewer, _delegate);
714             g.setClip(clipSave);
715 
716          // draw the title if a title renderer has been set
717             if (_titleRenderer != null) {
718                 JComponent titleComponent = _titleRenderer.getTitleRendererComponent(this._timeBarViewer);
719                 if (!_useTitleRendererComponentInPlace) {
720                     titleComponent.setBounds(_delegate.getTitleRect());
721                     titleComponent.paint(g);
722                 } else {
723                     boolean added = false;
724                     if (titleComponent.getParent() == null) {
725                         add(titleComponent);
726                         added = true;
727                     }
728                     titleComponent.setBounds(_delegate.getTitleRect());
729                     titleComponent.doLayout();
730                     // no double painting for the title renderer; only paint the first time
731                     if (added)
732                         titleComponent.paintAll(g);
733                 }
734             }
735             
736             // kick in the global assistant renderer
737             if (_globalAssistantRenderer != null) {
738                 _globalAssistantRenderer.doRenderingLast(_delegate, g);
739             }
740 
741             // primitive debug for monitoring paint time
742             if (SHOWPAINTTIME) {
743                 System.out.println(_delegate.getName() + " : paintTime " + (System.currentTimeMillis() - time) + " ms "
744                         + (System.nanoTime() - nanoTime) + " ns");
745             }
746         }
747 
748         /**
749          * Draws the selection rectangle if present.
750          * 
751          * @param g graphics
752          */
753         private void drawSelectionRect(Graphics g) {
754             if (_delegate.getSelectionRect() != null) {
755                 // normalize and remember
756                 _delegate.setLastSelRect(normalizeRectangle(_delegate.getSelectionRect()));
757                 Rectangle lastSelRect = _delegate.getLastSelRect();
758                 _miscRenderer.renderSelectionRect(g, this._timeBarViewer, lastSelRect);
759             }
760 
761         }
762 
763         /**
764          * Normalize a rectangle.
765          * 
766          * @param rect rectangle to normalize
767          * @return normalized (pos width nd heigth) rectangle
768          */
769         private Rectangle normalizeRectangle(Rectangle rect) {
770             int x = Math.min(rect.x, rect.x + rect.width);
771             int y = Math.min(rect.y, rect.y + rect.height);
772             int width = Math.abs(rect.width);
773             int height = Math.abs(rect.height);
774             return new Rectangle(x, y, width, height);
775         }
776 
777         /**
778          * Draws all markers for the diagram. If a marker is not currently displayed it will not be painted.
779          * 
780          * @param g Graphics to use
781          */
782         private void drawMarkers(Graphics g) {
783             if (_delegate.getMarkers() != null) {
784                 for (TimeBarMarker marker : _delegate.getMarkers()) {
785                     if (_delegate.isDisplayed(marker.getDate())) {
786                         drawMarker(g, marker);
787                     }
788                 }
789             }
790         }
791 
792         /**
793          * Draw a single marker.
794          * 
795          * @param g Graphics to be used
796          * @param marker marker to draw
797          */
798         private void drawMarker(Graphics g, TimeBarMarker marker) {
799             if (_markerRenderer != null) {
800                 int x = _delegate.xForDate(marker.getDate());
801                 boolean dragged = _delegate.getDraggedMarker() == marker;
802                 _markerRenderer.renderMarker(_delegate, g, marker, x, dragged);
803             }
804         }
805 
806         /**
807          * Draws the x axis using the time scale renderer.
808          * 
809          * @param g Graphics context to use
810          */
811         private void drawXAxis(Graphics g) {
812             if (_timeScaleRenderer != null) {
813                 _timeScaleRendererComponent = _timeScaleRenderer.getRendererComponent(_timeBarViewer, _delegate
814 
815                 .getTimeScalePosition() == TimeBarViewerInterface.TIMESCALE_POSITION_TOP);
816                 _timeScaleRendererComponent.setBounds(_delegate.getXAxisRect());
817                 Graphics gg = g.create(_delegate.getXAxisRect().x, _delegate.getXAxisRect().y,
818                         _delegate.getXAxisRect().width, _delegate.getXAxisRect().height);
819                 _timeScaleRendererComponent.paint(gg);
820                 gg.dispose();
821             }
822         }
823 
824         /**
825          * Draws the grid/background using the gridRenderer.
826          * 
827          * @param g graphics
828          */
829         private void drawGrid(Graphics g) {
830             if (_gridRenderer != null) {
831                 _gridRendererComponent = _gridRenderer.getRendererComponent(_timeBarViewer);
832                 _gridRendererComponent.setBounds(_delegate.getDiagramRect());
833                 Graphics gg = g.create(_delegate.getDiagramRect().x, _delegate.getDiagramRect().y,
834                         _delegate.getDiagramRect().width, _delegate.getDiagramRect().height);
835                 _gridRendererComponent.paint(gg);
836                 gg.dispose();
837             }
838         }
839 
840         /**
841          * Draw all rows. Rows outside the clipping bounds of the graphics context are not painted.
842          * 
843          * @param g Graphics to use
844          */
845         private void drawRows(Graphics g) {
846             if (_delegate.getOrientation() == Orientation.HORIZONTAL) {
847                 drawRowsHorizontal(g);
848             } else {
849                 drawRowsVertical(g);
850             }
851             // relation rendering
852             if (_relationRenderer != null) {
853                 _relationRenderer.renderRelations(_delegate, g);
854             }
855             drawGhostIntervals(g);
856         }
857 
858         /**
859          * Draw rows for horizontal orientation.
860          * 
861          * @param g Graphics to use
862          */
863         private void drawRowsHorizontal(Graphics g) {
864             g.setColor(Color.BLACK);
865 
866             // set the clipping to include only the heigth of the diagram rect
867             Rectangle clipSave = g.getClipBounds();
868 
869             Rectangle newClip = new Rectangle(0, _delegate.getDiagramRect().y, getWidth(), _delegate.getDiagramRect().height).intersection(g.getClipBounds());
870             g.setClip(newClip);
871 
872             
873 //            g.setClip(0, _delegate.getDiagramRect().y, getWidth(), _delegate.getDiagramRect().height);
874 
875             // separating line to the header
876             // MAYBE color configurable
877             g.drawLine(_delegate.getDiagramRect().x - 1, 0, _delegate.getDiagramRect().x - 1, getHeight());
878             g.drawLine(_delegate.getHierarchyRect().x + _delegate.getHierarchyWidth() - 1, 0,
879                     _delegate.getHierarchyRect().x + _delegate.getHierarchyWidth() - 1, getHeight());
880 
881             int upperYBound = _delegate.getDiagramRect().y;
882             int lowerYBound = upperYBound + _delegate.getDiagramRect().height;
883             if (g.getClipBounds() != null) {
884                 upperYBound = g.getClipBounds().y;
885                 lowerYBound = upperYBound + g.getClipBounds().height;
886             }
887             for (int r = _delegate.getFirstRow(); r <= _delegate.getFirstRow() + _delegate.getRowsDisplayed()
888                     && r < _delegate.getRowCount(); r++) {
889                 TimeBarRow row = _delegate.getRow(r);
890                 int rowHeight = _delegate.getTimeBarViewState().getRowHeight(row);
891                 // int y = (r - _delegate.getFirstRow()) * rowHeight + _delegate.getDiagramRect().y
892                 // - _delegate.getFirstRowOffset();
893                 int y = _delegate.yForRow(row);
894                 if (y + rowHeight < 0) {
895                     // Bypass rows with the bottom pixel above the drawing area
896                     continue;
897                 }
898 
899                 // row is drawn if either the beginning or the end is inside the
900                 // clipping rect
901                 // or if the upperBound is inside the row rect (clipping rect is
902                 // inside the row rect
903                 if ((y >= upperYBound && y <= lowerYBound)
904                         || (y + rowHeight >= upperYBound && y + rowHeight <= lowerYBound)
905                         || (upperYBound > y && upperYBound < y + rowHeight)) {
906                     drawRow(g, y, rowHeight, _delegate.getRow(r),
907                             _delegate.getSelectionModel().isSelected(_delegate.getRow(r)));
908                     // draw gaps if a renderer is set
909                     if (_gapRenderer != null) {
910                         drawRowGaps(g, y, rowHeight, _delegate.getRow(r),
911                                 _delegate.getSelectionModel().isSelected(_delegate.getRow(r)));
912                     }
913                 }
914                 if (y + rowHeight > lowerYBound) {
915                     // Omit all further checks if the row would be drawn off the bottom of the screen
916                     break;
917                 }
918             }
919             g.setClip(clipSave);
920         }
921 
922         /**
923          * Draw rows for vertical orientation.
924          * 
925          * @param g Graphics to use
926          */
927         private void drawRowsVertical(Graphics g) {
928             g.setColor(Color.BLACK);
929 
930             // set the clipping to include only the heigth of the diagram rect
931             Rectangle clipSave = g.getClipBounds();
932             
933             Rectangle newClip = new Rectangle(0, _delegate.getDiagramRect().y, getWidth(), _delegate.getDiagramRect().height).intersection(g.getClipBounds());
934             g.setClip(newClip);
935             //g.setClip(0, _delegate.getDiagramRect().y, getWidth(), _delegate.getDiagramRect().height);
936 
937             // separating line to the header
938             // MAYBE coloro configurable
939             g.drawLine(0, _delegate.getDiagramRect().y - 1, getWidth(), _delegate.getDiagramRect().y - 1);
940             g.drawLine(0, _delegate.getHierarchyRect().y + _delegate.getHierarchyWidth() - 1, getWidth(),
941                     _delegate.getHierarchyRect().y + _delegate.getHierarchyWidth() - 1);
942 
943             int leftXBound = _delegate.getDiagramRect().x;
944             int rightXBound = leftXBound + _delegate.getDiagramRect().width;
945             if (g.getClipBounds() != null) {
946                 leftXBound = g.getClipBounds().x;
947                 rightXBound = leftXBound + g.getClipBounds().width;
948             }
949 
950             for (int r = _delegate.getFirstRow(); r <= _delegate.getFirstRow() + _delegate.getRowsDisplayed()
951                     && r < _delegate.getRowCount(); r++) {
952                 TimeBarRow row = _delegate.getRow(r);
953                 int rowWidth = _delegate.getTimeBarViewState().getRowHeight(row);
954                 // int x = (r - _delegate.getFirstRow()) * rowWidth + _delegate.getDiagramRect().x
955                 // - _delegate.getFirstRowOffset();
956 
957                 int x = _delegate.yForRow(row);
958                 if (x + rowWidth < 0) {
959                     // Bypass columns with the right pixel to the left of the drawing area
960                     continue;
961                 }
962 
963                 // row is drawn if either the beginning or the end is inside the
964                 // clipping rect
965                 // or if the upperBound is inside the row rect (clipping rect is
966                 // inside the row rect
967                 if ((x >= leftXBound && x <= rightXBound)
968                         || (x + rowWidth >= leftXBound && x + rowWidth <= rightXBound)
969                         || (leftXBound > x && leftXBound < x + rowWidth)) {
970                     drawRowVertical(g, x, rowWidth, _delegate.getRow(r),
971                             _delegate.getSelectionModel().isSelected(_delegate.getRow(r)));
972                     // draw gaps if a renderer is set
973                     if (_gapRenderer != null) {
974                         drawRowGapsVertical(g, x, rowWidth, _delegate.getRow(r), _delegate.getSelectionModel()
975                                 .isSelected(_delegate.getRow(r)));
976                     }
977                 }
978                 if (x + rowWidth > rightXBound) {
979                     // Omit all further checks if the column would be drawn off the right of the screen
980                     break;
981                 }
982             }
983             g.setClip(clipSave);
984         }
985 
986         /**
987          * Draw a single row (horizontal).
988          * 
989          * @param g graphics
990          * @param y starting pos y
991          * @param height height
992          * @param row row to draw
993          * @param selected true if the row is selected
994          */
995         private void drawRow(Graphics g, int y, int height, TimeBarRow row, boolean selected) {
996             // draw the row header
997             drawRowHeader(g, y, height, row.getRowHeader(), selected);
998             // draw the hierarchy area
999             drawHierarchy(g, y, height, row, selected);
1000             // draw a line at the bottom of the row if enabled
1001             Rectangle diagramRect = _delegate.getDiagramRect();
1002             if (_delegate.getDrawRowGrid()) {
1003                 _miscRenderer.drawRowGridLine(g, diagramRect.x, y + height - 1, diagramRect.x + diagramRect.width, y
1004                         + height - 1);
1005             }
1006 
1007             // draw a background if selected or highlighted
1008             // highlighting is at higher priority than selection
1009             if (selected || _delegate.getHighlightedRow() == row) {
1010                 boolean highlighted = _delegate.getHighlightedRow() == row;
1011                 int markerHeight = height;
1012                 // calculate height for clipping
1013                 if (y + markerHeight > diagramRect.y + diagramRect.height) {
1014                     markerHeight = markerHeight - (y + markerHeight - (diagramRect.y + diagramRect.height));
1015                 }
1016                 _miscRenderer.drawRowBackground(g, diagramRect.x, y,
1017                         getWidth() - _delegate.getYAxisWidth() - _delegate.getHierarchyWidth(), markerHeight, selected,
1018                         highlighted);
1019 
1020             }
1021             // use the clip bounds (if given) to shorten the region to be
1022             // painted
1023             JaretDate start = _delegate.getStartDate();
1024             JaretDate end = _delegate.getEndDate();
1025             if (g.getClipBounds() != null) {
1026                 start = _delegate.dateForCoord(g.getClipBounds().x);
1027                 end = _delegate.dateForCoord(g.getClipBounds().x + g.getClipBounds().width);
1028             }
1029             // List intervals = row.getIntervals(_startDate, _endDate);
1030             List<Interval> intervals = row.getIntervals(start, end);
1031             for (Interval i : intervals) {
1032                 // apply filter on intervals if set
1033                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(i)) {
1034                     if (_delegate.getTimeBarViewState().getDrawOverlapping(row)) {
1035                         drawInterval(g, y, height, i, _delegate.getSelectionModel().isSelected(i), null, row);
1036                     } else {
1037                         drawInterval(g, y, height, i, _delegate.getSelectionModel().isSelected(i), _delegate
1038                                 .getOverlapStrategy().getOverlapInfo(row, i), row);
1039                     }
1040                 }
1041             }
1042         }
1043 
1044         /**
1045          * Draw a single row (vertical).
1046          * 
1047          * @param g graphics
1048          * @param x starting pos x
1049          * @param width width
1050          * @param row row to draw
1051          * @param selected true if the row is selected
1052          */
1053         private void drawRowVertical(Graphics g, int x, int width, TimeBarRow row, boolean selected) {
1054             // draw the row header
1055             drawRowHeaderVertical(g, x, width, row.getRowHeader(), selected);
1056             // draw the hierarchy area
1057             drawHierarchyVertical(g, x, width, row, selected);
1058             // draw a line at the bottom of the row if enabled
1059             Rectangle diagramRect = _delegate.getDiagramRect();
1060             if (_delegate.getDrawRowGrid()) {
1061                 _miscRenderer.drawRowGridLine(g, x + width - 1, diagramRect.y, x + width - 1, diagramRect.y
1062                         + diagramRect.height);
1063             }
1064 
1065             // draw a background if selected or highlighted
1066             // highlighting is at higher priority than selection
1067             if (selected || _delegate.getHighlightedRow() == row) {
1068                 boolean highlighted = _delegate.getHighlightedRow() == row;
1069                 int markerWidth = width;
1070                 // calculate width for clipping
1071                 if (x + markerWidth > diagramRect.x + diagramRect.width) {
1072                     markerWidth = markerWidth - (x + markerWidth - (diagramRect.x + diagramRect.width));
1073                 }
1074                 _miscRenderer.drawRowBackground(g, x, diagramRect.y, markerWidth,
1075                         getHeight() - _delegate.getYAxisWidth() - _delegate.getHierarchyWidth(), selected, highlighted);
1076             }
1077             // use the clip bounds (if given) to shorten the region to be
1078             // painted
1079             JaretDate start = _delegate.getStartDate();
1080             JaretDate end = _delegate.getEndDate();
1081             if (g.getClipBounds() != null) {
1082                 start = _delegate.dateForCoord(g.getClipBounds().y);
1083                 end = _delegate.dateForCoord(g.getClipBounds().y + g.getClipBounds().height);
1084             }
1085             List<Interval> intervals = row.getIntervals(start, end);
1086             for (Interval i : intervals) {
1087                 // apply filter on intervals if set
1088                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(i)) {
1089                     if (_delegate.getTimeBarViewState().getDrawOverlapping(row)) {
1090                         drawIntervalVertical(g, x, width, i, _delegate.getSelectionModel().isSelected(i), null, row);
1091                     } else {
1092                         drawIntervalVertical(g, x, width, i, _delegate.getSelectionModel().isSelected(i), _delegate
1093                                 .getOverlapStrategy().getOverlapInfo(row, i), row);
1094                     }
1095                 }
1096             }
1097         }
1098 
1099         /**
1100          * Draws the gaps beetwenn intervals (horizontal). The selection of the bordering interval sis done inside this
1101          * method. TODO maybe move the selection routine out TODO call for edges i.e. one interval is null
1102          * 
1103          * @param g Graphics for painting
1104          * @param y y coordinate
1105          * @param height rowHeight
1106          * @param row TimeBarRow to be painted
1107          * @param selected selection status of the row (not used)
1108          */
1109         private void drawRowGaps(Graphics g, int y, int height, TimeBarRow row, boolean selected) {
1110             // use the clip bounds (if given) to shorten the region to be
1111             // painted
1112             JaretDate start = _delegate.getStartDate();
1113             JaretDate end = _delegate.getEndDate();
1114             if (g.getClipBounds() != null) {
1115                 start = _delegate.dateForCoord(g.getClipBounds().x);
1116                 end = _delegate.dateForCoord(g.getClipBounds().x + g.getClipBounds().width);
1117             }
1118             // for the gaps we need a minimum of two intervals
1119             // those has to be in the displayed intervals, so we apply the
1120             // filter first (if set) and do the selection
1121             // ourself. The alogorithm is highly dependet on the ordering of the
1122             // list! This should be guaranteed by the model
1123             List<Interval> intervals = new ArrayList<Interval>();
1124             Interval firstInterval = null;
1125             for (Interval interval : row.getIntervals()) {
1126                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(interval)) {
1127                     if (interval.getEnd().compareTo(start) < 0) {
1128                         // before the starting date: remember the nearest
1129                         // interval
1130                         if (firstInterval == null
1131                                 || start.diffSeconds(interval.getEnd()) < start.diffSeconds(firstInterval.getEnd())) {
1132                             firstInterval = interval;
1133                         }
1134                     } else if (interval.contains(start)) {
1135                         // direct hit
1136                         firstInterval = interval;
1137                     } else {
1138                         // in or after the starting date: copy the intervals
1139                         // until we have one behind the end date or
1140                         // a direct hit
1141                         // First of all, add the firstInterval to the resulting
1142                         // list once
1143                         if (firstInterval != null) {
1144                             intervals.add(firstInterval);
1145                             firstInterval = null; // we don't need the
1146                             // reference
1147                             // and by setting it to null
1148                             // we do not add it twice
1149                         }
1150                         if (interval.contains(end)) {
1151                             // direct hit
1152                             intervals.add(interval);
1153                             break; // finished
1154                         }
1155                         if (interval.getBegin().compareTo(end) > 0) {
1156                             intervals.add(interval);
1157                             break; // found an interval beginning behind the
1158                             // end
1159                             // date
1160                         } else {
1161                             // all between: add
1162                             intervals.add(interval);
1163                         }
1164                     }
1165                 }
1166             }
1167             Interval lastInterval = null;
1168             for (Interval i : intervals) {
1169                 // draw gap if there is lastInterval
1170                 if (lastInterval != null) {
1171                     drawGap(g, row, y, height, lastInterval, i);
1172                 }
1173                 // remember last interval for next turn
1174                 lastInterval = i;
1175             }
1176         }
1177 
1178         /**
1179          * Draws the gaps beetwenn intervals (horizontal). The selection of the bordering interval sis done inside this
1180          * method. TODO maybe move the selection routine out TODO call for edges i.e. one interval is null
1181          * 
1182          * @param g Graphics for painting
1183          * @param x x coordinate
1184          * @param width rowHeight
1185          * @param row TimeBarRow to be painted
1186          * @param selected selection status of the row (not used)
1187          */
1188         private void drawRowGapsVertical(Graphics g, int x, int width, TimeBarRow row, boolean selected) {
1189             // use the clip bounds (if given) to shorten the region to be
1190             // painted
1191             JaretDate start = _delegate.getStartDate();
1192             JaretDate end = _delegate.getEndDate();
1193             if (g.getClipBounds() != null) {
1194                 start = _delegate.dateForCoord(g.getClipBounds().y);
1195                 end = _delegate.dateForCoord(g.getClipBounds().y + g.getClipBounds().height);
1196             }
1197             // for the gaps we need a minimum of two intervals
1198             // those has to be in the displayed intervals, so we apply the
1199             // filter first (if set) and do the selection
1200             // ourself. The alogorithm is highly dependet on the ordering of the
1201             // list! This should be guaranteed by the model
1202             List<Interval> intervals = new ArrayList<Interval>();
1203             Interval firstInterval = null;
1204             for (Interval interval : row.getIntervals()) {
1205                 if (_delegate.getIntervalFilter() == null || _delegate.getIntervalFilter().isInResult(interval)) {
1206                     if (interval.getEnd().compareTo(start) < 0) {
1207                         // before the starting date: remember the nearest
1208                         // interval
1209                         if (firstInterval == null
1210                                 || start.diffSeconds(interval.getEnd()) < start.diffSeconds(firstInterval.getEnd())) {
1211                             firstInterval = interval;
1212                         }
1213                     } else if (interval.contains(start)) {
1214                         // direct hit
1215                         firstInterval = interval;
1216                     } else {
1217                         // in or after the starting date: copy the intervals
1218                         // until we have one behind the end date or
1219                         // a direct hit
1220                         // First of all, add the firstInterval to the resulting
1221                         // list once
1222                         if (firstInterval != null) {
1223                             intervals.add(firstInterval);
1224                             firstInterval = null; // we don't need the
1225                             // reference
1226                             // and by setting it to null
1227                             // we do not add it twice
1228                         }
1229                         if (interval.contains(end)) {
1230                             // direct hit
1231                             intervals.add(interval);
1232                             break; // finished
1233                         }
1234                         if (interval.getBegin().compareTo(end) > 0) {
1235                             intervals.add(interval);
1236                             break; // found an interval beginning behind the
1237                             // end
1238                             // date
1239                         } else {
1240                             // all between: add
1241                             intervals.add(interval);
1242                         }
1243                     }
1244                 }
1245             }
1246             Interval lastInterval = null;
1247             for (Interval i : intervals) {
1248                 // draw gap if there is lastInterval
1249                 if (lastInterval != null) {
1250                     drawGapVertical(g, row, x, width, lastInterval, i);
1251                 }
1252                 // remember last interval for next turn
1253                 lastInterval = i;
1254             }
1255         }
1256 
1257         /**
1258          * Draw a single Interval (horizontal).
1259          * 
1260          * @param g Graphics to paint with
1261          * @param y starting y
1262          * @param height height for painting
1263          * @param i interval to be painted
1264          * @param selected true if the interval should be drawn selecetd
1265          * @param oiInfo overlap information or <code>null</code> if intervals are drawn overlapping
1266          * @param row row of the interval
1267          */
1268         private void drawInterval(Graphics g, int y, int height, Interval i, boolean selected, OverlapInfo oiInfo,
1269                 TimeBarRow row) {
1270             TimeBarRenderer renderer = getRenderer(i.getClass());
1271 
1272 
1273             if (renderer == null) {
1274                 throw new RuntimeException("no suitable renderer registered for " + i.getClass().getName());
1275             }
1276 
1277             boolean overlapping = oiInfo != null && oiInfo.maxOverlapping > 0;
1278 
1279                         
1280             if (oiInfo != null) {
1281                 float exactHeight;
1282                 // correct the bounds when overlapping
1283                 if (!_delegate.getUseUniformHeight()) {
1284                     exactHeight = 1.0f * _delegate.getTimeBarViewState().getRowHeight(row)
1285                         / (oiInfo.maxOverlapping + 1);
1286                 } else {
1287                     exactHeight = 1.0f * _delegate.getTimeBarViewState().getRowHeight(row)
1288                         / (_delegate.getOverlapStrategy().getMaxOverlapCount(row));
1289                 }
1290                 int yOffset = (int)(oiInfo.pos * exactHeight);
1291                 y += yOffset;
1292                 height = (int)((oiInfo.pos + 1) * exactHeight) - yOffset;
1293             }
1294             
1295 // old int code 
1296             // TODO take the fix (above) to SWT
1297 //            if (oiInfo != null) {
1298 //                // correct the bounds when overlapping
1299 //                if (!_delegate.getUseUniformHeight()) {
1300 //                    height = _delegate.getTimeBarViewState().getRowHeight(row) / (oiInfo.maxOverlapping + 1);
1301 //                } else {
1302 //                    height = _delegate.getTimeBarViewState().getRowHeight(row)
1303 //                            / (_delegate.getOverlapStrategy().getMaxOverlapCount(row));
1304 //                }
1305 //                y = y + oiInfo.pos * height;
1306 //            }
1307 
1308             Component component = renderer.getTimeBarRendererComponent(_timeBarViewer, i, selected, overlapping);
1309             int x = _delegate.xForDate(i.getBegin());
1310             int width = _delegate.xForDate(i.getEnd()) - x;
1311 
1312             // check preferred drawing bounds
1313             Rectangle intervalDrawingArea = new Rectangle(x, y, width, height);
1314             Rectangle drawingArea = renderer.getPreferredDrawingBounds(intervalDrawingArea, _delegate, i, selected,
1315                     overlapping);
1316             x = drawingArea.x;
1317             width = drawingArea.width;
1318             y = drawingArea.y;
1319             height = drawingArea.height;
1320 
1321             component.setBounds(x, y, width, height);
1322             Graphics gg = g.create(x, y, width, height);
1323             Rectangle ggClip = gg.getClipBounds();
1324             
1325             // calculate height for clipping
1326             Rectangle diagramRect = _delegate.getDiagramRect();
1327             if (y + height > diagramRect.y + diagramRect.height) {
1328                 height = height - (y + height - (diagramRect.y + diagramRect.height));
1329             }
1330             int upperClipBound = 0;
1331             if (y < diagramRect.y) {
1332                 upperClipBound = diagramRect.y - y;
1333             }
1334 
1335             if (x + width > diagramRect.x + diagramRect.width) {
1336                 width = width - (x + width - (diagramRect.x + diagramRect.width));
1337             }
1338 
1339             int clipX = x < diagramRect.x ? diagramRect.x - x : 0;
1340             int clipWidth = x < diagramRect.x ? Math.max(0, width - (diagramRect.x - x)) : width;
1341                         
1342             Rectangle newClip = new Rectangle(clipX, upperClipBound, clipWidth, height).intersection(ggClip);
1343             gg.setClip(newClip);
1344             
1345             component.paint(gg);
1346             gg.dispose();
1347         }
1348 
1349         /**
1350          * Draw a single Interval (vertical).
1351          * 
1352          * @param g Graphics to paint with
1353          * @param x starting x
1354          * @param width width for painting
1355          * @param i interval to be painted
1356          * @param selected true if the interval should be drawn selecetd
1357          * @param oiInfo overlap information or <code>null</code> if intervals are drawn overlapping
1358          * @param row row of the interval
1359          */
1360         private void drawIntervalVertical(Graphics g, int x, int width, Interval i, boolean selected,
1361                 OverlapInfo oiInfo, TimeBarRow row) {
1362             TimeBarRenderer renderer = getRenderer(i.getClass());
1363             if (renderer == null) {
1364                 throw new RuntimeException("no suitable renderer registered");
1365             }
1366 
1367             boolean overlapping = oiInfo != null && oiInfo.maxOverlapping > 0;
1368 
1369             if (oiInfo != null) {
1370                 // correct the bounds when overlapping
1371                 if (!_delegate.getUseUniformHeight()) {
1372                     width = _delegate.getTimeBarViewState().getRowHeight(row) / (oiInfo.maxOverlapping + 1);
1373                 } else {
1374                     width = _delegate.getTimeBarViewState().getRowHeight(row)
1375                             / (_delegate.getOverlapStrategy().getMaxOverlapCount(row));
1376                 }
1377                 x = x + oiInfo.pos * width;
1378             }
1379 
1380             Component component = renderer.getTimeBarRendererComponent(_timeBarViewer, i, selected, overlapping);
1381             int y = _delegate.xForDate(i.getBegin());
1382             int height = _delegate.xForDate(i.getEnd()) - y;
1383 
1384             // check preferred drawing bounds
1385             Rectangle intervalDrawingArea = new Rectangle(x, y, width, height);
1386             Rectangle drawingArea = renderer.getPreferredDrawingBounds(intervalDrawingArea, _delegate, i, selected,
1387                     overlapping);
1388             x = drawingArea.x;
1389             width = drawingArea.width;
1390             y = drawingArea.y;
1391             height = drawingArea.height;
1392 
1393             component.setBounds(x, y, width, height);
1394             Graphics gg = g.create(x, y, width, height);
1395             Rectangle ggClip = gg.getClipBounds();
1396             // calculate height for clipping
1397             Rectangle diagramRect = _delegate.getDiagramRect();
1398             if (x + width > diagramRect.x + diagramRect.width) {
1399                 width = width - (x + width - (diagramRect.x + diagramRect.width));
1400             }
1401             int upperClipBound = 0;
1402             if (x < diagramRect.x) {
1403                 upperClipBound = diagramRect.x - x;
1404             }
1405 
1406             // calculate height for clipping
1407             if (y + height > diagramRect.y + diagramRect.height) {
1408                 height = height - (y + height - (diagramRect.y + diagramRect.height));
1409             }
1410             // calc x clipping and set clipping rect
1411 
1412             int clipY = y < diagramRect.y ? diagramRect.y - y : 0;
1413             int clipHeight = y < diagramRect.y ? Math.max(0, height - (diagramRect.y - y)) : height;
1414             
1415             Rectangle newClip = new Rectangle(upperClipBound, clipY, width, clipHeight).intersection(ggClip);
1416             gg.setClip(newClip);
1417             
1418             component.paint(gg);
1419             gg.dispose();
1420         }
1421 
1422         /**
1423          * Draw the gap beetween to intervals using a GapRenderer (horizontal).
1424          * 
1425          * @param g Graphics for painting
1426          * @param row TimeBarRow the gap "belongs to"
1427          * @param y starting ccordinate y
1428          * @param height height for painting
1429          * @param i1 earlier interval
1430          * @param i2 later interval
1431          */
1432         private void drawGap(Graphics g, TimeBarRow row, int y, int height, Interval i1, Interval i2) {
1433             Component component = _gapRenderer.getTimeBarGapRendererComponent(_timeBarViewer, row, i1, i2);
1434             int x = _delegate.xForDate(i1.getEnd());
1435             int width = _delegate.xForDate(i2.getBegin()) - x;
1436             // handle minimum width requirements of the gap renderer
1437             if (_gapRenderer.getMinimumWidth() > 0) {
1438                 if (width < _gapRenderer.getMinimumWidth()) {
1439                     int diff = _gapRenderer.getMinimumWidth() - width;
1440                     width += diff;
1441                     x -= diff / 2;
1442                 }
1443             }
1444             // TODO clipping width
1445             component.setBounds(x, y, width, height);
1446             Graphics gg = g.create(x, y, width, height);
1447             // calculate height for clipping
1448             Rectangle diagramRect = _delegate.getDiagramRect();
1449             if (y + height > diagramRect.y + diagramRect.height) {
1450                 height = height - (y + height - (diagramRect.y + diagramRect.height));
1451             }
1452             int upperClipBound = 0;
1453             if (y < diagramRect.y) {
1454                 upperClipBound = diagramRect.y - y;
1455             }
1456 
1457             // calc x clipping and set clipping rect
1458             gg.setClip(x < diagramRect.x ? diagramRect.x - x : 0, upperClipBound, width, height);
1459             component.paint(gg);
1460             gg.dispose();
1461         }
1462 
1463         /**
1464          * Draw the gap beetween to intervals using a GapRenderer (vertical).
1465          * 
1466          * @param g Graphics for painting
1467          * @param row TimeBarRow the gap "belongs to"
1468          * @param x starting cordinate x
1469          * @param width width for painting
1470          * @param i1 earlier interval
1471          * @param i2 later interval
1472          */
1473         private void drawGapVertical(Graphics g, TimeBarRow row, int x, int width, Interval i1, Interval i2) {
1474             Component component = _gapRenderer.getTimeBarGapRendererComponent(_timeBarViewer, row, i1, i2);
1475             int y = _delegate.xForDate(i1.getEnd());
1476             int height = _delegate.xForDate(i2.getBegin()) - y;
1477             // handle minimum width requirements of the gap renderer
1478             if (_gapRenderer.getMinimumWidth() > 0) {
1479                 if (height < _gapRenderer.getMinimumWidth()) {
1480                     int diff = _gapRenderer.getMinimumWidth() - height;
1481                     height += diff;
1482                     y -= diff / 2;
1483                 }
1484             }
1485             // TODO clipping width
1486             component.setBounds(x, y, width, height);
1487             Graphics gg = g.create(x, y, width, height);
1488             // calculate height for clipping
1489             Rectangle diagramRect = _delegate.getDiagramRect();
1490             if (x + width > diagramRect.x + diagramRect.width) {
1491                 width = width - (x + width - (diagramRect.x + diagramRect.width));
1492             }
1493             int upperClipBound = 0;
1494             if (x < diagramRect.x) {
1495                 upperClipBound = diagramRect.x - x;
1496             }
1497 
1498             // calc x clipping and set clipping rect
1499             gg.setClip(upperClipBound, y < diagramRect.y ? diagramRect.y - y : 0, width, height);
1500             component.paint(gg);
1501             gg.dispose();
1502         }
1503 
1504         /**
1505          * Draw a row header (horizontal). If the header claims a smaller preferred size than the rowheight it is
1506          * vertically centered.
1507          * 
1508          * @param g Graphics
1509          * @param y upper bound
1510          * @param height row height
1511          * @param header the header object
1512          * @param selected true if the row is selected
1513          */
1514         private void drawRowHeader(Graphics g, int y, int height, TimeBarRowHeader header, boolean selected) {
1515             // draw only if a header renderer is set and the width is >0
1516             if (_headerRenderer != null && _delegate.getYAxisWidth() > 0) {
1517                 JComponent component = _headerRenderer.getHeaderRendererComponent(_timeBarViewer, header, selected);
1518                 int x = _delegate.getYAxisRect().x;
1519                 int width = _delegate.getYAxisWidth() - 1;
1520                 Rectangle diagramRect = _delegate.getDiagramRect();
1521                 Graphics gg;
1522                 int prefHeight = component.getPreferredSize().height;
1523                 if (prefHeight < height) {
1524                     int hCor = (height - prefHeight) / 2;
1525                     gg = g.create(x, y + hCor, width, prefHeight);
1526                     component.setBounds(x, y + hCor, width, prefHeight);
1527                     int upperClipBound = 0;
1528                     if (y + hCor < diagramRect.y) {
1529                         upperClipBound = diagramRect.y - y - hCor;
1530                     }
1531                     if (y + hCor + height > diagramRect.y + diagramRect.height) {
1532                         prefHeight = prefHeight - (y + hCor + prefHeight - (diagramRect.y + diagramRect.height));
1533                     }
1534                     // calc x clipping and set clipping rect
1535                     Rectangle clipSave = gg.getClipBounds();
1536                     Rectangle newClip = new Rectangle(0, upperClipBound, width, prefHeight).intersection(clipSave);
1537                     gg.setClip(newClip);
1538                 } else {
1539                     gg = g.create(x, y, width, height);
1540                     component.setBounds(x, y, width, height);
1541                     int upperClipBound = 0;
1542                     if (y < diagramRect.y) {
1543                         upperClipBound = diagramRect.y - y;
1544                     }
1545 
1546                     if (y + height > diagramRect.y + diagramRect.height) {
1547                         height = height - (y + height - (diagramRect.y + diagramRect.height));
1548                     }
1549                     // calc x clipping and set clipping rect
1550                     Rectangle clipSave = gg.getClipBounds();
1551                     Rectangle newClip = new Rectangle(x, upperClipBound, width, height).intersection(clipSave);
1552                     gg.setClip(newClip);
1553                 }
1554                 component.paint(gg);
1555                 gg.dispose();
1556             }
1557         }
1558 
1559         /**
1560          * Draw a row header (vertical). If the hedare claims a smaller preferred size than the rowheight it is
1561          * vertically centered.
1562          * 
1563          * @param g Graphics
1564          * @param x left bound
1565          * @param width width
1566          * @param header the header object
1567          * @param selected true if the row is selected
1568          */
1569         private void drawRowHeaderVertical(Graphics g, int x, int width, TimeBarRowHeader header, boolean selected) {
1570             // draw only if a header renderer is set and the width is >0
1571             if (_headerRenderer != null && _delegate.getYAxisWidth() > 0) {
1572                 JComponent component = _headerRenderer.getHeaderRendererComponent(_timeBarViewer, header, selected);
1573                 int y = _delegate.getYAxisRect().y;
1574                 int height = _delegate.getYAxisWidth() - 1;
1575                 Rectangle diagramRect = _delegate.getDiagramRect();
1576                 Graphics gg;
1577                 int prefWidth = component.getPreferredSize().width;
1578                 if (prefWidth < width) {
1579                     int wCor = (width - prefWidth) / 2;
1580                     gg = g.create(x + wCor, y, prefWidth, height);
1581                     component.setBounds(x + wCor, y, prefWidth, height);
1582                     int upperClipBound = 0;
1583                     if (x + wCor < diagramRect.x) {
1584                         upperClipBound = diagramRect.x - x - wCor;
1585                     }
1586                     if (x + wCor + width > diagramRect.x + diagramRect.width) {
1587                         prefWidth = prefWidth - (x + wCor + prefWidth - (diagramRect.x + diagramRect.width));
1588                     }
1589                     // calc y clipping and set clipping rect
1590                     Rectangle clipSave = gg.getClipBounds();
1591                     Rectangle newClip = new Rectangle(upperClipBound, 0, prefWidth, height).intersection(clipSave);
1592                     gg.setClip(newClip);
1593                 } else {
1594                     gg = g.create(x, y, width, height);
1595                     component.setBounds(x, y, width, height);
1596                     int upperClipBound = 0;
1597                     if (x < diagramRect.x) {
1598                         upperClipBound = diagramRect.x - x;
1599                     }
1600 
1601                     if (x + width > diagramRect.x + diagramRect.width) {
1602                         width = width - (x + width - (diagramRect.x + diagramRect.width));
1603                     }
1604                     // calc y clipping and set clipping rect
1605                     Rectangle clipSave = gg.getClipBounds();
1606                     Rectangle newClip = new Rectangle(upperClipBound, 0, width, height).intersection(clipSave);
1607                     gg.setClip(newClip);
1608                 }
1609                 component.paint(gg);
1610                 gg.dispose();
1611             }
1612         }
1613 
1614         /**
1615          * Draw the hierachy section for a row/node (horizontal).
1616          * 
1617          * @param g graphics
1618          * @param y begin y
1619          * @param height height of a row
1620          * @param row the row
1621          * @param selected true if the row is selected
1622          */
1623         private void drawHierarchy(Graphics g, int y, int height, TimeBarRow row, boolean selected) {
1624             // draw only if a hierarchy renderer is set and the width is >0
1625             if (_hierarchyRenderer != null && _delegate.getHierarchyWidth() > 0) {
1626                 int level = 0;
1627                 int depth = 0;
1628                 boolean expanded = false;
1629                 boolean leaf = true;
1630                 if (row instanceof TimeBarNode) {
1631                     TimeBarNode node = (TimeBarNode) row;
1632                     if (_delegate.getHierarchicalViewState().isExpanded(node)) {
1633                         expanded = true;
1634                     }
1635                     leaf = node.getChildren().size() == 0;
1636                     level = node.getLevel();
1637                     depth = _delegate.getHierarchicalModel().getDepth();
1638                 }
1639 
1640                 int x = _delegate.getHierarchyRect().x;
1641                 int width = _delegate.getHierarchyWidth() - 1;
1642 
1643                 JComponent component = _hierarchyRenderer.getHierarchyRendererComponent(_timeBarViewer, row, selected,
1644                         expanded, leaf, level, depth);
1645                 Rectangle diagramRect = _delegate.getDiagramRect();
1646                 Graphics gg;
1647 
1648                 gg = g.create(x, y, width, height);
1649                 component.setBounds(x, y, width, height);
1650                 if (y + height > diagramRect.y + diagramRect.height) {
1651                     height = height - (y + height - (diagramRect.y + diagramRect.height));
1652                 }
1653                 int upperClipBound = 0;
1654                 if (y < diagramRect.y) {
1655                     upperClipBound = diagramRect.y - y;
1656                 }
1657                 // calc x clipping and set clipping rect
1658                 Rectangle clipSave = gg.getClipBounds();
1659                 Rectangle newClip = new Rectangle(x, upperClipBound, width, height).intersection(clipSave);
1660                 gg.setClip(newClip);
1661 
1662                 component.paint(gg);
1663                 gg.dispose();
1664             }
1665 
1666         }
1667 
1668         /**
1669          * Draw the hierachy section for a row/node (vertical).
1670          * 
1671          * @param g graphics
1672          * @param x begin x
1673          * @param width width of a col
1674          * @param row the row
1675          * @param selected true if the row is selected
1676          */
1677         private void drawHierarchyVertical(Graphics g, int x, int width, TimeBarRow row, boolean selected) {
1678             // draw only if a hierarchy renderer is set and the width is >0
1679             if (_hierarchyRenderer != null && _delegate.getHierarchyWidth() > 0) {
1680                 int level = 0;
1681                 int depth = 0;
1682                 boolean expanded = false;
1683                 boolean leaf = true;
1684                 if (row instanceof TimeBarNode) {
1685                     TimeBarNode node = (TimeBarNode) row;
1686                     if (_delegate.getHierarchicalViewState().isExpanded(node)) {
1687                         expanded = true;
1688                     }
1689                     leaf = node.getChildren().size() == 0;
1690                     level = node.getLevel();
1691                     depth = _delegate.getHierarchicalModel().getDepth();
1692                 }
1693 
1694                 int y = _delegate.getHierarchyRect().y;
1695                 int height = _delegate.getHierarchyWidth() - 1;
1696 
1697                 JComponent component = _hierarchyRenderer.getHierarchyRendererComponent(_timeBarViewer, row, selected,
1698                         expanded, leaf, level, depth);
1699                 Rectangle diagramRect = _delegate.getDiagramRect();
1700                 Graphics gg;
1701 
1702                 gg = g.create(x, y, width, height);
1703                 component.setBounds(x, y, width, height);
1704 
1705                 if (x + width > diagramRect.x + diagramRect.width) {
1706                     width = width - (x + width - (diagramRect.x + diagramRect.width));
1707                 }
1708                 int upperClipBound = 0;
1709                 if (x < diagramRect.x) {
1710                     upperClipBound = diagramRect.x - x;
1711                 }
1712                 // calc y clipping and set clipping rect
1713                 Rectangle clipSave = gg.getClipBounds();
1714                 Rectangle newClip = new Rectangle(upperClipBound, y, width, height).intersection(clipSave);
1715                 gg.setClip(newClip);
1716 
1717                 component.paint(gg);
1718                 gg.dispose();
1719             }
1720 
1721         }
1722 
1723         /**
1724          * {@inheritDoc}
1725          */
1726         public Dimension getPreferredSize() {
1727             return new Dimension(PREFWIDTH, PREFHEIGHT);
1728         }
1729 
1730         // *** MouseListener
1731         /**
1732          * {@inheritDoc}
1733          */
1734         public void mouseEntered(MouseEvent e) {
1735         }
1736 
1737         /**
1738          * {@inheritDoc}
1739          */
1740         public void mouseExited(MouseEvent e) {
1741         }
1742 
1743         /**
1744          * {@inheritDoc}
1745          */
1746         public void mousePressed(MouseEvent e) {
1747             _delegate.mousePressed(e.getX(), e.getY(), e.isPopupTrigger(), e.getModifiersEx());
1748         }
1749 
1750         /**
1751          * {@inheritDoc}
1752          */
1753         public void mouseClicked(MouseEvent e) {
1754         }
1755 
1756         /**
1757          * {@inheritDoc}
1758          */
1759         public void mouseReleased(MouseEvent e) {
1760             boolean popupTrigger = e.isPopupTrigger();
1761             if (!popupTrigger && _requiresPopupTriggerCheck) {
1762                 popupTrigger = e.getButton() == MouseEvent.BUTTON3;
1763             }
1764 
1765             _delegate.mouseReleased(e.getX(), e.getY(), popupTrigger, e.getModifiersEx());
1766         }
1767 
1768         // *** End of MouseListener
1769 
1770         // *** MouseMotionListener
1771         /**
1772          * {@inheritDoc}
1773          */
1774         public void mouseDragged(MouseEvent e) {
1775             _delegate.mouseDragged(e.getX(), e.getY(), e.getModifiersEx());
1776         }
1777 
1778         /**
1779          * {@inheritDoc}
1780          */
1781         public void mouseMoved(MouseEvent e) {
1782             _delegate.mouseMoved(e.getX(), e.getY());
1783         }
1784 
1785         // *** End of MouseMotionListener
1786         // *** MouseWheelListener
1787         /**
1788          * {@inheritDoc}
1789          */
1790         public void mouseWheelMoved(MouseWheelEvent e) {
1791             if (e.getScrollType() == MouseWheelEvent.WHEEL_UNIT_SCROLL) {
1792                 int val = e.getUnitsToScroll();
1793                 if (_delegate.getXAxisRect() != null && e.getY() > _delegate.getXAxisRect().y
1794                         && e.getY() < _delegate.getXAxisRect().y + _delegate.getXAxisRect().height) {
1795                     // on the xaxis
1796                     _xScrollBar.setValue(_xScrollBar.getValue() + val * _xScrollBar.getModel().getExtent() / 5);
1797                 } else {
1798                     // y axis
1799                     _yScrollBar.getModel().setValue(_yScrollBar.getModel().getValue() + val * getRowHeight()); // TODO check configurabilty
1800                 }
1801             }
1802         }
1803         // *** End of MouseWheelListener
1804     }
1805 
1806     /**
1807      * @return Returns the timeScaleRenderer.
1808      */
1809     public TimeScaleRenderer getTimeScaleRenderer() {
1810         return _timeScaleRenderer;
1811     }
1812 
1813     /**
1814      * Set a renderer for the x axis. The Height for the x axis will be set to the preferred height of the renderer if
1815      * the renderer supplies one.
1816      * 
1817      * @param timeScaleRenderer The timeScaleRenderer to set.
1818      */
1819     public void setTimeScaleRenderer(TimeScaleRenderer timeScaleRenderer) {
1820         _timeScaleRenderer = timeScaleRenderer;
1821         if (_timeScaleRenderer != null && _timeScaleRenderer.getHeight() != -1) {
1822             // if the renderer announces a preferred height, get it and set it
1823             _delegate.setXAxisHeight(_timeScaleRenderer.getHeight());
1824         }
1825         // maybe the new time scale renderer is not a TickProvider ... tell the grid rederer
1826         if (_gridRenderer != null) {
1827             _gridRenderer.setTickProvider(null);
1828         }
1829         if (_timeScaleRenderer != null && _gridRenderer != null && _timeScaleRenderer instanceof ITickProvider
1830                 && _delegate.getTimeScalePosition() != TimeBarViewerInterface.TIMESCALE_POSITION_NONE) {
1831             _gridRenderer.setTickProvider((ITickProvider) _timeScaleRenderer);
1832         }
1833         _diagram.safeRepaint();
1834     }
1835 
1836     /**
1837      * Get the current misc renderer.
1838      * 
1839      * @return the misc renderer
1840      */
1841     public IMiscRenderer getMiscRenderer() {
1842         return _miscRenderer;
1843     }
1844 
1845     /**
1846      * Set the misc renderer to be used for rendering some parts/elements in the viewer.
1847      * 
1848      * @param miscRenderer the renderer to use
1849      */
1850     public void setMiscRenderer(IMiscRenderer miscRenderer) {
1851         _miscRenderer = miscRenderer;
1852         _diagram.safeRepaint();
1853     }
1854 
1855     /**
1856      * Retrieve the renderer that is currently used to render the title area.
1857      * 
1858      * @return the renderer or <code>null</code> if no renderer is set.
1859      */
1860     public ITitleRenderer getTitleRenderer() {
1861         return _titleRenderer;
1862     }
1863 
1864     /**
1865      * Set the title renderer.
1866      * 
1867      * @param titleRenderer the new renderer or <code>null</code> to disable titel rendering.
1868      */
1869     public void setTitleRenderer(ITitleRenderer titleRenderer) {
1870         _titleRenderer = titleRenderer;
1871         _diagram.safeRepaint();
1872     }
1873 
1874     /**
1875      * Retrieve the relation renderer currently set.
1876      * 
1877      * @return the relation renderer or <code>null</code> if none is set.
1878      */
1879     public IRelationRenderer getRelationRenderer() {
1880         return _relationRenderer;
1881     }
1882 
1883     /**
1884      * Set the relation renderer to use.
1885      * 
1886      * @param relationRenderer the renderer or <code>null</code> to disable relation rendering.
1887      */
1888     public void setRelationRenderer(IRelationRenderer relationRenderer) {
1889         _relationRenderer = relationRenderer;
1890         _diagram.safeRepaint();
1891     }
1892 
1893     /**
1894      * Retrieve the configured marker renderer.
1895      * 
1896      * @return the marker renderer
1897      */
1898     public IMarkerRenderer getMarkerRenderer() {
1899         return _markerRenderer;
1900     }
1901 
1902     /**
1903      * Set the marker renderer to be used.
1904      * 
1905      * @param markerRenderer the marker renderer to be used.
1906      */
1907     public void setMarkerRenderer(IMarkerRenderer markerRenderer) {
1908         _markerRenderer = markerRenderer;
1909         _diagram.safeRepaint();
1910     }
1911 
1912     /**
1913      * Get the current global assistant renderer.
1914      * 
1915      * @return the renderer currently in use
1916      */
1917     public IGlobalAssistantRenderer getGlobalAssistantRenderer() {
1918         return _globalAssistantRenderer;
1919     }
1920 
1921     /**
1922      * Set a gloabl assistant renederer.
1923      * 
1924      * @param globalAssistantRenderer the renderer
1925      */
1926     public void setGlobalAssistantRenderer(IGlobalAssistantRenderer globalAssistantRenderer) {
1927         _globalAssistantRenderer = globalAssistantRenderer;
1928         _diagram.safeRepaint();
1929     }
1930 
1931     /**
1932      * @return Returns the gridRenderer.
1933      */
1934     public GridRenderer getGridRenderer() {
1935         return _gridRenderer;
1936     }
1937 
1938     /**
1939      * @param gridRenderer The gridRenderer to set.
1940      */
1941     public void setGridRenderer(GridRenderer gridRenderer) {
1942         _gridRenderer = gridRenderer;
1943         // set the tick provider on the grid renderer
1944         if (_gridRenderer != null && _timeScaleRenderer != null && _timeScaleRenderer instanceof ITickProvider) {
1945             _gridRenderer.setTickProvider((ITickProvider) _timeScaleRenderer);
1946         }
1947         _diagram.safeRepaint();
1948     }
1949 
1950     /**
1951      * @return Returns the gapRenderer.
1952      */
1953     public TimeBarGapRenderer getGapRenderer() {
1954         return _gapRenderer;
1955     }
1956 
1957     /**
1958      * @param gapRenderer The gapRenderer to set.
1959      */
1960     public void setGapRenderer(TimeBarGapRenderer gapRenderer) {
1961         _gapRenderer = gapRenderer;
1962         _diagram.safeRepaint();
1963     }
1964 
1965     /**
1966      * Register a popup menu for a given interval class.
1967      * 
1968      * @param clazz class that the menu is for
1969      * @param popup popup menu to show
1970      */
1971     public void registerPopupMenu(Class<? extends Interval> clazz, JPopupMenu popup) {
1972         if (_registeredPopupMenues == null) {
1973             _registeredPopupMenues = new HashMap<Class<? extends Interval>, JPopupMenu>();
1974         }
1975         _registeredPopupMenues.put(clazz, popup);
1976     }
1977 
1978     /**
1979      * Retrieve the popup menu registered for a given interval class.
1980      * 
1981      * @param clazz class in question
1982      * @return menu or <code>null</code>
1983      */
1984     public JPopupMenu getPopupMenu(Class<? extends Interval> clazz) {
1985         if (_registeredPopupMenues == null) {
1986             return null;
1987         }
1988         JPopupMenu result = null;
1989         result = _registeredPopupMenues.get(clazz);
1990         if (result != null) {
1991             return result;
1992         }
1993 
1994         // direct interfaces
1995         Class<?>[] interfaces = clazz.getInterfaces();
1996         for (Class<?> c : interfaces) {
1997             result = _registeredPopupMenues.get(c);
1998             if (result != null) {
1999                 return result;
2000             }
2001         }
2002 
2003         // superclasses
2004         Class<?> sc = clazz.getSuperclass();
2005 
2006         while (sc != null) {
2007             result = _registeredPopupMenues.get(sc);
2008             if (result != null) {
2009                 return result;
2010             }
2011             // interfaces of the superclass
2012             Class<?>[] scinterfaces = sc.getInterfaces();
2013             for (Class<?> c : scinterfaces) {
2014                 result = _registeredPopupMenues.get(c);
2015                 if (result != null) {
2016                     return result;
2017                 }
2018             }
2019             sc = sc.getSuperclass();
2020         }
2021 
2022         return result;
2023     }
2024 
2025     /**
2026      * {@inheritDoc}
2027      */
2028     public void firePropertyChangeX(String propName, Object oldVal, Object newVal) {
2029         firePropertyChange(propName, oldVal, newVal);
2030     }
2031 
2032     /**
2033      * {@inheritDoc}
2034      */
2035     public boolean timeBarContains(Interval interval, Rectangle intervalRect, int x, int y, boolean overlapping) {
2036         JComponent component = getIntervalComponent(interval, intervalRect, overlapping);
2037         return component.contains(x, y);
2038     }
2039 
2040     /**
2041      * {@inheritDoc}
2042      */
2043     public Rectangle timeBarContainingRect(Interval interval, Rectangle intervalRect, boolean overlapping) {
2044         JComponent component = getIntervalComponent(interval, intervalRect);
2045         // get the containing rect or - if not set by the renderer -
2046         // use the component bounds
2047         Rectangle containingRect = (Rectangle) component.getClientProperty(TimeBarRenderer.CONTAINING_RECTANGLE);
2048         return containingRect;
2049     }
2050 
2051     /**
2052      * {@inheritDoc}
2053      */
2054     public void setCursor(int cursorType) {
2055         setCursor(Cursor.getPredefinedCursor(cursorType));
2056     }
2057 
2058     /**
2059      * {@inheritDoc}
2060      */
2061     public String getIntervalToolTipText(Interval interval, Rectangle intervalRect, int x, int y) {
2062         JComponent component = getIntervalComponent(interval, intervalRect);
2063         // String tooltip = component.getToolTipText(new MouseEvent(this, 0, 0,
2064         // 0, x - component.getX(), y - component.getY(), 0, false));
2065         String tooltip = component.getToolTipText(new MouseEvent(this, 0, 0, 0, x, y, 0, false));
2066         return tooltip;
2067     }
2068 
2069     /**
2070      * {@inheritDoc}
2071      */
2072     public JaretDate getStartDate() {
2073         return _delegate.getStartDate();
2074     }
2075 
2076     /**
2077      * {@inheritDoc}
2078      */
2079     public void setStartDate(JaretDate startDate) {
2080         _delegate.setStartDate(startDate);
2081     }
2082 
2083     /**
2084      * {@inheritDoc}
2085      */
2086     public JaretDate getMinDate() {
2087         return _delegate.getMinDate();
2088     }
2089 
2090     /**
2091      * {@inheritDoc}
2092      */
2093     public void setMinDate(JaretDate minDate) {
2094         _delegate.setMinDate(minDate);
2095     }
2096 
2097     /**
2098      * {@inheritDoc}
2099      */
2100     public JaretDate getMaxDate() {
2101         return _delegate.getMaxDate();
2102     }
2103 
2104     /**
2105      * {@inheritDoc}
2106      */
2107     public void setMaxDate(JaretDate maxDate) {
2108         _delegate.setMaxDate(maxDate);
2109     }
2110 
2111     /**
2112      * {@inheritDoc}
2113      */
2114     public TimeBarSelectionModel getSelectionModel() {
2115         return _delegate.getSelectionModel();
2116     }
2117 
2118     /**
2119      * {@inheritDoc}
2120      */
2121     public void setSelectionModel(TimeBarSelectionModel selectionModel) {
2122         _delegate.setSelectionModel(selectionModel);
2123     }
2124 
2125     /**
2126      * {@inheritDoc}
2127      */
2128     public int getFirstRowDisplayed() {
2129         return _delegate.getFirstRow();
2130     }
2131 
2132     /**
2133      * {@inheritDoc}
2134      */
2135     public void setFirstRowDisplayed(int rowIdx) {
2136         _delegate.setFirstRow(rowIdx);
2137     }
2138 
2139     /**
2140      * {@inheritDoc}
2141      */
2142     public void setFirstRowDisplayed(TimeBarRow row) {
2143         _delegate.setFirstRow(row);
2144     }
2145 
2146     /**
2147      * {@inheritDoc}
2148      */
2149     public void setFirstRow(int firstRow, int pixOffset) {
2150         _delegate.setFirstRow(firstRow, pixOffset);
2151     }
2152 
2153     /**
2154      * {@inheritDoc}
2155      */
2156     public void setLastRow(int index) {
2157         _delegate.setLastRow(index);
2158     }
2159 
2160     /**
2161      * {@inheritDoc}
2162      */
2163     public void setLastRow(TimeBarRow row) {
2164         _delegate.setLastRow(row);
2165     }
2166 
2167     /**
2168      * {@inheritDoc}
2169      */
2170     public JaretDate getEndDate() {
2171         return _delegate.getEndDate();
2172     }
2173 
2174     /**
2175      * {@inheritDoc}
2176      */
2177     public int getFirstRowOffset() {
2178         return _delegate.getFirstRowOffset();
2179     }
2180 
2181     /**
2182      * {@inheritDoc}
2183      */
2184     public void setFirstRowOffset(int offset) {
2185         _delegate.setFirstRowOffset(offset);
2186     }
2187 
2188     /**
2189      * {@inheritDoc}
2190      */
2191     public void setTimeScalePosition(int timeScalePosition) {
2192         if (timeScalePosition == TimeBarViewerInterface.TIMESCALE_POSITION_NONE) {
2193             if (_gridRenderer != null) {
2194                 _gridRenderer.setTickProvider(null);
2195             }
2196         } else {
2197             if (_gridRenderer != null && _timeScaleRenderer instanceof ITickProvider) {
2198                 _gridRenderer.setTickProvider((ITickProvider) _timeScaleRenderer);
2199             }
2200         }
2201         _delegate.setTimeScalePosition(timeScalePosition);
2202     }
2203 
2204     /**
2205      * {@inheritDoc}
2206      */
2207     public void setAdjustMinMaxDatesByModel(boolean adjust) {
2208         _delegate.setAdjustMinMaxDatesByModel(adjust);
2209     }
2210 
2211     /**
2212      * {@inheritDoc}
2213      */
2214     public boolean getAdjustMinMaxDatesByModel() {
2215         return _delegate.getAdjustMinMaxDatesByModel();
2216     }
2217 
2218     /**
2219      * {@inheritDoc}
2220      */
2221     public TimeBarRow rowForY(int y) {
2222         return _delegate.rowForY(y);
2223     }
2224 
2225     /**
2226      * {@inheritDoc}
2227      */
2228     public JaretDate dateForX(int x) {
2229         return _delegate.dateForCoord(x);
2230     }
2231 
2232     /**
2233      * {@inheritDoc}
2234      */
2235     public JaretDate dateForXY(int x, int y) {
2236         return _delegate.dateForCoord(x, y);
2237     }
2238 
2239     /**
2240      * {@inheritDoc}
2241      */
2242     public int xForDate(JaretDate date) {
2243         return _delegate.xForDate(date);
2244     }
2245 
2246     /**
2247      * {@inheritDoc}
2248      */
2249     public void highlightRow(int y) {
2250         _delegate.highlightRow(y);
2251     }
2252 
2253     /**
2254      * {@inheritDoc}
2255      */
2256     public void highlightRow(TimeBarRow timeBarRow) {
2257         _delegate.highlightRow(timeBarRow);
2258     }
2259 
2260     /**
2261      * {@inheritDoc}
2262      */
2263     public void deHighlightRow() {
2264         _delegate.deHighlightRow();
2265     }
2266 
2267     /**
2268      * {@inheritDoc}
2269      */
2270     public void setDrawRowGrid(boolean drawRowGrid) {
2271         _delegate.setDrawRowGrid(drawRowGrid);
2272     }
2273 
2274     /**
2275      * {@inheritDoc}
2276      */
2277     public boolean getDrawRowGrid() {
2278         return _delegate.getDrawRowGrid();
2279     }
2280 
2281     /**
2282      * {@inheritDoc}
2283      */
2284     public void setYAxisWidth(int width) {
2285         _delegate.setYAxisWidth(width);
2286     }
2287 
2288     /**
2289      * {@inheritDoc}
2290      */
2291     public int getYAxisWidth() {
2292         return _delegate.getYAxisWidth();
2293     }
2294 
2295     /**
2296      * {@inheritDoc}
2297      */
2298     public void setHierarchyWidth(int width) {
2299         _delegate.setHierarchyWidth(width);
2300     }
2301 
2302     /**
2303      * {@inheritDoc}
2304      */
2305     public int getHierarchyWidth() {
2306         return _delegate.getHierarchyWidth();
2307     }
2308 
2309     /**
2310      * {@inheritDoc}
2311      */
2312     public void addIntervalModificator(IntervalModificator intervalModificator) {
2313         _delegate.addIntervalModificator(intervalModificator);
2314     }
2315 
2316     /**
2317      * {@inheritDoc}
2318      */
2319     public void remIntervalModificator(IntervalModificator intervalModificator) {
2320         _delegate.remIntervalModificator(intervalModificator);
2321     }
2322 
2323     /**
2324      * {@inheritDoc}
2325      */
2326     public void setAutoscrollEnabled(boolean enableAutoscroll) {
2327         _delegate.setAutoscrollEnabled(enableAutoscroll);
2328     }
2329 
2330     /**
2331      * {@inheritDoc}
2332      */
2333     public boolean isAutoscrollEnabled() {
2334         return _delegate.isAutoscrollEnabled();
2335     }
2336 
2337     /**
2338      * {@inheritDoc}
2339      */
2340     public String getTimeScaleToolTipText(int x, int y) {
2341         if (_timeScaleRendererComponent != null) {
2342             return _timeScaleRendererComponent.getToolTipText(new MouseEvent(this, 0, 0, 0, x
2343                     - _delegate.getDiagramRect().x, y - _delegate.getXAxisRect().y, 0, false));
2344         }
2345         return null;
2346     }
2347 
2348     /**
2349      * {@inheritDoc}
2350      */
2351     public String getHeaderToolTipText(TimeBarRow row, int x, int y) {
2352         if (_headerRenderer != null && row != null) {
2353             JComponent component = _headerRenderer.getHeaderRendererComponent(this, row.getRowHeader(), false);
2354             if (component != null) {
2355                 component.setBounds(_delegate.getHeaderRect(row));
2356                 return component.getToolTipText(new MouseEvent(this, 0, 0, 0, x - _delegate.getYAxisRect().x, y
2357                         - _delegate.getYAxisRect().y, 0, false));
2358             }
2359         }
2360         return null;
2361     }
2362 
2363     /**
2364      * {@inheritDoc}
2365      */
2366     public String getHierarchyToolTipText(TimeBarNode node, int x, int y) {
2367         if (_hierarchyRenderer != null && node != null) {
2368             // TODO check additional parameters to be corrrect - maybe these are
2369             // useful for generating the tooltip
2370             JComponent component = _hierarchyRenderer.getHierarchyRendererComponent(this, node, false, false, false, 0,
2371                     1);
2372             component.setBounds(_delegate.getHierarchyRect(node));
2373             return component.getToolTipText(new MouseEvent(this, 0, 0, 0, x - _delegate.getHierarchyRect().x, y
2374                     - _delegate.getHierarchyRect().y, 0, false));
2375         }
2376         return null;
2377     }
2378 
2379     /**
2380      * {@inheritDoc}
2381      */
2382     public HierarchicalViewState getHierarchicalViewState() {
2383         return _delegate.getHierarchicalViewState();
2384     }
2385 
2386     /**
2387      * {@inheritDoc}
2388      */
2389     public void setHierarchicalViewState(HierarchicalViewState hierarchicalViewState) {
2390         _delegate.setHierarchicalViewState(hierarchicalViewState);
2391     }
2392 
2393     /**
2394      * {@inheritDoc}
2395      */
2396     public void setModel(HierarchicalTimeBarModel hModel) {
2397         _delegate.setModel(hModel);
2398     }
2399 
2400     /**
2401      * {@inheritDoc}
2402      */
2403     public HierarchicalTimeBarModel getHierarchicalModel() {
2404         return _delegate.getHierarchicalModel();
2405     }
2406 
2407     /**
2408      * {@inheritDoc}
2409      */
2410     public int getMarkerWidth(TimeBarMarker marker) {
2411         if (_markerRenderer != null) {
2412             return _markerRenderer.getMarkerWidth(marker);
2413         }
2414         return 0;
2415     }
2416 
2417     /**
2418      * {@inheritDoc}
2419      */
2420     public void setTitle(String title) {
2421         _delegate.setTitle(title);
2422     }
2423 
2424     /**
2425      * {@inheritDoc}
2426      */
2427     public String getTitle() {
2428         return _delegate.getTitle();
2429     }
2430 
2431     /**
2432      * Retrieve the context menu set for the body.
2433      * 
2434      * @return the context menu or <code>null</code>
2435      */
2436     public JPopupMenu getBodyContextMenu() {
2437         return _bodyContextMenu;
2438     }
2439 
2440     /**
2441      * Set the context menu to be used for the body area.
2442      * 
2443      * @param bodyContextMenu context menu or <code>null</code> for no context menu
2444      */
2445     public void setBodyContextMenu(JPopupMenu bodyContextMenu) {
2446         _bodyContextMenu = bodyContextMenu;
2447     }
2448 
2449     /**
2450      * Retrieve the context menu set for the time scale.
2451      * 
2452      * @return the context menu or <code>null</code>
2453      */
2454     public JPopupMenu getTimeScaleContextMenu() {
2455         return _timeScaleContextMenu;
2456     }
2457 
2458     /**
2459      * Set the context menu to be used for the time scale area.
2460      * 
2461      * @param timeScaleContextMenu context menu or <code>null</code> for no context menu
2462      */
2463     public void setTimeScaleContextMenu(JPopupMenu timeScaleContextMenu) {
2464         _timeScaleContextMenu = timeScaleContextMenu;
2465     }
2466 
2467     /**
2468      * Retrieve the context menu set for the header.
2469      * 
2470      * @return the context menu or <code>null</code>
2471      */
2472 
2473     public JPopupMenu getHeaderContextMenu() {
2474         return _headerContextMenu;
2475     }
2476 
2477     /**
2478      * Set the context menu to be used for the header area.
2479      * 
2480      * @param headerContextMenu context menu or <code>null</code> for no context menu
2481      */
2482     public void setHeaderContextMenu(JPopupMenu headerContextMenu) {
2483         _headerContextMenu = headerContextMenu;
2484     }
2485 
2486     /**
2487      * Retrieve the context menu set for the hierrarchy.
2488      * 
2489      * @return the context menu or <code>null</code>
2490      */
2491     public JPopupMenu getHierarchyContextMenu() {
2492         return _hierarchyContextMenu;
2493     }
2494 
2495     /**
2496      * Set the context menu to be used for the hierarchy area.
2497      * 
2498      * @param hierarchyContextMenu context menu or <code>null</code> for no context menu
2499      */
2500     public void setHierarchyContextMenu(JPopupMenu hierarchyContextMenu) {
2501         _hierarchyContextMenu = hierarchyContextMenu;
2502     }
2503 
2504     /**
2505      * Retrieve the context menu set for the title area.
2506      * 
2507      * @return the context menu or <code>null</code>
2508      */
2509     public JPopupMenu getTitleContextMenu() {
2510         return _titleContextMenu;
2511     }
2512 
2513     /**
2514      * Set the context menu to be used for the titel area.
2515      * 
2516      * @param titleContextMenu context menu or <code>null</code> for no context menu
2517      */
2518     public void setTitleContextMenu(JPopupMenu titleContextMenu) {
2519         _titleContextMenu = titleContextMenu;
2520     }
2521 
2522     /**
2523      * {@inheritDoc}
2524      */
2525     public void displayBodyContextMenu(int x, int y) {
2526         if (_bodyContextMenu != null) {
2527             _bodyContextMenu.show(_diagram, x, y);
2528         }
2529     }
2530 
2531     /**
2532      * {@inheritDoc}
2533      */
2534     public void displayTimeScaleContextMenu(int x, int y) {
2535         if (_timeScaleContextMenu != null) {
2536             _timeScaleContextMenu.show(_diagram, x, y);
2537         }
2538     }
2539 
2540     /**
2541      * {@inheritDoc}
2542      */
2543     public void displayIntervalContextMenu(Interval interval, int x, int y) {
2544         JPopupMenu menu = getPopupMenu(interval.getClass());
2545         if (menu != null) {
2546             menu.show(_diagram, x, y);
2547         }
2548     }
2549 
2550     /**
2551      * {@inheritDoc}
2552      */
2553     public void displayHeaderContextMenu(TimeBarRow row, int x, int y) {
2554         if (_headerContextMenu != null) {
2555             _headerContextMenu.show(_diagram, x, y);
2556         }
2557     }
2558 
2559     /**
2560      * {@inheritDoc}
2561      */
2562     public void displayHierarchyContextMenu(TimeBarRow row, int x, int y) {
2563         if (_hierarchyContextMenu != null) {
2564             _hierarchyContextMenu.show(_diagram, x, y);
2565         }
2566     }
2567 
2568     /**
2569      * {@inheritDoc}
2570      */
2571     public void displayTitleContextMenu(int x, int y) {
2572         if (_titleContextMenu != null) {
2573             _titleContextMenu.show(_diagram, x, y);
2574         }
2575     }
2576 
2577     /**
2578      * {@inheritDoc}
2579      */
2580     public boolean isInToggleArea(TimeBarNode node, int x, int y) {
2581         return true;
2582     }
2583 
2584     /**
2585      * {@inheritDoc}
2586      */
2587     public boolean isInHierarchySelectionArea(TimeBarNode node, int x, int y) {
2588         return false;
2589     }
2590 
2591     /**
2592      * Set the drawing mode.
2593      * 
2594      * @param drawOverlapping if set to true intervals will be painted on another. If set to false, every interval will
2595      * only get a fraction of the space corresponding to the count of overlapping intervals.
2596      */
2597     public void setDrawOverlapping(boolean drawOverlapping) {
2598         _delegate.setDrawOverlapping(drawOverlapping);
2599     }
2600 
2601     /**
2602      * Retrieve the drawing mode.
2603      * 
2604      * @return the drawing mode
2605      */
2606     public boolean getDrawOverlapping() {
2607         return _delegate.isDrawOverlapping();
2608     }
2609 
2610     /**
2611      * {@inheritDoc}
2612      */
2613     public void addMarker(TimeBarMarker marker) {
2614         _delegate.addMarker(marker);
2615     }
2616 
2617     /**
2618      * {@inheritDoc}
2619      */
2620     public void remMarker(TimeBarMarker marker) {
2621         _delegate.remMarker(marker);
2622     }
2623 
2624     /**
2625      * {@inheritDoc}
2626      */
2627     public List<TimeBarMarker> getMarkers() {
2628         return _delegate.getMarkers();
2629     }
2630 
2631     /**
2632      * {@inheritDoc}
2633      */
2634     public void addMarkers(List<TimeBarMarker> markers) {
2635         _delegate.addMarkers(markers);
2636     }
2637 
2638     /**
2639      * {@inheritDoc}
2640      */
2641     public int getSelectionDelta() {
2642         return _delegate.getSelectionDelta();
2643     }
2644 
2645     /**
2646      * {@inheritDoc}
2647      */
2648     public void setSelectionDelta(int selectionDelta) {
2649         _delegate.setSelectionDelta(selectionDelta);
2650     }
2651 
2652     /**
2653      * {@inheritDoc}
2654      */
2655     public boolean isLineDraggingAllowed() {
2656         return _delegate.isLineDraggingAllowed();
2657     }
2658 
2659     /**
2660      * {@inheritDoc}
2661      */
2662     public void setLineDraggingAllowed(boolean lineDraggingAllowed) {
2663         _delegate.setLineDraggingAllowed(lineDraggingAllowed);
2664     }
2665 
2666     /**
2667      * {@inheritDoc}
2668      */
2669     public int getYForRow(TimeBarRow row) {
2670         return _delegate.yForRow(row);
2671     }
2672 
2673     /**
2674      * {@inheritDoc}. This is the same as rowForY.
2675      */
2676     public TimeBarRow getRowForY(int y) {
2677         return _delegate.rowForY(y);
2678     }
2679 
2680     /**
2681      * {@inheritDoc}
2682      */
2683     public TimeBarRow getRowForXY(int x, int y) {
2684         return _delegate.rowForXY(x, y);
2685     }
2686 
2687     /**
2688      * {@inheritDoc}
2689      */
2690     public boolean isMilliAccuracy() {
2691         return _delegate.isMilliAccuracy();
2692     }
2693 
2694     /**
2695      * {@inheritDoc}
2696      */
2697     public void setMilliAccuracy(boolean milliAccuracy) {
2698         _delegate.setMilliAccuracy(milliAccuracy);
2699     }
2700 
2701     /**
2702      * {@inheritDoc}
2703      */
2704     public TimeBarNode getPpsRow() {
2705         return _delegate.getPpsRow();
2706     }
2707 
2708     /**
2709      * {@inheritDoc}
2710      */
2711     public boolean hasVariableXScale() {
2712         return _delegate.hasVariableXScale();
2713     }
2714 
2715     /**
2716      * {@inheritDoc}
2717      */
2718     public void setVariableXScale(boolean state) {
2719         _delegate.setVariableXScale(state);
2720     }
2721 
2722     /**
2723      * {@inheritDoc} Includes the timescale if present.
2724      */
2725     public void doScrollHorizontal(int diff) {
2726         Graphics g = _diagram.getGraphics();
2727         if (g == null) {
2728             return;
2729         }
2730         if (_delegate.getDiagramRect() == null) {
2731             return;
2732         }
2733 
2734         Rectangle d = new Rectangle(_delegate.getDiagramRect());
2735         if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
2736             if (_delegate.getTimeScalePosition() != TIMESCALE_POSITION_NONE) {
2737                 if (_delegate.getTimeScalePosition() == TIMESCALE_POSITION_TOP) {
2738                     d.y = _delegate.getXAxisRect().y;
2739                 }
2740                 d.height += _delegate.getXAxisRect().height;
2741             }
2742         } else {
2743             d.y = 0;
2744             d.height = d.height + _delegate.getHierarchyWidth() + _delegate.getYAxisWidth();
2745         }
2746 
2747         if (diff > 0) {
2748             // to the right
2749             g.copyArea(d.x + diff, d.y, d.width - diff, d.height, -diff, 0);
2750             _diagram.repaint(d.x + d.width - diff, d.y, diff, d.height);
2751         } else {
2752             diff = -diff;
2753             g.copyArea(d.x, d.y, d.width - diff, d.height, diff, 0);
2754             _diagram.repaint(d.x, d.y, diff, d.height);
2755         }
2756         g.dispose();
2757     }
2758 
2759     /**
2760      * {@inheritDoc} Includes header and/or hierarchy if present.
2761      */
2762     public void doScrollVertical(int diff) {
2763         Graphics g = _diagram.getGraphics();
2764         if (g == null) {
2765             return;
2766         }
2767 
2768         if (_delegate.getDiagramRect() == null) {
2769             return;
2770         }
2771 
2772         Rectangle d = new Rectangle(_delegate.getDiagramRect());
2773         if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
2774             d.x = d.x - _delegate.getHierarchyWidth() - _delegate.getYAxisWidth();
2775             d.width = d.width + _delegate.getHierarchyWidth() + _delegate.getYAxisWidth();
2776         } else {
2777             if (_delegate.getTimeScalePosition() == TIMESCALE_POSITION_TOP) {
2778                 d.x = d.x - _delegate.getXAxisHeight();
2779             }
2780             d.width = d.width + _delegate.getXAxisHeight();
2781         }
2782         if (diff > 0) {
2783             // downwards
2784             g.copyArea(d.x, d.y + diff, d.width, d.height - diff, 0, -diff);
2785             _diagram.repaint(d.x, d.y + d.height - diff, d.width, diff);
2786         } else {
2787             diff = -diff;
2788             g.copyArea(d.x, d.y, d.width, d.height - diff, 0, diff);
2789             _diagram.repaint(d.x, d.y, d.width, diff);
2790         }
2791         g.dispose();
2792     }
2793 
2794     /**
2795      * {@inheritDoc}
2796      */
2797     public boolean getOptimizeScrolling() {
2798         return _delegate.getOptimizeScrolling();
2799     }
2800 
2801     /**
2802      * {@inheritDoc}
2803      */
2804     public void setOptimizeScrolling(boolean optimizeScrolling) {
2805         _delegate.setOptimizeScrolling(optimizeScrolling);
2806     }
2807 
2808     /**
2809      * {@inheritDoc}
2810      */
2811     public Orientation getTBOrientation() {
2812         return _delegate.getOrientation();
2813     }
2814 
2815     /**
2816      * {@inheritDoc}
2817      */
2818     public void setTBOrientation(Orientation orientation) {
2819         _delegate.setOrientation(orientation);
2820     }
2821 
2822     /**
2823      * {@inheritDoc}
2824      */
2825     public int getAutoScaleRows() {
2826         return _delegate.getAutoScaleRows();
2827     }
2828 
2829     /**
2830      * {@inheritDoc}
2831      */
2832     public void setAutoScaleRows(int rows) {
2833         _delegate.setAutoScaleRows(rows);
2834     }
2835 
2836     /**
2837      * {@inheritDoc}
2838      */
2839     public int getXAxisHeight() {
2840         return _delegate.getXAxisHeight();
2841     }
2842 
2843     /**
2844      * {@inheritDoc}
2845      */
2846     public void setXAxisHeight(int height) {
2847         _delegate.setXAxisHeight(height);
2848     }
2849 
2850     /**
2851      * {@inheritDoc}
2852      */
2853     public void fireSelectionChanged() {
2854         // nothing to do in the swing version
2855     }
2856 
2857     /**
2858      * {@inheritDoc}
2859      */
2860     public void addTimeBarChangeListener(ITimeBarChangeListener listener) {
2861         _delegate.addTimeBarChangeListener(listener);
2862     }
2863 
2864     /**
2865      * {@inheritDoc}
2866      */
2867     public void removeTimeBarChangeListener(ITimeBarChangeListener listener) {
2868         _delegate.removeTimeBarChangeListener(listener);
2869     }
2870 
2871     /**
2872      * {@inheritDoc}
2873      */
2874     public void addFocussedIntervalListener(FocussedIntervalListener listener) {
2875         _delegate.addFocussedIntervalListener(listener);
2876     }
2877 
2878     /**
2879      * {@inheritDoc}
2880      */
2881     public void remFocussedIntervalListener(FocussedIntervalListener listener) {
2882         _delegate.remFocussedIntervalListener(listener);
2883     }
2884 
2885     /**
2886      * {@inheritDoc}
2887      */
2888     public ITimeBarViewState getTimeBarViewState() {
2889         return _delegate.getTimeBarViewState();
2890     }
2891 
2892     /**
2893      * {@inheritDoc}
2894      */
2895     public boolean isRowHeightDragginAllowed() {
2896         return _delegate.isRowHeightDraggingAllowed();
2897     }
2898 
2899     /**
2900      * {@inheritDoc}
2901      */
2902     public void setRowHeightDraggingAllowed(boolean rowHeightDraggingAllowed) {
2903         _delegate.setRowHeightDraggingAllowed(rowHeightDraggingAllowed);
2904     }
2905 
2906     /**
2907      * {@inheritDoc}
2908      */
2909     public boolean rowLineHit(int x, int y) {
2910         return _delegate.rowLineHit(x, y);
2911     }
2912 
2913     /**
2914      * {@inheritDoc}
2915      */
2916     public boolean isInRowAxis(int x, int y) {
2917         return _delegate.isInRowAxis(x, y);
2918     }
2919 
2920     /**
2921      * {@inheritDoc}
2922      */
2923     public boolean isInDiagram(int x, int y) {
2924         return _delegate.isInDiagram(x, y);
2925     }
2926 
2927     /**
2928      * {@inheritDoc}
2929      */
2930     public boolean getStrictClipTimeCheck() {
2931         return _delegate.getStrictClipTimeCheck();
2932     }
2933 
2934     /**
2935      * {@inheritDoc}
2936      */
2937     public void setStrictClipTimeCheck(boolean strictClipTimeCheck) {
2938         _delegate.setStrictClipTimeCheck(strictClipTimeCheck);
2939     }
2940 
2941     /**
2942      * {@inheritDoc}
2943      */
2944     public int getSecondsDisplayed() {
2945         return _delegate.getSecondsDisplayed();
2946     }
2947 
2948     /**
2949      * {@inheritDoc}
2950      */
2951     public IOverlapStrategy getOverlapStrategy() {
2952         return _delegate.getOverlapStrategy();
2953     }
2954 
2955     /**
2956      * {@inheritDoc}
2957      */
2958     public void setOverlapStrategy(IOverlapStrategy overlapStrategy) {
2959         _delegate.setOverlapStrategy(overlapStrategy);
2960     }
2961 
2962     /**
2963      * {@inheritDoc}
2964      */
2965     public int getScrollLookBackMinutes() {
2966         return _delegate.getScrollLookBackMinutes();
2967     }
2968 
2969     /**
2970      * {@inheritDoc}
2971      */
2972     public void setScrollLookBackMinutes(int scrollLookBackMinutes) {
2973         _delegate.setScrollLookBackMinutes(scrollLookBackMinutes);
2974     }
2975 
2976     /**
2977      * {@inheritDoc}
2978      */
2979     public void setScrollLookForwardMinutes(int scrollLookForwardMinutes) {
2980         _delegate.setScrollLookForwardMinutes(scrollLookForwardMinutes);
2981     }
2982 
2983     /**
2984      * {@inheritDoc}
2985      */
2986     public int getScrollLookForwardMinutes() {
2987         return _delegate.getScrollLookForwardMinutes();
2988     }
2989 
2990     /**
2991      * {@inheritDoc}
2992      */
2993     public String getName() {
2994         // null check becuase of the gtk plaf that calls getName before the component is fully initialized
2995         if (_delegate != null) {
2996             return _delegate.getName();
2997         } else {
2998             return null;
2999         }
3000     }
3001 
3002     /**
3003      * {@inheritDoc}
3004      */
3005     public void setName(String name) {
3006         _delegate.setName(name);
3007     }
3008 
3009     /**
3010      * {@inheritDoc}
3011      */
3012     public TimeBarViewerDelegate getDelegate() {
3013         return _delegate;
3014     }
3015 
3016     /**
3017      * {@inheritDoc}
3018      */
3019     public int getAutoscrollDelta() {
3020         return _delegate.getAutoscrollDelta();
3021     }
3022 
3023     /**
3024      * {@inheritDoc}
3025      */
3026     public void setAutoscrollDelta(int autoscrollDelta) {
3027         _delegate.setAutoscrollDelta(autoscrollDelta);
3028     }
3029 
3030     /**
3031      * {@inheritDoc}
3032      */
3033     public boolean getDragAllSelectedIntervals() {
3034         return _delegate.getDragAllSelectedIntervals();
3035     }
3036 
3037     /**
3038      * {@inheritDoc}
3039      */
3040     public void setDragAllSelectedIntervals(boolean dragAllSelectedIntervals) {
3041         _delegate.setDragAllSelectedIntervals(dragAllSelectedIntervals);
3042     }
3043 
3044     /**
3045      * {@inheritDoc}
3046      */
3047     public List<IIntervalRelation> getRelationsForCoord(int x, int y) {
3048         if (_relationRenderer != null) {
3049             return _relationRenderer.getRelationsForCoord(x, y);
3050         } else {
3051             return null;
3052         }
3053     }
3054 
3055     /**
3056      * {@inheritDoc}
3057      */
3058     public String getRelationTooltip(int x, int y) {
3059         if (_relationRenderer != null) {
3060             return _relationRenderer.getTooltip(x, y);
3061         }
3062         return null;
3063     }
3064 
3065     /**
3066      * {@inheritDoc}
3067      */
3068     public boolean getScrollOnFocus() {
3069         return _delegate.getScrollOnFocus();
3070     }
3071 
3072     /**
3073      * {@inheritDoc}
3074      */
3075     public void setScrollOnFocus(boolean scrollOnFocus) {
3076         _delegate.setScrollOnFocus(scrollOnFocus);
3077     }
3078 
3079     /**
3080      * {@inheritDoc}
3081      */
3082     public void addSelectionRectListener(ISelectionRectListener listener) {
3083         _delegate.addSelectionRectListener(listener);
3084     }
3085 
3086     /**
3087      * {@inheritDoc}
3088      */
3089     public void remSelectionRectListener(ISelectionRectListener listener) {
3090         _delegate.remSelectionRectListener(listener);
3091     }
3092 
3093     /**
3094      * {@inheritDoc}
3095      */
3096     public boolean getHideRoot() {
3097         return _delegate.getHideRoot();
3098     }
3099 
3100     /**
3101      * {@inheritDoc}
3102      */
3103     public void setHideRoot(boolean hideRoot) {
3104         _delegate.setHideRoot(hideRoot);
3105     }
3106 
3107     /**
3108      * {@inheritDoc}
3109      */
3110     public boolean getMarkerDraggingInDiagramArea() {
3111         return _delegate.getMarkerDraggingInDiagramArea();
3112     }
3113 
3114     /**
3115      * {@inheritDoc}
3116      */
3117     public void setMarkerDraggingInDiagramArea(boolean allowed) {
3118         _delegate.setMarkerDraggingInDiagramArea(allowed);
3119     }
3120 
3121     /**
3122      * {@inheritDoc}
3123      */
3124     public void clearRegionRect() {
3125         _delegate.clearRegionRect();
3126     }
3127 
3128     /**
3129      * {@inheritDoc}
3130      */
3131     public TBRect getRegionRect() {
3132         return _delegate.getRegionRect();
3133     }
3134 
3135     /**
3136      * {@inheritDoc}
3137      */
3138     public boolean getRegionRectEnable() {
3139         return _delegate.getRegionRectEnable();
3140     }
3141 
3142     /**
3143      * {@inheritDoc}
3144      */
3145     public void setRegionRectEnable(boolean enabled) {
3146         _delegate.setRegionRectEnable(enabled);
3147     }
3148 
3149     /**
3150      * {@inheritDoc} Repaint a rectangle. Overridden and extended widht and height by 1.
3151      */
3152     public void repaint(Rectangle r) {
3153         super.repaint(r.x, r.y, r.width + 1, r.height + 1);
3154     }
3155 
3156     /**
3157      * {@inheritDoc}
3158      */
3159     public boolean getUseUniformHeight() {
3160         return _delegate.getUseUniformHeight();
3161     }
3162 
3163     /**
3164      * {@inheritDoc}
3165      */
3166     public void setUseUniformHeight(boolean useUniformHeight) {
3167         _delegate.setUseUniformHeight(useUniformHeight);
3168     }
3169 
3170     /**
3171      * {@inheritDoc}
3172      */
3173     public void setSecondsDisplayed(int seconds, boolean center) {
3174         _delegate.setSecondsDisplayed(seconds, center);
3175     }
3176 
3177     /**
3178      * {@inheritDoc}
3179      */
3180     public void setSecondsDisplayed(int seconds, JaretDate centerDate) {
3181         _delegate.setSecondsDisplayed(seconds, centerDate);
3182     }
3183 
3184     /**
3185      * {@inheritDoc}
3186      */
3187     public boolean isDisplayed(JaretDate date) {
3188         return _delegate.isDisplayed(date);
3189     }
3190 
3191     /**
3192      * {@inheritDoc}
3193      */
3194     public void setInitialDisplayRange(JaretDate startDate, int secondsDisplayed) {
3195         _delegate.setInitialDisplayRange(startDate, secondsDisplayed);
3196     }
3197 
3198     /**
3199      * Retrieve the panel that the horizontal scroll bar is plcaed on (BorderLayout, CENETER). This can be used to add
3200      * special extensions in the scroll bar area.
3201      * 
3202      * @return th panel or <code>null</code> if the scroll bar has been suppressed
3203      */
3204     public JPanel getHorizontalScrollPanel() {
3205         return _horizontalScrollPanel;
3206     }
3207 
3208     /**
3209      * Retrieve the panel that the vertical scroll bar is plcaed on (BorderLayout, CENETER). This can be used to add
3210      * special extensions in the scroll bar area.
3211      * 
3212      * @return th panel or <code>null</code> if the scroll bar has been suppressed
3213      */
3214     public JPanel getVerticalScrollPanel() {
3215         return _verticalScrollPanel;
3216     }
3217 
3218     /**
3219      * {@inheritDoc}
3220      */
3221     public Pair<TimeBarRow, JaretDate> getPopUpInformation() {
3222         return _delegate.getPopUpInformation();
3223     }
3224 
3225     /** list of ghost intervals to be painted. */
3226     protected List<? extends Interval> _ghostIntervals;
3227     /** y offsets for the ghost intervals. */
3228     protected List<Integer> _ghostIntervalYCoordinates;
3229     /**
3230      * the origin for painting the ghost intervals/rows. The ghosted elements will paintetd relative to the y
3231      * coordinate.
3232      */
3233     protected Point _ghostOrigin;
3234     /** list of ghost rows to paint. */
3235     protected List<TimeBarRow> _ghostRows;
3236     /** y offsets for the ghost rows. */
3237     protected List<Integer> _ghostRowYCoordinates;
3238 
3239     /**
3240      * Set the list of ghost intervals to be drawn.
3241      * 
3242      * @param intervals list of intervals or <code>null</code> to delete ghosted intervals.
3243      * @param yCoordinates list of y offsets for the ghost intervals (maybe <code>null</code> when deleting ghost
3244      * intervals
3245      */
3246     public void setGhostIntervals(List<? extends Interval> intervals, List<Integer> yCoordinates) {
3247         _ghostIntervals = intervals;
3248         _ghostIntervalYCoordinates = yCoordinates;
3249         repaint();
3250     }
3251 
3252     /**
3253      * Draw ghost intervals and rows.
3254      * 
3255      * @param gc GC
3256      */
3257     private void drawGhosts(Graphics gc) {
3258         drawGhostIntervals(gc);
3259         drawGhostRows(gc);
3260     }
3261 
3262     /**
3263      * Draw ghost intervals. Y positions are dependant from the ghost y offsets and the ghost origin. (Always uses the
3264      * defaultRowHeight.)
3265      * 
3266      * @param gc GC
3267      */
3268     private void drawGhostIntervals(Graphics gc) {
3269         if (_ghostOrigin != null && _ghostIntervals != null) {
3270             for (int i = 0; i < _ghostIntervals.size(); i++) {
3271                 Interval interval = _ghostIntervals.get(i);
3272                 int yoff = _ghostIntervalYCoordinates.get(i);
3273 
3274                 Rectangle drawingArea = _delegate.getIntervalBounds(-1, interval);
3275                 if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
3276                     drawingArea.y = _ghostOrigin.y + yoff;
3277                     drawingArea.height = _delegate.getTimeBarViewState().getDefaultRowHeight();
3278                 } else {
3279                     drawingArea.x = _ghostOrigin.x + yoff;
3280                     drawingArea.width = _delegate.getTimeBarViewState().getDefaultRowHeight();
3281                 }
3282                 Graphics2D g2 = (Graphics2D) gc;
3283                 Color color = g2.getColor();
3284                 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
3285 
3286                 TimeBarRenderer renderer = getRenderer(interval.getClass());
3287                 if (renderer == null) {
3288                     throw new RuntimeException("no suitable renderer");
3289                 }
3290 
3291                 Component component = renderer.getTimeBarRendererComponent(this, interval, false, false);
3292 
3293                 component.setBounds(drawingArea);//
3294 
3295                 Graphics2D g22 = (Graphics2D) g2.create(drawingArea.x, drawingArea.y - _delegate.getRowHeight() / 2,
3296                         drawingArea.width, drawingArea.height);
3297                 component.paint(g22);
3298 
3299                 g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
3300                 g2.setColor(color);
3301 
3302             }
3303         }
3304     }
3305 
3306     /**
3307      * Draw ghost rows. Y positions are dependent from the ghost y offsets and the ghost origin.
3308      * 
3309      * @param gc GC
3310      */
3311     private void drawGhostRows(Graphics gc) {
3312         if (_ghostOrigin != null && _ghostRows != null) {
3313 
3314             Graphics2D g2 = (Graphics2D) gc;
3315             Color color = g2.getColor();
3316             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.4f));
3317 
3318             for (int i = 0; i < _ghostRows.size(); i++) {
3319                 TimeBarRow row = _ghostRows.get(i);
3320                 int yoff = _ghostRowYCoordinates.get(i);
3321 
3322                 if (_delegate.getOrientation().equals(Orientation.HORIZONTAL)) {
3323                     int y = _ghostOrigin.y + yoff;
3324                     int height = _delegate.getRowHeight(); // default height
3325                     _diagram.drawRow(g2, y, height, row, false);
3326                 } else {
3327                     int x = _ghostOrigin.x + yoff;
3328                     int width = _delegate.getRowHeight(); // default height
3329                     _diagram.drawRowVertical(g2, x, width, row, false);
3330                 }
3331             }
3332             g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 1.0f));
3333             g2.setColor(color);
3334         }
3335     }
3336 
3337     /**
3338      * Set the origin (current drag position) to shift the ghost elements.
3339      * 
3340      * @param x x coordinate
3341      * @param y y coordniate
3342      */
3343     public void setGhostOrigin(int x, int y) {
3344         _ghostOrigin = new Point(x, y);
3345         if (_ghostIntervals != null || _ghostRows != null) {
3346             repaint();
3347         }
3348     }
3349 
3350     /**
3351      * If set to true this will cause the title renderer component to be used directly instead of beeing just used to
3352      * paint.
3353      * 
3354      * @param useTitleRendererComponentInPlace true for direct use of the component
3355      */
3356     public void setUseTitleRendererComponentInPlace(boolean useTitleRendererComponentInPlace) {
3357         _useTitleRendererComponentInPlace = useTitleRendererComponentInPlace;
3358     }
3359 
3360     /**
3361      * {@inheritDoc}
3362      */
3363     public int scrollDateToVisible(JaretDate date) {
3364         return _delegate.scrollDateToVisible(date);
3365     }
3366 
3367     /**
3368      * {@inheritDoc}
3369      */
3370     public void scrollRowToVisible(TimeBarRow row) {
3371         _delegate.scrollRowToVisible(row);
3372     }
3373 
3374     /**
3375      * {@inheritDoc}
3376      */
3377     public void scrollIntervalToVisible(TimeBarRow row, Interval interval) {
3378         _delegate.scrollIntervalToVisible(row, interval);
3379     }
3380 
3381 }