View Javadoc

1   /*
2    *  File: SwtHierachy.java 
3    *  Copyright (c) 2004-2007  Peter Kliem (Peter.Kliem@jaret.de)
4    *  A commercial license is available, see http://www.jaret.de.
5    *
6    *  This program is free software; you can redistribute it and/or modify
7    *  it under the terms of the GNU General Public License as published by
8    *  the Free Software Foundation; either version 2 of the License, or
9    *  (at your option) any later version.
10   *
11   *  This program is distributed in the hope that it will be useful,
12   *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13   *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   *  GNU General Public License for more details.
15   *
16   *  You should have received a copy of the GNU General Public License
17   *  along with this program; if not, write to the Free Software
18   *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   */
20  package de.jaret.examples.timebars.hierarchy.swt;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Locale;
25  
26  import org.eclipse.jface.action.Action;
27  import org.eclipse.jface.action.MenuManager;
28  import org.eclipse.jface.resource.ImageDescriptor;
29  import org.eclipse.jface.resource.ImageRegistry;
30  import org.eclipse.jface.viewers.ILabelProvider;
31  import org.eclipse.jface.viewers.ILabelProviderListener;
32  import org.eclipse.jface.window.ApplicationWindow;
33  import org.eclipse.swt.SWT;
34  import org.eclipse.swt.dnd.DND;
35  import org.eclipse.swt.dnd.DragSource;
36  import org.eclipse.swt.dnd.DragSourceEvent;
37  import org.eclipse.swt.dnd.DragSourceListener;
38  import org.eclipse.swt.dnd.DropTarget;
39  import org.eclipse.swt.dnd.DropTargetEvent;
40  import org.eclipse.swt.dnd.DropTargetListener;
41  import org.eclipse.swt.dnd.TextTransfer;
42  import org.eclipse.swt.dnd.Transfer;
43  import org.eclipse.swt.events.DisposeEvent;
44  import org.eclipse.swt.events.DisposeListener;
45  import org.eclipse.swt.graphics.Image;
46  import org.eclipse.swt.graphics.Point;
47  import org.eclipse.swt.layout.GridData;
48  import org.eclipse.swt.layout.GridLayout;
49  import org.eclipse.swt.widgets.Composite;
50  import org.eclipse.swt.widgets.Control;
51  import org.eclipse.swt.widgets.Display;
52  import org.eclipse.swt.widgets.Menu;
53  import org.eclipse.swt.widgets.Shell;
54  
55  import de.jaret.examples.timebars.hierarchy.model.DemoTimeBarNode;
56  import de.jaret.examples.timebars.hierarchy.model.ModelCreator;
57  import de.jaret.examples.timebars.pdi.swt.SwtControlPanel;
58  import de.jaret.util.date.Interval;
59  import de.jaret.util.date.IntervalImpl;
60  import de.jaret.util.date.JaretDate;
61  import de.jaret.util.date.holidayenumerator.HolidayEnumeratorFactory;
62  import de.jaret.util.ui.ResourceImageDescriptor;
63  import de.jaret.util.ui.timebars.TimeBarMarkerImpl;
64  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
65  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
66  import de.jaret.util.ui.timebars.mod.DefaultIntervalModificator;
67  import de.jaret.util.ui.timebars.model.AddingTimeBarRowModel;
68  import de.jaret.util.ui.timebars.model.DefaultRowHeader;
69  import de.jaret.util.ui.timebars.model.DefaultTimeBarNode;
70  import de.jaret.util.ui.timebars.model.DefaultTimeBarRowModel;
71  import de.jaret.util.ui.timebars.model.HierarchicalTimeBarModel;
72  import de.jaret.util.ui.timebars.model.ISelectionRectListener;
73  import de.jaret.util.ui.timebars.model.PPSInterval;
74  import de.jaret.util.ui.timebars.model.TBRect;
75  import de.jaret.util.ui.timebars.model.TimeBarNode;
76  import de.jaret.util.ui.timebars.model.TimeBarRow;
77  import de.jaret.util.ui.timebars.swt.RowContextMenuHandler;
78  import de.jaret.util.ui.timebars.swt.TimeBarViewer;
79  import de.jaret.util.ui.timebars.swt.renderer.BoxTimeScaleRenderer;
80  import de.jaret.util.ui.timebars.swt.renderer.DefaultGridRenderer;
81  import de.jaret.util.ui.timebars.swt.renderer.DefaultHierarchyRenderer;
82  import de.jaret.util.ui.timebars.swt.renderer.DefaultTitleRenderer;
83  import de.jaret.util.ui.timebars.swt.renderer.RelationRenderer;
84  import de.jaret.util.ui.timebars.swt.util.actions.JaretTimeBarsActionFactory;
85  
86  /***
87   * Simple hierarchical view, SWT version. Scaling, manipulating the intervals, tree structure. The sum interval is
88   * rendered by a specialized sum renderer. The arrows beetwenn the intervals are a demonstration of the relation
89   * renderer that in fact is a global assistance renderer. Context menus on scale and hierarchy. Hierarchy using label
90   * provider including icons.
91   * <p>
92   * The example also demonstrates the usage of a variable xaxis definition. To see that version set _variableXAxis to
93   * true.
94   * </p>
95   * 
96   * @author Peter Kliem
97   * @version $Id: SwtHierachy.java 1083 2011-07-01 20:29:16Z kliem $
98   */
99  public class SwtHierachy extends ApplicationWindow {
100     // if set to true a variable xaxis scaling will be used
101     private static final boolean VARIABLE_XAXIS = false;
102 
103     static TimeBarViewer _tbv;
104 
105     /***
106      * If set to true, DND of nodes will be supported (for testing). This disables drawing of references.
107      */
108     private static final boolean SUPPORT_DND = false;
109 
110     public SwtHierachy() {
111         super(null);
112     }
113 
114     protected Control createContents(Composite parent) {
115         GridLayout gridLayout = new GridLayout();
116         gridLayout.numColumns = 1;
117         parent.setLayout(gridLayout);
118 
119         // create the model
120         HierarchicalTimeBarModel model = ModelCreator.createModel(2, 5);
121 
122         _tbv = new TimeBarViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
123         GridData gd = new GridData(GridData.FILL_BOTH);
124         _tbv.setLayoutData(gd);
125 
126         _tbv.setTimeScalePosition(TimeBarViewer.TIMESCALE_POSITION_TOP);
127 
128         _tbv.setModel(model);
129         
130         // test initial range
131         //_tbv.setInitialDisplayRange(new JaretDate().advanceDays(1), 120);
132         
133         _tbv.setPixelPerSecond(0.00144);
134 
135         // configure the title renderer with a background image and set the title
136         DefaultTitleRenderer titleRenderer = new DefaultTitleRenderer();
137         titleRenderer.setBackgroundRscName("/de/jaret/examples/timebars/hierarchy/swt/titlebg.png");
138         _tbv.setTitleRenderer(titleRenderer);
139         _tbv.setTitle("SwtHierarchy");
140 
141         // variable xaxis demonstration
142         if (VARIABLE_XAXIS) {
143             _tbv.setVariableXScale(true);
144             DefaultTimeBarNode scaleRow = (DefaultTimeBarNode) _tbv.getPpsRow();
145             scaleRow.setLevel(1);
146             model.getRootNode().addNode(scaleRow);
147             PPSInterval pps1 = new PPSInterval(_tbv.getPixelPerSecond() / 2);
148             pps1.setBegin(new JaretDate());
149             pps1.setEnd(new JaretDate().advanceDays(1));
150             scaleRow.addInterval(pps1);
151 
152             PPSInterval pps2 = new PPSInterval(_tbv.getPixelPerSecond() / 4);
153             pps2.setBegin(new JaretDate().advanceDays(3));
154             pps2.setEnd(new JaretDate().advanceDays(5));
155             scaleRow.addInterval(pps2);
156         }
157 
158         // create and configure hierarchy renderer
159         DefaultHierarchyRenderer hrenderer = new DefaultHierarchyRenderer();
160         hrenderer.setLabelProvider(new LabelProvider());
161         hrenderer.setDrawLabels(true);
162         hrenderer.setDrawIcons(true);
163         hrenderer.setLevelWidth(25);
164         hrenderer.setFixedLevelWidth(true);
165         hrenderer.setRscNames("/de/jaret/examples/timebars/hierarchy/swt/collapsed.png",
166                 "/de/jaret/examples/timebars/hierarchy/swt/expanded.png",
167                 "/de/jaret/examples/timebars/hierarchy/swt/leaf.png");
168         hrenderer.setSize(16);
169         _tbv.setHierarchyRenderer(hrenderer);
170         _tbv.setHierarchyWidth(200);
171 
172         // allow interval modifications using the default modificator
173         _tbv.addIntervalModificator(new DefaultIntervalModificator());
174 
175         // register sum renderer for merged intervals
176         _tbv.registerTimeBarRenderer(AddingTimeBarRowModel.MergedInterval.class, new SumRenderer());
177 
178         // create and configure a grid renderer
179         DefaultGridRenderer gridRenderer = new DefaultGridRenderer();
180         gridRenderer.setHolidayEnumerator(HolidayEnumeratorFactory.getHolidayEnumeratorInstance(Locale.getDefault(),
181                 "NRW"));
182         _tbv.setGridRenderer(gridRenderer);
183 
184         // time scale renderer
185         // DefaultTimeScaleRenderer scaleRenderer = new DefaultTimeScaleRenderer();
186         // scaleRenderer.setHolidayEnumerator(HolidayEnumeratorFactory.getHolidayEnumeratorInstance(Locale.getDefault(),
187         // "NRW"));
188         // _tbv.setTimeScaleRenderer(scaleRenderer);
189 
190         _tbv.setTimeScaleRenderer(new BoxTimeScaleRenderer());
191 
192         final RelationRenderer relationRenderer = new RelationRenderer();
193         if (!SUPPORT_DND) {
194             _tbv.setRelationRenderer(relationRenderer);
195         }
196         // sample time bar marker
197         TimeBarMarkerImpl tm = new TimeBarMarkerImpl(true, new JaretDate().advanceDays(1));
198         tm.setDescription("TimeBarMarker");
199         _tbv.addMarker(tm);
200 
201         // control panel
202         SwtControlPanel ctrl = new SwtControlPanel(parent, SWT.NULL, _tbv, null);
203         gd = new GridData(GridData.FILL_HORIZONTAL);
204         ctrl.setLayoutData(gd);
205 
206         // hierarchy control panel
207         SwtHierarchyControlPanel ctrl2 = new SwtHierarchyControlPanel(parent, SWT.NULL, _tbv);
208         gd = new GridData(GridData.FILL_HORIZONTAL);
209         ctrl2.setLayoutData(gd);
210 
211         // hierarchy control panel
212         SwtRelationRendererControlPanel ctrl3 = new SwtRelationRendererControlPanel(parent, SWT.NULL, _tbv);
213         gd = new GridData(GridData.FILL_HORIZONTAL);
214         ctrl3.setLayoutData(gd);
215 
216         // context menues
217         // timescale context menu
218         MenuManager mm = new MenuManager();
219         mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_SCALETOWEEK));
220         mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_SCALETOMONTH));
221         mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_SCALETOYEAR));
222         mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_CENTERSCALE));
223         _tbv.setScaleContextMenu(mm.createContextMenu(_tbv));
224 
225         // row context menu
226         RowContextMenuHandler hierarchyCtxHandler = new HCtxHandler();
227         _tbv.setHierarchyCtxHandler(hierarchyCtxHandler);
228 
229         // expand all nodes
230         TimeBarNode root = model.getRootNode();
231         _tbv.getHierarchicalViewState().setExpandedRecursive(root, true);
232 
233         // Zoom action and rect region selection
234         _tbv.setRegionRectEnable(true); // enable region selections
235 
236         // set up the body context menu
237         MenuManager bodyMM = new MenuManager();
238         bodyMM.add(new ZoomAction(_tbv));
239         bodyMM.add(new ClearRegionAction(_tbv));
240         Menu bodyContextMenu = bodyMM.createContextMenu(_tbv);
241         _tbv.setBodyContextMenu(bodyContextMenu);
242 
243 
244         MenuManager intervalMM = new MenuManager();
245         intervalMM.add(new DummyAction(_tbv));
246         Menu intervalContextMenu = intervalMM.createContextMenu(_tbv);
247         _tbv.setIntervalContextMenu(intervalContextMenu);
248         
249         if (SUPPORT_DND) {
250             initDND(_tbv, parent);
251         }
252 
253         // Interval modificator preventing intervals from beeing overlapped by shifting or sizing
254         // _tbv.addIntervalModificator(new DefaultIntervalModificator() {
255         // 
256         //            
257         // @Override
258         // public boolean newBeginAllowed(TimeBarRow row, Interval interval, JaretDate newBegin) {
259         // boolean result = true;
260         // for (Interval i : row.getIntervals()) {
261         // if (i != interval && i.contains(newBegin)) {
262         // result = false;
263         // break;
264         // }
265         // }
266         //                
267         // return result;
268         // }
269         // @Override
270         // public boolean newEndAllowed(TimeBarRow row, Interval interval, JaretDate newBegin) {
271         // boolean result = true;
272         // for (Interval i : row.getIntervals()) {
273         // if (i != interval && i.contains(newBegin)) {
274         // result = false;
275         // break;
276         // }
277         // }
278         //                
279         // return result;
280         // }
281         //            
282         // @Override
283         // public boolean shiftAllowed(TimeBarRow row, Interval interval, JaretDate newBegin) {
284         // boolean result = true;
285         // for (Interval i : row.getIntervals()) {
286         // if (i != interval && i.contains(newBegin)) {
287         // result = false;
288         // break;
289         // }
290         // }
291         //                
292         // return result;
293         // }
294         //            
295         // @Override
296         // public boolean isApplicable(TimeBarRow row, Interval interval) {
297         // return true;
298         // }
299         //
300         // });
301 
302         // add an Iselectionrectlistener for demonstration purposes
303         // _tbv.addSelectionRectListener(new ISelectionRectListener() {
304         //
305         // @Override
306         // public void selectionRectChanged(TimeBarViewerDelegate delegate, JaretDate beginDate, JaretDate endDate,
307         // List<TimeBarRow> rows) {
308         // System.out.println("SelRectChanged: "+beginDate.toDisplayString()+"--"+endDate.toDisplayString()+" rows:");
309         // for (TimeBarRow timeBarRow : rows) {
310         // System.out.println(timeBarRow.getRowHeader().getLabel());
311         // }
312         // }
313         //
314         // @Override
315         // public void selectionRectClosed(TimeBarViewerDelegate delegate) {
316         // System.out.println("SelRectClosed");
317         // }
318         //
319         //        
320         // @Override
321         // public void regionRectClosed(TimeBarViewerDelegate delegate) {
322         // System.out.println("region rect closed");
323         // }
324         //
325         // @Override
326         // public void regionRectChanged(TimeBarViewerDelegate delegate, TBRect tbrect) {
327         // System.out.println("region rect changed");
328         // }
329         //            
330         // });
331 
332         return _tbv;
333     }
334 
335     /***
336      * Action that zooms the timeline to the selection if present. It listens to changes on the region selection to
337      * determine its enablement.
338      * 
339      * @author kliem
340      * @version $Id: SwtHierachy.java 1083 2011-07-01 20:29:16Z kliem $
341      */
342     public class ZoomAction extends Action implements ISelectionRectListener {
343         private TimeBarViewer _tbv;
344 
345         public ZoomAction(TimeBarViewer tbv) {
346             _tbv = tbv;
347             setEnabled(false);
348             _tbv.addSelectionRectListener(this);
349         }
350 
351         /***
352          * {@inheritDoc}
353          */
354         public void run() {
355             if (_tbv.getRegionRect() != null) {
356                 TBRect tbrect = _tbv.getRegionRect();
357                 JaretDate startDate = tbrect.startDate;
358                 int seconds = tbrect.endDate.diffSeconds(tbrect.startDate);
359                 int pixel = _tbv.getDelegate().getDiagramRect().width;
360                 double pps = ((double) pixel) / ((double) seconds);
361                 _tbv.clearRegionRect();
362                 _tbv.setPixelPerSecond(pps);
363                 _tbv.setStartDate(startDate);
364                 // TODO row scaling
365             }
366         }
367 
368         /***
369          * {@inheritDoc}
370          */
371         public String getText() {
372             return "Zoom to region";
373         }
374 
375         public void regionRectChanged(TimeBarViewerDelegate delegate, TBRect tbrect) {
376             setEnabled(true);
377         }
378 
379         public void regionRectClosed(TimeBarViewerDelegate delegate) {
380             setEnabled(false);
381         }
382 
383         public void selectionRectChanged(TimeBarViewerDelegate delegate, JaretDate beginDate, JaretDate endDate,
384                 List<TimeBarRow> rows) {
385             // TODO Auto-generated method stub
386 
387         }
388 
389         public void selectionRectClosed(TimeBarViewerDelegate delegate) {
390             // TODO Auto-generated method stub
391         }
392     }
393 
394     /***
395      * Action that clears the region rect.
396      * 
397      * @author kliem
398      * @version $Id: SwtHierachy.java 1083 2011-07-01 20:29:16Z kliem $
399      */
400     public class ClearRegionAction extends Action {
401         private TimeBarViewer _tbv;
402 
403         public ClearRegionAction(TimeBarViewer tbv) {
404             _tbv = tbv;
405         }
406 
407         /***
408          * {@inheritDoc}
409          */
410         public void run() {
411             _tbv.clearRegionRect();
412         }
413 
414         /***
415          * {@inheritDoc}
416          */
417         public String getText() {
418             return "Clear region";
419         }
420 
421     }
422 
423     /***
424      * Simpled dummy action.
425      * 
426      * @author kliem
427      * @version $Id: SwtHierachy.java 1083 2011-07-01 20:29:16Z kliem $
428      */
429     public class DummyAction extends Action {
430         private TimeBarViewer _tbv;
431 
432         public DummyAction(TimeBarViewer tbv) {
433             _tbv = tbv;
434         }
435 
436         /***
437          * {@inheritDoc}
438          */
439         public void run() {
440             System.out.println("dummy action triggered");
441         }
442 
443         /***
444          * {@inheritDoc}
445          */
446         public String getText() {
447             return "Dummy action";
448         }
449 
450     }
451 
452     
453     public class HCtxHandler implements RowContextMenuHandler {
454         Menu ctx;
455 
456         public HCtxHandler() {
457             MenuManager mm = new MenuManager();
458             mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_EXPANDNODE));
459             mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_COLLAPSENODE));
460             mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_EXPANDALL));
461             mm.add(_tbv.getActionFactory().createStdAction(JaretTimeBarsActionFactory.ACTION_COLLAPSEALL));
462             ctx = mm.createContextMenu(_tbv);
463         }
464 
465         public Menu getContextMenu(TimeBarViewer tbv, TimeBarRow row) {
466             return ctx;
467         }
468     }
469 
470     protected void configureShell(Shell shell) {
471         super.configureShell(shell);
472         shell.setText(getClass().getName());
473         shell.setSize(800, 800);
474     }
475 
476     public static void main(String[] args) {
477         SwtHierachy test = new SwtHierachy();
478         test.setBlockOnOpen(true);
479         test.open();
480     }
481 
482     public class LabelProvider implements ILabelProvider {
483         ImageRegistry _imageRegistry;
484 
485         public Image getImage(Object element) {
486             if (element instanceof DemoTimeBarNode) {
487                 DemoTimeBarNode node = (DemoTimeBarNode) element;
488                 return node.isType() ? getImageRegistry().get("true") : getImageRegistry().get("false");
489             } else {
490                 return getImageRegistry().get("adding");
491             }
492 
493         }
494 
495         public String getText(Object element) {
496             if (element instanceof DemoTimeBarNode) {
497                 DemoTimeBarNode node = (DemoTimeBarNode) element;
498                 return node.getText();
499             } else if (element instanceof DefaultTimeBarNode && VARIABLE_XAXIS) {
500                 return "PPS";
501             } else {
502                 return "adding node";
503             }
504         }
505 
506         public void addListener(ILabelProviderListener listener) {
507         }
508 
509         public void dispose() {
510             if (_imageRegistry != null) {
511                 _imageRegistry.dispose();
512             }
513         }
514 
515         public boolean isLabelProperty(Object element, String property) {
516             return false;
517         }
518 
519         public void removeListener(ILabelProviderListener listener) {
520         }
521 
522         public ImageRegistry getImageRegistry() {
523             if (_imageRegistry == null) {
524                 _imageRegistry = new ImageRegistry();
525                 ImageDescriptor imgDesc = new ResourceImageDescriptor("/de/jaret/examples/timebars/hierarchy/true.gif");
526                 _imageRegistry.put("true", imgDesc);
527                 imgDesc = new ResourceImageDescriptor("/de/jaret/examples/timebars/hierarchy/false.gif");
528                 _imageRegistry.put("false", imgDesc);
529                 imgDesc = new ResourceImageDescriptor("/de/jaret/examples/timebars/hierarchy/adding.gif");
530                 _imageRegistry.put("adding", imgDesc);
531             }
532             return _imageRegistry;
533         }
534     }
535 
536     // information about ongoing drag operation
537     private Point _dragStart;
538     private int _startOffsetX;
539     private int _startOffsetY;
540     private JaretDate _dragStartDate;
541     private List<Interval> _draggedIntervals;
542     private List<Integer> _yOffsets;
543     private List<Interval> _origIntervals;
544     private TimeBarRow _draggedRow;
545     private TimeBarNode _parentNode;
546     private boolean _isRowDrag;
547 
548     /***
549      * Init the drag source and the drop target for the timebar viewer. Information will be provided as text for
550      * dropping outside the table viewer. Drag&Drop inside the viewer uses instance data to transport the information
551      * about the dragged interval. This example supports dragging all selected intervals and dragging a single row
552      * determined when the drag starts.
553      * 
554      * @param tbv timebarviewer
555      * @param parent parent of the timebarviewer (for disposal listening)
556      */
557     private void initDND(final TimeBarViewer tbv, Composite parent) {
558         // support move only
559         int operations = DND.DROP_MOVE;
560         final DragSource source = new DragSource(tbv, operations);
561 
562         // Provide data in Text format
563         Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
564         source.setTransfer(types);
565 
566         source.addDragListener(new DragSourceListener() {
567             public void dragStart(DragSourceEvent event) {
568 
569                 // check whether drag occured on header or hierarchy area
570                 if (_tbv.isInRowAxis(event.x, event.y)) {
571                     // possible row drag
572                     TimeBarRow row = _tbv.getRowForXY(event.x, event.y);
573                     if (row != null && !_tbv.rowLineHit(event.x, event.y)) {
574                         // row hit, start row drag
575                         _isRowDrag = true;
576                         _dragStart = new Point(event.x, event.y);
577 
578                         // capture the data for internal use
579                         // row drag: use row at starting position
580                         _draggedRow = row;
581                         _parentNode = getParent(_tbv.getHierarchicalModel().getRootNode(), (TimeBarNode) row);
582 
583                     } else {
584                         event.doit = false;
585                     }
586 
587                 } else {
588                     // Only start the drag if there is an interval at hand
589                     // and the point of the beginning drag is not near the border of the interval, allowing the
590                     // timebarviewers implementation of dragging for moving inside a row and for resizing to be usable
591                     // even with an installed drag source (The internal drag feature can be disabled when using
592                     // the systems drag and drop support)
593                     List<Interval> l = tbv.getIntervalsAt(event.x, event.y);
594                     if (l.size() > 0) {
595                         Interval interval = l.get(0);
596                         JaretDate date = tbv.dateForXY(event.x, event.y);
597                         if (Math.abs(date.diffSeconds(interval.getBegin())) < 1000
598                                 || Math.abs(date.diffSeconds(interval.getEnd())) < 1000) {
599                             System.out.println("Near the border of an interval -> no drag");
600                             event.doit = false;
601                         } else {
602                             // remember the point the drag started
603                             _dragStart = new Point(event.x, event.y);
604                             _dragStartDate = _tbv.dateForXY(event.x, event.y);
605                             _isRowDrag = false;
606                             // make sure the interval is selected
607                             _tbv.getSelectionModel().addSelectedInterval(interval);
608 
609                             // capture the data of the dragged intervals
610                             // drag all selected intervals
611                             // to allow changing the time of the intervals and drawing ghost intervals a copy of the
612                             // intervals will be made
613                             _draggedIntervals = new ArrayList<Interval>();
614                             _origIntervals = new ArrayList<Interval>();
615                             _yOffsets = new ArrayList<Integer>();
616 
617                             for (Interval i : _tbv.getSelectionModel().getSelectedIntervals()) {
618                                 _draggedIntervals.add(new IntervalImpl(i.getBegin().copy(), i.getEnd().copy()));
619                                 _origIntervals.add(i);
620                                 TimeBarRow row = _tbv.getModel().getRowForInterval(i);
621                                 int yOffset;
622                                 if (_tbv.getTBOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
623                                     yOffset = _tbv.getYForRow(row) - _dragStart.y;
624                                 } else {
625                                     yOffset = _tbv.getYForRow(row) - _dragStart.x;
626                                 }
627                                 _yOffsets.add(yOffset);
628                             }
629 
630                             TimeBarRow row = _tbv.getRowForXY(event.x, event.y);
631                             // store both x and y offsets since the orientation may be vertical or horizontal
632                             _startOffsetY = event.y - tbv.getYForRow(row);
633                             _startOffsetX = event.x - tbv.getYForRow(row);
634                         }
635                     } else {
636                         event.doit = false;
637                     }
638                 }
639             }
640 
641             public void dragSetData(DragSourceEvent event) {
642                 // Provide the data of the requested type.
643                 if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
644                     // unfortenatly the coordinates in the event are always 0,0 -> use the coordinates transferred in
645                     // startDrag to get the interval (a possibly is to use the selection, especially for dragging
646                     // multiple intervals)
647                     if (_isRowDrag) {
648                         event.data = "row: " + _draggedRow.getRowHeader().getLabel();
649                     } else {
650                         StringBuffer buf = new StringBuffer();
651                         buf.append("intervals:");
652                         for (Interval interval : _origIntervals) {
653                             buf.append(interval);
654                             buf.append(";");
655                         }
656                         event.data = buf.toString();
657                     }
658                 }
659             }
660 
661             public void dragFinished(DragSourceEvent event) {
662                 // drag finished -> if it is a move operation remove the original intervals
663                 // from their rows
664                 if (event.detail == DND.DROP_MOVE) {
665                     System.out.println("Perform move");
666                     if (_isRowDrag) {
667                         _parentNode.remNode((TimeBarNode) _draggedRow);
668                     } else {
669                         for (Interval interval : _origIntervals) {
670                             DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) _tbv.getModel().getRowForInterval(
671                                     interval);
672                             row.remInterval(interval);
673                         }
674                     }
675                 }
676                 // clear the dragged data
677                 // and the ghosts in the viewer
678                 _tbv.deHighlightRow();
679                 _tbv.setGhostIntervals(null, null);
680                 _tbv.setGhostRows(null, null);
681                 _draggedIntervals = null;
682                 _draggedRow = null;
683                 _isRowDrag = false;
684                 _origIntervals = null;
685             }
686 
687         });
688 
689         // ////////////////////
690         // Drop target
691 
692         // Allow data to be copied or moved to the drop target
693         operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
694         final DropTarget target = new DropTarget(tbv, operations);
695 
696         // Receive data in Text
697         final TextTransfer textTransfer = TextTransfer.getInstance();
698         types = new Transfer[] {textTransfer};
699         target.setTransfer(types);
700 
701         target.addDropListener(new DropTargetListener() {
702             public void dragEnter(DropTargetEvent event) {
703             }
704 
705             public void dragOver(DropTargetEvent event) {
706                 // event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
707 
708                 if (_isRowDrag && _draggedRow != null) {
709                     // row drag
710                     int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
711                     int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
712                     tbv.setGhostRow(_draggedRow, 0);
713                     tbv.setGhostOrigin(destX, destY);
714                     TimeBarRow overRow = tbv.getRowForXY(destX, destY);
715                     if (overRow != null) {
716                         tbv.highlightRow(overRow);
717                     } else {
718                         tbv.deHighlightRow();
719                     }
720 
721                 } else if (!_isRowDrag && _draggedIntervals != null && _draggedIntervals.size() > 0) {
722                     // map the coordinates from the event to the timebar viewer widget
723                     int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
724                     int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
725 
726                     JaretDate curDate = _tbv.dateForXY(destX, destY);
727                     long diffSeconds = _dragStartDate.diffSeconds(curDate);
728                     // correct the dragged interval bounds
729                     for (int i = 0; i < _draggedIntervals.size(); i++) {
730                         Interval orig = _origIntervals.get(i);
731                         Interval interval = _draggedIntervals.get(i);
732                         interval.setBegin(orig.getBegin().copy().advanceSeconds(-diffSeconds));
733                         interval.setEnd(orig.getEnd().copy().advanceSeconds(-diffSeconds));
734                     }
735 
736                     // check if we are over a row and highlight it
737                     // this will only highlight the row for the mouse pointer
738                     // if multiple intervals are dragged their respective rows will not be highlighted
739                     TimeBarRow overRow = tbv.getRowForXY(destX, destY);
740                     if (overRow != null) {
741                         tbv.highlightRow(overRow);
742                     } else {
743                         System.out.println("Row is null x,y:" + event.x + "," + event.y);
744                         tbv.deHighlightRow();
745                     }
746                     // tell the timebar viewer
747                     tbv.setGhostIntervals(_draggedIntervals, _yOffsets);
748                     tbv.setGhostOrigin(destX, destY);
749                 }
750             }
751 
752             public void dragOperationChanged(DropTargetEvent event) {
753             }
754 
755             public void dragLeave(DropTargetEvent event) {
756                 // leaving: do not leave a row highlighted
757                 tbv.deHighlightRow();
758                 tbv.setGhostIntervals(null, null);
759             }
760 
761             public void dropAccept(DropTargetEvent event) {
762             }
763 
764             public void drop(DropTargetEvent event) {
765                 // drop operation. the drop operation places the dragged intervals in the destination rows.
766                 // if a row can not be determined (no row under the dragged interval) the interval will be discarded
767                 // ATTENTION: the implementation herein does only work properly if no row sorters or row filters are
768                 // applied.
769                 // If that is the case it is up to the implementation to take of that.
770                 if (textTransfer.isSupportedType(event.currentDataType)) {
771                     String text = (String) event.data;
772                     System.out.println("DROP: " + text);
773 
774                     if (_isRowDrag && _draggedRow != null) {
775                         int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
776                         int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
777 
778                         TimeBarRow overRow = tbv.getRowForXY(destX, destY);
779                         System.out.println("over row " + overRow.getRowHeader());
780                         if (overRow != null) {
781                             // this is an action from the drag source listener ...
782                             // this has to be done right here because otherwise the node would be at two places
783                             // at the same time causing some redraw trouble ...
784                             _parentNode.remNode((TimeBarNode) _draggedRow);
785                             TimeBarNode node = (TimeBarNode) overRow;
786                             node.addNode((TimeBarNode) _draggedRow);
787                         }
788                     } else if (_draggedIntervals != null) {
789 
790                         for (int i = 0; i < _draggedIntervals.size(); i++) {
791                             int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
792                             int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
793                             int offY = _yOffsets.get(i);
794                             TimeBarRow overRow = null;
795                             if (_tbv.getTBOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
796                                 overRow = tbv.rowForY(destY + offY + _startOffsetY);
797                             } else {
798                                 overRow = tbv.rowForY(destX + offY + _startOffsetX);
799                             }
800                             if (overRow instanceof DefaultTimeBarRowModel) {
801                                 DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) overRow;
802                                 if (overRow != null) {
803                                     row.addInterval(_draggedIntervals.get(i));
804                                 }
805                             }
806                         }
807                     }
808                 }
809             }
810 
811             private DefaultTimeBarRowModel copyDraggedRow() {
812                 DefaultRowHeader header = (DefaultRowHeader) _draggedRow.getRowHeader();
813                 DefaultTimeBarRowModel row = new DefaultTimeBarRowModel(new DefaultRowHeader(header.getLabel()
814                         + "(copy)"));
815                 for (Interval interval : _draggedRow.getIntervals()) {
816                     row.addInterval(new IntervalImpl(interval.getBegin().copy(), interval.getEnd().copy()));
817                 }
818                 return row;
819             }
820         });
821 
822         // Dispose listener on parent of timebar viewer to dispose the dragsource and dragtarget BEFORE the timebar
823         // viewer
824         // this prevents an exception beeing thrown by SWT
825         parent.addDisposeListener(new DisposeListener() {
826             public void widgetDisposed(DisposeEvent e) {
827                 source.dispose();
828                 target.dispose();
829             }
830         });
831 
832     }
833 
834     /***
835      * Removes a node from it's parent node.
836      * 
837      * @param model
838      * @param draggedRow
839      */
840     private boolean removeNode(TimeBarNode root, TimeBarNode draggedRow) {
841         if (root.getChildren().contains(draggedRow)) {
842             root.remNode(draggedRow);
843             return true;
844         } else {
845             for (TimeBarNode node : root.getChildren()) {
846                 boolean result = removeNode(node, draggedRow);
847                 if (result) {
848                     return true;
849                 }
850             }
851         }
852         return false;
853     }
854 
855     private TimeBarNode getParent(TimeBarNode root, TimeBarNode draggedRow) {
856         if (root.getChildren().contains(draggedRow)) {
857             return root;
858         } else {
859             for (TimeBarNode node : root.getChildren()) {
860                 TimeBarNode result = getParent(node, draggedRow);
861                 if (result != null) {
862                     return result;
863                 }
864             }
865         }
866         return null;
867     }
868 
869 }