/*
* Copyright (c) 2012 Data Harmonisation Panel
*
* All rights reserved. This program and the accompanying materials are made
* available under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the License,
* or (at your option) any later version.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this distribution. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* HUMBOLDT EU Integrated Project #030962
* Data Harmonisation Panel <http://www.dhpanel.eu>
*/
package eu.esdihumboldt.hale.ui.views.tasks;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.layout.TreeColumnLayout;
import org.eclipse.jface.viewers.ColumnWeightData;
import org.eclipse.jface.viewers.DoubleClickEvent;
import org.eclipse.jface.viewers.IColorProvider;
import org.eclipse.jface.viewers.IDoubleClickListener;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeColumnViewerLabelProvider;
import org.eclipse.jface.viewers.TreeNode;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.jface.viewers.TreeViewerColumn;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.IActionBars;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.part.WorkbenchPart;
import eu.esdihumboldt.hale.schemaprovider.model.SchemaElement;
import eu.esdihumboldt.hale.ui.service.HaleServiceListener;
import eu.esdihumboldt.hale.ui.service.schema.SchemaService;
import eu.esdihumboldt.hale.ui.util.tree.CollectionTreeNodeContentProvider;
import eu.esdihumboldt.hale.ui.util.tree.DefaultTreeNode;
import eu.esdihumboldt.hale.ui.util.tree.MapTreeNode;
import eu.esdihumboldt.hale.ui.util.tree.SortedMapTreeNode;
import eu.esdihumboldt.hale.ui.views.schemas.ModelNavigationView;
import eu.esdihumboldt.hale.ui.views.tasks.internal.Messages;
import eu.esdihumboldt.hale.ui.views.tasks.model.ResolvedTask;
import eu.esdihumboldt.hale.ui.views.tasks.model.Task;
import eu.esdihumboldt.hale.ui.views.tasks.model.TaskUtils;
import eu.esdihumboldt.hale.ui.views.tasks.service.TaskService;
import eu.esdihumboldt.hale.ui.views.tasks.service.TaskServiceAdapter;
/**
* Task view based on a tree
*
* @author Simon Templer
* @partner 01 / Fraunhofer Institute for Computer Graphics Research
*/
public class TaskTreeView extends ViewPart {
/**
* The view ID
*/
public static String ID = "eu.esdihumboldt.hale.ui.views.tasks"; //$NON-NLS-1$
/**
* The tree viewer
*/
private TreeViewer tree;
private TaskService taskService;
private HaleServiceListener taskListener;
private SchemaService schemaService;
private MapTreeNode<SchemaElement, MapTreeNode<ResolvedTask, TreeNode>> sourceNode;
private MapTreeNode<SchemaElement, MapTreeNode<ResolvedTask, TreeNode>> targetNode;
private final Map<Task, DefaultTreeNode> taskNodes = new HashMap<Task, DefaultTreeNode>();
/**
* @see WorkbenchPart#createPartControl(Composite)
*/
@Override
public void createPartControl(Composite parent) {
Composite page = new Composite(parent, SWT.NONE);
GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
page.setLayoutData(data);
// tree column layout
TreeColumnLayout layout = new TreeColumnLayout();
page.setLayout(layout);
// tree viewer
tree = new TreeViewer(page, SWT.SINGLE | SWT.FULL_SELECTION | SWT.BORDER);
tree.setContentProvider(new CollectionTreeNodeContentProvider());
tree.setUseHashlookup(true);
tree.getTree().setHeaderVisible(true);
tree.getTree().setLinesVisible(true);
tree.setComparator(new TaskTreeComparator());
schemaService = (SchemaService) PlatformUI.getWorkbench().getService(SchemaService.class);
taskService = (TaskService) PlatformUI.getWorkbench().getService(TaskService.class);
// columns
IColorProvider colorProvider = new TaskColorProvider(Display.getCurrent());
// title/description
TreeViewerColumn description = new TreeViewerColumn(tree, SWT.LEFT);
description.getColumn().setText(Messages.TaskTreeView_TitleDescriptionText);
description.getColumn().setToolTipText(Messages.TaskTreeView_description_tooltip);
TreeColumnViewerLabelProvider descriptionLabelProvider = new TreeColumnViewerLabelProvider(
new TaskDescriptionLabelProvider(0));
descriptionLabelProvider.setProviders(colorProvider);
description.setLabelProvider(descriptionLabelProvider);
layout.setColumnData(description.getColumn(), new ColumnWeightData(4));
// value
TreeViewerColumn value = new TreeViewerColumn(tree, SWT.CENTER);
value.getColumn().setText("!"); //$NON-NLS-1$
value.getColumn().setToolTipText(Messages.TaskTreeView_value_tooltip);
TreeColumnViewerLabelProvider valueLabelProvider = new TreeColumnViewerLabelProvider(
new TaskValueLabelProvider(0));
valueLabelProvider.setProviders(colorProvider);
value.setLabelProvider(valueLabelProvider);
layout.setColumnData(value.getColumn(), new ColumnWeightData(0, 20));
// number of tasks
TreeViewerColumn number = new TreeViewerColumn(tree, SWT.RIGHT);
number.getColumn().setText("#"); //$NON-NLS-1$
number.getColumn().setToolTipText(Messages.TaskTreeView_NumberText);
TreeColumnViewerLabelProvider numberLabelProvider = new TreeColumnViewerLabelProvider(
new TaskCountLabelProvider(0));
numberLabelProvider.setProviders(colorProvider);
number.setLabelProvider(numberLabelProvider);
layout.setColumnData(number.getColumn(), new ColumnWeightData(0, 48));
// user data: status
TreeViewerColumn status = new TreeViewerColumn(tree, SWT.LEFT);
status.getColumn().setText(Messages.TaskTreeView_StatusText);
TreeColumnViewerLabelProvider statusLabelProvider = new TreeColumnViewerLabelProvider(new TaskStatusLabelProvider(0));
statusLabelProvider.setProviders(colorProvider);
status.setLabelProvider(statusLabelProvider);
layout.setColumnData(status.getColumn(), new ColumnWeightData(1));
status.setEditingSupport(new TaskStatusEditingSupport(tree, taskService));
// user data: comment
TreeViewerColumn comment = new TreeViewerColumn(tree, SWT.LEFT);
comment.getColumn().setText(Messages.TaskTreeView_CommentText);
TreeColumnViewerLabelProvider commentLabelProvider = new TreeColumnViewerLabelProvider(new TaskCommentLabelProvider(0));
commentLabelProvider.setProviders(colorProvider);
comment.setLabelProvider(commentLabelProvider);
layout.setColumnData(comment.getColumn(), new ColumnWeightData(4));
comment.setEditingSupport(new TaskCommentEditingSupport(tree, taskService));
// listeners
taskService.addListener(taskListener = new TaskServiceAdapter() {
@Override
public void tasksRemoved(final Iterable<Task> tasks) {
if (Display.getCurrent() != null) {
removeTasks(tasks);
}
else {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.syncExec(new Runnable() {
@Override
public void run() {
removeTasks(tasks);
}
});
}
}
@Override
public void tasksAdded(final Iterable<Task> tasks) {
if (Display.getCurrent() != null) {
addTasks(tasks);
}
else {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.syncExec(new Runnable() {
@Override
public void run() {
addTasks(tasks);
}
});
}
}
@Override
public void taskUserDataChanged(final ResolvedTask task) {
if (Display.getCurrent() != null) {
updateNode(task);
}
else {
final Display display = PlatformUI.getWorkbench().getDisplay();
display.syncExec(new Runnable() {
@Override
public void run() {
updateNode(task);
}
});
}
}
});
createInput();
configureActions();
// interaction
tree.addDoubleClickListener(new IDoubleClickListener() {
@Override
public void doubleClick(DoubleClickEvent event) {
ISelection selection = event.getSelection();
if (!selection.isEmpty() && selection instanceof IStructuredSelection) {
Object selected = ((IStructuredSelection) selection).getFirstElement();
if (selected instanceof TreeNode) {
// determine value
Object tmp = ((TreeNode) selected).getValue();
Object value;
if (tmp.getClass().isArray()) {
value = ((Object[]) tmp)[0];
}
else {
value = tmp;
}
if (value instanceof Task) {
// node is task node
Task task = (Task) value;
onDoubleClick(task.getMainContext().getIdentifier());
}
else if (value instanceof SchemaElement) {
SchemaElement type = (SchemaElement) value;
onDoubleClick(type.getIdentifier());
}
}
}
}
});
}
/**
* React on a double click on an item that represents the given identifier
*
* @param identifier the identifier
*/
protected void onDoubleClick(String identifier) {
//FIXME use selection mechanism instead of getting the view (real ugly)
ModelNavigationView modelView = (ModelNavigationView) PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage().findView(ModelNavigationView.ID);
if (modelView != null) {
modelView.selectItem(identifier);
}
}
private void configureActions() {
IActionBars bars = getViewSite().getActionBars();
// tool-bar
//IToolBarManager toolBar = bars.getToolBarManager();
//toolBar.add(...);
// menu
IMenuManager menu = bars.getMenuManager();
menu.add(new TaskProviderMenu());
}
/**
* Update the view
*/
private void createInput() {
TaskService taskService = (TaskService) PlatformUI.getWorkbench().getService(TaskService.class);
final Collection<TreeNode> input = new ArrayList<TreeNode>();
sourceNode = new SortedMapTreeNode<SchemaElement, MapTreeNode<ResolvedTask, TreeNode>>(Messages.TaskTreeView_SourceNodeTitle);
targetNode = new SortedMapTreeNode<SchemaElement, MapTreeNode<ResolvedTask, TreeNode>>(Messages.TaskTreeView_TargetNodeTitle);
input.add(sourceNode);
input.add(targetNode);
Collection<ResolvedTask> tasks = taskService.getResolvedTasks();
for (ResolvedTask task : tasks) {
addTask(task);
}
tree.setInput(input);
}
/**
* Add a resolved task
*
* @param task the task to add
*/
private void addTask(ResolvedTask task) {
// add task to model
MapTreeNode<ResolvedTask, TreeNode> parent = getParentNode(task, true);
if (parent != null) {
DefaultTreeNode node = new DefaultTreeNode(task);
parent.addChild(task, node);
taskNodes.put(task.getTask(), node);
// update viewer
tree.refresh(parent, true);
// update icons
TreeNode updateNode = parent.getParent();
while (updateNode != null) {
tree.update(updateNode, null);
updateNode = updateNode.getParent();
}
}
}
/**
* Adds the given tasks
*
* @param tasks the tasks to add
*/
protected void removeTasks(Iterable<Task> tasks) {
//TODO smart refresh
for (Task task : tasks) {
removeTask(task);
}
}
/**
* Remove a task
*
* @param task the task to remove
*/
@SuppressWarnings("unchecked")
private void removeTask(Task task) {
DefaultTreeNode node = taskNodes.get(task);
if (node != null) {
// remove task from model
MapTreeNode<ResolvedTask, TreeNode> parent = (MapTreeNode<ResolvedTask, TreeNode>) node.getParent();
if (parent != null) {
parent.removeChildNode(node);
taskNodes.remove(task);
// remove empty nodes
if (!parent.hasChildren()) {
MapTreeNode<SchemaElement, MapTreeNode<ResolvedTask, TreeNode>> root = (MapTreeNode<SchemaElement, MapTreeNode<ResolvedTask, TreeNode>>) parent.getParent();
root.removeChildNode(parent);
tree.refresh(root, true);
}
else {
tree.refresh(parent, true);
// update icons
TreeNode updateNode = parent.getParent();
while (updateNode != null) {
tree.update(updateNode, null);
updateNode = updateNode.getParent();
}
}
}
}
}
/**
* Update the node label for the given task
*
* @param task the task
*/
protected void updateNode(ResolvedTask task) {
DefaultTreeNode node = taskNodes.get(task.getTask());
if (node != null) {
node.setValues(task);
//refresh parent instead of update node (sorting) - tree.update(node, null);
// update parent nodes
TreeNode parent = node.getParent();
if (parent != null) {
tree.refresh(parent, true);
parent = parent.getParent();
}
else {
tree.update(node, null);
}
while (parent != null) {
tree.update(parent, null);
parent = parent.getParent();
}
}
}
/**
* Get the parent node for the given task
*
* @param task the task
* @param allowCreate allow creating the node if it does not yet exist
*
* @return the parent node
*/
private MapTreeNode<ResolvedTask, TreeNode> getParentNode(ResolvedTask task, boolean allowCreate) {
SchemaElement group = TaskUtils.getGroup(task);
if (group.getElementName().getNamespaceURI().equals(schemaService.getSourceNameSpace())) {
// source task
return getGroupNode(sourceNode, group, allowCreate);
}
else if (group.getElementName().getNamespaceURI().equals(schemaService.getTargetNameSpace())) {
// target task
return getGroupNode(targetNode, group, allowCreate);
}
else {
// invalid task ?
MapTreeNode<ResolvedTask, TreeNode> result = getGroupNode(sourceNode, group, false);
if (result == null) {
result = getGroupNode(targetNode, group, false);
}
return result;
}
}
/**
* Get the group node for the group defined by the given type definition
*
* @param rootNode the root node
* @param group the group's type definition
* @param allowCreate allow creating the node if it does not yet exist
*
* @return the group node
*/
private MapTreeNode<ResolvedTask, TreeNode> getGroupNode(
MapTreeNode<SchemaElement, MapTreeNode<ResolvedTask, TreeNode>> rootNode,
SchemaElement group, boolean allowCreate) {
MapTreeNode<ResolvedTask, TreeNode> groupNode = rootNode.getChild(group);
if (groupNode == null && allowCreate) {
groupNode = new SortedMapTreeNode<ResolvedTask, TreeNode>(group);
rootNode.addChild(group, groupNode);
// update viewer
tree.refresh(rootNode, true);
}
return groupNode;
}
/**
* Adds the given tasks
*
* @param tasks the tasks to add
*/
protected void addTasks(Iterable<Task> tasks) {
//TODO smart refresh
for (Task task : tasks) {
addTask(taskService.resolveTask(task));
}
}
/**
* @see WorkbenchPart#setFocus()
*/
@Override
public void setFocus() {
tree.getControl().setFocus();
}
/**
* @see WorkbenchPart#dispose()
*/
@Override
public void dispose() {
if (taskListener != null) {
TaskService taskService = (TaskService) PlatformUI.getWorkbench().getService(TaskService.class);
taskService.removeListener(taskListener);
}
super.dispose();
}
}