1   /****************************************************************************
2    **
3    ** This file is part of the yFiles extension package ySVG-2.3.
4    ** 
5    ** yWorks proprietary/confidential. Use is subject to license terms.
6    **
7    ** Redistribution of this file or of an unauthorized byte-code version
8    ** of this file is strictly forbidden.
9    **
10   ** Copyright (c) 2002-2010 by yWorks GmbH, Vor dem Kreuzberg 28,
11   ** 72070 Tuebingen, Germany. All rights reserved.
12   **
13   ***************************************************************************/
14  package demo.yext.svg;
15  
16  import yext.svg.io.SVGDOMEnhancer;
17  import yext.svg.io.SVGIOHandler;
18  
19  import y.base.Node;
20  import y.view.NodeRealizer;
21  import y.view.ViewMode;
22  
23  import javax.swing.JPanel;
24  import javax.swing.JToolTip;
25  import javax.swing.SwingUtilities;
26  import java.awt.Dimension;
27  import java.awt.Graphics2D;
28  import java.awt.EventQueue;
29  import java.awt.geom.AffineTransform;
30  import org.w3c.dom.Document;
31  import org.w3c.dom.Element;
32  
33  /**
34   * Demo that shows how to add HTML formatted tooltips for nodes to a
35   * generated SVG file.
36   */
37  public class HtmlTooltipDemo extends SVGExportDemo
38  {
39  
40    /**
41     * Add a view mode that displays a tooltip for each node
42     */
43    public HtmlTooltipDemo() {
44      view.addViewMode(new TooltipViewMode());
45    }
46  
47    /**
48     * Creates and configures the SVGIOHandlers to be used.
49     * This method will add a specialized SVGDOMEnhancer to
50     * the IOHandler.
51     */
52    protected SVGIOHandler createSVGIOHandler(boolean svgz)
53    {
54      SVGIOHandler ioh = super.createSVGIOHandler(svgz);
55      SVGDOMEnhancer enhancer = new HtmlTooltipDemo.TooltipEnhancer();
56      //draw edges behind nodes so that the node rollover does not get spoiled.
57      enhancer.setDrawEdgesFirst(true);
58      ioh.setSVGGraph2DRenderer(enhancer);
59      ioh.setCanvasSizeAssigned(false); //do not assign canvas size to avoid tooltop clipping
60      return ioh;
61    }
62  
63    /**
64     * Creates the HTML formatted tooltip to be displayed for a graph node.
65     * @param node the graph node for which the tooltip is created.
66     * @return a HTML formatted tooltip for specified node.
67     */
68    String createHTMLDescription(Node node) {
69      return "<html><head>" +
70      "<style><!--\n" +
71      ".tooltip {\n" +
72      "  font-size: 10pt;\n" +
73      "  position: absolute;\n" +
74      "  display: none;\n" +
75      "  background-color: #EDECF4;\n" +
76      "  border-width: 1px;\n" +
77      "  border-style: solid;\n" +
78      "  border-color: #E2860E; }\n" +
79      "\n" +
80      ".tooltip .head {\n" +
81      "  font-weight: bold;\n" +
82      "  background-color: #E2860E;\n" +
83      "  color: #EDECF4;\n" +
84      "  padding: 2px; }\n" +
85      "\n" +
86      ".tooltip .content {\n" +
87      "  color: #666666;\n" +
88      "  font-weight: bold;\n" +
89      "  padding: 4px; }\n" +
90      "--></style>\n" +
91      "</head>\n" +
92      "<div class=\"tooltip\">\n" +
93        "<div class=\"head\">Description</div>\n" +
94        "<div class=\"content\">Node " + node + "</div>\n" +
95      "</div>";
96    }
97  
98    /**
99     * Viewmode that displays a HTML formatted tooltip for each node
100    */
101   class TooltipViewMode extends ViewMode {
102 
103     public void mouseMoved(double x, double y) {
104       Node hitNode = getHitInfo(x,y).getHitNode();
105       if(hitNode != null) {
106         String tipText = createHTMLDescription(hitNode);
107         if(tipText != null) {
108           view.setToolTipText(tipText);
109         }
110       }
111     }
112   }
113 
114   /**
115    * Specialized SVGDOMEnhancer that adds the necessary data and functionality to
116    * display tooltips from within an SVG file.
117    */
118   class TooltipEnhancer extends SVGDOMEnhancer
119   {
120     JPanel labelContainer = new JPanel();
121 
122     /**
123      * Override a callback method. Before the graph will be written to the SVG DOM
124      * we add an additional javascript node to the definition block of the SVG.
125      * The script will be used to show and hide tooltip elements
126      */
127     protected void initializeDOM()
128     {
129       addToSVGDefinition(createScript());
130     }
131 
132     /**
133      * Creates a DOM element that defines a ecmascript function responsible
134      * for showing and hiding appropriate tooltips at the right position.
135      */
136     private Element createScript() {
137       Element script = createElement("script");
138       script.setAttribute("type", "text/ecmascript");
139 
140       String sourceCode =
141               "      var SVGDocument = null;\n" +
142               "      var SVGRoot = null;\n" +
143               "      var toolTip = null;\n" +
144               "      var TrueCoords = null;\n" +
145               "      var lastElement = null;\n" +
146               "      var initialized = null;\n" +
147               "      var tipGroup;\n" +
148               "      function Init(evt)\n" +
149               "      {\n" +
150               "         SVGDocument = evt.target.ownerDocument;\n" +
151               "         SVGRoot = SVGDocument.documentElement;\n" +
152               "         TrueCoords = SVGRoot.createSVGPoint();\n" +
153               "         initialized = evt;\n" +
154               "      };\n" +
155               "      function GetTrueCoords(evt)\n" +
156               "      {\n" +
157               "         var newScale = SVGRoot.currentScale;\n" +
158               "         var translation = SVGRoot.currentTranslate;\n" +
159               "         TrueCoords.x = (evt.clientX - translation.x)/newScale;\n" +
160               "         TrueCoords.y = (evt.clientY - translation.y)/newScale;\n" +
161               "      };\n" +
162               "      function HideTooltip( evt )\n" +
163               "      {\n" +
164               "         if(initialized == null) {\n" +
165               "           Init(evt);\n" +
166               "         }\n" +
167               "         if(tipGroup != null) {\n" +
168               "           tipGroup.setAttributeNS(null, 'visibility', 'hidden');\n" +
169               "         }\n" +
170               "      };\n" +
171               "      function ShowTooltip( evt )\n" +
172               "      {\n" +
173               "         if(initialized == null) {\n" +
174               "           Init(evt);\n" +
175               "         }\n" +
176               "         GetTrueCoords( evt );\n" +
177               "         var tipScale = 1/SVGRoot.currentScale;\n" +
178               "         var textWidth = 0;\n" +
179               "         var tspanWidth = 0;\n" +
180               "         var boxHeight = 20;\n" +
181               "         var targetElement = evt.currentTarget;\n" +
182               "         if ( lastElement != targetElement )\n" +
183               "         {\n" +
184               "            var targetId = targetElement.getAttributeNS(null, 'id');\n" +
185               "            var tipId = 'tooltip.' + targetId;\n" +
186               "            tipGroup = SVGDocument.getElementById(tipId);\n" +
187               "            var xPos = TrueCoords.x + (10 * tipScale);\n" +
188               "            var yPos = TrueCoords.y + (10 * tipScale);\n" +
189               "            tipGroup.setAttributeNS(null, 'transform', 'translate(' + xPos + ',' + yPos + ') scale(' + tipScale + ')');\n" +
190               "            tipGroup.setAttributeNS(null, 'visibility', 'visible');\n" +
191               "         }\n" +
192               "      };\n";
193 
194 
195       script.appendChild(createCDATASection(sourceCode));
196 
197       return script;
198     }
199 
200     protected void paint(Graphics2D gfx, NodeRealizer r, boolean sloppyMode) {
201       super.paint(gfx,r,sloppyMode);
202       paintDescription(gfx, r.getNode(), createGroupID(r.getNode()));
203     }
204 
205     /**
206      * Override a callback method.
207      * Adds onmouseover and onmouseout attributes to the element that represents
208      * a given yNode in SVG. These attributes trigger the javascript function defined
209      * above.
210      */
211     protected void nodeAddedToDOM(Node yNode, Element element) {
212       String desc = createHTMLDescription(yNode);
213       if(desc != null && !desc.equals("")) {
214         element.setAttribute(
215             "onmousemove",
216             "ShowTooltip(evt)");
217         element.setAttribute(
218             "onmouseout",
219             "HideTooltip(evt)");
220       }
221     }
222 
223     private void paintDescription(Graphics2D gfx, Node node, String objectId) {
224       String desc = createHTMLDescription(node);
225       if (desc != null && desc.trim().length() > 0) {
226         Document doc = getSVGDocument();
227         Element topLevelGroup = getTopLevelGroup();
228         Element tipGroup = doc.createElement("g");
229         tipGroup.setAttribute("visibility", "hidden");
230         tipGroup.setAttribute("pointer-events", "none");
231         tipGroup.setAttribute("id", "tooltip." + objectId);
232         topLevelGroup.appendChild(tipGroup);
233 
234         setTopLevelGroup(tipGroup);
235 
236         JToolTip label = new JToolTip();
237         label.setTipText(desc);
238         Dimension dim = label.getPreferredSize();
239         label.setSize(dim.width, dim.height);
240 
241         AffineTransform origTrans = gfx.getTransform();
242         gfx.setTransform(new AffineTransform());
243         SwingUtilities.paintComponent(gfx, label, labelContainer, 0, 0, dim.width, dim.height);
244         gfx.setTransform(origTrans);
245         setTopLevelGroup(topLevelGroup);
246       }
247     }
248   }
249 
250   /**
251    * Launches this demo.
252    * @param args ignored.
253    */
254   public static void main(String[] args) {
255     EventQueue.invokeLater(new Runnable() {
256       public void run() {
257         initLnF();
258         (new HtmlTooltipDemo()).start();
259       }
260     });
261   }
262 }
263