View Javadoc

1   /*
2    *  File: SwtCalendarExample.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.calendar.swt;
21  
22  import java.beans.PropertyChangeListener;
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.graphics.Point;
42  import org.eclipse.swt.graphics.Rectangle;
43  import org.eclipse.swt.layout.GridData;
44  import org.eclipse.swt.layout.GridLayout;
45  import org.eclipse.swt.widgets.Composite;
46  import org.eclipse.swt.widgets.Control;
47  import org.eclipse.swt.widgets.Display;
48  import org.eclipse.swt.widgets.Shell;
49  
50  import de.jaret.examples.timebars.calendar.model.Appointment;
51  import de.jaret.examples.timebars.calendar.model.AppointmentModificator;
52  import de.jaret.examples.timebars.calendar.model.CalendarIntervalFilter;
53  import de.jaret.examples.timebars.calendar.model.CalendarModel;
54  import de.jaret.examples.timebars.calendar.model.Day;
55  import de.jaret.examples.timebars.calendar.model.ModelCreator;
56  import de.jaret.examples.timebars.calendar.swt.renderer.AppointmentRenderer;
57  import de.jaret.examples.timebars.calendar.swt.renderer.CalendarGridRenderer;
58  import de.jaret.examples.timebars.calendar.swt.renderer.CalendarTimeScaleRenderer;
59  import de.jaret.examples.timebars.calendar.swt.renderer.DayHeaderRenderer;
60  import de.jaret.util.date.Interval;
61  import de.jaret.util.date.JaretDate;
62  import de.jaret.util.ui.timebars.TimeBarRowFilter;
63  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
64  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
65  import de.jaret.util.ui.timebars.model.DefaultTimeBarRowModel;
66  import de.jaret.util.ui.timebars.model.TimeBarModel;
67  import de.jaret.util.ui.timebars.model.TimeBarRow;
68  import de.jaret.util.ui.timebars.strategy.OverlapInfo;
69  import de.jaret.util.ui.timebars.swt.TimeBarViewer;
70  
71  /***
72   * SWT: example demonstrating the vertical orientation showing a calendar view.
73   * 
74   * @author Peter Kliem
75   * @version $Id: SwtCalendarExample.java 1104 2012-02-07 22:03:30Z kliem $
76   */
77  public class SwtCalendarExample extends ApplicationWindow {
78      private static TimeBarViewer _tbv;
79  
80      public SwtCalendarExample() {
81          super(null);
82      }
83  
84      protected Control createContents(Composite parent) {
85          GridLayout gridLayout = new GridLayout();
86          gridLayout.numColumns = 1;
87          parent.setLayout(gridLayout);
88  
89          TimeBarModel model = ModelCreator.createCalendarModel();
90  
91          GridData gd = new GridData(GridData.FILL_BOTH);
92  
93          _tbv = new TimeBarViewer(parent, SWT.H_SCROLL | SWT.V_SCROLL);
94          _tbv.setLayoutData(gd);
95  
96          _tbv.setTimeScalePosition(TimeBarViewer.TIMESCALE_POSITION_TOP);
97          _tbv.setModel(model);
98  
99  //        _tbv.setRowFilter(new TimeBarRowFilter() {
100 //            
101 //            @Override
102 //            public void removePropertyChangeListener(PropertyChangeListener listener) {
103 //            }
104 //            
105 //            @Override
106 //            public void addPropertyChangeListener(PropertyChangeListener listener) {
107 //            }
108 //            
109 //            @Override
110 //            public boolean isInResult(TimeBarRow row) {
111 //                Day d = (Day)row;
112 //                JaretDate date = new JaretDate(1,4,2007,0,0,0);
113 //                return d.getDayDate().compareDateTo(date)>=0;
114 //            }
115 //        });
116 
117         
118         
119         
120         _tbv.setPixelPerSecond(0.018);
121         _tbv.setDrawRowGrid(true);
122 
123         _tbv.setSelectionDelta(6);
124         // this is the col width!
125         _tbv.setRowHeight(150);
126 
127         _tbv.setTBOrientation(TimeBarViewerInterface.Orientation.VERTICAL);
128         // vertical: the y axiswidth is the height of the row headers!
129         _tbv.setYAxisWidth(20);
130 
131         // do not adjust the displaed time according to the model
132         // use the basedate day!
133         _tbv.setAdjustMinMaxDatesByModel(false);
134         _tbv.setMinDate(CalendarModel.BASEDATE.copy());
135         _tbv.setMaxDate(CalendarModel.BASEDATE.copy().advanceDays(1));
136 
137         // set the header renderer
138         _tbv.setHeaderRenderer(new DayHeaderRenderer());
139 
140         // timescale is default + special tooltip
141         _tbv.setTimeScaleRenderer(new CalendarTimeScaleRenderer());
142 
143         // register the AppointmentRenderer for rendering appointments
144         AppointmentRenderer ar = new AppointmentRenderer();
145         _tbv.registerTimeBarRenderer(Appointment.class, ar);
146 
147         // modifications are restricted
148         _tbv.addIntervalModificator(new AppointmentModificator());
149 
150         _tbv.setGridRenderer(new CalendarGridRenderer());
151 
152         CalendarControlPanel ctrl = new CalendarControlPanel(parent, SWT.NULL, _tbv, null);
153         gd = new GridData(GridData.FILL_HORIZONTAL);
154         ctrl.setLayoutData(gd);
155 
156         // add a listener for doubleclicks
157         // shows how to get intervals from a point
158         _tbv.addMouseListener(new MouseAdapter() {
159             public void mouseDoubleClick(MouseEvent e) {
160                 List<Interval> intervals = _tbv.getIntervalsAt(e.x, e.y);
161                 if (intervals != null && intervals.size() > 0) {
162                     for (Interval interval : intervals) {
163                         System.out.println("Doubleclicked: " + interval.toString());
164                     }
165                 }
166             }
167         });
168 
169         initDND(_tbv, parent);
170 
171         _tbv.setIntervalFilter(new CalendarIntervalFilter());
172         _tbv.setYAxisWidth(50);
173 
174         return _tbv;
175     }
176 
177     // information about ongoing drag operation
178     private Point _dragStart;
179     private int _startOffsetX;
180     private int _startOffsetY;
181     private JaretDate _dragStartDate;
182     private List<Interval> _draggedIntervals;
183     private List<Integer> _yOffsets;
184     private List<Interval> _origIntervals;
185     private List<TimeBarRow> _origRows;
186     private TimeBarRow _draggedRow;
187     private boolean _isRowDrag;
188     int _lastDragOverX;
189     int _lastDragOverY;
190 
191     /***
192      * Init the drag source and the drop target for the timebar viewer. Information will be provided as text for
193      * dropping outside the table viewer. Drag&Drop inside the viewer uses instance data to transport the information
194      * about the dragged intervals.
195      * 
196      * @param tbv timebarviewer
197      * @param parent parent of the timebarviewer (for disposal listening)
198      */
199     private void initDND(final TimeBarViewer tbv, Composite parent) {
200         // support move and copy
201         int operations = DND.DROP_COPY | DND.DROP_MOVE;
202         final DragSource source = new DragSource(tbv, operations);
203 
204         // Provide data in Text format
205         Transfer[] types = new Transfer[] {TextTransfer.getInstance()};
206         source.setTransfer(types);
207 
208         source.addDragListener(new DragSourceListener() {
209             public void dragStart(DragSourceEvent event) {
210                 boolean horizontal = _tbv.getTBOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL);
211                 TimeBarViewerDelegate delegate = (TimeBarViewerDelegate) _tbv.getData("delegate");
212 
213                 // check whether drag occured on the drag marker in the interval
214                 if (_tbv.isInDiagram(event.x, event.y)) {
215                     List<Interval> l = tbv.getIntervalsAt(event.x, event.y);
216                     if (l.size() > 0) {
217                         Interval interval = l.get(0);
218 
219                         TimeBarRow clickedRow = _tbv.getModel().getRowForInterval(interval);
220                         OverlapInfo oi = delegate.getOverlapStrategy().getOverlapInfo(clickedRow, interval);
221                         boolean overlap = oi != null ? oi.overlappingCount > 0 : false;
222 
223                         Rectangle bounds = _tbv.getIntervalBounds(interval);
224                         if (AppointmentRenderer.isInDragMark(bounds, event.x, event.y, horizontal, overlap)) {
225                             // remember the point the drag started
226                             _dragStart = new Point(event.x, event.y);
227                             _dragStartDate = _tbv.dateForXY(event.x, event.y);
228                             _isRowDrag = false;
229                             // make sure the interval is selected
230                             _tbv.getSelectionModel().addSelectedInterval(interval);
231 
232                             // capture the data of the dragged intervals
233                             // drag all selected intervals
234                             // to allow changing the time of the intervals and drawing ghost intervals a copy of the
235                             // intervals will be made
236                             _draggedIntervals = new ArrayList<Interval>();
237                             _origIntervals = new ArrayList<Interval>();
238                             _origRows = new ArrayList<TimeBarRow>();
239                             _yOffsets = new ArrayList<Integer>();
240 
241                             for (Interval i : _tbv.getSelectionModel().getSelectedIntervals()) {
242                                 _draggedIntervals.add(((Appointment) i).copy());
243                                 TimeBarRow row = _tbv.getModel().getRowForInterval(i);
244                                 _origIntervals.add(i);
245                                 _origRows.add(row);
246 
247                                 int yOffset;
248                                 if (_tbv.getTBOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
249                                     yOffset = _tbv.getYForRow(row) - _dragStart.y;
250                                 } else {
251                                     yOffset = _tbv.getYForRow(row) - _dragStart.x;
252                                 }
253                                 _yOffsets.add(yOffset);
254                             }
255 
256                             TimeBarRow row = _tbv.getRowForXY(event.x, event.y);
257                             // store both x and y offsets since the orientation may be vertical or horizontal
258                             _startOffsetY = event.y - tbv.getYForRow(row);
259                             _startOffsetX = event.x - tbv.getYForRow(row);
260                             return;
261                         }
262                     }
263                 }
264                 event.doit = false;
265             }
266 
267             public void dragSetData(DragSourceEvent event) {
268                 // Provide the data of the requested type.
269                 if (TextTransfer.getInstance().isSupportedType(event.dataType)) {
270                     // unfortenatly the coordinates in the event are always 0,0 -> use the coordinates transferred in
271                     // startDrag to get the interval (a possibly is to use the selection, especially for dragging
272                     // multiple intervals)
273                     if (_isRowDrag) {
274                         event.data = "row: " + _draggedRow.getRowHeader().getLabel();
275                     } else {
276                         StringBuffer buf = new StringBuffer();
277                         buf.append("intervals:");
278                         for (Interval interval : _origIntervals) {
279                             buf.append(interval);
280                             buf.append(";");
281                         }
282                         event.data = buf.toString();
283                     }
284                 }
285             }
286 
287             public void dragFinished(DragSourceEvent event) {
288                 // drag finished -> if it is a move operation remove the original intervals
289                 // from their rows
290                 if (event.detail == DND.DROP_MOVE) {
291                     System.out.println("Perform move");
292                     for (Interval interval : _origIntervals) {
293                         DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) _tbv.getModel().getRowForInterval(
294                                 interval);
295                         row.remInterval(interval);
296                     }
297                 }
298                 // clear the dragged data
299                 // and the ghosts in the viewer
300                 _tbv.deHighlightRow();
301                 _tbv.setGhostIntervals(null, null);
302                 _tbv.setGhostRows(null, null);
303                 _draggedIntervals = null;
304                 _draggedRow = null;
305                 _isRowDrag = false;
306                 _origIntervals = null;
307                 _origRows = null;
308             }
309         });
310 
311         // ////////////////////
312         // Drop target
313 
314         // Allow data to be copied or moved to the drop target
315         operations = DND.DROP_MOVE | DND.DROP_COPY | DND.DROP_DEFAULT;
316         final DropTarget target = new DropTarget(tbv, operations);
317 
318         // Receive data in Text
319         final TextTransfer textTransfer = TextTransfer.getInstance();
320         types = new Transfer[] {textTransfer};
321         target.setTransfer(types);
322 
323         target.addDropListener(new DropTargetListener() {
324             public void dragEnter(DropTargetEvent event) {
325             }
326 
327             public void dragOver(DropTargetEvent event) {
328                 // event.feedback = DND.FEEDBACK_SELECT | DND.FEEDBACK_SCROLL;
329 
330                 // prevent multiple dragOvers without move
331                 if (event.x != _lastDragOverX || event.y != _lastDragOverY) {
332                     _lastDragOverX = event.x;
333                     _lastDragOverY = event.y;
334 
335                     // System.out.println("dragover " + event);
336 
337                     if (!_isRowDrag && _draggedIntervals != null && _draggedIntervals.size() > 0) {
338                         // map the coordinates from the event to the timebar viewer widget
339                         int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
340                         int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
341 
342                         JaretDate curDate = _tbv.dateForXY(destX, destY);
343                         long diffSeconds = _dragStartDate.diffSeconds(curDate);
344                         // correct the dragged interval bounds
345                         for (int i = 0; i < _draggedIntervals.size(); i++) {
346                             Interval orig = _origIntervals.get(i);
347                             Interval interval = _draggedIntervals.get(i);
348                             interval.setBegin(orig.getBegin().copy().advanceSeconds(-diffSeconds));
349                             interval.setEnd(orig.getEnd().copy().advanceSeconds(-diffSeconds));
350                         }
351 
352                         // check if we are over a row and highlight it
353                         // this will only highlight the row for the mouse pointer
354                         // if multiple intervals are dragged their respective rows will not be highlighted
355                         TimeBarRow overRow = tbv.getRowForXY(destX, destY);
356                         if (overRow != null) {
357                             tbv.highlightRow(overRow);
358                         } else {
359                             System.out.println("Row is null x,y:" + event.x + "," + event.y);
360                             tbv.deHighlightRow();
361                         }
362                         // tell the timebar viewer
363                         tbv.setGhostIntervals(_draggedIntervals, _yOffsets);
364                         tbv.setGhostOrigin(destX, destY);
365                     }
366                 }
367             }
368 
369             public void dragOperationChanged(DropTargetEvent event) {
370             }
371 
372             public void dragLeave(DropTargetEvent event) {
373                 // leaving: do not leave a row highlighted
374                 tbv.deHighlightRow();
375                 tbv.setGhostIntervals(null, null);
376             }
377 
378             public void dropAccept(DropTargetEvent event) {
379             }
380 
381             public void drop(DropTargetEvent event) {
382                 // drop operation. the drop operation places the dragged intervals in the destination rows.
383                 // if a row can not be determined (no row under the dragged interval) the interval will be discarded
384                 if (textTransfer.isSupportedType(event.currentDataType)) {
385                     String text = (String) event.data;
386                     System.out.println("DROP: " + text);
387 
388                     if (_draggedIntervals != null) {
389                         for (int i = 0; i < _draggedIntervals.size(); i++) {
390                             int destY = Display.getCurrent().map(null, tbv, event.x, event.y).y;
391                             int destX = Display.getCurrent().map(null, tbv, event.x, event.y).x;
392                             int offY = _yOffsets.get(i);
393                             TimeBarRow overRow = null;
394                             if (_tbv.getTBOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL)) {
395                                 overRow = tbv.rowForY(destY + offY + _startOffsetY);
396                             } else {
397                                 overRow = tbv.rowForY(destX + offY + _startOffsetX);
398                             }
399                             DefaultTimeBarRowModel row = (DefaultTimeBarRowModel) overRow;
400 
401                             // NOTE: copies are placed in the dest rows
402                             if (overRow != null) {
403                                 row.addInterval(_draggedIntervals.get(i));
404                                 Appointment app = (Appointment) _draggedIntervals.get(i);
405                                 Day destDay = (Day) overRow;
406                                 JaretDate realBegin = app.getRealBegin().copy();
407                                 realBegin.setDate(destDay.getDayDate().getDay(), destDay.getDayDate().getMonth(),
408                                         destDay.getDayDate().getYear());
409                                 app.setRealBegin(realBegin);
410                                 JaretDate realEnd = app.getRealEnd().copy();
411                                 realEnd.setDate(destDay.getDayDate().getDay(), destDay.getDayDate().getMonth(), destDay
412                                         .getDayDate().getYear());
413                                 app.setRealEnd(realEnd);
414 
415                             }
416                         }
417                     }
418                 }
419             }
420 
421         });
422 
423         // Dispose listener on parent of timebar viewer to dispose the dragsource and dragtarget BEFORE the timebar
424         // viewer
425         // this prevents an exception beeing thrown by SWT
426         parent.addDisposeListener(new DisposeListener() {
427             public void widgetDisposed(DisposeEvent e) {
428                 source.dispose();
429                 target.dispose();
430             }
431         });
432 
433     }
434 
435     protected void configureShell(Shell shell) {
436         super.configureShell(shell);
437         shell.setText(getClass().getName());
438     }
439 
440     public static void main(String[] args) {
441         SwtCalendarExample test = new SwtCalendarExample();
442         test.setBlockOnOpen(true);
443         test.open();
444     }
445 
446 }