org.nuiton.jaxx.widgets.extra.tooltip.TipWindow Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jaxx-widgets-extra Show documentation
Show all versions of jaxx-widgets-extra Show documentation
Widgets graphiques utiles pour tous les développements (pour le moment sans lien avec JAXX).
/*
* %%Ignore-License
*
* 07/29/2009
*
* TipWindow.java - The actual window component representing the tool tip.
* Copyright (C) 2009 Robert Futrell
* robert_futrell at users.sourceforge.net
* http://fifesoft.com/rsyntaxtextarea
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*/
package org.nuiton.jaxx.widgets.extra.tooltip;
import javax.swing.BorderFactory;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSeparator;
import javax.swing.JWindow;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkListener;
import javax.swing.event.MouseInputAdapter;
import javax.swing.text.html.HTMLDocument;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Point;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import static org.nuiton.i18n.I18n.t;
/**
* The actual tool tip component.
*
* @author Robert Futrell
* @version 1.0
*/
public class TipWindow extends JWindow implements ActionListener {
protected FocusableTip ft;
protected Component component;
protected String text;
protected TipListener tipListener;
protected HyperlinkListener userHyperlinkListener;
protected static TipWindow visibleInstance;
/**
* Constructor, by default, is JEditorPane will be used for toolTip.
*
* @param owner The parent window.
* @param ft Focusable tip
* @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;
tipListener = new TipListener();
JEditorPane textArea = new JEditorPane("text/html", msg);
TipUtil.tweakTipEditorPane(textArea);
if (ft.getImageBase() != null) { // Base URL for images
((HTMLDocument) textArea.getDocument()).setBase(ft.getImageBase());
}
textArea.addMouseListener(tipListener);
textArea.addHyperlinkListener(e -> {
if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
TipWindow.this.ft.possiblyDisposeOfTipWindow();
}
});
init(ft, textArea);
}
/**
* To specify component to display
*
* @param owner The parent window.
* @param ft Focusable tip
* @param component custom component to display
*/
public TipWindow(Window owner, FocusableTip ft, Component component) {
super(owner);
init(ft, component);
}
protected void init(FocusableTip ft, Component component) {
this.ft = ft;
tipListener = new TipListener();
this.component = component;
JPanel cp = new JPanel(new BorderLayout());
cp.setBorder(BorderFactory.createCompoundBorder(BorderFactory
.createLineBorder(Color.BLACK), BorderFactory
.createEmptyBorder()));
cp.setBackground(TipUtil.getToolTipBackground());
component.addMouseListener(tipListener);
cp.add(component);
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() {
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
TipWindow.this.ft.possiblyDisposeOfTipWindow();
}
}
};
addKeyListener(ka);
component.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;
}
}
/**
* Return textArea of TipWindow
*
* @return textArea
*/
public Component getComponent() {
return component;
}
public void actionPerformed(ActionEvent e) {
if (!getFocusableWindowState()) {
setFocusableWindowState(true);
setBottomPanel();
component.removeMouseListener(tipListener);
pack();
addWindowFocusListener(new WindowAdapter() {
public void windowLostFocus(WindowEvent e) {
ft.possiblyDisposeOfTipWindow();
}
});
ft.removeListeners();
if (e == null) { // Didn't get here via our mouseover timer
requestFocus();
}
}
}
/**
* Disposes of this window.
*/
public void dispose() {
//System.out.println("[DEBUG]: Disposing...");
Container cp = getContentPane();
for (int i = 0; i < cp.getComponentCount(); i++) {
// Okay if listener is already removed
cp.getComponent(i).removeMouseListener(tipListener);
}
ft.removeListeners();
super.dispose();
}
/**
* Workaround for JEditorPane not returning its proper preferred size
* when rendering HTML until after layout already done. See
* http://forums.sun.com/thread.jspa?forumID=57&threadID=574810 for a
* discussion.
*
* @param width min width
* @param height min height
*/
public void fixSize(int width, int height) {
// Dimension d = textArea.getPreferredSize();
// Rectangle r;
// try {
//
// r = textArea.modelToView(textArea.getDocument().getLength()-1);
// d.height = r.y + r.height;
//
// // Ensure the text area doesn't start out too tall or wide.
// d = textArea.getPreferredSize();
// d.width = Math.min(d.width+25, width);
// d.height = Math.min(d.height, height);
//
// textArea.setPreferredSize(d);
//
// } catch (BadLocationException ble) { // Never happens
// ble.printStackTrace();
// }
//
// pack(); // Must re-pack to calculate proper size.
// Use specified size
Dimension d = component.getPreferredSize();
d.width = width;
d.height = height;
component.setPreferredSize(d);
component.setSize(d);
pack();
}
public String getText() {
return text;
}
protected void setBottomPanel() {
final JPanel panel = new JPanel(new BorderLayout());
panel.add(new JSeparator(), BorderLayout.NORTH);
boolean focusable = getFocusableWindowState();
if (focusable) {
SizeGrip sg = new SizeGrip();
sg.applyComponentOrientation(sg.getComponentOrientation()); // Workaround
panel.add(sg, BorderLayout.LINE_END);
MouseInputAdapter adapter = new MouseInputAdapter() {
protected Point lastPoint;
public void mouseDragged(MouseEvent e) {
Point p = e.getPoint();
SwingUtilities.convertPointToScreen(p, panel);
if (lastPoint == null) {
lastPoint = p;
} else {
int dx = p.x - lastPoint.x;
int dy = p.y - lastPoint.y;
setLocation(getX() + dx, getY() + dy);
lastPoint = p;
}
}
public void mousePressed(MouseEvent e) {
lastPoint = e.getPoint();
SwingUtilities.convertPointToScreen(lastPoint, panel);
}
};
panel.addMouseListener(adapter);
panel.addMouseMotionListener(adapter);
// Don't add tipListener to the panel or SizeGrip
} else {
panel.setOpaque(false);
JLabel label = new JLabel(t("nuitonwidgets.tootilp.focushotkey")/*FocusableTip.getString("FocusHotkey")*/);
Color fg = UIManager.getColor("Label.disabledForeground");
Font font = component.getFont();
font = font.deriveFont(font.getSize2D() - 1.0f);
label.setFont(font);
if (fg == null) { // Non BasicLookAndFeel-derived Looks
fg = Color.GRAY;
}
label.setOpaque(true);
Color bg = TipUtil.getToolTipBackground();
label.setBackground(bg);
label.setForeground(fg);
label.setHorizontalAlignment(SwingConstants.TRAILING);
label.setBorder(BorderFactory.createEmptyBorder(0, 5, 0, 5));
panel.add(label);
panel.addMouseListener(tipListener);
}
// Replace the previous SOUTH Component with the new one.
Container cp = getContentPane();
if (cp.getComponentCount() == 2) { // Skip first time through
Component comp = cp.getComponent(0);
cp.remove(0);
JScrollPane sp = new JScrollPane(comp);
sp.setViewportBorder(BorderFactory.createEmptyBorder());
sp.setBackground(component.getBackground());
sp.getViewport().setBackground(component.getBackground());
cp.add(sp);
// What was component 1 is now 0.
cp.getComponent(0).removeMouseListener(tipListener);
cp.remove(0);
}
cp.add(panel, BorderLayout.SOUTH);
}
/**
* Sets the listener for hyperlink events in this tip window.
*
* @param listener The new listener. The old listener (if any) is
* removed. A value of null
means "no listener."
*/
public void setHyperlinkListener(HyperlinkListener listener) {
if (component instanceof JEditorPane) {
JEditorPane textArea = (JEditorPane) component;
// We've added a separate listener, so remove only the user's.
if (userHyperlinkListener != null) {
textArea.removeHyperlinkListener(userHyperlinkListener);
}
userHyperlinkListener = listener;
if (userHyperlinkListener != null) {
textArea.addHyperlinkListener(userHyperlinkListener);
}
}
}
/**
* Listens for events in this window.
*/
protected class TipListener extends MouseAdapter {
public TipListener() {
}
public void mousePressed(MouseEvent e) {
actionPerformed(null); // Manually create "real" window
}
public void mouseExited(MouseEvent e) {
// Since we registered this listener on the child components of
// the JWindow, not the JWindow iteself, we have to be careful.
Component source = (Component) e.getSource();
Point p = e.getPoint();
SwingUtilities.convertPointToScreen(p, source);
if (!TipWindow.this.getBounds().contains(p)) {
ft.possiblyDisposeOfTipWindow();
}
}
}
}