package ctagsinterface.dockables; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.HierarchyEvent; import java.awt.event.HierarchyListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.Vector; import javax.swing.AbstractAction; import javax.swing.Action; import javax.swing.DefaultListCellRenderer; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JCheckBox; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JMenuItem; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JSplitPane; import javax.swing.Timer; import javax.swing.event.CaretEvent; import javax.swing.event.CaretListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import org.gjt.sp.jedit.EditBus; import org.gjt.sp.jedit.EditPane; import org.gjt.sp.jedit.MiscUtilities; import org.gjt.sp.jedit.Mode; import org.gjt.sp.jedit.Registers; import org.gjt.sp.jedit.View; 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.gui.DefaultFocusComponent; import org.gjt.sp.jedit.io.VFSManager; import org.gjt.sp.jedit.msg.EditPaneUpdate; import org.gjt.sp.jedit.msg.PropertiesChanged; import org.gjt.sp.jedit.msg.ViewUpdate; import org.gjt.sp.jedit.syntax.ModeProvider; import org.gjt.sp.jedit.textarea.JEditEmbeddedTextArea; import org.gjt.sp.jedit.textarea.JEditTextArea; import org.gjt.sp.jedit.textarea.TextArea; import ctagsinterface.main.CtagsInterfacePlugin; import ctagsinterface.main.Tag; import ctagsinterface.options.GeneralOptionPane; @SuppressWarnings("serial") public class Preview extends JPanel implements DefaultFocusComponent, CaretListener, ListSelectionListener { static public final String MESSAGE = CtagsInterfacePlugin.MESSAGE; View view; JList tags; DefaultListModel tagModel; TextArea text; boolean first = true; String file; Timer timer; Set<JEditTextArea> tracking; private JCheckBox wrap; private JCheckBox followCaret; private JPanel toolbar; private JPanel textPanel; private boolean toolbarShown; private JSplitPane split; public Preview(final View view) { super(new BorderLayout()); this.view = view; timer = null; tracking = new HashSet<JEditTextArea>(); file = null; tagModel = new DefaultListModel(); tags = new JList(tagModel); tags.setCellRenderer(new TagListCellRenderer()); tags.setVisibleRowCount(4); tags.addListSelectionListener(this); tags.addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent me) { if (me.getButton() == MouseEvent.BUTTON3) { final int index = tags.locationToIndex(me.getPoint()); if (index < 0) return; final Tag t = (Tag) tagModel.get(index); JPopupMenu menu = new JPopupMenu(); menu.add(new AbstractAction() { public Object getValue(String key) { if (key.equals(Action.NAME)) return "Copy absolute path to clipboard"; return super.getValue(key); } public void actionPerformed(ActionEvent e) { Registers.setRegister('$', t.getFile()); } }); menu.show(view, me.getXOnScreen(), me.getYOnScreen()); return; } if (me.getClickCount() < 2 || tags.getSelectedIndex() < 0) return; Tag t = (Tag) tagModel.getElementAt(tags.getSelectedIndex()); CtagsInterfacePlugin.jumpToTag(Preview.this.view, t); } }); textPanel = new JPanel(); textPanel.setLayout(new BorderLayout()); toolbarShown = false; toolbar = new JPanel(); followCaret = new JCheckBox("Follow caret", true); followCaret.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { setCaretTracking(Preview.this.view.getTextArea(), followCaret.isSelected()); } }); toolbar.add(followCaret); wrap = new JCheckBox("Soft wrap", GeneralOptionPane.getPreviewWrap()); wrap.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { GeneralOptionPane.setPreviewWrap(wrap.isSelected()); propertiesChanged(); } }); toolbar.add(wrap); text = new PreviewTextArea(); text.getBuffer().setProperty("folding","explicit"); textPanel.add(text, BorderLayout.CENTER); textPanel.add(text, BorderLayout.CENTER); EditPane.initPainter(text.getPainter()); text.getPainter().addMouseListener(new MouseAdapter() { public void mouseClicked(MouseEvent me) { if (me.getClickCount() == 2 && file != null) { CtagsInterfacePlugin.jumpToOffset(Preview.this.view, file, text.getCaretPosition()); } } }); text.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { if (((e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) && (e.getKeyCode() == KeyEvent.VK_C)) { copyPreviewSelection(); e.consume(); } } }); propertiesChanged(); text.setMinimumSize(new Dimension(150, 50)); split = new JSplitPane(getSplitOrientation(), new JScrollPane(tags), textPanel); split.setOneTouchExpandable(true); split.setDividerLocation(100); add(split, BorderLayout.CENTER); EditBus.addToBus(this); this.addHierarchyListener(new HierarchyListener() { public void hierarchyChanged(HierarchyEvent e) { if ((e.getChangeFlags() & HierarchyEvent.DISPLAYABILITY_CHANGED) > 0) setCaretTracking(Preview.this.view.getTextArea(), false); } }); this.addComponentListener(new ComponentListener() { public void componentHidden(ComponentEvent arg0) { updateCaretListenerState(); } public void componentMoved(ComponentEvent arg0) { } public void componentResized(ComponentEvent arg0) { updateCaretListenerState(); } public void componentShown(ComponentEvent arg0) { updateCaretListenerState(); } }); } private void copyPreviewSelection() { Registers.copy(text, '$'); } private void updateCaretListenerState() { boolean visible = isVisible() && getWidth() > 0 && getHeight() > 0; if (visible) { if (followCaret.isSelected()) setCaretTracking(view.getTextArea(), true); } else { Vector<JEditTextArea> textAreas = new Vector<JEditTextArea>(); textAreas.addAll(tracking); Iterator<JEditTextArea> it = textAreas.iterator(); while (it.hasNext()) setCaretTracking(it.next(), false); } } private void setCaretTracking(JEditTextArea textArea, boolean track) { if (track) caretUpdate(null); if (tracking.contains(textArea) == track) return; if (track) { tracking.add(textArea); textArea.addCaretListener(this); } else { tracking.remove(textArea); textArea.removeCaretListener(this); } } private int getSplitOrientation() { return GeneralOptionPane.getPreviewVerticalSplit() ? JSplitPane.VERTICAL_SPLIT : JSplitPane.HORIZONTAL_SPLIT; } private void propertiesChanged() { if (split != null) split.setOrientation(getSplitOrientation()); if (GeneralOptionPane.getPreviewToolbar() != toolbarShown) { toolbarShown = GeneralOptionPane.getPreviewToolbar(); if (toolbarShown) textPanel.add(toolbar, BorderLayout.NORTH); else textPanel.remove(toolbar); } String wrap; if (GeneralOptionPane.getPreviewWrap()) wrap = "soft"; else wrap = "none"; text.getBuffer().setProperty("wrap", wrap); EditPane.initPainter(text.getPainter()); } public void previewTag() { String name = null; try { name = CtagsInterfacePlugin.getDestinationTag(view); } catch (Exception e) { return; } if (name == null) return; VFSManager.runInWorkThread(new QueryTag(name)); } public void caretUpdate(CaretEvent e) { int delay = GeneralOptionPane.getPreviewDelay(); if (delay > 0) { if (timer == null) { timer = new Timer(delay, new ActionListener() { public void actionPerformed(ActionEvent arg0) { previewTag(); } }); timer.setRepeats(false); timer.start(); } else timer.restart(); } else previewTag(); } public void valueChanged(ListSelectionEvent e) { int index = tags.getSelectedIndex(); if (index < 0) return; Tag t = (Tag) tagModel.getElementAt(index); VFSManager.runInWorkThread(new PreviewBufferLoader(t)); } public void focusOnDefaultComponent() { tags.requestFocus(); } static public String getContents(String path) { StringBuffer contents = new StringBuffer(); String ret = null; BufferedReader input = null; try { input = new BufferedReader(new FileReader(path)); String line = null; while ((line = input.readLine()) != null) { contents.append(line); contents.append(System.getProperty("line.separator")); } ret = contents.toString(); } catch (IOException ex) { //ex.printStackTrace(); } finally { try { if (input!= null) input.close(); } catch (IOException ex) { //ex.printStackTrace(); } } return ret; } @EBHandler public void handlePropertiesChanged(PropertiesChanged msg) { propertiesChanged(); } @EBHandler public void handleViewUpdate(ViewUpdate msg) { if ((msg.getView() == view) && (msg.getWhat() == ViewUpdate.EDIT_PANE_CHANGED)) { updateCaretListenerState(); } } @EBHandler public void handleEditPaneUpdate(EditPaneUpdate msg) { if (msg.getWhat() == EditPaneUpdate.DESTROYED) { JEditTextArea textArea = msg.getEditPane().getTextArea(); if (tracking.contains(textArea)) setCaretTracking(textArea, false); } } private final class TagListCellRenderer extends DefaultListCellRenderer { //private Font tagListFont = new Font("Monospaced", Font.PLAIN, 12); public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { JLabel l = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); Tag tag = (Tag) tagModel.getElementAt(index); //l.setFont(tagListFont ); l.setText(getText(tag, false)); l.setToolTipText(getText(tag, true)); ImageIcon icon = tag.getIcon(); if (icon != null) l.setIcon(icon); return l; } /** * * @param tag * @param full if true, include full pathname (tooltip). if false, return short format (text) * @return */ String getText(Tag tag, boolean full) { StringBuffer s = new StringBuffer(); s.append(tag.getName()); String signature = tag.getExtension("signature"); if (signature != null && signature.length() > 0) s.append(signature); s.append(" "); int line = tag.getLine(); if (full) { s.append(tag.getFile()); if (line > -1) s.append(":" + line); } else { File f = new File(tag.getFile()); s.append(f.getName()); if (line > -1) s.append(":" + line); s.append(" (" + MiscUtilities.abbreviate(f.getParent()) + ")"); } return s.toString(); } } class PreviewTag implements Runnable { Vector<Tag> tags; public PreviewTag(Vector<Tag> tags) { this.tags = tags; } public void run() { tagModel.clear(); for (int i = 0; i < tags.size(); i++) tagModel.addElement(tags.get(i)); if (! tags.isEmpty()) Preview.this.tags.setSelectedIndex(0); } } class QueryTag implements Runnable { String name; public QueryTag(String name) { this.name = name; } public void run() { Vector<Tag> tags = CtagsInterfacePlugin.queryScopedTag(Preview.this.view, name); if (tags == null) return; VFSManager.runInAWTThread(new PreviewTag(tags)); } } class PreviewBufferLoader implements Runnable { Tag tag; public PreviewBufferLoader(Tag t) { tag = t; } public void run() { file = tag.getFile(); int line = tag.getLine(); if (line > -1) { String s = getContents(file); if (s != null) VFSManager.runInAWTThread(new PreviewBufferUpdate(s, line)); } } } class PreviewBufferUpdate implements Runnable { String s; int line; public PreviewBufferUpdate(String s, int line) { this.s = s; this.line = line; } public void run() { JEditBuffer buffer = text.getBuffer(); buffer.setReadOnly(false); text.setText(s); Mode mode = ModeProvider.instance.getModeForFile(file, buffer.getLineText(0)); if (mode == null) mode = ModeProvider.instance.getMode("text"); buffer.setMode(mode); text.scrollTo(line, 0, true); text.setCaretPosition(text.getLineStartOffset(line - 1)); buffer.setReadOnly(true); } } private class PreviewTextArea extends JEditEmbeddedTextArea { @Override public void createPopupMenu(MouseEvent evt) { // Create a context menu for the text area popup = new JPopupMenu(); String sel = getSelectedText(); if (sel != null && sel.length() > 0) { JMenuItem copyAction = new JMenuItem( jEdit.getProperty(MESSAGE + "copyPreviewSelection")); copyAction.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { copyPreviewSelection(); } }); popup.add(copyAction); } JMenuItem jumpAction = new JMenuItem( jEdit.getProperty(MESSAGE + "openInEditor")); jumpAction.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { CtagsInterfacePlugin.jumpToOffset(Preview.this.view, file, getCaretPosition()); } }); popup.add(jumpAction); } } }