2 * Copyright 2003 Sun Microsystems, Inc.
4 * See the file "license.terms" for information on usage and
5 * redistribution of this file, and for a DISCLAIMER OF ALL
8 import java.awt.BorderLayout;
9 import java.awt.FlowLayout;
12 import java.awt.event.ActionEvent;
13 import java.awt.event.ActionListener;
14 import java.awt.event.ItemEvent;
15 import java.awt.event.ItemListener;
17 import java.text.SimpleDateFormat;
19 import java.util.Calendar;
20 import java.util.Date;
21 import java.util.GregorianCalendar;
23 import javax.swing.JButton;
24 import javax.swing.JCheckBox;
25 import javax.swing.JFrame;
26 import javax.swing.JLabel;
27 import javax.swing.JPanel;
28 import javax.swing.JTextField;
29 import javax.swing.SwingUtilities;
33 * A talking clock powered by FreeTTS.
35 public abstract class Clock extends JFrame {
37 private JLabel timeLabel;
38 private JCheckBox announceCheckBox;
39 private JTextField intervalTextField;
40 private int timeFontSize = 24;
42 private GregorianCalendar calendar;
43 private SimpleDateFormat dateFormat;
45 private long lastSpeakTime; // in milliseconds
46 private int speakInterval = 300000; // in milliseconds
47 private int sleepTime = 5000; // in milliseconds
49 private static char announceMnemonic = 'A';
50 private static char minutesMnemonic = 'M';
51 private static char speakMnemonic = 'S';
53 private boolean debug = true;
57 * Constructs a default WebStartClock.
60 super("FreeTTS Clock");
61 setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
63 JPanel timePanel = new JPanel(new BorderLayout());
64 timePanel.setAlignmentY(JPanel.CENTER_ALIGNMENT);
66 timeLabel = new JLabel("Loading...", JLabel.CENTER);
67 Font oldFont = timeLabel.getFont();
68 timeLabel.setFont(new Font(oldFont.getFontName(), oldFont.getStyle(),
71 timePanel.add(timeLabel, BorderLayout.CENTER);
72 timePanel.add(createAnnouncePanel(), BorderLayout.SOUTH);
74 getContentPane().add(timePanel, BorderLayout.CENTER);
76 JButton speakButton = new JButton("Speak");
77 speakButton.setMnemonic(speakMnemonic);
79 speakButton.addActionListener(new ActionListener() {
80 public void actionPerformed(ActionEvent ae) {
81 Runnable speaker = new Runnable() {
86 (new Thread(speaker)).start();
90 getContentPane().add(speakButton, BorderLayout.SOUTH);
97 * Start running the clock.
99 public void startClock() {
100 ClockThread clock = new ClockThread();
106 * Creates the JPanel that allows you to specify the time announcing
111 private JPanel createAnnouncePanel() {
112 JPanel announcePanel = new JPanel(new FlowLayout());
113 announceCheckBox = new JCheckBox("announce every", true);
114 announceCheckBox.setMnemonic(announceMnemonic);
115 announceCheckBox.addItemListener(new ItemListener() {
116 public void itemStateChanged(ItemEvent ie) {
117 if (ie.getStateChange() == ItemEvent.SELECTED) {
118 lastSpeakTime = calendar.getTimeInMillis();
120 ("Last speak time: " + String.valueOf(lastSpeakTime));
125 // a text field to enter the time announcing interval
126 intervalTextField = new JTextField("5", 3);
127 intervalTextField.addActionListener(new ActionListener() {
128 public void actionPerformed(ActionEvent e) {
129 String text = intervalTextField.getText();
130 if (text.matches("[1-9][0-9]*")) {
131 debugPrintln("New announce interval : " + text);
132 speakInterval = Integer.parseInt(text) * 60000;
134 debugPrintln("Invalid minutes input: " + text);
135 intervalTextField.setText
136 (String.valueOf(speakInterval / 60000));
141 JLabel minutesLabel = new JLabel("mins");
142 minutesLabel.setDisplayedMnemonic(minutesMnemonic);
143 minutesLabel.setLabelFor(intervalTextField);
145 announcePanel.add(announceCheckBox);
146 announcePanel.add(intervalTextField);
147 announcePanel.add(minutesLabel);
148 return announcePanel;
153 * Creates the synthesizer, called by the constructor.
154 * Implement this method to create the appropriate synthesizer.
155 * The created synthesizer will be used by the <code>speak()</code>
156 * method, also to be implemented in the subclass.
158 public abstract void createSynthesizer();
162 * Asks the synthesizer to speak the time given in full text.
163 * Implement this method to call the appropriate method of the
164 * created synthesizer to speak the given time.
166 * @param time the time given in full text
168 protected abstract void speak(String time);
172 * Create the GregorianCalendar that keeps track of the time.
174 private void createCalendar() {
175 calendar = new GregorianCalendar();
177 // sets the format to display the current time
178 // the format is "3:50 PM"
179 dateFormat = new SimpleDateFormat("h:mm a");
180 dateFormat.setCalendar(calendar);
185 * Sets the time label.
187 * @time time the time to set
189 private void setTimeLabel(final String time) {
190 SwingUtilities.invokeLater(new Runnable() {
192 timeLabel.setText(time);
199 * Updates the calendar and the display with the current time.
201 private void updateTime() {
202 Date currentTime = new Date();
203 calendar.setTime(currentTime);
204 setTimeLabel(dateFormat.format(currentTime));
209 * Speaks the current time.
211 private void speakTime() {
212 lastSpeakTime = calendar.getTimeInMillis();
213 int hour = calendar.get(Calendar.HOUR_OF_DAY);
214 int min = calendar.get(Calendar.MINUTE);
216 if (hour < 0 || hour > 23) {
217 throw new IllegalArgumentException("Bad time format: hour");
219 if (min < 0 || min > 59) {
220 throw new IllegalArgumentException("Bad time format: min");
223 String theTime = TimeUtils.timeToString(hour, min);
229 * Return true if we enough time has elapsed since the last announce
232 * @return true its time to speak, false otherwise
234 private boolean isTimeToSpeak() {
235 return ((lastSpeakTime + speakInterval) < calendar.getTimeInMillis());
240 * Print method for debug purposes.
242 * @param the debug message to print
244 private void debugPrintln(String line) {
246 System.out.println(line);
252 * A thread for the clock.
254 class ClockThread extends Thread {
259 if (announceCheckBox.isSelected() && isTimeToSpeak()) {
264 Thread.sleep(sleepTime);
265 } catch (InterruptedException ie) {
266 ie.printStackTrace();