/* * Lokomo OneCMDB - An Open Source Software for Configuration * Management of Datacenter Resources * * Copyright (C) 2006 Lokomo Systems AB * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. * * Lokomo Systems AB can be contacted via e-mail: info@lokomo.com or via * paper mail: Lokomo Systems AB, Sv�rdv�gen 27, SE-182 33 * Danderyd, Sweden. * */ package org.onecmdb.rest.graph.main; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.Font; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.Currency; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JCheckBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JRadioButton; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.JTabbedPane; import javax.swing.JTextField; import javax.swing.SwingUtilities; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import javax.swing.table.AbstractTableModel; import javax.swing.table.TableModel; import javax.swing.text.html.HTML; import org.onecmdb.core.utils.bean.CiBean; import org.onecmdb.core.utils.graph.result.Template; import org.onecmdb.rest.graph.events.CEvents; import org.onecmdb.rest.graph.events.Event; import org.onecmdb.rest.graph.events.EventDispatcher; import org.onecmdb.rest.graph.events.IEventListener; import org.onecmdb.rest.graph.model.CIAttributeModel; import org.onecmdb.rest.graph.model.CIModel; import org.onecmdb.rest.graph.model.prefuse.InstanceGraphControl; import org.onecmdb.rest.graph.model.prefuse.TemplateModelControl; import org.onecmdb.rest.graph.panels.ReferenceTree; import org.onecmdb.rest.graph.prefuse.view.GraphView; import org.onecmdb.rest.graph.prefuse.view.RadialGraphView; import org.onecmdb.rest.graph.utils.swing.TextInputField; import org.onecmdb.swing.treetable.JTreeTable; import prefuse.Visualization; import prefuse.action.Action; import prefuse.data.Edge; import prefuse.data.Graph; import prefuse.data.Node; import prefuse.data.Tree; import prefuse.data.Tuple; import prefuse.data.event.TupleSetListener; import prefuse.data.tuple.TupleSet; import prefuse.util.ui.JPrefuseTree; import prefuse.util.ui.JValueSlider; public class MainInstanceBrowser extends JPanel implements IEventListener { private static final int LAYOUT_FORCE_DIRECTED = 1; private static final int LAYOUT_RADIAL = 2; private String rootCI; private InstanceGraphControl gControl = new InstanceGraphControl(); private ReferenceTree referenceTree; private GraphView gView; private String template; private CIAttributeModel instancePropertyControl; private JTreeTable pTree; private int graphType = LAYOUT_FORCE_DIRECTED; private JSplitPane rightSplit; private JLabel infoLabel = new JLabel(); private Box input; private JTabbedPane tp; private JTabbedPane referenceTp; private RadialGraphView rView; private JCheckBox exclude; private String instanceAlias; public MainInstanceBrowser(String root) { EventDispatcher.addEventListener(this); this.rootCI = root; System.out.println("New MainInstanceBrowser"); this.referenceTree = new ReferenceTree(TemplateModelControl.get(rootCI)); initUI(); } public void setInstanceTemplate(String template, String instanceAlias) { this.template = template; this.instanceAlias = instanceAlias; this.referenceTree.update(template, instanceAlias); gControl.reset(); gControl.setRoot(template, instanceAlias, true, true); if (instanceAlias != null) { gControl.setExclude(false); exclude.setSelected(false); } //gControl.update(); updateGraph(); } /** * CenterSpilt * Left * Reference Tree * Right * Top - GraphView * Bottom - TabPannel * Query * Attribute * Status */ private void initUI() { instancePropertyControl = new CIAttributeModel(); instancePropertyControl.setAdvanced(false); pTree = new JTreeTable(instancePropertyControl); final JSplitPane centerSplit = new JSplitPane(); centerSplit.setOrientation(JSplitPane.HORIZONTAL_SPLIT); /* final JSplitPane leftSplit = new JSplitPane(); leftSplit.setOrientation(JSplitPane.VERTICAL_SPLIT); */ rightSplit = new JSplitPane(); rightSplit.setOrientation(JSplitPane.VERTICAL_SPLIT); tp = new JTabbedPane(); tp.setTabPlacement(JTabbedPane.TOP); tp.add(new JScrollPane(pTree), "Attributes"); //tp.add(new JScrollPane(getControlPanel()), "Graph Control"); //leftSplit.setBottomComponent(getTemplateTreePanel()); //leftSplit.setTopComponent(getReferenceTreePanel()); rightSplit.setTopComponent(getGraphView(graphType)); rightSplit.setBottomComponent(tp); rightSplit.setOneTouchExpandable(true); rightSplit.setDividerLocation(200); /* JSplitPane refSplit = new JSplitPane(); refSplit.setOrientation(JSplitPane.VERTICAL_SPLIT); refSplit.setTopComponent(getReferenceTreePanel()); refSplit.setBottomComponent(getControlPanel()); */ centerSplit.setTopComponent(rightSplit); referenceTp = new JTabbedPane(); referenceTp.setBackground(Color.WHITE); referenceTp.add(this.referenceTree, "References"); referenceTp.add(getSearchPanel(), "Search"); referenceTp.add(getControlPanel(), "Graph Control"); centerSplit.setBottomComponent(referenceTp); centerSplit.setDividerLocation(200); centerSplit.setOneTouchExpandable(true); setLayout(new BorderLayout()); add(centerSplit, BorderLayout.CENTER); this.addComponentListener(new ComponentAdapter() { public void componentResized(ComponentEvent e) { centerSplit.setDividerLocation(0.70D); rightSplit.setDividerLocation(0.75D); invalidate(); } }); // Start.... } public JComponent getSearchPanel() { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.setBackground(Color.WHITE); input = new Box(BoxLayout.Y_AXIS); Box right = new Box(BoxLayout.Y_AXIS); right.setAlignmentX(Component.LEFT_ALIGNMENT); right.add(input); right.add(infoLabel); //right.setBorder(BorderFactory.createTitledBorder("Info")); right.setAlignmentY(Component.TOP_ALIGNMENT); panel.setAlignmentY(Component.TOP_ALIGNMENT); panel.add(Box.createHorizontalStrut(5)); panel.add(right); panel.add(Box.createHorizontalStrut(5)); return(new JScrollPane(panel)); } public JComponent getControlPanel() { JPanel panel = new JPanel(); panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS)); panel.setBackground(Color.WHITE); final JCheckBox forceBounds = new JCheckBox("Keep objects whithin window"); forceBounds.setBackground(Color.WHITE); forceBounds.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { gView.setEnforceBounds(forceBounds.isSelected()); } }); final JCheckBox animation = new JCheckBox("Dynamic graph", true); animation.setBackground(Color.WHITE); animation.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { gView.animate(animation.isSelected()); } }); exclude = new JCheckBox("Include non-referenced objects", true); exclude.setBackground(Color.WHITE); exclude.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { gControl.setExclude(exclude.isSelected()); SwingUtilities.invokeLater(new Runnable() { public void run() { reloadGraph(); } }); } }); final JValueSlider slider = new JValueSlider("Max distance", 0, 10, 1); slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent e) { gView.setDistance(slider.getValue().intValue()); } }); slider.setPreferredSize(new Dimension(290, 20)); slider.setMaximumSize(new Dimension(290, 20)); for (Component comp : slider.getComponents()) { comp.setEnabled(false); } final JCheckBox distanceFilter = new JCheckBox("Enable distance filter", false); distanceFilter.setBackground(Color.WHITE); distanceFilter.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (distanceFilter.isSelected()) { gView.setDistance(slider.getValue().intValue()); } gView.setEnableDistanceFilter(distanceFilter.isSelected()); for (Component comp : slider.getComponents()) { comp.setEnabled(distanceFilter.isSelected()); } } }); final TextInputField limit = new TextInputField("Max no of instances per template", "" + gControl.getMaxSize()); limit.getInput().addKeyListener(new KeyAdapter() { @Override public void keyReleased(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ENTER) { try { Integer i = Integer.parseInt(limit.getInput().getText()); gControl.setMaxSize(i); SwingUtilities.invokeLater(new Runnable() { public void run() { reloadGraph(); } }); } catch (Throwable t) { limit.getInput().setText("NaN"); } } } }); panel.setAlignmentX(Component.LEFT_ALIGNMENT); forceBounds.setAlignmentX(Component.LEFT_ALIGNMENT); animation.setAlignmentX(Component.LEFT_ALIGNMENT); exclude.setAlignmentX(Component.LEFT_ALIGNMENT); distanceFilter.setAlignmentX(Component.LEFT_ALIGNMENT); slider.setAlignmentX(Component.LEFT_ALIGNMENT); Box checks = new Box(BoxLayout.Y_AXIS); checks.add(forceBounds); checks.add(animation); checks.add(exclude); checks.add(distanceFilter); checks.add(slider); checks.setBorder(BorderFactory.createTitledBorder("")); Box max = new Box(BoxLayout.Y_AXIS); limit.setAlignmentX(Component.LEFT_ALIGNMENT); max.add(limit); max.setBorder(BorderFactory.createTitledBorder("")); Box left = new Box(BoxLayout.Y_AXIS); left.add(checks); left.add(Box.createVerticalStrut(5)); left.add(max); left.add(Box.createVerticalStrut(5)); Box help = new Box(BoxLayout.Y_AXIS); help.setBorder(BorderFactory.createTitledBorder("Graph - Help")); StringBuffer b = new StringBuffer(); b.append("<html>"); b.append("Hold left mouse button and move mouse:<br> -Move the graph<br>"); b.append("Hold right mouse button and move mouse:<br> -Zoom in/out<br>"); b.append("Turn mouse wheel: <br> -Zoom in/out<br>"); b.append("Single-click with right mouse button:<br> -Adjust zoom so all objects fit within window<br>"); b.append("Double-click with left mouse button:<br> -Center where the cursor is and zoom in<br>"); b.append("Single-click with left mouse button on an object:<br> -Select this object<br>"); b.append("Hold left mouse button on an object and move mouse:<br> -Move this object<br>"); b.append("</html>"); JLabel label = new JLabel(b.toString()); label.setFont(new Font("Times", Font.PLAIN, 12)); help.add(label); left.add(help); left.setAlignmentY(Component.TOP_ALIGNMENT); panel.setAlignmentY(Component.TOP_ALIGNMENT); panel.add(Box.createHorizontalStrut(5)); panel.add(left); panel.add(Box.createHorizontalStrut(5)); return(new JScrollPane(panel)); } /* private JComponent getTemplateTreeViewPanel() { Tree tree = TemplateModel.get(root).getTemplateGraph().getSpanningTree(); TreeView view = new TreeView(tree, "name"); view.getVisualization().getGroup(Visualization.FOCUS_ITEMS).addTupleSetListener( new TupleSetListener() { public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) { if (add.length == 1) { // Fire selection change... Node n = (Node)add[0]; EventDispatcher.fireEvent(this, new Event(CEvents.NODE_SELECTED, n)); } } } ); return(view); } private JComponent getTemplateTreePanel() { if (true) { return(new JPanel()); } Graph templateGraph = TemplateModel.get(root).getTemplateGraph(); Tree tree = templateGraph.getSpanningTree(); JPrefuseTree jtree = new JPrefuseTree(tree, "name"); CheckBoxTreeRenderer render = new CheckBoxTreeRenderer(); jtree.setCellRenderer(render); jtree.setEditable(true); NodTableEditor editor = new NodTableEditor(); editor.setListener(new IEventListener() { public void onEvent(Event e) { Node n = (Node)e.getData(); if (n.getBoolean("checked")) { EventDispatcher.fireEvent(this, new Event(CEvents.NODE_SELECTED, e.getData())); } else { EventDispatcher.fireEvent(this, new Event(CEvents.NODE_UNSELECTED, e.getData())); } } }); jtree.setCellEditor(editor); return(new JScrollPane(jtree)); } */ private JComponent getGraphView(int type) { switch(type) { case LAYOUT_FORCE_DIRECTED: { if (gView == null) { gView = new GraphView(gControl.getGraph(), "name", null); gView.getVisualization().putAction("updateGraph", new Action() { @Override public void run(double frac) { gControl.updateGraph(); } }); gView.getVisualization().getGroup(Visualization.FOCUS_ITEMS).addTupleSetListener( new TupleSetListener() { public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) { if (add.length == 1) { // Fire selection change... if (add[0] instanceof Node) { Node n = (Node)add[0]; EventDispatcher.fireEvent(this, new Event(CEvents.INSTANCE_ITEM_SELECTED, n.getString("alias"))); } } } } ); } return(gView); } case LAYOUT_RADIAL: { if (rView == null) { rView = new RadialGraphView(gControl.getGraph(), "name"); rView.getVisualization().putAction("updateGraph", new Action() { @Override public void run(double frac) { gControl.updateGraph(); } }); gView.getVisualization().getGroup(Visualization.FOCUS_ITEMS).addTupleSetListener( new TupleSetListener() { public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) { if (add.length == 1) { // Fire selection change... if (add[0] instanceof Node) { Node n = (Node)add[0]; EventDispatcher.fireEvent(this, new Event(CEvents.INSTANCE_ITEM_SELECTED, n.getString("alias"))); } } } } ); } return(rView); } } return(null); } public void onEvent(Event e) { switch(e.getType()) { case CEvents.NODE_SELECTED: { Node node = (Node) e.getData(); String alias = node.getString("alias"); String template = node.getString("template"); referenceTree.update(template, alias); gControl.reset(); gControl.addNode(alias, true, true); //gControl.update(); updateGraph(); } break; case CEvents.NODE_UNSELECTED: { Node node = (Node) e.getData(); String alias = node.getString("alias"); gControl.removeNode(alias); //gControl.update(); updateGraph(); } break; case CEvents.RELATION_NODE_UNSELECTED: { Node n = (Node) e.getData(); if (n.getParent() != null) { String parentAlias = n.getParent().getString("alias"); String alias = n.getString("alias"); Edge edge = n.getParentEdge(); Node source = edge.getSourceNode(); Node target = edge.getTargetNode(); String sourceAlias = source.getString("alias"); String targetAlias = target.getString("alias"); boolean inBound = edge.getBoolean("inBound"); if (inBound) { String tmp = sourceAlias; sourceAlias = targetAlias; targetAlias = tmp; } String refType = edge.getString("type"); if (!parentAlias.equals(alias)) { gControl.removeNode(alias); } // Remove relation. gControl.removeRelation(sourceAlias, refType, targetAlias); //gControl.update(); updateGraph(); } else { n.set("checked", true); } } break; case CEvents.RELATION_NODE_SELECTED: { Node n = (Node) e.getData(); while(n != null) { boolean checked = n.getBoolean("checked"); String alias = n.getString("alias"); System.out.println("REL: alias=" + alias + ", checked=" + checked); //gControl.addNode(alias, false, checked); Edge edge = n.getParentEdge(); if (edge != null) { String refType = "Reference"; refType = edge.getString("type"); boolean inBound = edge.getBoolean("inBound"); Node source = edge.getSourceNode(); Node target = edge.getTargetNode(); String sourceAlias = source.getString("alias"); String targetAlias = target.getString("alias"); if (inBound) { String tmp = sourceAlias; sourceAlias = targetAlias; targetAlias = tmp; } gControl.addNode(sourceAlias, false, true); gControl.addNode(targetAlias, false, true); gControl.addRelation(sourceAlias, refType, targetAlias); System.out.println("\tSOURCE=" + source.get("alias") + ", target=" + target.get("alias")); } n = n.getParent(); } //gControl.update(); updateGraph(); } break; case(CEvents.INSTANCE_ITEM_SELECTED): { String alias = (String) e.getData(); CiBean bean = gControl.getResult().findOffspringAlias(alias); if (bean != null) { CiBean templ = bean; if (!bean.isTemplate()) { templ = TemplateModelControl.get(rootCI).getBean(bean.getDerivedFrom()); } tp.setTitleAt(0, "Attributes for " + bean.getDisplayName()); instancePropertyControl.setRoot(new CIModel(templ, bean)); TableModel tModel = pTree.getModel(); if (tModel instanceof AbstractTableModel) { ((AbstractTableModel)tModel).fireTableDataChanged(); } } } break; case(CEvents.INSTANCE_SET_LAYOUT): int layout = (Integer)e.getData(); setGraphType(layout); break; } System.out.println("EVENT: " + e.getType() + " : " + e.getData()); } private void setGraphType(int type) { if (type == graphType) { return; } JComponent graph = getGraphView(type); if (graph == null) { return; } rightSplit.setTopComponent(null); rightSplit.setTopComponent(graph); invalidate(); } private void reloadGraph() { //gControl.update(); updateGraph(); } private void updateGraph() { new Thread(new Runnable() { public void run() { gControl.update(); // Update Info Label.. updateInfo(); switch(graphType) { case LAYOUT_FORCE_DIRECTED: gView.getVisualization().run("updateGraph"); gView.redrawAndZoomToFit(); break; case LAYOUT_RADIAL: rView.getVisualization().run("updateGraph"); rView.getVisualization().run("filter"); break; } } }).start(); } private void updateInfo() { infoLabel.setText("<html>Info<br>" + gControl.getResultAsHTML() + "</html>"); // Update input field. input.removeAll(); for (final Template t : gControl.getResult().getNodes()) { if (instanceAlias != null) { if (t.getId().equals(instanceAlias)) { continue; } } String text = gControl.getSearch(t.getId()); final TextInputField search = new TextInputField("Search " + t.getTemplate().getAlias(), text); search.setAlignmentX(Component.LEFT_ALIGNMENT); search.getInput().addKeyListener(new KeyListener() { public void keyPressed(KeyEvent e) { // TODO Auto-generated method stub } public void keyReleased(KeyEvent e) { gControl.setSearch(t.getId(), search.getInput().getText()); if (e.getKeyCode() == KeyEvent.VK_ENTER) { SwingUtilities.invokeLater(new Runnable() { public void run() { reloadGraph(); } }); } } public void keyTyped(KeyEvent e) { } }); input.add(search); } input.setBorder(BorderFactory.createTitledBorder("")); input.revalidate(); } }