/** * Constructor. * * @param owner The parent window. * @param msg The text of the tool tip. This can be HTML. */ public TipWindow(Window owner, FocusableTip ft, String msg) { super(owner); this.ft = ft; // Render plain text tool tips correctly. if (msg != null && msg.length() >= 6 && !msg.substring(0, 6).toLowerCase().equals("<html>")) { msg = "<html>" + RSyntaxUtilities.escapeForHtml(msg, "<br>", false); } this.text = msg; tipListener = new TipListener(); JPanel cp = new JPanel(new BorderLayout()); cp.setBorder(TipUtil.getToolTipBorder()); cp.setBackground(TipUtil.getToolTipBackground()); textArea = new JEditorPane("text/html", text); TipUtil.tweakTipEditorPane(textArea); if (ft.getImageBase() != null) { // Base URL for images ((HTMLDocument) textArea.getDocument()).setBase(ft.getImageBase()); } textArea.addMouseListener(tipListener); textArea.addHyperlinkListener( new HyperlinkListener() { public void hyperlinkUpdate(HyperlinkEvent e) { if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { TipWindow.this.ft.possiblyDisposeOfTipWindow(); } } }); cp.add(textArea); setFocusableWindowState(false); setContentPane(cp); setBottomPanel(); // Must do after setContentPane() pack(); // InputMap/ActionMap combo doesn't work for JWindows (even when // using the JWindow's JRootPane), so we'll resort to KeyListener KeyAdapter ka = new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_ESCAPE) { TipWindow.this.ft.possiblyDisposeOfTipWindow(); } } }; addKeyListener(ka); textArea.addKeyListener(ka); // Ensure only 1 TipWindow is ever visible. If the caller does what // they're supposed to and only creates these on the EDT, the // synchronization isn't necessary, but we'll be extra safe. synchronized (TipWindow.class) { if (visibleInstance != null) { visibleInstance.dispose(); } visibleInstance = this; } }