view java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/SpeechWizardPanel.java @ 0:9418ab7b7f3f

Initial import
author Fiore Martin <fiore@eecs.qmul.ac.uk>
date Fri, 16 Dec 2011 17:35:51 +0000
parents
children
line wrap: on
line source
/*  
 CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
  
 Copyright (C) 2011  Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/  

package uk.ac.qmul.eecs.ccmi.simpletemplate;

import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.MessageFormat;
import java.util.Collection;
import java.util.ResourceBundle;

import javax.swing.DefaultComboBoxModel;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFormattedTextField;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerModel;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import jwizardcomponent.JWizardComponents;
import jwizardcomponent.JWizardPanel;
import uk.ac.qmul.eecs.ccmi.gui.LoopComboBox;
import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
import uk.ac.qmul.eecs.ccmi.speech.SpeechUtilities;

/*
 * The abstract class providing basic implementation for the panels displayed when the template
 * wizard is run in order to build a diagram template. Subclasses will define the central component 
 * displayed in the panel. The central component is an input component (e.g. a JTextField),
 * through which the user enters the input required for at that particular step of the wizard.
 *  
 *  
 * @see Wizard
 */
@SuppressWarnings("serial")
abstract class SpeechWizardPanel extends JWizardPanel {
	public SpeechWizardPanel(JWizardComponents wizardComponents, String title, int next, int previous){
		super(wizardComponents,title);
		label = new JLabel(title);
		this.next = next;
		this.previous = previous;
	}
	
	@Override
	public void update(){
		Component focusOwner = assignFocus();
		NarratorFactory.getInstance().speak(
				new StringBuilder(getPanelTitle())
					.append(' ')
					.append(SpeechUtilities.getComponentSpeech(focusOwner)).toString());
		super.update();
	}
	
	@Override
	public void setPanelTitle(String title){
		label.setText(title);
		super.setPanelTitle(title);
	}
	
	protected Component assignFocus(){
		if(component != null)
			component.requestFocus();
		return component;
	}
	
	/**
	 * Lays out the components according to the layout manager. This method is used by subclasses 
	 * by passing the component the user use for input (e.g. a text field or a combo-box) as argument.
	 * such component is placed at the centre of the panel above the buttons.    
	 * @param centralComponent the component to be laid out at the centre dialog  
	 */
	protected void layoutComponents(JComponent centralComponent){
		component = centralComponent;
		/* pressing enter on the central component results in a switch to the next panel */
		component.addKeyListener(new KeyAdapter(){
			@Override
			public void keyPressed(KeyEvent evt){
				pressed = true;
			}
			
			@Override
			public void keyTyped(KeyEvent evt){
				/* switch on the next panel only if the press button started on the same window      * 
				 * this is to avoid keyTyped to be called after the panel switch and therefore refer *
				 * to a component different that the one the user pressed OK on                      */
				if(evt.getKeyChar() == '\n' && pressed)
					getWizardComponents().getNextButton().doClick();
				pressed = false;
			}
			boolean pressed = false;
		});
		
		GridBagConstraints constr = new GridBagConstraints();
		constr.gridx = 0;
		constr.gridy = 0;
		constr.gridwidth = 1;
		constr.gridheight = 1;
		constr.weightx = 1.0;
		constr.weighty = 0.0;
		constr.anchor = GridBagConstraints.PAGE_START;
		constr.fill = GridBagConstraints.BOTH;
		constr.insets = new Insets(5, 5, 5, 5);
		constr.ipadx = 0;
		constr.ipady = 0;
		
		/* Label */
		setLayout(new GridBagLayout());
		JPanel labelPanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
		label.setHorizontalAlignment(SwingConstants.LEADING);
		labelPanel.add(label);
		add(labelPanel,constr);
		
		/* JSeparator */
		constr.gridy = 1;
		constr.anchor = GridBagConstraints.WEST;
		constr.fill = GridBagConstraints.BOTH;
		constr.insets = new Insets(1, 1, 1, 1);
		add(new JSeparator(), constr);
		
		/* central component */
		Container centralComponentContainer;
		if(centralComponent instanceof JScrollPane ){
			centralComponentContainer = centralComponent;
		}else{
			centralComponentContainer = new JPanel(new GridBagLayout());
			centralComponentContainer.add(centralComponent
	        , new GridBagConstraints(0, 0, 1, 1, 0.0, 0.0
	        , GridBagConstraints.CENTER, GridBagConstraints.BOTH
	        , new Insets(0, 0, 0, 0), 0, 0));
		}
		constr.gridy = 2;
		constr.weighty = 1.0;
		constr.anchor = GridBagConstraints.CENTER;
		constr.insets = new Insets(0, 0, 0, 0); 
		add(centralComponentContainer,constr);
	}
	
	@Override
	public void next(){
		switchPanel(next);
	}

	@Override
	public void back(){
		switchPanel(previous);
	}
	
	private JLabel label;
	private int next;
	private int previous;
	private JComponent component;
	public static int OWN_SWITCH = -1;
	public static int DISABLE_SWITCH = -2;
}

@SuppressWarnings("serial")
class SelectWizardPanel extends SpeechWizardPanel {
	SelectWizardPanel(JWizardComponents wizardComponents, String title, Collection<String> options, int next, int previous, Model.Record record){
		super(wizardComponents,title,next,previous);
		String[] optionsArray = new String[options.size()];
		comboBox = new LoopComboBox(new DefaultComboBoxModel(options.toArray(optionsArray)));
		comboBox.addItemListener(SpeechUtilities.getSpeechComboBoxItemListener());
		layoutComponents(comboBox);
		this.record = record;
	}
	
	SelectWizardPanel(JWizardComponents wizardComponents, String title, Collection<String> options, int[] nexts, int previous, Model.Record record){
		this(wizardComponents, title, options, OWN_SWITCH, previous, record);
		this.nexts = nexts;
	}
	
	SelectWizardPanel(JWizardComponents wizardComponents, String title, Collection<String> options, int[] nexts, int previous){
		this(wizardComponents, title, options, nexts, previous,null);
	}

	@Override
	public void next(){
		if(record != null)
			record.value = (String)comboBox.getSelectedItem();
		if(nexts != null)
			switchPanel(nexts[comboBox.getSelectedIndex()]);
		else
			super.next();
	}
	
	@Override
	public void update(){
		if(record != null)
			comboBox.setSelectedItem(record.value);
		super.update();
	}
	
	JComboBox comboBox;
	int[] nexts;
	Model.Record record;
}

@SuppressWarnings("serial")
class TextWizardPanel extends SpeechWizardPanel {
	TextWizardPanel(JWizardComponents wizardComponents, String title, Collection<String> existingValues, int next, int previous, Model.Record record){
		super(wizardComponents,title,next,previous);
		textField = new JTextField();
		textField.setColumns(10);
		textField.addKeyListener(SpeechUtilities.getSpeechKeyListener(true));
		layoutComponents(textField);
		this.record = record;
		this.existingValues = existingValues;
	}
	
	public void next(){
		String text = textField.getText().trim();
		/* if the user enters a text he has already entered (that is, it's in the existingValues the don't go on */
		/* and notify the user they have to chose another text. The only exception is when the record contains   */ 
		/* the same text the user entered as that means they are going through the editing of an existing element*/
		if(text.isEmpty()||"\n".equals(text)){
				NarratorFactory.getInstance().speak(ResourceBundle.getBundle(SpeechWizardDialog.class.getName()).getString("dialog.error.empty_text"));
				return;
		}
		for(String value : existingValues){
			if(value.equals(text) && !text.equals(record.value)){
				NarratorFactory.getInstance().speak(MessageFormat.format( 
								ResourceBundle.getBundle(SpeechWizardDialog.class.getName()).getString("dialog.error.existing_value"),
								text));
				return;
			}
		}
		if(record != null)
			record.value = text;
		super.next();
	}
	
	@Override
	public void update(){
		if(record != null)
			textField.setText(record.value);
		super.update();
	}
	
	JTextField textField;
	Collection<String> existingValues;
	Model.Record record;
}

@SuppressWarnings("serial")
class SpinnerWizardPanel extends SpeechWizardPanel{
	public SpinnerWizardPanel(JWizardComponents wizardComponents, String title, SpinnerModel spinnerModel, int next, int previous, Model.Record record){
		super(wizardComponents,title,next,previous);
		this.record = record;
		spinner = new JSpinner(spinnerModel);
		spinner.addChangeListener(new ChangeListener(){
			@Override
			public void stateChanged(ChangeEvent evt) {
				 JSpinner s = (JSpinner)(evt.getSource());
				 NarratorFactory.getInstance().speak(s.getValue().toString());
			}
		});
		JFormattedTextField tf = ((JSpinner.DefaultEditor)spinner.getEditor()).getTextField();
		tf.setEditable(false);
		tf.setFocusable(false);
		tf.setBackground(Color.white);
		layoutComponents(spinner);
	}
	
	@Override
	public void next(){
		if(record != null)
			record.value = spinner.getValue().toString();
		super.next();
	}
	
	@Override
	public void update(){
		if(record != null){
			if(!record.value.isEmpty())
				spinner.setValue(Integer.parseInt(record.value));
		}
		super.update();
	}
	
	Model.Record record;
	JSpinner spinner;
}

@SuppressWarnings("serial")
class DummyWizardPanel extends JWizardPanel{
	DummyWizardPanel(JWizardComponents wizardComponents){
		super(wizardComponents);
	}
}