View Javadoc

1   /*
2    *  File: TimelineExample.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.timeline.swt;
21  
22  import java.beans.PropertyChangeEvent;
23  import java.beans.PropertyChangeListener;
24  
25  import org.eclipse.jface.window.ApplicationWindow;
26  import org.eclipse.swt.SWT;
27  import org.eclipse.swt.graphics.Point;
28  import org.eclipse.swt.graphics.Rectangle;
29  import org.eclipse.swt.layout.GridData;
30  import org.eclipse.swt.layout.GridLayout;
31  import org.eclipse.swt.widgets.Composite;
32  import org.eclipse.swt.widgets.Control;
33  import org.eclipse.swt.widgets.Shell;
34  
35  import de.jaret.examples.timebars.timeline.DistributeOverlapStrategy;
36  import de.jaret.examples.timebars.timeline.model.ModelCreator;
37  import de.jaret.examples.timebars.timeline.model.TimelineEvent;
38  import de.jaret.examples.timebars.timeline.swt.renderer.DetailEventRenderer;
39  import de.jaret.examples.timebars.timeline.swt.renderer.LowerGridRenderer;
40  import de.jaret.examples.timebars.timeline.swt.renderer.OverviewEventRenderer;
41  import de.jaret.util.date.Interval;
42  import de.jaret.util.date.JaretDate;
43  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
44  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
45  import de.jaret.util.ui.timebars.model.DefaultTimeBarNode;
46  import de.jaret.util.ui.timebars.model.IRowHeightStrategy;
47  import de.jaret.util.ui.timebars.model.ITimeBarViewState;
48  import de.jaret.util.ui.timebars.model.PPSInterval;
49  import de.jaret.util.ui.timebars.model.TimeBarModel;
50  import de.jaret.util.ui.timebars.model.TimeBarRow;
51  import de.jaret.util.ui.timebars.swt.TimeBarViewer;
52  import de.jaret.util.ui.timebars.swt.renderer.BoxTimeScaleRenderer;
53  import de.jaret.util.ui.timebars.swt.renderer.DefaultGridRenderer;
54  import de.jaret.util.ui.timebars.swt.util.TimeScaleDragSupport;
55  
56  /***
57   * Example showing a combination of two timebarviewers on a single model in a timeline fashion. The idea has been taken
58   * from http://simile.mit.edu/timeline/.
59   * 
60   * @author Peter Kliem
61   * @version $Id: SwtOverlapExample.java 559 2007-09-10 23:13:47Z olk $
62   */
63  public class TimelineExample extends ApplicationWindow {
64      /*** upper timebar viewer. */
65      private static TimeBarViewer _tbv;
66      /*** lower timebar viewer. */
67      private static TimeBarViewer _tbv2;
68  
69      /*** factor the lower viewer is zoomed out. */
70      private double LOWERFACTOR = 12.0;
71      /*** factor the hotspots are zoomed in. */
72      private double ZOOMFACTOR = 20.0;
73  
74      /*** number of rows to display. */
75      private int NUMROWS = 20;
76  
77      /*** helper to prevent endless loop in prop changes between the viewers. */
78      boolean ignorePropChangeFlag = false;
79  
80      public TimelineExample() {
81          super(null);
82      }
83  
84      protected Control createContents(Composite parent) {
85          GridLayout gridLayout = new GridLayout();
86          gridLayout.numColumns = 1;
87          gridLayout.verticalSpacing = 0;
88          parent.setLayout(gridLayout);
89  
90          // create (load) the model
91          ModelCreator creator = new ModelCreator("/de/jaret/examples/timebars/timeline/model/jfk.xml");
92          TimeBarModel model = creator.getModel();
93  
94          GridData gd = new GridData(GridData.FILL_BOTH);
95  
96          // create the upper time bar viwer
97          // no scroll bars for this one
98          _tbv = new TimeBarViewer(parent, SWT.NULL);
99          _tbv.setLayoutData(gd);
100         _tbv.setName("upper");
101 
102         _tbv.setTimeScalePosition(TimeBarViewer.TIMESCALE_POSITION_TOP);
103         _tbv.setModel(model);
104 
105         _tbv.setPixelPerSecond(0.0014351851851851852);
106 
107         configureTBV(_tbv);
108         // do not mark weekends
109         ((DefaultGridRenderer) _tbv.getGridRenderer()).setMarkWeekends(false);
110 
111         // register the renderer for the event detail
112         _tbv.registerTimeBarRenderer(TimelineEvent.class, new DetailEventRenderer());
113 
114         // set the look forward/back properties to include a wider range
115         _tbv.setScrollLookBackMinutes(24 * 60 * 3);
116         _tbv.setScrollLookForwardMinutes(24 * 60 * 3);
117 
118         // the second (lower) viewer
119         // only horizontal scroll
120         _tbv2 = new TimeBarViewer(parent, SWT.H_SCROLL);
121         gd = new GridData(GridData.FILL_HORIZONTAL);
122         gd.heightHint = 200;
123         _tbv2.setLayoutData(gd);
124         _tbv2.setName("lower");
125 
126         _tbv2.setTimeScalePosition(TimeBarViewer.TIMESCALE_POSITION_BOTTOM);
127         _tbv2.setModel(model);
128 
129         _tbv2.setPixelPerSecond(_tbv.getPixelPerSecond() / LOWERFACTOR);
130 
131         final LowerGridRenderer gridRenderer = new LowerGridRenderer();
132         _tbv2.setGridRenderer(gridRenderer);
133 
134         // register the renderer for the event detail
135         _tbv2.registerTimeBarRenderer(TimelineEvent.class, new OverviewEventRenderer());
136 
137         configureTBV(_tbv2);
138 
139         // create pps intervals for dynamic scaling
140         // this defines the "hot spots"
141         _tbv.setVariableXScale(true);
142         DefaultTimeBarNode scaleRow = (DefaultTimeBarNode) _tbv.getPpsRow();
143         PPSInterval i = new PPSInterval(_tbv.getPixelPerSecond() * ZOOMFACTOR);
144         i.setBegin(new JaretDate(22, 11, 1963, 0, 0, 0));
145         i.setEnd(new JaretDate(22, 11, 1963, 18, 0, 0));
146         scaleRow.addInterval(i);
147         i = new PPSInterval(_tbv.getPixelPerSecond() * ZOOMFACTOR * 3);
148         i.setBegin(new JaretDate(22, 11, 1963, 18, 0, 0));
149         i.setEnd(new JaretDate(23, 11, 1963, 0, 0, 0));
150         scaleRow.addInterval(i);
151         i = new PPSInterval(_tbv.getPixelPerSecond() * ZOOMFACTOR);
152         i.setBegin(new JaretDate(23, 11, 1963, 0, 0, 0));
153         i.setEnd(new JaretDate(24, 11, 1963, 0, 0, 0));
154         scaleRow.addInterval(i);
155 
156         // create pps intervals lower
157         _tbv2.setVariableXScale(true);
158         DefaultTimeBarNode scaleRowLower = (DefaultTimeBarNode) _tbv2.getPpsRow();
159         if (_tbv.getPpsRow() != null) {
160             for (Interval interval : _tbv.getPpsRow().getIntervals()) {
161                 PPSInterval ppsInterval = (PPSInterval) interval;
162                 i = new PPSInterval(ppsInterval.getPps() / LOWERFACTOR);
163                 i.setBegin(ppsInterval.getBegin().copy());
164                 i.setEnd(ppsInterval.getEnd().copy());
165                 scaleRowLower.addInterval(i);
166             }
167         }
168 
169         // property change listeners on both viewers, modifying the other viewer and the scale od the pps intervals
170         // ensure that the viewers do work smoothly
171         // Since there are some values set on the other viewer that have been calculated, a flag ensures, that we do not
172         // end up in an endless loop
173 
174         // adapt scale intervals and lower viewer whenever the scale of the upper viewer changes
175         _tbv.addPropertyChangeListener(new PropertyChangeListener() {
176 
177             public void propertyChange(PropertyChangeEvent evt) {
178                 if (ignorePropChangeFlag) {
179                     ignorePropChangeFlag = false;
180                     return;
181                 }
182                 if (evt.getPropertyName().equals(TimeBarViewerInterface.PROPERTYNAME_PIXELPERSECOND)) {
183                     ignorePropChangeFlag = true;
184                     _tbv2.setPixelPerSecond(_tbv.getPixelPerSecond() / LOWERFACTOR);
185 
186                     if (_tbv.getPpsRow() != null) {
187                         for (Interval interval : _tbv.getPpsRow().getIntervals()) {
188                             PPSInterval ppsInterval = (PPSInterval) interval;
189                             ppsInterval.setPps(_tbv.getPixelPerSecond() * ZOOMFACTOR);
190                         }
191                     }
192                     if (_tbv2.getPpsRow() != null) {
193                         for (Interval interval : _tbv2.getPpsRow().getIntervals()) {
194                             PPSInterval ppsInterval = (PPSInterval) interval;
195                             ppsInterval.setPps(_tbv2.getPixelPerSecond() * ZOOMFACTOR);
196                         }
197                     }
198                 } else if (evt.getPropertyName().equals(TimeBarViewerInterface.PROPERTYNAME_STARTDATE)) {
199                     JaretDate midDate = getMidDate(_tbv);
200                     ignorePropChangeFlag = true;
201                     setMidDate(_tbv2, midDate);
202 
203                     gridRenderer.setStartMark(_tbv.getStartDate().copy());
204                     gridRenderer.setEndMark(_tbv.getStartDate().copy().advanceSeconds(_tbv.getSecondsDisplayed()));
205                     _tbv2.redraw();
206 
207                 }
208             }
209 
210         });
211 
212         // adapt scale intervals and upper viewer whenever the scale of the lower viewer changes
213         _tbv2.addPropertyChangeListener(new PropertyChangeListener() {
214 
215             public void propertyChange(PropertyChangeEvent evt) {
216                 if (ignorePropChangeFlag) {
217                     ignorePropChangeFlag = false;
218                     return;
219                 }
220                 if (evt.getPropertyName().equals(TimeBarViewerInterface.PROPERTYNAME_PIXELPERSECOND)) {
221                     ignorePropChangeFlag = true;
222                     _tbv.setPixelPerSecond(_tbv2.getPixelPerSecond() / LOWERFACTOR);
223 
224                     if (_tbv.getPpsRow() != null) {
225                         for (Interval interval : _tbv.getPpsRow().getIntervals()) {
226                             PPSInterval ppsInterval = (PPSInterval) interval;
227                             ppsInterval.setPps(_tbv.getPixelPerSecond() * ZOOMFACTOR);
228                         }
229                     }
230                     if (_tbv2.getPpsRow() != null) {
231                         for (Interval interval : _tbv2.getPpsRow().getIntervals()) {
232                             PPSInterval ppsInterval = (PPSInterval) interval;
233                             ppsInterval.setPps(_tbv2.getPixelPerSecond() * ZOOMFACTOR);
234                         }
235                     }
236                 } else if (evt.getPropertyName().equals(TimeBarViewerInterface.PROPERTYNAME_STARTDATE)) {
237                     JaretDate midDate = getMidDate(_tbv2);
238                     ignorePropChangeFlag = true;
239                     setMidDate(_tbv, midDate);
240 
241                     gridRenderer.setStartMark(_tbv.getStartDate().copy());
242                     gridRenderer.setEndMark(_tbv.getStartDate().copy().advanceSeconds(_tbv.getSecondsDisplayed()));
243                     _tbv2.redraw();
244                 }
245             }
246 
247         });
248 
249         _tbv.setStartDate(new JaretDate(20, 11, 1963, 0, 0, 0));
250         _tbv2.redraw();
251 
252         return _tbv;
253     }
254 
255     /***
256      * Helper retrieving the date displayed in the middle of a viewer.
257      * 
258      * @param tbv the viewer
259      * @return the date in the middle of the viewer
260      */
261     private JaretDate getMidDate(TimeBarViewer tbv) {
262         TimeBarViewerDelegate delegate = (TimeBarViewerDelegate) tbv.getData("delegate");
263         Rectangle diagramRect = TimeBarViewer.convertRect(delegate.getDiagramRect());
264         JaretDate midDate = tbv.dateForX(diagramRect.x + diagramRect.width / 2);
265         return midDate;
266     }
267 
268     /***
269      * Helper setting the date displayed in the middle of a timebar viewer.
270      * 
271      * @param tbv the viewer
272      * @param midDate the date
273      */
274     private void setMidDate(TimeBarViewer tbv, JaretDate midDate) {
275         TimeBarViewerDelegate delegate = (TimeBarViewerDelegate) tbv.getData("delegate");
276         Rectangle diagramRect = TimeBarViewer.convertRect(delegate.getDiagramRect());
277 
278         int absX = delegate.xForDateAbs(midDate);
279         int absStartx = absX - diagramRect.width / 2;
280         JaretDate startDate = delegate.dateForCoordAbs(absStartx);
281         tbv.setStartDate(startDate);
282     }
283 
284     /***
285      * Do the configuration of the properties that are the same for both time bar viewers.
286      * 
287      * @param tbv TimeBarViewer to configure
288      */
289     private void configureTBV(TimeBarViewer tbv) {
290         // no selections
291         tbv.getSelectionModel().setRowSelectionAllowed(false);
292         tbv.getSelectionModel().setIntervalSelectionAllowed(false);
293         tbv.getSelectionModel().setMultipleSelectionAllowed(false); // also disable rect selection
294 
295         // hide the y axis
296         tbv.setYAxisWidth(0);
297 
298         // allow dragging to replace scrolling
299         TimeScaleDragSupport tsds = new TimeScaleDragSupport(tbv, true);
300 
301         // use the box timescale renderer
302         tbv.setTimeScaleRenderer(new BoxTimeScaleRenderer());
303 
304         // we will only render one row. This row should always be scaled to match the height of the diagram rectangle.
305         // the row height strategy will ensure that
306         tbv.getTimeBarViewState().setUseVariableRowHeights(true);
307         tbv.getTimeBarViewState().setRowHeightStrategy(new IRowHeightStrategy() {
308 
309             public int calculateRowHeight(TimeBarViewerDelegate delegate, ITimeBarViewState timeBarViewState,
310                     TimeBarRow row) {
311                 return delegate.getDiagramRect().height;
312             }
313 
314             public boolean overrideDefault() {
315                 return true;
316             }
317 
318         });
319 
320         // we use a simplified overlap strategy
321         TimeBarViewerDelegate delegate = (TimeBarViewerDelegate) tbv.getData("delegate");
322         tbv.setOverlapStrategy(new DistributeOverlapStrategy(delegate, NUMROWS));
323 
324     }
325 
326     protected void configureShell(Shell shell) {
327         super.configureShell(shell);
328         shell.setText(getClass().getName());
329         shell.setSize(new Point(1000, 600));
330     }
331 
332     public static void main(String[] args) {
333         TimelineExample test = new TimelineExample();
334         test.setBlockOnOpen(true);
335         test.open();
336     }
337 
338 }