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