/* * Copyright 2015 Ritesh Kapoor * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.ritesh.idea.plugin.ui.diff; import com.intellij.openapi.diff.DiffRequest; import com.intellij.openapi.diff.impl.DiffPanelImpl; import com.intellij.openapi.diff.impl.DiffUtil; import com.intellij.openapi.diff.impl.external.FrameDiffTool; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.event.EditorMouseAdapter; import com.intellij.openapi.editor.event.EditorMouseEvent; import com.intellij.openapi.editor.event.EditorMouseEventArea; import com.intellij.openapi.editor.ex.util.EditorUtil; import com.intellij.openapi.editor.markup.HighlighterLayer; import com.intellij.openapi.editor.markup.MarkupModel; import com.intellij.openapi.editor.markup.RangeHighlighter; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.openapi.ui.FrameWrapper; import com.intellij.openapi.ui.popup.JBPopup; import com.intellij.openapi.ui.popup.JBPopupFactory; import com.intellij.ui.JBColor; import com.intellij.ui.awt.RelativePoint; import com.ritesh.idea.plugin.reviewboard.Review; import com.ritesh.idea.plugin.reviewboard.Review.File.Comment; import com.ritesh.idea.plugin.ui.panels.CommentPanel; import com.ritesh.idea.plugin.ui.panels.CommentsListViewPanel; import java.awt.Component; import java.awt.Point; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.swing.JList; import javax.swing.ListCellRenderer; /** * @author Ritesh */ public class CommentsDiffTool extends FrameDiffTool { private Review.File file; private List<Comment> comments; private List<RangeHighlighter> newCommentHighlighters = new ArrayList<>(); private ListCellRenderer<Comment> listCellRenderer; private ActionListener actionListener; public CommentsDiffTool(Review.File file, List<Comment> comments) { this.file = file; this.comments = new ArrayList<>(comments); this.listCellRenderer = new ListCellRenderer<Comment>() { @Override public Component getListCellRendererComponent(JList list, final Comment value, final int index, boolean isSelected, boolean cellHasFocus) { return new CommentPanel(value.user, value.text, value.timestamp).getPanel(); } }; } @Override public void show(DiffRequest request) { final FrameWrapper frameWrapper = new FrameWrapper(request.getProject(), request.getGroupKey()); final DiffPanelImpl diffPanel = createDiffPanelImpl(request, frameWrapper.getFrame(), frameWrapper); final Editor editor = diffPanel.getEditor2(); updateHighLights(editor); editor.addEditorMouseListener(new EditorMouseAdapter() { @Override public void mouseClicked(EditorMouseEvent e) { if (e.getArea() != null && e.getArea().equals(EditorMouseEventArea.LINE_MARKERS_AREA)) { final Point locationOnScreen = e.getMouseEvent().getLocationOnScreen(); final int lineNumber = EditorUtil.yPositionToLogicalLine(editor, e.getMouseEvent()) + 1; showCommentsView(locationOnScreen, lineNumber, editor, e); } } }); DiffUtil.initDiffFrame(request.getProject(), frameWrapper, diffPanel, diffPanel.getComponent()); frameWrapper.setTitle(request.getWindowTitle()); frameWrapper.setPreferredFocusedComponent(diffPanel.getComponent()); frameWrapper.show(); } public void setActionListener(ActionListener actionListener) { this.actionListener = actionListener; } private void showCommentsView(Point locationOnScreen, final int lineNumber, final Editor editor, EditorMouseEvent mouseEvent) { List<Comment> comments = lineComments(CommentsDiffTool.this.comments).get(lineNumber); final CommentsListViewPanel<Comment> commentsListViewPanel = new CommentsListViewPanel(comments, listCellRenderer); final JBPopup popup = JBPopupFactory.getInstance().createComponentPopupBuilder(commentsListViewPanel, null) .setTitle("Comment") .setMovable(true) .setRequestFocus(true) .setCancelOnWindowDeactivation(false) .setAdText("Hit Ctrl+Enter to add comment & double click comment to delete.") .setResizable(true) .createPopup(); popup.show(new RelativePoint(mouseEvent.getMouseEvent())); commentsListViewPanel.setListener(new CommentsListViewPanel.CommentListener<Comment>() { @Override public void onAdd(String value, boolean issueOpened) { popup.dispose(); Comment newComment = new Comment(); newComment.text = value; newComment.firstLine = lineNumber; newComment.numberOfLines = 1; newComment.file = file; newComment.issueOpened = issueOpened; CommentsDiffTool.this.comments.add(newComment); updateHighLights(editor); actionListener.actionPerformed(new ActionEvent(this, 0, null)); } @Override public void onDelete(Comment value) { if (value != null && value.id == null) { CommentsDiffTool.this.comments.remove(value); updateHighLights(editor); } popup.dispose(); } }); } private void updateHighLights(Editor editor) { MarkupModel markup = editor.getMarkupModel(); for (RangeHighlighter customRangeHighlighter : newCommentHighlighters) { markup.removeHighlighter(customRangeHighlighter); } newCommentHighlighters.clear(); int lineCount = markup.getDocument().getLineCount(); Map<Integer, List<Comment>> lineComments = lineComments(comments); for (Map.Entry<Integer, List<Comment>> entry : lineComments.entrySet()) { if (entry.getKey() > lineCount) continue; boolean hasNewComments = false; for (Comment comment : entry.getValue()) { if (comment.id == null) { hasNewComments = true; break; } } TextAttributes attributes = new TextAttributes(); if (hasNewComments) attributes.setBackgroundColor(JBColor.PINK); else attributes.setBackgroundColor(JBColor.YELLOW); RangeHighlighter rangeHighlighter = markup .addLineHighlighter(entry.getKey() - 1, HighlighterLayer.SELECTION + (hasNewComments ? 2 : 1), attributes); rangeHighlighter.setGutterIconRenderer(new CommentGutterIconRenderer()); newCommentHighlighters.add(rangeHighlighter); } } private Map<Integer, List<Comment>> lineComments(List<Comment> comments) { Map<Integer, List<Comment>> result = new HashMap<>(); for (Comment comment : comments) { if (result.get(comment.firstLine) == null) { result.put(comment.firstLine, new ArrayList<Comment>()); } result.get(comment.firstLine).add(comment); } return result; } public List<Comment> getNewComments() { final List<Review.File.Comment> newComments = new ArrayList<>(); for (Comment comment : comments) { if (comment.id == null) newComments.add(comment); } return newComments; } }