View Javadoc

1   package com.atlassian.theplugin.idea.jira;
2   
3   import com.atlassian.theplugin.cfg.CfgUtil;
4   import com.atlassian.theplugin.commons.ServerType;
5   import com.atlassian.theplugin.commons.cfg.*;
6   import com.atlassian.theplugin.commons.configuration.PluginConfiguration;
7   import com.atlassian.theplugin.commons.remoteapi.RemoteApiException;
8   import com.atlassian.theplugin.commons.remoteapi.ServerData;
9   import com.atlassian.theplugin.configuration.IssueRecentlyOpenBean;
10  import com.atlassian.theplugin.configuration.JiraFilterConfigurationBean;
11  import com.atlassian.theplugin.configuration.JiraWorkspaceConfiguration;
12  import com.atlassian.theplugin.idea.Constants;
13  import com.atlassian.theplugin.idea.IdeaHelper;
14  import com.atlassian.theplugin.idea.PluginToolWindowPanel;
15  import com.atlassian.theplugin.idea.action.issues.RunIssueActionAction;
16  import com.atlassian.theplugin.idea.action.issues.activetoolbar.ActiveIssueUtils;
17  import com.atlassian.theplugin.idea.config.ProjectCfgManagerImpl;
18  import com.atlassian.theplugin.idea.jira.tree.JIRAFilterTree;
19  import com.atlassian.theplugin.idea.jira.tree.JIRAIssueTreeBuilder;
20  import com.atlassian.theplugin.idea.jira.tree.JIRAIssueTreeNode;
21  import com.atlassian.theplugin.idea.jira.tree.JiraFilterTreeSelectionListener;
22  import com.atlassian.theplugin.idea.ui.DialogWithDetails;
23  import com.atlassian.theplugin.idea.ui.PopupAwareMouseAdapter;
24  import com.atlassian.theplugin.jira.JIRAIssueProgressTimestampCache;
25  import com.atlassian.theplugin.jira.JIRAServerFacade;
26  import com.atlassian.theplugin.jira.JIRAServerFacadeImpl;
27  import com.atlassian.theplugin.jira.api.*;
28  import com.atlassian.theplugin.jira.cache.RecentlyOpenIssuesCache;
29  import com.atlassian.theplugin.jira.model.*;
30  import com.atlassian.theplugin.remoteapi.MissingPasswordHandlerJIRA;
31  import com.atlassian.theplugin.remoteapi.MissingPasswordHandlerQueue;
32  import com.atlassian.theplugin.util.PluginUtil;
33  import com.intellij.openapi.actionSystem.*;
34  import com.intellij.openapi.application.ApplicationManager;
35  import com.intellij.openapi.application.ModalityState;
36  import com.intellij.openapi.fileEditor.FileDocumentManager;
37  import com.intellij.openapi.progress.ProgressIndicator;
38  import com.intellij.openapi.progress.ProgressManager;
39  import com.intellij.openapi.progress.Task;
40  import com.intellij.openapi.project.Project;
41  import com.intellij.openapi.util.Pair;
42  import com.intellij.openapi.vcs.changes.Change;
43  import com.intellij.openapi.vcs.changes.ChangeListManager;
44  import com.intellij.openapi.vcs.changes.LocalChangeList;
45  import com.intellij.openapi.wm.WindowManager;
46  import com.intellij.ui.TreeSpeedSearch;
47  import org.jetbrains.annotations.NotNull;
48  import org.jetbrains.annotations.Nullable;
49  
50  import javax.swing.*;
51  import javax.swing.Timer;
52  import javax.swing.event.DocumentEvent;
53  import javax.swing.event.DocumentListener;
54  import javax.swing.event.HyperlinkEvent;
55  import javax.swing.event.HyperlinkListener;
56  import javax.swing.tree.TreePath;
57  import java.awt.*;
58  import java.awt.event.*;
59  import java.net.MalformedURLException;
60  import java.net.URL;
61  import java.util.*;
62  import java.util.List;
63  
64  public final class IssueListToolWindowPanel extends PluginToolWindowPanel implements DataProvider, IssueActionProvider {
65  
66  	public static final String PLACE_PREFIX = IssueListToolWindowPanel.class.getSimpleName();
67  	private ProjectCfgManagerImpl projectCfgManager;
68  	private final CfgManager cfgManager;
69  	private final PluginConfiguration pluginConfiguration;
70  	private JiraWorkspaceConfiguration jiraWorkspaceConfiguration;
71  
72  	private static final String SERVERS_TOOL_BAR = "ThePlugin.JiraServers.ServersToolBar";
73  	private JIRAFilterListModel jiraFilterListModel;
74  	private JIRAIssueTreeBuilder issueTreeBuilder;
75  	private JIRAIssueListModelBuilder jiraIssueListModelBuilder;
76  	private RecentlyOpenIssuesCache recentlyOpenIssuesCache;
77  	private JIRAFilterListBuilder jiraFilterListModelBuilder;
78  	private JiraIssueGroupBy groupBy;
79  	@NotNull
80  	private final JiraManualFilterDetailsPanel manualFilterEditDetailsPanel;
81  	private JIRAFilterTree jiraFilterTree;
82  
83  	private JIRAServerFacade jiraServerFacade;
84  
85  	private JIRAIssueListModel currentIssueListModel;
86  
87  	private SearchingJIRAIssueListModel searchingIssueListModel;
88  
89  	private JIRAServerModel jiraServerModel;
90  
91  	private ConfigurationListener configListener = new LocalConfigurationListener();
92  
93  	private boolean groupSubtasksUnderParent;
94  
95  	private static final String THE_PLUGIN_JIRA_ISSUES_ISSUES_TOOL_BAR = "ThePlugin.JiraIssues.IssuesToolBar";
96  
97  	private JIRAIssueListModel baseIssueListModel;
98  
99  	private Timer timer;
100 
101 	private static final int ONE_SECOND = 1000;
102 
103 	public IssueListToolWindowPanel(@NotNull final Project project,
104 			@NotNull final ProjectCfgManagerImpl projectCfgManager,
105 			@NotNull final CfgManager cfgManager,
106 			@NotNull final PluginConfiguration pluginConfiguration,
107 			@NotNull final JiraWorkspaceConfiguration jiraWorkspaceConfiguration,
108 			@NotNull final IssueToolWindowFreezeSynchronizator freezeSynchronizator,
109 			@NotNull final JIRAIssueListModel issueModel,
110 			@NotNull final JIRAIssueListModelBuilder jiraIssueListModelBuilder,
111 			@NotNull final RecentlyOpenIssuesCache recentlyOpenIssuesCache,
112 			@NotNull final JIRAFilterListBuilder filterListBuilder,
113 			@NotNull final JIRAServerModel jiraServerModel) {
114 		super(project, SERVERS_TOOL_BAR, THE_PLUGIN_JIRA_ISSUES_ISSUES_TOOL_BAR);
115 
116 		this.projectCfgManager = projectCfgManager;
117 		this.cfgManager = cfgManager;
118 		this.pluginConfiguration = pluginConfiguration;
119 		this.jiraWorkspaceConfiguration = jiraWorkspaceConfiguration;
120 		this.jiraIssueListModelBuilder = jiraIssueListModelBuilder;
121 		this.recentlyOpenIssuesCache = recentlyOpenIssuesCache;
122 
123 		jiraServerFacade = JIRAServerFacadeImpl.getInstance();
124 
125 		if (jiraWorkspaceConfiguration.getView() != null && jiraWorkspaceConfiguration.getView().getGroupBy() != null) {
126 			groupBy = jiraWorkspaceConfiguration.getView().getGroupBy();
127 			groupSubtasksUnderParent = jiraWorkspaceConfiguration.getView().isCollapseSubtasksUnderParent();
128 		} else {
129 			groupBy = JiraIssueGroupBy.TYPE;
130 			groupSubtasksUnderParent = false;
131 		}
132 		jiraFilterListModel = getJIRAFilterListModel();
133 		baseIssueListModel = issueModel;
134 		JIRAIssueListModel sortingIssueListModel = new SortingByPriorityJIRAIssueListModel(baseIssueListModel);
135 		searchingIssueListModel = new SearchingJIRAIssueListModel(sortingIssueListModel);
136 		currentIssueListModel = searchingIssueListModel;
137 
138 		issueTreeBuilder = new JIRAIssueTreeBuilder(getGroupBy(), groupSubtasksUnderParent, currentIssueListModel, project,
139 				projectCfgManager);
140 
141 		this.jiraServerModel = jiraServerModel;
142 
143 		jiraIssueListModelBuilder.setModel(baseIssueListModel);
144 		jiraFilterListModelBuilder = filterListBuilder;
145 		if (jiraFilterListModelBuilder != null) {
146 			jiraFilterListModelBuilder.setListModel(jiraFilterListModel);
147 			jiraFilterListModelBuilder.setProjectId(CfgUtil.getProjectId(project));
148 			jiraFilterListModelBuilder.setJiraWorkspaceCfg(jiraWorkspaceConfiguration);
149 		}
150 		currentIssueListModel.addModelListener(new LocalJiraIssueListModelListener());
151 
152 		currentIssueListModel.addFrozenModelListener(new FrozenModelListener() {
153 			public void modelFrozen(FrozenModel model, boolean frozen) {
154 				if (getStatusBarPane() != null) {
155 					getStatusBarPane().setEnabled(!frozen);
156 				}
157 				if (getSearchField() != null) {
158 					getSearchField().setEnabled(!frozen);
159 				}
160 			}
161 		});
162 
163 		manualFilterEditDetailsPanel = new JiraManualFilterDetailsPanel(jiraFilterListModel, this.jiraWorkspaceConfiguration,
164 				getProject(), jiraServerModel);
165 
166 		getStatusBarPane().addMoreIssuesListener(new HyperlinkListener() {
167 			public void hyperlinkUpdate(HyperlinkEvent e) {
168 				refreshIssues(false);
169 			}
170 		});
171 
172 		addIssuesTreeListeners();
173 		addSearchBoxListener();
174 		freezeSynchronizator.setIssueModel(currentIssueListModel);
175 		freezeSynchronizator.setServerModel(jiraServerModel);
176 		freezeSynchronizator.setFilterModel(jiraFilterListModel);
177 
178 		init(0);
179 
180 
181 		jiraFilterTree.addSelectionListener(new LocalJiraFilterTreeSelectionListener());
182 
183 		jiraFilterListModel.addModelListener(new JIRAFilterListModelListener() {
184 			public void manualFilterChanged(final JIRAManualFilter manualFilter, final ServerData jiraServer) {
185 				// refresh issue list
186 				refreshIssues(manualFilter, jiraServer, true);
187 			}
188 
189 			public void modelChanged(final JIRAFilterListModel listModel) {
190 			}
191 
192 			public void serverRemoved(final JIRAFilterListModel filterListModel) {
193 			}
194 
195 			public void serverAdded(final JIRAFilterListModel filterListModel) {
196 			}
197 
198 			public void serverNameChanged(final JIRAFilterListModel filterListModel) {
199 			}
200 		});
201 
202 	}
203 
204 	protected void showManualFilterPanel(final JIRAManualFilter manualFilter, final ServerData jiraServerCfg) {
205 		getSplitLeftPane().setOrientation(true);
206 		manualFilterEditDetailsPanel.setFilter(manualFilter, jiraServerCfg);
207 		getSplitLeftPane().setSecondComponent(manualFilterEditDetailsPanel);
208 		getSplitLeftPane().setProportion(MANUAL_FILTER_PROPORTION_VISIBLE);
209 	}
210 
211 	protected void hideManualFilterPanel() {
212 		getSplitLeftPane().setOrientation(true);
213 		getSplitLeftPane().setSecondComponent(null);
214 		getSplitLeftPane().setProportion(MANUAL_FILTER_PROPORTION_HIDDEN);
215 	}
216 
217 	public ProjectCfgManagerImpl getProjectCfgManager() {
218 		return projectCfgManager;
219 	}
220 
221 	public void init() {
222 		ProgressManager.getInstance().run(new Task.Backgroundable(project, "Retrieving recently viewed issues", false) {
223 			public void run(@NotNull final ProgressIndicator progressindicator) {
224 				recentlyOpenIssuesCache.loadRecenltyOpenIssues();
225 			}
226 		});
227 	}
228 
229 	@Override
230 	protected void addSearchBoxListener() {
231 		getSearchField().addDocumentListener(new DocumentListener() {
232 			public void insertUpdate(DocumentEvent e) {
233 				triggerDelayedSearchBoxUpdate();
234 			}
235 
236 			public void removeUpdate(DocumentEvent e) {
237 				triggerDelayedSearchBoxUpdate();
238 			}
239 
240 			public void changedUpdate(DocumentEvent e) {
241 				triggerDelayedSearchBoxUpdate();
242 			}
243 		});
244 		getSearchField().addKeyboardListener(new KeyListener() {
245 			public void keyTyped(KeyEvent e) {
246 			}
247 
248 			public void keyPressed(KeyEvent e) {
249 				if (e.getKeyCode() == KeyEvent.VK_ENTER) {
250 					getSearchField().addCurrentTextToHistory();
251 				}
252 			}
253 
254 			public void keyReleased(KeyEvent e) {
255 			}
256 		});
257 	}
258 
259 	private void triggerDelayedSearchBoxUpdate() {
260 		if (timer != null && timer.isRunning()) {
261 			return;
262 		}
263 		timer = new Timer(ONE_SECOND, new ActionListener() {
264 			public void actionPerformed(ActionEvent e) {
265 				searchingIssueListModel.setSearchTerm(getSearchField().getText());
266 			}
267 		});
268 		timer.setRepeats(false);
269 		timer.start();
270 	}
271 
272 	public JIRAIssueListModel getBaseIssueListModel() {
273 		return baseIssueListModel;
274 	}
275 
276 
277 	private void addIssuesTreeListeners() {
278 		getRightTree().addKeyListener(new KeyAdapter() {
279 			@Override
280 			public void keyPressed(KeyEvent e) {
281 				JIRAIssue issue = getSelectedIssue();
282 				if (e.getKeyCode() == KeyEvent.VK_ENTER && issue != null) {
283 					openIssue(issue);
284 				}
285 			}
286 		});
287 
288 		getRightTree().addMouseListener(new PopupAwareMouseAdapter() {
289 
290 			@Override
291 			public void mouseClicked(final MouseEvent e) {
292 				final JIRAIssue issue = getSelectedIssue();
293 				if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2 && issue != null) {
294 					openIssue(issue);
295 				}
296 			}
297 
298 			@Override
299 			protected void onPopup(MouseEvent e) {
300 				int selRow = getRightTree().getRowForLocation(e.getX(), e.getY());
301 				TreePath selPath = getRightTree().getPathForLocation(e.getX(), e.getY());
302 				if (selRow != -1 && selPath != null) {
303 					getRightTree().setSelectionPath(selPath);
304 					if (getSelectedIssue() != null) {
305 						launchContextMenu(e);
306 					}
307 				}
308 			}
309 		});
310 	}
311 
312 	private void launchContextMenu(MouseEvent e) {
313 		final DefaultActionGroup actionGroup = new DefaultActionGroup();
314 
315 		final ActionGroup configActionGroup = (ActionGroup) ActionManager
316 				.getInstance().getAction("ThePlugin.JiraIssues.IssuePopupMenu");
317 		actionGroup.addAll(configActionGroup);
318 
319 		final ActionPopupMenu popup = ActionManager.getInstance().createActionPopupMenu("Context menu", actionGroup);
320 		addIssueActionsSubmenu(actionGroup, popup);
321 
322 		final JPopupMenu jPopupMenu = popup.getComponent();
323 		jPopupMenu.show(e.getComponent(), e.getX(), e.getY());
324 	}
325 
326 	private void addIssueActionsSubmenu(DefaultActionGroup actionGroup, final ActionPopupMenu popup) {
327 		final DefaultActionGroup submenu = new DefaultActionGroup("Querying for Actions... ", true) {
328 			@Override
329 			public void update(AnActionEvent event) {
330 				super.update(event);
331 
332 				if (getChildrenCount() > 0) {
333 					event.getPresentation().setText("Issue Workflow Actions");
334 				}
335 			}
336 		};
337 		actionGroup.add(submenu);
338 
339 		final JIRAIssue issue = getSelectedIssue();
340 		List<JIRAAction> actions = JiraIssueAdapter.get(issue).getCachedActions();
341 		if (actions != null) {
342 			for (JIRAAction a : actions) {
343 				submenu.add(new RunIssueActionAction(this, jiraServerFacade, issue, a, jiraIssueListModelBuilder));
344 			}
345 		} else {
346 			Thread t = new Thread() {
347 				@Override
348 				public void run() {
349 					try {
350 						ServerData jiraServer = issue.getServer();
351 
352 						if (jiraServer != null) {
353 							final List<JIRAAction> actions = jiraServerFacade.getAvailableActions(jiraServer, issue);
354 
355 							JiraIssueAdapter.get(issue).setCachedActions(actions);
356 							SwingUtilities.invokeLater(new Runnable() {
357 								public void run() {
358 									JPopupMenu pMenu = popup.getComponent();
359 									if (pMenu.isVisible()) {
360 										for (JIRAAction a : actions) {
361 											submenu.add(new RunIssueActionAction(IssueListToolWindowPanel.this,
362 													jiraServerFacade, issue, a, jiraIssueListModelBuilder));
363 										}
364 
365 										// magic that makes the popup update itself. Don't ask - it is some sort of voodoo
366 										pMenu.setVisible(false);
367 										pMenu.setVisible(true);
368 									}
369 								}
370 							});
371 						}
372 					} catch (JIRAException e) {
373 						setStatusErrorMessage("Query for issue " + issue.getKey() + " actions failed: " + e.getMessage(), e);
374 					} catch (NullPointerException e) {
375 						// somebody unselected issue in the table, so let's just skip
376 					}
377 				}
378 			};
379 			t.start();
380 		}
381 	}
382 
383 	private JIRAIssue getSelectedIssue() {
384 		return getRightTree().getSelectedIssue();
385 	}
386 
387 	public void openIssue(@NotNull JIRAIssue issue) {
388 		if (issue.getServer() != null) {
389 			recentlyOpenIssuesCache.addIssue(issue);
390 			// todo check active issue
391 			IdeaHelper.getIssueDetailsToolWindow(getProject()).showIssue(issue, baseIssueListModel);
392 
393 		}
394 	}
395 
396 	public void openIssue(@NotNull final String issueKey, @NotNull final ServerData jiraServer) {
397 		JIRAIssue issue = null;
398 		for (JIRAIssue i : baseIssueListModel.getIssues()) {
399 			if (i.getKey().equals(issueKey) && i.getServer().getServerId().equals(jiraServer.getServerId())) {
400 				issue = i;
401 				break;
402 			}
403 		}
404 
405 		if (issue != null) {
406 			openIssue(issue);
407 		} else {
408 			Task.Backgroundable task = new Task.Backgroundable(getProject(), "Fetching JIRA issue " + issueKey, false) {
409 				private JIRAIssue issue;
410 				private Throwable exception;
411 
412 				@Override
413 				public void run(@NotNull ProgressIndicator progressIndicator) {
414 					progressIndicator.setIndeterminate(true);
415 					try {
416 						if (jiraServer != null) {
417 							issue = jiraServerFacade.getIssue(jiraServer, issueKey);
418 							jiraIssueListModelBuilder.updateIssue(issue);
419 //							recentlyOpenIssuesCache.addIssue(issue);
420 						} else {
421 							exception = new RuntimeException("No JIRA server defined!");
422 						}
423 					} catch (JIRAException e) {
424 						exception = e;
425 					}
426 				}
427 
428 				@Override
429 				public void onSuccess() {
430 					if (getProject().isDisposed()) {
431 						return;
432 					}
433 					if (exception != null) {
434 						final String serverName = jiraServer != null ? jiraServer.getName() : "[UNDEFINED!]";
435 						DialogWithDetails.showExceptionDialog(getProject(),
436 								"Cannot fetch issue " + issueKey + " from server " + serverName, exception);
437 						return;
438 					}
439 					if (issue != null) {
440 						openIssue(issue);
441 					}
442 				}
443 			};
444 			task.queue();
445 		}
446 	}
447 
448 	public boolean openIssue(@NotNull final String issueKey, @NotNull final String serverUrl) {
449 
450 		ServerData server = CfgUtil.findServer(
451 				serverUrl, cfgManager.getAllServers(CfgUtil.getProjectId(project), ServerType.JIRA_SERVER), projectCfgManager);
452 
453 		if (server != null) {
454 			openIssue(issueKey, server);
455 			return true;
456 		}
457 
458 		// server not found by exact url, trying to remove protocol from the address (http vs https) and slash at the end
459 		URL url;
460 
461 		try {
462 			url = new URL(serverUrl);
463 		} catch (MalformedURLException e) {
464 			PluginUtil.getLogger().warn("Error opening issue. Invalid url [" + serverUrl + "]", e);
465 			return false;
466 		}
467 
468 		server = CfgUtil.findServer(url, cfgManager.getAllServers(CfgUtil.getProjectId(project), ServerType.JIRA_SERVER),
469 				projectCfgManager);
470 
471 		if (server != null) {
472 			openIssue(issueKey, server);
473 			return true;
474 		}
475 
476 		return false;
477 	}
478 
479 	public void assignIssueToMyself(@NotNull final JIRAIssue issue) {
480 		// todo remove if statement
481 		if (issue == null) {
482 			return;
483 		}
484 		try {
485 			ServerData jiraServer = getSelectedServer();
486 			if (jiraServer != null) {
487 				assignIssue(issue, jiraServer.getUserName());
488 			}
489 		} catch (NullPointerException ex) {
490 			// todo remove NPE catch
491 			// whatever, means action was called when no issue was selected. Let's just swallow it
492 		}
493 	}
494 
495 	public void assignIssueToSomebody(@NotNull final JIRAIssue issue) {
496 		if (issue == null) {
497 			return;
498 		}
499 		final GetUserNameDialog getUserNameDialog = new GetUserNameDialog(issue.getKey());
500 		getUserNameDialog.show();
501 		if (getUserNameDialog.isOK()) {
502 			try {
503 				assignIssue(issue, getUserNameDialog.getName());
504 			} catch (NullPointerException ex) {
505 				// whatever, means action was called when no issue was selected. Let's just swallow it
506 			}
507 		}
508 	}
509 
510 	private void assignIssue(final JIRAIssue issue, final String assignee) {
511 
512 		Task.Backgroundable assign = new Task.Backgroundable(getProject(), "Assigning Issue", false) {
513 
514 			@Override
515 			public void run(@NotNull final ProgressIndicator indicator) {
516 				setStatusInfoMessage("Assigning issue " + issue.getKey() + " to " + assignee + "...");
517 				try {
518 
519 					ServerData jiraServer = getSelectedServer();
520 					if (jiraServer != null) {
521 						jiraServerFacade.setAssignee(jiraServer, issue, assignee);
522 						setStatusInfoMessage("Assigned issue " + issue.getKey() + " to " + assignee);
523 						jiraIssueListModelBuilder.reloadIssue(issue.getKey(), jiraServer);
524 					}
525 				} catch (JIRAException e) {
526 					setStatusErrorMessage("Failed to assign issue " + issue.getKey() + ": " + e.getMessage(), e);
527 				}
528 			}
529 		};
530 
531 		ProgressManager.getInstance().run(assign);
532 	}
533 
534 	public boolean createChangeListAction(@NotNull final JIRAIssue issue) {
535 		String changeListName = issue.getKey() + " - " + issue.getSummary();
536 		final ChangeListManager changeListManager = ChangeListManager.getInstance(getProject());
537 		ChangesetCreate c;
538 
539 		LocalChangeList changeList = changeListManager.findChangeList(changeListName);
540 		if (changeList == null) {
541 			c = new ChangesetCreate(issue.getKey());
542 			c.setChangesetName(changeListName);
543 			c.setChangestComment(changeListName + "\n");
544 			c.setActive(true);
545 			c.show();
546 			if (c.isOK()) {
547 				changeListName = c.getChangesetName();
548 				changeList = changeListManager.addChangeList(changeListName, c.getChangesetComment());
549 				if (c.isActive()) {
550 					changeListManager.setDefaultChangeList(changeList);
551 				}
552 			}
553 		} else {
554 			changeListManager.setDefaultChangeList(changeList);
555 			return true;
556 		}
557 
558 		return c.isOK();
559 	}
560 
561 	public void addCommentToSelectedIssue() {
562 		// todo move getSelectedIssue from the model to the tree
563 		final JIRAIssue issue = getSelectedIssue();
564 		if (issue != null) {
565 			addCommentToIssue(issue.getKey(), issue.getServer());
566 		}
567 	}
568 
569 	public void addCommentToIssue(final String issueKey, final ServerData jiraServer) {
570 		final IssueCommentDialog issueCommentDialog = new IssueCommentDialog(issueKey);
571 		issueCommentDialog.show();
572 		if (issueCommentDialog.isOK()) {
573 			Task.Backgroundable comment = new Task.Backgroundable(getProject(), "Commenting Issue", false) {
574 				@Override
575 				public void run(@NotNull final ProgressIndicator indicator) {
576 					EventQueue.invokeLater(new Runnable() {
577 						public void run() {
578 							setStatusInfoMessage("Commenting issue " + issueKey + "...");
579 						}
580 					});
581 					try {
582 						if (jiraServer != null) {
583 							jiraServerFacade.addComment(jiraServer, issueKey, issueCommentDialog.getComment());
584 							EventQueue.invokeLater(new Runnable() {
585 								public void run() {
586 									setStatusInfoMessage("Commented issue " + issueKey);
587 								}
588 							});
589 						}
590 					} catch (final JIRAException e) {
591 						EventQueue.invokeLater(new Runnable() {
592 							public void run() {
593 								setStatusErrorMessage("Issue not commented: " + e.getMessage(), e);
594 							}
595 						});
596 					}
597 				}
598 			};
599 
600 			ProgressManager.getInstance().run(comment);
601 		}
602 	}
603 
604     public boolean logWorkOrDeactivateIssue(final JIRAIssue issue, final ServerData jiraServer, String initialLog,
605 			final boolean deactivateIssue, DeactivateIssueResultHandler resultHandler) {
606 		if (issue != null) {
607 			final WorkLogCreateAndMaybeDeactivateDialog dialog =
608 					new WorkLogCreateAndMaybeDeactivateDialog(jiraServer, issue, getProject(), initialLog,
609 							deactivateIssue, jiraWorkspaceConfiguration);
610 			dialog.show();
611 			if (dialog.isOK()) {
612 				Task.Backgroundable logWork =
613                         new LogWorkWorkerTask(issue, dialog, jiraServer, deactivateIssue, resultHandler);
614 				ProgressManager.getInstance().run(logWork);
615 			}
616             return dialog.isOK();
617 		}
618 		return false;
619 	}
620 
621 	/**
622 	 * Blocking method. Must be called in the background thread.
623 	 *
624 	 * @param issue issue to work on
625 	 * @return modified issue or the same issue if no modification performed
626 	 */
627 	private JIRAIssue assignIssueAndPutInProgress(@NotNull final JIRAIssue issue) {
628 		JIRAIssue updatedIssue = issue;
629 		final ServerData server = issue.getServer();
630 
631 		if (!issue.getAssigneeId().equals(server.getUserName())) {
632 			setStatusInfoMessage("Assigning issue " + issue.getKey() + " to me...");
633 			try {
634 				jiraServerFacade.setAssignee(server, issue, server.getUserName());
635 			} catch (JIRAException e) {
636 				final String msg = "Error starting progress on issue. Assigning failed: ";
637 				setStatusErrorMessage(msg + e.getMessage(), e);
638 				PluginUtil.getLogger().warn(msg + e.getMessage(), e);
639 				return updatedIssue;
640 			}
641 		}
642 
643 		setStatusInfoMessage("Retrieving available actions for issue");
644 		List<JIRAAction> actions;
645 		try {
646 			actions = jiraServerFacade.getAvailableActions(server, issue);
647 		} catch (JIRAException e) {
648 			final String msg = "Error starting progress on issue. Retrieving actions failed: ";
649 			setStatusErrorMessage(msg + e.getMessage(), e);
650 			PluginUtil.getLogger().warn(msg + e.getMessage(), e);
651 			return updatedIssue;
652 		}
653 
654 		for (JIRAAction a : actions) {
655 			if (a.getId() == Constants.JiraActionId.START_PROGRESS.getId()) {
656 				setStatusInfoMessage("Starting progress on " + issue.getKey() + "...");
657 				try {
658 					jiraServerFacade.progressWorkflowAction(server, issue, a);
659 				} catch (JIRAException e) {
660 					final String msg = "Error starting progress on issue. Perform workflow action failed: ";
661 					setStatusErrorMessage(msg + e.getMessage(), e);
662 					PluginUtil.getLogger().warn(msg + e.getMessage(), e);
663 					return updatedIssue;
664 				}
665 				JIRAIssueProgressTimestampCache.getInstance().setTimestamp(server, issue);
666 				break;
667 			}
668 		}
669 
670 		setStatusInfoMessage("Refreshing issue");
671 		try {
672 			updatedIssue = jiraServerFacade.getIssue(server, issue.getKey());
673 		} catch (JIRAException e) {
674 			setStatusErrorMessage("Error starting progress on issue: " + e.getMessage(), e);
675 			PluginUtil.getLogger().warn("Error refreshing issue: " + e.getMessage(), e);
676 			return updatedIssue;
677 		}
678 
679 		if (updatedIssue.getStatusId() != Constants.JiraStatusId.IN_PROGRESS.getId()) {
680 			setStatusErrorMessage("Progress on " + issue.getKey() + " not started on JIRA side");
681 		} else {
682 			setStatusInfoMessage("Progress on " + issue.getKey() + " started");
683 		}
684 
685 		return updatedIssue;
686 	}
687 
688 	public void startWorkingOnIssueAndActivate(@NotNull final JIRAIssue issue, final ActiveJiraIssue newActiveIssue) {
689 
690 		final boolean isOk = createChangeListAction(issue);
691 
692 		if (isOk) {
693 			ProgressManager.getInstance().run(new Task.Backgroundable(getProject(), "Starting Work on Issue", false) {
694 
695 				private JIRAIssue updatedIssue = issue;
696 
697 				@Override
698 				public void run(@NotNull final ProgressIndicator indicator) {
699 					updatedIssue = assignIssueAndPutInProgress(issue);
700 				}
701 
702 				public void onSuccess() {
703 					jiraIssueListModelBuilder.updateIssue(updatedIssue);
704 					ActiveIssueUtils.setActiveJiraIssue(project, newActiveIssue, updatedIssue);
705 				}
706 			});
707 		} else {
708 			ActiveIssueUtils.setActiveJiraIssue(project, null, issue);
709 		}
710 
711 		pluginConfiguration.getGeneralConfigurationData().bumpCounter("a");
712 	}
713 
714 	private void refreshFilterModel() {
715 		try {
716 			jiraFilterListModelBuilder.rebuildModel(jiraServerModel);
717 		} catch (JIRAFilterListBuilder.JIRAServerFiltersBuilderException e) {
718 			Collection<Throwable> exceptions = new ArrayList<Throwable>();
719 			for (JIRAException ex : e.getExceptions().values()) {
720 				exceptions.add(ex);
721 			}
722 			//@todo show in message editPane
723 			setStatusErrorMessages("Some Jira servers did not return saved filters", exceptions);
724 		}
725 	}
726 
727 
728 	public void refreshIssues(final boolean reload) {
729 		JIRAManualFilter manualFilter = jiraFilterTree.getSelectedManualFilter();
730 		JIRASavedFilter savedFilter = jiraFilterTree.getSelectedSavedFilter();
731 		ServerData serverCfg = getSelectedServer();
732 		if (savedFilter != null) {
733 			refreshIssues(savedFilter, serverCfg, reload);
734 		} else if (manualFilter != null) {
735 			refreshIssues(manualFilter, serverCfg, reload);
736 		} else if (jiraFilterTree.isRecentlyOpenSelected()) {
737 			refreshRecenltyOpenIssues(reload);
738 		}
739 	}
740 
741 	private void refreshIssues(final JIRAManualFilter manualFilter, final ServerData jiraServerCfg, final boolean reload) {
742 		if (WindowManager.getInstance().getIdeFrame(getProject()) == null) {
743 			return;
744 		}
745 		Task.Backgroundable task = new Task.Backgroundable(getProject(), "Retrieving issues", false) {
746 			@Override
747 			public void run(@NotNull final ProgressIndicator indicator) {
748 				try {
749 					getStatusBarPane().setInfoMessage("Loading issues...", false);
750 					jiraIssueListModelBuilder.addIssuesToModel(manualFilter, jiraServerCfg,
751 							pluginConfiguration.getJIRAConfigurationData().getPageSize(), reload);
752 				} catch (JIRAException e) {
753 					setStatusErrorMessage(e.getMessage(), e);
754 				}
755 			}
756 		};
757 		ProgressManager.getInstance().run(task);
758 	}
759 
760 	private void refreshIssues(final JIRASavedFilter savedFilter, final ServerData jiraServerCfg, final boolean reload) {
761 		if (WindowManager.getInstance().getIdeFrame(getProject()) == null) {
762 			return;
763 		}
764 		Task.Backgroundable task = new Task.Backgroundable(getProject(), "Retrieving issues", false) {
765 			@Override
766 			public void run(@NotNull final ProgressIndicator indicator) {
767 				try {
768 					getStatusBarPane().setInfoMessage("Loading issues...", false);
769 					jiraIssueListModelBuilder.addIssuesToModel(savedFilter, jiraServerCfg,
770 							pluginConfiguration.getJIRAConfigurationData().getPageSize(), reload);
771 				} catch (JIRAException e) {
772 					setStatusErrorMessage(e.getMessage(), e);
773 				}
774 			}
775 		};
776 		ProgressManager.getInstance().run(task);
777 	}
778 
779 
780 	private void refreshRecenltyOpenIssues(final boolean reload) {
781 		if (WindowManager.getInstance().getIdeFrame(getProject()) == null) {
782 			return;
783 		}
784 		Task.Backgroundable task = new Task.Backgroundable(getProject(), "Retrieving issues", false) {
785 			@Override
786 			public void run(@NotNull final ProgressIndicator indicator) {
787 				try {
788 					getStatusBarPane().setInfoMessage("Loading issues...", false);
789 					jiraIssueListModelBuilder.addRecenltyOpenIssuesToModel(reload);
790 				} catch (JIRAException e) {
791 					setStatusErrorMessage(e.getMessage(), e);
792 				}
793 			}
794 		};
795 		ProgressManager.getInstance().run(task);
796 	}
797 
798 	@SuppressWarnings({"UnusedDeclaration"})
799 	public void configurationUpdated(final ProjectConfiguration aProjectConfiguration) {
800 		refreshModels();
801 	}
802 
803 	/**
804 	 * Must be called from dispatch thread
805 	 */
806 	public void refreshModels() {
807 		Task.Backgroundable task = new MetadataFetcherBackgroundableTask();
808 		ProgressManager.getInstance().run(task);
809 	}
810 
811 
812 	public void projectRegistered() {
813 
814 	}
815 
816 	public JiraIssueGroupBy getGroupBy() {
817 		return groupBy;
818 	}
819 
820 	public void setGroupBy(JiraIssueGroupBy groupBy) {
821 		this.groupBy = groupBy;
822 		issueTreeBuilder.setGroupBy(groupBy);
823 		issueTreeBuilder.rebuild(getRightTree(), getRightPanel());
824 //		expandAllRightTreeNodes();
825 
826 		// store in project workspace
827 		jiraWorkspaceConfiguration.getView().setGroupBy(groupBy);
828 	}
829 
830 	public void createIssue() {
831 
832 		if (jiraIssueListModelBuilder == null) {
833 			return;
834 		}
835 
836 		final ServerData server = getSelectedServer();
837 
838 		if (server != null) {
839 			final IssueCreateDialog issueCreateDialog =
840 					new IssueCreateDialog(this, project, jiraServerModel, server, jiraWorkspaceConfiguration);
841 
842 			issueCreateDialog.initData();
843 			issueCreateDialog.show();
844 		}
845 	}
846 
847 	public ConfigurationListener getConfigListener() {
848 		return configListener;
849 	}
850 
851 	public boolean isGroupSubtasksUnderParent() {
852 		return groupSubtasksUnderParent;
853 	}
854 
855 	public void setGroupSubtasksUnderParent(boolean state) {
856 		if (state != groupSubtasksUnderParent) {
857 			groupSubtasksUnderParent = state;
858 			issueTreeBuilder.setGroupSubtasksUnderParent(groupSubtasksUnderParent);
859 			issueTreeBuilder.rebuild(getRightTree(), getRightScrollPane());
860 //			expandAllRightTreeNodes();
861 			jiraWorkspaceConfiguration.getView().setCollapseSubtasksUnderParent(groupSubtasksUnderParent);
862 		}
863 	}
864 
865 	public JIRAFilterListModel getJIRAFilterListModel() {
866 		if (jiraFilterListModel == null) {
867 			jiraFilterListModel = new JIRAFilterListModel();
868 		}
869 		return jiraFilterListModel;
870 	}
871 
872 	public ServerData getSelectedServer() {
873 		ServerData server = jiraFilterTree != null ? jiraFilterTree.getSelectedServer() : null;
874 		if (server != null) {
875 			return server;
876 		}
877 
878 		if (projectCfgManager.getDefaultJiraServer() != null) {
879 			return projectCfgManager.getDefaultJiraServer();
880 		}
881 
882 		if (getSelectedIssue() != null) {
883 			return getSelectedIssue().getServer();
884 		}
885 
886 		return null;
887 	}
888 
889 	public boolean isRecentlyOpenFilterSelected() {
890 		return jiraFilterTree != null && jiraFilterTree.isRecentlyOpenSelected();
891 	}
892 
893 	/**
894 	 * @return list of recenlty open issues loaded earlier
895 	 */
896 	public List<JIRAIssue> getLoadedRecenltyOpenIssues() {
897 		return new ArrayList<JIRAIssue>(recentlyOpenIssuesCache.getLoadedRecenltyOpenIssues());
898 	}
899 
900 //	public List<JIRAIssue> loadRecenltyOpenIssues() {
901 //		return new ArrayList<JIRAIssue>(recentlyOpenIssuesCache.loadRecenltyOpenIssues());
902 //	}
903 
904 
905 	private class MetadataFetcherBackgroundableTask extends Task.Backgroundable {
906 		private Collection<ServerData> servers = null;
907 		private boolean refreshIssueList = false;
908 
909 		/**
910 		 * Clear server model and refill it with all enabled servers' data
911 		 */
912 		public MetadataFetcherBackgroundableTask() {
913 			super(IssueListToolWindowPanel.this.getProject(), "Retrieving JIRA information", false);
914 			fillServerData();
915 			jiraServerModel.clearAll();
916 			refreshIssueList = true;
917 		}
918 
919 		private void fillServerData() {
920 			servers = new ArrayList<ServerData>();
921 			for (JiraServerCfg serverCfg : cfgManager.getAllEnabledJiraServers(CfgUtil.getProjectId(getProject()))) {
922 				servers.add(projectCfgManager.getServerData(serverCfg));
923 			}
924 		}
925 
926 		/**
927 		 * Add requestes server's data to the server model
928 		 *
929 		 * @param server		   server added to the model with all fetched data
930 		 * @param refreshIssueList refresh issue list
931 		 */
932 		public MetadataFetcherBackgroundableTask(final JiraServerCfg server, boolean refreshIssueList) {
933 			super(IssueListToolWindowPanel.this.getProject(), "Retrieving JIRA information", false);
934 			this.servers = Arrays.asList(projectCfgManager.getServerData(server));
935 			this.refreshIssueList = refreshIssueList;
936 		}
937 
938 		@Override
939 		public void run(@NotNull final ProgressIndicator indicator) {
940 
941 			try {
942 				jiraServerModel.setModelFrozen(true);
943 
944 				for (ServerData server : servers) {
945 					try {
946 						//returns false if no cfg is available or login failed
947 						Boolean serverCheck = jiraServerModel.checkServer(server);
948 						if (serverCheck == null || !serverCheck) {
949 							setStatusErrorMessage("Unable to connect to server. " + jiraServerModel.getErrorMessage(server));
950 							MissingPasswordHandlerQueue.addHandler(new MissingPasswordHandlerJIRA(jiraServerFacade,
951 									(JiraServerCfg) cfgManager.getServer(CfgUtil.getProjectId(project), server), project));
952 							continue;
953 						}//@todo remove  saved filters download or merge with existing in listModel
954 
955 						final String serverStr = "[" + server.getName() + "] ";
956 						setStatusInfoMessage(serverStr + "Retrieving saved filters...");
957 						jiraServerModel.getSavedFilters(server);
958 						setStatusInfoMessage(serverStr + "Retrieving projects...");
959 						jiraServerModel.getProjects(server);
960 						setStatusInfoMessage(serverStr + "Retrieving issue types...");
961 						jiraServerModel.getIssueTypes(server, null, true);
962 						setStatusInfoMessage(serverStr + "Retrieving statuses...");
963 						jiraServerModel.getStatuses(server);
964 						setStatusInfoMessage(serverStr + "Retrieving resolutions...");
965 						jiraServerModel.getResolutions(server, true);
966 						setStatusInfoMessage(serverStr + "Retrieving priorities...");
967 						jiraServerModel.getPriorities(server, true);
968 						setStatusInfoMessage(serverStr + "Retrieving projects...");
969 						jiraServerModel.getProjects(server);
970 						setStatusInfoMessage(serverStr + "Server data query finished");
971 					} catch (RemoteApiException e) {
972 						setStatusErrorMessage("Unable to connect to server. " + jiraServerModel.getErrorMessage(server), e);
973 					} catch (JIRAException e) {
974 						setStatusErrorMessage("Cannot download details:" + e.getMessage(), e);
975 					}
976 				}
977 			} finally {
978 				// todo it should be probably called in the UI thread as most frozen listeners do something with UI controls
979 				jiraServerModel.setModelFrozen(false);
980 			}
981 		}
982 
983 		@Override
984 		public void onSuccess() {
985 			refreshFilterModel();
986 			if (refreshIssueList) {
987 				jiraFilterListModel.fireModelChanged();
988 			} else {
989 				jiraFilterListModel.fireServerAdded();
990 			}
991 		}
992 	}
993 
994 	@Nullable
995 	public Object getData(@NotNull final String dataId) {
996 		if (dataId.equals(Constants.ISSUE)) {
997 			return getSelectedIssue();
998 		}
999 		if (dataId.equals(Constants.SERVER)) {
1000 			return getSelectedServer();
1001 		}
1002 		return null;
1003 	}
1004 
1005 	private class LocalConfigurationListener extends ConfigurationListenerAdapter {
1006 
1007 		@Override
1008 		public void serverConnectionDataChanged(final ServerId serverId) {
1009 			ServerCfg server = cfgManager.getServer(CfgUtil.getProjectId(project), serverId);
1010 			if (server instanceof JiraServerCfg && server.getServerType() == ServerType.JIRA_SERVER) {
1011 				jiraServerModel.clear(server.getServerId());
1012 				Task.Backgroundable task = new MetadataFetcherBackgroundableTask((JiraServerCfg) server, true);
1013 				ProgressManager.getInstance().run(task);
1014 			}
1015 		}
1016 
1017 		@Override
1018 		public void serverNameChanged(final ServerId serverId) {
1019 			ServerCfg server = cfgManager.getServer(CfgUtil.getProjectId(project), serverId);
1020 			if (server instanceof JiraServerCfg) {
1021 				jiraServerModel.replace(projectCfgManager.getServerData(server));
1022 				refreshFilterModel();
1023 				jiraFilterListModel.fireServerNameChanged();
1024 			}
1025 		}
1026 
1027 		@Override
1028 		public void serverDisabled(final ServerId serverId) {
1029 			ServerCfg server = cfgManager.getServer(CfgUtil.getProjectId(project), serverId);
1030 			if (server instanceof JiraServerCfg && server.getServerType() == ServerType.JIRA_SERVER) {
1031 				removeServer(serverId, recenltyViewedAffected(server));
1032 			}
1033 		}
1034 
1035 		@Override
1036 		public void serverRemoved(final ServerCfg oldServer) {
1037 			if (oldServer instanceof JiraServerCfg && oldServer.getServerType() == ServerType.JIRA_SERVER) {
1038 				removeServer(oldServer.getServerId(), recenltyViewedAffected(oldServer));
1039 			}
1040 		}
1041 
1042 		@Override
1043 		public void serverEnabled(final ServerId serverId) {
1044 			ServerCfg server = cfgManager.getServer(CfgUtil.getProjectId(project), serverId);
1045 			addServer(server, recenltyViewedAffected(server));
1046 		}
1047 
1048 		@Override
1049 		public void serverAdded(final ServerCfg newServer) {
1050 			addServer(newServer, false);
1051 		}
1052 
1053 		private void addServer(final ServerCfg server, boolean refreshIssueList) {
1054 			if (server instanceof JiraServerCfg && server.getServerType() == ServerType.JIRA_SERVER) {
1055 				Task.Backgroundable task = new MetadataFetcherBackgroundableTask((JiraServerCfg) server, refreshIssueList);
1056 				ProgressManager.getInstance().run(task);
1057 
1058 			}
1059 		}
1060 
1061 		private void removeServer(final ServerId serverId, final boolean reloadIssueList) {
1062 			jiraServerModel.clear(serverId);
1063 			refreshFilterModel();
1064 			jiraFilterListModel.fireServerRemoved();
1065 
1066 			if (reloadIssueList) {
1067 				refreshRecenltyOpenIssues(true);
1068 			}
1069 		}
1070 
1071 		private boolean recenltyViewedAffected(final ServerCfg server) {
1072 			if (server instanceof JiraServerCfg && server.getServerType() == ServerType.JIRA_SERVER
1073 					&& jiraFilterTree.isRecentlyOpenSelected()) {
1074 				// check if some recenlty open issue come from enabled server; if yes then return true
1075 				JiraWorkspaceConfiguration conf = IdeaHelper.getProjectComponent(project, JiraWorkspaceConfiguration.class);
1076 				if (conf != null) {
1077 					final Collection<IssueRecentlyOpenBean> recentlyOpen = conf.getRecentlyOpenIssues();
1078 					if (recentlyOpen != null) {
1079 						for (IssueRecentlyOpenBean i : recentlyOpen) {
1080 							if (i.getServerId().equals(server.getServerId().toString())) {
1081 								return true;
1082 							}
1083 						}
1084 					}
1085 				}
1086 			}
1087 			return false;
1088 		}
1089 
1090 	}
1091 
1092 	@Override
1093 	public JTree createRightTree() {
1094 		JiraIssueListTree issueTree = new JiraIssueListTree();
1095 
1096 		new TreeSpeedSearch(issueTree) {
1097 			@Override
1098 			protected boolean isMatchingElement(Object o, String s) {
1099 				TreePath tp = (TreePath) o;
1100 				Object node = tp.getLastPathComponent();
1101 				if (node instanceof JIRAIssueTreeNode) {
1102 					JIRAIssueTreeNode jitn = (JIRAIssueTreeNode) node;
1103 					JIRAIssue issue = jitn.getIssue();
1104 					return issue.getKey().toLowerCase().contains(s.toLowerCase())
1105 							|| issue.getSummary().toLowerCase().contains(s.toLowerCase());
1106 				} else {
1107 					return super.isMatchingElement(o, s);
1108 				}
1109 			}
1110 		};
1111 
1112 		issueTreeBuilder.rebuild(issueTree, getRightPanel());
1113 		return issueTree;
1114 	}
1115 
1116 	@Override
1117 	public JiraIssueListTree getRightTree() {
1118 		return (JiraIssueListTree) super.getRightTree();
1119 	}
1120 
1121 	@Override
1122 	public JTree createLeftTree() {
1123 		if (jiraFilterTree == null) {
1124 			jiraFilterTree = new JIRAFilterTree(jiraWorkspaceConfiguration, getJIRAFilterListModel());
1125 		}
1126 
1127 		return jiraFilterTree;
1128 	}
1129 
1130 	@Override
1131 	public String getActionPlaceName() {
1132 		return PLACE_PREFIX + this.getProject().getName();
1133 	}
1134 
1135 	private class LocalJiraFilterTreeSelectionListener implements JiraFilterTreeSelectionListener {
1136 
1137 		public void selectedSavedFilterNode(final JIRASavedFilter savedFilter, final ServerData jiraServerCfg) {
1138 			hideManualFilterPanel();
1139 			refreshIssues(savedFilter, jiraServerCfg, true);
1140 			jiraWorkspaceConfiguration.getView().setViewServerId(jiraServerCfg.getServerId());
1141 			jiraWorkspaceConfiguration.getView().setViewFilterId(Long.toString(savedFilter.getId()));
1142 		}
1143 
1144 		public void selectedManualFilterNode(final JIRAManualFilter manualFilter, final ServerData jiraServerCfg) {
1145 			showManualFilterPanel(manualFilter, jiraServerCfg);
1146 			jiraWorkspaceConfiguration.getView().setViewServerId(jiraServerCfg.getServerId());
1147 			jiraWorkspaceConfiguration.getView().setViewFilterId(JiraFilterConfigurationBean.MANUAL_FILTER);
1148 
1149 			refreshIssues(manualFilter, jiraServerCfg, true);
1150 		}
1151 
1152 		public void selectionCleared() {
1153 			hideManualFilterPanel();
1154 
1155 			enableGetMoreIssues(false);
1156 
1157 			jiraWorkspaceConfiguration.getView().setViewServerId("");
1158 			jiraWorkspaceConfiguration.getView().setViewFilterId("");
1159 
1160 			jiraIssueListModelBuilder.reset();
1161 		}
1162 
1163 		public void selectedRecentlyOpenNode() {
1164 			hideManualFilterPanel();
1165 
1166 			// refresh issues view
1167 			refreshRecenltyOpenIssues(true);
1168 
1169 			jiraWorkspaceConfiguration.getView().setViewServerId("");
1170 			jiraWorkspaceConfiguration.getView().setViewFilterId(JiraFilterConfigurationBean.RECENTLY_OPEN_FILTER);
1171 		}
1172 	}
1173 
1174 	private class LogWorkWorkerTask extends Task.Backgroundable {
1175 		private final JIRAIssue issue;
1176 		private final WorkLogCreateAndMaybeDeactivateDialog dialog;
1177 		private final ServerData jiraServer;
1178 		private final boolean deactivateIssue;
1179         private DeactivateIssueResultHandler resultHandler;
1180         private boolean commitSuccess;
1181 
1182 		public LogWorkWorkerTask(JIRAIssue issue, WorkLogCreateAndMaybeDeactivateDialog dialog,
1183                                  ServerData jiraServer, boolean deactivateIssue,
1184                                  DeactivateIssueResultHandler resultHandler) {
1185 
1186 			super(IssueListToolWindowPanel.this.getProject(),
1187 					deactivateIssue ? "Stopping Work" : "Logging Work", false);
1188 
1189 			this.issue = issue;
1190 			this.dialog = dialog;
1191 			this.jiraServer = jiraServer;
1192 			this.deactivateIssue = deactivateIssue;
1193             this.resultHandler = resultHandler;
1194         }
1195 
1196 		@Override
1197 		public void run(@NotNull final ProgressIndicator indicator) {
1198 			try {
1199 				if (jiraServer != null) {
1200 					if (!deactivateIssue) {
1201                         logWork();
1202                     } else {
1203 
1204                         // 1.
1205                         commitChanges();
1206 
1207                         if (commitSuccess) {
1208                             try {
1209                                 // 2.
1210                                 logWork();
1211 
1212                                 // 3.
1213 
1214                                 // I should use a temporary variable to make the code more readable,
1215                                 // but the code is now indented funny in a weird way, so I am leaving
1216                                 // it as is so that the future generations have some fun looking at it too :P 
1217                                 if (!runWorkflowAction(new DeactivateIssueResultHandler() {
1218                                     public void success() {
1219                                         if (resultHandler != null) {
1220                                             resultHandler.success();
1221                                         }
1222                                     }
1223 
1224                                     public void failure(Throwable problem) {
1225                                         if (resultHandler != null) {
1226                                             resultHandler.failure(problem);
1227                                         }
1228                                     }
1229                                 })) {
1230                                     // workflow action not selected, need to call result handler manually
1231                                     if (resultHandler != null) {
1232                                         resultHandler.success();
1233                                     }
1234                                 }
1235 
1236                                 // 4.
1237                                 setStatusInfoMessage("Deactivated issue " + issue.getKey());
1238                                 jiraIssueListModelBuilder.reloadIssue(issue.getKey(), jiraServer);
1239                             } catch (JIRAException e) {
1240                                 if (resultHandler != null) {
1241                                     resultHandler.failure(e);
1242                                 }
1243                                 throw e;
1244                             }
1245                         } else if (resultHandler != null) {
1246                             resultHandler.failure(new JIRAException("Failed to commit changes"));
1247                         }
1248 					}
1249 				}
1250 			} catch (JIRAException e) {
1251 				if (deactivateIssue) {
1252 					setStatusErrorMessage("Issue not deactivated: " + e.getMessage(), e);
1253 				} else {
1254 					setStatusErrorMessage("Work not logged: " + e.getMessage(), e);
1255 				}
1256 			}
1257 		}
1258 
1259         private void commitChanges() {
1260             if (dialog.isCommitChanges()) {
1261                 final ChangeListManager changeListManager = ChangeListManager.getInstance(project);
1262                 final LocalChangeList list = dialog.getCurrentChangeList();
1263                 list.setComment(dialog.getComment());
1264 
1265                 ApplicationManager.getApplication().invokeAndWait(new Runnable() {
1266                     public void run() {
1267                         setStatusInfoMessage("Committing changes...");
1268                         FileDocumentManager.getInstance().saveAllDocuments();
1269                         List<Change> selectedChanges = dialog.getSelectedChanges();
1270                         commitSuccess = changeListManager.commitChangesSynchronouslyWithResult(
1271                                 list, selectedChanges);
1272                     }
1273                 }, ModalityState.defaultModalityState());
1274 
1275                 if (commitSuccess) {
1276                     WorkLogCreateAndMaybeDeactivateDialog.AfterCommit afterCommit =
1277                             dialog.getAfterCommitChangeSetAction();
1278 
1279                     switch (afterCommit) {
1280                         case DEACTIVATE_CHANGESET:
1281 
1282                             activateDefaultChangeList(changeListManager);
1283                             break;
1284                         case REMOVE_CHANGESET:
1285                             activateDefaultChangeList(changeListManager);
1286                             if (!"Default".equals(dialog.getCurrentChangeList().getName())) {
1287                                 changeListManager.removeChangeList(dialog.getCurrentChangeList());
1288                             }
1289                             break;
1290                         default:
1291                             break;
1292                     }
1293                     setStatusInfoMessage("Deactivated issue " + issue.getKey());
1294                 } else {
1295                     setStatusErrorMessage(
1296                             "Failed to commit change list while deactivating issue " + issue.getKey());
1297                 }
1298             } else {
1299                 // oh well, not having to commit is also a sort of success :) - yeah, I know, I suck
1300                 commitSuccess = true;
1301             }
1302         }
1303 
1304         private boolean runWorkflowAction(final DeactivateIssueResultHandler handler) {
1305             JIRAAction selectedAction = dialog.getSelectedAction();
1306             if (selectedAction != null) {
1307                 setStatusInfoMessage("Running action [" + selectedAction.getName()
1308                         + "] on issue " + issue.getKey());
1309                 final RunIssueActionAction riaa = new RunIssueActionAction(IssueListToolWindowPanel.this,
1310                         jiraServerFacade, issue, selectedAction, jiraIssueListModelBuilder);
1311                 SwingUtilities.invokeLater(new Runnable() {
1312                     public void run() {
1313                         riaa.runIssueAction(project, handler);
1314                     }
1315                 });
1316                 return true;
1317             }
1318             return false;
1319         }
1320 
1321         private void logWork() throws JIRAException {
1322             if (dialog.isLogTime()) {
1323                 setStatusInfoMessage("Logging work for issue " + issue.getKey() + "...");
1324                 Calendar cal = Calendar.getInstance();
1325                 cal.setTime(dialog.getStartDate());
1326 
1327                 String newRemainingEstimate = dialog.getRemainingEstimateUpdateMode()
1328                         .equals(RemainingEstimateUpdateMode.MANUAL)
1329                         ? dialog.getRemainingEstimateString() : null;
1330                 jiraServerFacade.logWork(jiraServer, issue, dialog.getTimeSpentString(),
1331                         cal, null,
1332                         !dialog.getRemainingEstimateUpdateMode()
1333                                 .equals(RemainingEstimateUpdateMode.UNCHANGED),
1334                         newRemainingEstimate);
1335                 JIRAIssueProgressTimestampCache.getInstance().setTimestamp(
1336                                     jiraServer, issue);
1337                 setStatusInfoMessage("Logged work for issue " + issue.getKey());
1338             }
1339         }
1340 
1341         private void activateDefaultChangeList(ChangeListManager changeListManager) {
1342 			List<LocalChangeList> chLists = changeListManager.getChangeLists();
1343 			for (LocalChangeList chl : chLists) {
1344 				if ("Default".equals(chl.getName())) {
1345 					changeListManager.setDefaultChangeList(chl);
1346 					break;
1347 				}
1348 			}
1349 		}
1350 	}
1351 
1352 	private class LocalJiraIssueListModelListener implements JIRAIssueListModelListener {
1353 		private boolean singleIssueChanged = false;
1354 
1355 		public void issueUpdated(final JIRAIssue issue) {
1356 			EventQueue.invokeLater(new Runnable() {
1357 				public void run() {
1358 					singleIssueChanged = true;
1359 					JiraIssueAdapter.clearCache(issue);
1360 					ActiveIssueUtils.checkIssueState(project, issue);
1361 				}
1362 			});
1363 
1364 		}
1365 
1366 		public void modelChanged(JIRAIssueListModel model) {
1367 			SwingUtilities.invokeLater(new ModelChangedRunnable());
1368 
1369 			SwingUtilities.invokeLater(new Runnable() {
1370 				public void run() {
1371 					if (!singleIssueChanged) {
1372 						jiraIssueListModelBuilder.checkActiveIssue(currentIssueListModel.getIssues());
1373 					}
1374 					singleIssueChanged = false;
1375 				}
1376 			});
1377 		}
1378 
1379 		public void issuesLoaded(JIRAIssueListModel model, int loadedIssues) {
1380 			if (loadedIssues >= pluginConfiguration.getJIRAConfigurationData().getPageSize()) {
1381 				enableGetMoreIssues(true);
1382 				setStatusInfoMessage("Loaded " + loadedIssues + " issues", true);
1383 			} else {
1384 				enableGetMoreIssues(false);
1385 				setStatusInfoMessage("Loaded " + loadedIssues + " issues");
1386 			}
1387 		}
1388 
1389 		private class ModelChangedRunnable implements Runnable {
1390 			public void run() {
1391 
1392                 // kalamon: not sure how this is possible given that both are @NotNull annotated, but see PL-1540
1393                 if (jiraServerModel == null || projectCfgManager == null) {
1394                     return;
1395                 }
1396                 
1397 				JiraIssueAdapter.clearCache();
1398 				ServerData srvcfg = getSelectedServer();
1399 				if (srvcfg == null && !isRecentlyOpenFilterSelected()) {
1400 					setStatusInfoMessage("Nothing selected");
1401 					issueTreeBuilder.rebuild(getRightTree(), getRightScrollPane());
1402 				} else if (srvcfg == null && isRecentlyOpenFilterSelected()) {
1403 					Map<Pair<String, ServerId>, String> projects = new HashMap<Pair<String, ServerId>, String>();
1404 					for (JiraServerCfg server : projectCfgManager.getCfgManager()
1405 							.getAllEnabledJiraServers(CfgUtil.getProjectId(project))) {
1406 						try {
1407 							for (JIRAProject p : jiraServerModel.getProjects(projectCfgManager.getServerData(server))) {
1408 								projects.put(new Pair<String, ServerId>(p.getKey(), server.getServerId()), p.getName());
1409 							}
1410 						} catch (JIRAException e) {
1411 							setStatusErrorMessage("Cannot retrieve projects from server [" + server.getName() + "]. "
1412 									+ e.getMessage(), e);
1413 						}
1414 					}
1415 					issueTreeBuilder.setProjectKeysToNames(projects);
1416 					issueTreeBuilder.rebuild(getRightTree(), getRightScrollPane());
1417 				} else if (srvcfg != null) {
1418 					Map<Pair<String, ServerId>, String> projectMap = new HashMap<Pair<String, ServerId>, String>();
1419 					try {
1420 						for (JIRAProject p : jiraServerModel.getProjects(srvcfg)) {
1421 							projectMap.put(new Pair<String, ServerId>(p.getKey(), new ServerId(srvcfg.getServerId())),
1422 									p.getName());
1423 						}
1424 					} catch (JIRAException e) {
1425 						setStatusErrorMessage("Cannot retrieve projects from server [" + srvcfg.getName() + "]. "
1426 								+ e.getMessage(), e);
1427 					}
1428 					issueTreeBuilder.setProjectKeysToNames(projectMap);
1429 					issueTreeBuilder.rebuild(getRightTree(), getRightScrollPane());
1430 //					expandAllRightTreeNodes();
1431 				}
1432 			}
1433 		}
1434 	}
1435 }