1765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye/*******************************************************************************
2765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * Copyright (c) 2011 Google, Inc.
3765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * All rights reserved. This program and the accompanying materials
4765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * are made available under the terms of the Eclipse Public License v1.0
5765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * which accompanies this distribution, and is available at
6765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * http://www.eclipse.org/legal/epl-v10.html
7765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *
8765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * Contributors:
9765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *    Google, Inc. - initial API and implementation
10765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *******************************************************************************/
11765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyepackage org.eclipse.wb.internal.core.model.property.table;
12765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
13765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport com.google.common.base.Charsets;
14765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport com.google.common.base.Joiner;
15765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
16765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.SWT;
17765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.browser.Browser;
18765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.browser.LocationAdapter;
19765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.browser.LocationEvent;
20765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.browser.ProgressAdapter;
21765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.browser.ProgressEvent;
22765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.graphics.Color;
23765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.graphics.Point;
24765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.widgets.Composite;
25765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.widgets.Control;
26765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.widgets.Event;
27765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.widgets.Label;
28765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.widgets.Listener;
29765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.swt.widgets.Shell;
30765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.ui.PlatformUI;
31765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.ui.browser.IWebBrowser;
32765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.ui.browser.IWorkbenchBrowserSupport;
33765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.wb.draw2d.IColorConstants;
34765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.wb.internal.core.DesignerPlugin;
35765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.wb.internal.core.EnvironmentUtils;
36765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.wb.internal.core.utils.reflect.ReflectionUtils;
37765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.wb.internal.core.utils.ui.GridDataFactory;
38765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport org.eclipse.wb.internal.core.utils.ui.PixelConverter;
39765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
40765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.io.StringReader;
41765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.net.URL;
42765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyeimport java.text.MessageFormat;
43765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
44765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye/**
45765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * Helper for displaying HTML tooltips.
46765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye *
47765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * @author scheglov_ke
48765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye * @coverage core.model.property.table
49765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye */
50765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbyepublic final class HtmlTooltipHelper {
51765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static Control createTooltipControl(Composite parent, String header, String details) {
52765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return createTooltipControl(parent, header, details, 0);
53765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
54765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
55765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static Control createTooltipControl(Composite parent,
56765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String header,
57765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String details,
58765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      int heightLimit) {
59765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare Control
60765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Control control;
61765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    try {
62765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String html = "<table cellspacing=2 cellpadding=0 border=0 margins=0 id=_wbp_tooltip_body>";
63765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (header != null) {
64765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        html += "<tr align=center><td><b>" + header + "</b></td></tr>";
65765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
66765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      html += "<tr><td align=justify>" + details + "</td></tr>";
67765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      html += "</table>";
68765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      control = createTooltipControl_Browser(parent, html, heightLimit);
69765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    } catch (Throwable e) {
70765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      control = createTooltipControl_Label(parent, details);
71765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
72765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // set listeners
73765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    {
74765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Listener listener = new Listener() {
75765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        @Override
76765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        public void handleEvent(Event event) {
77765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          Control tooltipControl = (Control) event.widget;
78765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          hideTooltip(tooltipControl);
79765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
80765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      };
81765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      control.addListener(SWT.MouseExit, listener);
82765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
83765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // done
84765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return control;
85765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
86765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
87765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
88765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Creates {@link Browser} for displaying tooltip.
89765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
90765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static Control createTooltipControl_Browser(Composite parent,
91765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String html,
92765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      final int heightLimitChars) {
93765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare styles
94765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    String styles;
95765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    try {
96765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        styles = DesignerPlugin.readFile(PropertyTable.class.getResourceAsStream("Tooltip.css"),
97765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye                Charsets.US_ASCII);
98765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        if (styles == null) {
99765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye            styles = "";
100765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
101765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    } catch (Throwable e) {
102765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      styles = "";
103765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
104765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare HTML with styles and tags
105765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    String wrappedHtml;
106765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    {
107765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String bodyAttributes =
108765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          MessageFormat.format(
109765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              "text=''{0}'' bgcolor=''{1}''",
110765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              getColorWebString(IColorConstants.tooltipForeground),
111765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              getColorWebString(IColorConstants.tooltipBackground));
112765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String closeElement =
113765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          EnvironmentUtils.IS_LINUX
114765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              ? "    <a href='' style='position:absolute;right:1em;' id=_wbp_close>Close</a>"
115765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              : "";
116765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      wrappedHtml =
117765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          /*CodeUtils.*/getSource(
118765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              "<html>",
119765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              "  <style CHARSET='ISO-8859-1' TYPE='text/css'>",
120765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              styles,
121765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              "  </style>",
122765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              "  <body " + bodyAttributes + ">",
123765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              closeElement,
124765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              html,
125765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              "  </body>",
126765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye              "</html>");
127765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
128765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare Browser
129765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    final Browser browser = new Browser(parent, SWT.NONE);
130765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    browser.setText(wrappedHtml);
131765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // open URLs in new window
132765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    browser.addLocationListener(new LocationAdapter() {
133765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      @Override
134765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      public void changing(LocationEvent event) {
135765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        event.doit = false;
136765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        hideTooltip((Browser) event.widget);
137765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        if (!"about:blank".equals(event.location)) {
138765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          try {
139765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye            IWorkbenchBrowserSupport support = PlatformUI.getWorkbench().getBrowserSupport();
140765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye            IWebBrowser browserSupport = support.createBrowser("wbp.browser");
141765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye            browserSupport.openURL(new URL(event.location));
142765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          } catch (Throwable e) {
143765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye            DesignerPlugin.log(e);
144765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          }
145765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
146765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
147765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    });
148765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // set size
149765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    {
150765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      int textLength = getTextLength(html);
151765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // horizontal hint
152765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      int hintH = 50;
153765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (textLength < 100) {
154765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        hintH = 40;
155765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
156765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // vertical hint
157765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      int hintV = textLength / hintH + 3;
158765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      hintV = Math.min(hintV, 8);
159765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // do set
160765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      GridDataFactory.create(browser).hintC(hintH, hintV);
161765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
162765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // tweak size after rendering HTML
163765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    browser.addProgressListener(new ProgressAdapter() {
164765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      @Override
165765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      public void completed(ProgressEvent event) {
166765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        browser.removeProgressListener(this);
167765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        tweakBrowserSize(browser, heightLimitChars);
168765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        browser.getShell().setVisible(true);
169765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
170765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    });
171765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // done
172765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return browser;
173765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
174765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
175765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static void tweakBrowserSize(Browser browser, int heightLimitChars) {
176765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    GridDataFactory.create(browser).grab().fill();
177765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // limit height
178765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (heightLimitChars != 0) {
179765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      PixelConverter pixelConverter = new PixelConverter(browser);
180765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      int maxHeight = pixelConverter.convertHeightInCharsToPixels(heightLimitChars);
181765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      expandShellToShowFullPage_Height(browser, maxHeight);
182765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
183765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // if no limit, then show all, so make as tall as required
184765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    if (heightLimitChars == 0) {
185765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      expandShellToShowFullPage_Height(browser, Integer.MAX_VALUE);
186765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
187765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
188765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
189765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static void expandShellToShowFullPage_Height(Browser browser, int maxHeight) {
190765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    try {
191765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Shell shell = browser.getShell();
192765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // calculate required
193765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      int contentHeight;
194765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      {
195765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        getContentOffsetHeight(browser);
196765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        contentHeight = getContentScrollHeight(browser);
197765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
198765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // apply height
199765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      int useHeight = Math.min(contentHeight + ((EnvironmentUtils.IS_LINUX) ? 2 : 10), maxHeight);
200765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      shell.setSize(shell.getSize().x, useHeight);
201765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // trim height to content
202765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      {
203765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        int offsetHeight = getBodyOffsetHeight(browser);
204765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        int scrollHeight = getBodyScrollHeight(browser);
205765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        int delta = scrollHeight - offsetHeight;
206765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        if (delta != 0 && delta < 10) {
207765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          Point size = shell.getSize();
208765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          shell.setSize(size.x, size.y + delta + 1);
209765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
210765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
211765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // trim width to content
212765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      {
213765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        int offsetWidth = getContentOffsetWidth(browser);
214765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        {
215765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          Point size = shell.getSize();
216765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          shell.setSize(offsetWidth + ((EnvironmentUtils.IS_MAC) ? 6 : 10), size.y);
217765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
218765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
219765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      // hide 'Close' if too narrow
220765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      if (EnvironmentUtils.IS_LINUX) {
221765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        if (shell.getSize().y < 30) {
222765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          hideCloseElement(browser);
223765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        }
224765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
225765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    } catch (Throwable e) {
226765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
227765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
228765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
229765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static int getContentOffsetWidth(Browser browser) throws Exception {
230765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return evaluateScriptInt(
231765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        browser,
232765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        "return document.getElementById('_wbp_tooltip_body').offsetWidth;");
233765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
234765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
235765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static int getContentOffsetHeight(Browser browser) throws Exception {
236765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return evaluateScriptInt(
237765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        browser,
238765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        "return document.getElementById('_wbp_tooltip_body').offsetHeight;");
239765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
240765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
241765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static int getContentScrollHeight(Browser browser) throws Exception {
242765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return evaluateScriptInt(
243765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        browser,
244765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        "return document.getElementById('_wbp_tooltip_body').scrollHeight;");
245765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
246765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
247765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static int getBodyOffsetHeight(Browser browser) throws Exception {
248765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return evaluateScriptInt(browser, "return document.body.offsetHeight;");
249765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
250765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
251765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static int getBodyScrollHeight(Browser browser) throws Exception {
252765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return evaluateScriptInt(browser, "return document.body.scrollHeight;");
253765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
254765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
255765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static int evaluateScriptInt(Browser browser, String script) throws Exception {
256765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    Object o = ReflectionUtils.invokeMethod(browser, "evaluate(java.lang.String)", script);
257765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return ((Number) o).intValue();
258765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
259765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
260765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static void hideCloseElement(Browser browser) throws Exception {
261765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    String script = "document.getElementById('_wbp_close').style.display = 'none'";
262765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    ReflectionUtils.invokeMethod(browser, "evaluate(java.lang.String)", script);
263765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
264765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
265765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
266765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the length of text in given HTML. Uses internal class, so may fail, in this case
267765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *         returns length on HTML.
268765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
269765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static int getTextLength(String html) {
270765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    StringReader htmlStringReader = new StringReader(html);
271765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    try {
272765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      ClassLoader classLoader = PropertyTable.class.getClassLoader();
273765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Class<?> readerClass =
274765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye          classLoader.loadClass("org.eclipse.jface.internal.text.html.HTML2TextReader");
275765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      Object reader = readerClass.getConstructors()[0].newInstance(htmlStringReader, null);
276765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      String text = (String) ReflectionUtils.invokeMethod(reader, "getString()");
277765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return text.length();
278765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    } catch (Throwable e) {
279765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return html.length();
280765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    }
281765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
282765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
283765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
284765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Returns a string representation of {@link Color} suitable for web pages.
285765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *
286765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @param color
287765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   *          the {@link Color} instance, not <code>null</code>.
288765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return a string representation of {@link Color} suitable for web pages.
289765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
290765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static String getColorWebString(final Color color) {
291765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    String colorString = "#" + Integer.toHexString(color.getRed());
292765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    colorString += Integer.toHexString(color.getGreen());
293765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    colorString += Integer.toHexString(color.getBlue());
294765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return colorString;
295765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
296765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
297765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
298765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * Creates {@link Label} if {@link Browser} can not be used.
299765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
300765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static Control createTooltipControl_Label(Composite parent, String html) {
301765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // prepare Label
302765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    final Label label = new Label(parent, SWT.WRAP);
303765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    label.setText(html);
304765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // set size
305765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    int requiredWidth = label.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
306765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    GridDataFactory.create(label).hintHC(50).hintHMin(requiredWidth);
307765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // copy colors
308765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    label.setForeground(parent.getForeground());
309765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    label.setBackground(parent.getBackground());
310765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    // done
311765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    parent.getDisplay().asyncExec(new Runnable() {
312765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      @Override
313765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    public void run() {
314765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        Shell shell = label.getShell();
315765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye        shell.setVisible(true);
316765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      }
317765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    });
318765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    return label;
319765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
320765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
321765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  private static void hideTooltip(Control tooltip) {
322765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye    tooltip.getShell().dispose();
323765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
324765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye
325765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  // Copied from CodeUtils.java: CodeUtils.getSource()
326765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  /**
327765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   * @return the source as single {@link String}, lines joined using "\n".
328765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye   */
329765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  public static String getSource(String... lines) {
330765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye      return Joiner.on('\n').join(lines);
331765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye  }
332765e52e2d30d0754625b8c7af6c36e93612f15beTor Norbye}
333