package ctagsinterface.jedit; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionListener; import java.util.Vector; import javax.swing.JToolTip; import javax.swing.Popup; import javax.swing.PopupFactory; import javax.swing.SwingWorker; import javax.swing.Timer; import javax.swing.ToolTipManager; import org.gjt.sp.jedit.EditBus; import org.gjt.sp.jedit.jEdit; import org.gjt.sp.jedit.EditBus.EBHandler; import org.gjt.sp.jedit.buffer.JEditBuffer; import org.gjt.sp.jedit.msg.EditPaneUpdate; import org.gjt.sp.jedit.msg.PropertiesChanged; import org.gjt.sp.jedit.textarea.JEditTextArea; import org.gjt.sp.jedit.textarea.TextAreaExtension; import org.gjt.sp.jedit.visitors.JEditVisitorAdapter; import ctagsinterface.main.CtagsInterfacePlugin; import ctagsinterface.main.Tag; import ctagsinterface.options.GeneralOptionPane; public class TagTooltip extends TextAreaExtension { private JEditTextArea textArea; private static Attacher attacher; { } public TagTooltip(JEditTextArea textArea) { this.textArea = textArea; } public static void start() { attachToAll(); attacher = new Attacher(); EditBus.addToBus(attacher); } private static void attachToAll() { jEdit.visit(new JEditVisitorAdapter() { public void visit(JEditTextArea textArea) { attachToTextArea(textArea); } }); } private static void attachToTextArea(JEditTextArea ta) { if (ta.getClientProperty("TagTooltip") != null) return; TagTooltip ext = new TagTooltip(ta); ta.getPainter().addExtension(ext); ta.putClientProperty("TagTooltip", ext); } public static void stop() { EditBus.removeFromBus(attacher); attacher = null; detachFromAll(); } public static void detachFromAll() { jEdit.visit(new JEditVisitorAdapter() { public void visit(JEditTextArea textArea) { detachFromTextArea(textArea); } }); } private static void detachFromTextArea(JEditTextArea ta) { TagTooltip ext = (TagTooltip) ta.getClientProperty("TagTooltip"); if (ext != null) { ta.getPainter().removeExtension(ext); ta.putClientProperty("TagTooltip", null); } } @Override public String getToolTipText(final int x, final int y) { int offset = textArea.xyToOffset(x, y); JEditBuffer buffer = textArea.getBuffer(); if((offset < 0) || (offset > buffer.getLength())) return null; int line = buffer.getLineOfOffset(offset); int index = offset - buffer.getLineStartOffset(line); final String tag = CtagsInterfacePlugin.getTagAt(textArea, line, index); if ((tag == null) || (tag.length() == 0)) return null; SwingWorker<String, Void> w = new DelayedTooltip(x, y, tag); w.execute(); return null; } private String getTagString(Tag tag) { StringBuffer sb = new StringBuffer(); sb.append(tag.getName()); String signature = tag.getExtension("signature"); if (signature != null && signature.length() > 0) sb.append(signature + " "); StringBuffer details = new StringBuffer(); String namespace = tag.getNamespace(); if (namespace != null && namespace.length() > 0) details.append(namespace); String kind = tag.getKind(); if (kind != null && kind.length() > 0) { if (details.length() > 0) details.append(" "); details.append(tag.getKind()); } if (details.length() > 0) sb.append(" (" + details.toString() + ")"); return sb.toString(); } private final class DelayedTooltip extends SwingWorker<String, Void> { private int x; private int y; private String tag; private Timer timer = null; private Popup popup = null; private boolean dismissed = false; private ActionListener dismiss; private MouseMotionListener mml; private KeyListener kl; private DelayedTooltip(int x, int y, String tag) { this.x = x; this.y = y; this.tag = tag; dismiss = new ActionListener() { public void actionPerformed(ActionEvent e) { dismissed = true; if (mml != null) textArea.getPainter().removeMouseMotionListener(mml); if (kl != null) textArea.getPainter().removeKeyListener(kl); if (timer != null) timer.stop(); if (popup != null) popup.hide(); } }; mml = new MouseMotionListener() { public void mouseDragged(MouseEvent e) { dismiss(); } public void mouseMoved(MouseEvent e) { dismiss(); } }; textArea.getPainter().addMouseMotionListener(mml); kl = new KeyListener() { public void keyPressed(KeyEvent e) { dismiss(); } public void keyReleased(KeyEvent e) { dismiss(); } public void keyTyped(KeyEvent e) { dismiss(); } }; textArea.getPainter().addKeyListener(kl); } private void dismiss() { dismiss.actionPerformed(null); } @Override protected void done() { if (dismissed) return; JToolTip tt = textArea.createToolTip(); try { String ttText = get(); if (ttText == null) return; tt.setTipText(ttText); } catch (Exception e) { return; } PopupFactory factory = PopupFactory.getSharedInstance(); int x1 = textArea.getLocationOnScreen().x + x; int y1 = textArea.getLocationOnScreen().y + y; popup = factory.getPopup(textArea, tt, x1, y1); popup.show(); int d = ToolTipManager.sharedInstance().getDismissDelay(); timer = new Timer(d, dismiss); timer.start(); } @Override protected String doInBackground() throws Exception { Vector<Tag> tags = CtagsInterfacePlugin.queryScopedTag( textArea.getView(), tag); if (tags == null || tags.isEmpty()) return null; StringBuffer sb = new StringBuffer("<html>"); boolean first = true; for (Tag t: tags) { if (! first) sb.append("<br>"); else first = false; sb.append(getTagString(t)); } sb.append("</html>"); return sb.toString(); } } public static class Attacher { @EBHandler public void handleEditPaneUpdate(EditPaneUpdate epu) { if (! GeneralOptionPane.getShowTooltips()) return; if (epu.getWhat().equals(EditPaneUpdate.CREATED)) attachToTextArea(epu.getEditPane().getTextArea()); else if (epu.getWhat().equals(EditPaneUpdate.DESTROYED)) detachFromTextArea(epu.getEditPane().getTextArea()); } @EBHandler public void handlePropertiesChanged(PropertiesChanged msg) { if (GeneralOptionPane.getShowTooltips()) attachToAll(); else detachFromAll(); } } }