View Javadoc

1   package com.atlassian.theplugin.idea.crucible.tree;
2   /**
3    * Copyright (C) 2008 Atlassian
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License");
6    * you may not use this file except in compliance with the License.
7    * You may obtain a copy of the License at
8    *
9    *    http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  import com.atlassian.theplugin.commons.crucible.CrucibleServerFacadeImpl;
19  import com.atlassian.theplugin.commons.crucible.ValueNotYetInitialized;
20  import com.atlassian.theplugin.commons.crucible.api.model.CrucibleFileInfo;
21  import com.atlassian.theplugin.commons.crucible.api.model.Reviewer;
22  import com.atlassian.theplugin.commons.crucible.api.model.VersionedComment;
23  import com.atlassian.theplugin.commons.exception.ServerPasswordNotProvidedException;
24  import com.atlassian.theplugin.commons.remoteapi.RemoteApiException;
25  import com.atlassian.theplugin.commons.util.Logger;
26  import com.atlassian.theplugin.idea.IdeaHelper;
27  import com.atlassian.theplugin.idea.ProgressAnimationProvider;
28  import com.atlassian.theplugin.idea.crucible.CrucibleConstants;
29  import com.atlassian.theplugin.idea.crucible.CrucibleFilteredModelProvider;
30  import com.atlassian.theplugin.idea.crucible.ReviewData;
31  import com.atlassian.theplugin.idea.crucible.comments.CrucibleReviewActionListener;
32  import com.atlassian.theplugin.idea.ui.tree.AtlassianTreeModel;
33  import com.atlassian.theplugin.idea.ui.tree.AtlassianTreeNode;
34  import com.atlassian.theplugin.idea.ui.tree.Filter;
35  import com.atlassian.theplugin.idea.ui.tree.NodeSearchAlgorithm;
36  import com.atlassian.theplugin.idea.ui.tree.file.CrucibleChangeSetTitleNode;
37  import com.atlassian.theplugin.idea.ui.tree.file.CrucibleFileNode;
38  import com.atlassian.theplugin.idea.ui.tree.file.FileTreeModelBuilder;
39  import com.atlassian.theplugin.util.PluginUtil;
40  import com.intellij.openapi.application.ApplicationManager;
41  import com.intellij.openapi.project.Project;
42  import com.intellij.util.ui.UIUtil;
43  
44  import javax.swing.*;
45  import java.awt.*;
46  import java.util.List;
47  
48  public final class ReviewItemTreePanel extends JPanel {
49  
50  	//	ProjectView.
51  	private AtlassianTreeWithToolbar reviewFilesTree = null;
52  	private static final int WIDTH = 150;
53  	private static final int HEIGHT = 250;
54  
55  	public static final Logger LOGGER = PluginUtil.getLogger();
56  
57  	private ProgressAnimationProvider progressAnimation = new ProgressAnimationProvider();
58  	private JLabel statusLabel;
59  	private CrucibleFilteredModelProvider.FILTER filter;
60  
61  	public ReviewItemTreePanel(final Project project, final CrucibleFilteredModelProvider.FILTER filter) {
62  		initLayout();
63  		final CrucibleReviewActionListener listener = new MyReviewActionListener(project);
64  		IdeaHelper.getReviewActionEventBroker(project).registerListener(listener);
65  		this.filter = filter;
66  	}
67  
68  	private void initLayout() {
69  		setLayout(new BorderLayout());
70  		setMinimumSize(new Dimension(WIDTH, HEIGHT));
71  		add(getReviewItemTree(), BorderLayout.CENTER);
72  		statusLabel = new JLabel();
73  		statusLabel.setBackground(UIUtil.getTreeTextBackground());
74  		add(statusLabel, BorderLayout.SOUTH);
75  	}
76  
77  	public JPanel getReviewItemTree() {
78  		if (reviewFilesTree == null) {
79  			reviewFilesTree = new AtlassianTreeWithToolbar("ThePlugin.Crucible.ReviewFileListToolBar");
80  			reviewFilesTree.setRootVisible(false);
81  		}
82  		return reviewFilesTree;
83  	}
84  
85  	@Override
86  	public void setEnabled(boolean b) {
87  		super.setEnabled(b);
88  		getReviewItemTree().setEnabled(b);
89  	}
90  
91  
92  	public ProgressAnimationProvider getProgressAnimation() {
93  		return progressAnimation;
94  	}
95  
96  	public void filterTreeNodes(CrucibleFilteredModelProvider.FILTER aFilter) {
97  		this.filter = aFilter;
98  		((CrucibleFilteredModelProvider) reviewFilesTree.getModelProvider()).setType(aFilter);
99  		reviewFilesTree.triggerModelUpdated();
100 		reviewFilesTree.revalidate();
101 		reviewFilesTree.repaint();
102 	}
103 
104 	private final class MyReviewActionListener extends CrucibleReviewActionListener {
105 		private Project project;
106 
107 		private MyReviewActionListener(Project project) {
108 			this.project = project;
109 		}
110 
111 		@Override
112 		public void focusOnFile(final ReviewData review, final CrucibleFileInfo file) {
113 			ApplicationManager.getApplication().invokeLater(new Runnable() {
114 				public void run() {
115 					AtlassianTreeNode node = reviewFilesTree.getModel().locateNode(new NodeSearchAlgorithm() {
116 						@Override
117 						public boolean check(final AtlassianTreeNode node) {
118 							if (node instanceof CrucibleFileNode) {
119 								CrucibleFileNode anode = (CrucibleFileNode) node;
120 								if (anode.getReview().equals(review) && anode.getFile().equals(file)) {
121 									return true;
122 								}
123 							}
124 							return false;
125 						}
126 					});
127 					reviewFilesTree.focusOnNode(node);
128 				}
129 			});
130 
131 		}
132 
133 		@Override
134 		public void focusOnVersionedCommentEvent(final ReviewData review, final CrucibleFileInfo file,
135 				final VersionedComment comment) {
136 			focusOnFile(review, file);
137 		}
138 
139 		@Override
140 		public void focusOnLineCommentEvent(final ReviewData review, final CrucibleFileInfo file,
141 				final VersionedComment comment, final boolean openIfClosed) {
142 			focusOnFile(review, file);
143 		}
144 
145 		@Override
146 		public void focusOnReview(final ReviewData review) {
147 			ApplicationManager.getApplication().invokeLater(new Runnable() {
148 				public void run() {
149 					AtlassianTreeNode node = reviewFilesTree.getModel().locateNode(new NodeSearchAlgorithm() {
150 						@Override
151 						public boolean check(final AtlassianTreeNode node) {
152 							if (node instanceof CrucibleChangeSetTitleNode) {
153 								CrucibleChangeSetTitleNode anode = (CrucibleChangeSetTitleNode) node;
154 								if (anode.getReview().equals(review)) {
155 									return true;
156 								}
157 							}
158 							return false;
159 						}
160 					});
161 					reviewFilesTree.focusOnNode(node);
162 				}
163 			});
164 		}
165 
166 		@Override
167 		public void showReview(final ReviewData reviewItem) {
168 			List<CrucibleFileInfo> files;
169 			try {
170 				files = reviewItem.getFiles();
171 			} catch (ValueNotYetInitialized valueNotYetInitialized) {
172 				try {
173 					files = CrucibleServerFacadeImpl.getInstance().getFiles(reviewItem.getServer(), reviewItem.getPermId());
174 				} catch (RemoteApiException e) {
175 					IdeaHelper.handleRemoteApiException(project, e);
176 					return;
177 				} catch (ServerPasswordNotProvidedException e) {
178 					IdeaHelper.handleMissingPassword(e);
179 					return;
180 				}
181 			}
182 			final List<CrucibleFileInfo> files1 = files;
183 			EventQueue.invokeLater(new MyRunnable(reviewItem, files1));
184 		}
185 
186 
187 		private String createGeneralInfoText(final ReviewData reviewItem) {
188 			final StringBuilder buffer = new StringBuilder();
189 			buffer.append("<html>");
190 			buffer.append("<body>");
191 			buffer.append(reviewItem.getAuthor().getDisplayName());
192 			buffer.append(" ");
193 			buffer.append("<font size=-1 color=");
194 			buffer.append(CrucibleConstants.CRUCIBLE_AUTH_COLOR);
195 			buffer.append(">AUTH</font>");
196 			buffer.append(" ");
197 			if (!reviewItem.getCreator().equals(reviewItem.getModerator())) {
198 				buffer.append(reviewItem.getModerator().getDisplayName());
199 			}
200 			buffer.append(" ");
201 			buffer.append("<font size=-1 color=");
202 			buffer.append(CrucibleConstants.CRUCIBLE_MOD_COLOR);
203 			buffer.append(">MOD</font>");
204 			int i = 0;
205 			List<Reviewer> reviewers;
206 			try {
207 				reviewers = reviewItem.getReviewers();
208 				if (reviewers != null) {
209 					buffer.append("<br>");
210 					for (Reviewer reviewer : reviewers) {
211 						if (i > 0) {
212 							buffer.append(", ");
213 						}
214 						buffer.append(reviewer.getDisplayName());
215 						i++;
216 					}
217 				}
218 			} catch (ValueNotYetInitialized valueNotYetInitialized) {
219 				//ignore
220 			}
221 			buffer.append("</body>");
222 			buffer.append("</html>");
223 
224 			return buffer.toString();
225 		}
226 
227 		private class MyRunnable implements Runnable {
228 			private final ReviewData reviewItem;
229 			private final List<CrucibleFileInfo> files1;
230 
231 			public MyRunnable(final ReviewData reviewItem, final List<CrucibleFileInfo> files1) {
232 				this.reviewItem = reviewItem;
233 				this.files1 = files1;
234 			}
235 
236 			public void run() {
237 
238 				statusLabel.setText(createGeneralInfoText(reviewItem));
239 				ModelProvider modelProvider = new ModelProvider() {
240 
241 					@Override
242 					public AtlassianTreeModel getModel(final AtlassianTreeWithToolbar.STATE state) {
243 						switch (state) {
244 							case DIRED:
245 								return FileTreeModelBuilder.buildTreeModelFromCrucibleChangeSet(project, reviewItem, files1);
246 							case FLAT:
247 								return FileTreeModelBuilder.buildFlatModelFromCrucibleChangeSet(project, reviewItem, files1);
248 							default:
249 								throw new IllegalStateException("Unknown model requested");
250 						}
251 					}
252 				};
253 				CrucibleFilteredModelProvider provider = new MyCrucibleFilteredModelProvider(modelProvider, filter);
254 				reviewFilesTree.setModelProvider(provider);
255 				reviewFilesTree.setRootVisible(true);
256 			}
257 		}
258 	}
259 
260 	private static class MyCrucibleFilteredModelProvider extends CrucibleFilteredModelProvider {
261 		private static final Filter COMMENT_FILTER = new Filter() {
262 				@Override
263 				public boolean isValid(final AtlassianTreeNode node) {
264 					if (node instanceof CrucibleFileNode) {
265 						CrucibleFileNode anode = (CrucibleFileNode) node;
266 						try {
267 							return anode.getFile().getNumberOfComments() > 0;
268 						} catch (ValueNotYetInitialized valueNotYetInitialized) {
269 							return false;
270 						}
271 					}
272 					return true;
273 				}
274 			};
275 
276 		public MyCrucibleFilteredModelProvider(final ModelProvider modelProvider, final FILTER filter) {
277 			super(modelProvider, filter);
278 		}
279 
280 		@Override
281 		public Filter getFilter(final FILTER type) {
282 			switch (type) {
283 				case FILES_ALL:
284 					return Filter.ALL;
285 				case FILES_WITH_COMMENTS_ONLY:
286 					return COMMENT_FILTER;
287 				default:
288 					throw new IllegalStateException("Unknows filtering requested: " + type.toString());
289 			}
290 		}
291 	}
292 
293 }