1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.xnap.commons.gui.util;
21
22 import java.awt.AWTEvent;
23 import java.awt.Component;
24 import java.awt.Cursor;
25 import java.awt.Point;
26 import java.awt.Toolkit;
27 import java.awt.event.AWTEventListener;
28 import java.awt.event.KeyEvent;
29 import java.awt.event.MouseEvent;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Map;
33 import java.util.Set;
34 import javax.swing.AbstractButton;
35 import javax.swing.Action;
36 import javax.swing.ImageIcon;
37 import javax.swing.JComponent;
38 import javax.swing.JPopupMenu;
39 import javax.swing.JToolTip;
40 import javax.swing.SwingUtilities;
41 import javax.swing.border.EmptyBorder;
42
43 /***
44 * Static helper class, that allows to enrich components with What's This help.
45 * <p>
46 * See {@link #setText(JComponent, String)} and {@link #enterWhatsThisMode()}.
47 *
48 * @see org.xnap.commons.gui.util.WhatsThisAction
49 *
50 * @author Felix Berger
51 */
52 public class WhatsThis {
53
54 private static EventHandler listener = new EventHandler();
55
56 private static HashMap<Component, Cursor> cursorsByComponent =
57 new HashMap<Component, Cursor>();
58
59 private static boolean justEntered;
60
61
62 private static Cursor gotHelpCursor = Toolkit.getDefaultToolkit().
63 createCustomCursor(((ImageIcon)IconHelper.getIcon("idea.png", 16)).getImage(),
64 new Point(0, 0), "gotHelpCursor");
65
66 private static Cursor noHelpCursor = Toolkit.getDefaultToolkit().
67 createCustomCursor(((ImageIcon)IconHelper.getIcon("contexthelp.png", 16)).getImage(),
68 new Point(0, 0), "noHelpCursor");
69
70 /***
71 * Sets the what's this text for the component.
72 * @param c the component
73 * @param text the text that should be displayed in the what's this popup
74 * for this component
75 */
76 public static void setText(JComponent c, String text)
77 {
78 c.putClientProperty(Action.LONG_DESCRIPTION, text);
79 }
80
81 /***
82 * Removes the what's this text for the component
83 * @param c the component
84 */
85 public static void removeText(JComponent c)
86 {
87 c.putClientProperty(Action.LONG_DESCRIPTION, null);
88 }
89
90 /***
91 * Retrieves the what's this text for the component. If
92 * <code>searchParents</code> is true their text is retrieved as fallback.
93 *
94 * @param c the component
95 * @param searchParents if true the component hierarchy is searched
96 * @return the text; null, if no what's this text has been set
97 */
98 public static String getText(Component c, boolean searchParents)
99 {
100 String text = null;
101 if (c instanceof AbstractButton) {
102
103 AbstractButton button = (AbstractButton)c;
104 if (button.getAction() != null) {
105 text = (String)button.getAction().getValue(Action.LONG_DESCRIPTION);
106 }
107 }
108 if (text == null && c instanceof JComponent) {
109 JComponent jc = (JComponent)c;
110 text = (String)jc.getClientProperty(Action.LONG_DESCRIPTION);
111 }
112 if (text == null && searchParents && c.getParent() != null) {
113 return getText(c.getParent(), searchParents);
114 }
115 return text;
116 }
117
118 /***
119 * Enters What's This mode.
120 * <p>
121 * A global mouse listener is added to the awt event queue looking for
122 * mouse movement and mouse clicks.
123 * <p>
124 * A special cursor is displayed for components that provide a What's this
125 * help text, another special cursor is displayed for components that don't.
126 * <p>
127 * The mode is exited automatically after the first mouse button click on
128 * any component. If the component in question has a What's this help
129 * text, the text is displayed in a popup.
130 */
131 public static void enterWhatsThisMode()
132 {
133 SwingUtilities.invokeLater(new Runnable() {
134 public void run() {
135 justEntered = true;
136 Toolkit.getDefaultToolkit().addAWTEventListener(listener,
137 AWTEvent.MOUSE_EVENT_MASK | AWTEvent.KEY_EVENT_MASK);
138 }
139 });
140
141 }
142
143 /***
144 * Exits What's this mode.
145 * <p>
146 * See {@link #enterWhatsThisMode()}. The global mouse listener is
147 * deregistered and the original cursors for all components are restored.
148 */
149 public static void exitWhatsThisMode()
150 {
151 Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
152 Set<Map.Entry<Component, Cursor>> pairs = cursorsByComponent.entrySet();
153 for (Iterator<Map.Entry<Component, Cursor>> i = pairs.iterator(); i.hasNext();) {
154 Map.Entry<Component, Cursor> entry = i.next();
155 entry.getKey().setCursor(entry.getValue());
156 }
157 cursorsByComponent.clear();
158 }
159
160 private static class WhatsThisPopup extends JPopupMenu
161 {
162 public WhatsThisPopup(String text)
163 {
164 setBorder(new EmptyBorder(0, 0, 0, 0));
165 JToolTip tip = new JToolTip();
166 add(tip);
167 tip.setTipText(GUIHelper.tt(text, 200));
168 }
169 }
170
171 private static class EventHandler implements AWTEventListener
172 {
173
174 public void eventDispatched(AWTEvent event)
175 {
176
177 switch (event.getID()) {
178 case MouseEvent.MOUSE_MOVED:
179 if (justEntered) {
180 justEntered = false;
181 entered((MouseEvent)event);
182 }
183 case MouseEvent.MOUSE_ENTERED:
184 entered((MouseEvent)event);
185 break;
186 case MouseEvent.MOUSE_EXITED:
187 exited((MouseEvent)event);
188 break;
189 case MouseEvent.MOUSE_CLICKED:
190 ((MouseEvent)event).consume();
191 break;
192 case MouseEvent.MOUSE_PRESSED:
193 showPopup((MouseEvent)event);
194 break;
195 case MouseEvent.MOUSE_RELEASED:
196 ((MouseEvent)event).consume();
197 break;
198 case KeyEvent.KEY_PRESSED:
199 keyPressed((KeyEvent)event);
200 break;
201 }
202 }
203
204 private void showPopup(MouseEvent e)
205 {
206 e.consume();
207
208 if (e.getSource() instanceof Component) {
209 Component c = (Component)e.getSource();
210 String whatsThisText = getText(c, true);
211 if (whatsThisText != null) {
212 WhatsThisPopup popup = new WhatsThisPopup(whatsThisText);
213 popup.show(c, e.getX(), e.getY());
214 }
215 }
216
217 exited(e);
218
219 WhatsThis.exitWhatsThisMode();
220 }
221
222 private void keyPressed(KeyEvent e)
223 {
224 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
225 WhatsThis.exitWhatsThisMode();
226 e.consume();
227 }
228 }
229
230 private void entered(MouseEvent e)
231 {
232 if (e.getSource() instanceof Component) {
233 Component c = (Component)e.getSource();
234 if (c.isCursorSet()) {
235 if (cursorsByComponent.containsKey(c)) {
236
237 }
238 cursorsByComponent.put(c, c.getCursor());
239 }
240 String whatsThisText = getText(c, true);
241 if (whatsThisText != null) {
242
243 c.setCursor(gotHelpCursor);
244 }
245 else {
246
247 c.setCursor(noHelpCursor);
248 }
249 }
250 }
251
252 private void exited(MouseEvent e)
253 {
254
255
256 if (e.getSource() instanceof Component) {
257 ((Component)e.getSource()).setCursor((Cursor)cursorsByComponent.get(e.getSource()));
258 }
259 }
260 }
261 }