diff java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/Wizard.java @ 0:78b7fc5391a2

first import, outcome of NIME 2014 hackaton
author Fiore Martin <f.martin@qmul.ac.uk>
date Tue, 08 Jul 2014 16:28:59 +0100
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/java/src/uk/ac/qmul/eecs/ccmi/simpletemplate/Wizard.java	Tue Jul 08 16:28:59 2014 +0100
@@ -0,0 +1,1210 @@
+/*  
+ 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.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Frame;
+import java.awt.GridBagLayout;
+import java.awt.GridLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.KeyEvent;
+import java.io.IOException;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import javax.swing.AbstractAction;
+import javax.swing.DefaultComboBoxModel;
+import javax.swing.JCheckBox;
+import javax.swing.JComboBox;
+import javax.swing.JComponent;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTextField;
+import javax.swing.KeyStroke;
+import javax.swing.SpinnerModel;
+
+import jwizardcomponent.FinishAction;
+import jwizardcomponent.JWizardComponents;
+import jwizardcomponent.JWizardPanel;
+import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties;
+import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties.Modifiers;
+import uk.ac.qmul.eecs.ccmi.gui.Diagram;
+import uk.ac.qmul.eecs.ccmi.gui.Edge;
+import uk.ac.qmul.eecs.ccmi.gui.LineStyle;
+import uk.ac.qmul.eecs.ccmi.gui.LoopComboBox;
+import uk.ac.qmul.eecs.ccmi.gui.LoopSpinnerNumberModel;
+import uk.ac.qmul.eecs.ccmi.gui.Node;
+import uk.ac.qmul.eecs.ccmi.gui.SpeechSummaryPane;
+import uk.ac.qmul.eecs.ccmi.simpletemplate.SimpleShapeNode.ShapeType;
+import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
+import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
+import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
+import uk.ac.qmul.eecs.ccmi.speech.SpeechUtilities;
+import uk.ac.qmul.eecs.ccmi.utils.GridBagUtilities;
+
+/**
+ *
+ * A Wizard-like sequence of screens prompted to the user to let they input (e.g. which shape a node will have or how many nodes an edge can connect at most) 
+ * how to build a template diagram. A template diagram is a prototype diagram
+ * (containing prototype nodes and edges) which can later on be used for creating instances
+ * of that type of diagram through clonation. The wizard is completely accessible via audio 
+ * as all the content and all focused components names are spoken out by the {@code Narrator} through a text to speech synthesizer.   
+ *
+ */
+public class Wizard {
+	public Wizard(Frame frame, Collection<String> existingDiagrams, Diagram diagramToEdit){
+		dialog = new SpeechWizardDialog(frame);
+		resources = ResourceBundle.getBundle(SpeechWizardDialog.class.getName());
+		
+		model = createModel(diagramToEdit);
+		node = new Model.Node();
+		edge = new Model.Edge();
+		property = new Model.Property();
+		modifier = new Model.Modifier();
+		
+		initWizardComponents(existingDiagrams,diagramToEdit);
+		
+		/* if the user is editing from an existing diagram, they have to choose a new name. They're switched *
+		 * directly to the diagram name panel so they have to enter a new name as they would otherwise       *
+		 * not be allowed to proceed.                                                                        */
+		if(diagramToEdit != null)
+			dialog.getWizardComponents().setCurrentIndex(DIAGRAM_NAME);
+		
+		/* when the user clicks on the finish button they'll be prompted with a summary text area dialog     * 
+		 * describing what they have created so far and asking for a confirmation to proceed with the actual *
+		 * creation of the template.                                                                 		 */
+		dialog.getWizardComponents().setFinishAction(new FinishAction(dialog.getWizardComponents()){
+			@Override
+			public void performAction(){
+				String[] options = {
+						resources.getString("dialog.summary.ok_button_label"),
+						resources.getString("dialog.summary.cancel_button_label")};
+				int result = SpeechSummaryPane.showDialog(
+						dialog, 
+						resources.getString("dialog.summary.title"), 
+						model.toString(),
+						SpeechSummaryPane.OK_CANCEL_OPTION,
+						options);
+				
+				if(result == SpeechSummaryPane.CANCEL){ // user wants to continue editing  
+					/* null arg will avoid default playerListener which speaks out the focused component */
+					SoundFactory.getInstance().play(SoundEvent.CANCEL,null);
+					return;
+				}
+				/* create the actual diagram (it will be return to the client class by execute()) */
+				diagram = createDiagram(model);
+				dialog.dispose();
+			}
+		});
+	}
+	
+	public Wizard(Frame frame, Collection<String> existingDiagrams){
+		this(frame,existingDiagrams,null);
+	}
+	
+	public Diagram execute(){
+		diagram = null;
+		dialog.show();
+		if(diagram == null)
+			SoundFactory.getInstance().play(SoundEvent.CANCEL);
+		return diagram;
+	}
+	
+	@SuppressWarnings("serial")
+	private void initWizardComponents(Collection<String> existingDiagrams, final Diagram diagramToEdit){
+		/* --- MAIN PANEL --- */
+		String[] choices = {
+				resources.getString("panel.home.choice.diagram_name"),
+				resources.getString("panel.home.choice.nodes"),
+				resources.getString("panel.home.choice.edges"),
+		};
+		int[] nexts = {DIAGRAM_NAME,NODES,EDGES};
+		
+		/* panel for the selection of main tasks when creating the diagram: enter diagram name, create node and      *
+		 * create edge. When a name is assigned an item is added to the selection which allows the user to finish    *
+		 * the template creation, much as they would do by pressing the finish button. If the user edits an existing * 
+		 * diagram they're prompted with a message to enter a new diagram name (as there cannot be two diagrams      *
+		 * with the same name. When the user enters the name the message goes away                                   */
+		add(HOME,new SelectWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.home.title.new"),
+				Arrays.asList(choices),
+				nexts,
+				SpeechWizardPanel.DISABLE_SWITCH
+				){
+			@Override
+			public void update(){
+				if(!model.diagramName.value.isEmpty()){
+					dialog.setFinishButtonEnabled(true);
+					/* if the diagram has a name the template creation can finish. So add a selection item to the *
+					 * comboBox unless it's already there from a previous update (item count < 4 )                */
+					if(comboBox.getItemCount() < 4)
+						((DefaultComboBoxModel)comboBox.getModel()).addElement(resources.getString("panel.home.choice.finish"));
+				}
+				super.update();
+			}
+			@Override
+			public void next(){
+				if(comboBox.getSelectedIndex() == 3)
+					dialog.getWizardComponents().getFinishButton().doClick();
+				else
+					super.next();
+			}
+		});
+
+		/* --- DIAGRAM NAME INPUT PANEL --- */
+		add(DIAGRAM_NAME, new TextWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString(diagramToEdit == null ? "panel.diagram_name.title" : "panel.diagram_name.title.editing_existing_diagram"),
+				existingDiagrams, 
+				HOME, 
+				HOME, 
+				model.diagramName	
+			){
+			@Override
+			public void update(){
+				/* this is a little nasty trick to achieve the following: when the user creates a new diagram out of an already existing 
+				 * one they're directly prompted with this panel. We want the name of the diagram to be there for awareness 
+				 * but at the same time it must not be accepted by the program as it would otherwise clash with 
+				 * with the starting diagam's. As the program accepts it when the text entered in the text field is equal to 
+				 * model.diagramName.value (for when the user wants to re-edit the name of a diagram they're creating) we must 
+				 * fill model.DiagramName.value with the name of the starting diagram to get it shown and spoken out but then 
+				 * it's assigned the empty string not to let the user to go forward */
+				if(diagramToEdit != null && model.diagramName.value.isEmpty()){
+					model.diagramName.value = diagramToEdit.getName();
+					super.update();
+					model.diagramName.value = "";
+				}else{
+					super.update();
+				}
+			}
+		});
+		
+		/* --- NODE ACTION SELECTION PANEL --- */
+		/* decide whether to add a new node or to edit/delete an existing node */ 
+		String[] nodeOptions = {
+				resources.getString("panel.nodes.actions.add"),
+				resources.getString("panel.nodes.actions.edit"),
+				resources.getString("panel.nodes.actions.del"),
+				resources.getString("panel.nodes.actions.finish")};
+		int[] nodeNexts = {NODE_TYPE,NODE_EDIT,NODE_DEL,HOME};
+		add(NODES, new ActionChooserPanel(
+				dialog.getWizardComponents(),
+				model.nodes.getNames(),
+				resources.getString("panel.nodes.title"),
+				nodeOptions,
+				nodeNexts,
+				HOME,
+				node
+			));
+		
+		/* --- NODE TYPE NAME INPUT PANEL --- */
+		add(NODE_TYPE,new TextWizardPanel(
+			dialog.getWizardComponents(),
+			resources.getString("panel.node_name.title"),
+			model.nodes.getNames(),
+			NODE_SHAPE,
+			NODES,
+			node.type
+			));
+
+		/* --- NODE TO DELETE SELECTION PANEL*/
+		add(NODE_DEL, new DeletePanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.node_del.title"),
+				NODES,
+				NODES,
+				model.nodes));
+		
+		/* -- NODE TO EDIT SELECTION PANEL */
+		add(NODE_EDIT, new EditPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.node_edit.title"),
+				NODE_TYPE,
+				NODES,
+				model.nodes,
+				node
+			));
+		
+		ShapeType[] shapeTypes = ShapeType.values();
+		ArrayList<String> shapeTypeNames = new ArrayList<String>(shapeTypes.length);
+		for(int i=0; i<shapeTypes.length;i++)
+			if(shapeTypes[i] != ShapeType.Transparent)
+				shapeTypeNames.add(shapeTypes[i].toString());
+		
+		/* -- NODE SHAPE SELECTION PANEL --- */ 
+		add(NODE_SHAPE, new SelectWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.node_shape.title"),
+				shapeTypeNames,
+				NODE_YESNO_PROPERTIES,
+				NODE_TYPE,
+				node.shape
+				));
+		
+		/* --- SELECT WHETHER THE THE NODE HAS TO HAVE PROPERTIES --- */
+		String[] yesnoPropertyOptions = {
+				resources.getString("panel.yesno_properties.add"),
+				resources.getString("panel.yesno_properties.finish")
+		};
+		int[] yesnoPropertyNexts = {PROPERTIES,NODES};
+		
+		add(NODE_YESNO_PROPERTIES,new SelectWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.yesno_properties.title"),
+				Arrays.asList(yesnoPropertyOptions),
+				yesnoPropertyNexts,
+				NODE_SHAPE
+				){
+			@Override
+			public void next(){
+				if(comboBox.getSelectedIndex() == 1){
+					Model.Node newNode = new Model.Node();
+					Model.copy(node, newNode);
+					model.nodes.put(newNode.id,newNode);
+				}
+				super.next();
+			}
+		});
+		
+		/* --- PROPERTIES ACTION SELECTION PANEL --- */
+		String[] propertyOptions = {
+				resources.getString("panel.properties.actions.add"),
+				resources.getString("panel.properties.actions.edit"),
+				resources.getString("panel.properties.actions.del"),
+				resources.getString("panel.properties.action.finish")};
+		int[] propertyNexts = {PROPERTY_TYPE,PROPERTY_EDIT,PROPERTY_DEL,NODES};
+		
+		add(PROPERTIES, new ActionChooserPanel(
+				dialog.getWizardComponents(),
+				node.properties.getNames(),
+				resources.getString("panel.properties.title"),
+				propertyOptions,
+				propertyNexts,
+				NODE_SHAPE,
+				property
+			){
+				@Override
+				public void next(){
+					/* if the user selects finish, create a new node put in it the values of */
+					/* the temporary property and store it in the model                      */  
+					if( (comboBox.getSelectedIndex() == 1 && comboBox.getItemCount() == 2)||
+							(comboBox.getSelectedIndex() == 3 && comboBox.getItemCount() == 4)){ 
+						Model.Node newNode = new Model.Node();
+						Model.copy(node, newNode);
+						model.nodes.put(newNode.id,newNode);
+					}
+					super.next();
+				}
+		});
+		
+		/* --- PROPERTY TYPE NAME INPUT PANEL --- */
+		add(PROPERTY_TYPE,new TextWizardPanel(
+			dialog.getWizardComponents(),
+			resources.getString("panel.property_name.title"),
+			node.properties.getNames(),
+			PROPERTY_POSITION,
+			PROPERTIES,
+			property.type
+			));
+		
+		/* --- PROPERTY TO DELETE SELECTION PANEL --- */
+		add(PROPERTY_DEL, new DeletePanel(
+			dialog.getWizardComponents(),
+			resources.getString("panel.property_del.title"),
+			PROPERTIES,
+			PROPERTIES,
+			node.properties
+			));
+
+		/* --- PROPERTY TO EDIT SELECTION PANEL --- */
+		add(PROPERTY_EDIT, new EditPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.property_edit.title"),
+				PROPERTY_TYPE,
+				PROPERTIES,
+				node.properties,
+				property
+				));
+		
+		/* --- PROPERTY POSITION SELECTION DIALOG --- */
+		SimpleShapeNode.Position positions[] = SimpleShapeNode.Position.values(); 
+		ArrayList<String> positionNames = new ArrayList<String>(positions.length);
+		for(int i=0; i<positions.length;i++)
+			positionNames.add(positions[i].toString());
+		int[] positionNexts = {PROPERTY_YESNO_MODIFIER,PROPERTY_SHAPE};
+		
+		
+		add(PROPERTY_POSITION, new SelectWizardPanel(
+			dialog.getWizardComponents(),
+			resources.getString("panel.property_position.title"),
+			positionNames,
+			positionNexts,
+			PROPERTY_TYPE,
+			property.position
+			));
+		
+		/* --- PROPERTY SHAPE SELECTION DIALOG --- */
+		shapeTypeNames.add(ShapeType.Transparent.toString());
+		add(PROPERTY_SHAPE, new SelectWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.property_shape.title"),
+				shapeTypeNames,
+				PROPERTY_YESNO_MODIFIER,
+				PROPERTY_POSITION,
+				property.shape
+				));
+		
+		/* --- SELECT WHETHER THE THE PROPERTY HAS TO HAVE MODIFIERS --- */
+		String[] yesnoModifierOptions = {
+				resources.getString("panel.yesno_modifiers.add"),
+				resources.getString("panel.yesno_modifiers.finish")
+		};
+		int[] yesnoModifierNexts = {MODIFIERS,PROPERTIES};
+		
+		add(PROPERTY_YESNO_MODIFIER,new SelectWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.yesno_modifiers.title"),
+				Arrays.asList(yesnoModifierOptions),
+				yesnoModifierNexts,
+				PROPERTY_POSITION
+				){
+			@Override
+			public void next(){
+				if(comboBox.getSelectedIndex() == 1){
+					Model.Property newProperty = new Model.Property();
+					Model.copy(property, newProperty);
+					node.properties.put(newProperty.id,newProperty);
+				}
+				super.next();
+			}
+		});
+		/* --- MODIFIERS ACTION SELECTION PANE --- */
+		String[] modifierOptions = {
+				resources.getString("panel.modifiers.actions.add"),
+				resources.getString("panel.modifiers.actions.edit"),
+				resources.getString("panel.modifiers.actions.del"),
+				resources.getString("panel.modifiers.actions.finish")
+		};
+		int[] modifiersNexts = {MODIFIER_TYPE,MODIFIER_EDIT,MODIFIER_DEL,PROPERTIES};
+		
+		add(MODIFIERS, new ActionChooserPanel(
+				dialog.getWizardComponents(),
+				property.modifiers.getNames(), 
+				resources.getString("panel.modifiers.title"),
+				modifierOptions, 
+				modifiersNexts, 
+				PROPERTY_POSITION, 
+				modifier){
+				
+				@Override
+				public void next(){
+					/* if the user selects finish, create a new property put in it the values of */
+					/* the temporary property and store it in the model                          */
+					if( (comboBox.getSelectedIndex() == 1 && comboBox.getItemCount() == 2)||
+							(comboBox.getSelectedIndex() == 3 && comboBox.getItemCount() == 4)){ 
+						Model.Property newProperty = new Model.Property();
+						Model.copy(property, newProperty);
+						node.properties.put(newProperty.id,newProperty);
+					}
+					super.next();
+				}
+		});
+		
+		/* --- MODIFIER TYPE PANEL --- */
+		add(MODIFIER_TYPE, new TextWizardPanel(
+				dialog.getWizardComponents(), 
+				resources.getString("panel.modifier_type.title"), 
+				property.modifiers.getNames(),
+				MODIFIER_FORMAT, 
+				MODIFIERS,
+				modifier.type
+				));
+		
+		add(MODIFIER_DEL, new DeletePanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.modifier_del.title"), 
+				MODIFIERS, 
+				MODIFIERS, 
+				property.modifiers));
+		
+		add(MODIFIER_EDIT, new EditPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.modifier_edit.title"),
+				MODIFIER_TYPE,
+				MODIFIERS,
+				property.modifiers,
+				modifier
+		));
+		
+		add(MODIFIER_FORMAT, new FormatWizardPanel());
+		/* --- EDGE ACTION SELECTION PANEL --- */
+		/* decide whether to add a new edge or to edit/delete an existing edge */ 
+		String[] edgeOptions = {
+				resources.getString("panel.edges.actions.add"),
+				resources.getString("panel.edges.actions.edit"),
+				resources.getString("panel.edges.actions.del"),
+				resources.getString("panel.edges.actions.finish")};
+		int[] edgeNexts = {EDGE_TYPE,EDGE_EDIT,EDGE_DEL,HOME};
+		add(EDGES, new ActionChooserPanel(
+				dialog.getWizardComponents(),
+				model.edges.getNames(),
+				resources.getString("panel.edges.title"),
+				edgeOptions,
+				edgeNexts,
+				HOME,
+				edge
+			));
+		
+		/* --- EDGE TYPE NAME INPUT PANEL --- */
+		add(EDGE_TYPE,new TextWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.edge_name.title"),
+				model.edges.getNames(),
+				EDGE_LINE_STYLE,
+				EDGES,
+				edge.type
+		));
+		
+		/* --- EDGE TO DELETE SELECTION PANEL --- */
+		add(EDGE_DEL, new DeletePanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.edge_del.title"),
+				EDGES,
+				EDGES,
+				model.edges));
+		
+		/* --- EDGE TO EDIT SELECTION PANEL --- */
+		add(EDGE_EDIT, new EditPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.edge_edit.title"),
+				EDGE_TYPE,
+				EDGES,
+				model.edges,
+				edge
+			));
+
+		/* --- LINE STYLE SELECTION PANEL --- */
+		LineStyle[] lineStyles = LineStyle.values();	
+		String[] lineStyleNames = new String[lineStyles.length];
+		for(int i=0; i<lineStyles.length;i++)
+			lineStyleNames[i] = lineStyles[i].toString();
+		                                 
+		add(EDGE_LINE_STYLE, new SelectWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.edge_linestyle.title"),
+				Arrays.asList(lineStyleNames),
+				EDGE_MIN_NODES,
+				EDGE_TYPE,
+				edge.lineStyle
+				));
+		
+		/* --- MIN NODES SELECTION PANEL --- */
+		SpinnerModel minNodesSpinnerModel = new LoopSpinnerNumberModel(2,2,4);
+		add(EDGE_MIN_NODES,new SpinnerWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.edge_min_nodes.title"),
+				minNodesSpinnerModel,
+				EDGE_MAX_NODES,
+				EDGE_LINE_STYLE,
+				edge.minNodes
+				));
+		
+		/* --- MAX NODES SELECTION PANEL --- */
+		SpinnerModel maxNodesSpinnerModel = new LoopSpinnerNumberModel(2,2,4);
+		add(EDGE_MAX_NODES, new SpinnerWizardPanel(
+			dialog.getWizardComponents(),
+			resources.getString("panel.edge_max_nodes.title"),
+			maxNodesSpinnerModel,
+			EDGE_YESNO_ARROW_HEAD,
+			EDGE_MIN_NODES,
+			edge.maxNodes
+			){
+				@Override
+				public void next(){
+					int min = Integer.parseInt(edge.minNodes.value);
+					int max = Integer.parseInt(spinner.getValue().toString());
+					if(min > max){
+						NarratorFactory.getInstance().speak(resources.getString("dialog.error.min_max"));
+					}else{
+						super.next();
+					}
+				}
+				
+		});
+		
+		/* --- SELECT WHETHER THE EDGE MUST HAVE ARROW HEADS OR NOT --- */
+		String[] arrowHeadOptions = {
+				resources.getString("panel.edge_yesno_arrow_head.actions.add"),
+				resources.getString("panel.edge_yesno_arrow_head.actions.finish")
+		};
+		int[] arrowHeadNexts = {EDGE_ARROW_HEAD,EDGES};
+		add(EDGE_YESNO_ARROW_HEAD,new SelectWizardPanel(
+				dialog.getWizardComponents(),
+				resources.getString("panel.edge_yesno_arrow_head.title"),
+				Arrays.asList(arrowHeadOptions),
+				arrowHeadNexts,
+				EDGE_MAX_NODES
+				){
+			@Override
+			public void next(){
+				if(comboBox.getSelectedIndex() == 1){
+					Model.Edge newEdge = new Model.Edge();
+					Model.copy(edge, newEdge);
+					model.edges.put(newEdge.id,newEdge);
+				}
+				super.next();
+			}
+		});
+		
+		/* --- ARROW HEAD SELECTION PANEL --- */
+		add(EDGE_ARROW_HEAD, new ArrowHeadPanel());
+		
+		add(LAST_PANEL, new DummyWizardPanel(dialog.getWizardComponents()));
+		
+		SpeechUtilities.changeTabListener((JComponent)dialog.getContentPane(), dialog);
+	}
+	
+	private void add(int index, JWizardPanel panel){
+		dialog.getWizardComponents().addWizardPanel(index,panel);
+	}
+	
+	private Diagram createDiagram(Model model){
+		/* create the node prototypes */
+		Node[] nodes = new Node[model.nodes.size()];
+		int i = 0;
+		for(Model.Node n : model.nodes.values()){
+			nodes[i] = createDiagramNode(n);
+			i++;
+		}
+		/* create the edge prototypes */
+		Edge[] edges = new Edge[model.edges.size()];
+		i = 0;
+		for(Model.Edge e : model.edges.values()){
+			edges[i] = createDiagramEdge(e);
+			i++;
+		}
+		return Diagram.newInstance(model.diagramName.value, nodes, edges, new SimpleShapePrototypePersistenceDelegate());
+	}
+	
+	private Node createDiagramNode(Model.Node n){
+		/* set up the properties */
+		LinkedHashMap<String,Set<String>> propertiesTypeDefinition = new LinkedHashMap<String,Set<String>>();
+		/* create the property type definition */
+		for(Model.Property modelProperty : n.properties.values()){
+			Set<String> modifiersTypeDefinition = new LinkedHashSet<String>();
+			for(Model.Modifier modifier : modelProperty.modifiers.values())
+				modifiersTypeDefinition.add(modifier.type.value);
+			propertiesTypeDefinition.put(modelProperty.type.value, modifiersTypeDefinition);
+		}
+		NodeProperties properties = new NodeProperties(propertiesTypeDefinition);	
+		/* now that properties object is created attach the views on it */
+		for(Model.Property modelProperty : n.properties.values()){
+			PropertyView propertyView = new PropertyView(
+					SimpleShapeNode.Position.valueOf(modelProperty.position.value),
+					modelProperty.shape.value.isEmpty() ? 
+							/* doesn't really matter as position is inside and shape won't be taken into account */ 
+							SimpleShapeNode.ShapeType.Rectangle :   
+							SimpleShapeNode.ShapeType.valueOf(modelProperty.shape.value) 
+					);
+			properties.setView(modelProperty.type.value, propertyView);
+			/* modifier view */
+			for(Model.Modifier modelModifier : modelProperty.modifiers.values()){
+				boolean bold = false;
+				boolean italic = false;
+				boolean underline = false;
+				String prefix = "";
+				String suffix = "";	
+				for(String value : modelModifier.format.values){
+					if(value.equals(resources.getString("modifier.format.bold"))){
+						bold = true;
+					}else if(value.equals(resources.getString("modifier.format.underline"))){
+						underline = true;
+					}else if(value.equals(resources.getString("modifier.format.italic"))){
+						italic = true;
+					}else if(value.equals(resources.getString("modifier.format.prefix"))){
+						prefix = modelModifier.affix.values[PREFIX_INDEX];
+					}else if(value.equals(resources.getString("modifier.format.suffix"))){
+						suffix = modelModifier.affix.values[SUFFIX_INDEX];
+					}
+				}
+				ModifierView modifierView = new ModifierView(underline,bold,italic,prefix,suffix);
+				properties.getModifiers(modelProperty.type.value).setView(modelModifier.type.value, modifierView);
+			}
+		}
+		return SimpleShapeNode.getInstance(
+				SimpleShapeNode.ShapeType.valueOf(n.shape.value), 
+				n.type.value, 
+				properties);
+	}
+	
+	private Edge createDiagramEdge(Model.Edge e){
+		/* create the arrow head array out of the string stored in the model */
+		ArrowHead[] arrowHeads = new ArrowHead[e.arrowHeads.values.length];
+		for(int i=0; i<e.arrowHeads.values.length;i++){
+			try {
+				arrowHeads[i] = ArrowHead.getArrowHeadFromString(e.arrowHeads.values[i]);
+			} catch (IOException ioe) {
+				throw new RuntimeException(ioe);// the wizard mustn't allow the user to enter different strings
+			}
+		}
+		return new SimpleShapeEdge(
+				e.type.value,
+				LineStyle.valueOf(e.lineStyle.value),
+				arrowHeads,
+				e.arrowHeadsDescriptions.values,
+				Integer.parseInt(e.minNodes.value),
+				Integer.parseInt(e.maxNodes.value)
+				);
+	}
+	
+	private Model createModel(Diagram diagram) {
+		Model model = new Model();
+		if(diagram == null)
+			return model;
+		
+		/* the name isn't copied as the user as to find a new one */
+		/* model.diagramName.value = diagram.getName();*/
+
+		/* nodes */
+		for(Node n : diagram.getNodePrototypes()){
+			if(!(n instanceof SimpleShapeNode))
+				continue;
+			Model.Node modelNode = createModelNode((SimpleShapeNode)n);
+			model.nodes.put(modelNode.id,modelNode);
+		}
+		/* edges */
+		for(Edge e : diagram.getEdgePrototypes()){
+			if(!(e instanceof SimpleShapeEdge))
+				continue;
+			Model.Edge modelEdge = createModelEdge((SimpleShapeEdge)e);
+			model.edges.put(modelEdge.id, modelEdge);
+		}
+		return model;
+	}
+	
+	/**
+	 * fills up the model node object with informations from the real diagram node 
+	 * @param n
+	 * @return
+	 */
+	private Model.Node createModelNode(SimpleShapeNode n){
+		Model.Node modelNode = new Model.Node();
+		modelNode.type.value = n.getType();
+		modelNode.shape.value = n.getShapeType().toString();
+		
+		NodeProperties properties = n.getProperties();
+		for(String propertyType : properties.getTypes()){
+			Model.Property modelProperty = new Model.Property();
+			modelProperty.type.value = propertyType;
+			/* if the view is not a PropertyView or is null then assign a default value         */
+			/* it should never happen but it's just to keep it safer and more forward compliant */ 
+			if(! (properties.getView(propertyType) instanceof PropertyView)){
+				modelProperty.position.value = SimpleShapeNode.Position.Inside.toString();
+			}else{
+				PropertyView propertyView = (PropertyView)properties.getView(propertyType);
+				modelProperty.position.value = propertyView.getPosition().toString();
+				modelProperty.shape.value = propertyView.getShapeType().toString();
+			}
+			Modifiers modifiers = properties.getModifiers(propertyType);
+			for(String modifierType : modifiers.getTypes()){
+				Model.Modifier modelModifier = new Model.Modifier();
+				modelModifier.type.value = modifierType;
+				if(modifiers.getView(modifierType) instanceof ModifierView){
+					ModifierView modifierView = (ModifierView)modifiers.getView(modifierType);
+					/* the string array with the modifier values must be created, so the size must be known before */
+					int numModifierValues = 0;
+					if(modifierView.isBold())
+						numModifierValues++;
+					if(modifierView.isItalic())
+						numModifierValues++;
+					if(modifierView.isUnderline())
+						numModifierValues++;
+					if(!modifierView.getPrefix().isEmpty())
+						numModifierValues++;
+					if(!modifierView.getSuffix().isEmpty())
+						numModifierValues++;
+					/* create the string array and fill it up with values */
+					modelModifier.format.values = new String[numModifierValues];
+					numModifierValues = 0;
+					if(modifierView.isBold())
+						modelModifier.format.values[numModifierValues++] = resources.getString("modifier.format.bold");
+					if(modifierView.isItalic())
+						modelModifier.format.values[numModifierValues++] = resources.getString("modifier.format.italic");
+					if(modifierView.isUnderline())
+						modelModifier.format.values[numModifierValues++] = resources.getString("modifier.format.underline");
+					if(!modifierView.getPrefix().isEmpty()){
+						modelModifier.format.values[numModifierValues++] = resources.getString("modifier.format.prefix");
+						modelModifier.affix.values[PREFIX_INDEX] = modifierView.getPrefix(); 
+					}
+							
+					if(!modifierView.getSuffix().isEmpty()){
+						modelModifier.format.values[numModifierValues++] = resources.getString("modifier.format.suffix"); 
+						modelModifier.affix.values[SUFFIX_INDEX] = modifierView.getSuffix();
+					}
+				}
+				modelProperty.modifiers.put(modelModifier.id, modelModifier);
+			}
+			modelNode.properties.put(modelProperty.id, modelProperty);
+		}
+		return modelNode;
+	}
+	
+	private Model.Edge createModelEdge(SimpleShapeEdge e){
+		Model.Edge modelEdge = new Model.Edge();
+		modelEdge.type.value = e.getType();
+		modelEdge.lineStyle.value = e.getStyle().toString();
+		modelEdge.maxNodes.value = Integer.toString(e.getMaxAttachedNodes());
+		modelEdge.minNodes.value = Integer.toString(e.getMinAttachedNodes());
+		
+		/* arrow heads and arrowheads descriptions */
+		modelEdge.arrowHeadsDescriptions.values = e.getAvailableEndDescriptions();
+		modelEdge.arrowHeads.values = new String[e.getHeads().length];
+		for(int i  =0; i<e.getHeads().length;i++){
+			modelEdge.arrowHeads.values[i] = e.getHeads()[i].toString();
+		}
+		
+		return modelEdge;
+	}
+	
+	private SpeechWizardDialog dialog;
+	private Diagram diagram;
+	private ResourceBundle resources;
+	private Model model;
+	/* these are the temporary variables where the data are stored during the wizard  *
+	 * when a sub task is completed ( node creation, edge creation, property creation)*  
+	 * the data stored in the temporary variables are saved in the model              */
+	private Model.Node node;
+	private Model.Property property;
+	private Model.Modifier modifier;
+	private Model.Edge edge;
+	
+	static int HOME = 0;
+	static int DIAGRAM_NAME = 1;
+	static int NODES = 2;
+	static int NODE_TYPE = 3;
+	static int NODE_DEL = 4;
+	static int NODE_EDIT = 5;
+	static int NODE_SHAPE = 6;
+	static int NODE_YESNO_PROPERTIES = 7;
+	static int PROPERTIES = 8;
+	static int PROPERTY_TYPE = 9;
+	static int PROPERTY_DEL = 10;
+	static int PROPERTY_EDIT = 11; 
+	static int PROPERTY_POSITION = 12;
+	static int PROPERTY_SHAPE = 13;
+	static int PROPERTY_YESNO_MODIFIER = 14;
+	static int MODIFIERS = 15;
+	static int MODIFIER_TYPE = 16;
+	static int MODIFIER_DEL = 17;
+	static int MODIFIER_EDIT = 18;
+	static int MODIFIER_FORMAT = 19;
+	static int EDGES = 20;
+	static int EDGE_TYPE = 21; 
+	static int EDGE_DEL = 22; 
+	static int EDGE_EDIT = 23;
+	static int EDGE_LINE_STYLE = 24;
+	static int EDGE_MIN_NODES = 25;
+	static int EDGE_MAX_NODES = 26;
+	static int EDGE_YESNO_ARROW_HEAD = 27;
+	static int EDGE_ARROW_HEAD = 28;
+	static int LAST_PANEL = 29;
+	
+	private static int PREFIX_INDEX = 0;
+	private static int SUFFIX_INDEX = 1;
+	
+	/* the abstract class from which the panels for Nodes, edges and Modifiers inherit 
+	 * It displays the actions (add,edit,delete,finish) on a comboBox. if elementNames is empty 
+	 * it means that no element has been created yet and therefore edit and delete actions are disabled 
+	 */
+	@SuppressWarnings("serial")
+	private static class ActionChooserPanel extends SpeechWizardPanel{
+		ActionChooserPanel(JWizardComponents wizardComponents,Collection<String> elementNames, String title, String[] options, int[] nexts, int previous, Model.Element temporaryElement){
+			super(wizardComponents,title,OWN_SWITCH, previous);
+			this.options = options;
+			comboBoxModel = new DefaultComboBoxModel();
+			comboBoxModel.addElement(options[0]);
+			comboBoxModel.addElement(options[3]);
+			comboBox = new LoopComboBox(comboBoxModel);
+			comboBox.addItemListener(SpeechUtilities.getSpeechComboBoxItemListener());
+			layoutComponents(comboBox);
+			this.elementNames = elementNames;
+			this.temporaryElement = temporaryElement;
+			this.nexts = nexts;
+		}
+
+		@Override
+		public void update(){
+			if(elementNames.isEmpty() && comboBoxModel.getSize() == 4){
+				comboBoxModel.removeElement(options[1]);
+				comboBoxModel.removeElement(options[2]);
+			}else if(!elementNames.isEmpty() && comboBoxModel.getSize() == 2){
+				comboBoxModel.insertElementAt(options[1],1);
+				comboBoxModel.insertElementAt(options[2],2);
+			}
+			super.update();
+		}
+		
+		@Override
+		public void next(){
+			/* if the selection was add element, then we clear the temporary holder */
+			if(comboBox.getSelectedIndex() == 0)
+				temporaryElement.clear();
+			/* jump to the selected next step, works both when it's only add/finish and when it's add/delete/edit/finish */
+			for(int i=0; i<options.length; i++)
+				if(comboBox.getSelectedItem().equals(options[i])){
+					switchPanel(nexts[i]);
+					return;
+				}
+		}
+		
+		JComboBox comboBox;
+		Collection<String> elementNames;
+		DefaultComboBoxModel comboBoxModel;
+		String[] options;
+		Model.Element temporaryElement;
+		int[] nexts;
+	}
+	
+	@SuppressWarnings("serial")
+	private static class DeletePanel extends SpeechWizardPanel {
+		DeletePanel(JWizardComponents wizardComponents,String title, int next, int previous, ModelMap<? extends Model.Element> elements){
+			super(wizardComponents,title,next, previous);
+			this.elements = elements;
+			comboBox = new LoopComboBox();
+			comboBox.addItemListener(SpeechUtilities.getSpeechComboBoxItemListener());
+			layoutComponents(comboBox);
+		}
+		
+		@Override
+		public void update(){
+			String[] options = new String[elements.values().size()];
+			options = elements.getNames().toArray(options);
+			comboBox.setModel(new DefaultComboBoxModel(options));
+			super.update();
+		}
+		
+		/**
+		 * the default behaviour is to delete the selected element 
+		 */
+		@Override
+		public void next(){
+			Model.Element elementToDelete = null;
+			for(Model.Element element : elements.values()){
+				if(element.type.value.equals(comboBox.getSelectedItem())){
+					elementToDelete = element;
+					break;
+				}
+			}
+			Object o = elements.remove(elementToDelete.id);
+			assert(o != null);
+			super.next();
+		}
+		
+		JComboBox comboBox;
+		ModelMap<? extends Model.Element> elements;
+	}
+	
+	@SuppressWarnings("serial")
+	private static class EditPanel extends DeletePanel {
+		EditPanel(JWizardComponents wizardComponents,
+				String title, 
+				int next, 
+				int previous, 
+				ModelMap<? extends Model.Element> elements,
+				Model.Element temporaryHolder){
+			super(wizardComponents, title,next, previous,elements);
+			this.temporaryHolder = temporaryHolder;
+			this.next = next;
+		}
+		
+		@Override
+		public void next(){
+			Model.Element selected = null;
+			for(Model.Element e : elements.values()){
+				if(e.type.value.equals(comboBox.getSelectedItem())){
+					selected = e;
+					break;
+				}
+			}
+			
+			Model.copy(selected, temporaryHolder);
+			switchPanel(next);
+		}
+		
+		int next;
+		Model.Element temporaryHolder;
+	}
+	
+	@SuppressWarnings("serial")
+	private class FormatWizardPanel extends SpeechWizardPanel{
+		FormatWizardPanel(){
+			super(dialog.getWizardComponents(),resources.getString("panel.modifier_format.title"),MODIFIERS,MODIFIER_TYPE);
+			String values[] = {
+					resources.getString("modifier.format.bold"),
+					resources.getString("modifier.format.underline"),
+					resources.getString("modifier.format.italic"),
+					resources.getString("modifier.format.prefix"),
+					resources.getString("modifier.format.suffix"),
+			};
+
+			checkBoxes = new JCheckBox[values.length];
+			checkBoxPanel = new JPanel(new GridLayout(0, 1));
+			for(int i=0; i<values.length;i++){
+				String value = values[i];
+				checkBoxes[i] = new JCheckBox(value);
+				checkBoxes[i].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
+				checkBoxes[i].getActionMap().put("enter", new AbstractAction(){
+					@Override
+					public void actionPerformed(ActionEvent arg0) {
+						getWizardComponents().getNextButton().doClick();
+					}
+				});
+				checkBoxes[i].addItemListener(SpeechUtilities.getCheckBoxSpeechItemListener());
+				/* prefix and suffix check boxes must have a JText area for the user to enter the String */
+				if(i == 3 || i == 4){
+					JPanel panel = new JPanel();
+					panel.setLayout(new FlowLayout(FlowLayout.LEFT,0,0));
+					panel.add(checkBoxes[i]);
+					if(i == 3){
+						prefixTextField = new JTextField();
+						prefixTextField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
+						prefixTextField.getActionMap().put("enter", new AbstractAction(){
+							@Override
+							public void actionPerformed(ActionEvent arg0) {
+								getWizardComponents().getNextButton().doClick();
+							}
+						});
+						prefixTextField.getAccessibleContext().setAccessibleName(checkBoxes[i].getText());
+						prefixTextField.setColumns(5);
+						prefixTextField.addKeyListener(SpeechUtilities.getSpeechKeyListener(true));
+						panel.add(prefixTextField);
+					}else{
+						suffixTextField = new JTextField();
+						suffixTextField.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
+						suffixTextField.getActionMap().put("enter", new AbstractAction(){
+							@Override
+							public void actionPerformed(ActionEvent arg0) {
+								getWizardComponents().getNextButton().doClick();
+							}
+						});
+						suffixTextField.getAccessibleContext().setAccessibleName(checkBoxes[i].getText());
+						suffixTextField.setColumns(5);
+						suffixTextField.addKeyListener(SpeechUtilities.getSpeechKeyListener(true));
+						panel.add(suffixTextField);
+					}
+					checkBoxPanel.add(panel);
+				}else{
+					checkBoxPanel.add(checkBoxes[i]);
+				}
+			}
+			JScrollPane scrollPane = new JScrollPane(checkBoxPanel); 
+			scrollPane.setFocusable(false);
+			layoutComponents(scrollPane);
+			dialog.getWizardComponents().getFinishButton().setEnabled(true);
+		}
+		
+		/* store the checks into the StrArrayRecord, it doesn't call super.next thus */
+		/* sub classes have to implement call the switch panel on their own          */
+		public void next(){
+			int numCheckedBoxes = 0;
+			for(JCheckBox check : checkBoxes){
+				if(check.isSelected())
+					numCheckedBoxes++;
+			}
+			String[] result = new String[numCheckedBoxes];
+			numCheckedBoxes = 0;
+			for(int i=0; i<checkBoxes.length;i++){
+				/* store the text value of the check boxes, if it's the prefix or suffix */
+				/* append the text entered by the user in the text areas                  */
+				if(checkBoxes[i].isSelected()){
+					String text = checkBoxes[i].getText();
+					if(i == 3)
+						modifier.affix.values[PREFIX_INDEX] = prefixTextField.getText();
+					else if(i == 4)
+						modifier.affix.values[SUFFIX_INDEX] = suffixTextField.getText();
+					result[numCheckedBoxes++] = text;
+				}
+			}
+			modifier.format.values = result;
+			Model.Modifier newModifier = new Model.Modifier();
+			Model.copy(modifier,newModifier);
+			property.modifiers.put(newModifier.id,newModifier);
+			super.next();
+		}
+		
+		@Override
+		public void update(){
+			/* set the check boxes and text field according to the modifier.format. so if we are editing an existing *
+			 * modifier we find the old values, else if it's a new modifier we find everything blank                 */
+			if(modifier.format != null){
+				prefixTextField.setText("");
+				suffixTextField.setText("");
+				for(JCheckBox check : checkBoxes){
+					/* temporarily remove the speech Item listener in order to avoid bla bla bla not triggered by user */
+					check.removeItemListener(SpeechUtilities.getCheckBoxSpeechItemListener());
+					check.setSelected(false);
+					for(String checkedValue : modifier.format.values){
+						if(checkedValue.equals(check.getText())){//for bold,italic,underline
+							check.setSelected(true);
+							if(checkedValue.equals(resources.getString("modifier.format.prefix"))){
+								prefixTextField.setText(modifier.affix.values[PREFIX_INDEX]);
+							}else if(checkedValue.equals(resources.getString("modifier.format.suffix"))){
+								suffixTextField.setText(modifier.affix.values[SUFFIX_INDEX]);
+							}
+							break;
+						}
+					}
+					check.addItemListener(SpeechUtilities.getCheckBoxSpeechItemListener());
+				}
+			}
+			super.update();
+		}
+		
+		@Override
+		protected Component assignFocus(){
+			/* focus on the first item */
+			checkBoxes[0].requestFocus();
+			return checkBoxes[0];
+		}
+		
+		JTextField prefixTextField;
+		JTextField suffixTextField;
+		JPanel checkBoxPanel;
+		JCheckBox checkBoxes[];
+	}
+	
+	@SuppressWarnings("serial")
+	private class ArrowHeadPanel extends SpeechWizardPanel {
+		ArrowHeadPanel(){
+			super(dialog.getWizardComponents(),resources.getString("panel.edge_arrow_head.title"),EDGES,EDGE_YESNO_ARROW_HEAD);
+			JPanel panel = new JPanel(new GridBagLayout());
+			final JScrollPane scrollPane = new JScrollPane(panel);
+			scrollPane.setFocusable(false);
+			GridBagUtilities gridBagUtils = new GridBagUtilities(); 
+			int numArrowHeads = ArrowHead.values().length;
+			arrowsCheckBoxes = new JCheckBox[numArrowHeads];
+			arrowsTextDescriptions = new JTextField[numArrowHeads];
+			for(int i=0; i<numArrowHeads; i++){
+				/* set up the key bindings for all the check boxes and text fields */
+				/* by pressing enter the wizard switches the next panel            */
+				arrowsCheckBoxes[i] = new JCheckBox(ArrowHead.values()[i].toString());
+				arrowsCheckBoxes[i].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
+				arrowsCheckBoxes[i].getActionMap().put("enter", new AbstractAction(){
+					@Override
+					public void actionPerformed(ActionEvent arg0) {
+						getWizardComponents().getNextButton().doClick();
+					}
+				});
+				arrowsTextDescriptions[i] = new JTextField();
+				arrowsTextDescriptions[i].getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER,0), "enter");
+				arrowsTextDescriptions[i].getActionMap().put("enter", new AbstractAction(){
+					@Override
+					public void actionPerformed(ActionEvent arg0) {
+						getWizardComponents().getNextButton().doClick();
+					}
+				});
+				/* add the speech to the check boxes */
+				arrowsCheckBoxes[i].addItemListener(SpeechUtilities.getCheckBoxSpeechItemListener());
+				
+				arrowsTextDescriptions[i].setPreferredSize(new Dimension(TEXTFIELD_SIZE, arrowsTextDescriptions[i].getPreferredSize().height));
+				arrowsTextDescriptions[i].getAccessibleContext().setAccessibleName(arrowsCheckBoxes[i].getText());
+				arrowsTextDescriptions[i].addKeyListener(SpeechUtilities.getSpeechKeyListener(true));
+				panel.add(arrowsCheckBoxes[i], gridBagUtils.label());
+				panel.add(arrowsTextDescriptions[i],gridBagUtils.field());
+			}
+			layoutComponents(scrollPane);
+		}
+		
+		@Override
+		public void update(){
+			/* restore the values (checkbox + text) currently in edge.arrowHeads into the panel components */
+			if(edge.arrowHeads != null){
+				for(int i=0; i<arrowsCheckBoxes.length;i++){
+					arrowsCheckBoxes[i].setSelected(false);
+					arrowsTextDescriptions[i].setText("");
+					for(int j=0; j< edge.arrowHeads.values.length; j++){
+						if(arrowsCheckBoxes[i].getText().equals(edge.arrowHeads.values[j])){
+							arrowsCheckBoxes[i].setSelected(true);
+							arrowsTextDescriptions[i].setText(edge.arrowHeadsDescriptions.values[j]);
+							break;
+						}
+					}
+				}
+			}
+			super.update();
+		}
+		@Override
+		public void next(){
+			/* check that the user has entered a text for all of the selected check boxes */
+			int numChecked = 0;//this is to keep count of the checked boxes, used after the check 
+			for(int i=0; i<arrowsCheckBoxes.length;i++){
+				JCheckBox checkBox = arrowsCheckBoxes[i];
+				if(checkBox.isSelected()){
+					numChecked++;
+					/* there cannot be a checked check box without the related textField filled in */
+					if(arrowsTextDescriptions[i].getText().trim().isEmpty()){
+						NarratorFactory.getInstance().speak(
+								MessageFormat.format(
+										resources.getString("dialog.error.empty_desc"),
+										checkBox.getText())
+						);		
+						return;
+					}
+				}
+			}
+			/* copy the label of the checked boxes and the text of the JTextField into the edge fields */
+			edge.arrowHeads.values = new String[numChecked];
+			edge.arrowHeadsDescriptions.values = new String[numChecked];
+			numChecked = 0;
+			for(int i=0; i<arrowsCheckBoxes.length;i++){
+				if(arrowsCheckBoxes[i].isSelected()){
+					edge.arrowHeads.values[numChecked] = arrowsCheckBoxes[i].getText();
+					edge.arrowHeadsDescriptions.values[numChecked] = arrowsTextDescriptions[i].getText().trim();
+					numChecked++;
+				}
+			}
+			/* put the edge (copy of) into the model */
+			Model.Edge newEdge = new Model.Edge();
+			Model.copy(edge, newEdge);
+			model.edges.put(newEdge.id,newEdge);
+			super.next();
+		}
+		
+		@Override
+		protected Component assignFocus(){
+			/* focus on the first item */
+			arrowsCheckBoxes[0].requestFocus();
+			return arrowsCheckBoxes[0];
+		}
+		
+		JCheckBox arrowsCheckBoxes[];
+		JTextField arrowsTextDescriptions[];
+		final int TEXTFIELD_SIZE = 100;
+	}
+}