package org.freeplane.view.swing.map; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Insets; import java.awt.Rectangle; import java.awt.Window; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.net.URI; import java.net.URL; import java.security.AccessControlException; import javax.swing.JEditorPane; import javax.swing.JScrollPane; import javax.swing.JToolTip; import javax.swing.SwingUtilities; import javax.swing.text.html.HTMLDocument; import javax.swing.text.html.HTMLEditorKit; import javax.swing.text.html.StyleSheet; import org.freeplane.core.ui.components.JRestrictedSizeScrollPane; import org.freeplane.core.ui.components.UITools; import org.freeplane.core.ui.components.html.ScaledEditorKit; import org.freeplane.core.util.Compat; import org.freeplane.core.util.HtmlUtils; import org.freeplane.core.util.LogUtils; import org.freeplane.features.mode.Controller; import org.freeplane.features.url.UrlManager; @SuppressWarnings("serial") public class NodeTooltip extends JToolTip { public class LinkMouseListener extends MouseAdapter implements MouseMotionListener{ public void mouseMoved(final MouseEvent ev) { final String link = HtmlUtils.getURLOfExistingLink((HTMLDocument) tip.getDocument(), tip.viewToModel(ev.getPoint())); boolean followLink = link != null; Controller currentController = Controller.getCurrentController(); final int requiredCursor; if(followLink){ currentController.getViewController().out(link); requiredCursor = Cursor.HAND_CURSOR; } else{ requiredCursor = Cursor.DEFAULT_CURSOR; } if (tip.getCursor().getType() != requiredCursor) { tip.setCursor(requiredCursor != Cursor.DEFAULT_CURSOR ? new Cursor(requiredCursor) : null); } } public void mouseClicked(final MouseEvent ev) { if (Compat.isPlainEvent(ev)) { final String linkURL = HtmlUtils.getURLOfExistingLink((HTMLDocument) tip.getDocument(), tip.viewToModel(ev.getPoint())); if (linkURL != null) { try { UrlManager.getController().loadURL(new URI(linkURL)); } catch (Exception e) { LogUtils.warn(e); } } } } public void mouseDragged(MouseEvent e) { } } final private JEditorPane tip; public NodeTooltip(){ tip = new JEditorPane(); tip.setContentType("text/html"); tip.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, false); final HTMLEditorKit kit = ScaledEditorKit.create(); tip.setEditorKit(kit); tip.setEditable(false); tip.setMargin(new Insets(0, 0, 0, 0)); final LinkMouseListener mouseListener = new LinkMouseListener(); tip.addMouseListener(mouseListener); tip.addMouseMotionListener(mouseListener); final HTMLDocument document = (HTMLDocument) tip.getDocument(); final StyleSheet styleSheet = document.getStyleSheet(); styleSheet.removeStyle("p"); styleSheet.removeStyle("body"); styleSheet.addRule("p {margin-top:0;}\n"); final JRestrictedSizeScrollPane scrollPane = new JRestrictedSizeScrollPane(tip); scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); scrollPane.setMaximumSize(new Dimension(Integer.MAX_VALUE, maximumWidth / 2)); scrollPane.addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { revalidate(); } }); UITools.setScrollbarIncrement(scrollPane); add(scrollPane); tip.setOpaque(false); // scrollPane.setOpaque(false); // scrollPane.getViewport().setOpaque(false); } private static int maximumWidth = Integer.MAX_VALUE; /** * set maximum width * 0 = no maximum width */ public static void setMaximumWidth(final int width) { maximumWidth = width; } @Override public void setTipText(String tipText) { try{ setTipTextUnsafe(tipText); } catch (Exception e1) { if(e1 instanceof AccessControlException) LogUtils.warn(e1.getMessage()); else LogUtils.severe(e1); final String localizedMessage = e1.getLocalizedMessage(); final String htmlEscapedText = HtmlUtils.plainToHTML(localizedMessage + '\n' + tipText); try{ setTipTextUnsafe(htmlEscapedText); } catch (Exception e2){ } } } private void setTipTextUnsafe(String tipText) throws Exception{ tip.setText(tipText); Dimension preferredSize = tip.getPreferredSize(); if (preferredSize.width < maximumWidth) { return ; } final HTMLDocument document = (HTMLDocument) tip.getDocument(); document.getStyleSheet().addRule("body { width: " + maximumWidth + "}"); // bad hack: call "setEditable" only to update view tip.setEditable(true); tip.setEditable(false); } @Override public Dimension getPreferredSize() { return getComponent(0).getPreferredSize(); } @Override public void layout() { Window window = SwingUtilities.windowForComponent(this); if(! window.getFocusableWindowState()){ window.setFocusableWindowState(true); } getComponent(0).setSize(getPreferredSize()); super.layout(); } void scrollUp() { tip.scrollRectToVisible(new Rectangle(1, 1)); } public void setBase(URL url){ ((HTMLDocument)tip.getDocument()).setBase(url); } }