/* * (c) 2005-2009 Carlos G�mez Rodr�guez, todos los derechos reservados / all rights reserved. * Licencia en license/bsd.txt / License in license/bsd.txt */ package org.f2o.absurdum.puck.gui.panels; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.Vector; import javax.swing.BoxLayout; import javax.swing.DefaultComboBoxModel; import javax.swing.JComboBox; import javax.swing.JPanel; import javax.swing.JTabbedPane; import org.f2o.absurdum.puck.gui.graph.Arrow; import org.f2o.absurdum.puck.gui.graph.Node; import org.f2o.absurdum.puck.i18n.UIMessages; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import eu.irreality.age.windowing.TabUtils; /* Created 08/02/2008 20:13:12 When it is done, this should be the class from where all panels associated to arrows should inherit. It should add a PropertiesPanel with the custom relationship properties. */ public class ArrowPanel extends GraphElementPanel { protected Arrow theArrow; protected Vector relTypes = new Vector(); //structural relationship types protected String relationshipType; //type of structural relationship or NO_STRUCTURAL_RELATIONSHIP //we don't know where they will be added but these combo boxes must exist protected JComboBox srcComboBox = new JComboBox(); protected JComboBox dstComboBox = new JComboBox(); protected JComboBox relComboBox = new JComboBox(); //this one may be invisible, but it should exist too //possible destinations, these nodes will be referenced by the combo boxes protected Vector srcNodes; protected Vector dstNodes; private PropertiesPanel customRelationshipsPanel; //the tabbed pane protected JTabbedPane jtp = new JTabbedPane(); //String denoting that there is no structural relationship associated to an arrow (only, maybe, custom relationships) public static final String NO_STRUCTURAL_RELATIONSHIP = UIMessages.getInstance().getMessage("structural.none"); public String getNameForElement() { if ( customRelationshipsPanel != null && relationshipType == NO_STRUCTURAL_RELATIONSHIP ) { String report = customRelationshipsPanel.getReport(); if ( report.length() > 0 ) return report; } return relationshipType; } public void setRelationshipType( String relType ) { this.relationshipType = relType; } public String getRelationshipType() { return relationshipType; } public Vector getPossibleSourceNodes() { return new Vector(); //return this.getGraphEditingPanel().getCharNodes(); } public Vector getPossibleDestinationNodes() { return new Vector(); //return this.getGraphEditingPanel().getItemNodes(); } //returns the index of the object in v whose name is s. protected int indexOf ( Vector v , String s ) { for ( int i = 0 ; i < v.size() ; i++ ) { if ( s.equals(v.get(i).toString()) ) return i; } return -1; } public void doInitMinimal() { addCustomRelationshipsTab(); srcNodes = getPossibleSourceNodes(); dstNodes = getPossibleDestinationNodes(); } public void linkWithGraph() { /* private PropertiesPanel temp = null; if ( customRelationshipsPanel != null ) temp = customRelationshipsPanel; */ removeAll(); jtp.removeAll(); this.add(jtp); initMinimal(); //init srcNodes, dstNodes srcComboBox = new JComboBox ( new DefaultComboBoxModel ( srcNodes ) ); dstComboBox = new JComboBox ( new DefaultComboBoxModel ( dstNodes ) ); relComboBox = new JComboBox ( new DefaultComboBoxModel ( relTypes) ); srcComboBox.addActionListener ( new ActionListener() { public void actionPerformed ( ActionEvent evt ) { int index = srcComboBox.getSelectedIndex(); if ( index < 0 ) { System.err.println("Warning: trying to set source of arrow panel " + ArrowPanel.this + " to index " + index + ". Stack trace:"); Thread.dumpStack(); return; } Node n = (Node) srcNodes.get(index); theArrow.setSource(n); } } ); dstComboBox.addActionListener ( new ActionListener() { public void actionPerformed ( ActionEvent evt ) { if ( dstComboBox.getSelectedIndex() >= 0 ) { int index = dstComboBox.getSelectedIndex(); if ( index < 0 ) { System.err.println("Warning: trying to set source of arrow panel " + ArrowPanel.this + " to index " + index + ". Stack trace:"); Thread.dumpStack(); return; } Node n = (Node) dstNodes.get(index); theArrow.setDestination(n); } } } ); relComboBox.addActionListener ( new ActionListener() { public void actionPerformed ( ActionEvent evt ) { relationshipType = (String) relTypes.get(relComboBox.getSelectedIndex()); } } ); srcComboBox.setSelectedIndex( indexOf(srcNodes,theArrow.getSource().getName()) ); dstComboBox.setSelectedIndex( indexOf(dstNodes,theArrow.getDestination().getName()) ); relComboBox.setSelectedIndex( indexOf(relTypes,relationshipType) ); } public void refresh() { srcComboBox.setModel(new DefaultComboBoxModel(getPossibleSourceNodes())); dstComboBox.setModel(new DefaultComboBoxModel(getPossibleDestinationNodes())); relComboBox.setModel( new DefaultComboBoxModel ( relTypes) ); srcComboBox.setSelectedIndex( indexOf(srcNodes,theArrow.getSource().getName()) ); dstComboBox.setSelectedIndex( indexOf(dstNodes,theArrow.getDestination().getName()) ); relComboBox.setSelectedIndex( indexOf(relTypes,relationshipType) ); } public ArrowPanel( Arrow theArrow ) { super(); this.theArrow = theArrow; } public void addCustomRelationshipsTab() { if ( customRelationshipsPanel == null ) //it could be != null and relevant if a save calls initMinimal and then the delayed load calls it too customRelationshipsPanel = new PropertiesPanel( UIMessages.getInstance().getMessage("label.relationships")); JPanel customRelationshipsTab = new JPanel(); customRelationshipsTab.setLayout(new BoxLayout(customRelationshipsTab, BoxLayout.PAGE_AXIS)); customRelationshipsTab.add(customRelationshipsPanel); jtp.add(customRelationshipsTab,UIMessages.getInstance().getMessage("tab.customrel")); } private String getDestinationName() { return dstComboBox.getSelectedItem().toString(); } public org.w3c.dom.Node getCustomRelationshipXML ( Document d ) { forceRealCustomRelationshipsInitFromXML(); Element result = d.createElement("Relationship"); result.setAttribute("id",getDestinationName()); Element elt = (Element) customRelationshipsPanel.getXML(d); /* * If the PropertyList is empty, then we don't really need the RelationshipList node, it's useless. * This happens if the panel was created for a structural relationship (has, has part, etc.) and thus the custom relationship panel is empty. */ if ( elt.getChildNodes().getLength() == 0 ) return null; result.appendChild(elt); return result; } private org.w3c.dom.Node cachedRelationshipsNode = null; boolean inittedRels = false; synchronized public final void forceRealCustomRelationshipsInitFromXML ( ) { if ( isCacheEnabled() && !inittedRels && cachedRelationshipsNode != null ) { doInitCustomRelationshipsFromXML(cachedRelationshipsNode); inittedRels = true; cachedRelationshipsNode = null; } } public final void initCustomRelationshipsFromXML ( org.w3c.dom.Node n ) { if ( isCacheEnabled() ) { cachedRelationshipsNode = n; /* synchronized(cachedNotInitted) { cachedNotInitted.offer(this); } */ } else doInitCustomRelationshipsFromXML ( n ); } public void doInitCustomRelationshipsFromXML ( org.w3c.dom.Node n ) { Element e = (Element) n; NodeList nl1 = e.getElementsByTagName("PropertyList"); if ( nl1.getLength() > 0 ) { Element plElt = (Element) nl1.item(0); customRelationshipsPanel.initFromXML(plElt); } } public String describeArrow() { return "[ src=" + srcComboBox.getSelectedItem() + "(" + theArrow.getSource() + ")" + " dst=" + dstComboBox.getSelectedItem() + "(" + theArrow.getDestination() + ")" + " ]"; } /* * It may be the case that an arrow has no source or destination if we are executing the deferred loading, * and in the meanwhile its source or destination is removed (by pressing Del, for example). * We need this method to be able to realise that and refrain from initialising the arrow. */ protected boolean hasSourceAndDestination ( ) { //not only the source and dest must not be null (in fact that's unimportant, i think) //but the source and dest must be registered with the graph element panel (and this is checked by seeing if they are in the //possible source and possible destination lists). //if the source or destination node has been removed before the deferred load takes place, the node's object will be in memory //but not in these lists, hence this check. return theArrow.getSource() != null && this.getPossibleSourceNodes().contains(theArrow.getSource()) && theArrow.getDestination() != null && this.getPossibleDestinationNodes().contains(theArrow.getDestination()); } }