/******************************************************************************* * Copyright (c) 2014, 2016 itemis AG 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: * Matthias Wienand (itemis AG) - initial API and implementation * Alexander Nyßen (itemis AG) - refactorings * *******************************************************************************/ package org.eclipse.gef.mvc.fx.policies; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.eclipse.gef.mvc.fx.models.FocusModel; import org.eclipse.gef.mvc.fx.models.SelectionModel; import org.eclipse.gef.mvc.fx.operations.AbstractCompositeOperation; import org.eclipse.gef.mvc.fx.operations.ChangeContentsOperation; import org.eclipse.gef.mvc.fx.operations.ChangeFocusOperation; import org.eclipse.gef.mvc.fx.operations.ITransactionalOperation; import org.eclipse.gef.mvc.fx.operations.ReverseUndoCompositeOperation; import org.eclipse.gef.mvc.fx.operations.SelectOperation; import org.eclipse.gef.mvc.fx.parts.IContentPart; import org.eclipse.gef.mvc.fx.parts.IContentPartFactory; import org.eclipse.gef.mvc.fx.parts.IRootPart; import org.eclipse.gef.mvc.fx.parts.IVisualPart; import org.eclipse.gef.mvc.fx.viewer.IViewer; import com.google.common.collect.SetMultimap; import javafx.scene.Node; /** * The {@link CreationPolicy} is an {@link AbstractPolicy} that * handles the creation of content. * <p> * It handles the creation by initiating the adding of a content child to the * content parent via the {@link ContentPolicy} of the parent * {@link IContentPart}, as well as the attachment of anchored content elements * via the {@link ContentPolicy}s of anchored {@link IContentPart}s. * <p> * This policy should be registered at an {@link IRootPart}. It depends on * {@link ContentPolicy}s being registered on all {@link IContentPart}s that are * affected by the creation. * * @author mwienand * @author anyssen * */ public class CreationPolicy extends AbstractPolicy { /** * Creates an {@link IContentPart} for the given content {@link Object} and * establishes parent and anchored relationships for the newly created part. * Besides, operations are created for the establishment of the parent and * anchored relationships within the content model. These operations are * part of the operation returned by {@link #commit()}. * * @param content * The content {@link Object} to be created. * @param parent * The {@link IContentPart} where the <i>content</i> is added as * a child or the {@link IRootPart} for 'root' content. * @param index * The index for the new element. * @param anchoreds * The {@link IContentPart} whose content should be attached to * the new content under the given roles. * @param doFocus * <code>true</code> if the newly created part should be focused. * @param doSelect * <code>true</code> if the newly created part should be * selected. * @return The {@link IContentPart} controlling the newly created content. */ public IContentPart<? extends Node> create(Object content, IVisualPart<? extends Node> parent, int index, SetMultimap<IContentPart<? extends Node>, String> anchoreds, boolean doFocus, boolean doSelect) { checkInitialized(); if (content == null) { throw new IllegalArgumentException( "The given content may not be null."); } if (parent == null) { throw new IllegalArgumentException( "The given parent may not be null."); } if (anchoreds == null) { throw new IllegalArgumentException( "The given anchored parts may not be null"); } IViewer viewer = getHost().getRoot().getViewer(); // add content to parent if (parent instanceof IRootPart) { // add content to viewer content ChangeContentsOperation changeContentsOperation = new ChangeContentsOperation( viewer); List<Object> newContents = new ArrayList<>(viewer.getContents()); newContents.add(index, content); changeContentsOperation.setNewContents(newContents); getCompositeOperation().add(changeContentsOperation); } else { // add content to parent ContentPolicy parentContentPolicy = parent .getAdapter(ContentPolicy.class); if (parentContentPolicy == null) { throw new IllegalStateException( "No ContentPolicy registered for <" + parent + ">."); } parentContentPolicy.init(); parentContentPolicy.addContentChild(content, index); ITransactionalOperation addToParentOperation = parentContentPolicy .commit(); if (addToParentOperation != null) { getCompositeOperation().add(addToParentOperation); } } // attach anchoreds to content for (IContentPart<? extends Node> anchored : anchoreds.keys()) { ContentPolicy anchoredPolicy = anchored .getAdapter(ContentPolicy.class); if (anchoredPolicy == null) { throw new IllegalStateException( "No ContentPolicy registered for <" + anchored + ">."); } anchoredPolicy.init(); for (String role : anchoreds.get(anchored)) { anchoredPolicy.attachToContentAnchorage(content, role); } ITransactionalOperation attachToAnchorageOperation = anchoredPolicy .commit(); if (attachToAnchorageOperation != null) { getCompositeOperation().add(attachToAnchorageOperation); } } locallyExecuteOperation(); IContentPart<? extends Node> contentPart = viewer.getContentPartMap() .get(content); if (doFocus) { // set as focus part ITransactionalOperation focusOperation = createFocusOperation( contentPart); if (focusOperation != null) { getCompositeOperation().add(focusOperation); } } if (doSelect) { // select the newly created part ITransactionalOperation selectOperation = createSelectOperation( contentPart); if (selectOperation != null) { getCompositeOperation().add(selectOperation); } } return contentPart; } /** * Creates an {@link IContentPart} for the given content {@link Object} and * establishes parent and anchored relationships for the newly created part. * The respective content operations are also created. * * In case the given part is to be created for root contents, the root part * is expected to be passed in as parent. The content will then be added to * the viewer contents. * * Besides, operations are created for the establishment of the parent and * anchored relationships within the content model. These operations are * part of the operation returned by {@link #commit()}. * * @param content * The content {@link Object} to be created. * @param parent * The {@link IContentPart} where the <i>content</i> is added as * a child. * @param anchoreds * The {@link IContentPart} whose content should be attached to * the new content under the given roles. * @return The {@link IContentPart} controlling the newly created content. */ public IContentPart<? extends Node> create(Object content, IVisualPart<? extends Node> parent, SetMultimap<IContentPart<? extends Node>, String> anchoreds) { int index = parent instanceof IRootPart ? getHost().getRoot().getViewer().getContents().size() : ((IContentPart<? extends Node>) parent) .getContentChildrenUnmodifiable().size(); return create(content, parent, index, anchoreds, true, true); } /** * Returns an {@link ITransactionalOperation} that adds the given * {@link IContentPart} to the {@link FocusModel} of the corresponding * {@link IViewer}. * * @param part * The {@link IContentPart} that is added to the viewer models. * @return An {@link ITransactionalOperation} that changes the viewer * models. */ protected ITransactionalOperation createFocusOperation( IContentPart<? extends Node> part) { // remove from focus model return new ChangeFocusOperation(part.getRoot().getViewer(), part); } @Override protected ITransactionalOperation createOperation() { return new ReverseUndoCompositeOperation("Create Content"); } /** * Returns an {@link ITransactionalOperation} that adds the given * {@link IContentPart} to the {@link SelectionModel} of the corresponding * {@link IViewer}. * * @param part * The {@link IContentPart} that is added to the viewer models. * @return An {@link ITransactionalOperation} that changes the viewer * models. */ protected ITransactionalOperation createSelectOperation( IContentPart<? extends Node> part) { return new SelectOperation(part.getRoot().getViewer(), Collections.singletonList(part)); } /** * Extracts a {@link AbstractCompositeOperation} from the operation created * by {@link #createOperation()}. The composite operation is used to combine * individual content change operations. * * @return The {@link AbstractCompositeOperation} that is used to combine * the individual content change operations. */ protected AbstractCompositeOperation getCompositeOperation() { return (AbstractCompositeOperation) getOperation(); } /** * Returns the {@link IContentPartFactory} of the current viewer. * * @return the {@link IContentPartFactory} of the current viewer. */ protected IContentPartFactory getContentPartFactory() { IViewer viewer = getHost().getRoot().getViewer(); IContentPartFactory cpFactory = viewer .getAdapter(IContentPartFactory.class); return cpFactory; } }