package com.jetbrains.crucible.ui.toolWindow.details; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.actionSystem.DefaultActionGroup; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.openapi.vcs.FilePath; import com.intellij.openapi.vcs.changes.issueLinks.LinkMouseListenerBase; import com.intellij.ui.PopupHandler; import com.intellij.ui.treeStructure.Tree; import com.intellij.util.ui.tree.TreeUtil; import com.jetbrains.crucible.actions.AddCommentAction; import com.jetbrains.crucible.model.Comment; import com.jetbrains.crucible.model.Review; import com.jetbrains.crucible.utils.CrucibleBundle; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.swing.*; import javax.swing.tree.*; import java.awt.*; import java.awt.event.MouseEvent; /** * User: ktisha */ public abstract class CommentsTree extends Tree { private static final Logger LOG = Logger.getInstance(CommentsTree.class); @NotNull protected final Review myReview; protected CommentsTree(@NotNull final Project project, @NotNull final Review review, @NotNull DefaultTreeModel model, @Nullable final Editor editor, @Nullable final FilePath filePath) { super(model); myReview = review; setExpandableItemsEnabled(false); setRowHeight(0); final CommentNodeRenderer renderer = new CommentNodeRenderer(this, review, project); setCellRenderer(renderer); getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION); final DefaultActionGroup group = new DefaultActionGroup(); final AddCommentAction replyToComment = new AddCommentAction(review, editor, filePath, CrucibleBundle.message("crucible.add.reply"), true); replyToComment.setContextComponent(this); group.add(replyToComment); PopupHandler.installUnknownPopupHandler(this, group, ActionManager.getInstance()); TreeUtil.expandAll(this); new MyLinkMouseListener(renderer, project, review).installOn(this); } protected static void addReplies(@NotNull Comment comment, @NotNull DefaultMutableTreeNode parentNode) { for (Comment reply : comment.getReplies()) { DefaultMutableTreeNode node = new DefaultMutableTreeNode(reply); parentNode.add(node); addReplies(reply, node); } } @Nullable public Comment getSelectedComment() { Object selected = getLastSelectedPathComponent(); if (selected == null) { return null; } assert selected instanceof DefaultMutableTreeNode; Object userObject = ((DefaultMutableTreeNode)selected).getUserObject(); assert userObject instanceof Comment; return (Comment)userObject; } public abstract void refresh(); private class MyLinkMouseListener extends LinkMouseListenerBase { private final CommentNodeRenderer myRenderer; private final Project myProject; private final Review myReview; public MyLinkMouseListener(CommentNodeRenderer renderer, Project project, Review review) { myRenderer = renderer; myProject = project; myReview = review; } @Override protected void handleTagClick(Object tag, MouseEvent event) { if (tag == null) { return; } CommentAction action = (CommentAction)tag; action.execute(new Runnable() { @Override public void run() { refresh(); } }); } @Nullable @Override protected Object getTagAt(MouseEvent e) { JTree tree = (JTree) e.getSource(); final TreePath path = tree.getPathForLocation(e.getX(), e.getY()); if (path == null) { return null; } Rectangle bounds = tree.getPathBounds(path); if (bounds == null) { return null; } int dx = e.getX() - bounds.x; int dy = e.getY() - bounds.y; CommentAction.Type linkType = myRenderer.getActionLink(dx, dy); if (linkType == null) { return null; } DefaultMutableTreeNode treeNode = (DefaultMutableTreeNode)path.getLastPathComponent(); Comment comment = (Comment)treeNode.getUserObject(); return linkType.createAction(myProject, myReview, comment); } } }