View Javadoc

1   /**
2    * Copyright (C) 2008 Atlassian
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.atlassian.theplugin.idea.bamboo;
18  
19  import com.atlassian.theplugin.commons.bamboo.BambooChangeSet;
20  import com.atlassian.theplugin.idea.Constants;
21  import com.atlassian.theplugin.idea.TableColumnInfo;
22  import com.atlassian.theplugin.idea.crucible.tree.AtlassianTreeWithToolbar;
23  import com.atlassian.theplugin.idea.crucible.tree.ModelProvider;
24  import com.atlassian.theplugin.idea.ui.AtlassianTableView;
25  import com.atlassian.theplugin.idea.ui.TableColumnProvider;
26  import com.atlassian.theplugin.idea.ui.TableItemSelectedListener;
27  import com.atlassian.theplugin.idea.ui.tree.AtlassianTreeModel;
28  import com.atlassian.theplugin.idea.ui.tree.file.BambooFileNode;
29  import com.atlassian.theplugin.idea.ui.tree.file.FileTreeModelBuilder;
30  import com.intellij.openapi.actionSystem.ActionGroup;
31  import com.intellij.openapi.actionSystem.ActionManager;
32  import com.intellij.openapi.actionSystem.DataProvider;
33  import com.intellij.openapi.project.Project;
34  import com.intellij.openapi.ui.Splitter;
35  import com.intellij.openapi.wm.ToolWindow;
36  import com.intellij.openapi.wm.ToolWindowAnchor;
37  import com.intellij.openapi.wm.ToolWindowManager;
38  import com.intellij.peer.PeerFactory;
39  import com.intellij.psi.PsiFile;
40  import com.intellij.ui.content.Content;
41  import com.intellij.util.ui.ListTableModel;
42  import org.jetbrains.annotations.NonNls;
43  import org.jetbrains.annotations.Nullable;
44  
45  import javax.swing.*;
46  import javax.swing.table.TableCellRenderer;
47  import javax.swing.tree.TreePath;
48  import java.awt.*;
49  import java.awt.event.MouseAdapter;
50  import java.awt.event.MouseEvent;
51  import java.util.Comparator;
52  import java.util.Date;
53  import java.util.HashMap;
54  import java.util.List;
55  
56  public final class BuildChangesToolWindow {
57  	private final Project project;
58  //	private final ToolWindow commitDetailsToolWindow;
59  
60  	public interface ChangesTree {
61  		boolean GROUP_BY_DIRECTORY_DEFAULT = true;
62  
63  		void showDiff();
64  
65  		void showDiffWithLocal();
66  
67  		void showRepositoryVersion();
68  	}
69  
70  	private static final String TOOL_WINDOW_TITLE = "Bamboo Build Changes";
71  
72  	private HashMap<String, CommitDetailsPanel> panelMap = new HashMap<String, CommitDetailsPanel>();
73  
74  	public BuildChangesToolWindow(Project project) {
75  		this.project = project;
76  //		ToolWindowManager twm = ToolWindowManager.getInstance(project);
77  //		commitDetailsToolWindow = twm.registerToolWindow(TOOL_WINDOW_TITLE, true, ToolWindowAnchor.BOTTOM);
78  //		commitDetailsToolWindow.setIcon(Constants.BAMBOO_COMMITS_ICON);
79  	}
80  
81  	public ChangesTree getChangesTree(String name) {
82  		return panelMap.get(name);
83  	}
84  
85  	public void showBuildChanges(String buildKey, String buildNumber, List<BambooChangeSet> commits) {
86  		String contentKey = buildKey + "-" + buildNumber;
87  
88  
89  		ToolWindowManager twm = ToolWindowManager.getInstance(project);
90  		ToolWindow commitDetailsToolWindow = twm.getToolWindow(TOOL_WINDOW_TITLE);
91  		if (commitDetailsToolWindow == null) {
92  			commitDetailsToolWindow = twm.registerToolWindow(TOOL_WINDOW_TITLE, true, ToolWindowAnchor.BOTTOM);
93  			commitDetailsToolWindow.setIcon(Constants.BAMBOO_COMMITS_ICON);
94  		}
95  
96  		Content content = commitDetailsToolWindow.getContentManager().findContent(contentKey);
97  
98  		if (content == null) {
99  			CommitDetailsPanel detailsPanel = new CommitDetailsPanel(project, contentKey, commits);
100 			panelMap.remove(contentKey);
101 			panelMap.put(contentKey, detailsPanel);
102 
103 			PeerFactory peerFactory = PeerFactory.getInstance();
104 			content = peerFactory.getContentFactory().createContent(detailsPanel, contentKey, true);
105 			content.setIcon(Constants.BAMBOO_COMMITS_ICON);
106 			content.putUserData(com.intellij.openapi.wm.ToolWindow.SHOW_CONTENT_ICON, Boolean.TRUE);
107 			commitDetailsToolWindow.getContentManager().addContent(content);
108 		}
109 
110 		commitDetailsToolWindow.getContentManager().setSelectedContent(content);
111 		commitDetailsToolWindow.show(null);
112 	}
113 
114 	private static class CommitDetailsPanel extends JPanel implements ChangesTree, DataProvider {
115 		private static final float SPLIT_RATIO = 0.6f;
116 		private AtlassianTreeWithToolbar fileTree = new AtlassianTreeWithToolbar(TOOLBAR_NAME);
117 		private final Project project;
118 
119 		private String name;
120 		private static final String TOOLBAR_NAME = "ThePlugin.Bamboo.CommitListToolBar";
121 
122 		public CommitDetailsPanel(Project project, String name, final List<BambooChangeSet> commits) {
123 			this.project = project;
124 			this.name = name;
125 
126 			if (commits == null || commits.size() == 0) {
127 				add(new JLabel("No commits in " + name));
128 				return;
129 			}
130 
131 			setLayout(new GridBagLayout());
132 
133 			Splitter split = new Splitter(false, SPLIT_RATIO);
134 			split.setShowDividerControls(true);
135 
136 
137 			JPanel tablePanel = new JPanel();
138 			tablePanel.setLayout(new BorderLayout());
139 			final AtlassianTableView<BambooChangeSet> commitsTable = createCommitsTable(commits);
140 			tablePanel.add(new JScrollPane(commitsTable), BorderLayout.CENTER);
141 
142 			split.setFirstComponent(tablePanel);
143 
144 			JPanel fileTreePanel = new JPanel();
145 			fileTreePanel.setLayout(new BorderLayout());
146 
147 			JLabel label = new JLabel("Changed Files");
148 			fileTreePanel.add(label, BorderLayout.NORTH);
149 
150 			fileTree.setRootVisible(false);
151 			fileTreePanel.add(fileTree, BorderLayout.CENTER);
152 
153 			split.setSecondComponent(fileTreePanel);
154 
155 			setLayout(new BorderLayout());
156 			add(split, BorderLayout.CENTER);
157 		}
158 
159 		@Nullable
160 		public Object getData(@NonNls final String dataId) {
161 			if (dataId.equals(Constants.FILE_TREE)) {
162 				return fileTree;
163 			} else if (dataId.equals(Constants.BUILD_CHANGES_WINDOW)) {
164 				return this;
165 			}
166 			return null;
167 		}
168 
169 
170 		private static class AuthorColumn extends TableColumnInfo<BambooChangeSet, String> {
171 			private static final int PREFERRED_WIDTH = 100;
172 
173 			@Override
174 			public String getColumnName() {
175 				return "Author";
176 			}
177 
178 			@Override
179 			public Class getColumnClass() {
180 				return String.class;
181 			}
182 
183 			@Override
184 			public int getPrefferedWidth() {
185 				return PREFERRED_WIDTH;
186 			}
187 
188 			@Override
189 			public String valueOf(final BambooChangeSet obj) {
190 				return obj.getAuthor();
191 			}
192 
193 			@Override
194 			public Comparator<BambooChangeSet> getComparator() {
195 				return new Comparator<BambooChangeSet>() {
196 					public int compare(BambooChangeSet o, BambooChangeSet o1) {
197 						return o.getAuthor().compareTo(o1.getAuthor());
198 					}
199 				};
200 			}
201 
202 		}
203 
204 		private static class DateColumn extends TableColumnInfo<BambooChangeSet, Date> {
205 			private static final int PREFERRED_WIDTH = 100;
206 
207 			@Override
208 			public String getColumnName() {
209 				return "Date";
210 			}
211 
212 			@Override
213 			public Class getColumnClass() {
214 				return Date.class;
215 			}
216 
217 			@Override
218 			public int getPrefferedWidth() {
219 				return PREFERRED_WIDTH;
220 			}
221 
222 			@Override
223 			public Date valueOf(BambooChangeSet o) {
224 				return o.getCommitDate();
225 			}
226 
227 			@Override
228 			public Comparator<BambooChangeSet> getComparator() {
229 				return new Comparator<BambooChangeSet>() {
230 					public int compare(BambooChangeSet o, BambooChangeSet o1) {
231 						return o1.getCommitDate().compareTo(o.getCommitDate());
232 					}
233 				};
234 			}
235 		}
236 
237 		private static class CommentColumn extends TableColumnInfo<BambooChangeSet, String> {
238 			private static final int PREFERRED_WIDTH = 600;
239 
240 			@Override
241 			public String getColumnName() {
242 				return "Comment";
243 			}
244 
245 			@Override
246 			public Class getColumnClass() {
247 				return String.class;
248 			}
249 
250 			@Override
251 			public int getPrefferedWidth() {
252 				return PREFERRED_WIDTH;
253 			}
254 
255 			@Override
256 			public String valueOf(BambooChangeSet o) {
257 				return o.getComment();
258 			}
259 
260 			@Override
261 			public Comparator<BambooChangeSet> getComparator() {
262 				return new Comparator<BambooChangeSet>() {
263 					public int compare(BambooChangeSet o, BambooChangeSet o1) {
264 						return o.getComment().compareTo(o1.getComment());
265 					}
266 				};
267 			}
268 		}
269 
270 		private AtlassianTableView<BambooChangeSet> createCommitsTable(final List<BambooChangeSet> commits) {
271 			TableColumnProvider prov = new TableColumnProvider() {
272 				public TableColumnInfo[] makeColumnInfo() {
273 					return new TableColumnInfo[]{new AuthorColumn(), new DateColumn(), new CommentColumn()};
274 				}
275 
276 				public TableCellRenderer[] makeRendererInfo() {
277 					return new TableCellRenderer[]{null, null, null};
278 				}
279 			};
280 			final AtlassianTableView<BambooChangeSet> atv = new AtlassianTableView<BambooChangeSet>(prov,
281 					new ListTableModel<BambooChangeSet>(prov.makeColumnInfo(), commits, 0), null);
282 			atv.addItemSelectedListener(new TableItemSelectedListener<BambooChangeSet>() {
283 				public void itemSelected(AtlassianTableView<BambooChangeSet> table, int noClicks) {
284 					final BambooChangeSet c = table.getSelectedObject();
285 					if (c == null) {
286 						fileTree.setModelProvider(ModelProvider.EMPTY_MODEL_PROVIDER);
287 					} else {
288 						fileTree.setModelProvider(new ModelProvider() {
289 							private AtlassianTreeModel diredModel =
290 									FileTreeModelBuilder.buildTreeModelFromBambooChangeSet(project, c);
291 							private AtlassianTreeModel flatModel =
292 									FileTreeModelBuilder.buildFlatTreeModelFromBambooChangeSet(project, c);
293 
294 							@Override
295 							public AtlassianTreeModel getModel(final AtlassianTreeWithToolbar.STATE state) {
296 								switch (state) {
297 									case DIRED:
298 										return diredModel;
299 									case FLAT:
300 										return flatModel;
301 									default:
302 										throw new IllegalStateException("Unknown model requested");
303 								}
304 							}
305 						});
306 					}
307 					fileTree.setRootVisible(false);
308 					fileTree.expandAll();
309 					fileTree.getTreeComponent().addMouseListener(new NavigateToCodeHandler(name));
310 				}
311 			});
312 			return atv;
313 		}
314 
315 		public void showDiff() {
316 			// todo
317 		}
318 
319 		public void showDiffWithLocal() {
320 			// todo
321 		}
322 
323 		public void showRepositoryVersion() {
324 			// todo
325 		}
326 
327 		private static final class NavigateToCodeHandler extends MouseAdapter {
328 
329 			private String place;
330 
331 			private NavigateToCodeHandler(String place) {
332 				this.place = place;
333 			}
334 
335 			@Nullable
336 			private BambooFileNode getBambooFileNode(MouseEvent e) {
337 				final JTree theTree = (JTree) e.getComponent();
338 
339 				TreePath path = theTree.getPathForLocation(e.getX(), e.getY());
340 				if (path == null) {
341 					return null;
342 				}
343 				Object o = path.getLastPathComponent();
344 				if (o instanceof BambooFileNode) {
345 					return (BambooFileNode) o;
346 				}
347 				return null;
348 
349 			}
350 
351 			@Override
352 			public void mouseClicked(MouseEvent e) {
353 				if (e.getClickCount() != 2) {
354 					return;
355 				}
356 
357 				final JTree theTree = (JTree) e.getComponent();
358 
359 				TreePath path = theTree.getPathForLocation(e.getX(), e.getY());
360 				if (path == null) {
361 					return;
362 				}
363 				Object o = path.getLastPathComponent();
364 				if (o instanceof BambooFileNode) {
365 					BambooFileNode bfn = (BambooFileNode) o;
366 					PsiFile psiFile = bfn.getPsiFile();
367 					if (psiFile != null && psiFile.canNavigateToSource() == true) {
368 						psiFile.navigate(true);
369 					}
370 				}
371 			}
372 
373 
374 			@Override
375 			public void mousePressed(MouseEvent e) {
376 				processPopup(e);
377 			}
378 
379 			@Override
380 			public void mouseReleased(MouseEvent e) {
381 				processPopup(e);
382 			}
383 
384 			public void processPopup(MouseEvent e) {
385 				if (e.isPopupTrigger() == false) {
386 					return;
387 				}
388 
389 				final JTree theTree = (JTree) e.getComponent();
390 
391 				TreePath path = theTree.getPathForLocation(e.getX(), e.getY());
392 				if (path == null) {
393 					return;
394 				}
395 				theTree.setSelectionPath(path);
396 
397 
398 				final BambooFileNode bfn = getBambooFileNode(e);
399 				if (bfn == null) {
400 					return;
401 				}
402 
403 
404 				ActionManager aManager = ActionManager.getInstance();
405 				ActionGroup menu = (ActionGroup) aManager.getAction(TOOLBAR_NAME);
406 				if (menu == null) {
407 					return;
408 				}
409 				aManager.createActionPopupMenu(place, menu).getComponent().show(e.getComponent(), e.getX(), e.getY());
410 
411 			}
412 
413 		}
414 	}
415 }