View Javadoc

1   /*
2    *  File: SwtOverlapExample.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.simple.swt;
21  
22  import java.awt.Rectangle;
23  import java.util.ArrayList;
24  import java.util.List;
25  
26  import org.eclipse.jface.window.ApplicationWindow;
27  import org.eclipse.swt.SWT;
28  import org.eclipse.swt.dnd.DND;
29  import org.eclipse.swt.dnd.DragSource;
30  import org.eclipse.swt.dnd.DragSourceEvent;
31  import org.eclipse.swt.dnd.DragSourceListener;
32  import org.eclipse.swt.dnd.DropTarget;
33  import org.eclipse.swt.dnd.DropTargetEvent;
34  import org.eclipse.swt.dnd.DropTargetListener;
35  import org.eclipse.swt.dnd.TextTransfer;
36  import org.eclipse.swt.dnd.Transfer;
37  import org.eclipse.swt.events.DisposeEvent;
38  import org.eclipse.swt.events.DisposeListener;
39  import org.eclipse.swt.events.MouseAdapter;
40  import org.eclipse.swt.events.MouseEvent;
41  import org.eclipse.swt.events.SelectionAdapter;
42  import org.eclipse.swt.events.SelectionEvent;
43  import org.eclipse.swt.graphics.Point;
44  import org.eclipse.swt.layout.GridData;
45  import org.eclipse.swt.layout.GridLayout;
46  import org.eclipse.swt.layout.RowLayout;
47  import org.eclipse.swt.widgets.Button;
48  import org.eclipse.swt.widgets.Composite;
49  import org.eclipse.swt.widgets.Control;
50  import org.eclipse.swt.widgets.Display;
51  import org.eclipse.swt.widgets.Shell;
52  
53  import de.jaret.examples.timebars.pdi.swt.SwtControlPanel;
54  import de.jaret.examples.timebars.simple.OtherIntervalImpl;
55  import de.jaret.examples.timebars.simple.model.ModelCreator;
56  import de.jaret.util.date.Interval;
57  import de.jaret.util.date.IntervalImpl;
58  import de.jaret.util.date.JaretDate;
59  import de.jaret.util.ui.timebars.TimeBarMarker;
60  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
61  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
62  import de.jaret.util.ui.timebars.mod.DefaultIntervalModificator;
63  import de.jaret.util.ui.timebars.model.DefaultRowHeader;
64  import de.jaret.util.ui.timebars.model.DefaultTimeBarModel;
65  import de.jaret.util.ui.timebars.model.DefaultTimeBarRowModel;
66  import de.jaret.util.ui.timebars.model.IRowHeightStrategy;
67  import de.jaret.util.ui.timebars.model.ITimeBarChangeListener;
68  import de.jaret.util.ui.timebars.model.ITimeBarViewState;
69  import de.jaret.util.ui.timebars.model.TimeBarModel;
70  import de.jaret.util.ui.timebars.model.TimeBarRow;
71  import de.jaret.util.ui.timebars.model.TimeBarSelectionListener;
72  import de.jaret.util.ui.timebars.model.TimeBarSelectionModel;
73  import de.jaret.util.ui.timebars.strategy.DefaultOverlapStrategy;
74  import de.jaret.util.ui.timebars.swt.TimeBarViewer;
75  import de.jaret.util.ui.timebars.swt.renderer.BoxTimeScaleRenderer;
76  import de.jaret.util.ui.timebars.swt.renderer.CombiningTimeScaleRenderer;
77  import de.jaret.util.ui.timebars.swt.renderer.DateStripRenderer;
78  import de.jaret.util.ui.timebars.swt.renderer.DefaultTitleRenderer;
79  
80  /***
81   * SWT: example demonstrating the overlap detection and adapted painting of intervals. Also contains an example of using
82   * Drag&Drop with the TimeBarViewer.
83   * 
84   * @author Peter Kliem
85   * @version $Id: SwtOverlapExample.java 1093 2011-10-04 20:53:06Z kliem $
86   */
87  public class SwtOverlapExample extends ApplicationWindow {
88      /*** if set to true an ITimeBarChangeListener will be registered for monitoring changes. */
89      private static final boolean MONITORINTERVALCHANGES = false;
90      private static TimeBarViewer _tbv;
91  
92      public SwtOverlapExample() {
93          super(null);
94      }
95  
96      protected Control createContents(Composite parent) {
97          GridLayout gridLayout = new GridLayout();
98          gridLayout.numColumns = 1;
99          parent.setLayout(gridLayout);
100 
101         TimeBarModel model = ModelCreator.createModel();
102         // TimeBarModel model = ModelCreator.createLargeModel();
103 
104         GridData gd = new GridData(GridData.FILL_BOTH);
105 
106         _tbv = new TimeBarViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
107         _tbv.setLayoutData(gd);
108 
109         _tbv.setTimeScalePosition(TimeBarViewer.TIMESCALE_POSITION_TOP);
110         _tbv.setModel(model);
111     //    _tbv.setModel(new DefaultTimeBarModel());
112 
113         // add a context menu on intervals
114         // commented to not introduce a jface dependencay in this example
115         // MenuManager ctxMM = new MenuManager();
116         // ctxMM.add(new Action() {
117         // @Override
118         // public String getText() {
119         // return "Test";
120         // }
121         // });
122         // Menu intervalContextMenu = ctxMM.createContextMenu(_tbv);
123         // _tbv.setIntervalContextMenu(intervalContextMenu);
124 
125         // add two interval modificator using different grid snaps
126         // the last row (r3) will be filled with other intevals using the other grid snap
127         _tbv.addIntervalModificator(new DefaultIntervalModificator() {
128             @Override
129             public double getSecondGridSnap() {
130                 return 100;
131             }
132  
133 //            @Override
134 //            public double getSecondGridSnap(TimeBarRow row, Interval interval) {
135 //            	if (row.getIntervals().size() == 1) {
136 //            		return 2000;
137 //            	} 
138 //            	return -1;
139 //            }
140 
141             @Override
142             public boolean isApplicable(TimeBarRow row, Interval interval) {
143                 return !(interval instanceof OtherIntervalImpl);
144             }
145 
146         });
147         _tbv.addIntervalModificator(new DefaultIntervalModificator() {
148             @Override
149             public double getSecondGridSnap() {
150                 return 1000;
151             }
152 
153             @Override
154             public boolean isApplicable(TimeBarRow row, Interval interval) {
155                 return (interval instanceof OtherIntervalImpl);
156             }
157 
158         });
159 
160         // optional other interval renderer
161         //_tbv.registerTimeBarRenderer(OtherIntervalImpl.class, new OtherIntervalRenderer());
162         
163         
164         _tbv.setPixelPerSecond(0.05);
165         _tbv.setDrawRowGrid(true);
166 
167         _tbv.setSelectionDelta(6);
168 
169         // do not assume sorted intervals
170         // --> tell the default strategy to do the sorting
171         ((DefaultOverlapStrategy) _tbv.getOverlapStrategy()).setAssumeSortedIntervals(false);
172 
173         // configure the title renderer with a background image and set the title
174         DefaultTitleRenderer titleRenderer = new DefaultTitleRenderer();
175         titleRenderer.setBackgroundRscName("/de/jaret/examples/timebars/hierarchy/swt/titlebg.png");
176         _tbv.setTitleRenderer(titleRenderer);
177         _tbv.setTitle("SwtOverlap");
178         
179         BoxTimeScaleRenderer btsr = new BoxTimeScaleRenderer();
180         // enable DST correction
181         //btsr.setCorrectDST(true);
182         
183         DateStripRenderer dsr = new DateStripRenderer();
184         CombiningTimeScaleRenderer ctsr = new CombiningTimeScaleRenderer(dsr, btsr);
185         
186         _tbv.setTimeScaleRenderer(ctsr);
187 //        _tbv.setTimeScaleRenderer(btsr);
188         
189         
190         SwtControlPanel ctrl = new SwtControlPanel(parent, SWT.NULL, _tbv, null);
191         gd = new GridData(GridData.FILL_HORIZONTAL);
192         ctrl.setLayoutData(gd);
193 
194         OverlapControlPanel ctrl2 = new OverlapControlPanel(parent, SWT.NULL, _tbv);
195         gd = new GridData(GridData.FILL_HORIZONTAL);
196         ctrl2.setLayoutData(gd);
197 
198         // add a listener for doubleclicks (and some test code for selecting specially rendered intervals)
199         // shows how to get intervals from a point
200         _tbv.addMouseListener(new MouseAdapter() {
201             public void mouseDoubleClick(MouseEvent e) {
202                 List<Interval> intervals = _tbv.getIntervalsAt(e.x, e.y);
203                 if (intervals != null && intervals.size() > 0) {
204                     for (Interval interval : intervals) {
205                         System.out.println("Doubleclicked: " + interval.toString());
206                     }
207                 }
208             }
209 //            public void mouseUp(MouseEvent e) {
210 //            	List<Interval> intervals = _tbv.getIntervalsAt(e.x, e.y);
211 //                if (intervals == null || intervals.size() == 0) {
212 //                	// check all intervals in the row in the actual range
213 //                	TimeBarRow row = _tbv.getRowForXY(e.x, e.y);
214 //                	if (row != null) {
215 //                    	intervals = row.getIntervals(_tbv.getStartDate(), _tbv.getEndDate());
216 //                		for (Interval interval : intervals) {
217 //							Rectangle bounds = getBounds(row, interval);
218 //							if (bounds.contains(e.x, e.y)) {
219 //								_tbv.getSelectionModel().setSelectedInterval(interval);
220 //								_tbv.redraw();
221 //							}
222 //						}
223 //                	}
224 //                }
225 //            }
226 //            /***
227 //             * Retrieve bounds of an interval including possible extended rendering space
228 //             * @param row
229 //             * @param interval
230 //             * @return
231 //             */
232 //			private Rectangle getBounds(TimeBarRow row, Interval interval) {
233 //				int PREFWIDTH = 4;
234 //				org.eclipse.swt.graphics.Rectangle bounds = _tbv.getIntervalBounds(interval);
235 //				int diff = (PREFWIDTH-bounds.width/2);
236 //
237 //				Rectangle result = new Rectangle(bounds.x-diff, bounds.y, bounds.width+2*diff, bounds.height);
238 //				return result;
239 //			}
240         });
241 
242         initDND(_tbv, parent);
243 
244         if (MONITORINTERVALCHANGES) {
245             registerChangeListener(_tbv);
246         }
247 
248         // additional controls
249         Composite addPanel = new Composite(parent, SWT.NULL);
250         gd = new GridData(GridData.FILL_HORIZONTAL);
251         addPanel.setLayoutData(gd);
252         addPanel.setLayout(new RowLayout());
253 
254         final Button addIntervals = new Button(addPanel, SWT.PUSH);
255         addIntervals.setText("Add intervals to selected row");
256         addIntervals.addSelectionListener(new SelectionAdapter() {
257             @Override
258             public void widgetSelected(SelectionEvent e) {
259                 List<TimeBarRow> selectedRows = _tbv.getSelectionModel().getSelectedRows();
260                 if (selectedRows.size() == 1) {
261                     // create some random intervals and add them at once
262                     List<Interval> intervals = new ArrayList<Interval>();
263                     for (int i = 0; i < 20; i++) {
264                         Interval interval = new IntervalImpl();
265                         JaretDate startDate = _tbv.getStartDate().copy().advanceHours(Math.random() * 10);
266                         JaretDate endDate = startDate.copy().advanceHours(Math.random() * 5);
267                         interval.setBegin(startDate);
268                         interval.setEnd(endDate);
269                         intervals.add(interval);
270                     }
271                     // add all intervals at once
272                     ((DefaultTimeBarRowModel) selectedRows.get(0)).addIntervals(intervals);
273                 }
274             }
275         });
276 
277         final Button clearIntervals = new Button(addPanel, SWT.PUSH);
278         clearIntervals.setText("Clear selected row");
279         clearIntervals.addSelectionListener(new SelectionAdapter() {
280             @Override
281             public void widgetSelected(SelectionEvent e) {
282                 List<TimeBarRow> selectedRows = _tbv.getSelectionModel().getSelectedRows();
283                 if (selectedRows.size() == 1) {
284                     // correct selection
285                     _tbv.getSelectionModel().remSelectedIntervals(
286                             ((DefaultTimeBarRowModel) selectedRows.get(0)).getIntervals());
287                     // clear all intervals at once
288                     ((DefaultTimeBarRowModel) selectedRows.get(0)).clear();
289                 }
290             }
291         });
292         final Button remIntervals = new Button(addPanel, SWT.PUSH);
293         remIntervals.setText("Remove selected intervals");
294         remIntervals.addSelectionListener(new SelectionAdapter() {
295             @Override
296             public void widgetSelected(SelectionEvent e) {
297                 List<Interval> selectedIntervals = _tbv.getSelectionModel().getSelectedIntervals();
298                 if (selectedIntervals.size() > 0) {
299                     selectedIntervals = new ArrayList<Interval>(selectedIntervals);
300                     // remove from selection
301                     _tbv.getSelectionModel().remSelectedIntervals(selectedIntervals);
302                     // go through all rows, try to remove the intervals
303                     // (This is the hard way ...)
304                     for (int i = 0; i < _tbv.getModel().getRowCount(); i++) {
305                         ((DefaultTimeBarRowModel) _tbv.getModel().getRow(i)).remIntervals(selectedIntervals);
306                     }
307                 }
308             }
309         });
310         final Button changeIntervals = new Button(addPanel, SWT.PUSH);
311         changeIntervals.setText("Change selected intervals");
312         changeIntervals.addSelectionListener(new SelectionAdapter() {
313             @Override
314             public void widgetSelected(SelectionEvent e) {
315                 List<Interval> selectedIntervals = _tbv.getSelectionModel().getSelectedIntervals();
316                 if (selectedIntervals.size() > 0) {
317                     for (Interval interval : selectedIntervals) {
318                         // can be done like this (discouraged): interval.getBegin().advanceHours(1);
319                         interval.setBegin(interval.getBegin().copy().advanceHours(1));
320                     }
321                 }
322             }
323         });
324 
325         addIntervals.setEnabled(false);
326         clearIntervals.setEnabled(false);
327         remIntervals.setEnabled(false);
328         changeIntervals.setEnabled(false);
329         _tbv.getSelectionModel().addTimeBarSelectionListener(new TimeBarSelectionListener() {
330 
331             private void check(TimeBarSelectionModel selectionModel) {
332                 boolean oneRowSelected = selectionModel.getSelectedRows().size() == 1;
333                 addIntervals.setEnabled(oneRowSelected);
334                 clearIntervals.setEnabled(oneRowSelected);
335 
336                 boolean containsIntervals = selectionModel.getSelectedIntervals().size() > 0;
337                 remIntervals.setEnabled(containsIntervals);
338                 changeIntervals.setEnabled(containsIntervals);
339             }
340 
341             public void elementAddedToSelection(TimeBarSelectionModel selectionModel, Object element) {
342                 check(selectionModel);
343             }
344 
345             public void elementRemovedFromSelection(TimeBarSelectionModel selectionModel, Object element) {
346                 check(selectionModel);
347             }
348 
349             public void selectionChanged(TimeBarSelectionModel selectionModel) {
350                 check(selectionModel);
351             }
352 
353         });
354 
355         // Strategy checkbox
356         final Button heightStrategyCheck = new Button(addPanel, SWT.CHECK);
357         heightStrategyCheck.setText("Height strategy");
358         heightStrategyCheck.setSelection(_tbv.getTimeBarViewState().getRowHeightStrategy() != null);
359         heightStrategyCheck.addSelectionListener(new SelectionAdapter() {
360 
361             public void widgetSelected(SelectionEvent e) {
362                 if (heightStrategyCheck.getSelection()) {
363                     _tbv.getTimeBarViewState().setRowHeightStrategy(new IRowHeightStrategy() {
364                         public int calculateRowHeight(TimeBarViewerDelegate delegate,
365                                 ITimeBarViewState timeBarViewState, TimeBarRow row) {
366                             int maxOverlap = timeBarViewState.getDefaultRowHeight();
367                             int height = delegate.getMaxOverlapCount(row) * maxOverlap;
368                             return height;
369                         }
370 
371                         public boolean overrideDefault() {
372                             return true;
373                         }
374                     });
375                 } else {
376                     _tbv.getTimeBarViewState().setRowHeightStrategy(null);
377                 }
378 
379             }
380 
381         });
382 
383         // final Button setStartDate = new Button(addPanel, SWT.PUSH);
384         // setStartDate.setText("Set Start date-");
385         // setStartDate.addSelectionListener(new SelectionAdapter() {
386         // @Override
387         // public void widgetSelected(SelectionEvent e) {
388         // _tbv.setAdjustMinMaxDatesByModel(false);
389         // _tbv.setMinDate(_tbv.getMinDate().copy().backHours(2));
390         // _tbv.setStartDate(_tbv.getStartDate().copy().backHours(2));
391         // }
392         // });
393         // final Button setStartDate2 = new Button(addPanel, SWT.PUSH);
394         // setStartDate2.setText("Set Start date+");
395         // setStartDate2.addSelectionListener(new SelectionAdapter() {
396         // @Override
397         // public void widgetSelected(SelectionEvent e) {
398         // _tbv.setAdjustMinMaxDatesByModel(false);
399         // _tbv.setMaxDate(_tbv.getMaxDate().copy().advanceHours(2));
400         // _tbv.setStartDate(_tbv.getStartDate().copy().advanceHours(2));
401         // }
402         // });
403 
404         
405         DefaultOverlapStrategy os = (DefaultOverlapStrategy)_tbv.getOverlapStrategy();
406         os.setAssumeSortedIntervals(false);
407 
408         
409         // TODO temp
410         final Button lastrow5 = new Button(addPanel, SWT.PUSH);
411         lastrow5.setText("last 5");
412         lastrow5.addSelectionListener(new SelectionAdapter() {
413             @Override
414             public void widgetSelected(SelectionEvent e) {
415                 _tbv.getDelegate().setLastRow(5);
416             }
417         });
418         final Button lastrow7 = new Button(addPanel, SWT.PUSH);
419         lastrow7.setText("last 7");
420         lastrow7.addSelectionListener(new SelectionAdapter() {
421             @Override
422             public void widgetSelected(SelectionEvent e) {
423                 _tbv.getDelegate().setLastRow(7);
424             }
425         });
426         final Button lastrow0 = new Button(addPanel, SWT.PUSH);
427         lastrow0.setText("last 0");
428         lastrow0.addSelectionListener(new SelectionAdapter() {
429             @Override
430             public void widgetSelected(SelectionEvent e) {
431                 _tbv.getDelegate().setLastRow(0);
432             }
433         });
434         final Button lastrow20 = new Button(addPanel, SWT.PUSH);
435         lastrow20.setText("last 20");
436         lastrow20.addSelectionListener(new SelectionAdapter() {
437             @Override
438             public void widgetSelected(SelectionEvent e) {
439                 _tbv.getDelegate().setLastRow(19);
440             }
441         });
442 
443         
444         
445         
446         return _tbv;
447     }
448 
449     private void registerChangeListener(TimeBarViewer tbv) {
450         tbv.addTimeBarChangeListener(new ITimeBarChangeListener() {
451 
452             public void intervalChangeCancelled(TimeBarRow row, Interval interval) {
453                 System.out.println("CHANGE CANCELLED " + row + " " + interval);
454             }
455 
456             public void intervalChangeStarted(TimeBarRow row, Interval interval) {
457                 System.out.println("CHANGE STARTED " + row + " " + interval);
458             }
459 
460             public void intervalChanged(TimeBarRow row, Interval interval, JaretDate oldBegin, JaretDate oldEnd) {
461                 System.out.println("CHANGE DONE " + row + " " + interval);
462             }
463 
464             public void intervalIntermediateChange(TimeBarRow row, Interval interval, JaretDate oldBegin,
465                     JaretDate oldEnd) {
466                 System.out.println("CHANGE INTERMEDIATE " + row + " " + interval);
467             }
468 
469             public void markerDragStarted(TimeBarMarker marker) {
470                 System.out.println("Marker drag started "+marker);
471             }
472 
473             public void markerDragStopped(TimeBarMarker marker) {
474                 System.out.println("Marker drag stopped "+marker);
475             }
476 
477         });
478 
479     }
480 
481     // information about ongoing drag operation
482     private Point _dragStart;
483     private int _startOffsetX;
484     private int _startOffsetY;
485     private JaretDate _dragStartDate;
486     private List<Interval> _draggedIntervals;
487     private List<Integer> _yOffsets;
488     private List<Interval> _origIntervals;
489     private TimeBarRow _draggedRow;
490     private boolean _isRowDrag;
491 
492     /***
493      * Init the drag source and the drop target for the timebar viewer. Information will be provided as text for
494      * dropping outside the table viewer. Drag&Drop inside the viewer uses instance data to transport the information
495      * about the dragged interval. This example supports dragging all selected intervals and dragging a single row
496      * determined when the drag starts.
497      * 
498      * @param tbv timebarviewer
499      * @param parent parent of the timebarviewer (for disposal listening)
500      */
501     private void initDND(final TimeBarViewer tbv, Composite parent) {
502         // support move and copy
503         int operations = DND.DROP_COPY | DND.DROP_MOVE;
504         final DragSource source = new DragSource(tbv, operations);
505 
506         // Provide data in Text format
507         Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
508         source.setTransfer(types);
509 
510         source.addDragListener(new DragSourceListener() {
511             public void dragStart(DragSourceEvent event) {
512 
513                 // check whether drag occured on header or hierarchy area
514                 if (_tbv.isInRowAxis(event.x, event.y)) {
515                     // possible row drag
516                     TimeBarRow row = _tbv.getRowForXY(event.x, event.y);
517                     if (row != null && !_tbv.rowLineHit(event.x, event.y)) {
518                         // row hit, start row drag
519                         _isRowDrag = true;
520                         _dragStart = new Point(event.x, event.y);
521 
522                         // capture the data for internal use
523                         // row drag: use row at starting position
524                         _draggedRow = row;
525 
526                     } else {
527                         event.doit = false;
528                     }
529 
530                 } else {
531                     // Only start the drag if there is an interval at hand
532                     // and the point of the beginning drag is not near the border of the interval, allowing the
533                     // timebarviewers implementation of dragging for moving inside a row and for resizing to be usable
534                     // even with an installed drag source (The internal drag feature can be disabled when using
535                     // the systems drag and drop support)
536                     List<Interval> l = tbv.getIntervalsAt(event.x, event.y);
537                     if (l.size() > 0) {
538                         Interval interval = l.get(0);
539                         JaretDate date = tbv.dateForXY(event.x, event.y);
540                         //int secondsForMargin = (int)(50.0/_tbv.getPixelPerSecond()); // 50 pixel margin
541                         int secondsForMargin = 1000; // 1000 second margin
542                         if (Math.abs(date.diffSeconds(interval.getBegin())) < secondsForMargin
543                                 || Math.abs(date.diffSeconds(interval.getEnd())) < secondsForMargin) {
544                             System.out.println("Near the border of an interval -> no drag");
545                             event.doit = false;
546                         } else {
547                             // remember the point the drag started
548                             _dragStart = new Point(event.x, event.y);
549                             _dragStartDate = _tbv.dateForXY(event.x, event.y);
550                             _isRowDrag = false;
551                             // make sure the interval is selected
552                             _tbv.getSelectionModel().addSelectedInterval(interval);
553 
554                             // capture the data of the dragged intervals
555                             // drag all selected intervals
556                             // to allow changing the time of the intervals and drawing ghost intervals a copy of the
557                             // intervals will be made
558                             _draggedIntervals = new ArrayList<Interval>();
559                             _origIntervals = new ArrayList<Interval>();
560                             _yOffsets = new ArrayList<Integer>();
561 
562                             for (Interval i : _tbv.getSelectionModel().getSelectedIntervals()) {
563                                 _draggedIntervals.add(new IntervalImpl(i.getBegin().copy(), i.getEnd().copy()));
564                                 _origIntervals.add(i);
565                                 TimeBarRow row = _tbv.getModel().getRowForInterval(i);
566                                 int yOffset;
567                                 if (_tbv.getTBOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
568                                     yOffset = _tbv.getYForRow(row) - _dragStart.y;
569                                 } else {
570                                     yOffset = _tbv.getYForRow(row) - _dragStart.x;
571                                 }
572                                 _yOffsets.add(yOffset);
573                             }
574 
575                             TimeBarRow row = _tbv.getRowForXY(event.x, event.y);
576                             // store both x and y offsets since the orientation may be vertical or horizontal
577                             _startOffsetY = event.y - tbv.getYForRow(row);
578                             _startOffsetX = event.x - tbv.getYForRow(row);
579                         }
580                     } else {
581                         event.doit = false;
582                     }
583                 }
584             }
585 
586             public void dragSetData(DragSourceEvent event) {
587                 // Provide the data of the requested type.
588                 if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
589                     // unfortenatly the coordinates in the event are always 0,0 -> use the coordinates transferred in
590                     // startDrag to get the interval (a possibly is to use the selection, especially for dragging
591                     // multiple intervals)
592                     if (_isRowDrag) {
593                         event.data = "row: " + _draggedRow.getRowHeader().getLabel();
594                     } else {
595                         StringBuffer buf = new StringBuffer();
596                         buf.append("intervals:");
597                         for (Interval interval : _origIntervals) {
598                             buf.append(interval);
599                             buf.append(";");
600                         }
601                         event.data = buf.toString();
602                     }
603                 }
604             }
605 
606             public void dragFinished(DragSourceEvent event) {
607                 // drag finished -> if it is a move operation remove the original intervals
608                 // from their rows
609                 if (event.detail == DND.DROP_MOVE) {
610                     System.out.println("Perform move");
611                     if (_isRowDrag) {
612                         DefaultTimeBarModel model = (DefaultTimeBarModel) _tbv.getModel();
613                         model.remRow(_draggedRow);
614                     } else {
615                         for (Interval interval : _origIntervals) {
616                             DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) _tbv.getModel().getRowForInterval(
617                                     interval);
618                             row.remInterval(interval);
619                         }
620                     }
621                 }
622                 // clear the dragged data
623                 // and the ghosts in the viewer
624                 _tbv.deHighlightRow();
625                 _tbv.setGhostIntervals(null, null);
626                 _tbv.setGhostRows(null, null);
627                 _draggedIntervals = null;
628                 _draggedRow = null;
629                 _isRowDrag = false;
630                 _origIntervals = null;
631             }
632         });
633 
634         // ////////////////////
635         // Drop target
636 
637         // Allow data to be copied or moved to the drop target
638         operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
639         final DropTarget target = new DropTarget(tbv, operations);
640 
641         // Receive data in Text
642         final TextTransfer textTransfer = TextTransfer.getInstance();
643         types = new Transfer[] {textTransfer};
644         target.setTransfer(types);
645 
646         target.addDropListener(new DropTargetListener() {
647         	final static int DELAY = 5; 
648         	int _verticalAutoscrollDelay = DELAY;
649         	
650             public void dragEnter(DropTargetEvent event) {
651             }
652 
653             public void dragOver(DropTargetEvent event) {
654                 // event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
655 
656                 if (_isRowDrag && _draggedRow != null) {
657                     // row drag
658                     int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
659                     int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
660                     tbv.setGhostRow(_draggedRow, 0);
661                     tbv.setGhostOrigin(destX, destY);
662                     TimeBarRow overRow = tbv.getRowForXY(destX, destY);
663                     if (overRow != null) {
664                         tbv.highlightRow(overRow);
665                     } else {
666                         tbv.deHighlightRow();
667                     }
668 
669                 } else if (!_isRowDrag && _draggedIntervals != null && _draggedIntervals.size() > 0) {
670                     // map the coordinates from the event to the timebar viewer widget
671                     int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
672                     int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
673 
674                     JaretDate curDate = _tbv.dateForXY(destX, destY);
675                     long diffSeconds = _dragStartDate.diffSeconds(curDate);
676                     // correct the dragged interval bounds
677                     for (int i = 0; i < _draggedIntervals.size(); i++) {
678                         Interval orig = _origIntervals.get(i);
679                         Interval interval = _draggedIntervals.get(i);
680                         interval.setBegin(orig.getBegin().copy().advanceSeconds(-diffSeconds));
681                         interval.setEnd(orig.getEnd().copy().advanceSeconds(-diffSeconds));
682                     }
683 
684                     // check if we are over a row and highlight it
685                     // this will only highlight the row for the mouse pointer
686                     // if multiple intervals are dragged their respective rows will not be highlighted
687                     TimeBarRow overRow = tbv.getRowForXY(destX, destY);
688                     if (overRow != null) {
689                         tbv.highlightRow(overRow);
690                     } else {
691                         System.out.println("Row is null x,y:" + event.x + "," + event.y);
692                         tbv.deHighlightRow();
693                     }
694                     // tell the timebar viewer
695                     tbv.setGhostIntervals(_draggedIntervals, _yOffsets);
696                     tbv.setGhostOrigin(destX, destY);
697                     
698                     // vertical autoscroll
699                     // the delay is initialized at the top of the drop target!
700                     int range = 15;
701                     Rectangle diagramRect = _tbv.getDelegate().getDiagramRect();
702                     if (destY<=diagramRect.y) {
703                         _verticalAutoscrollDelay--;
704                         if (_verticalAutoscrollDelay < 0) {
705 	                    	_verticalAutoscrollDelay = DELAY;
706 	                        int ridx = _tbv.getFirstRowDisplayed();
707 	                        if (ridx>0) {
708 	                            _tbv.setFirstRowDisplayed(ridx-1);
709 	                            _tbv.setFirstRowOffset(0);
710 	                        } else {
711 	                            _tbv.setFirstRowOffset(0);
712 	                        }
713                         }
714                     } else 
715                     if (destY>=diagramRect.y+diagramRect.height-range) {
716                         _verticalAutoscrollDelay--;
717                         if (_verticalAutoscrollDelay < 0) {
718 	                    	_verticalAutoscrollDelay = DELAY;
719                         	
720                         	TimeBarRow row = _tbv.rowForY(destY);
721 	                        int ridx = _tbv.getDelegate().getRowIndex(row);
722 	                        System.out.println("ridx "+ridx);
723 	                        if (ridx+1<_tbv.getDelegate().getRowCount()) {
724 	                            System.out.println("try ridx "+(ridx+1));
725 	                            TimeBarRow nextRow = _tbv.getDelegate().getRow(ridx+1);
726 	                            _tbv.setLastRow(nextRow);
727 //	                            _tbv.scrollRowToVisible(nextRow);
728 	                        }
729                         }
730                     } else {
731                     	_verticalAutoscrollDelay = DELAY;
732                     }
733                     
734                     
735                 }
736             }
737 
738             public void dragOperationChanged(DropTargetEvent event) {
739             }
740 
741             public void dragLeave(DropTargetEvent event) {
742                 // leaving: do not leave a row highlighted
743                 tbv.deHighlightRow();
744                 tbv.setGhostIntervals(null, null);
745             }
746 
747             public void dropAccept(DropTargetEvent event) {
748             }
749 
750             public void drop(DropTargetEvent event) {
751                 // drop operation. the drop operation places the dragged intervals in the destination rows.
752                 // if a row can not be determined (no row under the dragged interval) the interval will be discarded
753                 // The dragged intervals are copies of the original intervals, in a real application it might be better
754                 // to move the originals in case of a move operation.
755                 // The dragged row is not modified by the drag operation. So in case of a drop it is copied and
756                 // inserted.
757                 // ATTENTION: the implementation herein does only work properly if no row sorters or row filters are
758                 // applied.
759                 // If that is the case it is up to the implementation to take of that.
760                 if (textTransfer.isSupportedType(event.currentDataType)) {
761                     String text = (String) event.data;
762                     System.out.println("DROP: " + text);
763 
764                     if (_isRowDrag && _draggedRow != null) {
765                         int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
766                         int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
767 
768                         TimeBarRow overRow = tbv.getRowForXY(destX, destY);
769                         System.out.println("over row " + overRow.getRowHeader());
770                         if (overRow != null) {
771                             DefaultTimeBarRowModel row = copyDraggedRow();
772                             DefaultTimeBarModel model = (DefaultTimeBarModel) _tbv.getModel();
773 
774                             int index = model.getIndexForRow(overRow);
775                             System.out.println("index " + index);
776                             model.addRow(index, row);
777                         }
778                     } else if (_draggedIntervals != null) {
779 
780                         for (int i = 0; i < _draggedIntervals.size(); i++) {
781                             int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
782                             int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
783                             int offY = _yOffsets.get(i);
784                             TimeBarRow overRow = null;
785                             if (_tbv.getTBOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
786                                 overRow = tbv.rowForY(destY + offY + _startOffsetY);
787                             } else {
788                                 overRow = tbv.rowForY(destX + offY + _startOffsetX);
789                             }
790                             DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) overRow;
791                             if (overRow != null) {
792                                 row.addInterval(_draggedIntervals.get(i));
793                             }
794                         }
795                     }
796                 }
797             }
798 
799             private DefaultTimeBarRowModel copyDraggedRow() {
800                 DefaultRowHeader header = (DefaultRowHeader) _draggedRow.getRowHeader();
801                 DefaultTimeBarRowModel row = new DefaultTimeBarRowModel(new DefaultRowHeader(header.getLabel()
802                         + "(copy)"));
803                 for (Interval interval : _draggedRow.getIntervals()) {
804                     row.addInterval(new IntervalImpl(interval.getBegin().copy(), interval.getEnd().copy()));
805                 }
806                 return row;
807             }
808         });
809 
810         // Dispose listener on parent of timebar viewer to dispose the dragsource and dragtarget BEFORE the timebar
811         // viewer
812         // this prevents an exception beeing thrown by SWT
813         parent.addDisposeListener(new DisposeListener() {
814             public void widgetDisposed(DisposeEvent e) {
815                 source.dispose();
816                 target.dispose();
817             }
818         });
819 
820     }
821 
822     protected void configureShell(Shell shell) {
823         super.configureShell(shell);
824         shell.setText(getClass().getName());
825     }
826 
827     public static void main(String[] args) {
828         SwtOverlapExample test = new SwtOverlapExample();
829         test.setBlockOnOpen(true);
830         test.open();
831     }
832 
833     
834 
835 
836 
837 }