source: other-projects/rsyntax-textarea/src/java/org/fife/ui/rsyntaxtextarea/focusabletip/FocusableTip.java@ 25584

Last change on this file since 25584 was 25584, checked in by davidb, 12 years ago

Initial cut an a text edit area for GLI that supports color syntax highlighting

File size: 9.5 KB
RevLine 
[25584]1/*
2 * 07/29/2009
3 *
4 * FocusableTip.java - A focusable tool tip, like those in Eclipse.
5 *
6 * This library is distributed under a modified BSD license. See the included
7 * RSyntaxTextArea.License.txt file for details.
8 */
9package org.fife.ui.rsyntaxtextarea.focusabletip;
10
11import java.awt.Component;
12import java.awt.ComponentOrientation;
13import java.awt.Point;
14import java.awt.Rectangle;
15import java.awt.Window;
16import java.awt.event.ComponentEvent;
17import java.awt.event.ComponentListener;
18import java.awt.event.FocusEvent;
19import java.awt.event.FocusListener;
20import java.awt.event.KeyEvent;
21import java.awt.event.KeyListener;
22import java.awt.event.MouseEvent;
23import java.net.URL;
24import java.util.ResourceBundle;
25import javax.swing.*;
26import javax.swing.event.CaretEvent;
27import javax.swing.event.CaretListener;
28import javax.swing.event.HyperlinkListener;
29import javax.swing.event.MouseInputAdapter;
30
31import org.fife.ui.rsyntaxtextarea.PopupWindowDecorator;
32
33
34/**
35 * A focusable tool tip, similar to those found in Eclipse. The user
36 * can click in the tip and it becomes a "real," resizable window.
37 *
38 * @author Robert Futrell
39 * @version 1.0
40 */
41public class FocusableTip {
42
43 private JTextArea textArea;
44 private TipWindow tipWindow;
45 private URL imageBase;
46 private TextAreaListener textAreaListener;
47 private HyperlinkListener hyperlinkListener;
48 private String lastText;
49
50 /**
51 * The screen bounds in which the mouse has to stay for the currently
52 * displayed tip to stay visible.
53 */
54 private Rectangle tipVisibleBounds;
55
56 /**
57 * Margin from mouse cursor at which to draw focusable tip.
58 */
59 private static final int X_MARGIN = 18;
60
61 /**
62 * Margin from mouse cursor at which to draw focusable tip.
63 */
64 private static final int Y_MARGIN = 12;
65
66 private static final String MSG =
67 "org.fife.ui.rsyntaxtextarea.focusabletip.FocusableTip";
68 private static final ResourceBundle msg = ResourceBundle.getBundle(MSG);
69
70
71 public FocusableTip(JTextArea textArea, HyperlinkListener listener) {
72 setTextArea(textArea);
73 this.hyperlinkListener = listener;
74 textAreaListener = new TextAreaListener();
75 tipVisibleBounds = new Rectangle();
76 }
77
78
79 /**
80 * Compute the bounds in which the user can move the mouse without the
81 * tip window disappearing.
82 */
83 private void computeTipVisibleBounds() {
84 // Compute area that the mouse can move in without hiding the
85 // tip window. Note that Java 1.4 can only detect mouse events
86 // in Java windows, not globally.
87 Rectangle r = tipWindow.getBounds();
88 Point p = r.getLocation();
89 SwingUtilities.convertPointFromScreen(p, textArea);
90 r.setLocation(p);
91 tipVisibleBounds.setBounds(r.x,r.y-15, r.width,r.height+15*2);
92 }
93
94
95 private void createAndShowTipWindow(final MouseEvent e, final String text) {
96
97 Window owner = SwingUtilities.getWindowAncestor(textArea);
98 tipWindow = new TipWindow(owner, this, text);
99 tipWindow.setHyperlinkListener(hyperlinkListener);
100
101 // Give apps a chance to decorate us with drop shadows, etc.
102 PopupWindowDecorator decorator = PopupWindowDecorator.get();
103 if (decorator!=null) {
104 decorator.decorate(tipWindow);
105 }
106
107 // TODO: Position tip window better (handle RTL, edges of screen, etc.).
108 // Wrap in an invokeLater() to work around a JEditorPane issue where it
109 // doesn't return its proper preferred size until after it is displayed.
110 // See http://forums.sun.com/thread.jspa?forumID=57&threadID=574810
111 // for a discussion.
112 SwingUtilities.invokeLater(new Runnable() {
113 public void run() {
114
115 // If a new FocusableTip is requested while another one is
116 // *focused* and visible, the focused tip (i.e. "tipWindow")
117 // will be disposed of. If this Runnable is run after the
118 // dispose(), tipWindow will be null. All of this is done on
119 // the EDT so no synchronization should be necessary.
120 if (tipWindow==null) {
121 return;
122 }
123
124 tipWindow.fixSize();
125 ComponentOrientation o = textArea.getComponentOrientation();
126
127 Point p = e.getPoint();
128 SwingUtilities.convertPointToScreen(p, textArea);
129
130 // Ensure tool tip is in the window bounds.
131 // Multi-monitor support - make sure the completion window (and
132 // description window, if applicable) both fit in the same
133 // window in a multi-monitor environment. To do this, we decide
134 // which monitor the rectangle "p" is in, and use that one.
135 Rectangle sb = TipUtil.getScreenBoundsForPoint(p.x, p.y);
136 //Dimension ss = tipWindow.getToolkit().getScreenSize();
137
138 // Try putting our stuff "below" the mouse first.
139 int y = p.y + Y_MARGIN;
140 if (y+tipWindow.getHeight()>=sb.y+sb.height) {
141 y = p.y - Y_MARGIN - tipWindow.getHeight();
142 }
143
144 // Get x-coordinate of completions. Try to align left edge
145 // with the mouse first (with a slight margin).
146 int x = p.x - X_MARGIN; // ltr
147 if (!o.isLeftToRight()) {
148 x = p.x - tipWindow.getWidth() + X_MARGIN;
149 }
150 if (x<sb.x) {
151 x = sb.x;
152 }
153 else if (x+tipWindow.getWidth()>sb.x+sb.width) { // completions don't fit
154 x = sb.x + sb.width - tipWindow.getWidth();
155 }
156
157 tipWindow.setLocation(x, y);
158 tipWindow.setVisible(true);
159
160 computeTipVisibleBounds(); // Do after tip is visible
161 textAreaListener.install(textArea);
162 lastText = text;
163
164 }
165 });
166
167 }
168
169
170 /**
171 * Returns the base URL to use when loading images in this focusable tip.
172 *
173 * @return The base URL to use.
174 * @see #setImageBase(URL)
175 */
176 public URL getImageBase() {
177 return imageBase;
178 }
179
180
181 /**
182 * Returns localized text for the given key.
183 *
184 * @param key The key into the resource bundle.
185 * @return The localized text.
186 */
187 static String getString(String key) {
188 return msg.getString(key);
189 }
190
191
192 /**
193 * Disposes of the focusable tip currently displayed, if any.
194 */
195 public void possiblyDisposeOfTipWindow() {
196 if (tipWindow != null) {
197 tipWindow.dispose();
198 tipWindow = null;
199 textAreaListener.uninstall();
200 tipVisibleBounds.setBounds(-1, -1, 0, 0);
201 lastText = null;
202 textArea.requestFocus();
203 }
204 }
205
206
207 void removeListeners() {
208 //System.out.println("DEBUG: Removing text area listeners");
209 textAreaListener.uninstall();
210 }
211
212
213 /**
214 * Sets the base URL to use when loading images in this focusable tip.
215 *
216 * @param url The base URL to use.
217 * @see #getImageBase()
218 */
219 public void setImageBase(URL url) {
220 imageBase = url;
221 }
222
223
224 private void setTextArea(JTextArea textArea) {
225 this.textArea = textArea;
226 // Is okay to do multiple times.
227 ToolTipManager.sharedInstance().registerComponent(textArea);
228 }
229
230
231 public void toolTipRequested(MouseEvent e, String text) {
232
233 if (text==null || text.length()==0) {
234 possiblyDisposeOfTipWindow();
235 lastText = text;
236 return;
237 }
238
239 if (lastText==null || text.length()!=lastText.length()
240 || !text.equals(lastText)) {
241 possiblyDisposeOfTipWindow();
242 createAndShowTipWindow(e, text);
243 }
244
245 }
246
247
248 private class TextAreaListener extends MouseInputAdapter implements
249 CaretListener, ComponentListener, FocusListener, KeyListener {
250
251 public void caretUpdate(CaretEvent e) {
252 Object source = e.getSource();
253 if (source == textArea) {
254 possiblyDisposeOfTipWindow();
255 }
256 }
257
258 public void componentHidden(ComponentEvent e) {
259 handleComponentEvent(e);
260 }
261
262 public void componentMoved(ComponentEvent e) {
263 handleComponentEvent(e);
264 }
265
266 public void componentResized(ComponentEvent e) {
267 handleComponentEvent(e);
268 }
269
270 public void componentShown(ComponentEvent e) {
271 handleComponentEvent(e);
272 }
273
274 public void focusGained(FocusEvent e) {
275 }
276
277 public void focusLost(FocusEvent e) {
278 // Only dispose of tip if it wasn't the TipWindow that was clicked
279 // "c" can be null, at least on OS X, so we must check that before
280 // calling SwingUtilities.getWindowAncestor() to guard against an
281 // NPE.
282 Component c = e.getOppositeComponent();
283 boolean tipClicked = (c instanceof TipWindow) ||
284 (c!=null &&
285 SwingUtilities.getWindowAncestor(c) instanceof TipWindow);
286 if (!tipClicked) {
287 possiblyDisposeOfTipWindow();
288 }
289 }
290
291 private void handleComponentEvent(ComponentEvent e) {
292 possiblyDisposeOfTipWindow();
293 }
294
295 public void install(JTextArea textArea) {
296 textArea.addCaretListener(this);
297 textArea.addComponentListener(this);
298 textArea.addFocusListener(this);
299 textArea.addKeyListener(this);
300 textArea.addMouseListener(this);
301 textArea.addMouseMotionListener(this);
302 }
303
304 public void keyPressed(KeyEvent e) {
305 if (e.getKeyCode()==KeyEvent.VK_ESCAPE) {
306 possiblyDisposeOfTipWindow();
307 }
308 else if (e.getKeyCode()==KeyEvent.VK_F2) {
309 if (tipWindow!=null && !tipWindow.getFocusableWindowState()) {
310 tipWindow.actionPerformed(null);
311 e.consume(); // Don't do bookmarking stuff
312 }
313 }
314 }
315
316 public void keyReleased(KeyEvent e) {
317 }
318
319 public void keyTyped(KeyEvent e) {
320 }
321
322 public void mouseExited(MouseEvent e) {
323 // possiblyDisposeOfTipWindow();
324 }
325
326 public void mouseMoved(MouseEvent e) {
327 if (tipVisibleBounds==null ||
328 !tipVisibleBounds.contains(e.getPoint())) {
329 possiblyDisposeOfTipWindow();
330 }
331 }
332
333 public void uninstall() {
334 textArea.removeCaretListener(this);
335 textArea.removeComponentListener(this);
336 textArea.removeFocusListener(this);
337 textArea.removeKeyListener(this);
338 textArea.removeMouseListener(this);
339 textArea.removeMouseMotionListener(this);
340 }
341
342 }
343
344}
Note: See TracBrowser for help on using the repository browser.