/* Copyright 2014 BarD Software s.r.o This file is part of GanttProject, an opensource project management tool. GanttProject is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GanttProject is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GanttProject. If not, see <http://www.gnu.org/licenses/>. */ package net.sourceforge.ganttproject.chart.gantt; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Set; import net.sourceforge.ganttproject.GPLogger; import net.sourceforge.ganttproject.task.ResourceAssignment; import net.sourceforge.ganttproject.task.Task; import net.sourceforge.ganttproject.task.TaskContainmentHierarchyFacade; import net.sourceforge.ganttproject.task.TaskManager; import net.sourceforge.ganttproject.task.dependency.TaskDependency; import net.sourceforge.ganttproject.util.collect.Pair; import com.google.common.base.Predicate; import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Lists; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; /** * Represents all objects which are involved into a clipboard transaction on Gantt chart: tasks, dependencies * and resource assignments. It is not really what is placed in the system clipboard, it is rather a grouping of * the model objects * * @author dbarashev (Dmitry Barashev) */ public class ClipboardContents { private static final Comparator<? super Task> IN_DOCUMENT_ORDER = new Comparator<Task>() { @Override public int compare(Task left, Task right) { return left.getManager().getTaskHierarchy().compareDocumentOrder(left, right); } }; private final List<Task> myTasks = Lists.newArrayList(); private final List<TaskDependency> myIntraDeps = Lists.newArrayList(); private final List<TaskDependency> myIncomingDeps = Lists.newArrayList(); private final List<TaskDependency> myOutgoingDeps = Lists.newArrayList(); private final List<ResourceAssignment> myAssignments = Lists.newArrayList(); private final Multimap<Task, Task> myNestedTasks = LinkedHashMultimap.create(); private final TaskManager myTaskManager; private boolean isCut; public ClipboardContents(TaskManager taskManager) { myTaskManager = taskManager; } /** * Adds tasks to the clipboard contents * @param tasks */ public void addTasks(List<Task> tasks) { myTasks.addAll(tasks); } /** * Adds appropriate objects (dependencies and assignments) to the clipboard depending on the already placed tasks. */ private void build() { TaskContainmentHierarchyFacade taskHierarchy = myTaskManager.getTaskHierarchy(); final Set<Task> subtree = Sets.newHashSet(); Predicate<Pair<Task, Task>> predicate = new Predicate<Pair<Task,Task>>() { @Override public boolean apply(Pair<Task, Task> parent_child) { subtree.add(parent_child.second()); if (parent_child.first() != null) { myNestedTasks.put(parent_child.first(), parent_child.second()); } return true; } }; Collections.sort(myTasks, IN_DOCUMENT_ORDER); for (Task t : myTasks) { taskHierarchy.breadthFirstSearch(t, predicate); } Set<TaskDependency> intraDeps = Sets.newLinkedHashSet(); for (TaskDependency dependency : myTaskManager.getDependencyCollection().getDependencies()) { Task dependant = dependency.getDependant(); Task dependee = dependency.getDependee(); if (subtree.contains(dependant) && subtree.contains(dependee)) { intraDeps.add(dependency); } } for (Task t : subtree) { for (TaskDependency dep : t.getDependenciesAsDependant().toArray()) { if (intraDeps.contains(dep)) { continue; } myIncomingDeps.add(dep); } for (TaskDependency dep : t.getDependenciesAsDependee().toArray()) { if (intraDeps.contains(dep)) { continue; } myOutgoingDeps.add(dep); } } myIntraDeps.addAll(intraDeps); GPLogger.getLogger("Clipboard").fine(String.format( "Clipboard task (only roots): %s\ninternal-dependencies: %s\nincoming dependencies:%s\noutgoing dependencies:%s", myTasks, myIntraDeps, myIncomingDeps, myOutgoingDeps)); } /** * @return all clipboard tasks */ public List<Task> getTasks() { return myTasks; } /** * @return a list of dependencies where both successor and predecessor are in clipboard for any dep */ public List<TaskDependency> getIntraDeps() { return myIntraDeps; } /** * @return a list of dependencies where only successor is in clipboard for any dep */ public List<TaskDependency> getIncomingDeps() { return myIncomingDeps; } /** * @return a list of dependencies where only predecessor is in clipboard for any dep */ public List<TaskDependency> getOutgoingDeps() { return myOutgoingDeps; } public List<ResourceAssignment> getAssignments() { return myAssignments; } /** * Processes objects placed into the clipboard so that it was "cut" transaction */ public void cut() { build(); isCut = true; for (Task t : getTasks()) { myAssignments.addAll(Arrays.asList(t.getAssignments())); myTaskManager.deleteTask(t); t.delete(); } } /** * Processes objects placed into the clipboard so that it was "copy" transaction */ public void copy() { build(); isCut = false; // Nothing needs to be done, actually, in addition to what build() already does } public boolean isCut() { return isCut; } public Collection<Task> getNestedTasks(Task task) { return myNestedTasks.get(task); } }