1 19 20 package org.netbeans.editor.ext; 21 22 import java.awt.Dimension ; 23 import java.awt.Point ; 24 import java.awt.Rectangle ; 25 import java.awt.Font ; 26 import java.awt.Color ; 27 import java.awt.event.ActionListener ; 28 import java.awt.event.ActionEvent ; 29 import java.awt.event.MouseListener ; 30 import java.awt.event.MouseMotionListener ; 31 import java.awt.event.MouseAdapter ; 32 import java.awt.event.MouseEvent ; 33 import java.awt.event.ComponentEvent ; 34 import java.awt.event.ComponentAdapter ; 35 import java.awt.event.FocusListener ; 36 import java.awt.event.FocusEvent ; 37 import java.beans.PropertyChangeListener ; 38 import java.beans.PropertyChangeEvent ; 39 import java.beans.PropertyChangeSupport ; 40 import javax.swing.JComponent ; 41 import javax.swing.JLabel ; 42 import javax.swing.JRootPane ; 43 import javax.swing.JLayeredPane ; 44 import javax.swing.Timer ; 45 import javax.swing.Action ; 46 import javax.swing.ActionMap ; 47 import javax.swing.BorderFactory ; 48 import javax.swing.UIManager ; 49 import javax.swing.text.JTextComponent ; 50 import javax.swing.text.BadLocationException ; 51 import org.netbeans.editor.SettingsChangeListener; 52 import org.netbeans.editor.SettingsChangeEvent; 53 import org.netbeans.editor.Settings; 54 import org.netbeans.editor.Utilities; 55 import org.netbeans.editor.BaseKit; 56 import org.netbeans.editor.BaseTextUI; 57 import org.netbeans.editor.BaseDocument; 58 import org.netbeans.editor.WeakTimerListener; 59 import org.netbeans.editor.PopupManager; 60 import javax.swing.JTextArea ; 61 import org.netbeans.editor.EditorUI; 62 import org.netbeans.editor.GlyphGutter; 63 import javax.swing.JViewport ; 64 import javax.swing.text.Document ; 65 import javax.swing.text.Element ; 66 67 83 84 public class ToolTipSupport extends MouseAdapter 85 implements MouseMotionListener , ActionListener , PropertyChangeListener , 86 SettingsChangeListener, FocusListener { 87 88 89 public static final String PROP_TOOL_TIP = "toolTip"; 91 92 public static final String PROP_TOOL_TIP_TEXT = "toolTipText"; 94 95 public static final String PROP_STATUS = "status"; 97 98 public static final String PROP_ENABLED = "enabled"; 100 101 public static final String PROP_INITIAL_DELAY = "initialDelay"; 103 104 public static final String PROP_DISMISS_DELAY = "dismissDelay"; 106 private static final String UI_PREFIX = "ToolTip"; 108 109 public static final int INITIAL_DELAY = 200; 110 111 114 public static final int DISMISS_DELAY = 60000; 115 116 117 public static final int STATUS_HIDDEN = 0; 118 123 public static final int STATUS_VISIBILITY_ENABLED = 1; 124 128 public static final int STATUS_TEXT_VISIBLE = 2; 129 133 public static final int STATUS_COMPONENT_VISIBLE = 3; 134 135 138 private static final int MOUSE_EXTRA_HEIGHT = 5; 139 140 private ExtEditorUI extEditorUI; 141 142 private JComponent toolTip; 143 144 private String toolTipText; 145 146 private Timer enterTimer; 147 148 private Timer exitTimer; 149 150 private boolean enabled; 151 152 153 private int status; 154 155 private MouseEvent lastMouseEvent; 156 157 private PropertyChangeSupport pcs; 158 159 private PopupManager.HorizontalBounds horizontalBounds = PopupManager.ViewPortBounds; 160 private PopupManager.Placement placement = PopupManager.AbovePreferred; 161 162 private int verticalAdjustment; 163 private int horizontalAdjustment; 164 165 private boolean glyphListenerAdded = false; 166 167 169 public ToolTipSupport(ExtEditorUI extEditorUI) { 170 this.extEditorUI = extEditorUI; 171 172 enterTimer = new Timer (INITIAL_DELAY, new WeakTimerListener(this)); 173 enterTimer.setRepeats(false); 174 exitTimer = new Timer (DISMISS_DELAY, new WeakTimerListener(this)); 175 exitTimer.setRepeats(false); 176 177 Settings.addSettingsChangeListener(this); 178 extEditorUI.addPropertyChangeListener(this); 179 180 setEnabled(true); 181 } 182 183 186 public final JComponent getToolTip() { 187 if (toolTip == null) { 188 setToolTip(createDefaultToolTip()); 189 } 190 191 return toolTip; 192 } 193 194 202 public void setToolTip(JComponent toolTip) { 203 setToolTip(toolTip, PopupManager.ViewPortBounds, PopupManager.AbovePreferred); 204 } 205 206 public void setToolTip(JComponent toolTip, PopupManager.HorizontalBounds horizontalBounds, 207 PopupManager.Placement placement) { 208 setToolTip(toolTip, PopupManager.ViewPortBounds, PopupManager.AbovePreferred, 0, 0); 209 } 210 211 public void setToolTip(JComponent toolTip, PopupManager.HorizontalBounds horizontalBounds, 212 PopupManager.Placement placement, int horizontalAdjustment, int verticalAdjustment) { 213 JComponent oldToolTip = this.toolTip; 214 this.toolTip = toolTip; 215 this.horizontalBounds = horizontalBounds; 216 this.placement = placement; 217 this.horizontalAdjustment = horizontalAdjustment; 218 this.verticalAdjustment = verticalAdjustment; 219 220 if (status >= STATUS_VISIBILITY_ENABLED) { 221 ensureVisibility(); 222 } 223 224 firePropertyChange(PROP_TOOL_TIP, oldToolTip, this.toolTip); 225 } 226 227 229 protected JComponent createDefaultToolTip() { 230 return createTextToolTip(); 231 } 232 233 private JTextArea createTextToolTip() { 234 JTextArea tt = new JTextArea () { 235 public void setSize(int width, int height) { 236 Dimension prefSize = getPreferredSize(); 237 if (width >= prefSize.width) { 238 width = prefSize.width; 239 } else { super.setSize(width, 10000); prefSize = getPreferredSize(); } 245 if (height >= prefSize.height) { height = prefSize.height; 247 } else { super.setSize(width, 10000); 254 int offset = viewToModel(new Point (0, height)); 255 Document doc = getDocument(); 256 Element root = doc.getDefaultRootElement(); 257 int lineIndex = root.getElementIndex(offset); 258 lineIndex--; if (lineIndex >= 0) { 260 Element lineElem = root.getElement(lineIndex); 261 if (lineElem != null) { 262 try { 263 offset = lineElem.getStartOffset(); 264 doc.remove(offset, doc.getLength() - offset); 265 doc.insertString(offset, "...", null); 266 } catch (BadLocationException e) { 267 } 269 height = Math.min(height, getPreferredSize().height); 272 } 273 } 274 } 275 super.setSize(width, height); 276 } 277 }; 278 279 tt.setActionMap(new ActionMap ()); 281 tt.setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); 282 283 Font font = UIManager.getFont(UI_PREFIX + ".font"); Color backColor = UIManager.getColor(UI_PREFIX + ".background"); Color foreColor = UIManager.getColor(UI_PREFIX + ".foreground"); 287 if (font != null) { 288 tt.setFont(font); 289 } 290 if (foreColor != null) { 291 tt.setForeground(foreColor); 292 } 293 if (backColor != null) { 294 tt.setBackground(backColor); 295 } 296 297 tt.setOpaque(true); 298 tt.setBorder(BorderFactory.createCompoundBorder( 299 BorderFactory.createLineBorder(tt.getForeground()), 300 BorderFactory.createEmptyBorder(0, 3, 0, 3) 301 )); 302 303 return tt; 304 } 305 306 public void settingsChange(SettingsChangeEvent evt) { 307 } 308 309 public void propertyChange(PropertyChangeEvent evt) { 310 String propName = evt.getPropertyName(); 311 312 if (extEditorUI.COMPONENT_PROPERTY.equals(propName)) { 313 JTextComponent component = (JTextComponent )evt.getNewValue(); 314 if (component != null) { 316 component.addPropertyChangeListener(this); 317 318 disableSwingToolTip(component); 319 320 component.addFocusListener(this); 321 if (component.hasFocus()) { 322 focusGained(new FocusEvent (component, FocusEvent.FOCUS_GAINED)); 323 } 324 component.addMouseListener(this); 325 component.addMouseMotionListener(this); 326 327 GlyphGutter gg = extEditorUI.getGlyphGutter(); 328 if (gg != null && !glyphListenerAdded) { 329 glyphListenerAdded = true; 330 gg.addMouseListener(this); 331 gg.addMouseMotionListener(this); 332 } 333 334 335 } else { component = (JTextComponent )evt.getOldValue(); 337 338 component.removeFocusListener(this); 339 component.removePropertyChangeListener(this); 340 341 component.removeMouseListener(this); 342 component.removeMouseMotionListener(this); 343 344 GlyphGutter gg = extEditorUI.getGlyphGutter(); 345 if (gg != null) { 346 gg.removeMouseListener(this); 347 gg.removeMouseMotionListener(this); 348 } 349 setToolTipVisible(false); 350 351 } 352 } 353 354 if (JComponent.TOOL_TIP_TEXT_KEY.equals(propName)) { 355 JComponent component = (JComponent )evt.getSource(); 356 disableSwingToolTip(component); 357 358 componentToolTipTextChanged(evt); 359 } 360 361 } 362 363 private void disableSwingToolTip(final JComponent component) { 364 javax.swing.SwingUtilities.invokeLater( 365 new Runnable () { 366 public void run() { 367 javax.swing.ToolTipManager.sharedInstance().unregisterComponent(component); 369 370 GlyphGutter gg = extEditorUI.getGlyphGutter(); 372 if (gg != null) { 373 javax.swing.ToolTipManager.sharedInstance().unregisterComponent(gg); 374 } 375 } 376 } 377 ); 378 } 379 380 385 protected void updateToolTip() { 386 ExtEditorUI ui = extEditorUI; 387 if (ui == null) 388 return; 389 JTextComponent comp = ui.getComponent(); 390 if (comp == null) 391 return; 392 393 if (isGlyphGutterMouseEvent(lastMouseEvent)) { 394 setToolTipText(extEditorUI.getGlyphGutter().getToolTipText(lastMouseEvent)); 395 } else { BaseKit kit = Utilities.getKit(comp); 397 if (kit != null) { 398 Action a = kit.getActionByName(ExtKit.buildToolTipAction); 399 if (a != null) { 400 a.actionPerformed(new ActionEvent (comp, 0, "")); } 402 } 403 } 404 } 405 406 415 protected void setToolTipVisible(boolean visible) { 416 if (!visible) { enterTimer.stop(); 418 exitTimer.stop(); 419 } 420 421 if (visible && status < STATUS_VISIBILITY_ENABLED 422 || !visible && status >= STATUS_VISIBILITY_ENABLED 423 ) { 424 if (visible) { if (enabled) { 426 setStatus(STATUS_VISIBILITY_ENABLED); 427 updateToolTip(); 428 } 429 430 } else { if (toolTip != null) { 432 if (toolTip.isVisible()){ 433 toolTip.setVisible(false); 434 PopupManager pm = extEditorUI.getPopupManager(); 435 if (pm!=null){ 436 pm.uninstall(toolTip); 437 } 438 } 439 } 440 441 setStatus(STATUS_HIDDEN); 442 } 443 } 444 } 445 446 449 public boolean isToolTipVisible() { 450 return status > STATUS_VISIBILITY_ENABLED; 451 } 452 453 459 public final int getStatus() { 460 return status; 461 } 462 463 private void setStatus(int status) { 464 if (this.status != status) { 465 int oldStatus = this.status; 466 this.status = status; 467 firePropertyChange(PROP_STATUS, 468 new Integer (oldStatus), new Integer (this.status)); 469 } 470 } 471 472 474 public String getToolTipText() { 475 return toolTipText; 476 } 477 478 479 480 486 private static String makeDisplayable(String str , Font f) { 487 if( str == null || f == null){ 488 return str; 489 } 490 StringBuffer buf = new StringBuffer (str.length()); 491 char[] chars = str.toCharArray(); 492 for (int i = 0; i < chars.length; i++) { 493 char c = chars[i]; 494 switch (c) { 495 case '\t': buf.append(c); break; 496 case '\n': buf.append(c); break; 497 case '\r': buf.append(c); break; 498 case '\b': buf.append("\\b"); break; case '\f': buf.append("\\f"); break; default: 501 if( f == null || f.canDisplay( c ) ){ 502 buf.append(c); 503 } else { 504 buf.append("\\u"); String hex = Integer.toHexString(c); 506 for (int j = 0; j < 4 - hex.length(); j++){ 507 buf.append('0'); } 509 buf.append(hex); 510 } 511 } 512 } 513 return buf.toString(); 514 } 515 516 520 public void setToolTipText(String text) { 521 522 final String displayableText = makeDisplayable(text, UIManager.getFont(UI_PREFIX + ".font")); 524 Utilities.runInEventDispatchThread(new Runnable () { 525 public void run() { 526 String oldText = toolTipText; 527 toolTipText = displayableText; 528 529 firePropertyChange(PROP_TOOL_TIP_TEXT, oldText, toolTipText); 530 531 if (toolTipText != null) { 532 JTextArea ta = createTextToolTip(); 533 ta.setText(toolTipText); 534 setToolTip(ta); 535 536 } else { if (status == STATUS_TEXT_VISIBLE) { 538 setToolTipVisible(false); 539 } 540 } 541 } 542 }); 543 } 544 545 private void applyToolTipText() { 546 JComponent tt = getToolTip(); 547 if (tt != null) { 548 if (tt instanceof JLabel ) { 549 ((JLabel )tt).setText(toolTipText); 550 551 } else if (tt instanceof JTextComponent ) { 552 ((JTextComponent )tt).setText(toolTipText); 553 554 } else if (tt instanceof javax.swing.JToolTip ) { 555 ((javax.swing.JToolTip )tt).setTipText(toolTipText); 556 557 } else { 558 try { 559 java.lang.reflect.Method m = tt.getClass().getMethod("setText", new Class [] { String .class }); 561 if (m != null) { 562 m.invoke(toolTip, new Object [] { toolTipText }); 563 } 564 } catch (NoSuchMethodException e) { 565 } catch (IllegalAccessException e) { 566 } catch (java.lang.reflect.InvocationTargetException e) { 567 } 568 } 569 } 570 } 571 572 private boolean isGlyphGutterMouseEvent(MouseEvent evt) { 573 return (evt != null && evt.getSource() == extEditorUI.getGlyphGutter()); 574 } 575 576 private void ensureVisibility() { 577 JTextComponent component = extEditorUI.getComponent(); 579 if (component != null) { 580 int pos = component.viewToModel(getLastMouseEventPoint()); 582 Rectangle cursorBounds = null; 583 if (pos >= 0) { 584 try { 585 cursorBounds = component.modelToView(pos); 586 if (horizontalBounds == PopupManager.ScrollBarBounds){ 587 588 }else{ 589 if (placement == PopupManager.AbovePreferred || placement == PopupManager.Above){ 590 cursorBounds.y -= MOUSE_EXTRA_HEIGHT; 592 cursorBounds.height += 2 * MOUSE_EXTRA_HEIGHT; } else if (placement == PopupManager.BelowPreferred || placement == PopupManager.Below){ 594 cursorBounds.y = cursorBounds.y + cursorBounds.height + MOUSE_EXTRA_HEIGHT + 1; 595 cursorBounds.height += MOUSE_EXTRA_HEIGHT; } 597 } 598 599 } catch (BadLocationException e) { 600 } 601 } 602 if (cursorBounds == null) { cursorBounds = new Rectangle (getLastMouseEventPoint(), new Dimension (1, 1)); 604 } 605 606 PopupManager pm = extEditorUI.getPopupManager(); 608 609 if (toolTip != null && toolTip.isVisible()) toolTip.setVisible(false); 610 pm.install(toolTip, cursorBounds, placement, horizontalBounds, horizontalAdjustment, verticalAdjustment); 611 if (toolTip != null) toolTip.setVisible(true); 612 } 613 exitTimer.restart(); 614 } 615 616 621 public String getIdentifierUnderCursor() { 622 String word = null; 623 if (!isGlyphGutterMouseEvent(lastMouseEvent)) { 624 try { 625 JTextComponent component = extEditorUI.getComponent(); 626 BaseTextUI ui = (BaseTextUI)component.getUI(); 627 Point lmePoint = getLastMouseEventPoint(); 628 int pos = ui.viewToModel(component, lmePoint); 629 if (pos >= 0) { 630 BaseDocument doc = (BaseDocument)component.getDocument(); 631 int eolPos = Utilities.getRowEnd(doc, pos); 632 Rectangle eolRect = ui.modelToView(component, eolPos); 633 int lineHeight = extEditorUI.getLineHeight(); 634 if (lmePoint.x <= eolRect.x && lmePoint.y <= eolRect.y + lineHeight) { 635 word = Utilities.getIdentifier(doc, pos); 636 } 637 } 638 } catch (BadLocationException e) { 639 } 641 } 642 643 return word; 644 } 645 646 649 public boolean isEnabled() { 650 return enabled; 651 } 652 653 657 public void setEnabled(boolean enabled) { 658 if (enabled != this.enabled) { 659 this.enabled = enabled; 660 661 firePropertyChange(PROP_ENABLED, 662 enabled ? Boolean.FALSE : Boolean.TRUE, 663 enabled ? Boolean.TRUE : Boolean.FALSE 664 ); 665 666 if (!enabled) { 667 setToolTipVisible(false); 668 } 669 } 670 } 671 672 676 public int getInitialDelay() { 677 return enterTimer.getDelay(); 678 } 679 680 684 public void setInitialDelay(int delay) { 685 if (enterTimer.getDelay() != delay) { 686 int oldDelay = enterTimer.getDelay(); 687 enterTimer.setDelay(delay); 688 689 firePropertyChange(PROP_INITIAL_DELAY, 690 new Integer (oldDelay), new Integer (enterTimer.getDelay())); 691 } 692 } 693 694 698 public int getDismissDelay() { 699 return exitTimer.getDelay(); 700 } 701 702 706 public void setDismissDelay(int delay) { 707 if (exitTimer.getDelay() != delay) { 708 int oldDelay = exitTimer.getDelay(); 709 exitTimer.setDelay(delay); 710 711 firePropertyChange(PROP_DISMISS_DELAY, 712 new Integer (oldDelay), new Integer (exitTimer.getDelay())); 713 } 714 } 715 716 public void actionPerformed(ActionEvent evt) { 717 if (evt.getSource() == enterTimer) { 718 setToolTipVisible(true); 719 720 } else if (evt.getSource() == exitTimer) { 721 setToolTipVisible(false); 722 } 723 } 724 725 public void mouseClicked(MouseEvent evt) { 726 lastMouseEvent = evt; 727 setToolTipVisible(false); 728 } 729 730 public void mousePressed(MouseEvent evt) { 731 lastMouseEvent = evt; 732 setToolTipVisible(false); 733 } 734 735 public void mouseReleased(MouseEvent evt) { 736 lastMouseEvent = evt; 737 setToolTipVisible(false); 738 739 ExtEditorUI ui = extEditorUI; 742 if (ui != null) { 743 JTextComponent component = ui.getComponent(); 744 if (enabled && component != null && component.getCaret().isSelectionVisible()) { 745 enterTimer.restart(); 746 } 747 } 748 } 749 750 public void mouseEntered(MouseEvent evt) { 751 lastMouseEvent = evt; 752 } 753 754 public void mouseExited(MouseEvent evt) { 755 lastMouseEvent = evt; 756 setToolTipVisible(false); 757 } 758 759 public void mouseDragged(MouseEvent evt) { 760 lastMouseEvent = evt; 761 setToolTipVisible(false); 762 } 763 764 public void mouseMoved(MouseEvent evt) { 765 setToolTipVisible(false); 766 if (enabled) { 767 enterTimer.restart(); 768 769 } 770 lastMouseEvent = evt; 771 } 772 773 777 public final MouseEvent getLastMouseEvent() { 778 return lastMouseEvent; 779 } 780 781 783 private Point getLastMouseEventPoint() { 784 Point p = null; 785 MouseEvent lme = lastMouseEvent; 786 if (lme != null) { 787 p = lme.getPoint(); 788 if (lme.getSource() == extEditorUI.getGlyphGutter()) { 789 JTextComponent c = extEditorUI.getComponent(); 791 if (c != null) { 792 if (c.getParent() instanceof JViewport ) { 793 JViewport vp = (JViewport )c.getParent(); 794 p = new Point (vp.getViewPosition().x, p.y); 795 } 796 } 797 } 798 } 799 800 return p; 801 } 802 803 804 805 812 protected void componentToolTipTextChanged(PropertyChangeEvent evt) { 813 JComponent component = (JComponent )evt.getSource(); 814 setToolTipText(component.getToolTipText()); 815 } 816 817 private synchronized PropertyChangeSupport getPCS() { 818 if (pcs == null) { 819 pcs = new PropertyChangeSupport (this); 820 } 821 return pcs; 822 } 823 824 829 public void addPropertyChangeListener(PropertyChangeListener listener) { 830 getPCS().addPropertyChangeListener(listener); 831 } 832 833 public void removePropertyChangeListener(PropertyChangeListener listener) { 834 getPCS().removePropertyChangeListener(listener); 835 } 836 837 842 protected void firePropertyChange(String propertyName, 843 Object oldValue, Object newValue) { 844 getPCS().firePropertyChange(propertyName, oldValue, newValue); 845 } 846 847 public void focusGained(FocusEvent e) { 848 GlyphGutter gg = extEditorUI.getGlyphGutter(); 852 if (gg != null && !glyphListenerAdded) { 853 glyphListenerAdded = true; 854 gg.addMouseListener(this); 855 gg.addMouseMotionListener(this); 856 } 857 } 858 859 public void focusLost(FocusEvent e) { 860 871 } 872 873 } 874 | Popular Tags |