View Javadoc

1   /*
2    *  File: BoxTimeScaleRenderer.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.util.ui.timebars.swt.renderer;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.eclipse.swt.graphics.GC;
26  import org.eclipse.swt.graphics.Point;
27  import org.eclipse.swt.graphics.Rectangle;
28  import org.eclipse.swt.printing.Printer;
29  
30  import de.jaret.util.date.Interval;
31  import de.jaret.util.date.JaretDate;
32  import de.jaret.util.date.holidayenumerator.HolidayEnumerator;
33  import de.jaret.util.date.iterator.DateIterator;
34  import de.jaret.util.date.iterator.DayIterator;
35  import de.jaret.util.date.iterator.HourIterator;
36  import de.jaret.util.date.iterator.MillisecondIterator;
37  import de.jaret.util.date.iterator.MinuteIterator;
38  import de.jaret.util.date.iterator.MonthIterator;
39  import de.jaret.util.date.iterator.SecondIterator;
40  import de.jaret.util.date.iterator.WeekIterator;
41  import de.jaret.util.date.iterator.YearIterator;
42  import de.jaret.util.swt.SwtGraphicsHelper;
43  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
44  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
45  import de.jaret.util.ui.timebars.model.PPSInterval;
46  import de.jaret.util.ui.timebars.strategy.ITickProvider;
47  import de.jaret.util.ui.timebars.swt.TimeBarViewer;
48  
49  /**
50   * Timescalerenderer rendering strips of boxes for different time intervals using DateIterator instances. A holiday
51   * enumerator can be used to display special and holidays in the tooltip. Supports horizontal and vertical operation
52   * (vertical might be imperformant since vertical text is used).
53   * 
54   * @author Peter Kliem
55   * @version $Id: BoxTimeScaleRenderer.java 1086 2011-07-30 20:28:05Z kliem $
56   */
57  public class BoxTimeScaleRenderer extends RendererBase implements TimeScaleRenderer, ITickProvider {
58      /** default number of strips to render. */
59      private static final int DEFAULT_NUMBER_OF_STRIPS = 3;
60  
61      /** height of a box. */
62      protected static final int BOXHEIGHT = 20;
63  
64      /** additional gap between end of label and next line (approx). */
65      protected static final int ADDITIONALGAP = 7;
66  
67      /** gap between line and label. */
68      protected static final int GAP = 3;
69  
70      /** Bonus rewarded when an Iterator is already enabled for a format. */
71      protected static final int SETBONUS = 5;
72  
73      /** local cache for textextent height. */
74      protected int _textHeight = -1;
75  
76      /** actual number of strips to render. */
77      protected int _numberOfStrips = DEFAULT_NUMBER_OF_STRIPS;
78  
79      /** remember the last seen pps value. */
80      private double _lastPPS = -1;
81  
82      /** holiday enumerator for tooltips. */
83      protected HolidayEnumerator _holidayEnumerator;
84  
85      /** List of dateiterators for the strips. */
86      protected List<DateIterator> _iterators;
87  
88      /** format of the corresponding iterator label. */
89      protected List<DateIterator.Format> _formats;
90  
91      /** enabled state of the corresponding strip. */
92      protected List<Boolean> _enable;
93  
94      /** major ticks of the last rendering. */
95      protected List<JaretDate> _majorTicks;
96      /** minor ticks of the last rendering. */
97      protected List<JaretDate> _minorTicks;
98  
99      /**
100      * Construct the renderer for a printer device.
101      * 
102      * @param printer printer device
103      */
104     public BoxTimeScaleRenderer(Printer printer) {
105         super(printer);
106         initIterators();
107     }
108 
109     /**
110      * Initialize all possible iterators (strips).
111      */
112     private void initIterators() {
113         _iterators = new ArrayList<DateIterator>();
114         _formats = new ArrayList<DateIterator.Format>();
115         _enable = new ArrayList<Boolean>();
116 
117         _iterators.add(new MillisecondIterator(100));
118         _formats.add(DateIterator.Format.LONG);
119         _enable.add(true);
120 
121         _iterators.add(new MillisecondIterator(500));
122         _formats.add(DateIterator.Format.LONG);
123         _enable.add(true);
124 
125         _iterators.add(new SecondIterator(1));
126         _formats.add(DateIterator.Format.LONG);
127         _enable.add(true);
128 
129         _iterators.add(new SecondIterator(5));
130         _formats.add(DateIterator.Format.LONG);
131         _enable.add(true);
132 
133         _iterators.add(new SecondIterator(30));
134         _formats.add(DateIterator.Format.LONG);
135         _enable.add(true);
136 
137         _iterators.add(new MinuteIterator(1));
138         _formats.add(DateIterator.Format.LONG);
139         _enable.add(true);
140 
141         _iterators.add(new MinuteIterator(10));
142         _formats.add(DateIterator.Format.LONG);
143         _enable.add(true);
144 
145         _iterators.add(new MinuteIterator(30));
146         _formats.add(DateIterator.Format.LONG);
147         _enable.add(true);
148 
149         _iterators.add(new HourIterator(3));
150         _formats.add(DateIterator.Format.LONG);
151         _enable.add(true);
152 
153         _iterators.add(new HourIterator(12));
154         _formats.add(DateIterator.Format.LONG);
155         _enable.add(true);
156 
157         _iterators.add(new DayIterator());
158         _formats.add(DateIterator.Format.LONG);
159         _enable.add(true);
160 
161         _iterators.add(new WeekIterator());
162         _formats.add(DateIterator.Format.LONG);
163         _enable.add(true);
164 
165         _iterators.add(new MonthIterator());
166         _formats.add(DateIterator.Format.LONG);
167         _enable.add(true);
168 
169         _iterators.add(new YearIterator());
170         _formats.add(DateIterator.Format.LONG);
171         _enable.add(true);
172     }
173 
174     /**
175      * Default constructor.
176      */
177     public BoxTimeScaleRenderer() {
178         super(null);
179         initIterators();
180     }
181 
182     /**
183      * Set a holidayenumerator for displaying special days as tooltip.
184      * 
185      * @param he holidayenumerator to use
186      */
187     public void setHolidayEnumerator(HolidayEnumerator he) {
188         _holidayEnumerator = he;
189     }
190 
191     /**
192      * {@inheritDoc}
193      */
194     public void draw(GC gc, Rectangle drawingArea, TimeBarViewerDelegate delegate, boolean top, boolean printing) {
195         // if pps changed check which stripes to draw
196         if (_lastPPS != delegate.getPixelPerSecond()) {
197             checkStrips(gc, delegate, delegate.getStartDate());
198             _lastPPS = delegate.getPixelPerSecond();
199         }
200         if (_textHeight == -1) {
201             Point p = gc.textExtent("Q");
202             _textHeight = p.y;
203         }
204 
205         int lineWidth = gc.getLineWidth();
206         if (printing) {
207             gc.setLineWidth(getDefaultLineWidth());
208         }
209         // each drawing operation produces new tick dates
210         _majorTicks = new ArrayList<JaretDate>();
211         _minorTicks = new ArrayList<JaretDate>();
212 
213         if (!delegate.hasVariableXScale()) {
214             // plain scale
215             drawStrips(gc, drawingArea, delegate, top, printing, delegate.getStartDate().copy(), delegate
216                     .getStartDate().copy().advanceSeconds(delegate.getSecondsDisplayed() + 1));
217         } else {
218             // check strips for every part with different scale
219             JaretDate startDate = delegate.getStartDate().copy();
220             JaretDate endDate = delegate.getStartDate().copy().advanceSeconds(delegate.getSecondsDisplayed() + 1);
221             List<Interval> ppsIntervals = delegate.getPpsRow().getIntervals(startDate, endDate);
222             // shortcut if no ppsintervals are in the area just draw straight
223             if (ppsIntervals.size() == 0) {
224                 drawStrips(gc, drawingArea, delegate, top, printing, delegate.getStartDate().copy(), delegate
225                         .getStartDate().copy().advanceSeconds(delegate.getSecondsDisplayed() + 1));
226             } else {
227                 JaretDate d = startDate.copy();
228                 while (d.compareTo(endDate) < 0) {
229                     // calculate the strips for the current date in question
230                     checkStrips(gc, delegate, d);
231                     PPSInterval ppsInterval = delegate.getPPSInterval(d);
232                     JaretDate e;
233                     if (ppsInterval != null) {
234                         e = ppsInterval.getEnd();
235                     } else {
236                         PPSInterval nextInterval = delegate.nextPPSInterval(d);
237                         if (nextInterval != null) {
238                             e = nextInterval.getBegin();
239                         } else {
240                             e = endDate;
241                         }
242                     }
243                     drawStrips(gc, drawingArea, delegate, top, printing, d, e);
244                     d = e;
245                 }
246                 _lastPPS = -1; // force check next paint
247             }
248         }
249         gc.setLineWidth(lineWidth); // restore linewidth
250     }
251 
252     /**
253      * Draw the configured strips in the area indicated.
254      * 
255      * @param gc GC
256      * @param drawingArea complete drawing area for the scale
257      * @param delegate delegate
258      * @param top boolean for top position
259      * @param printing true for printing
260      * @param startDate start date for this part of the strip
261      * @param endDate end date for this part of the strip
262      */
263     private void drawStrips(GC gc, Rectangle drawingArea, TimeBarViewerDelegate delegate, boolean top,
264             boolean printing, JaretDate startDate, JaretDate endDate) {
265         boolean horizontal = delegate.getOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL);
266 
267         Rectangle clipSave = gc.getClipping();
268 
269         if (horizontal) {
270             // clip to start/end date for ensuring nothing is painted beyond the limits
271             int sx = delegate.xForDate(startDate);
272             Rectangle destRect = new Rectangle(sx, drawingArea.y, delegate.xForDate(endDate) - sx, drawingArea.height);
273             gc.setClipping(destRect.intersection(clipSave));
274 
275             int ox = drawingArea.x;
276             int basey = top ? drawingArea.y + drawingArea.height : drawingArea.y + scaleY(BOXHEIGHT);
277 
278             int drawn = 0;
279             // now draw the stripes
280             for (int i = 0; i < _iterators.size() && drawn < _numberOfStrips; i++) {
281                 DateIterator it = _iterators.get(i);
282                 if (_enable.get(i)) {
283                     drawn++;
284                     it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
285                     while (it.hasNextDate()) {
286                         JaretDate d = it.getNextDate();
287                         String label = it.getLabel(d, _formats.get(i));
288 
289                         // save the tic dates for the two first strips as the minor and major ticks
290                         if (drawn==1) {
291                             _minorTicks.add(d);
292                         } else if(drawn==2) {
293                             _majorTicks.add(d);
294                         }
295 
296                         int x = delegate.xForDate(d);
297                         gc.drawLine(x, basey, x, basey - scaleY(BOXHEIGHT));
298                         int yy = (scaleY(BOXHEIGHT) - _textHeight) / 2;
299                         gc.drawString(label, x + scaleX(GAP), basey - yy - _textHeight);
300                     }
301                     if (top) {
302                         gc.drawLine(ox, basey - scaleY(BOXHEIGHT), ox + drawingArea.width, basey - scaleY(BOXHEIGHT));
303                     } else {
304                         gc.drawLine(ox, basey, ox + drawingArea.width, basey);
305                     }
306                     basey = basey + (top ? -scaleY(BOXHEIGHT) : scaleY(BOXHEIGHT));
307                 }
308             }
309         } else {
310             // vertical strip
311             // clip to start/end date for ensuring nothing is painted beyond the limits
312             int sy = delegate.xForDate(startDate);
313             Rectangle destRect = new Rectangle(drawingArea.x, sy, drawingArea.width, delegate.xForDate(endDate) - sy);
314             gc.setClipping(destRect.intersection(clipSave));
315 
316             int oy = drawingArea.y;
317             int basex = top ? drawingArea.x + drawingArea.width : drawingArea.x + scaleX(BOXHEIGHT);
318 
319             int drawn = 0;
320             // now draw the stripes
321             for (int i = 0; i < _iterators.size() && drawn < _numberOfStrips; i++) {
322                 DateIterator it = _iterators.get(i);
323                 if (_enable.get(i)) {
324                     drawn++;
325                     it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
326                     while (it.hasNextDate()) {
327                         JaretDate d = it.getNextDate();
328                         String label = it.getLabel(d, _formats.get(i));
329 
330                         // save the tic dates for the two first strips as the minor and major ticks
331                         if (drawn==1) {
332                             _minorTicks.add(d);
333                         } else if(drawn==2) {
334                             _majorTicks.add(d);
335                         }
336 
337                         int y = delegate.xForDate(d);
338                         gc.drawLine(basex, y, basex - scaleX(BOXHEIGHT), y);
339                         int xx = (scaleX(BOXHEIGHT) - _textHeight) / 2;
340                         SwtGraphicsHelper.drawStringVertical(gc, label, basex - xx - _textHeight, y + scaleY(GAP));
341                     }
342                     if (top) {
343                         gc.drawLine(basex - scaleX(BOXHEIGHT), oy, basex - scaleX(BOXHEIGHT), oy + drawingArea.height);
344                     } else {
345                         gc.drawLine(basex, oy, basex, oy + drawingArea.height);
346                     }
347                     basex = basex + (top ? -scaleX(BOXHEIGHT) : scaleX(BOXHEIGHT));
348                 }
349             }
350         }
351         gc.setClipping(clipSave);
352     }
353 
354     /**
355      * Check which strips should be drawn.
356      * 
357      * @param gc GC
358      * @param delegate delegate
359      * @param startDate date to use for starting check
360      */
361     private void checkStrips(GC gc, TimeBarViewerDelegate delegate, JaretDate startDate) {
362         for (int i = 0; i < _iterators.size(); i++) {
363             DateIterator it = _iterators.get(i);
364             it.reInitialize(startDate, null);
365             if (it.previewNextDate() != null) {
366                 JaretDate current = it.getNextDate();
367                 JaretDate next = it.getNextDate();
368                 int width = delegate.xForDate(next) - delegate.xForDate(current);
369                 String label = it.getLabel(current, DateIterator.Format.LONG);
370                 Point p = gc.textExtent(label);
371                 int bonus = _enable.get(i) && _formats.get(i).equals(DateIterator.Format.LONG) ? SETBONUS : 0;
372                 if (width > p.x + GAP + ADDITIONALGAP - bonus) {
373                     _enable.set(i, true);
374                     _formats.set(i, DateIterator.Format.LONG);
375                 } else {
376                     label = it.getLabel(current, DateIterator.Format.MEDIUM);
377                     p = gc.textExtent(label);
378                     bonus = _enable.get(i) && _formats.get(i).equals(DateIterator.Format.MEDIUM) ? SETBONUS : 0;
379                     if (width > p.x + GAP + ADDITIONALGAP - bonus) {
380                         _enable.set(i, true);
381                         _formats.set(i, DateIterator.Format.MEDIUM);
382                     } else {
383                         label = it.getLabel(current, DateIterator.Format.SHORT);
384                         p = gc.textExtent(label);
385                         bonus = _enable.get(i) && _formats.get(i).equals(DateIterator.Format.SHORT) ? SETBONUS : 0;
386                         if (width > p.x + GAP + ADDITIONALGAP - bonus) {
387                             _enable.set(i, true);
388                             _formats.set(i, DateIterator.Format.SHORT);
389                         } else {
390                             _enable.set(i, false);
391                         }
392                     }
393                 }
394             } else {
395                 _enable.set(i, false);
396             }
397         }
398 
399     }
400 
401     /**
402      * {@inheritDoc}
403      */
404     public String getToolTipText(TimeBarViewer tbv, Rectangle drawingArea, int x, int y) {
405         String str = null;
406         JaretDate date = tbv.dateForX(x);
407         str = date.toDisplayString();
408         if (_holidayEnumerator != null) {
409             if (_holidayEnumerator.isHoliday(date.getDate()) || _holidayEnumerator.isSpecialDay(date.getDate())) {
410                 str += "\n" + _holidayEnumerator.getDayName(date.getDate());
411             }
412         }
413         return str;
414     }
415 
416     /**
417      * {@inheritDoc}
418      */
419     public int getHeight() {
420         if (_printer == null) {
421             return _numberOfStrips * BOXHEIGHT;
422         } else {
423             return scaleY(_numberOfStrips * BOXHEIGHT);
424         }
425     }
426 
427     /**
428      * {@inheritDoc}
429      */
430     public void dispose() {
431         // nothing to dispose
432     }
433 
434     /**
435      * {@inheritDoc}
436      */
437     public TimeScaleRenderer createPrintRenderer(Printer printer) {
438         return new BoxTimeScaleRenderer(printer);
439     }
440 
441     /**
442      * retrieve the number of strips to render.
443      * 
444      * @return number of strips
445      */
446     public int getNumberOfStrips() {
447         return _numberOfStrips;
448     }
449 
450     /**
451      * Set the maximum number of strips to render.
452      * 
453      * @param numberOfStrips maximum number of strips
454      */
455     public void setNumberOfStrips(int numberOfStrips) {
456         _numberOfStrips = numberOfStrips;
457     }
458 
459     /**
460      * {@inheritDoc}
461      */
462     public List<JaretDate> getMajorTicks(TimeBarViewerDelegate delegate) {
463         return _majorTicks;
464     }
465 
466     /**
467      * {@inheritDoc}
468      */
469     public List<JaretDate> getMinorTicks(TimeBarViewerDelegate delegate) {
470         return _minorTicks;
471     }
472 
473     /**
474      * Setup the iterators to do a DST correction.
475      * 
476      * @param correctDST true if a correction should be done.
477      */
478     public void setCorrectDST(boolean correctDST) {
479         for (DateIterator iterator : _iterators) {
480             iterator.setCorrectDST(correctDST);
481         }
482     }
483     
484     
485     /**
486      * {@inheritDoc}
487      */
488     public boolean supportsOptimizedScrolling() {
489         return true;
490     }
491 
492 }