1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package com.atlassian.theplugin.idea;
18
19 import com.atlassian.theplugin.commons.crucible.ValueNotYetInitialized;
20 import com.atlassian.theplugin.commons.crucible.api.model.Comment;
21 import com.atlassian.theplugin.commons.crucible.api.model.CrucibleFileInfo;
22 import com.atlassian.theplugin.commons.crucible.api.model.GeneralComment;
23 import com.atlassian.theplugin.commons.crucible.api.model.VersionedComment;
24 import com.atlassian.theplugin.idea.crucible.CrucibleFilteredModelProvider;
25 import com.atlassian.theplugin.idea.crucible.CrucibleHelper;
26 import com.atlassian.theplugin.idea.crucible.ReviewData;
27 import com.atlassian.theplugin.idea.crucible.comments.CrucibleReviewActionListener;
28 import com.atlassian.theplugin.idea.crucible.events.*;
29 import com.atlassian.theplugin.idea.ui.AtlassianToolbar;
30 import com.atlassian.theplugin.idea.ui.tree.*;
31 import com.atlassian.theplugin.idea.ui.tree.comment.FileNameNode;
32 import com.atlassian.theplugin.idea.ui.tree.comment.GeneralCommentTreeNode;
33 import com.atlassian.theplugin.idea.ui.tree.comment.GeneralSectionNode;
34 import com.atlassian.theplugin.idea.ui.tree.comment.VersionedCommentTreeNode;
35 import com.atlassian.theplugin.idea.ui.tree.file.FolderNode;
36 import com.atlassian.theplugin.util.PluginUtil;
37 import com.intellij.openapi.actionSystem.ActionGroup;
38 import com.intellij.openapi.actionSystem.ActionManager;
39 import com.intellij.openapi.application.ApplicationManager;
40 import com.intellij.openapi.editor.Editor;
41 import com.intellij.openapi.project.Project;
42 import com.intellij.util.ui.UIUtil;
43
44 import javax.swing.*;
45 import javax.swing.tree.TreePath;
46 import java.awt.*;
47 import java.awt.event.MouseAdapter;
48 import java.awt.event.MouseEvent;
49 import java.util.List;
50
51
52
53
54
55
56
57
58 public class CommentTreePanel extends JPanel {
59 private CrucibleReviewActionListener crucibleAgent = new MyCrucibleReviewActionListener();
60 private JScrollPane commentScroll;
61 private ProgressAnimationProvider progressAnimation = new ProgressAnimationProvider();
62 private CommentTree commentTree = new CommentTree();
63
64 private AtlassianTreeModel fullModel;
65
66 public static final AtlassianTreeNode ROOT = new FolderNode("/", AtlassianClickAction.EMPTY_ACTION);
67 private Project project;
68 private static final String TOOLBAR_ID = "ThePlugin.Crucible.Comment.ToolBar";
69 public static final String MENU_PLACE = "menu comments";
70 private static final String TOOLBAR_PLACE = "toolbar comments";
71 private CrucibleFilteredModelProvider.FILTER filter;
72
73 public CommentTreePanel(Project project, CrucibleFilteredModelProvider.FILTER filter) {
74 this.project = project;
75 this.filter = filter;
76 IdeaHelper.getReviewActionEventBroker(project).registerListener(crucibleAgent);
77 initialize();
78 }
79
80 private void initialize() {
81 setLayout(new BorderLayout());
82 setBackground(UIUtil.getTreeTextBackground());
83 progressAnimation.configure(this, commentScroll, BorderLayout.CENTER);
84 commentScroll = new JScrollPane();
85 add(AtlassianToolbar.createToolbar(TOOLBAR_PLACE, TOOLBAR_ID), BorderLayout.NORTH);
86 add(commentScroll, BorderLayout.CENTER);
87 }
88
89 private void addGeneralCommentTree(AtlassianTreeNode root, final ReviewData review,
90 GeneralComment generalComment, int depth) {
91 if (generalComment.isDeleted()) {
92 return;
93 }
94 GeneralCommentTreeNode commentNode
95 = new GeneralCommentTreeNode(review, generalComment, new GeneralCommentClickAction());
96 root.addNode(commentNode);
97 for (GeneralComment comment : generalComment.getReplies()) {
98 addGeneralCommentTree(commentNode, review, comment, depth + 1);
99 }
100
101
102 }
103
104 private void addVersionedCommentTree(AtlassianTreeNode root, final ReviewData review,
105 final CrucibleFileInfo file, VersionedComment versionedComment, int depth) {
106 if (versionedComment.isDeleted()) {
107 return;
108 }
109 VersionedCommentTreeNode commentNode = new VersionedCommentTreeNode(review, file, versionedComment,
110 new VersionedCommentClickAction());
111 root.addNode(commentNode);
112 for (VersionedComment comment : versionedComment.getReplies()) {
113 addVersionedCommentTree(commentNode, review, file, comment, depth + 1);
114 }
115 }
116
117
118 private AtlassianTreeModel createTreeModel(final ReviewData review) {
119 ROOT.removeAllChildren();
120 AtlassianTreeModel model = new AtlassianTreeModel(ROOT);
121
122 List<GeneralComment> generalComments;
123 try {
124 generalComments = review.getGeneralComments();
125 AtlassianTreeNode generalNode = new GeneralSectionNode(review, new AtlassianClickAction() {
126 public void execute(final AtlassianTreeNode node, final int noOfClicks) {
127 switch (noOfClicks) {
128 case 1:
129 GeneralSectionNode anode = (GeneralSectionNode) node;
130 IdeaHelper.getReviewActionEventBroker(project).trigger(
131 new FocusOnReviewEvent(crucibleAgent, anode.getReview()));
132 break;
133 default:
134
135 }
136
137 }
138 });
139 ROOT.addNode(generalNode);
140 for (GeneralComment comment : generalComments) {
141 addGeneralCommentTree(generalNode, review, comment, 0);
142 }
143 for (CrucibleFileInfo file : review.getFiles()) {
144 AtlassianTreeNode fileNode = new FileNameNode(review, file, new AtlassianClickAction() {
145 public void execute(final AtlassianTreeNode node, final int noOfClicks) {
146 switch (noOfClicks) {
147 case 1:
148 FileNameNode anode = (FileNameNode) node;
149 IdeaHelper.getReviewActionEventBroker(project).trigger(
150 new FocusOnFileEvent(crucibleAgent, anode.getReview(), anode.getFile()));
151 break;
152 default:
153
154 }
155
156 }
157 });
158 ROOT.addNode(fileNode);
159 for (VersionedComment comment : file.getVersionedComments()) {
160 addVersionedCommentTree(fileNode, review, file, comment, 0);
161 }
162
163 }
164 } catch (ValueNotYetInitialized valueNotYetInitialized) {
165 PluginUtil.getLogger().error(valueNotYetInitialized.getMessage());
166 }
167 return model;
168 }
169
170 public CommentTree getCommentTree() {
171 return commentTree;
172 }
173
174 public void filterTreeNodes(CrucibleFilteredModelProvider.FILTER aFilter) {
175 this.filter = aFilter;
176 commentTree.setModel(fullModel.getFilteredModel(getFilter(aFilter)));
177 refreshTree();
178 }
179
180 private Filter getFilter(final CrucibleFilteredModelProvider.FILTER aFilter) {
181 switch (aFilter) {
182 case FILES_ALL:
183 return Filter.ALL;
184 case FILES_WITH_COMMENTS_ONLY:
185 return new Filter() {
186 @Override
187 public boolean isValid(final AtlassianTreeNode node) {
188 if (node instanceof FileNameNode) {
189 FileNameNode anode = (FileNameNode) node;
190 try {
191 return anode.getFile().getNumberOfComments() > 0;
192 } catch (ValueNotYetInitialized valueNotYetInitialized) {
193 return false;
194 }
195 }
196 return true;
197 }
198 };
199 default:
200 throw new IllegalStateException("Unknow filtering requested (" + aFilter.toString() + ")");
201 }
202 }
203
204 private void refreshTree() {
205 commentTree.setRootVisible(false);
206 commentTree.expandAll();
207 commentScroll.setViewportView(commentTree);
208 commentTree.initializeUI();
209 commentTree.setVisible(true);
210 commentTree.setEnabled(true);
211 commentTree.revalidate();
212 commentTree.repaint();
213 commentTree.addMouseListener(new PopupMouseAdapter());
214 }
215
216 private class MyCrucibleReviewActionListener extends CrucibleReviewActionListener {
217
218 @Override
219 public void showReview(final ReviewData review) {
220 EventQueue.invokeLater(new Runnable() {
221 public void run() {
222 commentTree.setVisible(false);
223 fullModel = createTreeModel(review);
224 commentTree = new CommentTree(fullModel.getFilteredModel(getFilter(filter)));
225 refreshTree();
226 }
227 });
228 }
229
230 @Override
231 public void focusOnGeneralComments(final ReviewData review) {
232 EventQueue.invokeLater(new Runnable() {
233 public void run() {
234 AtlassianTreeModel model = (AtlassianTreeModel) commentTree.getModel();
235 AtlassianTreeNode node = model.locateNode(new SearchGeneralSectionAlgorithm());
236 commentTree.focusOnNode(node);
237 }
238 });
239 }
240
241 @Override
242 public void createdGeneralComment(final ReviewData review, final GeneralComment comment) {
243 EventQueue.invokeLater(new Runnable() {
244 public void run() {
245 AtlassianTreeNode newCommentNode
246 = new GeneralCommentTreeNode(review, comment, new GeneralCommentClickAction());
247
248 SearchGeneralCommentAlgorithm replacementLocator = new SearchGeneralCommentAlgorithm(review, comment);
249 AtlassianTreeNode changedNode = replaceNode(replacementLocator,
250 newCommentNode);
251 if (changedNode == null) {
252 SearchGeneralSectionAlgorithm parentLocator = new SearchGeneralSectionAlgorithm();
253 changedNode = addNewNode(parentLocator, newCommentNode);
254 }
255 addReplyNodes(review, newCommentNode, comment);
256 refreshNode(changedNode);
257 }
258 }
259 );
260 }
261
262 private void refreshNode(final AtlassianTreeNode node) {
263 if (node == null) {
264 return;
265 }
266 commentTree.expandFromNode(node);
267 ((AtlassianTreeModel) commentTree.getModel()).nodeChanged(node.getParent());
268 }
269
270 @Override
271 public void createdGeneralCommentReply(final ReviewData review, final GeneralComment parentComment,
272 final GeneralComment comment) {
273 EventQueue.invokeLater(new Runnable() {
274 public void run() {
275 GeneralCommentTreeNode newCommentNode = new GeneralCommentTreeNode(review, comment,
276 AtlassianClickAction.EMPTY_ACTION);
277 AtlassianTreeNode changedNode = replaceNode(new SearchGeneralCommentAlgorithm(review, comment),
278 newCommentNode);
279 if (changedNode == null) {
280 changedNode = addNewNode(new SearchGeneralCommentAlgorithm(review, parentComment), newCommentNode);
281 }
282 addReplyNodes(review, newCommentNode, comment);
283 refreshNode(changedNode);
284 }
285 }
286 );
287 }
288
289 @Override
290 public void createdVersionedComment(final ReviewData review, final CrucibleFileInfo file,
291 final VersionedComment comment) {
292 EventQueue.invokeLater(new Runnable() {
293 public void run() {
294
295 AtlassianTreeNode newCommentNode
296 = new VersionedCommentTreeNode(review, file, comment, new VersionedCommentClickAction());
297
298 AtlassianTreeNode changedNode = replaceNode(new SearchVersionedCommentAlgorithm(review, file, comment),
299 newCommentNode);
300 if (changedNode == null) {
301 changedNode = addNewNode(new NodeSearchAlgorithm() {
302 @Override
303 public boolean check(AtlassianTreeNode node) {
304 if (node instanceof FileNameNode) {
305 FileNameNode vnode = (FileNameNode) node;
306 if (vnode.getReview().getPermId().equals(review.getPermId())
307 && vnode.getFile().getPermId().equals(file.getPermId())) {
308 return true;
309 }
310 }
311 return false;
312 }
313 }, newCommentNode);
314 }
315 addReplyNodes(review, file, newCommentNode, comment);
316 refreshNode(changedNode);
317
318 Editor editor = CrucibleHelper.getEditorForCrucibleFile(review, file);
319 if (editor != null) {
320 CrucibleHelper.openFileOnComment(project, review, file, comment);
321 }
322 }
323 });
324 }
325
326 @Override
327 public void createdVersionedCommentReply(final ReviewData review, final CrucibleFileInfo file,
328 final VersionedComment parentComment, final VersionedComment comment) {
329
330 EventQueue.invokeLater(new Runnable() {
331 public void run() {
332 VersionedCommentTreeNode newCommentNode = new VersionedCommentTreeNode(review, file, comment,
333 AtlassianClickAction.EMPTY_ACTION);
334 AtlassianTreeNode changedNode = replaceNode(new SearchVersionedCommentAlgorithm(review, file, comment),
335 newCommentNode);
336 if (changedNode == null) {
337 changedNode = addNewNode(new SearchVersionedCommentAlgorithm(review, file, parentComment),
338 newCommentNode);
339 }
340 addReplyNodes(review, file, newCommentNode, comment);
341 refreshNode(changedNode);
342 }
343 });
344 }
345
346 @Override
347 public void updatedVersionedComment(final ReviewData review, final CrucibleFileInfo file,
348 final VersionedComment comment) {
349 EventQueue.invokeLater(new Runnable() {
350 public void run() {
351 VersionedCommentTreeNode newCommentNode = new VersionedCommentTreeNode(review, file, comment,
352 AtlassianClickAction.EMPTY_ACTION);
353 AtlassianTreeNode changedNode = replaceNode(new SearchVersionedCommentAlgorithm(review, file, comment),
354 newCommentNode);
355 refreshNode(changedNode);
356 }
357 });
358 }
359
360 @Override
361 public void updatedGeneralComment(final ReviewData review, final GeneralComment comment) {
362 EventQueue.invokeLater(new Runnable() {
363 public void run() {
364 GeneralCommentTreeNode newCommentNode = new GeneralCommentTreeNode(review, comment,
365 AtlassianClickAction.EMPTY_ACTION);
366 AtlassianTreeNode changedNode = replaceNode(new SearchGeneralCommentAlgorithm(review, comment),
367 newCommentNode);
368 refreshNode(changedNode);
369 }
370 });
371 }
372
373 @Override
374 public void removedComment(final ReviewData review, final Comment comment) {
375 EventQueue.invokeLater(new Runnable() {
376 public void run() {
377 removeNode(new NodeSearchAlgorithm() {
378 @Override
379 public boolean check(AtlassianTreeNode node) {
380 if (node instanceof VersionedCommentTreeNode) {
381 VersionedCommentTreeNode vnode = (VersionedCommentTreeNode) node;
382 if (vnode.getReview().getPermId().equals(review.getPermId())
383 && vnode.getComment().getPermId().equals(comment.getPermId())) {
384 return true;
385 }
386 } else if (node instanceof GeneralCommentTreeNode) {
387 GeneralCommentTreeNode vnode = (GeneralCommentTreeNode) node;
388 if (vnode.getReview().getPermId().equals(review.getPermId())
389 && vnode.getComment().getPermId().equals(comment.getPermId())) {
390 return true;
391 }
392 }
393 return false;
394 }
395 });
396 }
397 }
398 );
399
400 }
401
402 @Override
403 public void publishedGeneralComment(final ReviewData review, final GeneralComment comment) {
404 EventQueue.invokeLater(new Runnable() {
405 public void run() {
406 GeneralCommentTreeNode newCommentNode = new GeneralCommentTreeNode(review, comment,
407 AtlassianClickAction.EMPTY_ACTION);
408 AtlassianTreeNode changedNode = replaceNode(new SearchGeneralCommentAlgorithm(review, comment),
409 newCommentNode);
410 refreshNode(changedNode);
411 }
412 }
413 );
414 }
415
416 @Override
417 public void publishedVersionedComment(final ReviewData review, final CrucibleFileInfo file,
418 final VersionedComment comment) {
419 EventQueue.invokeLater(new Runnable() {
420 public void run() {
421 VersionedCommentTreeNode newCommentNode = new VersionedCommentTreeNode(review, file, comment,
422 AtlassianClickAction.EMPTY_ACTION);
423 AtlassianTreeNode changedNode = replaceNode(new SearchVersionedCommentAlgorithm(review, file, comment),
424 newCommentNode);
425 refreshNode(changedNode);
426 }
427
428 }
429 );
430 }
431
432 public void commentsChanged(final ReviewData review, final CrucibleFileInfo file) {
433 ApplicationManager.getApplication().invokeLater(new Runnable() {
434 public void run() {
435 }
436 });
437 }
438
439 @Override
440 public void focusOnFileComments(final ReviewData review, final CrucibleFileInfo file) {
441 EventQueue.invokeLater(new Runnable() {
442 public void run() {
443 AtlassianTreeModel model = (AtlassianTreeModel) commentTree.getModel();
444 AtlassianTreeNode node = model.locateNode(new NodeSearchAlgorithm() {
445 public boolean check(AtlassianTreeNode node) {
446 if (node instanceof FileNameNode) {
447 FileNameNode vnode = (FileNameNode) node;
448 if (vnode.getReview().equals(review)
449 && vnode.getFile().equals(file)) {
450 return true;
451 }
452 }
453 return false;
454 }
455 });
456 commentTree.focusOnNode(node);
457 }
458 });
459 }
460
461 private void removeNode(final NodeSearchAlgorithm nodeLocator) {
462 AtlassianTreeModel model = (AtlassianTreeModel) commentTree.getModel();
463 AtlassianTreeNode node = model.locateNode(nodeLocator);
464 if (node != null) {
465 model.removeNodeFromParent(node);
466 }
467 }
468
469 private AtlassianTreeNode replaceNode(final NodeSearchAlgorithm nodeLocator,
470 final AtlassianTreeNode newNode) {
471 AtlassianTreeModel model = (AtlassianTreeModel) commentTree.getModel();
472 AtlassianTreeNode node = model.locateNode(nodeLocator);
473 if (node != null) {
474 AtlassianTreeNode parent = (AtlassianTreeNode) node.getParent();
475 parent.remove(node);
476 parent.addNode(newNode);
477 return newNode;
478 }
479 return null;
480 }
481
482 private void addReplyNodes(final ReviewData review, final AtlassianTreeNode parentNode, final GeneralComment comment) {
483 for (GeneralComment reply : comment.getReplies()) {
484 GeneralCommentTreeNode childNode = new GeneralCommentTreeNode(review, reply, AtlassianClickAction.EMPTY_ACTION);
485 addNewNode(parentNode, childNode);
486 addReplyNodes(review, childNode, reply);
487 }
488 }
489
490 private void addReplyNodes(final ReviewData review, final CrucibleFileInfo file, final AtlassianTreeNode parentNode,
491 final VersionedComment comment) {
492 for (VersionedComment reply : comment.getReplies()) {
493 VersionedCommentTreeNode childNode = new VersionedCommentTreeNode(review, file, reply,
494 AtlassianClickAction.EMPTY_ACTION);
495 addNewNode(parentNode, childNode);
496 addReplyNodes(review, file, childNode, reply);
497 }
498
499 }
500
501 private AtlassianTreeNode addNewNode(NodeSearchAlgorithm parentLocator, AtlassianTreeNode newCommentNode) {
502 AtlassianTreeModel model = (AtlassianTreeModel) commentTree.getModel();
503 return addNewNode(model.locateNode(parentLocator), newCommentNode);
504 }
505
506 private AtlassianTreeNode addNewNode(AtlassianTreeNode parentNode, AtlassianTreeNode newCommentNode) {
507 if (parentNode != null) {
508 AtlassianTreeModel model = (AtlassianTreeModel) commentTree.getModel();
509 model.insertNodeInto(newCommentNode, parentNode, parentNode.getChildCount());
510 parentNode.addNode(newCommentNode);
511 commentTree.expandPath(new TreePath(newCommentNode.getPath()));
512 commentTree.expandPath(new TreePath(parentNode.getPath()));
513 commentTree.focusOnNode(newCommentNode);
514 return parentNode;
515 }
516 return null;
517 }
518
519 private class SearchVersionedCommentAlgorithm extends NodeSearchAlgorithm {
520 private final ReviewData review;
521 private final CrucibleFileInfo file;
522 private final VersionedComment parentComment;
523
524 public SearchVersionedCommentAlgorithm(final ReviewData review, final CrucibleFileInfo file,
525 final VersionedComment parentComment) {
526 this.review = review;
527 this.file = file;
528 this.parentComment = parentComment;
529 }
530
531 @Override
532 public boolean check(AtlassianTreeNode node) {
533 if (node instanceof VersionedCommentTreeNode) {
534 VersionedCommentTreeNode vnode = (VersionedCommentTreeNode) node;
535 if (vnode.getReview().getPermId().equals(review.getPermId())
536 && vnode.getFile().getPermId().equals(file.getPermId())
537 && vnode.getComment().getPermId().equals(parentComment.getPermId())) {
538 return true;
539 }
540 }
541 return false;
542 }
543 }
544
545 private class SearchGeneralCommentAlgorithm extends NodeSearchAlgorithm {
546 private final ReviewData review;
547 private final GeneralComment comment;
548
549 public SearchGeneralCommentAlgorithm(final ReviewData review, final GeneralComment comment) {
550 this.review = review;
551 this.comment = comment;
552 }
553
554 @Override
555 public boolean check(AtlassianTreeNode node) {
556 if (node instanceof GeneralCommentTreeNode) {
557 GeneralCommentTreeNode vnode = (GeneralCommentTreeNode) node;
558 if (vnode.getReview().getPermId().equals(review.getPermId())
559 && vnode.getComment().getPermId().equals(comment.getPermId())) {
560 return true;
561 }
562 }
563 return false;
564 }
565 }
566
567 private class SearchGeneralSectionAlgorithm extends NodeSearchAlgorithm {
568 @Override
569 public boolean check(AtlassianTreeNode node) {
570 return node instanceof GeneralSectionNode;
571 }
572 }
573 }
574
575 private class PopupMouseAdapter extends MouseAdapter {
576
577 @Override
578 public void mousePressed(MouseEvent e) {
579 processPopup(e);
580 }
581
582 @Override
583 public void mouseReleased(MouseEvent e) {
584 processPopup(e);
585 }
586
587 public void processPopup(MouseEvent e) {
588 if (!e.isPopupTrigger()) {
589 return;
590 }
591
592 final CommentTree tree = (CommentTree) e.getComponent();
593
594 TreePath path = tree.getPathForLocation(e.getX(), e.getY());
595 if (path == null) {
596 return;
597 }
598 tree.setSelectionPath(path);
599 Object o = path.getLastPathComponent();
600 if (!(o instanceof AtlassianTreeNode)) {
601 return;
602 }
603 ActionManager aManager = ActionManager.getInstance();
604 ActionGroup menu = (ActionGroup) aManager.getAction(TOOLBAR_ID);
605 if (menu == null) {
606 return;
607 }
608 aManager.createActionPopupMenu(MENU_PLACE, menu).getComponent().show(e.getComponent(), e.getX(), e.getY());
609 }
610 }
611
612 private class VersionedCommentClickAction implements AtlassianClickAction {
613 public void execute(final AtlassianTreeNode node, final int noOfClicks) {
614 VersionedCommentTreeNode anode = (VersionedCommentTreeNode) node;
615 CrucibleEvent event;
616 switch (noOfClicks) {
617 case 1:
618 if (anode.getComment().isFromLineInfo()
619 || anode.getComment().isToLineInfo()) {
620 event = new FocusOnLineCommentEvent(crucibleAgent,
621 anode.getReview(),
622 anode.getFile(), anode.getComment(), false);
623 } else {
624 event = new FocusOnVersionedCommentEvent(crucibleAgent,
625 anode.getReview(),
626 anode.getFile(), anode.getComment());
627 }
628 IdeaHelper.getReviewActionEventBroker(project).trigger(
629 event);
630 break;
631 case 2:
632 if (anode.getComment().isFromLineInfo()
633 || anode.getComment().isToLineInfo()) {
634 event = new FocusOnLineCommentEvent(crucibleAgent,
635 anode.getReview(),
636 anode.getFile(), anode.getComment(), true);
637 } else {
638 event = new FocusOnVersionedCommentEvent(crucibleAgent,
639 anode.getReview(),
640 anode.getFile(), anode.getComment());
641 }
642 IdeaHelper.getReviewActionEventBroker(project).trigger(
643 event);
644 break;
645 default:
646
647 }
648
649 }
650 }
651
652 private class GeneralCommentClickAction implements AtlassianClickAction {
653 public void execute(final AtlassianTreeNode node, final int noOfClicks) {
654 switch (noOfClicks) {
655 case 1:
656 GeneralCommentTreeNode anode = (GeneralCommentTreeNode) node;
657 IdeaHelper.getReviewActionEventBroker(project).trigger(
658 new FocusOnReviewEvent(crucibleAgent, anode.getReview()));
659 break;
660 default:
661
662 }
663
664 }
665 }
666 }