001/*
002 *    $RCSfile$
003 *
004 *     $Author: crawl $
005 *       $Date: 2014-08-26 00:46:48 +0000 (Tue, 26 Aug 2014) $
006 *   $Revision: 32920 $
007 *
008 *  For Details: http://kepler-project.org
009 *
010 * Copyright (c) 2007 The Regents of the University of California.
011 * All rights reserved.
012 *
013 * Permission is hereby granted, without written agreement and without
014 * license or royalty fees, to use, copy, modify, and distribute this
015 * software and its documentation for any purpose, provided that the
016 * above copyright notice and the following two paragraphs appear in
017 * all copies of this software.
018 *
019 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
020 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
021 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
022 * IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY
023 * OF SUCH DAMAGE.
024 *
025 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
026 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
027 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
028 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY
029 * OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
030 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
031 */
032package org.kepler.monitor.figure;
033
034import java.awt.Color;
035import java.awt.event.ActionEvent;
036import java.awt.event.ActionListener;
037import java.awt.geom.RectangularShape;
038
039import javax.swing.Timer;
040
041import diva.canvas.toolbox.BasicFigure;
042
043/**
044 * A "progress bar" monitor figure.
045 * 
046 * This figure has some similar functionality as that of
047 * {@link javax.swing.JProgressBar}.
048 * 
049 * <p>
050 * By default, the progress bar will be indeterminate. In this case, an
051 * animation is set up to show a bouncing box within the given displayable area.
052 * 
053 * @see org.kepler.monitor.test.ActorWithProgressBar
054 * 
055 * @author Carlos Rueda
056 * @version $Id: ProgressBarFigure.java 32920 2014-08-26 00:46:48Z crawl $
057 */
058public class ProgressBarFigure extends BaseFigure {
059
060        /**
061         * Creates an horizontal and indeterminate progress bar figure.
062         * 
063         * @param shape
064         *            Rectangular shape for the progress bar.
065         */
066        public ProgressBarFigure(RectangularShape shape) {
067                super(shape);
068
069                _orientation = Orientation.HORIZONTAL;
070                _indeterminate = true;
071
072                // _figs[0]: background; _figs[1]: foreground
073
074                _figs = new BasicFigure[2];
075                _figs[0] = new BasicFigure(shape);
076                _figs[0].setFillPaint(_backgroundColor);
077                this.add(_figs[0]);
078
079                _figs[1] = new BasicFigure(shape);
080                _figs[1].setFillPaint(_foregroundColor);
081                this.add(_figs[1]);
082        }
083
084        /**
085         * Sets the orientation and calls {@link #update()} if the orientation
086         * changes.
087         * 
088         * @param orientation
089         *            the orientation.
090         */
091        @Override
092    public void setOrientation(Orientation orientation) {
093                Orientation old = this._orientation;
094                this._orientation = orientation;
095                if (old != orientation) {
096                        update();
097                }
098        }
099
100        /**
101         * Set the indeterminate flag of this progress bar figure.
102         * 
103         * @param indeterminate
104         *            true for indeterminate, false for determinate.
105         */
106        public void setIndeterminate(boolean indeterminate) {
107                boolean old = this._indeterminate;
108                this._indeterminate = indeterminate;
109                if (old != indeterminate) {
110                        if (_started) {
111                                if (_indeterminate) {
112                                        startTimer();
113                                } else {
114                                        stopTimer();
115                                }
116                        }
117                        update();
118                }
119        }
120
121        /**
122         * Sets the maximum value, which is meaningful only for a determinate
123         * progress bar.
124         * 
125         * @param n
126         *            the maximum value.
127         */
128        public void setMaximum(int n) {
129                _range.max = n;
130                if (!_indeterminate) {
131                        update();
132                }
133        }
134
135        /**
136         * Sets the minimum value, which is meaningful only for a determinate
137         * progress bar.
138         * 
139         * @param n
140         *            the minimum value.
141         */
142        public void setMinimum(int n) {
143                _range.min = n;
144                if (!_indeterminate) {
145                        update();
146                }
147        }
148
149        /**
150         * Sets the current value, which is meaningful only for a determinate
151         * progress bar.
152         * 
153         * @param n
154         *            the current value.
155         */
156        public void setValue(int n) {
157                int old = _range.cur;
158                _range.cur = n;
159                if (old != n && !_indeterminate) {
160                        update();
161                }
162        }
163
164        /**
165         * Tells if this progress bar is indeterminate.
166         * 
167         * @return true iff this progress bar is indeterminate.
168         */
169        public boolean isIndeterminate() {
170                return _indeterminate;
171        }
172
173        /**
174         * Gets the current value associated for a determinate progress bar.
175         * 
176         * @return current value.
177         */
178        public int getCurrentValue() {
179                return _range.cur;
180        }
181
182        /**
183         * Gets the maximum value in the range associated for a determinate progress
184         * bar.
185         * 
186         * @return maximum value in range.
187         */
188        public int getMaximum() {
189                return _range.max;
190        }
191
192        /**
193         * Gets the minimum value in the range associated for a determinate progress
194         * bar.
195         * 
196         * @return minimum value in range.
197         */
198        public int getMinimum() {
199                return _range.min;
200        }
201
202        /**
203         * @return the backgroundColor
204         */
205        public Color getBackgroundColor() {
206                return _backgroundColor;
207        }
208
209        /**
210         * @param backgroundColor
211         *            the backgroundColor to set
212         */
213        public void setBackgroundColor(Color backgroundColor) {
214                this._backgroundColor = backgroundColor;
215                _figs[0].setFillPaint(backgroundColor);
216                update();
217        }
218
219        /**
220         * @return the foregroundColor
221         */
222        public Color getForegroundColor() {
223                return _foregroundColor;
224        }
225
226        /**
227         * @param foregroundColor
228         *            the foregroundColor to set
229         */
230        public void setForegroundColor(Color foregroundColor) {
231                this._foregroundColor = foregroundColor;
232                _figs[1].setFillPaint(foregroundColor);
233                update();
234        }
235
236        /**
237         * Sets the rectangular shape for this figure.
238         * 
239         * @param shape
240         */
241        public void setShape(RectangularShape shape) {
242                this._shape = (RectangularShape) shape.clone();
243                _figs[0].setShape(this._shape);
244                update();
245        }
246
247        public void setStarted(boolean started) {
248                log.debug(Boolean.valueOf(started));
249                _started = started;
250                if (_started) {
251                        _figs[1].setFillPaint(_foregroundColor);
252                        if (_indeterminate) {
253                                startTimer();
254                        }
255                } else {
256                        stopTimer();
257                        setValue(getMinimum() - 1);
258                        update();
259                }
260        }
261
262        // /////////////////////////////////////////////////////////////////
263        // // protected members ////
264
265        /**
266         * Updates the appearance of this progress bar.
267         */
268        @Override
269    protected void _update() {
270                double x = _shape.getX();
271                double y = _shape.getY();
272                double h = _shape.getHeight();
273                double w = _shape.getWidth();
274
275                RectangularShape rect1 = (RectangularShape) _figs[1].getShape();
276
277                if (!_started) {
278                        // a trick to "only" show the background
279                        _figs[1].setFillPaint(_backgroundColor);
280                        rect1.setFrame(x, y, w, h);
281                        _figs[1].setShape(rect1);
282                        return;
283                }
284
285                if (_orientation == Orientation.HORIZONTAL) {
286                        if (_indeterminate) {
287                                double xx = x + _indet.shift * w;
288                                double ww = _indet.bbSize * w;
289                                if (xx < x) {
290                                        ww -= x - xx;
291                                        xx = x;
292                                } else if (xx + ww > x + w) {
293                                        ww = x + w - xx;
294                                }
295                                rect1.setFrame(xx, y, ww, h);
296                        } else {
297                                // always, left to right:
298                                double ww = _range.fraction() * w;
299                                rect1.setFrame(x, y, ww, h);
300                        }
301                } else {
302                        if (_indeterminate) {
303                                double yy = y + _indet.shift * h;
304                                double hh = _indet.bbSize * h;
305                                if (yy < y) {
306                                        hh -= y - yy;
307                                        yy = y;
308                                } else if (yy + hh > y + h) {
309                                        hh = y + h - yy;
310                                }
311                                rect1.setFrame(x, yy, w, hh);
312                        } else {
313                                // always, bottom to top:
314                                double hh = _range.fraction() * h;
315                                rect1.setFrame(x, y + h - hh, w, hh);
316                        }
317                }
318                _figs[0].setShape(_shape);
319                _figs[1].setShape(rect1);
320        }
321
322        // /////////////////////////////////////////////////////////////////
323        // // private members ////
324
325        /**
326         * Starts a timer for animating this indeterminate progress bar. Does
327         * nothing if <code>!this.isIndeterminate()</code>.
328         */
329        private void startTimer() {
330                if (_timer == null && _indeterminate) {
331                        _timer = new Timer(_indet.delay, new ActionListener() {
332                                @Override
333                public void actionPerformed(ActionEvent ev) {
334                                        _indet.update();
335                                        // we can call _update() directly because this runs on the
336                                        // event thread:
337                                        _lastUpdate = System.currentTimeMillis();
338                                        _update();
339                                }
340                        });
341                        _timer.start();
342                        if (isDebugging) {
343                                log.debug("Started timer");
344                        }
345                }
346        }
347
348        /**
349         * Stops the timer for animating this indeterminate progress bar. Does
350         * nothing if no such timer has been created.
351         */
352        private void stopTimer() {
353                if (_timer != null) {
354                        _timer.stop();
355                        _timer = null;
356                        if (isDebugging) {
357                                log.debug("Stopped timer");
358                        }
359                }
360        }
361
362        /** to update an indeterminate progress bar */
363        private Timer _timer;
364
365        private boolean _started;
366
367        private Color _backgroundColor = new Color(185, 185, 255);
368        private Color _foregroundColor = Color.CYAN;
369        private BasicFigure[] _figs;
370
371        private boolean _indeterminate;
372
373        /** Range in case of a determinate progress bar */
374        private Range _range = new Range(0, 100);
375
376        /** Info in case of an indeterminate progress bar */
377        private Indet _indet = new Indet();
378
379        /** Range info for a determinate progress bar. */
380        private final static class Range {
381                Range(int min, int max) {
382                        this.min = min;
383                        this.max = max;
384                        this.cur = min - 1;
385                }
386
387                double fraction() {
388                        return cur < min ? 0.0 : min >= max ? 1.0 : (double) (cur - min)
389                                        / (max - min);
390                }
391
392                int min;
393                int max;
394                int cur;
395        }
396
397        /** Info for an indeterminate progress bar. */
398        private final static class Indet {
399                /** To repaint */
400                final int delay = 55;
401
402                /** bouncing box size */
403                final double bbSize = 0.3;
404
405                /** increment to update shift; should be &lt; bbSize */
406                double delta = 0.1;
407
408                /** we allow the bouncing box to "hide" by at most this fraction */
409                final double hideSize = delta;
410
411                /** current position of bouncing box */
412                double shift = Math.random(); // -hideSize;
413
414                /** updates position of bouncing box */
415                void update() {
416                        shift += delta;
417                        if (shift < -hideSize) {
418                                shift = -hideSize;
419                                delta *= -1;
420                        } else if (shift > 1.0 - (bbSize - hideSize)) {
421                                shift = 1.0 - (bbSize - hideSize);
422                                delta *= -1;
423                        }
424                }
425        }
426
427}