View Javadoc

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