1 package com.atlassian.theplugin.idea.bamboo.build;
2
3 import com.atlassian.theplugin.commons.bamboo.BambooChangeSet;
4 import com.atlassian.theplugin.commons.bamboo.BambooServerFacade;
5 import com.atlassian.theplugin.commons.bamboo.BambooServerFacadeImpl;
6 import com.atlassian.theplugin.commons.bamboo.BuildDetails;
7 import com.atlassian.theplugin.commons.exception.ServerPasswordNotProvidedException;
8 import com.atlassian.theplugin.commons.remoteapi.RemoteApiException;
9 import com.atlassian.theplugin.idea.Constants;
10 import com.atlassian.theplugin.idea.bamboo.BambooBuildAdapterIdea;
11 import com.atlassian.theplugin.idea.crucible.tree.AtlassianTreeWithToolbar;
12 import com.atlassian.theplugin.idea.crucible.tree.ModelProvider;
13 import com.atlassian.theplugin.idea.ui.tree.AtlassianTreeModel;
14 import com.atlassian.theplugin.idea.ui.tree.file.BambooFileNode;
15 import com.atlassian.theplugin.idea.ui.tree.file.FileTreeModelBuilder;
16 import com.atlassian.theplugin.idea.ui.tree.paneltree.SelectableLabel;
17 import com.atlassian.theplugin.idea.ui.tree.paneltree.TreeUISetup;
18 import com.atlassian.theplugin.util.PluginUtil;
19 import com.intellij.openapi.actionSystem.ActionGroup;
20 import com.intellij.openapi.actionSystem.ActionManager;
21 import com.intellij.openapi.actionSystem.DataProvider;
22 import com.intellij.openapi.progress.ProgressIndicator;
23 import com.intellij.openapi.progress.ProgressManager;
24 import com.intellij.openapi.progress.Task;
25 import com.intellij.openapi.project.Project;
26 import com.intellij.openapi.ui.Splitter;
27 import com.intellij.psi.PsiFile;
28 import org.jetbrains.annotations.NonNls;
29 import org.jetbrains.annotations.NotNull;
30 import org.jetbrains.annotations.Nullable;
31
32 import javax.swing.*;
33 import javax.swing.event.ListSelectionEvent;
34 import javax.swing.event.ListSelectionListener;
35 import javax.swing.tree.TreePath;
36 import java.awt.*;
37 import java.awt.event.ActionEvent;
38 import java.awt.event.ActionListener;
39 import java.awt.event.MouseAdapter;
40 import java.awt.event.MouseEvent;
41 import java.text.DateFormat;
42 import java.util.List;
43
44
45
46
47
48
49 public class CommitDetailsPanel extends JPanel implements DataProvider, ActionListener {
50
51 private static final float SPLIT_RATIO = 0.6f;
52 protected static final int ROW_HEIGHT = 16;
53
54 private AtlassianTreeWithToolbar fileTree = new AtlassianTreeWithToolbar(TOOLBAR_NAME, (TreeUISetup) null, null);
55 private final Project project;
56 private final BambooBuildAdapterIdea build;
57
58 private static final String TOOLBAR_NAME = "ThePlugin.Bamboo.CommitListToolBar";
59
60 public CommitDetailsPanel(Project project, final BambooBuildAdapterIdea build) {
61 setLayout(new GridBagLayout());
62
63 this.project = project;
64 this.build = build;
65
66 Task.Backgroundable changesTask = new Task.Backgroundable(project, "Retrieving changed files", false) {
67 @Override
68 public void run(@NotNull final ProgressIndicator indicator) {
69 try {
70 BambooServerFacade bambooFacade = BambooServerFacadeImpl.getInstance(PluginUtil.getLogger());
71 BuildDetails details = bambooFacade.getBuildDetails(
72 build.getServer(), build.getPlanKey(), build.getNumber());
73 final List<BambooChangeSet> commits = details.getCommitInfo();
74 SwingUtilities.invokeLater(new Runnable() {
75 public void run() {
76 fillContent(commits);
77 }
78 });
79 } catch (ServerPasswordNotProvidedException e) {
80 showError(e);
81 } catch (RemoteApiException e) {
82 showError(e);
83 }
84 }
85 };
86 ProgressManager.getInstance().run(changesTask);
87 }
88
89 public void actionPerformed(ActionEvent e) {
90
91 }
92
93 private void showError(final Exception e) {
94 SwingUtilities.invokeLater(new Runnable() {
95 public void run() {
96 add(new JLabel("Failed to retrieve changed files: " + e.getMessage()));
97 }
98 });
99 }
100
101 private class ChangeSetListModel extends DefaultListModel {
102 public ChangeSetListModel(List<BambooChangeSet> commits) {
103 int i = 0;
104 for (BambooChangeSet cs : commits) {
105 add(i++, cs);
106 }
107 }
108 }
109
110 private static final class ChangeSetRendererPanel extends JPanel {
111
112 private SelectableLabel comment = new SelectableLabel(true, true, "NOTHING YET", ROW_HEIGHT, false, false);
113 private SelectableLabel author = new SelectableLabel(true, true, "NOTHING HERE ALSO", ROW_HEIGHT, true, false);
114 private SelectableLabel date = new SelectableLabel(true, true, "NEITHER HERE", ROW_HEIGHT, false, false);
115
116 private ChangeSetRendererPanel() {
117 setLayout(new GridBagLayout());
118 GridBagConstraints gbc = new GridBagConstraints();
119 gbc.weightx = 1.0;
120 gbc.gridx = 0;
121 gbc.gridy = 0;
122 gbc.fill = GridBagConstraints.HORIZONTAL;
123 add(comment, gbc);
124 gbc.gridx++;
125 gbc.weightx = 0.0;
126 gbc.anchor = GridBagConstraints.LINE_END;
127 gbc.fill = GridBagConstraints.NONE;
128 author.setHorizontalAlignment(SwingConstants.RIGHT);
129 add(author, gbc);
130 gbc.gridx++;
131 add(date, gbc);
132 }
133
134 void setChangeSet(BambooChangeSet cs) {
135 comment.setText(" " + cs.getComment());
136 author.setText(cs.getAuthor());
137 DateFormat dfo = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
138 String commitDate = dfo.format(cs.getCommitDate());
139 date.setText(", " + commitDate + " ");
140 }
141
142 void setSelected(boolean selected) {
143 comment.setSelected(selected);
144 author.setSelected(selected);
145 date.setSelected(selected);
146 }
147 }
148
149 private static final ChangeSetRendererPanel CHANGEST_RENDERER_PANEL = new ChangeSetRendererPanel();
150
151 private void fillContent(List<BambooChangeSet> commits) {
152 if (commits == null || commits.size() == 0) {
153 add(new JLabel("No changes in " + build.getPlanKey() + "-" + build.getBuildNumberAsString()));
154 return;
155 }
156
157 Splitter split = new Splitter(false, SPLIT_RATIO);
158 split.setShowDividerControls(true);
159
160
161 JPanel listPanel = new JPanel();
162 listPanel.setLayout(new BorderLayout());
163
164 final JList changesList = new JList() {
165 @Override
166 public boolean getScrollableTracksViewportWidth() {
167 return true;
168 }
169 };
170 changesList.setModel(new ChangeSetListModel(commits));
171 changesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
172 changesList.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
173 public void valueChanged(ListSelectionEvent e) {
174 if (e.getValueIsAdjusting()) {
175 return;
176 }
177 fillFileTree((BambooChangeSet) changesList.getSelectedValue());
178 }
179 });
180 changesList.setCellRenderer(new ListCellRenderer() {
181 public Component getListCellRendererComponent(JList list, Object value, int index,
182 boolean isSelected, boolean cellHasFocus) {
183 CHANGEST_RENDERER_PANEL.setChangeSet((BambooChangeSet) value);
184 CHANGEST_RENDERER_PANEL.setSelected(isSelected);
185 CHANGEST_RENDERER_PANEL.validate();
186 return CHANGEST_RENDERER_PANEL;
187 }
188 });
189 final JScrollPane scroll = new JScrollPane(changesList, ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED,
190 ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER);
191 listPanel.add(scroll, BorderLayout.CENTER);
192
193 split.setFirstComponent(listPanel);
194
195 JPanel fileTreePanel = new JPanel();
196 fileTreePanel.setLayout(new BorderLayout());
197 fileTreePanel.add(new JLabel("Changed Files"), BorderLayout.NORTH);
198
199 fileTree.setRootVisible(false);
200 fileTree.getTreeComponent().addMouseListener(new NavigateToCodeHandler(build.getPlanKey()));
201 fileTreePanel.add(fileTree, BorderLayout.CENTER);
202
203 split.setSecondComponent(fileTreePanel);
204 split.setShowDividerControls(false);
205
206 setLayout(new BorderLayout());
207 add(split, BorderLayout.CENTER);
208
209 if (commits.size() == 1) {
210 changesList.getSelectionModel().setSelectionInterval(0, 0);
211 }
212 validate();
213 }
214
215 @Nullable
216 public Object getData(@NonNls final String dataId) {
217 if (dataId.equals(Constants.FILE_TREE)) {
218 return fileTree;
219 } else if (dataId.equals(Constants.BUILD_CHANGES_WINDOW)) {
220 return this;
221 }
222 return null;
223 }
224
225 private void fillFileTree(final BambooChangeSet changeSet) {
226 if (changeSet == null) {
227 fileTree.setModelProvider(ModelProvider.EMPTY_MODEL_PROVIDER);
228 } else {
229 fileTree.setModelProvider(new ModelProvider() {
230 private AtlassianTreeModel diredModel =
231 FileTreeModelBuilder.buildTreeModelFromBambooChangeSet(project, changeSet);
232 private AtlassianTreeModel flatModel =
233 FileTreeModelBuilder.buildFlatTreeModelFromBambooChangeSet(project, changeSet);
234
235 @Override
236 public AtlassianTreeModel getModel(final AtlassianTreeWithToolbar.State state) {
237 switch (state) {
238 case DIRED:
239 return diredModel;
240 case FLAT:
241 return flatModel;
242 default:
243 throw new IllegalStateException("Unknown model requested");
244 }
245 }
246 });
247 }
248 fileTree.setRootVisible(false);
249 fileTree.expandAll();
250 }
251
252 private final class NavigateToCodeHandler extends MouseAdapter {
253
254 private String place;
255
256 private NavigateToCodeHandler(String place) {
257 this.place = place;
258 }
259
260 @Nullable
261 private BambooFileNode getBambooFileNode(MouseEvent e) {
262 final JTree theTree = (JTree) e.getComponent();
263
264 TreePath path = theTree.getPathForLocation(e.getX(), e.getY());
265 if (path == null) {
266 return null;
267 }
268 Object o = path.getLastPathComponent();
269 if (o instanceof BambooFileNode) {
270 return (BambooFileNode) o;
271 }
272 return null;
273
274 }
275
276 @Override
277 public void mouseClicked(MouseEvent e) {
278 if (e.getClickCount() != 2) {
279 return;
280 }
281
282 final JTree theTree = (JTree) e.getComponent();
283
284 TreePath path = theTree.getPathForLocation(e.getX(), e.getY());
285 if (path == null) {
286 return;
287 }
288 Object o = path.getLastPathComponent();
289 if (o instanceof BambooFileNode) {
290 BambooFileNode bfn = (BambooFileNode) o;
291 PsiFile psiFile = bfn.getPsiFile();
292 if (psiFile != null && psiFile.canNavigateToSource()) {
293 psiFile.navigate(true);
294 }
295 }
296 }
297
298
299 @Override
300 public void mousePressed(MouseEvent e) {
301 processPopup(e);
302 }
303
304 @Override
305 public void mouseReleased(MouseEvent e) {
306 processPopup(e);
307 }
308
309 public void processPopup(MouseEvent e) {
310 if (!e.isPopupTrigger()) {
311 return;
312 }
313
314 final JTree theTree = (JTree) e.getComponent();
315
316 TreePath path = theTree.getPathForLocation(e.getX(), e.getY());
317 if (path == null) {
318 return;
319 }
320 theTree.setSelectionPath(path);
321
322
323 final BambooFileNode bfn = getBambooFileNode(e);
324 if (bfn == null) {
325 return;
326 }
327
328
329 ActionManager aManager = ActionManager.getInstance();
330 ActionGroup menu = (ActionGroup) aManager.getAction(TOOLBAR_NAME);
331 if (menu == null) {
332 return;
333 }
334 aManager.createActionPopupMenu(place, menu).getComponent().show(fileTree, e.getX(), e.getY());
335 }
336 }
337 }