1 19 20 package org.netbeans.modules.editor.hints; 21 22 import java.awt.*; 23 import java.awt.event.AWTEventListener ; 24 import java.awt.event.KeyEvent ; 25 import java.awt.event.KeyListener ; 26 import java.awt.event.MouseEvent ; 27 import java.awt.event.MouseListener ; 28 import java.util.logging.Level ; 29 import java.util.logging.LogRecord ; 30 import java.util.logging.Logger ; 31 import javax.swing.*; 32 import javax.swing.border.LineBorder ; 33 import javax.swing.event.ChangeEvent ; 34 import javax.swing.event.ChangeListener ; 35 import javax.swing.text.BadLocationException ; 36 import javax.swing.text.Document ; 37 import javax.swing.text.JTextComponent ; 38 import javax.swing.text.Position ; 39 import org.netbeans.editor.AnnotationDesc; 40 import org.netbeans.editor.Annotations; 41 import org.netbeans.editor.BaseDocument; 42 import org.netbeans.editor.Registry; 43 import org.netbeans.editor.Utilities; 44 import org.netbeans.modules.editor.hints.borrowed.ListCompletionView; 45 import org.netbeans.modules.editor.hints.borrowed.ScrollCompletionPane; 46 import org.netbeans.spi.editor.hints.ChangeInfo; 47 import org.netbeans.spi.editor.hints.Fix; 48 import org.netbeans.spi.editor.hints.LazyFixList; 49 import org.openide.ErrorManager; 50 import org.openide.cookies.EditCookie; 51 import org.openide.cookies.EditorCookie; 52 import org.openide.cookies.OpenCookie; 53 import org.openide.filesystems.FileObject; 54 import org.openide.loaders.DataObject; 55 import org.openide.loaders.DataObjectNotFoundException; 56 import org.openide.text.Annotation; 57 import org.openide.util.NbBundle; 58 import org.openide.util.RequestProcessor; 59 import org.openide.util.Task; 60 import org.openide.util.TaskListener; 61 62 63 69 public class HintsUI implements MouseListener , KeyListener , ChangeListener , AWTEventListener { 70 71 private static HintsUI INSTANCE; 72 private static final String POPUP_NAME = "hintsPopup"; 74 public static synchronized HintsUI getDefault() { 75 if (INSTANCE == null) 76 INSTANCE = new HintsUI(); 77 78 return INSTANCE; 79 } 80 81 static Logger UI_GESTURES_LOGGER = Logger.getLogger("org.netbeans.ui.editor.hints"); 82 83 private JTextComponent comp; 84 private LazyFixList hints = new StaticFixList(); 85 private Popup listPopup; 86 private JLabel hintIcon; 87 private ScrollCompletionPane hintListComponent; 88 private JLabel errorTooltip; 89 90 91 private HintsUI() { 92 Registry.addChangeListener(this); 93 stateChanged(null); 94 } 95 96 public JTextComponent getComponent() { 97 return comp; 98 } 99 100 public void setHints (LazyFixList hints, JTextComponent comp, boolean showPopup) { 101 if (this.hints.equals(hints) && this.comp == comp) { 102 return; 103 } 104 if (comp != this.comp || !this.hints.equals(hints) && comp != null) { 105 removePopups(); 106 } 107 boolean show = hints != null && comp != null; 108 if (!show && this.comp != null) { 109 removePopups(); 110 } 111 this.hints = hints == null ? new StaticFixList() : hints; 112 setComponent (comp); 113 if (show) { 114 showHints(); 115 if (showPopup) { 116 showPopup(); 117 } 118 } 119 } 120 121 public void setComponent (JTextComponent comp) { 122 boolean change = this.comp != comp; 123 if (change) { 124 unregister(); 125 this.comp = comp; 126 register(); 127 } 128 } 129 130 private void register() { 131 if (comp == null) { 132 return; 133 } 134 comp.addKeyListener (this); 135 } 136 137 private void unregister() { 138 if (comp == null) { 139 return; 140 } 141 comp.removeKeyListener (this); 142 } 143 144 145 public void removePopups() { 146 if (comp == null) { 147 return; 148 } 149 removeIconHint(); 150 removePopup(); 151 } 152 153 private void removeIconHint() { 154 if (hintIcon != null) { 155 Container c = hintIcon.getParent(); 156 if (c != null) { 157 Rectangle bds = hintIcon.getBounds(); 158 c.remove (hintIcon); 159 c.repaint (bds.x, bds.y, bds.width, bds.height); 160 } 161 } 162 } 163 164 private void removePopup() { 165 Toolkit.getDefaultToolkit().removeAWTEventListener(this); 166 if (listPopup != null) { 167 listPopup.hide(); 168 if (hintListComponent != null) { 169 hintListComponent.getView().removeMouseListener(this); 170 } 171 if (errorTooltip != null) { 172 errorTooltip.removeMouseListener(this); 173 } 174 hintListComponent = null; 175 errorTooltip = null; 176 listPopup = null; 177 if (hintIcon != null) 178 hintIcon.setToolTipText(NbBundle.getMessage(HintsUI.class, "HINT_Bulb")); } 180 } 181 182 boolean isKnownComponent(Component c) { 183 return c != null && 184 (c == comp 185 || c == hintIcon 186 || c == hintListComponent 187 || (c instanceof Container && ((Container)c).isAncestorOf(hintListComponent)) 188 ) 189 ; 190 } 191 192 private void showHints() { 193 if (comp == null || !comp.isDisplayable() || !comp.isShowing()) { 194 return; 195 } 196 configureBounds (getHintIcon()); 197 } 198 199 private void configureBounds (JComponent jc) { 200 JRootPane pane = comp.getRootPane(); 201 JLayeredPane lp = pane.getLayeredPane(); 202 Rectangle r = null; 203 try { 204 int pos = javax.swing.text.Utilities.getRowStart(comp, comp.getCaret().getDot()); 205 r = comp.modelToView (pos); 206 } catch (BadLocationException e) { 207 setHints (null, null, false); 208 ErrorManager.getDefault().notify (e); 209 return; 210 } 211 Point p = new Point(r.x - comp.getX(), r.y ); 212 213 Dimension d = jc.getPreferredSize(); 214 215 SwingUtilities.convertPointToScreen(p, comp); 216 SwingUtilities.convertPointFromScreen(p, lp); 217 jc.setBounds (p.x, p.y, d.width, d.height); 218 lp.add (jc, JLayeredPane.POPUP_LAYER); 219 jc.setVisible(true); 220 jc.repaint(); 221 } 222 223 private JLabel getHintIcon() { 224 if (hintIcon == null) { 225 hintIcon = new JLabel(); 226 hintIcon.addMouseListener (this); 227 hintIcon.setToolTipText(NbBundle.getMessage(HintsUI.class, "HINT_Bulb")); } 229 String iconBase = 230 "org/netbeans/modules/editor/hints/resources/error.png"; hintIcon.setIcon (new ImageIcon (org.openide.util.Utilities.loadImage(iconBase))); 232 return hintIcon; 233 } 234 235 public void showPopup() { 236 if (comp == null || (hints.isComputed() && hints.getFixes().isEmpty())) { 237 return; 238 } 239 if (hintIcon != null) 240 hintIcon.setToolTipText(null); 241 ToolTipManager.sharedInstance().setEnabled(false); 243 ToolTipManager.sharedInstance().setEnabled(true); 244 assert hintListComponent == null; 245 hintListComponent = 246 new ScrollCompletionPane(comp, hints, null, null); 247 248 hintListComponent.getView().addMouseListener (this); 249 hintListComponent.setName(POPUP_NAME); 250 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); 251 252 try { 253 int pos = javax.swing.text.Utilities.getRowStart(comp, comp.getCaret().getDot()); 254 Rectangle r = comp.modelToView (pos); 255 256 Point p = new Point (r.x + 5, r.y + 20); 257 SwingUtilities.convertPointToScreen(p, comp); 258 259 assert listPopup == null; 260 listPopup = getPopupFactory().getPopup( 261 comp, hintListComponent, p.x, p.y); 262 listPopup.show(); 263 } catch (BadLocationException ble) { 264 ErrorManager.getDefault().notify (ble); 265 setHints (null, null, false); 266 } 267 } 268 269 public void showPopup(LazyFixList fixes, String description, JTextComponent component, Point position) { 270 setHints(null, null, false); 271 setComponent(component); 272 273 if (comp == null || fixes == null) 274 return ; 275 276 this.hints = fixes; 277 278 Point p = new Point(position); 279 SwingUtilities.convertPointToScreen(p, comp); 280 281 if (hintIcon != null) 282 hintIcon.setToolTipText(null); 283 ToolTipManager.sharedInstance().setEnabled(false); 285 ToolTipManager.sharedInstance().setEnabled(true); 286 Toolkit.getDefaultToolkit().addAWTEventListener(this, AWTEvent.MOUSE_EVENT_MASK); 287 288 if (!fixes.isComputed() || fixes.getFixes().isEmpty()) { 289 assert listPopup == null; 291 errorTooltip = new JLabel("<html>" + translate(description)); errorTooltip.setBorder(new LineBorder (Color.BLACK)); 293 errorTooltip.addMouseListener(this); 294 295 listPopup = getPopupFactory().getPopup( 296 comp, errorTooltip, p.x, p.y); 297 } else { 298 assert hintListComponent == null; 299 hintListComponent = 300 new ScrollCompletionPane(comp, fixes, null, null); 301 302 hintListComponent.getView().addMouseListener (this); 303 hintListComponent.setName(POPUP_NAME); 304 assert listPopup == null; 305 listPopup = getPopupFactory().getPopup( 306 comp, hintListComponent, p.x, p.y); 307 } 308 309 listPopup.show(); 310 } 311 312 private PopupFactory pf = null; 313 private PopupFactory getPopupFactory() { 314 if (pf == null) { 315 pf = PopupFactory.getSharedInstance(); 316 } 317 return pf; 318 } 319 320 public void mouseClicked(java.awt.event.MouseEvent e) { 321 if (e.getSource() == hintListComponent || e.getSource() instanceof ListCompletionView) { 322 Fix f = null; 323 Object selected = hintListComponent.getView().getSelectedValue(); 324 325 if (selected instanceof Fix) { 326 f = (Fix) selected; 327 } 328 329 if (f != null) { 330 JTextComponent c = this.comp; 331 invokeHint (f); 332 setHints (null, null, false); 333 setComponent(c); 335 } 336 } 337 } 338 339 public void mouseEntered(java.awt.event.MouseEvent e) { 340 } 341 342 public void mouseExited(java.awt.event.MouseEvent e) { 343 } 344 345 public void mousePressed(java.awt.event.MouseEvent e) { 346 if (e.getSource() instanceof JLabel) { 347 if (!isPopupActive()) { 348 showPopup(); 349 } 350 } 351 } 352 353 public void mouseReleased(java.awt.event.MouseEvent e) { 354 } 355 356 public boolean isActive() { 357 boolean bulbShowing = hintIcon != null && hintIcon.isShowing(); 358 boolean popupShowing = hintListComponent != null && hintListComponent.isShowing(); 359 return bulbShowing || popupShowing; 360 } 361 362 public boolean isPopupActive() { 363 return hintListComponent != null && hintListComponent.isShowing(); 364 } 365 366 private ParseErrorAnnotation findAnnotation(Document doc, AnnotationDesc desc, int lineNum) { 367 DataObject od = (DataObject) doc.getProperty(Document.StreamDescriptionProperty); 368 369 if (od == null) 370 return null; 371 372 AnnotationHolder annotations = AnnotationHolder.getInstance(od.getPrimaryFile()); 373 374 for (Annotation a : annotations.getAnnotations()) { 375 if (a instanceof ParseErrorAnnotation) { 376 ParseErrorAnnotation pa = (ParseErrorAnnotation) a; 377 378 if (lineNum == pa.getLineNumber() 379 && org.openide.util.Utilities.compareObjects(desc.getShortDescription(), a.getShortDescription())) { 380 return pa; 381 } 382 } 383 } 384 385 return null; 386 } 387 388 boolean invokeDefaultAction() { 389 if (comp == null) { 390 Logger.getLogger(HintsUI.class.getName()).log(Level.WARNING, "HintsUI.invokeDefaultAction called, but comp == null"); 391 return false; 392 } 393 394 Document doc = comp.getDocument(); 395 396 if (doc instanceof BaseDocument) { 397 Annotations annotations = ((BaseDocument) doc).getAnnotations(); 398 399 try { 400 Rectangle carretRectangle = comp.modelToView(comp.getCaretPosition()); 401 int line = Utilities.getLineOffset((BaseDocument) doc, comp.getCaretPosition()); 402 AnnotationDesc desc = annotations.getActiveAnnotation(line); 403 Point p = comp.modelToView(Utilities.getRowStartFromLineOffset((BaseDocument) doc, line)).getLocation(); 404 p.y += carretRectangle.height; 405 ParseErrorAnnotation annotation = findAnnotation(doc, desc, line); 406 407 if (annotation == null) 408 return false; 409 410 showPopup(annotation.getFixes(), annotation.getDescription(), comp, p); 411 412 return true; 413 } catch (BadLocationException ex) { 414 ErrorManager.getDefault().notify(ex); 415 } 416 } 417 418 return false; 419 } 420 421 public void keyPressed(KeyEvent e) { 422 if (comp == null) { 423 return; 424 } 425 boolean bulbShowing = hintIcon != null && hintIcon.isShowing(); 426 boolean errorTooltipShowing = errorTooltip != null && errorTooltip.isShowing(); 427 428 if (errorTooltipShowing) { 429 removePopup(); 431 return ; 432 } 433 boolean popupShowing = hintListComponent != null && hintListComponent.isShowing(); 434 if ( e.getKeyCode() == KeyEvent.VK_ENTER ) { 435 if ( e.getModifiersEx() == (KeyEvent.CTRL_DOWN_MASK | KeyEvent.SHIFT_DOWN_MASK) 436 || e.getModifiersEx() == KeyEvent.ALT_DOWN_MASK) { 437 if ( !popupShowing) { 438 invokeDefaultAction(); 439 e.consume(); 440 } 441 } else if ( e.getModifiersEx() == 0 ) { 442 if (popupShowing) { 443 Fix f = null; 444 Object selected = hintListComponent.getView().getSelectedValue(); 445 446 if (selected instanceof Fix) { 447 f = (Fix) selected; 448 } 449 450 if (f != null) { 451 invokeHint(f); 452 } 453 454 e.consume(); 455 } 456 } 457 } else if ( e.getKeyCode() == KeyEvent.VK_ESCAPE ) { 458 if ( popupShowing ) { 459 removePopup(); 460 } 461 } else if ( popupShowing ) { 462 InputMap input = hintListComponent.getInputMap(); 463 Object actionTag = input.get(KeyStroke.getKeyStrokeForEvent(e)); 464 if (actionTag != null) { 465 Action a = hintListComponent.getActionMap().get(actionTag); 466 a.actionPerformed(null); 467 e.consume(); 468 return ; 469 } 470 } 471 } 472 473 public void keyReleased(KeyEvent e) { 474 } 475 476 public void keyTyped(KeyEvent e) { 477 } 478 479 private ChangeInfo changes; 480 481 private void invokeHint (final Fix f) { 482 if (UI_GESTURES_LOGGER.isLoggable(Level.FINE)) { 483 LogRecord rec = new LogRecord (Level.FINE, "GEST_HINT_INVOKED"); 484 485 rec.setResourceBundle(NbBundle.getBundle(HintsUI.class)); 486 rec.setParameters(new Object [] {f.getText()}); 487 UI_GESTURES_LOGGER.log(rec); 488 } 489 490 removePopups(); 491 final JTextComponent component = comp; 492 final Cursor cur = component.getCursor(); 493 component.setCursor (Cursor.getPredefinedCursor (Cursor.WAIT_CURSOR)); 494 Task t = null; 495 try { 496 t = RequestProcessor.getDefault().post(new Runnable () { 497 public void run() { 498 changes = f.implement(); 499 } 500 }); 501 } finally { 502 if (t != null) { 503 t.addTaskListener(new TaskListener() { 504 public void taskFinished(Task task) { 505 SwingUtilities.invokeLater(new Runnable () { 506 public void run() { 507 open(changes, component); 508 component.setCursor (cur); 509 } 510 }); 511 } 512 }); 513 } 514 } 515 } 516 517 private static void open(ChangeInfo changes, JTextComponent component) { 518 JTextComponent c = component; 519 if (changes != null && changes.size() > 0) { 520 ChangeInfo.Change change = changes.get(0); 521 FileObject file = change.getFileObject(); 522 if (file != null) { 523 try { 524 DataObject dob = 525 DataObject.find (file); 526 527 EditCookie ck = dob.getCookie(EditCookie.class); 528 529 if (ck != null) { 530 ck.edit(); 533 } else { 534 OpenCookie oc = dob.getCookie(OpenCookie.class); 535 536 oc.open(); 537 } 538 EditorCookie edit = dob.getCookie(EditorCookie.class); 539 540 JEditorPane[] panes = edit.getOpenedPanes(); 541 if (panes != null && panes.length > 0) { 542 c = panes[0]; 543 } else { 544 return; 545 } 546 547 } catch (DataObjectNotFoundException donfe) { 548 ErrorManager.getDefault().notify(donfe); 549 return; 550 } 551 } 552 Position start = change.getStart(); 554 Position end = change.getEnd(); 555 if (start != null) { 556 c.setSelectionStart(start.getOffset()); 557 } 558 if (end != null) { 559 c.setSelectionEnd(end.getOffset()); 560 } 561 } 562 } 563 564 public void stateChanged(ChangeEvent e) { 565 JTextComponent active = Registry.getMostActiveComponent(); 566 567 if (getComponent() != active) { 568 setHints(null, null, false); 569 setComponent(active); 570 } 571 } 572 573 public void eventDispatched(java.awt.AWTEvent aWTEvent) { 574 if (aWTEvent instanceof MouseEvent ) { 575 MouseEvent mv = (MouseEvent )aWTEvent; 576 if (mv.getID() == MouseEvent.MOUSE_CLICKED && mv.getClickCount() > 0) { 577 Component comp = (Component )aWTEvent.getSource(); 578 Container par = SwingUtilities.getAncestorNamed(POPUP_NAME, comp); if ( par == null ) { 582 removePopup(); 583 } 584 } 585 } 586 } 587 588 private static String [] c = new String [] {"&", "<", ">", "\n", "\""}; private static String [] tags = new String [] {"&", "<", ">", "<br>", """}; 591 private String translate(String input) { 592 for (int cntr = 0; cntr < c.length; cntr++) { 593 input = input.replaceAll(c[cntr], tags[cntr]); 594 } 595 596 return input; 597 } 598 599 } 600 | Popular Tags |