/*
* Copyright (c) 2008 Stiftung Deutsches Elektronen-Synchrotron,
* Member of the Helmholtz Association, (DESY), HAMBURG, GERMANY.
*
* THIS SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN "../AS IS" BASIS.
* WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR PARTICULAR PURPOSE AND
* NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR
* THE USE OR OTHER DEALINGS IN THE SOFTWARE. SHOULD THE SOFTWARE PROVE DEFECTIVE
* IN ANY RESPECT, THE USER ASSUMES THE COST OF ANY NECESSARY SERVICING, REPAIR OR
* CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE.
* NO USE OF ANY SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
* DESY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
* OR MODIFICATIONS.
* THE FULL LICENSE SPECIFYING FOR THE SOFTWARE THE REDISTRIBUTION, MODIFICATION,
* USAGE AND OTHER RIGHTS AND OBLIGATIONS IS INCLUDED WITH THE DISTRIBUTION OF THIS
* PROJECT IN THE FILE LICENSE.HTML. IF THE LICENSE IS NOT INCLUDED YOU MAY FIND A COPY
* AT HTTP://WWW.DESY.DE/LEGAL/LICENSE.HTM
*/
package org.csstudio.sds.ui.internal.actions;
import java.util.List;
import org.csstudio.sds.model.AbstractWidgetModel;
import org.csstudio.sds.model.ContainerModel;
import org.csstudio.sds.model.GroupingContainerModel;
import org.csstudio.sds.model.commands.SetPropertyCommand;
import org.csstudio.sds.ui.SdsUiPlugin;
import org.csstudio.sds.ui.internal.commands.AddWidgetCommand;
import org.csstudio.sds.ui.internal.commands.DeleteWidgetsCommand;
import org.csstudio.sds.ui.internal.commands.SetSelectionCommand;
import org.csstudio.sds.ui.internal.editor.WidgetCreationFactory;
import org.csstudio.ui.util.CustomMediaFactory;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.GraphicalViewer;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.ui.IWorkbenchPart;
/**
* An Action to create a group surrounding the selected Widgets.
*
* @author Kai Meyer & Sven Wende
*
*/
public final class CreateGroupAction extends AbstractWidgetSelectionAction {
public static final String ID = "org.csstudio.sds.ui.internal.actions.CreateGroupAction";
/**
* The offset for the surrounding {@link GroupingContainerModel}.
*/
private static final int OFFSET = 5;
public CreateGroupAction(IWorkbenchPart workbenchPart, GraphicalViewer viewer) {
super(workbenchPart, viewer);
setId(ID);
setText("Create Group");
setImageDescriptor(CustomMediaFactory.getInstance().getImageDescriptorFromPlugin(SdsUiPlugin.PLUGIN_ID, "/icons/addgroup.gif"));
}
@Override
protected boolean doCalculateEnabled(List<AbstractWidgetModel> selectedWidgets) {
return selectedWidgets.size()>1;
}
@Override
protected Command doCreateCommand(List<AbstractWidgetModel> widgets) {
CompoundCommand cmd = new CompoundCommand();
cmd.setLabel("Create Group");
ContainerModel parentContainer = getCommonAncestor(widgets);
// Create a new GroupingContainer
Rectangle bounds = getFittingBounds(widgets, parentContainer);
WidgetCreationFactory factory = new WidgetCreationFactory(GroupingContainerModel.ID);
// create a widget
ContainerModel container = (ContainerModel) factory.getNewObject();
// initialize widget
container.setLocation(bounds.x, bounds.y);
container.setWidth(bounds.width);
container.setHeight(bounds.height);
container.setLayer(parentContainer.getLayerSupport().getActiveLayer().getId());
// add new container
cmd.add(new AddWidgetCommand(parentContainer, container));
// select new container
cmd.add(new SetSelectionCommand(getGraphicalViewer(), container));
// remove widgets from surrounding container
cmd.add(new DeleteWidgetsCommand(null, parentContainer, widgets));
// adjust widget positions
for (AbstractWidgetModel w : widgets) {
Point p = this.adaptWidgetPosition(w, parentContainer, container);
cmd.add(new SetPropertyCommand(w, AbstractWidgetModel.PROP_POS_X, p.x));
cmd.add(new SetPropertyCommand(w, AbstractWidgetModel.PROP_POS_Y, p.y));
}
// add widgets to new container
cmd.add(new AddWidgetCommand(container, widgets));
return cmd;
}
/**
* Determines the common ancestor for all given {@link AbstractWidgetModel}
* s.
*
* @param widgets
* The {@link AbstractWidgetModel}s
* @return The {@link ContainerModel}, which is the ancestor for all
* {@link AbstractWidgetModel}s
*/
private ContainerModel getCommonAncestor(final List<AbstractWidgetModel> widgets) {
if (widgets.size() > 0) {
ContainerModel ancestor = widgets.get(0).getParent();
while (ancestor != null) {
boolean isForAllReachable = true;
for (AbstractWidgetModel widget : widgets) {
if (!isAncestorReachable(ancestor, widget)) {
isForAllReachable = false;
break;
}
}
if (isForAllReachable) {
return ancestor;
}
ancestor = ancestor.getParent();
}
}
return null;
}
/**
* Determines if the given {@link ContainerModel} is an ancestor of this
* model.
*
* @param ancestor
* The probably ancestor
* @param widget
* The {@link AbstractWidgetModel} which ancestor should be find
* @return true, if the given {@link ContainerModel} is an ancestor of this
* model, false otherwise
*/
private boolean isAncestorReachable(final ContainerModel ancestor, final AbstractWidgetModel widget) {
ContainerModel parent = widget.getParent();
while (parent != null) {
if (parent.equals(ancestor)) {
return true;
}
parent = parent.getParent();
}
return false;
}
/**
* Determines the bounds for the container, which surrounds for all selected
* widgets.
*
* @param widgets
* The widgets, which should be added to a container
* @return The bounds for the new container
*/
private Rectangle getFittingBounds(final List<AbstractWidgetModel> widgets, ContainerModel parentContainer) {
int x = widgets.get(0).getXForAncestor(parentContainer);
int y = widgets.get(0).getYForAncestor(parentContainer);
int absoluteWidth = widgets.get(0).getWidth() + widgets.get(0).getXForAncestor(parentContainer);
int absoluteHeight = widgets.get(0).getHeight() + widgets.get(0).getYForAncestor(parentContainer);
for (int i = 1; i < widgets.size(); i++) {
AbstractWidgetModel model = widgets.get(i);
x = Math.min(x, model.getXForAncestor(parentContainer));
y = Math.min(y, model.getYForAncestor(parentContainer));
absoluteWidth = Math.max(absoluteWidth, model.getXForAncestor(parentContainer) + model.getWidth());
absoluteHeight = Math.max(absoluteHeight, model.getYForAncestor(parentContainer) + model.getHeight());
}
return new Rectangle(x - OFFSET, y - OFFSET, absoluteWidth - x + 2 * OFFSET, absoluteHeight - y + 2 * OFFSET);
}
private Point adaptWidgetPosition(final AbstractWidgetModel widgetModel, ContainerModel parentContainer, ContainerModel container) {
int x = widgetModel.getXForAncestor(parentContainer) - container.getX();
int y = widgetModel.getYForAncestor(parentContainer) - container.getY();
return new Point(x, y);
}
}