/* * Copyright 2011 JBoss Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.drools.semantics.lang.util; import prefuse.Constants; import prefuse.Display; import prefuse.Visualization; import prefuse.action.ActionList; import prefuse.action.ItemAction; import prefuse.action.RepaintAction; import prefuse.action.animate.ColorAnimator; import prefuse.action.animate.LocationAnimator; import prefuse.action.animate.QualityControlAnimator; import prefuse.action.animate.VisibilityAnimator; import prefuse.action.assignment.ColorAction; import prefuse.action.assignment.FontAction; import prefuse.action.filter.FisheyeTreeFilter; import prefuse.action.layout.CollapsedSubtreeLayout; import prefuse.action.layout.graph.NodeLinkTreeLayout; import prefuse.activity.SlowInSlowOutPacer; import prefuse.controls.*; import prefuse.data.Tree; import prefuse.data.Tuple; import prefuse.data.event.TupleSetListener; import prefuse.data.io.DataIOException; import prefuse.data.io.TreeMLReader; import prefuse.data.search.PrefixSearchTupleSet; import prefuse.data.tuple.TupleSet; import prefuse.render.DefaultRendererFactory; import prefuse.render.EdgeRenderer; import prefuse.render.LabelRenderer; import prefuse.render.ShapeRenderer; import prefuse.util.ColorLib; import prefuse.util.FontLib; import prefuse.util.ui.JFastLabel; import prefuse.visual.VisualItem; import prefuse.visual.expression.InGroupPredicate; import prefuse.visual.sort.TreeDepthItemSorter; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; import java.awt.geom.Point2D; import java.io.InputStream; /** * Demonstration of a node-link tree viewer * * @version 1.0 * @author <a href="http://jheer.org">jeffrey heer</a> */ public class ProTreeView extends Display { /** * */ private static final long serialVersionUID = -2460952911303437417L; private static final String tree = "tree"; private static final String treeNodes = "tree.nodes"; private static final String treeEdges = "tree.edges"; private LabelRenderer m_nodeRenderer; private EdgeRenderer m_edgeRenderer; private NodeLinkTreeLayout treeLayout; private String m_label = "label"; private int m_orientation = Constants.ORIENT_TOP_BOTTOM; public ProTreeView(Tree t, String label) { super(new Visualization()); m_label = label; m_vis.add(tree, t); m_nodeRenderer = new LabelRenderer(m_label); m_nodeRenderer.setRenderType(ShapeRenderer.RENDER_TYPE_FILL); m_nodeRenderer.setHorizontalAlignment(Constants.LEFT); m_nodeRenderer.setRoundedCorner(8,8); m_edgeRenderer = new EdgeRenderer(Constants.EDGE_TYPE_CURVE); DefaultRendererFactory rf = new DefaultRendererFactory(m_nodeRenderer); rf.add(new InGroupPredicate(treeEdges), m_edgeRenderer); m_vis.setRendererFactory(rf); // colors ItemAction nodeColor = new NodeColorAction(treeNodes); ItemAction textColor = new ColorAction(treeNodes, VisualItem.TEXTCOLOR, ColorLib.rgb(0,0,0)); m_vis.putAction("textColor", textColor); ItemAction edgeColor = new ColorAction(treeEdges, VisualItem.STROKECOLOR, ColorLib.rgb(200,200,200)); // quick repaint ActionList repaint = new ActionList(); repaint.add(nodeColor); repaint.add(new RepaintAction()); m_vis.putAction("repaint", repaint); // full paint ActionList fullPaint = new ActionList(); fullPaint.add(nodeColor); m_vis.putAction("fullPaint", fullPaint); // animate paint change ActionList animatePaint = new ActionList(400); animatePaint.add(new ColorAnimator(treeNodes)); animatePaint.add(new RepaintAction()); m_vis.putAction("animatePaint", animatePaint); // create the tree layout action treeLayout = new NodeLinkTreeLayout(tree, m_orientation, 50, 0, 8); treeLayout.setLayoutAnchor(new Point2D.Double(400,50)); m_vis.putAction("treeLayout", treeLayout); CollapsedSubtreeLayout subLayout = new CollapsedSubtreeLayout(tree, m_orientation); m_vis.putAction("subLayout", subLayout); AutoPanAction autoPan = new AutoPanAction(); // create the filtering and layout ActionList filter = new ActionList(); filter.add(new FisheyeTreeFilter(tree, 2)); filter.add(new FontAction(treeNodes, FontLib.getFont("Tahoma", 12))); filter.add(treeLayout); filter.add(subLayout); filter.add(textColor); filter.add(nodeColor); filter.add(edgeColor); m_vis.putAction("filter", filter); // animated transition ActionList animate = new ActionList(1000); animate.setPacingFunction(new SlowInSlowOutPacer()); animate.add(autoPan); animate.add(new QualityControlAnimator()); animate.add(new VisibilityAnimator(tree)); animate.add(new LocationAnimator(treeNodes)); animate.add(new ColorAnimator(treeNodes)); animate.add(new RepaintAction()); m_vis.putAction("animate", animate); m_vis.alwaysRunAfter("filter", "animate"); // create animator for orientation changes ActionList orient = new ActionList(2000); orient.setPacingFunction(new SlowInSlowOutPacer()); orient.add(autoPan); orient.add(new QualityControlAnimator()); orient.add(new LocationAnimator(treeNodes)); orient.add(new RepaintAction()); m_vis.putAction("orient", orient); // ------------------------------------------------ // initialize the display setSize(800 ,450); setItemSorter(new TreeDepthItemSorter()); addControlListener(new ZoomToFitControl()); addControlListener(new ZoomControl()); addControlListener(new PanControl()); addControlListener(new FocusControl(1, "filter")); registerKeyboardAction( new OrientAction(Constants.ORIENT_LEFT_RIGHT), "left-to-right", KeyStroke.getKeyStroke("ctrl 1"), WHEN_FOCUSED); registerKeyboardAction( new OrientAction(Constants.ORIENT_TOP_BOTTOM), "top-to-bottom", KeyStroke.getKeyStroke("ctrl 2"), WHEN_FOCUSED); registerKeyboardAction( new OrientAction(Constants.ORIENT_RIGHT_LEFT), "right-to-left", KeyStroke.getKeyStroke("ctrl 3"), WHEN_FOCUSED); registerKeyboardAction( new OrientAction(Constants.ORIENT_BOTTOM_TOP), "bottom-to-top", KeyStroke.getKeyStroke("ctrl 4"), WHEN_FOCUSED); // ------------------------------------------------ // filter graph and perform layout setOrientation(m_orientation); m_vis.run("filter"); TupleSet search = new PrefixSearchTupleSet(); m_vis.addFocusGroup(Visualization.SEARCH_ITEMS, search); search.addTupleSetListener(new TupleSetListener() { public void tupleSetChanged(TupleSet t, Tuple[] add, Tuple[] rem) { m_vis.cancel("animatePaint"); m_vis.run("fullPaint"); m_vis.run("animatePaint"); } }); } // ------------------------------------------------------------------------ public void setOrientation(int orientation) { NodeLinkTreeLayout rtl = (NodeLinkTreeLayout)m_vis.getAction("treeLayout"); CollapsedSubtreeLayout stl = (CollapsedSubtreeLayout)m_vis.getAction("subLayout"); switch ( orientation ) { case Constants.ORIENT_LEFT_RIGHT: m_nodeRenderer.setHorizontalAlignment(Constants.LEFT); m_edgeRenderer.setHorizontalAlignment1(Constants.RIGHT); m_edgeRenderer.setHorizontalAlignment2(Constants.LEFT); m_edgeRenderer.setVerticalAlignment1(Constants.CENTER); m_edgeRenderer.setVerticalAlignment2(Constants.CENTER); treeLayout.setLayoutAnchor(new Point2D.Double(50,200)); break; case Constants.ORIENT_RIGHT_LEFT: m_nodeRenderer.setHorizontalAlignment(Constants.RIGHT); m_edgeRenderer.setHorizontalAlignment1(Constants.LEFT); m_edgeRenderer.setHorizontalAlignment2(Constants.RIGHT); m_edgeRenderer.setVerticalAlignment1(Constants.CENTER); m_edgeRenderer.setVerticalAlignment2(Constants.CENTER); treeLayout.setLayoutAnchor(new Point2D.Double(750,200)); break; case Constants.ORIENT_TOP_BOTTOM: m_nodeRenderer.setHorizontalAlignment(Constants.CENTER); m_edgeRenderer.setHorizontalAlignment1(Constants.CENTER); m_edgeRenderer.setHorizontalAlignment2(Constants.CENTER); m_edgeRenderer.setVerticalAlignment1(Constants.BOTTOM); m_edgeRenderer.setVerticalAlignment2(Constants.TOP); treeLayout.setLayoutAnchor(new Point2D.Double(400,50)); break; case Constants.ORIENT_BOTTOM_TOP: m_nodeRenderer.setHorizontalAlignment(Constants.CENTER); m_edgeRenderer.setHorizontalAlignment1(Constants.CENTER); m_edgeRenderer.setHorizontalAlignment2(Constants.CENTER); m_edgeRenderer.setVerticalAlignment1(Constants.TOP); m_edgeRenderer.setVerticalAlignment2(Constants.BOTTOM); treeLayout.setLayoutAnchor(new Point2D.Double(400,400)); break; default: throw new IllegalArgumentException( "Unrecognized orientation value: "+orientation); } m_orientation = orientation; rtl.setOrientation(orientation); stl.setOrientation(orientation); } public int getOrientation() { return m_orientation; } public static JFrame visualize_tree(InputStream source, final String field, final String sourceDRL, final String drlTree, final String titleStr) { Color BACKGROUND = Color.WHITE; Color FOREGROUND = Color.BLACK; Tree t = null; try { t = (Tree) new TreeMLReader().readGraph(source); } catch (DataIOException e1) { e1.printStackTrace(); } // create a new treemap final ProTreeView tview = new ProTreeView(t, field); tview.setBackground(BACKGROUND); tview.setForeground(FOREGROUND); JTextArea original = new JTextArea(); original.setText(sourceDRL); JScrollPane scroller1 = new JScrollPane(original); scroller1.setPreferredSize(new Dimension(400,100)); scroller1.setBackground(BACKGROUND); scroller1.setForeground(FOREGROUND); scroller1.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 14)); JTextArea parsed = new JTextArea(); parsed.setText(drlTree); JScrollPane scroller2 = new JScrollPane(parsed); scroller2.setPreferredSize(new Dimension(400,100)); scroller2.setBackground(BACKGROUND); scroller2.setForeground(FOREGROUND); scroller2.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 14)); JSplitPane splitter = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, scroller1, scroller2); final JFastLabel title = new JFastLabel(" "); title.setPreferredSize(new Dimension(350, 20)); title.setVerticalAlignment(SwingConstants.BOTTOM); title.setHorizontalAlignment(SwingConstants.CENTER); // title.setBorder(BorderFactory.createEmptyBorder(3,0,0,0)); title.setText(titleStr); title.setFont(FontLib.getFont("Tahoma", Font.PLAIN, 14)); title.setBackground(BACKGROUND); title.setForeground(FOREGROUND); tview.addControlListener(new ControlAdapter() { public void itemEntered(VisualItem item, MouseEvent e) { if ( item.canGetString(field) ) title.setText(titleStr + " : " + item.getString(field)); } public void itemExited(VisualItem item, MouseEvent e) { title.setText(titleStr); } }); JPanel panel = new JPanel(new BorderLayout()); panel.setBackground(BACKGROUND); panel.setForeground(FOREGROUND); panel.add(title, BorderLayout.NORTH); panel.add(tview, BorderLayout.CENTER); panel.add(splitter, BorderLayout.SOUTH); JFrame frame = new JFrame(titleStr); frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); frame.setContentPane(panel); frame.pack(); return frame; } // ------------------------------------------------------------------------ public class OrientAction extends AbstractAction { private static final long serialVersionUID = 1L; private int orientation; public OrientAction(int orientation) { this.orientation = orientation; } public void actionPerformed(ActionEvent evt) { setOrientation(orientation); getVisualization().cancel("orient"); getVisualization().run("treeLayout"); getVisualization().run("orient"); } } public class AutoPanAction extends prefuse.action.Action { private Point2D m_start = new Point2D.Double(); private Point2D m_end = new Point2D.Double(); private Point2D m_cur = new Point2D.Double(); private int m_bias = 150; public void run(double frac) { TupleSet ts = m_vis.getFocusGroup(Visualization.FOCUS_ITEMS); if ( ts.getTupleCount() == 0 ) return; if ( frac == 0.0 ) { int xbias=0, ybias=0; switch ( m_orientation ) { case Constants.ORIENT_LEFT_RIGHT: xbias = m_bias; break; case Constants.ORIENT_RIGHT_LEFT: xbias = -m_bias; break; case Constants.ORIENT_TOP_BOTTOM: ybias = m_bias; break; case Constants.ORIENT_BOTTOM_TOP: ybias = -m_bias; break; } VisualItem vi = (VisualItem)ts.tuples().next(); m_cur.setLocation(getWidth()/2, getHeight()/2); getAbsoluteCoordinate(m_cur, m_start); m_end.setLocation(vi.getX()+xbias, vi.getY()+ybias); } else { m_cur.setLocation(m_start.getX() + frac*(m_end.getX()-m_start.getX()), m_start.getY() + frac*(m_end.getY()-m_start.getY())); panToAbs(m_cur); } } } public static class NodeColorAction extends ColorAction { public NodeColorAction(String group) { super(group, VisualItem.FILLCOLOR); } public int getColor(VisualItem item) { if ( m_vis.isInGroup(item, Visualization.SEARCH_ITEMS) ) return ColorLib.rgb(255,190,190); else if ( m_vis.isInGroup(item, Visualization.FOCUS_ITEMS) ) return ColorLib.rgb(198,229,229); else if ( item.getDOI() > -1 ) return ColorLib.rgb(164,193,193); else return ColorLib.rgba(255,255,255,0); } } // end of inner class TreeMapColorAction } // end of class TreeMap