001: /*
002: * <copyright>
003: *
004: * Copyright 1997-2004 BBNT Solutions, LLC
005: * under sponsorship of the Defense Advanced Research Projects
006: * Agency (DARPA).
007: *
008: * You can redistribute this software and/or modify it under the
009: * terms of the Cougaar Open Source License as published on the
010: * Cougaar Open Source Website (www.cougaar.org).
011: *
012: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
013: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
014: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
015: * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
016: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
017: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
018: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
019: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
020: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
021: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
022: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
023: *
024: * </copyright>
025: */
026: package org.cougaar.glm.execution.eg;
027:
028: import java.awt.Dimension;
029: import java.awt.Insets;
030: import java.awt.Color;
031: import java.awt.Stroke;
032: import java.awt.LayoutManager;
033: import java.awt.BasicStroke;
034: import java.awt.Rectangle;
035: import java.awt.Font;
036: import java.awt.Graphics;
037: import java.awt.Graphics2D;
038: import java.awt.FontMetrics;
039: import java.awt.event.ActionListener;
040: import java.awt.event.ActionEvent;
041: import javax.swing.JComponent;
042: import javax.swing.JButton;
043: import javax.swing.JPanel;
044: import javax.swing.SwingUtilities;
045: import java.util.Calendar;
046: import java.text.SimpleDateFormat;
047: import java.text.DateFormat;
048: import java.util.GregorianCalendar;
049: import java.util.TimeZone;
050: import java.util.Date;
051:
052: public class TimeGUI extends JPanel implements ActionListener, Runnable {
053: public interface Listener {
054: void timePlay();
055:
056: void timePause();
057:
058: void timeStop();
059: }
060:
061: public static final int STOP = 0;
062: public static final int PLAY = 1;
063: public static final int PAUSE = 2;
064:
065: private static final int NROWS = 6;
066: private static final int NCOLUMNS = 7;
067: private static final int COLUMN_WIDTH = 16;
068: private static final int ROW_HEIGHT = 12;
069: private static final int XM = 4;
070: private static final int YM = 4;
071: private static final int CALENDAR_WIDTH = NCOLUMNS * COLUMN_WIDTH;
072: private static final int CALENDAR_HEIGHT = NROWS * ROW_HEIGHT;
073: private static final int CLOCK_WIDTH = CALENDAR_HEIGHT;
074: private static final int CLOCK_HEIGHT = CALENDAR_HEIGHT;
075: private static final int TOTAL_WIDTH = CALENDAR_WIDTH + CLOCK_WIDTH
076: + 2 * XM;
077: private static final int BUTTON_Y = CALENDAR_HEIGHT + ROW_HEIGHT
078: + YM;
079: private static final int BUTTON_HEIGHT = 25;
080: private static final int BUTTON_GAP = 2;
081: private static final int TOTAL_HEIGHT = BUTTON_Y + BUTTON_HEIGHT
082: + YM;
083: private static final int HUB_RADIUS = CLOCK_WIDTH * 1 / 32;
084: private static final int HOUR_HAND_RADIUS = CLOCK_WIDTH * 8 / 32;
085: private static final int MINUTE_HAND_RADIUS = CLOCK_WIDTH * 11 / 32;
086: private static final int SECOND_HAND_RADIUS = CLOCK_WIDTH * 14 / 32;
087: private static final int TICK_LENGTH = 3;
088: private static final TimeZone timeZone = TimeZone
089: .getTimeZone("GMT");
090: private static double[] sinTable = new double[60];
091: private static double[] cosTable = new double[60];
092: static {
093: for (int i = 0; i < sinTable.length; i++) {
094: sinTable[i] = Math.sin(Math.PI * i / 30.0);
095: }
096: for (int i = 0; i < cosTable.length; i++) {
097: cosTable[i] = Math.cos(Math.PI * i / 30.0);
098: }
099: }
100: private GregorianCalendar calendar = new GregorianCalendar(timeZone);
101: private double theRate; // The current rate
102: private long lastNow; // The start of this timing segment
103: private long theOffset; // The offset
104: private Thread thread;
105:
106: private JButton playButton = new JButton(DrawnIcon
107: .getPlayIcon(true));
108: private JButton pauseButton = new JButton(DrawnIcon
109: .getPauseIcon(true));
110: private JButton stopButton = new JButton(DrawnIcon
111: .getStopIcon(true));
112: private int state = STOP;
113: private Listener listener;
114:
115: public TimeGUI(Listener l) {
116: super ((LayoutManager) null);
117: listener = l;
118: playButton = new JButton(DrawnIcon.getPlayIcon(true));
119: playButton.setDisabledIcon(DrawnIcon.getPlayIcon(false));
120:
121: pauseButton = new JButton("", DrawnIcon.getPauseIcon(true));
122: stopButton = new JButton("", DrawnIcon.getStopIcon(true));
123: pauseButton.setDisabledIcon(DrawnIcon.getPauseIcon(false));
124: stopButton.setDisabledIcon(DrawnIcon.getStopIcon(false));
125: disableControls();
126: JButton[] buttons = new JButton[] { playButton, pauseButton,
127: stopButton };
128: int buttonWidth = (buttons.length - 1) * BUTTON_GAP;
129: for (int i = 0; i < buttons.length; i++) {
130: JButton b = buttons[i];
131: buttonWidth += b.getPreferredSize().width;
132: }
133: int bx = (TOTAL_WIDTH - buttonWidth) / 2;
134: for (int i = 0; i < buttons.length; i++) {
135: JButton b = buttons[i];
136: // b.setMargin(new Insets(0, 0, 0, 0));
137: // b.setVerticalAlignment(JButton.CENTER);
138: // b.setHorizontalAlignment(JButton.CENTER);
139: b.addActionListener(this );
140: Dimension sz = b.getPreferredSize();
141: int by = BUTTON_Y + (BUTTON_HEIGHT - sz.height) / 2;
142: b.setSize(sz);
143: b.setLocation(bx, by);
144: add(b);
145: bx += sz.width + BUTTON_GAP;
146: }
147: clockFormat.setTimeZone(timeZone);
148: noSecondsClockFormat.setTimeZone(timeZone);
149: noMinutesClockFormat.setTimeZone(timeZone);
150: noHoursClockFormat.setTimeZone(timeZone);
151: }
152:
153: public void disableControls() {
154: playButton.setEnabled(false);
155: pauseButton.setEnabled(false);
156: stopButton.setEnabled(false);
157: state = STOP;
158: }
159:
160: public void enableControls() {
161: state = STOP;
162: playButton.setEnabled(true);
163: }
164:
165: public void actionPerformed(ActionEvent e) {
166: Object button = e.getSource();
167: if (button == playButton) {
168: if (state != PLAY) {
169: listener.timePlay();
170: }
171: return;
172: }
173: if (button == pauseButton) {
174: if (state == PLAY) {
175: listener.timePause();
176: } else if (state == PAUSE) {
177: listener.timePlay();
178: }
179: return;
180: }
181: if (button == stopButton) {
182: if (state != STOP) {
183: listener.timeStop();
184: }
185: return;
186: }
187: }
188:
189: public void play() {
190: state = PLAY;
191: playButton.setEnabled(false);
192: stopButton.setEnabled(true);
193: pauseButton.setEnabled(true);
194: }
195:
196: public void pause() {
197: if (state == PLAY) {
198: state = PAUSE;
199: playButton.setEnabled(true);
200: } else if (state == PAUSE) {
201: state = PLAY;
202: playButton.setEnabled(false);
203: }
204: }
205:
206: public void stop() {
207: if (state != STOP) {
208: state = STOP;
209: playButton.setEnabled(true);
210: pauseButton.setEnabled(false);
211: stopButton.setEnabled(false);
212: }
213: }
214:
215: public void setTime(long newTime) {
216: synchronized (calendar) {
217: int oldDay = calendar.get(Calendar.DAY_OF_YEAR);
218: int oldYear = calendar.get(Calendar.YEAR);
219: calendar.setTime(new Date(newTime));
220: int newDay = calendar.get(Calendar.DAY_OF_YEAR);
221: int newYear = calendar.get(Calendar.YEAR);
222: if (oldDay != newDay || oldYear != newYear) {
223: repaint(0, 0, TOTAL_WIDTH, TOTAL_HEIGHT);
224: } else {
225: repaint(CALENDAR_WIDTH, 0, CLOCK_WIDTH, CLOCK_HEIGHT);
226: }
227: }
228: repaint(100);
229: }
230:
231: public synchronized void setTime(long newTime, double newRate) {
232: lastNow = System.currentTimeMillis();
233: theRate = newRate;
234: theOffset = newTime;
235: // if (newRate == 0.0) {
236: // rateString = " (Paused)";
237: // } else if (newRate != 1.0) {
238: // rateString = " (Rate " + newRate + ")";
239: // } else {
240: // rateString = " (Running)";
241: // }
242: if (thread == null) {
243: thread = new Thread(this , "TimeGUI");
244: thread.start();
245: }
246: notify();
247: }
248:
249: public synchronized void run() {
250: while (true) {
251: long now = System.currentTimeMillis();
252: long executionTime = (long) ((now - lastNow) * theRate + theOffset);
253: setTime(executionTime);
254: long nextExecutionTime = executionTime + 1000; // One second later
255: long nextNow = (long) (lastNow + (nextExecutionTime - theOffset)
256: / theRate);
257: long delay = nextNow - now;
258: if (delay < 100)
259: delay = 100;
260: if (delay > 10000)
261: delay = 10000;
262: try {
263: wait(delay);
264: } catch (InterruptedException ie) {
265: }
266: }
267: }
268:
269: private Dimension size = new Dimension(TOTAL_WIDTH, TOTAL_HEIGHT);
270:
271: public Dimension getPreferredSize() {
272: return size;
273: }
274:
275: public Dimension getMinSize() {
276: return size;
277: }
278:
279: public Dimension getMaxSize() {
280: return size;
281: }
282:
283: private boolean noMonths;
284: private boolean noDays;
285: private boolean noAMPM;
286: private boolean noHours;
287: private boolean noMinutes;
288: private boolean noSeconds;
289: private int hour24;
290: private int hour;
291: private int minute;
292: private int second;
293: private int year;
294: private int month; // Zero-based month index
295: private int dayOfMonth;
296: private int daysInMonth;
297: private int dayOfWeekOfFirstDay;
298: private int minDayOfWeek;
299: private String digitalClock;
300: private DateFormat clockFormat = new SimpleDateFormat("HH:mm:ss");
301: private DateFormat noSecondsClockFormat = new SimpleDateFormat(
302: "HH:mm:00");
303: private DateFormat noMinutesClockFormat = new SimpleDateFormat(
304: "HH:00:00");
305: private DateFormat noHoursClockFormat = new SimpleDateFormat(
306: "00:00:00");
307: private GregorianCalendar tempCalendar = new GregorianCalendar(
308: timeZone);
309:
310: private Font font = null;
311: private FontMetrics fm;
312: private int baseline = 0;
313:
314: public void paintComponent(Graphics g) {
315: Rectangle bounds = g.getClipBounds();
316: boolean paintCalendar = bounds.x < XM + CALENDAR_WIDTH;
317: g.setColor(getBackground());
318: if (font == null) {
319: font = new Font("Monospaced", Font.PLAIN, 12);
320: fm = g.getFontMetrics(font);
321: baseline = fm.getAscent() - 1;
322: }
323: g.setFont(font);
324: g.fillRect(0, 0, TOTAL_WIDTH, TOTAL_HEIGHT);
325: synchronized (calendar) {
326: noSeconds = theRate > 120.0; // No more than 2 revs/sec
327: noMinutes = theRate > 60.0 * 120.0; // No more than 2 revs/sec
328: noHours = theRate > 12.0 * 60.0 * 120.0; // No more than 2 revs/sec
329: noAMPM = theRate > 2.0 * 12.0 * 60.0 * 120.0; // No more than 4 blinks/sec
330: noDays = theRate > 30.0 * 2.0 * 12.0 * 60.0 * 120.0; // No more than 60 days/sec
331: noMonths = theRate > 12.0 * 30.0 * 2.0 * 12.0 * 60.0
332: * 120.0; // No more than 24 months/sec
333: if (noHours) {
334: digitalClock = noHoursClockFormat.format(calendar
335: .getTime());
336: } else if (noMinutes) {
337: digitalClock = noMinutesClockFormat.format(calendar
338: .getTime());
339: } else if (noSeconds) {
340: digitalClock = noSecondsClockFormat.format(calendar
341: .getTime());
342: } else {
343: digitalClock = clockFormat.format(calendar.getTime());
344: }
345: hour = calendar.get(Calendar.HOUR);
346: hour24 = calendar.get(Calendar.HOUR_OF_DAY);
347: minute = calendar.get(Calendar.MINUTE);
348: second = calendar.get(Calendar.SECOND);
349: year = calendar.get(Calendar.YEAR);
350: int newMonth = calendar.get(Calendar.MONTH)
351: - calendar.getActualMinimum(Calendar.MONTH);
352: if (newMonth != month) {
353: month = newMonth;
354: tempCalendar.setTime(calendar.getTime());
355: tempCalendar.set(Calendar.DAY_OF_MONTH, 1);
356: dayOfWeekOfFirstDay = (tempCalendar
357: .get(Calendar.DAY_OF_WEEK) - tempCalendar
358: .getActualMinimum(Calendar.DAY_OF_WEEK));
359: minDayOfWeek = tempCalendar
360: .getActualMinimum(Calendar.DAY_OF_WEEK);
361: }
362: dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
363: daysInMonth = calendar
364: .getActualMaximum(Calendar.DAY_OF_MONTH);
365: if (paintCalendar)
366: drawCalendar(g);
367: if (!noAMPM)
368: drawClock(g);
369: }
370: }
371:
372: private static final String[] MONTH_NAMES = { "January",
373: "February", "March", "April", "May", "June", "July",
374: "August", "September", "October", "November", "December", };
375:
376: private void drawCalendar(Graphics g) {
377: g.setColor(Color.black);
378: String title;
379: if (noMonths) {
380: title = Integer.toString(year);
381: } else {
382: title = MONTH_NAMES[month] + " " + year;
383: }
384: int tx = XM + (CALENDAR_WIDTH - fm.stringWidth(title)) / 2;
385: g.drawString(title, tx, baseline);
386: if (!noDays) {
387: for (int day = 1; day <= daysInMonth; day++) {
388: int ix = dayOfWeekOfFirstDay + day - 1;
389: int row = ix / 7;
390: int col = ix % 7;
391: int x = XM + COLUMN_WIDTH * col;
392: int y = YM + ROW_HEIGHT + ROW_HEIGHT * row;
393: String dayString = " ";
394: if (day < 10) {
395: dayString = " " + day;
396: } else {
397: dayString = "" + day;
398: }
399: if (day == dayOfMonth) {
400: g.fillRect(x, y + 2, COLUMN_WIDTH, ROW_HEIGHT);
401: g.setColor(Color.white);
402: g.drawString(dayString, x, y + baseline);
403: g.setColor(Color.black);
404: } else {
405: g.drawString(dayString, x, y + baseline);
406: }
407: }
408: }
409: }
410:
411: private Stroke stroke = new BasicStroke(2.0f);
412:
413: private void drawHand(Graphics g, int xc, int yc, int radius, int m) {
414: Graphics2D g2d = (Graphics2D) g;
415: int x = (int) (xc + radius * sinTable[m]);
416: int y = (int) (yc - radius * cosTable[m]);
417: g.drawLine(xc, yc, x, y);
418: }
419:
420: private void drawClock(Graphics g) {
421: Graphics2D g2d = (Graphics2D) g;
422: // g2d.setStroke(stroke);
423: g.setColor(Color.black);
424: int xc = XM + CALENDAR_WIDTH + CLOCK_WIDTH / 2;
425: int yc = YM + ROW_HEIGHT + CLOCK_HEIGHT / 2;
426: if (!noHours) {
427: g.drawString(digitalClock, xc
428: - fm.stringWidth(digitalClock) / 2, baseline);
429: }
430: g.drawOval(xc - SECOND_HAND_RADIUS, yc - SECOND_HAND_RADIUS,
431: 2 * SECOND_HAND_RADIUS, 2 * SECOND_HAND_RADIUS);
432: if (hour24 < 6 || hour24 >= 18) {
433: g.fillOval(xc - SECOND_HAND_RADIUS,
434: yc - SECOND_HAND_RADIUS, 2 * SECOND_HAND_RADIUS,
435: 2 * SECOND_HAND_RADIUS);
436: g.setColor(Color.white);
437: }
438: g.fillOval(xc - HUB_RADIUS, yc - HUB_RADIUS,
439: 2 * HUB_RADIUS + 1, 2 * HUB_RADIUS + 1);
440: g.drawLine(xc, yc - SECOND_HAND_RADIUS, xc, yc
441: - SECOND_HAND_RADIUS + TICK_LENGTH);
442: g.drawLine(xc, yc + SECOND_HAND_RADIUS, xc, yc
443: + SECOND_HAND_RADIUS - TICK_LENGTH);
444: g.drawLine(xc + SECOND_HAND_RADIUS, yc, xc + SECOND_HAND_RADIUS
445: - TICK_LENGTH, yc);
446: g.drawLine(xc - SECOND_HAND_RADIUS, yc, xc - SECOND_HAND_RADIUS
447: + TICK_LENGTH, yc);
448: if (!noHours) {
449: drawHand(g, xc, yc, HOUR_HAND_RADIUS,
450: (hour * 60 + minute) / 12);
451: }
452: if (!noMinutes) {
453: drawHand(g, xc, yc, MINUTE_HAND_RADIUS, minute);
454: }
455: if (!noSeconds) {
456: drawHand(g, xc, yc, SECOND_HAND_RADIUS, second);
457: }
458: }
459: }
|