/*******************************************************************************
* Copyright (c) 2004, 2010 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
* Jevgeni Holodkov - improvements
*******************************************************************************/
package org.eclipse.mylyn.internal.tasks.ui.views;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.jface.util.LocalSelectionTransfer;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerDropAdapter;
import org.eclipse.mylyn.internal.tasks.core.AbstractTask;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskCategory;
import org.eclipse.mylyn.internal.tasks.core.AbstractTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.DateRange;
import org.eclipse.mylyn.internal.tasks.core.LocalTask;
import org.eclipse.mylyn.internal.tasks.core.ScheduledTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.TaskCategory;
import org.eclipse.mylyn.internal.tasks.core.UncategorizedTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.UnmatchedTaskContainer;
import org.eclipse.mylyn.internal.tasks.core.UnsubmittedTaskContainer;
import org.eclipse.mylyn.internal.tasks.ui.TasksUiPlugin;
import org.eclipse.mylyn.internal.tasks.ui.util.AbstractRetrieveTitleFromUrlJob;
import org.eclipse.mylyn.internal.tasks.ui.util.TasksUiInternal;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskScheduleContentProvider.StateTaskContainer;
import org.eclipse.mylyn.internal.tasks.ui.views.TaskScheduleContentProvider.Unscheduled;
import org.eclipse.mylyn.tasks.core.AbstractRepositoryConnector;
import org.eclipse.mylyn.tasks.core.ITask;
import org.eclipse.mylyn.tasks.core.ITaskContainer;
import org.eclipse.mylyn.tasks.ui.TaskDropListener.Operation;
import org.eclipse.mylyn.tasks.ui.TasksUiUtil;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.FileTransfer;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.TransferData;
import org.eclipse.swt.dnd.URLTransfer;
import org.eclipse.ui.PlatformUI;
/**
* @author Mik Kersten
* @author Rob Elves (added URL based task creation support)
* @author Jevgeni Holodkov
* @author Sam Davis
*/
public class TaskListDropAdapter extends ViewerDropAdapter {
private boolean fileTransfer;
private boolean localTransfer;
public TaskListDropAdapter(Viewer viewer) {
super(viewer);
setFeedbackEnabled(true);
}
@Override
public boolean performDrop(final Object data) {
List<ITask> tasksToMove = new ArrayList<ITask>();
if (localTransfer) {
ISelection selection = LocalSelectionTransfer.getTransfer().getSelection();
List<ITask> tasks = TasksUiInternal.getTasksFromSelection(selection);
tasksToMove.addAll(tasks);
} else if (fileTransfer) {
// TODO implement dropping of files
} else if (data instanceof String) {
String text = (String) data;
AbstractTask task = createTaskFromUrl(text);
if (task == null) {
task = TasksUiInternal.createNewLocalTask(text);
}
if (task != null) {
tasksToMove.add(task);
final ITask newTask = task;
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
public void run() {
TasksUiUtil.openTask(newTask);
}
});
}
}
Object currentTarget = getCurrentTarget();
if (currentTarget instanceof LocalTask && areAllLocalTasks(tasksToMove) && getCurrentLocation() == LOCATION_ON) {
for (ITask task : tasksToMove) {
if (!((AbstractTask) task).contains(((LocalTask) currentTarget).getHandleIdentifier())) {
TasksUiInternal.getTaskList().addTask(task, (LocalTask) currentTarget);
}
}
} else {
if (currentTarget instanceof ITask && getCurrentLocation() == ViewerDropAdapter.LOCATION_ON
&& getCurrentOperation() != DND.DROP_MOVE) {
TasksUiInternal.getTaskDropHandler().loadTaskDropListeners();
Operation operation = (getCurrentOperation() == DND.DROP_COPY) ? Operation.COPY : Operation.LINK;
TasksUiInternal.getTaskDropHandler().fireTaskDropped(tasksToMove, (ITask) currentTarget, operation);
} else {
for (ITask task : tasksToMove) {
if (currentTarget instanceof UncategorizedTaskContainer) {
moveTask(task, (UncategorizedTaskContainer) currentTarget);
} else if (currentTarget instanceof TaskCategory) {
moveTask(task, (TaskCategory) currentTarget);
} else if (currentTarget instanceof UnmatchedTaskContainer) {
if (((UnmatchedTaskContainer) currentTarget).getRepositoryUrl().equals(task.getRepositoryUrl())) {
moveTask(task, (AbstractTaskCategory) currentTarget);
}
} else if (currentTarget instanceof ITask) {
ITask targetTask = (ITask) currentTarget;
TaskListView view = TaskListView.getFromActivePerspective();
if ((getCurrentLocation() == LOCATION_BEFORE || getCurrentLocation() == LOCATION_AFTER)
&& view != null && view.isScheduledPresentation()) {
if (targetTask instanceof AbstractTask) {
DateRange targetDate = ((AbstractTask) targetTask).getScheduledForDate();
TasksUiPlugin.getTaskActivityManager().setScheduledFor((AbstractTask) task, targetDate);
}
} else {
AbstractTaskCategory targetCategory = null;
// TODO: TaskCategory only used what about AbstractTaskCategory descendants?
ITaskContainer container = TaskCategory.getParentTaskCategory(targetTask);
if (container instanceof TaskCategory || container instanceof UncategorizedTaskContainer) {
targetCategory = (AbstractTaskCategory) container;
} else if (container instanceof UnmatchedTaskContainer) {
if (((UnmatchedTaskContainer) container).getRepositoryUrl().equals(
task.getRepositoryUrl())) {
targetCategory = (AbstractTaskCategory) container;
}
}
if (targetCategory != null) {
moveTask(task, targetCategory);
}
}
} else if (currentTarget instanceof ScheduledTaskContainer) {
ScheduledTaskContainer container = (ScheduledTaskContainer) currentTarget;
if (container instanceof Unscheduled) {
TasksUiPlugin.getTaskActivityManager().setScheduledFor((AbstractTask) task, null);
} else if (isValidTarget(container)) {
TasksUiPlugin.getTaskActivityManager().setScheduledFor((AbstractTask) task,
container.getDateRange());
}
} else if (currentTarget == null) {
moveTask(task, TasksUiPlugin.getTaskList().getDefaultCategory());
}
}
}
}
if (tasksToMove.size() == 1) {
getViewer().setSelection(new StructuredSelection(tasksToMove.get(0)));
}
return true;
}
private boolean isValidTarget(ScheduledTaskContainer container) {
// ignore incoming, outgoing, completed
return container instanceof Unscheduled || !(container instanceof StateTaskContainer);
}
private void moveTask(ITask task, AbstractTaskContainer container) {
if (!isUnsubmittedTask(task)) {
TasksUiInternal.getTaskList().addTask(task, container);
}
}
private boolean isUnsubmittedTask(ITask task) {
if (task instanceof AbstractTask) {
for (AbstractTaskContainer parent : ((AbstractTask) task).getParentContainers()) {
if (parent instanceof UnsubmittedTaskContainer) {
return true;
}
}
}
return false;
}
private boolean areAllLocalTasks(List<ITask> tasksToMove) {
for (ITask task : tasksToMove) {
if (!(task instanceof LocalTask) || isUnsubmittedTask(task)) {
return false;
}
}
return true;
}
/**
* @param data
* string containing url and title separated by <quote>\n</quote>
*/
private AbstractTask createTaskFromUrl(String data) {
if (!data.startsWith("http://") && !data.startsWith("https://")) { //$NON-NLS-1$ //$NON-NLS-2$
return null;
}
String[] urlTransfer = data.split("\n"); //$NON-NLS-1$
if (urlTransfer.length > 0) {
String url = urlTransfer[0];
AbstractRepositoryConnector connector = TasksUiPlugin.getRepositoryManager()
.getConnectorForRepositoryTaskUrl(url);
if (connector != null) {
// attempt to find task in task list
String repositoryUrl = connector.getRepositoryUrlFromTaskUrl(url);
String taskId = connector.getTaskIdFromTaskUrl(url);
AbstractTask task = TasksUiInternal.getTask(repositoryUrl, taskId, url);
if (task != null) {
return task;
}
if (repositoryUrl != null && taskId != null) {
// attempt to open task in background
// TODO: consider attaching a listener to OpenRepsitoryTaskJob to move task to drop target
TasksUiInternal.openRepositoryTask(connector.getConnectorKind(), repositoryUrl, taskId, null, 0);
}
} else {
// create local task, using title of web page as a summary
final String summary = Messages.TaskListDropAdapter__retrieving_from_URL_;
final LocalTask newTask = TasksUiInternal.createNewLocalTask(summary);
newTask.setUrl(url);
AbstractRetrieveTitleFromUrlJob job = new AbstractRetrieveTitleFromUrlJob(url) {
@Override
protected void titleRetrieved(final String pageTitle) {
// make sure summary was not changed in the mean time
if (newTask.getSummary().equals(summary)) {
newTask.setSummary(pageTitle);
TasksUiInternal.getTaskList().notifyElementChanged(newTask);
}
}
};
job.schedule();
return newTask;
}
}
return null;
}
@Override
public boolean validateDrop(Object targetObject, int operation, TransferData transferType) {
fileTransfer = false;
localTransfer = false;
if (FileTransfer.getInstance().isSupportedType(transferType)) {
fileTransfer = true;
// TODO handle all files
return false;
} else if (LocalSelectionTransfer.getTransfer().isSupportedType(transferType)) {
localTransfer = true;
Object target = getCurrentTarget();
if (target instanceof UncategorizedTaskContainer || target instanceof TaskCategory
|| target instanceof UnmatchedTaskContainer
|| (target instanceof ScheduledTaskContainer && isValidTarget((ScheduledTaskContainer) target))) {
return true;
} else if (target instanceof ITaskContainer
&& (getCurrentLocation() == ViewerDropAdapter.LOCATION_AFTER || getCurrentLocation() == ViewerDropAdapter.LOCATION_BEFORE)) {
return true;
} else if (target instanceof LocalTask && getCurrentLocation() == ViewerDropAdapter.LOCATION_ON) {
return true;
} else if (target instanceof ITask && getCurrentLocation() == ViewerDropAdapter.LOCATION_ON
&& getCurrentOperation() != DND.DROP_MOVE) {
return true;
} else {
return false;
}
} else if (URLTransfer.getInstance().isSupportedType(transferType)) {
return true;
}
return TextTransfer.getInstance().isSupportedType(transferType);
}
}