View Javadoc

1   /*
2    *  File: DefaultTimeScaleRenderer.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.HashMap;
24  import java.util.List;
25  import java.util.Map;
26  
27  import org.eclipse.swt.graphics.GC;
28  import org.eclipse.swt.graphics.Point;
29  import org.eclipse.swt.graphics.Rectangle;
30  import org.eclipse.swt.printing.Printer;
31  
32  import de.jaret.util.date.Interval;
33  import de.jaret.util.date.JaretDate;
34  import de.jaret.util.date.holidayenumerator.HolidayEnumerator;
35  import de.jaret.util.date.iterator.DateIterator;
36  import de.jaret.util.date.iterator.DayIterator;
37  import de.jaret.util.date.iterator.HourIterator;
38  import de.jaret.util.date.iterator.MillisecondIterator;
39  import de.jaret.util.date.iterator.MinuteIterator;
40  import de.jaret.util.date.iterator.MonthIterator;
41  import de.jaret.util.date.iterator.SecondIterator;
42  import de.jaret.util.date.iterator.WeekIterator;
43  import de.jaret.util.date.iterator.YearIterator;
44  import de.jaret.util.swt.SwtGraphicsHelper;
45  import de.jaret.util.ui.timebars.TickScaler.Range;
46  import de.jaret.util.ui.timebars.TimeBarViewerDelegate;
47  import de.jaret.util.ui.timebars.TimeBarViewerInterface;
48  import de.jaret.util.ui.timebars.model.PPSInterval;
49  import de.jaret.util.ui.timebars.strategy.ITickProvider;
50  import de.jaret.util.ui.timebars.swt.TimeBarViewer;
51  
52  /**
53   * A default timescale renderer rendering a readable scale for ranges from years to milliseconds. A holiday enumerator
54   * is used to display special and holidays in the tooltip. Suports horizontal and vertical orientation.
55   * 
56   * @author Peter Kliem
57   * @version $Id: DefaultTimeScaleRenderer.java 1086 2011-07-30 20:28:05Z kliem $
58   */
59  public class DefaultTimeScaleRenderer extends RendererBase implements TimeScaleRenderer, ITickProvider {
60  
61      /** preferred height. */
62      protected static final int PREFERREDHEIGHT = 50;
63  
64      /** length of minor ticks. */
65      protected static final int MINORLENGTH = 5;
66      /** length of major ticks. */
67      protected static final int MAJORLENGTH = 10;
68  
69      /** additional gap between end of label and next line (approx). */
70      protected static final int ADDITIONALGAP = 7;
71  
72      /** gap between line and label. */
73      protected static final int GAP = 3;
74  
75      /** Bonus rewarded when an Iterator is already enabled for a format. */
76      protected static final int SETBONUS = 5;
77  
78      /** remember the last seen pps value. */
79      private double _lastPPS = -1;
80  
81      /** major ticks of the last rendering. */
82      protected List<JaretDate> _majorTicks;
83      /** minor ticks of the last rendering. */
84      protected List<JaretDate> _minorTicks;
85  
86      /** List of date iterators for the strips. */
87      protected List<DateIterator> _iterators;
88  
89      /** format of the corresponding iterator label. */
90      protected List<DateIterator.Format> _formats;
91  
92      /** iterator for the middle main strip. */
93      protected DateIterator _midStrip;
94      /** iterator for the upmost strip. */
95      protected DateIterator _upperStrip;
96      /** iterator for the minor ticks. */
97      protected DateIterator _lowerStrip;
98      /** map defining upper iterators for middle iterators. */
99      protected Map<DateIterator, DateIterator> _upperMap = new HashMap<DateIterator, DateIterator>();
100 
101     /** holiday enumerator for tooltips. */
102     protected HolidayEnumerator _holidayEnumerator;
103 
104     /**
105      * Construct the renderer for a prinetr device.
106      * 
107      * @param printer printer device
108      */
109     public DefaultTimeScaleRenderer(Printer printer) {
110         super(printer);
111         initIterators();
112     }
113 
114     /**
115      * Default constructor.
116      */
117     public DefaultTimeScaleRenderer() {
118         super(null);
119         initIterators();
120     }
121 
122     /**
123      * Initialize all possible iterators (strips).
124      */
125     protected void initIterators() {
126         _iterators = new ArrayList<DateIterator>();
127         _formats = new ArrayList<DateIterator.Format>();
128 
129         DateIterator iterator = new MillisecondIterator(1);
130         _iterators.add(iterator);
131         _formats.add(DateIterator.Format.LONG);
132 
133         iterator = new MillisecondIterator(10);
134         _iterators.add(iterator);
135         _formats.add(DateIterator.Format.LONG);
136 
137         iterator = new MillisecondIterator(100);
138         _iterators.add(iterator);
139         _formats.add(DateIterator.Format.LONG);
140 
141         iterator = new MillisecondIterator(500);
142         _iterators.add(iterator);
143         _formats.add(DateIterator.Format.LONG);
144 
145         iterator = new SecondIterator(1);
146         _iterators.add(iterator);
147         _formats.add(DateIterator.Format.LONG);
148 
149         iterator = new SecondIterator(5);
150         _iterators.add(iterator);
151         _formats.add(DateIterator.Format.LONG);
152 
153         iterator = new SecondIterator(30);
154         _iterators.add(iterator);
155         _formats.add(DateIterator.Format.LONG);
156 
157         iterator = new MinuteIterator(1);
158         _iterators.add(iterator);
159         _formats.add(DateIterator.Format.LONG);
160 
161         iterator = new MinuteIterator(10);
162         _iterators.add(iterator);
163         _formats.add(DateIterator.Format.LONG);
164         _upperMap.put(iterator, new DayIterator(1));
165 
166         iterator = new MinuteIterator(30);
167         _iterators.add(iterator);
168         _formats.add(DateIterator.Format.LONG);
169         _upperMap.put(iterator, new DayIterator(1));
170 
171         iterator = new HourIterator(3);
172         _iterators.add(iterator);
173         _formats.add(DateIterator.Format.LONG);
174         _upperMap.put(iterator, new DayIterator(1));
175 
176         iterator = new HourIterator(12);
177         _iterators.add(iterator);
178         _formats.add(DateIterator.Format.LONG);
179         _upperMap.put(iterator, new DayIterator(1));
180 
181         iterator = new DayIterator();
182         _iterators.add(iterator);
183         _formats.add(DateIterator.Format.LONG);
184 
185         iterator = new WeekIterator();
186         _iterators.add(iterator);
187         _formats.add(DateIterator.Format.LONG);
188         _upperMap.put(iterator, new MonthIterator());
189 
190         iterator = new MonthIterator();
191         _iterators.add(iterator);
192         _formats.add(DateIterator.Format.LONG);
193         _upperMap.put(iterator, new YearIterator());
194 
195         iterator = new YearIterator();
196         _iterators.add(iterator);
197         _formats.add(DateIterator.Format.LONG);
198     }
199 
200     /**
201      * Set a holidayenumerator for diasplaying special days as tooltip.
202      * 
203      * @param he holidayenumerator to use
204      */
205     public void setHolidayEnumerator(HolidayEnumerator he) {
206         _holidayEnumerator = he;
207     }
208 
209     /**
210      * {@inheritDoc}
211      */
212     public void draw(GC gc, Rectangle drawingArea, TimeBarViewerDelegate delegate, boolean top, boolean printing) {
213         // each drawing operation produces new tick dates
214         _majorTicks = new ArrayList<JaretDate>();
215         _minorTicks = new ArrayList<JaretDate>();
216 
217         if (!delegate.hasVariableXScale()) {
218             // if pps changed check which stripes to draw
219             if (_lastPPS != delegate.getPixelPerSecond()) {
220                 checkStrips(gc, delegate, delegate.getStartDate(), null);
221                 _lastPPS = delegate.getPixelPerSecond();
222             }
223             // plain scale
224             // +1 second for millisecond scales since the getSecondsDisplayed method rounds to nearest lower second
225             // count
226             drawStrips(gc, delegate, drawingArea, top, delegate.getStartDate().copy(), delegate.getStartDate().copy()
227                     .advanceSeconds(delegate.getSecondsDisplayed() + 1), printing);
228         } else {
229             // check strips for every part with different scale
230             JaretDate startDate = delegate.getStartDate().copy();
231             JaretDate endDate = delegate.getStartDate().copy().advanceSeconds(delegate.getSecondsDisplayed());
232             List<Interval> ppsIntervals = delegate.getPpsRow().getIntervals(startDate, endDate);
233             // shortcut if no ppsintervals are in the area just draw straight
234             if (ppsIntervals.size() == 0) {
235                 // if pps changed check which stripes to draw
236                 if (_lastPPS != delegate.getPixelPerSecond()) {
237                     checkStrips(gc, delegate, delegate.getStartDate(), null);
238                     _lastPPS = delegate.getPixelPerSecond();
239                 }
240                 drawStrips(gc, delegate, drawingArea, top, delegate.getStartDate().copy(), delegate.getStartDate()
241                         .copy().advanceSeconds(delegate.getSecondsDisplayed() + 1), printing); // +1 -> see above
242             } else {
243                 JaretDate d = startDate.copy();
244                 while (d.compareTo(endDate) < 0) {
245                     PPSInterval ppsInterval = delegate.getPPSInterval(d);
246                     JaretDate e;
247                     if (ppsInterval != null) {
248                         e = ppsInterval.getEnd();
249                     } else {
250                         PPSInterval nextInterval = delegate.nextPPSInterval(d);
251                         if (nextInterval != null) {
252                             e = nextInterval.getBegin();
253                         } else {
254                             e = endDate;
255                         }
256                     }
257                     // calculate the strips for the current date in question
258                     checkStrips(gc, delegate, d, e);
259                     // only draw if the interval is not a break
260                     if (ppsInterval == null || !ppsInterval.isBreak()) {
261                         drawStrips(gc, delegate, drawingArea, top, d, e, printing);
262                     } else {
263                         // TODO draw break
264                     }
265                     d = e;
266                 }
267                 _lastPPS = -1; // force check next paint
268             }
269         }
270     }
271 
272     /**
273      * Draw the configured strips in the area indicated.
274      * 
275      * @param gc Graphics to draw with
276      * @param delegate delegate
277      * @param top boolean for top position
278      * @param startDate start date for this part of the strip
279      * @param endDate end date for this part of the strip
280      * @param printing true if used to print
281      */
282     private void drawStrips(GC gc, TimeBarViewerDelegate delegate, Rectangle drawingArea, boolean top,
283             JaretDate startDate, JaretDate endDate, boolean printing) {
284         boolean horizontal = delegate.getOrientation().equals(TimeBarViewerInterface.Orientation.HORIZONTAL);
285 
286         Rectangle clipSave = gc.getClipping();
287 
288         if (horizontal) {
289             // clip to start/end date for ensuring nothing is painted beyond the limits
290             int sx = delegate.xForDate(startDate);
291             Rectangle destRect = new Rectangle(sx, drawingArea.y, delegate.xForDate(endDate) - sx, drawingArea.height);
292             // gc.setClip(destRect.intersection(clipSave));
293 
294             int basey;
295             int minorOff;
296             int majorOff;
297             int majorLabelOff;
298             int dayOff;
299             if (!top) {
300                 basey = drawingArea.y;
301                 minorOff = scaleY(MINORLENGTH);
302                 majorOff = scaleY(MAJORLENGTH);
303                 majorLabelOff = scaleY(22);
304                 dayOff = scaleY(34);
305             } else {
306                 basey = drawingArea.y + drawingArea.height - 1;
307                 minorOff = scaleY(-MINORLENGTH);
308                 majorOff = scaleY(-MAJORLENGTH);
309                 majorLabelOff = scaleY(-10);
310                 dayOff = scaleY(-22);
311             }
312             int oy = basey;
313 
314             // draw top line
315             gc.drawLine(drawingArea.x, oy, drawingArea.x + drawingArea.width, oy);
316 
317             if (_lowerStrip != null) {
318                 DateIterator it = _lowerStrip;
319                 it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
320                 while (it.hasNextDate()) {
321                     JaretDate d = it.getNextDate();
322                     _minorTicks.add(d);
323                     int x = delegate.xForDate(d);
324                     gc.drawLine(x, oy, x, oy + minorOff);
325                 }
326             }
327             if (_midStrip != null) {
328                 DateIterator it = _midStrip;
329                 it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
330                 while (it.hasNextDate()) {
331                     JaretDate d = it.getNextDate();
332 
333                     _majorTicks.add(d);
334 
335                     int x = delegate.xForDate(d);
336                     gc.drawLine(x, oy, x, oy + majorOff);
337                     // label every two major ticks
338                     String label = it.getLabel(d, DateIterator.Format.LONG);
339                     SwtGraphicsHelper.drawStringCentered(gc, label, x, oy + majorLabelOff);
340                 }
341             }
342 
343             // draw upper part
344             if (_upperStrip != null) {
345                 DateIterator it = _upperStrip;
346                 it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
347                 while (it.hasNextDate()) {
348                     JaretDate d = it.getNextDate();
349                     int x = delegate.xForDate(d);
350 
351                     gc.drawLine(x, oy, x, oy + majorOff);
352                     String label = it.getLabel(d, DateIterator.Format.LONG);
353                     SwtGraphicsHelper.drawStringCentered(gc, label, x, oy + dayOff);
354                 }
355             }
356 
357         } else {
358             // vertical strip
359             // clip to start/end date for ensuring nothing is painted beyond the limits
360             int sy = delegate.xForDate(startDate);
361             Rectangle destRect = new Rectangle(drawingArea.x, sy, drawingArea.width, delegate.xForDate(endDate) - sy);
362             gc.setClipping(destRect.intersection(clipSave));
363 
364             int basex;
365             int minorOff;
366             int majorOff;
367             int majorLabelOff;
368             int dayOff;
369             if (!top) {
370                 basex = drawingArea.x;
371                 minorOff = scaleX(MINORLENGTH);
372                 majorOff = scaleX(MAJORLENGTH);
373                 majorLabelOff = scaleX(22);
374                 dayOff = scaleX(34);
375             } else {
376                 basex = drawingArea.x + drawingArea.width - 1;
377                 minorOff = scaleX(-MINORLENGTH);
378                 majorOff = scaleX(-MAJORLENGTH);
379                 majorLabelOff = scaleX(-10);
380                 dayOff = scaleX(-22);
381             }
382             int ox = basex;
383 
384             // draw left/right line
385             if (top) {
386                 gc.drawLine(drawingArea.x + drawingArea.width - 1, 0, drawingArea.x + drawingArea.width - 1,
387                         getHeight());
388             } else {
389                 gc.drawLine(0, 0, 0, getHeight());
390             }
391 
392             if (_lowerStrip != null) {
393                 DateIterator it = _lowerStrip;
394                 it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
395                 while (it.hasNextDate()) {
396                     JaretDate d = it.getNextDate();
397 
398                     _minorTicks.add(d);
399 
400                     int y = delegate.xForDate(d);
401                     gc.drawLine(ox, y, ox + minorOff, y);
402                 }
403             }
404             if (_midStrip != null) {
405                 DateIterator it = _midStrip;
406                 it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
407                 while (it.hasNextDate()) {
408                     JaretDate d = it.getNextDate();
409 
410                     _majorTicks.add(d);
411 
412                     int y = delegate.xForDate(d);
413 
414                     gc.drawLine(ox, y, ox + majorOff, y);
415                     String label = it.getLabel(d, DateIterator.Format.LONG);
416                     if (top) {
417                         SwtGraphicsHelper.drawStringRightAlignedVCenter(gc, label, ox + majorOff - 1, y);
418                     } else {
419                         SwtGraphicsHelper.drawStringLeftAlignedVCenter(gc, label, ox + majorOff + 1, y);
420                     }
421                 }
422             }
423 
424             // draw upper part
425             if (_upperStrip != null) {
426                 DateIterator it = _upperStrip;
427                 it.reInitialize(startDate, endDate.copy().advanceMillis(it.getApproxStepMilliSeconds()));
428                 while (it.hasNextDate()) {
429                     JaretDate d = it.getNextDate();
430                     int x = delegate.xForDate(d);
431 
432                     // String label = it.getLabel(DateIterator.Format.LONG);
433                     // if (_top) {
434                     // GraphicsHelper.drawStringRightAlignedVCenter(gc, label, ox + dayOff - 1, y);
435                     // } else {
436                     // GraphicsHelper.drawStringLeftAlignedVCenter(gc, label, ox + majorOff + 1, y);
437                     // }
438                 }
439             }
440 
441         }
442         gc.setClipping(clipSave);
443     }
444 
445     /**
446      * Check which strips should be drawn.
447      * 
448      * @param gc Graphics
449      * @param delegate delegate
450      * @param startDate date to use for starting check
451      */
452     private void checkStrips(GC gc, TimeBarViewerDelegate delegate, JaretDate startDate, JaretDate endDate) {
453         // System.out.println("checkstrips "+startDate.getDate().getTime()+" -- "+endDate.getDate().getTime());
454         for (int i = 0; i < _iterators.size(); i++) {
455             DateIterator it = _iterators.get(i);
456             it.reInitialize(startDate, endDate);
457             if (it.previewNextDate() != null) {
458                 JaretDate current = it.getNextDate();
459                 if (!it.hasNextDate()) {
460                     continue;
461                 }
462                 JaretDate next = it.getNextDate();
463                 int width = delegate.xForDate(next) - delegate.xForDate(current);
464                 String label = it.getLabel(current, DateIterator.Format.LONG);
465                 System.out.println("Label " + label);
466                 Point p = gc.textExtent(label);
467                 int bonus = _midStrip == it && _formats.get(i).equals(DateIterator.Format.LONG) ? SETBONUS : 0;
468                 if (width > p.x + GAP + ADDITIONALGAP - bonus) {
469                     _midStrip = it;
470                     System.out.println(it);
471                     if (it instanceof MillisecondIterator) {
472                         MillisecondIterator mit = (MillisecondIterator) it;
473                         System.out.println(mit.getApproxStepMilliSeconds());
474                     }
475                     _upperStrip = _upperMap.get(_midStrip);
476                     if (i > 0) {
477                         _lowerStrip = _iterators.get(i - 1);
478                     }
479                     break;
480                 }
481             }
482         }
483     }
484 
485     /**
486      * {@inheritDoc}
487      */
488     public String getToolTipText(TimeBarViewer tbv, Rectangle drawingArea, int x, int y) {
489 
490         TimeBarViewerDelegate delegate = (TimeBarViewerDelegate) tbv.getData("delegate");
491         JaretDate date = null;
492         if (delegate.getOrientation() == TimeBarViewerInterface.Orientation.HORIZONTAL) {
493             date = tbv.dateForX(x);
494         } else {
495             date = tbv.dateForX(y);
496         }
497 
498         // TODO
499         // String str = getToolTipTextForDate(date, _lastRange);
500         // if (_holidayEnumerator != null) {
501         // if (_holidayEnumerator.isHoliday(date.getDate()) || _holidayEnumerator.isSpecialDay(date.getDate())) {
502         // str += "\n" + _holidayEnumerator.getDayName(date.getDate());
503         // }
504         // }
505         // return str;
506         return date.toDisplayString();
507     }
508 
509     /**
510      * Convert date to string for tooltip display.
511      * 
512      * @param date date
513      * @param range last range
514      * @return string for displaying as tooltip
515      */
516     protected String getToolTipTextForDate(JaretDate date, Range range) {
517         String str;
518         if (range == Range.HOUR) {
519             str = date.toDisplayString();
520         } else {
521             str = date.toDisplayStringDate();
522         }
523         return str;
524     }
525 
526     /**
527      * {@inheritDoc}
528      */
529     public int getHeight() {
530         if (_printer == null) {
531             return PREFERREDHEIGHT;
532         } else {
533             return scaleY(PREFERREDHEIGHT);
534         }
535     }
536 
537     /**
538      * {@inheritDoc}
539      */
540     public void dispose() {
541         // nothing to dispose
542     }
543 
544     /**
545      * {@inheritDoc}
546      */
547     public TimeScaleRenderer createPrintRenderer(Printer printer) {
548         DefaultTimeScaleRenderer dtsr = new DefaultTimeScaleRenderer(printer);
549         dtsr.setHolidayEnumerator(_holidayEnumerator);
550         return dtsr;
551     }
552 
553     /**
554      * {@inheritDoc}
555      */
556     public List<JaretDate> getMajorTicks(TimeBarViewerDelegate delegate) {
557         return _majorTicks;
558     }
559 
560     /**
561      * {@inheritDoc}
562      */
563     public List<JaretDate> getMinorTicks(TimeBarViewerDelegate delegate) {
564         return _minorTicks;
565     }
566 
567     /**
568      * Setup the iterators to do a DST correction.
569      * 
570      * @param correctDST true if a correction should be done.
571      */
572     public void setCorrectDST(boolean correctDST) {
573         for (DateIterator iterator : _iterators) {
574             iterator.setCorrectDST(correctDST);
575         }
576     }
577     
578     /**
579      * {@inheritDoc}
580      */
581     public boolean supportsOptimizedScrolling() {
582         return true;
583     }
584 
585 
586 }