/*******************************************************************************
* Copyright (c) 2004, 2015 Tasktop Technologies and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Tasktop Technologies - initial API and implementation
* Frank Becker - fix for bug 280172
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.ui.views;
import java.text.MessageFormat;
import java.util.Iterator;
import java.util.Set;
import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.layout.GridLayoutFactory;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.util.IPropertyChangeListener;
import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.mylyn.commons.ui.CommonImages;
import org.eclipse.mylyn.commons.ui.SelectionProviderAdapter;
import org.eclipse.mylyn.commons.workbench.AbstractFilteredTree;
import org.eclipse.mylyn.commons.workbench.WorkbenchUtil;
import org.eclipse.mylyn.commons.workbench.search.SearchHistoryPopupDialog;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.ITaskListChangeListener;
import org.eclipse.mylyn.internal.tasks.core.TaskContainerDelta;
import org.eclipse.mylyn.internal.tasks.ui.TaskHistoryDropDown;
import org.eclipse.mylyn.internal.tasks.ui.TaskScalingHyperlink;
import org.eclipse.mylyn.internal.tasks.ui.TaskWorkingSetFilter;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.actions.ActivateTaskDialogAction;
import org.eclipse.mylyn.internal.tasks.ui.actions.RepositoryElementActionGroup;
import org.eclipse.mylyn.internal.tasks.ui.actions.TaskWorkingSetAction;
import org.eclipse.mylyn.internal.tasks.ui.dialogs.UiLegendDialog;
import org.eclipse.mylyn.internal.tasks.ui.editors.TaskListChangeAdapter;
import org.eclipse.mylyn.internal.tasks.ui.search.AbstractSearchHandler;
import org.eclipse.mylyn.internal.tasks.ui.search.AbstractSearchHandler.IFilterChangeListener;
import org.eclipse.mylyn.internal.tasks.ui.search.SearchUtil;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.internal.tasks.ui.workingsets.TaskWorkingSetUpdater;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.TaskActivityAdapter;
import org.eclipse.mylyn.tasks.ui.TasksUi;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.MenuDetectEvent;
import org.eclipse.swt.events.MenuDetectListener;
import org.eclipse.swt.events.MouseAdapter;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.IWorkingSet;
import org.eclipse.ui.IWorkingSetManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.forms.events.HyperlinkAdapter;
import org.eclipse.ui.forms.events.HyperlinkEvent;
import org.eclipse.ui.forms.events.IHyperlinkListener;
import org.eclipse.ui.forms.widgets.ImageHyperlink;
/**
* @author Mik Kersten
* @author Leo Dos Santos - Task Working Set UI
* @author David Green
*/
public class TaskListFilteredTree extends AbstractFilteredTree {
public static final String LABEL_SEARCH = Messages.TaskListFilteredTree_Search_repository_for_key_or_summary_;
private TaskScalingHyperlink workingSetLink;
private TaskScalingHyperlink activeTaskLink;
private WorkweekProgressBar taskProgressBar;
private int totalTasks;
private int completeTime;
private int completeTasks;
private int incompleteTime;
private IWorkingSet currentWorkingSet;
private MenuManager activeTaskMenuManager;
private TaskListToolTip taskListToolTip;
private ITaskListChangeListener changeListener;
private TaskListChangeAdapter taskProgressBarChangeListener;
private TaskActivityAdapter taskProgressBarActivityListener;
private IPropertyChangeListener taskProgressBarWorkingSetListener;
private TaskWorkingSetFilter workingSetFilter;
private final IWorkbenchWindow window;
private SelectionProviderAdapter activeTaskSelectionProvider;
private RepositoryElementActionGroup actionGroup;
private StructuredSelection activeSelection;
private final AbstractSearchHandler searchHandler;
private Composite searchComposite;
/**
* @param window
* can be null. Needed for the working sets to be displayed properly
*/
public TaskListFilteredTree(Composite parent, int treeStyle, AbstractSearchHandler searchHandler,
IWorkbenchWindow window) {
super(parent, treeStyle, searchHandler.createFilter());
this.searchHandler = searchHandler;
initSearchComposite();
hookContextMenu();
this.window = window;
indicateActiveTaskWorkingSet();
addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
if (changeListener != null) {
TasksUiInternal.getTaskList().removeChangeListener(changeListener);
}
if (taskProgressBarChangeListener != null) {
TasksUiInternal.getTaskList().removeChangeListener(taskProgressBarChangeListener);
}
if (taskProgressBarActivityListener != null) {
TasksUi.getTaskActivityManager().removeActivityListener(taskProgressBarActivityListener);
}
if (taskProgressBarWorkingSetListener != null) {
PlatformUI.getWorkbench()
.getWorkingSetManager()
.removePropertyChangeListener(taskProgressBarWorkingSetListener);
}
actionGroup.setSelectionProvider(null);
activeTaskMenuManager.dispose();
if (taskListToolTip != null) {
taskListToolTip.dispose();
}
}
});
}
private void hookContextMenu() {
actionGroup = new RepositoryElementActionGroup();
activeTaskMenuManager = new MenuManager("#PopupMenu"); //$NON-NLS-1$
activeTaskMenuManager.setRemoveAllWhenShown(true);
activeTaskMenuManager.addMenuListener(new IMenuListener() {
public void menuAboutToShow(IMenuManager manager) {
actionGroup.fillContextMenu(manager);
}
});
}
@Override
protected TreeViewer doCreateTreeViewer(Composite parent, int style) {
// Use a single Composite for the Tree to being able to use the
// TreeColumnLayout. See Bug 177891 for more details.
Composite container = new Composite(parent, SWT.None);
GridData gridData = new GridData(SWT.FILL, SWT.FILL, true, true);
gridData.verticalIndent = 0;
gridData.horizontalIndent = 0;
container.setLayoutData(gridData);
container.setLayout(new TreeColumnLayout());
return super.doCreateTreeViewer(container, style);
}
@Override
protected Composite createProgressComposite(Composite container) {
Composite progressComposite = new Composite(container, SWT.NONE);
GridLayout progressLayout = new GridLayout(1, false);
progressLayout.marginWidth = 4;
progressLayout.marginHeight = 0;
progressLayout.marginBottom = 0;
progressLayout.horizontalSpacing = 0;
progressLayout.verticalSpacing = 0;
progressComposite.setLayout(progressLayout);
progressComposite.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false, 4, 1));
taskProgressBar = new WorkweekProgressBar(progressComposite);
taskProgressBar.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
updateTaskProgressBar();
taskProgressBarChangeListener = new TaskListChangeAdapter() {
@Override
public void containersChanged(Set<TaskContainerDelta> containers) {
for (TaskContainerDelta taskContainerDelta : containers) {
if (taskContainerDelta.getElement() instanceof ITask) {
updateTaskProgressBar();
break;
}
}
}
};
TasksUiInternal.getTaskList().addChangeListener(taskProgressBarChangeListener);
taskProgressBarActivityListener = new TaskActivityAdapter() {
@Override
public void activityReset() {
updateTaskProgressBar();
}
};
TasksUiPlugin.getTaskActivityManager().addActivityListener(taskProgressBarActivityListener);
taskProgressBarWorkingSetListener = new IPropertyChangeListener() {
public void propertyChange(PropertyChangeEvent event) {
String property = event.getProperty();
if (IWorkingSetManager.CHANGE_WORKING_SET_CONTENT_CHANGE.equals(property)
|| IWorkingSetManager.CHANGE_WORKING_SET_REMOVE.equals(property)) {
updateTaskProgressBar();
}
}
};
PlatformUI.getWorkbench().getWorkingSetManager().addPropertyChangeListener(taskProgressBarWorkingSetListener);
return progressComposite;
}
@Override
protected Composite createSearchComposite(Composite container) {
searchComposite = new Composite(container, SWT.NONE);
GridLayout searchLayout = new GridLayout(2, false);
searchLayout.marginHeight = 0;
searchLayout.marginWidth = 0;
searchComposite.setLayout(searchLayout);
searchComposite.setLayoutData(new GridData(SWT.FILL, SWT.DEFAULT, true, false, 4, 1));
return searchComposite;
}
private void initSearchComposite() {
searchHandler.createSearchComposite(searchComposite);
searchHandler.adaptTextSearchControl(getTextSearchControl().getTextControl());
searchHandler.addFilterChangeListener(new IFilterChangeListener() {
public void filterChanged() {
getRefreshPolicy().filterChanged();
}
});
if (SearchUtil.supportsTaskSearch()) {
final TaskScalingHyperlink searchLink = new TaskScalingHyperlink(searchComposite, SWT.LEFT);
searchLink.setText(LABEL_SEARCH);
searchLink.addHyperlinkListener(new IHyperlinkListener() {
public void linkActivated(org.eclipse.ui.forms.events.HyperlinkEvent e) {
SearchUtil.openSearchDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
}
public void linkEntered(org.eclipse.ui.forms.events.HyperlinkEvent e) {
searchLink.setUnderlined(true);
}
public void linkExited(org.eclipse.ui.forms.events.HyperlinkEvent e) {
searchLink.setUnderlined(false);
}
});
}
}
private void updateTaskProgressBar() {
if (taskProgressBar.isDisposed()) {
return;
}
Set<ITask> tasksThisWeek = TasksUiPlugin.getTaskActivityManager().getScheduledForADayThisWeek();
if (workingSetFilter != null) {
for (Iterator<ITask> it = tasksThisWeek.iterator(); it.hasNext();) {
ITask task = it.next();
if (!workingSetFilter.select(task)) {
it.remove();
}
}
}
totalTasks = tasksThisWeek.size();
completeTime = 0;
completeTasks = 0;
incompleteTime = 0;
for (ITask task : tasksThisWeek) {
if (task instanceof AbstractTask) {
AbstractTask abstractTask = (AbstractTask) task;
if (task.isCompleted()) {
completeTasks++;
if (abstractTask.getEstimatedTimeHours() > 0) {
completeTime += abstractTask.getEstimatedTimeHours();
} else {
completeTime++;
}
} else {
if (abstractTask.getEstimatedTimeHours() > 0) {
incompleteTime += abstractTask.getEstimatedTimeHours();
} else {
incompleteTime++;
}
}
}
}
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
if (PlatformUI.isWorkbenchRunning() && !taskProgressBar.isDisposed()) {
taskProgressBar.reset(completeTime, (completeTime + incompleteTime));
taskProgressBar.setToolTipText(Messages.TaskListFilteredTree_Workweek_Progress
+ "\n" //$NON-NLS-1$
+ MessageFormat.format(Messages.TaskListFilteredTree_Estimated_hours, completeTime,
completeTime + incompleteTime)
+ "\n" //$NON-NLS-1$
+ MessageFormat.format(Messages.TaskListFilteredTree_Scheduled_tasks, completeTasks,
totalTasks));
}
}
});
}
@Override
protected Composite createActiveWorkingSetComposite(Composite container) {
final ImageHyperlink workingSetButton = new ImageHyperlink(container, SWT.FLAT);
workingSetButton.setImage(CommonImages.getImage(CommonImages.TOOLBAR_ARROW_RIGHT));
workingSetButton.setToolTipText(Messages.TaskListFilteredTree_Select_Working_Set);
workingSetLink = new TaskScalingHyperlink(container, SWT.LEFT);
workingSetLink.setText(TaskWorkingSetAction.LABEL_SETS_NONE);
workingSetLink.setUnderlined(false);
final TaskWorkingSetAction workingSetAction = new TaskWorkingSetAction();
workingSetButton.addHyperlinkListener(new IHyperlinkListener() {
public void linkActivated(org.eclipse.ui.forms.events.HyperlinkEvent e) {
workingSetAction.getMenu(workingSetButton).setVisible(true);
}
public void linkEntered(org.eclipse.ui.forms.events.HyperlinkEvent e) {
workingSetButton.setImage(CommonImages.getImage(CommonImages.TOOLBAR_ARROW_DOWN));
}
public void linkExited(org.eclipse.ui.forms.events.HyperlinkEvent e) {
workingSetButton.setImage(CommonImages.getImage(CommonImages.TOOLBAR_ARROW_RIGHT));
}
});
workingSetLink.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
if (currentWorkingSet != null) {
workingSetAction.run(currentWorkingSet);
} else {
workingSetAction.run();
}
}
});
return workingSetLink;
}
@Override
protected Composite createActiveTaskComposite(final Composite container) {
final ImageHyperlink activeTaskButton = new ImageHyperlink(container, SWT.LEFT);// SWT.ARROW | SWT.RIGHT);
activeTaskButton.setImage(CommonImages.getImage(CommonImages.TOOLBAR_ARROW_RIGHT));
activeTaskButton.setToolTipText(Messages.TaskListFilteredTree_Select_Active_Task);
activeTaskLink = new TaskScalingHyperlink(container, SWT.LEFT);
changeListener = new TaskListChangeAdapter() {
@Override
public void containersChanged(Set<TaskContainerDelta> containers) {
for (TaskContainerDelta taskContainerDelta : containers) {
if (taskContainerDelta.getElement() instanceof ITask) {
final AbstractTask changedTask = (AbstractTask) (taskContainerDelta.getElement());
if (changedTask.isActive()) {
if (Platform.isRunning() && PlatformUI.getWorkbench() != null) {
if (Display.getCurrent() == null) {
if (PlatformUI.getWorkbench().getDisplay() != null
&& !PlatformUI.getWorkbench().getDisplay().isDisposed()) {
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
indicateActiveTask(changedTask);
}
});
}
} else {
indicateActiveTask(changedTask);
}
}
}
}
}
}
};
TasksUiInternal.getTaskList().addChangeListener(changeListener);
activeTaskLink.setText(Messages.TaskListFilteredTree_Activate);
// avoid having the Hyperlink class show a native tooltip when it shortens the text which would overlap with the task list tooltip
activeTaskLink.setToolTipText(""); //$NON-NLS-1$
taskListToolTip = new TaskListToolTip(activeTaskLink);
ITask activeTask = TasksUi.getTaskActivityManager().getActiveTask();
if (activeTask != null) {
indicateActiveTask(activeTask);
}
activeTaskButton.addHyperlinkListener(new IHyperlinkListener() {
private Menu dropDownMenu;
public void linkActivated(HyperlinkEvent event) {
if (dropDownMenu != null) {
dropDownMenu.dispose();
}
TaskHistoryDropDown taskHistory = new TaskHistoryDropDown();
taskHistory.setScopedToWorkingSet(true);
dropDownMenu = new Menu(activeTaskButton);
taskHistory.fill(dropDownMenu, 0);
dropDownMenu.setVisible(true);
}
public void linkEntered(HyperlinkEvent event) {
activeTaskButton.setImage(CommonImages.getImage(CommonImages.TOOLBAR_ARROW_DOWN));
}
public void linkExited(HyperlinkEvent event) {
activeTaskButton.setImage(CommonImages.getImage(CommonImages.TOOLBAR_ARROW_RIGHT));
}
});
activeTaskLink.addMenuDetectListener(new MenuDetectListener() {
public void menuDetected(MenuDetectEvent e) {
// do not show menu for inactive task link
if (activeTaskSelectionProvider != null && activeSelection != null && !activeSelection.isEmpty()) {
// grab focus since the active task will become the active selection: this causes the focus listener on the task list viewer to reset the active selection when focus is set back to the task list
activeTaskLink.setFocus();
// set site selection to active task
activeTaskSelectionProvider.setSelection(activeSelection);
// show menu that has been registered with view
Menu activeTaskMenu = activeTaskMenuManager.createContextMenu(container);
activeTaskMenu.setVisible(true);
}
}
});
activeTaskLink.addMouseListener(new MouseAdapter() {
@Override
public void mouseDown(MouseEvent e) {
// only handle left clicks, context menu is handled by platform
if (e.button == 1) {
ITask activeTask = (TasksUi.getTaskActivityManager().getActiveTask());
if (activeTask == null) {
ActivateTaskDialogAction activateAction = new ActivateTaskDialogAction();
activateAction.init(PlatformUI.getWorkbench().getActiveWorkbenchWindow());
activateAction.run(null);
} else {
// if (TaskListFilteredTree.super.filterText.getText().length() > 0) {
// TaskListFilteredTree.super.filterText.setText("");
// TaskListFilteredTree.this.textChanged();
// }
// if (TaskListView.getFromActivePerspective().getDrilledIntoCategory() != null) {
// TaskListView.getFromActivePerspective().goUpToRoot();
// }
TasksUiInternal.refreshAndOpenTaskListElement(activeTask);
}
}
}
});
return activeTaskLink;
}
@Override
protected void textChanged() {
super.textChanged();
if (getFilterString() != null && !getFilterString().trim().equals("")) { //$NON-NLS-1$
setShowSearch(true);
} else {
setShowSearch(false);
}
}
public void indicateActiveTaskWorkingSet() {
if (window == null || workingSetLink == null || filterComposite == null || filterComposite.isDisposed()) {
return;
}
Set<IWorkingSet> activeSets = TaskWorkingSetUpdater.getActiveWorkingSets(window);
if (activeSets == null) {
return;
}
if (activeSets.size() == 0) {
workingSetLink.setText(TaskWorkingSetAction.LABEL_SETS_NONE);
workingSetLink.setToolTipText(Messages.TaskListFilteredTree_Edit_Task_Working_Sets_);
currentWorkingSet = null;
} else if (activeSets.size() > 1) {
workingSetLink.setText(Messages.TaskListFilteredTree__multiple_);
workingSetLink.setToolTipText(Messages.TaskListFilteredTree_Edit_Task_Working_Sets_);
currentWorkingSet = null;
} else {
Object[] array = activeSets.toArray();
IWorkingSet workingSet = (IWorkingSet) array[0];
workingSetLink.setText(workingSet.getLabel());
workingSetLink.setToolTipText(Messages.TaskListFilteredTree_Edit_Task_Working_Sets_);
currentWorkingSet = workingSet;
}
relayoutFilterControls();
}
public void indicateActiveTask(ITask task) {
if (Display.getCurrent() != null) {
if (filterComposite.isDisposed()) {
return;
}
activeTaskLink.setTask(task);
activeSelection = new StructuredSelection(task);
relayoutFilterControls();
}
}
private void relayoutFilterControls() {
filterComposite.layout();
}
public String getActiveTaskLabelText() {
return activeTaskLink.getText();
}
public void indicateNoActiveTask() {
if (filterComposite.isDisposed()) {
return;
}
activeTaskLink.setTask(null);
activeTaskLink.setText(Messages.TaskListFilteredTree_Activate);
activeTaskLink.setToolTipText(""); //$NON-NLS-1$
activeSelection = StructuredSelection.EMPTY;
relayoutFilterControls();
}
@Override
public void setFilterText(String string) {
if (filterText != null) {
filterText.setText(string);
selectAll();
}
}
@Override
protected String getFilterString() {
String text = super.getFilterString();
return (text != null) ? text.trim() : null;
}
public TaskWorkingSetFilter getWorkingSetFilter() {
return workingSetFilter;
}
public void setWorkingSetFilter(TaskWorkingSetFilter workingSetFilter) {
this.workingSetFilter = workingSetFilter;
}
public MenuManager getActiveTaskMenuManager() {
return activeTaskMenuManager;
}
public SelectionProviderAdapter getActiveTaskSelectionProvider() {
return activeTaskSelectionProvider;
}
public void setActiveTaskSelectionProvider(SelectionProviderAdapter activeTaskSelectionProvider) {
this.activeTaskSelectionProvider = activeTaskSelectionProvider;
this.actionGroup.setSelectionProvider(activeTaskSelectionProvider);
}
@SuppressWarnings("deprecation")
@Override
protected SearchHistoryPopupDialog getHistoryPopupDialog() {
return null;
}
@Override
protected Composite createAdditionalControls(Composite parent) {
Composite container = new Composite(parent, SWT.NULL);
GridLayoutFactory.fillDefaults().applyTo(container);
ImageHyperlink showUILegend = new ImageHyperlink(container, SWT.NONE);
showUILegend.setImage(CommonImages.QUESTION.createImage());
showUILegend.setToolTipText(Messages.TaskListFilteredTree_Show_Tasks_UI_Legend);
showUILegend.addHyperlinkListener(new HyperlinkAdapter() {
@Override
public void linkActivated(HyperlinkEvent e) {
UiLegendDialog uiLegendDialog = new UiLegendDialog(WorkbenchUtil.getShell());
uiLegendDialog.open();
}
});
return container;
}
}