1 19 20 package org.netbeans.modules.editor; 21 22 import javax.swing.event.DocumentEvent ; 23 import javax.swing.event.DocumentListener ; 24 import javax.swing.text.AbstractDocument ; 25 import javax.swing.text.JTextComponent ; 26 import javax.swing.text.StyledDocument ; 27 import javax.swing.text.BadLocationException ; 28 import org.openide.filesystems.FileChangeAdapter; 29 import org.openide.filesystems.FileObject; 30 import org.openide.text.Annotation; 31 import org.openide.text.Line; 32 import java.io.IOException ; 33 import java.util.HashMap ; 34 import org.netbeans.editor.ext.ToolTipSupport; 35 import org.openide.loaders.DataObjectNotFoundException; 36 import org.netbeans.editor.BaseDocument; 37 import org.netbeans.editor.Utilities; 38 import org.netbeans.editor.AnnotationDesc; 39 import org.netbeans.editor.ext.ExtEditorUI; 40 import org.netbeans.editor.ext.ExtUtilities; 41 import java.beans.PropertyChangeListener ; 42 import org.openide.cookies.EditorCookie; 43 import org.openide.loaders.DataObject; 44 import org.openide.cookies.InstanceCookie; 45 import java.util.Enumeration ; 46 import java.util.ArrayList ; 47 import java.beans.PropertyChangeEvent ; 48 import javax.swing.plaf.TextUI ; 49 import org.netbeans.editor.BaseTextUI; 50 import org.openide.filesystems.Repository; 51 import org.openide.util.RequestProcessor; 52 53 59 60 public class NbToolTip extends FileChangeAdapter { 61 62 private static final boolean debug = Boolean.getBoolean("netbeans.debug.editor.tooltip"); 63 64 private static final HashMap mime2tip = new HashMap (); 65 66 private static int lastRequestId; 67 68 private String mimeType; 69 70 private Annotation[] tipAnnotations; 71 72 private RequestProcessor toolTipRP = new RequestProcessor("ToolTip-Evaluator", 1); 74 static synchronized void buildToolTip(JTextComponent target) { 75 String mimeType = NbEditorUtilities.getMimeType(target.getDocument()); 76 NbToolTip tip = getTip(mimeType); 77 tip.buildTip(target); 78 } 79 80 private static int newRequestId() { 81 return ++lastRequestId; 82 } 83 84 private static int getLastRequestId() { 85 return lastRequestId; 86 } 87 88 89 private NbToolTip(String mimeType) { 90 this.mimeType = mimeType; 91 } 92 93 private static NbToolTip getTip(String mimeType) { 94 NbToolTip tip = (NbToolTip)mime2tip.get(mimeType); 95 if (tip == null) { 96 tip = new NbToolTip(mimeType); 97 mime2tip.put(mimeType, tip); 98 } 99 100 return tip; 101 } 102 103 private Annotation[] getTipAnnotations() { 104 Annotation[] annos; 105 synchronized (NbToolTip.class) { 106 annos = tipAnnotations; 107 } 108 109 if (annos == null) { 110 111 if (debug) { 112 System.err.println("Searching for tooltip annotations for mimeType=" + mimeType); 113 } 114 115 FileObject annoFolder = Repository.getDefault().getDefaultFileSystem(). 116 findResource("Editors/" + mimeType + "/ToolTips"); 118 if (debug) { 119 System.err.println("tooltip annotation folder=" + annoFolder); 120 } 121 122 if (annoFolder != null) { 123 ArrayList al = new ArrayList (); 124 Enumeration en = annoFolder.getChildren(false); 125 while (en.hasMoreElements()) { 126 FileObject fo = (FileObject)en.nextElement(); 127 128 if (debug) { 129 System.err.println("tooltip annotation fileobject=" + fo); 130 } 131 132 try { 133 DataObject dob = DataObject.find(fo); 134 InstanceCookie ic = (InstanceCookie)dob.getCookie(InstanceCookie.class); 135 136 if (debug) { 137 System.err.println("tooltip annotation instanceCookie=" + ic); 138 } 139 140 if (ic != null) { 141 Object a = ic.instanceCreate(); 142 143 if (debug) { 144 System.err.println("tooltip annotation instance=" + a); 145 } 146 147 if (a instanceof Annotation) { 148 149 if (debug) { 150 System.err.println("Found tooltip annotation=" + a 151 + ", class " + a.getClass() + " for mimeType=" + mimeType ); 154 } 155 156 al.add(a); 157 } 158 } 159 } catch (DataObjectNotFoundException e) { 160 } catch (IOException e) { 161 } catch (ClassNotFoundException e) { 162 } 163 } 164 165 annos = (Annotation[])al.toArray(new Annotation[al.size()]); 166 synchronized (NbToolTip.class) { 167 tipAnnotations = annos; 168 } 169 170 annoFolder.addFileChangeListener(this); 171 } 172 } 173 174 return annos; 175 } 176 177 private void buildTip(JTextComponent target) { 178 179 TextUI textUI = target.getUI(); 180 if (textUI!=null && textUI instanceof BaseTextUI){ 181 BaseTextUI btui = (BaseTextUI)textUI; 182 ExtEditorUI editorUI = (ExtEditorUI)btui.getEditorUI(); 183 ToolTipSupport tts = editorUI.getToolTipSupport(); 184 String toolTipText = btui.getToolTipText(target, tts.getLastMouseEvent().getPoint()); 185 if (toolTipText!=null){ 186 return; 187 } 188 } 189 190 Annotation[] annos = getTipAnnotations(); 191 if (annos != null) { 192 ExtEditorUI ui = ExtUtilities.getExtEditorUI(target); 193 if (ui != null) { 194 ToolTipSupport tts = ui.getToolTipSupport(); 195 if (tts != null) { 196 BaseDocument doc = Utilities.getDocument(target); 197 if (doc != null) { 198 DataObject dob = NbEditorUtilities.getDataObject(doc); 199 if (dob != null && dob.isValid()) { 200 EditorCookie ec = (EditorCookie)dob.getCookie(EditorCookie.class); 201 if (ec != null) { 202 StyledDocument openedDoc; 203 try { 204 openedDoc = ec.openDocument(); 205 } catch (IOException e) { 206 openedDoc = null; } 208 209 if (openedDoc != doc) { return; 211 } 212 213 doc.readLock(); 215 try { 216 int offset = target.viewToModel(tts.getLastMouseEvent().getPoint()); 217 if (offset >= 0) { 218 try { 219 int line = Utilities.getLineOffset(doc, offset); 220 int col = offset - Utilities.getRowStart(target, offset); 221 Line.Set ls = ec.getLineSet(); 222 if (ls != null) { 223 Line l = ls.getCurrent(line); 224 if (l != null) { 225 Line.Part lp = l.createPart(col, 0); 226 if (lp != null) { 227 AnnotationDesc annoDesc = doc.getAnnotations().getActiveAnnotation(line); 228 if (annoDesc != null && ((offset < annoDesc.getOffset() || offset >= annoDesc.getOffset() + annoDesc.getLength()))) { 229 annoDesc = null; 230 } 231 org.netbeans.editor.BaseKit kit = org.netbeans.editor.Utilities.getKit(target); 232 if (kit instanceof NbEditorKit) { 233 int requestId = newRequestId(); 234 toolTipRP.post(new Request(annoDesc, annos, lp, tts, doc, (NbEditorKit)kit, requestId)); 235 } 236 } 237 } 238 } 239 } catch (BadLocationException e) { 240 } 241 } 242 } finally { 243 doc.readUnlock(); 244 } 245 } 246 } 247 } 248 } 249 } 250 } 251 } 252 253 private static class Request implements Runnable , PropertyChangeListener , DocumentListener { 254 255 private ToolTipSupport tts; 256 257 private Annotation[] annos; 258 259 private AnnotationDesc annoDesc; 260 261 private Line.Part linePart; 262 263 private AbstractDocument doc; 264 265 private NbEditorKit kit; 266 267 private int requestId; 268 269 private boolean documentModified; 270 271 Request(AnnotationDesc annoDesc, Annotation[] annos, Line.Part lp, 272 ToolTipSupport tts, AbstractDocument doc, NbEditorKit kit, int requestId) { 273 this.annoDesc = annoDesc; 274 this.annos = annos; 275 this.linePart = lp; 276 this.tts = tts; 277 this.doc = doc; 278 this.kit = kit; 279 this.requestId = requestId; 280 } 281 282 public void run() { 283 if (tts == null) return; 284 285 if (tts == null || tts.getStatus() == ToolTipSupport.STATUS_HIDDEN) { 286 return; } 288 if (!isRequestValid()) { 289 return; 290 } 291 292 if (tts!=null) tts.addPropertyChangeListener(this); 293 294 kit.toolTipAnnotationsLock(doc); 295 try { 296 doc.readLock(); 297 try { 298 299 if (!isRequestValid()) { 300 return; 301 } 302 303 for (int i = 0; i < annos.length; i++) { 305 annos[i].attach(linePart); 306 } 307 308 if (annoDesc != null && tts != null) { 309 tts.setToolTipText(annoDesc.getShortDescription()); 310 annoDesc.addPropertyChangeListener(this); 311 } else { 312 for (int i = 0; i < annos.length; i++) { 313 String desc = annos[i].getShortDescription(); 314 if (desc != null && tts != null) { 315 tts.setToolTipText(desc); 316 } 317 annos[i].addPropertyChangeListener(this); 318 } 319 } 320 } finally { 321 doc.readUnlock(); 322 } 323 } finally { 324 kit.toolTipAnnotationsUnlock(doc); 325 } 326 } 327 328 private boolean isRequestValid() { 329 return (getLastRequestId() == this.requestId) 330 && !documentModified 331 && isDocumentValid(); 332 } 333 334 private boolean isDocumentValid() { 335 DataObject dob = NbEditorUtilities.getDataObject(doc); 336 if (dob != null) { 337 EditorCookie ec = (EditorCookie)dob.getCookie(EditorCookie.class); 338 if (ec != null) { 339 StyledDocument openedDoc; 340 try { 341 openedDoc = ec.openDocument(); 342 } catch (IOException e) { 343 openedDoc = null; } 345 346 return (openedDoc == doc); 347 } 348 } 349 return false; 350 } 351 352 private void dismiss() { 353 if (tts !=null) tts.removePropertyChangeListener(this); 354 tts = null; 356 if (annoDesc != null) { 357 annoDesc.removePropertyChangeListener(this); 358 } else { 359 for (int i = 0; i < annos.length; i++) { 360 annos[i].removePropertyChangeListener(this); 361 annos[i].detach(); 362 } 363 } 364 } 365 366 public void propertyChange(PropertyChangeEvent evt) { 367 String propName = evt.getPropertyName(); 368 if (Annotation.PROP_SHORT_DESCRIPTION.equals(propName) || AnnotationDesc.PROP_SHORT_DESCRIPTION.equals(propName)) { 369 if (evt.getNewValue() != null) { 370 final String tipText = (String )evt.getNewValue(); 371 Utilities.runInEventDispatchThread( new Runnable () { 373 public void run() { 374 if (tts != null) { 375 tts.setToolTipText(tipText); 376 } 377 } 378 } 379 ); 380 } 381 382 } else if (ToolTipSupport.PROP_STATUS.equals(propName)) { 383 if (((Integer )evt.getNewValue()).intValue() == ToolTipSupport.STATUS_HIDDEN) { 384 dismiss(); 385 } 386 } 387 } 388 389 public void insertUpdate(DocumentEvent evt) { 390 documentModified = true; 391 } 392 393 public void removeUpdate(DocumentEvent evt) { 394 documentModified = true; 395 } 396 397 public void changedUpdate(DocumentEvent evt) { 398 } 399 400 } 401 402 } 403 | Popular Tags |