upstream version 1.2.2
[debian/freetts] / demo / JSAPI / Player / PlayerPanel.java
1 /**
2  * Copyright 2001 Sun Microsystems, Inc.
3  * 
4  * See the file "license.terms" for information on usage and
5  * redistribution of this file, and for a DISCLAIMER OF ALL 
6  * WARRANTIES.
7  */
8 import java.awt.Color;
9 import java.awt.Component;
10 import java.awt.Container;
11 import java.awt.Cursor;
12 import java.awt.BorderLayout;
13 import java.awt.Dimension;
14 import java.awt.FlowLayout;
15 import java.awt.GridLayout;
16 import java.awt.GridBagLayout;
17 import java.awt.GridBagConstraints;
18 import java.awt.Insets;
19 import java.awt.event.ActionListener;
20 import java.awt.event.ActionEvent;
21 import java.awt.event.WindowAdapter;
22 import java.awt.event.WindowEvent;
23
24 import javax.swing.BorderFactory;
25 import javax.swing.ComboBoxModel;
26 import javax.swing.ListModel;
27 import javax.swing.ListSelectionModel;
28 import javax.swing.JButton;
29 import javax.swing.JComboBox;
30 import javax.swing.JComponent;
31 import javax.swing.JFrame;
32 import javax.swing.JLabel;
33 import javax.swing.JList;
34 import javax.swing.JPanel;
35 import javax.swing.JTextArea;
36 import javax.swing.JToggleButton;
37 import javax.swing.JScrollPane;
38 import javax.swing.JSlider;
39 import javax.swing.SwingUtilities;
40 import javax.swing.UIManager;
41 import javax.swing.UnsupportedLookAndFeelException;
42
43 import javax.swing.border.Border;
44 import javax.swing.border.EtchedBorder;
45 import javax.swing.border.TitledBorder;
46
47 import javax.swing.event.ChangeListener;
48 import javax.swing.event.ChangeEvent;
49
50 import javax.swing.plaf.basic.BasicArrowButton;
51 import javax.swing.plaf.metal.MetalLookAndFeel;
52
53 /**
54  * Defines and contains all the user-interface Swing objects of the Player.
55  * In terms of the model-view-controller (MVC) architecture, this implements
56  * the "view" and "control" elements. Its interacts with the PlayerModel 
57  * class, the "model" element.
58  */
59 public class PlayerPanel extends JPanel {
60
61     private PlayerModel playerModel;
62     
63     private int width = 600;
64     private int height = 450;
65     private int border = 30;
66
67     private Color backgroundColor = Color.LIGHT_GRAY;
68     private Color foregroundColor = Color.BLACK;
69     private Color controlColor = new Color(250, 250, 250);
70                             
71     private JComboBox synthesizerComboBox;
72     private JComboBox waveSynthesisComboBox;
73     private JComboBox voiceComboBox;
74         
75     private JTextArea speakingTextArea;
76     private JList speakablesList;
77     private int speakablesListVisibleRows = 5;
78     
79     private JToggleButton pauseButton;
80     private JButton playButton;
81     private JButton cancelButton;
82     private JButton stopButton;
83     private JButton deleteButton;
84     
85     private int initialVolume = 10;
86     private JSlider volumeSlider;
87     private JSlider speedSlider;
88     private JSlider pitchSlider;
89     private JSlider rangeSlider;
90     
91     private JButton fileButton;
92     private JTextArea textArea;
93     private int textAreaRows = 2;
94     private int textAreaColumns = 20;
95     private JButton clearTextButton;
96     private JButton speakTextButton;
97     private JButton speakJSMLButton;
98
99     private static char cancelMnemonic = 'A';
100     private static char clearMnemonic = 'C';
101     private static char deleteMnemonic = 'D';
102     private static char pauseMnemonic = 'U';
103     private static char pitchMnemonic = 'H';
104     private static char playMnemonic = 'P';
105     private static char playListMnemonic = 'L';
106     private static char rangeMnemonic = 'R';
107     private static char resumeMnemonic = 'E';
108     private static char stopMnemonic = 'T';
109     private static char speakMnemonic = 'S';
110     private static char speakJSMLMnemonic = 'J';
111     private static char synthesizerMnemonic = 'Y';
112     private static char textMnemonic = 'X';
113     private static char voiceMnemonic = 'O';
114     private static char volumeMnemonic = 'V';
115     private static char wordsPerMinMnemonic = 'W';
116     
117     
118     /**
119      * Constructs a PlayerPanel that interacts with the given PlayerModel.
120      *
121      * @param playerModel the PlayerModel that this PlayerPanel interacts
122      *   with.
123      */    
124     public PlayerPanel(PlayerModel playerModel) {
125
126         this.playerModel = playerModel;
127         
128         setSize(width, height);
129         setAlignmentY((float) 0.5);
130         setAlignmentX((float) 0.5);
131         
132         setLayout(new BorderLayout());
133         add(createMainPanel(), BorderLayout.NORTH);
134         add(createTextPanel(), BorderLayout.CENTER);    
135     }
136
137
138     /**
139      * Creates the main JPanel that is the upper JPanel of the
140      * user interface. It contains:
141      * <ol>
142      * <li> (on the left) volume and speaking rate control slides
143      * <li> (center) the synthesizer/voice selection combo boxes,
144      * the play list, and the buttons
145      * <li> (on the right) pitch and range control slides
146      * </ol>
147      *
148      * @return the upper JPanel of the application with all the controls
149      *    and play list 
150      */
151     private JPanel createMainPanel() {
152         JPanel centerPanel = new JPanel();
153
154         centerPanel.setLayout(new BorderLayout());
155         centerPanel.add(createLeftSliderPanel(), BorderLayout.WEST);
156         centerPanel.add(createSpeakablesPanel(), BorderLayout.CENTER);
157         centerPanel.add(createRightSliderPanel(), BorderLayout.EAST);
158         
159         return centerPanel;
160     }
161
162
163     /**
164      * Creates the TitledBordered JPanel and the play list it contains.
165      * This Panel is at the center of the Main Panel. The play list is
166      * contained within a JScrollPane.
167      *
168      * @return a JPanel that containts the play list
169      */
170     private JPanel createSpeakablesPanel() {
171         
172         ListModel playList = playerModel.getPlayList();
173         speakablesList = new JList(playList);
174         speakablesList.setVisibleRowCount(speakablesListVisibleRows);
175         speakablesList.setSelectionMode
176             (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
177         speakablesList.setSelectedIndex(0);
178         speakablesList.setDragEnabled(true);
179         
180         JLabel listTitle = new JLabel("Play List");
181         listTitle.setDisplayedMnemonic(playListMnemonic);
182         listTitle.setLabelFor(speakablesList);
183         
184         JScrollPane scrollPane = new JScrollPane(speakablesList);
185         scrollPane.add(listTitle);
186
187         JPanel centerPanel = new JPanel(new BorderLayout());
188         centerPanel.add(listTitle, BorderLayout.NORTH);
189         centerPanel.add(scrollPane, BorderLayout.CENTER);
190         centerPanel.add(createControlsPanel(), BorderLayout.SOUTH);
191                         
192         TitledBorder titledBorder = new TitledBorder("");
193         titledBorder.setTitleColor(foregroundColor);
194         titledBorder.setTitleJustification(TitledBorder.CENTER);
195         titledBorder.setBorder(new EtchedBorder(Color.WHITE, Color.BLACK));
196         centerPanel.setBorder(titledBorder);
197                         
198         JPanel speakablesPanel = new JPanel(new BorderLayout());
199         speakablesPanel.add(createSettingsPanel(), BorderLayout.NORTH);
200         speakablesPanel.add(centerPanel, BorderLayout.CENTER);
201         
202         return speakablesPanel;
203     }
204
205
206     /**
207      * Creates the settings JPanel and the synthesizer and voice
208      * JComboBoxes it contains. This JPanel is inside the speakables Panel.
209      *
210      * @return the JPanel of synthesizer and voice JComboBoxes
211      */
212     private JPanel createSettingsPanel() {
213         synthesizerComboBox = createComboBox
214             ((ComboBoxModel) playerModel.getSynthesizerList(),
215              "Synthesizer", "FreeTTS Synthesizer");
216                         
217         voiceComboBox = createComboBox
218             ((ComboBoxModel) playerModel.getVoiceList(),
219              "Voice", "Voice");
220         
221         JLabel synthesizerLabel = new JLabel("Synthesizer:");
222         synthesizerLabel.setDisplayedMnemonic(synthesizerMnemonic);
223         synthesizerLabel.setLabelFor(synthesizerComboBox);
224
225         JLabel voiceLabel = new JLabel("Voice:");
226         voiceLabel.setDisplayedMnemonic(voiceMnemonic);
227         voiceLabel.setLabelFor(voiceComboBox);
228         
229         JPanel leftPanel = new JPanel(new BorderLayout());
230         leftPanel.add(synthesizerLabel, BorderLayout.NORTH);
231         leftPanel.add(synthesizerComboBox, BorderLayout.CENTER);
232
233         JPanel rightPanel = new JPanel(new BorderLayout());
234         rightPanel.add(voiceLabel, BorderLayout.NORTH);
235         rightPanel.add(voiceComboBox, BorderLayout.CENTER);
236
237         JPanel settingsPanel = new JPanel();
238         
239         FlowLayout flowLayout = new FlowLayout();
240         flowLayout.setAlignment(FlowLayout.CENTER);
241         settingsPanel.setLayout(flowLayout);
242         settingsPanel.add(leftPanel);
243         settingsPanel.add(rightPanel);
244                 
245         addComboBoxListeners();
246                 
247         return settingsPanel;
248     }
249
250
251     /**
252      * Creates a non-editable ComboBox with the given attributes.
253      *
254      * @param model the ComboBoxModel this ComboBox is based on
255      * @param toolTipText the tooltip text
256      * @param prototypeDisplayValue the String used to calculate the
257      *    width of the ComboBox
258      */
259     public JComboBox createComboBox(ComboBoxModel model, String toolTipText,
260                                     String prototypeDisplayValue) {
261         JComboBox comboBox = new JComboBox(model);
262         comboBox.setToolTipText(toolTipText);
263         comboBox.setPrototypeDisplayValue(prototypeDisplayValue);
264         comboBox.setEditable(false);
265         return comboBox;
266     }
267
268
269     /**
270      * Adds listeners for the synthesizer and voices JComboBoxes
271      */
272     private void addComboBoxListeners() {
273         synthesizerComboBox.addActionListener(new ActionListener() 
274             {
275                 public void actionPerformed(ActionEvent e) {
276                     int selectedIndex = synthesizerComboBox.getSelectedIndex();
277                     Monitor monitor = playerModel.getMonitor();
278                     if (monitor != playerModel.getMonitor(selectedIndex)) {
279                         if (monitor != null) {
280                             monitor.setVisible(false);
281                         }
282                         if (playerModel.isMonitorVisible()) {
283                             monitor = playerModel.getMonitor(selectedIndex);
284                             monitor.setVisible(true);
285                             add(monitor, BorderLayout.SOUTH);
286                         }
287                         playerModel.setSynthesizer(selectedIndex);
288                     }
289                 }
290             });
291         voiceComboBox.addActionListener(new ActionListener() {
292                 public void actionPerformed(ActionEvent e) {
293                     Cursor oldCursor = getCursor();
294                     setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
295                     playerModel.setVoice(voiceComboBox.getSelectedIndex());
296                     setCursor(oldCursor);
297                     updateSliders();
298                 }
299             });
300     }
301
302
303     /**
304      * Creates the JPanel and the buttons it contains.
305      *
306      * @return a JPanel that contains the buttons. 
307      */
308     private JPanel createControlsPanel() {              
309
310         // create the buttons
311         playButton = createJButton("Play", playMnemonic);
312         cancelButton = createJButton("Cancel", cancelMnemonic);
313         stopButton = createJButton("Stop", stopMnemonic);
314
315         pauseButton = new JToggleButton("Pause");
316         pauseButton.setToolTipText("Pause");
317         pauseButton.setMnemonic(pauseMnemonic);
318         setControlColors(pauseButton);
319
320         JPanel buttonsPanel = new JPanel();
321         buttonsPanel.add(pauseButton);
322         buttonsPanel.add(playButton);
323         buttonsPanel.add(cancelButton);
324         buttonsPanel.add(stopButton);
325                 
326         JPanel controlsPanel = new JPanel(new BorderLayout());
327         controlsPanel.add(buttonsPanel, BorderLayout.CENTER);
328         
329         addControlsPanelActionListeners();
330
331         return controlsPanel;
332     }
333
334
335     /**
336      * Creates a JButton with the given label, which is also the tooltip
337      * text, and the given mnemonic.
338      *
339      * @param label the button label, which is also the tooltip text
340      * @param mnemonic mnemonic for the button
341      *
342      * @return a JButton
343      */
344     private JButton createJButton(String label, int mnemonic) {
345         JButton button = new JButton(label);
346         button.setToolTipText(label);
347         button.setMnemonic(mnemonic);
348         setControlColors(button);
349         return button;
350     }
351
352
353
354     /**
355      * Creates the JPanel where the volume and speaking rate JSliders are.
356      */
357     private JPanel createLeftSliderPanel() {
358         // create the sliders
359         playerModel.setVolume(initialVolume);
360
361         volumeSlider = new JSlider(JSlider.VERTICAL, 0, 10, initialVolume);
362         int speakingRate = (int) playerModel.getSpeakingRate();
363         if (speakingRate == -1) {
364             speakingRate = 0;
365         }
366         speedSlider = new JSlider(JSlider.VERTICAL, 0, 400, 0);
367                 
368         JPanel volumePanel = createSliderPanel
369             (volumeSlider, "Volume Control", 1, 5,
370              "Volume", volumeMnemonic);
371         JPanel speedPanel = createSliderPanel
372             (speedSlider, "Speed Control", 50, 100,
373              "Words/min", wordsPerMinMnemonic);
374                 
375         JPanel sliderPanel = new JPanel(new FlowLayout());
376         sliderPanel.add(volumePanel);
377         sliderPanel.add(speedPanel);
378         addLeftSliderPanelListeners();
379         
380         return sliderPanel;
381     }
382
383
384     /**
385      * Creates the JPanel where the pitch and range JSliders are.
386      */
387     private JPanel createRightSliderPanel() {
388         // create the sliders
389         pitchSlider = new JSlider(JSlider.VERTICAL, 50, 200, 50);
390         rangeSlider = new JSlider(JSlider.VERTICAL, 0, 50, 0);
391
392         JPanel pitchPanel = createSliderPanel
393             (pitchSlider, "Pitch Control", 25, 50, "Pitch/Hz", pitchMnemonic);
394         JPanel rangePanel = createSliderPanel
395             (rangeSlider, "Range Control", 5, 10, "Range", rangeMnemonic);
396         
397         JPanel sliderPanel = new JPanel();
398         sliderPanel.setLayout(new FlowLayout());
399         sliderPanel.add(pitchPanel);
400         sliderPanel.add(rangePanel);
401         
402         addRightSliderPanelListeners();
403         
404         return sliderPanel;
405     }
406
407
408     /**
409      * Adds ActionListeners for the JSliders.
410      */
411     private void addLeftSliderPanelListeners() {
412         volumeSlider.addChangeListener(new ChangeListener() {
413                 public void stateChanged(ChangeEvent ce) {
414                     if (playerModel.setVolume
415                         ((float) volumeSlider.getValue()) == false) {
416                         volumeSlider.setValue((int) playerModel.getVolume());
417                     }
418                 }
419             });
420         speedSlider.addChangeListener(new ChangeListener() {
421                 public void stateChanged(ChangeEvent ce) {
422                     if (playerModel.setSpeakingRate
423                         ((float) speedSlider.getValue()) == false) {
424                         speedSlider.setValue((int) playerModel.getSpeakingRate());
425                     }
426                 }
427             });
428     }
429
430
431     /**
432      * Adds ActionListeners for the JSliders.
433      */
434     private void addRightSliderPanelListeners() {
435         pitchSlider.addChangeListener(new ChangeListener() {
436                 public void stateChanged(ChangeEvent e) {
437                     if (playerModel.setPitch
438                         ((float) pitchSlider.getValue()) == false) {
439                         pitchSlider.setValue((int) playerModel.getPitch());
440                     }
441                 }
442             });
443         rangeSlider.addChangeListener(new ChangeListener() {
444                 public void stateChanged(ChangeEvent e) {
445                     if (playerModel.setRange
446                         ((float) rangeSlider.getValue()) == false) {
447                         rangeSlider.setValue((int) playerModel.getRange());
448                     }
449                 }
450             });
451     }
452
453
454     /**
455      * Updates all the Sliders with values from the PlayerModel.
456      */
457     private void updateSliders() {
458         int volume = (int) playerModel.getVolume();
459         if (volume > -1) {
460             volumeSlider.setValue(volume);
461         }
462         int rate = (int) playerModel.getSpeakingRate();
463         if (rate > -1) {
464             speedSlider.setValue(rate);
465         }
466         int pitch = (int) playerModel.getPitch();
467         if (pitch > -1) {
468             pitchSlider.setValue(pitch);
469         }
470         int range = (int) playerModel.getRange();
471         if (range > -1) {
472             rangeSlider.setValue(range);
473         }
474     }
475     
476
477     /**
478      * Creates a JPanel that contains the given JSlider, with the
479      * given attributes.
480      *
481      * @param slider the JSlider
482      * @param toolTipText the text for the tooltip
483      * @param minorTickSpacing the spacing between minor ticks
484      * @param majorTickSpacing the spacing between major ticks
485      * @param title the title of the JSlider
486      */
487     private JPanel createSliderPanel(JSlider slider, String toolTipText,
488                                      int minorTickSpacing,
489                                      int majorTickSpacing,
490                                      String title, char mnemonic) {
491         JPanel sliderPanel = new JPanel(new BorderLayout());
492                 
493         slider.setSize(getSize().width/2 - border,
494                        slider.getSize().height);
495         
496         slider.putClientProperty("JSlider.isFilled", Boolean.TRUE);
497         slider.setMinorTickSpacing(minorTickSpacing);
498         slider.setMajorTickSpacing(majorTickSpacing);
499         slider.setPaintTicks(true);
500         slider.setPaintLabels(true);
501         slider.setToolTipText(toolTipText);
502         
503         JLabel leftLabel = new JLabel(title);
504         leftLabel.setForeground(foregroundColor);
505         leftLabel.setDisplayedMnemonic(mnemonic);
506         leftLabel.setLabelFor(slider);
507         
508         sliderPanel.add(leftLabel, BorderLayout.NORTH);
509         sliderPanel.add(slider, BorderLayout.CENTER);
510                 
511         return sliderPanel;
512     }    
513
514
515     /**
516      * Adds ActionListeners to all the buttons
517      */
518     private void addControlsPanelActionListeners() {
519         playButton.addActionListener(new ActionListener() {
520                 public void actionPerformed(ActionEvent e) {
521                     int[] selectedIndices =
522                         speakablesList.getSelectedIndices();
523                     for (int i = 0; i < selectedIndices.length; i++) {
524                         if (selectedIndices[i] != -1) {
525                             playerModel.play(selectedIndices[i]);
526                         }
527                     }
528                 }
529             });
530         pauseButton.addActionListener(new ActionListener() {
531                 public void actionPerformed(ActionEvent e) {
532                     if (playerModel.isPaused()) {
533                         playerModel.resume();
534                     } else {
535                         playerModel.pause();
536                     }
537                 }
538             });
539         cancelButton.addActionListener(new ActionListener() {
540                 public void actionPerformed(ActionEvent e) {
541                     playerModel.cancel();
542                 }
543             });
544         stopButton.addActionListener(new ActionListener() {
545                 public void actionPerformed(ActionEvent e) {
546                     playerModel.stop();
547                     pauseButton.setEnabled(true);
548                 }
549             });
550     }
551
552
553     /**
554      * Creates the text JPanel is where the user enters text to be read.
555      *
556      * @return the JPanel with the textArea for text input
557      */
558     private JPanel createTextPanel() {
559         textArea = new JTextArea();
560         textArea.requestFocusInWindow();
561         textArea.setLineWrap(true);
562         textArea.setWrapStyleWord(true);
563
564         JScrollPane textScrollPane = new JScrollPane(textArea);
565
566         speakTextButton = createJButton("Speak Text", speakMnemonic);
567         speakJSMLButton = createJButton("Speak JSML", speakJSMLMnemonic);
568         clearTextButton = createJButton("Clear", clearMnemonic);
569                                         
570         BorderLayout borderLayout = new BorderLayout();
571         JPanel textPanel = new JPanel(borderLayout);
572         textPanel.setSize(width - border * 2, textPanel.getSize().height);
573
574         JPanel buttonsPanel = new JPanel();
575         buttonsPanel.add(speakTextButton);
576         buttonsPanel.add(speakJSMLButton);
577         buttonsPanel.add(clearTextButton);
578         
579         TitledBorder titledBorder = new TitledBorder("Enter text:");
580         JLabel titleLabel = new JLabel("Enter text:");
581         titleLabel.setDisplayedMnemonic(textMnemonic);
582         titleLabel.setLabelFor(textArea);
583                 
584         EtchedBorder border = new EtchedBorder(Color.WHITE, Color.BLACK);
585         textPanel.setBorder(border);
586         textPanel.add(titleLabel, BorderLayout.NORTH);
587         textPanel.add(textScrollPane, BorderLayout.CENTER);
588         textPanel.add(buttonsPanel, BorderLayout.SOUTH);
589
590         addTextPanelActionListeners();
591                 
592         return textPanel;
593     }
594
595
596     /**
597      * Adds ActionListeners to the buttons on the text panel.
598      */
599     private void addTextPanelActionListeners() {
600         clearTextButton.addActionListener(new ActionListener() {
601                 public void actionPerformed(ActionEvent e) {
602                     textArea.setText("");
603                 }
604             });
605         speakTextButton.addActionListener(new ActionListener() {
606                 public void actionPerformed(ActionEvent e) {
607                     String inputText = textArea.getText();
608                     if (inputText.length() > 0) {
609                         Playable textPlayable =
610                             Playable.createTextPlayable(inputText);
611                         playerModel.addPlayable(textPlayable);
612                         speakablesList.setSelectedValue(textPlayable, true);
613                         playerModel.play(textPlayable);
614                     }
615                 }
616             });
617         speakJSMLButton.addActionListener(new ActionListener() {
618                 public void actionPerformed(ActionEvent e) {
619                     String inputText = textArea.getText();
620                     if (inputText.length() > 0) {
621                         Playable jsmlPlayable =
622                             Playable.createJSMLPlayable(inputText);
623                         playerModel.addPlayable(jsmlPlayable);
624                         speakablesList.setSelectedValue(jsmlPlayable, true);
625                         playerModel.play(jsmlPlayable);
626                     }
627                 }
628             });
629     }
630
631
632     /**
633      * Sets the given control JComponent to the application defined color.
634      *
635      * @param component the JComponent to set color
636      */
637     private void setControlColors(JComponent component) {
638         component.setBackground(controlColor);
639     }
640
641
642     /**
643      * Returns the Playables JList.
644      *
645      * @return the Playables JList
646      */
647     public JList getPlayList() {
648         return speakablesList;
649     }
650     
651 }