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.jira;
18  
19  import com.atlassian.theplugin.cfg.CfgUtil;
20  import com.atlassian.theplugin.commons.cfg.CfgManager;
21  import com.atlassian.theplugin.commons.cfg.JiraServerCfg;
22  import com.atlassian.theplugin.commons.cfg.ServerCfg;
23  import com.atlassian.theplugin.commons.cfg.ServerId;
24  import com.atlassian.theplugin.commons.configuration.PluginConfigurationBean;
25  import com.atlassian.theplugin.commons.util.LoggerImpl;
26  import com.atlassian.theplugin.configuration.JiraFilterEntryBean;
27  import com.atlassian.theplugin.configuration.JiraFiltersBean;
28  import com.atlassian.theplugin.configuration.ProjectConfigurationBean;
29  import com.atlassian.theplugin.configuration.ProjectToolWindowTableConfiguration;
30  import com.atlassian.theplugin.idea.IdeaHelper;
31  import com.atlassian.theplugin.idea.action.jira.FilterTypeAction;
32  import com.atlassian.theplugin.idea.action.jira.JIRANextPageAction;
33  import com.atlassian.theplugin.idea.action.jira.JIRAPreviousPageAcion;
34  import com.atlassian.theplugin.idea.action.jira.JIRAShowIssuesFilterAction;
35  import com.atlassian.theplugin.idea.action.jira.RunJIRAActionAction;
36  import com.atlassian.theplugin.idea.action.jira.SavedFilterComboAction;
37  import com.atlassian.theplugin.idea.jira.table.JIRATableColumnProviderImpl;
38  import com.atlassian.theplugin.idea.jira.table.columns.IssueKeyColumn;
39  import com.atlassian.theplugin.idea.jira.table.columns.IssuePriorityColumn;
40  import com.atlassian.theplugin.idea.jira.table.columns.IssueStatusColumn;
41  import com.atlassian.theplugin.idea.jira.table.columns.IssueSummaryColumn;
42  import com.atlassian.theplugin.idea.jira.table.columns.IssueTypeColumn;
43  import com.atlassian.theplugin.idea.ui.AbstractTableToolWindowPanel;
44  import com.atlassian.theplugin.idea.ui.TableColumnProvider;
45  import com.atlassian.theplugin.jira.JIRAServer;
46  import com.atlassian.theplugin.jira.JIRAServerFacade;
47  import com.atlassian.theplugin.jira.JIRAServerFacadeImpl;
48  import com.atlassian.theplugin.jira.api.JIRAAction;
49  import com.atlassian.theplugin.jira.api.JIRAException;
50  import com.atlassian.theplugin.jira.api.JIRAIssue;
51  import com.atlassian.theplugin.jira.api.JIRAQueryFragment;
52  import com.atlassian.theplugin.jira.api.JIRASavedFilterBean;
53  import com.atlassian.theplugin.remoteapi.MissingPasswordHandlerJIRA;
54  import com.intellij.ide.BrowserUtil;
55  import com.intellij.ide.DataManager;
56  import com.intellij.openapi.actionSystem.ActionGroup;
57  import com.intellij.openapi.actionSystem.ActionManager;
58  import com.intellij.openapi.actionSystem.ActionPlaces;
59  import com.intellij.openapi.actionSystem.ActionPopupMenu;
60  import com.intellij.openapi.actionSystem.ActionToolbar;
61  import com.intellij.openapi.actionSystem.AnAction;
62  import com.intellij.openapi.actionSystem.AnActionEvent;
63  import com.intellij.openapi.actionSystem.DefaultActionGroup;
64  import com.intellij.openapi.project.Project;
65  import com.intellij.openapi.ui.popup.JBPopupFactory;
66  import com.intellij.openapi.ui.popup.PopupChooserBuilder;
67  import com.intellij.openapi.util.Key;
68  import com.intellij.openapi.vcs.changes.ChangeListManager;
69  import com.intellij.openapi.vcs.changes.LocalChangeList;
70  import com.intellij.util.ui.UIUtil;
71  
72  import javax.swing.*;
73  import java.awt.*;
74  import java.util.ArrayList;
75  import java.util.Calendar;
76  import java.util.List;
77  import java.util.Map;
78  
79  public class JIRAToolWindowPanel extends AbstractTableToolWindowPanel<JiraIssueAdapter> {
80      private static final int PAGE_SIZE = 50;
81  	public static final String JIRA_ATLASSIAN_TOOLWINDOW_SERVER_TOOL_BAR = "atlassian.jira.toolwindow";
82  	private static final Key<JIRAToolWindowPanel> WINDOW_PROJECT_KEY =  Key.create(JIRAToolWindowPanel.class.getName());
83  	
84  	private transient ActionToolbar filterToolbar;
85      private transient ActionToolbar filterEditToolbar;
86      private JIRAIssueFilterPanel jiraIssueFilterPanel;
87  
88      private TableColumnProvider columnProvider;
89  
90      private transient JiraFiltersBean filters;
91      private transient JIRAQueryFragment savedQuery;
92      private final List<JIRAQueryFragment> advancedQuery;
93  
94      private final transient JIRAServerFacade jiraServerFacade;
95      private final transient PluginConfigurationBean pluginConfiguration;
96  
97      private int maxIndex = PAGE_SIZE;
98      private int startIndex = 0;
99      private boolean nextPageAvailable = false;
100     private boolean prevPageAvailable = false;
101     private String sortColumn = "issuekey";
102     private String sortOrder = "ASC";
103 
104 
105 
106     private transient JIRAIssue selectedIssue = null;
107 	private CfgManager cfgManager;
108 
109 	@Override
110 	protected void handlePopupClick(Object selectedObject) {
111         selectedIssue = ((JiraIssueAdapter) selectedObject).getIssue();
112     }
113 
114 	public static JIRAToolWindowPanel getInstance(Project project, ProjectConfigurationBean projectConfigurationBean,
115 			final CfgManager cfgManager) {
116 
117         JIRAToolWindowPanel window = project.getUserData(WINDOW_PROJECT_KEY);
118 
119         if (window == null) {
120             window = new JIRAToolWindowPanel(project, IdeaHelper.getPluginConfiguration(),
121 					projectConfigurationBean, cfgManager);
122             project.putUserData(WINDOW_PROJECT_KEY, window);
123         }
124         return window;
125     }
126 
127 	@Override
128 	protected void handleDoubleClick(Object selectedObject) {
129 		AnAction action = ActionManager.getInstance().getAction("ThePlugin.JIRA.OpenIssue");
130 		action.actionPerformed(new AnActionEvent(null, DataManager.getInstance().getDataContext(this),
131 				ActionPlaces.UNKNOWN, action.getTemplatePresentation(),
132 				ActionManager.getInstance(), 0));
133     }
134 
135     @Override
136 	protected String getInitialMessage() {
137         return "Select a JIRA server to retrieve your issues.";
138     }
139 
140     @Override
141 	protected String getToolbarActionGroup() {
142         return "ThePlugin.JIRA.ServerToolBar";
143     }
144 
145     @Override
146 	protected String getPopupActionGroup() {
147         return "ThePlugin.JIRA.IssuePopupMenu";
148     }
149 
150     @Override
151 	protected TableColumnProvider getTableColumnProvider() {
152         if (columnProvider == null) {
153             columnProvider = new JIRATableColumnProviderImpl();
154         }
155         return columnProvider;
156     }
157 
158     @Override
159 	protected ProjectToolWindowTableConfiguration getTableConfiguration() {
160         return projectConfiguration.getJiraConfiguration().getTableConfiguration();
161     }
162 
163     public JIRAToolWindowPanel(Project project, PluginConfigurationBean pluginConfiguration,
164 			ProjectConfigurationBean projectConfigurationBean, final CfgManager cfgManager) {
165         super(project, projectConfigurationBean);
166 		this.cfgManager = cfgManager;
167 		this.project = project;
168 		this.pluginConfiguration = pluginConfiguration;
169         this.jiraServerFacade = JIRAServerFacadeImpl.getInstance();
170         this.advancedQuery = new ArrayList<JIRAQueryFragment>();
171 
172         createFilterToolBar();
173         createFilterEditToolBar();
174 
175         jiraIssueFilterPanel = new JIRAIssueFilterPanel(progressAnimation);
176     }
177 
178     public List<JiraFilterEntryBean> serializeQuery() {
179         List<JiraFilterEntryBean> query = new ArrayList<JiraFilterEntryBean>();
180         for (JIRAQueryFragment jiraQueryFragment : advancedQuery) {
181             query.add(new JiraFilterEntryBean(jiraQueryFragment.getMap()));
182         }
183         return query;
184     }
185 
186     public void restoreQuery(List<JiraFilterEntryBean> query, JiraFilterEntryBean savedFilter) {
187         advancedQuery.clear();
188         for (JiraFilterEntryBean filterMapBean : query) {
189             Map<String, String> filter = filterMapBean.getFilterEntry();
190             String className = filter.get("filterTypeClass");
191             try {
192                 Class<?> c = Class.forName(className);
193                 advancedQuery.add((JIRAQueryFragment) c.getConstructor(Map.class).newInstance(filter));
194             } catch (Exception e) {
195 				LoggerImpl.getInstance().error(e);
196             }
197         }
198         if (savedFilter != null) {
199             savedQuery = new JIRASavedFilterBean(savedFilter.getFilterEntry());
200         } else {
201             savedQuery = null;
202         }
203     }
204 
205     @Override
206 	public void applyAdvancedFilter() {
207         advancedQuery.clear();
208         advancedQuery.addAll(jiraIssueFilterPanel.getFilter());
209         startIndex = 0;
210         updateIssues(IdeaHelper.getCurrentJIRAServer(project));
211         filters.setManualFilter(serializeQuery());
212         filters.setSavedFilterUsed(false);
213 		JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
214 
215 		if (jiraServer != null) {
216 		projectConfiguration.
217                 getJiraConfiguration().setFiltersBean(
218 				jiraServer.getServer().getServerId().toString(), filters);
219 		}
220 
221 		hideJIRAIssuesFilter();
222         filterToolbarSetVisible(true);
223     }
224 
225     @Override
226 	public void cancelAdvancedFilter() {
227         filters.setManualFilter(serializeQuery());
228         filters.setSavedFilterUsed(false);
229 		JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
230 
231 		if (jiraServer != null) {
232 			projectConfiguration.getJiraConfiguration()
233 					.setFiltersBean(jiraServer.getServer().getServerId().toString(), filters);
234 		}
235 
236 		hideJIRAIssuesFilter();
237         filterToolbarSetVisible(true);
238     }
239 
240     @Override
241 	public void clearAdvancedFilter() {
242         advancedQuery.clear();
243         JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
244         if (jiraServer != null) {
245             jiraIssueFilterPanel.setJiraServer(jiraServer, advancedQuery);
246         }
247         filterToolbarSetVisible(false);
248         filterEditToolbarSetVisible(true);
249         setScrollPaneViewport(jiraIssueFilterPanel.$$$getRootComponent$$$());
250     }
251 
252 	public String getActionPlace() {
253 		return JIRA_ATLASSIAN_TOOLWINDOW_SERVER_TOOL_BAR + project.getName();
254 	}
255 
256 	public final void hideJIRAIssuesFilter() {
257         setScrollPaneViewport(table);
258         filterEditToolbarSetVisible(false);
259     }
260 
261     public final void showJIRAIssueFilter() {
262         JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
263         if (jiraServer != null) {
264             jiraIssueFilterPanel.setJiraServer(jiraServer, advancedQuery);
265         }
266         filterToolbarSetVisible(false);
267         filterEditToolbarSetVisible(true);
268         setScrollPaneViewport(jiraIssueFilterPanel.$$$getRootComponent$$$());
269     }
270 
271 
272     public void viewIssue() {
273 		final JiraIssueAdapter issueAdapter = table.getSelectedObject();
274 		if (issueAdapter == null) {
275 			return;
276 		}
277 		JIRAIssue issue = issueAdapter.getIssue();
278         BrowserUtil.launchBrowser(issue.getIssueUrl());
279     }
280 
281     public void editIssue() {
282 		final JiraIssueAdapter issueAdapter = table.getSelectedObject();
283 		if (issueAdapter == null) {
284 			return;
285 		}
286 		JIRAIssue issue = issueAdapter.getIssue();
287         BrowserUtil.launchBrowser(issue.getServerUrl() + "/secure/EditIssue!default.jspa?key=" + issue.getKey());
288     }
289 
290     public void showIssueActions() {
291 		final JiraIssueAdapter adapter = table.getSelectedObject();
292 		if (adapter == null) {
293 			return;
294 		}
295 		final JIRAIssue issue = adapter.getIssue();
296 		List<JIRAAction> actions = adapter.getCachedActions();
297 		if (actions != null) {
298 			showActionsPopup(adapter, actions);
299 		} else {
300 			Thread thread = new Thread("atlassian-idea-plugin show issue actions") {
301                 @Override
302 				public void run() {
303                     setStatusMessage("Getting available issue actions for issue " + issue.getKey() + "...");
304                     try {
305 						JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
306 						if (jiraServer != null) {
307 							List<JIRAAction> actions =
308 									jiraServerFacade.getAvailableActions(jiraServer.getServer(), issue);
309 							adapter.setCachedActions(actions);
310 							setStatusMessage("Retrieved actions for issue " + issue.getKey());
311 							showActionsPopup(adapter, actions);
312 						}
313 					} catch (JIRAException e) {
314                         setStatusMessage("Unable to retrieve available issue actions: " + e.getMessage(), true);
315                     }
316                 }
317 
318             };
319             thread.start();
320 		}
321     }
322 
323 	public void selectLastActiveServer() {
324 
325 		String uuidString = projectConfiguration.getJiraConfiguration().getSelectedServerId();
326 
327 		if (uuidString != null) {
328 				final ServerId serverId = new ServerId(uuidString);
329 				ServerCfg serverCfg = cfgManager.getServer(CfgUtil.getProjectId(project), serverId);
330 				if (serverCfg != null && serverCfg instanceof JiraServerCfg && serverCfg.isEnabled()) {
331 					selectServer((JiraServerCfg) serverCfg);
332 				}
333 			}
334 	}
335 
336 	final class ActionsPopupListener implements Runnable {
337 		private JiraIssueAdapter adapter;
338 		private JList list;
339 
340 		public ActionsPopupListener(JiraIssueAdapter adapter, JList list) {
341 			this.adapter = adapter;
342 			this.list = list;
343 		}
344 
345 		public void run() {
346 			JIRAAction action = (JIRAAction) list.getSelectedValue();
347 			RunJIRAActionAction ja = new RunJIRAActionAction(JIRAToolWindowPanel.this, jiraServerFacade, adapter, action);
348 			ja.runIssueActionOrLaunchBrowser(project);
349 		}
350 	}
351 
352 	private void showActionsPopup(JiraIssueAdapter adapter, List<JIRAAction> actions) {
353 		JList list = new JList();
354 		list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
355 		DefaultListModel lm = new DefaultListModel();
356 		for (JIRAAction a : actions) {
357 			lm.addElement(a);
358 		}
359 		list.setModel(lm);
360 		PopupChooserBuilder builder = JBPopupFactory.getInstance().createListPopupBuilder(list);
361 	    builder.setTitle("Actions available for " + adapter.getKey())
362 			.setRequestFocus(true)
363 			.setResizable(false)
364 			.setMovable(true)
365 			.setItemChoosenCallback(new ActionsPopupListener(adapter, list))
366 			.createPopup()
367 			.showInCenterOf(this);
368 	}
369 
370 	@Override
371 	protected void addCustomSubmenus(DefaultActionGroup actionGroup, final ActionPopupMenu popup) {
372 		final DefaultActionGroup submenu = new DefaultActionGroup("Querying For Actions... ", true) {
373 			@Override
374 			public void update(AnActionEvent event) {
375 				super.update(event);
376 
377 				if (getChildrenCount() > 0) {
378 					event.getPresentation().setText("Issue Actions");
379 				}
380 			}
381 		};
382 		actionGroup.add(submenu);
383 
384 		final JiraIssueAdapter adapter = table.getSelectedObject();
385 		if (adapter == null) {
386 			return;
387 		}
388 		final JIRAIssue issue = adapter.getIssue();
389 		List<JIRAAction> actions = adapter.getCachedActions();
390 		if (actions != null) {
391 			for (JIRAAction a : actions) {
392 				submenu.add(new RunJIRAActionAction(this, jiraServerFacade, adapter, a));
393 			}
394 		} else {
395 			new Thread() {
396 				@Override
397 				public void run() {
398 					try {
399 						JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
400 
401 						if (jiraServer != null) {
402 							final List<JIRAAction> actions =
403 									jiraServerFacade.getAvailableActions(jiraServer.getServer(), issue);
404 							adapter.setCachedActions(actions);
405 							SwingUtilities.invokeLater(new Runnable() {
406 								public void run() {
407 									JPopupMenu pMenu = popup.getComponent();
408 									if (pMenu.isVisible()) {
409 										for (JIRAAction a : actions) {
410 											submenu.add(new RunJIRAActionAction(JIRAToolWindowPanel.this,
411 													jiraServerFacade, adapter, a));
412 										}
413 										// magic that makes the popup update itself. Don't ask - it is some sort of voodoo
414 										pMenu.setVisible(false);
415 										pMenu.setVisible(true);
416 									}
417 								}
418 							});
419 						}
420 					} catch (JIRAException e) {
421 						setStatusMessage("Query for issue actions failed: " + e.getMessage(), true);
422 					} catch (NullPointerException e) {
423 						// somebody unselected issue in the table, so let's just skip
424 					}
425 				}
426 			}
427 			.start();
428 		}
429 	}
430 
431 	
432 	public void refreshIssuesPage() {
433 		JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
434 		if (jiraServer != null) {
435             updateIssues(jiraServer);
436             serializeQuery();
437         }
438     }
439 
440     public void refreshIssues() {
441         startIndex = 0;
442         refreshIssuesPage();
443     }
444 
445     public void clearIssues() {
446         listTableModel.setItems(new ArrayList<JiraIssueAdapter>());
447         listTableModel.fireTableDataChanged();
448         table.setEnabled(false);
449         editorPane.setText(wrapBody("No issues for selected criteria."));
450     }
451 
452     public void setIssues(List<JIRAIssue> issues) {
453         List<JiraIssueAdapter> adapters = new ArrayList<JiraIssueAdapter>();
454         for (JIRAIssue issue : issues) {
455             adapters.add(new JiraIssueAdapter(
456                     issue,
457                     pluginConfiguration.getJIRAConfigurationData().isDisplayIconDescription()));
458         }
459         listTableModel.setItems(adapters);
460         listTableModel.fireTableDataChanged();
461         table.setEnabled(true);
462         table.setForeground(UIUtil.getActiveTextColor());
463 
464         checkPrevPageAvaialble();
465         checkNextPageAvailable(issues);
466 
467         editorPane.setText(wrapBody("Loaded <b>" + issues.size() + "</b> issues starting at <b>" + (startIndex + 1) + "</b>."));
468     }
469 
470 
471     public void selectServer(JiraServerCfg server) {
472         if (server != null) {
473             projectConfiguration.getJiraConfiguration().setSelectedServerId(server.getServerId().toString());
474             hideJIRAIssuesFilter();
475             final JIRAServer jiraServer = new JIRAServer(server, jiraServerFacade);
476             IdeaHelper.setCurrentJIRAServer(jiraServer);
477             new Thread(new SelectServerTask(jiraServer, this), "atlassian-idea-plugin jira tab select server").start();
478         }
479     }
480 
481     private final class SelectServerTask implements Runnable {
482         private JIRAServer jiraServer;
483         private JIRAToolWindowPanel jiraPanel;
484 
485         public SelectServerTask(JIRAServer jiraServer, JIRAToolWindowPanel jiraToolWindowPanel) {
486             this.jiraServer = jiraServer;
487             this.jiraPanel = jiraToolWindowPanel;
488         }
489 
490         public void run() {
491             progressAnimation.startProgressAnimation();
492             filterToolbarSetVisible(false);
493             startIndex = 0;
494             clearIssues();
495 
496             if (jiraServer.checkServer() == false) {
497                 setStatusMessage("Unable to connect to server. " + jiraServer.getErrorMessage(), true);
498                 progressAnimation.stopProgressAnimation();
499                 EventQueue.invokeLater(new MissingPasswordHandlerJIRA(jiraServerFacade, jiraServer.getServer(), jiraPanel));
500                 return;
501             }
502             setStatusMessage("Retrieving saved filters...");
503             jiraServer.getSavedFilters();
504 
505             setStatusMessage("Retrieving projects...");
506             jiraServer.getProjects();
507 
508             setStatusMessage("Retrieving issue types...");
509             jiraServer.getIssueTypes();
510 
511             setStatusMessage("Retrieving statuses...");
512             jiraServer.getStatuses();
513 
514             setStatusMessage("Retrieving resolutions...");
515             jiraServer.getResolutions();
516 
517             setStatusMessage("Retrieving priorities...");
518             jiraServer.getPriorieties();
519 
520             if (jiraServer.equals(IdeaHelper.getCurrentJIRAServer(project))) {
521                 filters = projectConfiguration.getJiraConfiguration()
522                         .getJiraFilters(IdeaHelper.getCurrentJIRAServer(project).getServer().getServerId().toString());
523                 if (filters == null) {
524                     filters = new JiraFiltersBean();
525                 }
526                 restoreQuery(filters.getManualFilter(), filters.getSavedFilter());
527                 EventQueue.invokeLater(new Runnable() {
528                     public void run() {
529                         updateIssues(jiraServer);
530                     }
531                 });
532                 filterToolbarSetVisible(true);
533             }
534 
535             progressAnimation.stopProgressAnimation();
536         }
537     }
538 
539     private void createFilterToolBar() {
540         ActionManager actionManager = ActionManager.getInstance();
541 		DefaultActionGroup filterToolBar = new DefaultActionGroup("ThePlugin.JIRA.FilterToolBar" + project.getName(), false);
542 
543 		filterToolBar.add(new FilterTypeAction());
544 		filterToolBar.add(new SavedFilterComboAction(project));
545 		filterToolBar.add(new JIRAShowIssuesFilterAction());
546 		filterToolBar.addSeparator();
547 		filterToolBar.add(new JIRAPreviousPageAcion());
548 		filterToolBar.add(new JIRANextPageAction());
549 
550 		filterToolbar = actionManager.createActionToolbar("atlassian.toolwindow.filterToolBar" + project.getName(),
551                 filterToolBar, true);
552 
553 		toolBarPanel.add(filterToolbar.getComponent(), BorderLayout.CENTER);
554         filterToolbarSetVisible(false);
555     }
556 
557     private void createFilterEditToolBar() {
558         ActionManager actionManager = ActionManager.getInstance();
559         ActionGroup filterEditToolBar = (ActionGroup) actionManager.getAction("ThePlugin.JIRA.FilterEditToolBar");
560         filterEditToolbar = actionManager.createActionToolbar("atlassian.toolwindow.filterEditToolBar",
561                 filterEditToolBar, true);
562         toolBarPanel.add(filterEditToolbar.getComponent(), BorderLayout.SOUTH);
563         filterEditToolbarSetVisible(false);
564     }
565 
566     private void filterToolbarSetVisible(boolean visible) {
567         filterToolbar.getComponent().setVisible(visible);
568     }
569 
570     @Override
571 	protected void filterEditToolbarSetVisible(boolean visible) {
572         filterEditToolbar.getComponent().setVisible(visible);
573     }
574 
575     public void prevPage() {
576         startIndex -= PAGE_SIZE;
577         checkPrevPageAvaialble();
578 		JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
579 		if (jiraServer != null) {
580 			updateIssues(jiraServer);
581 		}
582 	}
583 
584     public void nextPage() {
585         startIndex += PAGE_SIZE;
586         checkPrevPageAvaialble();
587 		JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
588 		if (jiraServer != null) {
589 			updateIssues(jiraServer);
590 		}
591 	}
592 
593     private void checkPrevPageAvaialble() {
594         if (startIndex < 0) {
595             startIndex = 0;
596         }
597         if (startIndex == 0) {
598             prevPageAvailable = false;
599         } else {
600             prevPageAvailable = true;
601         }
602     }
603 
604     private void checkNextPageAvailable(List<?> result) {
605         if (result.size() < PAGE_SIZE) {
606             nextPageAvailable = false;
607         } else {
608             nextPageAvailable = true;
609         }
610     }
611 
612     private void disablePagesButton() {
613         prevPageAvailable = false;
614         nextPageAvailable = false;
615     }
616 
617     private void updateIssues(final JIRAServer jiraServer) {
618         table.setEnabled(false);
619         table.setForeground(UIUtil.getInactiveTextColor());
620         clearIssues();
621         disablePagesButton();
622         new Thread(new IssueRefreshTask(jiraServer), "atlassian-idea-plugin jira tab update issues").start();
623     }
624 
625     private final class IssueRefreshTask implements Runnable {
626         private JIRAServer jiraServer;
627 
628         private IssueRefreshTask(JIRAServer jiraServer) {
629             this.jiraServer = jiraServer;
630         }
631 
632         public void run() {
633             progressAnimation.startProgressAnimation();
634             JIRAServerFacade serverFacade = jiraServerFacade;
635             try {
636                 List<JIRAQueryFragment> query = new ArrayList<JIRAQueryFragment>();
637                 final List<JIRAIssue> result;
638                 checkTableSort();
639                 if (filters.getSavedFilterUsed()) {
640                     if (savedQuery != null) {
641                         query.add(savedQuery);
642                         setStatusMessage("Retrieving issues from <b>" + jiraServer.getServer().getName() + "</b>...");
643                         editorPane.setCaretPosition(0);
644                         result = serverFacade.getSavedFilterIssues(jiraServer.getServer(),
645                                 query, sortColumn, sortOrder, startIndex, maxIndex);
646                         EventQueue.invokeLater(new Runnable() {
647                             public void run() {
648                                 setIssues(result);
649                             }
650                         });
651                     }
652                 } else {
653                     for (JIRAQueryFragment jiraQueryFragment : advancedQuery) {
654                         if (jiraQueryFragment.getId() != JIRAServer.ANY_ID) {
655                             query.add(jiraQueryFragment);
656                         }
657                     }
658                     setStatusMessage("Retrieving issues from <b>" + jiraServer.getServer().getName() + "</b>...");
659                     editorPane.setCaretPosition(0);
660                     result = serverFacade.getIssues(jiraServer.getServer(),
661                             query, sortColumn, sortOrder, startIndex, maxIndex);
662                     EventQueue.invokeLater(new Runnable() {
663                         public void run() {
664                             setIssues(result);
665                         }
666                     });
667                 }
668             } catch (JIRAException e) {
669                 setStatusMessage("Error contacting server <b>" + jiraServer.getServer().getName() + "</b>", true);
670             } finally {
671                 progressAnimation.stopProgressAnimation();
672             }
673         }
674     }
675 
676     private void checkTableSort() {
677 		final int sortedColumnIndex = table.getTableViewModel().getSortedColumnIndex();
678 
679 		if (sortedColumnIndex < 0) {
680 			return;
681 		}
682 
683 
684 		String columnName  = table.getTableViewModel().getColumnName(sortedColumnIndex);
685 		
686         if (IssueTypeColumn.COLUMN_NAME.equals(columnName)) {
687             sortColumn = "issuetype";
688         } else {
689             if (IssueStatusColumn.COLUMN_NAME.equals(columnName)) {
690                 sortColumn = "status";
691             } else {
692                 if (IssuePriorityColumn.COLUMN_NAME.equals(columnName)) {
693                     sortColumn = "priority";
694                 } else {
695                     if (IssueKeyColumn.COLUMN_NAME.equals(columnName)) {
696                         sortColumn = "issuekey";
697                     } else {
698                         if (IssueSummaryColumn.COLUMN_NAME.equals(columnName)) {
699                             sortColumn = "description";
700                         }
701                     }
702                 }
703             }
704         }
705         if (table.getTableViewModel().getSortingType() == 1) {
706             if (IssuePriorityColumn.COLUMN_NAME.equals(columnName)) {
707                 sortOrder = "DESC";
708             } else {
709                 sortOrder = "ASC";
710             }
711         } else {
712             if (IssuePriorityColumn.COLUMN_NAME.equals(columnName)) {
713                 sortOrder = "ASC";
714             } else {
715                 sortOrder = "DESC";
716             }
717         }
718     }
719 
720     public void addQueryFragment(JIRAQueryFragment fragment) {
721         savedQuery = fragment;
722         if (fragment != null) {
723             filters.setSavedFilter(new JiraFilterEntryBean(fragment.getMap()));
724             filters.setSavedFilterUsed(true);
725         } else {
726             filters.setSavedFilterUsed(false);
727         }
728         startIndex = 0;
729 		JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
730 		if (jiraServer != null) {
731 			projectConfiguration.getJiraConfiguration().
732 					setFiltersBean(jiraServer.getServer().getServerId().toString(), filters);
733 		}
734 	}
735 
736     @SuppressWarnings("unchecked")
737     public List<JiraIssueAdapter> getIssues() {
738         return listTableModel.getItems();
739     }
740 
741     public JIRAIssue getCurrentIssue() {
742         Object selectedObject = table.getSelectedObject();
743         if (selectedObject != null) {
744             return ((JiraIssueAdapter) selectedObject).getIssue();
745         }
746         return null;
747     }
748 
749     public void assignIssueToMyself() {
750         JiraIssueAdapter adapter = table.getSelectedObject();
751         if (adapter == null) {
752             return;
753         }
754         final JIRAIssue issue = adapter.getIssue();
755         try {
756 			JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
757 			if (jiraServer != null) {
758 				assignIssue(issue, jiraServer.getServer().getUsername());
759 			}
760 		} catch (NullPointerException ex) {
761             // whatever, means action was called when no issue was selected. Let's just swallow it
762         }
763     }
764 
765     public void assignIssueToSomebody() {
766         final JIRAIssue issue = table.getSelectedObject().getIssue();
767 
768         final GetUserNameDialog getUserNameDialog = new GetUserNameDialog(issue.getKey());
769         getUserNameDialog.show();
770         if (getUserNameDialog.isOK()) {
771             try {
772                 assignIssue(issue, getUserNameDialog.getName());
773             } catch (NullPointerException ex) {
774                 // whatever, means action was called when no issue was selected. Let's just swallow it
775             }
776         }
777     }
778 
779     public void createChangeListAction(Project projectArg) {
780         JiraIssueAdapter adapter = table.getSelectedObject();
781         if (adapter == null) {
782             return;
783         }
784         final JIRAIssue issue = adapter.getIssue();
785         String changeListName = issue.getKey() + " - " + issue.getSummary();
786         final ChangeListManager changeListManager = ChangeListManager.getInstance(projectArg);
787 
788         LocalChangeList changeList = changeListManager.findChangeList(changeListName);
789         if (changeList == null) {
790             ChangesetCreate c = new ChangesetCreate(issue.getKey());
791             c.setChangesetName(changeListName);
792             c.setChangestComment(changeListName + "\n");
793             c.setActive(true);
794             c.show();
795             if (c.isOK()) {
796                 changeListName = c.getChangesetName();
797                 changeList = changeListManager.addChangeList(changeListName, c.getChangesetComment());
798                 if (c.isActive()) {
799                     changeListManager.setDefaultChangeList(changeList);
800                 }
801             }
802         } else {
803             changeListManager.setDefaultChangeList(changeList);
804         }
805     }
806 
807     public void addCommentToIssue() {
808         JiraIssueAdapter adapter = table.getSelectedObject();
809         if (adapter == null) {
810             return;
811         }
812         final JIRAIssue issue = adapter.getIssue();
813         final IssueComment issueComment = new IssueComment(issue.getKey());
814         issueComment.show();
815         if (issueComment.isOK()) {
816             Thread thread = new Thread("atlassian-idea-plugin comment issue") {
817                 @Override
818 				public void run() {
819                     setStatusMessage("Commenting issue " + issue.getKey() + "...");
820                     try {
821 						JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
822 						if (jiraServer != null) {
823 							jiraServerFacade.addComment(jiraServer.getServer(),
824 									issue, issueComment.getComment());
825 							setStatusMessage("Commented issue " + issue.getKey());
826 						}
827 					} catch (JIRAException e) {
828                         setStatusMessage("Issue not commented: " + e.getMessage(), true);
829                     }
830                 }
831 
832             };
833             thread.start();
834         }
835     }
836 
837     public void logWorkForIssue() {
838 		JiraIssueAdapter adapter = table.getSelectedObject();
839         if (adapter == null) {
840             return;
841         }
842         final JIRAIssue issue = adapter.getIssue();
843         final WorkLogCreate workLogCreate = new WorkLogCreate(jiraServerFacade, adapter, project);
844         workLogCreate.show();
845         if (workLogCreate.isOK()) {
846             Thread thread = new Thread("atlassian-idea-plugin work log") {
847                 @Override
848 				public void run() {
849                     setStatusMessage("Logging work for issue " + issue.getKey() + "...");
850                     try {
851                         Calendar cal = Calendar.getInstance();
852                         cal.setTime(workLogCreate.getStartDate());
853 						JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
854 						
855 						if (jiraServer != null) {
856 							JiraServerCfg server = jiraServer.getServer();
857 							String newRemainingEstimate = workLogCreate.getUpdateRemainingManually()
858 									? workLogCreate.getRemainingEstimateString() : null;
859 							jiraServerFacade.logWork(server, issue, workLogCreate.getTimeSpentString(),
860 									cal, workLogCreate.getComment(),
861 									!workLogCreate.getLeaveRemainingUnchanged(), newRemainingEstimate);
862 							if (workLogCreate.isStopProgressSelected()) {
863 								setStatusMessage("Stopping work for issue " + issue.getKey() + "...");
864 								jiraServerFacade.progressWorkflowAction(server, issue, workLogCreate.getInProgressAction());
865 								setStatusMessage("Work logged and progress stopped for issue " + issue.getKey());
866 								refreshIssuesPage();
867 							} else {
868 								setStatusMessage("Logged work for issue " + issue.getKey());
869 							}
870 						}
871 					} catch (JIRAException e) {
872                         setStatusMessage("Work not logged: " + e.getMessage(), true);
873                     }
874                 }
875 
876             };
877             thread.start();
878         }
879     }
880 
881     private void assignIssue(final JIRAIssue issue, final String assignee) {
882         Thread thread = new Thread("atlassian-idea-plugin assign issue issue") {
883             @Override
884 			public void run() {
885                 setStatusMessage("Assigning issue " + issue.getKey() + " to " + assignee + "...");
886                 try {
887 
888 					JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
889 					if (jiraServer != null) {
890 						jiraServerFacade.setAssignee(jiraServer.getServer(), issue, assignee);
891 						setStatusMessage("Assigned issue " + issue.getKey() + " to " + assignee);
892 					}
893 				} catch (JIRAException e) {
894                     setStatusMessage("Failed to assign issue: " + e.getMessage(), true);
895                 }
896             }
897 
898         };
899         thread.start();
900     }
901 
902     public void createIssue() {
903 		final JIRAServer jiraServer = IdeaHelper.getCurrentJIRAServer(project);
904 
905         if (jiraServer != null) {
906             final IssueCreate issueCreate = new IssueCreate(jiraServer);
907             issueCreate.initData();
908             issueCreate.show();
909             if (issueCreate.isOK()) {
910                 Thread thread = new Thread("atlassian-idea-plugin create issue") {
911 
912                     @Override
913 					public void run() {
914                         setStatusMessage("Creating new issue...");
915                         JIRAIssue newIssue;
916                         String message;
917                         boolean isError = false;
918                         try {
919                             newIssue = jiraServerFacade.createIssue(jiraServer.getServer(), issueCreate.getJIRAIssue());
920                             message =
921                                     "New issue created: <a href="
922                                             + newIssue.getIssueUrl()
923                                             + ">"
924                                             + newIssue.getKey()
925                                             + "</a>";
926                         } catch (JIRAException e) {
927                             message = "Failed to create new issue: " + e.getMessage();
928                             isError = true;
929                         }
930 
931                         setStatusMessage(message, isError);
932                     }
933                 };
934                 thread.start();
935             }
936         }
937     }
938 
939     public JiraFiltersBean getFilters() {
940         return filters;
941     }
942 
943     public boolean isNextPageAvailable() {
944         return nextPageAvailable;
945     }
946 
947     public boolean isPrevPageAvailable() {
948         return prevPageAvailable;
949     }
950 
951     public JIRAIssue getSelectedIssue() {
952         return selectedIssue;
953     }
954 }