fiore@0: /*
fiore@0: CCmI Editor - A Collaborative Cross-Modal Diagram Editing Tool
fiore@0:
fiore@0: Copyright (C) 2011 Queen Mary University of London (http://ccmi.eecs.qmul.ac.uk/)
fiore@0:
fiore@0: This program is free software: you can redistribute it and/or modify
fiore@0: it under the terms of the GNU General Public License as published by
fiore@0: the Free Software Foundation, either version 3 of the License, or
fiore@0: (at your option) any later version.
fiore@0:
fiore@0: This program is distributed in the hope that it will be useful,
fiore@0: but WITHOUT ANY WARRANTY; without even the implied warranty of
fiore@0: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
fiore@0: GNU General Public License for more details.
fiore@0:
fiore@0: You should have received a copy of the GNU General Public License
fiore@0: along with this program. If not, see .
fiore@0: */
fiore@0:
fiore@0: package uk.ac.qmul.eecs.ccmi.simpletemplate;
fiore@0:
fiore@0: import java.awt.Component;
fiore@0: import java.awt.Dimension;
fiore@0: import java.awt.FlowLayout;
fiore@0: import java.awt.Frame;
fiore@0: import java.awt.GridBagLayout;
fiore@0: import java.awt.GridLayout;
fiore@0: import java.awt.event.ActionEvent;
fiore@0: import java.awt.event.KeyEvent;
fiore@0: import java.io.IOException;
fiore@0: import java.text.MessageFormat;
fiore@0: import java.util.ArrayList;
fiore@0: import java.util.Arrays;
fiore@0: import java.util.Collection;
fiore@0: import java.util.LinkedHashMap;
fiore@0: import java.util.LinkedHashSet;
fiore@0: import java.util.ResourceBundle;
fiore@0: import java.util.Set;
fiore@0:
fiore@0: import javax.swing.AbstractAction;
fiore@0: import javax.swing.DefaultComboBoxModel;
fiore@0: import javax.swing.JCheckBox;
fiore@0: import javax.swing.JComboBox;
fiore@0: import javax.swing.JComponent;
fiore@0: import javax.swing.JPanel;
fiore@0: import javax.swing.JScrollPane;
fiore@0: import javax.swing.JTextField;
fiore@0: import javax.swing.KeyStroke;
fiore@0: import javax.swing.SpinnerModel;
fiore@0:
fiore@0: import jwizardcomponent.FinishAction;
fiore@0: import jwizardcomponent.JWizardComponents;
fiore@0: import jwizardcomponent.JWizardPanel;
fiore@0: import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties;
fiore@0: import uk.ac.qmul.eecs.ccmi.diagrammodel.NodeProperties.Modifiers;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.Diagram;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.Edge;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.LineStyle;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.LoopComboBox;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.LoopSpinnerNumberModel;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.Node;
fiore@0: import uk.ac.qmul.eecs.ccmi.gui.SpeechSummaryPane;
fiore@0: import uk.ac.qmul.eecs.ccmi.simpletemplate.SimpleShapeNode.ShapeType;
fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundEvent;
fiore@0: import uk.ac.qmul.eecs.ccmi.sound.SoundFactory;
fiore@0: import uk.ac.qmul.eecs.ccmi.speech.NarratorFactory;
fiore@0: import uk.ac.qmul.eecs.ccmi.speech.SpeechUtilities;
fiore@0: import uk.ac.qmul.eecs.ccmi.utils.GridBagUtilities;
fiore@0:
fiore@0: /**
fiore@0: *
fiore@0: * 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)
fiore@0: * how to build a template diagram. A template diagram is a prototype diagram
fiore@0: * (containing prototype nodes and edges) which can later on be used for creating instances
fiore@0: * of that type of diagram through clonation. The wizard is completely accessible via audio
fiore@0: * as all the content and all focused components names are spoken out by the {@link Narrator} through a text to speech synthesizer.
fiore@0: *
fiore@0: */
fiore@0: public class Wizard {
fiore@0: public Wizard(Frame frame, Collection existingDiagrams, Diagram diagramToEdit){
fiore@0: dialog = new SpeechWizardDialog(frame);
fiore@0: resources = ResourceBundle.getBundle(SpeechWizardDialog.class.getName());
fiore@0:
fiore@0: model = createModel(diagramToEdit);
fiore@0: node = new Model.Node();
fiore@0: edge = new Model.Edge();
fiore@0: property = new Model.Property();
fiore@0: modifier = new Model.Modifier();
fiore@0:
fiore@0: initWizardComponents(existingDiagrams,diagramToEdit);
fiore@0:
fiore@0: /* if the user is editing from an existing diagram, they have to choose a new name. They're switched *
fiore@0: * directly to the diagram name panel so they have to enter a new name as they would otherwise *
fiore@0: * not be allowed to proceed. */
fiore@0: if(diagramToEdit != null)
fiore@0: dialog.getWizardComponents().setCurrentIndex(DIAGRAM_NAME);
fiore@0:
fiore@0: /* when the user clicks on the finish button they'll be prompted with a summary text area dialog *
fiore@0: * describing what they have created so far and asking for a confirmation to proceed with the actual *
fiore@0: * creation of the template. */
fiore@0: dialog.getWizardComponents().setFinishAction(new FinishAction(dialog.getWizardComponents()){
fiore@0: @Override
fiore@0: public void performAction(){
fiore@0: String[] options = {
fiore@0: resources.getString("dialog.summary.ok_button_label"),
fiore@0: resources.getString("dialog.summary.cancel_button_label")};
fiore@0: int result = SpeechSummaryPane.showDialog(
fiore@0: dialog,
fiore@0: resources.getString("dialog.summary.title"),
fiore@0: model.toString(),
fiore@0: SpeechSummaryPane.OK_CANCEL_OPTION,
fiore@0: options);
fiore@0:
fiore@0: if(result == SpeechSummaryPane.CANCEL){ // user wants to continue editing
fiore@0: SoundFactory.getInstance().play(SoundEvent.CANCEL);
fiore@0: return;
fiore@0: }
fiore@0: /* create the actual diagram (it will be return to the client class by execute()) */
fiore@0: diagram = createDiagram(model);
fiore@0: dialog.dispose();
fiore@0: }
fiore@0: });
fiore@0: }
fiore@0:
fiore@0: public Wizard(Frame frame, Collection existingDiagrams){
fiore@0: this(frame,existingDiagrams,null);
fiore@0: }
fiore@0:
fiore@0: public Diagram execute(){
fiore@0: diagram = null;
fiore@0: dialog.show();
fiore@0: if(diagram == null)
fiore@0: SoundFactory.getInstance().play(SoundEvent.CANCEL);
fiore@0: return diagram;
fiore@0: }
fiore@0:
fiore@0: @SuppressWarnings("serial")
fiore@0: private void initWizardComponents(Collection existingDiagrams, final Diagram diagramToEdit){
fiore@0: /* --- MAIN PANEL --- */
fiore@0: String[] choices = {
fiore@0: resources.getString("panel.home.choice.diagram_name"),
fiore@0: resources.getString("panel.home.choice.nodes"),
fiore@0: resources.getString("panel.home.choice.edges"),
fiore@0: };
fiore@0: int[] nexts = {DIAGRAM_NAME,NODES,EDGES};
fiore@0:
fiore@0: /* panel for the selection of main tasks when creating the diagram: enter diagram name, create node and *
fiore@0: * create edge. When a name is assigned an item is added to the selection which allows the user to finish *
fiore@0: * the template creation, much as they would do by pressing the finish button. If the user edits an existing *
fiore@0: * diagram they're prompted with a message to enter a new diagram name (as there cannot be two diagrams *
fiore@0: * with the same name. When the user enters the name the message goes away */
fiore@0: add(HOME,new SelectWizardPanel(
fiore@0: dialog.getWizardComponents(),
fiore@0: resources.getString("panel.home.title.new"),
fiore@0: Arrays.asList(choices),
fiore@0: nexts,
fiore@0: SpeechWizardPanel.DISABLE_SWITCH
fiore@0: ){
fiore@0: @Override
fiore@0: public void update(){
fiore@0: if(!model.diagramName.value.isEmpty()){
fiore@0: dialog.setFinishButtonEnabled(true);
fiore@0: /* if the diagram has a name the template creation can finish. So add a selection item to the *
fiore@0: * comboBox unless it's already there from a previous update (item count < 4 ) */
fiore@0: if(comboBox.getItemCount() < 4)
fiore@0: ((DefaultComboBoxModel)comboBox.getModel()).addElement(resources.getString("panel.home.choice.finish"));
fiore@0: }
fiore@0: super.update();
fiore@0: }
fiore@0: @Override
fiore@0: public void next(){
fiore@0: if(comboBox.getSelectedIndex() == 3)
fiore@0: dialog.getWizardComponents().getFinishButton().doClick();
fiore@0: else
fiore@0: super.next();
fiore@0: }
fiore@0: });
fiore@0:
fiore@0: /* --- DIAGRAM NAME INPUT PANEL --- */
fiore@0: add(DIAGRAM_NAME, new TextWizardPanel(
fiore@0: dialog.getWizardComponents(),
fiore@0: resources.getString(diagramToEdit == null ? "panel.diagram_name.title" : "panel.diagram_name.title.editing_existing_diagram"),
fiore@0: existingDiagrams,
fiore@0: HOME,
fiore@0: HOME,
fiore@0: model.diagramName
fiore@0: ){
fiore@0: @Override
fiore@0: public void update(){
fiore@0: /* this is a little nasty trick to achieve the following: when the user creates a new diagram out of an already existing
fiore@0: * one they're directly prompted with this panel. We want the name of the diagram to be there for awareness
fiore@0: * but at the same time it must not be accepted by the program as it would otherwise clash with
fiore@0: * with the starting diagam's. As the program accepts it when the text entered in the text field is equal to
fiore@0: * model.diagramName.value (for when the user wants to re-edit the name of a diagram they're creating) we must
fiore@0: * fill model.DiagramName.value with the name of the starting diagram to get it shown and spoken out but then
fiore@0: * it's assigned the empty string not to let the user to go forward */
fiore@0: if(diagramToEdit != null && model.diagramName.value.isEmpty()){
fiore@0: model.diagramName.value = diagramToEdit.getName();
fiore@0: super.update();
fiore@0: model.diagramName.value = "";
fiore@0: }else{
fiore@0: super.update();
fiore@0: }
fiore@0: }
fiore@0: });
fiore@0:
fiore@0: /* --- NODE ACTION SELECTION PANEL --- */
fiore@0: /* decide whether to add a new node or to edit/delete an existing node */
fiore@0: String[] nodeOptions = {
fiore@0: resources.getString("panel.nodes.actions.add"),
fiore@0: resources.getString("panel.nodes.actions.edit"),
fiore@0: resources.getString("panel.nodes.actions.del"),
fiore@0: resources.getString("panel.nodes.actions.finish")};
fiore@0: int[] nodeNexts = {NODE_TYPE,NODE_EDIT,NODE_DEL,HOME};
fiore@0: add(NODES, new ActionChooserPanel(
fiore@0: dialog.getWizardComponents(),
fiore@0: model.nodes.getNames(),
fiore@0: resources.getString("panel.nodes.title"),
fiore@0: nodeOptions,
fiore@0: nodeNexts,
fiore@0: HOME,
fiore@0: node
fiore@0: ));
fiore@0:
fiore@0: /* --- NODE TYPE NAME INPUT PANEL --- */
fiore@0: add(NODE_TYPE,new TextWizardPanel(
fiore@0: dialog.getWizardComponents(),
fiore@0: resources.getString("panel.node_name.title"),
fiore@0: model.nodes.getNames(),
fiore@0: NODE_SHAPE,
fiore@0: NODES,
fiore@0: node.type
fiore@0: ));
fiore@0:
fiore@0: /* --- NODE TO DELETE SELECTION PANEL*/
fiore@0: add(NODE_DEL, new DeletePanel(
fiore@0: dialog.getWizardComponents(),
fiore@0: resources.getString("panel.node_del.title"),
fiore@0: NODES,
fiore@0: NODES,
fiore@0: model.nodes));
fiore@0:
fiore@0: /* -- NODE TO EDIT SELECTION PANEL */
fiore@0: add(NODE_EDIT, new EditPanel(
fiore@0: dialog.getWizardComponents(),
fiore@0: resources.getString("panel.node_edit.title"),
fiore@0: NODE_TYPE,
fiore@0: NODES,
fiore@0: model.nodes,
fiore@0: node
fiore@0: ));
fiore@0:
fiore@0: ShapeType[] shapeTypes = ShapeType.values();
fiore@0: ArrayList shapeTypeNames = new ArrayList(shapeTypes.length);
fiore@0: for(int i=0; i positionNames = new ArrayList(positions.length);
fiore@0: for(int i=0; i max){
fiore@0: NarratorFactory.getInstance().speak(resources.getString("dialog.error.min_max"));
fiore@0: }else{
fiore@0: super.next();
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: });
fiore@0:
fiore@0: /* --- SELECT WHETHER THE EDGE MUST HAVE ARROW HEADS OR NOT --- */
fiore@0: String[] arrowHeadOptions = {
fiore@0: resources.getString("panel.edge_yesno_arrow_head.actions.add"),
fiore@0: resources.getString("panel.edge_yesno_arrow_head.actions.finish")
fiore@0: };
fiore@0: int[] arrowHeadNexts = {EDGE_ARROW_HEAD,EDGES};
fiore@0: add(EDGE_YESNO_ARROW_HEAD,new SelectWizardPanel(
fiore@0: dialog.getWizardComponents(),
fiore@0: resources.getString("panel.edge_yesno_arrow_head.title"),
fiore@0: Arrays.asList(arrowHeadOptions),
fiore@0: arrowHeadNexts,
fiore@0: EDGE_MAX_NODES
fiore@0: ){
fiore@0: @Override
fiore@0: public void next(){
fiore@0: if(comboBox.getSelectedIndex() == 1){
fiore@0: Model.Edge newEdge = new Model.Edge();
fiore@0: Model.copy(edge, newEdge);
fiore@0: model.edges.put(newEdge.id,newEdge);
fiore@0: }
fiore@0: super.next();
fiore@0: }
fiore@0: });
fiore@0:
fiore@0: /* --- ARROW HEAD SELECTION PANEL --- */
fiore@0: add(EDGE_ARROW_HEAD, new ArrowHeadPanel());
fiore@0:
fiore@0: add(LAST_PANEL, new DummyWizardPanel(dialog.getWizardComponents()));
fiore@0:
fiore@0: SpeechUtilities.changeTabListener((JComponent)dialog.getContentPane(), dialog);
fiore@0: }
fiore@0:
fiore@0: private void add(int index, JWizardPanel panel){
fiore@0: dialog.getWizardComponents().addWizardPanel(index,panel);
fiore@0: }
fiore@0:
fiore@0: private Diagram createDiagram(Model model){
fiore@0: /* create the node prototypes */
fiore@0: Node[] nodes = new Node[model.nodes.size()];
fiore@0: int i = 0;
fiore@0: for(Model.Node n : model.nodes.values()){
fiore@0: nodes[i] = createDiagramNode(n);
fiore@0: i++;
fiore@0: }
fiore@0: /* create the edge prototypes */
fiore@0: Edge[] edges = new Edge[model.edges.size()];
fiore@0: i = 0;
fiore@0: for(Model.Edge e : model.edges.values()){
fiore@0: edges[i] = createDiagramEdge(e);
fiore@0: i++;
fiore@0: }
fiore@0: return Diagram.newInstance(model.diagramName.value, nodes, edges, new SimpleShapePrototypePersistenceDelegate());
fiore@0: }
fiore@0:
fiore@0: private Node createDiagramNode(Model.Node n){
fiore@0: /* set up the properties */
fiore@0: LinkedHashMap> propertiesTypeDefinition = new LinkedHashMap>();
fiore@0: /* create the property type definition */
fiore@0: for(Model.Property modelProperty : n.properties.values()){
fiore@0: Set modifiersTypeDefinition = new LinkedHashSet();
fiore@0: for(Model.Modifier modifier : modelProperty.modifiers.values())
fiore@0: modifiersTypeDefinition.add(modifier.type.value);
fiore@0: propertiesTypeDefinition.put(modelProperty.type.value, modifiersTypeDefinition);
fiore@0: }
fiore@0: NodeProperties properties = new NodeProperties(propertiesTypeDefinition);
fiore@0: /* now that properties object is created attach the views on it */
fiore@0: for(Model.Property modelProperty : n.properties.values()){
fiore@0: PropertyView propertyView = new PropertyView(
fiore@0: SimpleShapeNode.Position.valueOf(modelProperty.position.value),
fiore@0: modelProperty.shape.value.isEmpty() ?
fiore@0: /* doesn't really matter as position is inside and shape won't be taken into account */
fiore@0: SimpleShapeNode.ShapeType.Rectangle :
fiore@0: SimpleShapeNode.ShapeType.valueOf(modelProperty.shape.value)
fiore@0: );
fiore@0: properties.setView(modelProperty.type.value, propertyView);
fiore@0: /* modifier view */
fiore@0: for(Model.Modifier modelModifier : modelProperty.modifiers.values()){
fiore@0: boolean bold = false;
fiore@0: boolean italic = false;
fiore@0: boolean underline = false;
fiore@0: String prefix = "";
fiore@0: String suffix = "";
fiore@0: for(String value : modelModifier.format.values){
fiore@0: if(value.equals(resources.getString("modifier.format.bold"))){
fiore@0: bold = true;
fiore@0: }else if(value.equals(resources.getString("modifier.format.underline"))){
fiore@0: underline = true;
fiore@0: }else if(value.equals(resources.getString("modifier.format.italic"))){
fiore@0: italic = true;
fiore@0: }else if(value.equals(resources.getString("modifier.format.prefix"))){
fiore@0: prefix = modelModifier.affix.values[PREFIX_INDEX];
fiore@0: }else if(value.equals(resources.getString("modifier.format.suffix"))){
fiore@0: suffix = modelModifier.affix.values[SUFFIX_INDEX];
fiore@0: }
fiore@0: }
fiore@0: ModifierView modifierView = new ModifierView(underline,bold,italic,prefix,suffix);
fiore@0: properties.getModifiers(modelProperty.type.value).setView(modelModifier.type.value, modifierView);
fiore@0: }
fiore@0: }
fiore@0: return SimpleShapeNode.getInstance(
fiore@0: SimpleShapeNode.ShapeType.valueOf(n.shape.value),
fiore@0: n.type.value,
fiore@0: properties);
fiore@0: }
fiore@0:
fiore@0: private Edge createDiagramEdge(Model.Edge e){
fiore@0: /* create the arrow head array out of the string stored in the model */
fiore@0: ArrowHead[] arrowHeads = new ArrowHead[e.arrowHeads.values.length];
fiore@0: for(int i=0; i elementNames, String title, String[] options, int[] nexts, int previous, Model.Element temporaryElement){
fiore@0: super(wizardComponents,title,OWN_SWITCH, previous);
fiore@0: this.options = options;
fiore@0: comboBoxModel = new DefaultComboBoxModel();
fiore@0: comboBoxModel.addElement(options[0]);
fiore@0: comboBoxModel.addElement(options[3]);
fiore@0: comboBox = new LoopComboBox(comboBoxModel);
fiore@0: comboBox.addItemListener(SpeechUtilities.getSpeechComboBoxItemListener());
fiore@0: layoutComponents(comboBox);
fiore@0: this.elementNames = elementNames;
fiore@0: this.temporaryElement = temporaryElement;
fiore@0: this.nexts = nexts;
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@0: public void update(){
fiore@0: if(elementNames.isEmpty() && comboBoxModel.getSize() == 4){
fiore@0: comboBoxModel.removeElement(options[1]);
fiore@0: comboBoxModel.removeElement(options[2]);
fiore@0: }else if(!elementNames.isEmpty() && comboBoxModel.getSize() == 2){
fiore@0: comboBoxModel.insertElementAt(options[1],1);
fiore@0: comboBoxModel.insertElementAt(options[2],2);
fiore@0: }
fiore@0: super.update();
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@0: public void next(){
fiore@0: /* if the selection was add element, then we clear the temporary holder */
fiore@0: if(comboBox.getSelectedIndex() == 0)
fiore@0: temporaryElement.clear();
fiore@0: /* jump to the selected next step, works both when it's only add/finish and when it's add/delete/edit/finish */
fiore@0: for(int i=0; i elementNames;
fiore@0: DefaultComboBoxModel comboBoxModel;
fiore@0: String[] options;
fiore@0: Model.Element temporaryElement;
fiore@0: int[] nexts;
fiore@0: }
fiore@0:
fiore@0: @SuppressWarnings("serial")
fiore@0: private static class DeletePanel extends SpeechWizardPanel {
fiore@0: DeletePanel(JWizardComponents wizardComponents,String title, int next, int previous, ModelMap extends Model.Element> elements){
fiore@0: super(wizardComponents,title,next, previous);
fiore@0: this.elements = elements;
fiore@0: comboBox = new LoopComboBox();
fiore@0: comboBox.addItemListener(SpeechUtilities.getSpeechComboBoxItemListener());
fiore@0: layoutComponents(comboBox);
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@0: public void update(){
fiore@0: String[] options = new String[elements.values().size()];
fiore@0: options = elements.getNames().toArray(options);
fiore@0: comboBox.setModel(new DefaultComboBoxModel(options));
fiore@0: super.update();
fiore@0: }
fiore@0:
fiore@0: /**
fiore@0: * the default behaviour is to delete the selected element
fiore@0: */
fiore@0: @Override
fiore@0: public void next(){
fiore@0: Model.Element elementToDelete = null;
fiore@0: for(Model.Element element : elements.values()){
fiore@0: if(element.type.value.equals(comboBox.getSelectedItem())){
fiore@0: elementToDelete = element;
fiore@0: break;
fiore@0: }
fiore@0: }
fiore@0: Object o = elements.remove(elementToDelete.id);
fiore@0: assert(o != null);
fiore@0: super.next();
fiore@0: }
fiore@0:
fiore@0: JComboBox comboBox;
fiore@0: ModelMap extends Model.Element> elements;
fiore@0: }
fiore@0:
fiore@0: @SuppressWarnings("serial")
fiore@0: private static class EditPanel extends DeletePanel {
fiore@0: EditPanel(JWizardComponents wizardComponents,
fiore@0: String title,
fiore@0: int next,
fiore@0: int previous,
fiore@0: ModelMap extends Model.Element> elements,
fiore@0: Model.Element temporaryHolder){
fiore@0: super(wizardComponents, title,next, previous,elements);
fiore@0: this.temporaryHolder = temporaryHolder;
fiore@0: this.next = next;
fiore@0: }
fiore@0:
fiore@0: @Override
fiore@0: public void next(){
fiore@0: Model.Element selected = null;
fiore@0: for(Model.Element e : elements.values()){
fiore@0: if(e.type.value.equals(comboBox.getSelectedItem())){
fiore@0: selected = e;
fiore@0: break;
fiore@0: }
fiore@0: }
fiore@0:
fiore@0: Model.copy(selected, temporaryHolder);
fiore@0: switchPanel(next);
fiore@0: }
fiore@0:
fiore@0: int next;
fiore@0: Model.Element temporaryHolder;
fiore@0: }
fiore@0:
fiore@0: @SuppressWarnings("serial")
fiore@0: private class FormatWizardPanel extends SpeechWizardPanel{
fiore@0: FormatWizardPanel(){
fiore@0: super(dialog.getWizardComponents(),resources.getString("panel.modifier_format.title"),MODIFIERS,MODIFIER_TYPE);
fiore@0: String values[] = {
fiore@0: resources.getString("modifier.format.bold"),
fiore@0: resources.getString("modifier.format.underline"),
fiore@0: resources.getString("modifier.format.italic"),
fiore@0: resources.getString("modifier.format.prefix"),
fiore@0: resources.getString("modifier.format.suffix"),
fiore@0: };
fiore@0:
fiore@0: checkBoxes = new JCheckBox[values.length];
fiore@0: checkBoxPanel = new JPanel(new GridLayout(0, 1));
fiore@0: for(int i=0; i