Mercurial > hg > ccmiandroid
view src/uk/ac/qmul/eecs/ccmi/activities/TreeNavigation.java @ 0:e0ee6ac3a45f
first import
author | Fiore Martin <fiore@eecs.qmul.ac.uk> |
---|---|
date | Thu, 13 Dec 2012 20:00:21 +0000 |
parents | |
children | 66b3a838feca |
line wrap: on
line source
/* CCmI Diagram Editor for Android Copyright (C) 2012 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.activities; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService; import uk.ac.qmul.eecs.ccmi.accessibility.AccessibilityService.SoundEvent; import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleCheckbox; import uk.ac.qmul.eecs.ccmi.accessibility.AccessibleDialogBuilder; import uk.ac.qmul.eecs.ccmi.utilities.Stack; import uk.ac.qmul.eecs.ccmi.xmlparser.Diagram; import uk.ac.qmul.eecs.ccmi.xmlparser.DiagramUpdater; import uk.ac.qmul.eecs.ccmi.xmlparser.Edge; import uk.ac.qmul.eecs.ccmi.xmlparser.EdgeNode; import uk.ac.qmul.eecs.ccmi.xmlparser.EdgeType; import uk.ac.qmul.eecs.ccmi.xmlparser.HierarchyItem; import uk.ac.qmul.eecs.ccmi.xmlparser.LocalDiagramUpdater; import uk.ac.qmul.eecs.ccmi.xmlparser.Modifier; import uk.ac.qmul.eecs.ccmi.xmlparser.Node; import uk.ac.qmul.eecs.ccmi.xmlparser.NodeProperty; import uk.ac.qmul.eecs.ccmi.xmlparser.NodePropertyType; import uk.ac.qmul.eecs.ccmi.xmlparser.NodeType; import uk.ac.qmul.eecs.ccmi.xmlparser.PropertyValue; import android.support.v4.app.DialogFragment; import android.view.View; import android.widget.AdapterView; import android.widget.EditText; import android.widget.Spinner; /** * Provides a navigation of the model (the diagram) based on the tree hierarchy of the * <a href="http://ccmi.eecs.qmul.ac.uk">CCmI Diagram Editor</a> * */ class TreeNavigation { private static final String RENAME_DIALOG_TAG = "Rename "; private static final String EDIT_NODE_DIALOG_TAG = "Edit Node Dialog"; private static final String EDIT_EDGE_DIALOG_TAG = "Edit Edge Dialog"; private static final String EDIT_NODE_REF_DIALOG_TAG = "Edit Node Reference Dialog"; private static final String EDIT_LABEL_DIALOG_TAG = "Edit Label"; private static final String EDIT_ARROWHEAD_DIALOG_TAG = "Edit Arrow Head Dialog"; private static final String CONFIRMATION_DIALOG_TAG = "Are you sure you want to delete"; private static final String ADD_PROPERTY_DIALOG_TAG = "Add Property Dialog"; private static final String EDIT_PROPERTY_DIALOG_TAG = "Edit Property Dialog"; private static final String EDIT_MODIFIERS_DIALOG_TAG = "Edit Modifiers Dialog"; private Diagram diagram; private DiagramUpdater diagramUpdater; private Stack<HierarchyItem> path; private List<Node> selectedNodes = new ArrayList<Node>(4); /* cached list is filled up when next() and previous() and used to undo the navigation * if the list created is empty. Once created, then it's returned by getCurrentList() */ private String[] cachedList; private Stack<String> cachedHeaderTexts; /* controller for edit actions with access to the current status of the navigation */ private Controller controller; private Updateable updateable; /* the level currently displayed. Level item's toString() is on the header * * text and its children are displayed in the currentList */ private final static int PATH_MAX_LEN = 5; public final static int DIAGRAM_LEVEL = 0; public final static int TYPE_LEVEL = 1; public final static int ITEM_LEVEL = 2; public final static int REFERENCE_AND_PROPERTY_TYPE_LEVEL = 3; public final static int PROPERTY_AND_EDGE_REFERENCE_LEVEL = 4; // neved used public TreeNavigation(Diagram diagram, AccessibleDialogBuilder dialogBuilder, Updateable updateable){ this.diagram = diagram; this.updateable = updateable; diagramUpdater = new LocalDiagramUpdater(diagram); path = new Stack<HierarchyItem>(PATH_MAX_LEN); cachedHeaderTexts = new Stack<String>(PATH_MAX_LEN); /* tree path starts with top node */ path.push(diagram); cachedHeaderTexts.push(diagram.toString()); cachedList = buildCurrentChildList(); controller = new Controller(dialogBuilder); } public String[] getCurrentChildList(){ return cachedList; } public String getCurrentPath(){ StringBuilder builder = new StringBuilder(); for(HierarchyItem item : path){ builder.append('/').append(item); } return builder.toString(); } /* builds the String list of items to display when getCurrentListIsCalled */ private String[] buildCurrentChildList(){ String[] currentList = null; switch(path.level()){ case DIAGRAM_LEVEL :{ List<NodeType> nodeTypes = diagram.getPrototypes().getNodeTypes(); List<EdgeType> edgeTypes = diagram.getPrototypes().getEdgeTypes(); int numNodeTypes = nodeTypes.size(); int numEdgeTypes = edgeTypes.size(); currentList = new String[numNodeTypes+numEdgeTypes]; /* build the string for each node type */ StringBuilder builder = new StringBuilder(); for(int i=0; i<numNodeTypes;i++){ builder.setLength(0); builder.append(nodeTypes.get(i).getType()) .append(' ') .append('(') .append(count(nodeTypes.get(i))) .append(')'); currentList[i] = builder.toString(); } /* build the string for each edge type */ for(int j=0; j<numEdgeTypes; j++){ builder.setLength(0); builder.append(edgeTypes.get(j).getType()) .append(' ') .append('(') .append(count(edgeTypes.get(j))) .append(')'); currentList[numNodeTypes+j] = builder.toString(); } break; } case TYPE_LEVEL : { /* list all objects of a certain type */ LinkedList<String> items = new LinkedList<String>(); if(path.current() instanceof NodeType){ for(Node cNode : diagram.getComponents().getNodes()){ if(path.current().toString().equals(cNode.getType())) if(selectedNodes.contains(cNode)) items.add("<"+cNode+">"); // <selected node> else items.add(cNode.getName()); } }else{ for(Edge cEdge : diagram.getComponents().getEdges()){ if(path.current().toString().equals(cEdge.getType())) items.add(cEdge.getName()); } } currentList = items.toArray(new String[items.size()]); break; } case ITEM_LEVEL : { Map<String,Integer> edgeTypes = new LinkedHashMap<String,Integer>(); if(path.current() instanceof Node){ /* node displays the list of edge types connected to * * itself + the list of property types defined for itself */ Node node = (Node)path.current(); /* add the edge types of the edges the node is attached to */ for(Edge cEdge : diagram.getComponents().getEdges()){ for(EdgeNode attachedNode : cEdge.getAttachedNodes()){ if(attachedNode.getId() == node.getId()){ if(!edgeTypes.containsKey(cEdge.getType())){ edgeTypes.put(cEdge.getType(),1); }else{ edgeTypes.put(cEdge.getType(),edgeTypes.get(cEdge.getType())+1); } break; } } } /* build the array to return : first part is the edge types. second part property types / /* now the property types this node can have */ currentList = new String[edgeTypes.size()+node.getProperties().size()]; int i = 0; StringBuilder builder = new StringBuilder(); for(Map.Entry<String, Integer> entry : edgeTypes.entrySet()){ builder.setLength(0); currentList[i++] = builder.append(entry.getKey()) .append(' ') .append('(') .append(entry.getValue()) .append(')').toString(); } for(int j=i;j<currentList.length;j++){ builder.setLength(0); currentList[j] = builder.append(node.getProperties().get(j-i).getType()) .append(' ') .append('(') .append(node.getProperties().get(j-i).getValues().size()) .append(')').toString(); } }else{ // Edge /* edge displays the list of nodes attached to self */ Edge cEdge = (Edge)path.current(); currentList = new String[cEdge.getAttachedNodes().size()]; int i = 0; for(EdgeNode attachedNode : cEdge.getAttachedNodes()){ for(Node node : diagram.getComponents().getNodes()){ if(node.getId() == attachedNode.getId()){ currentList[i++] = (attachedNode.getHead()+" "+node.getName()+" "+attachedNode.getLabel()).trim(); } } } } break; } case REFERENCE_AND_PROPERTY_TYPE_LEVEL : { /* display the values of the selected property types */ if(path.current() instanceof NodeProperty){ // property type is displayed on the header NodeProperty property = (NodeProperty)path.current(); currentList = new String[property.getValues().size()]; NodeType nodeType = (NodeType)path.get(TYPE_LEVEL); /* for each property value build the string "mod1 mod2 mod3...value" * * where modn is the modifier type and value is the property value */ StringBuilder builder = new StringBuilder(); for(int i=0; i<currentList.length; i++){ builder.setLength(0); List<Integer> modifierIndexes = property.getValues().get(i).getModifiersIndexes(); for(NodePropertyType propertyType : nodeType.getPropretyTypes()){ if(propertyType.getType().equals(property.getType())){ ArrayList<Modifier> modifiers = propertyType.getModifiers(); for(Integer j : modifierIndexes){ builder.append(modifiers.get(j).getType()).append(' '); } } } currentList[i] = builder.append(property.getValues().get(i).getValue()).toString(); } // NodeProperty propertyType = (NodeProperty)path.current(); // Node node = (Node)path.get(ITEM_LEVEL); // for(NodeProperty property : node.getProperties()){ // if(property.getType().equals(propertyType.getType())){ // currentList = new String[property.getValues().size()]; // for(int i=0; i<currentList.length; i++){ // currentList[i] = property.getValues().get(i).getValue(); // } // break; // } // } }else{ // instance of EdgeType EdgeType edgeType = (EdgeType)path.current(); LinkedList<String> currentListItems = new LinkedList<String>(); int nodeID = ((Node)path.get(ITEM_LEVEL)).getId(); StringBuilder builder = new StringBuilder(); for(Edge edge : diagram.getComponents().getEdges()){ if(edge.getType().equals(edgeType.getType())){ for(EdgeNode attachedNode : edge.getAttachedNodes()){ if(attachedNode.getId() == nodeID){ currentListItems.add(makeEdgeReferenceString(edge,nodeID,builder)); } } } } currentList = currentListItems.toArray(new String[currentListItems.size()]); } break; } default : new IllegalStateException("Wrong path level: "+path.level()); } return currentList; } /** * Returns the label of the parent of the list currently visualized * * @return the label of the parent */ public String getCurrentItemName(){ String text = cachedHeaderTexts.current(); if(text != null) return text; else return path.current().toString(); } /** * Returns the level of the navigation at the moment the method is called. * * @return an {@code int} representing the current level. Possible values are * listed as {@code static} variables of this class. */ public int getCurrentLevel(){ return path.level(); } /** * Makes a child of the current {@code HierarchyItem} the current one. * * @param child the index of the child becoming the current * @return {@code true} if the current position has changed after the call **/ public boolean goNext(int child){ if(child < 0) throw new IllegalArgumentException("Wrong child index: "+child); HierarchyItem hierarchyItem = findNext(child); if(hierarchyItem == null) return false; path.push(hierarchyItem); /* saves the selected list item to display as a title on the next call to getCurrentHeaderText() */ cachedHeaderTexts.push(cachedList[child]); /* make the new list to return for next calls to getCurrentList() */ cachedList = buildCurrentChildList(); /* the selected hierarchy node has no children. go back and return false */ if(cachedList.length == 0){ goPrevious(); return false; } updateable.update(); return true; } private HierarchyItem findNext(int child){ switch(path.level()){ case DIAGRAM_LEVEL :{ List<NodeType> nodeTypes = diagram.getPrototypes().getNodeTypes(); List<EdgeType> edgeTypes = diagram.getPrototypes().getEdgeTypes(); int numNodeTypes = nodeTypes.size(); int numEdgeTypes = edgeTypes.size(); if(child >= numNodeTypes+numEdgeTypes) throw new IllegalArgumentException("Wrong child index: "+child); if(child < numNodeTypes){ return nodeTypes.get(child); }else{ return edgeTypes.get(child-numNodeTypes); } } case TYPE_LEVEL : { int numTypeItems = 0;// the number of nodes or edges found of the current type if(path.current() instanceof NodeType){ /* find the child-th node of the current type */ for(Node node : diagram.getComponents().getNodes()){ if(path.current().toString().equals(node.getType())){ if(numTypeItems == child){ return node; }else{ numTypeItems++; } } } }else{ // EdgeType for(Edge edge : diagram.getComponents().getEdges()){ /* find the child-th edge of the current type */ if(path.current().toString().equals(edge.getType())){ if(numTypeItems == child){ return edge; }else{ numTypeItems++; } } } } break; } case ITEM_LEVEL : { /* we can go further only for nodes */ if(path.current() instanceof Node){ Node node = (Node)path.current(); /* check if user clicked on an edge, this node is attached to */ Set<String> cNodeEdgeTypes = new LinkedHashSet<String>(); /* for each edge component */ for(Edge edge : diagram.getComponents().getEdges()){ /* for each attached node to this edge */ for(EdgeNode attachedNode : edge.getAttachedNodes()){ /* if it's this node we're looking for (cNode) */ if(attachedNode.getId() == node.getId()){ cNodeEdgeTypes.add(edge.getType()); } } } if(child < cNodeEdgeTypes.size()){ String selectedType = cNodeEdgeTypes.toArray(new String[cNodeEdgeTypes.size()])[child]; for(EdgeType edge : diagram.getPrototypes().getEdgeTypes()){ if(edge.getType().equals(selectedType)){ return edge; } } }else{// user selected a NodePropertyType put a NodeProperty in the path return node.getProperties().get(child-cNodeEdgeTypes.size()); } break; } } default : return null; // don't go further than REFERENCE_AND_PROPERTY_LEVEL } return null; } /** * Makes the father of the current {@code HierarchyItem} the current one. * @return @code true} if the current position has changed after the call */ public boolean goPrevious(){ if(path.level() == DIAGRAM_LEVEL) // level = DIAGRAM_LEVEL return false; path.pop(); cachedList = buildCurrentChildList(); cachedHeaderTexts.pop(); return true; } /** * Returns a controller, that can be used to * change the model (the diagram) in a way coherent to the tree navigation * * @return a controller connected to this navigation */ public Controller getController(){ return controller; } /* Counts the number of nodes or edges of a given type */ private int count(HierarchyItem hItem){ int count = 0; if(hItem instanceof NodeType){ NodeType node = (NodeType)hItem; for(Node cNode : diagram.getComponents().getNodes()) if(cNode.getType().equals(node.getType())) count++; }else if(hItem instanceof EdgeType){ EdgeType edge = (EdgeType)hItem; for(Edge cEdge : diagram.getComponents().getEdges()) if(cEdge.getType().equals(edge.getType())) count++; } return count; } /* Creates a string for the node reference of edge related to the node N equal to ID nodeID. * The string looks like "to N1, N2 and N3 vie E", where N1, N2 and N3 are the nodes connected * to N via E ( they can be more than one in case of a multiple ended edge ). */ private String makeEdgeReferenceString(Edge edge, int nodeID, StringBuilder builder) { builder.setLength(0); builder.append("to "); /* attach the name of the nodes interleaved by " and " */ for(int i=0; i<edge.getAttachedNodes().size(); i++){ EdgeNode attachedNode = edge.getAttachedNodes().get(i); if(attachedNode.getId() != nodeID){ for(Node cNode : diagram.getComponents().getNodes()){ if(cNode.getId() == attachedNode.getId()){ if(i == edge.getAttachedNodes().size()-1){ builder.append(cNode.getName()); }else if(i == edge.getAttachedNodes().size()-2){ builder.append(cNode.getName()).append(" and "); }else{ builder.append(cNode.getName()).append(", "); } break; } } } } builder.append(", via "); builder.append(edge.getName()); return builder.toString(); } /* A controller that changes the model (the data structures in the xml parser package) using the informations * * given by the TreeNavigation. When commands are issued by the user (through long click) the controller pops * * up a dialog according to what the user chose and which layer of the tree they are currently visualizing * * if the user issues the command (doesn't cancel the dialog) the model is updated and the updater * * is called to refresh and visualize the result */ class Controller implements AccessibleDialogBuilder.ButtonClickListener { AccessibleDialogBuilder dialogBuilder; HierarchyItem clickedItem; Controller(AccessibleDialogBuilder dialogBuilder){ this.dialogBuilder = dialogBuilder; }; /* * Responds to the long cick of the user. It acts according to the list item clicked * by the user (and therefore according to the hierarchy level currently displayed). * When a dialog needs to be shown, it used dialogBuilder.displayDialog() and register itself * as buttoClickListener to handle the click of the user in the same class. */ public boolean performEditAction(AdapterView<?> parent, View view, int position, long id, Updateable u) { switch(getCurrentLevel()){ case TreeNavigation.DIAGRAM_LEVEL : { List<NodeType> nodeTypes = diagram.getPrototypes().getNodeTypes(); if(position < nodeTypes.size()){ // user clicked on a node clickedItem = nodeTypes.get(position); /* build the properties for node constructor using node property types */ NodeType nodeType = nodeTypes.get(position); ArrayList<NodeProperty> properties = new ArrayList<NodeProperty>(nodeType.getPropretyTypes().size()); for(NodePropertyType pType : nodeType.getPropretyTypes()){ properties.add(new NodeProperty(pType.getType())); } /* construct the node and add it to the diagram */ Node node = new Node(((NodeType)clickedItem).getType(),properties); diagramUpdater.addNode(node); dialogBuilder.getAccessibilityService().speak("Node "+ node +" created"); }else{ // user clicked on edge List<EdgeType> edgeTypes = diagram.getPrototypes().getEdgeTypes(); clickedItem = edgeTypes.get(position - nodeTypes.size()); /* respect min and max attached nodes */ if(selectedNodes.size() < ((EdgeType)clickedItem).getMinAttachedNodes()){ dialogBuilder.getAccessibilityService().speak("you must select at least "+ ((EdgeType)clickedItem).getMinAttachedNodes()+" nodes"); return true; } if(selectedNodes.size() > ((EdgeType)clickedItem).getMaxAttachedNodes()){ dialogBuilder.getAccessibilityService().speak("you must select at most "+ ((EdgeType)clickedItem).getMaxAttachedNodes()+" nodes"); return true; } /* create the edge and add the edge nodes */ Edge edge = new Edge(((EdgeType)clickedItem).getType()); edge.getAttachedNodes().clear(); /* create all the node references (EdgeNode) for this edge */ for(Node n : diagram.getComponents().getNodes()){ if(selectedNodes.contains(n)) edge.getAttachedNodes().add(new EdgeNode(n.getId())); } diagramUpdater.addEdge(edge); StringBuilder builder = new StringBuilder(); builder.append(edge).append(" created between "); for(int i=0; i<selectedNodes.size(); i++){ Node sn = selectedNodes.get(i); builder.append(sn.getName()); if(i == selectedNodes.size() - 2 ){ builder.append(", and "); }else if (i != selectedNodes.size() - 1 ){ builder.append(", "); } } selectedNodes.clear(); // when an edge is added the selected node are cleared dialogBuilder.getAccessibilityService().speak(builder.toString()); } /* update the view */ cachedList = buildCurrentChildList(); updateable.update(); return true; } case TreeNavigation.TYPE_LEVEL :{ clickedItem = findNext(position); /* path.current() is what's displayed on the header */ if(path.current() instanceof NodeType){ // Node if(selectedNodes.contains(clickedItem)){ dialogBuilder.displayDialog(R.layout.alert_dialog_edit_node_unselect, EDIT_NODE_DIALOG_TAG, this); }else{ dialogBuilder.displayDialog(R.layout.alert_dialog_edit_node, EDIT_NODE_DIALOG_TAG, this); } }else{ // Edge dialogBuilder.displayDialog(R.layout.alert_dialog_edit_edge, EDIT_EDGE_DIALOG_TAG, this); } return true; } case TreeNavigation.ITEM_LEVEL :{ if(path.current() instanceof Node){ // Node clickedItem = findNext(position); /* edge types inside node cannot be edited directly */ if(clickedItem instanceof EdgeType) return false; /* if it ain't an EdgeType then t's a PropertyType */ /* property type items can be used to add new properties */ dialogBuilder.displayDialog(R.layout.alert_dialog_add_property, ADD_PROPERTY_DIALOG_TAG, this); }else{ // Edge clickedItem = ((Edge)path.current()).getAttachedNodes().get(position); dialogBuilder.displayDialog(R.layout.alert_dialog_edit_node_reference, EDIT_NODE_REF_DIALOG_TAG, this); } return true; } case TreeNavigation.REFERENCE_AND_PROPERTY_TYPE_LEVEL :{ if(path.current() instanceof NodeProperty){ NodeProperty nodeProperty = (NodeProperty)path.current(); clickedItem = nodeProperty.getValues().get(position); dialogBuilder.displayDialog(R.layout.alert_dialog_edit_property, EDIT_PROPERTY_DIALOG_TAG, this); return true; } return false; } case TreeNavigation.PROPERTY_AND_EDGE_REFERENCE_LEVEL :{ return false;// never happens } } return false; } /* this is the callback triggered when the user clicks on any bottom of the dialog shown. v is the button * The method first checks for the dialog tag to understand which dialog it's handling, then it checks * for the button tag to understand which button the user pressed. The first check though is on "CANCEL" * button as its tag it's the same for all the dialog */ @Override public void onClick(View v, DialogFragment dialogFragment, String dialogTag) { Object buttonTag = v.getTag(); AccessibilityService accessibility = dialogBuilder.getAccessibilityService(); if("CANCEL".equals(buttonTag)){ accessibility.speak("Cancel"); dialogFragment.dismiss(); return; } if(EDIT_NODE_DIALOG_TAG.equals(dialogTag) || EDIT_EDGE_DIALOG_TAG.equals(dialogTag)){ if("SELECT".equals(buttonTag)){ selectedNodes.add((Node)clickedItem); accessibility.playSound(SoundEvent.V_OK); accessibility.speak(clickedItem + " selected"); }else if("UNSELECT".equals(buttonTag)){ selectedNodes.remove(clickedItem); accessibility.playSound(SoundEvent.V_OK); accessibility.speak(clickedItem + " unselected"); }else if("RENAME".equals(buttonTag)){ dialogFragment.dismiss(); dialogBuilder.displayDialog(R.layout.alert_dialog_rename, RENAME_DIALOG_TAG+clickedItem, this); }else if("DELETE".equals(buttonTag)){ dialogFragment.dismiss(); dialogBuilder.displayDialog(R.layout.alert_dialog_confirmation, CONFIRMATION_DIALOG_TAG+ (EDIT_NODE_DIALOG_TAG.equals(dialogTag) ? " Node " : " Edge ") + clickedItem, this); } }else if(dialogTag.startsWith(RENAME_DIALOG_TAG)){ /* if it reached this point it'a "RENAME" button as "CANCEL" * * have been matched against at the beginning of the method */ EditText editText = (EditText)dialogFragment.getDialog().findViewById(R.id.text_edit); String text = editText.getText().toString().trim(); if(text.length() == 0){ accessibility.playSound(SoundEvent.V_ERROR); accessibility.speak("Text cannot be empty"); return; } String oldName = clickedItem.toString(); diagramUpdater.rename(clickedItem,text); accessibility.playSound(SoundEvent.V_OK); accessibility.speak(oldName+" renamed to "+clickedItem); }else if(dialogTag.startsWith(CONFIRMATION_DIALOG_TAG)){ /* if it reaches this point it's a "YES" as a "NO" button has "CANCEL" as its tag */ /* and the match against "CANCEL" match is performed first of all */ diagramUpdater.delete(clickedItem); /* if it's a selected node, remove it from selected */ selectedNodes.remove(clickedItem); accessibility.speak(clickedItem+" Deleted"); /* update the headers which show the number of children the current item contains */ if(path.current() instanceof NodeType || path.current() instanceof EdgeType){ cachedHeaderTexts.pop(); cachedHeaderTexts.push(path.current()+" ("+count(path.current())+')'); }else if(path.current() instanceof NodeProperty){ cachedHeaderTexts.pop(); cachedHeaderTexts.push(path.current()+" ("+((NodeProperty)path.current()).getValues().size()+')'); } }else if(ADD_PROPERTY_DIALOG_TAG.equals(dialogTag)){ /* if it reaches this point it's a "CREATE" as "CANCEL" would * * have been matched against at the beginning of the method */ EditText editText = (EditText)dialogFragment.getDialog().findViewById(R.id.text_edit); String text = editText.getText().toString().trim(); if(text.length() == 0){ accessibility.playSound(SoundEvent.V_ERROR); accessibility.speak("Text cannot be empty"); return; } diagramUpdater.addProperty((Node)path.get(ITEM_LEVEL),((NodeProperty)clickedItem),text); accessibility.playSound(SoundEvent.V_OK); accessibility.speak("Property "+text+" added"); }else if(EDIT_NODE_REF_DIALOG_TAG.equals(dialogTag)){ if("EDIT_LABEL".equals(buttonTag)){ dialogFragment.dismiss(); dialogBuilder.displayDialog(R.layout.alert_dialog_rename, EDIT_LABEL_DIALOG_TAG, this); }else{ // EDIT_ARROWHEAD dialogFragment.dismiss(); Edge edge = (Edge)path.current(); // get the edge this node reference belongs to String[] heads = null; /* build a string array with all the available head labels for this edge's type */ for(EdgeType edgeType : diagram.getPrototypes().getEdgeTypes()){ if(edgeType.getType().equals(edge.getType())){ heads = new String[edgeType.getHeads().size()]; for(int i=0; i<heads.length;i++) heads[i] = edgeType.getHeads().get(i).getHeadLabel(); break; } } if(heads == null || heads.length == 0){ accessibility.playSound(SoundEvent.V_ERROR); accessibility.speak("There are no arrow heads defined for "+edge); return; } dialogBuilder.displaySelectionDialog(EDIT_ARROWHEAD_DIALOG_TAG, heads, this); } }else if(EDIT_LABEL_DIALOG_TAG.equals(dialogTag)){ EditText editText = (EditText)dialogFragment.getDialog().findViewById(R.id.text_edit); String text = editText.getText().toString().trim(); if(text.length() == 0){ accessibility.playSound(SoundEvent.V_ERROR); accessibility.speak("Text cannot be empty"); return; } diagramUpdater.setLabel((EdgeNode)clickedItem,text); accessibility.playSound(SoundEvent.V_OK); accessibility.speak("Label set to "+clickedItem);// EdgeNode.toString = EdgeNode.getLabel }else if(EDIT_ARROWHEAD_DIALOG_TAG.equals(dialogTag)){ Spinner spinner = (Spinner)dialogFragment.getDialog().findViewById(R.id.selectionSpinner); ((EdgeNode)clickedItem).setHead(spinner.getSelectedItem().toString()); accessibility.playSound(SoundEvent.V_OK); accessibility.speak("Arrow head set to "+spinner.getSelectedItem().toString()); }else if(EDIT_PROPERTY_DIALOG_TAG.equals(dialogTag)){ if("RENAME".equals(buttonTag)){ dialogFragment.dismiss(); dialogBuilder.displayDialog(R.layout.alert_dialog_rename, RENAME_DIALOG_TAG, this); }else if("DELETE".equals(buttonTag)){ dialogFragment.dismiss(); dialogBuilder.displayDialog(R.layout.alert_dialog_confirmation, CONFIRMATION_DIALOG_TAG+ " property "+clickedItem, this); }else if("MODIFIERS".equals(buttonTag)){ dialogFragment.dismiss(); /* get the modifiers from the NodePropertyType of the node where this property is */ NodeProperty property = (NodeProperty)path.current(); String[] modifiers = null; boolean[] checks = null; for( NodePropertyType propertyType : ((NodeType)path.get(TYPE_LEVEL)).getPropretyTypes()){ if(propertyType.getType().equals(property.getType())){ modifiers = new String[propertyType.getModifiers().size()]; checks = new boolean[modifiers.length]; for(int i=0; i< modifiers.length; i++){ modifiers[i] = propertyType.getModifiers().get(i).getType(); if(((PropertyValue)clickedItem).getModifiersIndexes().contains(i)) checks[i] = true; } } } if(modifiers == null || modifiers.length == 0){ accessibility.playSound(SoundEvent.V_ERROR); accessibility.speak("There are no modifiers defined for "+property); return; } dialogBuilder.displayCheckDialog(EDIT_MODIFIERS_DIALOG_TAG, modifiers, checks, this); } }else if(EDIT_MODIFIERS_DIALOG_TAG.equals(dialogTag)){ /* pressed button is OK as CANCEL would have been cought at the beginning */ AccessibleCheckbox checkbox = (AccessibleCheckbox)dialogFragment.getDialog().findViewById(R.id.checkBox); boolean[] checks = checkbox.getChecks(); List<Integer> modifiers = new ArrayList<Integer>(checks.length); for(int i=0; i<checks.length; i++){ if(checks[i]) modifiers.add(i); } diagramUpdater.setModifiers((PropertyValue)clickedItem, modifiers); } /* update the view */ cachedList = buildCurrentChildList(); updateable.update(); dialogFragment.dismiss(); } } /** * * Simple interface to provide an update method * */ interface Updateable { /** * performs an update */ public void update(); } }