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.cfg.CfgUtil;
20 import com.atlassian.theplugin.commons.UIActionScheduler;
21 import com.atlassian.theplugin.commons.bamboo.*;
22 import com.atlassian.theplugin.commons.cfg.CfgManager;
23 import com.atlassian.theplugin.commons.cfg.ConfigurationListenerAdapter;
24 import com.atlassian.theplugin.commons.cfg.ProjectConfiguration;
25 import com.atlassian.theplugin.commons.configuration.PluginConfiguration;
26 import com.atlassian.theplugin.commons.crucible.CrucibleServerFacade;
27 import com.atlassian.theplugin.commons.crucible.CrucibleServerFacadeImpl;
28 import com.atlassian.theplugin.commons.util.LoggerImpl;
29 import com.atlassian.theplugin.configuration.WorkspaceConfigurationBean;
30 import com.atlassian.theplugin.crucible.model.CrucibleReviewListModel;
31 import com.atlassian.theplugin.idea.autoupdate.ConfirmPluginUpdateHandler;
32 import com.atlassian.theplugin.idea.autoupdate.PluginUpdateIcon;
33 import com.atlassian.theplugin.idea.bamboo.BambooStatusIcon;
34 import com.atlassian.theplugin.idea.bamboo.BuildListModelImpl;
35 import com.atlassian.theplugin.idea.bamboo.BuildStatusChangedToolTip;
36 import com.atlassian.theplugin.idea.config.ProjectCfgManagerImpl;
37 import com.atlassian.theplugin.idea.crucible.CruciblePatchSubmitExecutor;
38 import com.atlassian.theplugin.idea.crucible.CrucibleStatusChecker;
39 import com.atlassian.theplugin.idea.crucible.CrucibleStatusIcon;
40 import com.atlassian.theplugin.idea.crucible.editor.CrucibleEditorFactoryListener;
41 import com.atlassian.theplugin.idea.jira.IssueListToolWindowPanel;
42 import com.atlassian.theplugin.idea.ui.InformationDialogWithCheckBox;
43 import com.atlassian.theplugin.idea.ui.linkhiglighter.FileEditorListenerImpl;
44 import com.atlassian.theplugin.jira.model.JIRAIssueListModelBuilder;
45 import com.atlassian.theplugin.notification.crucible.CrucibleNotificationTooltip;
46 import com.atlassian.theplugin.notification.crucible.CrucibleReviewNotifier;
47 import com.atlassian.theplugin.remoteapi.MissingPasswordHandler;
48 import com.atlassian.theplugin.remoteapi.MissingPasswordHandlerQueue;
49 import com.atlassian.theplugin.util.PluginUtil;
50 import com.atlassian.theplugin.util.UsageStatisticsGenerator;
51 import com.intellij.ide.BrowserUtil;
52 import com.intellij.openapi.actionSystem.ActionManager;
53 import com.intellij.openapi.actionSystem.AnAction;
54 import com.intellij.openapi.actionSystem.DefaultActionGroup;
55 import com.intellij.openapi.application.ApplicationManager;
56 import com.intellij.openapi.application.ModalityState;
57 import com.intellij.openapi.components.ProjectComponent;
58 import com.intellij.openapi.editor.EditorFactory;
59 import com.intellij.openapi.project.Project;
60 import com.intellij.openapi.startup.StartupManager;
61 import com.intellij.openapi.ui.DialogWrapper;
62 import com.intellij.openapi.ui.Messages;
63 import com.intellij.openapi.util.IconLoader;
64 import com.intellij.openapi.vcs.changes.ChangeListManager;
65 import com.intellij.openapi.wm.ToolWindowManager;
66 import com.intellij.ui.content.ContentManagerAdapter;
67 import com.intellij.ui.content.ContentManagerEvent;
68 import com.jgoodies.forms.layout.CellConstraints;
69 import com.jgoodies.forms.layout.FormLayout;
70 import org.jetbrains.annotations.NotNull;
71
72 import javax.swing.*;
73 import javax.swing.event.HyperlinkEvent;
74 import javax.swing.event.HyperlinkListener;
75 import java.util.Collection;
76
77
78
79
80
81 public class ThePluginProjectComponent implements ProjectComponent {
82 private static final String THE_PLUGIN_TOOL_WINDOW_ICON = "/icons/ico_plugin_16.png";
83
84 private final WorkspaceConfigurationBean projectConfigurationBean;
85 private final Project project;
86
87 public CfgManager getCfgManager() {
88 return cfgManager;
89 }
90
91 private final CfgManager cfgManager;
92
93 private final UIActionScheduler actionScheduler;
94 private BambooStatusIcon statusBarBambooIcon;
95 private CrucibleStatusIcon statusBarCrucibleIcon;
96 private CrucibleNotificationTooltip crucibleTooltip;
97
98 private PluginUpdateIcon statusPluginUpdateIcon;
99 private BambooStatusChecker bambooStatusChecker;
100 private final BuildListModelImpl bambooModel;
101 private CrucibleStatusChecker crucibleStatusChecker;
102 private BambooStatusTooltipListener tooltipBambooStatusListener;
103
104 private final CrucibleServerFacade crucibleServerFacade;
105
106 private final ToolWindowManager toolWindowManager;
107
108 private boolean created;
109 private CrucibleReviewNotifier crucibleReviewNotifier;
110 private final CrucibleReviewListModel crucibleReviewListModel;
111 private final ProjectCfgManagerImpl projectCfgManager;
112 private final JIRAIssueListModelBuilder jiraIssueListModelBuilder;
113 private final PluginConfiguration pluginConfiguration;
114
115 private IssueListToolWindowPanel issuesToolWindowPanel;
116
117 private PluginToolWindow toolWindow;
118
119
120 private ConfigurationListenerImpl configurationListener;
121
122 private CrucibleEditorFactoryListener crucibleEditorFactoryListener;
123
124 private FileEditorListenerImpl fileEditorListener;
125
126 public ThePluginProjectComponent(Project project, ToolWindowManager toolWindowManager,
127 PluginConfiguration pluginConfiguration, UIActionScheduler actionScheduler,
128 WorkspaceConfigurationBean projectConfigurationBean, CfgManager cfgManager,
129 @NotNull IssueListToolWindowPanel issuesToolWindowPanel,
130 @NotNull PluginToolWindow pluginToolWindow,
131 @NotNull BuildListModelImpl bambooModel,
132 @NotNull final CrucibleStatusChecker crucibleStatusChecker,
133 @NotNull final CrucibleReviewNotifier crucibleReviewNotifier,
134 @NotNull final CrucibleReviewListModel crucibleReviewListModel,
135 @NotNull final ProjectCfgManagerImpl projectCfgManager,
136 @NotNull final JIRAIssueListModelBuilder jiraIssueListModelBuilder) {
137 this.project = project;
138 this.cfgManager = cfgManager;
139 this.jiraIssueListModelBuilder = jiraIssueListModelBuilder;
140 this.actionScheduler = actionScheduler;
141 this.toolWindowManager = toolWindowManager;
142 this.pluginConfiguration = pluginConfiguration;
143 this.projectConfigurationBean = projectConfigurationBean;
144 this.bambooModel = bambooModel;
145 this.crucibleStatusChecker = crucibleStatusChecker;
146 this.crucibleReviewNotifier = crucibleReviewNotifier;
147 this.crucibleReviewListModel = crucibleReviewListModel;
148 this.projectCfgManager = projectCfgManager;
149 this.crucibleServerFacade = CrucibleServerFacadeImpl.getInstance();
150 this.issuesToolWindowPanel = issuesToolWindowPanel;
151 this.toolWindow = pluginToolWindow;
152
153 jiraIssueListModelBuilder.setProject(project);
154 jiraIssueListModelBuilder.setProjectCfgManager(projectCfgManager);
155
156
157
158
159
160
161
162
163
164
165
166 statusBarBambooIcon = null;
167 statusBarCrucibleIcon = null;
168 statusPluginUpdateIcon = null;
169 created = false;
170 StartupManager.getInstance(project).registerPostStartupActivity(new Runnable() {
171 public void run() {
172 LoggerImpl.getInstance().info("Start: Project initializing");
173 initializePlugin();
174 LoggerImpl.getInstance().info("End: Project initialized");
175 }
176 });
177
178 }
179
180 public void initComponent() {
181 LoggerImpl.getInstance().info("Init ThePlugin project component.");
182 this.fileEditorListener = new FileEditorListenerImpl(project, projectCfgManager);
183
184 }
185
186 public void disposeComponent() {
187 LoggerImpl.getInstance().info("Dispose ThePlugin project component");
188 }
189
190 @NotNull
191 public String getComponentName() {
192 return "ThePluginProjectComponent";
193 }
194
195 private void initializePlugin() {
196
197
198
199
200
201
202
203 if (!created) {
204
205 toolWindow.register(toolWindowManager);
206
207 ChangeListManager.getInstance(project).registerCommitExecutor(
208 new CruciblePatchSubmitExecutor(project, crucibleServerFacade, projectCfgManager));
209
210 final MissingPasswordHandler pwdHandler = new MissingPasswordHandler(
211 BambooServerFacadeImpl.getInstance(PluginUtil.getLogger()),
212 projectCfgManager,
213 project);
214
215 this.bambooStatusChecker = new BambooStatusChecker(
216 actionScheduler,
217 projectCfgManager,
218 pluginConfiguration,
219 new Runnable() {
220 public void run() {
221 MissingPasswordHandlerQueue.addHandler(pwdHandler);
222 }
223 },
224 PluginUtil.getLogger());
225
226
227
228 issuesToolWindowPanel.refreshModels();
229
230
231
232 Icon toolWindowIcon = IconLoader.getIcon(THE_PLUGIN_TOOL_WINDOW_ICON);
233 toolWindow.getIdeaToolWindow().setIcon(toolWindowIcon);
234
235
236
237
238
239 toolWindow.registerPanel(PluginToolWindow.ToolWindowPanels.BUILDS);
240 toolWindow.registerPanel(PluginToolWindow.ToolWindowPanels.CRUCIBLE);
241 toolWindow.registerPanel(PluginToolWindow.ToolWindowPanels.ISSUES);
242
243 IdeaHelper.getAppComponent().getSchedulableCheckers().add(bambooStatusChecker);
244
245 bambooStatusChecker.registerListener(new BambooStatusListener() {
246 public void updateBuildStatuses(final Collection<BambooBuild> buildStatuses,
247 final Collection<Exception> generalExceptions) {
248
249 bambooModel.update(buildStatuses, generalExceptions);
250 }
251
252 public void resetState() {
253 }
254 });
255
256
257 statusBarBambooIcon = new BambooStatusIcon(this.project, cfgManager, toolWindow);
258 statusBarBambooIcon.updateBambooStatus(BuildStatus.UNKNOWN, new BambooPopupInfo());
259
260
261 final StatusIconBambooListener iconBambooStatusListener = new StatusIconBambooListener(statusBarBambooIcon);
262 bambooStatusChecker.registerListener(iconBambooStatusListener);
263
264
265
266 final BambooStatusDisplay bambooStatusDisplay = new BuildStatusChangedToolTip(project, toolWindow);
267 tooltipBambooStatusListener = new BambooStatusTooltipListener(bambooStatusDisplay, pluginConfiguration);
268 bambooStatusChecker.registerListener(tooltipBambooStatusListener);
269
270
271
272 statusBarBambooIcon.showOrHideIcon();
273
274
275 IdeaHelper.getAppComponent().getSchedulableCheckers().add(crucibleStatusChecker);
276
277 statusBarCrucibleIcon = new CrucibleStatusIcon(project, cfgManager, toolWindow);
278
279
280
281
282
283 statusBarCrucibleIcon.showOrHideIcon();
284
285 statusPluginUpdateIcon = new PluginUpdateIcon(project, pluginConfiguration, cfgManager);
286 ConfirmPluginUpdateHandler.getInstance().setDisplay(statusPluginUpdateIcon);
287
288
289 toolWindow.showHidePanels();
290
291 toolWindow.focusPanelIfExists(projectConfigurationBean.getActiveToolWindowTab());
292 toolWindow.getIdeaToolWindow().getContentManager().addContentManagerListener(new ContentManagerAdapter() {
293 @Override
294 public void selectionChanged(final ContentManagerEvent event) {
295 projectConfigurationBean.setActiveToolWindowTab(event.getContent().getDisplayName());
296 }
297 });
298
299 IdeaHelper.getAppComponent().rescheduleStatusCheckers(false);
300
301 configurationListener = new ConfigurationListenerImpl();
302 cfgManager.addProjectConfigurationListener(CfgUtil.getProjectId(project), configurationListener);
303 cfgManager.addProjectConfigurationListener(CfgUtil.getProjectId(project),
304 issuesToolWindowPanel.getConfigListener());
305
306 created = true;
307
308 crucibleEditorFactoryListener = new CrucibleEditorFactoryListener(project,
309 crucibleReviewListModel);
310 EditorFactory.getInstance()
311 .addEditorFactoryListener(crucibleEditorFactoryListener);
312
313
314 ActionManager aManager = ActionManager.getInstance();
315 DefaultActionGroup changesToolBar = (DefaultActionGroup) aManager.getAction("RepositoryChangesBrowserToolbar");
316 if (changesToolBar != null) {
317 AnAction action = aManager.getAction("ThePlugin.Crucible.ViewFisheyeChangesetItem");
318 changesToolBar.remove(action);
319 changesToolBar.add(action, aManager);
320 }
321
322 registerCrucibleNotifier();
323 issuesToolWindowPanel.init();
324 checkDefaultServerValues();
325 }
326 }
327
328 private void registerCrucibleNotifier() {
329 crucibleReviewListModel.addListener(crucibleReviewNotifier);
330 crucibleTooltip = new CrucibleNotificationTooltip(statusBarCrucibleIcon, project, toolWindow, pluginConfiguration);
331
332 crucibleReviewNotifier.registerListener(crucibleTooltip);
333 }
334
335
336 public void projectOpened() {
337
338
339 askForUserStatistics();
340 fileEditorListener.projectOpened();
341
342 }
343
344 private void checkDefaultServerValues() {
345 final InformationDialogWithCheckBox jiraDialog = new InformationDialogWithCheckBox(project,
346 PluginUtil.PRODUCT_NAME,
347 "Please set up default JIRA server in order to get all cool features of Atlassian IntelliJ Connector.");
348 if (projectCfgManager.getDefaultJiraServer() == null && !projectConfigurationBean.isDefaultJiraServerAsked()) {
349 SwingUtilities.invokeLater(new Runnable() {
350 public void run() {
351 jiraDialog.show();
352 projectConfigurationBean.setDefaultJiraServerAsked(jiraDialog.isDoNotShowChecked());
353 }
354 });
355 }
356 final InformationDialogWithCheckBox fishEyeDialog = new InformationDialogWithCheckBox(project,
357 PluginUtil.PRODUCT_NAME,
358 "Please set up default FishEye server in order to get all cool features of Atlassian IntelliJ Connector.");
359
360 if (projectCfgManager.getDefaultFishEyeServer() == null
361 && !projectConfigurationBean.isDefaultFishEyeServerAsked()) {
362 SwingUtilities.invokeLater(new Runnable() {
363 public void run() {
364 fishEyeDialog.show();
365 projectConfigurationBean.setDefaultFishEyeServerAsked(fishEyeDialog.isDoNotShowChecked());
366 }
367 });
368 }
369 }
370
371 public FileEditorListenerImpl getFileEditorListener() {
372 return fileEditorListener;
373 }
374
375 private void askForUserStatistics() {
376 ApplicationManager.getApplication().invokeLater(new Runnable() {
377 public void run() {
378 if (pluginConfiguration.getGeneralConfigurationData().getAnonymousEnhancedFeedbackEnabled() == null) {
379 UsageStatsDialog dlg = new UsageStatsDialog();
380 dlg.show();
381 int answer = dlg.getExitCode();
382 pluginConfiguration.getGeneralConfigurationData().setAnonymousEnhancedFeedbackEnabled(
383 answer == DialogWrapper.OK_EXIT_CODE);
384 }
385 }
386 }, ModalityState.defaultModalityState());
387 }
388
389 private class UsageStatsDialog extends DialogWrapper {
390 private static final String MSG_TEXT =
391 "We would greatly appreciate it if you would allow us to collect anonymous"
392 + "<br>usage statistics to help us provide a better quality product. Details"
393 + "<br>of what will be tracked are given "
394 + "<a href=\""
395 + UsageStatisticsGenerator.USAGE_STATS_HREF
396 + "\">here</a>. Is this OK?";
397
398 protected UsageStatsDialog() {
399 super((Project) null, false);
400 init();
401 setTitle(PluginUtil.getInstance().getName() + " Request");
402 setModal(true);
403 setOKButtonText("Yes");
404 setCancelButtonText("No");
405 }
406
407 protected JComponent createCenterPanel() {
408 JPanel p = new JPanel(new FormLayout("3dlu, p, 3dlu, p, 3dlu", "3dlu, p, 3dlu"));
409 CellConstraints cc = new CellConstraints();
410
411 JEditorPane textPane = new JEditorPane();
412 textPane.setContentType("text/html");
413 textPane.setEditable(false);
414 textPane.setOpaque(false);
415 textPane.putClientProperty(JEditorPane.HONOR_DISPLAY_PROPERTIES, true);
416 textPane.setText("<html>" + MSG_TEXT);
417 textPane.addHyperlinkListener(new HyperlinkListener() {
418 public void hyperlinkUpdate(HyperlinkEvent e) {
419 if (e.getEventType() == HyperlinkEvent.EventType.ACTIVATED) {
420 BrowserUtil.launchBrowser(e.getURL().toString());
421 }
422 }
423 });
424
425 p.add(new JLabel(Messages.getQuestionIcon()), cc.xy(2, 2));
426 p.add(textPane, cc.xy(4, 2));
427
428 return p;
429 }
430
431
432 }
433
434 public void projectClosed() {
435 if (created) {
436 fileEditorListener.projectClosed();
437
438 statusBarBambooIcon.hideIcon();
439 statusBarBambooIcon = null;
440 statusBarCrucibleIcon.hideIcon();
441 statusBarCrucibleIcon = null;
442 statusPluginUpdateIcon.hideIcon();
443 statusPluginUpdateIcon = null;
444
445 IdeaHelper.getAppComponent().getSchedulableCheckers().remove(bambooStatusChecker);
446 IdeaHelper.getAppComponent().getSchedulableCheckers().remove(crucibleStatusChecker);
447 IdeaHelper.getAppComponent().rescheduleStatusCheckers(true);
448
449
450
451 bambooStatusChecker.unregisterListener(tooltipBambooStatusListener);
452
453
454 cfgManager.removeProjectConfigurationListener(CfgUtil.getProjectId(project), configurationListener);
455 configurationListener = null;
456 cfgManager.removeProjectConfigurationListener(CfgUtil.getProjectId(project),
457 issuesToolWindowPanel.getConfigListener());
458
459
460 toolWindowManager.unregisterToolWindow(PluginToolWindow.TOOL_WINDOW_NAME);
461
462 EditorFactory.getInstance().removeEditorFactoryListener(crucibleEditorFactoryListener);
463
464
465 crucibleReviewNotifier.unregisterListener(crucibleTooltip);
466 crucibleReviewListModel.removeListener(crucibleReviewNotifier);
467
468 ActionManager aManager = ActionManager.getInstance();
469 DefaultActionGroup changesToolBar = (DefaultActionGroup) aManager.getAction("RepositoryChangesBrowserToolbar");
470 if (changesToolBar != null) {
471 AnAction action = aManager.getAction("ThePlugin.Crucible.ViewFisheyeChangesetItem");
472 changesToolBar.remove(action);
473 }
474
475 created = false;
476 }
477 }
478
479 public WorkspaceConfigurationBean getProjectConfigurationBean() {
480 return projectConfigurationBean;
481 }
482
483 public CrucibleStatusChecker getCrucibleStatusChecker() {
484 return crucibleStatusChecker;
485 }
486
487 public BambooStatusChecker getBambooStatusChecker() {
488 return bambooStatusChecker;
489 }
490
491 public JIRAIssueListModelBuilder getJiraIssueListModelBuilder() {
492 return jiraIssueListModelBuilder;
493 }
494
495 private class ConfigurationListenerImpl extends ConfigurationListenerAdapter {
496
497 @Override
498 public void configurationUpdated(final ProjectConfiguration aProjectConfiguration) {
499
500 statusBarBambooIcon.showOrHideIcon();
501 statusBarCrucibleIcon.showOrHideIcon();
502
503 toolWindow.showHidePanels();
504 }
505 }
506
507 }