diff options
Diffstat (limited to 'plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui')
204 files changed, 37543 insertions, 0 deletions
diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/ConcernComboContributionItem.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/ConcernComboContributionItem.java new file mode 100644 index 0000000000..2b38224bef --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/ConcernComboContributionItem.java @@ -0,0 +1,398 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.action; + +import java.util.Iterator; + +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.emf.transaction.NotificationFilter; +import org.eclipse.emf.transaction.ResourceSetChangeEvent; +import org.eclipse.emf.transaction.ResourceSetListener; +import org.eclipse.emf.transaction.ResourceSetListenerImpl; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.jface.action.ContributionItem; +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.events.FocusEvent; +import org.eclipse.swt.events.FocusListener; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IPartService; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.concern.ConcernDescription; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.SetCurrentConcernCommand; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.SetDefaultConcernCommand; + +/** + * A ControlContribution that uses a {@link org.eclipse.swt.widgets.Combo} as + * its control. + * + * @author ymortier + */ +public class ConcernComboContributionItem extends ContributionItem { + + /** + * ID for concern contribution. + */ + public static final String CONCERN_CONTRIBUTION_ID = "ConcernContribution"; + + private boolean forceSetText; + + private Combo combo; + + private final String[] initStrings; + + private ToolItem toolitem; + + private DDiagram diagram; + + private final IPartService service; + + private IPartListener partListener; + + private TransactionalEditingDomain domain; + + private ResourceSetListener listener; + + /** + * Constructor for ComboToolItem. + * + * @param partService + * used to add a PartListener + * @param initString + * the initial string displayed in the combo + */ + public ConcernComboContributionItem(final IPartService partService, final String initString) { + this(partService, new String[] { initString }); + } + + /** + * Constructor for ComboToolItem. + * + * @param partService + * used to add a PartListener + * @param initStrings + * the initial string displayed in the combo + */ + public ConcernComboContributionItem(final IPartService partService, final String[] initStrings) { + super(CONCERN_CONTRIBUTION_ID); + this.initStrings = initStrings; + service = partService; + partListener = new IPartListener() { + public void partActivated(final IWorkbenchPart part) { + + if (part instanceof DDiagramEditor) { + DDiagramEditor editor = (DDiagramEditor) part; + DDiagram editorDiagram = (DDiagram) editor.getRepresentation(); + domain = (TransactionalEditingDomain) editor.getAdapter(EditingDomain.class); + setDiagram(editorDiagram); + } + } + + public void partBroughtToTop(final IWorkbenchPart p) { + } + + public void partClosed(final IWorkbenchPart p) { + } + + public void partDeactivated(final IWorkbenchPart p) { + } + + public void partOpened(final IWorkbenchPart p) { + } + }; + partService.addPartListener(partListener); + } + + /** + * Get the current diagram. + * + * @return current diagram + */ + public DDiagram getDiagram() { + return diagram; + } + + private void diagramChanged() { + if (Display.getCurrent() == null) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + refresh(false); + } + }); + } else { + refresh(false); + } + } + + private String[] getPickableConcerns() { + if (getDiagram() != null && getDiagram().getDescription() != null && getDiagram().getDescription().getConcerns() != null) { + final String[] data = new String[getDiagram().getDescription().getConcerns().getOwnedConcernDescriptions().size()]; + for (int i = 0; i < getDiagram().getDescription().getConcerns().getOwnedConcernDescriptions().size(); i++) { + final ConcernDescription desc = getDiagram().getDescription().getConcerns().getOwnedConcernDescriptions().get(i); + data[i] = desc.getName(); + } + return data; + + } else { + return new String[0]; + } + } + + private void refresh(final boolean repopulateCombo) { + if (combo == null || combo.isDisposed()) { + return; + } + // $TODO GTK workaround + try { + if (diagram == null || domain == null) { + combo.setEnabled(false); + combo.setText(StringUtil.EMPTY_STRING); + } else { + if (repopulateCombo) { + combo.setItems(getPickableConcerns()); + } + + String currentConcern = StringUtil.EMPTY_STRING; + if (getDiagram().getCurrentConcern() != null) { + currentConcern = getDiagram().getCurrentConcern().getName(); + } + final int index = combo.indexOf(currentConcern); + if (index == -1 || forceSetText) { + combo.setText(currentConcern); + } else { + combo.select(index); + } + combo.setEnabled(true); + } + } catch (final SWTException exception) { + if (!"gtk".equals(SWT.getPlatform())) { + throw exception; + } + } + } + + /** + * Computes the width required by control. + * + * @param control + * The control to compute width + * @return int The width required + */ + protected int computeWidth(final Control control) { + return control.computeSize(100, 20, true).x; + } + + /** + * Creates and returns the control for this contribution item under the + * given parent composite. + * + * @param parent + * the parent composite + * @return the new control + */ + protected Control createControl(final Composite parent) { + combo = new Combo(parent, SWT.DROP_DOWN); + combo.addSelectionListener(new SelectionListener() { + public void widgetSelected(final SelectionEvent e) { + handleWidgetSelected(e); + } + + public void widgetDefaultSelected(final SelectionEvent e) { + handleWidgetDefaultSelected(e); + } + }); + combo.addFocusListener(new FocusListener() { + public void focusGained(final FocusEvent e) { + // do nothing + } + + public void focusLost(final FocusEvent e) { + refresh(false); + } + }); + + // Initialize width of combo + combo.setItems(initStrings); + toolitem.setWidth(computeWidth(combo)); + combo.setToolTipText("Current concern"); + refresh(true); + return combo; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.action.ContributionItem#dispose() + */ + @Override + public void dispose() { + if (partListener == null) { + return; + } + service.removePartListener(partListener); + removeSemanticListener(); + diagram = null; + combo = null; + partListener = null; + domain = null; + } + + /** + * The control item implementation of this <code>IContributionItem</code> + * method calls the <code>createControl</code> framework method. Subclasses + * must implement <code>createControl</code> rather than overriding this + * method. + * + * @param parent + * The parent of the control to fill + */ + @Override + public final void fill(final Composite parent) { + createControl(parent); + } + + /** + * The control item implementation of this <code>IContributionItem</code> + * method throws an exception since controls cannot be added to menus. + * + * @param parent + * The menu + * @param index + * Menu index + */ + @Override + public final void fill(final Menu parent, final int index) { + } + + /** + * The control item implementation of this <code>IContributionItem</code> + * method calls the <code>createControl</code> framework method to create a + * control under the given parent, and then creates a new tool item to hold + * it. Subclasses must implement <code>createControl</code> rather than + * overriding this method. + * + * @param parent + * The ToolBar to add the new control to + * @param index + * Index + */ + @Override + public void fill(final ToolBar parent, final int index) { + toolitem = new ToolItem(parent, SWT.SEPARATOR, index); + final Control control = createControl(parent); + toolitem.setControl(control); + } + + /** + * Sets the DDiagram. + * + * @param dia + * The diagram + */ + public void setDiagram(final DDiagram dia) { + if (diagram == dia) { + return; + } + + if (domain == null) { + domain = TransactionUtil.getEditingDomain(dia); + } + + diagram = dia; + removeSemanticListener(); + addSemanticListener(); + refresh(true); + } + + private void addSemanticListener() { + listener = new ResourceSetListenerImpl() { + + @Override + public NotificationFilter getFilter() { + return NotificationFilter.NOT_TOUCH.and(NotificationFilter.createNotifierFilter(diagram)); + } + + @Override + public boolean isPostcommitOnly() { + return true; + } + + @Override + public void resourceSetChanged(ResourceSetChangeEvent event) { + diagramChanged(); + } + }; + domain.addResourceSetListener(listener); + } + + private void removeSemanticListener() { + if (domain != null) { + domain.removeResourceSetListener(listener); + } + } + + /** + * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(SelectionEvent) + */ + private void handleWidgetDefaultSelected(final SelectionEvent event) { + if (diagram != null) { + if (combo.getSelectionIndex() >= 0) { + setCurrentConcern(combo.getItem(combo.getSelectionIndex())); + } else { + setCurrentConcern(combo.getText()); + } + } + refresh(false); + } + + private void setCurrentConcern(final String item) { + boolean foundConcern = false; + if (item != null) { + if (getDiagram() != null && getDiagram().getDescription() != null && getDiagram().getDescription().getConcerns() != null) { + final Iterator<ConcernDescription> it = getDiagram().getDescription().getConcerns().getOwnedConcernDescriptions().iterator(); + while (it.hasNext()) { + final ConcernDescription desc = it.next(); + if (desc.getName().equals(item)) { + foundConcern = true; + domain.getCommandStack().execute(new SetCurrentConcernCommand(domain, diagram, desc)); + } + } + } + } + if (!foundConcern) { + domain.getCommandStack().execute(new SetDefaultConcernCommand(domain, diagram)); + } + } + + /** + * @see org.eclipse.swt.events.SelectionListener#widgetSelected(SelectionEvent) + */ + private void handleWidgetSelected(final SelectionEvent event) { + forceSetText = true; + handleWidgetDefaultSelected(event); + forceSetText = false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/DeleteFromDiagramContributionItem.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/DeleteFromDiagramContributionItem.java new file mode 100644 index 0000000000..6ae3939531 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/DeleteFromDiagramContributionItem.java @@ -0,0 +1,137 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.action; + +import org.eclipse.gef.EditPart; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IPartService; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.sirius.diagram.tools.internal.actions.delete.DeleteFromDiagramAction; +import org.eclipse.sirius.diagram.tools.internal.graphical.edit.part.DDiagramRootEditPart; + +/** + * A ControlContribution that uses a {@link org.eclipse.swt.widgets.Button} as + * its control. + * + * @author cnotot + */ +public class DeleteFromDiagramContributionItem extends ActionContributionItem { + + private final IPartService service; + + private IPartListener partListener; + + private IWorkbenchPart representationPart; + + private final ISelectionListener editPartSelectionListener = new ISelectionListener() { + public void selectionChanged(final IWorkbenchPart part, final ISelection selection) { + // if selection is not in this item parent part => do nothing + if (representationPart != null && !representationPart.equals(part)) { + return; + } + getAction().setEnabled(shouldBeEnabled(selection)); + update(); + } + }; + + /** + * Default Constructor for ComboToolItem. + * + * @param action + * contributed action + */ + public DeleteFromDiagramContributionItem(final IAction action) { + super(action); + service = null; + } + + /** + * Constructor for ComboToolItem. + * + * @param action + * contributed action + * @param partService + * used to add a PartListener and a SelectionListener + */ + public DeleteFromDiagramContributionItem(final IAction action, final IPartService partService) { + super(action); + service = partService; + partListener = new IPartListener() { + public void partActivated(final IWorkbenchPart part) { + final EditPart editPArt = (EditPart) part.getAdapter(EditPart.class); + if (editPArt instanceof DDiagramRootEditPart) { + part.getSite().getPage().addSelectionListener(editPartSelectionListener); + } + + } + + public void partBroughtToTop(final IWorkbenchPart p) { + } + + public void partClosed(final IWorkbenchPart p) { + } + + public void partDeactivated(final IWorkbenchPart p) { + p.getSite().getPage().removeSelectionListener(editPartSelectionListener); + } + + public void partOpened(final IWorkbenchPart p) { + } + }; + partService.addPartListener(partListener); + IWorkbenchPart part = partService.getActivePart(); + if (part != null) { + ISelectionProvider selectionProvider = part.getSite().getSelectionProvider(); + if (selectionProvider != null) { + getAction().setEnabled(shouldBeEnabled(selectionProvider.getSelection())); + update(); + } + } + } + + public void setItemPart(IWorkbenchPart itemPart) { + this.representationPart = itemPart; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.jface.action.ContributionItem#dispose() + */ + @Override + public void dispose() { + if (partListener != null && service != null) { + service.removePartListener(partListener); + } + partListener = null; + representationPart = null; + super.dispose(); + } + + private boolean shouldBeEnabled(final ISelection selection) { + if (selection instanceof IStructuredSelection) { + final IAction action = getAction(); + if (action instanceof DeleteFromDiagramAction) { + final DeleteFromDiagramAction deleteAction = (DeleteFromDiagramAction) action; + return deleteAction.shouldBeEnabled(selection); + } + } + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/FindElementAction.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/FindElementAction.java new file mode 100644 index 0000000000..940bc4ef3a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/FindElementAction.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.action; + +import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.IWorkbenchPage; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.sirius.common.ui.tools.api.find.AbstractFindLabelDialog; +import org.eclipse.sirius.diagram.ui.tools.internal.find.BasicFindLabelEngine; + +/** + * A find element Eclipse action. + * + * @author glefur + */ +public class FindElementAction implements IObjectActionDelegate { + /** + * The action ID. + */ + public static final String ID = "org.eclipse.sirius.transversal.find.ui.binding.FindElementAction"; + + private DiagramEditor currentPart; + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.IObjectActionDelegate#setActivePart(org.eclipse.jface.action.IAction, + * org.eclipse.ui.IWorkbenchPart) + */ + public void setActivePart(final IAction action, final IWorkbenchPart targetPart) { + if (targetPart instanceof DiagramEditor) { + currentPart = (DiagramEditor) targetPart; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) + */ + public void run(final IAction action) { + if (currentPart != null) { + final AbstractFindLabelDialog dialog = new RevealFindLabelDialog(currentPart.getSite().getShell(), new BasicFindLabelEngine(currentPart), currentPart); + dialog.open(); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, + * org.eclipse.jface.viewers.ISelection) + */ + public void selectionChanged(final IAction action, final ISelection selection) { + final IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + final IWorkbenchPage page = window.getActivePage(); + if (page != null) { + final IWorkbenchPart targetPart = page.getActivePart(); + if (targetPart instanceof DiagramEditor) { + currentPart = (DiagramEditor) targetPart; + } + } + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/LaunchBehaviorContributionItem.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/LaunchBehaviorContributionItem.java new file mode 100644 index 0000000000..46389016f4 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/LaunchBehaviorContributionItem.java @@ -0,0 +1,194 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.action; + +import java.util.Iterator; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.render.editparts.RenderedDiagramRootEditPart; +import org.eclipse.jface.action.ContributionItem; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.swt.widgets.ToolBar; +import org.eclipse.swt.widgets.ToolItem; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IPartService; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.sirius.diagram.ImagesPath; +import org.eclipse.sirius.diagram.internal.edit.parts.DDiagramEditPart; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.api.requests.RequestConstants; + +/** + * Add a button to launch all behaviors. + * + * @author ymortier + */ +public class LaunchBehaviorContributionItem extends ContributionItem { + + /** + * ID for concern contribution. + */ + public static final String CONCERN_CONTRIBUTION_ID = "ConcernContributionBehavior"; + + private final IPartService service; + + private IPartListener partListener; + + private EditPart viewPointEditPart; + + private Button button; + + private ToolItem toolitem; + + /** + * Constructor for ComboToolItem. + * + * @param partService + * used to add a PartListener + */ + public LaunchBehaviorContributionItem(final IPartService partService) { + super(CONCERN_CONTRIBUTION_ID); + service = partService; + Assert.isNotNull(partService); + partListener = new IPartListener() { + public void partActivated(final IWorkbenchPart part) { + final EditPart editPArt = (EditPart) part.getAdapter(EditPart.class); + if (editPArt instanceof RenderedDiagramRootEditPart) { + final RenderedDiagramRootEditPart root = (RenderedDiagramRootEditPart) editPArt; + final Iterator<?> iterChildren = root.getChildren().iterator(); + while (iterChildren.hasNext()) { + final Object child = iterChildren.next(); + if (child instanceof DDiagramEditPart) { + viewPointEditPart = (GraphicalEditPart) child; + } + } + } + + } + + public void partBroughtToTop(final IWorkbenchPart p) { + } + + public void partClosed(final IWorkbenchPart p) { + } + + public void partDeactivated(final IWorkbenchPart p) { + } + + public void partOpened(final IWorkbenchPart p) { + } + }; + partService.addPartListener(partListener); + } + + /** + * Creates and returns the control for this contribution item under the + * given parent composite. + * + * @param parent + * the parent composite + * @return the new control + */ + protected Control createControl(final Composite parent) { + button = new Button(parent, SWT.PUSH); + button.setText("Launch Behavior"); + button.setSize(30, 20); + button.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent e) { + final Request request = new Request(RequestConstants.REQ_LAUNCH_RULE_TOOL); + viewPointEditPart.performRequest(request); + } + }); + final Image image = SiriusDiagramEditorPlugin.getInstance().getImage(SiriusDiagramEditorPlugin.findImageDescriptor(ImagesPath.GO_IMG)); + button.setImage(image); + button.setSize(image.getBounds().width + button.getBorderWidth(), image.getBounds().height + button.getBorderWidth()); + return button; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.action.ContributionItem#dispose() + */ + @Override + public void dispose() { + if (partListener == null) { + return; + } + if (button != null && !button.isDisposed()) { + button.dispose(); + } + if (service != null) { + service.removePartListener(this.partListener); + } + button = null; + partListener = null; + } + + /** + * The control item implementation of this <code>IContributionItem</code> + * method calls the <code>createControl</code> framework method. Subclasses + * must implement <code>createControl</code> rather than overriding this + * method. + * + * @param parent + * The parent of the control to fill + */ + @Override + public final void fill(final Composite parent) { + createControl(parent); + } + + /** + * The control item implementation of this <code>IContributionItem</code> + * method throws an exception since controls cannot be added to menus. + * + * @param parent + * The menu + * @param index + * Menu index + */ + @Override + public final void fill(final Menu parent, final int index) { + Assert.isTrue(false, "Can't add a control to a menu"); //$NON-NLS-1$ + } + + /** + * The control item implementation of this <code>IContributionItem</code> + * method calls the <code>createControl</code> framework method to create a + * control under the given parent, and then creates a new tool item to hold + * it. Subclasses must implement <code>createControl</code> rather than + * overriding this method. + * + * @param parent + * The ToolBar to add the new control to + * @param index + * Index + */ + @Override + public void fill(final ToolBar parent, final int index) { + toolitem = new ToolItem(parent, SWT.SEPARATOR, index); + final Control control = createControl(parent); + toolitem.setControl(control); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/RevealFindLabelDialog.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/RevealFindLabelDialog.java new file mode 100644 index 0000000000..6752f09e3a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/RevealFindLabelDialog.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.action; + +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.sirius.common.tools.api.find.AbstractFindLabelEngine; +import org.eclipse.sirius.common.ui.tools.api.find.AbstractFindLabelDialog; + +/** + * An implementation of the AbstractFindLabelDialog that use the "reveal" method + * when the "next" button is used. + * + * @author glefur + */ +public class RevealFindLabelDialog extends AbstractFindLabelDialog { + private final DiagramEditor editor; + + /** + * Constructor. + * + * @param parentShell + * the parent shell + * @param engine + * the find label engine + * @param currentEditor + * the current editor + */ + public RevealFindLabelDialog(final Shell parentShell, final AbstractFindLabelEngine engine, final DiagramEditor currentEditor) { + super(parentShell, engine); + this.editor = currentEditor; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.common.ui.tools.api.find.AbstractFindLabelDialog#selectElement(java.lang.Object) + */ + @Override + protected void selectElement(final Object selection) { + editor.getDiagramGraphicalViewer().select((EditPart) selection); + editor.getDiagramGraphicalViewer().reveal((EditPart) selection); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/SetStyleToWorkspaceImageContributionItem.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/SetStyleToWorkspaceImageContributionItem.java new file mode 100644 index 0000000000..904fa000e0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/action/SetStyleToWorkspaceImageContributionItem.java @@ -0,0 +1,152 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.action; + +import java.util.Iterator; + +import org.eclipse.gef.EditPart; +import org.eclipse.jface.action.ActionContributionItem; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.ui.IPartListener; +import org.eclipse.ui.IPartService; +import org.eclipse.ui.ISelectionListener; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.sirius.diagram.edit.api.part.IAbstractDiagramNodeEditPart; +import org.eclipse.sirius.diagram.tools.internal.graphical.edit.part.DDiagramRootEditPart; + +/** + * A ControlContribution that uses a {@link org.eclipse.swt.widgets.Button} as + * its control. + * + * @author Maxime Porhel (mporhel) + */ +public class SetStyleToWorkspaceImageContributionItem extends ActionContributionItem { + + private final IPartService service; + + private IPartListener partListener; + + private IWorkbenchPart representationPart; + + private final ISelectionListener editPartSelectionListener = new ISelectionListener() { + public void selectionChanged(final IWorkbenchPart part, final ISelection selection) { + if (representationPart != null && !representationPart.equals(part)) { + return; + } + getAction().setEnabled(shouldBeEnabled(selection)); + update(); + } + }; + + /** + * Default Constructor for ComboToolItem. + * + * @param action + * contributed action + */ + public SetStyleToWorkspaceImageContributionItem(final IAction action) { + super(action); + service = null; + } + + /** + * Constructor for ComboToolItem. + * + * @param action + * contributed action + * @param partService + * used to add a PartListener and a SelectionListener + * @param part + * this contribution item workbench part. + */ + public SetStyleToWorkspaceImageContributionItem(final IAction action, final IPartService partService, IWorkbenchPart part) { + super(action); + representationPart = part; + service = partService; + partListener = new IPartListener() { + public void partActivated(final IWorkbenchPart part) { + final EditPart editPArt = (EditPart) part.getAdapter(EditPart.class); + if (editPArt instanceof DDiagramRootEditPart) { + part.getSite().getPage().addSelectionListener(editPartSelectionListener); + } + + } + + public void partBroughtToTop(final IWorkbenchPart p) { + } + + public void partClosed(final IWorkbenchPart p) { + } + + public void partDeactivated(final IWorkbenchPart p) { + p.getSite().getPage().removeSelectionListener(editPartSelectionListener); + } + + public void partOpened(final IWorkbenchPart p) { + } + }; + partService.addPartListener(partListener); + IWorkbenchPart activePart = partService.getActivePart(); + if (activePart != null) { + ISelectionProvider selectionProvider = activePart.getSite().getSelectionProvider(); + if (selectionProvider != null) { + getAction().setEnabled(shouldBeEnabled(selectionProvider.getSelection())); + update(); + } + } + } + + /** + * Constructor for ComboToolItem. + * + * @param action + * contributed action + * @param partService + * used to add a PartListener and a SelectionListener + */ + public SetStyleToWorkspaceImageContributionItem(final IAction action, final IPartService partService) { + this(action, partService, null); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.jface.action.ContributionItem#dispose() + */ + @Override + public void dispose() { + if (partListener != null && service != null) { + service.removePartListener(partListener); + } + partListener = null; + representationPart = null; + super.dispose(); + } + + private boolean shouldBeEnabled(final ISelection selection) { + if (selection instanceof IStructuredSelection) { + boolean result = true; + final Iterator it = ((IStructuredSelection) selection).iterator(); + while (it.hasNext()) { + if (!(it.next() instanceof IAbstractDiagramNodeEditPart)) { + result = false; + } + } + return result; + } + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/color/ColorManager.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/color/ColorManager.java new file mode 100644 index 0000000000..a9fe3cebaa --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/color/ColorManager.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.color; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.FigureUtilities; +import org.eclipse.swt.graphics.Color; + +import org.eclipse.sirius.RGBValues; +import org.eclipse.sirius.ui.tools.api.color.VisualBindingManager; + +/** + * Manage the color in complement to {@link VisualBindingManager} with use of + * draw2d API to compute some colors. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class ColorManager { + + private static ColorManager defaultInstance; + + private Map<String, Color> lighterColorCache; + + /** + * Constructor. + */ + protected ColorManager() { + lighterColorCache = new HashMap<String, Color>(); + } + + /** + * Return the singleton instance. + * + * @return a default instance + */ + public static ColorManager getDefault() { + if (defaultInstance == null) { + defaultInstance = new ColorManager(); + } + return defaultInstance; + } + + /** + * Get a lighter color from the colorToLight. + * + * @param colorToLight + * the color to light + * @return A color get from the cache + */ + public Color getLighterColor(final RGBValues colorToLight) { + final String key = getKey(colorToLight); + if (!lighterColorCache.containsKey(key)) { + Color newLighterColor = FigureUtilities.mixColors(VisualBindingManager.getDefault().getColorFromRGBValues(colorToLight), ColorConstants.white, 0.4); + lighterColorCache.put(key, newLighterColor); + } + return lighterColorCache.get(key); + } + + private String getKey(RGBValues color) { + StringBuilder sb = new StringBuilder("Color"); + if (color != null) { + sb.append("_r:"); + sb.append(color.getRed()); + sb.append("_g:"); + sb.append(color.getGreen()); + sb.append("_b:"); + sb.append(color.getBlue()); + } + return sb.toString(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/decorators/AbstractSiriusDecorator.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/decorators/AbstractSiriusDecorator.java new file mode 100644 index 0000000000..58e415f3bc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/decorators/AbstractSiriusDecorator.java @@ -0,0 +1,265 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.decorators; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gef.editparts.AbstractConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.decorator.AbstractDecorator; +import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoration; +import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget; +import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramListEditPart; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramNameEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeListEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeListElementEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeListNameEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeNameEditPart; + +/** + * Abstract Sirius decorator. + * + * @author mchauvin + */ +public abstract class AbstractSiriusDecorator extends AbstractDecorator { + /** + * The margin to use during decoration. + */ + protected static final int MARGIN = -1; + + /** the decorations being displayed */ + private List<IDecoration> decorations = Collections.<IDecoration> emptyList(); + + /** + * Create a decorator. + * + * @param decoratorTarget + * target to decorate. + */ + public AbstractSiriusDecorator(final IDecoratorTarget decoratorTarget) { + super(decoratorTarget); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecorator#activate() + */ + public void activate() { + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecorator#refresh() + */ + public void refresh() { + removeDecorations(); + final View view = (View) getDecoratorTarget().getAdapter(View.class); + if (view != null && (shouldConsiderDetachedViews() || view.eResource() != null)) { + final EditPart editPart = (EditPart) getDecoratorTarget().getAdapter(EditPart.class); + if (!shouldBeDecorated(editPart)) { + return; + } + // Get margin + int margin = MARGIN; + if (editPart instanceof org.eclipse.gef.GraphicalEditPart) { + margin = MapModeUtil.getMapMode(((org.eclipse.gef.GraphicalEditPart) editPart).getFigure()).DPtoLP(margin); + } + + Image decorationImage = getDecorationImage(editPart); + if (null != decorationImage) { + if (editPart instanceof AbstractConnectionEditPart) { + addDecoration(getDecoratorTarget().addConnectionDecoration(decorationImage, 50, false)); + } else { + addDecoration(getDecoratorTarget().addShapeDecoration(decorationImage, getDirection(editPart), margin, false)); + } + } + } + } + + /** + * Indicates whether this decorator should consider detached {@link View}s + * (i.e. {@link View}s which eResource() is null). + * + * @return true if this decorator should consider detached {@link View}s, + * false otherwise. + */ + protected boolean shouldConsiderDetachedViews() { + return false; + } + + /** + * Specific refresh for an edit. Override if you need to do specific things. + * + * @param editPart + * edit part + */ + protected void refresh(final EditPart editPart) { + /* do nothing */ + } + + /** + * Get the position of the decorator according to edit part. + * + * @param editPart + * the edit part + * @return a Direction + */ + protected abstract Direction getDirection(final EditPart editPart); + + /** + * Check if the edit part respect conditions to be decorate. + * + * @param editPart + * the editPart to check + * @return true if the editPart respect conditions to be decorate, false + * otherwise + */ + protected boolean shouldBeDecorated(final EditPart editPart) { + boolean shouldBeDecorated = true; + if (editPart == null || editPart.getParent() == null || editPart.getRoot() == null || editPart.getViewer() == null) { + shouldBeDecorated = false; + } else if (editPart instanceof AbstractDiagramNameEditPart && !(editPart instanceof DNodeListElementEditPart)) { + /* Check that the editPart is not a name dEditPart */ + shouldBeDecorated = false; + } else if (editPart instanceof org.eclipse.gef.GraphicalEditPart) { + /* Check if the size of the figure is sufficient */ + shouldBeDecorated = figureIsBigEnoughToBeDecorated(editPart); + } + return shouldBeDecorated; + } + + private boolean figureIsBigEnoughToBeDecorated(final EditPart editPart) { + final IFigure figure = ((org.eclipse.gef.GraphicalEditPart) editPart).getFigure(); + + final Dimension size = figure.getSize(); + + if (size.width < 10 && size.width > 0 && size.height < 10 && size.height > 0) { + return false; + } + return true; + } + + /** + * Get the decoration image.<br> + * + * @param editPart + * the edit part to get the decoration image from + * @return <code>null</code> if no image found. + */ + protected abstract Image getDecorationImage(EditPart editPart); + + private void removeDecorations() { + for (final IDecoration decoration : decorations) { + if (decoration instanceof IFigure && ((IFigure) decoration).getParent() != null) { + ((IFigure) decoration).getParent().remove((IFigure) decoration); + } + + final GraphicalEditPart ownerEditPart = (GraphicalEditPart) getDecoratorTarget().getAdapter(GraphicalEditPart.class); + if (ownerEditPart != null && ownerEditPart.getRoot() != null && ownerEditPart.getViewer() != null) { + ownerEditPart.getViewer().getVisualPartMap().remove(decoration); + } + } + decorations = new ArrayList<IDecoration>(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.decorator.AbstractDecorator#deactivate() + */ + @Override + public void deactivate() { + removeDecorations(); + } + + /** + * Get the IDecorations of this DescribedDecorator. + * + * @return Returns the decorations. + */ + public List<IDecoration> getDecorations() { + return decorations; + } + + /** + * Add an IDecoration to this DescribedDecorator. + * + * @param decoration + * IDecoration to add. + */ + public void addDecoration(final IDecoration decoration) { + decorations.add(decoration); + } + + /** + * Get the underlying semantic element for given edit part. + * + * @param editPart + * to decorate + * @return <code>null</code> if not found. + */ + protected EObject getUnderlyingSemanticElement(EditPart editPart) { + EObject result = null; + // Precondition: + if (null == editPart) { + return result; + } + if (editPart instanceof IDiagramElementEditPart) { + result = ((IDiagramElementEditPart) editPart).resolveTargetSemanticElement(); + } + return result; + } + + /** + * Indicates if the given editPart should contain decorations according to + * its type. For example, {@link DNodeListNameEditPart}s should not be + * decorated. + * + * @param editPart + * the edit part to inspect + * @return true if the given editPart should contain decorations, false + * otherwise + */ + public boolean isDecorableEditPart(IDiagramElementEditPart editPart) { + boolean result = true; + if (editPart instanceof DNodeNameEditPart) { + EditPart parentEditPart = editPart.getParent(); + if (!(parentEditPart instanceof DNodeListEditPart) && !(parentEditPart instanceof AbstractDiagramListEditPart)) { + result = false; + } + } else if (editPart instanceof DNodeListNameEditPart) { + result = false; + } else if (editPart instanceof DNodeListElementEditPart) { + // We only decorate DNodeListElementEditParts if the semantic + // element is different from parent editpart + EditPart parentEditPart = editPart.getParent(); + if (parentEditPart.getModel() instanceof View && editPart.getModel() instanceof View) { + result = !(((View) parentEditPart.getModel()).getElement().equals(((View) editPart.getModel()).getElement())); + } + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractCachedSVGFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractCachedSVGFigure.java new file mode 100644 index 0000000000..37ddc88a6e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractCachedSVGFigure.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import java.awt.image.BufferedImage; +import java.util.Collection; +import java.util.Map; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.XYLayout; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Display; +import org.w3c.dom.Document; + +import com.google.common.collect.Lists; +import com.google.common.collect.MapMaker; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * A {@link AbstractCachedSVGFigure} is a {@link SVGFigure} corresponding to a + * svg image. {@link Image} are store in a map with soft values. + * + * @author mporhel + */ +public abstract class AbstractCachedSVGFigure extends SVGFigure implements StyledFigure, ITransparentFigure { + + /** + * Key separator. + */ + protected static final String SEPARATOR = "|"; + + /** + * Soft cache to store svg. + */ + private static final Map<String, Image> SVG_IMG_CACHE = new MapMaker().softValues().makeMap(); + + private static final String IMAGE_DIR = "images/"; //$NON-NLS-1$ + + private static final String IMAGE_EXT = ".svg"; //$NON-NLS-1$ + + private static final String IMAGE_NOT_FOUND = "NotFound"; //$NON-NLS-1$ + + private int viewpointAlpha = DEFAULT_ALPHA; + + private boolean transparent; + + /** + * Build a new {@link AbstractCachedSVGFigure} from an Image instance. + * + */ + public AbstractCachedSVGFigure() { + this.setLayoutManager(new XYLayout()); + } + + /** + * {@inheritDoc} + */ + public int getSiriusAlpha() { + return viewpointAlpha; + } + + /** + * {@inheritDoc} + */ + public boolean isTransparent() { + return transparent; + } + + /** + * {@inheritDoc} + */ + public void setSiriusAlpha(int alpha) { + this.viewpointAlpha = alpha; + + } + + /** + * {@inheritDoc} + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.figure.lite.svg.SVGFigure#paintFigure(org.eclipse.draw2d.Graphics) + */ + @Override + protected void paintFigure(Graphics graphics) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, graphics); + modifier.pushState(); + + Rectangle r = getClientArea(); + Image image = getCachedImage(getKey() + getContextKey(graphics), r, graphics); + // Draw the image + if (image != null) { + graphics.drawImage(image, r.x, r.y); + } + modifier.popState(); + } + + private String getContextKey(Graphics graphics) { + // CHECKSTYLE:OFF + int aaText = SWT.DEFAULT; + try { + aaText = graphics.getTextAntialias(); + } catch (Exception e) { + // not supported + } + // CHECKSTYLE:ON + + StringBuilder result = new StringBuilder(); + result.append(aaText); + result.append(SEPARATOR); + Rectangle r = getClientArea(); + result.append(getSpecifyCanvasWidth() ? r.width : -1); + result.append(SEPARATOR); + result.append(getSpecifyCanvasHeight() ? r.height : -1); + + return result.toString(); + } + + /** + * Get the image cached or create new one and cache it. + * + * @param key + * the key + * @param clientArea + * the client area + * @param graphics + * the graphical context + * @return an image store in a cache + */ + protected Image getCachedImage(final String key, Rectangle clientArea, Graphics graphics) { + + if (!SVG_IMG_CACHE.containsKey(key)) { + + /* Create the image if it does not exist */ + Document document = getDocument(); + if (document == null) { + return null; + } + + getTranscoder().setCanvasSize(getSpecifyCanvasWidth() ? clientArea.width : -1, getSpecifyCanvasHeight() ? clientArea.height : -1); + updateRenderingHints(graphics); + BufferedImage awtImage = getTranscoder().getBufferedImage(); + if (awtImage != null) { + SVG_IMG_CACHE.put(key, toSWT(Display.getCurrent(), awtImage)); + } + } + // Get the image from the cache + return SVG_IMG_CACHE.get(key); + } + + /** + * Compute a key for this figure. This key is used to store in cache the + * corresponding {@link org.eclipse.swt.graphics.Image}. + * + * The key must begin by the document key. + * + * @return The key corresponding to this BundleImageFigure. + */ + protected abstract String getKey(); + + /** + * The uri of the image to display when the file has not been found. + * + * @return The uri of the image to display when the file has not been found. + */ + protected static String getImageNotFoundURI() { + final String path = new StringBuffer(IMAGE_DIR).append(IMAGE_NOT_FOUND).append(IMAGE_EXT).toString(); + String pluginId = SiriusDiagramEditorPlugin.getInstance().getBundle().getSymbolicName(); + return "platform:/plugin/" + pluginId + "/" + path; + } + + /** + * Remove all entries whose key begins with the given key. Remove from the + * document map, the entries with the given keys to force to re-read the + * file. + * + * @param documentKey + * the document key. + * @return true of something was removed. + */ + protected static boolean doRemoveFromCache(final String documentKey) { + if (!StringUtil.isEmpty(documentKey)) { + boolean remove = false; + Collection<String> keyToRemove = Lists.newArrayList(); + for (String key : SVG_IMG_CACHE.keySet()) { + if (key.startsWith(documentKey)) { + keyToRemove.add(key); + } + } + + for (String toRemove : keyToRemove) { + SVG_IMG_CACHE.remove(toRemove); + remove = true; + } + boolean removedFromDocumentsMap = documentsMap.remove(documentKey) != null; + return remove || removedFromDocumentsMap; + } + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractGeoShapePolygonFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractGeoShapePolygonFigure.java new file mode 100644 index 0000000000..eda8c735d0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractGeoShapePolygonFigure.java @@ -0,0 +1,91 @@ +/****************************************************************************** + * Copyright (c) 2003, 2004, 2008 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Mariot Chauvin <mariot.chauvin@obeo.fr> - Checkstylized + ****************************************************************************/ + +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IPolygonAnchorableFigure; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; + +/** + * Base class for polygons in the Geometric shapes palette. + * + * author jschofie + * + * @author mchauvin + */ +public abstract class AbstractGeoShapePolygonFigure extends DefaultSizeNodeFigure implements IPolygonAnchorableFigure { + + /** + * Constructor. + * + * @param width + * figure width + * @param height + * figure height + * @param spacing + * figure spacing + */ + public AbstractGeoShapePolygonFigure(final int width, final int height, final int spacing) { + super(width, height); + setOpaque(true); + } + + /** + * This method is used to compute the shapes polygon points. This is + * currently based on the shapes bounding box. + * + * Subclasses must return their list of points based on the object size. + * + * @param rect + * the rectangle that the shape will fit in + * @return the point list + */ + protected abstract PointList calculatePoints(Rectangle rect); + + /** + * Get the content pane. + * + * @return the content pane + */ + public IFigure getContentPane() { + return (IFigure) getChildren().get(0); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure#paintFigure(org.eclipse.draw2d.Graphics) + */ + @Override + protected void paintFigure(final Graphics g) { + g.setBackgroundColor(getBackgroundColor()); + g.setForegroundColor(getForegroundColor()); + g.setLineWidth(getLineWidth()); + + final PointList points = calculatePoints(new Rectangle(getBounds())); + g.fillPolygon(points); + g.drawPolygon(points); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IPolygonAnchorableFigure#getPolygonPoints() + */ + public PointList getPolygonPoints() { + return calculatePoints(new Rectangle(getBounds())); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentEllipse.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentEllipse.java new file mode 100644 index 0000000000..0362b4c461 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentEllipse.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Ellipse; +import org.eclipse.draw2d.Graphics; + +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * An abstract ellipse figure to handle transparency. + * + * @mporhel + */ +public abstract class AbstractTransparentEllipse extends Ellipse implements StyledFigure, ITransparentFigure { + + private int viewpointAlpha = DEFAULT_ALPHA; + + private boolean transparent; + + /** + * {@inheritDoc} + */ + @Override + protected void fillShape(Graphics graphics) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, graphics); + modifier.pushState(); + super.fillShape(graphics); + modifier.popState(); + } + + /** + * {@inheritDoc} + */ + public int getSiriusAlpha() { + return viewpointAlpha; + } + + /** + * {@inheritDoc} + */ + public boolean isTransparent() { + return transparent; + } + + /** + * {@inheritDoc} + */ + public void setSiriusAlpha(int alpha) { + this.viewpointAlpha = alpha; + } + + /** + * {@inheritDoc} + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentImage.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentImage.java new file mode 100644 index 0000000000..c85be12799 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentImage.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.ImageFigure; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * An abstract image figure to handle transparency. + * + * @author mporhel + */ +public abstract class AbstractTransparentImage extends ImageFigure implements StyledFigure, ITransparentFigure { + + private int viewpointAlpha = DEFAULT_ALPHA; + + private boolean transparent; + + /** + * Create a new {@link AbstractTransparentImage}. + * + * @param flyWeightImage + * an image instance. + */ + public AbstractTransparentImage(final Image flyWeightImage) { + super(flyWeightImage); + } + + /** + * {@inheritDoc} + */ + @Override + protected void paintFigure(Graphics graphics) { + if (getImage() != null) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, graphics); + modifier.pushState(); + graphics.drawImage(getImage(), new Rectangle(getImage().getBounds()), getBounds()); + modifier.popState(); + } + } + + /** + * {@inheritDoc} + */ + public int getSiriusAlpha() { + return viewpointAlpha; + } + + /** + * {@inheritDoc} + */ + public boolean isTransparent() { + return transparent; + } + + /** + * {@inheritDoc} + */ + public void setSiriusAlpha(int alpha) { + this.viewpointAlpha = alpha; + + } + + /** + * {@inheritDoc} + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentNode.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentNode.java new file mode 100644 index 0000000000..30f3aa74ff --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentNode.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; + +/** + * An abstract node figure to handle transparency. + */ +public abstract class AbstractTransparentNode extends NodeFigure implements ITransparentFigure { + + private int viewpointAlpha = DEFAULT_ALPHA; + + private boolean transparent; + + /** + * {@inheritDoc} + */ + public int getSiriusAlpha() { + return viewpointAlpha; + } + + /** + * {@inheritDoc} + */ + public boolean isTransparent() { + return transparent; + } + + /** + * {@inheritDoc} + */ + public void setSiriusAlpha(int alpha) { + this.viewpointAlpha = alpha; + + } + + /** + * {@inheritDoc} + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentRectangle.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentRectangle.java new file mode 100644 index 0000000000..2d2df2b401 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AbstractTransparentRectangle.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.RectangleFigure; + +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * An abstract rectangle figure to handle transparency. + */ +public abstract class AbstractTransparentRectangle extends RectangleFigure implements StyledFigure, ITransparentFigure { + + private int viewpointAlpha = DEFAULT_ALPHA; + + private boolean transparent; + + /** + * {@inheritDoc} + */ + @Override + protected void fillShape(Graphics graphics) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, graphics); + modifier.pushState(); + super.fillShape(graphics); + modifier.popState(); + } + + /** + * {@inheritDoc} + */ + public int getSiriusAlpha() { + return viewpointAlpha; + } + + /** + * {@inheritDoc} + */ + public boolean isTransparent() { + return transparent; + } + + /** + * {@inheritDoc} + */ + public void setSiriusAlpha(int alpha) { + this.viewpointAlpha = alpha; + + } + + /** + * {@inheritDoc} + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ActionTriggerImageFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ActionTriggerImageFigure.java new file mode 100644 index 0000000000..37d88bc783 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ActionTriggerImageFigure.java @@ -0,0 +1,192 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.draw2d.MouseEvent; +import org.eclipse.draw2d.MouseListener; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.sirius.common.ui.tools.api.util.ISimpleAction; + +/** + * A figure that triggers actions when the user clicks on the image. + * + * + * @author ymortier + */ +public class ActionTriggerImageFigure extends MouseAwareImageFigure { + + /** The image that is shown when the user clicks on the figure. */ + protected Image clickedImage; + + /** + * Actions to triggers when the image is clicked (type of + * {@link ISimpleAction}. + */ + private List actions; + + /** + * the action trigger mandatory to simulate click + */ + private ActionTrigger actionTrigger; + + /** + * Create a new {@link ActionTriggerImageFigure}. + */ + public ActionTriggerImageFigure() { + super(); + this.init(); + } + + /** + * Create a new {@link ActionTriggerImageFigure}. + * + * @param imageWOFocus + * the image that is shown when the mouse is not on the figure. + * @param imageWFocus + * the image that is shown when the mouse is on the figure. + */ + public ActionTriggerImageFigure(final Image imageWOFocus, final Image imageWFocus) { + super(imageWOFocus, imageWFocus); + this.init(); + } + + /** + * Create a new {@link ActionTriggerImageFigure}. + * + * @param imageWOFocus + * the image that is shown when the mouse is not on the figure. + */ + public ActionTriggerImageFigure(final Image imageWOFocus) { + super(imageWOFocus); + this.init(); + } + + /** + * Initialize the figure. + */ + private void init() { + this.actions = new LinkedList(); + this.actionTrigger = new ActionTrigger(); + this.addMouseListener(this.actionTrigger); + } + + /** + * This class has the responsability to trigger actions when the figure is + * clicked. + * + * @author ymortier + */ + private class ActionTrigger implements MouseListener { + + /** + * @see MouseListener#mouseDoubleClicked(MouseEvent) + */ + public void mouseDoubleClicked(final MouseEvent me) { + // do nothing. + } + + /** + * @see MouseListener#mousePressed(MouseEvent) + */ + public void mousePressed(final MouseEvent me) { + if (clickedImage != null) { + ActionTriggerImageFigure.this.setImage(clickedImage); + } + trigger(); + me.consume(); + } + + /** + * @see MouseListener#mouseReleased(MouseEvent) + */ + public void mouseReleased(final MouseEvent me) { + if (getImage() == clickedImage) { + setImage(imageWFocus); + } + } + + } + + /** + * Trigger all actions. + */ + public void trigger() { + final Iterator iterActions = this.iterActions(); + while (iterActions.hasNext()) { + final ISimpleAction action = (ISimpleAction) iterActions.next(); + action.executeAction(); + } + } + + /** + * Add an action. + * + * @param simpleAction + * the action to add. + */ + public void addAction(final ISimpleAction simpleAction) { + this.actions.add(simpleAction); + } + + /** + * Add an action at the specified index. + * + * @param simpleAction + * the action to add. + * @param index + * index at which the specified element is to be inserted + */ + public void addAction(final ISimpleAction simpleAction, final int index) { + this.actions.add(index, simpleAction); + } + + /** + * Remove all actions. + */ + public void clearActions() { + this.actions.clear(); + } + + /** + * Remove an action. + * + * @param simpleAction + * the action to remove. + */ + public void removeAction(final ISimpleAction simpleAction) { + this.actions.remove(simpleAction); + } + + /** + * Return an iterator that iterates on actions. + * + * @return an iterator that iterates on actions. + */ + public Iterator iterActions() { + return this.actions.iterator(); + } + + /** + * Define the image that is shown when the user clicks on the figure. + * + * @param clickedImage + * the image that is shown when the user clicks on the figure. + */ + public void setClickedImage(final Image clickedImage) { + this.clickedImage = clickedImage; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirDefaultSizeNodeFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirDefaultSizeNodeFigure.java new file mode 100644 index 0000000000..e77cb21ab3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirDefaultSizeNodeFigure.java @@ -0,0 +1,240 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; + +import org.eclipse.sirius.diagram.ui.tools.api.figure.anchor.AnchorProvider; +import org.eclipse.sirius.diagram.ui.tools.api.figure.anchor.ZoomDependantAnchor; + +/** + * The air default size node figure to add custom anchor. + * + * @author ymortier + */ +public class AirDefaultSizeNodeFigure extends DefaultSizeNodeFigure { + + /** The zoom manager. */ + protected ZoomManager zoomManager; + + /** The anchor provider. */ + private AnchorProvider anchorProvider; + + /** The slidable anchor area. */ + private double slidableAnchorArea = -1.0; // disabled by default, so that we + // use the value from super. + + /** + * Create a new {@link AirDefaultSizeNodeFigure}. + * + * @param defSize + * the size. + * @param anchorProvider + * the anchor provider. + */ + public AirDefaultSizeNodeFigure(final Dimension defSize, final AnchorProvider anchorProvider) { + super(defSize); + this.anchorProvider = anchorProvider; + } + + /** + * Create a new {@link AirDefaultSizeNodeFigure}. + * + * @param width + * the width. + * @param height + * the height. + * @param anchorProvider + * the anchor provider. + */ + public AirDefaultSizeNodeFigure(final int width, final int height, final AnchorProvider anchorProvider) { + super(width, height); + this.anchorProvider = anchorProvider; + } + + /** + * Create an anchor with the provider given as parameter. + * + * @param provider + * the provider + * @param p + * the precision point + * @return the created anchor + */ + public ConnectionAnchor createAnchor(final AnchorProvider provider, final PrecisionPoint p) { + final ConnectionAnchor anchor = provider.createAnchor(this, p); + setZoomManagerToAnchor(anchor); + return anchor; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure#createAnchor(org.eclipse.draw2d.geometry.PrecisionPoint) + */ + @Override + protected ConnectionAnchor createAnchor(final PrecisionPoint p) { + if (this.anchorProvider != null) { + final ConnectionAnchor anchor = this.anchorProvider.createAnchor(this, p); + setZoomManagerToAnchor(anchor); + return anchor; + } + return super.createAnchor(p); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure#createConnectionAnchor(org.eclipse.draw2d.geometry.Point) + */ + @Override + protected ConnectionAnchor createConnectionAnchor(final Point p) { + return super.createConnectionAnchor(p); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure#createDefaultAnchor() + */ + @Override + protected ConnectionAnchor createDefaultAnchor() { + if (this.anchorProvider != null) { + final ConnectionAnchor anchor = this.anchorProvider.createDefaultAnchor(this); + setZoomManagerToAnchor(anchor); + return anchor; + } + return super.createDefaultAnchor(); + } + + private void setZoomManagerToAnchor(final ConnectionAnchor anchor) { + if (anchor instanceof ZoomDependantAnchor) { + ((ZoomDependantAnchor) anchor).setZoomManager(zoomManager); + } + } + + /** + * No insets. {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#getInsets() + */ + @Override + public Insets getInsets() { + return new Insets(0); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#getMinimumSize(int, int) + */ + @Override + public Dimension getMinimumSize(final int hint, final int hint2) { + return new Dimension(10, 10); // the minimun size. + } + + /** + * Overridden to allow user control using + * {@link #setSlidableAnchorArea(double)}. + * <p> + * {@inheritDoc} + */ + @Override + public double getSlidableAnchorArea() { + if (this.slidableAnchorArea >= 0) { + return this.slidableAnchorArea; + } else { + return super.getSlidableAnchorArea(); + } + } + + /** + * Specifies how large the area of the figure's bounds where SlidableAnchor + * will be created. The value must be below 1.0. A negative value means + * "use the default from the superclass". + * + * @param value + * the percentage of area of the figure's bounds where + * SlidableAnchor will be created. + */ + public void setSlidableAnchorArea(double value) { + if (value > 1.0) { + this.slidableAnchorArea = 1.0; + } else { + this.slidableAnchorArea = value; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#getMaximumSize() + */ + @Override + public Dimension getMaximumSize() { + return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE); + } + + /** + * Return the {@link AnchorProvider} of the figure. + * + * @return the {@link AnchorProvider} of the figure. + */ + public AnchorProvider getAnchorProvider() { + return anchorProvider; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure#getConnectionAnchor(java.lang.String) + */ + @Override + public ConnectionAnchor getConnectionAnchor(final String terminal) { + ConnectionAnchor connectAnchor = (ConnectionAnchor) getConnectionAnchors().get(terminal == null ? szAnchor : terminal); + if (connectAnchor == null) { + if (szAnchor.equals(terminal)) { + // get a new one - this figure doesn't support static anchors + connectAnchor = createDefaultAnchor(); + } else { + connectAnchor = createAnchor(BaseSlidableAnchor.parseTerminalString(terminal)); + } + getConnectionAnchors().put(terminal == null ? szAnchor : terminal, connectAnchor); + } + + return connectAnchor; + } + + /** + * Set the zoom manager. + * + * @param manager + * the zoom manager + */ + public void setZoomManager(final ZoomManager manager) { + if (zoomManager != manager) { + zoomManager = manager; + for (ConnectionAnchor anchor : (Iterable<ConnectionAnchor>) this.getConnectionAnchors().values()) { + if (anchor instanceof ZoomDependantAnchor) { + ((ZoomDependantAnchor) anchor).setZoomManager(manager); + } + } + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirNoteFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirNoteFigure.java new file mode 100644 index 0000000000..2996e5b8ef --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirNoteFigure.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.gmf.runtime.diagram.ui.figures.NoteFigure; + +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * Specific Figure to handle documentation notes. + * + * @author cbrun + * + */ +public class AirNoteFigure extends NoteFigure implements StyledFigure, ITransparentFigure { + + private int viewpointAlpha = DEFAULT_ALPHA; + + private boolean transparent; + + /** + * Create a new note with specicic values. + * + * @param width + * <code>int</code> value that is the default width in logical + * units + * @param height + * <code>int</code> value that is the default height in logical + * units + * @param insets + * <code>Insets</code> that is the empty margin inside the note + * figure in logical units + */ + public AirNoteFigure(final int width, final int height, final Insets insets) { + super(width, height, insets); + } + + /** + * Create a new note figure with default values. + */ + public AirNoteFigure() { + super(100, 50, new Insets(5, 5, 5, 5)); + } + + /** + * {@inheritDoc} + */ + @Override + protected void paintFigure(Graphics g) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, g); + modifier.pushState(); + super.paintFigure(g); + modifier.popState(); + + } + + /** + * {@inheritDoc} + */ + public int getSiriusAlpha() { + return viewpointAlpha; + } + + /** + * {@inheritDoc} + */ + public boolean isTransparent() { + return transparent; + } + + /** + * {@inheritDoc} + */ + public void setSiriusAlpha(int alpha) { + this.viewpointAlpha = alpha; + + } + + /** + * {@inheritDoc} + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirStyleDefaultSizeNodeFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirStyleDefaultSizeNodeFigure.java new file mode 100644 index 0000000000..f89ab3c7e4 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AirStyleDefaultSizeNodeFigure.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; + +/** + * The sefault size node for styles. + * + * @author ymortier + */ +public class AirStyleDefaultSizeNodeFigure extends DefaultSizeNodeFigure { + + /** + * Constructor. + * + * @param defSize + * the default size. + */ + public AirStyleDefaultSizeNodeFigure(final Dimension defSize) { + super(defSize); + } + + /** + * Constructor. + * + * @param width + * the initial width to initialize the default size with + * @param height + * the initial height to initialize the default size with + */ + public AirStyleDefaultSizeNodeFigure(final int width, final int height) { + super(width, height); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle) + */ + @Override + public void setBounds(final Rectangle rect) { + if (getParent() != null) { + super.setBounds(getParent().getBounds()); + } else { + super.setBounds(rect); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AlphaDropShadowBorder.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AlphaDropShadowBorder.java new file mode 100644 index 0000000000..ca30221ef8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/AlphaDropShadowBorder.java @@ -0,0 +1,127 @@ +/*********************************************************************** + * Copyright (c) 2008 Anyware Technologies + * + * 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: + * Simon Bernard (Anyware Technologies) - initial API and implementation + * Cedric Brun (Obeo) - changes to consider the included shape and not the wrapping one. + * + * $Id: AlphaDropShadowBorder.java,v 1.1 2008/08/12 13:24:50 jlescot Exp $ + **********************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.AbstractBackground; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.figures.DiagramColorConstants; +import org.eclipse.gmf.runtime.draw2d.ui.figures.DropShadowBorder; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IPolygonAnchorableFigure; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; +import org.eclipse.swt.graphics.Color; + +/** + * A border using a shadow<br> + * creation : 17 mai. 08 + * + * @author <a href="mailto:simon.bernard@anyware-tech.com">Simon Bernard</a> + * CHECKSTYLE:OFF + */ +public class AlphaDropShadowBorder extends AbstractBackground implements DropShadowBorder { + + private static final int DEFAULT_SHIFT_VALUE = 1; + + private static final Color SHADOW_COLOR = DiagramColorConstants.diagramDarkGray; + + private static final int DEFAULT_TRANSPARENCY = 65; + + private boolean shouldDrawShadow = true; + + private int shift = DEFAULT_SHIFT_VALUE; + + private IFigure shape; + + /** + * + * @param shape + */ + public AlphaDropShadowBorder(IFigure shape) { + super(); + this.shape = shape; + } + + public void setShouldDrawDropShadow(boolean drawDropShadow) { + shouldDrawShadow = drawDropShadow; + } + + public boolean shouldDrawDropShadow() { + return shouldDrawShadow; + } + + /** + * Method for determining the inset the border will take up on the shape. + * + * @param figure + * Figure that will be inset from the border + * @return Insets the Insets for the border on the given figure. + */ + @Override + public Insets getInsets(IFigure figure) { + Insets insetsNew = new Insets(); + insetsNew.top = 0; + insetsNew.left = 0; + insetsNew.bottom = MapModeUtil.getMapMode(figure).DPtoLP(shift * 2); + insetsNew.right = MapModeUtil.getMapMode(figure).DPtoLP(shift * 2); + return insetsNew; + } + + public Insets getTransparentInsets(IFigure figure) { + Insets insetsNew = new Insets(); + insetsNew.top = 0; + insetsNew.left = 0; + insetsNew.bottom = MapModeUtil.getMapMode(figure).DPtoLP(shift * 2); + insetsNew.right = MapModeUtil.getMapMode(figure).DPtoLP(shift * 2); + return insetsNew; + } + + @Override + public void paintBackground(IFigure figure, Graphics graphics, Insets insets) { + if (shouldDrawDropShadow()) { + int ORIGINALALPHA = graphics.getAlpha(); + graphics.pushState(); + graphics.setBackgroundColor(SHADOW_COLOR); + graphics.setAlpha(DEFAULT_TRANSPARENCY); + if (shape instanceof IRoundedCorner) { + int cWidth = ((IRoundedCorner) shape).getCornerWidth(); + int cHeight = ((IRoundedCorner) shape).getCornerWidth(); + Rectangle bounds = figure.getBounds().getCopy(); + bounds.translate(shift, shift); + graphics.fillRoundRectangle(bounds, cWidth, cHeight); + bounds.translate(shift, shift); + graphics.fillRoundRectangle(bounds, cWidth, cHeight); + + } else if (shape instanceof IPolygonAnchorableFigure) { + PointList polygonPoints = ((IPolygonAnchorableFigure) shape).getPolygonPoints(); + polygonPoints.translate(shift, shift); + graphics.fillPolygon(polygonPoints); + polygonPoints.translate(shift, shift); + graphics.fillPolygon(polygonPoints); + } else { + Rectangle bounds = figure.getBounds().getCopy(); + bounds.translate(shift, shift); + graphics.fillRoundRectangle(bounds, 0, 0); + bounds.translate(shift, shift); + graphics.fillRoundRectangle(bounds, 0, 0); + } + graphics.setAlpha(ORIGINALALPHA); + graphics.popState(); + + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/BundledImageFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/BundledImageFigure.java new file mode 100644 index 0000000000..3ead512cb4 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/BundledImageFigure.java @@ -0,0 +1,396 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.XYLayout; +import org.eclipse.swt.graphics.Color; +import org.w3c.dom.Document; +import org.w3c.dom.Element; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.BundledImage; +import org.eclipse.sirius.RGBValues; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.ui.tools.api.color.ColorManager; + +/** + * A {@link BundledImageFigure} is a Figure corresponding to an Image defined in + * a plugin. + * + * @author cbrun + */ +public class BundledImageFigure extends AbstractCachedSVGFigure { + + /** + * The stroke tag in the SVG file. + */ + private static final String SVG_STROKE = "stroke"; + /** + * The fill tag in the SVG file. + */ + private static final String SVG_FILL = "fill"; + + /** + * The stop-color tag in the SVG file. + */ + private static final String SVG_STOP_COLOR = "stop-color"; + + /** + * The name of the style attribute in the SVG file. + */ + private static final String SVG_STYLE_ATTRIBUTE_NAME = "style"; + + /** + * The id of the lighter stop color of the gradient in the SVG file. + */ + private static final String SVG_STOP_LIGHTER_ID = "stop1"; + + /** + * The id of the main stop color of the gradient in the SVG file. + */ + private static final String SVG_STOP_MAIN_ID = "stop2"; + + /** + * The id of the gradient element in the SVG file. + */ + private static final String SVG_GRADIENT_ELEMENT_ID = "elementWithGradient"; + + /** + * The id of the shadow element in the SVG file. + */ + private static final String SVG_SHADOW_ELEMENT_ID = "shadow"; + + private static final String IMAGE_DIR = "images/"; //$NON-NLS-1$ + + private static final String IMAGE_EXT = ".svg"; //$NON-NLS-1$ + + /** + * * The actual shapeName use to draw the SVG figure + */ + private String shapeName; + + /** + * The actual border color use to draw the SVG figure + */ + private String mainBorderColor; + + /** + * The actual lighter border color use to draw the shadow of SVG figure + */ + private String lighterBorderColor; + + /** + * The actual lighter gradient color use to draw the SVG figure + */ + private String lighterGradientColor; + + /** + * The actual main gradient color use to draw the SVG figure + */ + private String mainGradientColor; + + /** + * Build a new {@link BundledImageFigure} from an Image instance. + * + */ + public BundledImageFigure() { + this.setLayoutManager(new XYLayout()); + } + + /** + * Create the {@link BundledImageFigure} from a {@link BundledImage} + * instance. + * + * @param bundle + * {@link BundledImage} specification. + * @return new Figure. + */ + public static IFigure createImageFigure(final BundledImage bundle) { + final BundledImageFigure fig = new BundledImageFigure(); + fig.refreshFigure(bundle); + return fig; + } + + /** + * @param bundle + */ + private boolean updateShape(BundledImage bundledImage) { + boolean updated = false; + if (bundledImage != null && bundledImage.getShape() != null) { + String newShapeName = bundledImage.getShape().getName(); + if (!StringUtil.isEmpty(newShapeName) && !newShapeName.equals(getShapeName())) { + this.setURI(getImageFileURI(newShapeName), false); + this.setShapeName(newShapeName); + updated = true; + } + } + return updated; + } + + /** + * @param bundledImage + * @param force + * If the color must be force to refresh (in case of shape update + * for sample) + */ + private boolean updateColors(BundledImage bundledImage, boolean force) { + boolean updated = updateColorFields(bundledImage); + updated = updateDocumentColors(force || updated); + return updated; + } + + private boolean updateColorFields(BundledImage bundledImage) { + // Compute colors + RGBValues color = bundledImage.getColor(); + Color newLighterColor = ColorManager.getDefault().getLighterColor(color); + + RGBValues borderColor = bundledImage.getBorderColor(); + Color newBorderLighterColor = ColorManager.getDefault().getLighterColor(borderColor); + + // Get Hexa values + String hexaColor = getRGBValuesColorToHexa(color); + String hexaLighterColor = getColorToHexa(newLighterColor); + String hexaBorderColor = getRGBValuesColorToHexa(borderColor); + String hexaLighterBorderColor = getColorToHexa(newBorderLighterColor); + + boolean updated = false; + + if (hexaColor != null && (!hexaColor.equals(this.getMainGradientColor()))) { + this.setMainGradientColor(hexaColor); + updated = true; + } + + if (hexaLighterColor != null && (!hexaLighterColor.equals(this.getLighterGradientColor()))) { + this.setLighterGradientColor(hexaLighterColor); + updated = true; + } + + if (hexaBorderColor != null && (!hexaBorderColor.equals(this.getMainBorderColor()))) { + this.setMainBorderColor(hexaBorderColor); + updated = true; + } + + if (hexaLighterBorderColor != null && (!hexaLighterBorderColor.equals(this.getLighterBorderColor()))) { + this.setLighterBorderColor(hexaLighterBorderColor); + updated = true; + } + + return updated; + } + + private boolean updateDocumentColors(boolean needsUpdate) { + boolean updated = false; + if (needsUpdate) { + setURI(getURI(), false); + Document document = this.getDocument(); + if (document != null && needsUpdate) { + /* Update the border color (if exists). */ + Element gradientStep1 = document.getElementById(BundledImageFigure.SVG_STOP_LIGHTER_ID); + if (gradientStep1 != null) { + String gradientStep1Style = gradientStep1.getAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME); + gradientStep1.setAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME, getNewStyle(gradientStep1Style, BundledImageFigure.SVG_STOP_COLOR, getLighterGradientColor())); + updated = true; + } + + /* Update the main gradient color (if exists). */ + Element gradientStep2 = document.getElementById(BundledImageFigure.SVG_STOP_MAIN_ID); + if (gradientStep2 != null) { + String gradientStep2Style = gradientStep2.getAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME); + gradientStep2.setAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME, getNewStyle(gradientStep2Style, BundledImageFigure.SVG_STOP_COLOR, getMainGradientColor())); + updated = true; + } + + /* Update the shadow border (if exists). */ + Element shadow = document.getElementById(SVG_SHADOW_ELEMENT_ID); + if (shadow != null) { + String shadowStyle = shadow.getAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME); + shadow.setAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME, getNewStyle(shadowStyle, SVG_FILL, getLighterBorderColor())); + updated = true; + } + + /* Update the border color (if exists). */ + Element elementWithGradient = document.getElementById(BundledImageFigure.SVG_GRADIENT_ELEMENT_ID); + if (elementWithGradient != null) { + String elementWithGradientStyle = elementWithGradient.getAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME); + elementWithGradient.setAttribute(BundledImageFigure.SVG_STYLE_ATTRIBUTE_NAME, getNewStyle(elementWithGradientStyle, BundledImageFigure.SVG_STROKE, getMainBorderColor())); + updated = true; + } + } + } + return updated; + } + + /** + * @param shapeName + * @return + */ + private static String getImageFileURI(String shapeName) { + final String path = new StringBuffer(IMAGE_DIR).append(shapeName).append(IMAGE_EXT).toString(); + String pluginId = SiriusDiagramEditorPlugin.getInstance().getBundle().getSymbolicName(); + return "platform:/plugin/" + pluginId + "/" + path; + } + + /** + * @param color + * The color to transform in hexa value + * @return The hexa representation of the color. + */ + private static String getRGBValuesColorToHexa(final RGBValues color) { + String blankDigit = "0"; + StringBuffer colorInHexa = new StringBuffer(); + String hexaColor = Integer.toHexString(color.getRed()); + if (hexaColor.length() == 1) { + colorInHexa.append(blankDigit); + } + colorInHexa.append(hexaColor); + hexaColor = Integer.toHexString(color.getGreen()); + if (hexaColor.length() == 1) { + colorInHexa.append(blankDigit); + } + colorInHexa.append(hexaColor); + hexaColor = Integer.toHexString(color.getBlue()); + if (hexaColor.length() == 1) { + colorInHexa.append(blankDigit); + } + colorInHexa.append(hexaColor); + return colorInHexa.toString(); + } + + /** + * @param color + * The color to transform in hexa value + * @return The hexa representation of the color. + */ + private String getColorToHexa(Color color) { + String blankDigit = "0"; + StringBuffer colorInHexa = new StringBuffer(); + String hexaColor = Integer.toHexString(color.getRed()); + if (hexaColor.length() == 1) { + colorInHexa.append(blankDigit); + } + colorInHexa.append(hexaColor); + hexaColor = Integer.toHexString(color.getGreen()); + if (hexaColor.length() == 1) { + colorInHexa.append(blankDigit); + } + colorInHexa.append(hexaColor); + hexaColor = Integer.toHexString(color.getBlue()); + if (hexaColor.length() == 1) { + colorInHexa.append(blankDigit); + } + colorInHexa.append(hexaColor); + return colorInHexa.toString(); + } + + private static String getNewStyle(String actualStyle, String colorAttribute, String newColor) { + int indexOfColorAttribute = actualStyle.indexOf(colorAttribute); + String newStyle = actualStyle.substring(0, indexOfColorAttribute + colorAttribute.length() + 2); + newStyle = newStyle.concat(newColor); + newStyle = newStyle.concat(actualStyle.substring(actualStyle.indexOf(";", indexOfColorAttribute), actualStyle.length())); + return newStyle; + } + + /** + * refresh the figure. + * + * @param bundledImage + * the image associated to the figure + */ + public void refreshFigure(final BundledImage bundledImage) { + if (bundledImage != null) { + boolean updated = this.updateShape(bundledImage); + updated = this.updateColors(bundledImage, updated) || updated; + if (updated) { + this.contentChanged(); + } + } else { + this.setURI(null); + } + } + + protected String getShapeName() { + return shapeName; + } + + protected void setShapeName(String shapeName) { + this.shapeName = shapeName; + } + + protected String getMainBorderColor() { + return mainBorderColor; + } + + protected void setMainBorderColor(String mainBorderColor) { + this.mainBorderColor = mainBorderColor; + } + + protected String getLighterBorderColor() { + return lighterBorderColor; + } + + protected void setLighterBorderColor(String lighterBorderColor) { + this.lighterBorderColor = lighterBorderColor; + } + + protected String getLighterGradientColor() { + return lighterGradientColor; + } + + protected void setLighterGradientColor(String lighterGradientColor) { + this.lighterGradientColor = lighterGradientColor; + } + + protected String getMainGradientColor() { + return mainGradientColor; + } + + protected void setMainGradientColor(String mainGradientColor) { + this.mainGradientColor = mainGradientColor; + } + + /** + * Compute a key for this BundleImageFigure. This key is used to store in + * cache the corresponding {@link org.eclipse.swt.graphics.Image}. + * + * {@inheritDoc} + * + * @return The key corresponding to this BundleImageFigure. + */ + protected String getKey() { + StringBuffer result = new StringBuffer(); + result.append(getDocumentKey()); + result.append(SEPARATOR); + result.append(getSiriusAlpha()); + result.append(SEPARATOR); + return result.toString(); + } + + /** + * Compute a key for this BundleImageFigure. This key is used to store in + * cache the corresponding {@link org.eclipse.swt.graphics.Image}. + * + * {@inheritDoc} + * + * @return The key corresponding to this BundleImageFigure. + */ + protected String getDocumentKey() { + StringBuffer result = new StringBuffer(); + result.append(super.getDocumentKey()); + result.append(SEPARATOR); + result.append(shapeName); + result.append(SEPARATOR); + result.append(mainBorderColor); + result.append(SEPARATOR); + result.append(mainGradientColor); + return result.toString(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/DBorderedNodeFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/DBorderedNodeFigure.java new file mode 100644 index 0000000000..7b0b796acc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/DBorderedNodeFigure.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.gmf.runtime.diagram.ui.figures.BorderedNodeFigure; + +/** + * This figure overrides {@link BorderedNodeFigure} in order to force layout + * because of an issue where resizing a node will not relocate its bordered + * nodes. + * + * @author smonnier + * + */ +public class DBorderedNodeFigure extends BorderedNodeFigure { + + /** + * Creates a new DBorderedNodeFigure figure. + * + * @param mainFigure + * the figure to use with this figure + */ + public DBorderedNodeFigure(IFigure mainFigure) { + super(mainFigure); + } + + /** + * This method has been overridden to avoid comparing bounds of this and + * getMainFigure(). {@inheritDoc} + */ + @Override + protected void layout() { + getMainFigure().setBounds(this.getBounds().getCopy()); + getBorderItemContainer().invalidateTree(); + erase(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FigureQuery.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FigureQuery.java new file mode 100644 index 0000000000..5d8f275ce2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FigureQuery.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.Label; +import org.eclipse.gmf.runtime.draw2d.ui.figures.WrapLabel; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; + +/** + * A class aggregating all the queries (read-only!) having a {@link IFigure} as + * starting point. + * + * @author lredor + * + */ +public class FigureQuery { + + private IFigure figure; + + /** + * Create a new query. + * + * @param figure + * the element to query. + */ + public FigureQuery(IFigure figure) { + this.figure = figure; + } + + /** + * Return the label figure of this figure. Search in all chidren the first + * figure of kind {@link Label}, {@link WrapLabel} or + * {@link SiriusWrapLabel}. + * + * @return the first label figure or null if any + */ + public Option<IFigure> getLabelFigure() { + Option<IFigure> result = Options.newNone(); + if (figure instanceof SiriusWrapLabel || figure instanceof WrapLabel || figure instanceof Label) { + result = Options.newSome(figure); + } else { + for (IFigure childFigure : Iterables.filter(figure.getChildren(), IFigure.class)) { + Option<IFigure> temp = new FigureQuery(childFigure).getLabelFigure(); + if (temp.some()) { + result = temp; + break; + } + } + } + return result; + } + + /** + * Return the label of the first label figure of this figure. Search in all + * chidren the first figure of kind {@link Label}, {@link WrapLabel} or + * {@link SiriusWrapLabel}. + * + * @return the label of the first label figure or null if any + */ + public Option<String> getText() { + Option<String> result = Options.newNone(); + Option<IFigure> labelFigure = getLabelFigure(); + if (labelFigure.some()) { + if (labelFigure.get() instanceof SiriusWrapLabel) { + result = Options.newSome(((SiriusWrapLabel) labelFigure.get()).getText()); + } + if (labelFigure.get() instanceof WrapLabel) { + result = Options.newSome(((WrapLabel) labelFigure.get()).getText()); + } else if (labelFigure.get() instanceof Label) { + result = Options.newSome(((Label) labelFigure.get()).getText()); + } + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FoldingToggleAwareClippingStrategy.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FoldingToggleAwareClippingStrategy.java new file mode 100644 index 0000000000..c9291ac60e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FoldingToggleAwareClippingStrategy.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IClippingStrategy; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Rectangle; + +/** + * A clipping strategy to avoid (0,0) sized clip area for + * {@link FoldingToggleImageFigure} and allow repaint after user style + * customization. For other figures type, it keep the + * {@link org.eclipse.draw2d.Figure#paintChildren(org.eclipse.draw2d.Graphics)} + * behavior. + * + * @author mporhel + */ +public class FoldingToggleAwareClippingStrategy implements IClippingStrategy { + + /** + * {@inheritDoc} + */ + public Rectangle[] getClip(IFigure childFigure) { + if (childFigure != null) { + Rectangle bounds = childFigure.getBounds().getCopy(); + if (childFigure instanceof FoldingToggleImageFigure) { + bounds.setSize(FoldingToggleImageFigure.FOLD_ICON_HEIGHT, FoldingToggleImageFigure.FOLD_ICON_WIDTH); + } + return new Rectangle[] { bounds }; + } + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FoldingToggleImageFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FoldingToggleImageFigure.java new file mode 100644 index 0000000000..05596d14d0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/FoldingToggleImageFigure.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.swt.graphics.Image; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.diagram.business.internal.query.EdgeTargetQuery; +import org.eclipse.sirius.diagram.edit.api.part.IAbstractDiagramNodeEditPart; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.commands.ToggleFoldingStateCommand; + +/** + * A figure which displays and controls the folding state of an element. + * + * @author pcdavid + */ +public class FoldingToggleImageFigure extends ActionTriggerImageFigure { + + /** + * Width of the images used for the folding toggle. + */ + protected static final int FOLD_ICON_WIDTH = 9; + + /** + * Height of the images used for the folding toggle. + */ + protected static final int FOLD_ICON_HEIGHT = FOLD_ICON_WIDTH; + + private static final Image MINUS_IMAGE = SiriusDiagramEditorPlugin.getInstance().getImage(SiriusDiagramEditorPlugin.getBundledImageDescriptor("/icons/collapse.gif")); + + private static final Image PLUS_IMAGE = SiriusDiagramEditorPlugin.getInstance().getImage(SiriusDiagramEditorPlugin.getBundledImageDescriptor("/icons/expand.gif")); + + private final IAbstractDiagramNodeEditPart part; + + /** + * Constructor. + * + * @param part + * the element whose folding state to display and control. + */ + public FoldingToggleImageFigure(IAbstractDiagramNodeEditPart part) { + this.part = Preconditions.checkNotNull(part); + setSize(new Dimension(FOLD_ICON_WIDTH, FOLD_ICON_HEIGHT)); + show(null); + DDiagramElement element = part.resolveDiagramElement(); + if (element instanceof EdgeTarget) { + updateImage(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void paint(Graphics graphics) { + updateImage(); + super.paint(graphics); + } + + /** + * {@inheritDoc} + */ + @Override + public void trigger() { + DDiagramElement element = part.resolveDiagramElement(); + if (element instanceof EdgeTarget) { + TransactionalEditingDomain domain = part.getEditingDomain(); + domain.getCommandStack().execute(new ToggleFoldingStateCommand(domain, part)); + updateImage(); + } + } + + private void updateImage() { + if (part.isActive()) { + DDiagramElement element = part.resolveDiagramElement(); + if (element instanceof EdgeTarget) { + EdgeTargetQuery query = new EdgeTargetQuery((EdgeTarget) element); + if (!query.isFoldingPoint()) { + show(null); + } else { + switch (query.getFoldingState()) { + case FOLDED: + show(PLUS_IMAGE); + break; + case UNFOLDED: + case MIXED: + default: + show(MINUS_IMAGE); + break; + } + } + } + } + } + + private void show(Image img) { + setImageWOFocus(img); + setImageWFocus(img); + if (img == null) { + setSize(0, 0); + } else { + setSize(FOLD_ICON_WIDTH, FOLD_ICON_HEIGHT); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GaugeCompositeFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GaugeCompositeFigure.java new file mode 100644 index 0000000000..8320716bc9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GaugeCompositeFigure.java @@ -0,0 +1,188 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.RandomAccess; + +import org.eclipse.draw2d.XYLayout; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.draw2d.ui.figures.RectangularDropShadowLineBorder; + +import org.eclipse.sirius.AlignmentKind; + +/** + * Figures that paints many gauges. + * + * @author ymortier + */ +public class GaugeCompositeFigure extends AbstractTransparentNode implements StyledFigure { + private List<GaugeSectionFigure> gauges; + + private AlignmentKind alignment = AlignmentKind.HORIZONTAL_LITERAL; + + /** + * Create a new {@link GaugeCompositeFigure}. + */ + public GaugeCompositeFigure() { + this.setLayoutManager(new XYLayout()); + this.gauges = new ArrayList<GaugeSectionFigure>(); + this.setBorder(new RectangularDropShadowLineBorder()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setBounds(org.eclipse.draw2d.geometry.Rectangle) + */ + @Override + public void setBounds(final Rectangle rect) { + super.setBounds(rect); + this.computeChildrenBounds(rect); + } + + /** + * Set a new alignment. + * + * @param alignment + * new alignment. + * @since 2.0 + */ + public void setAlignment(final AlignmentKind alignment) { + this.alignment = alignment; + } + + /** + * Returns the gauge. + * + * @return the gauge. + */ + public List<GaugeSectionFigure> getGauges() { + return Collections.unmodifiableList(gauges); + } + + /** + * Returns the gauge at the specified index. + * + * @param index + * index of the gauge to return. + * @return the gauge at the specified index. + */ + public GaugeSectionFigure getGaugeAt(final int index) { + return this.gauges.get(index); + } + + /** + * Adds a gauge. + */ + public void addGauge() { + final GaugeSectionFigure gauge = GaugeSectionFigure.createDefaultSection(); + this.add(gauge, 0); + this.gauges.add(gauge); + this.computeChildrenBounds(this.getBounds()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#useLocalCoordinates() + */ + @Override + protected boolean useLocalCoordinates() { + return true; + } + + private void computeChildrenBounds(final Rectangle rect) { + final int borderInsetsH = (getBorder() == null || getBorder().getInsets(this) == null) ? 0 : getBorder().getInsets(this).left + getBorder().getInsets(this).right; + final int borderInsetsV = (getBorder() == null || getBorder().getInsets(this) == null) ? 0 : getBorder().getInsets(this).top + getBorder().getInsets(this).bottom; + + Dimension childDimension = null; + + int sideSize = -1; + int vSideSize = -1; + + int nbGauges = this.gauges.size(); + if (nbGauges == 0) { + nbGauges = 1; + } + + switch (this.alignment) { + case VERTICAL_LITERAL: + childDimension = new Dimension(rect.width - borderInsetsH, (rect.height - borderInsetsV) / nbGauges); + break; + case HORIZONTAL_LITERAL: + childDimension = new Dimension((rect.width - borderInsetsH) / nbGauges, rect.height - borderInsetsV); + break; + case SQUARE_LITERAL: + default: + // ... square ... + sideSize = (int) Math.ceil(Math.sqrt(nbGauges)); + // adjust v size + vSideSize = sideSize; + if (nbGauges != (sideSize * sideSize) && nbGauges % sideSize == 0) { + vSideSize--; + } + sideSize = sideSize <= 0 ? 1 : sideSize; + vSideSize = vSideSize <= 0 ? 1 : vSideSize; + childDimension = new Dimension((rect.width - borderInsetsH) / sideSize, (rect.height - borderInsetsV) / vSideSize); + break; + } + + List sections = this.getGauges(); + if (!(sections instanceof RandomAccess)) { + // Convert sections to an ArrayList to improve performance. + sections = new ArrayList(this.getGauges()); + } + for (int i = 0; i < sections.size(); i++) { + final GaugeSectionFigure figure = (GaugeSectionFigure) sections.get(i); + Point p = null; + switch (this.alignment) { + case VERTICAL_LITERAL: + p = new Point(0, childDimension.height * i); + break; + case HORIZONTAL_LITERAL: + p = new Point(childDimension.width * i, 0); + break; + case SQUARE_LITERAL: + default: + p = new Point(childDimension.width * (i % sideSize), childDimension.height * (Math.floor((double) i / sideSize) % vSideSize)); + break; + } + figure.setBounds(new Rectangle(p, childDimension)); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setTransparent(boolean transparent) { + super.setTransparent(transparent); + for (GaugeSectionFigure section : gauges) { + section.setTransparent(transparent); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setSiriusAlpha(int alpha) { + super.setSiriusAlpha(alpha); + for (GaugeSectionFigure section : gauges) { + section.setSiriusAlpha(alpha); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GaugeSectionFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GaugeSectionFigure.java new file mode 100644 index 0000000000..3c5184b2ff --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GaugeSectionFigure.java @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.FlowLayout; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.LineBorder; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.text.FlowPage; +import org.eclipse.draw2d.text.TextFlow; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * Figure of a section of a quadriptyque. + * + * @author ymortier + */ +public class GaugeSectionFigure extends AbstractTransparentNode { + + /** The minimal value. */ + private int min; + + /** The maximal value. */ + private int max; + + /** The value. */ + private int value; + + private final TextFlow textFlow = new TextFlow(StringUtil.EMPTY_STRING); + + /** + * Creates a new <code>QuadriptyqueSectionFigure</code>. + * + * @param min + * the minimal value. + * @param max + * the maximal value. + * @param value + * the value. + */ + public GaugeSectionFigure(final int min, final int max, final int value) { + this.setLayoutManager(new FlowLayout()); + final FlowPage flowPage = new FlowPage(); + flowPage.add(textFlow); + this.add(flowPage); + this.textFlow.setForegroundColor(ColorConstants.white); + this.min = min; + this.max = max; + this.value = value; + this.setBorder(new LineBorder()); + } + + /** + * Returns the minimal value. + * + * @return the minimal value. + */ + public int getMin() { + return min; + } + + /** + * Sets the minimal value. + * + * @param min + * the minimal value. + */ + public void setMin(final int min) { + this.min = min; + } + + /** + * Returns the maximal value. + * + * @return the maximal value. + */ + public int getMax() { + return max; + } + + /** + * Sets the maximal value. + * + * @param max + * the maximal value. + */ + public void setMax(final int max) { + this.max = max; + } + + /** + * Returns the value. + * + * @return the value. + */ + public int getValue() { + return value; + } + + /** + * Sets the value. + * + * @param value + * the value. + */ + public void setValue(final int value) { + this.value = value; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#useLocalCoordinates() + */ + @Override + protected boolean useLocalCoordinates() { + return true; + } + + /** + * Set the section label. + * + * @param label + * new label value. + */ + public void setLabel(final String label) { + this.textFlow.setText(label); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure#paintFigure(org.eclipse.draw2d.Graphics) + */ + @Override + protected void paintFigure(final Graphics graphics) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, graphics); + modifier.pushState(); + + super.paintFigure(graphics); + final Rectangle bounds = new Rectangle(this.getBounds()); + final int divisor = max < min ? 1 : max - min; + final int val = this.value > max ? max - min : this.value - min; + final int y; + if (divisor == 0) { + y = val == 0 ? 0 : bounds.height; + } else { + y = (int) (bounds.height * ((double) val / divisor)); + } + + graphics.setLineWidth(0); + graphics.setBackgroundColor(this.getBackgroundColor()); + graphics.setForegroundColor(this.getBackgroundColor()); + graphics.fillRectangle(+bounds.x, bounds.y, bounds.width, bounds.height - y); + graphics.setForegroundColor(this.getForegroundColor()); + graphics.setBackgroundColor(this.getForegroundColor()); + graphics.fillRectangle(bounds.x, bounds.y + bounds.height - y, bounds.width, y); + + modifier.popState(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#paintBorder(org.eclipse.draw2d.Graphics) + */ + @Override + protected void paintBorder(final Graphics graphics) { + graphics.setBackgroundColor(ColorConstants.white); + graphics.setForegroundColor(ColorConstants.white); + super.paintBorder(graphics); + } + + /** + * Create the default section. + * + * @return a default section. + */ + public static GaugeSectionFigure createDefaultSection() { + return new GaugeSectionFigure(1, 10, 5); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GradientHelper.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GradientHelper.java new file mode 100644 index 0000000000..92bff60d90 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GradientHelper.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2007, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.SWTGraphics; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Pattern; + +import org.eclipse.sirius.BackgroundStyle; +import org.eclipse.sirius.diagram.tools.internal.figure.util.GraphicsUtilities; +import org.eclipse.sirius.ui.tools.api.color.VisualBindingManager; + +/** + * Helper for the creation of gradient from ViewGradientFigureDesc. + * + * @author mporhel + * + */ +public final class GradientHelper { + + private GradientHelper() { + + } + + /** + * Set the gradation of colors. + * + * @param graphics + * the graphics + * @param figure + * the figure + */ + public static void setColorsGradation(final Graphics graphics, final ViewGradientFigureDesc figure) { + + final Rectangle zoomedBounds = GraphicsUtilities.zoomFillRectangle(graphics, figure.getBounds()); + if (zoomedBounds != null) { + final Pattern pattern = GradientHelper.getGradientPattern(figure.getBackgroundStyle().getValue(), zoomedBounds, figure.getBackgroundColor(), figure.getGradientColor()); + SWTGraphics swtGraphics = GraphicsUtilities.getSWTGraphics(graphics); + if (swtGraphics != null) { + swtGraphics.setBackgroundPattern(pattern); + } + } + + } + + /** + * Returns the pattern corresponding to the wanted gradient. + * + * @param backgroundStyle + * the backgroud style + * @param bounds + * the bounds + * @param backgroundColor + * the bachground color + * @param gradientColor + * the gradient color + * @return the wanted pattern + */ + public static Pattern getGradientPattern(final int backgroundStyle, final Rectangle bounds, final Color backgroundColor, final Color gradientColor) { + final Pattern pattern; + switch (backgroundStyle) { + case BackgroundStyle.GRADIENT_TOP_TO_BOTTOM: + pattern = GradientHelper.getGradientTopToBottom(bounds, backgroundColor, gradientColor); + break; + case BackgroundStyle.LIQUID: + pattern = GradientHelper.getGradientDiag(bounds, backgroundColor, gradientColor); + break; + case BackgroundStyle.GRADIENT_LEFT_TO_RIGHT: + default: + pattern = GradientHelper.getGradientLeftToRight(bounds, backgroundColor, gradientColor); + break; + } + return pattern; + } + + /** + * Returns the pattern corresponding to the LeftToRight gradient. + * + * @param bounds + * the bounds + * @param backgroundColor + * the background color + * @param gradientColor + * teh gradient color + * @return the correesponding pattern. + */ + public static Pattern getGradientLeftToRight(final Rectangle bounds, final Color backgroundColor, final Color gradientColor) { + return VisualBindingManager.getDefault().getPatternFromValue(bounds.x, bounds.y, bounds.x + bounds.width, bounds.y, backgroundColor, gradientColor); + } + + /** + * Returns the pattern corresponding to the diagonal gradient. + * + * @param bounds + * the bounds + * @param backgroundColor + * the background color + * @param gradientColor + * the gradient color + * @return a diagonal pattern + */ + public static Pattern getGradientDiag(final Rectangle bounds, final Color backgroundColor, final Color gradientColor) { + // size of the square use to compute gradient + int x = bounds.x; + int y = bounds.y; + int i = (bounds.width + bounds.height) / 2; + + int gradientZoneWidth = Math.max(bounds.width, i); + if (gradientZoneWidth != bounds.width) { + x = bounds.x - (i - bounds.width) / 2; + } + + int gradientZoneHeight = Math.max(bounds.height, i); + if (gradientZoneHeight != bounds.height) { + y = bounds.y - (i - bounds.height) / 2; + } + return VisualBindingManager.getDefault().getPatternFromValue(x, y, x + gradientZoneWidth, y + gradientZoneHeight, backgroundColor, gradientColor); + } + + /** + * Returns the pattern corresponding to the TopToBottom gradient. + * + * @param bounds + * the bounds + * @param backgroundColor + * the background color + * @param gradientColor + * the gradient color + * @return the correesponding pattern. + */ + public static Pattern getGradientTopToBottom(final Rectangle bounds, final Color backgroundColor, final Color gradientColor) { + return VisualBindingManager.getDefault().getPatternFromValue(bounds.x, bounds.y, bounds.x, bounds.y + bounds.height, backgroundColor, gradientColor); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GradientRoundedRectangle.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GradientRoundedRectangle.java new file mode 100644 index 0000000000..a9c496e843 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/GradientRoundedRectangle.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.MarginBorder; +import org.eclipse.draw2d.RoundedRectangle; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; +import org.eclipse.swt.graphics.Color; + +import org.eclipse.sirius.BackgroundStyle; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IContainerLabelOffsets; + +/** + * Basic implementation of RoundedRectangle shape with gradient and label + * capabilities. + * + * @author mporhel + */ +public class GradientRoundedRectangle extends RoundedRectangle implements ViewNodeContainerFigureDesc, ViewGradientFigureDesc, IRoundedCorner { + + private SiriusWrapLabel fLabelFigure; + + private Color gradientColor; + + private BackgroundStyle backgroundStyle; + + private boolean myUseLocalCoordinates; + + /** + * Create a new {@link GradientRoundedRectangle}. + * + * @param dimension + * dimension of the corner (with radius, height radius) + * @param backgroundStyle + * style of the wanted gradient + * + */ + public GradientRoundedRectangle(final Dimension dimension, final BackgroundStyle backgroundStyle) { + this.backgroundStyle = backgroundStyle; + this.setCornerDimensions(new Dimension(MapModeUtil.getMapMode().DPtoLP(dimension.width), MapModeUtil.getMapMode().DPtoLP(dimension.height))); + createBorder(); + createContents(); + } + + /** + * Create a new {@link GradientRoundedRectangle}. + */ + public GradientRoundedRectangle() { + this(new Dimension(8, 8), BackgroundStyle.GRADIENT_LEFT_TO_RIGHT_LITERAL); + } + + /** + * Sets the gradient color. + * + * @param color + * The gradient color + */ + public void setGradientColor(final Color color) { + this.gradientColor = color; + } + + /** + * {@inheritDoc} + * + * @see ViewGradientFigureDesc#getGradientColor() + */ + public Color getGradientColor() { + return this.gradientColor; + } + + /** + * Create the content of the figure. + */ + protected void createContents() { + fLabelFigure = new SiriusWrapLabel(); + fLabelFigure.setText(" "); + fLabelFigure.setTextWrap(true); + this.add(fLabelFigure); + } + + /** + * Create the border. + */ + protected void createBorder() { + this.setBorder(new MarginBorder(IContainerLabelOffsets.LABEL_OFFSET, 0, 0, 0)); + } + + /** + * {@inheritDoc} + * + * @was-generated + * @see org.eclipse.draw2d.Figure#useLocalCoordinates() + */ + @Override + protected boolean useLocalCoordinates() { + return myUseLocalCoordinates; + } + + /** + * {@inheritDoc} + * + * @was-generated + * @param useLocalCoordinates + */ + protected void setUseLocalCoordinates(final boolean useLocalCoordinates) { + myUseLocalCoordinates = useLocalCoordinates; + } + + /** + * Return the label figure. + * + * @see org.eclipse.sirius.diagram.ui.tools.api.figure.ViewNodeContainerFigureDesc#getLabelFigure() + * @return the label figure. + */ + public SiriusWrapLabel getLabelFigure() { + return fLabelFigure; + } + + /** + * {@inheritDoc} + */ + @Override + protected void fillShape(Graphics graphics) { + if (getGradientColor() != null) { + GradientHelper.setColorsGradation(graphics, this); + } else { + graphics.setBackgroundColor(getBackgroundColor()); + } + super.fillShape(graphics); + } + + /** + * {@inheritDoc} + * + * @see ViewGradientFigureDesc#getBackgroundStyle() + */ + public BackgroundStyle getBackgroundStyle() { + return backgroundStyle; + } + + /** + * {@inheritDoc} + */ + public int getCornerHeight() { + return this.corner.height; + } + + /** + * {@inheritDoc} + */ + public int getCornerWidth() { + return this.corner.width; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/IRoundedCorner.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/IRoundedCorner.java new file mode 100644 index 0000000000..bb77b49796 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/IRoundedCorner.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +/** + * Interface for figures having rounded corners. + * + * @author cedric + * + */ +public interface IRoundedCorner { + /** + * return the corner width. + * + * @return the corner width + */ + int getCornerWidth(); + + /** + * return the corner height. + * + * @return the corner height + */ + int getCornerHeight(); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ITransparentFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ITransparentFigure.java new file mode 100644 index 0000000000..cd2b689560 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ITransparentFigure.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +/** + * Interface for transparent figures. + * + * @author mporhel + */ +public interface ITransparentFigure { + + /** + * Default alpha value. + */ + int DEFAULT_ALPHA = 100; + + /** + * Return true if figure is in transparent mode. + * + * @return true if figure is in transparent mode. + */ + boolean isTransparent(); + + /** + * Enable/disable the tranparent mode of the figure. + * + * @param transparent + * the wanted mode. + */ + void setTransparent(boolean transparent); + + /** + * Get the alpha value. + * + * @return the alpha value. + */ + int getSiriusAlpha(); + + /** + * Set the alpha to the given value. Values may range from 0 to 255. A value + * of 0 is completely transparent. + * + * @param alpha + * an alpha value (0-255) + */ + void setSiriusAlpha(int alpha); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/IWorkspaceImageFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/IWorkspaceImageFigure.java new file mode 100644 index 0000000000..2f92418648 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/IWorkspaceImageFigure.java @@ -0,0 +1,47 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IFigure; + +import org.eclipse.sirius.ContainerStyle; +import org.eclipse.sirius.WorkspaceImage; + +/** + * Interface for worskpace image figures. + * + * @author mporhel + */ +public interface IWorkspaceImageFigure extends IFigure { + + /** + * Refreshes the figure. + * + * @param containerStyle + * the style of the container + */ + void refreshFigure(final ContainerStyle containerStyle); + + /** + * refresh the figure. + * + * @param workspaceImage + * the image associated to the figure + */ + void refreshFigure(final WorkspaceImage workspaceImage); + + /** + * Get the image aspect ratio. + * + * @return the image aspect ratio + */ + double getImageAspectRatio(); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/InvisibleResizableCompartmentFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/InvisibleResizableCompartmentFigure.java new file mode 100644 index 0000000000..00616374be --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/InvisibleResizableCompartmentFigure.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.gmf.runtime.diagram.ui.figures.ShapeCompartmentFigure; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; + +/** + * A compartment figure not drawing the borders.. + * + * @author cbrun + * + */ +public class InvisibleResizableCompartmentFigure extends ShapeCompartmentFigure { + + /** + * Create a new compartment figure without borders. + * + * @param title + * compartment title. + * @param mode + * mapping mode. + */ + public InvisibleResizableCompartmentFigure(final String title, final IMapMode mode) { + super(title, mode); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#paintBorder(org.eclipse.draw2d.Graphics) + */ + @Override + protected void paintBorder(final Graphics graphics) { + /* + * we don't want to draw the borders. + */ + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/LozengeFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/LozengeFigure.java new file mode 100644 index 0000000000..2b473638bd --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/LozengeFigure.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.Shape; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; + +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * Specific Figure to handle lozenge style. + * + * @author mporhel + * + */ +public class LozengeFigure extends Shape implements StyledFigure, ITransparentFigure { + + private int viewpointAlpha = DEFAULT_ALPHA; + + private boolean transparent; + + /** + * Create a new lozenge figure with default values. + */ + public LozengeFigure() { + } + + /** + * Outlines the lozenge. + * + * @param graphics + * <code>Graphics</code> object that allows to fill the surface + */ + @Override + protected void outlineShape(final Graphics graphics) { + final PointList pointList = getPointList(); + graphics.drawPolygon(pointList); + } + + /** + * Fills the Lozenge. + * + * @see org.eclipse.draw2d.Shape#fillShape(org.eclipse.draw2d.Graphics) + * @param graphics + * <code>Graphics</code> object that allows to draw to the + * surface + */ + @Override + protected void fillShape(final Graphics graphics) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, graphics); + modifier.pushState(); + graphics.fillPolygon(getPointList()); + modifier.popState(); + } + + /** + * Computes the polygon points of the lozenge. + * + * @return PointList list of the points + */ + protected PointList getPointList() { + final Rectangle r = new Rectangle(); + final PointList pointList = new PointList(); + + r.x = bounds.x + getLineWidth() / 2; + r.y = bounds.y + getLineWidth() / 2; + r.width = bounds.width - getLineWidth(); + r.height = bounds.height - getLineWidth(); + pointList.removeAllPoints(); + pointList.addPoint(r.x + r.width / 2, r.y); + pointList.addPoint(r.x + r.width, r.y + r.height / 2); + pointList.addPoint(r.x + r.width / 2, r.y + r.height); + pointList.addPoint(r.x, r.y + r.height / 2); + + return pointList; + } + + /** + * {@inheritDoc} + */ + public int getSiriusAlpha() { + return viewpointAlpha; + } + + /** + * {@inheritDoc} + */ + public boolean isTransparent() { + return transparent; + } + + /** + * {@inheritDoc} + */ + public void setSiriusAlpha(int alpha) { + this.viewpointAlpha = alpha; + + } + + /** + * {@inheritDoc} + */ + public void setTransparent(boolean transparent) { + this.transparent = transparent; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/MouseAwareImageFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/MouseAwareImageFigure.java new file mode 100644 index 0000000000..9cb014fcca --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/MouseAwareImageFigure.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.ImageFigure; +import org.eclipse.draw2d.MouseEvent; +import org.eclipse.draw2d.MouseMotionListener; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Image; + +/** + * A figure that shows an image that change on mouseExited and mouseEntered + * events. Note that it is the client's responsibility to dispose the given + * images. The image is resized to fit the client area size. + * + * + * @author ymortier + */ +public class MouseAwareImageFigure extends ImageFigure { + + /** Left Top corner. */ + private static final Point LEFT_TOP_CORNER = new Point(0, 0); + + /** the image that is shown when the mouse is not on the figure. */ + protected Image imageWOFocus; + + /** the image that is shown when the mouse is on the figure. */ + protected Image imageWFocus; + + /** the size. */ + private Dimension size; + + /** + * Create a new {@link MouseAwareImageFigure}. + */ + public MouseAwareImageFigure() { + super(); + init(); + } + + /** + * Create a {@link MouseAwareImageFigure} with the specified image that is + * shwon when the mouse is not on the figure. + * + * @param imageWOFocus + * the image that is shown when the mouse is not on the image. + */ + public MouseAwareImageFigure(final Image imageWOFocus) { + super(imageWOFocus); + this.imageWOFocus = imageWOFocus; + init(); + } + + /** + * Create a {@link MouseAwareImageFigure} with the specified images. + * + * @param imageWOFocus + * the image that is shown when the mouse is not on the image. + * @param imageWFocus + * the image that is shown when the mouse is on the image. + */ + public MouseAwareImageFigure(final Image imageWOFocus, final Image imageWFocus) { + super(imageWOFocus); + this.imageWOFocus = imageWOFocus; + this.imageWFocus = imageWFocus; + init(); + } + + /** + * Initialize the figure. + */ + private void init() { + this.size = new Dimension(); + this.addMouseMotionListener(new ToggleImage()); + } + + /** + * Define the image that is shown when the mouse is on the figure. + * + * @param imageWFocus + * the image that is shown when the mouse is on the figure. + */ + public void setImageWFocus(final Image imageWFocus) { + this.imageWFocus = imageWFocus; + } + + /** + * Return the image that is shown when the mouse is on the figure. + * + * @return the image that is shown when the mouse is on the figure. + */ + public Image getImageWFocus() { + return imageWFocus; + } + + /** + * Define the image that is shown when the mouse is not on the figure. + * + * @param imageWOFocus + * the image that is shown when the mouse is not on the figure. + */ + public void setImageWOFocus(final Image imageWOFocus) { + this.setImage(imageWOFocus); + this.imageWOFocus = imageWOFocus; + } + + /** + * Return the image that is shown when the mouse is not on the figure. + * + * @return the image that is shown when the mouse is not on the figure. + */ + public Image getImageWOFocus() { + return imageWOFocus; + } + + /** + * This class toggle the image that is shown when the mouse entered or + * exited the figure. + * + * @author ymortier + */ + private class ToggleImage implements MouseMotionListener { + + /** + * @see MouseMotionListener#mouseDragged(MouseEvent) + */ + public void mouseDragged(final MouseEvent me) { + // do nothing. + } + + /** + * @see MouseMotionListener#mouseEntered(MouseEvent) + */ + public void mouseEntered(final MouseEvent me) { + if (imageWFocus != null) { + MouseAwareImageFigure.this.setImage(imageWFocus); + } + } + + /** + * @see MouseMotionListener#mouseExited(MouseEvent) + */ + public void mouseExited(final MouseEvent me) { + MouseAwareImageFigure.this.setImage(imageWOFocus); + } + + /** + * @see MouseMotionListener#mouseHover(MouseEvent) + */ + public void mouseHover(final MouseEvent me) { + // do nothing. + } + + /** + * @see MouseMotionListener#mouseMoved(MouseEvent) + */ + public void mouseMoved(final MouseEvent me) { + // do nothing. + } + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.ImageFigure#paintFigure(org.eclipse.draw2d.Graphics) + */ + @Override + protected void paintFigure(final Graphics graphics) { + // super.paintFigure(graphics); + graphics.drawImage(this.getImage(), new Rectangle(LEFT_TOP_CORNER, super.getPreferredSize(0, 0)), new Rectangle(this.getLocation(), this.getSize())); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setSize(int, int) + */ + @Override + public void setSize(final int w, final int h) { + this.size.width = w; + this.size.height = h; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#getBounds() + */ + @Override + public Rectangle getBounds() { + final Rectangle realBounds = new Rectangle(super.getBounds()); + realBounds.setSize(this.size); + return realBounds; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.ImageFigure#getPreferredSize(int, int) + */ + @Override + public Dimension getPreferredSize(final int hint, final int hint2) { + return this.size; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ODesignEllipseFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ODesignEllipseFigure.java new file mode 100644 index 0000000000..443c8ade1c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ODesignEllipseFigure.java @@ -0,0 +1,78 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Rectangle; + +import org.eclipse.sirius.diagram.ui.tools.internal.figure.TransparentFigureGraphicsModifier; + +/** + * Specific Figure to handle ellipse style. + * + * @author mporhel + * + */ +public class ODesignEllipseFigure extends AbstractTransparentEllipse { + + /** + * Outlines the Ellipse. + * + * @Override + * @see org.eclipse.draw2d.Ellipse#outlineShape(org.eclipse.draw2d.Graphics) + * @param graphics + * <code>Graphics</code> object that allows to draw to the + * surface + */ + @Override + protected void outlineShape(final Graphics graphics) { + final Rectangle r = Rectangle.SINGLETON; + r.setBounds(getDrawBounds()); + r.width--; + r.height--; + r.shrink((lineWidth - 1) / 2, (lineWidth - 1) / 2); + graphics.drawOval(r); + } + + /** + * Fills the Ellipse. + * + * @Override + * @see org.eclipse.draw2d.Ellipse#fillShape(org.eclipse.draw2d.Graphics) + * @param graphics + * <code>Graphics</code> object that allows to draw to the + * surface + */ + @Override + protected void fillShape(final Graphics graphics) { + TransparentFigureGraphicsModifier modifier = new TransparentFigureGraphicsModifier(this, graphics); + modifier.pushState(); + graphics.fillOval(getDrawBounds()); + modifier.popState(); + } + + /** + * Computes the rectangle containing the Ellipse. + * + * @Override + * @see org.eclipse.draw2d.Ellipse#fillShape(org.eclipse.draw2d.Graphics) + * @return a rectangle which can contains the ellipse and its border + */ + protected Rectangle getDrawBounds() { + final Rectangle r = new Rectangle(super.getBounds()); + r.x = bounds.x + getLineWidth() / 2; + r.y = bounds.y + getLineWidth() / 2; + r.width = bounds.width - getLineWidth(); + r.height = bounds.height - getLineWidth(); + return r; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/OneLineMarginBorder.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/OneLineMarginBorder.java new file mode 100644 index 0000000000..d1ec721aca --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/OneLineMarginBorder.java @@ -0,0 +1,170 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.gmf.runtime.draw2d.ui.figures.OneLineBorder; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; + +/** + * Specific {@link OneLineBorder} with dash capabilities, two more supported + * positions ({@link PositionConstants#MIDDLE} and + * {@link PositionConstants#CENTER}) and margin capabilities. + * + * @author mporhel + */ +public class OneLineMarginBorder extends OneLineBorder { + + private int[] dash; + + private Insets margin = new Insets(); + + /** + * Constructor. + * + * @param position + * The position to set. + */ + public OneLineMarginBorder(int position) { + super(); + setPosition(position); + } + + /** + * Paints the border based on the inputs given. + * + * @param figure + * <code>IFigure</code> for which this is the border. + * @param graphics + * <code>Graphics</code> handle for drawing the border. + * @param insets + * Space to be taken up by this border. + */ + @Override + public void paint(IFigure figure, Graphics graphics, Insets insets) { + graphics.setLineDash(dash); + + super.paint(figure, graphics, insets); + + int one = MapModeUtil.getMapMode(figure).DPtoLP(1); + int widthInDP = getWidth() / one; + int halfWidthInLP = MapModeUtil.getMapMode(figure).DPtoLP(widthInDP / 2); + + switch (getPosition()) { + case PositionConstants.MIDDLE: + tempRect.y += halfWidthInLP; + tempRect.height -= getWidth(); + graphics.drawLine(tempRect.getTop(), tempRect.getBottom()); + break; + case PositionConstants.CENTER: + tempRect.x += halfWidthInLP; + tempRect.width -= getWidth(); + graphics.drawLine(tempRect.getLeft(), tempRect.getRight()); + break; + default: + break; + } + } + + @Override + public Insets getInsets(IFigure figure) { + Insets borderInsets; + + switch (getPosition()) { + case PositionConstants.TOP: + borderInsets = new Insets(getWidth(), 0, 0, 0); + break; + case PositionConstants.LEFT: + borderInsets = new Insets(0, getWidth(), 0, 0); + break; + case PositionConstants.BOTTOM: + borderInsets = new Insets(0, 0, getWidth(), 0); + break; + case PositionConstants.RIGHT: + borderInsets = new Insets(0, 0, 0, getWidth()); + break; + case PositionConstants.MIDDLE: + borderInsets = new Insets(getWidth() / 2, 0, getWidth() / 2, 0); + break; + case PositionConstants.CENTER: + borderInsets = new Insets(0, getWidth() / 2, 0, getWidth() / 2); + break; + default: + borderInsets = IFigure.NO_INSETS; + break; + } + + Insets globalInsets = new Insets(); + globalInsets.add(borderInsets); + globalInsets.add(margin); + + return globalInsets; + } + + /** + * Sets the dash pattern when the custom line style ( is in use. + * + * @param dashPattern + * the pixel pattern + */ + public void setLineDash(int[] dashPattern) { + int[] copy = null; + if (dashPattern != null) { + copy = new int[dashPattern.length]; + System.arraycopy(dashPattern, 0, copy, 0, dashPattern.length); + } + this.dash = copy; + } + + /** + * Set the extra padding to add to the border width. + * + * @param insets + * The blank padding Insets for the border + */ + public void setMargin(Insets insets) { + if (margin != null) { + margin = insets; + } else { + margin = new Insets(); + } + } + + /** + * Set the extra padding to add to the border width. + * + * @param t + * Top padding + * @param l + * Left padding + * @param b + * Bottom padding + * @param r + * Right padding + */ + public void setMargin(int t, int l, int b, int r) { + setMargin(new Insets(t, l, b, r)); + } + + /** + * Set the extra padding to add to the border width. + * + * @param allsides + * Padding size for all sides of the border. + * @since 2.0 + */ + public void setMargin(int allsides) { + setMargin(new Insets(allsides)); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ParallelogramFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ParallelogramFigure.java new file mode 100644 index 0000000000..7ab17389c1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ParallelogramFigure.java @@ -0,0 +1,59 @@ +/******************************************************************************* + * Copyright (c) 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; + +/** + * A parallelogram figure. + * + * @author cbrun + * + * This Figure represents a Parallelogram Figure + */ +public class ParallelogramFigure extends AbstractGeoShapePolygonFigure { + + /** the offset. */ + protected static final int JITTER = 20; + + /** + * Constructor - Creates a Parallelogram with a given Default size. + */ + public ParallelogramFigure() { + super(10, 10, 2); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.figure.AbstractGeoShapePolygonFigure#calculatePoints(org.eclipse.draw2d.geometry.Rectangle) + */ + @Override + protected PointList calculatePoints(final Rectangle rect) { + + final PointList points = new PointList(); + final Point p1 = new Point(rect.x + JITTER, rect.y); + final Point p2 = new Point(rect.x + rect.width - 1, rect.y); + + final Point p3 = new Point(rect.x + rect.width - JITTER, rect.y + rect.height - 1); + final Point p4 = new Point(rect.x, rect.y + rect.height - 1); + + points.addPoint(p1); + points.addPoint(p2); + points.addPoint(p3); + points.addPoint(p4); + points.addPoint(p1); + + return points; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/PolygoneAndPolylineDecoraction.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/PolygoneAndPolylineDecoraction.java new file mode 100644 index 0000000000..9d5d43e42b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/PolygoneAndPolylineDecoraction.java @@ -0,0 +1,160 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.PolygonDecoration; +import org.eclipse.draw2d.PolylineDecoration; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Color; + +/** + * A rotatable, polygon shaped with arrow decoration. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class PolygoneAndPolylineDecoraction extends PolygonDecoration { + + /** A triangle template. */ + public static final PointList TRIANGLE_TIP = new PointList(); + + static { + TRIANGLE_TIP.addPoint(-3, 1); + TRIANGLE_TIP.addPoint(-2, 0); + TRIANGLE_TIP.addPoint(-3, -1); + } + + PolylineDecoration polylineDecoration = new PolylineDecoration(); + + /** + * Constructs a PolygoneAndPolylineDecoraction. + */ + public PolygoneAndPolylineDecoraction() { + super(); + polylineDecoration.setTemplate(TRIANGLE_TIP); + } + + /** + * Sets the PolylineDecoration's point template. This template is an outline + * of the PolylineDecoration's region. (The default value is TRIANGLE_TIP + * which is a triangle whose tip is pointing to the right). + * + * @param pl + * the template + */ + public void setPolylineTemplate(PointList pl) { + polylineDecoration.setTemplate(pl); + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Polygon#outlineShape(org.eclipse.draw2d.Graphics) + */ + @Override + protected void outlineShape(Graphics g) { + super.outlineShape(g); + g.drawPolyline(polylineDecoration.getPoints()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Polyline#getBounds() + */ + @Override + public Rectangle getBounds() { + if (bounds == null) { + if (polylineDecoration != null) { + bounds = getPoints().getBounds().getUnion(polylineDecoration.getPoints().getBounds()).getExpanded(lineWidth / 2, lineWidth / 2); + } else { + // In case of constructor the first geBounds is called before + // the polylineDecoration creation. + bounds = super.getBounds(); + } + } + return bounds; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.PolygonDecoration#setRotation(double) + */ + @Override + public void setRotation(double angle) { + super.setRotation(angle); + polylineDecoration.setRotation(angle); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.PolygonDecoration#setScale(double, double) + */ + @Override + public void setScale(double x, double y) { + super.setScale(x, y); + if (polylineDecoration != null) { + // The polylineDecoration can be null (at least during the + // constructor) + polylineDecoration.setScale(x, y); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setBackgroundColor(org.eclipse.swt.graphics.Color) + */ + @Override + public void setBackgroundColor(Color bg) { + super.setBackgroundColor(bg); + polylineDecoration.setBackgroundColor(bg); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setForegroundColor(org.eclipse.swt.graphics.Color) + */ + @Override + public void setForegroundColor(Color fg) { + super.setForegroundColor(fg); + polylineDecoration.setForegroundColor(fg); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Polyline#setLineWidth(int) + */ + @Override + public void setLineWidth(int w) { + super.setLineWidth(w); + polylineDecoration.setLineWidth(w); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.PolygonDecoration#setLocation(org.eclipse.draw2d.geometry.Point) + */ + @Override + public void setLocation(Point p) { + super.setLocation(p); + polylineDecoration.setLocation(p); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SVGFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SVGFigure.java new file mode 100644 index 0000000000..846c38e57c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SVGFigure.java @@ -0,0 +1,326 @@ +/** + * Copyright (c) 2008 Borland Software Corporation + * + * 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: + * Dmitry Stadnik - initial API and implementation + * Obeo - Adaptations. + */ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import java.awt.RenderingHints; +import java.awt.geom.Rectangle2D; +import java.awt.image.BufferedImage; +import java.awt.image.WritableRaster; +import java.io.IOException; +import java.util.WeakHashMap; + +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.apache.batik.bridge.BridgeContext; +import org.apache.batik.dom.svg.SAXSVGDocumentFactory; +import org.apache.batik.util.XMLResourceDescriptor; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.widgets.Display; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NodeList; + +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.figure.svg.InferringNamespaceContext; +import org.eclipse.sirius.diagram.tools.internal.figure.svg.SVGUtils; +import org.eclipse.sirius.diagram.tools.internal.figure.svg.SimpleImageTranscoder; + +//CHECKSTYLE:OFF +public class SVGFigure extends Figure { + + private String uri; + + private boolean failedToLoadDocument, specifyCanvasWidth = true, specifyCanvasHeight = true; + + private SimpleImageTranscoder transcoder; + + protected static WeakHashMap<String, Document> documentsMap = new WeakHashMap<String, Document>(); + + public final String getURI() { + return uri; + } + + public final void setURI(String uri) { + setURI(uri, true); + } + + public void setURI(String uri, boolean loadOnDemand) { + this.uri = uri; + transcoder = null; + failedToLoadDocument = false; + if (loadOnDemand) { + loadDocument(); + } + } + + private void loadDocument() { + transcoder = null; + failedToLoadDocument = true; + if (uri == null) { + return; + } + String parser = XMLResourceDescriptor.getXMLParserClassName(); + SAXSVGDocumentFactory factory = new SAXSVGDocumentFactory(parser); + try { + String documentKey = getDocumentKey(); + Document document; + if (documentsMap.containsKey(documentKey)) + document = documentsMap.get(documentKey); + else { + document = factory.createDocument(uri); + documentsMap.put(documentKey, document); + } + transcoder = new SimpleImageTranscoder(document); + failedToLoadDocument = false; + } catch (IOException e) { + SiriusDiagramEditorPlugin.getInstance().logError("Error loading SVG file", e); + } + } + + protected final Document getDocument() { + if (failedToLoadDocument) { + return null; + } + if (transcoder == null) { + loadDocument(); + } + return transcoder == null ? null : transcoder.getDocument(); + } + + /** + * The key used to store the document. + * + * @return the key. + */ + protected String getDocumentKey() { + return uri; + } + + /** + * Returns true if document was loaded without errors; tries to load + * document if needed. + */ + public final boolean checkContentAvailable() { + return getDocument() != null; + } + + private XPath getXPath() { + XPath xpath = XPathFactory.newInstance().newXPath(); + xpath.setNamespaceContext(new InferringNamespaceContext(getDocument().getDocumentElement())); + return xpath; + } + + /** + * Executes XPath query over the SVG document. + */ + protected final NodeList getNodes(String query) { + Document document = getDocument(); + if (document != null) { + try { + return (NodeList) getXPath().evaluate(query, document, XPathConstants.NODESET); + } catch (XPathExpressionException e) { + throw new RuntimeException(e); + } + } + return null; + } + + /** + * Reads color value from the document. + */ + protected Color getColor(Element element, String attributeName) { + if (getDocument() == null || getDocument() != element.getOwnerDocument()) { + return null; + } + Color color = null; + // Make sure that CSSEngine is available. + BridgeContext ctx = transcoder.initCSSEngine(); + try { + color = SVGUtils.toSWTColor(element, attributeName); + } finally { + if (ctx != null) { + ctx.dispose(); + } + } + return color; + } + + @Override + protected void paintFigure(Graphics graphics) { + super.paintFigure(graphics); + Document document = getDocument(); + if (document == null) { + return; + } + Image image = null; + try { + Rectangle r = getClientArea(); + transcoder.setCanvasSize(specifyCanvasWidth ? r.width : -1, specifyCanvasHeight ? r.height : -1); + updateRenderingHints(graphics); + BufferedImage awtImage = transcoder.getBufferedImage(); + if (awtImage != null) { + image = toSWT(Display.getCurrent(), awtImage); + graphics.drawImage(image, r.x, r.y); + } + } finally { + if (image != null) { + image.dispose(); + } + } + } + + protected void updateRenderingHints(Graphics graphics) { + { + int aa = SWT.DEFAULT; + try { + aa = graphics.getAntialias(); + } catch (Exception e) { + // not supported + } + Object aaHint; + if (aa == SWT.ON) { + aaHint = RenderingHints.VALUE_ANTIALIAS_ON; + } else if (aa == SWT.OFF) { + aaHint = RenderingHints.VALUE_ANTIALIAS_OFF; + } else { + aaHint = RenderingHints.VALUE_ANTIALIAS_DEFAULT; + } + if (transcoder.getRenderingHints().get(RenderingHints.KEY_ANTIALIASING) != aaHint) { + transcoder.getRenderingHints().put(RenderingHints.KEY_ANTIALIASING, aaHint); + transcoder.contentChanged(); + } + } + { + int aa = SWT.DEFAULT; + try { + aa = graphics.getTextAntialias(); + } catch (Exception e) { + // not supported + } + Object aaHint; + if (aa == SWT.ON) { + aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_ON; + } else if (aa == SWT.OFF) { + aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_OFF; + } else { + aaHint = RenderingHints.VALUE_TEXT_ANTIALIAS_DEFAULT; + } + if (transcoder.getRenderingHints().get(RenderingHints.KEY_TEXT_ANTIALIASING) != aaHint) { + transcoder.getRenderingHints().put(RenderingHints.KEY_TEXT_ANTIALIASING, aaHint); + transcoder.contentChanged(); + } + } + } + + /** + * Converts an AWT based buffered image into an SWT <code>Image</code>. This + * will always return an <code>Image</code> that has 24 bit depth regardless + * of the type of AWT buffered image that is passed into the method. + * + * @param awtImage + * the {@link java.awt.image.BufferedImage} to be converted to an + * <code>Image</code> + * @return an <code>Image</code> that represents the same image data as the + * AWT <code>BufferedImage</code> type. + */ + protected static org.eclipse.swt.graphics.Image toSWT(Device device, BufferedImage awtImage) { + // We can force bitdepth to be 24 bit because BufferedImage getRGB + // allows us to always retrieve 24 bit data regardless of source color + // depth. + PaletteData palette = new PaletteData(0xFF0000, 0xFF00, 0xFF); + ImageData swtImageData = new ImageData(awtImage.getWidth(), awtImage.getHeight(), 24, palette); + // Ensure scansize is aligned on 32 bit. + int scansize = (((awtImage.getWidth() * 3) + 3) * 4) / 4; + WritableRaster alphaRaster = awtImage.getAlphaRaster(); + byte[] alphaBytes = new byte[awtImage.getWidth()]; + for (int y = 0; y < awtImage.getHeight(); y++) { + int[] buff = awtImage.getRGB(0, y, awtImage.getWidth(), 1, null, 0, scansize); + swtImageData.setPixels(0, y, awtImage.getWidth(), buff, 0); + if (alphaRaster != null) { + int[] alpha = alphaRaster.getPixels(0, y, awtImage.getWidth(), 1, (int[]) null); + for (int i = 0; i < awtImage.getWidth(); i++) { + alphaBytes[i] = (byte) alpha[i]; + } + swtImageData.setAlphas(0, y, awtImage.getWidth(), alphaBytes, 0); + } + } + return new org.eclipse.swt.graphics.Image(device, swtImageData); + } + + public final Rectangle2D getAreaOfInterest() { + getDocument(); + return transcoder == null ? null : transcoder.getCanvasAreaOfInterest(); + } + + public void setAreaOfInterest(Rectangle2D value) { + getDocument(); + if (transcoder != null) { + transcoder.setCanvasAreaOfInterest(value); + } + repaint(); + } + + public final boolean isSpecifyCanvasWidth() { + return specifyCanvasWidth; + } + + public void setSpecifyCanvasWidth(boolean specifyCanvasWidth) { + this.specifyCanvasWidth = specifyCanvasWidth; + contentChanged(); + } + + public final boolean isSpecifyCanvasHeight() { + return specifyCanvasHeight; + } + + public void setSpecifyCanvasHeight(boolean specifyCanvasHeight) { + this.specifyCanvasHeight = specifyCanvasHeight; + contentChanged(); + } + + /** + * Should be called when SVG document has been changed. It will be + * re-rendered and figure will be repainted. + */ + public void contentChanged() { + getDocument(); + if (transcoder != null) { + transcoder.contentChanged(); + } + repaint(); + } + + protected SimpleImageTranscoder getTranscoder() { + return transcoder; + } + + protected boolean getSpecifyCanvasWidth() { + return specifyCanvasWidth; + } + + protected boolean getSpecifyCanvasHeight() { + return specifyCanvasHeight; + } + // CHECKSTYLE:ON +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SVGWorkspaceImageFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SVGWorkspaceImageFigure.java new file mode 100644 index 0000000000..3cf30b9e99 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SVGWorkspaceImageFigure.java @@ -0,0 +1,273 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import java.io.File; + +import org.eclipse.core.resources.ResourcesPlugin; +import org.eclipse.core.runtime.Path; +import org.eclipse.draw2d.XYLayout; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.sirius.common.tools.api.resource.FileProvider; +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.ContainerStyle; +import org.eclipse.sirius.FlatContainerStyle; +import org.eclipse.sirius.WorkspaceImage; + +/** + * The {@link SVGWorkspaceImageFigure} is useful to load svg images using a + * cache. The image can be in the workspace, or if it's not found in the + * workspace it will be looked up in the plug-ins. + * + * @author mporhel + * + */ +public class SVGWorkspaceImageFigure extends AbstractCachedSVGFigure implements IWorkspaceImageFigure { + + private double imageAspectRatio = 1.0; + + private boolean keepAspectRatio = true; + + /** + * Create a new {@link SVGWorkspaceImageFigure}. + */ + public SVGWorkspaceImageFigure() { // final Image flyWeightImage) { + this.setLayoutManager(new XYLayout()); + minSize = new Dimension(0, 0); + } + + /** + * Create the {@link SVGWorkspaceImageFigure} from a {@link WorkspaceImage} + * instance. + * + * @param image + * {@link SVGWorkspaceImageFigure} specification. + * @return new Figure. + */ + public static SVGWorkspaceImageFigure createImageFigure(final WorkspaceImage image) { + SVGWorkspaceImageFigure fig = new SVGWorkspaceImageFigure(); + fig.refreshFigure(image); + return fig; + } + + /** + * Create the {@link SVGWorkspaceImageFigure} from a {@link ContainerStyle} + * instance. + * + * @param containerStyle + * {@link ContainerStyle} specification. + * @return new Figure. + */ + public static SVGWorkspaceImageFigure createImageFigure(final ContainerStyle containerStyle) { + if (containerStyle instanceof FlatContainerStyle) { + FlatContainerStyle style = (FlatContainerStyle) containerStyle; + SVGWorkspaceImageFigure fig = new SVGWorkspaceImageFigure(); + fig.refreshFigure(style); + return fig; + } + // TODO handle other styles.. + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setSize(int, int) + */ + @Override + public void setSize(final int w, final int h) { + if (keepAspectRatio) { + final int newHeight = (int) (w / imageAspectRatio); + super.setSize(w, newHeight); + } else { + super.setSize(w, h); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setMaximumSize(org.eclipse.draw2d.geometry.Dimension) + */ + @Override + public void setMaximumSize(final Dimension d) { + super.setMaximumSize(this.getSize()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setMinimumSize(org.eclipse.draw2d.geometry.Dimension) + */ + @Override + public void setMinimumSize(final Dimension d) { + super.setMinimumSize(this.getSize()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setPreferredSize(org.eclipse.draw2d.geometry.Dimension) + */ + @Override + public void setPreferredSize(final Dimension size) { + super.setPreferredSize(this.getSize()); + } + + /** + * Get the image aspect ratio. + * + * @return the image aspect ratio + */ + public double getImageAspectRatio() { + return imageAspectRatio; + } + + /** + * Refreshes the figure. + * + * @param containerStyle + * the style of the container + */ + public void refreshFigure(final ContainerStyle containerStyle) { + if (containerStyle instanceof FlatContainerStyle) { + final FlatContainerStyle style = (FlatContainerStyle) containerStyle; + boolean updated = this.updateImageURI(style.getBackgroundStyle().getName()); + if (updated) { + this.contentChanged(); + } + } else if (containerStyle instanceof WorkspaceImage) { + refreshFigure((WorkspaceImage) containerStyle); + } else { + this.setURI(null); + } + } + + /** + * refresh the figure. + * + * @param workspaceImage + * the image associated to the figure + */ + public void refreshFigure(final WorkspaceImage workspaceImage) { + if (workspaceImage != null) { + boolean updated = this.updateImageURI(workspaceImage.getWorkspacePath()); + if (updated) { + this.contentChanged(); + int canvasHeight = getTranscoder().getCanvasHeight(); + int canvasWidth = getTranscoder().getCanvasWidth(); + + if (canvasHeight == -1 || canvasWidth == -1) { + int width = getTranscoder().getBufferedImage().getWidth(); + int height = getTranscoder().getBufferedImage().getHeight(); + imageAspectRatio = (double) width / (double) height; + } else { + imageAspectRatio = (double) canvasWidth / (double) canvasHeight; + } + } + } else { + this.setURI(null); + } + } + + private boolean updateImageURI(String workspacePath) { + if (workspacePath != null) { + Option<String> existingImageUri = getImageUri(workspacePath, false); + if (existingImageUri.some()) { + setURI(existingImageUri.get()); + } else { + setURI(getImageNotFoundURI()); + } + return true; + } + return false; + } + + /** + * Return an optional uri as used in the document key to read svg files. + * + * @param workspacePath + * the workspace path of the file. + * @param force + * true to avoid to check that the file exists and is readble. + * @return an optional system uri. + */ + private static Option<String> getImageUri(String workspacePath, boolean force) { + final File imageFile = FileProvider.getDefault().getFile(new Path(workspacePath)); + if (imageFile != null && (force || imageFile.exists() && imageFile.canRead())) { + return Options.newSome(imageFile.toURI().toString()); + } + Option<String> nonExistingFile = Options.newNone(); + if (force) { + // Deleted file : retrieve the key. + nonExistingFile = Options.newSome(ResourcesPlugin.getWorkspace().getRoot().getLocationURI().toString() + workspacePath); + } + + return nonExistingFile; + } + + /** + * Compute a key for this {@link SVGWorkspaceImageFigure}. This key is used + * to store in cache the corresponding + * {@link org.eclipse.swt.graphics.Image}. + * + * {@inheritDoc} + * + * @return The key corresponding to this SVGWorkspaceImageFigure. + */ + protected String getKey() { + StringBuffer result = new StringBuffer(); + result.append(getDocumentKey()); + result.append(SEPARATOR); + result.append(getSiriusAlpha()); + result.append(SEPARATOR); + return result.toString(); + } + + /** + * Get an {@link Image} instance. The image will be stored in a cache. + * + * @param path + * the path is a "/project/file" path, if it's not found in the + * workspace, the class will look for the file in the plug-ins. + * @return an image instance given the path. + */ + public static Image flyWeightImage(String path) { + SVGWorkspaceImageFigure fig = new SVGWorkspaceImageFigure(); + fig.updateImageURI(path); + fig.contentChanged(); + return fig.getCachedImage(fig.getKey(), new Rectangle(0, 0, -1, -1), null); + } + + /** + * Remove all entries whose key begins with the given key. Remove from the + * document map, the entries with the given keys to force to re-read the + * file. + * + * @param workspacePath + * the modified or deleted image file path. + * @return an option with the document uri used as key for the svg file if a + * corresponding element was removed. + */ + public static Option<String> removeFromCache(String workspacePath) { + Option<String> imageUri = getImageUri(workspacePath, true); + if (imageUri.some()) { + if (doRemoveFromCache(imageUri.get())) { + return imageUri; + } + } + return Options.newNone(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SiriusWrapLabel.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SiriusWrapLabel.java new file mode 100644 index 0000000000..fe3bcc44dc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/SiriusWrapLabel.java @@ -0,0 +1,1774 @@ +/****************************************************************************** + * Copyright (c) 2002, 2007 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Obeo - Duplication to keep the same behavior as GMF 2.0.1 + ****************************************************************************/ +//CHECKSTYLE:OFF +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.FigureUtilities; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.draw2d.ui.internal.mapmode.IMapModeHolder; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.IMapMode; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontMetrics; +import org.eclipse.swt.graphics.Image; + +import com.ibm.icu.text.BreakIterator; +import com.ibm.icu.util.StringTokenizer; + +/** + * An extended label that has the following extra features: + * + * 1- It is capable of showing selection and focus feedback (primary or + * secondary) 2- It is capable of optionally underlining the label's text 3- It + * is capable of wrapping the label's text at a given width with a given + * alignment 4- It is capable of supporting multiple label icons (temporary + * feature) + * + * This class was originally deriving off Draw2d's <code>Label</code> class but + * with the introduction of the auto-wrapping feature, a copy had to be made + * overriding was not straightforward. Hopefully, this extended version can be + * pushed to opensource + * + * <p> + * Code taken from Eclipse reference bugzilla #98820 + * + * <p> + * Since GMF 2.1, there is a lot of refactoring and regresion (See + * https://bugs.eclipse.org/bugs/show_bug.cgi?id=274859). So to keep + * compatibility we duplicate the class + * org.eclipse.gmf.runtime.draw2d.ui.figures.WrapLabel into SiriusWrapLabel + * to keep the same behavior as GMF 2.0.1 There is only a little change in + * paintText figure (see javadoc for more details). + * + * @author melaasar + */ +@SuppressWarnings({ "rawtypes", "unchecked", "restriction" }) +public class SiriusWrapLabel extends Figure implements PositionConstants { + + private static final String _ellipse = "..."; //$NON-NLS-1$ + + private static final Dimension EMPTY_DIMENSION = new Dimension(0, 0); + + private static final Map mapModeConstantsMap = new WeakHashMap(); + + private static class MapModeConstants { + + private static final int MAX_IMAGE_INFO = 12; + + public final WeakReference mapModeRef; + + public final int nDPtoLP_3; + + public final int nDPtoLP_2; + + public final int nDPtoLP_0; + + public final Dimension dimension_nDPtoLP_0; + + public final WeakHashMap fontToEllipseTextSize = new WeakHashMap(); + + public final SingleIconInfo[] singleIconInfos = new SingleIconInfo[MAX_IMAGE_INFO]; + + public MapModeConstants(IMapMode mapMode) { + this.mapModeRef = new WeakReference(mapMode); + nDPtoLP_2 = mapMode.DPtoLP(2); + nDPtoLP_3 = mapMode.DPtoLP(3); + nDPtoLP_0 = mapMode.DPtoLP(0); + dimension_nDPtoLP_0 = new Dimension(nDPtoLP_0, nDPtoLP_0); + } + + public Dimension getEllipseTextSize(Font f) { + Dimension d = (Dimension) fontToEllipseTextSize.get(f); + if (d == null) { + IMapMode mapMode = (IMapMode) mapModeRef.get(); + d = FigureUtilities.getTextExtents(_ellipse, f); + d.height = FigureUtilities.getFontMetrics(f).getHeight(); + d = new Dimension(mapMode.DPtoLP(d.width), mapMode.DPtoLP(d.height)); + fontToEllipseTextSize.put(f, d); + } + return d; + } + + public SingleIconInfo getSingleIconInfo(Image image) { + if (image == null) { + return SingleIconInfo.NULL_INFO; + } + SingleIconInfo info; + for (int i = 0; i < MAX_IMAGE_INFO; ++i) { + info = singleIconInfos[i]; + if (info == null) { + info = new SingleIconInfo(image); + singleIconInfos[i] = info; + return info; + } + if (info.icon == image) { + return info; + } + } + int index = SingleIconInfo.count % MAX_IMAGE_INFO; + info = new SingleIconInfo(image); + singleIconInfos[index] = info; + return info; + } + } + + // reserve 1 bit + private static int FLAG_SELECTED = MAX_FLAG << 1; + + private static int FLAG_HASFOCUS = MAX_FLAG << 2; + + private static int FLAG_UNDERLINED = MAX_FLAG << 3; + + private static int FLAG_STRIKEDTHROUGH = MAX_FLAG << 4; + + private static int FLAG_WRAP = MAX_FLAG << 5; + + // reserve 3 bits + private static int FLAG_TEXT_ALIGN = MAX_FLAG << 6; + + private static int FLAG_WRAP_ALIGN = MAX_FLAG << 9; + + private static int FLAG_ICON_ALIGN = MAX_FLAG << 12; + + private static int FLAG_LABEL_ALIGN = MAX_FLAG << 15; + + private static int FLAG_TEXT_PLACEMENT = MAX_FLAG << 18; + + private MapModeConstants mapModeConstants; + + /** the original label's text */ + private String text; + + /** the label's text used in painting after applying required styles */ + private String subStringText; + + /** the size of text */ + private Dimension textSize; + + private Dimension ellipseTextSize; + + /** the location of text */ + private Point textLocation; + + /** the cached hint used to calculate text size */ + private int cachedPrefSizeHint_width; + + private int cachedPrefSizeHint_height; + + /** the icon location */ + private Point iconLocation; + + private static abstract class IconInfo { + /** + * Gets the icon at the index location. + * + * @param i + * the index to retrieve the icon of + * @return <code>Image</code> that corresponds to the given index. + */ + public abstract Image getIcon(int i); + + /** + * Gets the icon size of the icon at the given index. + * + * @param i + * @return the <code>Dimension</code> that is the size of the icon at + * the given index. + */ + public abstract Dimension getIconSize(IMapMode mapMode, int i); + + /** + * @return the number of icons + */ + public abstract int getNumberofIcons(); + + /** + * @return the <code>Dimension</code> that is the total size of all the + * icons. + */ + public abstract Dimension getTotalIconSize(IMapMode mapMode); + + public abstract void invalidate(); + + /** + * Sets the icon at the index location. + * + * @param icon + * @param i + */ + public abstract void setIcon(Image icon, int i); + + /** + * + */ + public abstract int getMaxIcons(); + + } + + private static class SingleIconInfo extends IconInfo { + + static int count; + + public static final SingleIconInfo NULL_INFO = new SingleIconInfo() { + public int getNumberofIcons() { + return 0; + } + }; + + final Image icon; + + /** total icon size */ + private Dimension totalIconSize; + + private SingleIconInfo() { + icon = null;// don't increment count, used only for NULL_INFO + } + + public SingleIconInfo(Image icon) { + this.icon = icon; + ++count; + } + + public final int getMaxIcons() { + return 1; + } + + public Image getIcon(int i) { + if (i == 0) { + return icon; + } else if (i > 0) { + return null; + } + throw new IndexOutOfBoundsException(); + } + + public void setIcon(Image img, int i) { + throw new UnsupportedOperationException(); + } + + public Dimension getIconSize(IMapMode mapMode, int i) { + if (i == 0) { + return getTotalIconSize(mapMode); + } + + throw new IndexOutOfBoundsException(); + } + + public int getNumberofIcons() { + return 1; + } + + public Dimension getTotalIconSize(IMapMode mapMode) { + if (totalIconSize != null) + return totalIconSize; + + if (icon != null && !icon.isDisposed()) { + org.eclipse.swt.graphics.Rectangle imgBounds = icon.getBounds(); + totalIconSize = new Dimension(mapMode.DPtoLP(imgBounds.width), mapMode.DPtoLP(imgBounds.height)); + } else { + totalIconSize = EMPTY_DIMENSION; + } + + return totalIconSize; + } + + public void invalidate() { + totalIconSize = null; + } + + } + + private static class MultiIconInfo extends IconInfo { + + /** the label icons */ + private ArrayList icons = new ArrayList(2); + + /** total icon size */ + private Dimension totalIconSize; + + public MultiIconInfo() { + super(); + } + + public int getMaxIcons() { + return -1; + } + + /** + * Gets the icon at the index location. + * + * @param i + * the index to retrieve the icon of + * @return <code>Image</code> that corresponds to the given index. + */ + public Image getIcon(int i) { + if (i >= icons.size()) + return null; + + return (Image) icons.get(i); + } + + /** + * Sets the icon at the index location. + * + * @param icon + * @param i + */ + public void setIcon(Image icon, int i) { + int size = icons.size(); + if (i >= size) { + for (int j = size; j < i; j++) + icons.add(null); + icons.add(icon); + icons.trimToSize(); + } else + icons.set(i, icon); + } + + /** + * Gets the icon size of the icon at the given index. + * + * @param i + * @return the <code>Dimension</code> that is the size of the icon at + * the given index. + */ + public Dimension getIconSize(IMapMode mapMode, int i) { + Image img = getIcon(i); + if (img != null && !img.isDisposed()) { + org.eclipse.swt.graphics.Rectangle imgBounds = img.getBounds(); + return new Dimension(mapMode.DPtoLP(imgBounds.width), mapMode.DPtoLP(imgBounds.height)); + } + return EMPTY_DIMENSION; + } + + /** + * @return the number of icons + */ + public int getNumberofIcons() { + return icons.size(); + } + + /** + * @return the <code>Dimension</code> that is the total size of all the + * icons. + */ + public Dimension getTotalIconSize(IMapMode mapMode) { + if (totalIconSize != null) + return totalIconSize; + int iconNum = getNumberofIcons(); + if (iconNum == 0) { + return totalIconSize = EMPTY_DIMENSION; + } + + totalIconSize = new Dimension(); + for (int i = 0; i < iconNum; i++) { + Dimension iconSize = getIconSize(mapMode, i); + totalIconSize.width += iconSize.width; + if (iconSize.height > totalIconSize.height) + totalIconSize.height = iconSize.height; + } + + return totalIconSize; + } + + /** + * + */ + public void invalidate() { + totalIconSize = null; + } + } + + private IconInfo iconInfo; + + /** the cached hint used to calculate text size */ + private int cachedTextSizeHint_width; + + private int cachedTextSizeHint_height; + + /** + * Construct an empty Label. + * + * @since 2.0 + */ + public SiriusWrapLabel() { + text = "";//$NON-NLS-1$ + // set defaults + setAlignmentFlags(CENTER, FLAG_TEXT_ALIGN); + setAlignmentFlags(CENTER, FLAG_ICON_ALIGN); + setAlignmentFlags(CENTER, FLAG_LABEL_ALIGN); + setAlignmentFlags(LEFT, FLAG_WRAP_ALIGN); + setPlacementFlags(EAST, FLAG_TEXT_PLACEMENT); + } + + /** + * Construct a Label with passed String as its text. + * + * @param s + * the label text + * @since 2.0 + */ + public SiriusWrapLabel(String s) { + if (s != null) { + text = s; + } else { + text = "";//$NON-NLS-1$ + } + // setBorder(new LineBorderEx(ColorConstants.red,3)); + } + + /** + * Construct a Label with passed Image as its icon. + * + * @param i + * the label image + * @since 2.0 + */ + public SiriusWrapLabel(Image i) { + text = "";//$NON-NLS-1$ + iconInfo = new SingleIconInfo(i); + } + + /** + * Construct a Label with passed String as text and passed Image as its + * icon. + * + * @param s + * the label text + * @param i + * the label image + * @since 2.0 + */ + public SiriusWrapLabel(String s, Image i) { + if (s != null) { + text = s; + } else { + text = "";//$NON-NLS-1$ + } + iconInfo = new SingleIconInfo(i); + } + + /** + * @return <code>IMapMode</code> used by this figure. <code>IMapMode</code> + * that allows for the coordinate mapping from device to logical + * units. + */ + private IMapMode getFigureMapMode() { + return (IMapMode) getMapModeConstants().mapModeRef.get(); + } + + private MapModeConstants getMapModeConstants() { + if (mapModeConstants == null) { + IMapMode mapMode = MapModeUtil.getMapMode(this); + while (mapMode instanceof IMapModeHolder) { + mapMode = ((IMapModeHolder) mapMode).getMapMode(); + } + mapModeConstants = (MapModeConstants) mapModeConstantsMap.get(mapMode); + if (mapModeConstants == null) { + mapModeConstants = new MapModeConstants(mapMode); + mapModeConstantsMap.put(mapMode, mapModeConstants); + } + } + return mapModeConstants; + } + + private void alignOnHeight(Point loc, Dimension size, int alignment) { + switch (alignment) { + case TOP: + loc.y = getInsets().top; + break; + case BOTTOM: + loc.y = bounds.height - size.height - getInsets().bottom; + break; + default: + loc.y = (bounds.height - size.height) / 2; + } + } + + private void alignOnWidth(Point loc, Dimension size, int alignment) { + switch (alignment) { + case LEFT: + loc.x = getInsets().left; + break; + case RIGHT: + loc.x = bounds.width - size.width - getInsets().right; + break; + default: + loc.x = (bounds.width - size.width) / 2; + } + } + + private void calculateAlignment(Dimension iconSize, int textPlacement) { + switch (textPlacement) { + case EAST: + case WEST: + alignOnHeight(textLocation, getTextSize(), getTextAlignment()); + alignOnHeight(getIconLocation(), iconSize, getIconAlignment()); + break; + case NORTH: + case SOUTH: + alignOnWidth(textLocation, getSubStringTextSize(), getTextAlignment()); + alignOnWidth(getIconLocation(), iconSize, getIconAlignment()); + break; + default: + break; + } + } + + /** + * Calculates the size of the Label using the passed Dimension as the size + * of the Label's text. + * + * @param txtSize + * the precalculated size of the label's text + * @return the label's size + * @since 2.0 + */ + protected Dimension calculateLabelSize(Dimension txtSize) { + Dimension iconSize = getTotalIconSize(); + boolean isEmpty = (iconSize.width == 0 && iconSize.height == 0); + int len = getText().length(); + if (len == 0 && isEmpty) { + return new Dimension(txtSize.width, txtSize.height); + } + int gap = (len == 0 || isEmpty) ? 0 : getIconTextGap(); + int placement = getTextPlacement(); + if (placement == WEST || placement == EAST) { + return new Dimension(iconSize.width + gap + txtSize.width, Math.max(iconSize.height, txtSize.height)); + } else { + return new Dimension(Math.max(iconSize.width, txtSize.width), iconSize.height + gap + txtSize.height); + } + } + + private void calculateLocations() { + textLocation = new Point(); + iconLocation = new Point(); + Dimension iconSize = getTotalIconSize(); + int textPlacement = getTextPlacement(); + calculatePlacement(iconSize, textPlacement); + calculateAlignment(iconSize, textPlacement); + Rectangle r = getBounds(); + Dimension ps = getPreferredSize(r.width, r.height); + int w = (r.width - ps.width) + (getTextSize().width - getSubStringTextSize().width); + int h = r.height - ps.height; + if (w == 0 && h == 0) { + return; + } + + Dimension offset = new Dimension(w, h); + switch (getLabelAlignment()) { + case LEFT: + offset.scale(0.0f); + break; + case RIGHT: + offset.scale(1.0f); + break; + case TOP: + offset.height = 0; + offset.scale(0.5f); + break; + case BOTTOM: + offset.height = offset.height * 2; + offset.scale(0.5f); + break; + case CENTER: + default: + offset.scale(0.5f); + break; + } + + switch (textPlacement) { + case EAST: + case WEST: + offset.height = 0; + break; + case NORTH: + case SOUTH: + offset.width = 0; + break; + default: + break; + } + + textLocation.translate(offset); + iconLocation.translate(offset); + } + + private void calculatePlacement(Dimension iconSize, int textPlacement) { + int gap = (getText().length() == 0 || (iconSize.width == 0 && iconSize.height == 0)) ? 0 : getIconTextGap(); + Insets insets = getInsets(); + switch (textPlacement) { + case EAST: + iconLocation.x = insets.left; + textLocation.x = iconSize.width + gap + insets.left; + break; + case WEST: + textLocation.x = insets.left; + iconLocation.x = getSubStringTextSize().width + gap + insets.left; + break; + case NORTH: + textLocation.y = insets.top; + iconLocation.y = getTextSize().height + gap + insets.top; + break; + case SOUTH: + textLocation.y = iconSize.height + gap + insets.top; + iconLocation.y = insets.top; + default: + break; + } + } + + /** + * Calculates the size of the Label's text size. The text size calculated + * takes into consideration if the Label's text is currently truncated. If + * text size without considering current truncation is desired, use + * {@link #calculateTextSize(int, int)}. + * + * @return the size of the label's text, taking into account truncation + * @since 2.0 + */ + protected Dimension calculateSubStringTextSize() { + Font f = getFont(); + return getTextExtents(getSubStringText(), f, getFigureMapMode().DPtoLP(FigureUtilities.getFontMetrics(f).getHeight())); + } + + /** + * Calculates and returns the size of the Label's text. Note that this + * Dimension is calculated using the Label's full text, regardless of + * whether or not its text is currently truncated. If text size considering + * current truncation is desired, use {@link #calculateSubStringTextSize()}. + * + * @param wHint + * a width hint + * @param hHint + * a height hint + * @return the size of the label's text, ignoring truncation + * @since 2.0 + */ + protected Dimension calculateTextSize(int wHint, int hHint) { + Font f = getFont(); + return getTextExtents(getWrappedText(wHint, hHint), f, getFigureMapMode().DPtoLP(FigureUtilities.getFontMetrics(f).getHeight())); + } + + private void clearLocations() { + iconLocation = textLocation = null; + } + + /** + * Returns the Label's icon. + * + * @return the label icon + * @since 2.0 + */ + public Image getIcon() { + return getIcon(0); + } + + /** + * Gets the label's icon at the given index + * + * @param index + * The icon index + * @return the <code>Image</code> that is the icon for the given index. + */ + public Image getIcon(int index) { + if (iconInfo == null) + return null; + return iconInfo.getIcon(index); + } + + /** + * Determines if there is any icons by checking if icon size is zeros. + * + * @return true if icons are present, false otherwise + */ + protected boolean hasIcons() { + return (getNumberofIcons() > 0); + } + + /** + * Returns the current alignment of the Label's icon. The default is + * {@link PositionConstants#CENTER}. + * + * @return the icon alignment + * @since 2.0 + */ + public int getIconAlignment() { + return getAlignment(FLAG_ICON_ALIGN); + } + + /** + * Returns the bounds of the Label's icon. + * + * @return the icon's bounds + * @since 2.0 + */ + public Rectangle getIconBounds() { + return new Rectangle(getBounds().getLocation().translate(getIconLocation()), getTotalIconSize()); + } + + /** + * Returns the location of the Label's icon relative to the Label. + * + * @return the icon's location + * @since 2.0 + */ + protected Point getIconLocation() { + if (iconLocation == null) + calculateLocations(); + return iconLocation; + } + + /** + * Returns the gap in pixels between the Label's icon and its text. + * + * @return the gap + * @since 2.0 + */ + public int getIconTextGap() { + return getMapModeConstants().nDPtoLP_3; + } + + /** + * @see IFigure#getMinimumSize(int, int) + */ + public Dimension getMinimumSize(int w, int h) { + if (minSize != null) + return minSize; + minSize = new Dimension(); + LayoutManager layoutManager = getLayoutManager(); + if (layoutManager != null) + minSize.setSize(layoutManager.getMinimumSize(this, w, h)); + Font f = getFont(); + Dimension d = getEllipseTextSize().getIntersected(getTextExtents(getText(), f, getFigureMapMode().DPtoLP(FigureUtilities.getFontMetrics(f).getHeight()))); + + Dimension labelSize = calculateLabelSize(d); + Insets insets = getInsets(); + labelSize.expand(insets.getWidth(), insets.getHeight()); + minSize.union(labelSize); + return minSize; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.IFigure#getPreferredSize(int, int) + */ + public Dimension getPreferredSize(int wHint, int hHint) { + if (prefSize == null || wHint != cachedPrefSizeHint_width || hHint != cachedPrefSizeHint_height) { + prefSize = calculateLabelSize(getTextSize(wHint, hHint)); + Insets insets = getInsets(); + prefSize.expand(insets.getWidth(), insets.getHeight()); + LayoutManager layoutManager = getLayoutManager(); + if (layoutManager != null) { + prefSize.union(layoutManager.getPreferredSize(this, wHint, hHint)); + } + prefSize.union(getMinimumSize(wHint, hHint)); + cachedPrefSizeHint_width = wHint; + cachedPrefSizeHint_height = hHint; + } + return prefSize; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.draw2d.IFigure#getMaximumSize() + */ + public Dimension getMaximumSize() { + // this assumes that getPreferredSize(wHint, hHint) is called before + return prefSize; + } + + /** + * Calculates the amount of the Label's current text will fit in the Label, + * including an elipsis "..." if truncation is required. + * + * @return the substring + * @since 2.0 + */ + public String getSubStringText() { + if (subStringText != null) + return subStringText; + + String theText = getText(); + int textLen = theText.length(); + if (textLen == 0) { + return subStringText = "";//$NON-NLS-1$;; + } + Dimension size = getSize(); + Dimension shrink = getPreferredSize(size.width, size.height).getDifference(size); + Dimension effectiveSize = getTextSize().getExpanded(-shrink.width, -shrink.height); + + if (effectiveSize.height == 0) { + return subStringText = "";//$NON-NLS-1$; + } + + Font f = getFont(); + FontMetrics metrics = FigureUtilities.getFontMetrics(f); + IMapMode mm = getFigureMapMode(); + int fontHeight = mm.DPtoLP(metrics.getHeight()); + int charAverageWidth = mm.DPtoLP(metrics.getAverageCharWidth()); + int maxLines = (int) (effectiveSize.height / (double) fontHeight); + if (maxLines == 0) { + return subStringText = "";//$NON-NLS-1$ + } + + StringBuffer accumlatedText = new StringBuffer(); + StringBuffer remainingText = new StringBuffer(theText); + + int effectiveSizeWidth = effectiveSize.width; + int widthHint = Math.max(effectiveSizeWidth - getEllipseTextSize().width, 0); + int i = 0, j = 0; + while (remainingText.length() > 0 && j++ < maxLines) { + i = getLineWrapPosition(remainingText.toString(), f, effectiveSizeWidth, fontHeight); + + if (accumlatedText.length() > 0) + accumlatedText.append('\n'); + + if (i == 0 || (remainingText.length() > i && j == maxLines)) { + i = getLargestSubstringConfinedTo(remainingText.toString(), f, widthHint, fontHeight, charAverageWidth); + accumlatedText.append(remainingText.substring(0, i)); + accumlatedText.append(getEllipse()); + } else + accumlatedText.append(remainingText.substring(0, i)); + remainingText.delete(0, i); + } + return subStringText = accumlatedText.toString(); + } + + /** + * Creates an equivalent text to that of the label's but with "\n"(s) + * inserted at the wrapping positions. This method assumes unlimited + * bounding box and is used by <code>calculateTextSize()</code> to calculate + * the perfect size of the text with wrapping + * + * @return the wrapped text + */ + private String getWrappedText(int wHint, int hHint) { + String theText = getText(); + if (wHint == -1 || theText.length() == 0 || !isTextWrapped()) + return theText; + + Dimension iconSize = getTotalIconSize(); + if (!(iconSize.width == 0 && iconSize.height == 0)) { + switch (getTextPlacement()) { + case EAST: + case WEST: + wHint -= iconSize.width + getIconTextGap(); + break; + case NORTH: + case SOUTH: + if (hHint != -1) + hHint -= iconSize.height + getIconTextGap(); + break; + default: + break; + } + } + + if ((hHint == 0) || (wHint == 0)) { + return "";//$NON-NLS-1$; + } + + Font f = getFont(); + int fontHeight = getFigureMapMode().DPtoLP(FigureUtilities.getFontMetrics(f).getHeight()); + int maxLines = Integer.MAX_VALUE; + if (hHint != -1) { + maxLines = (int) (hHint / (double) fontHeight); + if (maxLines == 0) { + return "";//$NON-NLS-1$;; + } + } + + StringBuffer accumlatedText = new StringBuffer(); + StringBuffer remainingText = new StringBuffer(theText); + int i = 0, j = 0; + + while (remainingText.length() > 0 && j++ < maxLines) { + if ((i = getLineWrapPosition(remainingText.toString(), f, wHint, fontHeight)) == 0) + break; + + if (accumlatedText.length() > 0) + accumlatedText.append('\n'); + accumlatedText.append(remainingText.substring(0, i)); + remainingText.delete(0, i); + } + return accumlatedText.toString(); + } + + /** + * Returns the size of the Label's current text. If the text is currently + * truncated, the truncated text with its ellipsis is used to calculate the + * size. + * + * @return the size of this label's text, taking into account truncation + * @since 2.0 + */ + protected Dimension getSubStringTextSize() { + return calculateSubStringTextSize(); + } + + /** + * Returns the size of the String constant "..." the ellipse based on the + * currently used Map mode size. + * + * @return the size of ellipse text + * + */ + private Dimension getEllipseTextSize() { + if (ellipseTextSize == null) { + ellipseTextSize = getMapModeConstants().getEllipseTextSize(getFont()); + } + return ellipseTextSize; + } + + /** + * Returns the text of the label. Note that this is the complete text of the + * label, regardless of whether it is currently being truncated. Call + * {@link #getSubStringText()}to return the label's current text contents + * with truncation considered. + * + * @return the complete text of this label + * @since 2.0 + */ + public String getText() { + return text; + } + + /** + * Returns the current alignment of the Label's text. The default text + * alignment is {@link PositionConstants#CENTER}. + * + * @return the text alignment + */ + public int getTextAlignment() { + return getAlignment(FLAG_TEXT_ALIGN); + } + + /** + * Returns the current alignment of the entire Label. The default label + * alignment is {@link PositionConstants#LEFT}. + * + * @return the label alignment + */ + private int getLabelAlignment() { + return getAlignment(FLAG_LABEL_ALIGN); + } + + /** + * Returns the bounds of the label's text. Note that the bounds are + * calculated using the label's complete text regardless of whether the + * label's text is currently truncated. + * + * @return the bounds of this label's complete text + * @since 2.0 + */ + public Rectangle getTextBounds() { + return new Rectangle(getBounds().getLocation().translate(getTextLocation()), getTextSize()); + } + + /** + * Returns the location of the label's text relative to the label. + * + * @return the text location + * @since 2.0 + */ + protected Point getTextLocation() { + if (textLocation != null) + return textLocation; + calculateLocations(); + return textLocation; + } + + /** + * Returns the current placement of the label's text relative to its icon. + * The default text placement is {@link PositionConstants#EAST}. + * + * @return the text placement + * @since 2.0 + */ + public int getTextPlacement() { + return getPlacement(FLAG_TEXT_PLACEMENT); + } + + /** + * Returns the size of the label's complete text. Note that the text used to + * make this calculation is the label's full text, regardless of whether the + * label's text is currently being truncated and is displaying an ellipsis. + * If the size considering current truncation is desired, call + * {@link #getSubStringTextSize()}. + * + * @param wHint + * a width hint + * @param hHint + * a height hint + * @return the size of this label's complete text + * @since 2.0 + */ + protected Dimension getTextSize(int wHint, int hHint) { + if (textSize == null || wHint != cachedTextSizeHint_width || hHint != cachedTextSizeHint_height) { + textSize = calculateTextSize(wHint, hHint); + cachedTextSizeHint_width = wHint; + cachedTextSizeHint_height = hHint; + } + return textSize; + } + + /** + * Gets the text size given the current size as a width hint + */ + private final Dimension getTextSize() { + Rectangle r = getBounds(); + return getTextSize(r.width, r.height); + } + + /** + * @see IFigure#invalidate() + */ + public void invalidate() { + prefSize = null; + minSize = null; + clearLocations(); + ellipseTextSize = null; + textSize = null; + subStringText = null; + if (iconInfo != null) + iconInfo.invalidate(); + super.invalidate(); + } + + /** + * Returns <code>true</code> if the label's text is currently truncated and + * is displaying an ellipsis, <code>false</code> otherwise. + * + * @return <code>true</code> if the label's text is truncated + * @since 2.0 + */ + public boolean isTextTruncated() { + return !getSubStringTextSize().equals(getTextSize()); + } + + /** + * @see org.eclipse.draw2d.Figure#paintFigure(org.eclipse.draw2d.Graphics) + */ + public void paintFigure(Graphics graphics) { + if (isSelected()) { + graphics.pushState(); + graphics.setBackgroundColor(ColorConstants.menuBackgroundSelected); + graphics.fillRectangle(getSelectionRectangle()); + graphics.popState(); + graphics.setForegroundColor(ColorConstants.white); + } + if (hasFocus()) { + graphics.pushState(); + graphics.setXORMode(true); + graphics.setForegroundColor(ColorConstants.menuBackgroundSelected); + graphics.setBackgroundColor(ColorConstants.white); + graphics.drawFocus(getSelectionRectangle().resize(-1, -1)); + graphics.popState(); + } + if (isOpaque()) + super.paintFigure(graphics); + Rectangle figBounds = getBounds(); + + graphics.translate(figBounds.x, figBounds.y); + if (hasIcons()) + paintIcons(graphics); + + String subString = getSubStringText(); + if (subString.length() > 0) { + if (!isEnabled()) { + graphics.translate(1, 1); + graphics.setForegroundColor(ColorConstants.buttonLightest); + paintText(graphics, subString); + graphics.translate(-1, -1); + graphics.setForegroundColor(ColorConstants.buttonDarker); + } else { + paintText(graphics, subString); + } + } + graphics.translate(-figBounds.x, -figBounds.y); + } + + /** + * Paints the text and optionally underlines it. <BR> + * The offset added to avoid truncating at the top make a truncating at the + * bottom so to avoid the bottom truncating problem we remove this fix. + * + * @param graphics + * The graphics context + * @param subString + * The string to draw + */ + private void paintText(Graphics graphics, String subString) { + StringTokenizer tokenizer = new StringTokenizer(subString, "\n"); //$NON-NLS-1$ + Font f = getFont(); + FontMetrics fontMetrics = FigureUtilities.getFontMetrics(f); + int fontHeight = getFigureMapMode().DPtoLP(fontMetrics.getHeight()); + int fontHeightHalf = fontHeight / 2; + int textWidth = getTextExtents(subString, f, fontHeight).width; + Point p = getTextLocation(); + int y = p.y; + int x = p.x; + final int wrapAlignment = getTextWrapAlignment(); + boolean isUnderlined = isTextUnderlined(); + boolean isStrikedThrough = isTextStrikedThrough(); + Rectangle clipRect = new Rectangle(); + graphics.getClip(clipRect); + int clipRectTopRight_x = clipRect.getTopRight().x; + // If the font's leading area is 0 then we need to add an offset to + // avoid truncating at the top (e.g. Korean fonts) + // if (0 == fontMetrics.getLeading()) { + // y += getMapModeConstants().nDPtoLP_2; // 2 is the leading area for + // default English + // } + + while (tokenizer.hasMoreTokens()) { + x = p.x; + String token = tokenizer.nextToken(); + int tokenWidth = getTextExtents(token, f, fontHeight).width; + + switch (wrapAlignment) { + case CENTER: + x += (textWidth - tokenWidth) / 2; + break; + case RIGHT: + x += textWidth - tokenWidth; + break; + default: + break; + } + + // increase the clipping rectangle by a small amount to account for + // font overhang + // from italic / irregular characters etc. + + if (tokenWidth + x <= clipRectTopRight_x) { + Rectangle newClipRect = new Rectangle(clipRect); + newClipRect.width += (tokenWidth / token.length()) / 2; + graphics.setClip(newClipRect); + } + + graphics.drawText(token, x, y); + graphics.setClip(clipRect); + + y += fontHeight; + + if (isUnderlined) + graphics.drawLine(x, y - 1, x + tokenWidth, y - 1); + if (isStrikedThrough) + graphics.drawLine(x, y - fontHeightHalf + 1, x + tokenWidth, y - fontHeightHalf + 1); + } + } + + /** + * Paints the icon(s) + * + * @param graphics + * The graphics context + */ + private void paintIcons(Graphics graphics) { + Point p = Point.SINGLETON; + p.setLocation(getIconLocation()); + + int num = getNumberofIcons(); + for (int i = 0; i < num; i++) { + Image icon = getIcon(i); + if (icon != null) { + graphics.drawImage(icon, p); + p.x += getIconSize(i).width; + } + } + } + + /** + * Sets the label's icon to the passed image. + * + * @param image + * the new label image + * @since 2.0 + */ + public void setIcon(Image image) { + setIcon(image, 0); + } + + /** + * Sets the label's icon at given index + * + * @param image + * The icon image or null to remove the icon + * @param index + * The icon index + */ + public void setIcon(Image image, int index) { + if (iconInfo == null) { + if (index == 0) { + iconInfo = getMapModeConstants().getSingleIconInfo(image); + } else { + iconInfo = new MultiIconInfo(); + iconInfo.setIcon(image, index); + } + revalidate(); + repaint();// Call repaint, in case the image dimensions are the + // same. + } else if (iconInfo.getIcon(index) != image) { + if (iconInfo.getMaxIcons() == 1) { + if (index == 0) { + iconInfo = getMapModeConstants().getSingleIconInfo(image); + revalidate(); + repaint();// Call repaint, in case the image dimensions are + // the same. + return; + } + IconInfo oldIconInfo = iconInfo; + iconInfo = new MultiIconInfo(); + iconInfo.setIcon(oldIconInfo.getIcon(0), 0); + } + iconInfo.setIcon(image, index); + revalidate(); + repaint();// Call repaint, in case the image dimensions are the + // same. + } + } + + /** + * Sets the icon alignment relative to the .abel's alignment to the passed + * value. The default is {@link PositionConstants#CENTER}. Other possible + * values are {@link PositionConstants#TOP}, + * {@link PositionConstants#BOTTOM},{@link PositionConstants#LEFT}and + * {@link PositionConstants#RIGHT}. + * + * @param align + * the icon alignment + * @since 2.0 + */ + public void setIconAlignment(int align) { + if (getIconAlignment() == align) + return; + setAlignmentFlags(align, FLAG_ICON_ALIGN); + clearLocations(); + repaint(); + } + + /** + * getIconSize + * + * @param index + * of icon to retrieve size of. + * @return Dimension representing the icon size. + */ + protected Dimension getIconSize(int index) { + if (iconInfo == null) + return EMPTY_DIMENSION; + return iconInfo.getIconSize(getFigureMapMode(), index); + } + + /** + * getIconNumber + * + * @return int number of icons in the wrap label + */ + protected int getNumberofIcons() { + if (iconInfo == null) + return 0; + return iconInfo.getNumberofIcons(); + } + + /** + * getTotalIconSize Calculates the total union of icon sizes + * + * @return Dimension that is the union of icon sizes + */ + protected Dimension getTotalIconSize() { + if (iconInfo == null) + return EMPTY_DIMENSION; + return iconInfo.getTotalIconSize(getFigureMapMode()); + } + + /** + * Sets the Label's alignment to the passed value. The default is + * {@link PositionConstants#CENTER}. Other possible values are + * {@link PositionConstants#TOP},{@link PositionConstants#BOTTOM}, + * {@link PositionConstants#LEFT}and {@link PositionConstants#RIGHT}. + * + * @param align + * label alignment + */ + public void setLabelAlignment(int align) { + if (getLabelAlignment() == align) + return; + setAlignmentFlags(align, FLAG_LABEL_ALIGN); + clearLocations(); + repaint(); + } + + /** + * Return the ellipse string. + * + * @return the <code>String</code> that represents the fact that the text + * has been truncated and that more text is available but hidden. + * Usually this is represented by "...". + */ + protected String getEllipse() { + return _ellipse; + } + + /** + * Sets the label's text. + * + * @param s + * the new label text + * @since 2.0 + */ + public void setText(String s) { + // "text" will never be null. + if (s == null) + s = "";//$NON-NLS-1$ + if (text.equals(s)) + return; + text = s; + revalidate(); + repaint(); // If the new text does not cause a new size, we still need + // to paint. + } + + /** + * Sets the text alignment of the Label relative to the label alignment. The + * default is {@link PositionConstants#CENTER}. Other possible values are + * {@link PositionConstants#TOP},{@link PositionConstants#BOTTOM}, + * {@link PositionConstants#LEFT}and {@link PositionConstants#RIGHT}. + * + * @param align + * the text alignment + * @since 2.0 + */ + public void setTextAlignment(int align) { + if (getTextAlignment() == align) + return; + setAlignmentFlags(align, FLAG_TEXT_ALIGN); + clearLocations(); + repaint(); + } + + /** + * Sets the text placement of the label relative to its icon. The default is + * {@link PositionConstants#EAST}. Other possible values are + * {@link PositionConstants#NORTH},{@link PositionConstants#SOUTH}and + * {@link PositionConstants#WEST}. + * + * @param where + * the text placement + * @since 2.0 + */ + public void setTextPlacement(int where) { + if (getTextPlacement() == where) + return; + setPlacementFlags(where, FLAG_TEXT_PLACEMENT); + revalidate(); + repaint(); + } + + /** + * Sets whether the label text should be underlined + * + * @param b + * Wether the label text should be underlined + */ + public void setTextUnderline(boolean b) { + if (isTextUnderlined() == b) + return; + setFlag(FLAG_UNDERLINED, b); + repaint(); + } + + /** + * @return whether the label text is underlined + */ + public boolean isTextUnderlined() { + return (flags & FLAG_UNDERLINED) != 0; + } + + /** + * Sets whether the label text should be striked-through + * + * @param b + * Wether the label text should be stricked-through + */ + public void setTextStrikeThrough(boolean b) { + if (isTextStrikedThrough() == b) + return; + setFlag(FLAG_STRIKEDTHROUGH, b); + repaint(); + } + + /** + * @return wether the label text is stricked-through + */ + public boolean isTextStrikedThrough() { + return (flags & FLAG_STRIKEDTHROUGH) != 0; + } + + /** + * Sets whether the label text should wrap + * + * @param b + * whether the label text should wrap + */ + public void setTextWrap(boolean b) { + if (isTextWrapped() == b) + return; + setFlag(FLAG_WRAP, b); + revalidate(); + repaint(); + } + + /** + * @return wether the label text wrap is on + */ + public boolean isTextWrapped() { + return (flags & FLAG_WRAP) != 0; + } + + /** + * Sets the wrapping width of the label text. This is only valid if text + * wrapping is turned on + * + * @param i + * The label text wrapping width + */ + public void setTextWrapWidth(int i) { + /* + * if (this.wrapWidth == i) return; this.wrapWidth = i; revalidate(); + * repaint(); + */ + } + + /** + * Sets the wrapping width of the label text. This is only valid if text + * wrapping is turned on + * + * @param i + * The label text wrapping width + */ + public void setTextWrapAlignment(int i) { + if (getTextWrapAlignment() == i) + return; + + setAlignmentFlags(i, FLAG_WRAP_ALIGN); + repaint(); + } + + /** + * @return the label text wrapping width + */ + public int getTextWrapAlignment() { + return getAlignment(FLAG_WRAP_ALIGN); + } + + /** + * setPlacementFlags + * + * @param align + * @param flagOffset + */ + private void setPlacementFlags(int align, int flagOffset) { + flags &= ~(0x7 * flagOffset); + switch (align) { + case EAST: + flags |= 0x1 * flagOffset; + break; + case WEST: + flags |= 0x2 * flagOffset; + break; + case NORTH: + flags |= 0x3 * flagOffset; + break; + case SOUTH: + flags |= 0x4 * flagOffset; + break; + default: + break; + } + } + + /** + * getPlacement + * + * @param flagOffset + * @return PositionConstant representing the placement + */ + private int getPlacement(int flagOffset) { + int wrapValue = flags & (0x7 * flagOffset); + if (wrapValue == 0x1 * flagOffset) + return EAST; + else if (wrapValue == 0x2 * flagOffset) + return WEST; + else if (wrapValue == 0x3 * flagOffset) + return NORTH; + else if (wrapValue == 0x4 * flagOffset) + return SOUTH; + + return EAST; + } + + /** + * setAlignmentFlags + * + * @param align + * @param flagOffset + */ + private void setAlignmentFlags(int align, int flagOffset) { + flags &= ~(0x7 * flagOffset); + switch (align) { + case CENTER: + flags |= 0x1 * flagOffset; + break; + case TOP: + flags |= 0x2 * flagOffset; + break; + case LEFT: + flags |= 0x3 * flagOffset; + break; + case RIGHT: + flags |= 0x4 * flagOffset; + break; + case BOTTOM: + flags |= 0x5 * flagOffset; + break; + default: + break; + } + } + + /** + * Retrieves the alignment value from the flags member. + * + * @param flagOffset + * that is the bitwise value representing the offset. + * @return PositionConstant representing the alignment + */ + private int getAlignment(int flagOffset) { + int wrapValue = flags & (0x7 * flagOffset); + if (wrapValue == 0x1 * flagOffset) + return CENTER; + else if (wrapValue == 0x2 * flagOffset) + return TOP; + else if (wrapValue == 0x3 * flagOffset) + return LEFT; + else if (wrapValue == 0x4 * flagOffset) + return RIGHT; + else if (wrapValue == 0x5 * flagOffset) + return BOTTOM; + + return CENTER; + } + + /** + * Sets the selection state of this label + * + * @param b + * true will cause the label to appear selected + */ + public void setSelected(boolean b) { + if (isSelected() == b) + return; + setFlag(FLAG_SELECTED, b); + repaint(); + } + + /** + * @return the selection state of this label + */ + public boolean isSelected() { + return (flags & FLAG_SELECTED) != 0; + } + + /** + * Sets the focus state of this label + * + * @param b + * true will cause a focus rectangle to be drawn around the text + * of the Label + */ + public void setFocus(boolean b) { + if (hasFocus() == b) + return; + setFlag(FLAG_HASFOCUS, b); + repaint(); + } + + /** + * @return the focus state of this label + */ + public boolean hasFocus() { + return (flags & FLAG_HASFOCUS) != 0; + } + + /** + * Returns the bounds of the text selection + * + * @return The bounds of the text selection + */ + private Rectangle getSelectionRectangle() { + Rectangle figBounds = getTextBounds(); + int expansion = getMapModeConstants().nDPtoLP_2; + figBounds.resize(expansion, expansion); + translateToParent(figBounds); + figBounds.intersect(getBounds()); + return figBounds; + } + + /** + * returns the position of last character within the supplied text that will + * fit within the supplied width. + * + * @param s + * a text string + * @param f + * font used to draw the text string + * @param w + * width in pixles. + * @param fontHeight + * int <b>mapped already to logical units</b>. + */ + private int getLineWrapPosition(String s, Font f, int w, int fontHeight) { + if (getTextExtents(s, f, fontHeight).width <= w) { + return s.length(); + } + // create an iterator for line breaking positions + BreakIterator iter = BreakIterator.getLineInstance(); + iter.setText(s); + int start = iter.first(); + int end = iter.next(); + + // if the first line segment does not fit in the width, + // determine the position within it where we need to cut + if (getTextExtents(s.substring(start, end), f, fontHeight).width > w) { + iter = BreakIterator.getCharacterInstance(); + iter.setText(s); + start = iter.first(); + } + + // keep iterating as long as width permits + do + end = iter.next(); + while (end != BreakIterator.DONE && getTextExtents(s.substring(start, end), f, fontHeight).width <= w); + return (end == BreakIterator.DONE) ? iter.last() : iter.previous(); + } + + /** + * Returns the largest substring of <i>s </i> in Font <i>f </i> that can be + * confined to the number of pixels in <i>availableWidth <i>. + * + * @param s + * the original string + * @param f + * the font + * @param w + * the available width + * @param fontHeight + * int <b>mapped already to logical units</b>. + * @param charAverageWidth + * int <b>mapped already to logical units</b>. + * @return the largest substring that fits in the given width + * @since 2.0 + */ + private int getLargestSubstringConfinedTo(String s, Font f, int w, int fontHeight, int charAverageWidth) { + float avg = charAverageWidth; + int min = 0; + int max = s.length() + 1; + + // The size of the current guess + int guess = 0, guessSize = 0; + while ((max - min) > 1) { + // Pick a new guess size + // New guess is the last guess plus the missing width in pixels + // divided by the average character size in pixels + guess = guess + (int) ((w - guessSize) / avg); + + if (guess >= max) + guess = max - 1; + if (guess <= min) + guess = min + 1; + + // Measure the current guess + guessSize = getTextExtents(s.substring(0, guess), f, fontHeight).width; + + if (guessSize < w) + // We did not use the available width + min = guess; + else + // We exceeded the available width + max = guess; + } + return min; + } + + /** + * Gets the tex extent scaled to the mapping mode + */ + private Dimension getTextExtents(String s, Font f, int fontHeight) { + if (s.length() == 0) { + return getMapModeConstants().dimension_nDPtoLP_0; + } else { + // height should be set using the font height and the number of + // lines in the string + Dimension d = FigureUtilities.getTextExtents(s, f); + IMapMode mapMode = getFigureMapMode(); + d.width = mapMode.DPtoLP(d.width); + d.height = fontHeight * new StringTokenizer(s, "\n").countTokens();//$NON-NLS-1$ + return d; + } + } + + /** + * Returns the current alignment of the entire Label. The default label + * alignment is {@link PositionConstants#LEFT}. + * + * @return the label alignment + */ + public int getLabelAlignment2() { + return getLabelAlignment(); + } + + // CHECKSTYLE:ON + + /** + * {@inheritDoc} + * + * Return false when this label part is not visible. (some edge + * labels intercepts the selection when they are hidden or empty and non + * visible) + */ + @Override + public boolean containsPoint(int x, int y) { + if (isVisible()) { + return super.containsPoint(x, y); + } + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/StyledFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/StyledFigure.java new file mode 100644 index 0000000000..868e8ebd3a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/StyledFigure.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IFigure; + +/** + * The figure showing the style. + * + * @author ymortier + */ +public interface StyledFigure extends IFigure { + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewGradientFigureDesc.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewGradientFigureDesc.java new file mode 100644 index 0000000000..9209dee4a7 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewGradientFigureDesc.java @@ -0,0 +1,39 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.swt.graphics.Color; + +import org.eclipse.sirius.BackgroundStyle; + +/** + * Common interface which add support of gradient for container and list figure + * descriptors. + * + * @author mporhel + */ +public interface ViewGradientFigureDesc extends IFigure { + /** + * Get the gradient color. + * + * @return the gradient color. + */ + Color getGradientColor(); + + /** + * Get the background style. + * + * @return the background style. + */ + BackgroundStyle getBackgroundStyle(); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerFigureDesc.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerFigureDesc.java new file mode 100644 index 0000000000..d7b5f51fd3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerFigureDesc.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.IFigure; + +/** + * We changed the generated class to an Interface in order to easily switch from + * an implementation to another during the edit part creation. + * + * @author cbrun + */ +public interface ViewNodeContainerFigureDesc extends IFigure { + /** + * Get the figure label. + * + * @return the figure label. + */ + SiriusWrapLabel getLabelFigure(); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerParallelogram.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerParallelogram.java new file mode 100644 index 0000000000..a20d447ed5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerParallelogram.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.MarginBorder; + +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IContainerLabelOffsets; + +/** + * Figure for the parallelogram shape. + * + * @author cbrun + * + */ +public class ViewNodeContainerParallelogram extends ParallelogramFigure implements ViewNodeContainerFigureDesc { + + private SiriusWrapLabel fContainerLabelFigure; + + private boolean myUseLocalCoordinates; + + /** + * Constructor. + */ + public ViewNodeContainerParallelogram() { + // setLayoutManager(new XYLayout()); + createContents(); + this.setBorder(new MarginBorder(IContainerLabelOffsets.LABEL_OFFSET, 0, 0, 0)); + } + + private void createContents() { + fContainerLabelFigure = new SiriusWrapLabel(); + fContainerLabelFigure.setText(" "); + fContainerLabelFigure.setTextWrap(true); + this.add(fContainerLabelFigure); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#useLocalCoordinates() + */ + @Override + protected boolean useLocalCoordinates() { + return myUseLocalCoordinates; + } + + protected void setUseLocalCoordinates(final boolean useLocalCoordinates) { + myUseLocalCoordinates = useLocalCoordinates; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.figure.ViewNodeContainerFigureDesc#getLabelFigure() + */ + public SiriusWrapLabel getLabelFigure() { + return fContainerLabelFigure; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerRectangleFigureDesc.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerRectangleFigureDesc.java new file mode 100644 index 0000000000..d42ac3184c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/ViewNodeContainerRectangleFigureDesc.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import org.eclipse.draw2d.MarginBorder; +import org.eclipse.draw2d.RectangleFigure; + +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IContainerLabelOffsets; + +/** + * Basic implementation of {@link ViewNodeContainerFigureDesc} with a rectangle + * shape. + * + * @author cbrun + * + */ +public class ViewNodeContainerRectangleFigureDesc extends RectangleFigure implements ViewNodeContainerFigureDesc { + + private SiriusWrapLabel fContainerLabelFigure; + + /** + * @was-generated + */ + private boolean myUseLocalCoordinates; + + /** + * Create a new {@link ViewNodeContainerFigureDesc}. + */ + public ViewNodeContainerRectangleFigureDesc() { + this.setFill(false); + createContents(); + this.setBorder(new MarginBorder(IContainerLabelOffsets.LABEL_OFFSET, 0, 0, 0)); + } + + private void createContents() { + fContainerLabelFigure = new SiriusWrapLabel(); + fContainerLabelFigure.setText(" "); + fContainerLabelFigure.setTextWrap(true); + this.add(fContainerLabelFigure); + } + + /** + * {@inheritDoc} + * + * @was-generated + * @see org.eclipse.draw2d.Figure#useLocalCoordinates() + */ + @Override + protected boolean useLocalCoordinates() { + return myUseLocalCoordinates; + } + + /** + * {@inheritDoc} + * + * @was-generated + * @param useLocalCoordinates + */ + protected void setUseLocalCoordinates(final boolean useLocalCoordinates) { + myUseLocalCoordinates = useLocalCoordinates; + } + + /** + * {@inheritDoc} + * + * @was-generated + * @see org.eclipse.sirius.diagram.ui.tools.api.figure.ViewNodeContainerFigureDesc#getLabelFigure() + */ + public SiriusWrapLabel getLabelFigure() { + return fContainerLabelFigure; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/WorkspaceImageFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/WorkspaceImageFigure.java new file mode 100644 index 0000000000..aaa012aade --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/WorkspaceImageFigure.java @@ -0,0 +1,209 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure; + +import java.io.File; +import java.net.MalformedURLException; + +import org.eclipse.core.runtime.Path; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.sirius.common.tools.api.resource.FileProvider; +import org.eclipse.sirius.ContainerStyle; +import org.eclipse.sirius.WorkspaceImage; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.image.ImagesPath; + +/** + * The {@link WorkspaceImageFigure} is useful to load images using a cache. The + * image can be in the workspace, or if it's not found in the workspace it will + * be looked up in the plug-ins. + * + * @author cbrun + * + */ +public class WorkspaceImageFigure extends AbstractTransparentImage implements IWorkspaceImageFigure { + + private double imageAspectRatio = 1.0; + + private boolean keepAspectRatio = true; + + /** + * Create a new {@link WorkspaceImageFigure}. + * + * @param flyWeightImage + * an image instance. + */ + public WorkspaceImageFigure(final Image flyWeightImage) { + super(flyWeightImage); + imageAspectRatio = (double) flyWeightImage.getBounds().width / flyWeightImage.getBounds().height; + minSize = new Dimension(0, 0); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setSize(int, int) + */ + @Override + public void setSize(final int w, final int h) { + if (keepAspectRatio) { + final int newHeight = (int) (w / imageAspectRatio); + super.setSize(w, newHeight); + } else { + super.setSize(w, h); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setMaximumSize(org.eclipse.draw2d.geometry.Dimension) + */ + @Override + public void setMaximumSize(final Dimension d) { + super.setMaximumSize(this.getSize()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setMinimumSize(org.eclipse.draw2d.geometry.Dimension) + */ + @Override + public void setMinimumSize(final Dimension d) { + super.setMinimumSize(this.getSize()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Figure#setPreferredSize(org.eclipse.draw2d.geometry.Dimension) + */ + @Override + public void setPreferredSize(final Dimension size) { + super.setPreferredSize(this.getSize()); + } + + /** + * Get an {@link Image} instance. The image will be stored in a cache. + * + * @param path + * the path is a "/project/file" path, if it's not found in the + * workspace, the class will look for the file in the plug-ins. + * @return an image instance given the path. + * @deprecated + */ + @Deprecated + public static Image flyWeightImage(final String path) { + if (path != null) { + final File imageFile = FileProvider.getDefault().getFile(new Path(path)); + ImageDescriptor desc = null; + if (imageFile != null && imageFile.exists() && imageFile.canRead()) { + try { + desc = SiriusDiagramEditorPlugin.findImageDescriptor(imageFile.toURI().toURL()); + } catch (MalformedURLException e) { + // do nothing + } + } + return WorkspaceImageFigure.flyWeightImage(desc); + } + return WorkspaceImageFigure.getImageNotFound(); + } + + /** + * Get an {@link Image} instance. The image will be stored in a cache. + * + * @param desc + * the image descriptor + * @return an image instance given the image descriptor. + * @deprecated + */ + @Deprecated + public static Image flyWeightImage(final ImageDescriptor desc) { + if (desc != null) { + return SiriusDiagramEditorPlugin.getInstance().getImage(desc); + } else { + return WorkspaceImageFigure.getImageNotFound(); + } + } + + private static Image getImageNotFound() { + return SiriusDiagramEditorPlugin.getInstance().getImage(SiriusDiagramEditorPlugin.findImageWithDimensionDescriptor(ImagesPath.IMAGE_NOT_FOUND)); + } + + /** + * Create an {@link WorkspaceImageFigure} instance from an image path. + * + * @param path + * the path is a "/project/file" path, if it's not found in the + * workspace, the class will look for the file in the plug-ins. + * @return an image instance given the path. + */ + @Deprecated + public static WorkspaceImageFigure createImageFigure(final String path) { + final WorkspaceImageFigure fig = new WorkspaceImageFigure(WorkspaceImageFigure.flyWeightImage(path)); + return fig; + } + + /** + * Create an {@link WorkspaceImageFigure} instance from a workspace image. + * + * @param wksImage + * : an instance of {@link WorkspaceImage}. + * @return the image figure built using the {@link WorkspaceImage} object. + */ + @Deprecated + public static WorkspaceImageFigure createImageFigure(final WorkspaceImage wksImage) { + final String path = wksImage.getWorkspacePath(); + return WorkspaceImageFigure.createImageFigure(path); + + } + + /** + * Get the image aspect ratio. + * + * @return the image aspect ratio + */ + public double getImageAspectRatio() { + return imageAspectRatio; + } + + /** + * Refresh the figure. + * + * @param bundledImage + * the image associated to the figure + */ + public void refreshFigure(final WorkspaceImage bundledImage) { + final String path = bundledImage.getWorkspacePath(); + final Image image = WorkspaceImageFigure.flyWeightImage(path); + if (!image.equals(this.getImage())) { + this.setImage(WorkspaceImageFigure.flyWeightImage(path)); + this.repaint(); + } + } + + /** + * Refresh the figure. + * + * @param containerStyle + * the style of the container + */ + public void refreshFigure(final ContainerStyle containerStyle) { + if (containerStyle instanceof WorkspaceImage) { + WorkspaceImage workspaceImage = (WorkspaceImage) containerStyle; + refreshFigure(workspaceImage); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/AirSlidableImageAnchor.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/AirSlidableImageAnchor.java new file mode 100644 index 0000000000..23e1002801 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/AirSlidableImageAnchor.java @@ -0,0 +1,330 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure.anchor; + +import java.util.Iterator; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.ImageFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg; +import org.eclipse.gmf.runtime.draw2d.ui.geometry.PointListUtilities; +import org.eclipse.gmf.runtime.gef.ui.figures.SlidableAnchor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; + +/** + * The {@link org.eclipse.gmf.runtime.gef.ui.figures.SlidableImageAnchor} for + * air. This anchor takes the client area into account. + * + * @author ymortier + */ +public class AirSlidableImageAnchor extends SlidableAnchor { + + private ImageAnchorLocation imageAnchorLocation; + + private ImageFigure imageFig; + + /** + * Create a new {@link AirSlidableImageAnchor}. + */ + public AirSlidableImageAnchor() { + super(); + } + + /** + * Create a new {@link AirSlidableImageAnchor}. + * + * @param f + * the container figure. + * @param imageFig + * the image figure. + * @param p + * the position. + */ + public AirSlidableImageAnchor(final IFigure f, final ImageFigure imageFig, final PrecisionPoint p) { + super(f, p); + this.imageFig = imageFig; + } + + /** + * Create a new {@link AirSlidableImageAnchor}. + * + * @param container + * the container figure. + * @param imageFig + * the image figure. + */ + public AirSlidableImageAnchor(final IFigure container, final ImageFigure imageFig) { + super(container); + this.imageFig = imageFig; + } + + /** + * Create a new {@link AirSlidableImageAnchor}. + * + * @param f + * the container figure. + */ + public AirSlidableImageAnchor(final IFigure f) { + super(f); + } + + /** + * Return the image. + * + * @return the image. + * @see org.eclipse.gmf.runtime.gef.ui.figures.SlidableImageAnchor#getImage() + */ + protected Image getImage() { + final ImageFigure imageFigure = getImageFigure(); + if (imageFigure != null) { + return imageFigure.getImage(); + } + return null; + } + + /** + * Return the image figure. + * + * @return the image figure. + */ + protected ImageFigure getImageFigure() { + ImageFigure ret = imageFig; + if (ret == null) { + final IFigure root = this.getOwner().getParent() == null ? this.getOwner() : this.getOwner().getParent(); + ret = AirSlidableImageAnchor.getImageFigure(root); + } + return ret; + } + + /** + * Returns the first {@link ImageFigure}. + * + * @param root + * the root figure. + * @return the first {@link ImageFigure}. + */ + private static ImageFigure getImageFigure(final IFigure root) { + ImageFigure ret = null; + if (root instanceof ImageFigure) { + ret = (ImageFigure) root; + } + final Iterator iterChilren = root.getChildren().iterator(); + while (iterChilren.hasNext() && ret == null) { + final IFigure next = (IFigure) iterChilren.next(); + ret = AirSlidableImageAnchor.getImageFigure(next); + } + return ret; + } + + private final class ImageAnchorLocation { + + private ImageData imgData; + + private ImageData transMaskData; + + private ImageAnchorLocation(final Image img) { + imgData = img.getImageData(); + transMaskData = imgData.getTransparencyMask(); + } + + /** + * @return Returns the imgData. + */ + protected ImageData getImageData() { + return imgData; + } + + /** + * @return Returns the transMaskData. + */ + protected ImageData getTransparencyMaskData() { + return transMaskData; + } + + /** + * isTransparentAt Accessor to determine if the image is transparent at + * a given point. + * + * @param x + * int location into the image + * @param y + * int location into the image + * @param checkAdjacent + * check adjacent pixels for transparency as well. + * @return boolean true if transparent, false otherwise. + */ + protected boolean isTransparentAt(final int x, final int y, final boolean checkAdjacent) { + // boundary checking + if (x < 0 || x >= getImageData().width || y < 0 || y >= getImageData().height) { + return true; + } + + // check for alpha channel + int transValue = 255; + // check for transparency mask + if (getTransparencyMaskData() != null) { + transValue = getTransparencyMaskData().getPixel(x, y) == 0 ? 0 : 255; + } + + if (transValue != 0) { + if (getImageData().alphaData != null) { + transValue = getImageData().getAlpha(x, y); + } + } + + // use a tolerance + boolean trans = false; + if (transValue < 10) { + trans = true; + + if (checkAdjacent) { + trans = trans && isTransparentAt(x + 1, y, false); + trans = trans && isTransparentAt(x + 1, y + 1, false); + trans = trans && isTransparentAt(x + 1, y - 1, false); + trans = trans && isTransparentAt(x - 1, y + 1, false); + trans = trans && isTransparentAt(x - 1, y, false); + trans = trans && isTransparentAt(x - 1, y - 1, false); + trans = trans && isTransparentAt(x, y + 1, false); + trans = trans && isTransparentAt(x, y - 1, false); + } + } + + return trans; + } + + /** + * getLocation Delegation function used by the ConnectionAnchor + * getLocation + * + * @param start + * the <code>Point</code> that is the beginning of a line + * segment used to calculate the anchor location inside the + * image. + * @param edge + * the <code>Point</code> that is the end of a line segment + * used to calculate the anchor location inside the image. + * @param isDefaultAnchor + * - true if location for the default anchor should be + * calculated + * @return Point representing the location inside the image to anchor + * to. + */ + private Point getLocation(final Point start, final Point edge, final Rectangle containerRect, final boolean isDefaultAnchor) { + + final Point top = containerRect.getTopLeft(); + getOwner().getParent().translateToRelative(top); + + Point ptIntersect = null; + final Rectangle imageBounds = getOwner().getBounds(); + final Dimension dim = edge.getDifference(top); + final Point edgeImg = new Point(Math.max(0, Math.min(dim.width, imageBounds.width - 1)), Math.max(0, Math.min(dim.height, imageBounds.height - 1))); + final Dimension startDim = start.getDifference(top); + final Point startImg = new Point(Math.max(0, Math.min(startDim.width, imageBounds.width - 1)), Math.max(0, Math.min(startDim.height, imageBounds.height - 1))); + ptIntersect = calculateIntersection(startImg, edgeImg); + if (ptIntersect == null) { + return null; + } + return ptIntersect.getTranslated(top.x, top.y); + } + + /** + * calculateIntersection Utility method to calculate the intersection + * point of a given point at an angle into the image to find the first + * opaque pixel. + * + * @param start + * Point that is in the center of the Image. + * @param edge + * Point that is on the edge of the Image. + * @return Point that is the intersection with the first opaque pixel. + */ + private Point calculateIntersection(final Point start, final Point edge) { + final Point opaque = new Point(edge); + + final LineSeg line = new LineSeg(start, edge); + long distance = Math.round(line.length()); + final Rectangle imageBounds = getOwner().getBounds(); + // otherwise calculate value + while (opaque.x >= 0 && opaque.x < imageBounds.width && opaque.y >= 0 && opaque.y < imageBounds.height) { + // convert x and y to client area. + final int imageWidth = getImageData().width; + final int imageHeight = getImageData().height; + final int x = (int) (opaque.x * (double) imageWidth / imageBounds.width); + final int y = (int) (opaque.y * (double) imageHeight / imageBounds.height); + if (!isTransparentAt(x, y, true)) { + return opaque; + } + + line.pointOn(distance, LineSeg.KeyPoint.ORIGIN, opaque); + distance--; + } + + // default is to fall through and return the chopbox point + return null; + } + } + + /** + * Returns bounds of the figure. + * + * @return the owner figure + */ + protected IFigure getContainer() { + return getOwner(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.draw2d.ui.figures.BaseSlidableAnchor#getLocation(org.eclipse.draw2d.geometry.Point, + * org.eclipse.draw2d.geometry.Point) + */ + @Override + protected Point getLocation(final Point ownReference, final Point foreignReference) { + final Image image = getImage(); + if (image == null) { + return super.getLocation(ownReference, foreignReference); + } + Rectangle bounds = getOwner().getBounds(); + final ImageFigure imageFigure = getImageFigure(); + if (imageFigure != null) { + bounds = imageFigure.getBounds(); + } + final Rectangle ownerRect = new Rectangle(bounds); + getOwner().translateToAbsolute(ownerRect); + final PointList intersections = getIntersectionPoints(ownReference, foreignReference); + Point loc = null; + if (intersections != null && intersections.size() != 0) { + final Point ptRef = PointListUtilities.pickFarestPoint(intersections, foreignReference); + final Point ptEdge = PointListUtilities.pickClosestPoint(intersections, foreignReference); + final ImageAnchorLocation imgAnchorLocation = getImageAnchorLocation(); + loc = imgAnchorLocation.getLocation(ptRef, ptEdge, ownerRect, isDefaultAnchor()); + if (loc != null) { + loc = normalizeToStraightlineTolerance(foreignReference, loc, 3); + } + } + return loc; + } + + private ImageAnchorLocation getImageAnchorLocation() { + if (this.imageAnchorLocation == null) { + final Image image = getImage(); + this.imageAnchorLocation = new ImageAnchorLocation(image); + } + return imageAnchorLocation; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/AnchorProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/AnchorProvider.java new file mode 100644 index 0000000000..798dce4a39 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/AnchorProvider.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure.anchor; + +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.geometry.PrecisionPoint; + +import org.eclipse.sirius.diagram.ui.tools.api.figure.AirDefaultSizeNodeFigure; + +/** + * The anchor provider. + * + * @author ymortier + */ +public interface AnchorProvider { + + /** + * Create and return an anchor. + * + * @param figure + * the figure that owns the anchor. + * + * @return the anchor. + */ + ConnectionAnchor createDefaultAnchor(AirDefaultSizeNodeFigure figure); + + /** + * Creates an anchor at the specified point (from the ratio of the + * reference's coordinates and bounds of the figure. + * + * @param figure + * the figure that owns the anchor. + * + * @param p + * relative reference for the <Code>SlidableAnchor</Code> + * @return a <code>ConnetionAnchor</code> for this figure with relative + * reference at p + */ + ConnectionAnchor createAnchor(AirDefaultSizeNodeFigure figure, PrecisionPoint p); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/ZoomDependantAnchor.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/ZoomDependantAnchor.java new file mode 100644 index 0000000000..925a89e709 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/anchor/ZoomDependantAnchor.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure.anchor; + +import org.eclipse.gef.editparts.ZoomManager; + +/** + * Anchor should implement if they need to know the zoom level for location + * computation. + * + * @author mchauvin + */ +public interface ZoomDependantAnchor { + /** + * Set the zoom manager. + * + * @param manager + * the zoom manager. + */ + void setZoomManager(ZoomManager manager); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/locator/DBorderItemLocator.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/locator/DBorderItemLocator.java new file mode 100644 index 0000000000..a970f58a28 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/locator/DBorderItemLocator.java @@ -0,0 +1,804 @@ +/****************************************************************************** + * Copyright (c) 2005, 2006 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Obeo - adaptation for designer + * Laurent Redor (Obeo) <laurent.redor@obeo.fr> - Trac #1735 : Port stability + ****************************************************************************/ + +package org.eclipse.sirius.diagram.ui.tools.api.figure.locator; + +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.Layer; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; + +/** + * A specific + * {@link org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator} for + * Designer. + * + * @author ymortier + */ +public class DBorderItemLocator extends BorderItemLocator { + + /** + * The number of sides. Used to avoid infinite loop exception in there is + * too many borderNode relative to the size of the container. + */ + private static final int NB_SIDES = 4; + + /** The left top offset. */ + private Dimension leftTopOffset; + + /** The right bottom offset. */ + private Dimension rightBottomOffset; + + /** + * Indicates whether the position of the border item has already been + * calculated. Uses to determine the conflicts with other border items + * locations. + */ + private boolean located; + + /** + * Indicates if the border item has moved. This determines if the + * computation of the position (relocate method) stems from a shift of the + * port itself or something else (a resizing of its container, for example). + * + */ + private boolean borderItemHasMoved; + + /** + * This list of figures is set during the build of the command in + * {@link org.eclipse.sirius.diagram.graphical.edit.policies.SpecificBorderItemSelectionEditPolicy} + * to be able to know the figures to ignore when draw2d will launch the + * redraw. + */ + private List<IFigure> figuresToIgnoreDuringNextRelocate = Lists.newArrayList(); + + /** + * Create an {@link DBorderItemLocator} with the specified parentFigure. + * + * @param parentFigure + * the parent figure. + */ + public DBorderItemLocator(final IFigure parentFigure) { + super(parentFigure); + } + + /** + * Create a {@link DBorderItemLocator} with the specified item, parentFigure + * and constraint. + * + * @param borderItem + * the border item. + * @param parentFigure + * the parent figure. + * @param constraint + * the constraint. + */ + public DBorderItemLocator(final IFigure borderItem, final IFigure parentFigure, final Rectangle constraint) { + super(borderItem, parentFigure, constraint); + } + + /** + * Create a {@link DBorderItemLocator} with the specified item and + * preferredSide. + * + * @param parentFigure + * the parent figure. + * @param preferredSide + * the preferred side. + */ + public DBorderItemLocator(final IFigure parentFigure, final int preferredSide) { + super(parentFigure, preferredSide); + } + + /** + * Define the right bottom offset. + * + * @param rightBottomOffset + * the right bottom offset. + */ + public void setRightBottomOffset(final Dimension rightBottomOffset) { + this.rightBottomOffset = rightBottomOffset; + } + + /** + * Return the right bottom offset. + * + * @return the right bottom offset. + */ + public Dimension getRightBottomOffset() { + if (this.rightBottomOffset != null) { + return rightBottomOffset; + } + return getBorderItemOffset(); + } + + /** + * Define the left top offset. + * + * @param leftTopOffset + * the left top offset. + */ + public void setLeftTopOffset(final Dimension leftTopOffset) { + this.leftTopOffset = leftTopOffset; + } + + /** + * Return the left top offset. + * + * @return the left top offset. + */ + public Dimension getLeftTopOffset() { + if (this.leftTopOffset != null) { + return leftTopOffset; + } + return getBorderItemOffset(); + } + + /** + * Get the preferred location. If none has been previously set, use the + * preferred side to take an initial guess. + * + * @param borderItem + * the border item + * @return point + */ + @Override + protected Point getPreferredLocation(final IFigure borderItem) { + final Point constraintLocation = getConstraint().getLocation(); + final Point ptAbsoluteLocation = this.getAbsoluteToBorder(constraintLocation); + return ptAbsoluteLocation; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator#relocate(org.eclipse.draw2d.IFigure) + */ + @Override + public void relocate(final IFigure borderItem) { + Rectangle parentBounds = getParentFigure().getBounds().getCopy(); + // If the relocate is called before that the figure have been + // "initialized", it is by-passed. + if (!(parentBounds.x == 0 && parentBounds.y == 0 && parentBounds.width <= 0 && parentBounds.height <= 0)) { + final Dimension size = getSize(borderItem); + final Rectangle rectSuggested = new Rectangle(getPreferredLocation(borderItem), size); + // If the border item has moved, we change the preferred side, + // otherwise we let the current side enabled + if (borderItemHasMoved) { + final int closestSide = DBorderItemLocator.findClosestSideOfParent(rectSuggested, getParentBorder()); + setPreferredSideOfParent(closestSide); + setCurrentSideOfParent(closestSide); + borderItemHasMoved = false; + } else { + // We use the notion if figuresToIgnoreDuringNextRelocate only + // if bordered node is moved. + figuresToIgnoreDuringNextRelocate.clear(); + } + final Point ptNewLocation = locateOnBorder(rectSuggested.getLocation(), getCurrentSideOfParent(), 0, borderItem, figuresToIgnoreDuringNextRelocate); + borderItem.setLocation(ptNewLocation); + figuresToIgnoreDuringNextRelocate.clear(); + borderItem.setSize(size); + this.located = true; + } + } + + @Override + protected Point locateOnBorder(Point suggestedLocation, int suggestedSide, int circuitCount, IFigure borderItem) { + List<IFigure> figuresToIgnore = Lists.newArrayList(); + figuresToIgnore.add(borderItem); + return locateOnBorder(suggestedLocation, suggestedSide, circuitCount, borderItem, figuresToIgnore); + } + + /** + * The preferred side takes precedence. Search prior on the suggestedSide + * then on the following side in the anticlockwise. This is behavior similar + * to that of GMF. We could envisage a more intelligent behavior that avoids + * passing over other borderedNodes even if it meant a move to the following + * side. + * + * @param suggestedLocation + * the suggested location + * @param suggestedSide + * the suggested side + * @param circuitCount + * recursion count to avoid an infinite loop + * @param borderItem + * the border item. + * @param portsFiguresToIgnore + * the ports figures to ignore + * @return point + */ + protected Point locateOnBorder(final Point suggestedLocation, final int suggestedSide, final int circuitCount, final IFigure borderItem, final Collection<IFigure> portsFiguresToIgnore) { + Point recommendedLocation = locateOnParent(suggestedLocation, suggestedSide, borderItem); + + if (circuitCount < NB_SIDES && conflicts(recommendedLocation, borderItem, portsFiguresToIgnore).some()) { + if (suggestedSide == PositionConstants.WEST) { + recommendedLocation = locateOnWestBorder(recommendedLocation, circuitCount, borderItem, portsFiguresToIgnore); + } else if (suggestedSide == PositionConstants.SOUTH) { + recommendedLocation = locateOnSouthBorder(recommendedLocation, circuitCount, borderItem, portsFiguresToIgnore); + } else if (suggestedSide == PositionConstants.EAST) { + recommendedLocation = locateOnEastBorder(recommendedLocation, circuitCount, borderItem, portsFiguresToIgnore); + } else { // NORTH + recommendedLocation = locateOnNorthBorder(recommendedLocation, circuitCount, borderItem, portsFiguresToIgnore); + } + } + return recommendedLocation; + } + + /** + * Locate the recommendedLocation on the south border : + * <UL> + * <LI>Search alternately to the left and to the right until find an + * available space</LI> + * <LI>And finally if there is no space on this border search on the east + * border.</LI> + * </UL> + * + * @param recommendedLocation + * The desired location + * @param circuitCount + * recursion count to avoid an infinite loop + * @param borderItem + * the figure representing the border item. + * @param portsFiguresToIgnore + * the ports figures to ignore + * @return the location where the border item can be put + */ + protected Point locateOnSouthBorder(final Point recommendedLocation, final int circuitCount, final IFigure borderItem, final Collection<IFigure> portsFiguresToIgnore) { + final Dimension borderItemSize = getSize(borderItem); + Point resultLocation = null; + final Point rightTestPoint = new Point(recommendedLocation); + final Point leftTestPoint = new Point(recommendedLocation); + boolean isStillFreeSpaceToTheRight = true; + boolean isStillFreeSpaceToTheLeft = true; + int rightVerticalGap = 0; + int leftVerticalGap = 0; + // The recommendedLocationForEast is set when we detected that there is + // not free space on right of south side. + Point recommendedLocationForEast = recommendedLocation; + while (resultLocation == null && (isStillFreeSpaceToTheRight || isStillFreeSpaceToTheLeft)) { + if (isStillFreeSpaceToTheRight) { + // Move to the right on the south side + rightTestPoint.x += rightVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(rightTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + rightVerticalGap = (optionalConflictingRectangle.get().x + optionalConflictingRectangle.get().width + 1) - rightTestPoint.x; + if (rightTestPoint.x + rightVerticalGap + borderItemSize.width > getParentBorder().getBottomRight().x) { + isStillFreeSpaceToTheRight = false; + if (circuitCount == NB_SIDES - 1) { + // There is no space on either side (so use the last + // conflicting position) + resultLocation = optionalConflictingRectangle.get().getTopLeft(); + } else { + recommendedLocationForEast = new Point(rightTestPoint.x + rightVerticalGap, optionalConflictingRectangle.get().y - borderItemSize.height - 1); + } + } + } else { + resultLocation = rightTestPoint; + } + } + if (isStillFreeSpaceToTheLeft && resultLocation == null) { + // Move to the left on the south side + leftTestPoint.x -= leftVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(leftTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + leftVerticalGap = leftTestPoint.x - (optionalConflictingRectangle.get().x - borderItemSize.width - 1); + if (leftTestPoint.x - leftVerticalGap < getParentBorder().getTopLeft().x) { + isStillFreeSpaceToTheLeft = false; + } + } else { + resultLocation = leftTestPoint; + } + } + } + if (resultLocation == null) { + // south is full, try east. + resultLocation = locateOnBorder(recommendedLocationForEast, PositionConstants.EAST, circuitCount + 1, borderItem, portsFiguresToIgnore); + } + return resultLocation; + } + + /** + * Locate the recommendedLocation on the north border : + * <UL> + * <LI>Search alternately to the left and to the right until find an + * available space</LI> + * <LI>And finally if there is no space on this border search on the west + * border.</LI> + * </UL> + * + * @param recommendedLocation + * The desired location + * @param circuitCount + * recursion count to avoid an infinite loop + * @param borderItem + * the figure representing the border item. + * @param portsFiguresToIgnore + * the ports figures to ignore + * @return the location where the border item can be put + */ + protected Point locateOnNorthBorder(final Point recommendedLocation, final int circuitCount, final IFigure borderItem, final Collection<IFigure> portsFiguresToIgnore) { + final Dimension borderItemSize = getSize(borderItem); + Point resultLocation = null; + final Point rightTestPoint = new Point(recommendedLocation); + final Point leftTestPoint = new Point(recommendedLocation); + boolean isStillFreeSpaceToTheRight = true; + boolean isStillFreeSpaceToTheLeft = true; + int rightVerticalGap = 0; + int leftVerticalGap = 0; + // The recommendedLocationForWest is set when we detected that there is + // not free space on left of north side. + Point recommendedLocationForWest = recommendedLocation; + while (resultLocation == null && (isStillFreeSpaceToTheRight || isStillFreeSpaceToTheLeft)) { + if (isStillFreeSpaceToTheRight) { + // Move to the right on the north side + rightTestPoint.x += rightVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(rightTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + rightVerticalGap = (optionalConflictingRectangle.get().x + optionalConflictingRectangle.get().width + 1) - rightTestPoint.x; + if (rightTestPoint.x + rightVerticalGap + borderItemSize.width > getParentBorder().getBottomRight().x) { + isStillFreeSpaceToTheRight = false; + } + } else { + resultLocation = rightTestPoint; + } + } + if (isStillFreeSpaceToTheLeft && resultLocation == null) { + // Move to the left on the north side + leftTestPoint.x -= leftVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(leftTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + leftVerticalGap = leftTestPoint.x - (optionalConflictingRectangle.get().x - borderItemSize.width - 1); + if (leftTestPoint.x - leftVerticalGap < getParentBorder().getTopLeft().x) { + isStillFreeSpaceToTheLeft = false; + if (circuitCount == NB_SIDES - 1) { + // There is no space on either side (so use the last + // conflicting position) + resultLocation = optionalConflictingRectangle.get().getTopLeft(); + } else { + recommendedLocationForWest = new Point(leftTestPoint.x - leftVerticalGap, optionalConflictingRectangle.get().y + optionalConflictingRectangle.get().height + 1); + } + } + } else { + resultLocation = leftTestPoint; + } + } + } + if (resultLocation == null) { + // North is full, try west. + resultLocation = locateOnBorder(recommendedLocationForWest, PositionConstants.WEST, circuitCount + 1, borderItem, portsFiguresToIgnore); + } + return resultLocation; + } + + /** + * Locate the recommendedLocation on the west border : + * <UL> + * <LI>Search alternately upward and downward until find an available space</LI> + * <LI>And finally if there is no space on this border search on the south + * border.</LI> + * </UL> + * + * @param recommendedLocation + * The desired location + * @param circuitCount + * recursion count to avoid an infinite loop + * @param borderItem + * the figure representing the border item. + * @param portsFiguresToIgnore + * the ports figures to ignore + * @return the location where the border item can be put + */ + protected Point locateOnWestBorder(final Point recommendedLocation, final int circuitCount, final IFigure borderItem, final Collection<IFigure> portsFiguresToIgnore) { + final Dimension borderItemSize = getSize(borderItem); + Point resultLocation = null; + final Point belowTestPoint = new Point(recommendedLocation); + final Point aboveTestPoint = new Point(recommendedLocation); + boolean isStillFreeSpaceAbove = true; + boolean isStillFreeSpaceBelow = true; + int belowVerticalGap = 0; + int aboveVerticalGap = 0; + // The recommendedLocationForSouth is set when we detected that there is + // not free space on bottom of west side. + Point recommendedLocationForSouth = recommendedLocation; + while (resultLocation == null && (isStillFreeSpaceAbove || isStillFreeSpaceBelow)) { + if (isStillFreeSpaceBelow) { + // Move down on the west side + belowTestPoint.y += belowVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(belowTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + belowVerticalGap = optionalConflictingRectangle.get().y + optionalConflictingRectangle.get().height - belowTestPoint.y + 1; + if (belowTestPoint.y + belowVerticalGap + borderItemSize.height > getParentBorder().getBottomLeft().y) { + isStillFreeSpaceBelow = false; + if (circuitCount == NB_SIDES - 1) { + // There is no space on either side (so use the last + // conflicting position) + resultLocation = optionalConflictingRectangle.get().getTopLeft(); + } else { + recommendedLocationForSouth = new Point(belowTestPoint.x + optionalConflictingRectangle.get().width + 1, belowTestPoint.y + belowVerticalGap); + } + } + } else { + resultLocation = belowTestPoint; + } + } + if (isStillFreeSpaceAbove && resultLocation == null) { + // Move up on the west side + aboveTestPoint.y -= aboveVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(aboveTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + aboveVerticalGap = aboveTestPoint.y - (optionalConflictingRectangle.get().y - borderItemSize.height - 1); + if (aboveTestPoint.y - aboveVerticalGap < getParentBorder().getTopRight().y) { + isStillFreeSpaceAbove = false; + } + } else { + resultLocation = aboveTestPoint; + } + } + } + if (resultLocation == null) { + // west is full, try south. + resultLocation = locateOnBorder(recommendedLocationForSouth, PositionConstants.SOUTH, circuitCount + 1, borderItem, portsFiguresToIgnore); + } + return resultLocation; + } + + /** + * Locate the recommendedLocation on the east border : + * <UL> + * <LI>Search alternately upward and downward until find an available space</LI> + * <LI>And finally if there is no space on this border search on the north + * border.</LI> + * </UL> + * + * @param recommendedLocation + * The desired location + * @param circuitCount + * recursion count to avoid an infinite loop + * @param borderItem + * the figure representing the border item. + * @param portsFiguresToIgnore + * the ports figures to ignore + * @return the location where the border item can be put + */ + protected Point locateOnEastBorder(final Point recommendedLocation, final int circuitCount, final IFigure borderItem, final Collection<IFigure> portsFiguresToIgnore) { + final Dimension borderItemSize = getSize(borderItem); + Point resultLocation = null; + final Point belowTestPoint = new Point(recommendedLocation); + final Point aboveTestPoint = new Point(recommendedLocation); + boolean isStillFreeSpaceAbove = true; + boolean isStillFreeSpaceBelow = true; + int belowVerticalGap = 0; + int aboveVerticalGap = 0; + // The recommendedLocationForNorth is set when we detected that there is + // not free space on top of east side. + Point recommendedLocationForNorth = recommendedLocation; + while (resultLocation == null && (isStillFreeSpaceAbove || isStillFreeSpaceBelow)) { + if (isStillFreeSpaceBelow) { + // Move down on the east side + belowTestPoint.y += belowVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(belowTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + belowVerticalGap = optionalConflictingRectangle.get().y + optionalConflictingRectangle.get().height - belowTestPoint.y + 1; + if (belowTestPoint.y + belowVerticalGap + borderItemSize.height > getParentBorder().getBottomLeft().y) { + isStillFreeSpaceBelow = false; + } + } else { + resultLocation = belowTestPoint; + } + } + if (isStillFreeSpaceAbove && resultLocation == null) { + // Move up on the east side + aboveTestPoint.y -= aboveVerticalGap; + Option<Rectangle> optionalConflictingRectangle = conflicts(aboveTestPoint, borderItem, portsFiguresToIgnore); + if (optionalConflictingRectangle.some()) { + aboveVerticalGap = aboveTestPoint.y - (optionalConflictingRectangle.get().y - borderItemSize.height - 1); + if (aboveTestPoint.y - aboveVerticalGap < getParentBorder().getTopRight().y) { + isStillFreeSpaceAbove = false; + if (circuitCount == NB_SIDES - 1) { + // There is no space on either side (so use the last + // conflicting position) + resultLocation = optionalConflictingRectangle.get().getTopLeft(); + } else { + recommendedLocationForNorth = new Point(optionalConflictingRectangle.get().x - borderItemSize.width - 1, aboveTestPoint.y - aboveVerticalGap); + } + } + } else { + resultLocation = aboveTestPoint; + } + } + } + if (resultLocation == null) { + // East is full, try north. + resultLocation = locateOnBorder(recommendedLocationForNorth, PositionConstants.NORTH, circuitCount + 1, borderItem, portsFiguresToIgnore); + } + return resultLocation; + } + + /** + * Ensure the suggested location actually lies on the parent boundary. The + * side takes precedence. + * + * @param suggestedLocation + * suggested location + * @param suggestedSide + * suggested side + * @param borderItem + * the border item. + * @return point the location point + */ + protected Point locateOnParent(final Point suggestedLocation, final int suggestedSide, final IFigure borderItem) { + final Rectangle bounds = getParentBorder(); + final int parentFigureWidth = bounds.width; + final int parentFigureHeight = bounds.height; + final int parentFigureX = bounds.x; + final int parentFigureY = bounds.y; + final Dimension borderItemSize = getSize(borderItem); + int newX = suggestedLocation.x; + int newY = suggestedLocation.y; + final int westX = parentFigureX - borderItemSize.width + getBorderItemOffset().width; + final int eastX = parentFigureX + parentFigureWidth - getBorderItemOffset().width; + final int southY = parentFigureY + parentFigureHeight - getBorderItemOffset().height; + final int northY = parentFigureY - borderItemSize.height + getBorderItemOffset().height; + if (suggestedSide == PositionConstants.WEST) { + if (suggestedLocation.x != westX) { + newX = westX; + } + if (suggestedLocation.y < bounds.getTopLeft().y) { + newY = northY + borderItemSize.height; + } else if (suggestedLocation.y > bounds.getBottomLeft().y - borderItemSize.height) { + newY = southY - borderItemSize.height; + } + } else if (suggestedSide == PositionConstants.EAST) { + if (suggestedLocation.x != eastX) { + newX = eastX; + } + if (suggestedLocation.y < bounds.getTopLeft().y) { + newY = northY + borderItemSize.height; + } else if (suggestedLocation.y > bounds.getBottomLeft().y - borderItemSize.height) { + newY = southY - borderItemSize.height; + } + } else if (suggestedSide == PositionConstants.SOUTH) { + if (suggestedLocation.y != southY) { + newY = southY; + } + if (borderItemSize.width > bounds.width) { + // The border item width is larger than the parent item. In that + // case, we will center the border item on the south side of the + // parent. + newX = parentFigureX - (borderItemSize.width - bounds.width) / 2; + } else if (suggestedLocation.x < bounds.getBottomLeft().x) { + newX = westX + borderItemSize.width; + } else if (suggestedLocation.x > bounds.getBottomRight().x - borderItemSize.width) { + newX = eastX - borderItemSize.width; + } + } else { // NORTH + if (suggestedLocation.y != northY) { + newY = northY; + } + if (borderItemSize.width > bounds.width) { + // The border item width is larger than the parent item. In that + // case, we will center the border item on the north side of the + // parent. + newX = parentFigureX - (borderItemSize.width - bounds.width) / 2; + } else if (suggestedLocation.x < bounds.getBottomLeft().x) { + newX = westX + borderItemSize.width; + } else if (suggestedLocation.x > bounds.getBottomRight().x - borderItemSize.width) { + newX = eastX - borderItemSize.width; + } + } + return new Point(newX, newY); + } + + /** + * Determine if the the given point conflicts with the position of an + * existing borderItemFigure. + * + * @param recommendedLocation + * The desired location + * @param targetBorderItem + * the border node for which we detect conflicts. + * @param portsFiguresToIgnore + * the ports figures to ignore + * @return the optional Rectangle of the border item that is in conflict + * with the given bordered node (a none option) + */ + protected Option<Rectangle> conflicts(final Point recommendedLocation, final IFigure targetBorderItem, final Collection<IFigure> portsFiguresToIgnore) { + final Rectangle recommendedRect = new Rectangle(recommendedLocation, getSize(targetBorderItem)); + final List borderItems = targetBorderItem.getParent().getChildren(); + final ListIterator iterator = borderItems.listIterator(); + while (iterator.hasNext()) { + final IFigure borderItem = (IFigure) iterator.next(); + if (!portsFiguresToIgnore.contains(borderItem)) { + boolean takeIntoAccount = true; + // We consider the brothers that : + // * have a parent without layoutManager and are directly in a + // Layer (this case + // corresponds to feedback of collapsed bordered nodes that are + // expanded during drop) + // * have a parent with a layoutManager and that contains a + // constraint of type DBorderItemLocator that is located. + if (borderItem.getParent().getLayoutManager() == null) { + if (!(borderItem.getParent() instanceof Layer)) { + takeIntoAccount = false; + } + } else if (borderItem.getParent().getLayoutManager().getConstraint(borderItem) instanceof DBorderItemLocator) { + final DBorderItemLocator airBorderItemLocator = (DBorderItemLocator) borderItem.getParent().getLayoutManager().getConstraint(borderItem); + takeIntoAccount = airBorderItemLocator.located; + } + if (borderItem.isVisible() && takeIntoAccount) { + final Rectangle rect = new Rectangle(borderItem.getBounds()); + if (!(portsFiguresToIgnore.contains(borderItem)) && borderItem != targetBorderItem && rect.intersects(recommendedRect)) { + return Options.newSome(rect); + } + } + } + } + return Options.newNone(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator#setConstraint(org.eclipse.draw2d.geometry.Rectangle) + */ + @Override + public void setConstraint(final Rectangle theConstraint) { + if (!theConstraint.equals(getConstraint())) { + borderItemHasMoved = true; + } + super.setConstraint(theConstraint); + } + + /** + * Provides a copy of the current {@link BorderItemLocator} constraint. + * + * @return a copy of the constraint rectangle. + */ + public Rectangle getCurrentConstraint() { + return super.getConstraint().getCopy(); + } + + /** + * Find the closest side when x,y is inside parent. + * + * @param proposedLocation + * the proposed location + * @param parentBorder + * the parent border + * @return draw constant + */ + public static int findClosestSideOfParent(final Rectangle proposedLocation, final Rectangle parentBorder) { + // Rectangle parentBorder = getParentBorder(); + final Point parentCenter = parentBorder.getCenter(); + final Point childCenter = proposedLocation.getCenter(); + + int position; + + if (childCenter.x < parentCenter.x) { + // West, North or South. + if (childCenter.y < parentCenter.y) { + // west or north + // closer to west or north? + final Point parentTopLeft = parentBorder.getTopLeft(); + if (childCenter.y < parentTopLeft.y) { + position = PositionConstants.NORTH; + } else if ((childCenter.x - parentTopLeft.x) <= (childCenter.y - parentTopLeft.y)) { + position = PositionConstants.WEST; + } else { + position = PositionConstants.NORTH; + } + } else { // west or south + final Point parentBottomLeft = parentBorder.getBottomLeft(); + if (childCenter.y > parentBottomLeft.y) { + position = PositionConstants.SOUTH; + } else if ((childCenter.x - parentBottomLeft.x) <= (parentBottomLeft.y - childCenter.y)) { + position = PositionConstants.WEST; + } else { + position = PositionConstants.SOUTH; + } + } + } else { + // EAST, NORTH or SOUTH + if (childCenter.y < parentCenter.y) { + // north or east + final Point parentTopRight = parentBorder.getTopRight(); + if (childCenter.y < parentTopRight.y) { + position = PositionConstants.NORTH; + } else if ((parentTopRight.x - childCenter.x) <= (childCenter.y - parentTopRight.y)) { + position = PositionConstants.EAST; + } else { + position = PositionConstants.NORTH; + } + } else { // south or east. + final Point parentBottomRight = parentBorder.getBottomRight(); + if (childCenter.y > parentBottomRight.y) { + position = PositionConstants.SOUTH; + } else if ((parentBottomRight.x - childCenter.x) <= (parentBottomRight.y - childCenter.y)) { + position = PositionConstants.EAST; + } else { + position = PositionConstants.SOUTH; + } + } + } + return position; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.figures.BorderItemLocator#getValidLocation(org.eclipse.draw2d.geometry.Rectangle, + * org.eclipse.draw2d.IFigure) + */ + @Override + public Rectangle getValidLocation(final Rectangle proposedLocation, final IFigure borderItem) { + final Rectangle realLocation = new Rectangle(proposedLocation); + final int side = DBorderItemLocator.findClosestSideOfParent(proposedLocation, getParentBorder()); + final Point newTopLeft = locateOnBorder(realLocation.getTopLeft(), side, 0, borderItem); + realLocation.setLocation(newTopLeft); + return realLocation; + } + + /** + * Lets consider this border item as not fixed. + */ + public void unfix() { + located = false; + } + + /** + * Get valid location. + * + * @param proposedLocation + * a proposed location and optionally size + * @param borderItem + * the border item in question + * @param figuresToIgnore + * list of figures to ignore during conflict detection. This list + * must contain at least the <code>borderItem</code>. + * @return a rectangle containing the valid location + */ + public Rectangle getValidLocation(Rectangle proposedLocation, IFigure borderItem, Collection<IFigure> figuresToIgnore) { + final Rectangle realLocation = new Rectangle(proposedLocation); + final int side = DBorderItemLocator.findClosestSideOfParent(proposedLocation, getParentBorder()); + final Point newTopLeft = locateOnBorder(realLocation.getTopLeft(), side, 0, borderItem, figuresToIgnore); + realLocation.setLocation(newTopLeft); + return realLocation; + } + + /** + * This method must be used only when commands are build to move a bordered + * node (for example in + * {@link org.eclipse.sirius.diagram.graphical.edit.policies.SpecificBorderItemSelectionEditPolicy} + * ) after calling {@link #getValidLocation(Rectangle, IFigure, Collection)} + * . + * + * @param figuresToIgnore + * The list of figures to ignore. + */ + public void setFiguresToIgnoresDuringNextRelocate(List<IFigure> figuresToIgnore) { + this.figuresToIgnoreDuringNextRelocate = figuresToIgnore; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/locator/NorthNameLocator.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/locator/NorthNameLocator.java new file mode 100644 index 0000000000..116125bbda --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/figure/locator/NorthNameLocator.java @@ -0,0 +1,148 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.figure.locator; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.figures.LayoutHelper; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; + +/** + * This locator locate the item in the north of the bordered figure. + * + * @author ymortier + */ +public class NorthNameLocator implements IBorderItemLocator { + + /** Constraints. */ + private Rectangle constraint; + + /** the figure around which this border item appears */ + private final IFigure parentFigure; + + /** The offset between the bordered figure and the border item. */ + private int offset = 10; + + /** + * Create a new <code>LilelineNameLocator</code>. + * + * @param parentFigure + * the parent figure. + */ + public NorthNameLocator(final IFigure parentFigure) { + this.parentFigure = parentFigure; + } + + /** + * Create a new <code>LilelineNameLocator</code>. + * + * @param parentFigure + * the parent figure. + * @param offset + * The offset between the bordered figure and the border item. + */ + public NorthNameLocator(final IFigure parentFigure, final int offset) { + this.parentFigure = parentFigure; + this.offset = offset; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator#getCurrentSideOfParent() + */ + public int getCurrentSideOfParent() { + return PositionConstants.NORTH; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator#getValidLocation(org.eclipse.draw2d.geometry.Rectangle, + * org.eclipse.draw2d.IFigure) + */ + public Rectangle getValidLocation(final Rectangle proposedLocation, final IFigure borderItem) { + final Rectangle realLocation = new Rectangle(proposedLocation); + final int x = (getParentBorder().x + (getParentBorder().width / 2)) - (getSize(borderItem).width / 2); + final int y = getParentBorder().y - getSize(borderItem).height - this.offset; + realLocation.setLocation(new Point(x, y)); + realLocation.setSize(getSize(borderItem)); + return realLocation; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator#setConstraint(org.eclipse.draw2d.geometry.Rectangle) + */ + public void setConstraint(final Rectangle constraint) { + this.constraint = constraint; + getParentFigure().revalidate(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Locator#relocate(org.eclipse.draw2d.IFigure) + */ + public void relocate(final IFigure target) { + final Rectangle proposed = new Rectangle(this.constraint); + this.getParentFigure().translateToAbsolute(proposed); + this.constraint = this.getValidLocation(proposed, target); + target.setSize(this.getSize(target)); + target.setLocation(this.constraint.getLocation()); + } + + /** + * Return the parentFigure. + * + * @return the parentFigure. + */ + public IFigure getParentFigure() { + return this.parentFigure; + } + + /** + * Utility to calculate the parent bounds with consideration for the handle + * bounds inset. + * + * @return <code>Rectangle</code> that is the bounds of the parent. + */ + protected Rectangle getParentBorder() { + Rectangle bounds = new Rectangle(getParentFigure().getBounds()); + if (getParentFigure() instanceof NodeFigure) { + bounds = new Rectangle(((NodeFigure) getParentFigure()).getHandleBounds()); + } + return bounds; + } + + /** + * Gets the size of the border item figure. + * + * @param borderItem + * the border item figure to get the size + * @return the size of the border item figure. + */ + protected final Dimension getSize(final IFigure borderItem) { + Dimension size = LayoutHelper.UNDEFINED.getSize(); + if (this.constraint != null) { + size = this.constraint.getSize(); + } + if (LayoutHelper.UNDEFINED.getSize().equals(size)) { + size = borderItem.getPreferredSize(); + } + return size; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/AbstractSiriusLayoutDataManager.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/AbstractSiriusLayoutDataManager.java new file mode 100644 index 0000000000..a89f742470 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/AbstractSiriusLayoutDataManager.java @@ -0,0 +1,828 @@ +/******************************************************************************* + * Copyright (c) 2009, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import java.util.Collection; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gef.EditPartViewer; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.notation.Bendpoints; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.IdentityAnchor; +import org.eclipse.gmf.runtime.notation.JumpLinkStatus; +import org.eclipse.gmf.runtime.notation.JumpLinkType; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationFactory; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.RelativeBendpoints; +import org.eclipse.gmf.runtime.notation.Routing; +import org.eclipse.gmf.runtime.notation.RoutingStyle; +import org.eclipse.gmf.runtime.notation.Smoothness; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.gmf.runtime.notation.datatype.RelativeBendpoint; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.DNodeList; +import org.eclipse.sirius.DNodeListElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.EdgeTarget; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.diagram.business.api.query.NodeQuery; +import org.eclipse.sirius.diagram.business.api.view.SiriusGMFHelper; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramBorderNodeEditPart; +import org.eclipse.sirius.diagram.internal.refresh.GMFHelper; +import org.eclipse.sirius.diagram.internal.refresh.borderednode.CanonicalDBorderItemLocator; +import org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData; +import org.eclipse.sirius.diagram.layoutdata.EdgeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.LayoutdataFactory; +import org.eclipse.sirius.diagram.layoutdata.LayoutdataPackage; +import org.eclipse.sirius.diagram.layoutdata.NodeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.Point; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.api.draw2d.ui.figures.FigureUtilities; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IBorderItemOffsets; + +/** + * An abstract implementation for {@link SiriusLayoutDataManager}. <BR> + * Provide a method to store a layout from a graphicalEditPart and iterates on + * it's children. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public abstract class AbstractSiriusLayoutDataManager implements SiriusLayoutDataManager { + + private static final Class<?>[] CLASS_EXCEPTIONS = new Class[] { DNodeListElement.class }; + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#storeLayoutData(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public void storeLayoutData(final IGraphicalEditPart rootEditPart) { + final Collection<LayoutDataKey> discoveredKeys = Sets.newHashSet(); + final EObject semanticElement = rootEditPart.resolveSemanticElement(); + final View toStoreView = (View) rootEditPart.getModel(); + if (toStoreView instanceof Edge && semanticElement instanceof DEdge) { + addEdgeLayoutData(null, (DEdge) semanticElement, rootEditPart.getRoot().getViewer()); + } else if (toStoreView instanceof Diagram && semanticElement instanceof DDiagram) { + addChildLayout((DDiagram) semanticElement, rootEditPart, discoveredKeys); + } else if (toStoreView instanceof Node) { + if (semanticElement instanceof DDiagramElement && semanticElement instanceof DSemanticDecorator) { + addChildLayout(null, (DSemanticDecorator) semanticElement, (Node) toStoreView, rootEditPart, discoveredKeys); + } + } + discoveredKeys.clear(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#applyLayout(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart, + * org.eclipse.gef.EditPartViewer) + */ + public void applyLayout(final IGraphicalEditPart rootEditPart) { + final EObject semanticElement = rootEditPart.resolveSemanticElement(); + final View toStoreView = (View) rootEditPart.getModel(); + if (toStoreView instanceof Edge) { + // TODOLRE : Manage the edge as root ? + } else if (toStoreView instanceof Diagram && semanticElement instanceof DDiagram) { + applyLayout((DDiagram) semanticElement, (Diagram) toStoreView, rootEditPart.getRoot().getViewer()); + } else if (toStoreView instanceof Node) { + if (semanticElement instanceof DDiagramElement && semanticElement instanceof DSemanticDecorator) { + applyLayout((DSemanticDecorator) semanticElement, (Node) toStoreView, rootEditPart.getRoot().getViewer(), null); + } + } + } + + /** + * @param semanticDecorator + * @param toStoreView + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + */ + private void applyLayout(final DDiagram diagram, final Diagram toStoreView, final EditPartViewer editPartViewer) { + // We don't apply layout on diagram but only on its node children (the + // edge is applied during source node). + for (final AbstractDNode node : Iterables.filter(diagram.getOwnedDiagramElements(), AbstractDNode.class)) { + final Node gmfNode = SiriusGMFHelper.getGmfNode(node); + if (gmfNode != null) { + applyLayout(node, gmfNode, editPartViewer, null); + } + } + } + + /** + * @param sourceNode + * @param editPartViewer + */ + private void applyLayoutToOutgoingEdge(final EdgeTarget sourceNode, final EditPartViewer editPartViewer) { + for (final DEdge edge : sourceNode.getOutgoingEdges()) { + final Edge gmfEdge = SiriusGMFHelper.getGmfEdge(edge); + if (gmfEdge != null) { + applyLayout(edge, gmfEdge, editPartViewer); + } + } + } + + /** + * @param edge + * @param gmfEdge + * @param editPartViewer + */ + private void applyLayout(final DEdge edge, final Edge gmfEdge, final EditPartViewer editPartViewer) { + final EdgeLayoutData layoutData = (EdgeLayoutData) getLayoutData(createKey(edge)); + if (layoutData != null) { + + final Bendpoints bendpoints = convertPointsToGMFBendpoint(layoutData); + gmfEdge.setBendpoints(bendpoints); + + if (layoutData.getSourceTerminal() != null) { + if (gmfEdge.getSourceAnchor() == null) { + gmfEdge.setSourceAnchor(NotationFactory.eINSTANCE.createIdentityAnchor()); + } + if (gmfEdge.getSourceAnchor() instanceof IdentityAnchor) { + ((IdentityAnchor) gmfEdge.getSourceAnchor()).setId(layoutData.getSourceTerminal()); + } + } else if (gmfEdge.getSourceAnchor() instanceof IdentityAnchor) { + gmfEdge.setSourceAnchor(null); + } + if (layoutData.getTargetTerminal() != null) { + if (gmfEdge.getTargetAnchor() == null) { + gmfEdge.setTargetAnchor(NotationFactory.eINSTANCE.createIdentityAnchor()); + } + if (gmfEdge.getTargetAnchor() instanceof IdentityAnchor) { + ((IdentityAnchor) gmfEdge.getTargetAnchor()).setId(layoutData.getTargetTerminal()); + } + } else if (gmfEdge.getTargetAnchor() instanceof IdentityAnchor) { + gmfEdge.setTargetAnchor(null); + } + final RoutingStyle routingStyle = (RoutingStyle) gmfEdge.getStyle(NotationPackage.eINSTANCE.getRoutingStyle()); + if (routingStyle != null) { + routingStyle.setRouting(Routing.get(layoutData.getRouting())); + routingStyle.setJumpLinkStatus(JumpLinkStatus.get(layoutData.getJumpLinkStatus())); + routingStyle.setJumpLinkType(JumpLinkType.get(layoutData.getJumpLinkType())); + routingStyle.setJumpLinksReverse(layoutData.isReverseJumpLink()); + routingStyle.setSmoothness(Smoothness.get(layoutData.getSmoothness())); + } + + applyLabelLayout(gmfEdge, layoutData); + } + } + + private void applyLabelLayout(final View gmfView, final AbstractLayoutData parentLayoutData) { + if (parentLayoutData != null) { + final Node labelNode = SiriusGMFHelper.getLabelNode(gmfView); + if (parentLayoutData.getLabel() != null && labelNode != null) { + if (!parentLayoutData.getLabel().eIsSet(LayoutdataPackage.eINSTANCE.getNodeLayoutData_Width()) + && !parentLayoutData.getLabel().eIsSet(LayoutdataPackage.eINSTANCE.getNodeLayoutData_Height())) { + Location location = NotationFactory.eINSTANCE.createLocation(); + location.setX(parentLayoutData.getLabel().getLocation().getX()); + location.setY(parentLayoutData.getLabel().getLocation().getY()); + labelNode.setLayoutConstraint(location); + } else { + Bounds bounds = NotationFactory.eINSTANCE.createBounds(); + bounds.setX(parentLayoutData.getLabel().getLocation().getX()); + bounds.setY(parentLayoutData.getLabel().getLocation().getY()); + bounds.setWidth(parentLayoutData.getLabel().getWidth()); + bounds.setHeight(parentLayoutData.getLabel().getHeight()); + labelNode.setLayoutConstraint(bounds); + } + } + } + } + + /** + * @param edgeLayoutData + * The layout data of the edge + * @return + */ + private Bendpoints convertPointsToGMFBendpoint(final EdgeLayoutData edgeLayoutData) { + final RelativeBendpoints result = NotationFactory.eINSTANCE.createRelativeBendpoints(); + + final List<RelativeBendpoint> relativeBendpoints = new LinkedList<RelativeBendpoint>(); + + final Point source = edgeLayoutData.getSourceRefPoint(); + final Point target = edgeLayoutData.getTargetRefPoint(); + + /* source and target may be null if edit part was not created */ + if (source != null && target != null) { + final org.eclipse.draw2d.geometry.Point sourceRefPoint = new org.eclipse.draw2d.geometry.Point(source.getX(), source.getY()); + final org.eclipse.draw2d.geometry.Point targetRefPoint = new org.eclipse.draw2d.geometry.Point(target.getX(), target.getY()); + + for (final Point point : edgeLayoutData.getPointList()) { + final org.eclipse.draw2d.geometry.Point tempPoint = new org.eclipse.draw2d.geometry.Point(point.getX(), point.getY()); + final Dimension s = tempPoint.getDifference(sourceRefPoint); + final Dimension t = tempPoint.getDifference(targetRefPoint); + relativeBendpoints.add(new RelativeBendpoint(s.width, s.height, t.width, t.height)); + } + } + result.setPoints(relativeBendpoints); + + return result; + } + + /** + * Search a layout corresponding to the semantic decorator and applies it to + * the node. Then it applies to it's children and outgoing edges. + * + * @param semanticDecorator + * The semantic decorator to search the corresponding layout + * @param toRestoreView + * Node on which to apply the layout + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + * @parentLayoutData the layout of the parent of <code>toRestoreView<code> + */ + private void applyLayout(final DSemanticDecorator semanticDecorator, final Node toRestoreView, final EditPartViewer editPartViewer, final NodeLayoutData parentLayoutData) { + LayoutDataKey key = createKey(semanticDecorator); + NodeLayoutData layoutData = (NodeLayoutData) getLayoutData(key); + + // If a direct child have the same layout data and key than its parents, + // look in the parent layout data 's children for a child layout data + // with the expected id. + if (parentLayoutData != null && parentLayoutData == layoutData && !StringUtil.isEmpty(key.getId())) { + layoutData = null; + for (NodeLayoutData childLayoutData : parentLayoutData.getChildren()) { + // if many children layout with same id, a choice will not be + // possible; + if (key.getId().equals(childLayoutData.getId())) { + if (layoutData == null) { + layoutData = childLayoutData; + } else { + layoutData = null; + break; + } + } + } + } + + if (layoutData != null) { + final Bounds bounds = NotationFactory.eINSTANCE.createBounds(); + final IGraphicalEditPart graphicalEditPart = (IGraphicalEditPart) editPartViewer.getEditPartRegistry().get(toRestoreView); + Point locationToApply; + boolean isCollapsed = false; + if (graphicalEditPart instanceof AbstractDiagramBorderNodeEditPart) { + // Specific treatment for border node + // Compute absolute location + locationToApply = LayoutDataHelper.INSTANCE.getAbsoluteLocation(layoutData); + // Compute the best location according to other existing + // bordered nodes. + Node parentNode = (Node) toRestoreView.eContainer(); + CanonicalDBorderItemLocator locator = new CanonicalDBorderItemLocator(parentNode, PositionConstants.NSEW); + if (semanticDecorator instanceof DDiagramElement) { + if (new DDiagramElementQuery((DDiagramElement) semanticDecorator).isIndirectlyCollapsed()) { + isCollapsed = true; + locator.setBorderItemOffset(IBorderItemOffsets.COLLAPSE_FILTER_OFFSET); + } else { + locator.setBorderItemOffset(IBorderItemOffsets.DEFAULT_OFFSET); + } + } else { + locator.setBorderItemOffset(IBorderItemOffsets.DEFAULT_OFFSET); + } + final Rectangle rect = new Rectangle(locationToApply.getX(), locationToApply.getY(), layoutData.getWidth(), layoutData.getHeight()); + final org.eclipse.draw2d.geometry.Point realLocation = locator.getValidLocation(rect, toRestoreView, Lists.newArrayList(toRestoreView)); + // Compute the new relative position to the parent + final org.eclipse.draw2d.geometry.Point parentAbsoluteLocation = ((IGraphicalEditPart) graphicalEditPart.getParent()).getFigure().getBounds().getTopLeft().getCopy(); + FigureUtilities.translateToAbsoluteByIgnoringScrollbar(((IGraphicalEditPart) graphicalEditPart.getParent()).getFigure(), parentAbsoluteLocation); + locationToApply.setX(realLocation.x); + locationToApply.setY(realLocation.y); + locationToApply = LayoutDataHelper.INSTANCE.getTranslated(locationToApply, parentAbsoluteLocation.negate()); + } else { + locationToApply = LayoutDataHelper.INSTANCE.getRelativeLocation(layoutData, graphicalEditPart); + + // Apply the location to the figure to, to correctly compute + // the relative location of the children + graphicalEditPart.getFigure().setLocation(new org.eclipse.draw2d.geometry.Point(locationToApply.getX(), locationToApply.getY())); + } + bounds.setX(locationToApply.getX()); + bounds.setY(locationToApply.getY()); + if (isCollapsed) { + Dimension dim = new NodeQuery(toRestoreView).getCollapsedSize(); + bounds.setHeight(dim.height); + bounds.setWidth(dim.width); + } else { + bounds.setHeight(layoutData.getHeight()); + bounds.setWidth(layoutData.getWidth()); + } + toRestoreView.setLayoutConstraint(bounds); + } + if (semanticDecorator instanceof DNode) { + applyLayoutToNodeChildren((DNode) semanticDecorator, editPartViewer, layoutData); + } else if (semanticDecorator instanceof DNodeContainer) { + applyLayoutToNodeContainerChildren((DNodeContainer) semanticDecorator, editPartViewer, layoutData); + } else if (semanticDecorator instanceof DNodeList) { + applyLayoutToNodeListChildren((DNodeList) semanticDecorator, editPartViewer, layoutData); + } else { + logWarnMessage(semanticDecorator); + } + // Deal with the outgoing edges + if (semanticDecorator instanceof EdgeTarget) { + applyLayoutToOutgoingEdge((EdgeTarget) semanticDecorator, editPartViewer); + } + } + + private void logWarnMessage(final DSemanticDecorator semanticDecorator) { + final Class<?> clazz = semanticDecorator.getClass(); + + boolean logWarn = true; + for (final Class<?> exceptionClass : CLASS_EXCEPTIONS) { + if (exceptionClass.isAssignableFrom(clazz)) { + logWarn = false; + break; + } + } + + if (logWarn) { + SiriusDiagramEditorPlugin.getInstance().logWarning("This kind of diagram element (" + semanticDecorator.getClass().getName() + ") is not yet managed by the LayoutDataManager."); + } + } + + /** + * Try to apply a layout to the children of the {@link DNode}. + * + * @param parentNode + * The parent containing children to apply layout on. + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + */ + private void applyLayoutToNodeChildren(final DNode parentNode, final EditPartViewer editPartViewer, final NodeLayoutData layoutData) { + // Restore Bordered nodes + applyLayoutForBorderedNodes(parentNode.getOwnedBorderedNodes(), editPartViewer, layoutData); + // Restore label + final Node gmfNode = SiriusGMFHelper.getGmfNode(parentNode); + applyLabelLayout(gmfNode, layoutData); + } + + /** + * Try to apply a layout to the children of the {@link DNodeContainer}. + * + * @param container + * The parent containing children to apply layout on. + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + */ + private void applyLayoutToNodeContainerChildren(final DNodeContainer container, final EditPartViewer editPartViewer, final NodeLayoutData layoutData) { + // Restore children + for (final DDiagramElement child : container.getOwnedDiagramElements()) { + if (child instanceof AbstractDNode) { + // Search the GMF node corresponding to the child + final Node gmfNode = SiriusGMFHelper.getGmfNode(child); + if (gmfNode != null) { + applyLayout(child, gmfNode, editPartViewer, layoutData); + } + } + } + // Restore Bordered nodes + applyLayoutForBorderedNodes(container.getOwnedBorderedNodes(), editPartViewer, layoutData); + // Restore label + final Node gmfNode = SiriusGMFHelper.getGmfNode(container); + applyLabelLayout(gmfNode, layoutData); + } + + /** + * Try to apply the layout to the bordered nodes. + * + * @param borderedNodes + * The list of bordered nodes to deals with + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + * @param parentLayoutData + * The layoutData of the parent of the borderedNodes + */ + private void applyLayoutForBorderedNodes(EList<DNode> borderedNodes, EditPartViewer editPartViewer, NodeLayoutData parentLayoutData) { + HashMap<Node, NodeLayoutData> nodesWithLayoutDataToApply = Maps.newHashMap(); + HashMap<Node, DSemanticDecorator> nodesWithCoresspondingDSemanticDecorator = Maps.newHashMap(); + // Search each bordered nodes that have layoutData to apply + for (final DNode child : borderedNodes) { + // Search the GMF node corresponding to the child + final Node gmfNode = SiriusGMFHelper.getGmfNode(child); + if (gmfNode != null) { + LayoutDataKey key = createKey(child); + NodeLayoutData layoutData = (NodeLayoutData) getLayoutData(key); + + // If a direct child have the same layout data and key than its + // parents, look in the parent layout data 's children for a + // child layout data with the expected id. + if (parentLayoutData != null && parentLayoutData == layoutData && !StringUtil.isEmpty(key.getId())) { + layoutData = null; + for (NodeLayoutData childLayoutData : parentLayoutData.getChildren()) { + // if many children layout with same id, a choice will + // not be possible + if (key.getId().equals(childLayoutData.getId())) { + if (layoutData == null) { + layoutData = childLayoutData; + } else { + layoutData = null; + break; + } + } + } + } + if (layoutData != null) { + nodesWithLayoutDataToApply.put(gmfNode, layoutData); + nodesWithCoresspondingDSemanticDecorator.put(gmfNode, child); + } + } + } + // Iterate over each bordered nodes which have layout data to apply to + Set<Node> toIgnore = nodesWithLayoutDataToApply.keySet(); + for (Entry<Node, NodeLayoutData> entry : nodesWithLayoutDataToApply.entrySet()) { + Node node = entry.getKey(); + applyLayoutForBorderedNode(nodesWithCoresspondingDSemanticDecorator.get(node), node, editPartViewer, entry.getValue(), toIgnore); + } + } + + /** + * Try to apply the layout to a bordered node. + * + * @param semanticDecorator + * The semantic decorator associated with this Node + * @param toRestoreView + * Node on which to apply the layout + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + * @param layoutData + * the layout to apply on <code>toRestoreView<code> + * @param portsNodesToIgnore + * The list of bordered nodes to ignore in the conflict detection + */ + private void applyLayoutForBorderedNode(final DSemanticDecorator semanticDecorator, final Node toRestoreView, final EditPartViewer editPartViewer, final NodeLayoutData layoutData, + final Set<Node> portsNodesToIgnore) { + final Bounds bounds = NotationFactory.eINSTANCE.createBounds(); + Point locationToApply; + boolean isCollapsed = false; + if (!(toRestoreView.eContainer() instanceof Node)) { + return; + } + Node parentNode = (Node) toRestoreView.eContainer(); + + Object parentGraphicalEditPart = editPartViewer.getEditPartRegistry().get(parentNode); + NodeQuery nodeQuery = new NodeQuery(toRestoreView); + + if (nodeQuery.isBorderedNode() && parentGraphicalEditPart instanceof IGraphicalEditPart) { + // Specific treatment for border node + // Compute absolute location + locationToApply = LayoutDataHelper.INSTANCE.getAbsoluteLocation(layoutData); + // Compute the best location according to other existing + // bordered nodes. + + CanonicalDBorderItemLocator locator = new CanonicalDBorderItemLocator(parentNode, PositionConstants.NSEW); + if (semanticDecorator instanceof DDiagramElement) { + if (new DDiagramElementQuery((DDiagramElement) semanticDecorator).isIndirectlyCollapsed()) { + isCollapsed = true; + locator.setBorderItemOffset(IBorderItemOffsets.COLLAPSE_FILTER_OFFSET); + } else { + locator.setBorderItemOffset(IBorderItemOffsets.DEFAULT_OFFSET); + } + } else { + locator.setBorderItemOffset(IBorderItemOffsets.DEFAULT_OFFSET); + } + + // CanonicalDBorderItemLocator works with absolute GMF parent + // location so we need to translate BorderedNode absolute location + // from Draw2D to GMF. + + Point delta = getGMFDraw2DDelta(parentNode, (IGraphicalEditPart) parentGraphicalEditPart); + final Rectangle rect = new Rectangle(locationToApply.getX() - delta.getX(), locationToApply.getY() - delta.getY(), layoutData.getWidth(), layoutData.getHeight()); + + final org.eclipse.draw2d.geometry.Point realLocation = locator.getValidLocation(rect, toRestoreView, portsNodesToIgnore); + + // Compute the new relative position to the parent + final org.eclipse.draw2d.geometry.Point parentAbsoluteLocation = GMFHelper.getAbsoluteBounds(parentNode).getTopLeft(); + locationToApply.setX(realLocation.x); + locationToApply.setY(realLocation.y); + locationToApply = LayoutDataHelper.INSTANCE.getTranslated(locationToApply, parentAbsoluteLocation.negate()); + + } else { + Object graphicalEditPart = editPartViewer.getEditPartRegistry().get(toRestoreView); + if (graphicalEditPart instanceof IGraphicalEditPart) { + locationToApply = LayoutDataHelper.INSTANCE.getRelativeLocation(layoutData, (IGraphicalEditPart) graphicalEditPart); + // Apply the location to the figure to, to correctly compute + // the relative location of the children + ((GraphicalEditPart) graphicalEditPart).getFigure().setLocation(new org.eclipse.draw2d.geometry.Point(locationToApply.getX(), locationToApply.getY())); + } else { + locationToApply = LayoutdataFactory.eINSTANCE.createPoint(); + } + } + bounds.setX(locationToApply.getX()); + bounds.setY(locationToApply.getY()); + if (isCollapsed) { + Dimension dim = new NodeQuery(toRestoreView).getCollapsedSize(); + bounds.setHeight(dim.height); + bounds.setWidth(dim.width); + } else { + bounds.setHeight(layoutData.getHeight()); + bounds.setWidth(layoutData.getWidth()); + } + + toRestoreView.setLayoutConstraint(bounds); + + if (semanticDecorator instanceof DNode) { + applyLayoutToNodeChildren((DNode) semanticDecorator, editPartViewer, layoutData); + } else if (semanticDecorator instanceof DNodeContainer) { + applyLayoutToNodeContainerChildren((DNodeContainer) semanticDecorator, editPartViewer, layoutData); + } else if (semanticDecorator instanceof DNodeList) { + applyLayoutToNodeListChildren((DNodeList) semanticDecorator, editPartViewer, layoutData); + } else { + logWarnMessage(semanticDecorator); + } + if (semanticDecorator instanceof EdgeTarget) { + applyLayoutToOutgoingEdge((EdgeTarget) semanticDecorator, editPartViewer); + } + } + + private Point getGMFDraw2DDelta(Node parentNode, IGraphicalEditPart parentEditPart) { + + Point delta = LayoutdataFactory.eINSTANCE.createPoint(); + + org.eclipse.draw2d.geometry.Point parentDraw2DAbsoluteLocation = parentEditPart.getFigure().getBounds().getTopLeft().getCopy(); + FigureUtilities.translateToAbsoluteByIgnoringScrollbar(parentEditPart.getFigure(), parentDraw2DAbsoluteLocation); + + org.eclipse.draw2d.geometry.Point parentGMFAbsoluteLocation = GMFHelper.getAbsoluteLocation(parentNode); + delta.setX(parentDraw2DAbsoluteLocation.x - parentGMFAbsoluteLocation.x); + delta.setY(parentDraw2DAbsoluteLocation.y - parentGMFAbsoluteLocation.y); + + return delta; + } + + /** + * Try to apply a layout to the children of the {@link DNodeList}. + * + * @param nodeList + * The parent containing children to apply layout on. + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + */ + private void applyLayoutToNodeListChildren(final DNodeList nodeList, final EditPartViewer editPartViewer, final NodeLayoutData layoutData) { + // Restore Bordered nodes + applyLayoutForBorderedNodes(nodeList.getOwnedBorderedNodes(), editPartViewer, layoutData); + + // Restore label + final Node gmfNode = SiriusGMFHelper.getGmfNode(nodeList); + applyLabelLayout(gmfNode, layoutData); + } + + /** + * Add the layout for the children of a node. + * + * @param parentNode + * The parent of the children + * @param parentLayoutData + * The corresponding layoutData + * @param parentEditPart + * The editPart corresponding to the parent LayoutData + * @param gmfView + * GMF view + * @param discoveredKeys + * The {@link LayoutDataKey} discovered during the current store + * action. + */ + protected void addNodeChildren(final DNode parentNode, final NodeLayoutData parentLayoutData, final IGraphicalEditPart parentEditPart, final View gmfView, Collection<LayoutDataKey> discoveredKeys) { + for (final DNode child : parentNode.getOwnedBorderedNodes()) { + checkDataAndAddChildLayout(parentLayoutData, child, parentEditPart, discoveredKeys); + } + // Add the label layout data (if exists). + addLabelLayoutData(parentLayoutData, gmfView); + } + + /** + * Add a layout (if we have enough information : GMF view and editPart). + * + * @param parentLayoutData + * The parent layout data + * @param child + * The child from which we want to add a new layout + * @param parentSavedEditPart + * The previous saved editPart (corresponds to parentLayoutData) + * @param discoveredKeys + * The {@link LayoutDataKey} discovered during the current store + * action. + */ + protected void checkDataAndAddChildLayout(final NodeLayoutData parentLayoutData, final AbstractDNode child, final IGraphicalEditPart parentSavedEditPart, Collection<LayoutDataKey> discoveredKeys) { + // Search the GMF node corresponding to the child + final Node gmfNode = SiriusGMFHelper.getGmfNode(child); + if (gmfNode != null) { + final IGraphicalEditPart editPart = (IGraphicalEditPart) parentSavedEditPart.getRoot().getViewer().getEditPartRegistry().get(gmfNode); + if (editPart != null) { + addChildLayout(parentLayoutData, child, gmfNode, editPart, discoveredKeys); + } + } + } + + /** + * Add children of the node. + * + * @param container + * The parent of the children + * @param parentLayoutData + * The corresponding layoutData + * @param parentEditPart + * The editPart corresponding to the parent LayoutData + * @param discoveredKeys + * The {@link LayoutDataKey} discovered during the current store + * action. + */ + protected void addNodeContainerChildren(final DNodeContainer container, final NodeLayoutData parentLayoutData, final IGraphicalEditPart parentEditPart, Collection<LayoutDataKey> discoveredKeys) { + for (final DDiagramElement child : container.getOwnedDiagramElements()) { + if (child instanceof AbstractDNode) { + checkDataAndAddChildLayout(parentLayoutData, (AbstractDNode) child, parentEditPart, discoveredKeys); + } + } + for (final DNode child : container.getOwnedBorderedNodes()) { + checkDataAndAddChildLayout(parentLayoutData, child, parentEditPart, discoveredKeys); + } + } + + /** + * Add children of the node. + * + * @param nodeList + * The parent of the children + * @param parentLayoutData + * The corresponding layoutData + * @param parentEditPart + * The editPart corresponding to the parent LayoutData + * @param discoveredKeys + * The {@link LayoutDataKey} discovered during the current store + * action. + */ + protected void addNodeListChildren(final DNodeList nodeList, final NodeLayoutData parentLayoutData, final IGraphicalEditPart parentEditPart, Collection<LayoutDataKey> discoveredKeys) { + for (final DNode child : nodeList.getOwnedBorderedNodes()) { + checkDataAndAddChildLayout(parentLayoutData, child, parentEditPart, discoveredKeys); + } + } + + /** + * Add the child layout of the diagram. + * + * @param diagram + * the diagram + * @param editPart + * The viewer responsible for the current editparts lifecycle + */ + private void addChildLayout(final DDiagram diagram, final IGraphicalEditPart diagramEditPart, final Collection<LayoutDataKey> discoveredKeys) { + + for (final AbstractDNode child : Iterables.filter(diagram.getOwnedDiagramElements(), AbstractDNode.class)) { + // Search the GMF node corresponding to the child + final Node gmfNode = SiriusGMFHelper.getGmfNode(child); + if (gmfNode != null) { + final IGraphicalEditPart editPart = (IGraphicalEditPart) diagramEditPart.getRoot().getViewer().getEditPartRegistry().get(gmfNode); + if (editPart != null) { + addChildLayout(null, child, gmfNode, editPart, discoveredKeys); + } + } + } + } + + /** + * Add a layout. + * + * @param parentLayoutData + * The parent layout data + * @param child + * The child from which we want to add a new layout + * @param gmfNode + * The corresponding GMF node. + * @param editPart + * The editPart corresponding to the new layout + */ + private void addChildLayout(final NodeLayoutData parentLayoutData, final DSemanticDecorator child, final Node gmfNode, final IGraphicalEditPart editPart, + final Collection<LayoutDataKey> discoveredKeys) { + final NodeLayoutData childLayoutData = LayoutDataHelper.INSTANCE.createNodeLayoutData(gmfNode, editPart, parentLayoutData); + if (parentLayoutData != null) { + parentLayoutData.getChildren().add(childLayoutData); + } + + LayoutDataKey childKey = createKey(child); + childLayoutData.setId(childKey.getId()); + + // If the current node have the same key than than one of the previously + // inspected node, the previously computed data might be replaced. It + // could so replaced one of the initially selected parts. + if (!discoveredKeys.contains(childKey)) { + addLayoutData(childKey, childLayoutData); + discoveredKeys.add(childKey); + } else if (parentLayoutData == null) { + // In this case, the same key is used for a root layout data and for + // an other view (child or border of an other view), the root data + // should be stored. + addLayoutData(childKey, childLayoutData); + } + + if (child instanceof DNode) { + addNodeChildren((DNode) child, childLayoutData, editPart, gmfNode, discoveredKeys); + } else if (child instanceof DNodeContainer) { + addNodeContainerChildren((DNodeContainer) child, childLayoutData, editPart, discoveredKeys); + } else if (child instanceof DNodeList) { + addNodeListChildren((DNodeList) child, childLayoutData, editPart, discoveredKeys); + } else { + logWarnMessage(child); + } + if (child instanceof EdgeTarget) { + addOutgoingEdge(childLayoutData, (EdgeTarget) child, editPart.getRoot().getViewer()); + } + } + + /** + * Add outgoing edge of the edgeTarget. + * + * @param parentLayoutData + * The parent layout data + * @param sourceOfEdge + * The DDiagramElement that is the source of the edge + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + */ + protected void addOutgoingEdge(final NodeLayoutData parentLayoutData, final EdgeTarget sourceOfEdge, final EditPartViewer editPartViewer) { + for (final DEdge outgoingEdge : sourceOfEdge.getOutgoingEdges()) { + addEdgeLayoutData(parentLayoutData, outgoingEdge, editPartViewer); + } + } + + /** + * Add edge layout data. + * + * @param parentLayoutData + * The parent layout data + * @param edge + * The DEdge + * @param editPartViewer + * The viewer responsible for the current editparts lifecycle. + */ + protected void addEdgeLayoutData(final NodeLayoutData parentLayoutData, final DEdge edge, final EditPartViewer editPartViewer) { + // Search the GMF edge corresponding to the child + final Edge gmfEdge = SiriusGMFHelper.getGmfEdge(edge); + if (gmfEdge != null) { + final EdgeLayoutData edgeLayoutData = LayoutDataHelper.INSTANCE.createEdgeLayoutData(gmfEdge, (ConnectionEditPart) editPartViewer.getEditPartRegistry().get(gmfEdge)); + if (parentLayoutData != null) { + parentLayoutData.getOutgoingEdges().add(edgeLayoutData); + } + + LayoutDataKey edgeKey = createKey(edge); + edgeLayoutData.setId(edgeKey.getId()); + + // Add the edge layout data. + addLayoutData(edgeKey, edgeLayoutData); + // Add the label layout data (if exists). + addLabelLayoutData(edgeLayoutData, gmfEdge); + } + } + + /** + * Add the layout data of the label of the edge. This layout data sets the + * <code>edgeLabelLayoutData</code> of the {@link EdgeLayoutData}. It's not + * added to the layout data with a key in the manager. + * + * @param parentLayoutData + * The edge layout data + * @param element + * The DEdge + * @param gmfElement + * The edge corresponding view + */ + private void addLabelLayoutData(final AbstractLayoutData parentLayoutData, final View gmfElement) { + final Node labelNode = SiriusGMFHelper.getLabelNode(gmfElement); + if (labelNode != null && parentLayoutData != null) { + final NodeLayoutData labelLayoutData = LayoutDataHelper.INSTANCE.createLabelLayoutData(labelNode); + if (labelNode.getElement() instanceof DSemanticDecorator) { + labelLayoutData.setId(createKey((DSemanticDecorator) labelNode.getElement()).getId()); + } + parentLayoutData.setLabel(labelLayoutData); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/GraphicalHelper.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/GraphicalHelper.java new file mode 100644 index 0000000000..6c96f74251 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/GraphicalHelper.java @@ -0,0 +1,205 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import org.eclipse.draw2d.FigureCanvas; +import org.eclipse.draw2d.FreeformViewport; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.base.Preconditions; + +import org.eclipse.sirius.diagram.tools.api.draw2d.ui.figures.FigureUtilities; + +/** + * Utility class to collect helper methods which deal with GraphicalOrdering but + * which are not part of its API. + * + * @author nlepine + */ +public final class GraphicalHelper { + private GraphicalHelper() { + // Prevent instantiation. + } + + /** + * Get the zoom factor. + * + * @param part + * the current part + * @return the zoom factor + */ + public static double getZoom(IGraphicalEditPart part) { + Preconditions.checkNotNull(part); + double scale = 1.0; + if (part.getRoot() instanceof DiagramRootEditPart) { + DiagramRootEditPart rootEditPart = (DiagramRootEditPart) part.getRoot(); + scale = rootEditPart.getZoomManager().getZoom(); + } + return scale; + } + + /** + * Applied zoom on relative point. + * + * @param part + * the current part + * @param relativePoint + * relative point + */ + public static void appliedZoomOnRelativePoint(IGraphicalEditPart part, Point relativePoint) { + double zoom = getZoom(part); + relativePoint.setLocation((int) (relativePoint.x / zoom), (int) (relativePoint.y / zoom)); + } + + /** + * Set the zoom factor. + * + * @param part + * the current part + * @param scale + * the zoom factor + */ + public static void setZoom(IGraphicalEditPart part, double scale) { + Preconditions.checkNotNull(part); + if (part.getRoot() instanceof DiagramRootEditPart) { + DiagramRootEditPart rootEditPart = (DiagramRootEditPart) part.getRoot(); + rootEditPart.getZoomManager().setZoom(scale); + } + } + + /** + * Returns the difference between the logical origin (0, 0) and the top-left + * point actually visible. This corresponds to how much the scrollbars + * "shift" the diagram. + * + * @param part + * an edit part on the view + * @return the scroll size + */ + public static Point getScrollSize(IGraphicalEditPart part) { + Preconditions.checkNotNull(part); + FreeformViewport viewport = FigureUtilities.getFreeformViewport(part.getFigure()); + if (viewport != null) { + return viewport.getViewLocation(); + } else { + return new Point(0, 0); + } + } + + /** + * . + * + * @param part + * an edit part on the view + * @param scrollPosition + * the scroll size + */ + public static void setScrollSize(IGraphicalEditPart part, Point scrollPosition) { + Preconditions.checkNotNull(part); + // FreeformViewport viewport = + // FigureUtilities.getFreeformViewport(part.getFigure()); + // if (viewport != null) { + // viewport.setLocation(scrollPosition); + // } + if (part.getViewer().getControl() instanceof FigureCanvas) { + // UIThreadRunnable.syncExec(new VoidResult() { + // public void run() { + ((FigureCanvas) part.getViewer().getControl()).scrollTo(scrollPosition.x, scrollPosition.y); + // } + // }); + } + } + + /** + * Converts a point from screen coordinates to logical coordinates. + * + * @param point + * the point to convert. + * @param part + * a part from the diagram. + */ + public static void screen2logical(Point point, IGraphicalEditPart part) { + point.translate(GraphicalHelper.getScrollSize(part)); + point.performScale(1.0d / GraphicalHelper.getZoom(part)); + } + + /** + * Converts a rectangle from screen coordinates to logical coordinates. + * + * @param rect + * the rectangle to convert. + * @param part + * a part from the diagram. + */ + public static void screen2logical(Rectangle rect, IGraphicalEditPart part) { + rect.translate(GraphicalHelper.getScrollSize(part)); + rect.performScale(1.0d / GraphicalHelper.getZoom(part)); + } + + /** + * Converts a dimension from screen coordinates to logical coordinates. + * Dimensions have no defined position, so only the current zoom level is + * take into account, not the scroll state. + * + * @param dim + * the dimension to convert. + * @param part + * a part from the diagram. + */ + public static void screen2logical(Dimension dim, IGraphicalEditPart part) { + dim.performScale(1.0d / GraphicalHelper.getZoom(part)); + } + + /** + * Converts a point from logical coordinates to screen coordinates. + * + * @param point + * the point to convert. + * @param part + * a part from the diagram. + */ + public static void logical2screen(Point point, IGraphicalEditPart part) { + point.performScale(GraphicalHelper.getZoom(part)); + point.translate(GraphicalHelper.getScrollSize(part).negate()); + } + + /** + * Converts a rectangle from logical coordinates to screen coordinates. + * + * @param rect + * the rectangle to convert. + * @param part + * a part from the diagram. + */ + public static void logical2screen(Rectangle rect, IGraphicalEditPart part) { + rect.performScale(GraphicalHelper.getZoom(part)); + rect.translate(GraphicalHelper.getScrollSize(part).negate()); + } + + /** + * Converts a dimension from logical coordinates to screen coordinates. + * Dimensions have no defined position, so only the current zoom level is + * take into account, not the scroll state. + * + * @param dim + * the dimension to convert. + * @param part + * a part from the diagram. + */ + public static void logical2Screen(Dimension dim, IGraphicalEditPart part) { + dim.performScale(GraphicalHelper.getZoom(part)); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ILayoutDataManagerProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ILayoutDataManagerProvider.java new file mode 100644 index 0000000000..74038efd14 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ILayoutDataManagerProvider.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import org.eclipse.sirius.DDiagram; + +/** + * Interface used be the extension point + * <code>org.eclipse.sirius.diagram.layoutDataManager</code> to implements to + * override the default behavior of Copy/Paste layout actions. + * + * @author <a href="mailto:maxime.porhel@obeo.fr">Maxime Porhel</a> + */ +public interface ILayoutDataManagerProvider { + + /** + * Returns <code>true</code> if this provider provides a specific layout + * data manager for the given diagram. + * + * @param diagram + * the current diagram. + * @return <code>true</code> if this provider provides a specific layout + * data manager for the given diagram. + */ + boolean provides(DDiagram diagram); + + /** + * Provides its specific layout data manager. It will be called once. + * + * @return the extension of the refresh mechanism. + */ + SiriusLayoutDataManager getLayoutDataManager(); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutConstants.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutConstants.java new file mode 100644 index 0000000000..0315138b85 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutConstants.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +/** + * This interface is used to keep track of the different constants available in + * the layout plug-in. + * + * @author cbrun + */ +public interface LayoutConstants { + /** + * The lowest priority. + */ + int LOWEST_PRIORITY = 20; + + /** + * A low priority. + */ + int LOW_PRIORITY = 10; + + /** + * A normal priority. + */ + int NORMAL_PRIORITY = 5; + + /** + * A high priority. + */ + int HIGH_PRIORITY = 1; + + /** + * The highest priority. + */ + int HIGHEST_PRIORITY = 0; +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutDataHelper.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutDataHelper.java new file mode 100644 index 0000000000..c8344dfe47 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutDataHelper.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import java.util.Map; + +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.Node; + +import org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData; +import org.eclipse.sirius.diagram.layoutdata.EdgeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.NodeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.Point; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.LayoutDataHelperImpl; + +/** + * Helper to manage the layout data. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ +public interface LayoutDataHelper { + /** + * The singleton instance of the LayoutDataHelper. + */ + LayoutDataHelper INSTANCE = new LayoutDataHelperImpl(); + + /** + * Create a node layoutData. + * + * @param gmfNode + * The corresponding GMF view + * @param editPart + * The corresponding editPart + * @param parentLayoutData + * The parent layout data + * @return a new NodeLayoutData + */ + NodeLayoutData createNodeLayoutData(Node gmfNode, IGraphicalEditPart editPart, NodeLayoutData parentLayoutData); + + /** + * Create an edge layoutData with the information of edge. + * + * @param gmfEdge + * The corresponding GMF view + * @param connectionEditPart + * The corresponding edit part + * @return a new NodeLayoutData + */ + EdgeLayoutData createEdgeLayoutData(Edge gmfEdge, ConnectionEditPart connectionEditPart); + + /** + * Create a label edge layoutData with the location of the label (the width + * and height of this {@link NodeLayoutData} are not set). + * + * @param labelNode + * the corresponding GMF view. + * @return a new NodeLayoutData + */ + NodeLayoutData createLabelLayoutData(Node labelNode); + + /** + * Compute the absolute location of the <code>nodeLayoutData</code>.<BR> + * Add recursively the location of its parent. + * + * @param nodeLayoutData + * The concern nodeLayoutData + * @return The absolute location + */ + Point getAbsoluteLocation(NodeLayoutData nodeLayoutData); + + /** + * Compute the relative location of the <code>nodeLayoutData</code> to the + * figure of the edit part.<BR> + * + * @param layoutData + * The concern nodeLayoutData + * @param editPart + * The corresponding edit part + * @return The relative location + */ + Point getRelativeLocation(NodeLayoutData layoutData, IGraphicalEditPart editPart); + + /** + * Creates a new Point which is translated by the values of the provided + * Point. + * + * @param originalPoint + * The point to translate. + * @param pt + * Point which provides the translation amounts. + * @return A new Point + */ + Point getTranslated(Point originalPoint, org.eclipse.draw2d.geometry.Point pt); + + /** + * Filter collection to get only root layout data. + * + * @param collection + * Collection to filter. + * @return Filtered collection. + */ + Map<? extends LayoutDataKey, ? extends AbstractLayoutData> getRootLayoutData(Map<? extends LayoutDataKey, ? extends AbstractLayoutData> collection); + + /** + * Create key from node layout data. + * + * @param layoutData + * Layout data. + * @return Created key. + */ + LayoutDataKey createKey(AbstractLayoutData layoutData); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutDataKey.java new file mode 100644 index 0000000000..bc56d6f610 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutDataKey.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +/** + * Interface for all kind of key use to store layoutData ( + * {@link org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData}. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ +public interface LayoutDataKey { + + /** + * Get the ID of this key. + * + * @return The ID of this key. + */ + String getId(); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutExtender.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutExtender.java new file mode 100644 index 0000000000..1487f48623 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutExtender.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import java.util.Hashtable; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ListItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider; + +/** + * Class providing extended service for {@link ExtendableLayoutProvider}s + * implementing the {@link ExtendableLayoutProvider} contract. + * + * @author cbrun + * + */ +public class LayoutExtender { + + private final Map<IGraphicalEditPart, Rectangle> updatedLocations = new WeakHashMap<IGraphicalEditPart, Rectangle>(); + + private final ExtendableLayoutProvider layouter; + + /** + * Create a new extender. + * + * @param layouter + * the layouter to extend. + */ + public LayoutExtender(final ExtendableLayoutProvider layouter) { + this.layouter = layouter; + } + + /** + * return all the updated bounds for each edit parts. + * + * @return all the updated bounds for each edit parts. + */ + public Map<IGraphicalEditPart, Rectangle> getUpdatedBounds() { + return updatedLocations; + } + + /** + * Notify the extender that edit parts layout is starting. + */ + public void startLayouting() { + this.updatedLocations.clear(); + + } + + /** + * Filter the relevant connections. + * + * @param editPartToNodeDict + * dict of edit parts. + * @param list + * list to filter. + * @return filtered list of connections. + */ + public List getRelevantConnections(final Hashtable editPartToNodeDict, final List list) { + final Iterator iterConnections = list.iterator(); + final boolean shouldHandleListItems = layouter.handleConnectableListItems(); + while (iterConnections.hasNext()) { + final Object next = iterConnections.next(); + if (next instanceof ConnectionEditPart) { + final ConnectionEditPart poly = (ConnectionEditPart) next; + EditPart from = poly.getSource(); + EditPart to = poly.getTarget(); + if (from instanceof IBorderItemEditPart) { + from = from.getParent(); + } else if (shouldHandleListItems && from instanceof ListItemEditPart) { + from = getFirstAnscestorinNodesMap(from, editPartToNodeDict); + } + if (to instanceof IBorderItemEditPart) { + to = to.getParent(); + } else if (shouldHandleListItems && to instanceof ListItemEditPart) { + to = getFirstAnscestorinNodesMap(to, editPartToNodeDict); + } + if (from == null || to == null) { + iterConnections.remove(); + } + } + } + return list; + } + + /** + * Called to keep track of every location change. + * + * @param nodes + * nodes to change. + * @param diff + * diff with the original location. + */ + public void keepLocationChanges(final List nodes, final Point diff) { + for (Node node : Iterables.filter(nodes, Node.class)) { + if (node.data instanceof ShapeEditPart) { + final IGraphicalEditPart gep = (IGraphicalEditPart) node.data; + final Rectangle intrinsicBounds = gep.getFigure().getBounds(); + final Rectangle nodeExt = layouter.provideNodeMetrics(node); + final Rectangle newBounds = new Rectangle(nodeExt.x + diff.x, nodeExt.y + diff.y, intrinsicBounds.width, intrinsicBounds.height); + updatedLocations.put(gep, newBounds); + } + } + } + + /** + * Filter edges for layouting. + * + * @param selectedObjects + * objects to layout. + * @param editPartToNodeDict + * dict of editparts to nodes. + * @return the filtered list of elements to layout. + */ + public List filterEdges(final List selectedObjects, final Map editPartToNodeDict) { + final List tmp = new LinkedList(selectedObjects); + final Iterator iterConnections = tmp.iterator(); + final boolean shouldHandleListItems = layouter.handleConnectableListItems(); + while (iterConnections.hasNext()) { + final Object next = iterConnections.next(); + if (next instanceof ConnectionEditPart) { + final ConnectionEditPart poly = (ConnectionEditPart) next; + EditPart from = poly.getSource(); + EditPart to = poly.getTarget(); + if (from instanceof IBorderItemEditPart) { + from = from.getParent(); + } else if (shouldHandleListItems && from instanceof ListItemEditPart) { + from = getFirstAnscestorinNodesMap(from, editPartToNodeDict); + } + if (to instanceof IBorderItemEditPart) { + to = to.getParent(); + } else if (shouldHandleListItems && to instanceof ListItemEditPart) { + to = getFirstAnscestorinNodesMap(to, editPartToNodeDict); + } + if (from == null || to == null) { + iterConnections.remove(); + } + } + } + return tmp; + } + + private EditPart getFirstAnscestorinNodesMap(final EditPart editPart, final Map editPartToNodeDict) { + EditPart ancestor = editPart; + while (ancestor != null) { + if (editPartToNodeDict.get(ancestor) != null) { + return ancestor; + } + ancestor = ancestor.getParent(); + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutUtils.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutUtils.java new file mode 100644 index 0000000000..50f8f3522d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/LayoutUtils.java @@ -0,0 +1,614 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.gmf.runtime.common.core.util.Proxy; +import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint; +import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.requests.CreateViewRequest; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.notation.Anchor; +import org.eclipse.gmf.runtime.notation.Bendpoints; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.RoutingStyle; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Shell; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.DRepresentation; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.DStylizable; +import org.eclipse.sirius.business.api.dialect.DialectManager; +import org.eclipse.sirius.business.api.session.CustomDataConstants; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.diagram.business.api.query.ViewQuery; +import org.eclipse.sirius.diagram.business.internal.query.DNodeQuery; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerViewNodeContainerCompartment2EditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerViewNodeContainerCompartmentEditPart; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.BorderItemLocatorProvider; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IStyleConfigurationRegistry; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.StyleConfiguration; +import org.eclipse.sirius.diagram.tools.api.part.DiagramEditPartService; +import org.eclipse.sirius.diagram.tools.internal.graphical.edit.DiagramCreationUtil; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.LayoutUtil; + +/** + * Useful operations. + * + * @author ymortier + */ +public final class LayoutUtils { + + /** + * Default width. + */ + public static final int DEFAULT_WIDTH = 10; + + /** + * Scale factor for width and height from diagram node size to draw2d + * bounds. + */ + public static final int SCALE = 10; + + /** + * The default container dimension. + */ + public static final Dimension DEFAULT_CONTAINER_DIMENSION = new Dimension(150, 70); + + private static final int TOP_MARGIN = 50; + + private static final int LEFT_MARGIN = 50; + + private static Map<IFigure, List<IFigure>> dummys = new HashMap<IFigure, List<IFigure>>(); + + private static Map<IFigure, Integer> dummysStack = new HashMap<IFigure, Integer>(); + + /** + * Avoid instantiation. + */ + private LayoutUtils() { + + } + + /** + * Return a valid location for the specified border item view. + * + * @param borderView + * the border item view. + * @param proposedLocation + * the proposed location. + * @param proposedDimension + * the proposed dimension. + * @param owner + * the owner. + * @param borderItemContainer + * the container figure. + * @param mainFigure + * the figure that contains border items. + * @return a valid location for the specified border item view. + */ + public static Rectangle getValidLocation(final DDiagramElement borderView, final Point proposedLocation, final Dimension proposedDimension, final DDiagramElement owner, + final IFigure borderItemContainer, final IFigure mainFigure) { + if (mainFigure.getBounds().getSize().height == 0 && mainFigure.getBounds().getSize().width == 0 && owner instanceof DNode) { + final Dimension defaultDimension = new DNodeQuery((DNode) owner).getDefaultDimension(); + mainFigure.getBounds().height = defaultDimension.height; + mainFigure.getBounds().width = defaultDimension.width; + } else if (mainFigure.getBounds().getSize().height == 0 && mainFigure.getBounds().getSize().width == 0 && owner instanceof DDiagramElementContainer) { + final Dimension defaultDimension = DEFAULT_CONTAINER_DIMENSION; + mainFigure.getBounds().height = defaultDimension.height; + mainFigure.getBounds().width = defaultDimension.width; + } + final StyleConfiguration styleConfiguration = IStyleConfigurationRegistry.INSTANCE.getStyleConfiguration(owner.getDiagramElementMapping(), ((DStylizable) owner).getStyle()); + final BorderItemLocatorProvider locatorProvider = styleConfiguration.getBorderItemLocatorProvider(); + final IFigure dummyFigure = new Figure(); + final IBorderItemLocator locator = locatorProvider.getBorderItemLocator(mainFigure, owner, borderView); + final Rectangle constraint = new Rectangle(proposedLocation, proposedDimension); + locator.setConstraint(constraint); + dummyFigure.setVisible(true); + borderItemContainer.add(dummyFigure); + final Rectangle rect = new Rectangle(constraint); + rect.translate(mainFigure.getBounds().getLocation().x, mainFigure.getBounds().getLocation().y); + mainFigure.translateToAbsolute(rect); + dummyFigure.setBounds(rect); + final Rectangle realLocation = locator.getValidLocation(rect, dummyFigure); + final Point parentOrigin = mainFigure.getBounds().getTopLeft(); + final Dimension d = realLocation.getTopLeft().getDifference(parentOrigin); + final Point location = new Point(d.width, d.height); + realLocation.setLocation(location); + + // dummy + final List<IFigure> dummysList = dummys.get(borderItemContainer); + if (dummysList != null) { + dummysList.add(dummyFigure); + } + + locator.relocate(dummyFigure); + realLocation.setLocation(dummyFigure.getBounds().getLocation()); + realLocation.setSize(proposedDimension); + + return new Rectangle(realLocation).getTranslated(-parentOrigin.x, -parentOrigin.y); + + } + + /** + * Indicates that the figure will receive dummy figures. + * + * @param figure + * the figure. + */ + public static void prepareFigureForDummyAdds(final IFigure figure) { + if (!dummys.containsKey(figure)) { + dummys.put(figure, new LinkedList<IFigure>()); + } else { + Integer currentValue = dummysStack.get(figure); + if (currentValue == null) { + currentValue = Integer.valueOf(0); + } + currentValue = Integer.valueOf(currentValue.intValue() + 1); + dummysStack.put(figure, currentValue); + } + } + + /** + * Remove all children that has been added since the last call of + * {@link #prepareFigureForDummyAdds(IFigure)} for the specified figure. + * + * @param figure + * the figure. + */ + public static void releaseDummys(final IFigure figure) { + if (dummysStack.containsKey(figure)) { + Integer currentValue = dummysStack.get(figure); + currentValue = Integer.valueOf(currentValue.intValue() - 1); + if (currentValue.intValue() == 0) { + dummysStack.remove(figure); + } else { + dummysStack.put(figure, currentValue); + } + } else { + Collection<IFigure> dummysChildren = dummys.get(figure); + if (dummysChildren == null) { + dummysChildren = Collections.emptyList(); + } + final Iterator<IFigure> iterRemove = dummysChildren.iterator(); + while (iterRemove.hasNext()) { + final IFigure next = iterRemove.next(); + figure.remove(next); + } + dummys.remove(figure); + } + } + + /** + * Return the default dimension according to the specified descriptor. + * + * @param viewDescriptor + * the descriptor. + * @return the default dimension according to the specified descriptor. + */ + public static Dimension getDefaultDimension(final CreateViewRequest.ViewDescriptor viewDescriptor) { + Dimension result = new Dimension(-1, -1); + final IAdaptable adapt = viewDescriptor.getElementAdapter(); + if (adapt instanceof Proxy) { + final Object element = ((Proxy) adapt).getRealObject(); + if (element instanceof DNode) { + result = new DNodeQuery((DNode) element).getDefaultDimension(); + } + } + return result; + } + + /** + * Initializes the layout of the diagram of <code>target</code> with the + * layout of the the diagram <code>source</code>. + * + * @param source + * the source diagram. + * @param target + * the target diagram. + */ + public static void initializeDiagramLayout(final Diagram source, final DRepresentation target) { + // + // Do refresh + DialectManager.INSTANCE.refresh(target, new NullProgressMonitor()); + + final Shell shell = new Shell(); + // + // Initializes diagram. + final DiagramEditPart diagramEditPart = LayoutUtils.createDiagramEditPart(target, shell); + final Diagram gmfTarget; + if (diagramEditPart == null) { + gmfTarget = null; + } else { + gmfTarget = diagramEditPart.getDiagramView(); + } + // + // dispose ui resources. + if (diagramEditPart != null) { + diagramEditPart.deactivate(); + } + + Display.getCurrent().asyncExec(new Runnable() { + public void run() { + shell.dispose(); + } + }); + // + // Maps a real semantic element with only one view. + final Map<EObject, List<View>> realSemanticToView = new HashMap<EObject, List<View>>(); + // + // initializes view bounds + if (gmfTarget != null) { + final List<View> sourceCandidates = new LinkedList<View>(); + LayoutUtils.computeSourceCandidates(source, sourceCandidates); + sourceCandidates.addAll(source.getEdges()); + LayoutUtils.initializeBounds(source, gmfTarget, realSemanticToView, sourceCandidates); + for (final Edge edge : (Iterable<Edge>) gmfTarget.getEdges()) { + LayoutUtils.initializeBounds(source, edge, realSemanticToView, sourceCandidates); + } + LayoutUtils.optimizeLayout(gmfTarget); + LayoutUtils.moveToUpperLeftCorner(gmfTarget); + } + } + + private static void moveToUpperLeftCorner(final View gmfTarget) { + final Dimension minDistanceToUpperLeftCorner = new Dimension(-1, -1); + for (final View view : (Iterable<View>) gmfTarget.getChildren()) { + if (view instanceof Node) { + final Node node = (Node) view; + if (node.getLayoutConstraint() instanceof Location) { + final Location location = (Location) node.getLayoutConstraint(); + minDistanceToUpperLeftCorner.width = minDistanceToUpperLeftCorner.width < 0 || location.getX() < minDistanceToUpperLeftCorner.width ? location.getX() + : minDistanceToUpperLeftCorner.width; + minDistanceToUpperLeftCorner.height = minDistanceToUpperLeftCorner.height < 0 || location.getY() < minDistanceToUpperLeftCorner.height ? location.getY() + : minDistanceToUpperLeftCorner.height; + } + } + } + final Dimension delta = new Dimension(0, 0); + if (minDistanceToUpperLeftCorner.width > LEFT_MARGIN) { + delta.width = minDistanceToUpperLeftCorner.width - LEFT_MARGIN; + } + if (minDistanceToUpperLeftCorner.height > TOP_MARGIN) { + delta.height = minDistanceToUpperLeftCorner.height - TOP_MARGIN; + } + + if (delta.height > 0 || delta.width > 0) { + for (final View view : (Iterable<View>) gmfTarget.getChildren()) { + if (view instanceof Node) { + final Node node = (Node) view; + if (node.getLayoutConstraint() instanceof Location) { + final Location location = (Location) node.getLayoutConstraint(); + location.setX(location.getX() - delta.width); + location.setY(location.getY() - delta.height); + } + } + } + } + + } + + private static void computeSourceCandidates(final View source, final List<View> candidates) { + final EObject gmfPOVSemanticElement = ViewUtil.resolveSemanticElement(source); + if (gmfPOVSemanticElement instanceof DSemanticDecorator && gmfPOVSemanticElement instanceof DDiagramElement) { + candidates.add(source); + } + for (final View child : (Iterable<View>) source.getChildren()) { + LayoutUtils.computeSourceCandidates(child, candidates); + } + } + + private static void initializeBounds(final Diagram source, final View target, final Map<EObject, List<View>> realSemanticToView, final List<View> sourceCandidates) { + final EObject gmfPOVSemanticElement = ViewUtil.resolveSemanticElement(target); + if (gmfPOVSemanticElement instanceof DDiagramElement && gmfPOVSemanticElement instanceof DSemanticDecorator) { + final View sourceView = LayoutUtils.findSourceView(target, realSemanticToView, sourceCandidates); + if (sourceView != null) { + LayoutUtils.copyConstraints(sourceView, target); + } + } + for (final View child : (Iterable<View>) target.getChildren()) { + LayoutUtils.initializeBounds(source, child, realSemanticToView, sourceCandidates); + } + } + + /** + * Copy constraints of source view and affect them to target view. + * + * @param sourceView + * the source view + * @param targetView + * the target view + */ + public static void copyConstraints(final View sourceView, final View targetView) { + if (sourceView instanceof Node && targetView instanceof Node) { + final Node nodeSource = (Node) sourceView; + final Node nodeTarget = (Node) targetView; + final LayoutConstraint sourceConstraint = nodeSource.getLayoutConstraint(); + if (sourceConstraint != null) { + nodeTarget.setLayoutConstraint((LayoutConstraint) EcoreUtil.copy(sourceConstraint)); + } + } else if (sourceView instanceof Edge && targetView instanceof Edge) { + final Edge edgeSource = (Edge) sourceView; + final Edge edgeTarget = (Edge) targetView; + if (edgeSource.getBendpoints() != null) { + edgeTarget.setBendpoints((Bendpoints) EcoreUtil.copy(edgeSource.getBendpoints())); + } + if (edgeSource.getSourceAnchor() != null) { + edgeTarget.setSourceAnchor((Anchor) EcoreUtil.copy(edgeSource.getSourceAnchor())); + } + if (edgeSource.getTargetAnchor() != null) { + edgeTarget.setTargetAnchor((Anchor) EcoreUtil.copy(edgeSource.getTargetAnchor())); + } + final RoutingStyle rstyle = (RoutingStyle) edgeSource.getStyle(NotationPackage.eINSTANCE.getRoutingStyle()); + if (rstyle != null) { + edgeTarget.getStyles().add(EcoreUtil.copy(rstyle)); + } + } + } + + private static View findSourceView(final View target, final Map<EObject, List<View>> realSemanticToView, final List<View> sourceCandidates) { + View result = null; + final EObject gmfPOVSemanticElement = ViewUtil.resolveSemanticElement(target); + if (gmfPOVSemanticElement instanceof DDiagramElement && gmfPOVSemanticElement instanceof DSemanticDecorator) { + final EObject targetRealSemanticElement = ((DSemanticDecorator) gmfPOVSemanticElement).getTarget(); + List<View> views = realSemanticToView.get(targetRealSemanticElement); + if (views == null) { + views = new ArrayList<View>(1); + } + // Search if one a the view is similar with target + for (final View sourceView : views) { + if (LayoutUtils.areSimilars(sourceView, target)) { + result = sourceView; + break; + } + } + // If any, search in the sourceCandidates + if (result == null) { + for (final View candidate : sourceCandidates) { + if (LayoutUtils.areSimilars(candidate, target)) { + result = candidate; + break; + } + } + if (result != null) { + sourceCandidates.remove(result); + views.add(result); + realSemanticToView.put(targetRealSemanticElement, views); + } + } + + // if (result == null && + // !realSemanticToView.containsKey(targetRealSemanticElement)) { + // for (final View candidate : sourceCandidates) { + // if (areSimilars(candidate, target)) { + // result = candidate; + // break; + // } + // } + // if (result != null) { + // sourceCandidates.remove(result); + // views.add(result); + // realSemanticToView.put(targetRealSemanticElement, views); + // } + // } + } + return result; + } + + private static DiagramEditPart createDiagramEditPart(final DRepresentation designerDiagram, final Shell shell) { + if (designerDiagram instanceof DSemanticDiagram) { + final DSemanticDiagram diag = (DSemanticDiagram) designerDiagram; + + final DiagramCreationUtil util = new DiagramCreationUtil(diag); + if (!util.findAssociatedGMFDiagram()) { + util.createNewGMFDiagram(); + SessionManager.INSTANCE.getSession(diag.getTarget()).getServices().putCustomData(CustomDataConstants.GMF_DIAGRAMS, diag, util.getAssociatedGMFDiagram()); + } + + final Diagram gmfDiag = util.getAssociatedGMFDiagram(); + + if (gmfDiag != null) { + final DiagramEditPartService tool = new DiagramEditPartService(); + final DiagramEditPart diagramEditPart = tool.createDiagramEditPart(gmfDiag, shell, new PreferencesHint("DView")); + diagramEditPart.refresh(); + + // performs an arrange all for canonical views. + LayoutUtil.arrange(diagramEditPart); + + // validate to have all nodes in the right position + // flush the viewer to have all connections and ports + return diagramEditPart; + } + } + return null; + } + + private static boolean areSimilars(final View source, final View target) { + boolean result = false; + // Check if the view type is the same + if (source != null && target != null && source.eClass() == target.eClass()) { + // Check if the representation is the same + final EObject representationSource = ViewUtil.resolveSemanticElement(source); + final EObject representationTarget = ViewUtil.resolveSemanticElement(target); + + if (representationSource != null && representationTarget != null && representationSource.eClass() == representationTarget.eClass()) { + // Check if the semantic target is the same + if (representationSource instanceof DSemanticDecorator && representationTarget instanceof DSemanticDecorator) { + final EObject semSource = ((DSemanticDecorator) representationSource).getTarget(); + final EObject semTarget = ((DSemanticDecorator) representationTarget).getTarget(); + + if (semSource != null && semSource == semTarget) { + // Check if the source and the target is + boolean sourceIsLabel = false; + boolean targetIsLabel = false; + try { + sourceIsLabel = new ViewQuery(source).isForNameEditPart(); + } catch (final NumberFormatException e) { + // do nothing + } + try { + targetIsLabel = new ViewQuery(target).isForNameEditPart(); + } catch (final NumberFormatException e) { + // do nothing + } + // Test if the source and the target is the same type + // (label or not) + result = sourceIsLabel == targetIsLabel; + } + } + } + } + return result; + } + + /** + * Optimize the layout of the view. + * + * @param view + * the view to optimize. + */ + private static void optimizeLayout(final View view) { + for (final Object object : view.getChildren()) { + if (object instanceof View) { + LayoutUtils.optimizeLayout((View) object); + } + } + final EObject gmfPOVSemanticElement = ViewUtil.resolveSemanticElement(view); + if (gmfPOVSemanticElement instanceof DNodeContainer) { + LayoutUtils.optimizeContainerLayout(view); + } + } + + /** + * Optimize the layout of this container. + * + * @param view + * the container to optimize. + */ + private static void optimizeContainerLayout(final View view) { + + final Bounds containerBounds; + if (view instanceof Node && ((Node) view).getLayoutConstraint() instanceof Bounds) { + containerBounds = (Bounds) ((Node) view).getLayoutConstraint(); + } else { + containerBounds = null; + } + + if (containerBounds != null) { + + // + // Gets the size of the container + final Dimension minSize = new Dimension(-1, -1); + // + // Gets the compartment. + View compartment = null; + for (final Object object : view.getChildren()) { + if (object instanceof View) { + final View child = (View) object; + final int id; + try { + id = Integer.parseInt(child.getType()); + if (id == DNodeContainerViewNodeContainerCompartment2EditPart.VISUAL_ID || id == DNodeContainerViewNodeContainerCompartmentEditPart.VISUAL_ID) { + compartment = child; + } + } catch (final NumberFormatException nfe) { + // silent. + } + } + } + + if (compartment != null) { + LayoutUtils.moveToUpperLeftCorner(compartment); + for (final Object object : compartment.getChildren()) { + if (object instanceof Node) { + final Node child = (Node) object; + if (child.getLayoutConstraint() instanceof Bounds) { + final Bounds bounds = (Bounds) child.getLayoutConstraint(); + final int height = bounds.getY() + bounds.getHeight() + TOP_MARGIN; + final int width = bounds.getWidth() + bounds.getX() + LEFT_MARGIN; + minSize.width = minSize.width < 0 || width > minSize.width ? width : minSize.width; + minSize.height = minSize.height < 0 || height > minSize.height ? height : minSize.height; + } + } + } + } + + final Dimension delta = new Dimension(0, 0); + + int height = containerBounds.getHeight(); + if (height != -1 && minSize.height > height) { + delta.height = minSize.height - height; + containerBounds.setHeight(minSize.height); + } + int width = containerBounds.getWidth(); + if (width != -1 && minSize.width > width) { + delta.width = minSize.width - width; + containerBounds.setWidth(minSize.width); + } + + if (delta.width > 0 || delta.height > 0) { + final View containerView = ViewUtil.getContainerView(view); + if (containerView != null) { + for (final Object object : containerView.getChildren()) { + if (object instanceof Node) { + final Node child = (Node) object; + LayoutUtils.moveChild(containerBounds, delta, child); + } + } + } + } + + } + + } + + private static void moveChild(final Bounds containerBounds, final Dimension delta, final Node child) { + if (child.getLayoutConstraint() instanceof Location) { + final Location location = (Location) child.getLayoutConstraint(); + if (location.getX() > containerBounds.getX() && delta.width > 0) { + location.setX(location.getX() + delta.width); + } + if (location.getY() > containerBounds.getY() && delta.height > 0) { + location.setY(location.getY() + delta.height); + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/PinHelper.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/PinHelper.java new file mode 100644 index 0000000000..a507a0a605 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/PinHelper.java @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.emf.ecore.EObject; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; + +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.ArrangeConstraint; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.business.api.diagramtype.DiagramTypeDescriptorRegistry; +import org.eclipse.sirius.business.api.diagramtype.IDiagramTypeDescriptor; +import org.eclipse.sirius.business.internal.query.DDiagramElementContainerExperimentalQuery; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; + +/** + * Helper class to test and manipulate the "pinned" status of + * {@link DDiagramElement}. + * + * @author pcdavid + * + * @since 4.0 + */ +public final class PinHelper { + + /** + * Set of {@link ArrangeConstraint} to specify pinned + * {@link DDiagramElement}. + */ + public static final Collection<ArrangeConstraint> PINNED_CONSTRAINTS = new ArrayList<ArrangeConstraint>(); + + static { + PINNED_CONSTRAINTS.add(ArrangeConstraint.KEEP_LOCATION); + PINNED_CONSTRAINTS.add(ArrangeConstraint.KEEP_SIZE); + PINNED_CONSTRAINTS.add(ArrangeConstraint.KEEP_RATIO); + } + + /** + * Get the pinned status of the {@link DDiagramElement} associated to this + * {@link IDiagramElementEditPart} <code>self</code>. + * + * @param self + * the edit part from which to test the pinned status of the + * associated {@link DDiagramElement}. + * + * @return <code>true</code> if the associated {@link DDiagramElement} is + * pinned, false else. + * + * @see {@link PinHelper#isPinned(DDiagramElement)} + */ + public boolean isPinned(final IDiagramElementEditPart self) { + boolean isPinned = false; + EObject diagramElement = self.resolveDiagramElement(); + if (diagramElement instanceof DDiagramElement) { + DDiagramElement dDiagramElement = (DDiagramElement) diagramElement; + isPinned = isPinned(dDiagramElement); + } + return isPinned; + } + + /** + * Get the pinned status of the {@link DDiagramElement} + * <code>dDiagramElement</code>. The pinned status is defined by having the + * following {@link ArrangeConstraint} through + * {@link AbstractDNode#getArrangeConstraints()} or + * {@link DEdge#getArrangeConstraints()} : + * + * <ol> + * <li> + * {@link ArrangeConstraint#KEEP_LOCATION}</li> + * <li> + * {@link ArrangeConstraint#KEEP_SIZE}</li> + * <li> + * {@link ArrangeConstraint#KEEP_RATIO}</li> + * </ol> + * + * @param dDiagramElement + * the {@link DDiagramElement} from which to test the pinned + * status. + * + * @return <code>true</code> if the associated {@link DDiagramElement} is + * pinned, false else. + */ + public boolean isPinned(final DDiagramElement dDiagramElement) { + boolean isPinned = false; + List<ArrangeConstraint> constraints = getArrangeConstraints(dDiagramElement); + isPinned = constraints != null && constraints.containsAll(PINNED_CONSTRAINTS); + return isPinned; + } + + /** + * Mark a {@link DDiagramElement} as pinned if possible (depending on its + * actual type : {@link AbstractDNode} or {@link DEdge}). + * + * @param dDiagramElement + * the {@link DDiagramElement} to pin + * + * @return <code>true</code> if <code>dDiagramElement</code> has been + * pinned, <code>false</code> if it could not + */ + public boolean markAsPinned(DDiagramElement dDiagramElement) { + boolean pinned = false; + List<ArrangeConstraint> constraints = getArrangeConstraints(dDiagramElement); + if (constraints != null) { + constraints.addAll(PINNED_CONSTRAINTS); + pinned = true; + } + return pinned; + } + + /** + * Mark a {@link DDiagramElement} as un-pinned if possible (depending on its + * actual type : {@link AbstractDNode} or {@link DEdge}). + * + * @param dDiagramElement + * the {@link DDiagramElement} to un-pin + * + * @return <code>true</code> if the <code>dDiagramElement</code> was + * un-pinned, <code>false</code> if it could not + */ + public boolean markAsUnpinned(DDiagramElement dDiagramElement) { + boolean pinned = false; + List<ArrangeConstraint> constraints = getArrangeConstraints(dDiagramElement); + if (constraints != null) { + constraints.removeAll(PINNED_CONSTRAINTS); + pinned = true; + } + return pinned; + } + + private List<ArrangeConstraint> getArrangeConstraints(final EObject diagramElement) { + List<ArrangeConstraint> constraints = null; + if (diagramElement instanceof AbstractDNode) { + final AbstractDNode node = (AbstractDNode) diagramElement; + constraints = node.getArrangeConstraints(); + } else if (diagramElement instanceof DEdge) { + final DEdge edge = (DEdge) diagramElement; + constraints = edge.getArrangeConstraints(); + } + return constraints; + } + + /** + * Indicates if the given ddiagram is allowing pin/unpin. + * + * @param diagram + * the diagram to inspect + * @return true if the given ddiagram is allowing layouting mode, false + * otherwise + */ + public static Predicate<DDiagramElement> allowsPinUnpin(DDiagram diagram) { + // default return value is true for non-Region element (for basic + // DDiagram that are not handled + // by any DiagramDescriptionProvider). + Predicate<DDiagramElement> result = new Predicate<DDiagramElement>() { + public boolean apply(DDiagramElement dde) { + if (dde instanceof DDiagramElementContainer) { + DDiagramElementContainerExperimentalQuery query = new DDiagramElementContainerExperimentalQuery((DDiagramElementContainer) dde); + return !query.isRegion(); + } + return true; + } + }; + + // If an aird has been opened from the Package Explorer View, then + // we return false as no diagram is associated to this editor + if (diagram == null || diagram.getDescription() == null) { + return Predicates.alwaysFalse(); + } + + // If diagram is not null, we search for a possible + // DiagramDescriptionProvider handling this type of diagram + for (final IDiagramTypeDescriptor diagramTypeDescriptor : DiagramTypeDescriptorRegistry.getInstance().getAllDiagramTypeDescriptors()) { + if (diagramTypeDescriptor.getDiagramDescriptionProvider().handles(diagram.getDescription().eClass().getEPackage())) { + // This DiagramDescriptionProvider may forbid pin/unpin actions. + Predicate<DDiagramElement> allowsPinUnpin = diagramTypeDescriptor.getDiagramDescriptionProvider().allowsPinUnpin(); + if (allowsPinUnpin != null) { + result = allowsPinUnpin; + break; + } + } + } + + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/SiriusLayoutDataManager.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/SiriusLayoutDataManager.java new file mode 100644 index 0000000000..a9b646f855 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/SiriusLayoutDataManager.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData; + +/** + * An interface for all the SiriusLayoutDataManager for mapping key ( + * {@link LayoutDataKey}) and layoutData ({@link AbstractLayoutData}). + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public interface SiriusLayoutDataManager { + + /** + * Get the layout data corresponding to the key. + * + * @param key + * The key + * @return the layout data corresponding to the key or null if not found. + */ + AbstractLayoutData getLayoutData(final LayoutDataKey key); + + /** + * Add a layout data according to the key. + * + * @param key + * The key + * @param layoutData + * The layout data + */ + void addLayoutData(final LayoutDataKey key, final AbstractLayoutData layoutData); + + /** + * Create a key corresponding to the semanticDecorator and available for + * this manager. + * + * @param semanticDecorator + * the semantic decorator + * @return a new key corresponding to the semanticDecorator and available + * for this manager. + */ + LayoutDataKey createKey(final DSemanticDecorator semanticDecorator); + + /** + * Store the layout data for this edit part and all it's children. + * + * @param rootEditPart + * the root of the editParts to store. + */ + void storeLayoutData(IGraphicalEditPart rootEditPart); + + /** + * Apply the current layout data to the rootEditPart. + * + * @param rootEditPart + * the root edit from which we would try to apply the current + * stored layout + */ + void applyLayout(IGraphicalEditPart rootEditPart); + + /** + * Check if the manager contains data. + * + * @return true if the manager contains data, false otherwise. + */ + boolean containsData(); + + /** + * Remove all the stored layout data. + */ + void clearLayoutData(); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/SiriusLayoutDataManagerForSemanticElementsFactory.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/SiriusLayoutDataManagerForSemanticElementsFactory.java new file mode 100644 index 0000000000..2694c90b44 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/SiriusLayoutDataManagerForSemanticElementsFactory.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout; + +import org.eclipse.sirius.diagram.ui.tools.internal.layout.semantic.SiriusLayoutDataManagerForSemanticElements; + +/** + * A factory to give access to a {@link SiriusLayoutDataManager} managed by + * semantic elements. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class SiriusLayoutDataManagerForSemanticElementsFactory { + private static final SiriusLayoutDataManagerForSemanticElementsFactory INSTANCE = new SiriusLayoutDataManagerForSemanticElementsFactory(); + + private static final SiriusLayoutDataManagerForSemanticElements VIEWPOINT_LAYOUT_DATA_MANAGER = new SiriusLayoutDataManagerForSemanticElements(); + + /** + * gives access to the singleton instance of + * <code>SiriusLayoutDataManagerForSemanticElementsFactory</code>. + * + * @return the singleton instance + */ + public static SiriusLayoutDataManagerForSemanticElementsFactory getInstance() { + return INSTANCE; + } + + /** + * Get the {@link SiriusLayoutDataManager}. + * + * @return an instance of + * {@link SiriusLayoutDataManagerForSemanticElements} + */ + public SiriusLayoutDataManager getSiriusLayoutDataManager() { + return VIEWPOINT_LAYOUT_DATA_MANAGER; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractEdgeDecorateSemanticElementOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractEdgeDecorateSemanticElementOrdering.java new file mode 100644 index 0000000000..1b20a55a80 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractEdgeDecorateSemanticElementOrdering.java @@ -0,0 +1,92 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DSemanticDecorator; + +/** + * This class orders a list of {@link DSemanticDecorator}s that represent an + * edge. + * + * @author ymortier + */ +public abstract class AbstractEdgeDecorateSemanticElementOrdering extends AbstractViewEdgeOrdering { + + /** + * Return the semantic element that is the source or the target of the edges + * to sort. + * + * @return the semantic element that is the source or the target of the + * edges to sort. + */ + public EObject getSemanticElementConnector() { + return ((DSemanticDecorator) this.getEdgeTargetConnector()).getTarget(); + } + + /** + * Compare two {@link EObject}s. The return value depends on the relation + * order of <code>eObject1</code> and <code>eObject2</code>. It returns a + * positive number if <code>eObject1</code> is greater than + * <code>eObject2</code>, a negative number if <code>eObject1</code> is + * lesser that <code>eObject2</code> or <code>0</code> if + * <code>eObject1</code> equals <code>eObject2</code>. + * + * @param eObject1 + * the first element to compare. + * @param eObject2 + * the second element to compare. + * @return a positive number if <code>eObject1</code> is greater than + * <code>eObject2</code>, a negative number if <code>eObject1</code> + * is lesser that <code>eObject2</code> or <code>0</code> if + * <code>eObject1</code> equals <code>eObject2</code>. + */ + public abstract int compare(EObject eObject1, EObject eObject2); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewEdgeOrdering#compare(org.eclipse.sirius.DEdge, + * org.eclipse.sirius.DEdge) + */ + @Override + public int compare(final DEdge vp1, final DEdge vp2) { + final DSemanticDecorator dc1 = vp1; + final DSemanticDecorator dc2 = vp2; + return compare(dc1.getTarget(), dc2.getTarget()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewEdgeOrdering#isAbleToManageViewEdge(org.eclipse.sirius.DEdge) + */ + @Override + public final boolean isAbleToManageViewEdge(final DEdge viewEdge) { + return isAbleToManageSemanticElement(viewEdge.getTarget()); + } + + /** + * Return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified semantic element. + * + * @param semanticElement + * the semantic element to check. + * @return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified semantic element. + */ + public abstract boolean isAbleToManageSemanticElement(EObject semanticElement); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractEdgeViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractEdgeViewOrdering.java new file mode 100644 index 0000000000..d29e4ee988 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractEdgeViewOrdering.java @@ -0,0 +1,142 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.View; + +/** + * This class is able to sort a list of {@link Edge}s. + * + * @author ymortier + */ +public abstract class AbstractEdgeViewOrdering extends AbstractViewOrdering { + + /** + * The {@link View} that is the source or the target of edges to sort. + */ + private View connector; + + /** + * Return The {@link View} that is the source or the target of edges to + * sort. + * + * @return The {@link View} that is the source or the target of edges to + * sort. + */ + public View getConnector() { + return this.connector; + } + + /** + * Set The {@link View} that is the source or the target of edges to sort. + * + * @param connector + * The {@link View} that is the source or the target of edges to + * sort. + */ + public void setConnector(final View connector) { + this.connector = connector; + this.isSorted = false; + } + + /** + * Compare two {@link Edge}s. It returns a positive number if + * <code>edge1</code> is greater than <code>edge2</code>, a negative number + * if <code>edge1</code> is lesser than <code>edge2</code> or <code>0</code> + * if <code>edge1</code> equals <code>edge2</code>. + * + * + * @param edge1 + * the first edge. + * @param edge2 + * the second edge. + * @return a positive number if <code>node1</code> is greater than + * <code>node2</code>, a negative number if <code>node1</code> is + * lesser than <code>node2</code> or <code>0</code> if + * <code>node1</code> equals <code>node2</code>. + */ + public abstract int compare(Edge edge1, Edge edge2); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewOrdering#sortViews(java.util.List) + */ + @Override + protected List<View> sortViews(final List<View> views) { + Collections.sort(views, new EdgeComparator()); + return views; + } + + /** + * The comparator of {@link Edge}s. + * + * @author ymortier + */ + private class EdgeComparator implements Comparator<View> { + + /** + * {@inheritDoc} + * + * @see java.util.Comparator#compare(T, T) + */ + public int compare(final View view0, final View view1) { + + final Edge edge0 = (Edge) view0; + final Edge edge1 = (Edge) view1; + + int comparison; + if (AbstractEdgeViewOrdering.this.isAbleToManageEdge(edge0)) { + if (AbstractEdgeViewOrdering.this.isAbleToManageEdge(edge1)) { + comparison = AbstractEdgeViewOrdering.this.compare(edge0, edge1); + } else { + comparison = 1; + } + } else { + if (AbstractEdgeViewOrdering.this.isAbleToManageEdge(edge1)) { + comparison = 1; + } else { + comparison = 0; + } + } + return comparison; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#isAbleToManageView(org.eclipse.gmf.runtime.notation.View) + */ + public final boolean isAbleToManageView(final View view) { + if (view instanceof Edge) { + return isAbleToManageEdge((Edge) view); + } + return false; + } + + /** + * Return <code>true</code> if this {@link ViewOrdering} is able to manage + * the specified {@link Edge}. + * + * @param edge + * the edge to check. + * @return <code>true</code> if this {@link ViewOrdering} is able to manage + * the specified {@link Edge}. + */ + public abstract boolean isAbleToManageEdge(Edge edge); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractNodeDecorateSemanticElementOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractNodeDecorateSemanticElementOrdering.java new file mode 100644 index 0000000000..511c3bbb46 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractNodeDecorateSemanticElementOrdering.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.DSemanticDecorator; + +/** + * This class orders a list of {@link DSemanticDecorator}s that represent a + * node. + * + * @author ymortier + */ +public abstract class AbstractNodeDecorateSemanticElementOrdering extends AbstractViewNodeOrdering { + + /** + * Compare two {@link EObject}s. The return value depends on the relation + * order of <code>eObject1</code> and <code>eObject2</code>. It returns a + * positive number if <code>eObject1</code> is greater than + * <code>eObject2</code>, a negative number if <code>eObject1</code> is + * lesser that <code>eObject2</code> or <code>0</code> if + * <code>eObject1</code> equals <code>eObject2</code>. + * + * @param eObject1 + * the first element to compare. + * @param eObject2 + * the second element to compare. + * @return a positive number if <code>eObject1</code> is greater than + * <code>eObject2</code>, a negative number if <code>eObject1</code> + * is lesser that <code>eObject2</code> or <code>0</code> if + * <code>eObject1</code> equals <code>eObject2</code>. + */ + public abstract int compare(EObject eObject1, EObject eObject2); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewNodeOrdering#compare(org.eclipse.sirius.AbstractDNode, + * org.eclipse.sirius.AbstractDNode) + */ + @Override + public int compare(final AbstractDNode vp1, final AbstractDNode vp2) { + final DSemanticDecorator dc1 = vp1; + final DSemanticDecorator dc2 = vp2; + return compare(dc1.getTarget(), dc2.getTarget()); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewNodeOrdering#isAbleToManageAbstractViewNode(org.eclipse.sirius.AbstractDNode) + */ + @Override + public final boolean isAbleToManageAbstractViewNode(final AbstractDNode node) { + return isAbleToManageSemanticElement(node.getTarget()); + } + + /** + * Return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified semantic element. + * + * @param semanticElement + * the semantic element to check. + * @return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified semantic element. + */ + public abstract boolean isAbleToManageSemanticElement(EObject semanticElement); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractNodeViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractNodeViewOrdering.java new file mode 100644 index 0000000000..3609dfd0ea --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractNodeViewOrdering.java @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.Collections; +import java.util.Comparator; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +/** + * This class is able to sort a list of {@link Node}. + * + * @author ymortier + */ +public abstract class AbstractNodeViewOrdering extends AbstractViewOrdering { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewOrdering#sortViews(java.util.List) + */ + @Override + protected final List<View> sortViews(final List<View> views) { + Collections.sort(views, new NodeComparator()); + return views; + } + + /** + * Compare two {@link Node}s. It returns a positive number if + * <code>node1</code> is greater than <code>node2</code>, a negative number + * if <code>node1</code> is lesser than <code>node2</code> or <code>0</code> + * if <code>node1</code> equals <code>node2</code>. + * + * + * @param node1 + * the first node. + * @param node2 + * the second node. + * @return a positive number if <code>node1</code> is greater than + * <code>node2</code>, a negative number if <code>node1</code> is + * lesser than <code>node2</code> or <code>0</code> if + * <code>node1</code> equals <code>node2</code>. + */ + public abstract int compare(Node node1, Node node2); + + /** + * The comparator of {@link Node}s. + * + * @author ymortier + */ + private class NodeComparator implements Comparator<View> { + + /** + * {@inheritDoc} + * + * @see java.util.Comparator#compare(T, T) + */ + public int compare(final View view0, final View view1) { + int result; + + final Node node0 = (Node) view0; + final Node node1 = (Node) view1; + + if (AbstractNodeViewOrdering.this.isAbleToManageNode(node0)) { + if (!AbstractNodeViewOrdering.this.isAbleToManageNode(node1)) { + result = -1; + } else { + result = AbstractNodeViewOrdering.this.compare(node0, node1); + } + } else { + if (AbstractNodeViewOrdering.this.isAbleToManageNode(node1)) { + result = 1; + } else { + result = 0; + } + } + return result; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#isAbleToManageView(org.eclipse.gmf.runtime.notation.View) + */ + public final boolean isAbleToManageView(final View view) { + if (view instanceof Node) { + return isAbleToManageNode((Node) view); + } + return false; + } + + /** + * Return <code>true</code> if this {@link ViewOrdering} is able to manage + * the specified {@link Node}. + * + * @param node + * the node to check. + * @return <code>true</code> if this {@link ViewOrdering} is able to manage + * the specified {@link Node}. + */ + public abstract boolean isAbleToManageNode(Node node); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractSemanticTreeOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractSemanticTreeOrdering.java new file mode 100644 index 0000000000..3cc48bd31e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractSemanticTreeOrdering.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DSemanticDecorator; + +/** + * Tree ordering to order semantic elements. + * + * @author ymortier + */ +public abstract class AbstractSemanticTreeOrdering extends AbstractTreeViewOrdering { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractTreeViewOrdering#getChildren(org.eclipse.gmf.runtime.notation.View, + * java.util.List) + */ + @Override + public List getChildren(final View parent, final List views) { + // FIXME YMO doesn't work fine if there are many views that have the + // same target. + final List semantics = new ArrayList(views.size()); + // + // Gets the parent semantic element. + final EObject semanticParent = this.resolveSemanticElement(parent); + if (semanticParent == null) { + return Collections.EMPTY_LIST; + } + // + // Keep a trace semantic -> view. + final Map semanticToView = new HashMap(); + final Iterator iterViews = views.iterator(); + // + // Find all semantic elements. + while (iterViews.hasNext()) { + final View currentView = (View) iterViews.next(); + // Only Nodes are able to compose a tree. + if (currentView instanceof Node) { + final EObject semanticElement = this.resolveSemanticElement(currentView); + if (semanticElement != null) { + semantics.add(semanticElement); + semanticToView.put(semanticElement, currentView); + } + } + } + // + // Gets children. + final List semanticChildren = this.getSemanticChildren(semanticParent, semantics); + final List viewRoots = new ArrayList(semanticChildren.size()); + // + // Gets view roots. + final Iterator iterSemanticRoots = semanticChildren.iterator(); + while (iterSemanticRoots.hasNext()) { + viewRoots.add(semanticToView.get(iterSemanticRoots.next())); + } + return viewRoots; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractTreeViewOrdering#getRoots(java.util.List) + */ + @Override + public List getRoots(final List views) { + // FIXME YMO doesn't work fine if there are many views that have the + // same target. + final List semantics = new ArrayList(views.size()); + // + // Keep a trace semantic -> view. + final Map semanticToView = new HashMap(); + final Iterator iterViews = views.iterator(); + // + // Find all semantic elements. + while (iterViews.hasNext()) { + final View currentView = (View) iterViews.next(); + // Only Nodes are able to compose a tree. + if (currentView instanceof Node) { + final EObject semanticElement = this.resolveSemanticElement(currentView); + if (semanticElement != null) { + semantics.add(semanticElement); + semanticToView.put(semanticElement, currentView); + } + } + } + // + // Gets roots. + final List semanticRoots = this.getSemanticRoots(semantics); + final List viewRoots = new ArrayList(semanticRoots.size()); + // + // Gets view roots. + final Iterator iterSemanticRoots = semanticRoots.iterator(); + while (iterSemanticRoots.hasNext()) { + viewRoots.add(semanticToView.get(iterSemanticRoots.next())); + } + return viewRoots; + } + + /** + * Return the semantic elements that are the roots of the tree. + * + * @param eObjects + * the semantic elements that are on the diagram. + * @return the semantic elements that are the roots of the tree. + */ + public abstract List getSemanticRoots(List eObjects); + + /** + * Return the semantic elements that are the children of + * <code>semanticParent</code>. + * + * @param semanticParent + * the semantic parent. + * @param candidates + * all semantic candidates. + * @return the semantic elements that are the children of + * <code>semanticParent</code>. + */ + public abstract List getSemanticChildren(EObject semanticParent, List candidates); + + /** + * Resolves the real semantic element of the specified GMF view. + * + * @param gmfView + * the GMF view. + * @return the real semantic element of the specified GMF view. + */ + protected EObject resolveSemanticElement(final View gmfView) { + EObject semanticElement = null; + final EObject semanticView = ViewUtil.resolveSemanticElement(gmfView); + if (semanticView instanceof DSemanticDecorator) { + semanticElement = ((DSemanticDecorator) semanticView).getTarget(); + } + return semanticElement; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractTreeViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractTreeViewOrdering.java new file mode 100644 index 0000000000..b86ada72fc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractTreeViewOrdering.java @@ -0,0 +1,506 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +/** + * An abstract Tree like view ordering. + * + * @author ymortier + */ +public abstract class AbstractTreeViewOrdering implements ViewOrdering { + + /** The views to sort. */ + private List<View> views; + + /** The resulted grid view. */ + private GridView gridView; + + /** Indicates that this instance is aware of user interaction. */ + private boolean userAwareCapable; + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering#getSortedViews() + */ + public List<View> getSortedViews() { + return views; + } + + /** + * <code>true</code> if this instance is aware of user interaction. + * + * @param userAwareCapable + * <code>true</code> if this instance is aware of user + * interaction. + */ + public void setUserAwareCapable(final boolean userAwareCapable) { + this.userAwareCapable = userAwareCapable; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#getSortedViewsAsGrid() + */ + public GridView getSortedViewsAsGrid() { + if (this.gridView == null) { + buildTree(); + } + return this.gridView; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#isAbleToManageView(org.eclipse.gmf.runtime.notation.View) + */ + public boolean isAbleToManageView(final View view) { + return view instanceof Node; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#setViews(java.util.Collection) + */ + public <T extends View> void setViews(final Collection<T> views) { + this.views = Collections.<View> unmodifiableList(new ArrayList<T>(views)); + this.gridView = null; + } + + /** + * Return the views that are the roots of trees to display. + * + * @param views + * the views. + * @return the views that are the roots of trees to display. + */ + public abstract List<View> getRoots(List<View> views); + + /** + * Returns all children of the parent view. + * + * @param parent + * the parent view. + * @param views + * the candidates. + * @return all children of the parent view. + */ + public abstract List<View> getChildren(View parent, List<View> views); + + /** + * Build the tree. + */ + private void buildTree() { + + List<View> roots = this.getRoots(this.views); + final UserAwareCapableOrdering userAwareCapableOrdering = new UserAwareCapableOrdering(); + userAwareCapableOrdering.setViews(roots); + roots = new ArrayList<View>(userAwareCapableOrdering.getSortedViews()); + + final AirTree dummyRoot = new AirTree(null); + + final Iterator<View> iterRoots = roots.iterator(); + while (iterRoots.hasNext()) { + final View view = iterRoots.next(); + final AirTree airTree = new AirTree(view); + airTree.setParent(dummyRoot); + } + + final List<AirTree> currentViews = new ArrayList<AirTree>(dummyRoot.getChildren()); + while (!currentViews.isEmpty()) { + final AirTree airTree = currentViews.get(0); + List<View> childrenView = this.getChildren(airTree.getView(), this.views); + if (userAwareCapable) { + final UserAwareCapableOrdering ordering = new UserAwareCapableOrdering(); + ordering.setViews(childrenView); + childrenView = ordering.getSortedViews(); + } + final Iterator<View> iterChildrenView = childrenView.iterator(); + while (iterChildrenView.hasNext()) { + final View currentChild = iterChildrenView.next(); + final AirTree childAirTree = new AirTree(currentChild); + childAirTree.setParent(airTree); + currentViews.add(childAirTree); + } + currentViews.remove(airTree); + } + final ExtendedGrid extendedGrid = getExtendedGrid(dummyRoot); + final View[][] gridViews = (View[][]) extendedGrid.toArray(new View[extendedGrid.getNbColumns()][extendedGrid.getNbLines()]); + this.gridView = GridView.create(gridViews); + clear(); + } + + /** + * Clears all the informations calculated for getting the views. + */ + // Defined to avoid Memory leaks when referencing disposed Views + protected void clear() { + + } + + /** + * @param dummyRoot + */ + private ExtendedGrid getExtendedGrid(final AirTree dummyRoot) { + final ExtendedGrid extendedGrid = new ExtendedGrid(); + + List<AirTree> currentNodes = dummyRoot.getChildren(); + // + // index of the current line. + int lineIndex = 0; + while (!currentNodes.isEmpty()) { + // + // the next line. + final List<AirTree> next = new LinkedList<AirTree>(); + // + // appends a line to the grid. + extendedGrid.appendLine(); + // index of the node in the line. + int nodeIndexInLine = 0; + // iterate over the line. + for (final AirTree node : currentNodes) { + + // the parent. + final AirTree parent = node.getParent(); + // + // index of the child into the parent's children list. + final int childIndex = parent.getChildren().indexOf(node); + + final int childrenNumber = parent.getChildren().size(); + if (childIndex == 0) { + // + // compute the number of columns to add. + int nbColumnsToAdd = childrenNumber - (childrenNumber % 2); + if (lineIndex > 0) { + int parentColumnIndex = getParentColumnIndex(extendedGrid, parent, lineIndex); + extendedGrid.insertColumn(parentColumnIndex); + extendedGrid.insertColumn(parentColumnIndex); + + for (int i = 0; i < nbColumnsToAdd; i++) { + parentColumnIndex = getParentColumnIndex(extendedGrid, parent, lineIndex); + if (i % 2 == 0) { + extendedGrid.insertColumn(parentColumnIndex); + } else { + extendedGrid.insertColumn(parentColumnIndex + 1); + } + } + extendedGrid.insertColumn(parentColumnIndex + 1); + extendedGrid.insertColumn(parentColumnIndex + 1); + + parentColumnIndex = getParentColumnIndex(extendedGrid, parent, lineIndex); + extendedGrid.setData(node.getView(), getColumnIndex(parentColumnIndex, childrenNumber, childIndex), lineIndex); + } else { + nbColumnsToAdd = childrenNumber; + for (int i = 0; i < nbColumnsToAdd; i++) { + extendedGrid.appendColumn(); + } + extendedGrid.setData(node.getView(), 0, lineIndex); + } + } else { + int columnIndex; + if (lineIndex == 0) { + columnIndex = childIndex; + } else { + final int parentIndex = getParentColumnIndex(extendedGrid, parent, lineIndex); + columnIndex = getColumnIndex(parentIndex, childrenNumber, childIndex); + if (childrenNumber % 2 == 0 && (childrenNumber / 2) <= childIndex) { + columnIndex += 1; + } + } + extendedGrid.setData(node.getView(), columnIndex, lineIndex); + } + + // + // append children for the next line. + next.addAll(node.getChildren()); + // next node. + nodeIndexInLine++; + } + currentNodes = next; + lineIndex++; + } + // reduceGrid(extendedGrid, dummyRoot); + return extendedGrid; + } + + private int getColumnIndex(final int parentColumnIndex, final int childrenNumber, final int childIndex) { + return parentColumnIndex - (int) Math.floor((double) childrenNumber / 2) + childIndex; + } + + private int getParentColumnIndex(final ExtendedGrid extendedGrid, final AirTree parent, final int lineIndex) { + return extendedGrid.indexOf(parent.getView(), lineIndex - 1); + } + + /** + * The tree. + * + * @author ymortier + */ + private static class AirTree { + + /** The children of this view. */ + private List<AirTree> children = new LinkedList<AirTree>(); + + /** The current View. */ + private View view; + + /** The parent. */ + private AirTree parent; + + /** + * Create a new {@link AirTree}. + * + * @param view + * the view of the node. + */ + public AirTree(final View view) { + this.view = view; + } + + /** + * Return the view. + * + * @return the view. + */ + public View getView() { + return view; + } + + /** + * Return the children. + * + * @return the children. + */ + public List<AirTree> getChildren() { + return children; + } + + /** + * Return the parent of this element. + * + * @return the parent of this element. + */ + public AirTree getParent() { + return parent; + } + + /** + * Set the parent of this element. + * + * @param parent + * the parent of this element. + */ + public void setParent(final AirTree parent) { + this.parent = parent; + parent.children.add(this); + } + } + + private static class UserAwareCapableOrdering extends AbstractNodeViewOrdering { + + /** The orientation. */ + private int orientation = PositionConstants.HORIZONTAL; + + /** + * Default constructor. + */ + public UserAwareCapableOrdering() { + // empty. + } + + /** + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractNodeViewOrdering#compare(org.eclipse.gmf.runtime.notation.Node, + * org.eclipse.gmf.runtime.notation.Node) + */ + @Override + public int compare(final Node node1, final Node node2) { + final Location loc1 = (Location) node1.getLayoutConstraint(); + final Location loc2 = (Location) node2.getLayoutConstraint(); + + if (this.orientation == PositionConstants.HORIZONTAL) { + return loc1.getX() - loc2.getX(); + } else { + return loc1.getY() - loc2.getY(); + } + } + + /** + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractNodeViewOrdering#isAbleToManageNode(org.eclipse.gmf.runtime.notation.Node) + */ + @Override + public boolean isAbleToManageNode(final Node node) { + return node.getLayoutConstraint() instanceof Location; + } + + } + + /** + * A super grid. + * + * @author ymortier + */ + private static class ExtendedGrid { + + /** the grid. */ + private List<List<Object>> grid; + + /** the number of columns. */ + private int nbColumns; + + /** The number of lines. */ + private int nbLines; + + /** + * Creates an empty grid. + */ + public ExtendedGrid() { + this.grid = new ArrayList<List<Object>>(); + this.nbColumns = 0; + this.nbLines = 0; + } + + /** + * Append a column. + */ + public void appendColumn() { + final ArrayList<Object> column = new ArrayList<Object>(nbLines); + for (int i = 0; i < nbLines; i++) { + column.add(null); + } + grid.add(column); + nbColumns++; + } + + /** + * Insert the column before the column at the specified index. + * + * @param index + * an index. + */ + public void insertColumn(final int index) { + if (index >= this.nbColumns) { + this.appendColumn(); + } else { + final ArrayList<Object> column = new ArrayList<Object>(nbLines); + for (int i = 0; i < nbLines; i++) { + column.add(null); + } + grid.add(index, column); + nbColumns++; + } + + } + + /** + * Appends a line. + */ + public void appendLine() { + final Iterator<List<Object>> iterColumns = this.grid.iterator(); + while (iterColumns.hasNext()) { + final List<Object> column = iterColumns.next(); + column.add(null); + } + nbLines++; + } + + /** + * Set the data at the specified index. + * + * @param data + * the data + * @param columnIndex + * the column index. + * @param lineIndex + * the line index. + */ + public void setData(final Object data, final int columnIndex, final int lineIndex) { + if (columnIndex >= nbColumns || columnIndex < 0) { + throw new IllegalArgumentException(); + } + if (lineIndex >= nbLines || lineIndex < 0) { + throw new IllegalArgumentException(); + } + final List<Object> column = grid.get(columnIndex); + column.set(lineIndex, data); + } + + /** + * Return the number of columns. + * + * @return the number of columns. + */ + public int getNbColumns() { + return nbColumns; + } + + /** + * Return the number of lines. + * + * @return the number of lines. + */ + public int getNbLines() { + return nbLines; + } + + /** + * Return the column index of the specified data for the specified line. + * + * @param data + * the data. + * @param lineIndex + * the line. + * @return the column index of the specified data for the specified line + * or -1 if the data is not in the line. + */ + public int indexOf(final Object data, final int lineIndex) { + int result = -1; + for (int i = 0; i < this.grid.size() && result < 0; i++) { + final List<Object> currentColumn = this.grid.get(i); + if (currentColumn.get(lineIndex) != null && currentColumn.get(lineIndex).equals(data)) { + result = i; + } + } + return result; + } + + /** + * TODO + * + * @param array + * @return + */ + public Object[][] toArray(final Object[][] array) { + for (int i = 0; i < nbColumns; i++) { + final List<Object> column = grid.get(i); + for (int j = 0; j < nbLines; j++) { + array[i][j] = column.get(j); + } + } + return array; + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewEdgeOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewEdgeOrdering.java new file mode 100644 index 0000000000..0af1a609d8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewEdgeOrdering.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import org.eclipse.gmf.runtime.notation.Edge; + +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.EdgeTarget; + +/** + * This class orders a list of {@link DEdge}s. + * + * @author ymortier + */ +public abstract class AbstractViewEdgeOrdering extends AbstractEdgeViewOrdering { + + /** + * Return the {@link EdgeTarget} that is the source or the target of + * {@link DEdge}s to sort. + * + * @return the {@link EdgeTarget} that is the source or the target of + * {@link DEdge}s to sort. + */ + public EdgeTarget getEdgeTargetConnector() { + return (EdgeTarget) this.getConnector().getElement(); + } + + /** + * Compare two {@link DEdge}s. The return value depends on the relation + * order of <code>vp1</code> and <code>vp2</code>. It returns a positive + * number if <code>vp1</code> is greater than <code>vp2</code>, a negative + * number if <code>vp1</code> is lesser that <code>vp2</code> or + * <code>0</code> if <code>vp1</code> equals <code>vp2</code>. + * + * @param vp1 + * the first element to compare. + * @param vp2 + * the second element to compare. + * @return a positive number if <code>vp1</code> is greater than + * <code>vp2</code>, a negative number if <code>vp1</code> is lesser + * that <code>vp2</code> or <code>0</code> if <code>vp1</code> + * equals <code>vp2</code>. + */ + public abstract int compare(DEdge vp1, DEdge vp2); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractEdgeViewOrdering#compare(org.eclipse.gmf.runtime.notation.Edge, + * org.eclipse.gmf.runtime.notation.Edge) + */ + @Override + public int compare(final Edge edge1, final Edge edge2) { + final DEdge viewEdge1 = (DEdge) edge1.getElement(); + final DEdge viewEdge2 = (DEdge) edge2.getElement(); + return AbstractViewEdgeOrdering.this.compare(viewEdge1, viewEdge2); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractEdgeViewOrdering#isAbleToManageEdge(org.eclipse.gmf.runtime.notation.Edge) + */ + @Override + public final boolean isAbleToManageEdge(final Edge edge) { + if (edge.getElement() instanceof DEdge) { + return this.isAbleToManageViewEdge((DEdge) edge.getElement()); + } + return false; + } + + /** + * Return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified {@link DEdge}. + * + * @param viewEdge + * the view edge to check. + * @return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified {@link DEdge}. + */ + public abstract boolean isAbleToManageViewEdge(DEdge viewEdge); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewNodeOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewNodeOrdering.java new file mode 100644 index 0000000000..7fb8263ed3 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewNodeOrdering.java @@ -0,0 +1,81 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import org.eclipse.gmf.runtime.notation.Node; + +import org.eclipse.sirius.AbstractDNode; + +/** + * This class orders a list of {@link AbstractDNode}s. + * + * + * @author ymortier + */ +public abstract class AbstractViewNodeOrdering extends AbstractNodeViewOrdering { + + /** + * Compare two {@link AbstractDNode}s. The return value depends on the + * relation order of <code>vp1</code> and <code>vp2</code>. It returns a + * positive number if <code>vp1</code> is greater than <code>vp2</code>, a + * negative number if <code>vp1</code> is lesser that <code>vp2</code> or + * <code>0</code> if <code>vp1</code> equals <code>vp2</code>. + * + * @param vp1 + * the first element to compare. + * @param vp2 + * the second element to compare. + * @return a positive number if <code>vp1</code> is greater than + * <code>vp2</code>, a negative number if <code>vp1</code> is lesser + * that <code>vp2</code> or <code>0</code> if <code>vp1</code> + * equals <code>vp2</code>. + */ + public abstract int compare(AbstractDNode vp1, AbstractDNode vp2); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractNodeViewOrdering#compare(org.eclipse.gmf.runtime.notation.Node, + * org.eclipse.gmf.runtime.notation.Node) + */ + @Override + public final int compare(final Node node1, final Node node2) { + final AbstractDNode viewNode1 = (AbstractDNode) node1.getElement(); + final AbstractDNode viewNode2 = (AbstractDNode) node2.getElement(); + return compare(viewNode1, viewNode2); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractNodeViewOrdering#isAbleToManageNode(org.eclipse.gmf.runtime.notation.Node) + */ + @Override + public final boolean isAbleToManageNode(final Node node) { + if (node.getElement() instanceof AbstractDNode) { + return isAbleToManageAbstractViewNode((AbstractDNode) node.getElement()); + } + return false; + } + + /** + * Return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified node. + * + * @param node + * the node to check. + * @return <code>true</code> if this + * {@link org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering} + * is able to manage the specified node. + */ + public abstract boolean isAbleToManageAbstractViewNode(AbstractDNode node); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewOrdering.java new file mode 100644 index 0000000000..037d9e0f96 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/AbstractViewOrdering.java @@ -0,0 +1,84 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.View; + +/** + * A simple partially implementation of {@link ViewOrdering}. + * + * @author ymortier + */ +public abstract class AbstractViewOrdering implements ViewOrdering { + + /** The sorted views. */ + protected List<View> sortedViews; + + /** + * <code>true</code> if the views are sorted, <code>false</code> otherwise. + */ + protected boolean isSorted; + + /** + * Set the list of views to sort. + * + * @param views + * the list of views to sort. + * @param <T> + * class which extends {@link View} + */ + public <T extends View> void setViews(final Collection<T> views) { + this.sortedViews = new ArrayList<View>(views); + this.isSorted = false; + } + + /** + * The result is not modifiable. All attempts to modify the result will + * throw an {@link UnsupportedOperationException}. + * + * @return the sorted views. + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrdering#getSortedViews() + */ + public List<View> getSortedViews() { + if (!isSorted) { + this.sortedViews = this.sortViews(this.sortedViews); + isSorted = true; + } + return this.sortedViews; + } + + /** + * Sorts the views. + * + * @param views + * the list of views to sort, this parameter can be modified by + * the implementation. + * @return the list of sorted views. + */ + protected abstract List<View> sortViews(List<View> views); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#getSortedViewsAsGrid() + */ + public GridView getSortedViewsAsGrid() { + final List<View> list = this.getSortedViews(); + final View[][] views = new View[1][]; + views[0] = list.toArray(new View[list.size()]); + final GridView gridView = GridView.create(views); + return gridView; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/GridView.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/GridView.java new file mode 100644 index 0000000000..990ad8a226 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/GridView.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.View; + +/** + * Represents a grid of views. + * + * @author ymortier + */ +public final class GridView { + + /** All columns. */ + private List<Column> columns; + + /** + * Call the method {@link #create(View[][])} to create an instance. + */ + private GridView() { + // empty. + } + + /** + * Create a new instance of {@link GridView}. + * + * @param views + * the grid. + * @return the new instance. + */ + public static GridView create(final View[][] views) { + final GridView gridView = new GridView(); + int maxLength = -1; + for (View[] view : views) { + if (view.length > maxLength) { + maxLength = view.length; + } + } + // + // copy the array. + final View[][] copyView = new View[views.length][]; + System.arraycopy(views, 0, copyView, 0, views.length); + // + // equilibrate the array. + for (int i = 0; i < views.length; i++) { + copyView[i] = new View[maxLength]; + System.arraycopy(views[i], 0, copyView[i], 0, views[i].length); + } + // + // Create columns + gridView.columns = new ArrayList<Column>(copyView.length); + for (int i = 0; i < views.length; i++) { + final Column column = gridView.new Column(views[i], i); + gridView.columns.add(column); + } + gridView.columns = Collections.unmodifiableList(gridView.columns); + return gridView; + } + + /** + * Return the number of columns of the grid. + * + * @return the number of columns of the grid. + */ + public int getColumnsCount() { + return this.columns.size(); + } + + /** + * Return the number of lines. + * + * @return the number of lines. + */ + public int getLinesCount() { + return getColumnsCount() > 0 ? this.getColumnAt(0).getViewsCount() : 0; + } + + /** + * Return the {@link Column} that is at the specified index. + * + * @param index + * the index of the column. + * @return the {@link Column} that is at the specified index. + * @throws IndexOutOfBoundsException + * if the index is out ouf bounds. + */ + public Column getColumnAt(final int index) throws IndexOutOfBoundsException { + return this.columns.get(index); + } + + /** + * Return the view at the specified index. + * + * @param columnIndex + * the index of the column. + * @param lineIndex + * the index of the line. + * @return the view at the specified index. + * @throws IndexOutOfBoundsException + * if the <code>columnIndex</code> or <code>lineIndex</code> is + * out of bounds of the grid. + */ + public View getViewAt(final int columnIndex, final int lineIndex) throws IndexOutOfBoundsException { + return this.getColumnAt(columnIndex).getViewAt(lineIndex); + } + + /** + * Return an {@link Iterator} that iterates all columns of the grid. Each + * elements is a {@link Column} that represents the column. + * + * @return an {@link Iterator} that iterates all columns of the grid. Each + * elements is a {@link Column} that represents the column. + */ + public Iterator<Column> iteratorColumns() { + return this.columns.iterator(); + } + + /** + * Represents a column of the grid. + * + * @author ymortier + */ + public class Column { + + /** The views of the column. */ + private List<View> views; + + /** The index of the column. */ + private int index; + + /** + * Create a new {@link Column}. + * + * @param views + * the views of the column. + * @throws IllegalArgumentException + * if <code>index</code> is lesser than <code>0</code>. + */ + Column(final View[] views, final int index) throws IllegalArgumentException { + this.views = new ArrayList<View>(Arrays.asList(views)); + this.views = Collections.unmodifiableList(this.views); + this.index = index; + } + + /** + * Return the left sibling of the specified view. + * + * @param view + * a view of this column. + * @return the left sibling of the specified view or <code>null</code> + * if the specified view has no left sibling. + * @throws IllegalArgumentException + * if the view is not in the column. + */ + public View getLeftSibling(final View view) throws IllegalArgumentException { + if (!this.views.contains(view)) { + throw new IllegalArgumentException("The view is not in the column"); + } + if (this.index == 0) { + return null; + } + return GridView.this.getViewAt(this.index - 1, this.views.indexOf(view)); + } + + /** + * Return the right sibling of the specified view. + * + * @param view + * a view of this column. + * @return the left sibling of the specified view or <code>null</code> + * if the specified view has no right sibling. + * @throws IllegalArgumentException + * if the view is not in the column. + */ + public View getRightSibling(final View view) throws IllegalArgumentException { + if (!this.views.contains(view)) { + throw new IllegalArgumentException("The view is not in the column"); + } + if (this.index + 1 == GridView.this.getColumnsCount()) { + return null; + } + return GridView.this.getViewAt(this.index + 1, this.views.indexOf(view)); + } + + /** + * Return the view that is at the specified index. + * + * @param viewIndex + * the index of the view. + * @return the view that is at the specified index. + * @throws IndexOutOfBoundsException + * if <code>index</code> is out of bounds. + */ + public View getViewAt(final int viewIndex) throws IndexOutOfBoundsException { + return this.views.get(viewIndex); + } + + /** + * Return the index of the specified view or -1 if the view is not in + * this column. + * + * @param view + * the view. + * @return the index of the specified view or -1 if the view is not in + * this column. + */ + public int indexOf(final View view) { + return this.views.indexOf(view); + } + + /** + * Return the number of views that are in the column. + * + * @return the number of views that are in the column. + */ + public int getViewsCount() { + return this.views.size(); + } + + /** + * Return the left sibling column. + * + * @return the left sibling column. + */ + public Column getLeftSibling() { + if (this.index != 0) { + return GridView.this.columns.get(index - 1); + } + return null; + } + + /** + * Returns the index of the column. + * + * @return the index of the column. + */ + public int getIndex() { + return index; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/GridViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/GridViewOrdering.java new file mode 100644 index 0000000000..e341bceac6 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/GridViewOrdering.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.Collection; + +import org.eclipse.gmf.runtime.notation.View; + +/** + * An ordering that is able to order views into a grid. + * + * @author ymortier + */ +public interface GridViewOrdering { + + /** + * Returns the views sorted by this ordering. + * + * @return the views sorted by this ordering. + */ + GridView getSortedViewsAsGrid(); + + /** + * Returns <code>true</code> if this {@link ViewOrdering} is able to manage + * the specified {@link View}. + * + * @param view + * the view to check. + * @return <code>true</code> if this {@link ViewOrdering} is able to manage + * the specified {@link View}. + */ + boolean isAbleToManageView(View view); + + /** + * Set the list of views to sort. + * + * @param views + * the list of views to sort. + * @param <T> + * class which extends View + */ + <T extends View> void setViews(Collection<T> views); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleCompositeEdgeViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleCompositeEdgeViewOrdering.java new file mode 100644 index 0000000000..fe4f663959 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleCompositeEdgeViewOrdering.java @@ -0,0 +1,118 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.View; + +/** + * A simple composite of {@link AbstractEdgeViewOrdering}s. + * + * @author ymortier + */ +public class SimpleCompositeEdgeViewOrdering extends AbstractEdgeViewOrdering { + + /** The orderings. */ + private List<AbstractEdgeViewOrdering> edgeViewOrderings = new LinkedList<AbstractEdgeViewOrdering>(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractEdgeViewOrdering#setConnector(org.eclipse.gmf.runtime.notation.View) + */ + @Override + public void setConnector(final View connector) { + super.setConnector(connector); + final Iterator<AbstractEdgeViewOrdering> iterEdgeViewOrderings = this.edgeViewOrderings.iterator(); + while (iterEdgeViewOrderings.hasNext()) { + final AbstractEdgeViewOrdering current = iterEdgeViewOrderings.next(); + current.setConnector(connector); + } + } + + /** + * Add a new {@link AbstractEdgeViewOrdering}. + * + * @param edgeViewOrdering + * the {@link AbstractEdgeViewOrdering} to add. + */ + public void addEdgeViewOrdering(final AbstractEdgeViewOrdering edgeViewOrdering) { + this.edgeViewOrderings.add(edgeViewOrdering); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractEdgeViewOrdering#compare(org.eclipse.gmf.runtime.notation.Edge, + * org.eclipse.gmf.runtime.notation.Edge) + */ + @Override + public int compare(final Edge edge1, final Edge edge2) { + final AbstractEdgeViewOrdering viewOrdering1 = this.getViewOrderingFor(edge1); + final AbstractEdgeViewOrdering viewOrdering2 = this.getViewOrderingFor(edge2); + + int comparison; + + if (viewOrdering1 == null) { + if (viewOrdering2 == null) { + comparison = 0; + } else { + comparison = 1; + } + } else if (viewOrdering2 == null) { + comparison = -1; + } else { + if (viewOrdering1 == viewOrdering2) { + comparison = viewOrdering1.compare(edge1, edge2); + } else { + comparison = viewOrdering1.hashCode() - viewOrdering2.hashCode(); + } + } + return comparison; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractEdgeViewOrdering#isAbleToManageEdge(org.eclipse.gmf.runtime.notation.Edge) + */ + @Override + public boolean isAbleToManageEdge(final Edge edge) { + return this.getViewOrderingFor(edge) != null; + } + + /** + * Return the first {@link ViewOrdering} that is able to manage the + * specified view or <code>null</code> if no {@link ViewOrdering} is + * availble for the view. + * + * @param view + * the view to manage. + * @return the first {@link ViewOrdering} that is able to manage the + * specified view or <code>null</code> if no {@link ViewOrdering} is + * availble for the view. + */ + protected AbstractEdgeViewOrdering getViewOrderingFor(final Edge view) { + final Iterator<AbstractEdgeViewOrdering> iterViewOrderings = this.edgeViewOrderings.iterator(); + while (iterViewOrderings.hasNext()) { + final AbstractEdgeViewOrdering currentViewOrdering = iterViewOrderings.next(); + if (currentViewOrdering.isAbleToManageView(view)) { + return currentViewOrdering; + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleCompositeViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleCompositeViewOrdering.java new file mode 100644 index 0000000000..80d8934d6f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleCompositeViewOrdering.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import org.eclipse.gmf.runtime.notation.View; + +/** + * A simple composite view ordering. + * + * @author ymortier + */ +public class SimpleCompositeViewOrdering extends AbstractViewOrdering { + + /** All view orderings. */ + private List<ViewOrdering> viewOrderings = new LinkedList<ViewOrdering>(); + + /** + * Adds a new {@link ViewOrdering}. + * + * @param viewOrdering + * the {@link ViewOrdering} to add. + */ + public void addViewOrdering(final ViewOrdering viewOrdering) { + this.viewOrderings.add(viewOrdering); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewOrdering#sortViews(java.util.List) + */ + @Override + protected List<View> sortViews(final List<View> views) { + // + // First populate a map : + // - ViewOrdering -> List of views to sort. + final Iterator<View> iterAllViews = views.iterator(); + final Map<ViewOrdering, List<View>> viewOrderingsToViews = new HashMap<ViewOrdering, List<View>>(); + final List<View> nullViewOrdering = new LinkedList<View>(); + while (iterAllViews.hasNext()) { + final View currentView = iterAllViews.next(); + final ViewOrdering viewOrdering = this.getViewOrderingFor(currentView); + if (viewOrdering == null) { + nullViewOrdering.add(currentView); + } else { + List<View> currentViews = viewOrderingsToViews.get(viewOrdering); + if (currentViews == null) { + currentViews = new LinkedList<View>(); + viewOrderingsToViews.put(viewOrdering, currentViews); + } + currentViews.add(currentView); + } + } + // + // Second, sort all views. + final List<View> sortedViews = new LinkedList<View>(); + final Iterator<ViewOrdering> iterViewOrderings = this.viewOrderings.listIterator(); + while (iterViewOrderings.hasNext()) { + final ViewOrdering currentViewOrdering = iterViewOrderings.next(); + final List<View> correspondingViews = viewOrderingsToViews.get(currentViewOrdering); + if (correspondingViews != null) { + currentViewOrdering.setViews(correspondingViews); + sortedViews.addAll(currentViewOrdering.getSortedViews()); + } + } + // + // Third, Append all views that have no ViewOrdering + sortedViews.addAll(nullViewOrdering); + return sortedViews; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#isAbleToManageView(org.eclipse.gmf.runtime.notation.View) + */ + public boolean isAbleToManageView(final View view) { + return getViewOrderingFor(view) != null; + } + + /** + * Return the first {@link ViewOrdering} that is able to manage the + * specified view or <code>null</code> if no {@link ViewOrdering} is + * available for the view. + * + * @param view + * the view to manage. + * @return the first {@link ViewOrdering} that is able to manage the + * specified view or <code>null</code> if no {@link ViewOrdering} is + * available for the view. + */ + protected ViewOrdering getViewOrderingFor(final View view) { + final Iterator<ViewOrdering> iterViewOrderings = this.viewOrderings.listIterator(); + while (iterViewOrderings.hasNext()) { + final ViewOrdering currentViewOrdering = iterViewOrderings.next(); + if (currentViewOrdering.isAbleToManageView(view)) { + return currentViewOrdering; + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleViewOrdering.java new file mode 100644 index 0000000000..0ce1d57996 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/SimpleViewOrdering.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.List; + +import org.eclipse.gmf.runtime.notation.View; + +/** + * Doesn't sort the list. + * + * @author ymortier + */ +public class SimpleViewOrdering extends AbstractViewOrdering { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.AbstractViewOrdering#sortViews(java.util.List) + */ + @Override + protected List<View> sortViews(final List<View> views) { + return views; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.GridViewOrdering#isAbleToManageView(org.eclipse.gmf.runtime.notation.View) + */ + public boolean isAbleToManageView(final View view) { + return true; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrdering.java new file mode 100644 index 0000000000..194f06cf21 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrdering.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.List; + +import org.eclipse.gmf.runtime.notation.View; + +/** + * Represents a type that is able to order views. + * + * @author ymortier + */ +public interface ViewOrdering extends GridViewOrdering { + + /** + * Return the views ordered by this {@link ViewOrdering}. The resulted list + * might not be modified by the caller. An unmodifiable list will throws an + * {@link UnsupportedOperationException} if the caller attempts to modify + * the list. + * + * @return the views ordered by this {@link ViewOrdering}. + */ + List<View> getSortedViews(); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrderingHint.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrderingHint.java new file mode 100644 index 0000000000..659694719c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrderingHint.java @@ -0,0 +1,155 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.gmf.runtime.notation.View; + +/** + * A singleton that provides {@link ViewOrdering}. + * + * @author ymortier + */ +public final class ViewOrderingHint { + + /** The shared instance. */ + private static ViewOrderingHint instance = new ViewOrderingHint(); + + /** All view ordering. */ + private Map<View, ViewOrdering> viewOrderingsStock; + + /** All edge view ordering. */ + private Map<View, AbstractEdgeViewOrdering> edgeViewOrderingsStock; + + /** + * Avoid instantiation from external. + */ + private ViewOrderingHint() { + this.viewOrderingsStock = new WeakHashMap<View, ViewOrdering>(); + this.edgeViewOrderingsStock = new WeakHashMap<View, AbstractEdgeViewOrdering>(); + } + + /** + * Return the shared instance. + * + * @return the shared instance. + */ + public static ViewOrderingHint getInstance() { + return instance; + } + + /** + * Put a new {@link ViewOrdering}. + * + * @param view + * the container. + * @param viewOrdering + * the view ordering. + */ + public void putViewOrdering(final View view, final ViewOrdering viewOrdering) { + final ViewOrdering oldOrdering = this.viewOrderingsStock.get(view); + if (oldOrdering == null) { + this.viewOrderingsStock.put(view, viewOrdering); + } else if (oldOrdering instanceof SimpleCompositeViewOrdering) { + final SimpleCompositeViewOrdering compositeViewOrdering = (SimpleCompositeViewOrdering) oldOrdering; + compositeViewOrdering.addViewOrdering(viewOrdering); + } else if (oldOrdering != viewOrdering) { + final SimpleCompositeViewOrdering compositeViewOrdering = new SimpleCompositeViewOrdering(); + compositeViewOrdering.addViewOrdering(oldOrdering); + compositeViewOrdering.addViewOrdering(viewOrdering); + this.viewOrderingsStock.put(view, compositeViewOrdering); + } + } + + /** + * Put a new {@link AbstractEdgeViewOrdering}. + * + * @param view + * the container. + * @param edgeViewOrdering + * the view ordering. + */ + public void putEdgeViewOrdering(final View view, final AbstractEdgeViewOrdering edgeViewOrdering) { + final ViewOrdering oldOrdering = this.edgeViewOrderingsStock.get(view); + if (oldOrdering == null) { + this.edgeViewOrderingsStock.put(view, edgeViewOrdering); + } else if (oldOrdering instanceof SimpleCompositeEdgeViewOrdering) { + final SimpleCompositeEdgeViewOrdering compositeViewOrdering = (SimpleCompositeEdgeViewOrdering) oldOrdering; + compositeViewOrdering.addEdgeViewOrdering(edgeViewOrdering); + } else if (oldOrdering instanceof AbstractEdgeViewOrdering) { + final SimpleCompositeEdgeViewOrdering compositeViewOrdering = new SimpleCompositeEdgeViewOrdering(); + compositeViewOrdering.addEdgeViewOrdering((AbstractEdgeViewOrdering) oldOrdering); + compositeViewOrdering.addEdgeViewOrdering(edgeViewOrdering); + this.edgeViewOrderingsStock.put(view, compositeViewOrdering); + } + } + + /** + * Return the view ordering to use by the specified container edit part. + * + * @param view + * the edit part. + * @return the view ordering to use by the specified container edit part. + */ + public ViewOrdering consumeViewOrdering(final View view) { + final ViewOrdering result = this.viewOrderingsStock.get(view); + this.viewOrderingsStock.remove(view); + return result; + } + + /** + * Return the edge view ordering to use by the specified container edit + * part. + * + * @param view + * the edit part. + * @return the edge view ordering to use by the specified container edit + * part. + */ + public AbstractEdgeViewOrdering consumeEdgeViewOrdering(final View view) { + final AbstractEdgeViewOrdering result = this.edgeViewOrderingsStock.get(view); + this.edgeViewOrderingsStock.remove(view); + return result; + } + + /** + * Removes all registered Hints for the given {@link View}. + * + * @param view + * the {@link View} to remove all registered Hints from + */ + public void removeAllHints(View view) { + removeHints(view); + } + + /** + * Removes all registered Hints for the given View and all its children. + * + * @param view + * the view to remove the registered Hints from + */ + private void removeHints(View view) { + if (this.viewOrderingsStock.get(view) != null) { + this.viewOrderingsStock.remove(view); + } + if (this.edgeViewOrderingsStock.get(view) != null) { + this.edgeViewOrderingsStock.remove(view); + } + for (Object child : view.getChildren()) { + if (child instanceof View) { + removeHints((View) child); + } + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrderingProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrderingProvider.java new file mode 100644 index 0000000000..e25f57c756 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/ordering/ViewOrderingProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.ordering; + +import org.eclipse.sirius.description.DiagramElementMapping; + +/** + * Represents an object that provides {@link ViewOrdering}. + * + * @author ymortier + */ +public interface ViewOrderingProvider { + + /** + * Return <code>true</code> if this provider provides {@link ViewOrdering}s. + * for the specified mapping. + * + * @param mapping + * the mapping. + * @return <code>true</code> if this provider provides {@link ViewOrdering} + * s. for the specified mapping. + */ + boolean provides(DiagramElementMapping mapping); + + /** + * Return the {@link ViewOrdering} to use for the specified + * {@link DiagramElementMapping}. + * + * @param mapping + * the mapping. + * @return the {@link ViewOrdering} to use for the specified + * {@link DiagramElementMapping}. + */ + ViewOrdering getViewOrdering(DiagramElementMapping mapping); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/AbstractLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/AbstractLayoutProvider.java new file mode 100644 index 0000000000..f056ba4902 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/AbstractLayoutProvider.java @@ -0,0 +1,547 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.service.IOperation; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.DslCommonPlugin; +import org.eclipse.sirius.common.tools.api.profiler.ProfilerTask; +import org.eclipse.sirius.common.ui.SiriusTransPlugin; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.ui.tools.api.layout.PinHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.provider.LayoutService; + +/** + * An abstract layout provider. + * + * @author ymortier + */ +public abstract class AbstractLayoutProvider extends AbstractLayoutEditPartProvider { + + /** + * Modeler Category. + */ + public static final String GENERIC_MODELER_CAT = "Generic Modeler"; + + /** + * Arrange all. + */ + public static final ProfilerTask ARRANGE_ALL = new ProfilerTask(GENERIC_MODELER_CAT, "Arrange All"); + + /** Map all views with a its associated {@link ChangeBoundsRequest}. */ + protected Map<View, List<Request>> viewsToChangeBoundsRequest; + + /** + * Create a new {@link AbstractLayoutProvider}. + * + */ + public AbstractLayoutProvider() { + this.viewsToChangeBoundsRequest = new HashMap<View, List<Request>>(); + } + + /** + * Set the map that maps all views with a its associated + * {@link ChangeBoundsRequest}. + * + * @param viewsToChangeBoundsRequest + * Map all views with a its associated + * {@link ChangeBoundsRequest}. + */ + public void setViewsToChangeBoundsRequest(final Map<View, List<Request>> viewsToChangeBoundsRequest) { + this.viewsToChangeBoundsRequest = viewsToChangeBoundsRequest; + } + + /** + * Return the map that maps all views with a its associated + * {@link ChangeBoundsRequest}. + * + * @return the map that maps all views with a its associated + * {@link ChangeBoundsRequest}. + */ + public Map<View, List<Request>> getViewsToChangeBoundsRequest() { + return viewsToChangeBoundsRequest; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(org.eclipse.gef.GraphicalEditPart, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public final Command layoutEditParts(final GraphicalEditPart containerEditPart, final IAdaptable layoutHint) { + DslCommonPlugin.PROFILER.startWork(ARRANGE_ALL); + + this.getViewsToChangeBoundsRequest().clear(); + + /* + * If we are asked to layout a whole diagram, check if a specific + * provider has been registered for it (through the + * org.eclipse.sirius.common.ui.airLayoutProvider extension point), and use + * that provider if there is. + */ + if (containerEditPart instanceof DiagramEditPart) { + final DiagramEditPart diagramEditPart = (DiagramEditPart) containerEditPart; + final LayoutProvider diagramLayoutProvider = getDiagramLayoutProvider(diagramEditPart, layoutHint); + if (diagramLayoutProvider != null) { + final Command command = getCommandFromDiagramLayoutProvider(diagramLayoutProvider, diagramEditPart, layoutHint); + command.setLabel("Arrange all"); + DslCommonPlugin.PROFILER.stopWork(ARRANGE_ALL); + return command; + } + } + + final Command command = buildCommand(containerEditPart, layoutHint); + command.setLabel("Arrange all"); + this.getViewsToChangeBoundsRequest().clear(); + AbstractLayoutProvider.resetWrappedCommand(command); + DslCommonPlugin.PROFILER.stopWork(ARRANGE_ALL); + return command; + } + + /** + * Get the the diagram layout provider if there is one. + * + * @param diagramEditPart + * the diagram edit part + * @param layoutHint + * the layout hint + * @return the diagram layout provider if there is one, <code>null</code> + * otherwise + */ + private LayoutProvider getDiagramLayoutProvider(final DiagramEditPart diagramEditPart, final IAdaptable layoutHint) { + final LayoutProvider candidate = LayoutService.getProvider(diagramEditPart); + if (candidate != null && candidate.isDiagramLayoutProvider()) { + return candidate; + } else { + return null; + } + } + + private Command getCommandFromDiagramLayoutProvider(final LayoutProvider diagramLayoutProvider, final DiagramEditPart diagramEditPart, final IAdaptable layoutHint) { + Command command = null; + final AbstractLayoutEditPartProvider layoutProvider = diagramLayoutProvider.getLayoutNodeProvider(diagramEditPart); + try { + final Method method = layoutProvider.getClass().getMethod("layoutEditParts", new Class[] { GraphicalEditPart.class, IAdaptable.class }); + Assert.isNotNull(method); + /* this code seems to avoid an infinite loop */ + if (method.getDeclaringClass() != AbstractLayoutProvider.class) { + command = layoutProvider.layoutEditParts(diagramEditPart, layoutHint); + } + } catch (final SecurityException e) { + SiriusTransPlugin.getPlugin().error("Layout Error", e); + } catch (final NoSuchMethodException e) { + SiriusTransPlugin.getPlugin().error("Layout Error", e); + } + if (command == null) { + final List<?> children = diagramEditPart.getChildren(); + command = layoutProvider.layoutEditParts(children, layoutHint); + } + return command; + } + + /** + * Build the command which performs the layout of all the edit parts in the + * specified container. + * <p> + * The resulting command is a compound command built recursively by calling + * {@link #layoutEditParts(List, IAdaptable)} on all the descendants of the + * specified container and then on the container itself. + * + * @param containerEditPart + * the container to layout. + * @param layoutHint + * the hint to use. + * @return a command to execute to perform the layout of the container and + * all its descendants. + */ + private Command buildCommand(final GraphicalEditPart containerEditPart, final IAdaptable layoutHint) { + final CompoundCommand cc = new CompoundCommand(); + // Depth-first recursion. + for (final IGraphicalEditPart graphicalEditPart : Iterables.filter(containerEditPart.getChildren(), IGraphicalEditPart.class)) { + final Command command = buildCommand(graphicalEditPart, layoutHint); + if (command.canExecute()) { + cc.add(command); + } + } + // Base case: layout the container's direct children. + final Command command = this.layoutEditParts(containerEditPart.getChildren(), layoutHint); + if (command.canExecute()) { + cc.add(command); + } + return cc; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.common.core.service.IProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) + */ + public boolean provides(final IOperation operation) { + return false; + } + + /** + * Search a request for the specified edit part and of the specified type. + * + * @param editPart + * the edit part. + * @param requestType + * the type of the request. + * @return the found request. + */ + protected Request findRequest(final IGraphicalEditPart editPart, final Object requestType) { + if (editPart != null) { + return findRequest(editPart.getNotationView(), requestType); + } + return null; + } + + /** + * Search a request for the specified view and of the specified type. + * + * @param notationView + * the view. + * @param requestType + * the type of the request. + * @return the found request. + */ + protected Request findRequest(final View notationView, final Object requestType) { + final List<Request> requests = this.getViewsToChangeBoundsRequest().get(notationView); + Request result = null; + if (requests != null) { + final Iterator<Request> iterRequests = requests.iterator(); + while (iterRequests.hasNext() && result == null) { + final Request currentRequest = iterRequests.next(); + if (currentRequest.getType() != null && currentRequest.getType().equals(requestType)) { + result = currentRequest; + } + } + } + return result; + } + + /** + * Create a command with the specified request. + * + * @param request + * the request. + * @param editPart + * the edit part. + * @return an executable command. + */ + protected Command buildCommandWrapper(final Request request, final EditPart editPart) { + if (editPart instanceof IGraphicalEditPart) { + List<Request> requests = this.getViewsToChangeBoundsRequest().get(((IGraphicalEditPart) editPart).getNotationView()); + if (requests == null) { + requests = new LinkedList<Request>(); + this.getViewsToChangeBoundsRequest().put(((IGraphicalEditPart) editPart).getNotationView(), requests); + } + requests.add(request); + } + return new CommandWrapper(request, editPart); + } + + /** + * Tests whether an edit part should be considered as pinned (fixed size and + * location) during the layout. + * + * @param part + * the edit part. + * @return <code>true</code> if the edit part should be considered as + * pinned. + */ + protected boolean isPinned(final IGraphicalEditPart part) { + boolean isPinned = false; + if (part.resolveSemanticElement() instanceof DDiagramElement) { + DDiagramElement dDiagramElement = (DDiagramElement) part.resolveSemanticElement(); + isPinned = new PinHelper().isPinned(dDiagramElement); + } + return isPinned; + } + + /** + * Wraps a GMF Command. + * + * @author ymortier + */ + protected static class CommandWrapper extends Command { + + /** The wrapped command. */ + private Command executedCommand; + + /** The request. */ + private final Request request; + + /** The edit part. */ + private final EditPart editPart; + + /** + * Create a new Command wrapper. + * + * @param request + * the request. + * @param editPart + * the edit part. + */ + public CommandWrapper(final Request request, final EditPart editPart) { + this.request = request; + this.editPart = editPart; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.commands.Command#execute() + */ + @Override + public void execute() { + final Command cmd = this.getWrappedCommand(); + cmd.execute(); + executedCommand = cmd; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.commands.Command#canExecute() + */ + @Override + public boolean canExecute() { + return this.getWrappedCommand().canExecute(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.commands.Command#canUndo() + */ + @Override + public boolean canUndo() { + return this.getWrappedCommand().canUndo(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.commands.Command#undo() + */ + @Override + public void undo() { + this.getWrappedCommand().undo(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.commands.Command#redo() + */ + @Override + public void redo() { + this.getWrappedCommand().redo(); + } + + private Command getWrappedCommand() { + final Command result; + if (executedCommand == null) { + final Command cmd = editPart.getCommand(request); + if (cmd == null) { + result = UnexecutableCommand.INSTANCE; + } else { + result = cmd; + } + } else { + result = executedCommand; + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String getLabel() { + return getWrappedCommand().getLabel(); + } + + /** + * Returns the request that is causing the command. + * + * @return the request + */ + public Request getRequest() { + return request; + } + + /** + * Return the associated edit part. + * + * @return the editPart + */ + public EditPart getEditPart() { + return editPart; + } + } + + /** + * Return the relative bounds of the specified edit part. + * + * @param graphicalEditPart + * the edit part. + * @return the bounds of the specified edit part. + */ + protected Rectangle getBounds(final IGraphicalEditPart graphicalEditPart) { + Object existingRequest = this.findRequest(graphicalEditPart, org.eclipse.gef.RequestConstants.REQ_RESIZE); + final Rectangle figureBounds = new Rectangle(graphicalEditPart.getFigure().getBounds()); + if (existingRequest instanceof ChangeBoundsRequest) { + final ChangeBoundsRequest changeBoundsRequest = (ChangeBoundsRequest) existingRequest; + if (changeBoundsRequest.getSizeDelta() != null) { + adjustBounds(figureBounds, changeBoundsRequest); + } + } + existingRequest = this.findRequest(graphicalEditPart, org.eclipse.gef.RequestConstants.REQ_MOVE); + if (existingRequest instanceof ChangeBoundsRequest) { + final ChangeBoundsRequest changeBoundsRequest = (ChangeBoundsRequest) existingRequest; + if (changeBoundsRequest.getMoveDelta() != null) { + figureBounds.x += changeBoundsRequest.getMoveDelta().x; + figureBounds.y += changeBoundsRequest.getMoveDelta().y; + } + } + return figureBounds; + } + + private void adjustBounds(final Rectangle figureBounds, final ChangeBoundsRequest changeBoundsRequest) { + switch (changeBoundsRequest.getResizeDirection()) { + case PositionConstants.NORTH: + figureBounds.height += changeBoundsRequest.getSizeDelta().height; + figureBounds.y -= changeBoundsRequest.getSizeDelta().height; + break; + case PositionConstants.SOUTH: + figureBounds.height += changeBoundsRequest.getSizeDelta().height; + break; + case PositionConstants.EAST: + figureBounds.width += changeBoundsRequest.getSizeDelta().width; + break; + case PositionConstants.WEST: + figureBounds.width += changeBoundsRequest.getSizeDelta().width; + figureBounds.x -= changeBoundsRequest.getSizeDelta().width; + break; + case PositionConstants.NORTH_EAST: + figureBounds.height += changeBoundsRequest.getSizeDelta().height; + figureBounds.y -= changeBoundsRequest.getSizeDelta().height; + figureBounds.width += changeBoundsRequest.getSizeDelta().width; + break; + case PositionConstants.NORTH_WEST: + figureBounds.height += changeBoundsRequest.getSizeDelta().height; + figureBounds.y -= changeBoundsRequest.getSizeDelta().height; + figureBounds.width += changeBoundsRequest.getSizeDelta().width; + figureBounds.x -= changeBoundsRequest.getSizeDelta().width; + break; + case PositionConstants.SOUTH_WEST: + figureBounds.height += changeBoundsRequest.getSizeDelta().height; + figureBounds.width += changeBoundsRequest.getSizeDelta().width; + figureBounds.x -= changeBoundsRequest.getSizeDelta().width; + break; + default: + figureBounds.height += changeBoundsRequest.getSizeDelta().height; + figureBounds.width += changeBoundsRequest.getSizeDelta().width; + } + } + + /** + * Return the first figure of type <code>type</code> that is the figure + * <code>searchRoot</code> or a child of <code>searchRoot</code>. + * + * @param searchRoot + * the root figure to start the search. + * @param type + * the type of the figure to return. + * @return Return the first figure of type <code>type</code> that is the + * figure <code>searchRoot</code> or a child of + * <code>searchRoot</code>. + */ + protected IFigure findChild(final IFigure searchRoot, final Class<?> type) { + IFigure result = null; + if (type.isInstance(searchRoot)) { + result = searchRoot; + } + final Iterator<?> iterChildren = searchRoot.getChildren().iterator(); + while (iterChildren.hasNext() && result == null) { + final IFigure child = (IFigure) iterChildren.next(); + result = findChild(child, type); + } + return result; + } + + /** + * Return all edit parts contained in root + root. + * + * @param root + * the root edit part + * @return all edit parts contained in root + root. + */ + @SuppressWarnings("unchecked") + public List<EditPart> getAllEditParts(final EditPart root) { + final Set<EditPart> editParts = new HashSet<EditPart>(); + editParts.add(root); + if (root instanceof GraphicalEditPart) { + editParts.addAll(((GraphicalEditPart) root).getSourceConnections()); + editParts.addAll(((GraphicalEditPart) root).getTargetConnections()); + } + final Iterator<EditPart> iterChildren = root.getChildren().iterator(); + while (iterChildren.hasNext()) { + final EditPart next = iterChildren.next(); + editParts.addAll(getAllEditParts(next)); + } + return new ArrayList<EditPart>(editParts); + } + + private static void resetWrappedCommand(final Command command) { + if (command instanceof CommandWrapper) { + ((CommandWrapper) command).executedCommand = null; + } else if (command instanceof CompoundCommand) { + final Object[] children = ((CompoundCommand) command).getChildren(); + for (Object element : children) { + if (element instanceof Command) { + AbstractLayoutProvider.resetWrappedCommand((Command) element); + } + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/CompoundLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/CompoundLayoutProvider.java new file mode 100644 index 0000000000..74e08b3916 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/CompoundLayoutProvider.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gmf.runtime.common.core.service.IOperation; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; + +/** + * A compound layout provider. + * + * @author ymortier + */ +public class CompoundLayoutProvider extends AbstractLayoutProvider { + + /** the list of delegated providers. */ + private final List<AbstractLayoutEditPartProvider> delegatedProviders = new LinkedList<AbstractLayoutEditPartProvider>(); + + /** The list of providers that provides for the next operation. */ + private final List<AbstractLayoutEditPartProvider> realDelegatedProviders = new LinkedList<AbstractLayoutEditPartProvider>(); + + /** + * Adds a provider. + * + * @param provider + * the provider to add. + */ + public void addProvider(final AbstractLayoutEditPartProvider provider) { + this.delegatedProviders.add(provider); + this.realDelegatedProviders.add(provider); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(java.util.List, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) { + final CompoundCommand cc = new CompoundCommand(); + final ArrayList<AbstractLayoutEditPartProvider> inverse = new ArrayList<AbstractLayoutEditPartProvider>(this.realDelegatedProviders); + final Iterator<AbstractLayoutEditPartProvider> iterRealDelegatedProviders = inverse.listIterator(); + while (iterRealDelegatedProviders.hasNext()) { + final AbstractLayoutEditPartProvider provider = iterRealDelegatedProviders.next(); + if (provider instanceof AbstractLayoutProvider) { + ((AbstractLayoutProvider) provider).setViewsToChangeBoundsRequest(this.getViewsToChangeBoundsRequest()); + } + final Command command = provider.layoutEditParts(new ArrayList(selectedObjects), layoutHint); + if (command != null && command.canExecute()) { + cc.add(command); + } + if (provider instanceof AbstractLayoutProvider) { + this.getViewsToChangeBoundsRequest().putAll(((AbstractLayoutProvider) provider).getViewsToChangeBoundsRequest()); + } + } + this.getViewsToChangeBoundsRequest().clear(); + return cc; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.provider.AbstractLayoutProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) + */ + @Override + public boolean provides(final IOperation operation) { + boolean result = false; + this.realDelegatedProviders.clear(); + final Iterator<AbstractLayoutEditPartProvider> iterDelegatedProviders = this.delegatedProviders.listIterator(); + while (iterDelegatedProviders.hasNext()) { + final AbstractLayoutEditPartProvider currentProvider = iterDelegatedProviders.next(); + if (currentProvider.provides(operation)) { + result = true; + this.realDelegatedProviders.add(currentProvider); + } + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/DefaultLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/DefaultLayoutProvider.java new file mode 100644 index 0000000000..f1fe125832 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/DefaultLayoutProvider.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gmf.runtime.common.core.service.IOperation; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.ILayoutNodeOperation; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.LayoutType; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.common.tools.DslCommonPlugin; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.provider.LayoutService; + +/** + * Default layout provider. It delegates the operation using the + * {@link LayoutService}. + * + * @author ymortier + */ +public class DefaultLayoutProvider extends AbstractLayoutProvider { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.provider.AbstractLayoutProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) + */ + @Override + public boolean provides(final IOperation operation) { + if (operation instanceof ILayoutNodeOperation) { + final ILayoutNodeOperation layoutNodeOperation = (ILayoutNodeOperation) operation; + final IAdaptable layoutHint = layoutNodeOperation.getLayoutHint(); + final String layoutType = (String) layoutHint.getAdapter(String.class); + if (LayoutType.DEFAULT.equals(layoutType)) { + return true; + } + } + return false; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(java.util.List, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) { + DslCommonPlugin.PROFILER.startWork(ARRANGE_ALL); + final CompoundCommand cc = new CompoundCommand(); + final Map<EditPart, List<EditPart>> allLayouts = this.split(selectedObjects); + final Iterator<Entry<EditPart, List<EditPart>>> iterEntries = allLayouts.entrySet().iterator(); + while (iterEntries.hasNext()) { + final Entry<EditPart, List<EditPart>> currentEntry = iterEntries.next(); + final IGraphicalEditPart container = (IGraphicalEditPart) currentEntry.getKey(); + final List<EditPart> children = currentEntry.getValue(); + if (container instanceof DiagramEditPart) { + // addConnection. + final DiagramEditPart diagramEditPart = (DiagramEditPart) container; + children.addAll(diagramEditPart.getConnections()); + } + final LayoutProvider airLayoutProvider = LayoutService.getProvider(container); + if (airLayoutProvider != null) { + final AbstractLayoutEditPartProvider gmfLayoutProvider = airLayoutProvider.getLayoutNodeProvider(container); + if (gmfLayoutProvider != null) { + if (gmfLayoutProvider instanceof AbstractLayoutProvider) { + ((AbstractLayoutProvider) gmfLayoutProvider).setViewsToChangeBoundsRequest(this.getViewsToChangeBoundsRequest()); + } + cc.add(gmfLayoutProvider.layoutEditParts(children, layoutHint)); + if (gmfLayoutProvider instanceof AbstractLayoutProvider) { + this.getViewsToChangeBoundsRequest().putAll(((AbstractLayoutProvider) gmfLayoutProvider).getViewsToChangeBoundsRequest()); + // ((AbstractAirLayoutProvider) + // gmfLayoutProvider).getViewsToChangeBoundsRequest().clear(); + } + } + } + } + // this.getViewsToChangeBoundsRequest().clear(); + DslCommonPlugin.PROFILER.stopWork(ARRANGE_ALL); + return cc; + } + + /** + * Split all edit parts according to their container. Returns a map + * (container -> children to layout). + * + * @param editParts + * the + * @return a map (container -> children to layout). + */ + protected Map<EditPart, List<EditPart>> split(final List<EditPart> editParts) { + final Map<EditPart, List<EditPart>> result = new HashMap<EditPart, List<EditPart>>(); + final Iterator<EditPart> iterEditParts = editParts.listIterator(); + while (iterEditParts.hasNext()) { + final Object next = iterEditParts.next(); + if (next instanceof ConnectionEditPart) { + final ConnectionEditPart connectionEditPart = (ConnectionEditPart) next; + final EditPart container = this.getDiagramEditPart(connectionEditPart); + DefaultLayoutProvider.addToMap(result, container, connectionEditPart); + } else if (next instanceof IGraphicalEditPart) { + final IGraphicalEditPart graphicalEditPart = (IGraphicalEditPart) next; + final EditPart container = graphicalEditPart.getParent(); + DefaultLayoutProvider.addToMap(result, container, graphicalEditPart); + } else { + iterEditParts.remove(); + } + } + return result; + } + + /** + * Add the object <code>value</code> to the list of objects that is in the + * map <code>map</code> for the key <code>key</code>. + * + * @param map + * the map. + * @param key + * the key. + * @param value + * the value to add. + * @since 2.0 + */ + protected static void addToMap(final Map<EditPart, List<EditPart>> map, final EditPart key, final EditPart value) { + List<EditPart> values = map.get(key); + if (values == null) { + values = new LinkedList<EditPart>(); + map.put(key, values); + } + values.add(value); + } + + /** + * Remove all elements that are not an instance of the specified type from + * the List <code>list</code>. + * + * @param list + * the list to filter. + * @param type + * the type to get. + */ + protected static void retainType(final List<?> list, final Class<?> type) { + final Iterator<?> iterElements = list.iterator(); + while (iterElements.hasNext()) { + if (!type.isInstance(iterElements.next())) { + iterElements.remove(); + } + } + } + + /** + * Return the diagram edit part of the specified edit part or + * <code>null</code> if the diagram edit part can not be retrieved. + * + * @param editPart + * an edit part. + * @return the diagram edit part of the specified edit part or + * <code>null</code> if the diagram edit part can not be retrieved. + */ + protected DiagramEditPart getDiagramEditPart(final EditPart editPart) { + EditPart current = editPart; + while (current != null) { + if (current instanceof DiagramEditPart) { + return (DiagramEditPart) current; + } + if (current instanceof ConnectionEditPart) { + current = ((ConnectionEditPart) current).getSource(); + } else { + current = current.getParent(); + } + } + return null; + } + + /** + * Return all GMF views of the specified {@link EditPart}s. The key of the + * map is a view and the value is the associated {@link EditPart}. + * + * @param editParts + * the edit parts. + * @return all GMF views of the specified {@link EditPart}s. + * @param <T> + * a class which implements {@link EditPart} + */ + protected <T extends EditPart> Map<View, T> getViews(final List<T> editParts) { + final Map<View, T> result = new HashMap<View, T>(); + final Iterator<T> iterEditParts = editParts.iterator(); + while (iterEditParts.hasNext()) { + final T next = iterEditParts.next(); + if (next.getModel() instanceof View) { + result.put((View) next.getModel(), next); + } + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/ExtendableLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/ExtendableLayoutProvider.java new file mode 100644 index 0000000000..dbcb2f96f4 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/ExtendableLayoutProvider.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.graph.Node; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutExtender; + +/** + * This interface is the lowest common denominator a layout provider needs to + * respect to be able to leverage the layout extensions. + * + * @author cbrun + * + */ +public interface ExtendableLayoutProvider { + /** + * return true if the layout handle connectable list items. + * + * @return true if the layout handle connectable list items. + */ + boolean handleConnectableListItems(); + + /** + * should return the node metric. + * + * @param node + * a node. + * @return the node metric + */ + Rectangle provideNodeMetrics(final Node node); + + /** + * return the layout extender used. + * + * @return the layout extender used. + */ + LayoutExtender getExtender(); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/GridLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/GridLayoutProvider.java new file mode 100644 index 0000000000..b2f731b039 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/GridLayoutProvider.java @@ -0,0 +1,555 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutExtender; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.GridView; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.GridView.Column; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.GridViewOrdering; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.SimpleViewOrdering; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.ViewOrderingHint; + +/** + * A layout provider that arranges views according to the specified + * {@link GridViewOrdering}. + * + * @author ymortier + */ +public class GridLayoutProvider extends DefaultLayoutProvider implements ExtendableLayoutProvider { + + /** Each case of the grid has the same dimension. */ + public static final int SAME_DIMENSION = 1; + + /** Each case dimension is computed by line or column. */ + public static final int DIMENSION_BY_LINE_OR_COLUMN = 2; + + /** Each case has its owned dimension. */ + public static final int FREE_DIMENSION = 3; + + /** Cache location to improve performances. */ + private Map<Point, Point> locationsCache = new HashMap<Point, Point>(); + + /** The mode of the column size. */ + private int columnSizeMode = GridLayoutProvider.SAME_DIMENSION; + + /** + * The mode of the line size. + */ + private int lineSizeMode = GridLayoutProvider.SAME_DIMENSION; + + /** The padding. */ + private Insets padding = new Insets(30, 30, 30, 30); + + /** + * Maps each column with its max width. + * <ul> + * <li>Key : Integer, index of the column.</li> + * <li>Value : Integer, the max width.</li> + * </ul> + */ + private Map<Integer, Integer> maxWidths = new HashMap<Integer, Integer>(); + + /** + * Maps each line with its max height. + * <ul> + * <li>Key : Integer, index of the line.</li> + * <li>Value : Integer, the max height.</li> + * </ul> + */ + private Map<Integer, Integer> maxHeights = new HashMap<Integer, Integer>(); + + private final LayoutExtender extender = new LayoutExtender(this); + + /** + * Return the padding. + * + * @return the padding. + */ + public Insets getPadding() { + return padding; + } + + /** + * Returns the column size computing style. The style can be + * <ul> + * <li>{@link #SAME_DIMENSION}</li> + * <li>{@link #DIMENSION_BY_LINE_OR_COLUMN}</li> + * <li>{@link #FREE_DIMENSION}</li> + * </ul> + * + * @return the column size computing style. + */ + public int getColumnSizeMode() { + return columnSizeMode; + } + + /** + * Sets the column size computing style. The style can be + * <ul> + * <li>{@link #SAME_DIMENSION}</li> + * <li>{@link #DIMENSION_BY_LINE_OR_COLUMN}</li> + * <li>{@link #FREE_DIMENSION}</li> + * </ul> + * + * @param columnSizeMode + * the column size computing style. + */ + public void setColumnSizeMode(final int columnSizeMode) { + if (columnSizeMode < GridLayoutProvider.SAME_DIMENSION || columnSizeMode > GridLayoutProvider.FREE_DIMENSION) { + throw new IllegalArgumentException("Unknown mode : " + columnSizeMode); + } + this.columnSizeMode = columnSizeMode; + } + + /** + * Returns the line size computing style. The style can be + * <ul> + * <li>{@link #SAME_DIMENSION}</li> + * <li>{@link #DIMENSION_BY_LINE_OR_COLUMN}</li> + * <li>{@link #FREE_DIMENSION}</li> + * </ul> + * + * @return the line size computing style. + */ + public int getLineSizeMode() { + return lineSizeMode; + } + + /** + * Sets the line size computing style. The style can be + * <ul> + * <li>{@link #SAME_DIMENSION}</li> + * <li>{@link #DIMENSION_BY_LINE_OR_COLUMN}</li> + * <li>{@link #FREE_DIMENSION}</li> + * </ul> + * + * @param lineSizeMode + * the line size computing style. + */ + public void setLineSizeMode(final int lineSizeMode) { + if (lineSizeMode < GridLayoutProvider.SAME_DIMENSION || lineSizeMode > GridLayoutProvider.FREE_DIMENSION) { + throw new IllegalArgumentException("Unknown mode : " + lineSizeMode); + } + this.lineSizeMode = lineSizeMode; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.provider.DefaultLayoutProvider#layoutEditParts(java.util.List, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public Command layoutEditParts(@SuppressWarnings("rawtypes") + final List selectedObjects, final IAdaptable layoutHint) { + extender.startLayouting(); + this.maxHeights.clear(); + this.maxWidths.clear(); + this.locationsCache.clear(); + final Iterator<?> iterEditParts = selectedObjects.iterator(); + final List<View> views = new ArrayList<View>(selectedObjects.size()); + final Map<View, IGraphicalEditPart> viewsToEditPartMap = new HashMap<View, IGraphicalEditPart>(); + while (iterEditParts.hasNext()) { + final Object next = iterEditParts.next(); + if (next instanceof ShapeEditPart && !(next instanceof IBorderItemEditPart)) { + final ShapeEditPart shapeEditPart = (ShapeEditPart) next; + final View view = shapeEditPart.getNotationView(); + viewsToEditPartMap.put(view, shapeEditPart); + views.add(view); + } else { + iterEditParts.remove(); + } + } + GridViewOrdering viewOrdering = ViewOrderingHint.getInstance().consumeViewOrdering(getContainerEditPart(selectedObjects).getNotationView()); + if (viewOrdering == null) { + viewOrdering = new SimpleViewOrdering(); + } + viewOrdering.setViews(views); + final GridView gridView = viewOrdering.getSortedViewsAsGrid(); + + final Command command = this.buildGrid(gridView, viewsToEditPartMap); + // + // Layout the container. + final Command layoutContainer = this.getLayoutContainerCommand(getContainerEditPart(selectedObjects), gridView); + + this.maxHeights.clear(); + this.maxWidths.clear(); + this.locationsCache.clear(); + + final CompoundCommand cc = new CompoundCommand(); + cc.add(command); + if (layoutContainer != null && layoutContainer.canExecute()) { + cc.add(layoutContainer); + } + return cc; + } + + /** + * Return the command that layout the container of this grid. + * + * @param gridView + * the grid. + * @return the command that layout the container of this grid. + */ + private Command getLayoutContainerCommand(final IGraphicalEditPart containerEditPart, final GridView gridView) { + final Dimension containerMinDimension = getLayoutDimensions(gridView); + final Dimension expand = new Dimension(0, 0); + + final Dimension containerBounds = this.getBounds(containerEditPart).getSize(); + if (containerBounds.width < containerMinDimension.width) { + expand.width = containerMinDimension.width - containerBounds.width; + } + if (containerBounds.height < containerMinDimension.height) { + expand.height = containerMinDimension.height - containerBounds.height; + } + + if (expand.width > 0 || expand.height > 0) { + final Object existingRequest = this.viewsToChangeBoundsRequest.get(containerEditPart.getNotationView()); + if (existingRequest == null) { + final ChangeBoundsRequest cbr = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_RESIZE); + cbr.setResizeDirection(PositionConstants.SOUTH_EAST); + cbr.setSizeDelta(expand); + final Command command = this.buildCommandWrapper(cbr, containerEditPart); + return command; + } else if (existingRequest instanceof ChangeBoundsRequest) { + final ChangeBoundsRequest cbr = (ChangeBoundsRequest) existingRequest; + cbr.setResizeDirection(PositionConstants.SOUTH_EAST); + cbr.setSizeDelta(expand); + } + } + return null; + } + + /** + * Creates the command that arranges all views in a grid format. + * + * @param gridView + * the grid. + * @param viewsToEditPart + * map each view to its edit part. + * @return the command that arranges all views in a grid format. + */ + protected Command buildGrid(final GridView gridView, final Map<View, IGraphicalEditPart> viewsToEditPart) { + final CompoundCommand cc = new CompoundCommand(); + if (this.getColumnSizeMode() != GridLayoutProvider.FREE_DIMENSION || this.getLineSizeMode() != GridLayoutProvider.FREE_DIMENSION) { + // init size. + final Iterator<Column> iterColumns = gridView.iteratorColumns(); + while (iterColumns.hasNext()) { + final Column currentColumn = iterColumns.next(); + final Integer columnIndex = Integer.valueOf(currentColumn.getIndex()); + this.maxWidths.put(columnIndex, Integer.valueOf(-1)); + for (int i = 0; i < currentColumn.getViewsCount(); i++) { + final Integer lineIndex = Integer.valueOf(i); + final View view = currentColumn.getViewAt(i); + Dimension size = new Dimension(0, 0); + if (view != null) { + final IGraphicalEditPart editPart = viewsToEditPart.get(view); + size = getBounds(editPart).getSize(); + } + if (maxHeights.get(lineIndex) == null) { + maxHeights.put(lineIndex, Integer.valueOf(-1)); + } + if (size.width > maxWidths.get(columnIndex).intValue()) { + maxWidths.put(columnIndex, Integer.valueOf(size.width)); + } + if (size.height > maxHeights.get(lineIndex).intValue()) { + maxHeights.put(lineIndex, Integer.valueOf(size.height)); + } + + } + } + } + // + // Create command + final Iterator<Column> iterColumns = gridView.iteratorColumns(); + while (iterColumns.hasNext()) { + final Column currentColumn = iterColumns.next(); + for (int i = 0; i < currentColumn.getViewsCount(); i++) { + final Point caseLocation = getLocation(currentColumn.getIndex(), i, gridView, viewsToEditPart); + if (currentColumn.getViewAt(i) != null) { + final IGraphicalEditPart editPart = viewsToEditPart.get(currentColumn.getViewAt(i)); + final Point newLocation = new Point(caseLocation.x + getPadding().left, caseLocation.y + getPadding().top); + final Command command = createChangeBoundsCommand(editPart, newLocation); + if (command != null && command.canExecute()) { + cc.add(command); + } + } + } + } + + return cc; + } + + /** + * Create the command that changes the bounds of the specified edit part. + * + * @param editPart + * the specified edit part + * + * @param newPosition + * the new position of the figure. + * @return the command that changes the bounds of the specified edit part. + */ + protected Command createChangeBoundsCommand(final IGraphicalEditPart editPart, final Point newPosition) { + Command result = null; + final Object existingRequest = this.findRequest(editPart, org.eclipse.gef.RequestConstants.REQ_MOVE); + ChangeBoundsRequest request = null; + double scale = 1.0; + if (editPart.getRoot() instanceof DiagramRootEditPart) { + final ZoomManager zoomManager = ((DiagramRootEditPart) editPart.getRoot()).getZoomManager(); + scale = zoomManager.getZoom(); + } + if (existingRequest instanceof ChangeBoundsRequest) { + request = (ChangeBoundsRequest) existingRequest; + } else if (existingRequest == null) { + request = new ChangeBoundsRequest(); + request.setEditParts(editPart); + result = this.buildCommandWrapper(request, editPart); + // this.getViewsToChangeBoundsRequest().put(editPart.getNotationView(), + // request); + } + if (newPosition != null) { + final Rectangle intrinsicBounds = editPart.getFigure().getBounds(); + final Dimension delta = newPosition.getDifference(intrinsicBounds.getLocation()); + delta.width *= scale; + delta.height *= scale; + if ((delta.width != 0 || delta.height != 0) && request != null) { + request.setMoveDelta(new Point(delta.width, delta.height)); + request.setLocation(newPosition); + request.setType(org.eclipse.gef.RequestConstants.REQ_MOVE); + result = this.buildCommandWrapper(request, editPart); + // String nameToDisplay = editPart.getModel().toString(); + // if (editPart.getModel() instanceof + // org.eclipse.gmf.runtime.notation.Node) { + // org.eclipse.gmf.runtime.notation.Node n = + // (org.eclipse.gmf.runtime.notation.Node) editPart.getModel(); + // nameToDisplay = n.getElement().toString(); + // } + // System.out.println("newPosition:" + newPosition + + // ", figureBounds: " + intrinsicBounds + " --> " + + // nameToDisplay); + extender.getUpdatedBounds().put(editPart, new Rectangle(newPosition, intrinsicBounds.getSize())); + } else { + // no move, return null. + return null; + } + } + return result; + } + + /** + * Return the max dimension. + * + * @return the max dimension. + */ + private Dimension getDiagramMaxDimension() { + int maxHeight = -1; + final int maxWidth = -1; + final Iterator<Integer> iterHeights = this.maxHeights.values().iterator(); + while (iterHeights.hasNext()) { + final Integer currentValue = iterHeights.next(); + if (currentValue.intValue() > maxHeight) { + maxHeight = currentValue.intValue(); + } + } + final Iterator<Integer> iterWidths = this.maxWidths.values().iterator(); + while (iterWidths.hasNext()) { + final Integer currentValue = iterWidths.next(); + if (currentValue.intValue() > maxWidth) { + maxHeight = currentValue.intValue(); + } + } + return new Dimension(maxWidth, maxHeight); + } + + private Dimension getLayoutDimensions(final GridView gridView) { + final Dimension result = new Dimension(); + switch (this.getLineSizeMode()) { + case FREE_DIMENSION: + case DIMENSION_BY_LINE_OR_COLUMN: + final Iterator<Integer> iterHeights = this.maxHeights.values().iterator(); + while (iterHeights.hasNext()) { + final Integer currentValue = iterHeights.next(); + result.height += currentValue.intValue(); + result.height += getPadding().top; + result.height += getPadding().bottom; + } + break; + case SAME_DIMENSION: + result.height = getDiagramMaxDimension().height + (getPadding().top + getPadding().bottom) * gridView.getLinesCount(); + break; + default: + break; + } + + switch (this.getColumnSizeMode()) { + case FREE_DIMENSION: + case DIMENSION_BY_LINE_OR_COLUMN: + final Iterator<Integer> iterWidths = this.maxWidths.values().iterator(); + while (iterWidths.hasNext()) { + final Integer currentValue = iterWidths.next(); + result.width += currentValue.intValue(); + result.width += getPadding().left; + result.width += getPadding().top; + } + break; + case SAME_DIMENSION: + result.width = getDiagramMaxDimension().width + (getPadding().left + getPadding().right) * gridView.getColumnsCount(); + break; + default: + break; + } + return result; + } + + // FIXME do a real implementation. + /** + * Get the the first parent editPart of selected objects. + * + * @param selectedObjects + * the list of selected object + * @return an {@link EditPart} instance + */ + protected IGraphicalEditPart getContainerEditPart(final List<?> selectedObjects) { + if (selectedObjects != null && !selectedObjects.isEmpty()) { + return (IGraphicalEditPart) ((EditPart) selectedObjects.iterator().next()).getParent(); + } + return null; + } + + /** + * Return the location of the case (x, y). + * + * @param x + * the x coordinate. + * @param y + * the y coordinate. + * @param gridView + * the grid. + * @param viewsToEditParts + * maps each view with ots corresponding edit part. + * @return the location of the case + */ + public Point getLocation(final int x, final int y, final GridView gridView, final Map<View, IGraphicalEditPart> viewsToEditParts) { + final Point point = new Point(x, y); + final Point cachedLocation = this.locationsCache.get(point); + if (cachedLocation != null) { + return cachedLocation; + } + final Point location = new Point(); + if (x == 0 && y == 0) { + location.x = 0; + location.y = 0; + } else { + if (x == 0) { + location.x = 0; + } else { + final Point left = getLocation(x - 1, y, gridView, viewsToEditParts); + final View leftView = gridView.getViewAt(x - 1, y); + Dimension size = new Dimension(0, 0); + if (leftView != null) { + final IGraphicalEditPart leftEditPart = viewsToEditParts.get(leftView); + size = this.getBounds(leftEditPart).getSize(); + } + switch (this.getColumnSizeMode()) { + case FREE_DIMENSION: + location.x = left.x + size.width + getPadding().left + getPadding().right; + break; + case SAME_DIMENSION: + location.x = left.x + getDiagramMaxDimension().width + getPadding().left + getPadding().right; + break; + case DIMENSION_BY_LINE_OR_COLUMN: + location.x = left.x + this.maxWidths.get(Integer.valueOf(x - 1)).intValue() + getPadding().right + getPadding().left; + break; + default: + break; + } + } + if (y == 0) { + location.y = 0; + } else { + final Point top = getLocation(x, y - 1, gridView, viewsToEditParts); + final View topView = gridView.getViewAt(x, y - 1); + Dimension size = new Dimension(0, 0); + if (topView != null) { + final IGraphicalEditPart topEditPart = viewsToEditParts.get(topView); + size = this.getBounds(topEditPart).getSize(); + } + switch (this.getLineSizeMode()) { + case FREE_DIMENSION: + location.y = top.y + size.height + getPadding().top + getPadding().bottom; + break; + case SAME_DIMENSION: + location.y = top.y + getDiagramMaxDimension().height + getPadding().top + getPadding().bottom; + break; + case DIMENSION_BY_LINE_OR_COLUMN: + location.y = top.y + this.maxHeights.get(Integer.valueOf(y - 1)).intValue() + getPadding().top + getPadding().bottom; + break; + default: + break; + } + } + } + this.locationsCache.put(point, location); + return location; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider#getExtender() + */ + public LayoutExtender getExtender() { + return extender; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider#handleConnectableListItems() + */ + public boolean handleConnectableListItems() { + return false; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider#provideNodeMetrics(org.eclipse.draw2d.graph.Node) + */ + public Rectangle provideNodeMetrics(Node node) { + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/InlineEdgeLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/InlineEdgeLayoutProvider.java new file mode 100644 index 0000000000..520ab5545d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/InlineEdgeLayoutProvider.java @@ -0,0 +1,940 @@ +/******************************************************************************* + * Copyright (c) 2007, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.FreeformViewport; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gmf.runtime.common.core.service.IOperation; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; +import org.eclipse.gmf.runtime.diagram.ui.requests.SetAllBendpointRequest; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.AbstractEdgeViewOrdering; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.ViewOrderingHint; + +/** + * <p> + * This provider layouts edge "inline" along nodes. The client can choose the + * side of the node to use ({@link PositionConstants}), and the start position + * of the line. + * <p> + * <p> + * Here are the possible sides that can be used : + * <ul> + * <li>{@link PositionConstants#NORTH} : the top of the node.</li> + * <li>{@link PositionConstants#WEST} : the left of the node.</li> + * <li>{@link PositionConstants#EAST} : the right of the node.</li> + * <li>{@link PositionConstants#SOUTH} : the bottom of the node.</li> + * + * TODOYMO implements + * <li>{@link PositionConstants#NSEW} : all sides of the node.</li> + * <li>{@link PositionConstants#SOUTH_EAST}, + * {@link PositionConstants#SOUTH_WEST}, {@link PositionConstants#NORTH_EAST}, + * {@link PositionConstants#NORTH_WEST} : the combination of the two positions.</li> + * END TODOYMO + * <li>any other combination : compute the better side according to the source + * and target nodes.</li> + * </ul> + * </p> + * <p> + * Here are the possible start positions : + * <ul> + * <li>{@link PositionConstants#TOP}</li> + * <li>{@link PositionConstants#BOTTOM}</li> + * <li>{@link PositionConstants#RIGHT}</li> + * <li>{@link PositionConstants#LEFT}</li> + * <li>{@link PositionConstants#CENTER} (horizontal)</li> + * <li>{@link PositionConstants#MIDDLE} (vertical)</li> + * </ul> + * </p> + * <p> + * The two values <code>changeNodeHeight</code> and + * <code>changeNodeWitdth</code> indicate if this layout provider can change the + * dimension of nodes. + * </p> + * + * FIXME to terminate. + * + * @author ymortier + */ +public class InlineEdgeLayoutProvider extends DefaultLayoutProvider { + /** The default padding. */ + private static final Insets DEFAULT_PADDING = new Insets(30, 30, 10, 30); + + /** + * Map each Connection with its {@link MoveEdgeDescriptor} instance. + */ + protected Map connectionsToMoveEdgeDescriptor = new HashMap(); + + /** The side to use. */ + private int side; + + /** The position of the first edge. */ + private int start; + + /** <code>true</code> if the height of nodes can be changed. */ + private boolean changeNodeHeight; + + /** <code>true</code> if the width of nodes can be changed. */ + private boolean changeNodeWidth; + + /** The padding. */ + private Insets paddings = InlineEdgeLayoutProvider.DEFAULT_PADDING; + + /** + * The alignment ({@link PositionConstants#HORIZONTAL} or + * {@link PositionConstants#VERTICAL} or {@link PositionConstants#NONE}). + * Default is {@link PositionConstants#NONE} + */ + private int alignment = PositionConstants.NONE; + + /** + * Set the side to use. + * + * @param side + * the side to use. + */ + public void setSide(final int side) { + this.side = side; + } + + /** + * Return the side to use. + * + * @return the side to use. + */ + public int getSide() { + return side; + } + + /** + * Set the start position. + * + * @param start + * the start position. + */ + public void setStart(final int start) { + this.start = start; + } + + /** + * Return the start position. + * + * @return the start position. + */ + public int getStart() { + return start; + } + + /** + * <code>true</code> if the height of nodes can be changed. + * + * @param changeNodeHeight + * <code>true</code> if the height of nodes can be changed. + */ + public void setChangeNodeHeight(final boolean changeNodeHeight) { + this.changeNodeHeight = changeNodeHeight; + } + + /** + * <code>true</code> if the width of nodes can be changed. + * + * @param changeNodeWidth + * <code>true</code> if the width of nodes can be changed. + */ + public void setChangeNodeWidth(final boolean changeNodeWidth) { + this.changeNodeWidth = changeNodeWidth; + } + + /** + * Return <code>true</code> if the height of nodes can be changed. + * + * @return <code>true</code> if the height of nodes can be changed. + */ + public boolean isChangeNodeHeight() { + return changeNodeHeight; + } + + /** + * Return <code>true</code> if the width of nodes can be changed. + * + * @return <code>true</code> if the width of nodes can be changed. + */ + public boolean isChangeNodeWidth() { + return changeNodeWidth; + } + + /** + * Set the paddings. + * + * @param paddings + * the paddings. + */ + public void setPaddings(final Insets paddings) { + if (paddings != null) { + this.paddings = paddings; + } else { + this.paddings = InlineEdgeLayoutProvider.DEFAULT_PADDING; + } + } + + /** + * Return the paddings. + * + * @return the paddings. + */ + public Insets getPaddings() { + return paddings; + } + + /** + * Return the alignment. + * + * @return the alignment. + */ + public int getAlignment() { + return alignment; + } + + /** + * Set the alignment. Possible alignments are : + * <ul> + * <li> {@link PositionConstants#HORIZONTAL} : start.x = end.x</li> + * <li> {@link PositionConstants#VERTICAL} : start.y = end.y</li> + * <li> {@link PositionConstants#NONE}</li> + * </ul> + * + * @param alignment + * the alignment. + */ + public void setAlignment(final int alignment) { + this.alignment = alignment; + switch (this.alignment) { + case PositionConstants.VERTICAL: + case PositionConstants.HORIZONTAL: + case PositionConstants.NONE: + break; + default: + this.alignment = PositionConstants.NONE; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.provider.DefaultLayoutProvider#layoutEditParts(java.util.List, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public Command layoutEditParts(@SuppressWarnings("rawtypes") + final List selectedObjects, final IAdaptable layoutHint) { + final CompoundCommand cc = new CompoundCommand(); + + this.connectionsToMoveEdgeDescriptor.clear(); + + @SuppressWarnings("unchecked") + final Map<EditPart, List<EditPart>> containerToChildren = this.split(selectedObjects); + final Iterator<Entry<EditPart, List<EditPart>>> iterLayouts = containerToChildren.entrySet().iterator(); + while (iterLayouts.hasNext()) { + final Entry<EditPart, List<EditPart>> currentLayout = iterLayouts.next(); + final IGraphicalEditPart container = (IGraphicalEditPart) currentLayout.getKey(); + final List<EditPart> children = currentLayout.getValue(); + DefaultLayoutProvider.retainType(children, ConnectionEditPart.class); + final AbstractEdgeViewOrdering viewOrdering = ViewOrderingHint.getInstance().consumeEdgeViewOrdering(container.getNotationView()); + final Command command = this.createChangeBoundsCommand(children, viewOrdering); + if (command != null && command.canExecute()) { + cc.add(command); + } + } + return cc; + } + + /** + * Create the command that changes the bounds of the specified connections. + * + * @param connections + * the connections to layout (instances of + * {@link ConnectionEditPart}). + * @param viewOrdering + * the view ordering. + * @return the command that changes the bounds of the specified connections. + */ + protected Command createChangeBoundsCommand(final List connections, final AbstractEdgeViewOrdering viewOrdering) { + final CompoundCommand cc = new CompoundCommand(); + + final Iterator<EditPart> iterNodes = this.getNodesEditPart(connections).iterator(); + + while (iterNodes.hasNext()) { + final IGraphicalEditPart currentEditPart = (IGraphicalEditPart) iterNodes.next(); + final List<ConnectionEditPart> connectionsToInit = new ArrayList<ConnectionEditPart>(currentEditPart.getSourceConnections().size() + currentEditPart.getTargetConnections().size()); + connectionsToInit.addAll(currentEditPart.getSourceConnections()); + connectionsToInit.addAll(currentEditPart.getTargetConnections()); + DefaultLayoutProvider.retainType(connections, ConnectionEditPart.class); + this.initEdgesStep(currentEditPart, viewOrdering, connectionsToInit); + } + // + // Manage alignement ! + this.align(viewOrdering); + // + // Create commands. + final Iterator iterDescriptors = this.connectionsToMoveEdgeDescriptor.values().iterator(); + while (iterDescriptors.hasNext()) { + final MoveEdgeDescriptor currentDescriptor = (MoveEdgeDescriptor) iterDescriptors.next(); + final Command command = this.createChangeEdgeBoundsCommand(currentDescriptor); + if (command != null && command.canExecute()) { + cc.add(command); + } + } + + return cc; + } + + /** + * Return all edit parts that are a source or a target of one connection of + * <code>connections</code>. + * + * @param connections + * the connections. + * @return all edit parts that are a source or a target of one connection of + * <code>connections</code>. + */ + protected Set<EditPart> getNodesEditPart(final List<ConnectionEditPart> connections) { + final Set<EditPart> result = new HashSet<EditPart>(); + final Iterator<ConnectionEditPart> iterConnections = connections.iterator(); + while (iterConnections.hasNext()) { + final ConnectionEditPart connectionEditPart = iterConnections.next(); + if (connectionEditPart.getSource() instanceof IGraphicalEditPart) { + result.add(connectionEditPart.getSource()); + } + if (connectionEditPart.getTarget() instanceof IGraphicalEditPart) { + result.add(connectionEditPart.getTarget()); + } + } + return result; + } + + /** + * Init the {@link MoveEdgeDescriptor} of the specified connector. + * + * @param connector + * the edit part that is the source or the target of connections + * to init. + * @param ordering + * the ordering. + * @param connections + * the connections to layout for this connector (instances of + * {@link ConnectionEditPart}). + */ + protected void initEdgesStep(final IGraphicalEditPart connector, final AbstractEdgeViewOrdering ordering, final List<ConnectionEditPart> connections) { + final Map<View, ConnectionEditPart> viewsToConnections = this.getViews(connections); + ordering.setConnector(connector.getNotationView()); + ordering.setViews(viewsToConnections.keySet()); + final Iterator<View> iterViews = ordering.getSortedViews().iterator(); + int step = 0; + while (iterViews.hasNext()) { + final View currentView = iterViews.next(); + final ConnectionEditPart currentConnection = viewsToConnections.get(currentView); + + MoveEdgeDescriptor moveEdgeDescriptor = (MoveEdgeDescriptor) this.connectionsToMoveEdgeDescriptor.get(currentConnection); + if (moveEdgeDescriptor == null) { + // The connection has no MoveEdgeDescriptor. + // Let's create one. + moveEdgeDescriptor = new MoveEdgeDescriptor(currentConnection); + this.connectionsToMoveEdgeDescriptor.put(currentConnection, moveEdgeDescriptor); + } + + if (connector.getSourceConnections().contains(currentConnection)) { + // source + moveEdgeDescriptor.setSourceStep(step); + } else { + // target + moveEdgeDescriptor.setTargetStep(step); + } + step++; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.provider.DefaultLayoutProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) + */ + @Override + public boolean provides(final IOperation operation) { + return false; + } + + /** + * Create the command that changes the bounds of the specified edge. + * + * @param moveEdgeDescriptor + * the instance that describes how to move the edge. + * @return the created command + */ + protected Command createChangeEdgeBoundsCommand(final MoveEdgeDescriptor moveEdgeDescriptor) { + // the connection edit part. + final ConnectionEditPart connectionEditPart = moveEdgeDescriptor.getConnectionEditPart(); + + final Point sourcePoint = moveEdgeDescriptor.getSourceLocation(true); + final Point targetPoint = moveEdgeDescriptor.getTargetLocation(true); + + final PointList points = new PointList(2); + points.addPoint(sourcePoint); + points.addPoint(targetPoint); + + final Point ref1 = new Point(0, 0); + final Point ref2 = new Point(0, 0); + + final FreeformViewport viewport = this.getFreeformViewport(moveEdgeDescriptor.connectionEditPart.getConnectionFigure().getSourceAnchor().getOwner()); + final Point delta = viewport.getViewLocation(); + + // the zoom. + double scale = 1.0; + if (moveEdgeDescriptor.getConnectionEditPart().getRoot() instanceof DiagramRootEditPart) { + final ZoomManager zoomManager = ((DiagramRootEditPart) moveEdgeDescriptor.getConnectionEditPart().getRoot()).getZoomManager(); + scale = zoomManager.getZoom(); + } + + if (moveEdgeDescriptor.getConnectionEditPart().getSource() instanceof IGraphicalEditPart) { + final IGraphicalEditPart graphicalEditPart = (IGraphicalEditPart) moveEdgeDescriptor.getConnectionEditPart().getSource(); + ref1.y = this.getBounds(graphicalEditPart).getSize().height / 2; + } + if (moveEdgeDescriptor.getConnectionEditPart().getTarget() instanceof IGraphicalEditPart) { + final IGraphicalEditPart graphicalEditPart = (IGraphicalEditPart) moveEdgeDescriptor.getConnectionEditPart().getTarget(); + ref2.y = this.getBounds(graphicalEditPart).getSize().height / 2; + } + + final Point source = connectionEditPart.getConnectionFigure().getSourceAnchor().getReferencePoint().getTranslated(delta); + source.x = (int) ((double) source.x * (double) 1 / scale); + source.y = (int) ((double) source.y * (double) 1 / scale); + + final Point target = connectionEditPart.getConnectionFigure().getTargetAnchor().getReferencePoint().getTranslated(delta); + target.x = (int) ((double) target.x * (double) 1 / scale); + target.y = (int) ((double) target.y * (double) 1 / scale); + + final SetAllBendpointRequest setAllBendpointRequest = new SetAllBendpointRequest(RequestConstants.REQ_SET_ALL_BENDPOINT, points, source, target); + + final Command command = this.buildCommandWrapper(setAllBendpointRequest, connectionEditPart); + if (command != null && command.canExecute()) { + // this.getViewsToChangeBoundsRequest().put(connectionEditPart.getNotationView(), + // bendpointRequest); + } + return command; + } + + /** + * Returns the {@link FreeformViewport} that owned this figure. + * + * @return the {@link FreeformViewport} that owned this figure. + */ + private FreeformViewport getFreeformViewport(final IFigure figure) { + IFigure current = figure; + while (!(current instanceof FreeformViewport) && current != null) { + current = current.getParent(); + } + return (FreeformViewport) current; + } + + /** + * Create and return a command that resize connectors. + * + * @return and return a command that resize connectors. + */ + protected Command createResizeConnectorsCommand() { + final CompoundCommand cc = new CompoundCommand(); + return cc; + } + + /** + * Describe how an edge should be located. + * + * @author ymortier + */ + protected class MoveEdgeDescriptor { + + /** The edge to move. */ + private ConnectionEditPart connectionEditPart; + + /** The source step. */ + private int sourceStep; + + /** The target step. */ + private int targetStep; + + /** + * Create a new {@link MoveEdgeDescriptor}. + * + * @param connectionEditPart + * the connection. + */ + public MoveEdgeDescriptor(final ConnectionEditPart connectionEditPart) { + this.connectionEditPart = connectionEditPart; + } + + /** + * Return the source step. + * + * @return the source step. + */ + public int getSourceStep() { + return sourceStep; + } + + /** + * Return the target step. + * + * @return the target step. + */ + public int getTargetStep() { + return targetStep; + } + + /** + * Define the source step. + * + * @param sourceStep + * the source step. + */ + public void setSourceStep(final int sourceStep) { + this.sourceStep = sourceStep; + } + + /** + * Define the target step. + * + * @param targetStep + * the target step. + */ + public void setTargetStep(final int targetStep) { + this.targetStep = targetStep; + } + + /** + * Return the connection edit part. + * + * @return the connection edit part. + */ + public ConnectionEditPart getConnectionEditPart() { + return connectionEditPart; + } + + /** + * Return the location of the source of this edge according to its + * <code>sourceStep</code>. + * + * @param relative + * if <code>true</code> then the returned location is + * relative to the source figure, the location is an absolute + * location otherwise. + * @return the location of the source of this edge according to its + * <code>sourceStep</code>. + */ + public Point getSourceLocation(final boolean relative) { + final int pos = InlineEdgeLayoutProvider.this.computePos(this.sourceStep); + final Rectangle sourceBounds = getBounds((IGraphicalEditPart) connectionEditPart.getSource()); + final Point sourceLocation = InlineEdgeLayoutProvider.this.computePrimaryPoint(pos, sourceBounds); + if (!relative) { + sourceLocation.translate(sourceBounds.getLocation()); + } + return sourceLocation; + } + + /** + * Return the location of the target of this edge according to its + * <code>targetStep</code>. + * + * @param relative + * if <code>true</code> then the returned location is + * relative to the target figure, the location is an absolute + * location otherwise. + * @return the location of the target of this edge according to its + * <code>sourceStep</code>. + */ + public Point getTargetLocation(final boolean relative) { + final int pos = InlineEdgeLayoutProvider.this.computePos(this.targetStep); + final Rectangle targetBounds = getBounds((IGraphicalEditPart) connectionEditPart.getTarget()); + final Point targetLocation = InlineEdgeLayoutProvider.this.computePrimaryPoint(pos, targetBounds); + if (!relative) { + targetLocation.translate(targetBounds.getLocation()); + } + return targetLocation; + } + + /** + * Approximate the new <code>sourceStep</code> assuming the + * <code>desiredLocation</code>. + * + * @param desiredLocation + * the desired source location. + * @param relative + * <code>true</code> if <code>desiredLocation</code> is + * relative to the source figure. + */ + public void approximateSourceStep(final Point desiredLocation, final boolean relative) { + final Rectangle sourceBounds = getBounds((IGraphicalEditPart) connectionEditPart.getSource()); + final Point location = desiredLocation.getCopy(); + if (!relative) { + location.x = location.x - sourceBounds.x; + location.y = location.y - sourceBounds.y; + } + final int pos = posFromPrimaryPoint(location, sourceBounds); + this.sourceStep = stepFromPos(pos); + } + + /** + * Approximate the new <code>targetStep</code> assuming the + * <code>desiredLocation</code>. + * + * @param desiredLocation + * the desired target location. + * @param relative + * <code>true</code> if <code>desiredLocation</code> is + * relative to the target figure. + */ + public void approximateTargetStep(final Point desiredLocation, final boolean relative) { + final Rectangle targetBounds = getBounds((IGraphicalEditPart) connectionEditPart.getTarget()); + final Point location = desiredLocation.getCopy(); + if (!relative) { + location.x = location.x - targetBounds.x; + location.y = location.y - targetBounds.y; + } + final int pos = posFromPrimaryPoint(location, targetBounds); + this.targetStep = stepFromPos(pos); + } + + } + + private int computePos(final int current) { + + int result = current; + + switch (this.side) { + case PositionConstants.EAST: + case PositionConstants.WEST: + case PositionConstants.EAST_WEST: + result = this.getPaddings().top + (this.getPaddings().top + this.getPaddings().bottom) * current; + break; + case PositionConstants.NORTH: + case PositionConstants.SOUTH: + case PositionConstants.NORTH_SOUTH: + result = this.getPaddings().left + (this.getPaddings().left + this.getPaddings().right) * current; + break; + default: + break; + } + return result; + } + + private int stepFromPos(final int pos) { + + int result = pos; + + switch (this.side) { + case PositionConstants.EAST: + case PositionConstants.WEST: + case PositionConstants.EAST_WEST: + result = (int) ((pos - this.getPaddings().top) / ((double) this.getPaddings().top + this.getPaddings().bottom)); + break; + case PositionConstants.NORTH: + case PositionConstants.SOUTH: + case PositionConstants.NORTH_SOUTH: + result = (int) ((pos - this.getPaddings().left) / ((double) this.getPaddings().left + this.getPaddings().right)); + break; + default: + break; + } + return result; + } + + private Point computePrimaryPoint(final int pos, final Rectangle connectorBounds) { + final Point result = new Point(); + switch (this.getSide()) { + case PositionConstants.EAST: + result.x = 0; + switch (this.getStart()) { + case PositionConstants.BOTTOM: + result.y = connectorBounds.height - pos; + break; + case PositionConstants.MIDDLE: + result.y = connectorBounds.height / 2 + pos; + break; + default: + result.y = pos; + } + break; + case PositionConstants.WEST: + case PositionConstants.EAST_WEST: + result.x = connectorBounds.width; + switch (this.getStart()) { + case PositionConstants.BOTTOM: + result.y = connectorBounds.height - pos; + break; + case PositionConstants.MIDDLE: + result.y = connectorBounds.height / 2 + pos; + break; + default: + result.y = pos; + break; + } + break; + case PositionConstants.NORTH: + result.y = 0; + switch (this.getStart()) { + case PositionConstants.RIGHT: + result.x = connectorBounds.width - pos; + break; + case PositionConstants.CENTER: + result.x = connectorBounds.width / 2 + pos; + break; + default: + break; + } + break; + case PositionConstants.SOUTH: + case PositionConstants.NORTH_SOUTH: + result.y = connectorBounds.height; + switch (this.getStart()) { + case PositionConstants.RIGHT: + result.x = connectorBounds.width - pos; + break; + case PositionConstants.CENTER: + result.x = connectorBounds.width / 2 + pos; + break; + default: + break; + } + break; + default: + break; + } + return result; + } + + private int posFromPrimaryPoint(final Point pt, final Rectangle connectorBounds) { + + int result = 0; + + switch (this.getSide()) { + case PositionConstants.EAST: + case PositionConstants.WEST: + case PositionConstants.EAST_WEST: + switch (this.getStart()) { + case PositionConstants.BOTTOM: + result = connectorBounds.height - pt.y; + break; + case PositionConstants.MIDDLE: + result = pt.y - connectorBounds.height / 2; + break; + default: + result = pt.y; + break; + } + break; + case PositionConstants.NORTH: + case PositionConstants.SOUTH: + case PositionConstants.NORTH_SOUTH: + switch (this.getStart()) { + case PositionConstants.RIGHT: + result = connectorBounds.width - pt.x; + break; + case PositionConstants.CENTER: + result = pt.x - connectorBounds.width / 2; + break; + default: + result = pt.x; + break; + } + break; + default: + break; + } + return result; + } + + /** + * Align the edge according to the specified <code>alignment</code> value. + * + * @param viewOrdering + * the edge view ordering + */ + protected void align(final AbstractEdgeViewOrdering viewOrdering) { + switch (this.getAlignment()) { + case PositionConstants.HORIZONTAL: + this.horizontalAlign(viewOrdering); + break; + case PositionConstants.VERTICAL: + this.verticalAlign(viewOrdering); + break; + default: + // do nothing. + } + } + + // start.y = end.y + private void verticalAlign(final AbstractEdgeViewOrdering viewOrdering) { + final Map connectorsToStepRearrangeMin = this.initConnectorsToStepRearrangeMin(); + boolean again = true; + while (again) { + again = false; + final Iterator iterMoveDescriptors = this.connectionsToMoveEdgeDescriptor.values().iterator(); + while (iterMoveDescriptors.hasNext()) { + final MoveEdgeDescriptor currentDescriptor = (MoveEdgeDescriptor) iterMoveDescriptors.next(); + final Point sourcePoint = currentDescriptor.getSourceLocation(false); + final Point targetPoint = currentDescriptor.getTargetLocation(false); + if (sourcePoint.y < targetPoint.y - 29 || sourcePoint.y > targetPoint.y + 29) { + if (sourcePoint.y < targetPoint.y) { + sourcePoint.y = targetPoint.y; + currentDescriptor.approximateSourceStep(sourcePoint, false); + final IGraphicalEditPart connector = (IGraphicalEditPart) currentDescriptor.getConnectionEditPart().getSource(); + final int connectorStart = ((Integer) connectorsToStepRearrangeMin.get(connector.getNotationView())).intValue(); + final int result = this.rearrangeConnector(connector, connectorStart, viewOrdering); + connectorsToStepRearrangeMin.put(connector.getNotationView(), Integer.valueOf(result)); + if (result != connectorStart) { + again = true; + } + } else { + targetPoint.y = sourcePoint.y; + currentDescriptor.approximateTargetStep(targetPoint, false); + final IGraphicalEditPart connector = (IGraphicalEditPart) currentDescriptor.getConnectionEditPart().getTarget(); + final int connectorStart = ((Integer) connectorsToStepRearrangeMin.get(connector.getNotationView())).intValue(); + final int result = this.rearrangeConnector(connector, connectorStart, viewOrdering); + connectorsToStepRearrangeMin.put(connector.getNotationView(), Integer.valueOf(result)); + if (result != connectorStart) { + again = true; + } + } + } + } + } + } + + // start.y = end.y + private void horizontalAlign(final AbstractEdgeViewOrdering viewOrdering) { + boolean again = true; + while (again) { + final Iterator iterMoveDescriptors = this.connectionsToMoveEdgeDescriptor.values().iterator(); + while (iterMoveDescriptors.hasNext()) { + final MoveEdgeDescriptor currentDescriptor = (MoveEdgeDescriptor) iterMoveDescriptors.next(); + final Point sourcePoint = currentDescriptor.getSourceLocation(false); + final Point targetPoint = currentDescriptor.getTargetLocation(false); + if (sourcePoint.x < targetPoint.x - 10 || sourcePoint.x > targetPoint.x + 10) { + if (sourcePoint.x < targetPoint.x) { + sourcePoint.x = targetPoint.x; + currentDescriptor.approximateSourceStep(sourcePoint, false); + again = true; + } else { + targetPoint.x = sourcePoint.x; + currentDescriptor.approximateTargetStep(targetPoint, false); + again = true; + } + + } + } + } + } + + private Map initConnectorsToStepRearrangeMin() { + final Map result = new HashMap(); + final Iterator iterDescriptors = this.connectionsToMoveEdgeDescriptor.values().iterator(); + while (iterDescriptors.hasNext()) { + final MoveEdgeDescriptor currentDescriptor = (MoveEdgeDescriptor) iterDescriptors.next(); + final ConnectionEditPart connectionEditPart = currentDescriptor.getConnectionEditPart(); + if (connectionEditPart.getSource() instanceof IGraphicalEditPart) { + final View sourceConnector = ((IGraphicalEditPart) connectionEditPart.getSource()).getNotationView(); + if (!result.containsKey(sourceConnector)) { + result.put(sourceConnector, Integer.valueOf(0)); + } + } + if (connectionEditPart.getTarget() instanceof IGraphicalEditPart) { + final View targetConnector = ((IGraphicalEditPart) connectionEditPart.getTarget()).getNotationView(); + if (!result.containsKey(targetConnector)) { + result.put(targetConnector, Integer.valueOf(0)); + } + } + } + return result; + } + + private int rearrangeConnector(final IGraphicalEditPart connector, final int connectorStart, final AbstractEdgeViewOrdering viewOrdering) { + int result = connectorStart; + final List<EditPart> connections = new ArrayList<EditPart>(connector.getSourceConnections().size() + connector.getTargetConnections().size()); + Iterables.addAll(connections, Iterables.filter(connector.getSourceConnections(), EditPart.class)); + Iterables.addAll(connections, Iterables.filter(connector.getTargetConnections(), EditPart.class)); + + final Map<View, EditPart> views = this.getViews(connections); + viewOrdering.setConnector(connector.getNotationView()); + viewOrdering.setViews(views.keySet()); + + final List<View> sortedViews = viewOrdering.getSortedViews(); + + final Iterator<View> iterViews = sortedViews.iterator(); + int current = 0; + while (iterViews.hasNext()) { + final View currentView = iterViews.next(); + final ConnectionEditPart connectionEditPart = (ConnectionEditPart) views.get(currentView); + final MoveEdgeDescriptor descriptor = (MoveEdgeDescriptor) this.connectionsToMoveEdgeDescriptor.get(connectionEditPart); + final boolean source = connectionEditPart.getSource().equals(connector); + if (source) { + // source + int step = descriptor.getSourceStep(); + if (step < current) { + step = current; + } + if (step > current) { + if (result == connectorStart) { + result = step + 1; + } + current = step; + } + descriptor.setSourceStep(step); + } else { + // target + int step = descriptor.getTargetStep(); + if (step < current) { + step = current; + } + if (step > current) { + if (result == connectorStart) { + result = step + 1; + } + current = step; + } + descriptor.setTargetStep(step); + } + current++; + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/LayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/LayoutProvider.java new file mode 100644 index 0000000000..570a5eb44b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/LayoutProvider.java @@ -0,0 +1,62 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; + +/** + * A class that provides + * {@link org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutNodeProvider} + * for a specific container. + * + * @author ymortier + */ +public interface LayoutProvider { + /** + * Simple flag to easily switch between the old/new behaviors during + * development. + */ + boolean ENABLE_BORDERED_NODES_ARRANGE_ALL = true; + + /** + * Return <code>true</code> if this provider provides a + * {@link AbstractLayoutEditPartProvider} for the specified container. A + * diagram layout provider should return <code>true</code> if container is + * an instance of + * {@link org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart}. + * + * @param container + * the container. + * @return <code>true</code> if this provider provides a + * {@link AbstractLayoutEditPartProvider} for the specified + * container. + */ + boolean provides(IGraphicalEditPart container); + + /** + * Return the {@link AbstractLayoutEditPartProvider} to use with the + * container. + * + * @param container + * the container. + * @return the {@link AbstractLayoutEditPartProvider} to use with the + * container. + */ + AbstractLayoutEditPartProvider getLayoutNodeProvider(IGraphicalEditPart container); + + /** + * Check if this provider is a diagram provider. + * + * @return <code>true</code> if this provider is a diagram provider. + */ + boolean isDiagramLayoutProvider(); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/LineLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/LineLayoutProvider.java new file mode 100644 index 0000000000..20e1976e22 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/layout/provider/LineLayoutProvider.java @@ -0,0 +1,302 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.layout.provider; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.service.IOperation; +import org.eclipse.gmf.runtime.diagram.ui.editparts.CompartmentEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.ILayoutNodeOperation; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.LayoutType; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.SimpleViewOrdering; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.ViewOrdering; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.ViewOrderingHint; + +/** + * Layout edit parts in line according to the {@link ViewOrdering} to use. + * + * @author ymortier + */ +public class LineLayoutProvider extends AbstractLayoutProvider { + + /** The default padding. */ + private static final Insets DEFAULT_PADDING = new Insets(30, 30, 30, 30); + + /** + * <code>true</code> if the line is horizontal, <code>false</code> if the + * line is vertical. + */ + private boolean horizontal = true; + + /** + * <code>true</code> if the line is horizontal, <code>false</code> if the + * line is vertical. + * + * @param horizontal + * <code>true</code> if the line is horizontal, + * <code>false</code> if the line is vertical. + */ + public void setHorizontal(final boolean horizontal) { + this.horizontal = horizontal; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(java.util.List, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) { + final Iterator<?> iterEditParts = selectedObjects.iterator(); + final List<View> views = new ArrayList<View>(selectedObjects.size()); + final Map<View, ShapeEditPart> viewsToEditPartMap = new HashMap<View, ShapeEditPart>(); + while (iterEditParts.hasNext()) { + final Object next = iterEditParts.next(); + if (next instanceof ShapeEditPart && !(next instanceof IBorderItemEditPart)) { + final ShapeEditPart shapeEditPart = (ShapeEditPart) next; + final View view = shapeEditPart.getNotationView(); + viewsToEditPartMap.put(view, shapeEditPart); + views.add(view); + } else { + iterEditParts.remove(); + } + } + ViewOrdering viewOrdering = ViewOrderingHint.getInstance().consumeViewOrdering(getContainerEditPart(selectedObjects).getNotationView()); + if (viewOrdering == null) { + // use a simple view ordering ... too bad. + viewOrdering = new SimpleViewOrdering(); + } + + viewOrdering.setViews(views); + final List<View> sortedViews = viewOrdering.getSortedViews(); + final List<ShapeEditPart> sortedEditParts = new ArrayList<ShapeEditPart>(sortedViews.size()); + final Iterator<View> iterSortedViews = sortedViews.listIterator(); + while (iterSortedViews.hasNext()) { + final View currentView = iterSortedViews.next(); + final ShapeEditPart currentEditPart = viewsToEditPartMap.get(currentView); + sortedEditParts.add(currentEditPart); + } + return createNodeChangeBoundCommands(sortedEditParts); + } + + /** + * Create the change bounds commands. + * + * @param sortedNodes + * the nodes to move. + * @return the change bounds command. + */ + protected Command createNodeChangeBoundCommands(final List<ShapeEditPart> sortedNodes) { + final CompoundCommand result = new CompoundCommand(); + final Iterator<ShapeEditPart> iterEditParts = sortedNodes.iterator(); + int currentX = 0; + while (iterEditParts.hasNext()) { + final ShapeEditPart shapeEditPart = iterEditParts.next(); + if (!(shapeEditPart instanceof IBorderItemEditPart)) { + + final View view = shapeEditPart.getNotationView(); + // the zoom. + double scale = 1.0; + if (shapeEditPart.getRoot() instanceof DiagramRootEditPart) { + final ZoomManager zoomManager = ((DiagramRootEditPart) shapeEditPart.getRoot()).getZoomManager(); + scale = zoomManager.getZoom(); + } + // + // Compute request data. + final Point ptOldLocation = shapeEditPart.getFigure().getBounds().getLocation(); + // shapeEditPart.getFigure().translateToAbsolute(ptOldLocation); + final int locationX = horizontal ? currentX + this.getPadding().left : this.getPadding().left; + final int locationY = horizontal ? this.getPadding().top : currentX + this.getPadding().top; + final Point ptLocation = new Point(locationX, locationY); + final Dimension delta = ptLocation.getDifference(ptOldLocation); + + final Object existingRequest = this.findRequest(view, org.eclipse.gef.RequestConstants.REQ_MOVE); + int step = 0; + if (existingRequest == null) { + final ChangeBoundsRequest request = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_MOVE); + request.setEditParts(shapeEditPart); + request.setMoveDelta(new Point(delta.width * scale, delta.height * scale)); + request.setLocation(new Point(ptLocation.x * scale, ptLocation.y * scale)); + step = this.horizontal ? getBounds(shapeEditPart).width : getBounds(shapeEditPart).height; + + final Command cmd = this.buildCommandWrapper(request, shapeEditPart); + if (cmd != null && cmd.canExecute()) { + result.add(cmd); + // this.getViewsToChangeBoundsRequest().put(view, + // request); + } + } else if (existingRequest instanceof ChangeBoundsRequest) { + final ChangeBoundsRequest changeBoundsRequest = (ChangeBoundsRequest) existingRequest; + changeBoundsRequest.setMoveDelta(new Point(delta.width * scale, delta.height * scale)); + changeBoundsRequest.setLocation(new Point(ptLocation.x * scale, ptLocation.y * scale)); + + step = this.horizontal ? getBounds(shapeEditPart).width : getBounds(shapeEditPart).height; + } + currentX += horizontal ? step + getPadding().right + getPadding().left : step + this.getPadding().bottom + this.getPadding().top; + + // check the size of the container. + EditPart container = shapeEditPart.getParent(); + while (container instanceof CompartmentEditPart) { + container = container.getParent(); + } + if (container instanceof ShapeEditPart) { + final ShapeEditPart containerEditPart = (ShapeEditPart) container; + + // The minimum witdh + final int minWidth = this.horizontal ? ((getPadding().left + getPadding().right) * sortedNodes.size()) + (getNodeMaxWidth(sortedNodes) * sortedNodes.size()) : getPadding().left + + getNodeMaxWidth(sortedNodes) + getPadding().right; + // The minimum height + final int minHeight = this.horizontal ? getPadding().top + this.getNodeMaxHeight(sortedNodes) + this.getPadding().bottom : ((getPadding().top + getPadding().bottom) * sortedNodes + .size()) + (this.getNodeMaxHeight(sortedNodes) * sortedNodes.size()); + + final Dimension minDimension = new Dimension(minWidth, minHeight); + + final Dimension difference = minDimension.getDifference(containerEditPart.getFigure().getBounds().getSize()); + if (difference.width > 0 || difference.height > 0) { + final Object existingContainerRequest = this.findRequest(containerEditPart, org.eclipse.gef.RequestConstants.REQ_RESIZE); // ;this.getViewsToChangeBoundsRequest().get(containerEditPart.getNotationView()); + createChangeBoundsCommand(result, existingContainerRequest, containerEditPart, difference, scale); + } + + } + + } + } + return result; + } + + private void createChangeBoundsCommand(final CompoundCommand compoundCommand, final Object existingContainerRequest, final ShapeEditPart containerEditPart, final Dimension difference, + final double scale) { + + if (existingContainerRequest == null) { + final ChangeBoundsRequest changeBoundsRequest = new ChangeBoundsRequest(); + changeBoundsRequest.setEditParts(containerEditPart); + changeBoundsRequest.setResizeDirection(PositionConstants.SOUTH_EAST); + changeBoundsRequest.setSizeDelta(new Dimension((int) (difference.width * scale), (int) (difference.height * scale))); + changeBoundsRequest.setLocation(new Point(0, 0)); + changeBoundsRequest.setType(org.eclipse.gef.RequestConstants.REQ_RESIZE); + final Command cmd = this.buildCommandWrapper(changeBoundsRequest, containerEditPart); + if (cmd.canExecute()) { + compoundCommand.add(cmd); + // this.getViewsToChangeBoundsRequest().put(containerEditPart.getNotationView(), + // changeBoundsRequest); + } + } else if (existingContainerRequest instanceof ChangeBoundsRequest) { + final ChangeBoundsRequest changeBoundsRequest = (ChangeBoundsRequest) existingContainerRequest; + changeBoundsRequest.setResizeDirection(PositionConstants.SOUTH_EAST); + changeBoundsRequest.setSizeDelta(new Dimension((int) (difference.width * scale), (int) (difference.height * scale))); + } + } + + /** + * Return the maximum width of all nodes (instances of {@link ShapeEditPart} + * ) that are in the specified list. + * + * @param nodes + * the nodes. + * @return the maximum width of all nodes that are in the specified list. + */ + protected int getNodeMaxWidth(final List<ShapeEditPart> nodes) { + int max = -1; + for (final ShapeEditPart shapeEditPart : nodes) { + final Object existingRequest = this.getViewsToChangeBoundsRequest().get(shapeEditPart.getNotationView()); + int width = shapeEditPart.getFigure().getBounds().width; + if (existingRequest instanceof ChangeBoundsRequest) { + width = width + ((ChangeBoundsRequest) existingRequest).getSizeDelta().width; + } + if (width > max) { + max = width; + } + } + return max; + } + + /** + * Return the maximum height of all nodes (instances of + * {@link ShapeEditPart}) that are in the specified list. + * + * @param nodes + * the nodes. + * @return the maximum width of all nodes that are in the specified list. + */ + protected int getNodeMaxHeight(final List<ShapeEditPart> nodes) { + int max = -1; + for (final ShapeEditPart shapeEditPart : nodes) { + final int height = this.getBounds(shapeEditPart).height; + if (height > max) { + max = height; + } + } + return max; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.provider.AbstractLayoutProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) + */ + @Override + public boolean provides(final IOperation operation) { + final View cview = getContainer(operation); + if (cview == null) { + return false; + } + final IAdaptable layoutHint = ((ILayoutNodeOperation) operation).getLayoutHint(); + final String layoutType = (String) layoutHint.getAdapter(String.class); + return LayoutType.DEFAULT.equals(layoutType); + } + + /** + * Return the padding to use. + * + * @return the padding to use. + */ + public Insets getPadding() { + return DEFAULT_PADDING; + } + + // FIXME do a real implementation. + /** + * Get the container edit part of an object list. Currently the function + * takes only the first object of the list + * + * @param selectedObjects + * the selected object + * @return the container edit part + */ + protected IGraphicalEditPart getContainerEditPart(final List<EditPart> selectedObjects) { + if (selectedObjects != null && !selectedObjects.isEmpty()) { + return (IGraphicalEditPart) (selectedObjects.iterator().next()).getParent(); + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/policy/CompoundEditPolicy.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/policy/CompoundEditPolicy.java new file mode 100644 index 0000000000..015029bdd5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/policy/CompoundEditPolicy.java @@ -0,0 +1,250 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.policy; + +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPolicy; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; + +/** + * This edit policy is composed of many edit policies. + * + * @author ymortier + */ +public class CompoundEditPolicy implements EditPolicy { + + /** The delegated edit policies. */ + private List<EditPolicy> delegatedEditPolicies = new LinkedList<EditPolicy>(); + + /** The host. */ + private EditPart host; + + /** + * if <code>true</code> then null command are added to the resulted compound + * command (so the command is unexecutable). + */ + private boolean allowNullCommand; + + /** + * <code>true</code> if the resulted command can contain null value. + * + * @param allowNullCommand + * <code>true</code> if the resulted command can contain null + * value. + */ + public void setAllowNullCommand(final boolean allowNullCommand) { + this.allowNullCommand = allowNullCommand; + } + + /** + * Returns <code>true</code> if the resulted command can contain null value. + * + * @return <code>true</code> if the resulted command can contain null value. + */ + public boolean isAllowNullCommand() { + return allowNullCommand; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#activate() + */ + public void activate() { + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext()) { + iterEditPolicies.next().activate(); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#deactivate() + */ + public void deactivate() { + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext()) { + iterEditPolicies.next().deactivate(); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#eraseSourceFeedback(org.eclipse.gef.Request) + */ + public void eraseSourceFeedback(final Request request) { + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext()) { + iterEditPolicies.next().eraseSourceFeedback(request); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#eraseTargetFeedback(org.eclipse.gef.Request) + */ + public void eraseTargetFeedback(final Request request) { + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext()) { + iterEditPolicies.next().eraseTargetFeedback(request); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#getCommand(org.eclipse.gef.Request) + */ + public Command getCommand(final Request request) { + CompoundCommand ret = null; + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.listIterator(); + while (iterEditPolicies.hasNext()) { + final EditPolicy editPolicy = iterEditPolicies.next(); + final Command command = editPolicy.getCommand(request); + if (command != null || isAllowNullCommand()) { + if (ret == null) { + ret = new CompoundCommand(); + } + ret.add(command); + } + } + /* + * If there are only null commands in the result, we should return null. + */ + boolean containsOnlyNullCommands = true; + if (ret != null) { + for (final Object c : ret.getCommands()) { + if (c != null) { + containsOnlyNullCommands = false; + break; + } + } + } + return containsOnlyNullCommands ? null : ret; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#getHost() + */ + public EditPart getHost() { + return this.host; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#getTargetEditPart(org.eclipse.gef.Request) + */ + public EditPart getTargetEditPart(final Request request) { + EditPart res = null; + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext() && res == null) { + final EditPolicy next = iterEditPolicies.next(); + res = next.getTargetEditPart(request); + } + return res; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#setHost(org.eclipse.gef.EditPart) + */ + public void setHost(final EditPart editpart) { + this.host = editpart; + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext()) { + iterEditPolicies.next().setHost(editpart); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#showSourceFeedback(org.eclipse.gef.Request) + */ + public void showSourceFeedback(final Request request) { + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext()) { + iterEditPolicies.next().showSourceFeedback(request); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#showTargetFeedback(org.eclipse.gef.Request) + */ + public void showTargetFeedback(final Request request) { + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext()) { + iterEditPolicies.next().showTargetFeedback(request); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.EditPolicy#understandsRequest(org.eclipse.gef.Request) + */ + public boolean understandsRequest(final Request request) { + boolean res = false; + final Iterator<EditPolicy> iterEditPolicies = this.delegatedEditPolicies.iterator(); + while (iterEditPolicies.hasNext() && !res) { + res = iterEditPolicies.next().understandsRequest(request); + } + return res; + } + + /** + * Adds an edit policy. + * + * @param editPolicy + * the edit policy to add. + */ + public void addEditPolicy(final EditPolicy editPolicy) { + if (editPolicy == null) { + throw new IllegalArgumentException("the edit policy is null"); + } + this.delegatedEditPolicies.add(editPolicy); + } + + /** + * Removes an edit policy. + * + * @param policy + * the policy to remove. + */ + public void removeEditPolicy(final EditPolicy policy) { + this.delegatedEditPolicies.remove(policy); + } + + /** + * Return all edit policies. + * + * @return all edit policies. + */ + public List<EditPolicy> getEditPolicies() { + return this.delegatedEditPolicies; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/AbstractPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/AbstractPropertySection.java new file mode 100644 index 0000000000..b86b64a11b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/AbstractPropertySection.java @@ -0,0 +1,370 @@ +/****************************************************************************** + * Copyright (c) 2003, 2009 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Obeo - adaptation + ****************************************************************************/ + +package org.eclipse.sirius.diagram.ui.tools.api.properties; + +import org.eclipse.core.commands.operations.OperationHistoryFactory; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.emf.edit.domain.IEditingDomainProvider; +import org.eclipse.emf.transaction.NotificationFilter; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gmf.runtime.diagram.ui.properties.sections.AbstractModelerPropertySection; +import org.eclipse.gmf.runtime.emf.ui.properties.sections.UndoableModelPropertySheetEntry; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.layout.FormAttachment; +import org.eclipse.swt.layout.FormData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IActionBars; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.ui.views.properties.IPropertySheetEntry; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.IPropertySourceProvider; +import org.eclipse.ui.views.properties.PropertySheetPage; +import org.eclipse.ui.views.properties.PropertySheetSorter; +import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; + +/** + * An abstract property section that sorts the viewer by the order returned by + * {@link IPropertySource#getPropertyDescriptors()}. + * + * @author ymortier + */ +public abstract class AbstractPropertySection extends AbstractModelerPropertySection { + + /** + * the property sheet page for this section. + */ + protected PropertySheetPage page; + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AdvancedPropertySection#createControls(org.eclipse.swt.widgets.Composite, + * org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage) + */ + @Override + public void createControls(final Composite parent, final TabbedPropertySheetPage aTabbedPropertySheetPage) { + super.createControls(parent, aTabbedPropertySheetPage); + final Composite composite = getWidgetFactory().createFlatFormComposite(parent); + FormData data = null; + + final String tableLabelStr = getTableLabel(); + CLabel tableLabel = null; + if (tableLabelStr != null && tableLabelStr.length() > 0) { + tableLabel = getWidgetFactory().createCLabel(composite, tableLabelStr); + data = new FormData(); + data.left = new FormAttachment(0, 0); + data.top = new FormAttachment(0, 0); + tableLabel.setLayoutData(data); + } + + // + // remove the other page. + page = new SortedPropertySheetPage(); + final UndoableModelPropertySheetEntry root = new UndoableModelPropertySheetEntry(OperationHistoryFactory.getOperationHistory()); + + root.setPropertySourceProvider(getPropertySourceProvider()); + page.setRootEntry(root); + + page.createControl(composite); + data = new FormData(); + data.left = new FormAttachment(0, 0); + data.right = new FormAttachment(100, 0); + if (tableLabel == null) { + data.top = new FormAttachment(0, 0); + } else { + data.top = new FormAttachment(tableLabel, 0, SWT.BOTTOM); + } + data.bottom = new FormAttachment(100, 0); + data.height = 100; + data.width = 100; + page.getControl().setLayoutData(data); + + setActionBars(aTabbedPropertySheetPage.getSite().getActionBars()); + ((SortedPropertySheetPage) page).setSorter(new AirPropertySorter()); + } + + private class AirPropertySorter extends PropertySheetSorter { + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.PropertySheetSorter#compare(org.eclipse.ui.views.properties.IPropertySheetEntry, + * org.eclipse.ui.views.properties.IPropertySheetEntry) + */ + @Override + public int compare(final IPropertySheetEntry entryA, final IPropertySheetEntry entryB) { + return super.compare(entryA, entryB); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.PropertySheetSorter#compareCategories(java.lang.String, + * java.lang.String) + */ + @Override + public int compareCategories(final String categoryA, final String categoryB) { + int result = Integer.MIN_VALUE; + IPropertySource propertySource = null; + // + // Gets the propertySource. + if (getPropertySourceProvider() != null && getSelectedObject() != null) { + propertySource = getPropertySourceProvider().getPropertySource(getSelectedObject()); + } + if (propertySource == null) { + result = super.compareCategories(categoryA, categoryB); + } + // + // Special cases. + if (categoryA == null) { + result = categoryB == null ? 0 : 1; + } + if (categoryB == null) { + result = categoryA == null ? 0 : -1; + } + if (categoryA != null && categoryA.equals(categoryB)) { + result = 0; + } + // + // general cases. + if (propertySource != null) { + final IPropertyDescriptor[] descriptors = propertySource.getPropertyDescriptors(); + for (int i = 0; i < descriptors.length && result == Integer.MIN_VALUE; i++) { + if (descriptors[i].getCategory() != null && descriptors[i].getCategory().equals(categoryA)) { + result = -1; + } else if (descriptors[i].getCategory() != null && descriptors[i].getCategory().equals(categoryB)) { + result = 1; + } + } + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.PropertySheetSorter#sort(org.eclipse.ui.views.properties.IPropertySheetEntry[]) + */ + @Override + public void sort(final IPropertySheetEntry[] entries) { + super.sort(entries); + } + + } + + /** + * Sets and prepares the actionBars for this section. + * + * @param actionBars + * the action bars for this page + * @see org.eclipse.gmf.runtime.common.ui.properties.TabbedPropertySheetPage#setActionBars(org.eclipse.ui.IActionBars) + */ + public void setActionBars(final IActionBars actionBars) { + + actionBars.getMenuManager().removeAll(); + actionBars.getToolBarManager().removeAll(); + actionBars.getStatusLineManager().removeAll(); + + page.makeContributions(actionBars.getMenuManager(), actionBars.getToolBarManager(), actionBars.getStatusLineManager()); + + actionBars.getToolBarManager().update(true); + + } + + /** + * Returns the PropertySource provider. The default implementation returns + * static adapter factory for the properties services. If the extending + * class needs to use a different provider then this method has to be + * overwriten. + * + * @return The PropertySource provider + */ + protected IPropertySourceProvider getPropertySourceProvider() { + return propertiesProvider; + } + + /** + * Returns the label for the table. The default implementation returns null, + * that is, there is no label. + * + * @return The label for the table + */ + protected String getTableLabel() { + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AbstractModelerPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public void setInput(final IWorkbenchPart part, final ISelection selection) { + final IEditingDomainProvider provider = (IEditingDomainProvider) part.getAdapter(IEditingDomainProvider.class); + if (provider != null) { + final EditingDomain theEditingDomain = provider.getEditingDomain(); + if (theEditingDomain instanceof TransactionalEditingDomain) { + setEditingDomain((TransactionalEditingDomain) theEditingDomain); + } + } + + // Set the eObject for the section, too. The workbench part may not + // adapt to IEditingDomainProvider, in which case the selected EObject + // will be used to derive the editing domain. + if (!selection.isEmpty() && selection instanceof IStructuredSelection) { + final Object firstElement = ((IStructuredSelection) selection).getFirstElement(); + + if (firstElement != null) { + final EObject adapted = unwrap(firstElement); + + if (adapted != null) { + setEObject(adapted); + } + } + } + + try { + page.selectionChanged(part, selection); + // CHECKSTYLE:OFF + } catch (RuntimeException e) { + // CHECKSTYLE:ON + // can occur if the page references semantic element that have been + // deleted on CDO + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AbstractModelerPropertySection#dispose() + */ + @Override + public void dispose() { + super.dispose(); + + if (page != null) { + page.dispose(); + page = null; + } + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#refresh() + */ + @Override + public void refresh() { + try { + page.refresh(); + // CHECKSTYLE:OFF + } catch (RuntimeException e) { + // CHECKSTYLE:ON + // can occur if the page references semantic element that have been + // deleted on CDO + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#shouldUseExtraSpace() + */ + @Override + public boolean shouldUseExtraSpace() { + return true; + } + + /** + * Update if nessesary, upon receiving the model event. + * + * @see #aboutToBeShown() + * @see #aboutToBeHidden() + * @param notification + * - even notification + * @param element + * - element that has changed + */ + @Override + public void update(final Notification notification, final EObject element) { + if (!isDisposed()) { + postUpdateRequest(new Runnable() { + + public void run() { + if (!isDisposed() && !isNotifierDeleted(notification)) { + refresh(); + } + } + }); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AbstractModelerPropertySection#getFilter() + */ + @Override + public NotificationFilter getFilter() { + return NotificationFilter.createEventTypeFilter(Notification.SET).or(NotificationFilter.createEventTypeFilter(Notification.UNSET)) + .or(NotificationFilter.createEventTypeFilter(Notification.ADD)).or(NotificationFilter.createEventTypeFilter(Notification.ADD_MANY)) + .or(NotificationFilter.createEventTypeFilter(Notification.REMOVE)).or(NotificationFilter.createEventTypeFilter(Notification.REMOVE_MANY)) + .and(NotificationFilter.createNotifierTypeFilter(EObject.class)); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AbstractModelerPropertySection#addToEObjectList(java.lang.Object) + */ + @Override + protected boolean addToEObjectList(final Object object) { + /* not implemented */ + return true; + } + + /** + * Returns the selected object. + * + * @return the selected objet. + */ + public abstract Object getSelectedObject(); + + /** + * A property sheet page that allows to change the sorter of the table + * viewer. + * + * @author ymortier + */ + private static class SortedPropertySheetPage extends PropertySheetPage { + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.PropertySheetPage#setSorter(org.eclipse.ui.views.properties.PropertySheetSorter) + */ + @Override + public void setSorter(final PropertySheetSorter sorter) { + super.setSorter(sorter); + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/MiscPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/MiscPropertySection.java new file mode 100644 index 0000000000..a2c102edd5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/MiscPropertySection.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.properties; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.diagram.ui.properties.sections.AdvancedPropertySection; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.IPropertySourceProvider; +import org.eclipse.ui.views.properties.PropertyDescriptor; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.diagram.tools.internal.properties.DefaultPropertySource; + +/** + * This property section provides debug informations. + * + * @author ymortier + */ +public class MiscPropertySection extends AdvancedPropertySection implements IPropertySourceProvider { + + /** + * Returns the property source of the specified object. + * + * @param object + * the object. + * @return the property source of the specified object. + */ + public IPropertySource getPropertySource(final Object object) { + + IPropertySource propSrc = null; + + if (object instanceof IPropertySource) { + propSrc = (IPropertySource) object; + } else if (object instanceof EditPart) { + propSrc = new MiscPropertySource(object); + } else if (object instanceof Collection<?>) { + final Collection<?> collection = (Collection<?>) object; + final IPropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[collection.size()]; + final Iterator<?> iterCollection = collection.iterator(); + int i = 0; + while (iterCollection.hasNext()) { + final Object next = iterCollection.next(); + propertyDescriptors[i] = new PropertyDescriptor(next, i + StringUtil.EMPTY_STRING); + i++; + } + propSrc = new DefaultPropertySource(propertyDescriptors); + + } else if (object instanceof Object[]) { + final Object[] collection = (Object[]) object; + final IPropertyDescriptor[] propertyDescriptors = new PropertyDescriptor[collection.length]; + for (int i = 0; i < collection.length; i++) { + Object next = collection[i]; + if (next == null) { + next = "null object"; + } + propertyDescriptors[i] = new PropertyDescriptor(next, i + ""); + } + propSrc = new DefaultPropertySource(propertyDescriptors); + } else if (object != null) { + propSrc = new MiscPropertySource(object); + } + return propSrc; + } + + /** + * Returns the provider. + * + * @return the provider. + */ + @Override + protected IPropertySourceProvider getPropertySourceProvider() { + return this; + } + + /** + * Modify/unwrap selection. + * + * @param selected + * the current selected object + * @return the unwrapped object + */ + protected Object transformSelection(final Object selected) { + + if (selected instanceof EditPart) { + return selected; + + } + return selected; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AdvancedPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public void setInput(final IWorkbenchPart part, final ISelection selection) { + if (selection.isEmpty() || !(selection instanceof StructuredSelection)) { + super.setInput(part, selection); + return; + } + final StructuredSelection structuredSelection = (StructuredSelection) selection; + final ArrayList<Object> transformedSelection = new ArrayList<Object>(structuredSelection.size()); + final Iterator<?> it = structuredSelection.iterator(); + while (it.hasNext()) { + final Object r = transformSelection(it.next()); + if (r != null) { + transformedSelection.add(r); + } + } + super.setInput(part, new StructuredSelection(transformedSelection)); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/MiscPropertySource.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/MiscPropertySource.java new file mode 100644 index 0000000000..344f5e7502 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/MiscPropertySource.java @@ -0,0 +1,179 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.properties; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.PropertyDescriptor; + +import org.eclipse.sirius.common.ui.SiriusTransPlugin; + +/** + * This property source uses the reflection Java API to display debug + * informations. + * + * @author ymortier + */ +public class MiscPropertySource implements IPropertySource { + + /** The selected object. */ + private Object selectedObject; + + /** + * Create a new {@link MiscPropertySource}. + * + * @param selectedObject + * the edit part to analyse. + */ + public MiscPropertySource(final Object selectedObject) { + this.selectedObject = selectedObject; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#getEditableValue() + */ + public Object getEditableValue() { + return this.selectedObject; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors() + */ + public IPropertyDescriptor[] getPropertyDescriptors() { + final List<Field> allFields = new LinkedList<Field>(); + Field[] fields = this.selectedObject.getClass().getDeclaredFields(); + allFields.addAll(Arrays.asList(fields)); + Class<?> currentClass = this.selectedObject.getClass().getSuperclass(); + while (currentClass != Object.class) { + fields = currentClass.getDeclaredFields(); + allFields.addAll(Arrays.asList(fields)); + currentClass = currentClass.getSuperclass(); + } + final PropertyDescriptor[] propertyDescriptors = new ReflectPropertyDescriptor[allFields.size()]; + final String categName = this.selectedObject.getClass().getName().substring(this.selectedObject.getClass().getName().lastIndexOf('.') + 1); + final Iterator<Field> iterFields = allFields.iterator(); + int i = 0; + while (iterFields.hasNext()) { + final Field currentField = iterFields.next(); + if (!Modifier.isStatic(currentField.getModifiers())) { + final ReflectPropertyDescriptor reflectPropertyDescriptor = new ReflectPropertyDescriptor(currentField, currentField.getName(), categName); + propertyDescriptors[i++] = reflectPropertyDescriptor; + } + } + final int realSize = i; + final IPropertyDescriptor[] descriptors = new ReflectPropertyDescriptor[realSize]; + System.arraycopy(propertyDescriptors, 0, descriptors, 0, realSize); + return descriptors; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object) + */ + public Object getPropertyValue(final Object id) { + + Object value = null; + if (id instanceof Field) { + final Field field = (Field) id; + final boolean oldAccessible = field.isAccessible(); + try { + field.setAccessible(true); + try { + value = field.get(this.selectedObject); + } catch (final IllegalArgumentException e) { + SiriusTransPlugin.getPlugin().error("Error while getting property value", e); + } catch (final IllegalAccessException e) { + SiriusTransPlugin.getPlugin().error("Error while getting property value", e); + } + } finally { + field.setAccessible(oldAccessible); + } + } + return value; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#isPropertySet(java.lang.Object) + */ + public boolean isPropertySet(final Object id) { + // does nothing + return true; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#resetPropertyValue(java.lang.Object) + */ + public void resetPropertyValue(final Object id) { + // does nothing. + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java.lang.Object, + * java.lang.Object) + */ + public void setPropertyValue(final Object id, final Object value) { + // does nothing. + } + + /** + * The reflect property descriptor. Use Reflect Java API to retrieves meta + * informations. + * + * @author ymortier + */ + private static class ReflectPropertyDescriptor extends PropertyDescriptor { + + /** The name of the category. */ + private String category; + + /** + * Create a new descriptor. + * + * @param id + * the id of the property. + * @param displayName + * the name to display. + * @param category + * the name of the category. + */ + public ReflectPropertyDescriptor(final Object id, final String displayName, final String category) { + super(id, displayName); + this.category = category; + } + + /** + * @see org.eclipse.ui.views.properties.PropertyDescriptor#getCategory() + */ + @Override + public String getCategory() { + return category; + } + + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/filter/AbstractPropertyFilter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/filter/AbstractPropertyFilter.java new file mode 100644 index 0000000000..fcfc065e93 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/filter/AbstractPropertyFilter.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.properties.filter; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.IFilter; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DSemanticDecorator; + +/** + * The base filter for property. The job of this filter is to initialize the + * <code>notationView</code>, the <code>viewPointElement</code> and the + * <code>semanticElements</code> attributes. The method + * {@link AbstractPropertyFilter#select(Object)} returns always + * <code>true</code>. + * + * @author ymortier + */ +public abstract class AbstractPropertyFilter implements IFilter { + + /** The edit part. */ + protected EditPart editPart; + + /** The GMF model element. */ + protected View notationView; + + /** The viewpoint model element. */ + protected EObject viewPointElement; + + /** The semantic model elements. */ + protected List<EObject> semanticElements; + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IFilter#select(java.lang.Object) + */ + public boolean select(final Object toTest) { + this.notationView = null; + this.viewPointElement = null; + this.semanticElements = Collections.emptyList(); + if (toTest instanceof EditPart) { + this.editPart = (EditPart) toTest; + if (editPart.getModel() instanceof View) { + this.notationView = (View) editPart.getModel(); + } + } else if (toTest instanceof View) { + this.notationView = (View) toTest; + } else if (toTest instanceof DDiagramElement) { + this.viewPointElement = (DDiagramElement) toTest; + } + if (viewPointElement == null && notationView != null) { + if (notationView.getElement() != null) { + this.viewPointElement = notationView.getElement(); + } + } + if (viewPointElement instanceof DSemanticDecorator) { + final DSemanticDecorator decorateSemanticElement = (DSemanticDecorator) viewPointElement; + // avoid doubles. + final Set<EObject> tmpSemanticElements = new HashSet<EObject>(); + if (viewPointElement instanceof DDiagramElement) { + tmpSemanticElements.addAll(((DDiagramElement) viewPointElement).getSemanticElements()); + } + tmpSemanticElements.add(decorateSemanticElement.getTarget()); + this.semanticElements = new ArrayList<EObject>(tmpSemanticElements); + } + return true; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/filter/MiscPropertyFilter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/filter/MiscPropertyFilter.java new file mode 100644 index 0000000000..3c3f298dc9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/properties/filter/MiscPropertyFilter.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.properties.filter; + +/** + * Filters the Misc property section. The section should appears only if the + * input selection is an edit part. + * + * @author ymortier + */ +public class MiscPropertyFilter extends AbstractPropertyFilter { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter#select(java.lang.Object) + */ + @Override + public boolean select(final Object toTest) { + return super.select(toTest) && this.editPart != null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/util/EditPartTools.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/util/EditPartTools.java new file mode 100644 index 0000000000..a606f9bc01 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/util/EditPartTools.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.util; + +import java.util.Iterator; + +import org.eclipse.gef.EditPart; + +/** + * Tools for edit part. + * + * @author ymortier + */ +public final class EditPartTools { + + /** + * Avoid instantiation. + */ + private EditPartTools() { + } + + /** + * Returns the first edit part that is an instance of the class + * <code>editPartType</code>. + * + * @param root + * the root edit part. + * @param editPartType + * the type of the edit part. + * @return the first edit part that is an instance of the class + * <code>editPartType</code>. + */ + public static EditPart getEditPartOfType(final EditPart root, final Class<?> editPartType) { + if (root == null || editPartType == null) { + throw new IllegalArgumentException("root or editPartType is null"); + } + EditPart result = null; + if (editPartType.isInstance(root)) { + result = root; + } + if (result == null) { + @SuppressWarnings("unchecked") + final Iterator<EditPart> iterChildren = root.getChildren().iterator(); + while (iterChildren.hasNext() && result == null) { + result = EditPartTools.getEditPartOfType(iterChildren.next(), editPartType); + } + } + return result; + } + + /** + * Returns an ancestor of the given edit part. + * + * @param <T> + * the type of the ancestor to return. + * @param aChild + * a child edit part. + * @param type + * the type of the ancestor to return. + * @return the found edit part. + */ + public static <T extends EditPart> T getParentOfType(final EditPart aChild, final Class<T> type) { + T result = null; + EditPart current = aChild; + while (result == null && current != null) { + if (type.isInstance(current)) { + result = type.cast(current); + } else { + current = current.getParent(); + } + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/util/GMFNotationHelper.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/util/GMFNotationHelper.java new file mode 100644 index 0000000000..151b71c5c1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/api/util/GMFNotationHelper.java @@ -0,0 +1,431 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.api.util; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.Resource; +import org.eclipse.gmf.runtime.diagram.core.preferences.PreferencesHint; +import org.eclipse.gmf.runtime.diagram.core.services.ViewService; +import org.eclipse.gmf.runtime.diagram.core.util.ViewType; +import org.eclipse.gmf.runtime.diagram.ui.preferences.IPreferenceConstants; +import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationFactory; +import org.eclipse.gmf.runtime.notation.ShapeStyle; +import org.eclipse.gmf.runtime.notation.Size; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferenceConverter; +import org.eclipse.swt.graphics.RGB; + +import org.eclipse.sirius.common.ui.SiriusTransPlugin; + +/** + * Utility class to work with the GMF Annotation model. + * + * @author cbrun + * + */ +public final class GMFNotationHelper { + /* + * This is an utility class, should not be initialized! + */ + private GMFNotationHelper() { + + } + + /** + * Return the X position of GMF {@link Node}. + * + * @param gmfView + * the GMF {@link Node}. + * @return the X position of the node + */ + public static int getX(final Node gmfView) { + if (gmfView.getLayoutConstraint() instanceof Bounds) { + final Bounds nodeBounds = (Bounds) gmfView.getLayoutConstraint(); + return nodeBounds.getX(); + } + return 0; + } + + /** + * Return the Y position of GMF {@link Node}. + * + * @param gmfView + * the GMF {@link Node}. + * @return the Y position of the node + */ + public static int getY(final Node gmfView) { + if (gmfView.getLayoutConstraint() instanceof Bounds) { + final Bounds nodeBounds = (Bounds) gmfView.getLayoutConstraint(); + return nodeBounds.getY(); + } + return 0; + } + + /** + * Return the width of GMF {@link Node}. + * + * @param gmfView + * the GMF {@link Node}. + * @return the width of the node + */ + public static int getWidth(final Node gmfView) { + if (gmfView.getLayoutConstraint() instanceof Bounds) { + final Bounds nodeBounds = (Bounds) gmfView.getLayoutConstraint(); + return nodeBounds.getWidth(); + } + return 0; + } + + /** + * Return the height of GMF {@link Node}. + * + * @param gmfView + * the GMF {@link Node}. + * @return the height of the node + */ + public static int getHeight(final Node gmfView) { + if (gmfView.getLayoutConstraint() instanceof Bounds) { + final Bounds nodeBounds = (Bounds) gmfView.getLayoutConstraint(); + return nodeBounds.getHeight(); + } + return 0; + } + + /** + * Return the text of a note. + * + * @param note + * a GMF Note. + * @return the text of the note. + */ + public static String getNoteDescription(final Node note) { + final Iterator it = note.getStyles().iterator(); + while (it.hasNext()) { + final Object obj = it.next(); + if (obj instanceof ShapeStyle) { + return ((ShapeStyle) obj).getDescription(); + } + } + return ""; + } + + /** + * return a list of nodes corresponding to notes. + * + * @param gmfDiagram + * any GMF Diagram + * @return a list of nodes corresponding to notes. + */ + public static Collection<Node> getNotes(final Diagram gmfDiagram) { + final Collection<Node> result = new ArrayList<Node>(); + final Iterator<EObject> it = gmfDiagram.eAllContents(); + while (it.hasNext()) { + final EObject obj = it.next(); + if (obj instanceof Node && GMFNotationHelper.isNote((Node) obj)) { + result.add((Node) obj); + } + } + return result; + } + + /** + * Usefull to get all the note attachments of a GMF diagram. + * + * @param gmfDiagram + * any GMF diagram. + * @return a list of {@link Edge} which are the note attachments. + */ + public static Collection<Edge> getNotesAttachments(final Diagram gmfDiagram) { + final Collection<Edge> result = new ArrayList<Edge>(); + final Iterator<EObject> it = gmfDiagram.eAllContents(); + while (it.hasNext()) { + final EObject obj = it.next(); + if (obj instanceof Edge && GMFNotationHelper.isNoteAttachment((Edge) obj)) { + result.add((Edge) obj); + } + } + return result; + } + + /** + * Tell whether an Edge is a note attachment or not. + * + * @param obj + * any GMF Edge. + * @return true if the edge is a note attachment. + */ + public static boolean isNoteAttachment(final Edge obj) { + return ViewType.NOTEATTACHMENT.equals(obj.getType()); + } + + /** + * tell whether a node is a note or not. + * + * @param node + * any node. + * @return true if the node is a Note + */ + public static boolean isNote(final Node node) { + return ViewType.NOTE.equals(node.getType()); + } + + /** + * Browse the resource content of the given element and retrieve GMF + * {@link Diagram} corresponding to this element if available. + * + * @param eObject + * any eObject of the Resource containing the diagrams. + * @return the first GMF Diagram targeting the eObject, null if not found. + */ + public static Diagram findGMFDiagram(final EObject eObject) { + final Iterator<EObject> it = eObject.eResource().getAllContents(); + while (it.hasNext()) { + final Object obj = it.next(); + if (obj instanceof Diagram) { + if (((Diagram) obj).getElement() == eObject) { + return (Diagram) obj; + } + } + } + return null; + } + + /** + * Return the list of all the GMF diagrams contained in the resource. + * + * @param resource + * the resource + * @return the list of GMF diagram contained in the resource. + */ + public static Collection<Diagram> getGMFDiagrams(final Resource resource) { + final Collection<Diagram> result = new ArrayList<Diagram>(); + final Iterator<EObject> it = resource.getAllContents(); + while (it.hasNext()) { + final EObject obj = it.next(); + if (obj instanceof Diagram) { + result.add((Diagram) obj); + } + } + return result; + } + + /** + * Create a new Note and attach it on the diagram. + * + * @param container + * the container diagram. + * @param noteText + * the content of the note. + * @return the newly created note. + */ + public static Node createNote(final Diagram container, final String noteText) { + + final Node note = ViewService.createNode(container, ViewType.NOTE, PreferencesHint.USE_DEFAULTS); + final Iterator it = note.getStyles().iterator(); + while (it.hasNext()) { + final Object cur = it.next(); + if (cur instanceof ShapeStyle) { + ((ShapeStyle) cur).setDescription(noteText); + } + } + return note; + + } + + /** + * Create a new Note and attach it on the diagram. + * + * @param container + * the container diagram. + * @param noteText + * the content of the note. + * @param preferencesStore + * the preferencesStore of the Calling plugin. + * @return the newly created note. + */ + public static Node createNote(final Diagram container, final String noteText, final IPreferenceStore preferencesStore) { + final Node note = GMFNotationHelper.createNote(container, noteText); + + try { + // In Eclipse 3.5 createNote returns a Shape that does not have the + // generic color. + Class<?> shapeClass = Class.forName("org.eclipse.gmf.runtime.notation.Shape"); + + Method methodSetDescription = shapeClass.getMethod("setDescription", String.class); + methodSetDescription.invoke(note, noteText); + + Method methodSetFillColor = shapeClass.getMethod("setFillColor", int.class); + RGB fillRGB = PreferenceConverter.getColor(preferencesStore, IPreferenceConstants.PREF_NOTE_FILL_COLOR); + methodSetFillColor.invoke(note, FigureUtilities.RGBToInteger(fillRGB).intValue()); + + Method methodSetLineColor = shapeClass.getMethod("setLineColor", int.class); + RGB lineRGB = PreferenceConverter.getColor(preferencesStore, IPreferenceConstants.PREF_NOTE_LINE_COLOR); + methodSetLineColor.invoke(note, FigureUtilities.RGBToInteger(lineRGB).intValue()); + + } catch (final ClassNotFoundException cnfe) { + // We are not in Eclipse 3.3 : Do nothing else + } catch (SecurityException e) { + SiriusTransPlugin.getPlugin().error("SecurityException while accessing Shape class", e); + } catch (NoSuchMethodException e) { + SiriusTransPlugin.getPlugin().error("NoSuchMethodException while accessing Shape class", e); + } catch (IllegalArgumentException e) { + SiriusTransPlugin.getPlugin().error("IllegalArgumentException while accessing Shape class", e); + } catch (IllegalAccessException e) { + SiriusTransPlugin.getPlugin().error("IllegalAccessException while accessing Shape class", e); + } catch (InvocationTargetException e) { + SiriusTransPlugin.getPlugin().error("InvocationTargetException while accessing Shape class", e); + } + return note; + + } + + /** + * Create a LayoutPosition instance from a set of integer values. + * + * @param x + * x position. + * @param y + * y position . + * @param width + * layout width. + * @param height + * layout height. + * @return the newly created {@link LayoutConstraint}. + */ + public static LayoutConstraint createLayoutPosition(final BigInteger x, final BigInteger y, final BigInteger width, final BigInteger height) { + final Bounds layout = NotationFactory.eINSTANCE.createBounds(); + layout.setX(x.intValue()); + layout.setY(y.intValue()); + layout.setWidth(width.intValue()); + layout.setHeight(height.intValue()); + return layout; + } + + /** + * Browse the given diagram to find a GMF Node targeting the given element. + * + * @param diagram + * a GMF Diagram. + * @param modelElement + * any element. + * @return a Node targeting the model element if found, null otherwise. + */ + public static Node findGMFNode(final Diagram diagram, final EObject modelElement) { + final Iterator<EObject> it = diagram.eAllContents(); + while (it.hasNext()) { + final EObject cur = it.next(); + if (cur instanceof Node) { + if (((Node) cur).getElement() == modelElement) { + return (Node) cur; + } + } + } + return null; + } + + /** + * Create a new note attachment and append it on the diagram. + * + * @param note + * : the note . + * @param target + * : the GMF node to attach to. + */ + public static void createNoteAttachment(final Node note, final Node target) { + ViewService.createEdge(note, target, ViewType.NOTEATTACHMENT, PreferencesHint.USE_DEFAULTS); + } + + /** + * Computes and returns the absolute location of the specified {@link Node}. + * Returns (0,0) if the node is <code>null</code>. + * + * @param node + * the node. + * @return the absolute location of the specified {@link Node}. + */ + public static Point getAbsoluteLocation(final Node node) { + final Point location = new Point(0, 0); + EObject current = node; + // + // Iterates over all parents. + while (current instanceof Node) { + if (((Node) current).getLayoutConstraint() instanceof Location) { + final Location nodeLocation = (Location) ((Node) current).getLayoutConstraint(); + location.x += nodeLocation.getX(); + location.y += nodeLocation.getY(); + } + current = current.eContainer(); + } + return location; + } + + /** + * Get a copy. + * + * @param bounds + * Bounds to copy + * + * @return the copy + */ + public static Bounds getCopy(Bounds bounds) { + Bounds copy = NotationFactory.eINSTANCE.createBounds(); + copy.setX(bounds.getX()); + copy.setY(bounds.getY()); + copy.setWidth(bounds.getWidth()); + copy.setHeight(bounds.getHeight()); + return copy; + } + + /** + * Get a copy. + * + * @param location + * Location to copy + * + * @return the copy + */ + public static Location getCopy(Location location) { + Location copy = NotationFactory.eINSTANCE.createLocation(); + copy.setX(location.getX()); + copy.setY(location.getY()); + return copy; + } + + /** + * Get a copy. + * + * @param size + * Size to copy + * + * @return the copy + */ + public static Size getCopy(Size size) { + Size copy = NotationFactory.eINSTANCE.createSize(); + copy.setWidth(size.getWidth()); + copy.setHeight(size.getHeight()); + return copy; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateBehaviorToolsCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateBehaviorToolsCommand.java new file mode 100644 index 0000000000..7483907184 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateBehaviorToolsCommand.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.Collection; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.helper.concern.ConcernService; +import org.eclipse.sirius.description.tool.BehaviorTool; + +/** + * Specific command to update activated behaviors. + * + * @author mporhel + */ +public final class ActivateBehaviorToolsCommand extends RecordingCommand { + + private final Collection<BehaviorTool> newElements; + + private final DDiagram diagram; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param newElements + * elements to add + * @param dDiagram + * the current diagram. + */ + public ActivateBehaviorToolsCommand(TransactionalEditingDomain domain, DDiagram dDiagram, Collection<BehaviorTool> newElements) { + super(domain, "Activate behavior tools"); + this.diagram = dDiagram; + this.newElements = newElements; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diagram == null || newElements == null) { + return; + } + + for (BehaviorTool tool : newElements) { + diagram.getActivateBehaviors().add(tool); + } + ConcernService.resetCurrentConcern(diagram); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateFiltersCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateFiltersCommand.java new file mode 100644 index 0000000000..4058d4ba1e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateFiltersCommand.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.common.tools.api.listener.NotificationUtil; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.SiriusFactory; +import org.eclipse.sirius.business.api.helper.concern.ConcernService; +import org.eclipse.sirius.description.concern.ConcernDescription; +import org.eclipse.sirius.description.filter.CompositeFilterDescription; +import org.eclipse.sirius.description.filter.Filter; +import org.eclipse.sirius.description.filter.FilterDescription; +import org.eclipse.sirius.description.filter.VariableFilter; +import org.eclipse.sirius.diagram.tools.internal.filter.FilterTools; + +/** + * Specific command to update activated filters. + * + * @author mporhel + */ +public final class ActivateFiltersCommand extends RecordingCommand { + + private final Collection<FilterDescription> newElements; + + private final DDiagram diagram; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param diagram + * the current diagram. + * @param newElements + * elements to activate + */ + public ActivateFiltersCommand(TransactionalEditingDomain domain, DDiagram diagram, Collection<FilterDescription> newElements) { + super(domain, "Activate filters"); + this.newElements = newElements; + this.diagram = diagram; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diagram == null || newElements == null) { + return; + } + + initFilterVariablesHistory(); + + doActivateFilters(); + + if (newElements.size() > 0) { + NotificationUtil.sendNotification(diagram, org.eclipse.sirius.common.tools.api.listener.Notification.Kind.START, org.eclipse.sirius.common.tools.api.listener.Notification.VISIBILITY_UPDATE); + } + } + + private void doActivateFilters() { + boolean containsVariableFilters = false; + final List<FilterDescription> newActivatedFilters = new ArrayList<FilterDescription>(); + final List<FilterDescription> previousActivatedFilters = new ArrayList<FilterDescription>(diagram.getActivatedFilters()); + + ConcernDescription oldConcern = getAndResetConcern(); + + try { + for (FilterDescription filterDesc : newElements) { + if (filterDesc instanceof CompositeFilterDescription && diagram instanceof DSemanticDiagram) { + containsVariableFilters = containsVariableFilters | handleVariableInit((CompositeFilterDescription) filterDesc, (DSemanticDiagram) diagram); + } + /* + * In all the cases, if we've got a filter, then enable it.. + */ + newActivatedFilters.add(filterDesc); + } + // need to remove previous filters to handle creation of + // new graphical elements on variable filters activation. + if (containsVariableFilters && previousActivatedFilters.size() > 0) { + diagram.getActivatedFilters().clear(); + } + diagram.getActivatedFilters().addAll(newActivatedFilters); + // reactivation of previous activated filters. + if (containsVariableFilters && previousActivatedFilters.size() > 0) { + diagram.getActivatedFilters().addAll(previousActivatedFilters); + } + } catch (final InterruptedException e) { + ConcernService.setCurrentConcern(diagram, oldConcern); + } + } + + private ConcernDescription getAndResetConcern() { + ConcernDescription oldConcern = null; + if (newElements.size() > 0 && diagram.getCurrentConcern() != null) { + oldConcern = diagram.getCurrentConcern(); + ConcernService.resetCurrentConcern(diagram); + } + return oldConcern; + } + + private void initFilterVariablesHistory() { + if (diagram.getFilterVariableHistory() == null) { + diagram.setFilterVariableHistory(SiriusFactory.eINSTANCE.createFilterVariableHistory()); + } + } + + private boolean handleVariableInit(final CompositeFilterDescription composite, final DSemanticDiagram vp) throws InterruptedException { + boolean containsVariableFilters = false; + final Iterator<Filter> it = composite.getFilters().iterator(); + while (it.hasNext()) { + final Filter objFilter = it.next(); + /* + * If we have a variable filter we need to open the dialog and set + * the different variables.. + */ + if (objFilter instanceof VariableFilter) { + final VariableFilter filter = (VariableFilter) objFilter; + final Map<String, EObject> variables = FilterTools.askForFilterValues(vp, filter); + filter.setFilterContext(variables); + containsVariableFilters = true; + } + } + return containsVariableFilters; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateRulesCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateRulesCommand.java new file mode 100644 index 0000000000..48701ce255 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ActivateRulesCommand.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.Collection; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.validation.ValidationRule; + +/** + * Specific command to update activated rules. + * + * @author mporhel + */ +public final class ActivateRulesCommand extends RecordingCommand { + + private final Collection<ValidationRule> newElements; + + private final DDiagram diagram; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param diagram + * the current diagram. + * @param newElements + * elements to activate + */ + public ActivateRulesCommand(TransactionalEditingDomain domain, DDiagram diagram, Collection<ValidationRule> newElements) { + super(domain, "Activate validation rules"); + this.newElements = newElements; + this.diagram = diagram; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diagram == null || newElements == null) { + return; + } + + for (ValidationRule rule : newElements) { + diagram.getActivatedRules().add(rule); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ChangeLayerActivationCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ChangeLayerActivationCommand.java new file mode 100644 index 0000000000..c59403864a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/ChangeLayerActivationCommand.java @@ -0,0 +1,115 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.SubProgressMonitor; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; + +import org.eclipse.sirius.common.tools.api.listener.Notification; +import org.eclipse.sirius.common.tools.api.listener.NotificationUtil; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.dialect.command.RefreshRepresentationsCommand; +import org.eclipse.sirius.business.api.query.IdentifiedElementQuery; +import org.eclipse.sirius.business.internal.metamodel.helper.LayerHelper; +import org.eclipse.sirius.description.Layer; + +/** + * Specific command to change layer activation. + * + * @author mporhel + */ +public final class ChangeLayerActivationCommand extends RecordingCommand { + + private DDiagram dDiagram; + + private Layer layer; + + private IProgressMonitor monitor; + + /** + * Default Constructor. + * + * @param domain + * the editing domain. + * @param dDiagram + * the {@link DDiagram} for which change the activated layers + * @param layer + * the {@link Layer} concerned by this change + * @deprecated use + * {@link ChangeLayerActivationCommand#ChangeLayerActivationCommand(TransactionalEditingDomain, DDiagram, Layer, IProgressMonitor)} + * instead + */ + public ChangeLayerActivationCommand(TransactionalEditingDomain domain, DDiagram dDiagram, Layer layer) { + this(domain, dDiagram, layer, new NullProgressMonitor()); + } + + /** + * Default Constructor. + * + * @param domain + * the editing domain. + * @param dDiagram + * the {@link DDiagram} for which change the activated layers + * @param layer + * the {@link Layer} concerned by this change + * @param monitor + * a {@link IProgressMonitor} to show progression of layer + * activation changes + */ + public ChangeLayerActivationCommand(TransactionalEditingDomain domain, DDiagram dDiagram, Layer layer, IProgressMonitor monitor) { + super(domain, dDiagram.getActivatedLayers().contains(layer) ? "Hide" : "Show" + " \"" + new IdentifiedElementQuery(layer).getLabel() + "\" layer"); + this.dDiagram = dDiagram; + this.layer = layer; + this.monitor = monitor; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + try { + monitor.beginTask("Apply layer modifications...", 3); + boolean launchRefresh = !LayerHelper.containsOnlyTools(layer) || layer.getCustomization() != null; + if (!launchRefresh) { + NotificationUtil.sendNotification(dDiagram, Notification.Kind.START, Notification.VISIBILITY); + } + monitor.worked(1); + if (dDiagram.getActivatedLayers().contains(layer)) { + dDiagram.getActivatedLayers().remove(layer); + } else { + dDiagram.getActivatedLayers().add(layer); + } + monitor.worked(1); + + if (launchRefresh) { + new RefreshRepresentationsCommand(TransactionUtil.getEditingDomain(dDiagram), new SubProgressMonitor(monitor, 1), dDiagram).execute(); + } else { + NotificationUtil.sendNotification(dDiagram, Notification.Kind.STOP, Notification.VISIBILITY); + } + monitor.worked(1); + } finally { + monitor.done(); + } + } + + @Override + public void dispose() { + super.dispose(); + dDiagram = null; + layer = null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateBehaviorToolsCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateBehaviorToolsCommand.java new file mode 100644 index 0000000000..35eac07b1a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateBehaviorToolsCommand.java @@ -0,0 +1,63 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.Collection; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.helper.concern.ConcernService; +import org.eclipse.sirius.description.tool.BehaviorTool; + +/** + * Specific command to update activated behaviors. + * + * @author mporhel + */ +public final class DeactivateBehaviorToolsCommand extends RecordingCommand { + + private final Collection<BehaviorTool> oldElements; + + private final DDiagram diagram; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param oldElements + * elements to remove + * @param dDiagram + * the current diagram. + */ + public DeactivateBehaviorToolsCommand(TransactionalEditingDomain domain, DDiagram dDiagram, Collection<BehaviorTool> oldElements) { + super(domain, "Deactivate behavior tools"); + this.diagram = dDiagram; + this.oldElements = oldElements; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diagram == null || oldElements == null) { + return; + } + + for (BehaviorTool tool : oldElements) { + diagram.getActivateBehaviors().remove(tool); + } + ConcernService.resetCurrentConcern(diagram); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateFiltersCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateFiltersCommand.java new file mode 100644 index 0000000000..ab1afa02da --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateFiltersCommand.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.Collection; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.common.tools.api.listener.NotificationUtil; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.helper.concern.ConcernService; +import org.eclipse.sirius.description.filter.FilterDescription; + +/** + * Specific command to update activated filters. + * + * @author mporhel + */ +public final class DeactivateFiltersCommand extends RecordingCommand { + + private final Collection<FilterDescription> oldElements; + + private final DDiagram diagram; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param diagram + * the current diagram. + * @param oldElements + * elements to deactivate + */ + public DeactivateFiltersCommand(TransactionalEditingDomain domain, DDiagram diagram, Collection<FilterDescription> oldElements) { + super(domain, "Deactivate filters"); + this.oldElements = oldElements; + this.diagram = diagram; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diagram == null || oldElements == null) { + return; + } + + for (FilterDescription filter : oldElements) { + diagram.getActivatedFilters().remove(filter); + } + + if (oldElements.size() > 0) { + ConcernService.resetCurrentConcern(diagram); + NotificationUtil.sendNotification(diagram, org.eclipse.sirius.common.tools.api.listener.Notification.Kind.START, org.eclipse.sirius.common.tools.api.listener.Notification.VISIBILITY_UPDATE); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateRulesCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateRulesCommand.java new file mode 100644 index 0000000000..06d7f6116e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/DeactivateRulesCommand.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.Collection; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.validation.ValidationRule; + +/** + * Specific command to update activated rules. + * + * @author mporhel + */ +public final class DeactivateRulesCommand extends RecordingCommand { + + private final Collection<ValidationRule> oldElements; + + private final DDiagram diagram; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param diagram + * the current diagram. + * @param oldElements + * elements to deactivate + */ + public DeactivateRulesCommand(TransactionalEditingDomain domain, DDiagram diagram, Collection<ValidationRule> oldElements) { + super(domain, "Dectivate validation rules"); + this.oldElements = oldElements; + this.diagram = diagram; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diagram == null || oldElements == null) { + return; + } + + for (ValidationRule rule : oldElements) { + diagram.getActivatedRules().remove(rule); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/EdgeRoutingStyleChangedCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/EdgeRoutingStyleChangedCommand.java new file mode 100644 index 0000000000..9a493cad82 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/EdgeRoutingStyleChangedCommand.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.diagram.edit.api.part.IDiagramEdgeEditPart; + +/** + * Specific command to update the routing style of an edge edit part. + * + * @author mporhel + */ +public final class EdgeRoutingStyleChangedCommand extends RecordingCommand { + + private final IDiagramEdgeEditPart editpart; + + private final Notification msg; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param msg + * the notification + * @param editpart + * the edge edit part to update. + */ + public EdgeRoutingStyleChangedCommand(TransactionalEditingDomain domain, IDiagramEdgeEditPart editpart, Notification msg) { + super(domain, "Change routing style"); + this.editpart = editpart; + this.msg = msg; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (editpart == null || msg == null) { + return; + } + + editpart.routingStyleChanged(msg); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/FindElementCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/FindElementCommand.java new file mode 100644 index 0000000000..d49c3ecd3c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/FindElementCommand.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.ui.IObjectActionDelegate; +import org.eclipse.ui.handlers.HandlerUtil; + +import org.eclipse.sirius.diagram.ui.tools.api.action.FindElementAction; + +/** + * A command to find an element. + * + * @author mchauvin + */ +public class FindElementCommand extends AbstractHandler { + + private IObjectActionDelegate action; + + /** + * Construct a new instance. + */ + public FindElementCommand() { + action = new FindElementAction(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.commands.AbstractHandler#execute(org.eclipse.core.commands.ExecutionEvent) + */ + public Object execute(final ExecutionEvent event) throws ExecutionException { + action.selectionChanged(null, HandlerUtil.getCurrentSelection(event)); + action.run(null); + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/InitializeHiddenElementsCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/InitializeHiddenElementsCommand.java new file mode 100644 index 0000000000..d645af1ac6 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/InitializeHiddenElementsCommand.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.List; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; + +/** + * Specific command to add hidden elements to transient reference + * hiddenElements. + * + * @author mporhel + */ +public final class InitializeHiddenElementsCommand extends RecordingCommand { + + private final DDiagram dDiagram; + + private final List<DDiagramElement> hiddenElements; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param hiddenElements + * the hiddens elements + * @param dDiagram + * the DDiagram to update. + */ + public InitializeHiddenElementsCommand(TransactionalEditingDomain domain, DDiagram dDiagram, List<DDiagramElement> hiddenElements) { + super(domain, "Initialize hidden elements"); + this.dDiagram = dDiagram; + this.hiddenElements = hiddenElements; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (dDiagram == null || hiddenElements == null || hiddenElements.isEmpty()) { + return; + } + + dDiagram.getHiddenElements().addAll(hiddenElements); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SemanticChangedCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SemanticChangedCommand.java new file mode 100644 index 0000000000..2d883f047b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SemanticChangedCommand.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.edit.internal.part.DiagramElementEditPartOperation; + +/** + * Specific command to update the edit part when semantic changed. + * + * @author mporhel + */ +public final class SemanticChangedCommand extends RecordingCommand { + + private final IDiagramElementEditPart part; + + private final Notification msg; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param msg + * the notification + * @param editpart + * the diagram element edit part to update. + */ + public SemanticChangedCommand(TransactionalEditingDomain domain, IDiagramElementEditPart editpart, Notification msg) { + super(domain, "Semantic changed"); + this.part = editpart; + this.msg = msg; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (part != null && msg != null) { + DiagramElementEditPartOperation.semanticChanged(part, msg); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SetCurrentConcernCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SetCurrentConcernCommand.java new file mode 100644 index 0000000000..aee79db5bb --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SetCurrentConcernCommand.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import java.util.Iterator; +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.common.tools.api.listener.NotificationUtil; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.SiriusFactory; +import org.eclipse.sirius.business.api.helper.concern.ConcernService; +import org.eclipse.sirius.description.concern.ConcernDescription; +import org.eclipse.sirius.description.filter.CompositeFilterDescription; +import org.eclipse.sirius.description.filter.FilterDescription; +import org.eclipse.sirius.description.filter.VariableFilter; +import org.eclipse.sirius.diagram.tools.internal.filter.FilterTools; + +/** + * Specific command to set the current concern. + * + * @author mporhel + */ +public class SetCurrentConcernCommand extends RecordingCommand { + + private final ConcernDescription desc; + + private final DDiagram diagram; + + /** + * Constructor. + * + * @param domain + * the editing domain + * @param diagram + * the current diagram + * @param desc + * the requested concern description + */ + public SetCurrentConcernCommand(final TransactionalEditingDomain domain, DDiagram diagram, final ConcernDescription desc) { + super(domain, "Set current concern"); + this.diagram = diagram; + this.desc = desc; + } + + /** + * {@inheritDoc} + */ + @Override + public boolean canUndo() { + return false; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diagram == null || desc == null) { + return; + } + + NotificationUtil.sendNotification(diagram, org.eclipse.sirius.common.tools.api.listener.Notification.Kind.START, org.eclipse.sirius.common.tools.api.listener.Notification.VISIBILITY_UPDATE); + + final ConcernDescription previousConcern = diagram.getCurrentConcern(); + if (diagram.getFilterVariableHistory() == null) { + diagram.setFilterVariableHistory(SiriusFactory.eINSTANCE.createFilterVariableHistory()); + } + try { + final Iterator<FilterDescription> itFilter = desc.getFilters().iterator(); + while (itFilter.hasNext()) { + final FilterDescription fil = itFilter.next(); + if (fil instanceof CompositeFilterDescription) { + final Iterator<?> it2 = ((CompositeFilterDescription) fil).getFilters().iterator(); + while (it2.hasNext()) { + final Object objFilter = it2.next(); + /* + * If we have a variable filter we need to open the + * dialog and set the different variables.. + */ + if (objFilter instanceof VariableFilter && diagram instanceof DSemanticDiagram) { + final VariableFilter filter = (VariableFilter) objFilter; + Map<String, EObject> variables; + variables = FilterTools.askForFilterValues((DSemanticDiagram) diagram, filter); + filter.setFilterContext(variables); + } + } + } + } + ConcernService.setCurrentConcern(diagram, desc); + } catch (final InterruptedException e) { + ConcernService.setCurrentConcern(diagram, previousConcern); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SetDefaultConcernCommand.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SetDefaultConcernCommand.java new file mode 100644 index 0000000000..053182ea83 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/commands/SetDefaultConcernCommand.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.commands; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.common.tools.api.listener.NotificationUtil; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.helper.concern.ConcernService; + +/** + * Specific command to set current concern to default. + * + * @author mporhel + */ +public final class SetDefaultConcernCommand extends RecordingCommand { + + private final DDiagram diag; + + /** + * Constructor. + * + * @param domain + * the editing domain. + * @param diagram + * the current diagram. + */ + public SetDefaultConcernCommand(TransactionalEditingDomain domain, DDiagram diagram) { + super(domain, "Set current concern to default"); + this.diag = diagram; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + if (diag == null) { + return; + } + + if (diag.getDescription() != null && diag.getDescription().getDefaultConcern() != null) { + ConcernService.setCurrentConcern(diag, diag.getDescription().getDefaultConcern()); + } else { + ConcernService.setCurrentConcern(diag, null); + } + + NotificationUtil.sendNotification(diag, org.eclipse.sirius.common.tools.api.listener.Notification.Kind.START, org.eclipse.sirius.common.tools.api.listener.Notification.VISIBILITY_UPDATE); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean canUndo() { + /* we should not be able to undo this */ + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/ColorPalettePopup.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/ColorPalettePopup.java new file mode 100644 index 0000000000..742a37126d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/ColorPalettePopup.java @@ -0,0 +1,385 @@ +/****************************************************************************** + * Copyright (c) 2005, 2008 IBM Corporation 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: + * IBM Corporation - initial API and implementation + ****************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.dialogs; + +import java.util.HashMap; +import java.util.Iterator; + +import org.eclipse.gmf.runtime.common.ui.util.WindowUtil; +import org.eclipse.gmf.runtime.diagram.ui.properties.internal.l10n.DiagramUIPropertiesMessages; +import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.SWT; +import org.eclipse.swt.SWTException; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Device; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.ImageData; +import org.eclipse.swt.graphics.PaletteData; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.ColorDialog; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; +import org.eclipse.swt.widgets.Shell; + +/** + * Copy of gmf code. + */ +public class ColorPalettePopup { + + /** variable to store previous color */ + private int previousColor; + + private Button customColorButton; + + private HashMap buttonMap = new HashMap(); + + /** + * A descirptor for an inventory color + */ + private static class InventoryColorDescriptor extends ImageDescriptor { + + /** the default preference color */ + private static final RGB OUTLINE_COLOR = new RGB(192, 192, 192); + + private RGB rgb; + + public InventoryColorDescriptor(RGB colorValue) { + this.rgb = colorValue; + + } + + /** + * @see org.eclipse.jface.resource.ImageDescriptor#getImageData() + */ + @Override + public ImageData getImageData() { + ImageData data = new ImageData(ICON_SIZE.x, ICON_SIZE.y, 1, new PaletteData(new RGB[] { rgb, OUTLINE_COLOR })); + + for (int i = 0; i < ICON_SIZE.y; i++) { + data.setPixel(0, i, 1); + } + for (int i = 0; i < ICON_SIZE.y; i++) { + data.setPixel(ICON_SIZE.x - 1, i, 1); + } + for (int i = 0; i < ICON_SIZE.x; i++) { + data.setPixel(i, 0, 1); + } + for (int i = 0; i < ICON_SIZE.x; i++) { + data.setPixel(i, ICON_SIZE.y - 1, 1); + } + return data; + } + + /** + * Creates and returns a new SWT image for this image descriptor. The + * returned image must be explicitly disposed using the image's dispose + * call. The image will not be automatically garbage collected. In the + * even of an error, a default image is returned if + * <code>returnMissingImageOnError</code> is true, otherwise + * <code>null</code> is returned. + * <p> + * Note: Even if <code>returnMissingImageOnError</code> is true, it is + * still possible for this method to return <code>null</code> in extreme + * cases, for example if SWT runs out of image handles. + * </p> + * + * @return a new image or <code>null</code> if the image could not be + * created + * + */ + // CHECKSTYLE:OFF + @Override + public Image createImage() { + + Device device = Display.getCurrent(); + ImageData data = getImageData(); + if (data == null) { + data = DEFAULT_IMAGE_DATA; + } + + /* + * Try to create the supplied image. If there is an SWT Exception + * try and create the default image if that was requested. Return + * null if this fails. + */ + + try { + if (data.transparentPixel >= 0) { + ImageData maskData = data.getTransparencyMask(); + return new Image(device, data, maskData); + } + return new Image(device, data); + } catch (SWTException exception) { + + try { + return new Image(device, DEFAULT_IMAGE_DATA); + } catch (SWTException nextException) { + return null; + } + + } + } + } + + /** inventory colors. */ + private static final InventoryColorDescriptor WHITE = new InventoryColorDescriptor(new RGB(255, 255, 255)); + + private static final InventoryColorDescriptor BLACK = new InventoryColorDescriptor(new RGB(0, 0, 0)); + + private static final InventoryColorDescriptor LIGHT_GRAY = new InventoryColorDescriptor(new RGB(192, 192, 192)); + + private static final InventoryColorDescriptor GRAY = new InventoryColorDescriptor(new RGB(128, 128, 128)); + + private static final InventoryColorDescriptor RED = new InventoryColorDescriptor(new RGB(227, 164, 156)); + + private static final InventoryColorDescriptor GREEN = new InventoryColorDescriptor(new RGB(166, 193, 152)); + + private static final InventoryColorDescriptor BLUE = new InventoryColorDescriptor(new RGB(152, 168, 191)); + + private static final InventoryColorDescriptor YELLOW = new InventoryColorDescriptor(new RGB(225, 225, 135)); + + private static final InventoryColorDescriptor PURPLE = new InventoryColorDescriptor(new RGB(184, 151, 192)); + + private static final InventoryColorDescriptor TEAL = new InventoryColorDescriptor(new RGB(155, 199, 204)); + + private static final InventoryColorDescriptor PINK = new InventoryColorDescriptor(new RGB(228, 179, 229)); + + private static final InventoryColorDescriptor ORANGE = new InventoryColorDescriptor(new RGB(237, 201, 122)); + + /** the inventory color list key: anRGB, value: anImage */ + private static final HashMap imageColorMap = new HashMap(); + + private static final String CUSTOM_COLOR_STRING = DiagramUIPropertiesMessages.ColorPalettePopup_custom; + + /** default color icon width. */ + public static final Point ICON_SIZE = new Point(20, 20); + + // CHECKSTYLE:ON + + static { + + // inventory colors + imageColorMap.put(WHITE.rgb, WHITE.createImage()); + imageColorMap.put(BLACK.rgb, BLACK.createImage()); + imageColorMap.put(LIGHT_GRAY.rgb, LIGHT_GRAY.createImage()); + imageColorMap.put(GRAY.rgb, GRAY.createImage()); + imageColorMap.put(RED.rgb, RED.createImage()); + imageColorMap.put(GREEN.rgb, GREEN.createImage()); + imageColorMap.put(BLUE.rgb, BLUE.createImage()); + imageColorMap.put(YELLOW.rgb, YELLOW.createImage()); + imageColorMap.put(PURPLE.rgb, PURPLE.createImage()); + imageColorMap.put(TEAL.rgb, TEAL.createImage()); + imageColorMap.put(PINK.rgb, PINK.createImage()); + imageColorMap.put(ORANGE.rgb, ORANGE.createImage()); + } + + private Shell shell; + + private RGB selectedColor; + + /** + * The default color to be used if the user presses the default button. + */ + private boolean useDefaultColor; + + /** + * Creates a color palette popup above the specified shell. + * + * @param parent + * a Shell control which will be the parent of the new instance + * (cannot be null) + * @param preferenceId + * the preference id + * @param rowHeight + * the row height + * @deprecated Use the other constructor. This one does not retrieve the + * default value from the correct preference store. + */ + @Deprecated + public ColorPalettePopup(Shell parent, String preferenceId, int rowHeight) { + this(parent, rowHeight); + } + + /** + * Creates a PopupList above the specified shell. + * + * @param parent + * a widget which will be the parent of the new instance (cannot + * be null) + * @param rowHeight + * the row height + */ + public ColorPalettePopup(Shell parent, int rowHeight) { + shell = new Shell(parent, ColorPalettePopup.checkStyle(SWT.NONE)); + GridLayout layout = new GridLayout(4, true); + layout.horizontalSpacing = 0; + layout.marginWidth = 0; + layout.marginHeight = 0; + layout.verticalSpacing = 0; + shell.setLayout(layout); + + // CHECKSTYLE:OFF + for (Iterator e = imageColorMap.keySet().iterator(); e.hasNext();) { + // CHECKSTYLE:ON + Button button = new Button(shell, SWT.PUSH); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + data.heightHint = rowHeight; + data.widthHint = rowHeight; + button.setLayoutData(data); + + final RGB rgb = (RGB) e.next(); + final Image image = (Image) imageColorMap.get(rgb); + button.setImage(image); + button.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent e1) { + selectedColor = rgb; + shell.dispose(); + } + }); + buttonMap.put(rgb, button); + } + // Button defaultButton = new Button(shell, SWT.PUSH); + // defaultButton.setText(DEAFULT_COLOR_STRING); + // GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + // data.horizontalSpan = 4; + // data.heightHint = rowHeight; + // defaultButton.setLayoutData(data); + // + // defaultButton.addSelectionListener(new SelectionAdapter() { + // + // public void widgetSelected(SelectionEvent event) { + // useDefaultColor = true; + // shell.dispose(); + // } + // }); + + Button moreColors = new Button(shell, SWT.PUSH); + moreColors.setText(CUSTOM_COLOR_STRING); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_FILL); + data.horizontalSpan = 4; + data.heightHint = rowHeight; + moreColors.setLayoutData(data); + + moreColors.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(SelectionEvent event) { + + ColorDialog dialog = new ColorDialog(Display.getCurrent().getActiveShell()); + dialog.setRGB(FigureUtilities.integerToRGB(getPreviousColor())); + WindowUtil.centerDialog(dialog.getParent(), Display.getCurrent().getActiveShell()); + dialog.open(); + selectedColor = dialog.getRGB(); + shell.dispose(); + + } + }); + // close dialog if user selects outside of the shell + shell.addListener(SWT.Deactivate, new Listener() { + + public void handleEvent(Event e) { + shell.setVisible(false); + } + }); + customColorButton = moreColors; + + } + + /** + * @param style + * @return + */ + private static int checkStyle(int style) { + int mask = SWT.LEFT_TO_RIGHT | SWT.RIGHT_TO_LEFT; + return style & mask; + } + + /** + * Launches the Popup List, waits for an item to be selected and then closes + * PopupList. + * + * @param location + * the initial size and location of the PopupList; the dialog + * will be positioned so that it does not run off the screen and + * the largest number of items are visible + * + * @return the text of the selected item or null if no item is selected + */ + public RGB open(Point location) { + + Point listSize = shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, false); + shell.setBounds(location.x, location.y, listSize.x, listSize.y); + + shell.open(); + shell.setFocus(); + Display display = shell.getDisplay(); + Button prevButton = (Button) buttonMap.get(FigureUtilities.integerToRGB(getPreviousColor())); + if (prevButton != null) { + shell.setDefaultButton(prevButton); + } else { + shell.setDefaultButton(customColorButton); + } + while (!shell.isDisposed() && shell.isVisible()) { + if (!display.readAndDispatch()) { + display.sleep(); + } + } + return getSelectedColor(); + } + + /** + * Gets the color the user selected. Could be null as the user may have + * cancelled the gesture or they may have selected the default color button. + * See {@link #useDefaultColor()}. + * + * @return the selected color or null + */ + public RGB getSelectedColor() { + return selectedColor; + } + + /** + * Returns true if the user selected to use the default color. + * + * @return true if the default color is to be used; false otherwise + */ + public boolean useDefaultColor() { + return useDefaultColor; + } + + /** + * Returns the previous color. + * + * @return the previous color. + */ + public int getPreviousColor() { + return previousColor; + } + + /** + * Sets the previous color. + * + * @param previousColor + * the previous color. + */ + public void setPreviousColor(int previousColor) { + this.previousColor = previousColor; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/DiagramElementsSelectionDialog.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/DiagramElementsSelectionDialog.java new file mode 100644 index 0000000000..6102f9b1ff --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/DiagramElementsSelectionDialog.java @@ -0,0 +1,893 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.dialogs; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ICheckStateProvider; +import org.eclipse.jface.viewers.IColorProvider; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.window.Window; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Combo; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.CheckedTreeSelectionDialog; + +import com.google.common.base.Function; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.ImagesPath; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.providers.FilteredTreeContentProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline.OutlineLabelProvider; +import org.eclipse.sirius.provider.SiriusItemProviderAdapterFactory; +import org.eclipse.sirius.ui.business.api.provider.AbstractDDiagramElementLabelItemProvider; +import org.eclipse.sirius.ui.tools.api.color.VisualBindingManager; + +/** + * A dialog box which allows the user to edit a boolean property/flag of a + * sub-set of the elements in a diagram. The dialog presents all the elements in + * the diagram and indicates their state (selected or not). The user can edit + * this state individually for each element. When the operation is validated by + * the user (by closing the dialog with the OK button) the specified editing + * operations are applied to the elements whose state has been changed (i.e. + * they have been selected or deselected). + * <p> + * What the notion of "selected" means can be customized through 3 + * programmer-specified functions: + * <ul> + * <li>a predicate to detect whether an element is selected or not (e.g. + * "the element is hidden")</li> + * <li>an action to apply to an element to make it selected (e.g. + * "set the element as hidden")</li> + * <li>an action to apply to an element to make it deselected (e.g. + * "set the element as not-hidden/reveal the element")</li> + * </ul> + * + * @author pcdavid + */ +public class DiagramElementsSelectionDialog { + + private static final Function<Object, Void> DO_NOTHING = new Function<Object, Void>() { + public Void apply(Object from) { + return null; + }; + }; + + /** + * The internal dialog used by this dialog. + */ + protected CustomTreeSelectionDialog dialog; + + /** + * The diagram associated to this dialog. + */ + protected DDiagram diagram; + + /** + * The filtering mode currently associated to the tree viewer. Can be : + * <ul> + * <li>All elements</li> + * <li>Only checked elements</li> + * <li>Only unchecked elements</li> + * </ul> + */ + protected FilteringMode mode = FilteringMode.SHOW_ALL; + + private final String title; + + private final String message; + + private Predicate<Object> isSelected = Predicates.alwaysTrue(); + + // Grayed elements will not be selectable. + private Predicate<Object> isGrayed = Predicates.alwaysFalse(); + + private Function<Object, Void> selectedAction = DO_NOTHING; + + private Function<Object, Void> deselectedAction = DO_NOTHING; + + private FilteredTreeContentProvider contentProvider; + + /** + * A customized version of CheckedTreeSelectionDialog with a combo to filter + * the view to show all elements/only checked elements/only unchecked + * elements. + * + * @author pcdavid + */ + protected final class CustomTreeSelectionDialog extends CheckedTreeSelectionDialog { + + /** + * A String used to identify the Text allowing user to type regular + * expression (can be used for testing). + */ + public static final String REGEXP_TOOL_TIP = "Expression that will be used to filer elements by name (for example 'abc', 'a?c', '*c'...)"; + + /** + * The title of the Group allowing user to type Regular Expressions and + * filter the selection. + */ + private static final String REGEXP_TITLE = "Filter elements by name"; + + /** + * The String explaining to user how to use regular expressions. + */ + private static final String REGEXP_EXPLANATIONS = "? = any character, * = any String"; + + /** + * A matcher used to determine if a given DDiagramElement is matching + * the regular expression typed by user. It's updated each time the user + * modify the regular expression. + */ + protected DiagramElementsSelectionDialogPatternMatcher patternMatcher; + + /** + * Collection of elements currently checked by user. + */ + private final Set<Object> checkedElements = Sets.newHashSet(); + + private CustomTreeSelectionDialog(Shell parent, ILabelProvider labelProvider, ITreeContentProvider contentProvider) { + super(parent, labelProvider, contentProvider); + patternMatcher = new DiagramElementsSelectionDialogPatternMatcher(""); + } + + @Override + public void setInitialSelections(Object[] selectedElements) { + setInitialElementSelections(Lists.newArrayList(selectedElements)); + } + + @Override + public void setInitialElementSelections(List selectedElements) { + List<Object> filteredSeletection = Lists.newArrayList(Iterables.filter(selectedElements, Predicates.not(isGrayed))); + checkedElements.addAll(filteredSeletection); + super.setInitialElementSelections(filteredSeletection); + } + + /** + * Returns the elements currently checked by user. + * + * @return the elements currently checked by user + */ + public Set<Object> getCheckedElements() { + return checkedElements; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.dialogs.Dialog#createContents(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createContents(Composite parent) { + Control result = super.createContents(parent); + getTreeViewer().setCheckStateProvider(new ICheckStateProvider() { + + public boolean isGrayed(Object element) { + return isGrayed.apply(element); + } + + public boolean isChecked(Object element) { + return checkedElements.contains(element); + } + }); + getTreeViewer().addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + if (!isGrayed.apply(event.getElement())) { + if (event.getChecked()) { + checkedElements.add(event.getElement()); + } else { + checkedElements.remove(event.getElement()); + } + } + } + }); + return result; + } + + /** + * This method has been overridden to be able to insert selection + * buttons between the top label and the tree viewer. + * + * {@inheritDoc} + */ + @Override + protected Label createMessageArea(Composite composite) { + Label createMessageArea = super.createMessageArea(composite); + + createSelectionButtonsAfterMessageArea(composite); + + // creating a text zone to allow user to type regular expressions + createRegexpTypeZone(composite); + return createMessageArea; + } + + /** + * This method has been overridden to remove the selection buttons that + * are generically created after the tree viewer. This method should not + * return a null value. Otherwise, in case of empty list we will have a + * NPE. + * + * {@inheritDoc} + */ + @Override + protected Composite createSelectionButtons(Composite composite) { + Composite buttonComposite = new Composite(composite, SWT.RIGHT) { + /** + * This method has been overridden to have an "empty" size for + * this composite. {@inheritDoc} + * + * @see org.eclipse.swt.widgets.Composite#computeSize(int, int, + * boolean) + */ + @Override + public Point computeSize(int wHint, int hHint, boolean b) { + return super.computeSize(0, 0, b); + } + }; + buttonComposite.setVisible(false); + return buttonComposite; + } + + /** + * Creates selection buttons. + * + * @param composite + * the parent composite + * @return the selection buttons composite + */ + protected Composite createSelectionButtonsAfterMessageArea(Composite composite) { + Composite buttonComposite = new Composite(composite, SWT.RIGHT); + GridLayout layout = new GridLayout(); + layout.numColumns = 7; + layout.makeColumnsEqualWidth = false; + buttonComposite.setLayout(layout); + buttonComposite.setFont(composite.getFont()); + + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_END | GridData.GRAB_HORIZONTAL); + data.grabExcessHorizontalSpace = true; + composite.setData(data); + + new Label(buttonComposite, SWT.LEAD).setText("Show"); + final Combo choices = new Combo(buttonComposite, SWT.READ_ONLY); + choices.add(FilteringMode.SHOW_ALL.getName()); + choices.add(FilteringMode.SHOW_ONLY_CHECKED_ELEMENTS.getName()); + choices.add(FilteringMode.SHOW_ONLY_UNCHECKED_ELEMENTS.getName()); + choices.select(0); + choices.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + switch (choices.getSelectionIndex()) { + case 0: + updateFilteringMode(FilteringMode.SHOW_ALL); + break; + case 1: + updateFilteringMode(FilteringMode.SHOW_ONLY_CHECKED_ELEMENTS); + break; + case 2: + updateFilteringMode(FilteringMode.SHOW_ONLY_UNCHECKED_ELEMENTS); + break; + default: + throw new RuntimeException(); + } + } + }); + data = new GridData(GridData.HORIZONTAL_ALIGN_END | GridData.GRAB_HORIZONTAL); + data.grabExcessHorizontalSpace = true; + data.horizontalSpan = 2; + choices.setLayoutData(data); + + data = new GridData(GridData.HORIZONTAL_ALIGN_FILL | GridData.GRAB_HORIZONTAL); + data.grabExcessHorizontalSpace = true; + addButton(buttonComposite, "Check All", SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.CHECK_ALL_ICON), new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + checkAll(); + if (choices.getSelectionIndex() == 1) { + updateFilteringMode(FilteringMode.SHOW_ONLY_CHECKED_ELEMENTS); + } else if (choices.getSelectionIndex() == 2) { + updateFilteringMode(FilteringMode.SHOW_ONLY_UNCHECKED_ELEMENTS); + } + } + }).setLayoutData(data); + + addButton(buttonComposite, "Uncheck All", SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.UNCHECK_ALL_ICON), new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + uncheckAll(); + if (choices.getSelectionIndex() == 1) { + updateFilteringMode(FilteringMode.SHOW_ONLY_CHECKED_ELEMENTS); + } else if (choices.getSelectionIndex() == 2) { + updateFilteringMode(FilteringMode.SHOW_ONLY_UNCHECKED_ELEMENTS); + } + } + }).setLayoutData(data); + + addButton(buttonComposite, "Expand All", SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.EXPAND_ALL_ICON), new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + expandAll(); + } + }).setLayoutData(data); + + addButton(buttonComposite, "Collapse All", SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.COLLAPSE_ALL_ICON), new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + collapseAll(); + } + }).setLayoutData(data); + + return buttonComposite; + } + + /** + * Creates a zone in which user will be able to type a Regular + * Expression to filter the shown elements. + * + * @param composite + * the parent composite + */ + private void createRegexpTypeZone(Composite composite) { + // Step 1 : create Group + Group expregGroup = new Group(composite, SWT.NONE); + expregGroup.setText(REGEXP_TITLE); + GridLayout expregLayout = new GridLayout(); + expregGroup.setLayout(expregLayout); + expregGroup.setFont(composite.getFont()); + expregGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Step 2 : create explanations zone + final Label explanationsLabel = new Label(expregGroup, SWT.NONE); + explanationsLabel.setText(REGEXP_EXPLANATIONS); + + // Step 3 : create the text zone in which user will type the expreg + final Text regularExpressionText = new Text(expregGroup, SWT.BORDER); + regularExpressionText.setToolTipText(REGEXP_TOOL_TIP); + regularExpressionText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + + // Step 4 : add modify listener to this textZone + regularExpressionText.addModifyListener(new ModifyListener() { + + public void modifyText(ModifyEvent e) { + String typedRegex = ((Text) e.getSource()).getText(); + // Each time the regular expression is modified, the + // patternMatcher is updated + setPatternMatcher(new DiagramElementsSelectionDialogPatternMatcher(typedRegex)); + updateFilteringMode(mode); + } + }); + } + + /** + * Sets the matcher used to determine if a given DDiagramElement is + * matching the regular expression typed by user. + * + * @param patternMatcher + * the patternMatcher to set + */ + public void setPatternMatcher(DiagramElementsSelectionDialogPatternMatcher patternMatcher) { + this.patternMatcher = patternMatcher; + } + + private Button addButton(Composite parent, String toolTipText, Image image, SelectionListener action) { + Button button = new Button(parent, SWT.PUSH); + button.setToolTipText(toolTipText); + button.setImage(image); + button.addSelectionListener(action); + return button; + } + + private void checkAll() { + Object root = getTreeViewer().getInput(); + setRecursiveState(root, true); + } + + private void uncheckAll() { + Object root = getTreeViewer().getInput(); + setRecursiveState(root, false); + } + + private void setRecursiveState(Object element, boolean state) { + getTreeViewer().setChecked(element, state); + if (!isGrayed.apply(element)) { + if (state) { + checkedElements.add(element); + } else { + checkedElements.remove(element); + } + } + for (Object child : contentProvider.getChildren(element)) { + setRecursiveState(child, state); + } + } + + private void expandAll() { + getTreeViewer().expandAll(); + } + + private void collapseAll() { + getTreeViewer().collapseAll(); + } + + /** + * Updates the treeViewer after a change in the filteringMode or the + * typed regular expression. + * + * @param filteringMode + * the new filtering mode + */ + public void updateFilteringMode(FilteringMode filteringMode) { + mode = filteringMode; + this.refresh(); + // We expand the tree so that all elements matching the regular + // expression (i.e. all visible leafs) are correctly shown + getTreeViewer().expandAll(); + getTreeViewer().setAllChecked(false); + for (Object element : checkedElements) { + getTreeViewer().setChecked(element, true); + } + } + + /** + * Indicates if the given element is checked <b>AND</b> is matching the + * currently typed regular expression. + * + * @param element + * the element to test + * @return true if the given element is checked <b>AND</b> is matching + * the currently typed regular expression, false otherwise. + */ + public boolean isMatchingExpregOrHasMatchingExpregDescendantsCheckedMode(Object element) { + Predicate<Object> isCheckedElementPredicate = Predicates.in(checkedElements); + Predicate<Object> isMatchinExpregPredicate = getRegexpMatchPredicate(); + return isOrHasDescendant(element, Predicates.and(isCheckedElementPredicate, isMatchinExpregPredicate)); + } + + /** + * Indicates if the given element is unchecked <b>AND</b> is matching + * the currently typed regular expression. + * + * @param element + * the element to test + * @return true if the given element is unchecked <b>AND</b> is matching + * the currently typed regular expression, false otherwise. + */ + public boolean isMatchingExpregOrHasMatchingExpregDescendantsUncheckedMode(Object element) { + Predicate<Object> isUncheckedElementPredicate = Predicates.not(Predicates.in(checkedElements)); + Predicate<Object> isMatchinExpregPredicate = getRegexpMatchPredicate(); + return isOrHasDescendant(element, Predicates.and(isUncheckedElementPredicate, isMatchinExpregPredicate)); + } + + /** + * Indicates if the given element is matching the currently typed + * regular expression. + * + * @param element + * the element to test + * @return true if the given element is matching the currently typed + * regular expression, false otherwise. + */ + public boolean isMatchingExpregOrHasMatchingExpregDescendantsAllMode(Object element) { + return isOrHasDescendant(element, getRegexpMatchPredicate()); + } + + /** + * Indicates if the given element or at least one of its children checks + * the given predicate. + * + * @param element + * the element to check + * @param pred + * the predicate to sue + * @return true if the given element or at least one of its children + * checks the given predicate, false otherwise + */ + public boolean isOrHasDescendant(Object element, final Predicate<Object> pred) { + boolean matches = pred.apply(element); + if (matches) { + return true; + } else { + return Iterables.any(Arrays.asList(contentProvider.getChildren(element)), new Predicate<Object>() { + public boolean apply(Object input) { + return isOrHasDescendant(input, pred); + } + }); + } + } + + /** + * Refreshes this dialog's viewer. + */ + public void refresh() { + getTreeViewer().refresh(); + } + + /** + * Returns a Predicate indicating if an object is matching the Regular + * Expression currently typed by user. + * + * @return a Predicate indicating if an object is matching the Regular + * Expression currently typed by user + */ + public Predicate<Object> getRegexpMatchPredicate() { + return patternMatcher.getMatchPredicate(); + } + } + + /** + * Represents the various kinds of filtering supported by the tree viewer. + * + * @author pcdavid + */ + protected enum FilteringMode { + /** + * Filtering mode in which all elements are considered. + */ + SHOW_ALL("all elements"), + /** + * Filtering mode in which only checked elements are considered. + */ + SHOW_ONLY_CHECKED_ELEMENTS("only checked elements"), + /** + * Filtering mode in which only unchecked elements are considered. + */ + SHOW_ONLY_UNCHECKED_ELEMENTS("only unchecked elements"); + + private final String name; + + private FilteringMode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + } + + /** + * Constructor. + * + * @param title + * the title of the dialog window. + * @param message + * the message for the dialog. + */ + public DiagramElementsSelectionDialog(String title, String message) { + this.title = title; + this.message = message; + } + + /** + * Sets the predicate to use to detect which elements of the diagram are + * selected, in the sense of the criterion to be edited. + * + * @param isSelectedPredicate + * the predicate to used to detect selected elements of the + * diagram. + */ + public void setSelectionPredicate(Predicate<Object> isSelectedPredicate) { + this.isSelected = isSelectedPredicate; + } + + /** + * Sets the predicate to use to detect which elements of the diagram are + * selected, in the sense of the criterion to be edited. + * + * @param isGrayedPredicate + * the predicate to used to detect selected elements of the + * diagram. + */ + public void setGrayedPredicate(Predicate<Object> isGrayedPredicate) { + this.isGrayed = isGrayedPredicate != null ? isGrayedPredicate : Predicates.alwaysFalse(); + } + + /** + * @param parent + * @return + */ + private Set<Object> getAllChildren(Object parent) { + Set<Object> result = new HashSet<Object>(); + Object[] children = contentProvider.getChildren(parent); + for (Object element : children) { + result.add(element); + result.addAll(getAllChildren(element)); + } + return result; + } + + /** + * Sets the operation to be applied on elements which are newly selected by + * the user. + * + * @param selectedAction + * the operation to apply to newly selected elements. + */ + public void setSelectedAction(Function<Object, Void> selectedAction) { + this.selectedAction = selectedAction; + } + + /** + * Sets the operation to be applied on elements which are deselected by the + * user. + * + * @param deselectedAction + * the operation to apply to deselected elements. + */ + public void setDeselectedAction(Function<Object, Void> deselectedAction) { + this.deselectedAction = deselectedAction; + } + + /** + * Asks the end-user for a list of elements to select/de-select, and applies + * the corresponding changes. + * + * @param parent + * the shell to use to interact with the user, if required. + * @param ddiagram + * the diagram whose elements to edit. + * @param includeNodeLabel + * include node label (if there are on border) in the tree + * content + * @return <code>true</code> if the operation was correctly executed, + * <code>false</code> if it was canceled by the user. + */ + public boolean open(Shell parent, DDiagram ddiagram, boolean includeNodeLabel) { + boolean result = false; + diagram = ddiagram; + + initContentProvider(includeNodeLabel); + + Set<Object> allSelectedElements = Collections.unmodifiableSet(getAllSelectedElements()); + Option<Set<Object>> response = askUserForNewSelection(parent, allSelectedElements); + if (response.some()) { + Set<Object> selectedAfter = response.get(); + applyRequestedChanges(allSelectedElements, selectedAfter); + assert selectedAfter.equals(allSelectedElements); + result = true; + } + diagram = null; + dialog = null; + contentProvider = null; + return result; + } + + /** + * Init the content provider. + * + * @param includeNodeLabel + * include node label (if there are on border) in the tree + * content + */ + protected void initContentProvider(boolean includeNodeLabel) { + AdapterFactory adapterFactory = getAdapterFactory(); + Predicate<Object> predicate = Predicates.instanceOf(DDiagramElement.class); + if (includeNodeLabel) { + predicate = Predicates.or(predicate, Predicates.instanceOf(AbstractDDiagramElementLabelItemProvider.class)); + } + contentProvider = new FilteredTreeContentProvider(adapterFactory, predicate); + } + + /** + * Return all selected elements of the diagram that are display in the tree. + * + * @return All selected elements of the diagram that are display in the + * tree. + */ + protected Set<Object> getAllSelectedElements() { + Set<Object> treeElements = getAllChildren(diagram); + return Sets.newHashSet(Iterators.filter(treeElements.iterator(), Predicates.and(isSelected, Predicates.not(isGrayed)))); + } + + /** + * Asks the user to edit the set of elements which should be selected/match + * the criterion being edited. + * + * @param parent + * the parent shell to use if user interaction requires opening + * new windows. + * @param initialSelection + * the set of elements to display as checked on dialog opening. + * @return the new set of all the elements in the diagram which were + * selected by the user, or <code>Options.newNone()</code> if the + * user canceled the operation. + */ + protected Option<Set<Object>> askUserForNewSelection(Shell parent, Set<Object> initialSelection) { + setupDialog(parent, initialSelection); + int result = dialog.open(); + if (result == Window.OK) { + Set<Object> selectedAfter = getElementsSelectedAfter(); + return Options.newSome(selectedAfter); + } else { + return Options.newNone(); + } + } + + /** + * Create an configure a selection dialog which allows the user to select a + * sub-set of the elements in the diagram. + * + * @param parent + * the parent shell. + * @param initialSelection + * the set of elements to display as checked on dialog opening. + */ + protected void setupDialog(Shell parent, Set<Object> initialSelection) { + dialog = new CustomTreeSelectionDialog(parent, new SelectionDialogLabelProvider(), contentProvider); + dialog.setTitle(title); + + String msg = message; + if (!Predicates.alwaysFalse().equals(isGrayed)) { + StringBuilder sb = new StringBuilder(message); + sb.append("\n"); + sb.append("The wizard will have no effect on grayed elements."); + msg = sb.toString(); + } + dialog.setMessage(msg); + dialog.setInput(diagram); + dialog.addFilter(new ModeFilter()); + dialog.setInitialElementSelections(Lists.newArrayList(initialSelection)); + } + + /** + * Returns the elements selected when the dialog is about to close. + * + * @return the elements selected when the dialog is about to close + */ + protected Set<Object> getElementsSelectedAfter() { + Set<Object> selectedElements = Sets.newHashSet(); + for (Object obj : dialog.checkedElements) { + if (obj instanceof DDiagramElement) { + selectedElements.add(obj); + } else if (obj instanceof AbstractDDiagramElementLabelItemProvider) { + selectedElements.add(obj); + } + } + return selectedElements; + } + + /** + * Updates the status of the elements according to the user request. + * + * @param selectedBefore + * all (and only) the elements in the diagram which were actually + * pinned before the action. + * @param selectedAfter + * all (and only) the elements in the diagram which should be + * pinned as requested by the user. + */ + protected void applyRequestedChanges(Set<Object> selectedBefore, Set<Object> selectedAfter) { + for (Object dde : Sets.difference(selectedBefore, selectedAfter)) { + deselectedAction.apply(dde); + } + for (Object dde : Sets.difference(selectedAfter, selectedBefore)) { + selectedAction.apply(dde); + } + } + + /** + * Returns the adapter factory used by this viewer. + * + * @return The adapter factory used by this viewer. + */ + private AdapterFactory getAdapterFactory() { + List<AdapterFactory> factories = new ArrayList<AdapterFactory>(); + factories.add(new SiriusItemProviderAdapterFactory()); + factories.add(new ResourceItemProviderAdapterFactory()); + factories.add(new EcoreItemProviderAdapterFactory()); + factories.add(new ReflectiveItemProviderAdapterFactory()); + return new ComposedAdapterFactory(factories); + } + + private class ModeFilter extends ViewerFilter { + + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + boolean show = true; + // Step 1: only showing check/unchecked element if required + switch (mode) { + case SHOW_ALL: + show = dialog.isMatchingExpregOrHasMatchingExpregDescendantsAllMode(element); + break; + case SHOW_ONLY_CHECKED_ELEMENTS: + show = dialog.isMatchingExpregOrHasMatchingExpregDescendantsCheckedMode(element); + break; + case SHOW_ONLY_UNCHECKED_ELEMENTS: + show = dialog.isMatchingExpregOrHasMatchingExpregDescendantsUncheckedMode(element); + break; + default: + show = true; + break; + } + + // Step 2: only showing elements which target has not been + // deleted + boolean isNonDangling = true; + if (show) { + DDiagramElement underlyingDDiagramElement = null; + if (element instanceof DDiagramElement) { + underlyingDDiagramElement = (DDiagramElement) element; + } else if (element instanceof AbstractDDiagramElementLabelItemProvider && ((AbstractDDiagramElementLabelItemProvider) element).getDiagramElementTarget().some()) { + underlyingDDiagramElement = ((AbstractDDiagramElementLabelItemProvider) element).getDiagramElementTarget().get(); + } + + if (underlyingDDiagramElement != null && underlyingDDiagramElement.eResource() != null) { + isNonDangling = underlyingDDiagramElement.getTarget() != null && underlyingDDiagramElement.getTarget().eResource() != null; + } else { + isNonDangling = false; + } + } + return show && isNonDangling; + } + } + + private class SelectionDialogLabelProvider extends OutlineLabelProvider implements IColorProvider { + + /** + * {@inheritDoc} + */ + public Color getForeground(final Object element) { + + Color foreground = null; + if (isGrayed.apply(element)) { + foreground = VisualBindingManager.getDefault().getColorFromName("light_gray"); //$NON-NLS-1$ + } + return foreground; + } + + /** + * {@inheritDoc} + */ + public Color getBackground(Object element) { + return null; + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/DiagramElementsSelectionDialogPatternMatcher.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/DiagramElementsSelectionDialogPatternMatcher.java new file mode 100644 index 0000000000..d384a28368 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/DiagramElementsSelectionDialogPatternMatcher.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.dialogs; + +import org.eclipse.gmf.runtime.common.core.util.StringMatcher; + +import com.google.common.base.Predicate; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.ui.business.api.provider.AbstractDDiagramElementLabelItemProvider; + +/** + * <p> + * A {@link StringMatcher} used to detect elements that match a regular + * expression. + * </p> + * + * This Matcher creates a MatchPredicate, that can be used to determine if a + * given Object matches a regular expression. + * + * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> + */ +public class DiagramElementsSelectionDialogPatternMatcher { + + private Predicate<Object> matchPredicate; + + private StringMatcher stringMatcher; + + /** + * Creates a new PatternMatcher. + * + * + * @param expreg + * the regular expression (for example '?a?' or 'abc' or '*c') ; + * <code>null</code> or empty regular expression will be replaced + * by '*' + * + */ + public DiagramElementsSelectionDialogPatternMatcher(String expreg) { + String computedExpreg = expreg; + if (expreg == null) { + computedExpreg = ""; + } + // If the regular expression ends with a space, we have to use the exact + // value of the given expreg + if (computedExpreg.endsWith(" ")) { + computedExpreg = computedExpreg.substring(0, computedExpreg.lastIndexOf(' ')); + } else { + // Otherwise, we add a star to make 'XYZ' recognized by the 'X' + // expreg (as in quick outline for example) + computedExpreg = computedExpreg + "*"; + } + this.stringMatcher = new StringMatcher(computedExpreg, true, false); + + } + + /** + * Creates a {@link Predicate} that can be applied on any Object. This + * predicates will return true if the tested element is a + * {@link DDiagramElement} and that its name is matching the regular + * expression used to construct this Matcher. + * + * @return a {@link Predicate} that can be applied on any Object to + * determine if whether it's matching the regular expression used to + * construct this Matcher + */ + public Predicate<Object> getMatchPredicate() { + if (matchPredicate == null) { + matchPredicate = new Predicate<Object>() { + + public boolean apply(Object input) { + String elementName = null; + if (input instanceof DDiagramElement) { + DDiagramElement element = (DDiagramElement) input; + elementName = element.getName(); + } else if (input instanceof AbstractDDiagramElementLabelItemProvider) { + AbstractDDiagramElementLabelItemProvider element = (AbstractDDiagramElementLabelItemProvider) input; + elementName = element.getText(element.getTarget()); + } + return elementName != null && stringMatcher.match(elementName); + } + }; + } + return matchPredicate; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/ExtendedFeatureEditorDialog.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/ExtendedFeatureEditorDialog.java new file mode 100644 index 0000000000..eaae9367d8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/ExtendedFeatureEditorDialog.java @@ -0,0 +1,535 @@ +/** + * <copyright> + * + * Copyright (c) 2002-2008 IBM Corporation 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: + * IBM - Initial API and implementation + * Obeo - Adapt for extension + * + * </copyright> + * + * $Id: FeatureEditorDialog.java,v 1.11 2007/03/23 17:36:45 marcelop Exp $ + */ + +package org.eclipse.sirius.diagram.ui.tools.internal.dialogs; + +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.common.notify.impl.AdapterFactoryImpl; +import org.eclipse.emf.common.util.BasicEList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.emf.edit.provider.IItemLabelProvider; +import org.eclipse.emf.edit.provider.ItemProvider; +import org.eclipse.emf.edit.ui.EMFEditUIPlugin; +import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider; +import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.KeyAdapter; +import org.eclipse.swt.events.KeyEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ExtensionFeatureDescription; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; + +/** + * Dialog to edit an extended reference. + * + * @author ymortier + */ +public class ExtendedFeatureEditorDialog extends Dialog { + + /** the list of choosen elements. */ + protected EList<EObject> result; + + /** The list of choices. */ + private List<EObject> choices; + + /** The current values of the reference. */ + private List<EObject> referenceValues; + + /** The reference to edit. */ + private ExtensionFeatureDescription extReference; + + private boolean multiLine; + + private ItemProvider values; + + private IContentProvider contentProvider; + + private ILabelProvider labelProvider; + + /** + * Creates a new <code>ExtendedFeatureEditorDialog</code>. see + * {@link org.eclipse.emf.edit.ui.celleditor.FeatureEditorDialog}. + * + * @param parent + * the parent. + * @param choices + * the choices. + * @param referenceValues + * the reference values. + * @param extReference + * the ext reference. + */ + public ExtendedFeatureEditorDialog(final Shell parent, final List<EObject> choices, final List<EObject> referenceValues, final ExtensionFeatureDescription extReference) { + super(parent); + this.choices = choices; + this.referenceValues = referenceValues; + this.extReference = extReference; + final List<AdapterFactory> adapterFactories = Collections.emptyList(); + final AdapterFactory adapterFactory = new ComposedAdapterFactory(adapterFactories); + values = new ItemProvider(adapterFactory, this.referenceValues); + contentProvider = new AdapterFactoryContentProvider(adapterFactory); + this.choices.removeAll(this.referenceValues); + this.labelProvider = new LabelProvider() { + @Override + public String getText(final Object element) { + if (element instanceof EObject) { + final ModelAccessor accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor((EObject) element); + return accessor.getQualifiedName((EObject) element, true); + } + return String.valueOf(element); + } + + @Override + public Image getImage(final Object element) { + ImageDescriptor descriptor = null; + if (element instanceof EObject) { + final EObject target = (EObject) element; + final IItemLabelProvider myLabelProvider = (IItemLabelProvider) SiriusDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory().adapt(target, IItemLabelProvider.class); + descriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(myLabelProvider.getImage(target)); + + } + if (descriptor == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + } + return SiriusDiagramEditorPlugin.getInstance().getImage(descriptor); + } + + }; + + } + + /** + * Creates the dialog. + * + * This code is cut/paste from the + * {@link org.eclipse.emf.edit.ui.celleditor.FeatureEditorDialog} class. + * + * @param parent + * the parent. + * @return the control. + */ + @Override + protected Control createDialogArea(final Composite parent) { + final Composite contents = (Composite) super.createDialogArea(parent); + + final GridLayout contentsGridLayout = (GridLayout) contents.getLayout(); + contentsGridLayout.numColumns = 3; + + final GridData contentsGridData = (GridData) contents.getLayoutData(); + contentsGridData.horizontalAlignment = SWT.FILL; + contentsGridData.verticalAlignment = SWT.FILL; + + final Composite choiceComposite = createChoiceComposite(contents); + + final Table choiceTable = this.choices == null ? null : new Table(choiceComposite, SWT.MULTI | SWT.BORDER); + if (choiceTable != null) { + final GridData choiceTableGridData = new GridData(); + choiceTableGridData.widthHint = Display.getCurrent().getBounds().width / 5; + choiceTableGridData.heightHint = Display.getCurrent().getBounds().height / 3; + choiceTableGridData.verticalAlignment = SWT.FILL; + choiceTableGridData.horizontalAlignment = SWT.FILL; + choiceTableGridData.grabExcessHorizontalSpace = true; + choiceTableGridData.grabExcessVerticalSpace = true; + choiceTable.setLayoutData(choiceTableGridData); + } + + final TableViewer choiceTableViewer = this.choices == null ? null : new TableViewer(choiceTable); + if (choiceTableViewer != null) { + choiceTableViewer.setContentProvider(new AdapterFactoryContentProvider(new AdapterFactoryImpl())); + choiceTableViewer.setLabelProvider(labelProvider); + choiceTableViewer.setInput(new ItemProvider(this.choices)); + } + + // We use multi even for a single line because we want to respond to the + // enter key. + // + + int style = multiLine ? SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL : SWT.MULTI; + style = style | SWT.BORDER; + + final Text choiceText = createChoiceText(choiceComposite, style); + + final Composite controlButtons = createControlButtonsArea(contents); + + new Label(controlButtons, SWT.NONE); + + final Button addButton = createAddButton(controlButtons); + + final Button removeButton = createRemoveButton(controlButtons); + + final Label spaceLabel = new Label(controlButtons, SWT.NONE); + final GridData spaceLabelGridData = new GridData(); + spaceLabelGridData.verticalSpan = 2; + spaceLabel.setLayoutData(spaceLabelGridData); + + final Button upButton = createUpButton(controlButtons); + + final Button downButton = createDownButton(controlButtons); + + final Composite featureComposite = createFeatureComposite(contents); + + final Table featureTable = new Table(featureComposite, SWT.MULTI | SWT.BORDER); + final GridData featureTableGridData = new GridData(); + featureTableGridData.widthHint = Display.getCurrent().getBounds().width / 5; + featureTableGridData.heightHint = Display.getCurrent().getBounds().height / 3; + featureTableGridData.verticalAlignment = SWT.FILL; + featureTableGridData.horizontalAlignment = SWT.FILL; + featureTableGridData.grabExcessHorizontalSpace = true; + featureTableGridData.grabExcessVerticalSpace = true; + featureTable.setLayoutData(featureTableGridData); + + final TableViewer featureTableViewer = new TableViewer(featureTable); + featureTableViewer.setContentProvider(contentProvider); + featureTableViewer.setLabelProvider(labelProvider); + featureTableViewer.setInput(values); + if (!values.getChildren().isEmpty()) { + featureTableViewer.setSelection(new StructuredSelection(values.getChildren().get(0))); + } + setTableViewerListener(choiceTableViewer, featureTableViewer, addButton, removeButton); + + setUpButtonListener(upButton, featureTableViewer); + setDownButtonListener(downButton, featureTableViewer); + + setAddButtonListener(addButton, choiceTableViewer, featureTableViewer, choiceText); + setRemoveButtonListener(removeButton, choiceTableViewer, featureTableViewer, choiceText); + + return contents; + } + + private Text createChoiceText(final Composite parent, final int style) { + final Text choiceText = this.choices == null ? new Text(parent, style) : null; + if (choiceText != null) { + final GridData choiceTextGridData = new GridData(); + choiceTextGridData.widthHint = Display.getCurrent().getBounds().width / 5; + choiceTextGridData.verticalAlignment = SWT.BEGINNING; + choiceTextGridData.horizontalAlignment = SWT.FILL; + choiceTextGridData.grabExcessHorizontalSpace = true; + if (multiLine) { + choiceTextGridData.verticalAlignment = SWT.FILL; + choiceTextGridData.grabExcessVerticalSpace = true; + } + choiceText.setLayoutData(choiceTextGridData); + } + + if (choiceText != null) { + choiceText.addKeyListener(new KeyAdapter() { + @Override + public void keyPressed(final KeyEvent event) { + if (!multiLine && (event.character == '\r' || event.character == '\n')) { + + // Object value = + // EcoreUtil.createFromString((EDataType) + // eClassifier, choiceText.getText()); + // values.getChildren().add(value); + // choiceText.setText(StringUtil.EMPTY_STRING); + // featureTableViewer.setSelection(new + // StructuredSelection(value)); + // event.doit = false; + + } else if (event.character == '\33') { + choiceText.setText(StringUtil.EMPTY_STRING); + event.doit = false; + } + } + }); + } + return choiceText; + } + + private Composite createChoiceComposite(final Composite contents) { + final Composite choiceComposite = new Composite(contents, SWT.NONE); + + final GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + data.horizontalAlignment = SWT.END; + choiceComposite.setLayoutData(data); + + final GridLayout layout = new GridLayout(); + data.horizontalAlignment = SWT.FILL; + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.numColumns = 1; + choiceComposite.setLayout(layout); + + final Label choiceLabel = new Label(choiceComposite, SWT.NONE); + choiceLabel.setText(this.choices == null ? EMFEditUIPlugin.INSTANCE.getString("_UI_Value_label") : EMFEditUIPlugin.INSTANCE.getString("_UI_Choices_label")); + final GridData choiceLabelGridData = new GridData(); + choiceLabelGridData.verticalAlignment = SWT.FILL; + choiceLabelGridData.horizontalAlignment = SWT.FILL; + choiceLabel.setLayoutData(choiceLabelGridData); + + return choiceComposite; + } + + private Composite createFeatureComposite(final Composite contents) { + final Composite featureComposite = new Composite(contents, SWT.NONE); + + final GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + data.horizontalAlignment = SWT.END; + featureComposite.setLayoutData(data); + + final GridLayout layout = new GridLayout(); + data.horizontalAlignment = SWT.FILL; + layout.marginHeight = 0; + layout.marginWidth = 0; + layout.numColumns = 1; + featureComposite.setLayout(layout); + + final Label featureLabel = new Label(featureComposite, SWT.NONE); + featureLabel.setText(this.extReference.getName()); + final GridData featureLabelGridData = new GridData(); + featureLabelGridData.horizontalSpan = 2; + featureLabelGridData.horizontalAlignment = SWT.FILL; + featureLabelGridData.verticalAlignment = SWT.FILL; + featureLabel.setLayoutData(featureLabelGridData); + + return featureComposite; + } + + private Composite createControlButtonsArea(final Composite parent) { + final Composite controlButtons = new Composite(parent, SWT.NONE); + final GridData controlButtonsGridData = new GridData(); + controlButtonsGridData.verticalAlignment = SWT.FILL; + controlButtonsGridData.horizontalAlignment = SWT.FILL; + controlButtons.setLayoutData(controlButtonsGridData); + + final GridLayout controlsButtonGridLayout = new GridLayout(); + controlButtons.setLayout(controlsButtonGridLayout); + return controlButtons; + } + + private Button createAddButton(final Composite controlButtons) { + final Button addButton = new Button(controlButtons, SWT.PUSH); + addButton.setText(EMFEditUIPlugin.INSTANCE.getString("_UI_Add_label")); + final GridData addButtonGridData = new GridData(); + addButtonGridData.verticalAlignment = SWT.FILL; + addButtonGridData.horizontalAlignment = SWT.FILL; + addButton.setLayoutData(addButtonGridData); + return addButton; + } + + private Button createRemoveButton(final Composite controlButtons) { + final Button removeButton = new Button(controlButtons, SWT.PUSH); + removeButton.setText(EMFEditUIPlugin.INSTANCE.getString("_UI_Remove_label")); + final GridData removeButtonGridData = new GridData(); + removeButtonGridData.verticalAlignment = SWT.FILL; + removeButtonGridData.horizontalAlignment = SWT.FILL; + removeButton.setLayoutData(removeButtonGridData); + return removeButton; + } + + private Button createUpButton(final Composite controlButtons) { + final Button upButton = new Button(controlButtons, SWT.PUSH); + upButton.setText(EMFEditUIPlugin.INSTANCE.getString("_UI_Up_label")); + final GridData upButtonGridData = new GridData(); + upButtonGridData.verticalAlignment = SWT.FILL; + upButtonGridData.horizontalAlignment = SWT.FILL; + upButton.setLayoutData(upButtonGridData); + return upButton; + } + + private Button createDownButton(final Composite controlButtons) { + final Button downButton = new Button(controlButtons, SWT.PUSH); + downButton.setText(EMFEditUIPlugin.INSTANCE.getString("_UI_Down_label")); + final GridData downButtonGridData = new GridData(); + downButtonGridData.verticalAlignment = SWT.FILL; + downButtonGridData.horizontalAlignment = SWT.FILL; + downButton.setLayoutData(downButtonGridData); + return downButton; + } + + private void setUpButtonListener(final Button upButton, final TableViewer featureTableViewer) { + upButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent event) { + final IStructuredSelection selection = (IStructuredSelection) featureTableViewer.getSelection(); + int minIndex = 0; + final Iterator<?> i = selection.iterator(); + while (i.hasNext()) { + final Object value = i.next(); + final int index = values.getChildren().indexOf(value); + values.getChildren().move(Math.max(index - 1, minIndex++), value); + } + } + }); + } + + private void setDownButtonListener(final Button downButton, final TableViewer featureTableViewer) { + downButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(final SelectionEvent event) { + final IStructuredSelection selection = (IStructuredSelection) featureTableViewer.getSelection(); + int maxIndex = values.getChildren().size() - selection.size(); + final Iterator<?> i = selection.iterator(); + while (i.hasNext()) { + final Object value = i.next(); + final int index = values.getChildren().indexOf(value); + values.getChildren().move(Math.min(index + 1, maxIndex++), value); + } + } + }); + } + + private void setAddButtonListener(final Button addButton, final TableViewer choiceTableViewer, final TableViewer featureTableViewer, final Text choiceText) { + addButton.addSelectionListener(new SelectionAdapter() { + // event is null when choiceTableViewer is double clicked + @Override + public void widgetSelected(final SelectionEvent event) { + if (choiceTableViewer != null) { + final IStructuredSelection selection = (IStructuredSelection) choiceTableViewer.getSelection(); + final Iterator<?> i = selection.iterator(); + while (i.hasNext()) { + final Object value = i.next(); + if (!values.getChildren().contains(value)) { + values.getChildren().add(value); + choiceTableViewer.remove(value); + } + } + featureTableViewer.setSelection(selection); + } else if (choiceText != null) { + + // Object value = EcoreUtil.createFromString((EDataType) + // eClassifier, choiceText.getText()); + // values.getChildren().add(value); + // choiceText.setText(StringUtil.EMPTY_STRING); + // featureTableViewer.setSelection(new + // StructuredSelection(value)); + + // Ignore + + } + } + }); + } + + private void setRemoveButtonListener(final Button removeButton, final TableViewer choiceTableViewer, final TableViewer featureTableViewer, final Text choiceText) { + removeButton.addSelectionListener(new SelectionAdapter() { + // event is null when featureTableViewer is double clicked + @Override + public void widgetSelected(final SelectionEvent event) { + final IStructuredSelection selection = (IStructuredSelection) featureTableViewer.getSelection(); + Object firstValue = null; + final Iterator<?> i = selection.iterator(); + while (i.hasNext()) { + final Object value = i.next(); + if (firstValue == null) { + firstValue = value; + } + values.getChildren().remove(value); + choiceTableViewer.add(value); + } + + if (!values.getChildren().isEmpty()) { + featureTableViewer.setSelection(new StructuredSelection(values.getChildren().get(0))); + } + + if (choiceTableViewer != null) { + choiceTableViewer.setSelection(selection); + } else if (choiceText != null) { + if (firstValue != null) { + // String value = EcoreUtil.convertToString((EDataType) + // eClassifier, firstValue); + // choiceText.setText(value); + } + } + } + }); + } + + private void setTableViewerListener(final TableViewer choiceTableViewer, final TableViewer featureTableViewer, final Button addButton, final Button removeButton) { + if (choiceTableViewer != null) { + choiceTableViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(final DoubleClickEvent event) { + if (addButton.isEnabled()) { + addButton.notifyListeners(SWT.Selection, null); + } + } + }); + + featureTableViewer.addDoubleClickListener(new IDoubleClickListener() { + public void doubleClick(final DoubleClickEvent event) { + if (removeButton.isEnabled()) { + removeButton.notifyListeners(SWT.Selection, null); + } + } + }); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.dialogs.Dialog#okPressed() + */ + @Override + protected void okPressed() { + result = new BasicEList(values.getChildren()); + super.okPressed(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.dialogs.Dialog#close() + */ + @Override + public boolean close() { + contentProvider.dispose(); + return super.close(); + } + + /** + * Returns the selected references. + * + * @return the selected references. + */ + public EList<EObject> getResult() { + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/SelectDiagramElementBackgroundImageDialog.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/SelectDiagramElementBackgroundImageDialog.java new file mode 100644 index 0000000000..5df774906f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/SelectDiagramElementBackgroundImageDialog.java @@ -0,0 +1,145 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.dialogs; + +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.UpdateValueStrategy; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.emf.databinding.EMFProperties; +import org.eclipse.jface.databinding.swt.ISWTObservableValue; +import org.eclipse.jface.databinding.swt.WidgetProperties; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.fieldassist.ControlDecoration; +import org.eclipse.jface.fieldassist.FieldDecoration; +import org.eclipse.jface.fieldassist.FieldDecorationRegistry; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.swt.widgets.Text; + +import org.eclipse.sirius.BasicLabelStyle; +import org.eclipse.sirius.SiriusFactory; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.WorkspaceImage; + +/** + * Dialog to select an image from workspace. + * + * @author fmorel + */ +public class SelectDiagramElementBackgroundImageDialog extends Dialog { + + private static final String DIALOG_TITLE = "Select background image from workspace"; + + private WorkspaceImage workspaceImageForWorkspace; + + private Label workspacePathLabel; + + private Text workspacePathText; + + private Button browseButton; + + /** + * Creates an instance of the copy to image dialog. + * + * @param shell + * the parent shell + * @param basicLabelStyle + * {@link BasicLabelStyle} + */ + public SelectDiagramElementBackgroundImageDialog(Shell shell, BasicLabelStyle basicLabelStyle) { + super(shell); + this.workspaceImageForWorkspace = SiriusFactory.eINSTANCE.createWorkspaceImage(); + if (basicLabelStyle instanceof WorkspaceImage) { + WorkspaceImage workspaceImage = (WorkspaceImage) basicLabelStyle; + workspaceImageForWorkspace.setWorkspacePath(workspaceImage.getWorkspacePath()); + } + } + + /** + * Creates and returns the contents of the upper part of this dialog (above + * the button bar). {@inheritDoc} + * + * @see org.eclipse.jface.dialogs.Dialog#createDialogArea(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Control createDialogArea(final Composite parent) { + Composite mainComposite = (Composite) super.createDialogArea(parent); + + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 1; + gridLayout.marginHeight = 5; + gridLayout.marginWidth = 5; + mainComposite.setLayout(gridLayout); + mainComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + + createFromWorkspaceComposite(mainComposite); + return mainComposite; + } + + private void createFromWorkspaceComposite(Composite mainComposite) { + Composite fromWorkspaceComposite = new Composite(mainComposite, SWT.NONE); + + GridLayout gridLayout = new GridLayout(); + gridLayout.numColumns = 3; + gridLayout.marginHeight = 0; + gridLayout.marginWidth = 0; + fromWorkspaceComposite.setLayout(gridLayout); + fromWorkspaceComposite.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true)); + + workspacePathLabel = new Label(fromWorkspaceComposite, SWT.NONE); + workspacePathLabel.setText("Path:"); + + workspacePathText = new Text(fromWorkspaceComposite, SWT.FLAT | SWT.BORDER); + workspacePathText.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); + ControlDecoration controlDecoration = new ControlDecoration(workspacePathText, SWT.LEFT | SWT.TOP); + FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR); + controlDecoration.setImage(fieldDecoration.getImage()); + + ISWTObservableValue workspacePathTextObservable = WidgetProperties.text(SWT.Modify).observe(workspacePathText); + IObservableValue workspaceImageObservable = EMFProperties.value(SiriusPackage.Literals.WORKSPACE_IMAGE__WORKSPACE_PATH).observe(workspaceImageForWorkspace); + DataBindingContext ctx = new DataBindingContext(); + WorkspacePathValidator workspacePathValidator = new WorkspacePathValidator(controlDecoration); + ctx.bindValue(workspacePathTextObservable, workspaceImageObservable, new UpdateValueStrategy().setAfterConvertValidator(workspacePathValidator), null); + + browseButton = new Button(fromWorkspaceComposite, SWT.NONE); + browseButton.setText("Browse"); + browseButton.addSelectionListener(new WorkspaceImagePathSelector(workspacePathText)); + } + + /** + * Configures the shell in preparation for opening this window in it. + * {@inheritDoc} + * + * @see org.eclipse.jface.window.Window#configureShell(org.eclipse.swt.widgets.Shell) + */ + @Override + protected void configureShell(final Shell shell) { + super.configureShell(shell); + shell.setText(DIALOG_TITLE); + shell.setMinimumSize(500, 100); + setShellStyle(getShellStyle() | SWT.RESIZE | SWT.MAX); + } + + /** + * return the image path. + * + * @return The image path + */ + public String getImagePath() { + return workspaceImageForWorkspace.getWorkspacePath(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/WorkspaceImagePathSelector.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/WorkspaceImagePathSelector.java new file mode 100644 index 0000000000..8f2b588e8a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/WorkspaceImagePathSelector.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.dialogs; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.resources.IContainer; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.gmf.runtime.diagram.ui.image.ImageFileFormat; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.PlatformUI; + +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.ui.tools.api.resource.WorkspaceResourceDialogWithFilter; + +/** + * A {@link SelectionAdapter} to select a image path in the workspace. + * + * @author <a href="mailto:esteban.dugueperoux@obeo.fr">Esteban Dugueperoux</a> + */ +public class WorkspaceImagePathSelector extends SelectionAdapter { + + /** All supported image file extensions. */ + private static final List<String> IMAGE_FILE_EXTENSIONS = new ArrayList<String>(); + + static { + for (ImageFileFormat imageFileFormat : ImageFileFormat.VALUES) { + IMAGE_FILE_EXTENSIONS.add(imageFileFormat.getName().toLowerCase()); + } + } + + private Text workspacePathText; + + /** + * Default constructor. + * + * @param workspacePathText + * the {@link Text} field displaying the selected path + */ + public WorkspaceImagePathSelector(Text workspacePathText) { + this.workspacePathText = workspacePathText; + } + + /** + * {@inheritDoc} + */ + public void widgetSelected(SelectionEvent e) { + List<ViewerFilter> filters = Lists.newArrayList(); + if (IMAGE_FILE_EXTENSIONS != null) { + filters.add(new FileExtensionFilter(IMAGE_FILE_EXTENSIONS)); + } + IFile[] selectedResources = WorkspaceResourceDialogWithFilter.openFileSelection(PlatformUI.getWorkbench().getDisplay().getActiveShell(), "Background image", "Select the image file:", false, + null, filters); + if (selectedResources != null && selectedResources.length == 1) { + workspacePathText.setText(((IFile) selectedResources[0]).getFullPath().makeRelative().toString()); + } + } + + /** + * A filter based on file extension. + * + * @author lredor + */ + private static class FileExtensionFilter extends ViewerFilter { + /** + * The list of extensions for which we want to keep file. + */ + private List<String> extensions; + + public FileExtensionFilter(List<String> extensions) { + this.extensions = extensions; + } + + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + boolean isValid = true; + if (element instanceof IFile) { + // In the case of a file, we'll check if its extension is one of + // the expected + IFile file = (IFile) element; + isValid = extensions.contains(file.getFileExtension().toLowerCase()); + } else if (element instanceof IContainer) { + // In the case of a container, we'll probe its content to see if + // it contains a valid file + isValid = false; + IContainer container = (IContainer) element; + if (!container.isDerived()) { + try { + final IResource[] members = ((IContainer) container).members(); + for (IResource member : members) { + isValid = select(viewer, parentElement, member); + if (isValid) { + // we found a valid resource in this folder, it + // is useless to probe any further + break; + } + } + } catch (final CoreException e) { + // This shouldn't happen + } + } + } + return isValid; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/WorkspacePathValidator.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/WorkspacePathValidator.java new file mode 100644 index 0000000000..cb847a408c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dialogs/WorkspacePathValidator.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.dialogs; + +import java.io.File; + +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.databinding.validation.ValidationStatus; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Path; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.fieldassist.ControlDecoration; + +import org.eclipse.sirius.common.tools.api.resource.FileProvider; + +/** + * A {@link IValidator} to validate the workspace path entered in the text + * field. + * + * @author <a href="mailto:esteban.dugueperoux@obeo.fr">Esteban Dugueperoux</a> + */ +public class WorkspacePathValidator implements IValidator { + + private ControlDecoration controlDecoration; + + /** + * Default constructor. + * + * @param controlDecoration + * the {@link ControlDecoration} + */ + public WorkspacePathValidator(ControlDecoration controlDecoration) { + this.controlDecoration = controlDecoration; + } + + /** + * {@inheritDoc} + */ + public IStatus validate(Object value) { + IStatus status = Status.OK_STATUS; + if (value instanceof String && controlDecoration.getControl().isEnabled()) { + String text = (String) value; + if (text.trim().length() > 0) { + IPath path = new Path(text); + File file = FileProvider.getDefault().getFile(path); + if (file == null || !file.exists()) { + controlDecoration.show(); + controlDecoration.setDescriptionText("The specified path does not correspond to an image in the workspace or in the runtime"); + status = ValidationStatus.error("NonExistingImageResource"); + } + } + } + if (status.isOK()) { + controlDecoration.hide(); + } + return status; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dnd/DragAndDropWrapper.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dnd/DragAndDropWrapper.java new file mode 100644 index 0000000000..c7544d9905 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/dnd/DragAndDropWrapper.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2008, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.dnd; + +import org.eclipse.draw2d.Figure; +import org.eclipse.draw2d.IFigure; +import org.eclipse.gef.editparts.AbstractGraphicalEditPart; + +/** + * This class is a wrapper of an Object useful to manage drag and drop. + * + * @author Mariot Chauvin (mchauvin) + */ +public class DragAndDropWrapper extends AbstractGraphicalEditPart { + + private static final IFigure FIGURE = new Figure(); + + private Object wrappedObject; + + /** + * Constructor. + * + * @param eObj + * the wrapped object + */ + public DragAndDropWrapper(final Object eObj) { + wrappedObject = eObj; + } + + /** + * Get the wrapped Object. + * + * @return the wrapped object + */ + public Object getObject() { + return this.wrappedObject; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#createFigure() + */ + @Override + protected IFigure createFigure() { + return FIGURE; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.editparts.AbstractEditPart#createEditPolicies() + */ + @Override + protected void createEditPolicies() { + // do nothing + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/edit/command/CommandFactory.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/edit/command/CommandFactory.java new file mode 100644 index 0000000000..ec77797bb7 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/edit/command/CommandFactory.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.edit.command; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gmf.runtime.common.core.command.CommandResult; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.emf.commands.core.command.AbstractTransactionalCommand; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; + +/** + * Helper class to convert an {@link AbstractModelChangeOperation}s into either + * a plain EMF Transaction {@link RecordingCommand} or a GMF-compatible + * {@link ICommand}. + * + * @author pcdavid + */ +public final class CommandFactory { + private CommandFactory() { + // Prevent instantiation. + } + + /** + * Converts these operations into an EMF Transaction recording command. + * + * @param ted + * the editing domain in which to command will execute. + * @param operations + * the array of operation to convert. + * + * @param <T> + * the return type of the operation. + * @return a recording command which will execute this operation. + */ + public static <T> RecordingCommand createRecordingCommand(TransactionalEditingDomain ted, final Collection<AbstractModelChangeOperation<T>> operations) { + String name = operations.isEmpty() ? "Do nothing" : operations.iterator().next().getName(); + return new RecordingCommandsExecutor<T>(ted, name, operations); + } + + /** + * Converts this operation into an EMF Transaction recording command. + * + * @param ted + * the editing domain in which to command will execute. + * @param operation + * the operation to convert. + * + * @param <T> + * the return type of the operation. + * @return a recording command which will execute this operation. + */ + public static <T> RecordingCommand createRecordingCommand(TransactionalEditingDomain ted, final AbstractModelChangeOperation<T> operation) { + return CommandFactory.createRecordingCommand(ted, Collections.singleton(operation)); + } + + /** + * Converts these operations into an EMF Transaction recording command. + * + * @param ted + * the editing domain in which to command will execute. + * @param operations + * the array of operation to convert. + * + * @param <T> + * the return type of the operation. + * @return a recording command which will execute this operation. + */ + public static <T> RecordingCommand createRecordingCommand(TransactionalEditingDomain ted, final AbstractModelChangeOperation<T>... operations) { + return CommandFactory.createRecordingCommand(ted, Arrays.asList(operations)); + } + + /** + * Converts these operations into a GMF-compatible ICommand. + * + * @param ted + * the editing domain in which to command will execute. + * @param operations + * the operations to convert. + * + * @param <T> + * the return types of the operations. + * @return a GMF-compatible command which will execute this operation. + */ + public static <T> ICommand createICommand(TransactionalEditingDomain ted, final Collection<AbstractModelChangeOperation<T>> operations) { + String name = operations.isEmpty() ? "Do nothing" : operations.iterator().next().getName(); + return new AbstractTransactionalCommand(ted, name, null) { + @Override + protected CommandResult doExecuteWithResult(IProgressMonitor monitor, IAdaptable info) throws ExecutionException { + List<T> results = new ArrayList<T>(); + for (AbstractModelChangeOperation<T> operation : operations) { + T value = operation.execute(); + if (value != null) { + results.add(value); + } + } + return CommandResult.newOKCommandResult(results.size() == 1 ? results.get(0) : results); + } + }; + } + + /** + * Converts this operation into a GMF-compatible ICommand. + * + * @param ted + * the editing domain in which to command will execute. + * @param operation + * the operation to convert. + * + * @param <T> + * the return type of the operation. + * @return a GMF-compatible command which will execute this operation. + */ + public static <T> ICommand createICommand(TransactionalEditingDomain ted, final AbstractModelChangeOperation<T> operation) { + return CommandFactory.createICommand(ted, Collections.singleton(operation)); + } + + /** + * Converts these operations into a GMF-compatible ICommand. + * + * @param ted + * the editing domain in which to command will execute. + * @param operations + * the operations to convert. + * + * @param <T> + * the return types of the operations. + * @return a GMF-compatible command which will execute this operation. + */ + public static <T> ICommand createICommand(TransactionalEditingDomain ted, final AbstractModelChangeOperation<T>... operations) { + return CommandFactory.createICommand(ted, Arrays.asList(operations)); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/edit/command/RecordingCommandsExecutor.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/edit/command/RecordingCommandsExecutor.java new file mode 100644 index 0000000000..0bc5d0247d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/edit/command/RecordingCommandsExecutor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.edit.command; + +import java.util.Collection; + +import org.eclipse.emf.transaction.RecordingCommand; +import org.eclipse.emf.transaction.TransactionalEditingDomain; + +import org.eclipse.sirius.diagram.business.internal.operation.AbstractModelChangeOperation; + +/** + * A Recording Command used in CommandFactory to execute a collection of + * AbstractModelChangeOperation<T>. + * + * @author smonnier + */ +public class RecordingCommandsExecutor<T> extends RecordingCommand { + + private Collection<AbstractModelChangeOperation<T>> operations; + + /** + * Constructor. + * + * @param domain + * my domain + * @param label + * my user-friendly label + * @param operations + * the array of operation to convert. + */ + public RecordingCommandsExecutor(TransactionalEditingDomain domain, String label, Collection<AbstractModelChangeOperation<T>> operations) { + super(domain, label); + this.operations = operations; + } + + /** + * {@inheritDoc} + */ + @Override + protected void doExecute() { + for (AbstractModelChangeOperation<T> operation : operations) { + operation.execute(); + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/DiagramSemanticElementLockedNotificationFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/DiagramSemanticElementLockedNotificationFigure.java new file mode 100644 index 0000000000..2fd9237a25 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/DiagramSemanticElementLockedNotificationFigure.java @@ -0,0 +1,313 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.figure; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.List; + +import org.eclipse.draw2d.Ellipse; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.Label; +import org.eclipse.draw2d.LayeredPane; +import org.eclipse.draw2d.Viewport; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.LayerConstants; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramColorRegistry; +import org.eclipse.gmf.runtime.draw2d.ui.internal.figures.TransparentBorder; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.RGB; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.provider.SiriusEditPlugin; +import org.eclipse.sirius.ecore.extender.business.api.permission.LockStatus; + +/** + * A figure to display a lock notification for the semantic element represented + * by a diagram. + * + * @author <a href="mailto:steve.monnier@obeo.fr">Steve Monnier</a> + */ +public class DiagramSemanticElementLockedNotificationFigure extends Ellipse { + + /** + * Border color of the notification figure when locked by me. + */ + public static final RGB BORDER_COLOR_LOCKED_BY_ME = new RGB(50, 205, 50); + + /** + * Border color of the notification figure when locked by other. + */ + public static final RGB BORDER_COLOR_LOCKED_BY_OTHER = new RGB(255, 0, 0); + + private static final int DEFAULT_WIDTH = 25; + + private static final int DEFAULT_HEIGHT = 25; + + /** The PERMISSION_GRANTED_TO_CURRENT_USER_EXCLUSIVELY icon descriptor. */ + private static final ImageDescriptor LOCK_BY_ME_IMAGE_DESCRIPTOR = SiriusEditPlugin.Implementation + .getBundledImageDescriptor("icons/full/decorator/permission_granted_to_current_user_exclusively.gif"); + + /** The PERMISSION_GRANTED_TO_CURRENT_USER_EXCLUSIVELY icon descriptor. */ + private static final ImageDescriptor LOCK_BY_OTHER_IMAGE_DESCRIPTOR = SiriusEditPlugin.Implementation.getBundledImageDescriptor("icons/full/decorator/permission_denied.gif"); + + /** + * The transparency of this shape in percent. Must be in [0, 100] range. + */ + private int transparency = 20; + + private PropertyChangeListener propListener; + + private Viewport viewport; + + private DiagramRootEditPart rootEditPart; + + /** + * Create a new instance. + * + * @param rootEditPart + * the editor root edit part + * @param message + * the message to display in the notification + * @param lockStatus + * the {@link LockStatus} to display in the notification + * @param height + * notification figure height + * @param width + * notification figure width + */ + public DiagramSemanticElementLockedNotificationFigure(DiagramRootEditPart rootEditPart, String message, LockStatus lockStatus, int height, int width) { + Image lockStatusImage; + Label label; + + this.rootEditPart = rootEditPart; + this.viewport = (Viewport) rootEditPart.getFigure(); + this.setSize(width, height); + this.setLineWidth(3); + updateLocation(); + + if (LockStatus.LOCKED_BY_ME.equals(lockStatus)) { + this.setForegroundColor(DiagramColorRegistry.getInstance().getColor(BORDER_COLOR_LOCKED_BY_ME)); + lockStatusImage = SiriusDiagramEditorPlugin.getInstance().getImage(LOCK_BY_ME_IMAGE_DESCRIPTOR); + label = new Label(message, lockStatusImage); + } else if (LockStatus.LOCKED_BY_OTHER.equals(lockStatus)) { + this.setForegroundColor(DiagramColorRegistry.getInstance().getColor(BORDER_COLOR_LOCKED_BY_OTHER)); + lockStatusImage = SiriusDiagramEditorPlugin.getInstance().getImage(LOCK_BY_OTHER_IMAGE_DESCRIPTOR); + label = new Label(message, lockStatusImage); + } else { + label = new Label(message); + } + label.setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT); + this.add(label); + + propListener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + updateLocation(); + } + }; + + } + + /** + * Create a new instance. + * + * @param rootEditPart + * the editor root edit part + * @param message + * the message to display in the notification + * @param lockStatus + * the {@link LockStatus} to display in the notification + */ + public DiagramSemanticElementLockedNotificationFigure(DiagramRootEditPart rootEditPart, String message, LockStatus lockStatus) { + this(rootEditPart, message, lockStatus, DEFAULT_HEIGHT, DEFAULT_WIDTH); + } + + private void updateLocation() { + Point viewLocation = viewport.getViewLocation().getCopy(); + + viewLocation.performScale(1.0d / rootEditPart.getZoomManager().getZoom()); + + this.setLocation(new Point(viewLocation.x, viewLocation.y)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + super.addNotify(); + viewport.addPropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + viewport.removePropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener); + viewport = null; + } + + /** + * Override to use local coordinates. .{@inheritDoc} + */ + protected boolean useLocalCoordinates() { + return true; + } + + /** + * Create a new notification figure and display it to the diagram. + * + * @param rootEditPart + * the diagram root edit part + * @param message + * the message + * @param lockStatus + * the {@link LockStatus} to display in the notification + */ + public static void createNotification(DiagramRootEditPart rootEditPart, String message, LockStatus lockStatus) { + final LayeredPane pane = (LayeredPane) rootEditPart.getLayer(LayerConstants.PRINTABLE_LAYERS); + + final IFigure notificationFigure = new DiagramSemanticElementLockedNotificationFigure(rootEditPart, message, lockStatus); + removeNotification(rootEditPart); + pane.add(notificationFigure); + } + + /** + * Create a new notification figure and display it to the diagram. + * + * @param rootEditPart + * the diagram root edit part + * @param message + * the message + * @param lockStatus + * the {@link LockStatus} to display in the notification + * @param height + * notification figure height + * @param width + * notification figure width + */ + public static void createNotification(DiagramRootEditPart rootEditPart, String message, LockStatus lockStatus, int height, int width) { + final LayeredPane pane = (LayeredPane) rootEditPart.getLayer(LayerConstants.PRINTABLE_LAYERS); + + final IFigure notificationFigure = new DiagramSemanticElementLockedNotificationFigure(rootEditPart, message, lockStatus, height, width); + removeNotification(rootEditPart); + pane.add(notificationFigure); + } + + /** + * Create a new notification figure and display it to the diagram. + * + * @param rootEditPart + * the diagram root edit part + * @param lockStatus + * the {@link LockStatus} to display in the notification + */ + public static void createNotification(DiagramRootEditPart rootEditPart, LockStatus lockStatus) { + final LayeredPane pane = (LayeredPane) rootEditPart.getLayer(LayerConstants.PRINTABLE_LAYERS); + + final IFigure notificationFigure = new DiagramSemanticElementLockedNotificationFigure(rootEditPart, "", lockStatus); + removeNotification(rootEditPart); + pane.add(notificationFigure); + } + + /** + * Removes the notification figure from the diagram. + * + * @param rootEditPart + * the diagram root edit part + */ + public static void removeNotification(DiagramRootEditPart rootEditPart) { + final LayeredPane pane = (LayeredPane) rootEditPart.getLayer(LayerConstants.PRINTABLE_LAYERS); + List<IFigure> figuresToRemove = Lists.newArrayList(); + // Collects notification figures that needs to be removed + for (DiagramSemanticElementLockedNotificationFigure diagramSemanticElementLockedNotificationFigure : Iterables.filter(pane.getChildren(), DiagramSemanticElementLockedNotificationFigure.class)) { + figuresToRemove.add(diagramSemanticElementLockedNotificationFigure); + } + // Removes these notation figures from Layer + for (IFigure iFigure : figuresToRemove) { + pane.remove(iFigure); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Shape#paintFigure(org.eclipse.draw2d.Graphics) + */ + public void paintFigure(Graphics g) { + applyTransparency(g); + super.paintFigure(g); + g.setAlpha(255); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.handles.HandleBounds#getHandleBounds() + */ + public Rectangle getHandleBounds() { + Insets insets = new Insets(0, 0, 0, 0); + if (getBorder() instanceof TransparentBorder) { + insets = ((TransparentBorder) getBorder()).getTransparentInsets(this); + } + + // Ignore the insets when placing the handles + return new Rectangle(getBounds().x + insets.left, getBounds().y + insets.top, getBounds().width - (insets.right + insets.left), getBounds().height - (insets.bottom + insets.top)); + } + + /** + * Returns transparency value (belongs to [0, 100] interval). + * + * @return transparency + * @since 1.2 + */ + public int getTransparency() { + return transparency; + } + + /** + * Sets the transparency if the given parameter is in [0, 100] range. + * + * @param transparency + * The transparency to set + * @since 1.2 + */ + public void setTransparency(int transparency) { + if (transparency != this.transparency && transparency >= 0 && transparency <= 100) { + this.transparency = transparency; + repaint(); + } + } + + /** + * Converts transparency value from percent range [0, 100] to alpha range + * [0, 255] and applies converted value. 0% corresponds to alpha 255 and + * 100% corresponds to alpha 0. + * + * @param g + * The Graphics used to paint + * @since 1.2 + */ + protected void applyTransparency(Graphics g) { + g.setAlpha(255 - transparency * 255 / 100); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/ICollapseMode.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/ICollapseMode.java new file mode 100644 index 0000000000..3be901bc01 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/ICollapseMode.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.figure; + +import org.eclipse.draw2d.geometry.Dimension; + +/** + * Constants for collapsed nodes. + * + * @author mporhel + */ +public interface ICollapseMode { + + /** + * Indicates the current collapse mode. + * + * - true : border nodes are collapsed (height, weight = 0, no available + * selection) + * + * - false : border nodes are minimized and transparents (selection and move + * are enabled) + * + * This flag is only there to ease the switch between the two behavior, as + * the client was not sure which mode was best. + */ + boolean DEFAULT = false; + + /** + * Offset for default collapsed nodes (in default mode). + */ + Dimension COLLAPSE_DEFAULT_OFFSET = new Dimension(0, 0); + + /** + * Offset for collapsed nodes (in minimized mode). + */ + Dimension COLLAPSE_MINIMIZED_OFFSET = new Dimension(2, 2); + + /** + * dimension for collapsed nodes (in default mode). + */ + Dimension COLLAPSED_DIMENSION = new Dimension(0, 0); + + /** + * dimension for collapsed nodes (in minimized mode). + */ + // we decrease the minimized_dimension of collapsed nodes in minimized mode + Dimension MINIMIZED_DIMENSION = new Dimension(1, 1); +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/NotificationFigure.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/NotificationFigure.java new file mode 100644 index 0000000000..c9c17ac541 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/NotificationFigure.java @@ -0,0 +1,267 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.figure; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import org.eclipse.draw2d.ColorConstants; +import org.eclipse.draw2d.Graphics; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.Label; +import org.eclipse.draw2d.LayeredPane; +import org.eclipse.draw2d.MouseEvent; +import org.eclipse.draw2d.MouseListener; +import org.eclipse.draw2d.RectangleFigure; +import org.eclipse.draw2d.Shape; +import org.eclipse.draw2d.Viewport; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.LayerConstants; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramColorRegistry; +import org.eclipse.gmf.runtime.draw2d.ui.internal.figures.TransparentBorder; + +/** + * A figure to display a notification. + * + * @author mchauvin + * @provisional + */ +public class NotificationFigure extends RectangleFigure { + + private static final int WARNING_COLOR_VALUE = 13369343; + + private static final int WIDTH = 400; + + private static final int HEIGHT = 30; + + private static final int TEXT_OFFSET = 5; + + /** + * The transparency of this shape in percent. Must be in [0, 100] range. + */ + private int transparency = 20; + + private PropertyChangeListener propListener; + + private Viewport viewport; + + private DiagramRootEditPart rootEditPart; + + /** + * Create a new instance. + * + * @param rootEditPart + * the editor root edit part + * @param message + * the message to display in the notification + */ + public NotificationFigure(DiagramRootEditPart rootEditPart, String message) { + + this.rootEditPart = rootEditPart; + this.viewport = (Viewport) rootEditPart.getFigure(); + + this.setSize(WIDTH, HEIGHT); + this.setBackgroundColor(DiagramColorRegistry.getInstance().getColor(Integer.valueOf(WARNING_COLOR_VALUE))); + + updateLocation(); + + Cross cross = new Cross(); + int crossSize = cross.getSize().width; + cross.setLocation(new Point(WIDTH - crossSize - 3, 3)); + this.add(cross); + cross.initialize(); + + Label label = new Label(message); + label.setSize(WIDTH - TEXT_OFFSET - crossSize, HEIGHT - TEXT_OFFSET); + this.add(label); + + propListener = new PropertyChangeListener() { + public void propertyChange(PropertyChangeEvent evt) { + updateLocation(); + } + }; + } + + private void updateLocation() { + Point viewLocation = viewport.getViewLocation().getCopy(); + Dimension viewSize = viewport.getSize().getCopy(); + /* we divide by 2 to center the notification figure */ + + // Workaround, as the size of the figure follows zoom, we apply twice + // the zoom on figure offset to balance + double notationFigureOffset = ((double) (viewSize.width - WIDTH) / 2d) / (rootEditPart.getZoomManager().getZoom() * rootEditPart.getZoomManager().getZoom()); + + viewLocation.performScale(1.0d / rootEditPart.getZoomManager().getZoom()); + + this.setLocation(new Point(viewLocation.x + notationFigureOffset, viewLocation.y)); + } + + /** + * {@inheritDoc} + */ + @Override + public void addNotify() { + super.addNotify(); + viewport.addPropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener); + } + + /** + * {@inheritDoc} + */ + @Override + public void removeNotify() { + super.removeNotify(); + viewport.removePropertyChangeListener(Viewport.PROPERTY_VIEW_LOCATION, propListener); + viewport = null; + } + + /** + * Override to use local coordinates. .{@inheritDoc} + */ + protected boolean useLocalCoordinates() { + return true; + } + + /** + * Create a new notification figure and display it to the diagram. + * + * @param rootEditPart + * the diagram root edit part + * @param message + * the message + */ + public static void createNotification(DiagramRootEditPart rootEditPart, String message) { + final LayeredPane pane = (LayeredPane) rootEditPart.getLayer(LayerConstants.PRINTABLE_LAYERS); + + final IFigure nf = new NotificationFigure(rootEditPart, message); + pane.add(nf); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.Shape#paintFigure(org.eclipse.draw2d.Graphics) + */ + public void paintFigure(Graphics g) { + applyTransparency(g); + super.paintFigure(g); + g.setAlpha(255); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gef.handles.HandleBounds#getHandleBounds() + */ + public Rectangle getHandleBounds() { + Insets insets = new Insets(0, 0, 0, 0); + if (getBorder() instanceof TransparentBorder) { + insets = ((TransparentBorder) getBorder()).getTransparentInsets(this); + } + + // Ignore the insets when placing the handles + return new Rectangle(getBounds().x + insets.left, getBounds().y + insets.top, getBounds().width - (insets.right + insets.left), getBounds().height - (insets.bottom + insets.top)); + } + + /** + * Returns transparency value (belongs to [0, 100] interval). + * + * @return transparency + * @since 1.2 + */ + public int getTransparency() { + return transparency; + } + + /** + * Sets the transparency if the given parameter is in [0, 100] range. + * + * @param transparency + * The transparency to set + * @since 1.2 + */ + public void setTransparency(int transparency) { + if (transparency != this.transparency && transparency >= 0 && transparency <= 100) { + this.transparency = transparency; + repaint(); + } + } + + /** + * Converts transparency value from percent range [0, 100] to alpha range + * [0, 255] and applies converted value. 0% corresponds to alpha 255 and + * 100% corresponds to alpha 0. + * + * @param g + * The Graphics used to paint + * @since 1.2 + */ + protected void applyTransparency(Graphics g) { + g.setAlpha(255 - transparency * 255 / 100); + } + + /** + * A cross figure to close the notation figure. + * + * @author mchauvin + */ + private static class Cross extends Shape { + + public Cross() { + this.setSize(10, 10); + } + + /** + * Override to use local coordinates. .{@inheritDoc} + */ + protected boolean useLocalCoordinates() { + return true; + } + + @Override + protected void fillShape(Graphics graphics) { + graphics.pushState(); + int x = getBounds().x; + int y = getBounds().y; + int[] shape = new int[] { x, y, x + 2, y, x + 4, y + 2, x + 5, y + 2, x + 7, y, x + 9, y, x + 9, y + 2, x + 7, y + 4, x + 7, y + 5, x + 9, y + 7, x + 9, y + 9, x + 7, y + 9, x + 5, y + 7, + x + 4, y + 7, x + 2, y + 9, x, y + 9, x, y + 7, x + 2, y + 5, x + 2, y + 4, x, y + 2, }; + graphics.setBackgroundColor(ColorConstants.red); + graphics.fillPolygon(shape); + graphics.setForegroundColor(ColorConstants.black); + graphics.drawPolygon(shape); + graphics.popState(); + } + + @Override + protected void outlineShape(Graphics arg0) { + + } + + /** + * Initialize this figure. It should be done <b>after</b> adding this + * figure to its notation parent figure. + */ + public void initialize() { + this.addMouseListener(new MouseListener.Stub() { + @Override + public void mousePressed(MouseEvent me) { + getParent().getParent().remove(getParent()); + } + }); + } + + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/TransparentFigureGraphicsModifier.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/TransparentFigureGraphicsModifier.java new file mode 100644 index 0000000000..67f80afd26 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/TransparentFigureGraphicsModifier.java @@ -0,0 +1,61 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.figure; + +import org.eclipse.draw2d.Graphics; + +import org.eclipse.sirius.diagram.ui.tools.api.figure.ITransparentFigure; + +/** + * Specific handler to configure {@link Graphics} regarding the current + * {@link ITransparentFigure}. + * + * @author mporhel + */ +public class TransparentFigureGraphicsModifier { + + private ITransparentFigure figure; + + private Graphics graphics; + + /** + * Constructor. + * + * @param figure + * the current figure. + * @param graphics + * the current graphics + */ + public TransparentFigureGraphicsModifier(ITransparentFigure figure, Graphics graphics) { + this.figure = figure; + this.graphics = graphics; + } + + /** + * Pushes the current state of the current graphics onto a stack. Enable + * transparency if figure's transparent mode is enabled. + */ + public void pushState() { + if (graphics != null && figure != null && figure.isTransparent()) { + graphics.pushState(); + graphics.setAlpha(figure.getSiriusAlpha()); + } + } + + /** + * Pops the previous state of the graphics off the stack. + */ + public void popState() { + if (graphics != null && figure != null && figure.isTransparent()) { + graphics.popState(); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/locator/FeedbackDBorderItemLocator.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/locator/FeedbackDBorderItemLocator.java new file mode 100644 index 0000000000..bd734b9137 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/figure/locator/FeedbackDBorderItemLocator.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2013 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.figure.locator; + +import java.util.Collection; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.figures.BorderedNodeFigure; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.diagram.ui.tools.api.figure.locator.DBorderItemLocator; + +/** + * A specific bordered item locator for feedback when moving a border node. + * + * @author fbarbin + */ +public class FeedbackDBorderItemLocator extends DBorderItemLocator { + + /** + * Default constructor. + * + * @param parentFigure + * the feedback target figure. + */ + public FeedbackDBorderItemLocator(IFigure parentFigure) { + super(parentFigure); + } + + @Override + protected Option<Rectangle> conflicts(Point recommendedLocation, IFigure targetBorderItem, Collection<IFigure> portsFiguresToIgnore) { + final Rectangle recommendedRect = new Rectangle(recommendedLocation, getSize(targetBorderItem)); + IFigure parentFigure = getParentFigure(); + if (parentFigure instanceof BorderedNodeFigure) { + parentFigure = ((BorderedNodeFigure) parentFigure).getBorderItemContainer(); + } + final List borderItems = parentFigure.getChildren(); + final ListIterator iterator = borderItems.listIterator(); + while (iterator.hasNext()) { + final IFigure borderItem = (IFigure) iterator.next(); + if (!portsFiguresToIgnore.contains(borderItem)) { + if (borderItem.isVisible()) { + final Rectangle rect = new Rectangle(borderItem.getBounds()); + if (!(portsFiguresToIgnore.contains(borderItem)) && borderItem != targetBorderItem && rect.intersects(recommendedRect)) { + return Options.newSome(rect); + } + } + } + } + return Options.newNone(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/find/BasicFindLabelEngine.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/find/BasicFindLabelEngine.java new file mode 100644 index 0000000000..e05c49da2a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/find/BasicFindLabelEngine.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.find; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.editparts.AbstractGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ITextAwareEditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.DiagramEditor; + +import com.google.common.base.Predicate; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.common.tools.api.find.AbstractFindLabelEngine; +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.diagram.ui.tools.api.figure.AirStyleDefaultSizeNodeFigure; +import org.eclipse.sirius.diagram.ui.tools.api.figure.FigureQuery; + +/** + * A basic implementation of the AbstractFindLabelEngine. This engine simple + * search for labels starting with the search criterion + * + * @author glefur + */ +public class BasicFindLabelEngine extends AbstractFindLabelEngine { + /** + * A predicate to identify edit parts which are label and which can thus be + * matched with text. + */ + private static final Predicate<EditPart> IS_LABEL_EDIT_PART = new Predicate<EditPart>() { + public boolean apply(final EditPart part) { + final boolean result; + if (part instanceof AbstractGraphicalEditPart && part instanceof ITextAwareEditPart) { + result = true; + } else if (part instanceof IGraphicalEditPart && ((IGraphicalEditPart) part).getFigure() instanceof AirStyleDefaultSizeNodeFigure) { + result = true; + } else { + result = false; + } + return result; + } + }; + + /** + * All diagrams label. + */ + protected List allDiagramLabels; + + /** + * The editor where the search is performed. + */ + protected DiagramEditor editor; + + /** + * This constructor will initialize the engine with all labels contained + * within <tt>currentEditor</tt>. + * + * @param currentEditor + * The editor on which the search is performed. + */ + public BasicFindLabelEngine(final DiagramEditor currentEditor) { + super(); + this.editor = currentEditor; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.common.tools.api.find.AbstractFindLabelEngine#filterLabels(java.lang.String) + */ + @Override + protected List filterLabels(final String search) { + final List<EditPart> result = Lists.newArrayList(); + for (Object obj : allLabels()) { + final AbstractGraphicalEditPart label = (AbstractGraphicalEditPart) obj; + if (matches(label, search)) { + result.add(label); + } + } + return result; + } + + private boolean matches(final AbstractGraphicalEditPart label, final String search) { + return matches(getText(label), search); + } + + private boolean matches(final String text, final String search) { + return text.indexOf(search) != -1; + } + + /** + * Returns the text associated to a label edit part and which is used for + * textual search. + */ + private String getText(final AbstractGraphicalEditPart label) { + final IFigure figure = label.getFigure(); + final String text = getText(figure); + return text != null ? text : ""; + } + + private String getText(final IFigure figure) { + Option<String> result = new FigureQuery(figure).getText(); + if (result.some()) { + return result.get(); + } + return null; + } + + private List allLabels() { + if (allDiagramLabels == null) { + findAllDiagramLabels(); + } + return allDiagramLabels; + } + + /** + * Finds all the edit parts in the editor which represent labels, and are + * thus candidates for text matching. + */ + private void findAllDiagramLabels() { + allDiagramLabels = Lists.newArrayList(); + allDiagramLabels.addAll(Collections2.filter((Collection<? extends EditPart>) findAllEditParts(), IS_LABEL_EDIT_PART)); + } + + private Collection<? extends EditPart> findAllEditParts() { + final DiagramEditPart diagram = editor.getDiagramEditPart(); + final Collection<? extends EditPart> roots = Lists.newArrayList(diagram); + roots.addAll(diagram.getConnections()); + final Collection<? extends EditPart> editParts = Lists.newArrayList(); + for (EditPart root : roots) { + addAllContainedEditParts(root, editParts); + } + return editParts; + } + + /** + * Add an edit part and all its descendants into the specified collection. + */ + private void addAllContainedEditParts(final EditPart ep, final Collection coll) { + coll.add(ep); + for (Object child : ep.getChildren()) { + addAllContainedEditParts((EditPart) child, coll); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/AdvancedSiriusLayoutDataManager.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/AdvancedSiriusLayoutDataManager.java new file mode 100644 index 0000000000..e66c2b3614 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/AdvancedSiriusLayoutDataManager.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import java.util.Map; + +import org.eclipse.sirius.diagram.layoutdata.EdgeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.NodeLayoutData; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey; +import org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager; + +/** + * Interface to manage + * {@link org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager} + * . + * + * @author dlecan + */ +public interface AdvancedSiriusLayoutDataManager extends SiriusLayoutDataManager { + + /** + * Get only root node layout data, that is to say only the layout data + * without parent. + * + * @return Map. + */ + Map<? extends LayoutDataKey, ? extends NodeLayoutData> getRootNodeLayoutData(); + + /** + * Get node layout data. + * + * @return Map. + */ + Map<? extends NodeLayoutDataKey, NodeLayoutData> getNodeLayoutData(); + + /** + * Get edge layout data. + * + * @return Map. + */ + Map<? extends EdgeLayoutDataKey, EdgeLayoutData> getEdgeLayoutData(); + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ArrangeAllWithAutoSize.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ArrangeAllWithAutoSize.java new file mode 100644 index 0000000000..0ddbfdda37 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ArrangeAllWithAutoSize.java @@ -0,0 +1,365 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +import java.util.Set; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.editparts.AbstractGraphicalEditPart; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; +import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; +import org.eclipse.gmf.runtime.draw2d.ui.internal.graph.VirtualNode; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.business.internal.query.DDiagramElementContainerExperimentalQuery; +import org.eclipse.sirius.business.internal.query.DNodeContainerExperimentalQuery; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramBorderNodeEditPart; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramElementContainerEditPart; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramNameEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramContainerEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramListEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DDiagramEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.SquareEditPart; +import org.eclipse.sirius.diagram.internal.operation.RegionContainerUpdateLayoutOperation; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.preferences.SiriusDiagramPreferencesKeys; +import org.eclipse.sirius.diagram.ui.tools.api.layout.PinHelper; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.provider.AbstractCompositeLayoutProvider; + +/** + * This class capture all the needed changes to make to the arrange all behavior + * so that auto-size is automatically applied on containers. + * + * @author cbrun + */ +public class ArrangeAllWithAutoSize { + /** + * Return true if this part should be autosized. + * + * @param part + * The concern part + * @return Return true if this part should be auto-size + */ + public static boolean shouldBeAutosized(final IGraphicalEditPart part) { + boolean enabled = ArrangeAllWithAutoSize.isEnabled() && (ArrangeAllWithAutoSize.isContainer(part) || ArrangeAllWithAutoSize.isList(part)) && !ArrangeAllWithAutoSize.isPinned(part); + if (enabled && ArrangeAllWithAutoSize.isRegion(part)) { + EditPart regionContainerCompartment = part.getParent(); + if (regionContainerCompartment != null) { + EditPart regionContainer = regionContainerCompartment.getParent(); + enabled = regionContainer instanceof IGraphicalEditPart && !ArrangeAllWithAutoSize.isPinned((IGraphicalEditPart) regionContainer); + } + } + return enabled; + } + + private static boolean isPinned(final IGraphicalEditPart part) { + if (part instanceof IDiagramElementEditPart) { + IDiagramElementEditPart diagramElementEditPart = (IDiagramElementEditPart) part; + DDiagramElement dDiagramElement = diagramElementEditPart.resolveDiagramElement(); + return new PinHelper().isPinned(dDiagramElement); + } + return false; + } + + private static boolean isContainer(final IGraphicalEditPart part) { + return part instanceof IDiagramContainerEditPart; + } + + private static boolean isList(final IGraphicalEditPart part) { + return part instanceof IDiagramListEditPart; + } + + /** + * return true if the auto-size on arrange all feature is enabled in the + * preferences. + * + * @return true if the auto-size on arrange all feature is enabled in the + * preferences. + */ + public static boolean isEnabled() { + return SiriusDiagramEditorPlugin.getInstance().getPluginPreferences().getBoolean(SiriusDiagramPreferencesKeys.PREF_AUTOSIZE_ON_ARRANGE.name()); + } + + /** + * Prepare the auto-size support for an incoming arrange all. This + * preparation involve activating specific behavior on edit parts figures so + * that they are able, later on, to provide their 'incoming size after + * auto-size has been applied' so that the layout can leverage this + * information. + * + * @param unmodifiableIterator + * list iterator of edit parts. + */ + public void prepareForArrangeAll(final Iterator<AbstractDiagramElementContainerEditPart> unmodifiableIterator) { + final Set<IFigure> parentFiguresToValidateToGetAutosizeDimensions = Sets.newLinkedHashSet(); + while (unmodifiableIterator.hasNext()) { + final AbstractDiagramElementContainerEditPart ep = unmodifiableIterator.next(); + if (ArrangeAllWithAutoSize.shouldBeAutosized(ep)) { + ep.forceFigureAutosize(); + if (ep.getParent() instanceof AbstractGraphicalEditPart) { + parentFiguresToValidateToGetAutosizeDimensions.add(((AbstractGraphicalEditPart) ep.getParent()).getFigure()); + } + } + } + for (final IFigure parentFig : parentFiguresToValidateToGetAutosizeDimensions) { + parentFig.validate(); + } + } + + /** + * Return the dimension this edit part is going to take after application of + * the auto-size behavior. + * + * @param ep + * any edit part part of the initialization process. + * @return the dimension this edit part is going to take after application + * of the auto-size behavior. + */ + public Dimension getSizeToConsiderDuringArrangeAll(final IGraphicalEditPart ep) { + final Dimension size; + if (ArrangeAllWithAutoSize.shouldBeAutosized(ep) && ep instanceof AbstractDiagramElementContainerEditPart) { + size = ((AbstractDiagramElementContainerEditPart) ep).getAutosizedDimensions().getSize(); + } else { + size = ep.getFigure().getBounds().getSize(); + } + return size; + } + + // CHECKSTYLE:OFF + /** + * Create the "Arrange All" sub-commands considering setting all the + * containers as auto-sized. + * + * @param globalDiff + * current diff. + * @param vi + * iterator of views + * @param cc + * command to update. + * @param provider + * a layouter responsible to provide the node metrics. + * @param minxX + * the X coordinate of the reference point. + * @param minxY + * the Y coordinate of the reference point. + */ + public void createSubCommands(Point globalDiff, ListIterator vi, final CompoundCommand cc, final AbstractCompositeLayoutProvider provider, final int minX, final int minY) { + List<Node> nodes = Lists.newArrayList(vi); + vi = nodes.listIterator(); + // Now set the position of the icons. This causes the + // arc connection points to be recalculated + while (vi.hasNext()) { + final Node node = (Node) vi.next(); + + if (node.data instanceof ShapeEditPart) { + createSubCommands(globalDiff, cc, provider, minX, minY, nodes, node); + } + } + } + + private void createSubCommands(Point globalDiff, final CompoundCommand cc, final AbstractCompositeLayoutProvider provider, final int minX, final int minY, List<Node> nodes, final Node node) { + Point diffP = diff(minX, minY, nodes, provider, node); + diffP.x = Math.max(diffP.x, globalDiff.x); + diffP.y = Math.max(diffP.y, globalDiff.y); + if (diffP.x > -node.getPadding().left) { + diffP.x = -node.getPadding().left; + } + if (diffP.y > -node.getPadding().bottom) { + diffP.y = -node.getPadding().bottom; + } + + Rectangle nodeExt = provider.translateToGraph(provider.provideNodeMetrics(node)); + + createSubCommands(cc, node, diffP, nodeExt); + } + + private void createSubCommands(final CompoundCommand cc, final Node node, Point diff, final Rectangle nodeExt) { + if (node.getParent() instanceof VirtualNode) { + if (nodeExt.x > node.getPadding().left) { + nodeExt.x -= node.getPadding().left; + } + if (nodeExt.y > node.getPadding().top) { + nodeExt.y -= node.getPadding().top; + } + } + + final Point pt = new Point(); + if (nodeExt.x + diff.x > 0) { + pt.x = nodeExt.x + diff.x; + } else { + pt.x = nodeExt.x; + } + if (nodeExt.y + diff.y > 0) { + pt.y = nodeExt.y + diff.y; + } else { + pt.y = nodeExt.y; + } + final Point ptLocation = new Point(pt.x, pt.y); + final IGraphicalEditPart gep = (IGraphicalEditPart) node.data; + final Point ptOldLocation = gep.getFigure().getBounds().getLocation(); + + gep.getFigure().translateToAbsolute(ptOldLocation); + gep.getFigure().translateToAbsolute(ptLocation); + + if (gep.getParent() instanceof DDiagramEditPart) { + if (node.x == node.getPadding().left) { + ptLocation.x -= node.getPadding().left; + } + if (node.y == node.getPadding().bottom) { + ptLocation.y -= node.getPadding().bottom; + } + } + + final Dimension delta = ptLocation.getDifference(ptOldLocation); + final ChangeBoundsRequest request = new ChangeBoundsRequest(org.eclipse.gef.RequestConstants.REQ_MOVE); + + final boolean isRegion = isRegion(gep); + if (isRegion) { + final Dimension size = nodeExt.getSize().getCopy(); + final Dimension oldSize = gep.getFigure().getBounds().getSize(); + + gep.getFigure().translateToAbsolute(oldSize); + gep.getFigure().translateToAbsolute(size); + final Dimension deltaSize = size.getDifference(oldSize); + + request.setType(org.eclipse.gef.RequestConstants.REQ_RESIZE); + request.setConstrainedResize(true); + request.setSizeDelta(deltaSize); + } + + if (!(delta.height == 0 && delta.width == 0) || isRegion && !(request.getSizeDelta().height == 0 && request.getSizeDelta().width == 0)) { + request.setEditParts(gep); + request.setMoveDelta(new Point(delta.width, delta.height)); + request.setLocation(ptLocation); + + if (ArrangeAllWithAutoSize.shouldBeAutosized(gep)) { + final CompoundCommand cmd = new CompoundCommand(); + cmd.add(gep.getCommand(request)); + cmd.add(gep.getCommand(new Request(RequestConstants.REQ_AUTOSIZE))); + cc.add(cmd); + } else { + final Command cmd = gep.getCommand(request); + if (cmd != null && cmd.canExecute()) { + cc.add(cmd); + } + } + } + + if (isRegionContainer(gep)) { + cc.add(new ICommandProxy(CommandFactory.createICommand(gep.getEditingDomain(), new RegionContainerUpdateLayoutOperation((org.eclipse.gmf.runtime.notation.Node) gep.getModel())))); + } + + } + + private static boolean isRegion(IGraphicalEditPart gep) { + if (gep instanceof AbstractDiagramElementContainerEditPart) { + DDiagramElement resolveDiagramElement = ((AbstractDiagramElementContainerEditPart) gep).resolveDiagramElement(); + if (resolveDiagramElement instanceof DDiagramElementContainer) { + return new DDiagramElementContainerExperimentalQuery((DDiagramElementContainer) resolveDiagramElement).isRegion(); + } + } + return false; + } + + private static boolean isRegionContainer(IGraphicalEditPart gep) { + if (gep instanceof IDiagramContainerEditPart) { + DDiagramElement resolveDiagramElement = ((IDiagramContainerEditPart) gep).resolveDiagramElement(); + if (resolveDiagramElement instanceof DNodeContainer) { + return new DNodeContainerExperimentalQuery((DNodeContainer) resolveDiagramElement).isRegionContainer(); + } + } + return false; + } + + private Point diff(final int minX, final int minY, List<Node> nodes, AbstractCompositeLayoutProvider provider, Node firstNode) { + Point ptLayoutMin = new Point(-1, -1); + for (Node node : nodes) { + // ignore ghost node + if (node.data != null) { + Rectangle nodeExt = provider.provideNodeMetrics(node); + nodeExt = ArrangeAllWithAutoSize.extendBoundingBoxWithBorderedNodes(node, nodeExt); + if (ptLayoutMin.x == -1) { + ptLayoutMin.x = nodeExt.x; + ptLayoutMin.y = nodeExt.y; + } else { + ptLayoutMin.x = Math.min(ptLayoutMin.x, nodeExt.x); + ptLayoutMin.y = Math.min(ptLayoutMin.y, nodeExt.y); + } + } + + } + if (ptLayoutMin.x == firstNode.x) { + return provider.translateFromGraph(new Rectangle(0, 0, 0, 0)).getLocation(); + } + return provider.translateFromGraph(new Rectangle(minX - ptLayoutMin.x, minY - ptLayoutMin.y, 0, 0)).getLocation(); + // return new Point(minX - ptLayoutMin.x, minY - ptLayoutMin.y); + } + + static Rectangle extendBoundingBoxWithBorderedNodes(Node node, Rectangle box) { + Rectangle extendedBox = box.getCopy(); + if (node.data instanceof IGraphicalEditPart) { + IGraphicalEditPart gep = (IGraphicalEditPart) node.data; + /* + * 'delta' is how much the bordered node's parent has moved from its + * original position. For the bordered nodes, we can only get their + * original position (through the figure), but we assume they are + * moved along with their parent of the same delta. + */ + Dimension delta = box.getLocation().getDifference(gep.getFigure().getBounds().getLocation()); + for (AbstractDiagramBorderNodeEditPart borderedNode : Iterables.filter(gep.getChildren(), AbstractDiagramBorderNodeEditPart.class)) { + Rectangle translate = borderedNode.getFigure().getBounds().getCopy().translate(delta.width, delta.height); + extendedBox.union(translate); + } + Rectangle extendedBoxName = new Rectangle(); + for (AbstractDiagramNameEditPart nameNode : Iterables.filter(gep.getChildren(), AbstractDiagramNameEditPart.class)) { + Rectangle translate = nameNode.getFigure().getBounds().getCopy(); + extendedBoxName.union(new Dimension(0, translate.height)); + } + for (SquareEditPart nameNode : Iterables.filter(gep.getChildren(), SquareEditPart.class)) { + Dimension translate = gep.getFigure().getMinimumSize().getCopy(); + extendedBoxName.union(new Dimension(0, translate.height)); + } + + if (extendedBoxName.height > extendedBox.y) { + extendedBox.translate(extendedBoxName.width, extendedBoxName.height); + } + } + if (extendedBox.x < node.getPadding().left) { + extendedBox.x = node.getPadding().left; + } + if (extendedBox.y < node.getPadding().bottom) { + extendedBox.y = node.getPadding().bottom; + } + return extendedBox; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/AutoSizeAndRegionAwareGraphLayout.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/AutoSizeAndRegionAwareGraphLayout.java new file mode 100644 index 0000000000..84d9582709 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/AutoSizeAndRegionAwareGraphLayout.java @@ -0,0 +1,600 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.LayoutManager; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.XYLayout; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.graph.DirectedGraph; +import org.eclipse.draw2d.graph.DirectedGraphLayout; +import org.eclipse.draw2d.graph.Edge; +import org.eclipse.draw2d.graph.EdgeList; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.draw2d.graph.NodeList; +import org.eclipse.draw2d.graph.Subgraph; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.draw2d.ui.internal.graph.AdvancedSubGraph; +import org.eclipse.gmf.runtime.draw2d.ui.internal.graph.CompositeDirectedGraphLayout; +import org.eclipse.gmf.runtime.draw2d.ui.internal.graph.VirtualNode; + +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.common.tools.api.util.Options; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.business.internal.query.DDiagramElementContainerExperimentalQuery; +import org.eclipse.sirius.business.internal.query.DNodeContainerExperimentalQuery; +import org.eclipse.sirius.diagram.edit.api.part.IAbstractDiagramNodeEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramContainerEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramNameEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.AbstractDNodeContainerCompartmentEditPart; + +/** + * Customized version of the standard CompositeDirectedGraphLayout to improve + * auto-size handling. The original class does not support extensibility, so its + * whole code has been copied here and adapted, from the version in GMF 1.0.101. + * <p> + * Changes from original: + * <ul> + * <li>Backport of the fix in version 1.4 of the fils in CVS ( + * <code>http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.gmf/plugins/org.eclipse.gmf.runtime.draw2d.ui/src/org/eclipse/gmf/runtime/draw2d/ui/internal/graph/CompositeDirectedGraphLayout.java?revision=1.4&root=Modeling_Project&view=markup</code> + * )</li> + * <LI>Add a graph direction variable as in GMF 1.3.0 to manage correctly the + * leftRight layout of CompositeLayout</LI> + * <LI>Change the insets used in recursiveHandleVirtualNode to get the computed + * Insets of our CompositeLayout</LI> + * </ul> + * + * @author pcdavid + */ +// CHECKSTYLE:OFF +@SuppressWarnings("restriction") +public class AutoSizeAndRegionAwareGraphLayout extends CompositeDirectedGraphLayout { + + private int specificGraphDirection = PositionConstants.SOUTH; + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.draw2d.graph.DirectedGraphLayout#visit(org.eclipse.draw2d + * .graph.DirectedGraph) + */ + @Override + public void visit(DirectedGraph graph) { + specificGraphDirection = graph.getDirection(); + layoutNodes(graph.nodes, false); + } + + private void layoutNodes(NodeList nodes, boolean virtualPass) { + EdgeList edges = new EdgeList(); + for (Iterator iter = nodes.iterator(); iter.hasNext();) { + Node element = (Node) iter.next(); + if (element instanceof Subgraph && !(element instanceof VirtualNode)) { + layoutNodes(((Subgraph) element).members, virtualPass); + } + for (Iterator edgesIter = element.outgoing.iterator(); edgesIter.hasNext();) { + Edge edge = (Edge) edgesIter.next(); + if (nodes.contains(edge.target)) { + edges.add(edge); + } + } + } + if (!virtualPass) { + VirtualNodesToNodes virtualNodesNodes = new VirtualNodesToNodes(); + createVirtualNodes(nodes, edges, virtualNodesNodes); + NodeList vituralNodes = virtualNodesNodes.getVirtualNodes(); + int size = vituralNodes.size(); + if (size > 0) { + edges = virtualNodesNodes.getEdges(); + for (Iterator iter = vituralNodes.iterator(); iter.hasNext();) { + Subgraph virtualNode = (Subgraph) iter.next(); + layoutNodes(virtualNode.members, true); + } + adjustVirtualNodesWidthAndHeight(vituralNodes); + } + } + + Map nodeToOutGoing = new HashMap(); + Map nodeToIncomingGoing = new HashMap(); + removeDisconnectedEdges(nodes, nodeToOutGoing, nodeToIncomingGoing); + if (nodes.size() > 0) { + Node parent = getParent(nodes.getNode(0)); + DirectedGraph g = new DirectedGraph(); + g.setDirection(specificGraphDirection); + g.nodes = nodes; + g.edges = edges; + + final boolean concernRegions = (parent == null && areRegions(nodes)) || (parent != null && getRegionContainer(parent).some()); + /* + * Handle regions : deactivate the default behavior where all nodes + * of a same row have the same vertical bounds + */ + if (!concernRegions) { + DirectedGraphLayout layout = new DirectedGraphLayout(); + layout.visit(g); + } + + /* + * <handle_bordered_nodes> + * + * Translate all the elements at this level of the appropriate + * offsets to consider their bordered nodes. Otherwise elements are + * put too close to their parent's border, and the bordered nodes + * cause the appearance of scrollbars. + */ + Dimension offsets = getOffsets(nodes); + for (int j = 0; j < nodes.size(); j++) { + Node n = nodes.getNode(j); + n.x += offsets.width; + n.y += offsets.height; + } + + /* + * Handle regions : deactivate the default behavior where all nodes + * of a same row have the same vertical bounds + */ + if (concernRegions) { + adjustRegionsLayout(nodes, parent); + } + + if (parent instanceof AdvancedSubGraph) { + adjustAutoSizeNodeWidthAndHeight((AdvancedSubGraph) parent); + } + } + + restoreDisconnectedEdges(nodeToOutGoing, nodeToIncomingGoing); + } + + private void adjustRegionsLayout(NodeList nodes, Node parent) { + if (nodes.isEmpty()) + return; + + DNodeContainer regionContainer = getRegionContainer(nodes, parent); + if (regionContainer != null) { + DNodeContainerExperimentalQuery query = new DNodeContainerExperimentalQuery(regionContainer); + if (query.isVerticalStackContainer()) { + adjustRegionLayout(nodes, parent, true); + } else if (query.isHorizontaltackContainer()) { + adjustRegionLayout(nodes, parent, false); + } + } + } + + /* + * @param vertical : vertical if true, horizontal if false. + */ + private void adjustRegionLayout(NodeList nodes, Node parent, boolean vertical) { + int commonWidth = 0; + int commonHeight = 0; + for (int j = 0; j < nodes.size(); j++) { + Node n = nodes.getNode(j); + commonWidth = Math.max(commonWidth, n.width); + commonHeight = Math.max(commonHeight, n.height); + } + + // Start regions after parent label. + int y = getLabelHeight(parent); + int x = 0; + + for (int j = 0; j < nodes.size(); j++) { + Node n = nodes.getNode(j); + n.x = x; + n.y = y; + + if (vertical) { + n.width = commonWidth; + y += n.height; + } else { + x += n.width; + n.height = commonHeight; + } + } + } + + private DNodeContainer getRegionContainer(NodeList nodes, Node parent) { + DNodeContainer rc = null; + if (parent != null) { + rc = getRegionContainer(parent).get(); + } else { + Option<DDiagramElementContainer> region = getRegion(nodes.getNode(0)); + rc = region.some() ? (DNodeContainer) region.get().eContainer() : null; + } + + return rc; + } + + private int getLabelHeight(Node parent) { + if (parent != null && parent.data instanceof AbstractDNodeContainerCompartmentEditPart) { + AbstractDNodeContainerCompartmentEditPart comp = (AbstractDNodeContainerCompartmentEditPart) parent.data; + EditPart containerEditPart = comp.getParent(); + IDiagramNameEditPart nameEditPart = Iterables.getFirst(Iterables.filter(containerEditPart.getChildren(), IDiagramNameEditPart.class), null); + if (nameEditPart != null && nameEditPart.getFigure() != null) { + return nameEditPart.getFigure().getBounds().height; + } + } + return 0; + } + + /* no parent detect regions */ + private boolean areRegions(NodeList nodes) { + boolean areRegions = false; + for (int j = 0; j < nodes.size(); j++) { + Node n = nodes.getNode(j); + areRegions = areRegions || getRegion(n).some(); + } + return areRegions; + } + + private Option<DNodeContainer> getRegionContainer(Node node) { + DNodeContainer dnc = null; + if (node != null && (node.data instanceof IDiagramContainerEditPart || node.data instanceof AbstractDNodeContainerCompartmentEditPart)) { + EObject element = ((IGraphicalEditPart) node.data).resolveSemanticElement(); + if (element instanceof DNodeContainer) { + dnc = (DNodeContainer) element; + } + } + + if (dnc != null && new DNodeContainerExperimentalQuery(dnc).isRegionContainer()) { + return Options.newSome(dnc); + } + return Options.newNone(); + } + + private Option<DDiagramElementContainer> getRegion(Node node) { + if (node.data instanceof IAbstractDiagramNodeEditPart) { + DDiagramElement element = ((IAbstractDiagramNodeEditPart) node.data).resolveDiagramElement(); + if (element instanceof DDiagramElementContainer && new DDiagramElementContainerExperimentalQuery((DDiagramElementContainer) element).isRegion()) { + return Options.newSome((DDiagramElementContainer) element); + } + } + return Options.newNone(); + } + + /** + * Computes how much the specified nodes should be moved to make enough room + * for the parent container to contain them + * <em>with their bordered nodes</em>. + */ + private Dimension getOffsets(NodeList nodes) { + Node node = nodes.getNode(0); + int left = node.x, top = node.y; + Rectangle bbox = ArrangeAllWithAutoSize.extendBoundingBoxWithBorderedNodes(node, new Rectangle(node.x, node.y, node.width, node.height)); + int leftBorder = bbox.x, topBorder = bbox.y; + for (int i = 1; i < nodes.size(); i++) { + node = nodes.getNode(i); + left = Math.min(left, node.x); + top = Math.min(top, node.y); + bbox = ArrangeAllWithAutoSize.extendBoundingBoxWithBorderedNodes(node, new Rectangle(node.x, node.y, node.width, node.height)); + leftBorder = Math.min(leftBorder, bbox.x); + topBorder = Math.min(topBorder, bbox.y); + } + Point diff = new Point(); + if (node.getParent() != null) { + diff = new Point(node.getParent().x - node.x, node.getParent().y - node.y); + } + if (diff.x < 0 && diff.y < 0) { + return new Dimension(Math.max(0, left - leftBorder), Math.max(0, top - topBorder)); + } + return new Dimension(Math.max(0, Math.abs(left - leftBorder)), Math.max(0, Math.abs(top - topBorder))); + } + + private void restoreDisconnectedEdges(Map nodeToOutGoing, Map nodeToIncomingGoing) { + restoreEdges(nodeToOutGoing.entrySet(), true); + restoreEdges(nodeToIncomingGoing.entrySet(), false); + } + + private void removeDisconnectedEdges(NodeList nodes, Map nodeToOutGoing, Map nodeToIncomingGoing) { + for (Iterator iter = nodes.iterator(); iter.hasNext();) { + Node element = (Node) iter.next(); + pushExtraEdges(nodes, nodeToOutGoing, element, element.outgoing, false); + pushExtraEdges(nodes, nodeToIncomingGoing, element, element.incoming, true); + } + } + + private void createVirtualNodes(NodeList nodes, EdgeList edges, VirtualNodesToNodes virtualNodesNodes) { + Set handledEdges = new HashSet(); + recursiveHandleVirtualNode(nodes, edges, virtualNodesNodes, handledEdges, new HashSet(nodes)); + } + + private void recursiveHandleVirtualNode(NodeList nodes, EdgeList edges, VirtualNodesToNodes virtualNodesNodes, Set handledEdges, Set nodesSnapeShot) { + for (Iterator edgeIter = edges.iterator(); edgeIter.hasNext();) { + Edge element = (Edge) edgeIter.next(); + if (handledEdges.contains(element)) { + continue; + } + handledEdges.add(element); + if (!nodesSnapeShot.contains(element.source) || !nodesSnapeShot.contains(element.target)) { + continue; + } + Node source = element.source; + Node target = element.target; + boolean sourceHandled = true; + boolean targetHandled = true; + Subgraph sg = virtualNodesNodes.getVirtualContainer(source); + Subgraph sg1 = virtualNodesNodes.getVirtualContainer(target); + if (sg == null) { + sourceHandled = false; + sg = sg1; + } + if (sg1 == null) { + targetHandled = false; + } + if (sourceHandled == false && targetHandled == false) { + sg = new VirtualNode(null, source.getParent()); + sg.setPadding(new Insets(source.getPadding())); + if (source.getParent() == null) { + nodes.add(sg); + } + } + if (!sourceHandled) { + addNode(sg, source, nodes); + virtualNodesNodes.addNode(sg, source); + } + if (!targetHandled) { + addNode(sg, target, nodes); + virtualNodesNodes.addNode(sg, target); + } + // order is important; so we should start handling the outgoing and + // the incoming + // edges only after the source and target had been handled + if (!sourceHandled) { + recursiveHandleVirtualNode(nodes, source.outgoing, virtualNodesNodes, handledEdges, nodesSnapeShot); + recursiveHandleVirtualNode(nodes, source.incoming, virtualNodesNodes, handledEdges, nodesSnapeShot); + } + if (!targetHandled) { + recursiveHandleVirtualNode(nodes, target.outgoing, virtualNodesNodes, handledEdges, nodesSnapeShot); + recursiveHandleVirtualNode(nodes, target.incoming, virtualNodesNodes, handledEdges, nodesSnapeShot); + } + } + } + + private void pushExtraEdges(NodeList nodes, Map nodeToIncomingGoing, Node element, List list, boolean sourceCheck) { + List edges = new ArrayList(); + for (Iterator iterator = list.iterator(); iterator.hasNext();) { + Edge edge = (Edge) iterator.next(); + Node nodeToCheck = sourceCheck ? edge.source : edge.target; + if (!nodes.contains(nodeToCheck)) { + edges.add(edge); + iterator.remove(); + Node sourceNode = null; + Node targetNode = null; + sourceNode = getParent(edge.source); + targetNode = getParent(edge.target); + sourceNode = (!sourceCheck || sourceNode != null) ? sourceNode : edge.source; + targetNode = (sourceCheck || targetNode != null) ? targetNode : edge.target; + if (!sourceCheck && sourceNode != null && targetNode != null && sourceNode != targetNode && (edge.source != sourceNode || edge.target != targetNode)) { + Edge virtualEdge = new Edge(sourceNode, targetNode, edge.getDelta(), edge.weight); + virtualEdge.setPadding(edge.getPadding()); + } + } + } + if (!edges.isEmpty()) { + nodeToIncomingGoing.put(element, edges); + } + } + + private Node getParent(Node node) { + Node parent = node.getParent(); + if (parent instanceof VirtualNode) { + parent = parent.getParent(); + } + return parent; + } + + private void restoreEdges(Set entries, boolean outgoing) { + for (Iterator iter = entries.iterator(); iter.hasNext();) { + Map.Entry entry = (Map.Entry) iter.next(); + Node node = (Node) entry.getKey(); + List edgesList = (List) entry.getValue(); + for (Iterator iterator = edgesList.iterator(); iterator.hasNext();) { + Edge edgeToRestore = (Edge) iterator.next(); + if (outgoing) { + node.outgoing.add(edgeToRestore); + } else { + node.incoming.add(edgeToRestore); + } + } + + } + } + + private void adjustVirtualNodesWidthAndHeight(NodeList vituralNodes) { + for (Iterator iter = vituralNodes.iterator(); iter.hasNext();) { + Subgraph subGraph = (Subgraph) iter.next(); + adjustVirtualNodeWidthAndHeight(subGraph); + } + + } + + private void adjustVirtualNodeWidthAndHeight(Subgraph subGraph) { + NodeList nodes = subGraph.members; + if (nodes.isEmpty()) { + return; + } + int size = nodes.size(); + Node node = nodes.getNode(0); + int top = node.y, left = node.x, bottom = top + node.height, right = left + node.width; + for (int index = 1; index < size; index++) { + node = (Node) nodes.get(index); + if (top > node.y) { + top = node.y; + } + if (bottom < (node.y + node.height)) { + bottom = node.y + node.height; + } + if (left > node.x) { + left = node.x; + } + if (right < (node.x + node.width)) { + right = node.x + node.width; + } + } + subGraph.width = right - left; + subGraph.height = bottom - top; + } + + private void adjustAutoSizeNodeWidthAndHeight(AdvancedSubGraph subGraph) { + if (!subGraph.isAutoSize()) { + return; + } + + NodeList nodes = subGraph.members; + if (nodes.isEmpty()) { + return; + } + int size = nodes.size(); + Node node = nodes.getNode(0); + int top = node.y, left = node.x, bottom = top + node.height, right = left + node.width; + Node topNode, leftNode; + topNode = leftNode = node; + for (int index = 1; index < size; index++) { + node = (Node) nodes.get(index); + if (top > node.y) { + top = node.y; + topNode = node; + } + if (bottom < (node.y + node.height)) { + bottom = node.y + node.height; + } + if (left > node.x) { + left = node.x; + leftNode = node; + } + if (right < (node.x + node.width)) { + right = node.x + node.width; + } + } + int xDiff = 0; + int yDiff = 0; + if (subGraph.isHasBufferedZone()) { + xDiff = leftNode.x; + yDiff = topNode.y; + } + subGraph.width = right - left + xDiff; + subGraph.height = bottom - top + yDiff; + + /* + * <auto-size_fix> + * + * This algorithm comes from + * org.eclipse.gmf.runtime.diagram.ui.layout.FreeFormLayoutEx + * .layout(IFigure parent). + */ + if (subGraph.data instanceof IGraphicalEditPart) { + IGraphicalEditPart gep = (IGraphicalEditPart) subGraph.data; + IFigure f = gep.getFigure(); + + LayoutManager lm = f.getParent().getLayoutManager(); + Point offset; + if (lm instanceof XYLayout) { + offset = ((XYLayout) lm).getOrigin(f.getParent()); + } else { + offset = new Point(); + } + + Rectangle bounds = f.getBounds().getCopy(); + + if (bounds.width == -1 || bounds.height == -1) { + Dimension _preferredSize = f.getPreferredSize(bounds.width, bounds.height); + bounds = bounds.getCopy(); + if (bounds.width == -1) { + bounds.width = _preferredSize.width; + } + if (bounds.height == -1) { + bounds.height = _preferredSize.height; + } + } + Dimension min = f.getMinimumSize(); + Dimension max = f.getMaximumSize(); + + if (min.width > bounds.width) { + bounds.width = min.width; + } else if (max.width < bounds.width) { + bounds.width = max.width; + } + + if (min.height > bounds.height) { + bounds.height = min.height; + } else if (max.height < bounds.height) { + bounds.height = max.height; + } + bounds = bounds.getTranslated(offset); + + subGraph.width = Math.max(subGraph.width, bounds.width); + subGraph.height = Math.max(subGraph.height, bounds.height); + } + /* + * </auto-size_fix> + */ + } + + private void addNode(Subgraph parent, Node node, NodeList nodes) { + if (node.getParent() != null) { + node.getParent().members.remove(node); + } + node.setParent(parent); + parent.addMember(node); + nodes.remove(node); + } + + @SuppressWarnings("serial") + private static class VirtualNodesToNodes extends HashMap { + Set virtualNodes = new HashSet(); + + public void addNode(Subgraph sg, Node node) { + virtualNodes.add(sg); + put(node, sg); + } + + public EdgeList getEdges() { + EdgeList edges = new EdgeList(); + for (Iterator iter = virtualNodes.iterator(); iter.hasNext();) { + Node element = (Node) iter.next(); + for (Iterator iterator = element.outgoing.iterator(); iterator.hasNext();) { + Edge edge = (Edge) iterator.next(); + if (virtualNodes.contains(edge.target)) { + edges.add(edge); + } + + } + } + return edges; + } + + public Subgraph getVirtualContainer(Node node) { + return (Subgraph) get(node); + } + + public NodeList getVirtualNodes() { + NodeList nodeList = new NodeList(); + nodeList.addAll(virtualNodes); + return nodeList; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/DiagramLayoutCustomization.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/DiagramLayoutCustomization.java new file mode 100644 index 0000000000..630fd3d89d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/DiagramLayoutCustomization.java @@ -0,0 +1,184 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import java.util.Collection; + +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.AbstractBorderedShapeEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.CompartmentEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.CompositeLayout; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.description.Layout; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramBorderNodeEditPart; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramElementContainerEditPart; + +/** + * Class capturing the diagram layout customization made in a viewpoint + * specification model. + * + * @author cbrun + * + */ +public class DiagramLayoutCustomization { + /** + * This constant represent the distance from the side of a border node to + * the first bendpoint when the router is a border item "aware" one. + */ + private static final int BORDER_NODE_ROUTING_SPACE = 10; + + private int padding = 30; + + /** + * Create a new Diagram Customization. + */ + public DiagramLayoutCustomization() { + + } + + /** + * Return the Insets that one should use as a padding for the node. + * + * @param ep + * any editpart. + * @return the Insets that one should use as a padding for the node. + */ + public Insets getNodePadding(final GraphicalEditPart ep) { + /* + * This method has been redirected to allow redefinition of the node + * padding (which is final in the super class). + */ + Insets inSetPadding = new Insets(this.padding); + if (ep instanceof CompartmentEditPart || ep instanceof AbstractDiagramElementContainerEditPart && ((AbstractDiagramElementContainerEditPart) ep).isRegion()) { + // No padding for regions. + inSetPadding = new Insets(0); + } else if (ep instanceof AbstractBorderedShapeEditPart) { + // check if the direct parent is added already to the graph + + int maxWidth = -1; + int maxHeight = -1; + for (final AbstractDiagramBorderNodeEditPart borderNode : Iterables.filter(ep.getChildren(), AbstractDiagramBorderNodeEditPart.class)) { + int figTopMargin = borderNode.getFigure().getBounds().width / 4; + int figLeftMargin = borderNode.getFigure().getBounds().height / 4; + /* + * If we've got connections then we should add a bit more + * padding to avoid the first bendpoints of the connection as + * the router starts with an horizontal or vertical straight + * line when being from a port. + */ + final int nbConnections = borderNode.getSourceConnections().size() + ep.getTargetConnections().size(); + if (nbConnections > 0) { + figLeftMargin += figLeftMargin + BORDER_NODE_ROUTING_SPACE; + figTopMargin += figTopMargin + BORDER_NODE_ROUTING_SPACE; + } + if (figTopMargin > maxWidth) { + maxWidth = figTopMargin; + } + if (figLeftMargin > maxHeight) { + maxHeight = figLeftMargin; + } + } + inSetPadding = new Insets(Math.max(inSetPadding.top, maxWidth), Math.max(inSetPadding.left, maxHeight), Math.max(inSetPadding.bottom, maxWidth), Math.max(inSetPadding.right, maxHeight)); + } + return inSetPadding; + + } + + /** + * Initialize the diagram customization padding looking for specific + * settings in the given objects. + * + * @param selectedObjects + * collection of {@link IGraphicalEditPart} + */ + public void initializePaddingWithEditParts(final Collection selectedObjects) { + padding = findPaddingFromSelection(selectedObjects); + } + + /** + * Initialize the diagram customization padding looking for specific + * settings in the given views. + * + * @param views + * collection {@link View} + */ + public void initializePaddingWithViews(final Collection<View> views) { + padding = findPaddingFromViews(views); + } + + private int findPaddingFromViews(final Collection<View> views) { + int foundPadding = 30; + for (final View obj : views) { + foundPadding = getPadding(obj); + if (foundPadding != 30) + break; + } + return foundPadding; + } + + private int findPaddingFromSelection(final Collection selectedObjects) { + int foundPadding = 30; + Collection<IGraphicalEditPart> filteredSelection = Collections2.filter(selectedObjects, Predicates.instanceOf(GraphicalEditPart.class)); + for (final IGraphicalEditPart obj : filteredSelection) { + foundPadding = getEditPartPadding(obj); + if (foundPadding != 30) + break; + } + return foundPadding; + } + + private int getEditPartPadding(final IGraphicalEditPart container) { + return getPadding(container.getNotationView()); + } + + private int getPadding(final View gmfView) { + final Layout foundLayout = DiagramLayoutCustomization.findLayoutSettings(gmfView); + if (foundLayout instanceof CompositeLayout) { + return ((CompositeLayout) foundLayout).getPadding(); + } + return 30; + } + + /** + * return the layout setting associated with a given {@link View}. + * + * @param view + * any GMF View. + * @return the layout setting associated with a given {@link View}. Null if + * there is no such setting. + */ + public static Layout findLayoutSettings(final View view) { + Layout foundLayout = null; + if (view.getDiagram() != null) { + final EObject modelElement = view.getDiagram().getElement(); + if (modelElement instanceof DDiagram) { + final DDiagram vp = (DDiagram) modelElement; + final DiagramDescription desc = vp.getDescription(); + if (desc != null) { + foundLayout = desc.getLayout(); + } + return foundLayout; + } + } + return foundLayout; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/EdgeLayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/EdgeLayoutDataKey.java new file mode 100644 index 0000000000..20695e7bef --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/EdgeLayoutDataKey.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey; + +/** + * Interface for all kind of key use to store EdgelayoutData. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public interface EdgeLayoutDataKey extends LayoutDataKey { + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/IsPinnedPredicate.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/IsPinnedPredicate.java new file mode 100644 index 0000000000..943232a4c1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/IsPinnedPredicate.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import java.util.ArrayList; + +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.NoteEditPart; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.api.preferences.SiriusDiagramPreferencesKeys; +import org.eclipse.sirius.diagram.ui.tools.api.layout.PinHelper; + +/** + * A predicate to identify pinned/fixed edit-parts. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ +public class IsPinnedPredicate implements Predicate<IGraphicalEditPart> { + + ArrayList<IDiagramElementEditPart> elementsToKeepFixed; + + /** + * Default constructor. + * + * @param elementsToKeepFixed + * IDiagramElementEditPart which are not actually pinned but have + * to stay fixed. + */ + protected IsPinnedPredicate(ArrayList<IDiagramElementEditPart> elementsToKeepFixed) { + this.elementsToKeepFixed = elementsToKeepFixed; + } + + /** + * {@inheritDoc} + * + * @see com.google.common.base.Predicate#apply(java.lang.Object) + */ + public boolean apply(final IGraphicalEditPart part) { + boolean result = false; + if (part instanceof NoteEditPart) { + result = applyNote((NoteEditPart) part); + } else { + result = isPinnedOrKeepFixed(part); + } + return result; + } + + /** + * Applies this predicate to the given note. + * + * @param part + * the NoteEditPart that the predicate should act on + * @return the value of this predicate when applied to the input + * {@code part} + */ + private boolean applyNote(final NoteEditPart part) { + boolean result = false; + boolean connectedToPinnedElement = false; + for (ConnectionEditPart sourceConn : Iterables.filter(part.getSourceConnections(), ConnectionEditPart.class)) { + if (sourceConn.getTarget() instanceof IGraphicalEditPart) { + connectedToPinnedElement = connectedToPinnedElement || isPinnedOrKeepFixed((IGraphicalEditPart) sourceConn.getTarget()); + } + } + for (ConnectionEditPart targetConn : Iterables.filter(part.getTargetConnections(), ConnectionEditPart.class)) { + if (targetConn.getSource() instanceof IGraphicalEditPart) { + connectedToPinnedElement = connectedToPinnedElement || isPinnedOrKeepFixed((IGraphicalEditPart) targetConn.getSource()); + } + } + if (connectedToPinnedElement) { + result = true; + } else { + if (!SiriusDiagramEditorPlugin.getInstance().getPluginPreferences().getBoolean(SiriusDiagramPreferencesKeys.PREF_MOVE_NOTES_DURING_LATOUT.name())) { + result = true; + } + } + return result; + } + + private boolean isPinnedOrKeepFixed(IGraphicalEditPart part) { + boolean isPinnedOrKeepFixed = false; + if (part.resolveSemanticElement() instanceof DDiagramElement) { + DDiagramElement dDiagramElement = (DDiagramElement) part.resolveSemanticElement(); + isPinnedOrKeepFixed = new PinHelper().isPinned(dDiagramElement) || (elementsToKeepFixed != null && elementsToKeepFixed.contains(part)); + } + return isPinnedOrKeepFixed; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/LayoutDataHelperImpl.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/LayoutDataHelperImpl.java new file mode 100644 index 0000000000..f13182c78a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/LayoutDataHelperImpl.java @@ -0,0 +1,327 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import java.util.Map; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.emf.common.util.EList; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.draw2d.ui.figures.PolylineConnectionEx; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.ConnectorStyle; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.IdentityAnchor; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.Size; + +import com.google.common.base.Predicate; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.diagram.business.api.query.NodeQuery; +import org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData; +import org.eclipse.sirius.diagram.layoutdata.EdgeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.LayoutdataFactory; +import org.eclipse.sirius.diagram.layoutdata.NodeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.Point; +import org.eclipse.sirius.diagram.tools.api.draw2d.ui.figures.FigureUtilities; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.semantic.SemanticEdgeLayoutDataKey; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.semantic.SemanticNodeLayoutDataKey; + +/** + * Helper to manage the layout data. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ +public class LayoutDataHelperImpl implements LayoutDataHelper { + + private static final Predicate<EObject> ROOT_PREDICATE = new Predicate<EObject>() { + + /** + * {@inheritDoc} + */ + public boolean apply(final EObject input) { + return input.eContainer() == null; + } + }; + + /** + * Creates the default LayoutDataHelper implementation. + */ + public LayoutDataHelperImpl() { + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataHelper#createNodeLayoutData(org.eclipse.gmf.runtime.notation.Node, + * org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart, + * org.eclipse.sirius.diagram.layoutdata.NodeLayoutData) + */ + public NodeLayoutData createNodeLayoutData(final Node node, final IGraphicalEditPart editPart, final NodeLayoutData parentLayoutData) { + final NodeLayoutData result = LayoutdataFactory.eINSTANCE.createNodeLayoutData(); + + Dimension primarySize = new Dimension(0, 0); + // Compute the relative location from the parent layout data + // 1-Get the relative location + final org.eclipse.draw2d.geometry.Point relativeLocation = editPart.getFigure().getBounds().getLocation().getCopy(); + + // 2-Transform to absolute location + FigureUtilities.translateToAbsoluteByIgnoringScrollbar(editPart.getFigure(), relativeLocation); + + boolean isCollapsed = false; + if (new DDiagramElementQuery((DDiagramElement) node.getElement()).isIndirectlyCollapsed()) { + isCollapsed = true; + } + if (isCollapsed) { + LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds) { + NodeQuery nodeQuery = new NodeQuery(node); + Option<Bounds> option = nodeQuery.getExtendedBounds(); + if (option.some()) { + Bounds unCollapseBounds = option.get(); + int deltaX = ((Bounds) layoutConstraint).getX() - unCollapseBounds.getX(); + int deltaY = ((Bounds) layoutConstraint).getY() - unCollapseBounds.getY(); + + relativeLocation.setLocation(relativeLocation.x - deltaX, relativeLocation.y - deltaY); + primarySize = new Dimension(unCollapseBounds.getWidth(), unCollapseBounds.getHeight()); + } + } + } else { + final Integer width = (Integer) ViewUtil.getStructuralFeatureValue(node, NotationPackage.eINSTANCE.getSize_Width()); + final Integer height = (Integer) ViewUtil.getStructuralFeatureValue(node, NotationPackage.eINSTANCE.getSize_Height()); + primarySize = new Dimension(width.intValue(), height.intValue()); + if (width.intValue() == -1 || height.intValue() == -1) { + primarySize = editPart.getFigure().getSize().getCopy(); + } + } + + // 3-Remove the parent absolute location + if (parentLayoutData != null) { + final Point parentAbsoluteLocation = LayoutDataHelper.INSTANCE.getAbsoluteLocation(parentLayoutData); + relativeLocation.translate(-parentAbsoluteLocation.getX(), -parentAbsoluteLocation.getY()); + } + + result.setHeight(primarySize.height); + result.setWidth(primarySize.width); + final Point location = LayoutdataFactory.eINSTANCE.createPoint(); + location.setX(relativeLocation.x); + location.setY(relativeLocation.y); + result.setLocation(location); + + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataHelper#createEdgeLayoutData(org.eclipse.gmf.runtime.notation.Edge) + */ + public EdgeLayoutData createEdgeLayoutData(final Edge gmfEdge, final ConnectionEditPart connectionEditPart) { + final EdgeLayoutData result = LayoutdataFactory.eINSTANCE.createEdgeLayoutData(); + final ConnectorStyle connectorStyle = (ConnectorStyle) gmfEdge.getStyle(NotationPackage.eINSTANCE.getConnectorStyle()); + if (connectorStyle != null) { + result.setRouting(connectorStyle.getRouting().getValue()); + result.setJumpLinkStatus(connectorStyle.getJumpLinkStatus().getValue()); + result.setJumpLinkType(connectorStyle.getJumpLinkType().getValue()); + result.setReverseJumpLink(connectorStyle.isJumpLinksReverse()); + result.setSmoothness(connectorStyle.getSmoothness().getValue()); + } + if (connectionEditPart != null) { + final PolylineConnectionEx polylineConnectionEx = (PolylineConnectionEx) connectionEditPart.getFigure(); + polylineConnectionEx.getConnectionRouter().route(polylineConnectionEx); + final org.eclipse.draw2d.geometry.Point originialSourceRefPoint = polylineConnectionEx.getSourceAnchor().getReferencePoint().getCopy(); + polylineConnectionEx.translateToRelative(originialSourceRefPoint); + result.setSourceRefPoint(createPoint(originialSourceRefPoint)); + + final org.eclipse.draw2d.geometry.Point originialTargetRefPoint = polylineConnectionEx.getTargetAnchor().getReferencePoint().getCopy(); + polylineConnectionEx.translateToRelative(originialTargetRefPoint); + result.setTargetRefPoint(createPoint(originialTargetRefPoint)); + + initPointList(result.getPointList(), polylineConnectionEx.getPoints().getCopy()); + } + if (gmfEdge.getSourceAnchor() instanceof IdentityAnchor) { + result.setSourceTerminal(((IdentityAnchor) gmfEdge.getSourceAnchor()).getId()); + } + if (gmfEdge.getTargetAnchor() instanceof IdentityAnchor) { + result.setTargetTerminal(((IdentityAnchor) gmfEdge.getTargetAnchor()).getId()); + } + + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataHelper#createLabelLayoutData(org.eclipse.gmf.runtime.notation.Node) + */ + public NodeLayoutData createLabelLayoutData(final Node labelNode) { + final NodeLayoutData result = LayoutdataFactory.eINSTANCE.createNodeLayoutData(); + + if (labelNode.getLayoutConstraint() instanceof Size) { + final Integer width = (Integer) ViewUtil.getStructuralFeatureValue(labelNode, NotationPackage.eINSTANCE.getSize_Width()); + final Integer height = (Integer) ViewUtil.getStructuralFeatureValue(labelNode, NotationPackage.eINSTANCE.getSize_Height()); + + result.setWidth(width); + result.setHeight(height); + } + + final Integer x = (Integer) ViewUtil.getStructuralFeatureValue(labelNode, NotationPackage.eINSTANCE.getLocation_X()); + final Integer y = (Integer) ViewUtil.getStructuralFeatureValue(labelNode, NotationPackage.eINSTANCE.getLocation_Y()); + + final Point location = LayoutdataFactory.eINSTANCE.createPoint(); + location.setX(x); + location.setY(y); + + result.setLocation(location); + + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataHelper#getAbsoluteLocation(org.eclipse.sirius.diagram.layoutdata.NodeLayoutData) + */ + public Point getAbsoluteLocation(final NodeLayoutData nodeLayoutData) { + Point result = getCopy(nodeLayoutData.getLocation()); + if (nodeLayoutData.eContainer() instanceof NodeLayoutData) { + result = getTranslated(result, getAbsoluteLocation((NodeLayoutData) nodeLayoutData.eContainer())); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataHelper#getRelativeLocation(org.eclipse.sirius.diagram.layoutdata.NodeLayoutData, + * org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public Point getRelativeLocation(final NodeLayoutData layoutData, final IGraphicalEditPart editPart) { + final Point result = getAbsoluteLocation(layoutData); + final org.eclipse.draw2d.geometry.Point p = new org.eclipse.draw2d.geometry.Point(result.getX(), result.getY()); + FigureUtilities.translateToRelativeByIgnoringScrollbar(editPart.getFigure(), p); + result.setX(p.x); + result.setY(p.y); + + return result; + } + + /** + * Create a new point from a draw2d point. + * + * @param draw2dPoint + * The original point + * @return A new point + */ + private Point createPoint(final org.eclipse.draw2d.geometry.Point draw2dPoint) { + final Point newPoint = LayoutdataFactory.eINSTANCE.createPoint(); + newPoint.setX(draw2dPoint.x); + newPoint.setY(draw2dPoint.y); + return newPoint; + } + + /** + * Initialize a list of {@link Point} from a {@link PointList}. + * + * @param pointsList + * the list to initialize + * @param draw2dPointsList + * the original points of the line + */ + private void initPointList(final EList<Point> pointList, final PointList draw2dPointsList) { + for (int i = 0; i < draw2dPointsList.size(); i++) { + pointList.add(createPoint(draw2dPointsList.getPoint(i))); + } + + } + + /** + * Return a copy of this Point. + * + * @param point + * the point to copy + * @return a copy of this Point + */ + private Point getCopy(final Point point) { + final Point copy = LayoutdataFactory.eINSTANCE.createPoint(); + copy.setX(point.getX()); + copy.setY(point.getY()); + return copy; + } + + /** + * Creates a new Point which is translated by the values of the provided + * Point. + * + * @param originalPoint + * The point to translate. + * @param pt + * Point which provides the translation amounts. + * @return A new Point + */ + protected Point getTranslated(final Point originalPoint, final Point pt) { + final Point translatedPoint = LayoutdataFactory.eINSTANCE.createPoint(); + translatedPoint.setX(originalPoint.getX() + pt.getX()); + translatedPoint.setY(originalPoint.getY() + pt.getY()); + return translatedPoint; + } + + /** + * Creates a new Point which is translated by the values of the provided + * Point. + * + * @param originalPoint + * The point to translate. + * @param pt + * Point which provides the translation amounts. + * @return A new Point + */ + public Point getTranslated(final Point originalPoint, final org.eclipse.draw2d.geometry.Point pt) { + final Point translatedPoint = LayoutdataFactory.eINSTANCE.createPoint(); + translatedPoint.setX(originalPoint.getX() + pt.x); + translatedPoint.setY(originalPoint.getY() + pt.y); + return translatedPoint; + } + + /** + * {@inheritDoc} + */ + public Map<? extends LayoutDataKey, ? extends AbstractLayoutData> getRootLayoutData(final Map<? extends LayoutDataKey, ? extends AbstractLayoutData> collection) { + return Maps.filterValues(collection, ROOT_PREDICATE); + } + + /** + * {@inheritDoc} + */ + public LayoutDataKey createKey(final AbstractLayoutData layoutData) { + LayoutDataKey result; + if (layoutData instanceof NodeLayoutData) { + result = new SemanticNodeLayoutDataKey(layoutData.getId()); + } else if (layoutData instanceof EdgeLayoutData) { + result = new SemanticEdgeLayoutDataKey(layoutData.getId()); + } else { + throw new IllegalArgumentException("Layoutdata of type " + layoutData.getClass() + " is unknown"); + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/LayoutUtil.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/LayoutUtil.java new file mode 100644 index 0000000000..90d112c6f5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/LayoutUtil.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.gmf.runtime.diagram.ui.actions.ActionIds; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.requests.ArrangeRequest; + +/** + * Some utility methods for layout. + * + * @author mchauvin + */ +public final class LayoutUtil { + + /** + * Avoid instantiation. + */ + private LayoutUtil() { + + } + + /** + * Launch an arrange all on the diagram. + * + * @param diagramEditPart + * diagramEditPart; + */ + public static void arrange(final DiagramEditPart diagramEditPart) { + final ArrangeRequest arrangeRequest = new ArrangeRequest(ActionIds.ACTION_ARRANGE_ALL); + final List<Object> partsToArrange = new ArrayList<Object>(1); + partsToArrange.add(diagramEditPart); + arrangeRequest.setPartsToArrange(partsToArrange); + diagramEditPart.performRequest(arrangeRequest); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/NodeLayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/NodeLayoutDataKey.java new file mode 100644 index 0000000000..9e5d9bd424 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/NodeLayoutDataKey.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey; + +/** + * Interface for all kind of key use to store + * {@link org.eclipse.sirius.diagram.layoutdata.NodeLayoutData}. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ +public interface NodeLayoutDataKey extends LayoutDataKey { + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/PinnedElementsHandler.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/PinnedElementsHandler.java new file mode 100644 index 0000000000..5315419469 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/PinnedElementsHandler.java @@ -0,0 +1,910 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout; + +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.EAST; +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.NORTH; +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.NORTH_EAST; +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.NORTH_WEST; +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.SOUTH; +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.SOUTH_EAST; +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.SOUTH_WEST; +import static org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction.WEST; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.SortedSet; + +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.decorator.IDecoratorTarget.Direction; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; + +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; + +/** + * Fixes the layout of a particular level as done by a base layout provider to + * take pinned elements into account. + * <p> + * The general algorithm is: + * <ol> + * <li>move all the pinned elements back to their original positions;</li> + * <li>pack the elements by removing horizontal and vertical gaps the first step + * may have created;</li> + * <li>resolve potential overlaps between pinned elements and unpinned elements + * by moving unpinned elements aside.</li> + * </ol> + * + * @author pcdavid + */ +public class PinnedElementsHandler { + /** + * A flag to enable additional but potentially costly asserts and/or debug + * messages. + */ + private static final boolean DEBUG = false; + + /** + * Constant to indicate that padding information should be included in + * bounds/bounding-box computation. + */ + private static final boolean INCLUDE_PADDING = true; + + /** + * Constant to indicate that padding information should be not included in + * bounds/bounding-box computation. + */ + private static final boolean EXCLUDE_PADDING = false; + + /** + * The gap width to leave in between parts in addition to their padding + * during the packing phase. If gaps are too small, there is not enough room + * left to route the edges between parts, which can cause them to take a + * longer route. Set to twice the default gap. + */ + private static final int MINIMAL_GAP_WIDTH = 60; + + /** + * A predicate to identify pinned/fixed edit-parts. + */ + private final Predicate<IGraphicalEditPart> isPinned; + + /** + * Compares points in left-to-right, top-to-bottom ("reading") order. + */ + private final Comparator<Point> pointComparator = new Comparator<Point>() { + public int compare(final Point p1, final Point p2) { + if (p1.y != p2.y) { + return p1.y - p2.y; + } else { + return p1.x - p2.x; + } + } + }; + + /** + * A comparator used to order edit parts from top-left to bottom-right. Used + * to produce a more regular and predictable result. + */ + private final Comparator<IGraphicalEditPart> positionComparator = new Comparator<IGraphicalEditPart>() { + public int compare(final IGraphicalEditPart igep1, final IGraphicalEditPart igep2) { + return pointComparator.compare(getCurrentPosition(igep1), getCurrentPosition(igep2)); + } + }; + + /** + * A comparator to sort edit-parts from left to right (taking their top-left + * point as reference). + */ + private final Comparator<IGraphicalEditPart> leftToRightComparator = new Comparator<IGraphicalEditPart>() { + public int compare(final IGraphicalEditPart p1, final IGraphicalEditPart p2) { + return getCurrentPosition(p1).x - getCurrentPosition(p2).x; + } + }; + + /** + * A comparator to sort edit-parts from top to bottom (taking their top-left + * point as reference). + */ + private final Comparator<IGraphicalEditPart> topToBottomComparator = new Comparator<IGraphicalEditPart>() { + public int compare(final IGraphicalEditPart p1, final IGraphicalEditPart p2) { + return getCurrentPosition(p1).y - getCurrentPosition(p2).y; + } + }; + + /** + * All the edit parts to consider for the layout, ordered by their position. + */ + private final SortedSet<IGraphicalEditPart> allEditParts = Sets.newTreeSet(positionComparator); + + /** + * All the pinned edit parts. A subset of <code>allEditParts</code>. + */ + private final SortedSet<IGraphicalEditPart> fixedEditParts = Sets.newTreeSet(positionComparator); + + /** + * The initial bounds of the edit parts, as computed by previous layout + * providers but not yet applied. + */ + private final Map<IGraphicalEditPart, Rectangle> initialBounds; + + /** + * The currently computed bounds of the edit parts. Stored separately so + * they can be easily reset. + */ + private final Map<IGraphicalEditPart, Rectangle> currentBounds = Maps.newHashMap(); + + /** + * Provides access to additional layout constraints and preferences + * specified in the VSM. + */ + private final DiagramLayoutCustomization layoutCustomization; + + /** + * IDiagramElementEditPart which are not actually pinned but have to stay + * fixed. + */ + private ArrayList<IDiagramElementEditPart> elementsToKeepFixed; + + /** + * Creates a new resolver. + * + * @param parts + * all the edit parts to consider. + * @param initialBounds + * the initial locations of the edit parts, as computed by + * previous layout providers but not yet applied. + * @param elementsToKeepFixed + * IDiagramElementEditPart which are not actually pinned but have + * to stay fixed + */ + public PinnedElementsHandler(final Collection<IGraphicalEditPart> parts, final Map<IGraphicalEditPart, Rectangle> initialBounds, ArrayList<IDiagramElementEditPart> elementsToKeepFixed) { + this.initialBounds = Collections.unmodifiableMap(getAllInitialPositions(parts, initialBounds)); + this.elementsToKeepFixed = elementsToKeepFixed; + this.allEditParts.addAll(parts); + this.isPinned = new IsPinnedPredicate(this.elementsToKeepFixed); + this.fixedEditParts.addAll(Collections2.filter(parts, isPinned)); + this.layoutCustomization = new DiagramLayoutCustomization(); + this.layoutCustomization.initializePaddingWithEditParts(Lists.newArrayList(parts)); + } + + private Map<IGraphicalEditPart, Rectangle> getAllInitialPositions(final Collection<IGraphicalEditPart> parts, final Map<IGraphicalEditPart, Rectangle> explicitBounds) { + final Map<IGraphicalEditPart, Rectangle> result = Maps.newHashMap(explicitBounds); + for (IGraphicalEditPart part : parts) { + if (!result.containsKey(part)) { + final Rectangle bounds = part.getFigure().getBounds().getCopy(); + result.put(part, bounds); + } + } + return result; + } + + /** + * Computes the new locations of all the parts to move to handle pinned + * elements properly. + * + * @return for each part which needs to move, the new position it should be + * moved to. + */ + public Map<IGraphicalEditPart, Point> computeSolution() { + resetPinnedElements(); + removeGaps(); + resolveOverlaps(); + return getSolution(); + } + + private boolean hasRemainingSolvableOverlaps() { + for (IGraphicalEditPart part : fixedEditParts) { + if (!Collections2.filter(findOverlappingParts(part), Predicates.not(isPinned)).isEmpty()) { + return true; + } + } + return false; + } + + /** + * Move all the pinned parts back to their original positions. We can not + * tell the original layout algorithm to simply ignore them (too many + * side-effects) or consider them as obstacles, so we must cleanup after it. + */ + private void resetPinnedElements() { + for (IGraphicalEditPart part : fixedEditParts) { + // Access currentBounds directly: #setCurrentPosition() refuses to + // move pinned parts. + currentBounds.put(part, part.getFigure().getBounds().getCopy()); + } + } + + /** + * Pack the layout to avoid vertical and horizontal gaps left by the pinned + * parts after they are moved back to their original positions. Only the + * gaps which take the whole height/width of the diagram and are wide enough + * are taken into account ("wide enough" meaning beyond the configurable + * padding associated to the diagram elements). + */ + private void removeGaps() { + if (fixedEditParts.isEmpty()) { + /* + * The packing algorithms are potentially costly if there are many + * elements. If there are no pinned elements, the basic layout + * algorithms should not have created any substantial gaps, so don't + * try to find gaps which very probably do not exist. + */ + return; + } + packHorizontally(); + packVertically(); + } + + /** + * Finds all the packable vertical gaps with no edit-parts and moves the + * parts beyond them to the left to reduce the gap to the minimum while + * still respecting the parts' padding. + */ + private void packHorizontally() { + final int[] hRange = getHorizontalRange(allEditParts, EXCLUDE_PADDING); + final List<IGraphicalEditPart> movableParts = Lists.newArrayList(Collections2.filter(allEditParts, Predicates.not(isPinned))); + Collections.sort(movableParts, leftToRightComparator); + for (int i = 0; i < movableParts.size(); i++) { + /* + * Split the movable parts into two groups (left and right) on index + * i. If there is a sufficiently large gap between the two groups, + * move the elements of the right group to the left. + */ + final Set<IGraphicalEditPart> leftSide = Sets.newHashSet(movableParts.subList(0, i)); + final Rectangle leftBox; + final Insets leftPadding; + if (i == 0) { + // Artificial box corresponding to the left margin of the figure + // along which to pack. + leftBox = new Rectangle(hRange[0] - 1, 0, 1, 1); + leftPadding = new Insets(0, 0, 0, 0); + } else { + leftBox = getBoundingBox(leftSide, EXCLUDE_PADDING); + leftPadding = getPadding(leftSide); + } + + final Set<IGraphicalEditPart> rightSide = Sets.newHashSet(movableParts.subList(i, movableParts.size())); + final Rectangle rightBox = getBoundingBox(rightSide, EXCLUDE_PADDING); + final Insets rightPadding = getPadding(rightSide); + + final int currentGapWidth = rightBox.getLeft().x - leftBox.getRight().x; + final int minGapWidth = Math.max(MINIMAL_GAP_WIDTH, Math.max(leftPadding.right, rightPadding.left)); + if (i == 0 && isHorizontalOriginFree(allEditParts, hRange[0])) { + // We can move the rightSide elements to the free space + translate(rightSide, new Dimension(-currentGapWidth, 0)); + } else if (currentGapWidth > minGapWidth) { + translate(rightSide, new Dimension(-(currentGapWidth - minGapWidth), 0)); + } + } + } + + /** + * Check if the some parts of the diagram are already to the x origin + * position. + * + * @param parts + * List of parts to check + * @param xOrigin + * The x coordinate to consider as the origin + * @return true if the x origin is free + */ + private boolean isHorizontalOriginFree(SortedSet<IGraphicalEditPart> parts, int xOrigin) { + boolean result = true; + for (IGraphicalEditPart part : parts) { + final Rectangle bounds = getCurrentBounds(part, false); + result = result && bounds.x != xOrigin; + } + return result; + } + + /** + * Check if the some parts of the diagram are already to the y origin + * position. + * + * @param parts + * List of parts to check + * @param yOrigin + * The y coordinate to consider as the origin + * @return true if the x origin is free + */ + private boolean isVerticalOriginFree(SortedSet<IGraphicalEditPart> parts, int yOrigin) { + boolean result = true; + for (IGraphicalEditPart part : parts) { + final Rectangle bounds = getCurrentBounds(part, false); + result = result && bounds.y != yOrigin; + } + return result; + } + + /** + * Compute the horizontal position range for the given <code>parts</code>. + * + * @param parts + * The list of parts to consider + * @param includePadding + * true if we must include padding, false otherwise + * @return the horizontal range + */ + private int[] getHorizontalRange(final Collection<IGraphicalEditPart> parts, final boolean includePadding) { + int minPadding = getSmallestHorizontalMargin(parts); + int min = minPadding; + int max = Integer.MIN_VALUE; + for (IGraphicalEditPart part : parts) { + final Rectangle bounds = getCurrentBounds(part, includePadding); + min = Math.min(min, bounds.getLeft().x); + max = Math.max(max, bounds.getRight().x); + } + // The minimum can not be less than the minPadding + min = Math.max(min, minPadding); + return new int[] { min, max }; + } + + /** + * Get the smallest margin to the left of the container for the given + * <code>parts</code>. + * + * @param parts + * The list of parts to consider + * @return the smallest margin to the left of the container + */ + private int getSmallestHorizontalMargin(final Collection<IGraphicalEditPart> parts) { + int min = Integer.MAX_VALUE; + for (IGraphicalEditPart part : parts) { + if (part.getParent() instanceof DiagramEditPart) { + // We don't consider padding in diagram + min = 0; + } else { + min = Math.min(min, layoutCustomization.getNodePadding(part).left); + } + } + return min; + } + + /** + * Finds all the packable horizontal gaps with no edit-parts and moves the + * parts beyond them to the top to reduce the gap to the minimum while still + * respecting the parts' padding. + */ + private void packVertically() { + final int[] vRange = getVerticalRange(allEditParts, EXCLUDE_PADDING); + final List<IGraphicalEditPart> movableParts = Lists.newArrayList(Collections2.filter(allEditParts, Predicates.not(isPinned))); + Collections.sort(movableParts, topToBottomComparator); + for (int i = 0; i < movableParts.size(); i++) { + /* + * Split the movable parts into two groups (top and bottom) on index + * i. If there is a sufficiently large gap between the two groups, + * move the elements of the bottom group to the top. + */ + final Set<IGraphicalEditPart> topSide = Sets.newHashSet(movableParts.subList(0, i)); + final Rectangle topBox; + final Insets topPadding; + if (i == 0) { + // Artificial box corresponding to the top margin of the figure + // along which to pack. + topBox = new Rectangle(0, vRange[0] - 1, 1, 1); + topPadding = new Insets(0, 0, 0, 0); + } else { + topBox = getBoundingBox(topSide, EXCLUDE_PADDING); + topPadding = getPadding(topSide); + } + + final Set<IGraphicalEditPart> bottomSide = Sets.newHashSet(movableParts.subList(i, movableParts.size())); + final Rectangle bottomBox = getBoundingBox(bottomSide, EXCLUDE_PADDING); + final Insets bottomPadding = getPadding(bottomSide); + + final int currentGapWidth = bottomBox.getTop().y - topBox.getBottom().y; + final int minGapWidth = Math.max(MINIMAL_GAP_WIDTH, Math.max(topPadding.bottom, bottomPadding.top)); + if (i == 0 && isVerticalOriginFree(allEditParts, vRange[0])) { + // We can move the bottomSide elements to the free space + translate(bottomSide, new Dimension(0, -currentGapWidth)); + } else if (currentGapWidth > minGapWidth) { + translate(bottomSide, new Dimension(0, -(currentGapWidth - minGapWidth))); + } + } + } + + /** + * Compute the vertical position range for the given <code>parts</code>. + * + * @param parts + * The list of parts to consider + * @param includePadding + * true if we must include padding, false otherwise + * @return the vertical range + */ + private int[] getVerticalRange(final Collection<IGraphicalEditPart> parts, final boolean includePadding) { + int minPadding = getSmallestVerticalMargin(parts); + int min = minPadding; + int max = Integer.MIN_VALUE; + for (IGraphicalEditPart part : parts) { + final Rectangle bounds = getCurrentBounds(part, includePadding); + min = Math.min(min, bounds.getTop().y); + max = Math.max(max, bounds.getBottom().y); + } + // The minimum can not be less than the minPadding + min = Math.max(min, minPadding); + return new int[] { min, max }; + } + + /** + * Get the smallest margin to the top of the container for the given + * <code>parts</code>. + * + * @param parts + * The list of parts to consider + * @return the smallest margin to the top of the container + */ + private int getSmallestVerticalMargin(final Collection<IGraphicalEditPart> parts) { + int min = Integer.MAX_VALUE; + for (IGraphicalEditPart part : parts) { + if (part.getParent() instanceof DiagramEditPart) { + // We don't consider padding in diagram + min = 0; + } else { + min = Math.min(min, layoutCustomization.getNodePadding(part).top); + } + } + return min; + } + + /** + * Resolve all the overlaps between pinned parts and movable parts. + */ + private void resolveOverlaps() { + for (IGraphicalEditPart part : fixedEditParts) { + resolveOverlaps(part); + } + assert !hasRemainingSolvableOverlaps() : "solvable but unsolved overlaps remain"; + } + + /** + * Resolve all the overlaps concerning the given fixed edit part. This + * method may also resolve overlaps concerning other fixed parts in the + * process, but will at least resolve all the (solvable) ones concerning the + * specified part. After successful execution of this method, the only parts + * overlapping <code>fixedPart</code>, if any, are also fixed parts, and + * thus are unsolvable overlaps. + * + * @param fixedPart + * the fixed edit part to consider. + */ + private void resolveOverlaps(final IGraphicalEditPart fixedPart) { + final Set<IGraphicalEditPart> solvableOverlaps = Sets.filter(findOverlappingParts(fixedPart), Predicates.not(isPinned)); + final Map<Direction, SortedSet<IGraphicalEditPart>> groupedOverlaps = groupByDirection(fixedPart, solvableOverlaps); + for (Entry<Direction, SortedSet<IGraphicalEditPart>> group : groupedOverlaps.entrySet()) { + // For a same group, we kept the movedPositions to allow a complete + // rollback to move again several parts in same time + Map<IGraphicalEditPart, Point> previousMovedPositionsBefore = Maps.newHashMap(); + for (IGraphicalEditPart part : group.getValue()) { + assert overlaps(fixedPart, part); + previousMovedPositionsBefore = moveAside(Collections.singleton(part), Collections.singleton(fixedPart), group.getKey(), previousMovedPositionsBefore); + assert !overlaps(fixedPart, part); + } + } + assert Collections2.filter(findOverlappingParts(fixedPart), Predicates.not(isPinned)).isEmpty() : "solvable but unsolved overlaps remain"; + } + + /** + * Move the specified movable parts in the specified direction enough to + * avoid overlaps with all the specified fixed parts while not creating any + * new overlap with other fixed parts. All the movable parts are translated + * of the same amount, as a group. More movable parts than the ones + * specified explicitly may be move along as they are "pushed" aside to make + * enough room. + * + * @param parts + * the parts to move. + * @param fixedParts + * the fixed parts to avoid. + * @param dir + * the general direction in which to move the movable parts. + * @param previousMovedPositionsOfSameDir + * the list of original position of each edit parts that have + * previously moved in this direction + * @return The positions done during this step (and previous steps) to + * eventually used it to restore the previous position. + */ + private Map<IGraphicalEditPart, Point> moveAside(final Set<IGraphicalEditPart> parts, final Set<IGraphicalEditPart> fixedParts, final Direction dir, + Map<IGraphicalEditPart, Point> previousMovedPositionsOfSameDir) { + /* + * First try to move just enough to avoid the explicitly specified + * obstacles. + */ + addSavePositions(parts, previousMovedPositionsOfSameDir); + tryMove(parts, fixedParts, dir); + final Set<IGraphicalEditPart> overlaps = findOverlappingParts(parts); + if (!overlaps.isEmpty()) { + /* + * We created new overlaps. Try a more aggressive change, taking + * more parts into consideration and/or moving further. + */ + Set<IGraphicalEditPart> newMovables = parts; + Set<IGraphicalEditPart> newFixed = fixedParts; + + final Set<IGraphicalEditPart> movableOverlaps = Sets.newHashSet(Collections2.filter(overlaps, Predicates.not(isPinned))); + if (!movableOverlaps.isEmpty()) { + /* + * If we created new overlaps with movable parts, simply re-try + * with an extended set of movable parts including the ones we + * need to push along. + */ + newMovables = Sets.union(parts, movableOverlaps); + } + + final Set<IGraphicalEditPart> fixedOverlaps = Sets.newHashSet(Collections2.filter(overlaps, isPinned)); + if (!fixedOverlaps.isEmpty()) { + /* + * If we created new overlaps with other fixed parts, re-try + * with an extended set of fixed obstacles to avoid. + */ + newFixed = Sets.union(fixedParts, fixedOverlaps); + } + + /* + * Retry with the new, extended sets of parts to consider. + */ + assert newMovables.size() > parts.size() || newFixed.size() > fixedParts.size(); + moveParts(newMovables, previousMovedPositionsOfSameDir); + moveAside(newMovables, newFixed, dir, previousMovedPositionsOfSameDir); + } + /* + * Check that the specified movable parts no longer overlap with the + * specified fixed parts. + */ + assert Sets.intersection(Sets.filter(findOverlappingParts(fixedParts), Predicates.not(isPinned)), parts).isEmpty(); + return previousMovedPositionsOfSameDir; + } + + private Map<IGraphicalEditPart, Point> addSavePositions(final Set<IGraphicalEditPart> parts, Map<IGraphicalEditPart, Point> positionsBefore) { + for (IGraphicalEditPart part : parts) { + positionsBefore.put(part, getCurrentPosition(part)); + } + return positionsBefore; + } + + /** + * Translate all the given <code>parts</code> of the same amount in the + * specified <code>direction</code> as far as required to avoid overlaps + * with the specified <code>fixedParts</code>. The move may create new + * overlaps with parts other than those in <code>fixedParts</code>. + */ + private void tryMove(final Set<IGraphicalEditPart> parts, final Set<IGraphicalEditPart> fixedParts, final Direction direction) { + assert !Sets.intersection(Sets.filter(findOverlappingParts(fixedParts), Predicates.not(isPinned)), parts).isEmpty(); + final Rectangle movablesBox = getBoundingBox(parts, EXCLUDE_PADDING); + final Insets movablesPadding = getPadding(parts); + final Rectangle fixedBox = getBoundingBox(fixedParts, EXCLUDE_PADDING); + final Insets fixedPadding = getPadding(fixedParts); + final Dimension move = computeMoveVector(movablesBox, movablesPadding, fixedBox, fixedPadding, direction); + for (IGraphicalEditPart part : parts) { + translate(part, move); + } + assert Sets.intersection(Sets.filter(findOverlappingParts(fixedParts), Predicates.not(isPinned)), parts).isEmpty(); + } + + /** + * Computes the global padding of a set of edit parts. + */ + private Insets getPadding(final Set<IGraphicalEditPart> parts) { + final Rectangle smallBox = getBoundingBox(parts, EXCLUDE_PADDING); + final Rectangle bigBox = getBoundingBox(parts, INCLUDE_PADDING); + final int top = verticalDistance(bigBox.getTop(), smallBox.getTop()); + final int left = horizontalDistance(bigBox.getLeft(), smallBox.getLeft()); + final int bottom = verticalDistance(bigBox.getBottom(), smallBox.getBottom()); + final int right = horizontalDistance(bigBox.getRight(), smallBox.getRight()); + return new Insets(top, left, bottom, right); + } + + private void translate(final Set<IGraphicalEditPart> parts, final Dimension move) { + for (IGraphicalEditPart part : parts) { + translate(part, move); + } + } + + private void translate(final IGraphicalEditPart part, final Dimension move) { + setCurrentPosition(part, getCurrentPosition(part).getTranslated(move)); + } + + /** + * Computes a move vector suitable to translate <code>movable</code> in the + * specified <code>direction</code> until it no longer overlaps with + * <code>fixed</code>. Assumes the two rectangles specified overlap when + * taking their padding into account. + */ + private Dimension computeMoveVector(final Rectangle movable, final Insets movablePadding, final Rectangle fixed, final Insets fixedPadding, final Direction direction) { + final Dimension move; + if (direction == NORTH) { + move = computeNorthMoveVector(movable, movablePadding, fixed, fixedPadding); + } else if (direction == SOUTH) { + move = computeSouthMoveVector(movable, movablePadding, fixed, fixedPadding); + } else if (direction == EAST) { + move = computeEastMoveVector(movable, movablePadding, fixed, fixedPadding); + } else if (direction == WEST) { + move = computeWestMoveVector(movable, movablePadding, fixed, fixedPadding); + } else if (direction == NORTH_EAST) { + move = computeMoveVector(movable, movablePadding, fixed, fixedPadding, NORTH).expand(computeMoveVector(movable, movablePadding, fixed, fixedPadding, EAST)); + } else if (direction == NORTH_WEST) { + move = computeMoveVector(movable, movablePadding, fixed, fixedPadding, NORTH).expand(computeMoveVector(movable, movablePadding, fixed, fixedPadding, WEST)); + } else if (direction == SOUTH_EAST) { + move = computeMoveVector(movable, movablePadding, fixed, fixedPadding, SOUTH).expand(computeMoveVector(movable, movablePadding, fixed, fixedPadding, EAST)); + } else if (direction == SOUTH_WEST) { + move = computeMoveVector(movable, movablePadding, fixed, fixedPadding, SOUTH).expand(computeMoveVector(movable, movablePadding, fixed, fixedPadding, WEST)); + } else { + move = null; + assert false : "Unknown direction"; + } + return move; + } + + private Dimension computeWestMoveVector(final Rectangle movable, final Insets movablePadding, final Rectangle fixed, final Insets fixedPadding) { + final Dimension move; + if (movable.intersects(fixed)) { + final int padding = Math.max(movablePadding.right, fixedPadding.left); + move = new Dimension(-(padding + horizontalDistance(fixed.getLeft(), movable.getRight())), 0); + } else { + final int dx1 = horizontalDistance(fixed.getExpanded(fixedPadding).getLeft(), movable.getRight()); + final int dx2 = horizontalDistance(fixed.getLeft(), movable.getExpanded(movablePadding).getRight()); + move = new Dimension(-Math.max(dx1, dx2), 0); + } + return move; + } + + private Dimension computeEastMoveVector(final Rectangle movable, final Insets movablePadding, final Rectangle fixed, final Insets fixedPadding) { + final Dimension move; + if (movable.intersects(fixed)) { + final int padding = Math.max(movablePadding.left, fixedPadding.right); + move = new Dimension(padding + horizontalDistance(fixed.getRight(), movable.getLeft()), 0); + } else { + final int dx1 = horizontalDistance(fixed.getExpanded(fixedPadding).getRight(), movable.getLeft()); + final int dx2 = horizontalDistance(fixed.getRight(), movable.getExpanded(movablePadding).getLeft()); + move = new Dimension(Math.max(dx1, dx2), 0); + } + return move; + } + + private Dimension computeSouthMoveVector(final Rectangle movable, final Insets movablePadding, final Rectangle fixed, final Insets fixedPadding) { + final Dimension move; + if (movable.intersects(fixed)) { + final int padding = Math.max(movablePadding.top, fixedPadding.bottom); + move = new Dimension(0, padding + verticalDistance(fixed.getBottom(), movable.getTop())); + } else { + final int dy1 = verticalDistance(fixed.getExpanded(fixedPadding).getBottom(), movable.getTop()); + final int dy2 = verticalDistance(fixed.getBottom(), movable.getExpanded(movablePadding).getTop()); + move = new Dimension(0, Math.max(dy1, dy2)); + } + return move; + } + + private Dimension computeNorthMoveVector(final Rectangle movable, final Insets movablePadding, final Rectangle fixed, final Insets fixedPadding) { + final Dimension move; + if (movable.intersects(fixed)) { + final int padding = Math.max(movablePadding.bottom, fixedPadding.top); + move = new Dimension(0, -(padding + verticalDistance(fixed.getTop(), movable.getBottom()))); + } else { + final int dy1 = verticalDistance(fixed.getExpanded(fixedPadding).getTop(), movable.getBottom()); + final int dy2 = verticalDistance(fixed.getTop(), movable.getExpanded(movablePadding).getBottom()); + move = new Dimension(0, -Math.max(dy1, dy2)); + } + return move; + } + + private int verticalDistance(final Point p1, final Point p2) { + return Math.abs(p1.y - p2.y); + } + + private int horizontalDistance(final Point p1, final Point p2) { + return Math.abs(p1.x - p2.x); + } + + private Map<Direction, SortedSet<IGraphicalEditPart>> groupByDirection(final IGraphicalEditPart origin, final Set<IGraphicalEditPart> parts) { + final Map<Direction, SortedSet<IGraphicalEditPart>> result = Maps.newHashMap(); + for (IGraphicalEditPart part : parts) { + final Direction dir = getDirection(origin, part); + if (!result.containsKey(dir)) { + result.put(dir, Sets.newTreeSet(positionComparator)); + } + result.get(dir).add(part); + } + return result; + } + + private Direction getDirection(final IGraphicalEditPart sourcePart, final IGraphicalEditPart destPart) { + final Point source = getCurrentBounds(sourcePart, EXCLUDE_PADDING).getCenter(); + final Point dest = getCurrentBounds(destPart, EXCLUDE_PADDING).getCenter(); + final int dx = dest.x - source.x; + final int dy = dest.y - source.y; + final Direction result; + if (dx < 0) { + if (dy < 0) { + result = NORTH_WEST; + } else if (dy == 0) { + result = WEST; + } else { + result = SOUTH_WEST; + } + } else if (dx > 0) { + if (dy < 0) { + result = NORTH_EAST; + } else if (dy == 0) { + result = EAST; + } else { + result = SOUTH_EAST; + } + } else { + if (dy < 0) { + result = NORTH; + } else if (dy == 0) { + // Default to SOUTH if the parts have the same center + result = EAST; + } else { + result = SOUTH; + } + } + return result; + } + + /** + * Returns all the parts which overlap with any of the specified parts. Does + * not consider internal overlap between the specified elements themselves. + */ + private Set<IGraphicalEditPart> findOverlappingParts(final Set<IGraphicalEditPart> parts) { + final Set<IGraphicalEditPart> result = Sets.newHashSet(); + for (IGraphicalEditPart part : parts) { + result.addAll(findOverlappingParts(part)); + } + result.removeAll(parts); + return result; + } + + private Set<IGraphicalEditPart> findOverlappingParts(final IGraphicalEditPart part) { + final Set<IGraphicalEditPart> result = Sets.newHashSet(); + for (IGraphicalEditPart candidate : allEditParts) { + if (overlaps(candidate, part)) { + result.add(candidate); + } + } + return result; + } + + private boolean overlaps(final IGraphicalEditPart part1, final IGraphicalEditPart part2) { + if (part1 == part2) { + return false; + } else { + return getCurrentBounds(part1, EXCLUDE_PADDING).intersects(getCurrentBounds(part2, INCLUDE_PADDING)) + || getCurrentBounds(part1, INCLUDE_PADDING).intersects(getCurrentBounds(part2, EXCLUDE_PADDING)); + } + } + + private Rectangle getBoundingBox(final Set<IGraphicalEditPart> parts, final boolean includePadding) { + Rectangle box = null; + for (IGraphicalEditPart part : parts) { + if (box == null) { + box = getCurrentBounds(part, includePadding); + } else { + box = box.getUnion(getCurrentBounds(part, includePadding)); + } + } + return box; + } + + private Point getInitialPosition(final IGraphicalEditPart part) { + return getInitialBounds(part).getTopLeft(); + } + + private Rectangle getInitialBounds(final IGraphicalEditPart part) { + return initialBounds.get(part); + } + + private Map<IGraphicalEditPart, Point> getSolution() { + final Map<IGraphicalEditPart, Point> result = Maps.newHashMap(); + for (IGraphicalEditPart part : currentBounds.keySet()) { + result.put(part, currentBounds.get(part).getTopLeft()); + } + return result; + } + + private Point getCurrentPosition(final IGraphicalEditPart part) { + return getCurrentBounds(part, EXCLUDE_PADDING).getTopLeft(); + } + + private Rectangle getCurrentBounds(final IGraphicalEditPart part, final boolean includePadding) { + final Rectangle bounds; + if (currentBounds.containsKey(part)) { + bounds = currentBounds.get(part); + } else { + bounds = getInitialBounds(part); + } + if (includePadding == INCLUDE_PADDING) { + final Insets padding = layoutCustomization.getNodePadding(part); + return bounds.getExpanded(padding); + } else { + return bounds; + } + } + + private void setCurrentPosition(final IGraphicalEditPart part, final Point position) { + Preconditions.checkArgument(!isPinned.apply(part), "Pinned elements can not move"); + if (position.equals(getInitialPosition(part))) { + currentBounds.remove(part); + } else { + final Rectangle oldBounds = getCurrentBounds(part, EXCLUDE_PADDING); + final Rectangle newBounds = new Rectangle(position.x, position.y, oldBounds.width, oldBounds.height); + currentBounds.put(part, newBounds); + } + } + + /** + * Move all the specified parts to the new positions specified in the map. + */ + private void moveParts(Set<IGraphicalEditPart> partToReset, final Map<IGraphicalEditPart, Point> positions) { + for (IGraphicalEditPart editPart : partToReset) { + // If the editPart has not been moved in the same direction, its + // initial position can not be found. + if (positions.get(editPart) != null) { + setCurrentPosition(editPart, positions.get(editPart)); + positions.remove(editPart); + } + } + } + + @SuppressWarnings("unused") + private void printInitialState() { + debugMessage("==============================================================================="); + debugMessage("Initial state (before #resolveOverlaps()"); + debugMessage("----------------------------------------"); + for (IGraphicalEditPart part : allEditParts) { + debugMessage("- " + part.getClass().getSimpleName() + " (semantic: " + part.resolveSemanticElement() + ")"); + debugMessage(" Pinned: " + isPinned.apply(part)); + debugMessage(" Intrinsic bounds (main figure): " + part.getFigure().getBounds()); + debugMessage(" Initial bounds (after previous layout pass): " + getInitialBounds(part)); + } + debugMessage(""); + } + + @SuppressWarnings("unused") + private void printResolvedState() { + debugMessage("Solution (only moved elements)"); + debugMessage("------------------------------"); + for (IGraphicalEditPart part : currentBounds.keySet()) { + debugMessage("- " + part.getClass().getSimpleName() + " (semantic: " + part.resolveSemanticElement() + ")"); + debugMessage(" Pinned: " + isPinned.apply(part)); + debugMessage(" Intrinsic bounds (main figure): " + part.getFigure().getBounds()); + debugMessage(" Initial bounds (after previous layout pass): " + getInitialBounds(part)); + debugMessage(" Computed bounds (after resolution): " + getCurrentPosition(part)); + } + debugMessage(""); + } + + private void debugMessage(final String msg) { + if (DEBUG) { + // CHECKSTYLE:OFF + System.out.println("DEBUG: " + msg); + // CHECKSTYLE:ON + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerDescriptor.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerDescriptor.java new file mode 100644 index 0000000000..49d2084442 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerDescriptor.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.data.extension; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; + +import org.eclipse.sirius.common.ui.SiriusTransPlugin; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ILayoutDataManagerProvider; + +/** + * Describes a extension as contributed to the + * {@link LayoutDataManagerRegistryListener#LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION_POINT} + * extension point. + * + * @author mporhel + * + */ +public class LayoutDataManagerDescriptor { + + /** + * Name of the attribute corresponding to the contributed class's path. + */ + public static final String LAYOUT_DATA_MANAGER_PROVIDER_CLASS_NAME = "class"; + + /** + * Name of the attribute corresponding to the contributed id. + */ + public static final String LAYOUT_DATA_MANAGER_PROVIDER_ID = "icon"; + + /** + * Configuration element of this descriptor . + */ + private final IConfigurationElement element; + + /** + * The path of the contributed class. + */ + private String extensionClassName; + + /** + * The {@link ILayoutDataManagerProvider} described by this descriptor. + */ + private ILayoutDataManagerProvider extension; + + private String id; + + /** + * Instantiates a descriptor with all information. + * + * @param configuration + * Configuration element from which to create this descriptor. + */ + public LayoutDataManagerDescriptor(IConfigurationElement configuration) { + element = configuration; + extensionClassName = configuration.getAttribute(LAYOUT_DATA_MANAGER_PROVIDER_CLASS_NAME); + } + + /** + * Creates an instance of this descriptor's + * {@link ILayoutDataManagerProvider} . + * + * @return A new instance of this descriptor's + * {@link ILayoutDataManagerProvider}. + */ + public ILayoutDataManagerProvider getLayoutDataManagerProvider() { + if (extension == null) { + try { + extension = (ILayoutDataManagerProvider) element.createExecutableExtension(LAYOUT_DATA_MANAGER_PROVIDER_CLASS_NAME); + } catch (CoreException e) { + SiriusTransPlugin.INSTANCE.error(e.getMessage(), e); + } + } + return extension; + } + + /** + * Return the id of the current tab extension. + * + * @return the id of the current tab extension. + */ + public String getId() { + if (id == null) { + id = element.getAttribute(LAYOUT_DATA_MANAGER_PROVIDER_ID); + } + return id; + } + + /** + * Returns the fully qualified name of the contributed class. + * + * @return the fully qualified name of the contributed class + */ + public String getExtensionClassName() { + return extensionClassName; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerRegistry.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerRegistry.java new file mode 100644 index 0000000000..1d2495f380 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerRegistry.java @@ -0,0 +1,161 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.data.extension; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ILayoutDataManagerProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager; +import org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManagerForSemanticElementsFactory; + +/** + * Registry containing all layout data manager providers that have been parsed + * from the + * {@link LayoutDataManagerRegistryListener#LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION_POINT} + * extension point. + * + * @author mporhel + * + */ +public final class LayoutDataManagerRegistry { + /** + * The registered {@link LayoutDataManagerDescriptor}s. + */ + private static final Map<LayoutDataManagerDescriptor, SiriusLayoutDataManager> EXTENSIONS = Maps.newLinkedHashMap(); + + /** + * Utility classes don't need a default constructor. + */ + private LayoutDataManagerRegistry() { + + } + + /** + * Adds an extension to the registry, with the given behavior. + * + * @param extension + * The extension that is to be added to the registry + */ + public static void addExtension(LayoutDataManagerDescriptor extension) { + EXTENSIONS.put(extension, null); + } + + /** + * Removes all extensions from the registry. This will be called at plugin + * stopping. + */ + public static void clearRegistry() { + EXTENSIONS.clear(); + } + + /** + * Returns a copy of the registered extensions list. + * + * @return A copy of the registered extensions list. + */ + public static Collection<LayoutDataManagerDescriptor> getRegisteredExtensions() { + return Lists.newArrayList(EXTENSIONS.keySet()); + } + + /** + * Removes a phantom from the registry. + * + * @param extensionClassName + * Qualified class name of the sync element which corresponding + * phantom is to be removed from the registry. + */ + public static void removeExtension(String extensionClassName) { + for (LayoutDataManagerDescriptor extension : getRegisteredExtensions()) { + if (extension.getExtensionClassName().equals(extensionClassName)) { + EXTENSIONS.remove(extension); + } + } + } + + /** + * Get the extension with the given id. + * + * @param id + * the requested id. + * + * @return the layout data manager descriptor with the requested id if it + * exists. + */ + public static LayoutDataManagerDescriptor getRegisteredExtension(String id) { + for (LayoutDataManagerDescriptor desc : EXTENSIONS.keySet()) { + if (!StringUtil.isEmpty(desc.getId()) && desc.getId().equals(id)) { + return desc; + } + } + return null; + } + + /** + * Get the {@link SiriusLayoutDataManager} found applicable for the given + * {@link DDiagram}. + * + * The default manager (based on semantic elements) will always be the last + * returned manager. + * + * @param diagram + * the diagram which needs a layout data ma,ager. + * + * @return a list of {@link SiriusLayoutDataManager} instances. + */ + public static List<SiriusLayoutDataManager> getSiriusLayoutDataManagers(DDiagram diagram) { + List<SiriusLayoutDataManager> applicableManagers = Lists.newArrayList(); + for (LayoutDataManagerDescriptor descriptor : getRegisteredExtensions()) { + ILayoutDataManagerProvider provider = descriptor.getLayoutDataManagerProvider(); + if (provider != null && provider.provides(diagram)) { + SiriusLayoutDataManager layoutDataManager = EXTENSIONS.get(descriptor); + if (layoutDataManager == null) { + layoutDataManager = provider.getLayoutDataManager(); + EXTENSIONS.put(descriptor, layoutDataManager); + } + applicableManagers.add(layoutDataManager); + } + } + applicableManagers.add(SiriusLayoutDataManagerForSemanticElementsFactory.getInstance().getSiriusLayoutDataManager()); + return applicableManagers; + } + + /** + * Get all known {@link SiriusLayoutDataManager} . + * + * The default manager (based on semantic elements) will always be the last + * returned manager. + * + * @return a list of {@link SiriusLayoutDataManager} instances. + */ + public static List<SiriusLayoutDataManager> getAllSiriusLayoutDataManagers() { + List<SiriusLayoutDataManager> applicableManagers = Lists.newArrayList(); + for (LayoutDataManagerDescriptor descriptor : getRegisteredExtensions()) { + ILayoutDataManagerProvider provider = descriptor.getLayoutDataManagerProvider(); + if (provider != null) { + SiriusLayoutDataManager layoutDataManager = EXTENSIONS.get(descriptor); + if (layoutDataManager == null) { + layoutDataManager = provider.getLayoutDataManager(); + EXTENSIONS.put(descriptor, layoutDataManager); + } + applicableManagers.add(layoutDataManager); + } + } + applicableManagers.add(SiriusLayoutDataManagerForSemanticElementsFactory.getInstance().getSiriusLayoutDataManager()); + return applicableManagers; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerRegistryListener.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerRegistryListener.java new file mode 100644 index 0000000000..d29c4ea3e0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/data/extension/LayoutDataManagerRegistryListener.java @@ -0,0 +1,151 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.data.extension; + +import java.util.Set; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionDelta; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.IRegistryChangeEvent; +import org.eclipse.core.runtime.IRegistryChangeListener; +import org.eclipse.core.runtime.Platform; + +import com.google.common.collect.Sets; + +/** + * This listener will allow us to be aware of contribution changes against the + * {@link LayoutDataManagerRegistryListener#LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION_POINT} + * extension point. + * + * @author mporhel + * + */ +public class LayoutDataManagerRegistryListener implements IRegistryChangeListener { + + /** Name of the extension point to parse for extensions. */ + public static final String LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION_POINT = "org.eclipse.sirius.diagram.layoutDataManager"; //$NON-NLS-1$ + + /** Name of the extension point's "Layout Data Manager Extension" tag. */ + private static final String LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION = "layoutDataManagerProvider"; //$NON-NLS-1$ + + /** + * initialize this listener. + */ + public void init() { + final IExtensionRegistry registry = Platform.getExtensionRegistry(); + registry.addRegistryChangeListener(this, LayoutDataManagerRegistryListener.LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION_POINT); + this.parseInitialContributions(); + } + + /** + * Dispose this listener. + */ + public void dispose() { + final IExtensionRegistry registry = Platform.getExtensionRegistry(); + registry.removeRegistryChangeListener(this); + LayoutDataManagerRegistry.clearRegistry(); + } + + /** + * Parses a single extension contribution. + * + * @param extension + * Parses the given extension and adds its contribution to the + * registry. + */ + private void parseExtension(IExtension extension) { + final IConfigurationElement[] configElements = extension.getConfigurationElements(); + for (IConfigurationElement elem : configElements) { + if (LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION.equals(elem.getName())) { + + try { + LayoutDataManagerRegistry.addExtension(new LayoutDataManagerDescriptor(elem)); + } catch (IllegalArgumentException e) { + // Do nothing + } + } + } + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.core.runtime.IRegistryEventListener#added(org.eclipse.core.runtime.IExtensionPoint[]) + */ + public void added(IExtensionPoint[] extensionPoints) { + for (IExtensionPoint extensionPoint : extensionPoints) { + for (IExtension extension : extensionPoint.getExtensions()) { + parseExtension(extension); + } + } + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.core.runtime.IRegistryChangeListener#registryChanged(org.eclipse.core.runtime.IRegistryChangeEvent) + */ + public void registryChanged(IRegistryChangeEvent event) { + Set<IExtension> addedExtensions = Sets.newLinkedHashSet(); + for (IExtensionDelta extensionDelta : event.getExtensionDeltas()) { + addedExtensions.add(extensionDelta.getExtension()); + } + added(addedExtensions.toArray(new IExtension[addedExtensions.size()])); + } + + /** + * Behavior when the given extensions are added. + * + * @param extensions + * the added extensions + */ + public void added(IExtension[] extensions) { + for (IExtension extension : extensions) { + parseExtension(extension); + } + } + + /** + * Though this listener reacts to the extension point changes, there could + * have been contributions before it's been registered. This will parse + * these initial contributions. + */ + private void parseInitialContributions() { + final IExtensionRegistry registry = Platform.getExtensionRegistry(); + + for (IExtension extension : registry.getExtensionPoint(LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION_POINT).getExtensions()) { + parseExtension(extension); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.core.runtime.IRegistryEventListener#removed(org.eclipse.core.runtime.IExtension[]) + */ + public void removed(IExtension[] extensions) { + for (IExtension extension : extensions) { + final IConfigurationElement[] configElements = extension.getConfigurationElements(); + for (IConfigurationElement elem : configElements) { + if (LAYOUT_DATA_MANAGER_PROVIDER_EXTENSION_POINT.equals(elem.getName())) { + final String extensionClassName = elem.getAttribute(LayoutDataManagerDescriptor.LAYOUT_DATA_MANAGER_PROVIDER_CLASS_NAME); + LayoutDataManagerRegistry.removeExtension(extensionClassName); + } + } + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/DEdgeLayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/DEdgeLayoutDataKey.java new file mode 100644 index 0000000000..e156b4e0ff --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/DEdgeLayoutDataKey.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.diagram; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; + +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.EdgeLayoutDataKey; + +/** + * Kind of key use to store the layout data corresponding to an {@link DEdge}. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class DEdgeLayoutDataKey implements EdgeLayoutDataKey { + /** + * The target of this EdgeLayoutData + */ + private DEdge target; + + /** + * Default constructor. + * + * @param key + * The key + */ + public DEdgeLayoutDataKey(final DEdge key) { + this.target = key; + } + + protected EObject getTarget() { + return target; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey#getId() + */ + public String getId() { + return EcoreUtil.getURI(getTarget()).fragment(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/DNodeLayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/DNodeLayoutDataKey.java new file mode 100644 index 0000000000..41db4edf8d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/DNodeLayoutDataKey.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.diagram; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; + +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.NodeLayoutDataKey; + +/** + * Kind of key use to store the layout data corresponding to an + * {@link AbstractDNode} or a {@link DDiagram}. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class DNodeLayoutDataKey implements NodeLayoutDataKey { + /** + * The target of this LayoutData (can only be a DDiagram or an + * AbstractDNode). + */ + private EObject target; + + /** + * Default constructor. + * + * @param target + * The target of the + */ + public DNodeLayoutDataKey(final EObject target) { + if (!(target instanceof AbstractDNode || target instanceof DDiagram)) { + throw new IllegalArgumentException("The key uses to store this layout data can only be an AbstractDNode or a DDiagram."); + } + this.target = target; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object obj) { + if (obj instanceof DNodeLayoutDataKey) { + return this.getTarget().equals(((DNodeLayoutDataKey) obj).getTarget()); + } else { + return super.equals(obj); + } + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return super.hashCode(); + } + + protected EObject getTarget() { + return target; + } + + protected void setTarget(final EObject target) { + this.target = target; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey#getId() + */ + public String getId() { + return EcoreUtil.getURI(getTarget()).fragment(); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/SiriusLayoutDataManagerForDDiagram.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/SiriusLayoutDataManagerForDDiagram.java new file mode 100644 index 0000000000..04ee8de72f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/diagram/SiriusLayoutDataManagerForDDiagram.java @@ -0,0 +1,150 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.diagram; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData; +import org.eclipse.sirius.diagram.layoutdata.EdgeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.NodeLayoutData; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey; +import org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager; + +/** + * SiriusLayoutDataManager drives by the DDiagram (DNode, DEdge, ...). Use + * for drag'n'drop and creation process. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class SiriusLayoutDataManagerForDDiagram implements SiriusLayoutDataManager { + Map<DNodeLayoutDataKey, NodeLayoutData> nodeLayoutDataMap = new HashMap<DNodeLayoutDataKey, NodeLayoutData>(); + + Map<DEdgeLayoutDataKey, EdgeLayoutData> edgeLayoutDataMap = new HashMap<DEdgeLayoutDataKey, EdgeLayoutData>(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#addLayoutData(org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey, + * org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData) + */ + public void addLayoutData(final LayoutDataKey key, final AbstractLayoutData layoutData) { + if (!checkKeyType(key)) { + // Kind of key not manage + return; + } + if (key instanceof DNodeLayoutDataKey) { + if (layoutData instanceof NodeLayoutData) { + nodeLayoutDataMap.put((DNodeLayoutDataKey) key, (NodeLayoutData) layoutData); + } else { + // Bad type of layoutData for this key + } + } else if (key instanceof DEdgeLayoutDataKey) { + if (layoutData instanceof EdgeLayoutData) { + edgeLayoutDataMap.put((DEdgeLayoutDataKey) key, (EdgeLayoutData) layoutData); + } else { + // Bad type of layoutData for this key + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#getLayoutData(org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey) + */ + public AbstractLayoutData getLayoutData(final LayoutDataKey key) { + AbstractLayoutData result = null; + if (checkKeyType(key)) { + if (key instanceof DNodeLayoutDataKey) { + result = nodeLayoutDataMap.get(key); + } else if (key instanceof DEdgeLayoutDataKey) { + result = edgeLayoutDataMap.get(key); + } + } + return result; + } + + /** + * Check if the key is manage by this manager. + * + * @param key + * the key to check + * @return + */ + private boolean checkKeyType(final LayoutDataKey key) { + return key instanceof DNodeLayoutDataKey || key instanceof DEdgeLayoutDataKey; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#applyLayout(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public void applyLayout(final IGraphicalEditPart rootEditPart) { + // TODO Auto-generated method stub + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#clearLayoutData() + */ + public void clearLayoutData() { + // TODO Auto-generated method stub + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#containsData() + */ + public boolean containsData() { + // TODO Auto-generated method stub + return false; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#createKey(org.eclipse.sirius.DSemanticDecorator) + */ + public LayoutDataKey createKey(final DSemanticDecorator semanticDecorator) { + // TODO Auto-generated method stub + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#getInstance() + */ + public SiriusLayoutDataManager getInstance() { + // TODO Auto-generated method stub + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#storeLayoutData(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public void storeLayoutData(final IGraphicalEditPart rootEditPart) { + // TODO Auto-generated method stub + + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/OrderedTreeOrdering.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/OrderedTreeOrdering.java new file mode 100644 index 0000000000..b3fe0e7b7c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/OrderedTreeOrdering.java @@ -0,0 +1,185 @@ +/******************************************************************************* + * Copyright (c) 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.ordering; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerInterpreter; +import org.eclipse.sirius.business.api.logger.RuntimeLoggerManager; +import org.eclipse.sirius.description.AbstractNodeMapping; +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.description.OrderedTreeLayout; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.AbstractTreeViewOrdering; +import org.eclipse.sirius.tools.api.interpreter.InterpreterUtil; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; + +/** + * Ordering for an ordered tree. + * + * @author mchauvin + */ +public class OrderedTreeOrdering extends AbstractTreeViewOrdering { + + private Collection<String> domainClasses; + + private OrderedTreeLayout layout; + + private Map<View, List<Node>> map; + + /** + * Default constructor. + * + * @param layout + * the ordered tree layout + */ + public OrderedTreeOrdering(final OrderedTreeLayout layout) { + this.layout = layout; + this.domainClasses = getDomainClasses(); + map = new WeakHashMap<View, List<Node>>(); + } + + private Collection<String> getDomainClasses() { + final Collection<String> classes = new ArrayList<String>(); + for (AbstractNodeMapping mapping : layout.getNodeMapping()) { + if (!StringUtil.isEmpty(mapping.getDomainClass())) { + classes.add(mapping.getDomainClass()); + } + } + return classes; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.common.ui.business.api.layout.AbstractTreeViewOrdering#getRoots(java.util.List) + */ + @Override + public List<View> getRoots(final List<View> views) { + final List<View> result = new LinkedList<View>(views); + final Iterator<View> iterViews = views.iterator(); + while (iterViews.hasNext()) { + final View currentView = iterViews.next(); + map.put(currentView, computeChildren(currentView, views)); + for (Node node : map.get(currentView)) { + result.remove(node); + } + } + + return result; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.sirius.common.ui.business.api.layout.AbstractTreeViewOrdering#clear() + */ + @Override + protected void clear() { + this.map.clear(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.common.ui.business.api.layout.AbstractTreeViewOrdering#getChildren(org.eclipse.gmf.runtime.notation.View, + * java.util.List) + */ + @Override + public List<View> getChildren(final View parent, final List<View> views) { + if (map.containsKey(parent)) { + return (List) map.get(parent); + } + return Collections.<View> emptyList(); + } + + /** + * Returns all children of the parent view. + * + * @param parent + * the parent view. + * @param views + * the candidates. + * @return all children of the parent view. + */ + public List<Node> computeChildren(final View parent, final List<View> views) { + + final List<Node> result = new LinkedList<Node>(); + + if (parent.getElement() instanceof DSemanticDecorator) { + final EObject element = ((DSemanticDecorator) parent.getElement()).getTarget(); + if (element != null && element.eResource() != null) { + // The element could be null in case of diagram that is not in + // refreshAuto mode and that have a semantic element deleted. + final ModelAccessor accesor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(element); + + if (accesor != null && isInstanceOfOneDomainClass(element, accesor)) { + + final IInterpreter interpreter = InterpreterUtil.getInterpreter(element); + final RuntimeLoggerInterpreter safeInterpreter = RuntimeLoggerManager.INSTANCE.decorate(interpreter); + + final Collection<EObject> acceleoResult = safeInterpreter.evaluateCollection(element, layout, DescriptionPackage.eINSTANCE.getOrderedTreeLayout_ChildrenExpression()); + final List<EObject> children = new ArrayList<EObject>(acceleoResult); + result.addAll(getChildrenNode(views, children)); + + } + } + } + + return result; + } + + private List<Node> getChildrenNode(final List<View> views, final List<EObject> children) { + final List<Node> childrenNodes = new LinkedList<Node>(); + final Iterator<EObject> iterChildren = children.iterator(); + while (iterChildren.hasNext()) { + final EObject child = iterChildren.next(); + final Iterator<View> iterViews = views.iterator(); + while (iterViews.hasNext()) { + final View view = iterViews.next(); + if (view instanceof Node && view.getElement() instanceof DSemanticDecorator) { + final EObject currentSemantic = ((DSemanticDecorator) view.getElement()).getTarget(); + if (currentSemantic.equals(child)) { + childrenNodes.add((Node) view); + break; + } + } + } + } + return childrenNodes; + } + + private boolean isInstanceOfOneDomainClass(final EObject element, final ModelAccessor accessor) { + boolean isInstance = false; + final Iterator<String> it = domainClasses.iterator(); + while (it.hasNext() && !isInstance) { + isInstance = accessor.eInstanceOf(element, it.next()); + } + + return isInstance; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/OrderedTreeViewOrderingProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/OrderedTreeViewOrderingProvider.java new file mode 100644 index 0000000000..8a86c485f9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/OrderedTreeViewOrderingProvider.java @@ -0,0 +1,96 @@ +/******************************************************************************* + * Copyright (c) 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.ordering; + +import java.util.Map; +import java.util.WeakHashMap; + +import org.eclipse.sirius.business.api.helper.SiriusUtil; +import org.eclipse.sirius.description.AbstractNodeMapping; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.description.Layout; +import org.eclipse.sirius.description.OrderedTreeLayout; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.ViewOrdering; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.ViewOrderingProvider; + +/** + * A tree ordered view ordering {@link ViewOrdering} provider. + * + * @author mchauvin + */ +public class OrderedTreeViewOrderingProvider implements ViewOrderingProvider { + + /** the ordering. */ + + private Map<OrderedTreeLayout, OrderedTreeOrdering> orderings = new WeakHashMap<OrderedTreeLayout, OrderedTreeOrdering>(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrderingProvider#getViewOrdering(org.eclipse.sirius.description.DiagramElementMapping) + */ + public ViewOrdering getViewOrdering(final DiagramElementMapping mapping) { + + if (mapping instanceof AbstractNodeMapping) { + final AbstractNodeMapping nodeMapping = (AbstractNodeMapping) mapping; + final DiagramDescription desc = SiriusUtil.findDiagramDescription(nodeMapping); + final Layout layout = desc.getLayout(); + if (layout instanceof OrderedTreeLayout) { + if (((OrderedTreeLayout) layout).getNodeMapping().contains(mapping)) { + return getOrdering((OrderedTreeLayout) layout); + } + } + } + return null; + /* + * if (provides(mapping)) return getOrdering((NodeMapping)mapping); + * return null; + */ + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.ordering.ViewOrderingProvider#provides(org.eclipse.sirius.description.DiagramElementMapping) + */ + public boolean provides(final DiagramElementMapping mapping) { + if (mapping instanceof AbstractNodeMapping) { + + final AbstractNodeMapping nodeMapping = (AbstractNodeMapping) mapping; + final DiagramDescription desc = SiriusUtil.findDiagramDescription(nodeMapping); + final Layout layout = desc.getLayout(); + if (layout instanceof OrderedTreeLayout) { + if (((OrderedTreeLayout) layout).getNodeMapping().contains(mapping)) { + return true; + } + } + } + return false; + } + + /** + * Return the ordering. + * + * @return the ordering. + */ + private OrderedTreeOrdering getOrdering(final OrderedTreeLayout layout) { + + if (orderings.containsKey(layout)) { + return orderings.get(layout); + } + + final OrderedTreeOrdering ordering = new OrderedTreeOrdering(layout); + ordering.setUserAwareCapable(true); + orderings.put(layout, ordering); + return ordering; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/ViewOrderingProviderRegistry.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/ViewOrderingProviderRegistry.java new file mode 100644 index 0000000000..ba0024b568 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/ordering/ViewOrderingProviderRegistry.java @@ -0,0 +1,123 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.ordering; + +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedList; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.Platform; +import org.eclipse.emf.common.EMFPlugin; + +import org.eclipse.sirius.description.DiagramElementMapping; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.ui.tools.api.layout.ordering.ViewOrderingProvider; + +/** + * This class registers all {@link ViewOrderingProvider}s. + * + * @author ymortier + */ +public final class ViewOrderingProviderRegistry { + + /** The shared instance. */ + private static ViewOrderingProviderRegistry instance = new ViewOrderingProviderRegistry(); + + /** Name of the extension point to parse for style configuration providers. */ + private static final String VIEW_ORDERING_PROVIDER_EXTENSION_POINT = "org.eclipse.sirius.diagram.viewOrderingProvider"; //$NON-NLS-1$ + + /** Externalized here to avoid too many distinct usages. */ + private static final String TAG_ENGINE = "viewOrderingProvider"; + + /** The providers. */ + private Collection<ViewOrderingProvider> viewOrderingProviders; + + /** + * Avoid instantiation from outside. + */ + private ViewOrderingProviderRegistry() { + this.parseExtensionMetadata(); + } + + /** + * This will parse the currently running platform for extensions and store + * all the match engines that can be found. + */ + private void parseExtensionMetadata() { + if (EMFPlugin.IS_ECLIPSE_RUNNING) { + this.viewOrderingProviders = new LinkedList<ViewOrderingProvider>(); + final IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(VIEW_ORDERING_PROVIDER_EXTENSION_POINT).getExtensions(); + for (IExtension extension : extensions) { + final IConfigurationElement[] configElements = extension.getConfigurationElements(); + for (IConfigurationElement configElement : configElements) { + if (configElement.getName().equals(TAG_ENGINE)) { + try { + final ViewOrderingProvider viewOrderingProvider = (ViewOrderingProvider) configElement.createExecutableExtension("providerClass"); + this.viewOrderingProviders.add(viewOrderingProvider); + } catch (final CoreException e) { + SiriusDiagramEditorPlugin.getInstance().logError("Impossible to load the view ordering provider : " + configElement.getName(), e); + } + } + } + } + } + } + + /** + * Return the shared instance. + * + * @return the shared instance. + */ + public static ViewOrderingProviderRegistry getInstance() { + return instance; + } + + /** + * Return all providers. + * + * @return all providers. + */ + public Collection<ViewOrderingProvider> getAllProviders() { + return this.viewOrderingProviders; + } + + /** + * Return the {@link ViewOrderingProvider} to use for the specified mapping. + * Return <code>null</code> if no {@link ViewOrderingProvider} is available + * for the specified mapping. + * + * @param mapping + * the mapping. + * @return the {@link ViewOrderingProvider} to use for the specified + * mapping. + */ + public ViewOrderingProvider getProvider(final DiagramElementMapping mapping) { + final Iterator<ViewOrderingProvider> iterProviders = this.getAllProviders().iterator(); + while (iterProviders.hasNext()) { + final ViewOrderingProvider currentProvider = iterProviders.next(); + // try { + if (currentProvider.provides(mapping)) { + return currentProvider; + } + // } catch (final RuntimeException e) { + // SiriusDiagramEditorPlugin.getInstance().logWarning( + // "The view ordering provider " + + // currentProvider.getClass().getName() + // + " has been removed from the ViewOrderingProviderRegistry since + // it threw an Exception in its provides method.", e); + // } + } + return null; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AbstractCompositeLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AbstractCompositeLayoutProvider.java new file mode 100644 index 0000000000..83ac0105bc --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AbstractCompositeLayoutProvider.java @@ -0,0 +1,480 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Insets; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.graph.DirectedGraph; +import org.eclipse.draw2d.graph.DirectedGraphLayout; +import org.eclipse.draw2d.graph.Edge; +import org.eclipse.draw2d.graph.EdgeList; +import org.eclipse.draw2d.graph.Node; +import org.eclipse.draw2d.graph.NodeList; +import org.eclipse.draw2d.graph.Subgraph; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gmf.runtime.common.core.service.IOperation; +import org.eclipse.gmf.runtime.diagram.ui.editparts.CompartmentEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.GroupEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeCompartmentEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeEditPart; +import org.eclipse.gmf.runtime.diagram.ui.providers.internal.CompositeLayoutProvider; +import org.eclipse.gmf.runtime.draw2d.ui.internal.graph.AdvancedSubGraph; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Size; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramElementContainerEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutExtender; +import org.eclipse.sirius.diagram.ui.tools.api.layout.PinHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.ArrangeAllWithAutoSize; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.AutoSizeAndRegionAwareGraphLayout; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.DiagramLayoutCustomization; + +/** + * Abstract base class to factor out the common code between + * {@link CompositeLeftRightLayoutProvider}, + * {@link CompositeDownTopLayoutProvider} and + * {@link CompositeTopDownLayoutProvider}. + * + * @author pcdavid + */ +@SuppressWarnings("restriction") +public abstract class AbstractCompositeLayoutProvider extends CompositeLayoutProvider implements ExtendableLayoutProvider { + + private final LayoutExtender extender = new LayoutExtender(this); + + private final DiagramLayoutCustomization padder = new DiagramLayoutCustomization(); + + private final ArrangeAllWithAutoSize autoSize = new ArrangeAllWithAutoSize(); + + private Predicate<Object> validateAllElementInArrayListAreIDiagramElementEditPart = new Predicate<Object>() { + + public boolean apply(Object input) { + return input instanceof IDiagramElementEditPart; + } + }; + + private ArrayList<IDiagramElementEditPart> elementsToKeepFixed = Lists.newArrayList(); + + /** + * {@inheritDoc} + * + * Made public to be available from ArrangeAllWithAutoSize. + */ + @Override + public abstract Rectangle translateToGraph(final Rectangle r); + + /** + * {@inheritDoc} + * + * Made public to be available from ArrangeAllWithAutoSize. + */ + @Override + public abstract Rectangle translateFromGraph(final Rectangle rect); + + /** + * {@inheritDoc} + */ + @Override + public boolean provides(final IOperation operation) { + return true; + } + + /** + * {@inheritDoc} + */ + public boolean handleConnectableListItems() { + return shouldHandleConnectableListItems(); + } + + /** + * {@inheritDoc} + */ + public Rectangle provideNodeMetrics(final Node node) { + return getNodeMetrics(node); + } + + /** + * {@inheritDoc} + */ + public LayoutExtender getExtender() { + return extender; + } + + /** + * {@inheritDoc} + */ + @Override + public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) { + padder.initializePaddingWithEditParts(selectedObjects); + extender.startLayouting(); + + // Clear the list of elements to keep fixed. + elementsToKeepFixed.clear(); + // Finds if there are unpinned diagram elements to keep fixed stored in + // the LayoutHint as a Collection + if (layoutHint.getAdapter(Collection.class) instanceof ArrayList<?> + && Iterables.all((ArrayList<?>) layoutHint.getAdapter(Collection.class), validateAllElementInArrayListAreIDiagramElementEditPart)) { + elementsToKeepFixed = new ArrayList<IDiagramElementEditPart>((ArrayList<IDiagramElementEditPart>) layoutHint.getAdapter(Collection.class)); + } + // return super.layoutEditParts(selectedObjects, layoutHint); + Command result = super.layoutEditParts(selectedObjects, layoutHint); + + // Clear the list of elements to keep fixed (to avoid memory leak) + elementsToKeepFixed.clear(); + + return result; + + } + + /** + * {@inheritDoc} + */ + @Override + protected DirectedGraphLayout createGraphLayout() { + return new AutoSizeAndRegionAwareGraphLayout(); + } + + /** + * {@inheritDoc} + */ + @SuppressWarnings("rawtypes") + @Override + protected List getRelevantConnections(final Hashtable editPartToNodeDict) { + /* + * We're wrapping the original hashtable with this forwarding one not + * failling when called with a null get because we've got cases where + * editparts are linked through connections not having source/target + * (for instance during folding). + */ + @SuppressWarnings("serial") + final List list = super.getRelevantConnections(new Hashtable() { + + @Override + public synchronized Object get(Object key) { + if (key != null) { + return editPartToNodeDict.get(key); + } + return null; + } + + @Override + public synchronized Enumeration keys() { + return editPartToNodeDict.keys(); + } + + }); + return extender.getRelevantConnections(editPartToNodeDict, list); + } + + // CHECKSTYLE:OFF + + /* + * The code in the methods below is copy/pasted and slightly adapted from + * GMF, as it could not be overridden cleanly. + */ + + @Override + protected Command update_diagram(org.eclipse.gef.GraphicalEditPart diagramEP, DirectedGraph g, boolean isLayoutForSelected) { + /* + * We define the layout default margin at 0 otherwise consecutive calls + * to the ArrangeAll actions will always move the nodes in containers. + */ + + CompoundCommand cc = new CompoundCommand(""); //$NON-NLS-1$ + + final Point diff = getLayoutPositionDelta(g, isLayoutForSelected); + layoutDefaultMargin = MapModeUtil.getMapMode(diagramEP.getFigure()).DPtoLP(25); + + Command cmd = createNodeChangeBoundCommands(g, diff); + if (cmd != null) { + cc.add(cmd); + } + + cmd = createEdgesChangeBoundsCommands(g, diff); + if (cmd != null) { + cc.add(cmd); + } + + return cc; + } + + /** + * Only used if ENABLE_PARTIAL_OVERLAP_FIX is true. This is an almost + * verbatim copy of the version in the parent class, except for the use of + * the custom getNodeMetricsConsideringBorderedNodes. + */ + private Point getLayoutPositionDelta(DirectedGraph g, boolean isLayoutForSelected) { + // If laying out selected objects, use diff variables to + // position objects at topleft corner of enclosing rectangle. + if (isLayoutForSelected) { + ListIterator vi; + vi = g.nodes.listIterator(); + Point ptLayoutMin = new Point(-1, -1); + while (vi.hasNext()) { + Node node = (Node) vi.next(); + // ignore ghost node + if (node.data != null) { + Rectangle nodeExt = getNodeMetricsConsideringBorderedNodes(node); + if (ptLayoutMin.x == -1) { + ptLayoutMin.x = nodeExt.x; + ptLayoutMin.y = nodeExt.y; + } else { + ptLayoutMin.x = Math.min(ptLayoutMin.x, nodeExt.x); + ptLayoutMin.y = Math.min(ptLayoutMin.y, nodeExt.y); + } + } + } + + return new Point(this.minX - ptLayoutMin.x, this.minY - ptLayoutMin.y); + } + + return new Point(layoutDefaultMargin, layoutDefaultMargin); + } + + private Rectangle getNodeMetricsConsideringBorderedNodes(Node node) { + Rectangle result = getNodeMetrics(node); + if (node instanceof Subgraph && node.data instanceof IGraphicalEditPart) { + Subgraph subGraph = (Subgraph) node; + NodeList children = subGraph.members; + for (int i = 0; i < children.size(); i++) { + Node child = children.getNode(i); + if (child instanceof IBorderItemEditPart) { + result.union(getNodeMetrics(child)); + } + } + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.providers.internal.DefaultProvider#build_edges(java.util.List, + * java.util.Map) + * @deprecated + */ + @Override + protected EdgeList build_edges(final List selectedObjects, final Map editPartToNodeDict) { + return buildEdges(selectedObjects, editPartToNodeDict); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.providers.internal.DefaultProvider#build_edges(java.util.List, + * java.util.Map) + */ + @Override + protected EdgeList buildEdges(final List selectedObjects, final Map editPartToNodeDict) { + return super.buildEdges(extender.filterEdges(selectedObjects, editPartToNodeDict), editPartToNodeDict); + } + + /* + * (non-Javadoc) + * + * @see + * org.eclipse.gmf.runtime.diagram.ui.providers.internal.DefaultProvider + * #build_nodes(java.util.List, java.util.Map, + * org.eclipse.draw2d.graph.Subgraph) + * + * @deprecated + */ + @Override + @Deprecated + protected NodeList build_nodes(List selectedObjects, Map editPartToNodeDict, Subgraph rootGraph) { + return buildNodes(selectedObjects, editPartToNodeDict, rootGraph); + } + + @Override + protected NodeList buildNodes(List selectedObjects, Map editPartToNodeDict, Subgraph rootGraph) { + ListIterator li = selectedObjects.listIterator(); + // <added for auto-size> + autoSize.prepareForArrangeAll(Iterators.filter(li, AbstractDiagramElementContainerEditPart.class)); + // </added for auto-size> + + NodeList nodes = new NodeList(); + li = selectedObjects.listIterator(); + while (li.hasNext()) { + IGraphicalEditPart gep = (IGraphicalEditPart) li.next(); + boolean hasChildren = hasChildren(gep); + if (!(gep instanceof IBorderItemEditPart) && (gep instanceof ShapeEditPart || gep instanceof ShapeCompartmentEditPart)) { + GraphicalEditPart ep = (GraphicalEditPart) gep; + Point position = ep.getFigure().getBounds().getLocation(); + if (minX == -1) { + minX = position.x; + minY = position.y; + } else { + minX = Math.min(minX, position.x); + minY = Math.min(minY, position.y); + } + Node n = null; + if (hasChildren && !(gep instanceof GroupEditPart)) { + AdvancedSubGraph subGraph = null; + if (rootGraph != null) { + subGraph = new AdvancedSubGraph(ep, rootGraph); + } else { + subGraph = new AdvancedSubGraph(ep); + } + subGraph.setAutoSize(isAutoSizeOn(subGraph, ep)); + if (gep instanceof CompartmentEditPart) { + subGraph.setHasBufferedZone(true); + } + subGraph.setDirection(getLayoutDirection(ep)); + n = subGraph; + } else { + if (rootGraph != null) { + n = new Node(ep, rootGraph); + } else { + n = new Node(ep); + } + } + adjustNodePadding(n, editPartToNodeDict); + // <modified for auto-size> + Dimension size = autoSize.getSizeToConsiderDuringArrangeAll(ep); + // </modified for auto-size> + setNodeMetrics(n, new Rectangle(position.x, position.y, size.width, size.height)); + editPartToNodeDict.put(ep, n); + nodes.add(n); + if (hasChildren && !(gep instanceof GroupEditPart)) { + buildNodes(gep.getChildren(), editPartToNodeDict, (Subgraph) n); + } + } + } + return nodes; + } + + private boolean isAutoSizeOn(AdvancedSubGraph subGraph, IGraphicalEditPart gEP) { + if (gEP instanceof CompartmentEditPart && subGraph.getParent() instanceof AdvancedSubGraph) { + if (((AdvancedSubGraph) subGraph.getParent()).isAutoSize()) { + return true; + } + } else { + View notationView = gEP.getNotationView(); + if (notationView != null && notationView instanceof org.eclipse.gmf.runtime.notation.Node) { + org.eclipse.gmf.runtime.notation.Node node = (org.eclipse.gmf.runtime.notation.Node) notationView; + LayoutConstraint contraint = node.getLayoutConstraint(); + if (contraint instanceof Size) { + Size size = (Size) contraint; + if (size.getHeight() != -1 && size.getWidth() != -1) { + return ArrangeAllWithAutoSize.isEnabled(); + } + return true; + } + } + } + return false; + } + + @Override + protected void createSubCommands(Point diff, ListIterator vi, CompoundCommand cc) { + final List nodes = Lists.newArrayList(vi); + if (ArrangeAllWithAutoSize.isEnabled()) { + autoSize.createSubCommands(diff, nodes.listIterator(), cc, this, minX, minY); + } else { + // No auto-size : region will not be moved. + super.createSubCommands(diff, nodes.listIterator(), cc); + } + extender.keepLocationChanges(nodes, diff); + } + + /** + * {@inheritDoc} + */ + @Override + protected void adjustNodePadding(final Node node, final Map editPartToNodeDict) { + // <modification> + final GraphicalEditPart ep = (GraphicalEditPart) node.data; + final Insets padding = padder.getNodePadding(ep); + // </modification> + // check if the direct parent is added already to the graph + final GraphicalEditPart parent = (GraphicalEditPart) ep.getParent(); + if (parent != null && node.getParent() != null && editPartToNodeDict.get(parent) != node.getParent()) { + // now the direct parent is not added to the graph so, we had + // to adjust the padding of the node to consider the parent + final IFigure thisFigure = parent.getFigure(); + final IFigure parentFigure = ((GraphicalEditPart) node.getParent().data).getFigure(); + final Point parentLocation = parentFigure.getBounds().getLocation(); + final Point nodeLocation = thisFigure.getBounds().getLocation(); + thisFigure.translateToAbsolute(nodeLocation); + parentFigure.translateToAbsolute(parentLocation); + final Dimension delta = nodeLocation.getDifference(parentLocation); + final Rectangle rect = translateToGraph(new Rectangle(delta.width, delta.height, 0, 0)); + padding.top += rect.y; + padding.left += rect.x; + } + node.setPadding(padding); + } + + /** + * Method override to avoid move of edges that have considered as fixed. + * + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.providers.internal.CompositeLayoutProvider#routeThrough(org.eclipse.draw2d.graph.Edge, + * org.eclipse.gef.ConnectionEditPart, org.eclipse.draw2d.graph.Node, + * org.eclipse.draw2d.graph.Node, + * org.eclipse.draw2d.geometry.PointList, + * org.eclipse.draw2d.geometry.Point) + */ + @Override + protected Command routeThrough(Edge edge, ConnectionEditPart connectEP, Node source, Node target, PointList points, Point diff) { + if (connectEP instanceof IGraphicalEditPart && isPinned((IGraphicalEditPart) connectEP) || (elementsToKeepFixed != null && elementsToKeepFixed.contains(connectEP))) { + return null; + } + return super.routeThrough(edge, connectEP, source, target, points, diff); + } + + /** + * Tests whether an edit part should be considered as pinned (fixed size and + * location) during the layout. + * + * @param part + * the edit part. + * @return <code>true</code> if the edit part should be considered as + * pinned. + */ + protected boolean isPinned(final IGraphicalEditPart part) { + boolean isPinned = false; + if (part.resolveSemanticElement() instanceof DDiagramElement) { + DDiagramElement dDiagramElement = (DDiagramElement) part.resolveSemanticElement(); + isPinned = new PinHelper().isPinned(dDiagramElement); + } + return isPinned; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AbstractProviderDescriptor.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AbstractProviderDescriptor.java new file mode 100644 index 0000000000..cf5a82367f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AbstractProviderDescriptor.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.core.runtime.IConfigurationElement; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutConstants; + +/** + * A basic descriptor. + * + * @author ymortier + */ +public abstract class AbstractProviderDescriptor implements Comparable<AbstractProviderDescriptor> { + + /** Configuration element of this descriptor. */ + protected final IConfigurationElement element; + + /** The priority of this provider. */ + private int priority = LayoutConstants.NORMAL_PRIORITY; + + /** The name of the provider. */ + private String providerClassName; + + /** + * Create a new {@link AbstractProviderDescriptor}. + * + * @param element + * the configuration element. + */ + public AbstractProviderDescriptor(final IConfigurationElement element) { + this.element = element; + this.priority = getPriorityValue(getAttribute("priority", "low")); //$NON-NLS-1$//$NON-NLS-2$ + this.providerClassName = getAttribute("providerClass", null); //$NON-NLS-1$ + } + + /** + * Return the provider class name. + * + * @return the provider class name. + */ + public String getProviderClassName() { + return providerClassName; + } + + /** + * Returns the value of the attribute <code>name</code> of this descriptor's + * configuration element. if the attribute hasn't been set, we'll return + * <code>defaultValue</code> instead. + * + * @param name + * Name of the attribute we seek the value of. + * @param defaultValue + * Value to return if the attribute hasn't been set. + * @return The value of the attribute <code>name</code>, + * <code>defaultValue</code> if it hasn't been set. + */ + private String getAttribute(final String name, final String defaultValue) { + final String value = element.getAttribute(name); + if (value != null) { + return value; + } + if (defaultValue != null) { + return defaultValue; + } + throw new IllegalArgumentException("The " + name + " attribute is missing"); //$NON-NLS-1$ + } + + /** + * Return the priority of this provider. + * + * @return the priority of this provider. + */ + public int getPriority() { + return priority; + } + + /** + * Returns the value of the priority described by the given {@link String}.<br/> + * Returned values according to <code>priorityString</code> value : + * <ul> + * <li>"lowest" => + * {@value org.eclipse.sirius.ecore.extender.business.api.accessor.ExtenderConstants#PRIORITY_LOWEST} + * </li> + * <li>"low" => + * {@value org.eclipse.sirius.ecore.extender.business.api.accessor.ExtenderConstants#PRIORITY_LOW} + * </li> + * <li>"high" => + * {@value org.eclipse.sirius.ecore.extender.business.api.accessor.ExtenderConstants#PRIORITY_HIGH} + * </li> + * <li>"highest" => + * {@value org.eclipse.sirius.ecore.extender.business.api.accessor.ExtenderConstants#PRIORITY_HIGHEST} + * </li> + * <li>anything else => + * {@value org.eclipse.sirius.ecore.extender.business.api.accessor.ExtenderConstants#PRIORITY_NORMAL} + * </li> + * </ul> + * + * @param priorityString + * {@link String} value of the priority we seek. + * @return <code>int</code> corresponding to the given priority + * {@link String}. + */ + private int getPriorityValue(final String priorityString) { + int priorityValue = LayoutConstants.NORMAL_PRIORITY; + if ("lowest".equals(priorityString)) { //$NON-NLS-1$ + priorityValue = LayoutConstants.LOWEST_PRIORITY; + } else if ("low".equals(priorityString)) { //$NON-NLS-1$ + priorityValue = LayoutConstants.LOW_PRIORITY; + } else if ("high".equals(priorityString)) { //$NON-NLS-1$ + priorityValue = LayoutConstants.HIGH_PRIORITY; + } else if ("highest".equals(priorityString)) { //$NON-NLS-1$ + priorityValue = LayoutConstants.HIGHEST_PRIORITY; + } + return priorityValue; + } + + /** + * {@inheritDoc} + */ + public int compareTo(final AbstractProviderDescriptor other) { + final int nombre1 = other.getPriority(); + final int nombre2 = priority; + return nombre2 - nombre1; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AdjustedGridLayout.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AdjustedGridLayout.java new file mode 100644 index 0000000000..fdb5b31f1d --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/AdjustedGridLayout.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.gef.Request; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.requests.RequestConstants; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.GridLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.ArrangeAllWithAutoSize; + +/** + * Specialization of the {@link GridLayoutProvider} to handle the specific + * layout adjustements made on the arrange all. + * + * @author cbrun + * + */ +public class AdjustedGridLayout extends GridLayoutProvider { + /** + * {@inheritDoc} + */ + @Override + protected Command createChangeBoundsCommand(final IGraphicalEditPart editPart, final Point newPosition) { + Command result = null; + final Command settingBounds = super.createChangeBoundsCommand(editPart, newPosition); + if (ArrangeAllWithAutoSize.isEnabled() && ArrangeAllWithAutoSize.shouldBeAutosized(editPart)) { + final Command autoSizeCmd = editPart.getCommand(new Request(RequestConstants.REQ_AUTOSIZE)); + if (settingBounds != null) { + final CompoundCommand cpd = new CompoundCommand(); + cpd.add(settingBounds); + cpd.add(autoSizeCmd); + result = cpd; + + } else { + result = autoSizeCmd; + } + + } else { + result = settingBounds; + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/ArrangeAllOnlyLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/ArrangeAllOnlyLayoutProvider.java new file mode 100644 index 0000000000..7c3dd39950 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/ArrangeAllOnlyLayoutProvider.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2009, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; + +/** + * {@link ArrangeSelectionLayoutProvider} that <b>only accepts ArrangeAll + * actions</b> (and does nothing if the current Action is an Arrange Selection + * Action). + * <p> + * Layout provider that add a list of diagram elements to keep fixed in the + * LayoutHint, during arrange selection action. This information will be used + * later in the PinnedElementHandler and PinnedElementLayoutProvider. It is + * executed before the generic arrange operation. + * </p> + * + * @author <a href="mailto:alex.lagarde@obeo.fr">Alex Lagarde</a> + */ +public class ArrangeAllOnlyLayoutProvider extends ArrangeSelectionLayoutProvider { + + /** + * Creates a new ArrangeAllOnlyLayoutProvider. + * + * @param clp + * The layout provider to call after finding diagram element to + * keep fixed on arrange all + */ + public ArrangeAllOnlyLayoutProvider(AbstractLayoutProvider clp) { + super(clp); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/ArrangeSelectionLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/ArrangeSelectionLayoutProvider.java new file mode 100644 index 0000000000..8b4925d5c7 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/ArrangeSelectionLayoutProvider.java @@ -0,0 +1,249 @@ +/******************************************************************************* + * Copyright (c) 2010, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeNodeEditPart; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DEdgeEditPart; +import org.eclipse.sirius.diagram.ui.tools.api.layout.PinHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; + +/** + * Layout provider that add a list of diagram elements to keep fixed in the + * LayoutHint, during arrange selection action. This information will be used + * later in the PinnedElementHandler and PinnedElementLayoutProvider. It is + * executed before the generic arrange operation. + * + * @author smonnier + * + */ +public class ArrangeSelectionLayoutProvider extends AbstractLayoutProvider { + + /** + * The initial layout provider that arrange the nodes (launch before the + * arrange of bordered nodes and before updating the list of diagram element + * to keep fixed on arrange). + */ + private AbstractLayoutProvider initialLayoutProvider; + + /** + * List of ShapeNodeEditPart not selected on the current diagram. + */ + private ArrayList<ShapeNodeEditPart> notSelectedShapeNodeEditPart; + + /** + * List of IDiagramElementEditPart not selected and unpinned on the current + * diagram. + */ + private ArrayList<IDiagramElementEditPart> notSelectedShapeNodeEditPartAndUnpinned; + + /** + * Predicate to check if an EditPart is not selected + */ + private Predicate<EditPart> editPartIsNotSelected = new Predicate<EditPart>() { + + public boolean apply(EditPart input) { + return input.getSelected() == EditPart.SELECTED_NONE; + } + }; + + /** + * Predicate to check if a IDiagramElementEditPart is unpinned + */ + private Predicate<IDiagramElementEditPart> diagramElementEditPartIsUnpinned = new Predicate<IDiagramElementEditPart>() { + + public boolean apply(IDiagramElementEditPart input) { + return !new PinHelper().isPinned(input); + } + }; + + /** + * The default constructor. + * + * @param clp + * The layout provider to call after finding diagram element to + * keep fixed on arrange all. + */ + public ArrangeSelectionLayoutProvider(AbstractLayoutProvider clp) { + initialLayoutProvider = clp; + } + + /** + * {@inheritDoc} + * + * This method is overridden to have the arrange selection acting as an + * arrange all where non selected elements are pinned. + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(java.util.List, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public Command layoutEditParts(List selectedObjects, IAdaptable layoutHint) { + if (selectedObjects.isEmpty()) { + return UnexecutableCommand.INSTANCE; + } + + IAdaptable updatedLayoutHint = layoutHint; + + CompoundCommand result = new CompoundCommand(); + + boolean arrangeIsArrangeSelection = false; + if (selectedObjects instanceof LinkedList<?>) { + LinkedList<?> selectedObjectsLinkedList = (LinkedList<?>) selectedObjects; + if (selectedObjectsLinkedList.get(0) instanceof IGraphicalEditPart) { + IGraphicalEditPart igep = (IGraphicalEditPart) selectedObjectsLinkedList.get(0); + List topLevelEditParts = Lists.newArrayList(Iterables.filter(igep.getParent().getChildren(), ShapeNodeEditPart.class)); + arrangeIsArrangeSelection = validateArrangeIsArrangeSelection(selectedObjectsLinkedList, topLevelEditParts); + if (arrangeIsArrangeSelection) { + // Find out unselected diagram element on container (same + // parent) that are unpinned + notSelectedShapeNodeEditPart = Lists.newArrayList(Iterables.filter(topLevelEditParts, editPartIsNotSelected)); + notSelectedShapeNodeEditPart.removeAll(selectedObjectsLinkedList); + notSelectedShapeNodeEditPartAndUnpinned = Lists.newArrayList(Iterables.filter(Iterables.filter(notSelectedShapeNodeEditPart, IDiagramElementEditPart.class), + diagramElementEditPartIsUnpinned)); + // Add all children of not selected editPart in the unpinned + // list (indeed this avoid a move of children + // and + // LayoutStabilityOnManualRefreshTest.testLayoutStabilityOnOneViewCreatedDuringManualRefreshWithoutPinnedElement + // and + // LayoutStabilityOnManualRefreshTest.testLayoutStabilityOnOneViewCreatedDuringManualRefreshWithPinnedElement. + addChildrenToNotSelectedUnpinnedList(notSelectedShapeNodeEditPart); + // Add all edges that connect two unpinned elements in the + // unpinned list + if (igep.getRoot().getChildren().size() == 1 && igep.getRoot().getChildren().get(0) instanceof DiagramEditPart) { + addEdgesToNotSelectedUnpinnedList((DiagramEditPart) igep.getRoot().getChildren().get(0)); + } + + // Update Layout Hint to find later the list of unselected + // diagram element on diagram that are unpinned as elements + // to keep fixed in PinnedElementsHandler + final IAdaptable originalHint = layoutHint; + updatedLayoutHint = new IAdaptable() { + public Object getAdapter(@SuppressWarnings("rawtypes") + Class adapter) { + if (Collection.class.equals(adapter)) { + return notSelectedShapeNodeEditPartAndUnpinned; + } else { + return originalHint.getAdapter(adapter); + } + } + }; + // add all top level edit parts to have the arrange + // selection acting as an arrange all where non selected + // elements are pinned + selectedObjectsLinkedList.clear(); + selectedObjectsLinkedList.addAll(topLevelEditParts); + } + } + } + + result.add(lauchPrimaryArrangeAll(selectedObjects, updatedLayoutHint)); + + return result; + } + + /** + * Add, if needed, children that are also unpinned. + * + * @param notSelectedParent + * list of parents which are not selected for which you want to + * browse the children to possibly add to the unpinned list + */ + private void addChildrenToNotSelectedUnpinnedList(ArrayList<? extends EditPart> notSelectedParent) { + for (EditPart editPart : notSelectedParent) { + ArrayList<EditPart> notSelectedChildrenShapeNodeEditPart = Lists.newArrayList(Iterables.filter(editPart.getChildren(), editPartIsNotSelected)); + if (!notSelectedChildrenShapeNodeEditPart.isEmpty()) { + notSelectedShapeNodeEditPartAndUnpinned.addAll(Lists.newArrayList(Iterables.filter( + Iterables.filter(Iterables.filter(notSelectedChildrenShapeNodeEditPart, ShapeNodeEditPart.class), IDiagramElementEditPart.class), diagramElementEditPartIsUnpinned))); + addChildrenToNotSelectedUnpinnedList(notSelectedChildrenShapeNodeEditPart); + } + } + } + + /** + * Adds all the edges that connect two pinned nodes (or considered as such) + * in the list of <code>notSelectedShapeNodeEditPartAndUnpinned</code>. This + * list of edges is used in {@link AbstractCompositeLayoutProvider} to avoid + * the call of method routeThrough. + */ + private void addEdgesToNotSelectedUnpinnedList(DiagramEditPart diagramEditPart) { + for (Object connection : diagramEditPart.getConnections()) { + if (connection instanceof DEdgeEditPart) { + final DEdgeEditPart dEdgeEditPart = (DEdgeEditPart) connection; + if (isPinnedOrConsiderAs(dEdgeEditPart.getSource(), notSelectedShapeNodeEditPartAndUnpinned) + && isPinnedOrConsiderAs(dEdgeEditPart.getTarget(), notSelectedShapeNodeEditPartAndUnpinned)) { + notSelectedShapeNodeEditPartAndUnpinned.add(dEdgeEditPart); + } + } + } + } + + /** + * Tests whether an edit part is pinned or should be considered as pinned + * (fixed size and location) during the layout. + * + * @param editPart + * the edit part. + * @param editPartsConsiderAsPinned + * list of editParts that have to be consider as pinned + * @return <code>true</code> if the edit part is pinned or should be + * considered as pinned. + */ + protected boolean isPinnedOrConsiderAs(EditPart editPart, List<IDiagramElementEditPart> editPartsConsiderAsPinned) { + return editPart instanceof IGraphicalEditPart && (isPinned((IGraphicalEditPart) editPart) || editPartsConsiderAsPinned.contains(editPart)); + } + + /** + * Validate if the Arrange action is an Arrange Selection action. + * + * @param selectedObjects + * List of selected element + * @param diagramTopElements + * @return + */ + private boolean validateArrangeIsArrangeSelection(LinkedList<?> selectedObjects, List diagramTopElements) { + // We validate that we do not have the same element as the top diagram + // elements. + boolean result = selectedObjects.size() != diagramTopElements.size() || !selectedObjects.containsAll(diagramTopElements); + return result; + } + + /** + * Launches the primary arrange all that arrange all nodes. + * + * @param selectedObjects + * the objects to arrange. + * @param layoutHint + * the layout hint. + * @return the arrange command. + */ + protected Command lauchPrimaryArrangeAll(final List selectedObjects, final IAdaptable layoutHint) { + return initialLayoutProvider.layoutEditParts(selectedObjects, layoutHint); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/BorderItemAwareLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/BorderItemAwareLayoutProvider.java new file mode 100644 index 0000000000..df8d9101ba --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/BorderItemAwareLayoutProvider.java @@ -0,0 +1,1553 @@ +/******************************************************************************* + * Copyright (c) 2010, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Ray; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gef.EditPart; +import org.eclipse.gef.EditPartViewer; +import org.eclipse.gef.GraphicalEditPart; +import org.eclipse.gef.Request; +import org.eclipse.gef.RequestConstants; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.commands.UnexecutableCommand; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.common.core.service.IOperation; +import org.eclipse.gmf.runtime.diagram.core.util.ViewUtil; +import org.eclipse.gmf.runtime.diagram.ui.commands.ICommandProxy; +import org.eclipse.gmf.runtime.diagram.ui.commands.SetBoundsCommand; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderedShapeEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ShapeCompartmentEditPart; +import org.eclipse.gmf.runtime.diagram.ui.figures.BorderedNodeFigure; +import org.eclipse.gmf.runtime.diagram.ui.internal.commands.SetConnectionBendpointsCommand; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.ILayoutNode; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.ILayoutNodeOperation; +import org.eclipse.gmf.runtime.draw2d.ui.figures.IBorderItemLocator; +import org.eclipse.gmf.runtime.emf.core.util.EObjectAdapter; +import org.eclipse.gmf.runtime.gef.ui.figures.DefaultSizeNodeFigure; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Edge; +import org.eclipse.gmf.runtime.notation.LayoutConstraint; +import org.eclipse.gmf.runtime.notation.Location; +import org.eclipse.gmf.runtime.notation.Node; +import org.eclipse.gmf.runtime.notation.Size; +import org.eclipse.gmf.runtime.notation.View; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Collections2; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.business.internal.query.DNodeContainerExperimentalQuery; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramBorderNodeEditPart; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramNameEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDDiagramEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramContainerEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.AbstractDNodeContainerCompartmentEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DEdgeEditPart; +import org.eclipse.sirius.diagram.internal.operation.RegionContainerUpdateLayoutOperation; +import org.eclipse.sirius.diagram.tools.internal.graphical.edit.policies.ChangeBoundRequestRecorder; +import org.eclipse.sirius.diagram.tools.internal.part.SiriusDiagramGraphicalViewer; +import org.eclipse.sirius.diagram.ui.tools.api.figure.locator.DBorderItemLocator; +import org.eclipse.sirius.diagram.ui.tools.api.layout.PinHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.edit.command.CommandFactory; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.ArrangeAllWithAutoSize; + +/** + * Layout provider that arranges all border items after another layout provider + * (<code>initialLayoutProvider</code>) that arranges all the nodes (with + * ChangeBoundsCommand). + * + * The layout is made with several iterations. During each iterations, we store + * the port center location of this iteration and the previous one. We compare + * the current location with the two previous iteration to know if the port has + * been moved. If no port is moved, we stop the arrange process. + * + * @author ymortier + * @author lredor + */ +public class BorderItemAwareLayoutProvider extends AbstractLayoutProvider { + + /** + * Class to store the data of the previous iteration of the layout. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ + private static class BorderItemLayoutData { + /** + * The center location of the border item at the previous iteration. + */ + Point previousCenterLocation; + + /** + * The center location of the border item two iterations ago. + */ + Point previousPreviousCenterLocation; + + /** + * True if the border item is considered has moved, false otherwise. + */ + boolean isMoved; + + protected Point getPreviousPreviousCenterLocation() { + return previousPreviousCenterLocation; + } + + protected void setPreviousPreviousCenterLocation(Point previousPreviousCenterLocation) { + this.previousPreviousCenterLocation = previousPreviousCenterLocation; + } + + protected Point getPreviousCenterLocation() { + return previousCenterLocation; + } + + protected void setPreviousCenterLocation(Point newCenterLocation) { + setPreviousPreviousCenterLocation(getPreviousCenterLocation()); + this.previousCenterLocation = newCenterLocation; + } + + protected boolean isMoved() { + return isMoved; + } + + protected void setMoved(boolean moved) { + this.isMoved = moved; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + String result = ""; + if (isMoved) { + result += "Border item has been moved since previous layout."; + } else { + result += "Border item has not been moved since previous layout."; + } + result += " " + getPreviousCenterLocation(); + return result; + } + } + + /** + * Class that store data about the element that is on the other side of the + * border node. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ + private static class BorderItemOppositeElementData { + /** + * The center of the opposite element. + */ + Point center; + + /** + * The location of this element on its container if it's a border node + * (PositionConstants). Otherwise, this field equals + * PositionConstants.NONE + */ + int side; + + public BorderItemOppositeElementData(Point centerPoint) { + this(centerPoint, PositionConstants.NONE); + } + + public BorderItemOppositeElementData(Point centerPoint, int side) { + center = centerPoint; + this.side = side; + } + } + + /** + * An abstract comparator for all comparators managed by the opposite + * element of the bordered nodes. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ + private abstract class AbstractCoordinateComparator implements Comparator<IBorderItemEditPart> { + + /** + * Gives an OppositeElementData for each border item. + */ + Map<IBorderItemEditPart, BorderItemOppositeElementData> oppositeElementsDataByEditPart; + + /** + * Default constructor. + * + * @param oppositeElementsDataByEditPart + */ + public AbstractCoordinateComparator(final Map<IBorderItemEditPart, BorderItemOppositeElementData> oppositeElementsDataByEditPart) { + this.oppositeElementsDataByEditPart = oppositeElementsDataByEditPart; + } + } + + /** + * A comparator for border item on the east side of its parent. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ + private class EastCoordinateComparator extends AbstractCoordinateComparator { + /** + * Default constructor. + * + * @param vectorsByEditPart + */ + public EastCoordinateComparator(final Map<IBorderItemEditPart, BorderItemOppositeElementData> oppositeElementsDataByEditPart) { + super(oppositeElementsDataByEditPart); + } + + /** + * {@inheritDoc} + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(final IBorderItemEditPart o1, final IBorderItemEditPart o2) { + int result = 0; + final BorderItemOppositeElementData p1 = oppositeElementsDataByEditPart.get(o1); + final BorderItemOppositeElementData p2 = oppositeElementsDataByEditPart.get(o2); + + if (p1.center.y == p2.center.y) { + if (p1.side == PositionConstants.NORTH) { + result = p1.center.x < p2.center.x ? 1 : -1; + } else { + result = p1.center.x > p2.center.x ? 1 : -1; + } + } else { + result = p1.center.y > p2.center.y ? 1 : -1; + } + return result; + } + } + + /** + * A comparator for border item on the south side of its parent. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ + private class SouthCoordinateComparator extends AbstractCoordinateComparator { + /** + * Default constructor. + * + * @param vectorsByEditPart + */ + public SouthCoordinateComparator(final Map<IBorderItemEditPart, BorderItemOppositeElementData> targetPointsByEditPart) { + super(targetPointsByEditPart); + } + + /** + * {@inheritDoc} + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(final IBorderItemEditPart o1, final IBorderItemEditPart o2) { + int result = 0; + final BorderItemOppositeElementData p1 = oppositeElementsDataByEditPart.get(o1); + final BorderItemOppositeElementData p2 = oppositeElementsDataByEditPart.get(o2); + + if (p1.center.x == p2.center.x) { + if (p1.side == PositionConstants.WEST) { + result = p1.center.y < p2.center.y ? 1 : -1; + } else { + result = p1.center.y > p2.center.y ? 1 : -1; + } + } else { + result = p1.center.x > p2.center.x ? 1 : -1; + } + return result; + } + } + + /** + * A comparator for border item on the west side of its parent. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ + private class WestCoordinateComparator extends AbstractCoordinateComparator { + /** + * Default constructor. + * + * @param vectorsByEditPart + */ + public WestCoordinateComparator(final Map<IBorderItemEditPart, BorderItemOppositeElementData> targetPointsByEditPart) { + super(targetPointsByEditPart); + } + + /** + * {@inheritDoc} + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(final IBorderItemEditPart o1, final IBorderItemEditPart o2) { + int result = 0; + final BorderItemOppositeElementData p1 = oppositeElementsDataByEditPart.get(o1); + final BorderItemOppositeElementData p2 = oppositeElementsDataByEditPart.get(o2); + + if (p1.center.y == p2.center.y) { + if (p1.side == PositionConstants.NORTH) { + result = p1.center.x > p2.center.x ? 1 : -1; + } else { + result = p1.center.x < p2.center.x ? 1 : -1; + } + } else { + result = p1.center.y > p2.center.y ? 1 : -1; + } + return result; + } + } + + /** + * A comparator for border item on the north side of its parent. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ + private class NorthCoordinateComparator extends AbstractCoordinateComparator { + /** + * Default constructor. + * + * @param vectorsByEditPart + */ + public NorthCoordinateComparator(final Map<IBorderItemEditPart, BorderItemOppositeElementData> targetPointsByEditPart) { + super(targetPointsByEditPart); + } + + /** + * {@inheritDoc} + * + * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) + */ + public int compare(final IBorderItemEditPart o1, final IBorderItemEditPart o2) { + int result = 0; + final BorderItemOppositeElementData p1 = oppositeElementsDataByEditPart.get(o1); + final BorderItemOppositeElementData p2 = oppositeElementsDataByEditPart.get(o2); + + if (p1.center.x == p2.center.x) { + if (p1.side == PositionConstants.WEST) { + result = p1.center.y > p2.center.y ? 1 : -1; + } else { + result = p1.center.y < p2.center.y ? 1 : -1; + } + } else { + result = p1.center.x > p2.center.x ? 1 : -1; + } + return result; + } + } + + /** + * The margin of the bordered nodes : + * <UL> + * <LI>half on the right and half on the left</LI> + * <LI>or half on the top and half on the bottom.</LI> + * </UL> + */ + public static final int MARGIN = 16; + + private static final int MAX_ITERATIONS = 10; + + /** + * The initial layout provider that arrange the nodes (launch before the + * arrange of bordered nodes). + */ + AbstractLayoutProvider initialLayoutProvider; + + /** + * Tell if the normal arrange process will be called before the border item + * arrange. + */ + boolean launchNormalArrange; + + /** + * Stores the location of each border edit part compute during the previous + * iteration. + */ + Map<IBorderItemEditPart, BorderItemLayoutData> previousIterationDatasbyEditPart = new HashMap<IBorderItemEditPart, BorderItemLayoutData>(); + + private Predicate<Object> validateAllElementInArrayListAreIDiagramElementEditPart = new Predicate<Object>() { + + public boolean apply(Object input) { + return input instanceof IDiagramElementEditPart; + } + }; + + /** + * The default constructor. + * + * @param clp + * The layout provider to call before calling the layout of the + * border items. + */ + public BorderItemAwareLayoutProvider(final AbstractLayoutProvider clp) { + initialLayoutProvider = clp; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider#layoutEditParts(java.util.List, + * org.eclipse.core.runtime.IAdaptable) + */ + @Override + public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) { + return layoutEditParts(selectedObjects, layoutHint, true); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.business.api.layout.provider.AbstractLayoutProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) + */ + @Override + public boolean provides(final IOperation operation) { + boolean result = true; + if (operation instanceof ILayoutNodeOperation) { + final ILayoutNodeOperation layoutNodeOperation = (ILayoutNodeOperation) operation; + for (ILayoutNode layoutNode : (Iterable<ILayoutNode>) layoutNodeOperation.getLayoutNodes()) { + final Node node = layoutNode.getNode(); + final EObject semanticElement = ViewUtil.resolveSemanticElement(node); + if (semanticElement instanceof DDiagramElement) { + final DDiagram diagram = ((DDiagramElement) semanticElement).getParentDiagram(); + if (diagram.getDescription().getLayout() != null) { + result = false; + } + } else if (!(semanticElement instanceof DSemanticDecorator)) { + result = false; + } + } + } + return result; + } + + /** + * Layout this list of selected objects, using the specified layout hint. + * The selected objects all reside within the same parent container. Other + * elements that are part of the container but not specified in the list of + * objects, are ignored. + * + * @param selectedObjects + * <code>List</code> of <code>EditPart</code> objects that are to + * be layed out. + * @param layoutHint + * <code>IAdaptable</code> hint to the provider to determine the + * layout kind. + * @param normalArrangeMustBeCalled + * Tell if the normal arrange process must be called before the + * border item arrange + * @return <code>Command</code> that when executed will layout the edit + * parts in the container + */ + public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint, final boolean normalArrangeMustBeCalled) { + this.launchNormalArrange = normalArrangeMustBeCalled; + + if (selectedObjects.isEmpty()) { + return UnexecutableCommand.INSTANCE; + } + + CompoundCommand result = new CompoundCommand(); + + if (launchNormalArrange) { + // Create a request recorder to record all ChangeBounds requests by + // editparts. + final EditPartViewer root = ((EditPart) selectedObjects.get(0)).getViewer(); + if (root instanceof SiriusDiagramGraphicalViewer) { + final ChangeBoundRequestRecorder recorder = ((SiriusDiagramGraphicalViewer) root).getChangeBoundRequestRecorder(); + recorder.startRecording(); + result.add(lauchPrimaryArrangeAll(selectedObjects, layoutHint)); + recorder.stopRecording(); + registerChangeBoundsCommand(recorder); + recorder.dispose(); + } + } + + // Finds if there are unpinned diagram elements to keep fixed stored in + // the LayoutHint as a Collection + ArrayList<IDiagramElementEditPart> elementsToKeepFixed = Lists.newArrayList(); + if (layoutHint.getAdapter(Collection.class) instanceof ArrayList<?> + && Iterables.all((ArrayList<?>) layoutHint.getAdapter(Collection.class), validateAllElementInArrayListAreIDiagramElementEditPart)) { + elementsToKeepFixed = (ArrayList<IDiagramElementEditPart>) layoutHint.getAdapter(Collection.class); + } + + // Create the specific command to layout the border items. + final Command layoutBorderItems = layoutBorderItems(selectedObjects, 1, elementsToKeepFixed); + if (layoutBorderItems != null && layoutBorderItems.canExecute()) { + result.add(layoutBorderItems); + } + + resetBoundsOfPinnedElements(selectedObjects, result, elementsToKeepFixed); + this.getViewsToChangeBoundsRequest().clear(); + if (result.size() == 0) { + result = null; // removeCommandsForPinnedElements(result); + } + return result; + } + + /** + * Launches the primary arrange all that arrange all nodes. + * + * @param selectedObjects + * the objects to arrange. + * @param layoutHint + * the layout hint. + * @return the arrange command. + */ + protected Command lauchPrimaryArrangeAll(final List selectedObjects, final IAdaptable layoutHint) { + return initialLayoutProvider.layoutEditParts(selectedObjects, layoutHint); + } + + /** + * Register all the change bounds command recording during the initial + * layout (layout without moving ports). + * + * @param recorder + * The request recorder + */ + protected void registerChangeBoundsCommand(final ChangeBoundRequestRecorder recorder) { + for (Entry<EditPart, ChangeBoundsRequest> entry : recorder.getAllRequests().entries()) { + final EditPart editPart = entry.getKey(); + if (editPart instanceof IGraphicalEditPart) { + ChangeBoundsRequest cbr = entry.getValue(); + final List<EditPart> editParts = cbr.getEditParts(); + if (editParts != null) { + for (EditPart ep : editParts) { + final View v = ((IGraphicalEditPart) ep).getNotationView(); + List<Request> requests = this.getViewsToChangeBoundsRequest().get(v); + if (requests == null) { + requests = new LinkedList<Request>(); + this.getViewsToChangeBoundsRequest().put(v, requests); + } + requests.add(cbr); + } + } + } + } + } + + /** + * Reset the size and location of the pinned elements to there values they + * have before the arrange process. + * + * @param selectedObjects + * The selected elements + * @param compoundCommand + * Contains all the commands to execute at the end of the layout. + * @param elementsToKeepFixed + * IDiagramElementEditPart which are not actually pinned but have + * to stay fixed + */ + private void resetBoundsOfPinnedElements(final List selectedObjects, final CompoundCommand compoundCommand, ArrayList<IDiagramElementEditPart> elementsToKeepFixed) { + final String commandName = "Set Bounds"; + for (IGraphicalEditPart graphicalEditPart : Iterables.filter(selectedObjects, IGraphicalEditPart.class)) { + EObject semanticElement = graphicalEditPart.resolveSemanticElement(); + if (semanticElement instanceof DDiagramElement) { + DDiagramElement dDiagramElement = (DDiagramElement) semanticElement; + if (new PinHelper().isPinned(dDiagramElement) || (elementsToKeepFixed != null && elementsToKeepFixed.contains(graphicalEditPart))) { + final TransactionalEditingDomain editingDomain = TransactionUtil.getEditingDomain(semanticElement); + View notationView = graphicalEditPart.getNotationView(); + if (notationView instanceof Node) { + final Node node = (Node) notationView; + resetBounds(compoundCommand, commandName, node, editingDomain); + } + + //Keep the GMF model consistent for pinned RegionContainer and Regions + if (graphicalEditPart instanceof IDiagramContainerEditPart && dDiagramElement instanceof DNodeContainer + && new DNodeContainerExperimentalQuery((DNodeContainer) dDiagramElement).isRegionContainer()) { + AbstractDNodeContainerCompartmentEditPart comp = Iterables.getFirst(Iterables.filter(graphicalEditPart.getChildren(), AbstractDNodeContainerCompartmentEditPart.class), null); + if (comp != null && comp.getNotationView() != null) { + for (Node region : Iterables.filter(comp.getChildren(), Node.class)) { + resetBounds(compoundCommand, commandName, region, editingDomain); + } + } + compoundCommand.add(new ICommandProxy(CommandFactory.createICommand(graphicalEditPart.getEditingDomain(), + new RegionContainerUpdateLayoutOperation((Node) graphicalEditPart.getModel())))); + } + } + } + } + } + + private void resetBounds(final CompoundCommand compoundCommand, final String commandName, Node node, TransactionalEditingDomain editingDomain) { + final EObjectAdapter objectAdapter = new EObjectAdapter(node); + + final LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Bounds) { + final Bounds bounds = (Bounds) layoutConstraint; + final SetBoundsCommand setBoundsCommand = new SetBoundsCommand(editingDomain, commandName, objectAdapter, + new Rectangle(bounds.getX(), bounds.getY(), bounds.getWidth(), bounds.getHeight())); + compoundCommand.add(new ICommandProxy(setBoundsCommand)); + } else if (layoutConstraint instanceof Location) { + final Location location = (Location) layoutConstraint; + final SetBoundsCommand setBoundsCommand = new SetBoundsCommand(editingDomain, commandName, objectAdapter, new Point(location.getX(), location.getY())); + compoundCommand.add(new ICommandProxy(setBoundsCommand)); + } else if (layoutConstraint instanceof Size) { + final Size size = (Size) layoutConstraint; + final SetBoundsCommand setBoundsCommand = new SetBoundsCommand(editingDomain, commandName, objectAdapter, new Dimension(size.getWidth(), size.getHeight())); + compoundCommand.add(new ICommandProxy(setBoundsCommand)); + } + } + + /** + * Method getBendpointsChangedCommand Different signature method that allows + * a command to constructed for changing the bendpoints without requiring + * the original Request. + * + * @param connection + * Connection to generate the bendpoints changed command from + * @param edge + * notation element that the command will operate on. + * @param editingDomain + * the concern editing domain + * @return Command SetBendpointsCommand that contains the point changes for + * the connection. + */ + protected Command getBendpointsChangedCommand(Connection connection, Edge edge, TransactionalEditingDomain editingDomain) { + Point ptRef1 = connection.getSourceAnchor().getReferencePoint(); + connection.translateToRelative(ptRef1); + + Point ptRef2 = connection.getTargetAnchor().getReferencePoint(); + connection.translateToRelative(ptRef2); + + SetConnectionBendpointsCommand sbbCommand = new SetConnectionBendpointsCommand(editingDomain); + sbbCommand.setEdgeAdapter(new EObjectAdapter(edge)); + sbbCommand.setNewPointList(connection.getPoints(), ptRef1, ptRef2); + + return new ICommandProxy(sbbCommand); + } + + /** + * Layout all the border items of the selected elements. + * + * @param selectedObjects + * The selected elements + * @param elementsToKeepFixed + * @param launchNormalArrange + * Tell if the normal arrange process will be called before the + * border item arrange + * @param elementsToKeepFixed + * IDiagramElementEditPart which are not actually pinned but have + * to stay fixed + * @return The command to execute to layout the border items. + */ + private Command layoutBorderItems(final List<?> selectedObjects, final int nbIterations, ArrayList<IDiagramElementEditPart> elementsToKeepFixed) { + CompoundCommand cc = new CompoundCommand(); + for (Object object : selectedObjects) { + if (object instanceof GraphicalEditPart) { + final Command layoutBorderItems = layoutBorderItems((GraphicalEditPart) object, elementsToKeepFixed); + if (layoutBorderItems != null && layoutBorderItems.canExecute()) { + cc.add(layoutBorderItems); + } + } + } + if (hasBeenMovedBorderItemsDuringLastIteration() && nbIterations < MAX_ITERATIONS) { + // Remove all the request not called (because we compute again best + // locations) + removeRequestsOfThisCommand(cc); + // We try to optimize the border items location with the previous + // compute locations (record in + cc = (CompoundCommand) layoutBorderItems(selectedObjects, nbIterations + 1, elementsToKeepFixed); + } + clearBorderItemLocations(); + return cc; + } + + /** + * Remove the request corresponding to this command of the map that maps all + * views with a its associated {@link ChangeBoundsRequest}. + * + * @param cc + * The compoundCommand that is not executed and for which we + * wan't to remove the corresponding request. + */ + private void removeRequestsOfThisCommand(CompoundCommand cc) { + for (Object childCommand : cc.getCommands()) { + if (childCommand instanceof CompoundCommand) { + removeRequestsOfThisCommand((CompoundCommand) childCommand); + } else if (childCommand instanceof CommandWrapper) { + CommandWrapper wrap = (CommandWrapper) childCommand; + if (wrap.getEditPart() instanceof IGraphicalEditPart) { + List<Request> requests = getViewsToChangeBoundsRequest().get(((IGraphicalEditPart) wrap.getEditPart()).getNotationView()); + if (requests != null) { + requests.remove(wrap.getRequest()); + } + } + } + } + } + + /** + * @return true if almost one of the border items has been moved during the + * last iteration, false otherwise. + */ + private boolean hasBeenMovedBorderItemsDuringLastIteration() { + for (IBorderItemEditPart borderItemEditPart : previousIterationDatasbyEditPart.keySet()) { + if (previousIterationDatasbyEditPart.get(borderItemEditPart).isMoved()) { + return true; + } + } + return false; + } + + /** + * Clear the data of border items of the previous iterations. + */ + private void clearBorderItemLocations() { + previousIterationDatasbyEditPart.clear(); + } + + /** + * Layout all the border items of this graphicalEditPart. + * + * @param graphicalEditPart + * The current element to deal with launchNormalArrange + * @param elementsToKeepFixed + * @param launchNormalArrange + * Tell if the normal arrange process will be called before the + * border item arrange + * @param elementsToKeepFixed + * IDiagramElementEditPart which are not actually pinned but have + * to stay fixed + * @return The command to execute to layout the border items of this + * graphical edit part. + */ + private Command layoutBorderItems(final GraphicalEditPart graphicalEditPart, ArrayList<IDiagramElementEditPart> elementsToKeepFixed) { + final CompoundCommand result = new CompoundCommand(); + if (graphicalEditPart instanceof IBorderedShapeEditPart) { + final IBorderedShapeEditPart borderedEditPart = (IBorderedShapeEditPart) graphicalEditPart; + if (borderedEditPart.getBorderedFigure() != null && !borderedEditPart.getBorderedFigure().getBorderItemContainer().getChildren().isEmpty()) { + final Command layoutBorderItems = layoutBorderItems(borderedEditPart, elementsToKeepFixed); + if (layoutBorderItems != null && layoutBorderItems.canExecute()) { + result.add(layoutBorderItems); + } + } + + } + for (Object editPart : graphicalEditPart.getChildren()) { + if (editPart instanceof GraphicalEditPart) { + final Command layoutBorderItems = layoutBorderItems((GraphicalEditPart) editPart, elementsToKeepFixed); + if (layoutBorderItems != null && layoutBorderItems.canExecute()) { + result.add(layoutBorderItems); + } + } + } + return result; + } + + /** + * Layout all the border items of this borderedShapeEditPart. + * + * @param borderedShapeEditPart + * The current element to deal with + * @param launchNormalArrange + * Tell if the normal arrange process will be called before the + * border item arrange + * @param elementsToKeepFixed + * IDiagramElementEditPart which are not actually pinned but have + * to stay fixed + * @return The command to execute to layout the border items of this + * graphical edit part. + */ + private Command layoutBorderItems(final IBorderedShapeEditPart borderedShapeEditPart, ArrayList<IDiagramElementEditPart> elementsToKeepFixed) { + CompoundCommand resCommand = null; + if (borderedShapeEditPart instanceof IGraphicalEditPart) { + + IGraphicalEditPart castedEditPart = (IGraphicalEditPart) borderedShapeEditPart; + if (borderedShapeEditPart.getMainFigure() != null) { + resCommand = new CompoundCommand(); + + // Get the zoom level + double scale = 1.0; + if (castedEditPart.getRoot() instanceof DiagramRootEditPart) { + final ZoomManager zoomManager = ((DiagramRootEditPart) castedEditPart.getRoot()).getZoomManager(); + scale = zoomManager.getZoom(); + } + + // Get the bounds of the container after arrange all (the + // container of the border node). + final Rectangle containerBoundsAfterArrangeAll = getBounds(castedEditPart, scale); + final Point containerCenterAfterArrangeAll = containerBoundsAfterArrangeAll.getCenter(); + + // Use the center of the opposite element to determine the ray + // (this ray is then use to determine the side on which to put + // the border node) + final Map<IBorderItemEditPart, Ray> headings = new HashMap<IBorderItemEditPart, Ray>(); + for (Object child : castedEditPart.getChildren()) { + if (child instanceof IBorderItemEditPart) { + if (!isPinned((IBorderItemEditPart) child) && !(elementsToKeepFixed != null && elementsToKeepFixed.contains(child))) { + computeHeading((IBorderItemEditPart) child, containerCenterAfterArrangeAll, scale, headings); + } + } + } + + final Point topLeft = containerBoundsAfterArrangeAll.getTopLeft(); + // Make some trigonometry calculations to know which ports must + // be on top, bottom, right, left border of their container. + final double absoluteCos = Math.abs(BorderItemAwareLayoutProvider.cos(new Ray(Math.abs(containerCenterAfterArrangeAll.x - topLeft.x), Math.abs(containerCenterAfterArrangeAll.y + - topLeft.y)))); + final double absoluteSin = Math.abs(BorderItemAwareLayoutProvider.sin(new Ray(Math.abs(containerCenterAfterArrangeAll.x - topLeft.x), Math.abs(containerCenterAfterArrangeAll.y + - topLeft.y)))); + + final List<IBorderItemEditPart> tops = getNorthBorderItems(headings, absoluteCos, scale, containerCenterAfterArrangeAll); + final List<IBorderItemEditPart> bottoms = getSouthBorderItems(headings, absoluteCos, scale, containerCenterAfterArrangeAll); + final List<IBorderItemEditPart> rights = getEastBorderItems(headings, absoluteSin, scale, containerCenterAfterArrangeAll); + final List<IBorderItemEditPart> lefts = getWestBorderItems(headings, absoluteSin, scale, containerCenterAfterArrangeAll); + + unfixLocator(tops); + unfixLocator(bottoms); + unfixLocator(rights); + unfixLocator(lefts); + + final Command topCommand = layoutItems(tops, containerBoundsAfterArrangeAll, PositionConstants.NORTH, scale); + if (topCommand != null && topCommand.canExecute()) { + resCommand.add(topCommand); + } + final Command bottomCommand = layoutItems(bottoms, containerBoundsAfterArrangeAll, PositionConstants.SOUTH, scale); + if (bottomCommand != null && bottomCommand.canExecute()) { + resCommand.add(bottomCommand); + } + final Command rightCommand = layoutItems(rights, containerBoundsAfterArrangeAll, PositionConstants.EAST, scale); + if (rightCommand != null && rightCommand.canExecute()) { + resCommand.add(rightCommand); + } + final Command leftCommand = layoutItems(lefts, containerBoundsAfterArrangeAll, PositionConstants.WEST, scale); + if (leftCommand != null && leftCommand.canExecute()) { + resCommand.add(leftCommand); + } + for (IBorderItemEditPart topBorderItemEditPart : tops) { + if (topBorderItemEditPart.getSourceConnections().size() > 0) { + final Object connection = topBorderItemEditPart.getSourceConnections().get(0); + if (connection instanceof DEdgeEditPart) { + final DEdgeEditPart viewEdgeEditPart = (DEdgeEditPart) connection; + viewEdgeEditPart.getPrimaryShape().refreshLine(); + } + } else if (topBorderItemEditPart.getTargetConnections().size() > 0) { + final Object connection = topBorderItemEditPart.getTargetConnections().get(0); + if (connection instanceof DEdgeEditPart) { + final DEdgeEditPart viewEdgeEditPart = (DEdgeEditPart) connection; + viewEdgeEditPart.refresh(); + } + } + } + } + } + return resCommand; + } + + private void computeHeading(IBorderItemEditPart borderItemEditPart, Point containerCenterAfterArrangeAll, double scale, Map<IBorderItemEditPart, Ray> headings) { + final Ray heading = getHeading(borderItemEditPart, containerCenterAfterArrangeAll, scale); + if (heading != null) { + headings.put(borderItemEditPart, heading); + } + } + + /** + * Layout all the border items on the location (N, S, E or W). + * + * @param items + * A list of border items to layout + * @param containerBounds + * The bounds of the container after arrange all + * @param position + * The position of items on its container. Possible values can be + * found in {@link PositionConstants} and include NORTH, SOUTH, + * EAST and WEST. + * @param zoomScale + * The scale of the diagram + * @return Command A command to layout all items. + */ + protected Command layoutItems(final List<IBorderItemEditPart> items, final Rectangle containerBounds, final int position, final double zoomScale) { + + final CompoundCommand res = new CompoundCommand(); + + final boolean width = position == PositionConstants.NORTH || position == PositionConstants.SOUTH; + + final int availableSpace = width ? containerBounds.width : containerBounds.height; + + final int between = (int) ((availableSpace - getSize(items, width, zoomScale) - MARGIN) / (float) items.size()); + + int current = MARGIN / 2; + + for (IBorderItemEditPart borderItemEditPart : items) { + final ChangeBoundsRequest request = new ChangeBoundsRequest(RequestConstants.REQ_MOVE); + Point newLocation; + switch (position) { + case PositionConstants.NORTH: + newLocation = new Point(current, 0); + break; + case PositionConstants.SOUTH: + newLocation = new Point(current, containerBounds.height); + break; + case PositionConstants.EAST: + newLocation = new Point(containerBounds.width, current); + break; + case PositionConstants.WEST: + newLocation = new Point(0, current); + break; + default: + throw new IllegalArgumentException("Invalid items position."); + } + + newLocation = newLocation.getTranslated(containerBounds.getTopLeft()); + // Store the location compute for this border item during this + // iteration + addBorderItemData(borderItemEditPart, newLocation); + + request.setEditParts(borderItemEditPart); + request.setLocation(newLocation); + final Rectangle boundsBorderItem = getBounds(borderItemEditPart, zoomScale); + final Dimension difference = newLocation.getDifference(boundsBorderItem.getTopLeft()); + request.setMoveDelta(new Point(difference.width, difference.height)); + final Command command = this.buildCommandWrapper(request, borderItemEditPart); + + res.add(command); + + current += between + (int) ((width ? boundsBorderItem.width : boundsBorderItem.height) * zoomScale); + + } + return res; + } + + /** + * Store the location compute for this border item during this iteration. + * + * @param borderItemEditPart + * The concerned border item + * @param newLocation + * The new location computed during the last iteration + */ + private void addBorderItemData(IBorderItemEditPart borderItemEditPart, Point newLocation) { + BorderItemLayoutData data = previousIterationDatasbyEditPart.get(borderItemEditPart); + if (data == null) { + data = new BorderItemLayoutData(); + data.setMoved(true); + data.setPreviousCenterLocation(newLocation); + } else { + boolean alreadyUsedLocation = data.getPreviousCenterLocation().equals(newLocation); + if (!alreadyUsedLocation) { + alreadyUsedLocation = data.getPreviousPreviousCenterLocation() != null && data.getPreviousPreviousCenterLocation().equals(newLocation); + } + data.setPreviousCenterLocation(newLocation); + if (alreadyUsedLocation) { + data.setMoved(false); + } else { + data.setMoved(true); + } + } + previousIterationDatasbyEditPart.put(borderItemEditPart, data); + } + + /** + * Consider all the border item locators with free location (ie the location + * of the border item will be recomputed). + * + * @param editParts + * The border items to reset + */ + private void unfixLocator(final List<IBorderItemEditPart> editParts) { + for (IBorderItemEditPart borderItemEditPart : editParts) { + final IBorderItemLocator borderItemLocator = borderItemEditPart.getBorderItemLocator(); + if (borderItemLocator instanceof DBorderItemLocator) { + ((DBorderItemLocator) borderItemLocator).unfix(); + } + } + } + + private int getSize(final List<IBorderItemEditPart> tops, final boolean width, final double zoomScale) { + int size = 0; + + for (IBorderItemEditPart editPart : tops) { + int editPartSize = (int) (((GraphicalEditPart) editPart).getFigure().getBounds().width * zoomScale); + if (!width) { + editPartSize = (int) (((GraphicalEditPart) editPart).getFigure().getBounds().height * zoomScale); + } + size += editPartSize; + } + return size; + } + + /** + * Compute the cosinus of a vector. + * + * @param ray + * The vector + * @return The cosinus value of the vector + */ + private static double cos(final Ray ray) { + return ray.x / Math.sqrt(Math.pow(ray.x, 2) + Math.pow(ray.y, 2)); + } + + /** + * Compute the sinus of a vector. + * + * @param ray + * The vector + * @return The sinus value of the vector + */ + private static double sin(final Ray ray) { + return ray.y / Math.sqrt(Math.pow(ray.x, 2) + Math.pow(ray.y, 2)); + } + + /** + * + * @param parts + * @param scale + * @return + */ + private Map<IBorderItemEditPart, BorderItemOppositeElementData> getOppositeElementsData(List<IBorderItemEditPart> parts, double scale) { + final Map<IBorderItemEditPart, BorderItemOppositeElementData> targetPoints = new HashMap<IBorderItemEditPart, BorderItemOppositeElementData>(); + for (IBorderItemEditPart borderItemEditPart : parts) { + final BorderItemOppositeElementData oppositeElementData = getOppositeElementData(borderItemEditPart, scale); + if (oppositeElementData != null) { + targetPoints.put(borderItemEditPart, oppositeElementData); + } + } + return targetPoints; + } + + /** + * Returns the list of border items to be at the North position. This border + * items are sorted. To sort this element we compute again the rays with a + * new distant point to be more precise on the order. + * + * @param headings + * List of vector by border items + * @param containerAbsoluteCos + * The absolute cos of the container + * @param containerCenter + * the center of the container of the border items + * @return the list of border items to be at the North position. + */ + private List<IBorderItemEditPart> getNorthBorderItems(final Map<IBorderItemEditPart, Ray> headings, final double containerAbsoluteCos, double scale, final Point containerCenter) { + + final List<IBorderItemEditPart> parts = new LinkedList<IBorderItemEditPart>(); + + for (Map.Entry<IBorderItemEditPart, Ray> entry : headings.entrySet()) { + final Ray ray = entry.getValue(); + if (ray.y != 0) { + + final double cos = BorderItemAwareLayoutProvider.cos(ray); + + if (Math.abs(cos) < containerAbsoluteCos && ray.y < 0) { + parts.add(entry.getKey()); + } + } + } + + final Map<IBorderItemEditPart, BorderItemOppositeElementData> oppositeElementsDataByEditPart = getOppositeElementsData(parts, scale); + Collections.sort(parts, new NorthCoordinateComparator(oppositeElementsDataByEditPart)); + + return parts; + + } + + /** + * Returns the list of border items to be at the South position. + * + * @param headings + * List of vector by border items + * @param containerAbsoluteCos + * The absolute cos of the container + * @return the list of border items to be at the South position. + */ + private List<IBorderItemEditPart> getSouthBorderItems(final Map<IBorderItemEditPart, Ray> headings, final double absoluteCos, double scale, final Point containerCenter) { + + final List<IBorderItemEditPart> parts = new LinkedList<IBorderItemEditPart>(); + + for (Map.Entry<IBorderItemEditPart, Ray> entry : headings.entrySet()) { + final Ray ray = entry.getValue(); + if (ray.y != 0) { + final double cos = BorderItemAwareLayoutProvider.cos(ray); + + if (Math.abs(cos) < absoluteCos && ray.y > 0) { + parts.add(entry.getKey()); + } + } + } + + final Map<IBorderItemEditPart, BorderItemOppositeElementData> oppositeElementsDataByEditPart = getOppositeElementsData(parts, scale); + Collections.sort(parts, new SouthCoordinateComparator(oppositeElementsDataByEditPart)); + + return parts; + } + + /** + * Returns the list of border items to be at the East position. + * + * @param headings + * List of vector by border items + * @param containerAbsoluteSin + * The absolute sinus of the container + * @return the list of border items to be at the East position. + */ + private List<IBorderItemEditPart> getEastBorderItems(final Map<IBorderItemEditPart, Ray> headings, final double containerAbsoluteSin, double scale, final Point containerCenter) { + + final List<IBorderItemEditPart> parts = new LinkedList<IBorderItemEditPart>(); + + for (Map.Entry<IBorderItemEditPart, Ray> entry : headings.entrySet()) { + final Ray ray = entry.getValue(); + + if (ray.x != 0) { + final double sin = BorderItemAwareLayoutProvider.sin(ray); + + if (Math.abs(sin) < containerAbsoluteSin && ray.x > 0) { + parts.add(entry.getKey()); + } + } + + } + + final Map<IBorderItemEditPart, BorderItemOppositeElementData> targetPoints = getOppositeElementsData(parts, scale); + Collections.sort(parts, new EastCoordinateComparator(targetPoints)); + + return parts; + } + + /** + * Returns the list of border items to be at the West position. + * + * @param headings + * List of vector by border items + * @param containerAbsoluteSin + * The absolute sinus of the container + * @return the list of border items to be at the West position. + */ + private List<IBorderItemEditPart> getWestBorderItems(final Map<IBorderItemEditPart, Ray> headings, final double containerAbsoluteSin, double scale, final Point containerCenter) { + + final List<IBorderItemEditPart> parts = new LinkedList<IBorderItemEditPart>(); + + for (Map.Entry<IBorderItemEditPart, Ray> entry : headings.entrySet()) { + final Ray ray = entry.getValue(); + if (ray.x != 0) { + final double sin = BorderItemAwareLayoutProvider.sin(ray); + + if (Math.abs(sin) < containerAbsoluteSin && ray.x < 0) { + parts.add(entry.getKey()); + } + } + } + + final Map<IBorderItemEditPart, BorderItemOppositeElementData> oppositeElementsDataByEditPart = getOppositeElementsData(parts, scale); + Collections.sort(parts, new WestCoordinateComparator(oppositeElementsDataByEditPart)); + + return parts; + } + + /** + * Get heading (vector, angle, ...) between the center of the edit part and + * the center of the edit part at the other side of the edge. Return null if + * : + * <UL> + * <LI>there is no edge that is come from or go back to + * <code>editPart</code></LI> + * <LI>there is many edges that is come from or go back to + * <code>editPart</code> + * <LI> + * + * @param editPart + * The editPart of the current border item + * @param containerCenterAfterArrange + * The center of the border item parent after arrangeAll + * @param scale + * The scale of the current diagram + * @param launchNormalArrange + * Tell if the normal arrange process will be called before the + * border item arrange + * @return A vector representing the edge + */ + private Ray getHeading(final IBorderItemEditPart editPart, final Point containerCenterAfterArrange, final double scale) { + final Point targetPoint = getTargetPoint(editPart, scale); + if (targetPoint != null) { + return new Ray(containerCenterAfterArrange, targetPoint); + } + return null; + } + + /** + * @param editPart + * @param scale + * @param targetPoint + * @return + */ + private Point getTargetPoint(final IBorderItemEditPart editPart, final double scale) { + Point targetPoint = null; + final GraphicalEditPart target = getTarget(editPart); + + if (target != null && editPart != target && !isAncestor(editPart, target) && !isAncestor(target, editPart)) { + if (previousIterationDatasbyEditPart.get(target) != null) { + targetPoint = previousIterationDatasbyEditPart.get(target).getPreviousCenterLocation(); + } else { + targetPoint = getBounds((IGraphicalEditPart) target, scale).getCenter(); + } + } + return targetPoint; + } + + private BorderItemOppositeElementData getOppositeElementData(final IBorderItemEditPart editPart, final double scale) { + BorderItemOppositeElementData oppositeElementData = null; + final GraphicalEditPart target = getTarget(editPart); + + if (target != null && editPart != target && !isAncestor(editPart, target) && !isAncestor(target, editPart)) { + Point targetPoint; + if (previousIterationDatasbyEditPart.get(target) != null) { + targetPoint = previousIterationDatasbyEditPart.get(target).getPreviousCenterLocation(); + } else { + targetPoint = getBounds((IGraphicalEditPart) target, scale).getCenter(); + } + if (target instanceof IBorderItemEditPart) { + oppositeElementData = new BorderItemOppositeElementData(targetPoint, DBorderItemLocator.findClosestSideOfParent(new Rectangle(targetPoint, new Dimension(1, 1)), + getBounds((IGraphicalEditPart) target.getParent(), scale))); + } else { + oppositeElementData = new BorderItemOppositeElementData(targetPoint); + } + } + return oppositeElementData; + } + + /** + * Return the edit part that is at the other side of the edge. Return null + * if : + * <UL> + * <LI>there is no edge that is come from or go back to + * <code>editPart</code></LI> + * <LI>there is many edges that is come from or go back to + * <code>editPart</code> + * <LI> + * + * @param editPart + * A border edit part that is on an extremity of an edge. + * @return the opposite graphical edit part + */ + private GraphicalEditPart getTarget(final IBorderItemEditPart editPart) { + GraphicalEditPart target = null; + if (editPart.getSourceConnections().size() == 1 && editPart.getTargetConnections().isEmpty()) { + target = (GraphicalEditPart) ((ConnectionEditPart) editPart.getSourceConnections().get(0)).getTarget(); + } else if (editPart.getSourceConnections().isEmpty() && editPart.getTargetConnections().size() == 1) { + target = (GraphicalEditPart) ((ConnectionEditPart) editPart.getTargetConnections().get(0)).getSource(); + } + return target; + } + + /** + * Check if <code>childCandidate</code> is contained in + * <code>parentCandidate</code>. + * + * @param childCandidate + * The child candidate + * @param parentCandidate + * The parent candidate + * @return true if parentCandidate contains the childCandidate. + */ + private boolean isAncestor(final EditPart childCandidate, final EditPart parentCandidate) { + EditPart currentParent = childCandidate.getParent(); + while (currentParent != null) { + if (currentParent == parentCandidate) { + return true; + } + currentParent = currentParent.getParent(); + } + return false; + } + + /** + * Get the absolute bounds that this edit part will have after the execution + * of arrangeAll.<BR> + * The return bounds take into account the scale, ie if the real bounds is + * {100, 200} the return bounds is {50, 100}. + * + * @param graphicalEditPart + * The edit part to deal with + * @param scale + * The current scale of the diagram + * @return The bounds after arrangeAll + */ + protected Rectangle getBounds(final IGraphicalEditPart graphicalEditPart, final double scale) { + return getBounds(graphicalEditPart, scale, null); + } + + /** + * Get the absolute bounds that this edit part will have after the execution + * of arrangeAll.<BR> + * The return bounds take into account the scale, ie if the real bounds is + * {100, 200} the return bounds is {50, 100}. + * + * @param graphicalEditPart + * The edit part to deal with + * @param scale + * The current scale of the diagram + * @param parentMoveDelta + * The parent move delta if it is know, null otherwise + * @return The bounds after arrangeAll + */ + protected Rectangle getBounds(final IGraphicalEditPart graphicalEditPart, final double scale, final Dimension parentMoveDelta) { + return getBounds(graphicalEditPart, scale, parentMoveDelta, true, true); + } + + /** + * Get the absolute bounds that this edit part will have after the execution + * of arrangeAll.<BR> + * The return bounds take into account the scale, ie if the real bounds is + * {100, 200} the return bounds is {50, 100}. + * + * @param graphicalEditPart + * The edit part to deal with + * @param scale + * The current scale of the diagram + * @param parentMoveDelta + * The parent move delta if it is know, null otherwise + * @param processX + * A boolean value used to validate if calculating width is + * needed + * @param processY + * A boolean value used to validate if calculating height is + * needed + * @return The bounds after arrangeAll + */ + protected Rectangle getBounds(final IGraphicalEditPart graphicalEditPart, final double scale, final Dimension parentMoveDelta, final boolean processX, final boolean processY) { + + Rectangle bounds = null; + boolean isPinned = false; + if (graphicalEditPart.resolveSemanticElement() instanceof DDiagramElement) { + DDiagramElement dDiagramElement = (DDiagramElement) graphicalEditPart.resolveSemanticElement(); + isPinned = new PinHelper().isPinned(dDiagramElement); + if (isPinned) { + bounds = graphicalEditPart.getFigure().getBounds().getCopy(); + } + } + + if (!isPinned) { + // Compute location after arrange + bounds = this.getBounds(graphicalEditPart); + + if (bounds != null) { + final Dimension moveDelta = bounds.getTopLeft().getDifference(graphicalEditPart.getFigure().getBounds().getTopLeft()); + moveDelta.scale(1 / scale); + + // final Dimension sizeDelta = + // bounds.getSize().getDifference(graphicalEditPart.getFigure().getBounds().getSize()); + // sizeDelta.scale(1 / scale); + + bounds = new Rectangle(graphicalEditPart.getFigure().getBounds()).translate(new Point(moveDelta.width, moveDelta.height)); + // bounds.setSize(bounds.getSize().getExpanded(sizeDelta)); + } + } + if (parentMoveDelta != null) { + bounds = bounds.getTranslated(new Point(parentMoveDelta.width, parentMoveDelta.height)); + } else if (!(graphicalEditPart.getParent() instanceof IDDiagramEditPart) && bounds != null) { + final IGraphicalEditPart parent = (IGraphicalEditPart) graphicalEditPart.getParent(); + final Rectangle parentBounds = getBounds(parent, scale); + + if (!isPinned(parent)) { + final Dimension moveDelta = getScaledMoveDelta(parent, parentBounds, scale); + bounds = bounds.getTranslated(new Point(moveDelta.width, moveDelta.height)); + } + } + graphicalEditPart.getFigure().translateToAbsolute(bounds); + + if (!isPinned && launchNormalArrange) { + // Compute size after arrange (with auto-size) + final Dimension moveDelta = getScaledMoveDelta(graphicalEditPart, bounds, scale); + final Dimension sizeWithAutoSize = getSizeAfterAutoSize(graphicalEditPart, bounds, scale, moveDelta, processX, processY); + bounds.setSize(sizeWithAutoSize); + } + return bounds; + } + + /** + * Compute the delta between the actual location of the <code>part</code> + * and the target bounds. + * + * @param part + * The edit part to deal with + * @param targetBounds + * The target bounds + * @param scale + * The current scale of the diagram + * @return the delta between the actual location of the <code>part</code> + * and the target bounds + */ + private Dimension getScaledMoveDelta(final IGraphicalEditPart part, final Rectangle targetBounds, final double scale) { + final Point topLeft = part.getFigure().getBounds().getTopLeft(); + part.getFigure().translateToAbsolute(topLeft); + final Dimension moveDelta = targetBounds.getTopLeft().getDifference(topLeft); + moveDelta.scale(1 / scale); + return moveDelta; + } + + /** + * Compute the size of this editPart after arrange all if this editPart is + * in auto-size mode. This calculation is approximate by the leftmost child + * and its size. + * + * @param part + * The concern edit part + * @param actualBounds + * The current bounds of this edit part + * @param scale + * The current scale of the diagram + * @param moveDelta + * The parent move delta if it is know, null otherwise + * @return The size after arrange all (if needed). + */ + private Dimension getSizeAfterAutoSize(final IGraphicalEditPart part, final Rectangle actualBounds, final double scale, final Dimension moveDelta, final boolean processX, final boolean processY) { + final Dimension result = new Dimension(actualBounds.getSize()); + boolean shouldWidthAutoSized = ArrangeAllWithAutoSize.shouldBeAutosized(part) || part instanceof ShapeCompartmentEditPart; + boolean shouldHeightAutoSized = shouldWidthAutoSized; + if ((!shouldWidthAutoSized && !shouldHeightAutoSized) && part.getNotationView() instanceof Node) { + final Node node = (Node) part.getNotationView(); + final LayoutConstraint layoutConstraint = node.getLayoutConstraint(); + if (layoutConstraint instanceof Size) { + final Size size = (Size) layoutConstraint; + shouldWidthAutoSized = size.getWidth() == -1; + shouldHeightAutoSized = size.getHeight() == -1; + } + } + + // Take into account the default size for some specific figures (we set + // a default size explicitly in createMainFigure of + // DNodeContainerEditPart and DNodeContainerEditPart) + Dimension defaultSize = new Dimension(0, 0); + final IFigure f = part.getFigure(); + if (f instanceof BorderedNodeFigure && ((BorderedNodeFigure) f).getMainFigure() instanceof DefaultSizeNodeFigure) { + defaultSize = ((DefaultSizeNodeFigure) ((BorderedNodeFigure) f).getMainFigure()).getDefaultSize(); + } + if (shouldWidthAutoSized && processX) { + final int rightSizeXCoordinate = getRightSizeXCoordinateOfRightMostChild(part, scale, moveDelta); + result.width = Math.max(defaultSize.width, rightSizeXCoordinate - actualBounds.x); + } + if (shouldHeightAutoSized && processY) { + final int bottomSizeYCoordinate = getBottomSizeYCoordinateOfLowestChild(part, scale, moveDelta); + result.height = Math.max(defaultSize.height, bottomSizeYCoordinate - actualBounds.y); + } + return result; + } + + /** + * Return the x axis coordinate of the right size of the rightmost child + * (after the arrange all). + * + * @param part + * The parent + * @param scale + * The current scale of the diagram + * @param moveDelta + * The parent move delta if it is know, null otherwise + * @param launchNormalArrange + * Tell if the normal arrange process will be called before the + * border item arrange + * @return the x axis coordinate of the right size of the rightmost child + */ + private int getRightSizeXCoordinateOfRightMostChild(final IGraphicalEditPart part, final double scale, final Dimension moveDelta) { + int result = 0; + final Collection<IGraphicalEditPart> children = Collections2.filter( + part.getChildren(), + Predicates.and(Predicates.instanceOf(IGraphicalEditPart.class), Predicates.not(Predicates.instanceOf(AbstractDiagramBorderNodeEditPart.class)), + Predicates.not(Predicates.instanceOf(AbstractDiagramNameEditPart.class)))); + for (IGraphicalEditPart child : children) { + if (child instanceof ShapeCompartmentEditPart) { + // Only delegates to the grandchildren + final Collection<IGraphicalEditPart> grandchildren = Collections2.filter( + child.getChildren(), + Predicates.and(Predicates.instanceOf(IGraphicalEditPart.class), Predicates.not(Predicates.instanceOf(AbstractDiagramBorderNodeEditPart.class)), + Predicates.not(Predicates.instanceOf(AbstractDiagramNameEditPart.class)))); + for (IGraphicalEditPart grandchild : grandchildren) { + final Rectangle bounds = getBounds(grandchild, scale, moveDelta, true, false); + final int rightSizeXCoordinate = bounds.x + bounds.width; + if (result < rightSizeXCoordinate) { + result = rightSizeXCoordinate; + } + } + } else { + final Rectangle bounds = getBounds(child, scale, moveDelta, true, false); + final int rightSizeXCoordinate = bounds.x + bounds.width; + if (result < rightSizeXCoordinate) { + result = rightSizeXCoordinate; + } + } + } + return result; + } + + /** + * Return the y axis coordinate of the bottom size of the lowest child + * (after the arrange all). + * + * @param part + * The parent + * @param scale + * The current scale of the diagram + * @param moveDelta + * The parent move delta if it is know, null otherwise + * @return the y axis coordinate of the bottom size of the lowest child + */ + private int getBottomSizeYCoordinateOfLowestChild(final IGraphicalEditPart part, final double scale, final Dimension moveDelta) { + int result = 0; + final Collection<IGraphicalEditPart> children = Collections2.filter( + part.getChildren(), + Predicates.and(Predicates.instanceOf(IGraphicalEditPart.class), Predicates.not(Predicates.instanceOf(AbstractDiagramBorderNodeEditPart.class)), + Predicates.not(Predicates.instanceOf(AbstractDiagramNameEditPart.class)))); + for (IGraphicalEditPart child : children) { + if (child instanceof ShapeCompartmentEditPart) { + // Only delegates to the grandchildren + final Collection<IGraphicalEditPart> grandchildren = Collections2.filter( + child.getChildren(), + Predicates.and(Predicates.instanceOf(IGraphicalEditPart.class), Predicates.not(Predicates.instanceOf(AbstractDiagramBorderNodeEditPart.class)), + Predicates.not(Predicates.instanceOf(AbstractDiagramNameEditPart.class)))); + for (IGraphicalEditPart grandchild : grandchildren) { + final Rectangle bounds = getBounds(grandchild, scale, moveDelta, false, true); + final int bottomSizeYCoordinate = bounds.y + bounds.height; + if (result < bottomSizeYCoordinate) { + result = bottomSizeYCoordinate; + } + } + } else { + final Rectangle bounds = getBounds(child); + final int bottomSizeYCoordinate = bounds.y + bounds.height; + if (result < bottomSizeYCoordinate) { + result = bottomSizeYCoordinate; + } + } + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeDownTopLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeDownTopLayoutProvider.java new file mode 100644 index 0000000000..1553291cfd --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeDownTopLayoutProvider.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.ConnectionEditPart; + +/** + * Specialization of a down to top diagram layout leveraging specific layout + * settings associated with a given diagram instance. + * + * @author cbrun + */ +@SuppressWarnings("restriction") +public class CompositeDownTopLayoutProvider extends AbstractCompositeLayoutProvider { + /** + * {@inheritDoc} + */ + @Override + public Rectangle translateToGraph(final Rectangle r) { + final Rectangle rDP = new Rectangle(r); + getMapMode().LPtoDP(rDP); + return rDP; + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle translateFromGraph(final Rectangle rect) { + final Rectangle rLP = new Rectangle(rect); + getMapMode().DPtoLP(rLP); + return rLP; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean layoutTopDown(final ConnectionEditPart poly) { + return true; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeDownTopProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeDownTopProvider.java new file mode 100644 index 0000000000..21ec61f316 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeDownTopProvider.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.CompoundLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider; + +/** + * {@link org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutNodeProvider} + * with a Down/Top organization. + * + * @author ymortier + */ +public class CompositeDownTopProvider implements LayoutProvider { + + /** The delegated GMF provider. */ + private AbstractLayoutEditPartProvider layoutNodeProvider; + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider#getLayoutNodeProvider(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public AbstractLayoutEditPartProvider getLayoutNodeProvider(final IGraphicalEditPart container) { + if (this.layoutNodeProvider == null) { + final CompoundLayoutProvider clp = new CompoundLayoutProvider(); + final CompositeDownTopLayoutProvider cdtp = new CompositeDownTopLayoutProvider(); + + clp.addProvider(cdtp); + clp.addProvider(new PinnedElementsLayoutProvider(cdtp)); + if (ENABLE_BORDERED_NODES_ARRANGE_ALL) { + // ArrangeSelectionLayoutProvider wrap all providers to manage + // the selected diagram element on diagram "Arrange all" + AbstractLayoutProvider abstractLayoutProvider = new BorderItemAwareLayoutProvider(clp); + this.layoutNodeProvider = new ArrangeSelectionLayoutProvider(abstractLayoutProvider); + } else { + this.layoutNodeProvider = new ArrangeSelectionLayoutProvider(clp); + } + } + return this.layoutNodeProvider; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider#provides(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public boolean provides(final IGraphicalEditPart container) { + /* + * we should always provide even if container is not the diagram, to + * handle the case of arrange all action when an element in a container + * is selected + */ + return true; + } + + public boolean isDiagramLayoutProvider() { + return true; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeLeftRightLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeLeftRightLayoutProvider.java new file mode 100644 index 0000000000..8eec27a4c1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeLeftRightLayoutProvider.java @@ -0,0 +1,77 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.draw2d.graph.DirectedGraph; +import org.eclipse.gef.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart; + +/** + * Specialization of a left to right diagram layout leveraging specific layout + * settings associated with a given diagram instance. + * + * @author cbrun + */ +@SuppressWarnings("restriction") +public class CompositeLeftRightLayoutProvider extends AbstractCompositeLayoutProvider { + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.layout.provider.AbstractCompositeLayoutProvider#translateToGraph(org.eclipse.draw2d.geometry.Rectangle) + */ + @Override + public Rectangle translateToGraph(final Rectangle r) { + final Rectangle rDP = new Rectangle(r); + getMapMode().LPtoDP(rDP); + return rDP; + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle translateFromGraph(final Rectangle rect) { + final Rectangle rLP = new Rectangle(rect); + getMapMode().DPtoLP(rLP); + return rLP; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean layoutTopDown(final ConnectionEditPart poly) { + return false; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.providers.internal.CompositeLayoutProvider#createGraph() + */ + @Override + protected DirectedGraph createGraph() { + DirectedGraph g = super.createGraph(); + g.setDirection(PositionConstants.EAST); + return g; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.providers.internal.CompositeLayoutProvider#getLayoutDirection(org.eclipse.gmf.runtime.diagram.ui.editparts.GraphicalEditPart) + */ + protected int getLayoutDirection(GraphicalEditPart ep) { + return PositionConstants.EAST; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeLeftRightProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeLeftRightProvider.java new file mode 100644 index 0000000000..f3ca716c25 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeLeftRightProvider.java @@ -0,0 +1,79 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.description.CompositeLayout; +import org.eclipse.sirius.description.Layout; +import org.eclipse.sirius.description.LayoutDirection; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.CompoundLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.DiagramLayoutCustomization; + +/** + * Provides {@link CompositeLeftRightLayoutProvider}. + * + * @author ymortier + */ +public class CompositeLeftRightProvider implements LayoutProvider { + + /** The delegated GMF provider. */ + private AbstractLayoutEditPartProvider layoutNodeProvider; + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider#getLayoutNodeProvider(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public AbstractLayoutEditPartProvider getLayoutNodeProvider(final IGraphicalEditPart container) { + if (this.layoutNodeProvider == null) { + final CompoundLayoutProvider clp = new CompoundLayoutProvider(); + final CompositeLeftRightLayoutProvider cdtp = new CompositeLeftRightLayoutProvider(); + clp.addProvider(cdtp); + clp.addProvider(new PinnedElementsLayoutProvider(cdtp)); + if (ENABLE_BORDERED_NODES_ARRANGE_ALL) { + // ArrangeSelectionLayoutProvider wrap all providers to manage + // the selected diagram element on diagram "Arrange all" + AbstractLayoutProvider abstractLayoutProvider = new BorderItemAwareLayoutProvider(clp); + this.layoutNodeProvider = new ArrangeSelectionLayoutProvider(abstractLayoutProvider); + } else { + this.layoutNodeProvider = new ArrangeSelectionLayoutProvider(clp); + } + } + return this.layoutNodeProvider; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider#provides(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public boolean provides(final IGraphicalEditPart container) { + return isInDDiagramWithConfiguredLeftRightLayout(container.getNotationView()); + } + + private boolean isInDDiagramWithConfiguredLeftRightLayout(final View view) { + final Layout foundLayout = DiagramLayoutCustomization.findLayoutSettings(view); + if (foundLayout instanceof CompositeLayout) { + return ((CompositeLayout) foundLayout).getDirection() == LayoutDirection.LEFT_TO_RIGHT; + } + return false; + } + + public boolean isDiagramLayoutProvider() { + return true; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeTopDownLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeTopDownLayoutProvider.java new file mode 100644 index 0000000000..3d2b7b27c8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeTopDownLayoutProvider.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.ConnectionEditPart; + +/** + * Specialization of a down to top diagram layout leveraging specific layout + * settings associated with a given diagram instance. + * + * @author cbrun + */ +@SuppressWarnings("restriction") +public class CompositeTopDownLayoutProvider extends AbstractCompositeLayoutProvider { + /** + * {@inheritDoc} + */ + @Override + public Rectangle translateToGraph(final Rectangle r) { + final Rectangle rDP = new Rectangle(r); + getMapMode().LPtoDP(rDP); + return rDP; + } + + /** + * {@inheritDoc} + */ + @Override + public Rectangle translateFromGraph(final Rectangle rect) { + final Rectangle rLP = new Rectangle(rect); + getMapMode().DPtoLP(rLP); + return rLP; + } + + /** + * {@inheritDoc} + */ + @Override + protected boolean layoutTopDown(final ConnectionEditPart poly) { + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeTopDownProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeTopDownProvider.java new file mode 100644 index 0000000000..220b9b0a5c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/CompositeTopDownProvider.java @@ -0,0 +1,82 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.description.CompositeLayout; +import org.eclipse.sirius.description.Layout; +import org.eclipse.sirius.description.LayoutDirection; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.CompoundLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.DiagramLayoutCustomization; + +/** + * Provides + * {@link org.eclipse.sirius.common.ui.tools.api.graphical.layout.CompositeTopDownProvider} + * . + * + * @author ymortier + */ +public class CompositeTopDownProvider implements LayoutProvider { + + /** The delegated GMF provider. */ + private AbstractLayoutEditPartProvider layoutNodeProvider; + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider#getLayoutNodeProvider(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public AbstractLayoutEditPartProvider getLayoutNodeProvider(final IGraphicalEditPart container) { + if (this.layoutNodeProvider == null) { + final CompoundLayoutProvider clp = new CompoundLayoutProvider(); + final CompositeTopDownLayoutProvider cdtp = new CompositeTopDownLayoutProvider(); + + clp.addProvider(cdtp); + clp.addProvider(new PinnedElementsLayoutProvider(cdtp)); + if (ENABLE_BORDERED_NODES_ARRANGE_ALL) { + // ArrangeSelectionLayoutProvider wrap all providers to manage + // the selected diagram element on diagram "Arrange all" + AbstractLayoutProvider abstractLayoutProvider = new BorderItemAwareLayoutProvider(clp); + this.layoutNodeProvider = new ArrangeSelectionLayoutProvider(abstractLayoutProvider); + } else { + this.layoutNodeProvider = new ArrangeSelectionLayoutProvider(clp); + } + } + return this.layoutNodeProvider; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider#provides(org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart) + */ + public boolean provides(final IGraphicalEditPart container) { + return isInDDiagramWithConfiguredTopDownLayout(container.getNotationView()); + } + + private boolean isInDDiagramWithConfiguredTopDownLayout(final View view) { + final Layout foundLayout = DiagramLayoutCustomization.findLayoutSettings(view); + if (foundLayout instanceof CompositeLayout) { + return ((CompositeLayout) foundLayout).getDirection() == LayoutDirection.TOP_TO_BOTTOM; + } + return false; + } + + public boolean isDiagramLayoutProvider() { + return true; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/LayoutProviderDescriptor.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/LayoutProviderDescriptor.java new file mode 100644 index 0000000000..1ea3375082 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/LayoutProviderDescriptor.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IConfigurationElement; + +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider; + +/** + * The descriptor of a {@link LayoutProvider}. + * + * @author ymortier + */ +public class LayoutProviderDescriptor extends AbstractProviderDescriptor { + + /** The layout provider. */ + private LayoutProvider provider; + + /** + * Create a new descriptor with the specified configuration element. + * + * @param element + * the configuration element. + */ + public LayoutProviderDescriptor(final IConfigurationElement element) { + super(element); + } + + /** + * Return the layout provider. + * + * @return the layout provider. + */ + public LayoutProvider getProviderInstance() { + if (provider == null) { + try { + provider = (LayoutProvider) element.createExecutableExtension("providerClass"); //$NON-NLS-1$ + } catch (final CoreException e) { + SiriusDiagramEditorPlugin.getInstance().logError("CoreException during the initialization of the AIR Layout Provider " + this.getProviderClassName(), e); + } + } + return provider; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/LayoutService.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/LayoutService.java new file mode 100644 index 0000000000..1e0b15ade6 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/LayoutService.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.Platform; +import org.eclipse.emf.common.EMFPlugin; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider; + +/** + * This class provides layout facilities based on the declared providers. + * + * @author ymortier + */ +public final class LayoutService { + + /** All providers. */ + private static List<LayoutProviderDescriptor> layoutProviders = new ArrayList<LayoutProviderDescriptor>(); + + static { + LayoutService.parseExtensionMetadata(); + } + + /** Name of the extension point to parse for extender providers. */ + private static final String LAYOUT_PROVIDER_EXTENSION_POINT = "org.eclipse.sirius.diagram.layoutProvider"; //$NON-NLS-1$ + + /** Externalized here to avoid too many distinct usages. */ + private static final String TAG_ENGINE = "layoutProvider"; + + /** + * Avoid instantiation. + */ + private LayoutService() { + + } + + /** + * This will parse the currently running platform for extensions and store + * all the match engines that can be found. + */ + private static void parseExtensionMetadata() { + if (EMFPlugin.IS_ECLIPSE_RUNNING) { + final IExtension[] extensions = Platform.getExtensionRegistry().getExtensionPoint(LAYOUT_PROVIDER_EXTENSION_POINT).getExtensions(); + for (IExtension extension : extensions) { + final IConfigurationElement[] configElements = extension.getConfigurationElements(); + for (IConfigurationElement configElement : configElements) { + final AbstractProviderDescriptor desc = LayoutService.parseEngine(configElement); + if (desc instanceof LayoutProviderDescriptor) { + layoutProviders.add((LayoutProviderDescriptor) desc); + } + } + } + Collections.sort(layoutProviders); + } + } + + private static AbstractProviderDescriptor parseEngine(final IConfigurationElement configElement) { + if (!configElement.getName().equals(TAG_ENGINE)) { + return null; + } + final AbstractProviderDescriptor desc = new LayoutProviderDescriptor(configElement); + return desc; + } + + /** + * Return the {@link LayoutProvider} to use with the specified + * {@link org.eclipse.gef.EditPart}. + * + * @param editPart + * the edit part. + * @return the {@link LayoutProvider} to use with the specified + * {@link org.eclipse.gef.EditPart}. + */ + public static LayoutProvider getProvider(final IGraphicalEditPart editPart) { + final Iterator<LayoutProviderDescriptor> iterDescriptors = LayoutService.layoutProviders.listIterator(); + while (iterDescriptors.hasNext()) { + final LayoutProviderDescriptor currentDescriptor = iterDescriptors.next(); + final LayoutProvider provider = currentDescriptor.getProviderInstance(); + if (provider != null) { + if (provider.provides(editPart)) { + return provider; + } + } else { + iterDescriptors.remove(); + } + } + return null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/OrderedTreeLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/OrderedTreeLayoutProvider.java new file mode 100644 index 0000000000..5df978f1af --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/OrderedTreeLayoutProvider.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.services.layout.AbstractLayoutEditPartProvider; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.View; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.description.Layout; +import org.eclipse.sirius.description.OrderedTreeLayout; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutUtils; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.AbstractLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.CompoundLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.GridLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.LayoutProvider; + +/** + * A tree ordered LayoutProvider, used for diagrams which have configured a + * {@link OrderedTreeLayout}. + * <p> + * Uses a properly configured {@link GridLayoutProvider} internally, a tree + * being seen as a partially filled grid. + * + * @author mchauvin + */ +public class OrderedTreeLayoutProvider implements LayoutProvider { + /** + * Simple flag to easily switch between the old/new behaviors during + * development. + */ + private static final boolean PIN_MANAGEMENT_ON_TREE_LAYOUT = true; + + /** + * The underlying grid provider. + */ + private GridLayoutProvider airGridLayoutProvider; + + /** + * {@inheritDoc} + */ + public AbstractLayoutEditPartProvider getLayoutNodeProvider(final IGraphicalEditPart container) { + AbstractLayoutProvider layoutProvider = null; + if (isDDiagramWithConfiguredOrderedTreeLayout(container)) { + if (PIN_MANAGEMENT_ON_TREE_LAYOUT) { + final CompoundLayoutProvider clp = new CompoundLayoutProvider(); + GridLayoutProvider layoutNodeProvider = getGridLayoutProvider(); + clp.addProvider(layoutNodeProvider); + clp.addProvider(new PinnedElementsLayoutProvider(layoutNodeProvider)); + // We return an ArrangeAllOnlyLayoutProvider as no + // "Arrange Selection" should be done on such layout. + if (ENABLE_BORDERED_NODES_ARRANGE_ALL) { + AbstractLayoutProvider abstractLayoutProvider = new BorderItemAwareLayoutProvider(clp); + layoutProvider = new ArrangeAllOnlyLayoutProvider(abstractLayoutProvider); + } else { + layoutProvider = new ArrangeAllOnlyLayoutProvider(clp); + } + } else { + return getGridLayoutProvider(); + } + } + return layoutProvider; + } + + /** + * {@inheritDoc} + */ + public boolean provides(final IGraphicalEditPart container) { + return isDDiagramWithConfiguredOrderedTreeLayout(container); + } + + /** + * {@inheritDoc} + */ + public boolean isDiagramLayoutProvider() { + return false; + } + + /** + * Tests whether the specified edit part is a {@link DDiagram} which has + * been configured to use an {@link OrderedTreeLayout}. + */ + private boolean isDDiagramWithConfiguredOrderedTreeLayout(final IGraphicalEditPart container) { + final View view = container.getNotationView(); + final EObject modelElement = view.getElement(); + if (view instanceof Diagram && modelElement instanceof DDiagram) { + final DDiagram vp = (DDiagram) modelElement; + final DiagramDescription desc = vp.getDescription(); + if (desc != null) { + final Layout layout = desc.getLayout(); + if (layout instanceof OrderedTreeLayout) { + return true; + } + } + } + return false; + } + + /** + * Returns a properly configured {@link GridLayoutProvider} suitable to + * layout a tree. + */ + private GridLayoutProvider getGridLayoutProvider() { + if (airGridLayoutProvider == null) { + airGridLayoutProvider = new AdjustedGridLayout(); + airGridLayoutProvider.setColumnSizeMode(GridLayoutProvider.DIMENSION_BY_LINE_OR_COLUMN); + airGridLayoutProvider.setLineSizeMode(GridLayoutProvider.DIMENSION_BY_LINE_OR_COLUMN); + airGridLayoutProvider.getPadding().left = LayoutUtils.SCALE; + airGridLayoutProvider.getPadding().right = LayoutUtils.SCALE; + } + return airGridLayoutProvider; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/PinnedElementsLayoutProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/PinnedElementsLayoutProvider.java new file mode 100644 index 0000000000..2b202eecc2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/provider/PinnedElementsLayoutProvider.java @@ -0,0 +1,217 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.provider; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.commands.Command; +import org.eclipse.gef.commands.CompoundCommand; +import org.eclipse.gef.editparts.ZoomManager; +import org.eclipse.gef.requests.ChangeBoundsRequest; +import org.eclipse.gmf.runtime.diagram.ui.editparts.ConnectionEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramRootEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; + +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramElementContainerEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramBorderNodeEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramContainerEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramListEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramNameEditPart; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramNodeEditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerViewNodeContainerCompartment2EditPart; +import org.eclipse.sirius.diagram.internal.edit.parts.DNodeContainerViewNodeContainerCompartmentEditPart; +import org.eclipse.sirius.diagram.ui.tools.api.layout.PinHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.DefaultLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.api.layout.provider.ExtendableLayoutProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.ArrangeAllWithAutoSize; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.PinnedElementsHandler; + +/** + * A layout provider which handles pinned elements after an initial layout. + * + * @author pcdavid + */ +public class PinnedElementsLayoutProvider extends DefaultLayoutProvider { + /** + * The layout provider which was executed before us. Needed to obtain the + * new + */ + private final ExtendableLayoutProvider baseProvider; + + private Predicate<Object> validateAllElementInArrayListAreIDiagramElementEditPart = new Predicate<Object>() { + + public boolean apply(Object input) { + return input instanceof IDiagramElementEditPart; + } + }; + + /** + * Creates a <code>FixOverlapsProvider</code>. + * + * @param baseProvider + * the layout provider which computed the initial locations. + */ + public PinnedElementsLayoutProvider(final ExtendableLayoutProvider baseProvider) { + this.baseProvider = baseProvider; + } + + /** + * {@inheritDoc} + */ + @Override + public Command layoutEditParts(final List selectedObjects, final IAdaptable layoutHint) { + final Map<IGraphicalEditPart, Rectangle> initialBounds = Maps.newHashMap(baseProvider.getExtender().getUpdatedBounds()); + if (ArrangeAllWithAutoSize.isEnabled()) { + adjustAutoSizedContainers(initialBounds); + } + final Collection<IGraphicalEditPart> editParts = Lists.newArrayList(Iterables.filter(selectedObjects, IGraphicalEditPart.class)); + // Removes the connectionEditPart + editParts.removeAll(Lists.newArrayList(Iterables.filter(selectedObjects, ConnectionEditPart.class))); + final CompoundCommand cc = new CompoundCommand(); + + // Finds if there are unpinned diagram elements to keep fixed stored in + // the LayoutHint as a Collection + ArrayList<IDiagramElementEditPart> elementstToKeepFixed = Lists.newArrayList(); + if (layoutHint.getAdapter(Collection.class) instanceof ArrayList<?> + && Iterables.all((ArrayList<?>) layoutHint.getAdapter(Collection.class), validateAllElementInArrayListAreIDiagramElementEditPart)) { + elementstToKeepFixed = (ArrayList<IDiagramElementEditPart>) layoutHint.getAdapter(Collection.class); + } + handlePinnedElements(editParts, initialBounds, cc, elementstToKeepFixed); + return cc.unwrap(); + } + + private void adjustAutoSizedContainers(final Map<IGraphicalEditPart, Rectangle> initialBounds) { + PinHelper pinHelper = new PinHelper(); + for (Entry<IGraphicalEditPart, Rectangle> entry : initialBounds.entrySet()) { + if (entry.getKey() instanceof AbstractDiagramElementContainerEditPart) { + final AbstractDiagramElementContainerEditPart container = (AbstractDiagramElementContainerEditPart) entry.getKey(); + if (!pinHelper.isPinned(container.resolveDiagramElement())) { + final Rectangle bounds = entry.getValue().getCopy(); + final Rectangle autoSizedBounds = container.getAutosizedDimensions(); + bounds.width = autoSizedBounds.width; + bounds.height = autoSizedBounds.height; + entry.setValue(bounds); + } + } + } + } + + private void handlePinnedElements(final Collection<IGraphicalEditPart> editParts, final Map<IGraphicalEditPart, Rectangle> initialBounds, final CompoundCommand cmd, + ArrayList<IDiagramElementEditPart> elementstToKeepFixed) { + /* + * Depth-first recursion on containers. + */ + for (IGraphicalEditPart part : editParts) { + final Collection<IGraphicalEditPart> children = getChildrenOfInterest(part); + if (!children.isEmpty()) { + handlePinnedElements(children, initialBounds, cmd, elementstToKeepFixed); + } + } + /* + * Base case: handle pinned elements at this particular level. + */ + final Map<IGraphicalEditPart, Rectangle> initialBoundsForThisLevel = Maps.filterEntries(initialBounds, new Predicate<Map.Entry<IGraphicalEditPart, Rectangle>>() { + public boolean apply(final Entry<IGraphicalEditPart, Rectangle> input) { + return editParts.contains(input.getKey()); + } + }); + final PinnedElementsHandler handler = new PinnedElementsHandler(editParts, initialBoundsForThisLevel, elementstToKeepFixed); + final Map<IGraphicalEditPart, Point> newPositions = handler.computeSolution(); + for (Entry<IGraphicalEditPart, Point> entry : newPositions.entrySet()) { + final IGraphicalEditPart part = entry.getKey(); + final Point position = entry.getValue(); + final Command cbc = createChangeBoundsCommand(part, position); + cmd.add(cbc); + } + } + + /** + * Finds the "real" children of the specified edit part that needs to be + * laid out. + */ + private Collection<IGraphicalEditPart> getChildrenOfInterest(final IGraphicalEditPart gep) { + final Iterable<IGraphicalEditPart> rawChildren = Iterables.filter(gep.getChildren(), IGraphicalEditPart.class); + // Ignore these, which are technically children edit parts but not + // "inside" the container. + final Predicate<Object> invalidChildKind = Predicates.or(Predicates.instanceOf(IDiagramBorderNodeEditPart.class), Predicates.instanceOf(IDiagramNameEditPart.class)); + // These are OK. + final Predicate<Object> validChildKind = Predicates.or(Predicates.instanceOf(IDiagramNodeEditPart.class), Predicates.instanceOf(IDiagramContainerEditPart.class), + Predicates.instanceOf(IDiagramListEditPart.class)); + final Predicate<Object> isProperChild = Predicates.and(validChildKind, Predicates.not(invalidChildKind)); + final Collection<IGraphicalEditPart> result = Lists.newArrayList(Iterables.filter(rawChildren, isProperChild)); + // Containers have an intermediate level of children edit parts. We + // ignore these "wrapper" parts, but must look inside for proper + // children of the container. + for (IGraphicalEditPart part : Iterables.filter(rawChildren, Predicates.not(isProperChild))) { + if (part instanceof DNodeContainerViewNodeContainerCompartmentEditPart || part instanceof DNodeContainerViewNodeContainerCompartment2EditPart) { + result.addAll(getChildrenOfInterest(part)); + } + } + return result; + } + + /** + * Create the command that changes the bounds of the specified edit part. + * + * @param editPart + * the specified edit part + * + * @param newPosition + * the new position of the figure. + * @return the command that changes the bounds of the specified edit part. + */ + protected Command createChangeBoundsCommand(final IGraphicalEditPart editPart, final Point newPosition) { + Command result = null; + final Object existingRequest = this.findRequest(editPart, org.eclipse.gef.RequestConstants.REQ_MOVE); + ChangeBoundsRequest request = null; + double scale = 1.0; + if (editPart.getRoot() instanceof DiagramRootEditPart) { + final ZoomManager zoomManager = ((DiagramRootEditPart) editPart.getRoot()).getZoomManager(); + scale = zoomManager.getZoom(); + } + if (existingRequest instanceof ChangeBoundsRequest) { + request = (ChangeBoundsRequest) existingRequest; + } else if (existingRequest == null) { + request = new ChangeBoundsRequest(); + request.setEditParts(editPart); + result = this.buildCommandWrapper(request, editPart); + } + if (newPosition != null) { + final Dimension delta = newPosition.getDifference(editPart.getFigure().getBounds().getLocation()); + delta.width *= scale; + delta.height *= scale; + if (request != null) { + request.setMoveDelta(new Point(delta.width, delta.height)); + request.setLocation(newPosition); + request.setType(org.eclipse.gef.RequestConstants.REQ_MOVE); + } else { + // no move, return null. + return null; + } + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/AbstractSemanticLayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/AbstractSemanticLayoutDataKey.java new file mode 100644 index 0000000000..f6afc2e260 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/AbstractSemanticLayoutDataKey.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.semantic; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; + +import com.google.common.base.Objects; + +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey; + +/** + * Common behavior for Semantic*LayoutDataKey classes. + * + * @author dlecan + */ +public abstract class AbstractSemanticLayoutDataKey implements LayoutDataKey, Comparable<AbstractSemanticLayoutDataKey> { + + /** URI Fragment of the element's target semantic element. */ + private final String semanticElementURIFragment; + + /** + * Constructor. + * + * @param semanticElement + * The element the create the key. + */ + public AbstractSemanticLayoutDataKey(final EObject semanticElement) { + this.semanticElementURIFragment = EcoreUtil.getURI(semanticElement).fragment(); + } + + /** + * Constructor. + * + * @param uriFragment + * String to use to create the new key. + */ + public AbstractSemanticLayoutDataKey(final String uriFragment) { + semanticElementURIFragment = uriFragment; + } + + protected String getSemanticElementURIFragment() { + return semanticElementURIFragment; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return Objects.hashCode(semanticElementURIFragment); + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(final Object obj) { + boolean result = false; + if (this == obj) { + result = true; + } else if (obj != null && getClass().isAssignableFrom(obj.getClass()) && getSemanticElementURIFragment() != null) { + result = getId().equals(((LayoutDataKey) obj).getId()); + } + return result; + } + + /** + * {@inheritDoc} + */ + @Override + public String toString() { + return "Key ID: " + getId(); + } + + /** + * {@inheritDoc} + */ + public String getId() { + return getSemanticElementURIFragment(); + } + + /** + * {@inheritDoc} + */ + public int compareTo(final AbstractSemanticLayoutDataKey o) { + return getId().compareTo(o.getId()); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SemanticEdgeLayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SemanticEdgeLayoutDataKey.java new file mode 100644 index 0000000000..1e0a7f6cc1 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SemanticEdgeLayoutDataKey.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.semantic; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.diagram.ui.tools.internal.layout.EdgeLayoutDataKey; + +/** + * Kind of key use to store the layout data corresponding to a semantic element + * represented by a {@link org.eclipse.sirius.DEdge}. The key is the URI of + * the semantic element. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + */ +public class SemanticEdgeLayoutDataKey extends AbstractSemanticLayoutDataKey implements EdgeLayoutDataKey { + + /** + * Constructor. + * + * @param uriFragment + * String to use. + */ + public SemanticEdgeLayoutDataKey(final String uriFragment) { + super(uriFragment); + } + + /** + * Default constructor. + * + * @param semanticElement + * The element to build the key + */ + public SemanticEdgeLayoutDataKey(final EObject semanticElement) { + super(semanticElement); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SemanticNodeLayoutDataKey.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SemanticNodeLayoutDataKey.java new file mode 100644 index 0000000000..59ff42b786 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SemanticNodeLayoutDataKey.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.semantic; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.diagram.ui.tools.internal.layout.NodeLayoutDataKey; + +/** + * Kind of key use to store the layout data corresponding to a semantic element + * represented by a {@link org.eclipse.sirius.AbstractDNode} or a + * {@link org.eclipse.sirius.DDiagram}. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class SemanticNodeLayoutDataKey extends AbstractSemanticLayoutDataKey implements NodeLayoutDataKey { + + /** + * Constructor. + * + * @param uriFragment + * String to use. + */ + public SemanticNodeLayoutDataKey(final String uriFragment) { + super(uriFragment); + } + + /** + * Default constructor. + * + * @param semanticElement + * The element to build the key + */ + public SemanticNodeLayoutDataKey(final EObject semanticElement) { + super(semanticElement); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SiriusLayoutDataManagerForSemanticElements.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SiriusLayoutDataManagerForSemanticElements.java new file mode 100644 index 0000000000..185c8fc6a9 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/layout/semantic/SiriusLayoutDataManagerForSemanticElements.java @@ -0,0 +1,173 @@ +/******************************************************************************* + * Copyright (c) 2009, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.layout.semantic; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData; +import org.eclipse.sirius.diagram.layoutdata.EdgeLayoutData; +import org.eclipse.sirius.diagram.layoutdata.NodeLayoutData; +import org.eclipse.sirius.diagram.ui.tools.api.layout.AbstractSiriusLayoutDataManager; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataHelper; +import org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey; +import org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager; +import org.eclipse.sirius.diagram.ui.tools.internal.layout.AdvancedSiriusLayoutDataManager; + +/** + * SiriusLayoutDataManager drives by the semantic elements (EObject). Use for + * layout duplication. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class SiriusLayoutDataManagerForSemanticElements extends AbstractSiriusLayoutDataManager implements AdvancedSiriusLayoutDataManager { + + private static final SiriusLayoutDataManagerForSemanticElements INSTANCE = new SiriusLayoutDataManagerForSemanticElements(); + + private final Map<SemanticNodeLayoutDataKey, NodeLayoutData> nodeLayoutDataMap = new HashMap<SemanticNodeLayoutDataKey, NodeLayoutData>(); + + private final Map<SemanticEdgeLayoutDataKey, EdgeLayoutData> edgeLayoutDataMap = new HashMap<SemanticEdgeLayoutDataKey, EdgeLayoutData>(); + + /** + * Default constructor. + */ + public SiriusLayoutDataManagerForSemanticElements() { + // Nothing. + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#addLayoutData(org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey, + * org.eclipse.sirius.diagram.layoutdata.AbstractLayoutData) + */ + public void addLayoutData(final LayoutDataKey key, final AbstractLayoutData layoutData) { + if (!checkKeyType(key)) { + // Kind of key not manage + layoutData.setId(null); + return; + } + + if (key instanceof SemanticNodeLayoutDataKey) { + if (layoutData instanceof NodeLayoutData) { + nodeLayoutDataMap.put((SemanticNodeLayoutDataKey) key, (NodeLayoutData) layoutData); + } else { + // Bad type of layoutData for this key + } + } else if (key instanceof SemanticEdgeLayoutDataKey) { + if (layoutData instanceof EdgeLayoutData) { + edgeLayoutDataMap.put((SemanticEdgeLayoutDataKey) key, (EdgeLayoutData) layoutData); + } else { + // Bad type of layoutData for this key + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#getLayoutData(org.eclipse.sirius.diagram.ui.tools.api.layout.LayoutDataKey) + */ + public AbstractLayoutData getLayoutData(final LayoutDataKey key) { + AbstractLayoutData result = null; + if (checkKeyType(key)) { + if (key instanceof SemanticNodeLayoutDataKey) { + result = nodeLayoutDataMap.get(key); + } else if (key instanceof SemanticEdgeLayoutDataKey) { + result = edgeLayoutDataMap.get(key); + } + } + return result; + } + + /** + * Check if the key is manage by this manager. + * + * @param key + * the key to check + * @return + */ + private boolean checkKeyType(final LayoutDataKey key) { + return key instanceof SemanticNodeLayoutDataKey || key instanceof SemanticEdgeLayoutDataKey; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#getInstance() + */ + public SiriusLayoutDataManager getInstance() { + return INSTANCE; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#createKey(org.eclipse.sirius.DSemanticDecorator) + */ + public LayoutDataKey createKey(final DSemanticDecorator semanticDecorator) { + LayoutDataKey result = null; + final EObject realSemanticElement = semanticDecorator.getTarget(); + if (semanticDecorator instanceof DEdge) { + result = new SemanticEdgeLayoutDataKey(realSemanticElement); + } else if (semanticDecorator instanceof AbstractDNode || semanticDecorator instanceof DDiagram) { + result = new SemanticNodeLayoutDataKey(realSemanticElement); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#containsData() + */ + public boolean containsData() { + return !nodeLayoutDataMap.isEmpty() || !edgeLayoutDataMap.isEmpty(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.layout.SiriusLayoutDataManager#clearLayoutData() + */ + public void clearLayoutData() { + nodeLayoutDataMap.clear(); + edgeLayoutDataMap.clear(); + } + + /** + * {@inheritDoc} + */ + public Map<SemanticEdgeLayoutDataKey, EdgeLayoutData> getEdgeLayoutData() { + return edgeLayoutDataMap; + } + + /** + * {@inheritDoc} + */ + public Map<? extends LayoutDataKey, ? extends NodeLayoutData> getRootNodeLayoutData() { + return (Map<? extends LayoutDataKey, ? extends NodeLayoutData>) LayoutDataHelper.INSTANCE.getRootLayoutData(nodeLayoutDataMap); + } + + /** + * {@inheritDoc} + */ + public Map<SemanticNodeLayoutDataKey, NodeLayoutData> getNodeLayoutData() { + return nodeLayoutDataMap; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/BehaviorsPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/BehaviorsPropertySection.java new file mode 100644 index 0000000000..eb77d9d3b8 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/BehaviorsPropertySection.java @@ -0,0 +1,133 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.business.api.componentization.DiagramComponentizationManager; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.description.tool.BehaviorTool; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.ActivateBehaviorToolsCommand; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.DeactivateBehaviorToolsCommand; + +/** + * Sections that allows the user to choose activated filters. + * + * @author ymortier + */ +public class BehaviorsPropertySection extends FiltersPropertySection { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#getAppliedElements() + */ + @Override + protected Collection<?> getAppliedElements() { + final Collection<BehaviorTool> result = new HashSet<BehaviorTool>(); + if (getDiagram() != null) { + result.addAll(getDiagram().getActivateBehaviors()); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#getAvailableElements() + */ + @Override + protected Collection<?> getAvailableElements() { + final Collection<BehaviorTool> result = new HashSet<BehaviorTool>(); + final DDiagram diagram = getDiagram(); + if (diagram != null && diagram.getDescription() != null) { + Session session = null; + if (diagram instanceof DSemanticDiagram) { + session = SessionManager.INSTANCE.getSession(((DSemanticDiagram) diagram).getTarget()); + } + final Iterator<?> iterTools = new DiagramComponentizationManager().getAllTools(session.getSelectedSiriuss(false), diagram.getDescription()).iterator(); + while (iterTools.hasNext()) { + final Object currentTool = iterTools.next(); + if (currentTool instanceof BehaviorTool) { + result.add((BehaviorTool) currentTool); + } + } + } + result.removeAll(getAppliedElements()); + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#createFeatureComposite(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Composite createFeatureComposite(final Composite composite) { + final Composite featureComposite = getWidgetFactory().createComposite(composite, SWT.NONE); + featureComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + featureComposite.setLayout(new GridLayout()); + + final Label featureLabel = getWidgetFactory().createLabel(featureComposite, "Activated Behaviors", SWT.NONE); + featureLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + return featureComposite; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#createChoiceComposite(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Composite createChoiceComposite(final Composite composite) { + final Composite choiceComposite = getWidgetFactory().createComposite(composite, SWT.NONE); + choiceComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + choiceComposite.setLayout(new GridLayout()); + + final Label choiceLabel = getWidgetFactory().createLabel(choiceComposite, "Available Behaviors", SWT.NONE); + choiceLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + return choiceComposite; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#newElementsSelected(java.util.Collection) + */ + @Override + protected void newElementsSelected(final Collection<?> newElements) { + domain.getCommandStack().execute(new ActivateBehaviorToolsCommand(domain, getDiagram(), Lists.newArrayList(Iterables.filter(newElements, BehaviorTool.class)))); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#oldElementsRemoved(java.util.Collection) + */ + @Override + protected void oldElementsRemoved(final Collection<?> oldElements) { + domain.getCommandStack().execute(new DeactivateBehaviorToolsCommand(domain, getDiagram(), Lists.newArrayList(Iterables.filter(oldElements, BehaviorTool.class)))); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/CompositeEObjectPropertySource.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/CompositeEObjectPropertySource.java new file mode 100644 index 0000000000..d33657cdd0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/CompositeEObjectPropertySource.java @@ -0,0 +1,83 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.EReference; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.gef.requests.GroupRequest; +import org.eclipse.ui.IEditorPart; + +import org.eclipse.sirius.common.ui.tools.api.util.EclipseUIUtil; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditor; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.tools.api.requests.RequestConstants; +import org.eclipse.sirius.ui.tools.api.properties.AbstractCompositeEObjectPropertySource; + +/** + * Specialization for the manage of Diagram elements. + * + * @author ymortier + */ +public class CompositeEObjectPropertySource extends AbstractCompositeEObjectPropertySource { + + /** + * Creates a new <code>CompositeEObjectPropertySource</code>. + */ + public CompositeEObjectPropertySource() { + super(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java.lang.Object, + * java.lang.Object) + */ + @Override + public void setPropertyValue(final Object id, final Object value) { + super.setPropertyValue(id, value); + final Identifier identifier = (Identifier) id; + /* + * FIXME CBR : the following code handle the case where the reference is + * a "Many" one in refreshing + */ + if (identifier.getId() instanceof String) { + if (identifier.getEObject().eClass().getEStructuralFeature(((String) identifier.getId()).toLowerCase()) != null) { + final EStructuralFeature feat = identifier.getEObject().eClass().getEStructuralFeature(((String) identifier.getId()).toLowerCase()); + if (feat instanceof EReference && ((EReference) feat).isMany()) { + final IEditorPart part = EclipseUIUtil.getActiveEditor(); + if (part instanceof SiriusDiagramEditor) { + ((SiriusDiagramEditor) part).getDiagramEditPart().performRequest(new GroupRequest(RequestConstants.REQ_REFRESH_VIEWPOINT)); + } + } + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.ui.tools.api.properties.AbstractCompositeEObjectPropertySource#getItemProvidersAdapterFactory() + */ + @Override + protected AdapterFactory getItemProvidersAdapterFactory() { + AdapterFactory adapterFactory = null; + final IEditorPart part = EclipseUIUtil.getActiveEditor(); + if (part instanceof DDiagramEditor) { + adapterFactory = ((DDiagramEditor) part).getAdapterFactory(); + } else { + adapterFactory = SiriusDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory(); + } + return adapterFactory; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramColorAndFontPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramColorAndFontPropertySection.java new file mode 100644 index 0000000000..67d5183558 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramColorAndFontPropertySection.java @@ -0,0 +1,139 @@ +/******************************************************************************* + * Copyright (c) 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.internal.properties.Properties; +import org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.DiagramColorsAndFontsPropertySection; +import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities; +import org.eclipse.gmf.runtime.emf.core.util.PackageUtil; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.description.DescriptionFactory; +import org.eclipse.sirius.description.UserFixedColor; +import org.eclipse.sirius.diagram.internal.refresh.diagram.ViewPropertiesSynchronizer; +import org.eclipse.sirius.diagram.ui.tools.internal.dialogs.ColorPalettePopup; + +/** + * Class necessary to remove default color option. + * + * @author jdupont + * + */ +@SuppressWarnings("restriction") +public class DiagramColorAndFontPropertySection extends DiagramColorsAndFontsPropertySection { + + /** + * {@inheritDoc} + * + * @overides + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ColorsAndFontsPropertySection#changeColor(org.eclipse.swt.events.SelectionEvent, + * org.eclipse.swt.widgets.Button, java.lang.String, java.lang.String, + * org.eclipse.jface.resource.ImageDescriptor) + */ + @Override + protected RGB changeColor(final SelectionEvent event, final Button button, final String propertyId, final String commandName, final ImageDescriptor imageDescriptor) { + + RGB colorToReturn = null; + + if (!Properties.ID_FILLCOLOR.equals(propertyId) && !Properties.ID_LINECOLOR.equals(propertyId) && !Properties.ID_FONTCOLOR.equals(propertyId)) { + colorToReturn = super.changeColor(event, button, propertyId, commandName, imageDescriptor); + } else { + final ColorPalettePopup popup = new ColorPalettePopup(button.getParent().getShell(), IDialogConstants.BUTTON_BAR_HEIGHT); + final Rectangle r = button.getBounds(); + final Point location = button.getParent().toDisplay(r.x, r.y); + popup.open(new Point(location.x, location.y + r.height)); + if (popup.getSelectedColor() == null && !popup.useDefaultColor()) { + return null; + } + // selectedColor should be null if we are to use the default color + final RGB selectedColor = popup.getSelectedColor(); + + final EStructuralFeature feature = (EStructuralFeature) PackageUtil.getElement(propertyId); + + // Update model in response to user + + final List<ICommand> commands = new ArrayList<ICommand>(); + final Iterator<?> it = getInputIterator(); + + colorToReturn = selectedColor; + RGB color = selectedColor; + while (it.hasNext()) { + final IGraphicalEditPart ep = (IGraphicalEditPart) it.next(); + color = selectedColor; + if (popup.useDefaultColor()) { + final Object preferredValue = ep.getPreferredValue(feature); + if (preferredValue instanceof Integer) { + color = FigureUtilities.integerToRGB((Integer) preferredValue); + } + } + + // If we are using default colors, we want to return the color + // of the first selected element to be consistent + if (colorToReturn == null) { + colorToReturn = color; + } + + if (color != null) { + final RGB finalColor = color; // need a final variable + commands.add(createCommand(commandName, ((View) ep.getModel()).eResource(), new Runnable() { + + public void run() { + final ENamedElement element = PackageUtil.getElement(propertyId); + if (element instanceof EStructuralFeature) { + ep.setStructuralFeatureValue(feature, FigureUtilities.RGBToInteger(finalColor)); + } + + // get the view. + final View view = (View) ep.getModel(); + // change the color. + final UserFixedColor newColor = DescriptionFactory.eINSTANCE.createUserFixedColor(); + newColor.setName("<anonymous>"); + newColor.setBlue(finalColor.blue); + newColor.setGreen(finalColor.green); + newColor.setRed(finalColor.red); + + IInterpreter interpreter = new EObjectQuery(view).getSession().getInterpreter(); + new ViewPropertiesSynchronizer().synchronizeDDiagramElementStyleColorProperties(view, newColor, propertyId, interpreter); + } + })); + } + } + if (!commands.isEmpty()) { + executeAsCompositeCommand(commandName, commands); + final Image overlyedImage = new ColorOverlayImageDescriptor(imageDescriptor.getImageData(), color).createImage(); + disposeImage(button.getImage()); + button.setImage(overlyedImage); + } + } + return colorToReturn; + + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramConnectionAppearancePropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramConnectionAppearancePropertySection.java new file mode 100644 index 0000000000..f93361d58b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramConnectionAppearancePropertySection.java @@ -0,0 +1,316 @@ +/******************************************************************************* + * Copyright (c) 2008, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.internal.properties.Properties; +import org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ConnectionAppearancePropertySection; +import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities; +import org.eclipse.gmf.runtime.emf.core.util.PackageUtil; +import org.eclipse.gmf.runtime.notation.Routing; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.BracketEdgeStyle; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.EdgeStyle; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.description.DescriptionFactory; +import org.eclipse.sirius.description.UserFixedColor; +import org.eclipse.sirius.diagram.internal.refresh.diagram.ViewPropertiesSynchronizer; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.actions.style.ResetStylePropertiesToDefaultValuesAction; +import org.eclipse.sirius.diagram.ui.tools.internal.dialogs.ColorPalettePopup; + +/** + * allow color customization of diagram edges. + * + * @author fmorel + * + */ +@SuppressWarnings("restriction") +public class DiagramConnectionAppearancePropertySection extends ConnectionAppearancePropertySection { + + /** button to set back the view to default color. */ + protected Button resetStylePropertiesToDefaultValuesButton; + + private Composite routingGroups; + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ColorsAndFontsPropertySection#changeColor(org.eclipse.swt.events.SelectionEvent, + * org.eclipse.swt.widgets.Button, java.lang.String, java.lang.String, + * org.eclipse.jface.resource.ImageDescriptor) + */ + @Override + protected RGB changeColor(final SelectionEvent event, final Button button, final String propertyId, final String commandName, final ImageDescriptor imageDescriptor) { + + RGB colorToReturn = null; + if (!Properties.ID_FILLCOLOR.equals(propertyId) && !Properties.ID_LINECOLOR.equals(propertyId) && !Properties.ID_FONTCOLOR.equals(propertyId)) { + colorToReturn = super.changeColor(event, button, propertyId, commandName, imageDescriptor); + } else { + final ColorPalettePopup popup = new ColorPalettePopup(button.getParent().getShell(), IDialogConstants.BUTTON_BAR_HEIGHT); + // popup.setPreviousColor(previousColor); + final Rectangle r = button.getBounds(); + final Point location = button.getParent().toDisplay(r.x, r.y); + popup.open(new Point(location.x, location.y + r.height)); + if (popup.getSelectedColor() == null && !popup.useDefaultColor()) { + return null; + } + // selectedColor should be null if we are to use the default color + final RGB selectedColor = popup.getSelectedColor(); + + final EStructuralFeature feature = (EStructuralFeature) PackageUtil.getElement(propertyId); + + // Update model in response to user + + final List<ICommand> commands = new ArrayList<ICommand>(); + final Iterator<?> it = getInputIterator(); + + colorToReturn = selectedColor; + RGB color = selectedColor; + while (it.hasNext()) { + final IGraphicalEditPart ep = (IGraphicalEditPart) it.next(); + + color = selectedColor; + if (popup.useDefaultColor()) { + final Object preferredValue = ep.getPreferredValue(feature); + if (preferredValue instanceof Integer) { + color = FigureUtilities.integerToRGB((Integer) preferredValue); + } + } + + // If we are using default colors, we want to return the color + // of the first selected element to be consistent + if (colorToReturn == null) { + colorToReturn = color; + } + + if (color != null) { + final RGB finalColor = color; // need a final variable + commands.add(createCommand(commandName, ((View) ep.getModel()).eResource(), new Runnable() { + + public void run() { + final ENamedElement element = PackageUtil.getElement(propertyId); + if (element instanceof EStructuralFeature) { + ep.setStructuralFeatureValue(feature, FigureUtilities.RGBToInteger(finalColor)); + } + + // get the view. + final View view = (View) ep.getModel(); + + // change the color. + final UserFixedColor newColor = DescriptionFactory.eINSTANCE.createUserFixedColor(); + newColor.setName("<anonymous>"); + newColor.setBlue(finalColor.blue); + newColor.setGreen(finalColor.green); + newColor.setRed(finalColor.red); + + IInterpreter interpreter = new EObjectQuery(view).getSession().getInterpreter(); + new ViewPropertiesSynchronizer().synchronizeDDiagramElementStyleColorProperties(view, newColor, propertyId, interpreter); + } + })); + } + } + if (!commands.isEmpty()) { + executeAsCompositeCommand(commandName, commands); + final Image overlyedImage = new ColorOverlayImageDescriptor(imageDescriptor.getImageData(), color).createImage(); + disposeImage(button.getImage()); + button.setImage(overlyedImage); + } + } + return colorToReturn; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ShapeColorsAndFontsPropertySection#createFontsGroup(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Composite createFontsGroup(final Composite parent) { + final Composite toolBar = super.createFontsGroup(parent); + + final String path = "icons/undo_edit.gif"; + final Image image = SiriusDiagramEditorPlugin.getInstance().getBundledImage(path); + + resetStylePropertiesToDefaultValuesButton = new Button(toolBar, SWT.PUSH); + resetStylePropertiesToDefaultValuesButton.setToolTipText(ResetStylePropertiesToDefaultValuesAction.ACTION_NAME); + resetStylePropertiesToDefaultValuesButton.setImage(image); + resetStylePropertiesToDefaultValuesButton.addSelectionListener(new ResetStylePropertiesToDefaultValuesSelectionAdapter(this)); + resetStylePropertiesToDefaultValuesButton.setEnabled(!isReadOnly()); + + return toolBar; + } + + /** + * Overridden to access to the routing group. + * + * {@inheritDoc} + */ + @Override + protected void createRouterOptionsGroup(Composite groups) { + super.createRouterOptionsGroup(groups); + routingGroups = groups; + } + + /** + * Overridden to display property of selection only if semantic element of + * selection exists. + * + * {@inheritDoc} + */ + @Override + public void setInput(IWorkbenchPart workbenchPart, ISelection selection) { + if (selection.isEmpty() || !(selection instanceof StructuredSelection)) { + super.setInput(workbenchPart, selection); + return; + } + StructuredSelection structuredSelection = (StructuredSelection) selection; + List<Object> newSelection = new ArrayList<Object>(); + Iterator<?> it = structuredSelection.iterator(); + while (it.hasNext()) { + Object selectionItem = it.next(); + if (transformSelection(selectionItem) != null) { + newSelection.add(selectionItem); + } + } + composite.setVisible(!newSelection.isEmpty()); + super.setInput(workbenchPart, new StructuredSelection(newSelection)); + } + + /** + * Transform selection to have {@link DSemanticDecorator} instead of + * {@link EditPart} or null if the semantic element (target) not exists. + * + * @param selection + * the currently selected object + * @return the unwrapped object + */ + protected Object transformSelection(final Object selection) { + + Object object = selection; + + if (object instanceof EditPart) { + object = ((EditPart) object).getModel(); + } else if (object instanceof IAdaptable) { + object = ((IAdaptable) object).getAdapter(View.class); + } + + if (object instanceof View) { + object = ((View) object).getElement(); + } + + if (object instanceof DSemanticDecorator) { + EObject target = ((DSemanticDecorator) object).getTarget(); + if (target == null || target.eResource() == null) { + object = null; + } + } + return object; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ColorsAndFontsPropertySection#dispose() + */ + @Override + public void dispose() { + resetStylePropertiesToDefaultValuesButton = null; + routingGroups = null; + super.dispose(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ShapeColorsAndFontsPropertySection#refresh() + */ + @Override + public void refresh() { + if (!isDisposed() && getSingleInput() != null) { + super.refresh(); + executeAsReadAction(new Runnable() { + + public void run() { + boolean hasBracketStyle = false; + final IGraphicalEditPart ep = getSingleInput(); + if (ep != null) { + final View view = (View) ep.getModel(); + final EObject dDiagElement = view.getElement(); + if (dDiagElement instanceof DEdge) { + DEdge dEdge = (DEdge) dDiagElement; + EdgeStyle edgeStyle = dEdge.getOwnedStyle(); + hasBracketStyle = edgeStyle instanceof BracketEdgeStyle; + } + boolean isReadOnly = isReadOnly(); + boolean isCustomizedView = ResetStylePropertiesToDefaultValuesSelectionAdapter.isCustomizedView(view); + resetStylePropertiesToDefaultValuesButton.setEnabled(!isReadOnly && isCustomizedView); + enableRoutingGroup(hasBracketStyle); + } + } + }); + } + } + + /** + * Disable the routing group and all its children for Bracket edge. + * + * @param hasBracketStyle + * true if we must disable, false otherwise + */ + private void enableRoutingGroup(boolean hasBracketStyle) { + routingGroups.setEnabled(!hasBracketStyle); + for (Control childControl : routingGroups.getChildren()) { + childControl.setEnabled(!hasBracketStyle); + if (childControl instanceof Composite) { + Composite childComposite = (Composite) childControl; + for (Control childOfChildControl : childComposite.getChildren()) { + childOfChildControl.setEnabled(!hasBracketStyle); + } + } + } + for (Object object : Routing.VALUES) { + Object buttonObj = buttons.get(object); + if (buttonObj instanceof Button) { + Button button = (Button) buttonObj; + button.setEnabled(!hasBracketStyle); + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramShapeColorAndFontPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramShapeColorAndFontPropertySection.java new file mode 100644 index 0000000000..17be9d354e --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DiagramShapeColorAndFontPropertySection.java @@ -0,0 +1,445 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.ecore.ENamedElement; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.common.core.command.ICommand; +import org.eclipse.gmf.runtime.diagram.core.util.ViewType; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.internal.properties.Properties; +import org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ShapeColorsAndFontsPropertySection; +import org.eclipse.gmf.runtime.draw2d.ui.figures.FigureUtilities; +import org.eclipse.gmf.runtime.emf.core.util.PackageUtil; +import org.eclipse.gmf.runtime.notation.NotationPackage; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.dialogs.IDialogConstants; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.accessibility.AccessibleAdapter; +import org.eclipse.swt.accessibility.AccessibleEvent; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.IWorkbenchPart; + +import org.eclipse.sirius.common.tools.api.interpreter.IInterpreter; +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.BasicLabelStyle; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.business.api.query.EObjectQuery; +import org.eclipse.sirius.description.DescriptionFactory; +import org.eclipse.sirius.description.UserFixedColor; +import org.eclipse.sirius.diagram.ImagesPath; +import org.eclipse.sirius.diagram.business.api.image.ImageSelector; +import org.eclipse.sirius.diagram.business.api.image.ImageSelectorService; +import org.eclipse.sirius.diagram.edit.api.part.IDiagramElementEditPart; +import org.eclipse.sirius.diagram.internal.refresh.diagram.ViewPropertiesSynchronizer; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.actions.style.ResetStylePropertiesToDefaultValuesAction; +import org.eclipse.sirius.diagram.tools.internal.actions.style.SetStyleToWorkspaceImageAction; +import org.eclipse.sirius.diagram.ui.tools.internal.dialogs.ColorPalettePopup; + +/** + * allow color customization of diagram nodes. + * + * @author fmorel + */ +@SuppressWarnings("restriction") +public class DiagramShapeColorAndFontPropertySection extends ShapeColorsAndFontsPropertySection { + + /** + * button to set back the view to default color. + */ + protected Button resetStylePropertiesToDefaultValuesButton; + + /** + * button to allow user to select an image in the workspace and set the + * selected image as view backgroud image. + */ + protected Button setStyleToWorkspaceImageButton; + + /** + * button to set back the view to default color. + */ + private Button fontUnderlineButton; + + /** + * button to set back the view to default color. + */ + private Button fontStrikeThroughButton; + + /** + * {@inheritDoc} + * + * @overrides + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ColorsAndFontsPropertySection#changeColor(org.eclipse.swt.events.SelectionEvent, + * org.eclipse.swt.widgets.Button, java.lang.String, java.lang.String, + * org.eclipse.jface.resource.ImageDescriptor) + */ + @Override + protected RGB changeColor(final SelectionEvent event, final Button button, final String propertyId, final String commandName, final ImageDescriptor imageDescriptor) { + + RGB colorToReturn = null; + + if (!Properties.ID_FILLCOLOR.equals(propertyId) && !Properties.ID_LINECOLOR.equals(propertyId) && !Properties.ID_FONTCOLOR.equals(propertyId)) { + colorToReturn = super.changeColor(event, button, propertyId, commandName, imageDescriptor); + } else { + final ColorPalettePopup popup = new ColorPalettePopup(button.getParent().getShell(), IDialogConstants.BUTTON_BAR_HEIGHT); + final Rectangle r = button.getBounds(); + final Point location = button.getParent().toDisplay(r.x, r.y); + popup.open(new Point(location.x, location.y + r.height)); + if (popup.getSelectedColor() == null && !popup.useDefaultColor()) { + return null; + } + // selectedColor should be null if we are to use the default color + final RGB selectedColor = popup.getSelectedColor(); + + final EStructuralFeature feature = (EStructuralFeature) PackageUtil.getElement(propertyId); + + // Update model in response to user + + final List<ICommand> commands = new ArrayList<ICommand>(); + final Iterator<?> it = getInputIterator(); + + colorToReturn = selectedColor; + RGB color = selectedColor; + while (it.hasNext()) { + final IGraphicalEditPart ep = (IGraphicalEditPart) it.next(); + color = selectedColor; + if (popup.useDefaultColor()) { + final Object preferredValue = ep.getPreferredValue(feature); + if (preferredValue instanceof Integer) { + color = FigureUtilities.integerToRGB((Integer) preferredValue); + } + } + + // If we are using default colors, we want to return the color + // of the first selected element to be consistent + if (colorToReturn == null) { + colorToReturn = color; + } + + if (color != null) { + final RGB finalColor = color; // need a final variable + commands.add(createCommand(commandName, ((View) ep.getModel()).eResource(), new Runnable() { + + public void run() { + final ENamedElement element = PackageUtil.getElement(propertyId); + if (element instanceof EStructuralFeature) { + ep.setStructuralFeatureValue(feature, FigureUtilities.RGBToInteger(finalColor)); + } + + // get the view. + final View view = (View) ep.getModel(); + // change the color. + final UserFixedColor newColor = DescriptionFactory.eINSTANCE.createUserFixedColor(); + newColor.setName("<anonymous>"); + newColor.setBlue(finalColor.blue); + newColor.setGreen(finalColor.green); + newColor.setRed(finalColor.red); + + IInterpreter interpreter = new EObjectQuery(view).getSession().getInterpreter(); + // new + // ViewPropertiesSynchronizer().synchronizeDDiagramElementStyleProperties(view); + new ViewPropertiesSynchronizer().synchronizeDDiagramElementStyleColorProperties(view, newColor, propertyId, interpreter); + } + + })); + } + } + if (!commands.isEmpty()) { + executeAsCompositeCommand(commandName, commands); + final Image overlyedImage = new ColorOverlayImageDescriptor(imageDescriptor.getImageData(), color).createImage(); + disposeImage(button.getImage()); + button.setImage(overlyedImage); + } + } + return colorToReturn; + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ColorsAndFontsPropertySection#createFontsAndColorsGroups(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Group createFontsAndColorsGroups(final Composite parent) { + return super.createFontsAndColorsGroups(parent); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ShapeColorsAndFontsPropertySection#createFontsGroup(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Composite createFontsGroup(final Composite parent) { + final Composite toolBar = super.createFontsGroup(parent); + + final Image imageUndo = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.UNDO_ICON); + final Image imageImage = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.IMAGE_ICON); + final Image imageUnderline = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.UNDERLINE_ICON); + final Image imageStrikeThrough = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.STRIKE_THROUGH_ICON); + + fontUnderlineButton = new Button(toolBar, SWT.TOGGLE); + fontUnderlineButton.setImage(imageUnderline); + fontUnderlineButton.getAccessible().addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(final AccessibleEvent e) { + e.result = "Underline"; + } + }); + + fontStrikeThroughButton = new Button(toolBar, SWT.TOGGLE); + fontStrikeThroughButton.setImage(imageStrikeThrough); + fontStrikeThroughButton.getAccessible().addAccessibleListener(new AccessibleAdapter() { + @Override + public void getName(final AccessibleEvent e) { + e.result = "StrikeThrough"; + } + }); + + new Label(toolBar, SWT.LEFT); + new Label(toolBar, SWT.LEFT); + new Label(toolBar, SWT.LEFT); + + setStyleToWorkspaceImageButton = new Button(toolBar, SWT.PUSH); + setStyleToWorkspaceImageButton.setToolTipText(SetStyleToWorkspaceImageAction.SET_STYLE_TO_WORKSPACE_IMAGE_ACTION_NAME); + setStyleToWorkspaceImageButton.setImage(imageImage); + setStyleToWorkspaceImageButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(final SelectionEvent event) { + setBackgroundImage(event); + } + }); + setStyleToWorkspaceImageButton.setEnabled(!isReadOnly()); + + resetStylePropertiesToDefaultValuesButton = new Button(toolBar, SWT.PUSH); + resetStylePropertiesToDefaultValuesButton.setToolTipText(ResetStylePropertiesToDefaultValuesAction.ACTION_NAME); + resetStylePropertiesToDefaultValuesButton.setImage(imageUndo); + resetStylePropertiesToDefaultValuesButton.addSelectionListener(new ResetStylePropertiesToDefaultValuesSelectionAdapter(this)); + resetStylePropertiesToDefaultValuesButton.setEnabled(!isReadOnly()); + + fontUnderlineButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(final SelectionEvent event) { + updateFontUnderline(); + } + }); + + fontStrikeThroughButton.addSelectionListener(new SelectionAdapter() { + + @Override + public void widgetSelected(final SelectionEvent event) { + updateFontStrikeThrough(); + } + }); + + return toolBar; + } + + /** + * Overridden to display property of selection only if semantic element of + * selection exists. + * + * {@inheritDoc} + */ + @Override + public void setInput(IWorkbenchPart workbenchPart, ISelection selection) { + if (selection.isEmpty() || !(selection instanceof StructuredSelection)) { + super.setInput(workbenchPart, selection); + return; + } + StructuredSelection structuredSelection = (StructuredSelection) selection; + List<Object> newSelection = new ArrayList<Object>(); + Iterator<?> it = structuredSelection.iterator(); + while (it.hasNext()) { + Object selectionItem = it.next(); + if (transformSelection(selectionItem) != null) { + newSelection.add(selectionItem); + } + } + composite.setVisible(!newSelection.isEmpty()); + super.setInput(workbenchPart, new StructuredSelection(newSelection)); + } + + /** + * Transform selection to have {@link DSemanticDecorator} instead of + * {@link EditPart} or null if the semantic element (target) not exists. + * + * @param selection + * the currently selected object + * @return the unwrapped object + */ + protected Object transformSelection(final Object selection) { + + Object object = selection; + + if (object instanceof EditPart) { + object = ((EditPart) object).getModel(); + } else if (object instanceof IAdaptable) { + object = ((IAdaptable) object).getAdapter(View.class); + } + + if (object instanceof View) { + object = ((View) object).getElement(); + } + + if (object instanceof DSemanticDecorator) { + EObject target = ((DSemanticDecorator) object).getTarget(); + if (target == null || target.eResource() == null) { + object = null; + } + } + return object; + } + + /** + * Change fill color to default color. + * + * @param event + * event from the button push. + */ + protected void setBackgroundImage(final SelectionEvent event) { + ImageSelector imageSelector = ImageSelectorService.INSTANCE.getImageSelector(); + List<BasicLabelStyle> styles = getStyles(); + for (BasicLabelStyle basicLabelStyle : styles) { + String imagePath = imageSelector.selectImage(basicLabelStyle); + if (imagePath != null) { + ImageSelectorService.INSTANCE.updateStyle(basicLabelStyle, imagePath); + } + } + } + + private List<BasicLabelStyle> getStyles() { + List<BasicLabelStyle> styles = new ArrayList<BasicLabelStyle>(); + for (Object selectedEditPart : input) { + if (selectedEditPart instanceof IDiagramElementEditPart) { + IDiagramElementEditPart diagramElementEditPart = (IDiagramElementEditPart) selectedEditPart; + DDiagramElement dde = diagramElementEditPart.resolveDiagramElement(); + DDiagramElementQuery ddeQuery = new DDiagramElementQuery(dde); + Option<BasicLabelStyle> oldStyle = ddeQuery.getLabelStyle(); + if (oldStyle.some()) { + BasicLabelStyle basicLabelStyle = oldStyle.get(); + styles.add(basicLabelStyle); + } + } + } + return styles; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ColorsAndFontsPropertySection#dispose() + */ + @Override + public void dispose() { + super.dispose(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ShapeColorsAndFontsPropertySection#refresh() + */ + @Override + public void refresh() { + if (!isDisposed()) { + super.refresh(); + executeAsReadAction(new Runnable() { + + public void run() { + final IGraphicalEditPart ep = getSingleInput(); + if (ep != null) { + final View view = (View) ep.getModel(); + boolean isCustomizedView = ResetStylePropertiesToDefaultValuesSelectionAdapter.isCustomizedView(view); + boolean enableWorkspaceImageButton = true; + if (ViewType.NOTE.equals(view.getType()) || ViewType.TEXT.equals(view.getType())) { + enableWorkspaceImageButton = false; + } + final boolean isReadOnly = isReadOnly(); + if (resetStylePropertiesToDefaultValuesButton != null) { + resetStylePropertiesToDefaultValuesButton.setEnabled(!isReadOnly && isCustomizedView); + } + if (setStyleToWorkspaceImageButton != null) { + setStyleToWorkspaceImageButton.setEnabled(!isReadOnly && enableWorkspaceImageButton); + } + boolean underlined = (Boolean) ep.getStructuralFeatureValue(NotationPackage.eINSTANCE.getFontStyle_Underline()); + boolean striked = (Boolean) ep.getStructuralFeatureValue(NotationPackage.eINSTANCE.getFontStyle_StrikeThrough()); + fontUnderlineButton.setSelection(underlined); + fontStrikeThroughButton.setSelection(striked); + } + } + }); + } + } + + private void updateFontUnderline() { + // Update model in response to user + final List<ICommand> commands = new ArrayList<ICommand>(); + final Iterator<?> it = getInputIterator(); + + while (it.hasNext()) { + final IGraphicalEditPart ep = (IGraphicalEditPart) it.next(); + commands.add(createCommand(FONT_COMMAND_NAME, ((View) ep.getModel()).eResource(), new Runnable() { + + public void run() { + ep.setStructuralFeatureValue(NotationPackage.eINSTANCE.getFontStyle_Underline(), Boolean.valueOf(fontUnderlineButton.getSelection())); + } + })); + } + + executeAsCompositeCommand(FONT_COMMAND_NAME, commands); + + } + + private void updateFontStrikeThrough() { + // Update model in response to user + final List<ICommand> commands = new ArrayList<ICommand>(); + final Iterator<?> it = getInputIterator(); + + while (it.hasNext()) { + final IGraphicalEditPart ep = (IGraphicalEditPart) it.next(); + commands.add(createCommand(FONT_COMMAND_NAME, ((View) ep.getModel()).eResource(), new Runnable() { + + public void run() { + ep.setStructuralFeatureValue(NotationPackage.eINSTANCE.getFontStyle_StrikeThrough(), Boolean.valueOf(fontStrikeThroughButton.getSelection())); + } + })); + } + + executeAsCompositeCommand(FONT_COMMAND_NAME, commands); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DocumentationPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DocumentationPropertySection.java new file mode 100644 index 0000000000..47eda9942a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/DocumentationPropertySection.java @@ -0,0 +1,297 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.List; + +import org.eclipse.emf.common.command.CompoundCommand; +import org.eclipse.emf.ecore.EAttribute; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.edit.command.SetCommand; +import org.eclipse.emf.edit.domain.EditingDomain; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.text.Document; +import org.eclipse.jface.text.ITextListener; +import org.eclipse.jface.text.TextEvent; +import org.eclipse.jface.text.source.SourceViewer; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.views.properties.tabbed.AbstractPropertySection; +import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; + +import org.eclipse.sirius.description.DescriptionPackage; +import org.eclipse.sirius.diagram.internal.edit.parts.DDiagramEditPart; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditor; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; + +/** + * Section that allows to set a documentation about a diagram. + * + * @author smonnier + * + */ +public class DocumentationPropertySection extends AbstractPropertySection { + + /** The label width that will be used for section names. */ + public static final int LABEL_WIDTH = 292; + + /** Full path of the help icon. */ + public static final String ICONS_PREFERENCES_HELP = "icons/help.gif"; //$NON-NLS-1$ + + /** The default size of the SourceViewer. */ + protected static final int SOURCE_VIEWER_DEFAULT_SIZE = 150; + + /** SourceViewer control of the section. */ + protected SourceViewer sourceViewer; + + /** Label control of the section. */ + protected CLabel nameLabel; + + /** Main composite. **/ + protected Composite composite; + + /** + * Current selected object or first object in the selection when multiple + * objects are selected. + */ + protected EObject eObject; + + /** The list of currently selected objects. */ + protected List<EObject> eObjectList; + + /** + * Internal text listener for updating all content dependent actions. The + * updating is done asynchronously. + */ + class TextListener implements ITextListener { + + public void textChanged(TextEvent event) { + handleTextModified(); + } + + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.ITabbedPropertySection#createControls(org.eclipse.swt.widgets.Composite, + * org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage) + */ + @Override + public void createControls(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage) { + super.createControls(parent, tabbedPropertySheetPage); + + composite = getWidgetFactory().createFlatFormComposite(parent); + composite.setLayout(new GridLayout(3, false)); + + nameLabel = getWidgetFactory().createCLabel(composite, getLabelText(), SWT.TOP); + nameLabel.setLayoutData(new GridData(SWT.NULL, SWT.TOP, false, true)); + + CLabel help = getWidgetFactory().createCLabel(composite, ""); + help.setImage(getHelpIcon()); + help.setLayoutData(new GridData(SWT.NULL, SWT.TOP, false, true)); + help.setToolTipText(getToolTipText()); + + int styles = SWT.V_SCROLL | SWT.H_SCROLL | SWT.MULTI | SWT.BORDER; + sourceViewer = new SourceViewer(composite, null, styles); + GridData layoutData = new GridData(GridData.FILL_BOTH); + layoutData.widthHint = SOURCE_VIEWER_DEFAULT_SIZE; + layoutData.heightHint = SOURCE_VIEWER_DEFAULT_SIZE; + sourceViewer.getControl().setLayoutData(layoutData); + + TextListener fTextListener = new TextListener(); + sourceViewer.addTextListener(fTextListener); + // sourceViewer.addTextInputListener(fTextListener); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.editor.properties.sections.common.AbstractSiriusPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public void setInput(IWorkbenchPart part, ISelection selection) { + super.setInput(part, selection); + nameLabel.setText(getLabelText()); + if (selection instanceof StructuredSelection && ((StructuredSelection) selection).getFirstElement() instanceof DDiagramEditPart) { + DDiagramEditPart ddep = (DDiagramEditPart) ((StructuredSelection) selection).getFirstElement(); + eObject = ((Diagram) ddep.getModel()).getElement(); + eObjectList = ((IStructuredSelection) selection).toList(); + } + } + + /** + * Handle the text modified event. + */ + protected void handleTextModified() { + String newText = sourceViewer.getDocument().get(); + boolean equals = isEqual(newText); + + if (!equals) { + EditingDomain editingDomain = ((SiriusDiagramEditor) getPart()).getEditingDomain(); + Object value = getFeatureValue(newText); + if (eObjectList.size() == 1) { + // apply the property change to single selected object + editingDomain.getCommandStack().execute(SetCommand.create(editingDomain, eObject, getFeature(), value)); + } else { + CompoundCommand compoundCommand = new CompoundCommand(); + /* apply the property change to all selected elements */ + for (EObject nextObject : eObjectList) { + compoundCommand.append(SetCommand.create(editingDomain, nextObject, getFeature(), value)); + } + editingDomain.getCommandStack().execute(compoundCommand); + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.view.ITabbedPropertySection#refresh() + */ + @Override + public void refresh() { + if (getFeatureAsText() != null) { + sourceViewer.setDocument(new Document(getFeatureAsText())); + } + } + + /** + * Determine if the provided string value is an equal representation of the + * current setting of the text property. + * + * @param newText + * the new string value. + * @return <code>True</code> if the new string value is equal to the current + * property setting, <code>False</code> otherwise. + * @see org.eclipse.sirius.editor.properties.sections.AbstractMultilinePropertySection#isEqual(String) + */ + protected boolean isEqual(String newText) { + return getFeatureAsText().equals(newText); + } + + /** + * Get the feature for the text field of this section. + * + * @return The feature for the text. + * @see org.eclipse.sirius.editor.properties.sections.AbstractMultilinePropertySection#getFeature() + */ + public EAttribute getFeature() { + return DescriptionPackage.eINSTANCE.getDocumentedElement_Documentation(); + } + + /** + * Get the base description. + * + * @return The description for the feature. + * @see org.eclipse.sirius.editor.properties.sections.AbstractMultilinePropertySection#getPropertyDescription() + */ + protected String getPropertyDescription() { + return "Use this field to save notes about this representation."; + } + + protected String getToolTipText() { + return getPropertyDescription(); + } + + /** + * Get the value of the default feature as text for the text field of the + * section. + * + * @return The value of the default feature as text. + */ + protected String getDefaultFeatureAsText() { + String value = ""; + if (eObject != null && eObject.eGet(getFeature()) != null) { + value = eObject.eGet(getFeature()).toString(); + } + return value; + } + + /** + * Get the value of the feature as text for the text field of the section. + * + * @return The value of the feature as text. + */ + protected String getFeatureAsText() { + // final EStructuralFeature eFeature = getFeature(); + // final IItemPropertyDescriptor propertyDescriptor = + // getPropertyDescriptor(eFeature); + // if (propertyDescriptor != null) + // return + // propertyDescriptor.getLabelProvider(eObject).getText(eObject.eGet(eFeature)); + return getDefaultFeatureAsText(); + } + + /** + * Get the new value of the feature for the text field of the section. + * + * @param newText + * The new value of the feature as a string. + * @return The new value of the feature. + * @see org.eclipse.sirius.editor.properties.sections.AbstractMultilinePropertySection#getFeatureValue() + */ + protected Object getFeatureValue(String newText) { + return newText; + } + + /** + * Get the default label for the text field of the section. + * + * @return The default label for the text field. + */ + protected String getDefaultLabelText() { + return "Documentation:"; //$NON-NLS-1$ + } + + /** + * Get the label for the text field of the section. + * + * @return The label for the text field. + */ + protected String getLabelText() { + return getDefaultLabelText(); + } + + /** + * {@inheritDoc} + */ + protected void makeReadonly() { + sourceViewer.setEditable(false); + } + + /** + * {@inheritDoc} + */ + protected void makeWrittable() { + sourceViewer.setEditable(true); + } + + /** + * Creates and return the help icon to show in our label. + * + * @return The help icon to show in our label. + */ + protected Image getHelpIcon() { + ImageDescriptor findImageDescriptor = SiriusDiagramEditorPlugin.findImageDescriptor(ICONS_PREFERENCES_HELP); + return SiriusDiagramEditorPlugin.getInstance().getImage(findImageDescriptor); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ExtendedPropertySource.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ExtendedPropertySource.java new file mode 100644 index 0000000000..2cc54d7022 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ExtendedPropertySource.java @@ -0,0 +1,422 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.common.ui.celleditor.ExtendedDialogCellEditor; +import org.eclipse.emf.ecore.EEnumLiteral; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.emf.edit.provider.IItemPropertyDescriptor; +import org.eclipse.emf.edit.provider.IItemPropertySource; +import org.eclipse.emf.edit.ui.provider.PropertyDescriptor; +import org.eclipse.gmf.runtime.emf.core.util.EMFCoreUtil; +import org.eclipse.jface.viewers.BaseLabelProvider; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ILabelProvider; +import org.eclipse.jface.viewers.TextCellEditor; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.views.properties.IPropertyDescriptor; +import org.eclipse.ui.views.properties.IPropertySource; + +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.diagram.ui.tools.internal.dialogs.ExtendedFeatureEditorDialog; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ExtensionFeatureDescription; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; +import org.eclipse.sirius.ecore.extender.business.api.accessor.exception.FeatureNotFoundException; +import org.eclipse.sirius.ecore.extender.business.api.permission.IPermissionAuthority; + +/** + * Property source for the extension framework. + * + * @author ymortier + */ +public class ExtendedPropertySource implements IPropertySource { + + /** Empty String array. */ + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + + /** Empty properties descriptors array. */ + private static final IPropertyDescriptor[] EMPTY = new IPropertyDescriptor[0]; + + /** + * The target. + */ + private DSemanticDecorator target; + + /** + * The adapterFactory of the caller + */ + private AdapterFactory adapterFactory; + + /** + * Create a new {@link ExtendedPropertySource} for the specified element. + * + * @param target + * the element. + * @param adapterFactory + * the adapterFactory of the caller + */ + public ExtendedPropertySource(DSemanticDecorator target, AdapterFactory adapterFactory) { + this.target = target; + this.adapterFactory = adapterFactory; + } + + /** + * Returns the model accesor. + * + * @return the model accesor. + */ + protected ModelAccessor getModelAccessor() { + return SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(target); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#getEditableValue() + */ + public Object getEditableValue() { + return target.getTarget(); + } + + /** + * Returns the properties descriptors. {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyDescriptors() + */ + public IPropertyDescriptor[] getPropertyDescriptors() { + if (this.target == null || this.target.getTarget() == null || this.getModelAccessor() == null) { + return EMPTY; + } + final List<IPropertyDescriptor> propertyDescriptors = new ArrayList<IPropertyDescriptor>(); + for (Object featureDescription : getModelAccessor().getAllExtensionFeatureDescriptions(this.target.getTarget())) { + if (featureDescription instanceof ExtensionFeatureDescription) { + final boolean isReferenceContainment = ((ExtensionFeatureDescription) featureDescription).isReference() && ((ExtensionFeatureDescription) featureDescription).isContainment(); + if (!isReferenceContainment) { + propertyDescriptors.add(createPropertyDescriptor((ExtensionFeatureDescription) featureDescription)); + } + } + } + return propertyDescriptors.toArray(new IPropertyDescriptor[propertyDescriptors.size()]); + } + + private IPropertyDescriptor createPropertyDescriptor(final ExtensionFeatureDescription featureDescription) { + final IPropertyDescriptor result; + final IItemPropertyDescriptor emfPropertyDescriptor = featureDescription.getPropertyDescriptor(target.getTarget()); + if (emfPropertyDescriptor == null) { + result = new ExtendedPropertyDescriptor(featureDescription); + } else { + // + // the feature description provides a property descriptor. We can + // use it to creates the UI. + result = new PropertyDescriptor(target.getTarget(), emfPropertyDescriptor); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#getPropertyValue(java.lang.Object) + */ + public Object getPropertyValue(final Object id) { + Object object = null; + if (id instanceof ExtensionFeatureDescription) { + try { + object = getModelAccessor().eGet(target.getTarget(), ExtendedPropertySource.getFeatureName((ExtensionFeatureDescription) id)); + } catch (final FeatureNotFoundException e) { + // do nothing -> return null + } + } else { + final IItemPropertySource ips = (IItemPropertySource) adapterFactory.adapt(target.getTarget(), IItemPropertySource.class); + if (ips != null) { + object = ips.getPropertyDescriptor(target.getTarget(), id).getPropertyValue(target.getTarget()); + } + if (target.getTarget() instanceof IAdaptable) { + final IPropertySource propertySource = (IPropertySource) ((IAdaptable) target.getTarget()).getAdapter(IPropertySource.class); + object = propertySource.getPropertyValue(id); + } + + } + return object != null ? object : StringUtil.EMPTY_STRING; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#isPropertySet(java.lang.Object) + */ + public boolean isPropertySet(final Object id) { + return this.getPropertyValue(id) != null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#resetPropertyValue(java.lang.Object) + */ + public void resetPropertyValue(final Object id) { + // TODOYMO Auto-generated method stub + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySource#setPropertyValue(java.lang.Object, + * java.lang.Object) + */ + public void setPropertyValue(final Object id, final Object value) { + if (id instanceof ExtensionFeatureDescription) { + final ExtensionFeatureDescription desc = (ExtensionFeatureDescription) id; + if (desc.isAttribute()) { + try { + this.getModelAccessor().eSet(target.getTarget(), ExtendedPropertySource.getFeatureName((ExtensionFeatureDescription) id), value); + } catch (final FeatureNotFoundException e) { + SiriusPlugin.getDefault().error("Error while setting the property value", e); + } + } else if (desc.isReference()) { + this.getModelAccessor().eClear(target.getTarget(), ExtendedPropertySource.getFeatureName((ExtensionFeatureDescription) id)); + if (value instanceof Collection) { + final Iterator<?> iterValues = ((Collection<?>) value).iterator(); + while (iterValues.hasNext()) { + try { + this.getModelAccessor().eAdd(target.getTarget(), ExtendedPropertySource.getFeatureName((ExtensionFeatureDescription) id), iterValues.next()); + } catch (final FeatureNotFoundException e) { + // do nothing + } + } + } + } + } else { + final IItemPropertySource ips = (IItemPropertySource) adapterFactory.adapt(target.getTarget(), IItemPropertySource.class); + if (ips != null) { + ips.getPropertyDescriptor(target.getTarget(), id).setPropertyValue(target.getTarget(), value); + } + } + // else throw new AssertionError?(); + } + + /** + * The property source descriptor. + * + * @author ymortier + */ + private class ExtendedPropertyDescriptor extends BaseLabelProvider implements IPropertyDescriptor, ILabelProvider { + + /** The description of this extension. */ + private ExtensionFeatureDescription extensionDescription; + + /** + * Creates a new <code>ExtendedPropertyDescriptor</code>. + * + * @param extensionDescription + * the corresponding extension. + */ + public ExtendedPropertyDescriptor(final ExtensionFeatureDescription extensionDescription) { + this.extensionDescription = extensionDescription; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertyDescriptor#createPropertyEditor(org.eclipse.swt.widgets.Composite) + */ + public CellEditor createPropertyEditor(final Composite parent) { + final EObject editableValue = (EObject) getEditableValue(); + CellEditor editor = null; + + if (!getPermissionAuthority(editableValue).canEditInstance(editableValue)) { + // do nothing -> leave editor to null + } else if (extensionDescription.isAttribute()) { + editor = new TextCellEditor(parent); + } else if (extensionDescription.isReference()) { + List<EObject> referenceValues = Collections.emptyList(); + try { + referenceValues = (List<EObject>) getModelAccessor().eGet(editableValue, ExtendedPropertySource.getFeatureName(extensionDescription)); + } catch (final FeatureNotFoundException e) { + SiriusPlugin.getDefault().error("Error while retrieving reference values", e); + } + final List<EObject> ref = referenceValues == null ? Collections.EMPTY_LIST : referenceValues; + + editor = new ExtendedDialogCellEditor(parent, getLabelProvider()) { + + @Override + protected Object openDialogBox(final Control cellEditorWindow) { + final ExtendedFeatureEditorDialog dialog = new ExtendedFeatureEditorDialog(parent.getShell(), getChoices(), ref, extensionDescription); + dialog.open(); + return dialog.getResult(); + } + }; + } else { + throw new UnsupportedOperationException("unknown extension"); + } + return editor; + } + + private IPermissionAuthority getPermissionAuthority(final EObject instance) { + final ModelAccessor accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(instance); + return accessor.getPermissionAuthority(); + } + + /** + * Returns the values that can be added into the reference description. + * + * @return the values that can be added into the reference description. + */ + private List<EObject> getChoices() { + final List<EObject> result = new LinkedList<EObject>(); + if (this.extensionDescription.isReference()) { + if (!extensionDescription.isContainment()) { + final EObject root = EcoreUtil.getRootContainer(target.getTarget()); + result.addAll(getModelAccessor().eAllContents(root, extensionDescription.getTypeName())); + } + } + return result; + } + + /** + * @see IPropertyDescriptor#getCategory() + */ + public String getCategory() { + return "Extended"; + } + + /** + * @see IPropertyDescriptor#getDescription() + */ + public String getDescription() { + return "Property source for the extension framework"; + } + + /** + * @see IPropertyDescriptor#getDisplayName() + */ + public String getDisplayName() { + return ExtendedPropertySource.getFeatureName(this.extensionDescription); + } + + /** + * @see IPropertyDescriptor#getFilterFlags() + */ + public String[] getFilterFlags() { + return EMPTY_STRING_ARRAY; + } + + /** + * @see IPropertyDescriptor#getHelpContextIds() + */ + public Object getHelpContextIds() { + return null; + } + + /** + * @see IPropertyDescriptor#getId() + */ + public Object getId() { + return this.extensionDescription; + } + + /** + * @see IPropertyDescriptor#getLabelProvider() + */ + public ILabelProvider getLabelProvider() { + return this; + } + + /** + * @see IPropertyDescriptor#isCompatibleWith(IPropertyDescriptor) + */ + public boolean isCompatibleWith(final IPropertyDescriptor anotherProperty) { + if (anotherProperty instanceof ExtendedPropertyDescriptor) { + return this.extensionDescription.equals(((ExtendedPropertyDescriptor) anotherProperty).extensionDescription); + } + return false; + } + + /** + * @see ILabelProvider#getImage(Object) + */ + public Image getImage(final Object element) { + // No specific image by default + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ILabelProvider#getText(java.lang.Object) + */ + public String getText(final Object element) { + + String text = null; + + if (element instanceof EObject) { + try { + text = String.valueOf(getModelAccessor().eGet((EObject) element, ExtendedPropertySource.getFeatureName(extensionDescription))); + } catch (final FeatureNotFoundException e) { + SiriusPlugin.getDefault().error("Error while getting the property value", e); + text = e.getMessage(); + } + } else if (element instanceof Collection) { + final StringBuffer sb = new StringBuffer("["); + boolean first = true; + final Iterator<?> iterCollection = ((Collection<?>) element).iterator(); + while (iterCollection.hasNext()) { + final Object next = iterCollection.next(); + if (first) { + first = false; + } else { + sb.append(", "); + } + if (next instanceof EObject) { + sb.append(EMFCoreUtil.getQualifiedName((EObject) next, true)); + } else if (next instanceof Collection) { + sb.append(this.getText(next)); + } else { + sb.append(String.valueOf(next)); + } + } + sb.append("]"); + text = sb.toString(); + } else if (element == null) { + text = ""; + } else if (element instanceof EEnumLiteral) { + text = ((EEnumLiteral) element).getLiteral(); + } else { + return String.valueOf(element); + } + return text; + } + } + + /** + * + * @param extensionDescription + * @return + */ + private static String getFeatureName(final ExtensionFeatureDescription extensionDescription) { + return extensionDescription.getName(); + + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ExtensionSemanticPropertiesSection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ExtensionSemanticPropertiesSection.java new file mode 100644 index 0000000000..c02bd89eab --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ExtensionSemanticPropertiesSection.java @@ -0,0 +1,182 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.edit.provider.IItemPropertySource; +import org.eclipse.emf.edit.ui.provider.PropertySource; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.diagram.ui.properties.sections.AdvancedPropertySection; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.IPropertySourceProvider; + +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; + +/** + * Property section mixing both Extension and Semantic attributes. + * + * @author ymortier + * + */ +public class ExtensionSemanticPropertiesSection extends AdvancedPropertySection implements IPropertySourceProvider { + + private List<Object> transformedSelection; + + private IWorkbenchPart part; + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.IPropertySourceProvider#getPropertySource(java.lang.Object) + */ + public IPropertySource getPropertySource(final Object object) { + + IPropertySource propertySrc = null; + final AdapterFactory adapterFactory = getAdapterFactory(object); + if (object instanceof IPropertySource) { + propertySrc = (IPropertySource) object; + } else if (object instanceof DSemanticDecorator) { + propertySrc = new ExtendedPropertySource((DSemanticDecorator) object, adapterFactory); + } else { + if (adapterFactory != null) { + final IItemPropertySource ips = (IItemPropertySource) adapterFactory.adapt(object, IItemPropertySource.class); + if (ips != null) { + propertySrc = new PropertySource(object, ips); + } + } + if (propertySrc == null && object instanceof IAdaptable) { + propertySrc = (IPropertySource) ((IAdaptable) object).getAdapter(IPropertySource.class); + } + + } + return propertySrc; + } + + /** + * Get the adapter factory. + * + * @param object + * the object + * @return the retrieved adapter factory + */ + protected AdapterFactory getAdapterFactory(final Object object) { + AdapterFactory adapterFactory = null; + if (object != null) { + if (part instanceof DDiagramEditor) { + adapterFactory = ((DDiagramEditor) part).getAdapterFactory(); + } else { + adapterFactory = SiriusDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory(); + } + } + return adapterFactory; + } + + @Override + protected IPropertySourceProvider getPropertySourceProvider() { + return this; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AdvancedPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public void setInput(final IWorkbenchPart workbenchPart, final ISelection selection) { + this.part = workbenchPart; + if (selection.isEmpty() || !(selection instanceof StructuredSelection)) { + super.setInput(workbenchPart, selection); + return; + } + final StructuredSelection structuredSelection = (StructuredSelection) selection; + transformedSelection = new ArrayList<Object>(structuredSelection.size()); + final Iterator<?> it = structuredSelection.iterator(); + while (it.hasNext()) { + final Object r = transformSelection(it.next()); + if (r != null) { + transformedSelection.add(r); + } + } + super.setInput(workbenchPart, new StructuredSelection(transformedSelection)); + } + + /** + * Transform the selection to get the designer element from the GMF one. + * + * @param selected + * the GMF element + * @return the Designer element + */ + protected Object transformSelection(final Object selected) { + + Object dElement = selected; + + if (selected instanceof EditPart) { + final Object model = ((EditPart) selected).getModel(); + if (model instanceof View) { + final EObject element = ((View) model).getElement(); + if (element instanceof DSemanticDecorator) { + dElement = element; + } + } + + } else if (selected instanceof View) { + if (((View) selected).getElement() instanceof DSemanticDecorator) { + dElement = ((View) selected).getElement(); + } + } else if (selected instanceof IAdaptable) { + final View view = (View) ((IAdaptable) selected).getAdapter(View.class); + if (view != null) { + if (view.getElement() instanceof DSemanticDecorator) { + dElement = view.getElement(); + } + } + } + return dElement; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.gmf.runtime.diagram.ui.properties.sections.AdvancedPropertySection#refresh() + */ + @Override + public void refresh() { + if (this.transformedSelection != null) { + final Iterator<?> inputs = this.transformedSelection.iterator(); + boolean isValid = true; + while (inputs.hasNext() && isValid) { + final Object input = inputs.next(); + if (input instanceof DSemanticDecorator && (((DSemanticDecorator) input).eResource() == null || ((DSemanticDecorator) input).eResource().getResourceSet() == null)) { + isValid = false; + } + } + if (!isValid) { + this.setInput(this.part, StructuredSelection.EMPTY); + } + } + super.refresh(); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/FiltersPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/FiltersPropertySection.java new file mode 100644 index 0000000000..d2ad921578 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/FiltersPropertySection.java @@ -0,0 +1,485 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.emf.common.notify.Adapter; +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.ComposeableAdapterFactory; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.emf.edit.provider.ItemProvider; +import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; +import org.eclipse.emf.edit.ui.provider.AdapterFactoryContentProvider; +import org.eclipse.emf.edit.ui.provider.AdapterFactoryLabelProvider; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.gef.EditPart; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Table; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.views.properties.tabbed.AbstractPropertySection; +import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.description.audit.provider.AuditItemProviderAdapterFactory; +import org.eclipse.sirius.description.filter.FilterDescription; +import org.eclipse.sirius.description.filter.provider.FilterItemProviderAdapterFactory; +import org.eclipse.sirius.description.provider.DescriptionItemProviderAdapterFactory; +import org.eclipse.sirius.description.tool.provider.ToolItemProviderAdapterFactory; +import org.eclipse.sirius.description.validation.provider.ValidationItemProviderAdapterFactory; +import org.eclipse.sirius.diagram.internal.edit.parts.DDiagramEditPart; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.ActivateFiltersCommand; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.DeactivateFiltersCommand; +import org.eclipse.sirius.provider.SiriusItemProviderAdapterFactory; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; +import org.eclipse.sirius.ecore.extender.business.api.permission.IPermissionAuthority; + +/** + * This Property section shows currently activated filters and helps in + * adding/removing new ones. + * + * + * + * @author cbrun + */ +@Deprecated +public class FiltersPropertySection extends AbstractPropertySection { + + /** The editing domain. */ + protected TransactionalEditingDomain domain; + + /** the choice table. */ + protected Table choiceTable; + + /** teh feature table. */ + protected Table featureTable; + + AdapterFactoryLabelProvider myAdapterFactoryLabelProvider; + + private ComposedAdapterFactory adapterFactory; + + private DDiagram diagram; + + private TableViewer availableElementsTableViewer; + + private TableViewer selectedElementsTableViewer; + + private Button addButton; + + private Button removeButton; + + private final IDoubleClickListener availableElementsTableDoubleClickListener = new IDoubleClickListener() { + public void doubleClick(final DoubleClickEvent event) { + if (addButton.isEnabled()) { + addButton.notifyListeners(SWT.Selection, null); + } + } + }; + + private final IDoubleClickListener selectedElementsTableDoubleClickListener = new IDoubleClickListener() { + public void doubleClick(final DoubleClickEvent event) { + if (removeButton.isEnabled()) { + removeButton.notifyListeners(SWT.Selection, null); + } + } + }; + + /** The edit part to refresh. */ + private EditPart editPart; + + private final Adapter viewpointListener = new AdapterImpl() { + + @Override + public void notifyChanged(final Notification msg) { + super.notifyChanged(msg); + viewpointChanged(); + } + + }; + + // TODOCBR comment this. + /** + * . + * + * @param newElements + * . + */ + protected void newElementsSelected(final Collection<?> newElements) { + domain.getCommandStack().execute(new ActivateFiltersCommand(domain, getDiagram(), Lists.newArrayList(Iterables.filter(newElements, FilterDescription.class)))); + } + + // TODOCBR comment this ! + /** + * . + * + * @param oldElements + * . + */ + protected void oldElementsRemoved(final Collection<?> oldElements) { + domain.getCommandStack().execute(new DeactivateFiltersCommand(domain, getDiagram(), Lists.newArrayList(Iterables.filter(oldElements, FilterDescription.class)))); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#aboutToBeShown() + */ + @Override + public void aboutToBeShown() { + availableElementsTableViewer.addDoubleClickListener(availableElementsTableDoubleClickListener); + selectedElementsTableViewer.addDoubleClickListener(selectedElementsTableDoubleClickListener); + + addButton.addSelectionListener(new SelectionListener() { + + public void widgetDefaultSelected(final SelectionEvent e) { + + } + + public void widgetSelected(final SelectionEvent e) { + final List<Object> newElements = new ArrayList<Object>(); + final IStructuredSelection selection = (IStructuredSelection) availableElementsTableViewer.getSelection(); + final Iterator<?> i = selection.iterator(); + while (i.hasNext()) { + final Object value = i.next(); + newElements.add(value); + } + /* + * affect the viewpoint model + */ + newElementsSelected(newElements); + } + + }); + removeButton.addSelectionListener(new SelectionListener() { + + public void widgetDefaultSelected(final SelectionEvent e) { + + } + + public void widgetSelected(final SelectionEvent e) { + final List<Object> oldElements = new ArrayList<Object>(); + final IStructuredSelection selection = (IStructuredSelection) selectedElementsTableViewer.getSelection(); + Object firstValue = null; + + final Iterator<?> i = selection.iterator(); + while (i.hasNext()) { + final Object value = i.next(); + if (firstValue == null) { + firstValue = value; + } + selectedElementsTableViewer.remove(value); + oldElements.add(value); + } + oldElementsRemoved(oldElements); + + } + + }); + } + + private void addSemanticListener(final DDiagram viewPoint) { + viewPoint.eAdapters().add(viewpointListener); + } + + /** + * Create the adpater factory. + * + * @return the created factory + */ + protected ComposedAdapterFactory createAdapterFactory() { + final List<ComposeableAdapterFactory> factories = new ArrayList<ComposeableAdapterFactory>(); + factories.add(new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE)); + fillItemProviderFactories(factories); + return new ComposedAdapterFactory(factories); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#createControls(org.eclipse.swt.widgets.Composite, + * org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage) + */ + @Override + public void createControls(final Composite parent, final TabbedPropertySheetPage aTabbedPropertySheetPage) { + super.createControls(parent, aTabbedPropertySheetPage); + + final Composite composite = getWidgetFactory().createFlatFormComposite(parent); + composite.setLayout(new GridLayout(3, false)); + + final Composite choiceComposite = createChoiceComposite(composite); + + choiceTable = getWidgetFactory().createTable(choiceComposite, SWT.MULTI | SWT.BORDER); + choiceTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + availableElementsTableViewer = new TableViewer(choiceTable); + + final Composite controlButtons = getWidgetFactory().createComposite(composite, SWT.NONE); + controlButtons.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + + final GridLayout controlsButtonGridLayout = new GridLayout(); + controlButtons.setLayout(controlsButtonGridLayout); + + new Label(controlButtons, SWT.NONE); + + addButton = getWidgetFactory().createButton(controlButtons, "Add >", SWT.PUSH); + addButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + + removeButton = getWidgetFactory().createButton(controlButtons, "< Remove", SWT.PUSH); + removeButton.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + + final Label spaceLabel = new Label(controlButtons, SWT.NONE); + final GridData spaceLabelGridData = new GridData(); + spaceLabelGridData.verticalSpan = 2; + spaceLabel.setLayoutData(spaceLabelGridData); + + final Composite featureComposite = createFeatureComposite(composite); + + featureTable = getWidgetFactory().createTable(featureComposite, SWT.MULTI | SWT.BORDER); + featureTable.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + + selectedElementsTableViewer = new TableViewer(featureTable); + + } + + private IPermissionAuthority getPermissionAuthority() { + final ModelAccessor accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(diagram); + return accessor.getPermissionAuthority(); + } + + /** + * Create the feature section. + * + * @param composite + * the parent container. + * @return the created composite + */ + protected Composite createFeatureComposite(final Composite composite) { + final Composite featureComposite = getWidgetFactory().createComposite(composite, SWT.NONE); + featureComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + featureComposite.setLayout(new GridLayout()); + + final Label featureLabel = getWidgetFactory().createLabel(featureComposite, "Applied filters", SWT.NONE); + featureLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + return featureComposite; + } + + /** + * Create the choice section. + * + * @param composite + * the parent container + * @return teh created composite + */ + protected Composite createChoiceComposite(final Composite composite) { + final Composite choiceComposite = getWidgetFactory().createComposite(composite, SWT.NONE); + choiceComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + choiceComposite.setLayout(new GridLayout()); + + final Label choiceLabel = getWidgetFactory().createLabel(choiceComposite, "Available filters", SWT.NONE); + choiceLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + return choiceComposite; + } + + /** + * fill the item provider factory list. + * + * @param factories + * the list to fill + */ + protected void fillItemProviderFactories(final List<ComposeableAdapterFactory> factories) { + factories.add(new SiriusItemProviderAdapterFactory()); + factories.add(new DescriptionItemProviderAdapterFactory()); + factories.add(new ToolItemProviderAdapterFactory()); + factories.add(new FilterItemProviderAdapterFactory()); + factories.add(new ValidationItemProviderAdapterFactory()); + factories.add(new AuditItemProviderAdapterFactory()); + factories.add(new EcoreItemProviderAdapterFactory()); + factories.add(new ResourceItemProviderAdapterFactory()); + factories.add(new ReflectiveItemProviderAdapterFactory()); + } + + /** + * Get all applied elements. + * + * @return the applied elements. + */ + protected Collection<?> getAppliedElements() { + final Collection<FilterDescription> result = new HashSet<FilterDescription>(); + if (diagram != null) { + result.addAll(diagram.getActivatedFilters()); + } + return result; + } + + /** + * Get all available elements. + * + * @return the available elements + */ + protected Collection<?> getAvailableElements() { + final Collection<FilterDescription> result = new HashSet<FilterDescription>(); + + if (diagram != null && diagram.getDescription() != null) { + final DiagramDescription desc = diagram.getDescription(); + result.addAll(desc.getFilters()); + } + result.removeAll(getAppliedElements()); + return result; + } + + protected DDiagram getDiagram() { + return diagram; + } + + protected EditPart getEditPart() { + return editPart; + } + + private ItemProvider getItemProvider(final Collection<?> choices) { + return new ItemProvider(getItemProvidersAdapterFactory(), choices); + } + + private AdapterFactory getItemProvidersAdapterFactory() { + if (adapterFactory == null) { + adapterFactory = createAdapterFactory(); + } + return adapterFactory; + } + + private AdapterFactoryLabelProvider getLabelProvider() { + if (myAdapterFactoryLabelProvider == null) { + myAdapterFactoryLabelProvider = new AdapterFactoryLabelProvider(getItemProvidersAdapterFactory()); + } + return myAdapterFactoryLabelProvider; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#refresh() + */ + @Override + public void refresh() { + Display.getDefault().syncExec(new Runnable() { + + public void run() { + /* + * Set content/label provider + */ + if (!availableElementsTableViewer.getTable().isDisposed()) { + availableElementsTableViewer.setLabelProvider(getLabelProvider()); + selectedElementsTableViewer.setLabelProvider(getLabelProvider()); + availableElementsTableViewer.setContentProvider(new AdapterFactoryContentProvider(getItemProvidersAdapterFactory())); + selectedElementsTableViewer.setContentProvider(new AdapterFactoryContentProvider(getItemProvidersAdapterFactory())); + + availableElementsTableViewer.setInput(getItemProvider(getAvailableElements())); + selectedElementsTableViewer.setInput(getItemProvider(getAppliedElements())); + } + } + + }); + } + + private void removeSemanticListener(final DDiagram dia) { + dia.eAdapters().remove(viewpointListener); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public void setInput(final IWorkbenchPart part, final ISelection selection) { + super.setInput(part, selection); + if (selection instanceof IStructuredSelection) { + final Object input = ((IStructuredSelection) selection).getFirstElement(); + if (input instanceof DDiagramEditPart) { + final EObject vp = ((DDiagramEditPart) input).resolveSemanticElement(); + editPart = ((DDiagramEditPart) input).getRoot(); + if (vp instanceof DDiagram) { + setSirius((DDiagram) vp); + this.domain = ((DDiagramEditPart) input).getEditingDomain(); + } + } + } + refresh(); + } + + private void setSirius(final DDiagram newDia) { + if (!getPermissionAuthority().canEditInstance(newDia)) { + featureTable.setEnabled(false); + addButton.setEnabled(false); + removeButton.setEnabled(false); + choiceTable.setEnabled(false); + } + if (diagram == newDia) { + return; + } + + if (diagram != null) { + removeSemanticListener(diagram); + } + diagram = newDia; + refresh(); + + if (diagram != null) { + addSemanticListener(diagram); + } + + } + + private void viewpointChanged() { + refresh(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.ui.views.properties.tabbed.AbstractPropertySection#dispose() + */ + @Override + public void dispose() { + super.dispose(); + if (getDiagram() != null) { + removeSemanticListener(getDiagram()); + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ResetStylePropertiesToDefaultValuesSelectionAdapter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ResetStylePropertiesToDefaultValuesSelectionAdapter.java new file mode 100644 index 0000000000..98fb19d400 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ResetStylePropertiesToDefaultValuesSelectionAdapter.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.diagram.ui.properties.sections.appearance.ColorsAndFontsPropertySection; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.Style; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.diagram.business.api.query.ViewQuery; +import org.eclipse.sirius.diagram.edit.api.part.AbstractDiagramContainerEditPart; +import org.eclipse.sirius.diagram.tools.internal.commands.ResetStylePropertiesToDefaultValuesCommand; + +/** + * A {@link SelectionAdapter} to reset some style properties to their default + * values. + * + * @author <a href="mailto:esteban.dugueperoux@obeo.fr">Esteban Dugueperoux</a> + */ +public class ResetStylePropertiesToDefaultValuesSelectionAdapter extends SelectionAdapter { + + private ColorsAndFontsPropertySection colorAndFontPropertySection; + + /** + * Default constructor. + * + * @param colorAndFontPropertySection + * the {@link ColorsAndFontsPropertySection} from which get + * selected styles for which reset customizations + */ + public ResetStylePropertiesToDefaultValuesSelectionAdapter(ColorsAndFontsPropertySection colorAndFontPropertySection) { + this.colorAndFontPropertySection = colorAndFontPropertySection; + } + + @Override + public void widgetSelected(SelectionEvent e) { + List<?> input = colorAndFontPropertySection.getInput(); + if (!input.isEmpty()) { + DDiagram dDiagram = (DDiagram) colorAndFontPropertySection.getSingleInput().getNotationView().getDiagram().getElement(); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(dDiagram); + Map<View, DDiagramElement> customizedViews = new LinkedHashMap<View, DDiagramElement>(); + Map<IGraphicalEditPart, Style> oldStyles = new HashMap<IGraphicalEditPart, Style>(); + for (Object obj : input) { + if (obj instanceof IGraphicalEditPart) { + IGraphicalEditPart graphicalEditPart = (IGraphicalEditPart) obj; + View notationView = graphicalEditPart.getNotationView(); + DDiagramElement dDiagramElement = null; + EObject semanticElement = graphicalEditPart.resolveSemanticElement(); + if (semanticElement instanceof DDiagramElement) { + dDiagramElement = (DDiagramElement) semanticElement; + if (new DDiagramElementQuery(dDiagramElement).isCustomized() || new ViewQuery(notationView).isCustomized()) { + customizedViews.put(notationView, dDiagramElement); + oldStyles.put(graphicalEditPart, dDiagramElement.getStyle()); + } + } else if (dDiagramElement == null && new ViewQuery(notationView).isCustomized()) { + customizedViews.put(notationView, dDiagramElement); + } + } + } + if (!customizedViews.isEmpty()) { + Command resetStylePropertiesToDefaultValuesCommand = new ResetStylePropertiesToDefaultValuesCommand(domain, dDiagram, customizedViews); + domain.getCommandStack().execute(resetStylePropertiesToDefaultValuesCommand); + Set<Entry<IGraphicalEditPart, Style>> entrySet = oldStyles.entrySet(); + for (Entry<IGraphicalEditPart, Style> entry : entrySet) { + if (entry.getKey() instanceof AbstractDiagramContainerEditPart) { + AbstractDiagramContainerEditPart graphicalEditPart = (AbstractDiagramContainerEditPart) entry.getKey(); + graphicalEditPart.reInitFigure(); + } + } + } + } + } + + /** + * Tells if the specified {@link View} is customized. + * + * @param view + * the specified {@link View} + * @return true if the specified {@link View} is customized, false else + */ + public static boolean isCustomizedView(View view) { + boolean isCustomized = false; + EObject element = view.getElement(); + if (element instanceof DDiagramElement) { + DDiagramElement dDiagramElement = (DDiagramElement) element; + DDiagramElementQuery dDiagramElementQuery = new DDiagramElementQuery(dDiagramElement); + if (dDiagramElementQuery.isCustomized()) { + isCustomized = true; + } + } + ViewQuery viewQuery = new ViewQuery(view); + if (!isCustomized && viewQuery.isCustomized()) { + isCustomized = true; + } + return isCustomized; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/SemanticAndExtensionPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/SemanticAndExtensionPropertySection.java new file mode 100644 index 0000000000..0af524e285 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/SemanticAndExtensionPropertySection.java @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.Iterator; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.IPropertySourceProvider; + +import org.eclipse.sirius.DDiagramElement; + +/** + * Displays the semantic and the extension properties. + * + * @author ymortier + */ +public class SemanticAndExtensionPropertySection extends SemanticPropertySection { + + /** The provider for the extension. */ + private IPropertySourceProvider extendedPropertySourceProvider; + + /** + * Creates a new <code>SemanticAndExtensionPropertySection</code>. + */ + public SemanticAndExtensionPropertySection() { + this.extendedPropertySourceProvider = new ExtensionSemanticPropertiesSection(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.SemanticPropertySection#getPropertySource(java.lang.Object) + */ + @Override + public IPropertySource getPropertySource(final Object object) { + final IPropertySource propertySource = super.getPropertySource(object); + if (propertySource instanceof CompositeEObjectPropertySource) { + final CompositeEObjectPropertySource compositePropertySource = (CompositeEObjectPropertySource) propertySource; + if (object instanceof DDiagramElement) { + final DDiagramElement viewPointElement = (DDiagramElement) object; + final Iterator<EObject> iterElementsToDestroy = viewPointElement.getSemanticElements().iterator(); + while (iterElementsToDestroy.hasNext()) { + final EObject semanticElement = iterElementsToDestroy.next(); + final IPropertySource extendedPropertySource = this.extendedPropertySourceProvider.getPropertySource(object); + compositePropertySource.addPropertySource(semanticElement, extendedPropertySource); + } + } + } + return propertySource; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/SemanticPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/SemanticPropertySection.java new file mode 100644 index 0000000000..3ce88d2e93 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/SemanticPropertySection.java @@ -0,0 +1,234 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.edit.provider.IItemPropertySource; +import org.eclipse.emf.edit.ui.provider.PropertySource; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.views.properties.IPropertySource; +import org.eclipse.ui.views.properties.IPropertySourceProvider; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.ui.tools.api.properties.AbstractPropertySection; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; + +/** + * Properties for the semantic model. + * + * @author ymortier + */ +public class SemanticPropertySection extends AbstractPropertySection implements IPropertySourceProvider { + + private List<Object> transformedSelection; + + private IWorkbenchPart part; + + /** + * Returns the property source of the specified objet. + * + * @param object + * the object. + * @return the property source of the specified objet. + */ + public IPropertySource getPropertySource(final Object object) { + + IPropertySource propSrc = null; + + if (object instanceof IPropertySource) { + propSrc = (IPropertySource) object; + } else if (object instanceof DDiagramElement) { + final DDiagramElement diagramElement = (DDiagramElement) object; + final Iterator<EObject> iterElementsToDestroy = diagramElement.getSemanticElements().iterator(); + final CompositeEObjectPropertySource propertySource = new CompositeEObjectPropertySource(); + final ModelAccessor accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(diagramElement); + while (iterElementsToDestroy.hasNext()) { + final EObject semanticElement = iterElementsToDestroy.next(); + if (!(accessor.isExtension(semanticElement))) { + final AdapterFactory af = getAdapterFactory(semanticElement); + if (af != null) { + final IItemPropertySource ips = (IItemPropertySource) af.adapt(semanticElement, IItemPropertySource.class); + if (ips != null) { + final IPropertySource targetPropertySource = new PropertySource(semanticElement, ips); + propertySource.addPropertySource(semanticElement, targetPropertySource); + } + } + } + } + propSrc = propertySource; + } else if (object instanceof DSemanticDecorator) { + final CompositeEObjectPropertySource propertySource = new CompositeEObjectPropertySource(); + final ModelAccessor accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor((EObject) object); + final EObject semanticElement = ((DSemanticDecorator) object).getTarget(); + if (!(accessor.isExtension(semanticElement))) { + final AdapterFactory af = getAdapterFactory(semanticElement); + if (af != null) { + final IItemPropertySource ips = (IItemPropertySource) af.adapt(semanticElement, IItemPropertySource.class); + if (ips != null) { + final IPropertySource targetPropertySource = new PropertySource(semanticElement, ips); + propertySource.addPropertySource(semanticElement, targetPropertySource); + } + } + } + propSrc = propertySource; + } else { + final AdapterFactory af = getAdapterFactory(object); + if (af != null) { + final IItemPropertySource ips = (IItemPropertySource) af.adapt(object, IItemPropertySource.class); + if (ips != null) { + return new PropertySource(object, ips); + } + } + if (object instanceof IAdaptable) { + propSrc = (IPropertySource) ((IAdaptable) object).getAdapter(IPropertySource.class); + } + } + return propSrc; + + } + + /** + * Returns the provider. + * + * @return the provider. + */ + @Override + protected IPropertySourceProvider getPropertySourceProvider() { + return this; + } + + /** + * Transform selection to have {@link DSemanticDecorator} instead of + * {@link EditPart} or null if the semantic element (target) not exists. + * + * @param selection + * the currently selected object + * @return the unwrapped object + */ + protected Object transformSelection(final Object selection) { + + Object object = selection; + + if (object instanceof EditPart) { + object = ((EditPart) object).getModel(); + } else if (object instanceof IAdaptable) { + object = ((IAdaptable) object).getAdapter(View.class); + } + + if (object instanceof View) { + object = ((View) object).getElement(); + } + + if (object instanceof DSemanticDecorator) { + EObject target = ((DSemanticDecorator) object).getTarget(); + if (target == null || target.eResource() == null) { + object = null; + } + } + return object; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.AbstractPropertySection#setInput(org.eclipse.ui.IWorkbenchPart, + * org.eclipse.jface.viewers.ISelection) + */ + @Override + public void setInput(final IWorkbenchPart workbenchPart, final ISelection selection) { + this.part = workbenchPart; + if (selection.isEmpty() || !(selection instanceof StructuredSelection)) { + super.setInput(workbenchPart, selection); + return; + } + final StructuredSelection structuredSelection = (StructuredSelection) selection; + transformedSelection = new ArrayList<Object>(structuredSelection.size()); + final Iterator<?> it = structuredSelection.iterator(); + while (it.hasNext()) { + final Object r = transformSelection(it.next()); + if (r != null) { + transformedSelection.add(r); + } + } + super.setInput(workbenchPart, new StructuredSelection(transformedSelection)); + } + + /** + * Get the adapter factory. + * + * @param object + * the object + * @return the retrieved adapter factory + */ + protected AdapterFactory getAdapterFactory(final Object object) { + AdapterFactory adapterFactory = null; + if (object != null) { + if (part instanceof DDiagramEditor) { + adapterFactory = ((DDiagramEditor) part).getAdapterFactory(); + } else { + adapterFactory = SiriusDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory(); + } + } + return adapterFactory; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.AbstractPropertySection#refresh() + */ + @Override + public void refresh() { + if (this.transformedSelection != null) { + final Iterator<?> inputs = this.transformedSelection.iterator(); + boolean isValid = true; + while (inputs.hasNext() && isValid) { + final Object input = inputs.next(); + if (input instanceof DSemanticDecorator && (((DSemanticDecorator) input).eResource() == null || ((DSemanticDecorator) input).eResource().getResourceSet() == null)) { + isValid = false; + } + } + if (!isValid) { + this.setInput(this.part, StructuredSelection.EMPTY); + } + } + super.refresh(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.AbstractPropertySection#getSelectedObject() + */ + @Override + public Object getSelectedObject() { + Object result = null; + if (!transformedSelection.isEmpty()) { + result = transformedSelection.get(0); + } + return result; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/StylePropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/StylePropertySection.java new file mode 100644 index 0000000000..d1d53b7b0b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/StylePropertySection.java @@ -0,0 +1,285 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.EStructuralFeature; +import org.eclipse.emf.edit.provider.IItemPropertySource; +import org.eclipse.emf.edit.ui.provider.PropertySource; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.notation.ConnectorStyle; +import org.eclipse.gmf.runtime.notation.FontStyle; +import org.eclipse.gmf.runtime.notation.Routing; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.ui.views.properties.IPropertySource; + +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.DStylizable; +import org.eclipse.sirius.EdgeRouting; +import org.eclipse.sirius.EdgeStyle; +import org.eclipse.sirius.FontFormat; +import org.eclipse.sirius.LabelStyle; +import org.eclipse.sirius.RGBValues; +import org.eclipse.sirius.Style; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; +import org.eclipse.sirius.ecore.extender.business.api.permission.IPermissionAuthority; + +/** + * Properties section that shows the properties of a style of a DiagramElement. + * + * @author ymortier + */ +public class StylePropertySection extends SemanticPropertySection { + + private Map<DStylizable, View> map = new HashMap<DStylizable, View>(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.SemanticPropertySection#getPropertySource(java.lang.Object) + */ + @Override + public IPropertySource getPropertySource(final Object object) { + + IPropertySource propSrc = null; + + if (object instanceof IPropertySource) { + propSrc = (IPropertySource) object; + } + // + // We should display the ColorMapping if the style is an EdgeStyle and + // if the stroke color is not null. + else if (object instanceof EObject && !getPermissionAuthority((EObject) object).canEditInstance((EObject) object)) { + // do nothing -> return null + } else if (object instanceof DStylizable && ((DStylizable) object).getStyle() instanceof EdgeStyle && ((EdgeStyle) ((DStylizable) object).getStyle()).getStrokeColor() != null) { + final DStylizable stylizable = (DStylizable) object; + final View view = map.get(stylizable); + + final EdgeStyle edgeStyle = (EdgeStyle) stylizable.getStyle(); + final RGBValues rgb = edgeStyle.getStrokeColor(); + final CompositeEObjectPropertySource propertySource = new CompositeEObjectPropertySource(); + AdapterFactory af = getAdapterFactory(edgeStyle); + if (af != null) { + final IItemPropertySource ips = (IItemPropertySource) af.adapt(edgeStyle, IItemPropertySource.class); + if (ips != null) { + final IPropertySource targetPropertySource = new StylePropertySource(edgeStyle, view, ips); + propertySource.addPropertySource(edgeStyle, targetPropertySource); + } + } + af = getAdapterFactory(rgb); + if (af != null) { + final IItemPropertySource ips = (IItemPropertySource) af.adapt(rgb, IItemPropertySource.class); + if (ips != null) { + final IPropertySource targetPropertySource = new StylePropertySource(rgb, view, ips); + propertySource.addPropertySource(rgb, targetPropertySource); + } + } + propSrc = propertySource; + } else { + // + // Other styles. + propSrc = getPropertySourceForOtherStyles(object); + } + return propSrc; + + } + + private IPropertySource getPropertySourceForOtherStyles(final Object object) { + + IPropertySource propSrc = null; + + if (object instanceof DStylizable) { + final DStylizable stylizable = (DStylizable) object; + final View view = map.get(stylizable); + final Style style = stylizable.getStyle(); + if (style != null) { + final AdapterFactory af = getAdapterFactory(style); + if (af != null) { + final IItemPropertySource ips = (IItemPropertySource) af.adapt(style, IItemPropertySource.class); + if (ips != null) { + final IPropertySource targetPropertySource = new StylePropertySource(style, view, ips); + propSrc = targetPropertySource; + } + } + } + } + if (propSrc == null) { + final AdapterFactory af = getAdapterFactory(object); + if (af != null) { + final IItemPropertySource ips = (IItemPropertySource) af.adapt(object, IItemPropertySource.class); + if (ips != null) { + propSrc = new StylePropertySource(object, ips); + } + } + if (propSrc == null && object instanceof IAdaptable) { + propSrc = (IPropertySource) ((IAdaptable) object).getAdapter(IPropertySource.class); + } + } + + return propSrc; + } + + private IPermissionAuthority getPermissionAuthority(final EObject instance) { + final ModelAccessor accessor = SiriusPlugin.getDefault().getModelAccessorRegistry().getModelAccessor(instance); + return accessor.getPermissionAuthority(); + } + + /** + * Transform selection to have {@link DSemanticDecorator} instead of + * {@link EditPart} or null if the semantic element (target) not exists. + * + * @param selection + * the currently selected object + * @return the unwrapped object + */ + @Override + protected Object transformSelection(final Object selection) { + + Object object = selection; + View view = null; + + if (object instanceof EditPart) { + object = ((EditPart) object).getModel(); + } else if (object instanceof IAdaptable) { + object = ((IAdaptable) object).getAdapter(View.class); + } + + if (object instanceof View) { + view = (View) object; + object = view.getElement(); + } + + if (view != null && object instanceof DStylizable) { + map.put((DStylizable) object, view); + } + + if (object instanceof DSemanticDecorator) { + EObject target = ((DSemanticDecorator) object).getTarget(); + if (target == null || target.eResource() == null) { + object = null; + } + } + return object; + } + + /** + * {@inheritDoc} + * + * This is used to automatically set the "custom" attribute of a Style if + * there is any manual change in the PropertySection. + * + * @author mPorhel + * + */ + private static class StylePropertySource extends PropertySource { + + private View view; + + /** + * An instance is constructed from an object and its item property + * source. + */ + public StylePropertySource(final Object object, final IItemPropertySource itemPropertySource) { + super(object, itemPropertySource); + this.view = null; + } + + /** + * An instance is constructed from an object, its edit part and its item + * property source. + */ + public StylePropertySource(final Object object, final View view, final IItemPropertySource itemPropertySource) { + super(object, itemPropertySource); + this.view = view; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.emf.edit.ui.provider.PropertySource#setPropertyValue(java.lang.Object, + * java.lang.Object) + */ + @Override + public void setPropertyValue(final Object propertyId, final Object value) { + if (this.object instanceof Style && propertyId instanceof String) { + Style style = (Style) object; + String featureName = (String) propertyId; + EStructuralFeature feature = style.eClass().getEStructuralFeature(featureName); + if (feature != null && !style.getCustomFeatures().contains(feature.getName())) { + style.getCustomFeatures().add(feature.getName()); + } + } else if (object instanceof EObject && ((EObject) object).eContainer() instanceof Style && propertyId instanceof String) { + EObject containedValue = (EObject) object; + Style style = (Style) containedValue.eContainer(); + EStructuralFeature feature = containedValue.eContainingFeature(); + if (feature != null && !style.getCustomFeatures().contains(feature.getName())) { + style.getCustomFeatures().add(feature.getName()); + } + } + super.setPropertyValue(propertyId, value); + + if (this.object instanceof LabelStyle) { + updateNotationView(propertyId, value); + } else if (this.object instanceof EdgeStyle) { + updateNotationView(propertyId, value); + } else if (this.object instanceof RGBValues) { + updateNotationView(propertyId, value); + } + } + + private void updateNotationView(final Object propertyId, final Object value) { + if (view != null && view.getStyles() != null) { + for (Object notationStyle : view.getStyles()) { + if (notationStyle instanceof FontStyle) { + final FontStyle fontStyle = (FontStyle) notationStyle; + if (value instanceof FontFormat && propertyId.equals(SiriusPackage.Literals.BASIC_LABEL_STYLE__LABEL_FORMAT.getName())) { + if (FontFormat.BOLD_LITERAL == value) { + fontStyle.setBold(true); + fontStyle.setItalic(false); + } else if (FontFormat.ITALIC_LITERAL == value) { + fontStyle.setBold(false); + fontStyle.setItalic(true); + } else if (FontFormat.BOLD_ITALIC_LITERAL == value) { + fontStyle.setBold(true); + fontStyle.setItalic(true); + } else if (FontFormat.NORMAL_LITERAL == value) { + fontStyle.setBold(false); + fontStyle.setItalic(false); + } + } else if (value instanceof Integer && propertyId.equals(SiriusPackage.Literals.BASIC_LABEL_STYLE__LABEL_SIZE.getName())) { + fontStyle.setFontHeight((Integer) value); + } + } else if (notationStyle instanceof ConnectorStyle) { + ConnectorStyle connectorStyle = (ConnectorStyle) notationStyle; + if (value instanceof EdgeRouting && propertyId.equals(SiriusPackage.Literals.EDGE_STYLE__ROUTING_STYLE.getName())) { + if (EdgeRouting.MANHATTAN_LITERAL == value) { + connectorStyle.setRouting(Routing.RECTILINEAR_LITERAL); + } else if (EdgeRouting.STRAIGHT_LITERAL == value) { + connectorStyle.setRouting(Routing.MANUAL_LITERAL); + } else if (EdgeRouting.TREE_LITERAL == value) { + connectorStyle.setRouting(Routing.TREE_LITERAL); + } + } + } + } + } + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ValidationPropertySection.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ValidationPropertySection.java new file mode 100644 index 0000000000..19667b9bc0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/ValidationPropertySection.java @@ -0,0 +1,121 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties; + +import java.util.Collection; +import java.util.HashSet; + +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Label; + +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.description.validation.ValidationRule; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.ActivateRulesCommand; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.DeactivateRulesCommand; + +/** + * This Property section shows currently activated validation rules and helps in + * adding/removing new ones. + * + * + * + * @author cbrun + * + */ +public class ValidationPropertySection extends FiltersPropertySection { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#getAppliedElements() + */ + @Override + protected Collection<?> getAppliedElements() { + final Collection<ValidationRule> result = new HashSet<ValidationRule>(); + if (getDiagram() != null) { + result.addAll(getDiagram().getActivatedRules()); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#getAvailableElements() + */ + @Override + protected Collection<?> getAvailableElements() { + final Collection<ValidationRule> result = new HashSet<ValidationRule>(); + if (getDiagram() != null && getDiagram().getDescription() != null && getDiagram().getDescription().getValidationSet() != null) { + result.addAll(getDiagram().getDescription().getValidationSet().getAllRules()); + } + result.removeAll(getAppliedElements()); + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#createFeatureComposite(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Composite createFeatureComposite(final Composite composite) { + final Composite featureComposite = getWidgetFactory().createComposite(composite, SWT.NONE); + featureComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + featureComposite.setLayout(new GridLayout()); + + final Label featureLabel = getWidgetFactory().createLabel(featureComposite, "Activated rules", SWT.NONE); + featureLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, false, false)); + return featureComposite; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#createChoiceComposite(org.eclipse.swt.widgets.Composite) + */ + @Override + protected Composite createChoiceComposite(final Composite composite) { + final Composite choiceComposite = getWidgetFactory().createComposite(composite, SWT.NONE); + choiceComposite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true)); + choiceComposite.setLayout(new GridLayout()); + + final Label choiceLabel = getWidgetFactory().createLabel(choiceComposite, "Available rules", SWT.NONE); + choiceLabel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + return choiceComposite; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#newElementsSelected(java.util.Collection) + */ + @Override + protected void newElementsSelected(final Collection<?> newElements) { + domain.getCommandStack().execute(new ActivateRulesCommand(domain, getDiagram(), Lists.newArrayList(Iterables.filter(newElements, ValidationRule.class)))); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.internal.properties.FiltersPropertySection#oldElementsRemoved(java.util.Collection) + */ + @Override + protected void oldElementsRemoved(final Collection<?> oldElements) { + domain.getCommandStack().execute(new DeactivateRulesCommand(domain, getDiagram(), Lists.newArrayList(Iterables.filter(oldElements, ValidationRule.class)))); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/ExtendedPropertyFilter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/ExtendedPropertyFilter.java new file mode 100644 index 0000000000..80774b7879 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/ExtendedPropertyFilter.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties.filter; + +import java.util.Iterator; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessorsRegistry; + +/** + * Filters the extension property section. + * + * @author ymortier + */ +public class ExtendedPropertyFilter extends AbstractPropertyFilter { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter#select(java.lang.Object) + */ + @Override + public boolean select(final Object toTest) { + final boolean select = super.select(toTest); + boolean areAnnotation = false; + ModelAccessorsRegistry modelAccessorRegistry = SiriusPlugin.getDefault().getModelAccessorRegistry(); + final Iterator<EObject> iterSemantics = this.semanticElements.iterator(); + while (iterSemantics.hasNext() && !areAnnotation) { + final EObject next = iterSemantics.next(); + ModelAccessor modelAccessor = modelAccessorRegistry.getModelAccessor(next); + if (modelAccessor.hasExtension(next)) { + areAnnotation = true; + } else if (modelAccessor.isExtension(next)) { + areAnnotation = true; + } + + } + return select && areAnnotation; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/SemanticExtensionPropertyFilter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/SemanticExtensionPropertyFilter.java new file mode 100644 index 0000000000..b2c2fa6fe0 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/SemanticExtensionPropertyFilter.java @@ -0,0 +1,37 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties.filter; + +import org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter; + +/** + * Filters the Extension property section. Shows the section only if the + * semantic element has extended properties. + * + * @author ymortier + */ +public class SemanticExtensionPropertyFilter extends AbstractPropertyFilter { + + private SemanticPropertyFilter semanticPropertyFilter = new SemanticPropertyFilter(); + + private ExtendedPropertyFilter extendedPropertyFilter = new ExtendedPropertyFilter(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter#select(java.lang.Object) + */ + @Override + public boolean select(final Object toTest) { + return super.select(toTest) && semanticPropertyFilter.select(toTest) && extendedPropertyFilter.select(toTest); + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/SemanticPropertyFilter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/SemanticPropertyFilter.java new file mode 100644 index 0000000000..2963af0345 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/SemanticPropertyFilter.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties.filter; + +import java.util.Iterator; + +import org.eclipse.emf.ecore.EObject; + +import org.eclipse.sirius.SiriusPlugin; +import org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessor; +import org.eclipse.sirius.ecore.extender.business.api.accessor.ModelAccessorsRegistry; + +/** + * Filters for semantic elements. + * + * @author ymortier + */ +public class SemanticPropertyFilter extends AbstractPropertyFilter { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter#select(java.lang.Object) + */ + @Override + public boolean select(final Object toTest) { + final boolean select = super.select(toTest); + boolean areAnnotation = true; + ModelAccessorsRegistry modelAccessorRegistry = SiriusPlugin.getDefault().getModelAccessorRegistry(); + final Iterator<EObject> iterSemantics = this.semanticElements.iterator(); + while (iterSemantics.hasNext() && areAnnotation) { + final EObject next = iterSemantics.next(); + ModelAccessor modelAccessor = modelAccessorRegistry.getModelAccessor(next); + if (!modelAccessor.isExtension(next)) { + areAnnotation = false; + } + } + return select && !areAnnotation; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/StylePropertyFilter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/StylePropertyFilter.java new file mode 100644 index 0000000000..48a507bf11 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/properties/filter/StylePropertyFilter.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2007, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.properties.filter; + +import org.eclipse.sirius.DStylizable; +import org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter; + +/** + * Filter for the style section. + * + * @author ymortier + */ +public class StylePropertyFilter extends AbstractPropertyFilter { + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.ui.tools.api.properties.filter.AbstractPropertyFilter#select(java.lang.Object) + */ + @Override + public boolean select(final Object toTest) { + return super.select(toTest) && this.viewPointElement instanceof DStylizable; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/BracketConnectionRouter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/BracketConnectionRouter.java new file mode 100644 index 0000000000..fd3df8be50 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/BracketConnectionRouter.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2011, 2012 Obeo. All Rights Reserved. + * 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: + * Obeo - initial API and implementation + ****************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.routers; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.draw2d.AbstractRouter; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.geometry.PointList; + +import org.eclipse.sirius.diagram.business.internal.bracket.BracketConnectionQuery; + +/** + * Routes Connections directly from the source anchor to the target anchor with + * some bendpoints inside. Adapts bendpoints location from orientation and + * offset. + * + * @author <a href="mailto:esteban.dugueperoux@obeo.fr">Esteban Dugueperoux</a> + */ +public class BracketConnectionRouter extends AbstractRouter { + + /** The figure constraints for each {@link Connection}. */ + private Map<Connection, Object> constraints = new HashMap<Connection, Object>(); + + /** A common {@link BracketConnectionQuery} to the source and target anchor. */ + private BracketConnectionQuery bracketConnectionQuery; + + /** + * Constructs a new DimensionConnectionRouter. + * + * @param bracketConnectionQuery + * {@link BracketConnectionQuery} + */ + public BracketConnectionRouter(BracketConnectionQuery bracketConnectionQuery) { + this.bracketConnectionQuery = bracketConnectionQuery; + } + + /** + * Sets the constraint for the given {@link Connection}. + * + * @param connection + * The connection whose constraint we are setting + * @param constraint + * The constraint + */ + public void setConstraint(Connection connection, Object constraint) { + constraints.put(connection, constraint); + } + + /** + * Gets the constraint for the given {@link Connection}. + * + * @param connection + * The connection whose constraint we are retrieving + * @return The constraint + */ + public Object getConstraint(Connection connection) { + return constraints.get(connection); + } + + /** + * Removes the given connection from the map of constraints. + * + * @param connection + * The connection to remove + */ + public void remove(Connection connection) { + constraints.remove(connection); + } + + /** + * Routes the given Connection directly between the source and target + * anchors. + * + * @param connection + * the {@link Connection} to be routed + */ + public void route(Connection connection) { + final PointList newPointList = bracketConnectionQuery.getPointListFromConstraint(); + connection.setPoints(newPointList); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DBranchRouter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DBranchRouter.java new file mode 100644 index 0000000000..b661321a55 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DBranchRouter.java @@ -0,0 +1,204 @@ +/****************************************************************************** + * Copyright (c) 2004, 2010 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Obeo - some corrections + ****************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.routers; + +import org.eclipse.draw2d.AbstractRouter; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg; +import org.eclipse.gmf.runtime.draw2d.ui.internal.routers.OrthogonalRouterUtilities; + +/** + * Specific AIR Tree Router to correct some anomalies from the GMF one. + * + * (original author is sshaw) + * + * @author ymortier + */ +public class DBranchRouter extends AbstractRouter { + + private final DTreeRouter tree; + + /** + * Creates a new branch router. + * + * @param tree + * the tree router. + */ + public DBranchRouter(final DTreeRouter tree) { + super(); + this.tree = tree; + } + + /** + * bla. + * + * @see org.eclipse.draw2d.ConnectionRouter#route(org.eclipse.draw2d.Connection) + * + * case 1: connection has never been routed before and needs points to + * be populated. points.size() < 4 + * + * case 2: user moved the trunk vertex of the connection by either + * moving the line attached to the target or second last target line + * + * case 3: user moved the source or target shape causing a layout of + * the connection. + * + * case 4: user moved the source line attached to the source shape. + * + * case 5: connection is being rerouted as a result of an invalidation + * from case 2. + * + * @param conn + * the connection. + * + */ + public void route(final Connection conn) { + internalRoute(conn); + } + + /** + * @param conn + */ + private void internalRoute(final Connection conn) { + final Point ptTrunkLoc = getTrunkLocation(conn); + + getTree().setTrunkLocation(conn, ptTrunkLoc); + + final Point ptSourceLoc = getSourceLocation(conn, ptTrunkLoc); + + final PointList points = recreateBranch(conn, ptSourceLoc, ptTrunkLoc); + conn.setPoints(points); + } + + /** + * getTrunkLocation Method to retrieve the trunk location in relative + * coordinates. + * + * @param conn + * Connection being routed + * @return Point that is the trunk location in relative coordinates. + */ + protected Point getTrunkLocation(final Connection conn) { + + final PointList points = getTree().getPointsFromConstraint(conn); + final Point ptTrunkLoc = getTree().getTrunkLocation(conn); // default; + + // check valid again based on constraint + if (getTree().isTreeBranch(conn, points)) { + if (getTree().isTopDown(conn)) { + ptTrunkLoc.x = points.getPoint(3).x; + } else { + ptTrunkLoc.y = points.getPoint(3).y; + } + + if (getTree().isOrthogonalTreeBranch(conn, points)) { + if (getTree().isTopDown(conn)) { + ptTrunkLoc.y = points.getPoint(2).y; + } else { + ptTrunkLoc.x = points.getPoint(2).x; + } + } + } + + return ptTrunkLoc; + } + + /** + * getSourceLocation Method to retrieve the source location where the + * connection is connected to the source element. + * + * @param conn + * Connection to be routed. + * @param ptTrunkLoc + * Point trunk location in relative coordinates + * @return Point source location in relative coordinates + */ + public Point getSourceLocation(final Connection conn, final Point ptTrunkLoc) { + + /* source reference is in absolute */ + final Point ptSourceRef = conn.getSourceAnchor().getReferencePoint(); + + /* get trunk location in absolute */ + Point absoluteTrunkLocation = ptTrunkLoc.getCopy(); + conn.translateToAbsolute(absoluteTrunkLocation); + + final boolean bTopDown = getTree().isTopDown(conn); + + /* branch offset is in absolute */ + final int branchOffset = bTopDown ? ptSourceRef.x : ptSourceRef.y; + + Point ref; + + if (bTopDown) { + ref = new Point(branchOffset, absoluteTrunkLocation.y); + } else { + ref = new Point(absoluteTrunkLocation.x, branchOffset); + } + + /* + * the reference point should be in relative for + * getOrthogonalLineSegToAnchorLoc method + */ + conn.translateToRelative(ref); + + final LineSeg line = OrthogonalRouterUtilities.getOrthogonalLineSegToAnchorLoc(conn, conn.getSourceAnchor(), ref); + return line.getOrigin(); + } + + /** + * recreateBranch Utility method used to recreate the points list for the + * branch connection given a trunk vertex location and a source attachpoint + * location. + * + * @param conn + * Connection used to do translate points to relative + * coordinates. + * @param ptSourceLoc + * Point that is attached to the source node + * @param ptTrunkLoc + * Point that is the vertex between the line attached to the + * target and the "shoulder" line that holds the individual + * source branches. + * @return PointList that represents the full connection tree branch. + */ + public PointList recreateBranch(final Connection conn, final Point ptSourceLoc, final Point ptTrunkLoc) { + final PointList points = new PointList(4); + final boolean bTopDown = getTree().isTopDown(conn); + + points.addPoint(new Point(ptSourceLoc)); + + final Point pt2 = bTopDown ? new Point(ptSourceLoc.x, ptTrunkLoc.y) : new Point(ptTrunkLoc.x, ptSourceLoc.y); + points.addPoint(pt2); + + points.addPoint(new Point(ptTrunkLoc)); + + final LineSeg line = OrthogonalRouterUtilities.getOrthogonalLineSegToAnchorLoc(conn, conn.getTargetAnchor(), ptTrunkLoc); + final Point ptTargetLoc = line.getOrigin(); + + final Point pt4 = bTopDown ? new Point(ptTrunkLoc.x, ptTargetLoc.y) : new Point(ptTargetLoc.x, ptTrunkLoc.y); + points.addPoint(pt4); + + return points; + } + + /** + * getTree Getter method for the container tree router. + * + * @return Returns the tree. + */ + protected DTreeRouter getTree() { + return tree; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DForestRouter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DForestRouter.java new file mode 100644 index 0000000000..255f94c125 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DForestRouter.java @@ -0,0 +1,336 @@ +/****************************************************************************** + * Copyright (c) 2004 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Obeo - adaptation + * Maxime Porhel (Obeo) <maxime.porhel@obeo.fr> - Trac bug #1524 : Layout issue after modification made in breakdown diagram. + ****************************************************************************/ + +package org.eclipse.sirius.diagram.ui.tools.internal.routers; + +import java.util.HashMap; + +import org.eclipse.draw2d.BendpointConnectionRouter; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.ConnectionAnchor; +import org.eclipse.draw2d.ConnectionRouter; +import org.eclipse.draw2d.FreeformViewport; +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.PrecisionPoint; +import org.eclipse.gmf.runtime.draw2d.ui.internal.routers.ITreeConnection; +import org.eclipse.gmf.runtime.draw2d.ui.internal.routers.OrthogonalRouter; + +/** + * A router for Sirius . + * + * @author ymortier + */ +public class DForestRouter extends BendpointConnectionRouter implements OrthogonalRouter { + + private final HashMap connections = new HashMap(); + + private final HashMap trunkVertexes = new HashMap(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.BendpointConnectionRouter#remove(org.eclipse.draw2d.Connection) + */ + @Override + public void remove(final Connection conn) { + if (conn.getSourceAnchor() == null || conn.getTargetAnchor() == null) { + return; + } + + final ConnectionRouter connectionRouter = getSubRouter(conn); + if (connectionRouter != null) { + connectionRouter.remove(conn); + } + + super.remove(conn); + } + + /** + * Utility method to retrieve the sub router that manages the individual + * trees. + * + * @param conn + * <code>Connection</code> to be routered + * @return <code>TreeRouter</code> that will end up routing the given + * <code>Connection</code>. + */ + public DTreeRouter getSubRouter(final Connection conn) { + if (conn.getTargetAnchor() == null) { + return null; + } + + String hint = "base"; //$NON-NLS-1$ + if (conn instanceof ITreeConnection) { + hint = ((ITreeConnection) conn).getHint(); + } + + final AnchorKey connectionKey = new AnchorKey(conn.getTargetAnchor(), hint); + DTreeRouter connectionRouter = (DTreeRouter) connections.get(connectionKey); + if (connectionRouter == null) { + DTreeRouter oldConnectionRouter = null; + if (!"base".equals(hint)) { + // Search only with hint (if the targetAnchor has been moved but + // the target is the same) + oldConnectionRouter = getSubRouterWithHint(hint); + } + if (connectionRouter == null) { + connectionRouter = new DTreeRouter(); + if (oldConnectionRouter != null) { + // Reuse the trunk location of the previous router to avoid + // to reset with the default trunk location + try { + connectionRouter.setTrunkLocation(conn, oldConnectionRouter.getTrunkLocation(conn)); + } catch (NullPointerException e) { + // We are probably in reconnection, so the + // oldConnectionRouter has not already the connection in + // its list. + connectionRouter.setTrunkOrientation(oldConnectionRouter.getTrunkOrientation()); + connectionRouter.setTrunkVertex(oldConnectionRouter.getTrunkVertex()); + } + } + // remove connection of other routers after reconnectiontool + for (Object obj : connections.values()) { + if (obj instanceof DTreeRouter) { + ((DTreeRouter) obj).remove(conn); + } + } + } + connections.put(connectionKey, connectionRouter); + } + + return connectionRouter; + } + + /** + * Search the router only on the hint of the ITreeConnection (usefull for + * example if the targetAnchor has been moved on the same target node and we + * want to use the same router). + * + * @param hint + * The hint about the connection which determines which tree this + * connection will be contributed to + * @return <code>TreeRouter</code> that will end up routing the given + * <code>Connection</code>. + */ + private DTreeRouter getSubRouterWithHint(String hint) { + for (Object obj : connections.keySet()) { + if (obj instanceof AnchorKey) { + if (hint.equals(((AnchorKey) obj).getQualifier())) { + return (DTreeRouter) connections.get(obj); + } + } + } + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.BendpointConnectionRouter#route(org.eclipse.draw2d.Connection) + */ + @Override + public void route(final Connection conn) { + internalRoute(conn); + } + + /** + * @param conn + */ + private void internalRoute(final Connection conn) { + if (conn != null) { + if (conn.getTargetAnchor().getOwner() == null || conn.getSourceAnchor().getOwner() == null) { + final PointList points = conn.getPoints(); + points.removeAllPoints(); + + final Point delta = getFreeformViewport(conn).getViewLocation(); + + final Point ref1 = conn.getTargetAnchor().getReferencePoint().getCopy(); + // conn.getTargetAnchor().getOwner().translateToAbsolute(ref1); + final Point ref2 = conn.getSourceAnchor().getReferencePoint().getCopy(); + // conn.getSourceAnchor().getOwner().translateToAbsolute(ref2); + final PrecisionPoint precisePt = new PrecisionPoint(); + + precisePt.setLocation(conn.getSourceAnchor().getLocation(ref1).getTranslated(delta)); + points.addPoint(precisePt); + + precisePt.setLocation(conn.getTargetAnchor().getLocation(ref2).getTranslated(delta)); + points.addPoint(precisePt); + conn.setPoints(points); + return; + } + + final DTreeRouter treeRouter = getSubRouter(conn); + + if (treeRouter != null) { + + // remove existing trunk vertex before routing occurs. + Dimension trunk = treeRouter.getTrunkVertex(); + if (trunk != null) { + final AnchorKey trunkKey = new AnchorKey(conn.getTargetAnchor(), trunk); + trunkVertexes.remove(trunkKey); + } + + treeRouter.route(conn); + + trunk = treeRouter.getTrunkVertex(); + final Dimension adjustedTrunk = accountForTrunkOverlap(trunk, conn); + if (!adjustedTrunk.equals(trunk)) { + treeRouter.setTrunkVertex(adjustedTrunk); + treeRouter.invalidate(conn); + } + + } + } + } + + /** + * Returns the {@link FreeformViewport} that owned this figure. + * + * @return the {@link FreeformViewport} that owned this figure. + */ + private FreeformViewport getFreeformViewport(final IFigure figure) { + IFigure current = figure; + while (!(current instanceof FreeformViewport) && current != null) { + current = current.getParent(); + } + return (FreeformViewport) current; + } + + /** + * This method is copy/paste from + * org.eclipse.gmf.runtime.draw2d.ui.internal.routers.ForestRouter + * + * Makes sure the routed tree doesn't intersect with an existing tree in the + * "forest". This is called recursively for each trunk. + * + * @param trunk + * <code>Dimension</code> trunkVertex value to compare + * @param conn + * <code>Connection</code> that is connection currently being + * routed + * @return <code>Dimension</code> new trunk vertex value + */ + private Dimension accountForTrunkOverlap(final Dimension trunk, final Connection conn) { + Dimension result = trunk; + if (conn.getTargetAnchor() != null && conn.getTargetAnchor().getOwner() == null) { + + final AnchorKey trunkKey = new AnchorKey(conn.getTargetAnchor(), trunk); + + // check if trunk vertex doesn't exist or if it exceeds a maximum + // then + // return. + int ownerExt = conn.getTargetAnchor().getOwner().getBounds().width / 2; + int trunkExt = trunk.width; + + if (conn instanceof ITreeConnection) { + if (((ITreeConnection) conn).getOrientation() == ITreeConnection.Orientation.VERTICAL) { + ownerExt = conn.getTargetAnchor().getOwner().getBounds().height / 2; + trunkExt = trunk.height; + } + } + + if (trunkVertexes.get(trunkKey) == null || Math.abs(trunkExt) > ownerExt) { + trunkVertexes.put(trunkKey, Boolean.TRUE); + result = trunk; + } else { + final Dimension newTrunk = new Dimension(trunk); + if (((ITreeConnection) conn).getOrientation() == ITreeConnection.Orientation.HORIZONTAL) { + newTrunk.expand(10, 0); + } else { + newTrunk.expand(0, 10); + } + result = accountForTrunkOverlap(newTrunk, conn); + } + } + return result; + } + + /** + * This class is copy/paste from + * org.eclipse.gmf.runtime.draw2d.ui.internal.routers.ForestRouter + * + * @author sshaw + */ + private static class AnchorKey { + + private final ConnectionAnchor anchor; + + private final Object qualifier; + + AnchorKey(final ConnectionAnchor anchor, final Object qualifier) { + this.anchor = anchor; + this.qualifier = qualifier; + } + + @Override + public boolean equals(final Object object) { + boolean isEqual = false; + AnchorKey hashKey; + + if (object instanceof AnchorKey) { + hashKey = (AnchorKey) object; + final ConnectionAnchor hkA1 = hashKey.getAnchor(); + final Object hkA2 = hashKey.getQualifier(); + + isEqual = hkA1.equals(anchor) && hkA2.equals(qualifier); + } + return isEqual; + } + + /** + * Accessor to retrieve the <code>ConnectionAnchor</code> that is stored + * as part of the key. + * + * @return the <code>ConnectionAnchor</code> that is used for the key. + */ + public ConnectionAnchor getAnchor() { + return anchor; + } + + /** + * Accessor to retrieve the qualifier object that is stored as part of + * the key. + * + * @return the <code>Object</code> that is designated the qualifier. + */ + public Object getQualifier() { + return qualifier; + } + + /** + * {@inheritDoc} + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return anchor.hashCode() ^ qualifier.hashCode(); + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.AbstractRouter#invalidate(org.eclipse.draw2d.Connection) + */ + @Override + public void invalidate(final Connection conn) { + if (conn != null && conn.getSourceAnchor() != null && conn.getTargetAnchor() != null) { + super.invalidate(conn); + } + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DTreeRouter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DTreeRouter.java new file mode 100644 index 0000000000..c256e33296 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/DTreeRouter.java @@ -0,0 +1,563 @@ +/****************************************************************************** + * Copyright (c) 2004 IBM Corporation 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: + * IBM Corporation - initial API and implementation + * Obeo - adaptation + * Maxime Porhel (Obeo) <maxime.porhel@obeo.fr> - Trac bug #1501 : Issues with tree routing style. + ****************************************************************************/ + +package org.eclipse.sirius.diagram.ui.tools.internal.routers; + +import java.util.ArrayList; +import java.util.List; +import java.util.ListIterator; + +import org.eclipse.draw2d.AbsoluteBendpoint; +import org.eclipse.draw2d.Bendpoint; +import org.eclipse.draw2d.BendpointConnectionRouter; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.geometry.Dimension; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gmf.runtime.draw2d.ui.geometry.LineSeg; +import org.eclipse.gmf.runtime.draw2d.ui.internal.routers.ITreeConnection; +import org.eclipse.gmf.runtime.draw2d.ui.internal.routers.OrthogonalRouter; +import org.eclipse.gmf.runtime.draw2d.ui.mapmode.MapModeUtil; + +import com.google.common.collect.Lists; + +/** + * The tree router of a forest router. + */ +public class DTreeRouter extends BendpointConnectionRouter implements OrthogonalRouter { + + private static final int DEFAULT_TRUNK_HEIGHT = 16; + + /** The branch router. */ + private final DBranchRouter branchRouter = new DBranchRouter(this); + + /** All connections. */ + private final ArrayList connectionList = new ArrayList(); + + private Dimension trunkVertex; + + private Orientation trunkOrientation; + + private boolean updatingPeers; + + /** + * Describes the orientation of the tree. + */ + static final class Orientation { + + /** + * Constant for the top orientation + */ + public static final Orientation TOP = new Orientation(); + + /** + * Constant for the bottom orientation + */ + public static final Orientation BOTTOM = new Orientation(); + + /** + * Constant for the right orientation + */ + public static final Orientation RIGHT = new Orientation(); + + /** + * Constant for the left orientation + */ + public static final Orientation LEFT = new Orientation(); + + private Orientation() { + // Empty constructor + } + + /** + * getEdge Method to return the edge point of the given Rectangle + * representative of the orientation value of the instance. + * + * @param bounds + * Rectangle to retrieve the edge value from. + * @return Point that is the edge of the rectangle for the orientation + * of this. + */ + public Point getEdge(final Rectangle bounds) { + Point result = bounds.getLeft(); + if (this == TOP) { + result = bounds.getTop(); + } else if (this == BOTTOM) { + result = bounds.getBottom(); + } else if (this == RIGHT) { + result = bounds.getRight(); + } + return result; + } + + } + + /** + * Create a new DTreeRouter. + */ + public DTreeRouter() { + super(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.AbstractRouter#invalidate(org.eclipse.draw2d.Connection) + */ + @Override + public void invalidate(final Connection conn) { + if (conn.getSourceAnchor() == null || conn.getSourceAnchor().getOwner() == null || conn.getTargetAnchor() == null || conn.getTargetAnchor().getOwner() == null) { + return; + } + + final ListIterator li = Lists.newArrayList(connectionList).listIterator(); + while (li.hasNext()) { + final Connection connNext = (Connection) li.next(); + + if (!trunkVertexEqual(connNext, conn)) { + updateConstraint(connNext); + } + } + } + + private boolean trunkVertexEqual(final Connection connMaster, final Connection connSlave) { + final PointList cmPts = connMaster.getPoints(); + final PointList csPts = connSlave.getPoints(); + if (cmPts.size() > 2 && csPts.size() > 2) { + return cmPts.getPoint(2).equals(csPts.getPoint(2)); + } + + return false; + } + + private Rectangle getTargetAnchorRelativeBounds(final Connection conn) { + final Rectangle bounds = conn.getTargetAnchor().getOwner().getBounds().getCopy(); + conn.getTargetAnchor().getOwner().translateToAbsolute(bounds); + conn.translateToRelative(bounds); + return bounds; + } + + private Rectangle getSourceAnchorRelativeBounds(final Connection conn) { + final Rectangle bounds = conn.getSourceAnchor().getOwner().getBounds().getCopy(); + conn.getSourceAnchor().getOwner().translateToAbsolute(bounds); + conn.translateToRelative(bounds); + return bounds; + } + + /** + * getTrunkLocation Method to retrieve the trunk location in relative + * coordinates based on current tree state. + * + * @param conn + * Connection being routed + * @return Point that is the trunk location in relative coordinates. + */ + public Point getTrunkLocation(final Connection conn) { + final Dimension vertex = getTrunkVertex(); + final Point target = getTrunkOrientation().getEdge(getTargetAnchorRelativeBounds(conn)); + Point ptTrunkLoc = new Point(vertex.width, vertex.height); + ptTrunkLoc = ptTrunkLoc.getTranslated(target); + return ptTrunkLoc; + } + + /** + * setTrunkLocation Setter method to set the trunk location. Translates the + * point into a relative point from the target edge. + * + * @param conn + * Connection being routed + * @param ptTrunkLoc + * Point that is the trunk location in relative coordinates. + */ + public void setTrunkLocation(final Connection conn, final Point ptTrunkLoc) { + final Point ptRelTrunkLoc = new Point(ptTrunkLoc); + + final Rectangle targetAnchorBounds = getTargetAnchorRelativeBounds(conn); + + // update orientation + if (isTopDown(conn)) { + if (ptTrunkLoc.y < targetAnchorBounds.getCenter().y) { + setTrunkOrientation(Orientation.TOP); + } else { + setTrunkOrientation(Orientation.BOTTOM); + } + } else { + if (ptTrunkLoc.x < targetAnchorBounds.getCenter().x) { + setTrunkOrientation(Orientation.LEFT); + } else { + setTrunkOrientation(Orientation.RIGHT); + } + } + + final Point target = getTrunkOrientation().getEdge(targetAnchorBounds); + + final Dimension currentVertex = ptRelTrunkLoc.getDifference(target); + setTrunkVertex(currentVertex); + } + + /** + * UpdateConstraint Updates the constraint value for the connection based on + * the tree vertex. + * + * @param conn + * Connection whose constraint is to be updated. + */ + protected void updateConstraint(final Connection conn) { + boolean update = conn != null && conn.getSourceAnchor() != null && conn.getTargetAnchor() != null; + update = update && conn.getSourceAnchor().getOwner() != null && conn.getTargetAnchor().getOwner() != null; + if (update) { + if (isUpdatingPeers()) { + return; + } + + List bendpoints = (List) conn.getRoutingConstraint(); + if (bendpoints == null) { + bendpoints = new ArrayList(conn.getPoints().size()); + } + + final Point sourceRefPoint = conn.getSourceAnchor().getReferencePoint(); + conn.translateToRelative(sourceRefPoint); + + final Point targetRefPoint = conn.getTargetAnchor().getReferencePoint(); + conn.translateToRelative(targetRefPoint); + + final Point ptTrunk = getTrunkLocation(conn); + final Point ptSource = getBranchRouter().getSourceLocation(conn, ptTrunk); + + bendpoints.clear(); + final PointList pts = getBranchRouter().recreateBranch(conn, ptSource, ptTrunk); + for (int i = 0; i < pts.size(); i++) { + final Bendpoint bp = new AbsoluteBendpoint(pts.getPoint(i)); + bendpoints.add(bp); + } + + setUpdatingPeers(true); + + try { + setConstraint(conn, bendpoints); + conn.invalidate(); + conn.validate(); + } finally { + setUpdatingPeers(false); + } + } + } + + /** + * getPointsFromConstraint Utility method retrieve the PointList equivalent + * of the bendpoint constraint set in the Connection. + * + * @param conn + * Connection to retrieve the constraint from. + * @return PointList list of points that is the direct equivalent of the set + * constraint. + */ + public PointList getPointsFromConstraint(final Connection conn) { + final List bendpoints = (List) conn.getRoutingConstraint(); + if (bendpoints == null) { + return new PointList(); + } + + final PointList points = new PointList(bendpoints.size()); + for (int i = 0; i < bendpoints.size(); i++) { + final Bendpoint bp = (Bendpoint) bendpoints.get(i); + points.addPoint(bp.getLocation()); + } + + DTreeRouter.straightenPoints(points, MapModeUtil.getMapMode(conn).DPtoLP(3)); + return points; + } + + /** + * straightenPoints This is a simpler version of the. + * + * @see updateIfNotRectilinear that simply ensures that the lines are + * horizontal or vertical without any intelligence in terms of shortest + * distance around a rectangle. + * + * @param newLine + * PointList to check for rectilinear qualities and change if + * necessary. + * @param tolerance + * int tolerance value by which points will be straightened in + * HiMetrics + */ + protected static void straightenPoints(final PointList newLine, final int tolerance) { + for (int i = 0; i < newLine.size() - 1; i++) { + final Point ptCurrent = newLine.getPoint(i); + final Point ptNext = newLine.getPoint(i + 1); + + final int xDelta = Math.abs(ptNext.x - ptCurrent.x); + final int yDelta = Math.abs(ptNext.y - ptCurrent.y); + + if (xDelta < yDelta) { + if (xDelta > tolerance) { + return; + } + if (i == newLine.size() - 2) { + // The last point is more important than the other (not + // change the end of the line) + ptCurrent.x = ptNext.x; + } else { + ptNext.x = ptCurrent.x; + } + } else { + if (yDelta > tolerance) { + return; + } + if (i == newLine.size() - 2) { + // The last point is more important than the other (not + // change the end of the line) + ptCurrent.y = ptNext.y; + } else { + ptNext.y = ptCurrent.y; + } + } + + newLine.setPoint(ptNext, i + 1); + } + } + + /** + * Returns the branch router in the chain. + * + * @return The getBranchRouter router + * + */ + protected DBranchRouter getBranchRouter() { + return branchRouter; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.BendpointConnectionRouter#remove(org.eclipse.draw2d.Connection) + */ + @Override + public void remove(final Connection conn) { + if (conn.getSourceAnchor() == null || conn.getTargetAnchor() == null) { + return; + } + + final int index = connectionList.indexOf(conn); + connectionList.remove(conn); + for (int i = index + 1; i < connectionList.size(); i++) { + ((Connection) connectionList.get(i)).revalidate(); + } + + getBranchRouter().remove(conn); + } + + /** + * isTopDown Utility method to determine if the connection should routed in + * a top-down fashion or in a horizontal fashion. + * + * @param conn + * Connection to query + * @return boolean true if connection should be routed top-down, false + * otherwise. + */ + public boolean isTopDown(final Connection conn) { + if (conn instanceof ITreeConnection) { + return ((ITreeConnection) conn).getOrientation().equals(ITreeConnection.Orientation.VERTICAL); + } + return true; + } + + /** + * checkTrunkVertex Method to initialize the trunk vertex to a default value + * if not already set + * + * @param conn + * Connection to be routed. + */ + private void checkTrunkVertex(final Connection conn) { + if (getTrunkVertex() == null) { + final Rectangle sourceRect = getSourceAnchorRelativeBounds(conn); + final Rectangle targetRect = getTargetAnchorRelativeBounds(conn); + + final Dimension defaultTrunk = new Dimension(0, DEFAULT_TRUNK_HEIGHT); + conn.translateToRelative(defaultTrunk); + + if (isTopDown(conn)) { + if (sourceRect.getCenter().y < targetRect.getCenter().y) { + setTrunkVertex(new Dimension(0, -defaultTrunk.height)); + setTrunkOrientation(Orientation.TOP); + } else { + setTrunkVertex(new Dimension(0, defaultTrunk.height)); + setTrunkOrientation(Orientation.BOTTOM); + } + } else { + if (sourceRect.getCenter().x < targetRect.getCenter().x) { + setTrunkVertex(new Dimension(-defaultTrunk.height, 0)); + setTrunkOrientation(Orientation.LEFT); + } else { + setTrunkVertex(new Dimension(defaultTrunk.height, 0)); + setTrunkOrientation(Orientation.RIGHT); + } + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.BendpointConnectionRouter#route(org.eclipse.draw2d.Connection) + */ + @Override + public void route(final Connection conn) { + internalRoute(conn); + } + + /** + * @param conn + */ + private void internalRoute(final Connection conn) { + if (conn.getSourceAnchor() == null || conn.getSourceAnchor().getOwner() == null || conn.getTargetAnchor() == null || conn.getTargetAnchor().getOwner() == null) { + super.route(conn); + return; + } + + if (!connectionList.contains(conn)) { + connectionList.add(conn); + } + + checkTrunkVertex(conn); + + getBranchRouter().route(conn); + invalidate(conn); + } + + /** + * Returns the trunk vertex. + * + * @return Returns the truckVertex. + */ + protected Dimension getTrunkVertex() { + return trunkVertex; + } + + /** + * Sets the trunk vertex. + * + * @param trunkVertex + * The trunkVertex to set. + */ + protected void setTrunkVertex(final Dimension trunkVertex) { + this.trunkVertex = trunkVertex; + } + + /** + * Returns the trunk orientation. + * + * @return Returns the trunkOrientation. + */ + protected Orientation getTrunkOrientation() { + return trunkOrientation; + } + + /** + * Sets the trunk orientation. + * + * @param trunkOrientation + * The trunkOrientation to set. + */ + protected void setTrunkOrientation(final Orientation trunkOrientation) { + this.trunkOrientation = trunkOrientation; + } + + /** + * Utility method to determine if the given set of points conforms to the + * constraints of being an orthogonal connection tree-branch. 1. Points size + * must be 4. 2. Source point resides with-in boundary of source shape based + * on orientation 3. Target point resides with-in boundary of target shape + * based on orientation 4. Middle line is perpendicular to the 2 end lines. + * + * @param conn + * the <code>Connection</code> to test + * @param points + * <code>PointList</code> to test constraints against + * @return <code>boolean</code> <code>true</code> if points represent valid + * orthogaonl tree branch, <code>false</code> otherwise. + */ + public boolean isOrthogonalTreeBranch(final Connection conn, final PointList points) { + boolean result = false; + if (isTreeBranch(conn, points)) { + final LineSeg branch = new LineSeg(points.getPoint(0), points.getPoint(1)); + final LineSeg trunkShoulder = new LineSeg(points.getPoint(1), points.getPoint(2)); + final LineSeg trunk = new LineSeg(points.getPoint(2), points.getPoint(3)); + + if (isTopDown(conn)) { + result = branch.isVertical() && trunkShoulder.isHorizontal() && trunk.isVertical(); + } else { + result = branch.isHorizontal() && trunkShoulder.isVertical() && trunk.isHorizontal(); + } + } + + return result; + } + + /** + * Utility method to determine if the given set of points conforms to the + * constraints of being a connection tree-branch. 1. Points size must be 4. + * 2. Source point resides with-in boundary of source shape based on + * orientation 3. Target point resides with-in boundary of target shape + * based on orientation + * + * @param conn + * the <code>Connection</code> to test + * @param points + * the <code>PointList</code> to test constraints against + * @return <code>boolean</code> <code>true</code> if points represent valid + * tree branch, <code>false</code> otherwise. + */ + public boolean isTreeBranch(final Connection conn, final PointList points) { + boolean result = false; + if (points.size() == 4) { + // just check if ends are with-in the owner bounding box + final Rectangle targetBounds = getTargetAnchorRelativeBounds(conn); + final Rectangle sourceBounds = getSourceAnchorRelativeBounds(conn); + + if (isTopDown(conn)) { + result = (points.getPoint(0).x > sourceBounds.x && points.getPoint(0).x < sourceBounds.x + sourceBounds.width) + && (points.getPoint(3).x > targetBounds.x && points.getPoint(3).x < targetBounds.x + targetBounds.width); + } else { + result = (points.getPoint(0).y > sourceBounds.y && points.getPoint(0).y < sourceBounds.y + sourceBounds.height) + && (points.getPoint(3).y > targetBounds.y && points.getPoint(3).y < targetBounds.y + targetBounds.height); + } + } + + return result; + } + + /** + * Returns the updating peers. + * + * @return Returns the updatingPeers. + */ + protected boolean isUpdatingPeers() { + return updatingPeers; + } + + /** + * Set the updating peers. + * + * @param updatingPeers + * The updatingPeers to set. + */ + protected void setUpdatingPeers(final boolean updatingPeers) { + this.updatingPeers = updatingPeers; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/SiriusBendpointConnectionRouter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/SiriusBendpointConnectionRouter.java new file mode 100644 index 0000000000..eeb9f39d0b --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/routers/SiriusBendpointConnectionRouter.java @@ -0,0 +1,88 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.routers; + +import java.util.Collections; +import java.util.List; + +import org.eclipse.draw2d.Bendpoint; +import org.eclipse.draw2d.BendpointConnectionRouter; +import org.eclipse.draw2d.Connection; +import org.eclipse.draw2d.geometry.Point; +import org.eclipse.draw2d.geometry.PointList; +import org.eclipse.draw2d.geometry.PrecisionPoint; + +/** + * This class override the default BendpointConnectionRouter to avoid the + * duplication of the start and the end point of the figure. <BR> + * It seems that the default BendpointConnectionRouter waiting a constraint + * without the start and the end point. But the GMF + * ConnectionEditPart.refreshBendpoint set a constraint with the start and the + * end point. + * + * @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a> + * + */ +public class SiriusBendpointConnectionRouter extends BendpointConnectionRouter { + private static final PrecisionPoint A_POINT = new PrecisionPoint(); + + /** + * {@inheritDoc} + * + * @see org.eclipse.draw2d.BendpointConnectionRouter#route(org.eclipse.draw2d.Connection) + */ + @Override + public void route(Connection conn) { + PointList points = conn.getPoints(); + points.removeAllPoints(); + + List bendpoints = (List) getConstraint(conn); + if (bendpoints == null) { + bendpoints = Collections.EMPTY_LIST; + } + + Point ref1; + Point ref2; + + if (bendpoints.isEmpty()) { + ref1 = conn.getTargetAnchor().getReferencePoint(); + ref2 = conn.getSourceAnchor().getReferencePoint(); + } else if (bendpoints.size() >= 3) { + // Take the point just after the start point + ref1 = new Point(((Bendpoint) bendpoints.get(1)).getLocation()); + conn.translateToAbsolute(ref1); + // Take the point just before the end point + ref2 = new Point(((Bendpoint) bendpoints.get(bendpoints.size() - 2)).getLocation()); + conn.translateToAbsolute(ref2); + } else { + ref1 = new Point(((Bendpoint) bendpoints.get(0)).getLocation()); + conn.translateToAbsolute(ref1); + ref2 = new Point(((Bendpoint) bendpoints.get(bendpoints.size() - 1)).getLocation()); + conn.translateToAbsolute(ref2); + } + + A_POINT.setLocation(conn.getSourceAnchor().getLocation(ref1)); + conn.translateToRelative(A_POINT); + points.addPoint(A_POINT); + + if (bendpoints.size() > 2) { + for (int i = 1; i < bendpoints.size() - 1; i++) { + Bendpoint bp = (Bendpoint) bendpoints.get(i); + points.addPoint(bp.getLocation()); + } + } + + A_POINT.setLocation(conn.getTargetAnchor().getLocation(ref2)); + conn.translateToRelative(A_POINT); + points.addPoint(A_POINT); + conn.setPoints(points); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/util/EditPartQuery.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/util/EditPartQuery.java new file mode 100644 index 0000000000..e225ab57ac --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/util/EditPartQuery.java @@ -0,0 +1,236 @@ +/******************************************************************************* + * Copyright (c) 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.util; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.eclipse.draw2d.IFigure; +import org.eclipse.draw2d.PositionConstants; +import org.eclipse.draw2d.geometry.Rectangle; +import org.eclipse.gef.EditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderItemEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IBorderedShapeEditPart; +import org.eclipse.gmf.runtime.diagram.ui.editparts.IGraphicalEditPart; +import org.eclipse.gmf.runtime.gef.ui.figures.NodeFigure; +import org.eclipse.gmf.runtime.notation.Bounds; +import org.eclipse.gmf.runtime.notation.Node; + +import com.google.common.base.Preconditions; +import com.google.common.base.Predicate; +import com.google.common.base.Predicates; +import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; + +import org.eclipse.sirius.diagram.tools.api.graphical.edit.styles.IBorderItemOffsets; + +/** + * Queries on GMF edit parts. + * <p> + * The class is named <code>EditPartQuery</code> instead of the longer but more + * accurate <code>IGraphicalEditPartQuery</code> only to avoid an overly long + * and cumbersome name. + * + * @author pcdavid + */ +public class EditPartQuery { + private final IGraphicalEditPart part; + + /** + * Constructor. + * + * @param part + * the graphical edit part to query. + */ + public EditPartQuery(IGraphicalEditPart part) { + this.part = Preconditions.checkNotNull(part); + } + + /** + * Returns the first ancestor (in the hierarchy of edit parts) of this part + * which is of the specified type. + * + * @param <T> + * the type. + * @param type + * the type of the ancestor to look for. + * @return the closest ancestor of the specified part which is compatible + * with <code>type</code>, or <code>null</code> if there is none. + */ + public <T> T getFirstAncestorOfType(Class<T> type) { + if (part == null) { + return null; + } + EditPart current = part.getParent(); + while (current != null && !type.isInstance(current)) { + current = current.getParent(); + } + return type.cast(current); + } + + /** + * Returns all ancestors (in the hierarchy of edit parts) of this part which + * are of the specified type. + * + * @param <T> + * the type. + * @param type + * the type of the ancestor to look for. + * @return all the ancestors of this part which are compatible with + * <code>type</code> from the closest to the farthest. + */ + public <T> List<T> getAllAncestorsOfType(Class<T> type) { + if (part == null) { + return null; + } + ArrayList<T> result = Lists.newArrayList(); + EditPart current = part.getParent(); + while (current != null) { + if (type.isInstance(current)) { + result.add(type.cast(current)); + } + current = current.getParent(); + } + return result; + } + + /** + * Return the list of Node corresponding to the BorderItemEditPart that is + * on the expected side. + * + * @param expectedSide + * The side ({@link org.eclipse.draw2d.PositionConstants}) where + * the children must be + * @return the list of Node corresponding to the BorderItemEditPart that is + * on the expected side. + */ + public List<Node> getBorderedNodes(final int expectedSide) { + List<Node> result = new ArrayList<Node>(); + if (part instanceof IBorderedShapeEditPart) { + Iterable<IBorderItemEditPart> bordersItemPart = (Iterable<IBorderItemEditPart>) Iterables.filter(part.getChildren(), + Predicates.and(Predicates.instanceOf(IBorderItemEditPart.class), new Predicate<IBorderItemEditPart>() { + public boolean apply(IBorderItemEditPart input) { + int currentSide = input.getBorderItemLocator().getCurrentSideOfParent(); + return expectedSide == currentSide; + } + })); + for (IBorderItemEditPart borderItemEditPart : bordersItemPart) { + result.add((Node) borderItemEditPart.getModel()); + } + } + return result; + } + + /** + * Return a Map with a move delta for each nodes that must be moved + * following the resize of the parent. + * + * @param expectedSide + * the side on which the border item appears as defined in + * {@link PositionConstants}. Possible values are + * <ul> + * <li>{@link org.eclipse.draw2d.PositionConstants#EAST}, if + * parent is resized from North or South</li> + * <li>{@link org.eclipse.draw2d.PositionConstants#WEST}, if + * parent is resized from North or South</li> + * <li>{@link org.eclipse.draw2d.PositionConstants#NORTH}, if + * parent is resized from East or West</li> + * <li>{@link org.eclipse.draw2d.PositionConstants#SOUTH}, if + * parent is resized from East or West</li> + * </ul> + * @param parentResizeSize + * the resize size of the parent + * @return A Map with a move delta for each nodes that must be moved + */ + public Map<Node, Integer> getBorderedNodesToMoveWithDelta(int expectedSide, int parentResizeSize) { + Map<Node, Integer> result = new HashMap<Node, Integer>(); + if (part instanceof IBorderedShapeEditPart) { + // calculate the parent bounds with consideration for the handle + // bounds inset. + IFigure parentFigure = ((IBorderedShapeEditPart) part).getMainFigure(); + Rectangle parentBounds = parentFigure.getBounds(); + if (parentFigure instanceof NodeFigure) { + parentBounds = ((NodeFigure) parentFigure).getHandleBounds().getCopy(); + } + + List<Node> expectedSideNodes = getBorderedNodes(expectedSide); + if (parentResizeSize < 0) { + // The container is reduced + if (expectedSide == PositionConstants.EAST || expectedSide == PositionConstants.WEST) { + // If the parent is reduced from the north (or from the + // south and the new height is too small, the bordered node + // must be moved to be near the bottom of parent. + result = getBorderedNodesToMoveVerticallyWithDelta(expectedSideNodes, parentBounds, parentResizeSize); + } else if (expectedSide == PositionConstants.NORTH || expectedSide == PositionConstants.SOUTH) { + // If the parent is reduced from the east (or from the west + // and the new width is too small, the bordered node must be + // moved to be near the right of parent. + result = getBorderedNodesToMoveHorizontallyWithDelta(expectedSideNodes, parentBounds, parentResizeSize); + } + } + } + return result; + } + + /** + * @param nodes + * Nodes potentially concerned by this resize. + * @param parentBounds + * The available bounds to consider for the parent + * @param parentResizeSize + * the resize size of the parent + */ + private Map<Node, Integer> getBorderedNodesToMoveVerticallyWithDelta(List<Node> nodes, Rectangle parentBounds, int parentResizeSize) { + Map<Node, Integer> result = new HashMap<Node, Integer>(); + for (Node node : nodes) { + if (node.getLayoutConstraint() instanceof Bounds) { + Bounds borderedNodeBounds = (Bounds) node.getLayoutConstraint(); + // Compute the distance between the bottom of the + // container and the bottom of the bordered node (we + // add the default_offset because it is used for + // our DBorderItemLocator). + int distanceFromBottom = parentBounds.height - IBorderItemOffsets.DEFAULT_OFFSET.height - (borderedNodeBounds.getY() + borderedNodeBounds.getHeight()); + if (distanceFromBottom < -parentResizeSize) { + result.put(node, distanceFromBottom + parentResizeSize); + } + } + } + return result; + } + + /** + * @param nodes + * Nodes potentially concerned by this resize. + * @param parentBounds + * The available bounds to consider for the parent + * @param parentResizeSize + * the resize size of the parent + */ + private Map<Node, Integer> getBorderedNodesToMoveHorizontallyWithDelta(List<Node> nodes, Rectangle parentBounds, int parentResizeSize) { + Map<Node, Integer> result = new HashMap<Node, Integer>(); + for (Node node : nodes) { + if (node.getLayoutConstraint() instanceof Bounds) { + Bounds borderedNodeBounds = (Bounds) node.getLayoutConstraint(); + // Compute the distance between the right of the + // container and the right of the bordered node (we + // add the default_offset because it is used for + // our DBorderItemLocator). + int distanceFromRight = parentBounds.width - IBorderItemOffsets.DEFAULT_OFFSET.height - (borderedNodeBounds.getX() + borderedNodeBounds.getWidth()); + if (distanceFromRight < -parentResizeSize) { + result.put(node, distanceFromRight + parentResizeSize); + } + } + } + return result; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/outlineview/DiagramOutlineWithBookPages.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/outlineview/DiagramOutlineWithBookPages.java new file mode 100644 index 0000000000..8db5d6eab2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/outlineview/DiagramOutlineWithBookPages.java @@ -0,0 +1,204 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.outlineview; + +import org.eclipse.gef.GraphicalViewer; +import org.eclipse.jface.action.Action; +import org.eclipse.jface.action.IAction; +import org.eclipse.jface.action.IToolBarManager; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.part.PageBook; + +import org.eclipse.sirius.common.ui.tools.api.util.IObjectActionDelegateWrapper; +import org.eclipse.sirius.common.ui.tools.api.util.SWTUtil; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePage; +import org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePageListener; +import org.eclipse.sirius.diagram.ui.tools.internal.views.providers.filters.FiltersTableViewer; +import org.eclipse.sirius.diagram.ui.tools.internal.views.providers.layers.LayersTableViewer; +import org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline.OutlineComparator; +import org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline.OutlineContentProvider; +import org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline.OutlineLabelProvider; + +/** + * A diagram outline page which has layer page, to hide or show layers, and a + * filter page to hide or show layers. + * + * @author mchauvin + */ +public class DiagramOutlineWithBookPages extends DiagramOutlinePage { + + /** The id of the layers. 3 */ + protected static final int ID_LAYERS = 3; + + /** The id of the layers. 4 */ + protected static final int ID_FILTERS = 4; + + /** the layers tooltip text. */ + private static final String LAYER_TIP_TEXT = "Layers"; + + /** the layers tooltip text. */ + private static final String FILTER_TIP_TEXT = "Filters"; + + /** The layers icon descriptor. */ + private static final ImageDescriptor DESC_LAYER = SiriusDiagramEditorPlugin.getBundledImageDescriptor("icons/layers.gif"); + + /** The filters icon descriptor. */ + private static final ImageDescriptor DESC_FILTER = SiriusDiagramEditorPlugin.getBundledImageDescriptor("icons/filters.gif"); + + /** The show layers action */ + private IAction showLayersAction; + + /** The show layers action */ + private IAction showFiltersAction; + + /** The layers SWT control */ + private Control layers; + + /** The filters SWT control */ + private Control filters; + + /** + * Constructor. + * + * @param input + * the input for the outline tree viewer + * @param viewer + * the graphical viewer the graphical viewer reference + * @param menuContributions + * the popup menu contribution for the outline tree viewer + */ + public DiagramOutlineWithBookPages(final Object input, final GraphicalViewer viewer, final IObjectActionDelegateWrapper[] menuContributions) { + super(input, new OutlineLabelProvider(), new OutlineContentProvider(), new OutlineComparator(), viewer, menuContributions); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePage#createControls(org.eclipse.ui.part.PageBook) + */ + @Override + protected void createControls(final PageBook pb) { + super.createControls(pb); + layers = createLayersControl(pb); + filters = createFiltersControl(pb); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePage#configureControls(org.eclipse.jface.action.IToolBarManager) + */ + @Override + protected void configureControls(final IToolBarManager tbm) { + super.configureControls(tbm); + configureLayers(tbm); + configureFilters(tbm); + } + + private void configureLayers(final IToolBarManager tbm) { + showLayersAction = new Action() { + @Override + public void run() { + showPage(ID_LAYERS); + } + }; + showLayersAction.setImageDescriptor(DESC_LAYER); + showLayersAction.setToolTipText(LAYER_TIP_TEXT); + tbm.add(showLayersAction); + } + + private void configureFilters(final IToolBarManager tbm) { + showFiltersAction = new Action() { + @Override + public void run() { + showPage(ID_FILTERS); + } + }; + showFiltersAction.setImageDescriptor(DESC_FILTER); + showFiltersAction.setToolTipText(FILTER_TIP_TEXT); + tbm.add(showFiltersAction); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePage#showPage(int) + */ + @Override + protected void showPage(final int id) { + switch (id) { + case ID_FILTERS: + super.uncheckProvidedActions(); + showLayersAction.setChecked(false); + showFiltersAction.setChecked(true); + super.showPage(filters); + break; + case ID_LAYERS: + super.uncheckProvidedActions(); + showFiltersAction.setChecked(false); + showLayersAction.setChecked(true); + super.showPage(layers); + break; + default: + showLayersAction.setChecked(false); + showFiltersAction.setChecked(false); + super.showPage(id); + break; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePage#dispose() + */ + @Override + public void dispose() { + if (contentProvider instanceof DiagramOutlinePageListener) { + this.removeListener((DiagramOutlinePageListener) contentProvider); + } + this.filters.dispose(); + this.layers.dispose(); + super.dispose(); + } + + /** + * Create the main control for layers. + * + * @param parent + * the parent + * @return the created control. + */ + protected Control createLayersControl(final Composite parent) { + + final Composite control = SWTUtil.createCompositeBothFill(parent, 1, false); + LayersTableViewer.createLayersTableViewer(control, this.diagramWorkbenchPart); + return control; + } + + /** + * Create the main control for layers. + * + * @param parent + * the parent + * @return the created control. + */ + protected Control createFiltersControl(final Composite parent) { + + final Composite control = SWTUtil.createCompositeBothFill(parent, 1, false); + FiltersTableViewer.createFiltersTableViewer(control, this.diagramWorkbenchPart); + return control; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersActivationAdapter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersActivationAdapter.java new file mode 100644 index 0000000000..3845413a53 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersActivationAdapter.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.filters; + +import java.util.Collection; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.description.filter.FilterDescription; + +/** + * An adapter to listen layer activation change. + * + * @author mchauvin + */ +public class FiltersActivationAdapter extends AdapterImpl { + + /** The structured viewer to update. */ + private StructuredViewer viewer; + + /** + * Set the viewer. + * + * @param viewer + * the viewer to update when the model change + */ + public void setViewer(final Viewer viewer) { + if (viewer instanceof StructuredViewer) { + this.viewer = (StructuredViewer) viewer; + } + } + + private void update(final DDiagram diagram, final FilterDescription filter, final boolean activate) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + if (viewer != null) { + viewer.update(filter, null); + } + } + }); + } + + private void update(final DDiagram notifier, final Collection<FilterDescription> filters, final boolean b) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + if (viewer != null && filters != null) { + for (FilterDescription filter : filters) { + viewer.update(filter, null); + } + } + } + }); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification) + */ + @Override + public void notifyChanged(final Notification msg) { + final Object notifier = msg.getNotifier(); + if (notifier instanceof DDiagram) { + final int featureID = msg.getFeatureID(DDiagram.class); + if (featureID == SiriusPackage.DDIAGRAM__ACTIVATED_FILTERS) { + + switch (msg.getEventType()) { + + case Notification.ADD: + final FilterDescription filterToAdd = (FilterDescription) msg.getNewValue(); + update((DDiagram) notifier, filterToAdd, true); + break; + case Notification.ADD_MANY: + @SuppressWarnings("unchecked") + final Collection<FilterDescription> filtersToAdd = (Collection<FilterDescription>) msg.getNewValue(); + update((DDiagram) notifier, filtersToAdd, true); + break; + case Notification.REMOVE: + final FilterDescription filterToRemove = (FilterDescription) msg.getOldValue(); + update((DDiagram) notifier, filterToRemove, false); + break; + case Notification.REMOVE_MANY: + @SuppressWarnings("unchecked") + final Collection<FilterDescription> filtersToRemove = (Collection<FilterDescription>) msg.getOldValue(); + update((DDiagram) notifier, filtersToRemove, false); + break; + default: + break; + } + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersCellModifier.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersCellModifier.java new file mode 100644 index 0000000000..ba8411c6de --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersCellModifier.java @@ -0,0 +1,130 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.filters; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gef.ui.palette.PaletteViewer; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.swt.widgets.Item; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.filter.FilterDescription; +import org.eclipse.sirius.diagram.tools.internal.handler.ChangeFilterActivation; + +/** + * The cell modifier. + * + * @author mchauvin + */ +public class FiltersCellModifier implements ICellModifier { + private final IDiagramWorkbenchPart diagramPart; + + private final String[] filtersColumns; + + /** + * Construct a new cell modifier. + * + * @param adapter + * the layer activation adapter + * @param part + * the workbench diagram part + * @param columns + * the layer table columns + */ + public FiltersCellModifier(final FiltersActivationAdapter adapter, final IDiagramWorkbenchPart part, final String[] columns) { + diagramPart = part; + filtersColumns = columns; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ICellModifier#canModify(java.lang.Object, + * java.lang.String) + */ + public boolean canModify(final Object element, final String property) { + + if (property.equals(filtersColumns[0])) { + /* first column */ + return true; + } + return false; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ICellModifier#getValue(java.lang.Object, + * java.lang.String) + */ + public Object getValue(final Object element, final String property) { + + final FilterDescription filter = (FilterDescription) element; + Object result = null; + + if (property.equals(filtersColumns[0])) { + /* first column */ + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + final EObject designerElement = ((View) obj).getElement(); + if (designerElement instanceof DDiagram) { + final List<FilterDescription> activatedFilters = ((DDiagram) designerElement).getActivatedFilters(); + if (activatedFilters.contains(element)) { + result = Boolean.TRUE; + } else { + result = Boolean.FALSE; + } + } + } + } else { + /* second column */ + result = filter.getName(); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ICellModifier#modify(java.lang.Object, + * java.lang.String, java.lang.Object) + */ + public void modify(final Object element, final String property, final Object value) { + Object objElement; + + if (element instanceof Item) { + objElement = ((Item) element).getData(); + } else { + objElement = element; + } + + final FilterDescription filterDescription = (FilterDescription) objElement; + + if (property.equals(filtersColumns[0])) { + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + final EObject designerElement = ((View) obj).getElement(); + final PaletteViewer paletteViewer = diagramPart.getDiagramGraphicalViewer().getEditDomain().getPaletteViewer(); + if (designerElement instanceof DDiagram && paletteViewer != null) { + final Runnable change = new ChangeFilterActivation(diagramPart, (DDiagram) designerElement, filterDescription, value.equals(Boolean.TRUE)); + change.run(); + } + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersContentProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersContentProvider.java new file mode 100644 index 0000000000..85fbf9c24a --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersContentProvider.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.filters; + +import java.util.Collections; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.DiagramDescription; + +/** + * The content provider. + * + * @author mchauvin + */ +public class FiltersContentProvider implements IStructuredContentProvider { + + /** The EMF adapter */ + private FiltersActivationAdapter filtersActivationAdapter; + + /** The diagram workbench part */ + private IDiagramWorkbenchPart diagramPart; + + /** + * Create a new content providers to display layers. + * + * @param adapter + * the layer activation adapter + * @param part + * the part responsible of the diagram access + */ + public FiltersContentProvider(final FiltersActivationAdapter adapter, final IDiagramWorkbenchPart part) { + filtersActivationAdapter = adapter; + diagramPart = part; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(final Object inputElement) { + if (inputElement instanceof DiagramDescription) { + final DiagramDescription diagramDesc = (DiagramDescription) inputElement; + return diagramDesc.getFilters().toArray(); + } + return Collections.EMPTY_LIST.toArray(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + if (diagramPart != null) { + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + removeListenerFrom((View) obj); + } + diagramPart = null; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { + if (oldInput != null) { + filtersActivationAdapter.setViewer(viewer); + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + removeListenerFrom((View) obj); + } + } + if (newInput != null) { + filtersActivationAdapter.setViewer(viewer); + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + addListenerTo((View) obj); + } + } + } + + private void removeListenerFrom(final View oldInput) { + final EObject element = oldInput.getElement(); + if (element instanceof DDiagram) { + final DDiagram diagram = (DDiagram) element; + diagram.eAdapters().remove(filtersActivationAdapter); + } + } + + private void addListenerTo(final View newInput) { + final EObject element = newInput.getElement(); + if (element instanceof DDiagram) { + final DDiagram diagram = (DDiagram) element; + diagram.eAdapters().add(filtersActivationAdapter); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersLabelProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersLabelProvider.java new file mode 100644 index 0000000000..daced55348 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersLabelProvider.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.filters; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.ITableLabelProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.description.filter.FilterDescription; +import org.eclipse.sirius.diagram.ImagesPath; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; + +/** + * The label provider. + * + * @author mchauvin + */ +public class FiltersLabelProvider extends LabelProvider implements ITableLabelProvider { + + private IDiagramWorkbenchPart diagramPart; + + /** + * Construct a new Layer label provider. + * + * @param part + * the part responsible of the diagram access. + */ + public FiltersLabelProvider(final IDiagramWorkbenchPart part) { + diagramPart = part; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnImage(java.lang.Object, + * int) + */ + public Image getColumnImage(final Object element, final int columnIndex) { + if (columnIndex == 0 && diagramPart != null) { + + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + final EObject designerElement = ((View) obj).getElement(); + if (designerElement instanceof DDiagram) { + final List<FilterDescription> activatedFilters = ((DDiagram) designerElement).getActivatedFilters(); + Image img = null; + if (activatedFilters.contains(element)) { + img = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.ACTIVE_FILTER_ICON); + } else { + img = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.INACTIVE_FILTER_ICON); + } + return img; + } + } + + } + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ITableLabelProvider#getColumnText(java.lang.Object, + * int) + */ + public String getColumnText(final Object element, final int columnIndex) { + switch (columnIndex) { + case 1: + if (element instanceof FilterDescription) { + return ((FilterDescription) element).getName(); + } + break; + default: + break; + } + return null; + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.BaseLabelProvider#dispose() + */ + @Override + public void dispose() { + super.dispose(); + diagramPart = null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersTableViewer.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersTableViewer.java new file mode 100644 index 0000000000..cc366495e5 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/filters/FiltersTableViewer.java @@ -0,0 +1,99 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.filters; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.CheckboxCellEditor; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.diagram.ImagesPath; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; + +/** + * Filters table viewer. + * + * @author mchauvin + */ +public final class FiltersTableViewer { + + private static final String[] COLUMNS = { " ", "Filter" }; + + /** + * Avoid instantiation. + */ + private FiltersTableViewer() { + + } + + /** + * Create a new Filters table viewer. + * + * @param control + * the parent composite + * @param workbenchPart + * the workbench part to access diagram part + * @return new viewer to enable or disable filters + */ + public static TableViewer createFiltersTableViewer(final Composite control, final IDiagramWorkbenchPart workbenchPart) { + + final TableViewer tableViewer = new TableViewer(control, SWT.BORDER | SWT.FULL_SELECTION); + final Table table = tableViewer.getTable(); + + table.setLayoutData(new GridData(GridData.FILL_BOTH)); + + TableColumn tc = new TableColumn(table, SWT.LEFT, 0); + tc.setText(COLUMNS[0]); + tc.setWidth(30); + tc.setImage(SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.FILTER_ACTIVATION_ICON)); + + tc = new TableColumn(table, SWT.LEFT, 1); + tc.setText(COLUMNS[1]); + tc.setWidth(200); + + // Can only changes the first column - the visible column + final CellEditor[] editors = new CellEditor[2]; + editors[0] = new CheckboxCellEditor(table); + for (int i = 1; i < 2; i++) { + editors[i] = null; + } + + tableViewer.setColumnProperties(COLUMNS); + + tableViewer.setCellEditors(editors); + final FiltersActivationAdapter adapter = new FiltersActivationAdapter(); + tableViewer.setCellModifier(new FiltersCellModifier(adapter, workbenchPart, COLUMNS)); + tableViewer.setContentProvider(new FiltersContentProvider(adapter, workbenchPart)); + tableViewer.setLabelProvider(new FiltersLabelProvider(workbenchPart)); + + if (workbenchPart != null) { + final EObject eObj = workbenchPart.getDiagram().getElement(); + if (eObj instanceof DDiagram) { + final DDiagram diagram = (DDiagram) eObj; + tableViewer.setInput(diagram.getDescription()); + } + } + + /* Lines are not visible in the specifications */ + table.setLinesVisible(true); + table.setHeaderVisible(true); + return tableViewer; + + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersActivationAdapter.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersActivationAdapter.java new file mode 100644 index 0000000000..1628445032 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersActivationAdapter.java @@ -0,0 +1,107 @@ +/******************************************************************************* + * Copyright (c) 2008, 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.layers; + +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.common.notify.impl.AdapterImpl; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.description.Layer; +import org.eclipse.sirius.diagram.tools.api.graphical.edit.palette.PaletteManager; + +/** + * An adapter to listen layer activation change. + * + * @author mchauvin + */ +public class LayersActivationAdapter extends AdapterImpl { + + /** The structured viewer to update. */ + private StructuredViewer viewer; + + /** The palette manager to update. */ + private PaletteManager paletteManager; + + /** + * Set the viewer. + * + * @param viewer + * the viewer to update when the model change + */ + public void setViewer(final Viewer viewer) { + if (viewer instanceof StructuredViewer) { + this.viewer = (StructuredViewer) viewer; + } + } + + /** + * Set the palette manager. + * + * @param paletteManager + * the palette manager to update when the model change + */ + public void setPaletteManager(final PaletteManager paletteManager) { + this.paletteManager = paletteManager; + } + + private void update(final DDiagram diagram, final Layer layer, final boolean activate) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + if (viewer != null) { + viewer.update(layer, null); + } + + if (paletteManager != null) { + /* refresh the palette */ + if (activate) { + paletteManager.showLayer(layer); + } else { + paletteManager.hideLayer(layer); + } + } + + } + }); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.common.notify.impl.AdapterImpl#notifyChanged(org.eclipse.emf.common.notify.Notification) + */ + @Override + public void notifyChanged(final Notification msg) { + final Object notifier = msg.getNotifier(); + if (notifier instanceof DDiagram) { + final int featureID = msg.getFeatureID(DDiagram.class); + if (featureID == SiriusPackage.DDIAGRAM__ACTIVATED_LAYERS) { + + switch (msg.getEventType()) { + + case Notification.ADD: + final Layer layerToAdd = (Layer) msg.getNewValue(); + update((DDiagram) notifier, layerToAdd, true); + break; + case Notification.REMOVE: + final Layer layerToRemove = (Layer) msg.getOldValue(); + update((DDiagram) notifier, layerToRemove, false); + break; + default: + break; + } + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersCellModifier.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersCellModifier.java new file mode 100644 index 0000000000..ad6eceb0ca --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersCellModifier.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.layers; + +import java.lang.reflect.InvocationTargetException; +import java.util.List; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.common.command.Command; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.swt.widgets.Item; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.progress.IProgressService; + +import org.eclipse.sirius.common.tools.api.util.EqualityHelper; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.query.IdentifiedElementQuery; +import org.eclipse.sirius.description.Layer; +import org.eclipse.sirius.diagram.tools.api.editor.DDiagramEditor; +import org.eclipse.sirius.diagram.ui.tools.internal.commands.ChangeLayerActivationCommand; + +/** + * The cell modifier. + * + * @author mchauvin + */ +public class LayersCellModifier implements ICellModifier { + + /** The EMF adapter */ + private final LayersActivationAdapter layerActivationAdapter; + + private final IDiagramWorkbenchPart diagramPart; + + private final String[] layerColumns; + + /** + * Construct a new cell modifier. + * + * @param adapter + * the layer activation adapter + * @param part + * the workbench diagram part + * @param columns + * the layer table columns + */ + public LayersCellModifier(final LayersActivationAdapter adapter, final IDiagramWorkbenchPart part, final String[] columns) { + layerActivationAdapter = adapter; + diagramPart = part; + layerColumns = columns; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ICellModifier#canModify(java.lang.Object, + * java.lang.String) + */ + public boolean canModify(final Object element, final String property) { + + if (property.equals(layerColumns[0])) { + /* first column */ + return true; + } + return false; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ICellModifier#getValue(java.lang.Object, + * java.lang.String) + */ + public Object getValue(final Object element, final String property) { + + final Layer layer = (Layer) element; + Object result = null; + + if (property.equals(layerColumns[0])) { + /* first column */ + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + final EObject designerElement = ((View) obj).getElement(); + if (designerElement instanceof DDiagram) { + final List<Layer> activatedLayers = ((DDiagram) designerElement).getActivatedLayers(); + if (EqualityHelper.contains(activatedLayers, (EObject) element)) { + result = Boolean.TRUE; + } else { + result = Boolean.FALSE; + } + } + } + } else { + /* second column */ + result = new IdentifiedElementQuery(layer).getLabel(); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ICellModifier#modify(java.lang.Object, + * java.lang.String, java.lang.Object) + */ + public void modify(final Object element, final String property, final Object value) { + + Object objElement; + + if (element instanceof Item) { + objElement = ((Item) element).getData(); + } else { + objElement = element; + } + + final Layer layer = (Layer) objElement; + + if (property.equals(layerColumns[0])) { + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + + final EObject designerElement = ((View) obj).getElement(); + + final DDiagramEditor diagramEditor = (DDiagramEditor) diagramPart.getDiagramGraphicalViewer().getProperty(DDiagramEditor.EDITOR_ID); + + if (designerElement instanceof DDiagram && diagramEditor != null) { + final DDiagram dDiagram = (DDiagram) designerElement; + layerActivationAdapter.setPaletteManager(diagramEditor.getPaletteManager()); + + final IWorkbench wb = PlatformUI.getWorkbench(); + final IProgressService ps = wb.getProgressService(); + try { + ps.busyCursorWhile(new IRunnableWithProgress() { + + public void run(final IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + monitor.beginTask("Apply layer modifications...", 1); + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(dDiagram); + Command changeActivatedLayersCmd = new ChangeLayerActivationCommand(domain, dDiagram, layer); + domain.getCommandStack().execute(changeActivatedLayersCmd); + monitor.done(); + } + }); + } catch (final InvocationTargetException e) { + if (e.getCause() instanceof RuntimeException) { + throw (RuntimeException) e.getCause(); + } + throw new RuntimeException(e.getCause()); + } catch (final InterruptedException e) { + throw new RuntimeException(e); + } + } + } + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersContentProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersContentProvider.java new file mode 100644 index 0000000000..f05b0b2775 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersContentProvider.java @@ -0,0 +1,164 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.layers; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.Viewer; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.business.api.componentization.DiagramComponentizationManager; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.description.DiagramDescription; +import org.eclipse.sirius.description.Layer; + +/** + * The content provider. + * + * @author mchauvin + */ +public class LayersContentProvider implements IStructuredContentProvider { + + /** The EMF adapter */ + private final LayersActivationAdapter layerActivationAdapter; + + /** The Session listener */ + private final LayersEventsListener eventListener; + + /** The diagram workbench part */ + private IDiagramWorkbenchPart diagramPart; + + /** the diagram session */ + private Session session; + + /** + * Create a new content providers to display layers. + * + * @param adapter + * the layer activation adapter + * @param listener + * the session listener + * @param part + * the part responsible of the diagram access + */ + public LayersContentProvider(final LayersActivationAdapter adapter, final LayersEventsListener listener, final IDiagramWorkbenchPart part) { + layerActivationAdapter = adapter; + eventListener = listener; + diagramPart = part; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(final Object inputElement) { + if (inputElement instanceof DDiagram) { + final DiagramDescription diagramDesc = ((DDiagram) inputElement).getDescription(); + final Collection<Layer> allLayers = new DiagramComponentizationManager().getAllLayers(session.getSelectedSiriuss(false), diagramDesc); + final Collection<Layer> layers = new ArrayList<Layer>(); + for (final Layer layer : allLayers) { + if (layer != null && layer != diagramDesc.getDefaultLayer()) { + layers.add(layer); + } + } + return layers.toArray(); + } + return Collections.EMPTY_LIST.toArray(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + if (diagramPart != null) { + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + removeListenerFrom((View) obj); + } + diagramPart = null; + } + if (session != null) { + session = null; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { + layerActivationAdapter.setViewer(viewer); + eventListener.setViewer(viewer); + + if (oldInput != null) { + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + removeListenerFrom((View) obj); + } + } + if (newInput != null) { + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + addListenerTo((View) obj); + } + } + } + + private void removeListenerFrom(final View oldInput) { + final EObject element = oldInput.getElement(); + if (element instanceof DDiagram) { + final DDiagram diagram = (DDiagram) element; + diagram.eAdapters().remove(layerActivationAdapter); + removeListenerToSession(); + } + } + + private void addListenerTo(final View newInput) { + final EObject element = newInput.getElement(); + if (element instanceof DDiagram) { + final DDiagram diagram = (DDiagram) element; + diagram.eAdapters().add(layerActivationAdapter); + if (diagram instanceof DSemanticDiagram) { + session = SessionManager.INSTANCE.getSession(((DSemanticDiagram) diagram).getTarget()); + addListenerToSession(); + } + } + } + + private void addListenerToSession() { + if (session != null) { + session.addListener(eventListener); + } + } + + private void removeListenerToSession() { + if (session != null) { + session.removeListener(eventListener); + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersEventsListener.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersEventsListener.java new file mode 100644 index 0000000000..545393cd48 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersEventsListener.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.layers; + +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.ui.PlatformUI; + +import org.eclipse.sirius.business.api.session.SessionListener; + +/** + * A listener to update the viewer. + * + * @author mchauvin + */ +public class LayersEventsListener implements SessionListener { + + /** The structured viewer to update. */ + private StructuredViewer viewer; + + /** + * Set the viewer. + * + * @param viewer + * the viewer to update when the model change + */ + public void setViewer(final Viewer viewer) { + if (viewer instanceof StructuredViewer) { + this.viewer = (StructuredViewer) viewer; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.business.api.session.SessionListener#notify(int) + */ + public void notify(final int changeKind) { + switch (changeKind) { + case SessionListener.SELECTED_VIEWS_CHANGE_KIND: + case SessionListener.VSM_UPDATED: + updateViewer(); + break; + default: + break; + } + + } + + private void updateViewer() { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + if (viewer != null && !viewer.getControl().isDisposed()) { + viewer.refresh(); + } + } + }); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersLabelProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersLabelProvider.java new file mode 100644 index 0000000000..18af9c8d1c --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersLabelProvider.java @@ -0,0 +1,178 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.layers; + +import java.util.List; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.editparts.DiagramEditPart; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.Point; + +import org.eclipse.sirius.common.tools.api.util.EqualityHelper; +import org.eclipse.sirius.common.tools.api.util.StringUtil; +import org.eclipse.sirius.common.ui.tools.api.util.ImageProvider; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.business.api.query.IdentifiedElementQuery; +import org.eclipse.sirius.description.Layer; +import org.eclipse.sirius.diagram.ImagesPath; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; + +/** + * The label provider. + * + * @author mchauvin + */ +public class LayersLabelProvider extends ColumnLabelProvider { + + private IDiagramWorkbenchPart diagramPart; + + private int columnIndex; + + /** + * Construct a new Layer label provider. + * + * @param part + * the part responsible of the diagram access. + */ + public LayersLabelProvider(final IDiagramWorkbenchPart part) { + diagramPart = part; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ColumnLabelProvider#getImage(java.lang.Object) + */ + @Override + public Image getImage(Object element) { + Image result = null; + if (columnIndex == 0 && diagramPart != null) { + final DiagramEditPart diaEditPart = diagramPart.getDiagramEditPart(); + final Object obj = diaEditPart.getModel(); + if (obj instanceof View) { + final EObject designerElement = ((View) obj).getElement(); + if (designerElement instanceof DDiagram) { + final List<Layer> activatedLayers = ((DDiagram) designerElement).getActivatedLayers(); + Image img = null; + if (EqualityHelper.contains(activatedLayers, (EObject) element)) { + img = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.ACTIVE_LAYER_ICON); + } else { + img = SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.INACTIVE_LAYER_ICON); + } + result = img; + } + } + } else if (columnIndex == 1) { + if (element instanceof Layer) { + Layer layer = (Layer) element; + if (!StringUtil.isEmpty(layer.getIcon())) { + result = ImageProvider.getImageFromPath(layer.getIcon()); + } + } + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ColumnLabelProvider#getText(java.lang.Object) + */ + @Override + public String getText(Object element) { + switch (columnIndex) { + case 2: + if (element instanceof Layer) { + return new IdentifiedElementQuery((Layer) element).getLabel(); + } + break; + default: + break; + } + return null; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipText(java.lang.Object) + */ + @Override + public String getToolTipText(Object element) { + if (element instanceof Layer) { + String endUserDoc = ((Layer) element).getEndUserDocumentation(); + if (endUserDoc != null && endUserDoc.trim().length() > 0) { + return endUserDoc; + } + } + return super.getToolTipText(element); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipShift(java.lang.Object) + */ + @Override + public Point getToolTipShift(final Object object) { + return new Point(5, 5); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipDisplayDelayTime(java.lang.Object) + */ + @Override + public int getToolTipDisplayDelayTime(final Object object) { + return 200; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.CellLabelProvider#getToolTipStyle(java.lang.Object) + */ + @Override + public int getToolTipStyle(final Object object) { + return SWT.SHADOW_OUT; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ColumnLabelProvider#update(org.eclipse.jface.viewers.ViewerCell) + */ + @Override + public void update(final ViewerCell cell) { + columnIndex = cell.getColumnIndex(); + super.update(cell); + } + + /** + * + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.BaseLabelProvider#dispose() + */ + @Override + public void dispose() { + super.dispose(); + diagramPart = null; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersTableViewer.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersTableViewer.java new file mode 100644 index 0000000000..7771c931e2 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/layers/LayersTableViewer.java @@ -0,0 +1,106 @@ +/******************************************************************************* + * Copyright (c) 2009 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.layers; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.gmf.runtime.diagram.ui.parts.IDiagramWorkbenchPart; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.CheckboxCellEditor; +import org.eclipse.jface.viewers.ColumnViewerToolTipSupport; +import org.eclipse.jface.viewers.TableViewer; +import org.eclipse.jface.window.ToolTip; +import org.eclipse.swt.SWT; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Table; +import org.eclipse.swt.widgets.TableColumn; + +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.diagram.ImagesPath; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; + +/** + * Factory class to create a layers table viewer. + * + * @author mchauvin + */ +public final class LayersTableViewer { + + private static final String[] COLUMNS = { " ", " ", "Layer" }; + + /** + * Avoid instantiation. + */ + private LayersTableViewer() { + } + + /** + * Create a new Layers table viewer. + * + * @param control + * the parent SWT control + * @param diagramPart + * the workbench part to access diagram part + * @return new viewer to hide or show layers. + */ + public static TableViewer createLayersTableViewer(final Composite control, final IDiagramWorkbenchPart diagramPart) { + + final TableViewer tableViewer = new TableViewer(control, SWT.BORDER | SWT.FULL_SELECTION); + ColumnViewerToolTipSupport.enableFor(tableViewer, ToolTip.RECREATE); + + final Table table = tableViewer.getTable(); + + table.setLayoutData(new GridData(GridData.FILL_BOTH)); + + TableColumn tc = new TableColumn(table, SWT.LEFT, 0); + tc.setText(COLUMNS[0]); + tc.setWidth(30); + tc.setImage(SiriusDiagramEditorPlugin.getInstance().getBundledImage(ImagesPath.LAYER_ACTIVATION_ICON)); + + tc = new TableColumn(table, SWT.CENTER, 1); + tc.setText(COLUMNS[1]); + tc.setWidth(30); + + tc = new TableColumn(table, SWT.LEFT, 2); + tc.setText(COLUMNS[2]); + tc.setWidth(200); + + // Can only changes the first column - the visible column + final CellEditor[] editors = new CellEditor[3]; + editors[0] = new CheckboxCellEditor(table); + for (int i = 1; i < 3; i++) { + editors[i] = null; + } + + tableViewer.setColumnProperties(COLUMNS); + + tableViewer.setCellEditors(editors); + final LayersActivationAdapter adapter = new LayersActivationAdapter(); + final LayersEventsListener sessionListener = new LayersEventsListener(); + tableViewer.setCellModifier(new LayersCellModifier(adapter, diagramPart, COLUMNS)); + tableViewer.setContentProvider(new LayersContentProvider(adapter, sessionListener, diagramPart)); + tableViewer.setLabelProvider(new LayersLabelProvider(diagramPart)); + + if (diagramPart != null) { + final EObject eObj = diagramPart.getDiagram().getElement(); + if (eObj instanceof DDiagram) { + final DDiagram diagram = (DDiagram) eObj; + tableViewer.setInput(diagram); + } + } + + /* Lines are not visible in the specifications */ + table.setLinesVisible(true); + table.setHeaderVisible(true); + return tableViewer; + } + +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineComparator.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineComparator.java new file mode 100644 index 0000000000..e3958c0c08 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineComparator.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline; + +import org.eclipse.jface.viewers.ViewerComparator; + +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; + +/** + * A comparator for the outline tree viewer. + * + * @author Mariot Chauvin (mchauvin) + */ +public class OutlineComparator extends ViewerComparator { + + private static final int VISIBLE_ELEMENT = 0; + + private static final int HIDDEN_ELEMENT = 2; + + private static final int OTHER_NON_VISIBLE_ELEMENT = 4; + + private static final int VIEW_NODE = 0; + + private static final int VIEW_EDGE = 1; + + private static final int OTHER = 64; + + /** + * Constructor. + */ + public OutlineComparator() { + super(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ViewerComparator#category(java.lang.Object) + */ + @Override + public int category(final Object element) { + + int value = 0; + if (element instanceof DDiagramElement) { + if (((DDiagramElement) element).isVisible()) { + value += VISIBLE_ELEMENT; + } else if (new DDiagramElementQuery((DDiagramElement) element).isHidden()) { + value += HIDDEN_ELEMENT; + } else { + value += OTHER_NON_VISIBLE_ELEMENT; + } + + if (element instanceof DEdge) { + value += VIEW_EDGE; + } else { + value += VIEW_NODE; + } + + } else { + value += OTHER; + } + return value; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ViewerComparator#isSorterProperty(java.lang.Object, + * java.lang.String) + */ + @Override + public boolean isSorterProperty(final Object element, final String property) { + return false; + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineContentProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineContentProvider.java new file mode 100644 index 0000000000..3e5faeb798 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineContentProvider.java @@ -0,0 +1,329 @@ +/******************************************************************************* + * Copyright (c) 2007, 2010 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.emf.transaction.util.TransactionUtil; +import org.eclipse.gmf.runtime.notation.Diagram; +import org.eclipse.gmf.runtime.notation.View; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DDiagramElementContainer; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DSemanticDiagram; +import org.eclipse.sirius.business.api.componentization.DiagramMappingsManager; +import org.eclipse.sirius.business.api.componentization.DiagramMappingsManagerRegistry; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.business.api.session.Session; +import org.eclipse.sirius.business.api.session.SessionManager; +import org.eclipse.sirius.business.internal.metamodel.helper.LayerHelper; +import org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePageListener; +import org.eclipse.sirius.provider.SiriusItemProviderAdapterFactory; +import org.eclipse.sirius.ui.business.api.provider.AbstractDDiagramElementLabelItemProvider; +import org.eclipse.sirius.ui.business.api.provider.DEdgeLabelItemProvider; +import org.eclipse.sirius.ui.business.api.provider.DNodeLabelItemProvider; + +/** + * This class is responsible of the outline tree viewer content. + * + * @author Mariot Chauvin (mchauvin) + */ +public class OutlineContentProvider implements ITreeContentProvider, DiagramOutlinePageListener { + + /** The EMF adapter */ + OutlineContentResourceSetListener outlineContentResourceSetListener = new OutlineContentResourceSetListener(); + + private boolean checkParent(final EObject parent) { + + boolean check = false; + + // should never happen but avoid an NPE if it happens + if (parent == null) { + check = true; + } else if (parent instanceof DDiagram) { + check = true; + } else if (parent instanceof DDiagramElementContainer) { + check = true; + } + + return check; + } + + private DDiagram getDiagramContainer(final EObject element) { + + DDiagram dia = null; + + if (element instanceof DDiagram) { + dia = (DDiagram) element; + } else if (element != null) { + dia = getDiagramContainer(element.eContainer()); + } + return dia; + } + + private List<Object> clearFilteredElements(final List<? extends Object> elements, final DDiagram vp) { + Session session = SessionManager.INSTANCE.getSession(((DSemanticDiagram) vp).getTarget()); + DiagramMappingsManager mappingManager = DiagramMappingsManagerRegistry.INSTANCE.getDiagramMappingsManager(session, vp); + final List<Object> result = new ArrayList<Object>(elements.size()); + final Iterator<? extends Object> it = elements.iterator(); + while (it.hasNext()) { + final Object element = it.next(); + if (element instanceof DDiagramElement) { + DDiagramElement dde = (DDiagramElement) element; + if (!isFiltered(mappingManager, dde)) { + result.add(dde); + } + } else if (element instanceof AbstractDDiagramElementLabelItemProvider) { + Option<DDiagramElement> optionTarget = ((AbstractDDiagramElementLabelItemProvider) element).getDiagramElementTarget(); + if (optionTarget.some() && !isFiltered(mappingManager, optionTarget.get())) { + result.add(element); + } + } + } + return result; + } + + /** + * Check if the diagram element is filtered. + * + * @param dde + * The diagram element to check. + */ + private boolean isFiltered(DiagramMappingsManager session, DDiagramElement dde) { + boolean isFiltered = true; + if (dde.isVisible()) { + isFiltered = false; + } else if (LayerHelper.isInActivatedLayer(session, dde) && !new DDiagramElementQuery(dde).isFiltered()) { + isFiltered = false; + } + return isFiltered; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object) + */ + public Object getParent(final Object element) { + + Object theParent = null; + + // diagram is the root object + if (element instanceof DDiagram) { + return theParent; + } else if (element instanceof DNode) { + + final DNode vn = (DNode) element; + final EObject parent = vn.eContainer(); + + if (parent instanceof DNode) { + theParent = parent; + } else if (checkParent(parent)) { + theParent = parent; + } + } + + else if (element instanceof DDiagramElement) { + + final DDiagramElement vpe = (DDiagramElement) element; + final EObject parent = vpe.eContainer(); + + if (checkParent(parent)) { + theParent = parent; + } + } + + return theParent; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object) + */ + public Object[] getChildren(final Object parentElement) { + + Object[] children = null; + + if (parentElement instanceof DDiagram) { + final DDiagram diagram = (DDiagram) parentElement; + children = clearFilteredElements(diagram.getOwnedDiagramElements(), diagram).toArray(); + } + + else if (parentElement instanceof DNode) { + final DNode dNode = (DNode) parentElement; + List<Object> originalChildren = new ArrayList<Object>(dNode.getOwnedBorderedNodes()); + + // if the current node should have a DNodeLabelItem has children + if (DNodeLabelItemProvider.hasRelevantLabelItem(dNode)) { + originalChildren.add(0, new DNodeLabelItemProvider(getAdapterFactory(), dNode)); + } + children = clearFilteredElements(originalChildren, getDiagramContainer(dNode)).toArray(); + } + + else if (parentElement instanceof DEdge) { + final DEdge dEdge = (DEdge) parentElement; + List<Object> originalChildren = new ArrayList<Object>(); + + // if the current node should have a DEdgeLabelItem has children + if (DEdgeLabelItemProvider.hasRelevantLabelItem(dEdge)) { + originalChildren.add(0, new DEdgeLabelItemProvider(getAdapterFactory(), dEdge)); + } + // if (DEdgeBeginLabelItemProvider.hasRelevantLabelItem(dEdge)) { + // originalChildren.add(0, new + // DEdgeBeginLabelItemProvider(getAdapterFactory(), dEdge)); + // } + // if (DEdgeEndLabelItemProvider.hasRelevantLabelItem(dEdge)) { + // originalChildren.add(0, new + // DEdgeEndLabelItemProvider(getAdapterFactory(), dEdge)); + // } + children = clearFilteredElements(originalChildren, getDiagramContainer(dEdge)).toArray(); + } + + else if (parentElement instanceof DDiagramElementContainer) { + final DDiagramElementContainer diagramElementContainer = (DDiagramElementContainer) parentElement; + children = clearFilteredElements(diagramElementContainer.getElements(), getDiagramContainer(diagramElementContainer)).toArray(); + } + + return children; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object) + */ + public boolean hasChildren(final Object element) { + + boolean hasChildren = false; + + if (element instanceof DDiagram || element instanceof DNode || element instanceof DEdge || element instanceof DDiagramElementContainer) { + hasChildren = getChildren(element).length > 0; + } + // in other case return false + return hasChildren; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object) + */ + public Object[] getElements(final Object inputElement) { + + // in case of diagram return the viewpoint + if (inputElement instanceof Diagram) { + final Diagram diagram = (Diagram) inputElement; + final EObject viewpointDiagram = diagram.getElement(); + if (viewpointDiagram != null) { + return new Object[] { viewpointDiagram }; + } + } + + // all other case return the element + return new Object[] { inputElement }; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public void dispose() { + // do nothing + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) { + if (oldInput != null) { + removeListenerFrom((View) oldInput); + } + if (newInput != null) { + outlineContentResourceSetListener.setViewer(viewer); + addListenerTo((View) newInput); + } + } + + private void removeListenerFrom(final View oldInput) { + final EObject element = oldInput.getElement(); + if (element instanceof DDiagram) { + final DDiagram diagram = (DDiagram) element; + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(diagram); + /* Editing domain is null */ + if (domain != null) { + domain.removeResourceSetListener(outlineContentResourceSetListener); + } + } + } + + private void addListenerTo(final View newInput) { + final EObject element = newInput.getElement(); + if (element instanceof DDiagram) { + final DDiagram diagram = (DDiagram) element; + TransactionalEditingDomain domain = TransactionUtil.getEditingDomain(diagram); + if (domain != null) { + domain.addResourceSetListener(outlineContentResourceSetListener); + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.common.ui.tools.api.outline.DiagramOutlinePageListener#activate(int) + */ + public void activate(int page) { + this.outlineContentResourceSetListener.activate(page); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.common.ui.tools.api.outline.DiagramOutlinePageListener#deactivate(int) + */ + public void deactivate(int page) { + this.outlineContentResourceSetListener.deactivate(page); + } + + /** + * Returns the adapter factory used by this viewer. + * + * @return The adapter factory used by this viewer. + */ + public AdapterFactory getAdapterFactory() { + List<AdapterFactory> factories = new ArrayList<AdapterFactory>(); + factories.add(new SiriusItemProviderAdapterFactory()); + factories.add(new ResourceItemProviderAdapterFactory()); + factories.add(new EcoreItemProviderAdapterFactory()); + factories.add(new ReflectiveItemProviderAdapterFactory()); + return new ComposedAdapterFactory(factories); + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineContentResourceSetListener.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineContentResourceSetListener.java new file mode 100644 index 0000000000..3c17cd185f --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineContentResourceSetListener.java @@ -0,0 +1,326 @@ +/******************************************************************************* + * Copyright (c) 2010, 2012 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline; + +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.common.notify.Notification; +import org.eclipse.emf.ecore.provider.EcoreItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.ComposedAdapterFactory; +import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory; +import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory; +import org.eclipse.emf.transaction.DemultiplexingListener; +import org.eclipse.emf.transaction.ResourceSetChangeEvent; +import org.eclipse.emf.transaction.TransactionalEditingDomain; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.swt.widgets.Display; + +import com.google.common.collect.Sets; + +import org.eclipse.sirius.AbstractDNode; +import org.eclipse.sirius.DDiagram; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DNode; +import org.eclipse.sirius.DNodeContainer; +import org.eclipse.sirius.DNodeList; +import org.eclipse.sirius.SiriusPackage; +import org.eclipse.sirius.business.api.helper.SiriusUtil; +import org.eclipse.sirius.diagram.tools.internal.editor.DiagramOutlinePageListener; +import org.eclipse.sirius.provider.SiriusItemProviderAdapterFactory; +import org.eclipse.sirius.ui.business.api.provider.DEdgeLabelItemProvider; +import org.eclipse.sirius.ui.business.api.provider.DNodeLabelItemProvider; + +/** + * This class is an EMF Transaction resource listener which listen post commit + * notifications to update a {@link StructuredViewer} of the outline. + * + * @author Mariot Chauvin (mchauvin) + */ +public class OutlineContentResourceSetListener extends DemultiplexingListener implements DiagramOutlinePageListener { + + /** The structured viewer to update. */ + private StructuredViewer viewer; + + private boolean active; + + private Set<DDiagram> toRefresh; + + private Set<Object> toUpdate; + + /** flag signaling a refresh request made while the outline was disabled. */ + private boolean deferredRefresh; + + /** + * Set the viewer. + * + * @param viewer + * the viewer to update when the model change + */ + public void setViewer(final Viewer viewer) { + if (viewer instanceof StructuredViewer) { + this.viewer = (StructuredViewer) viewer; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.transaction.DemultiplexingListener#resourceSetChanged(org.eclipse.emf.transaction.ResourceSetChangeEvent) + */ + @Override + public void resourceSetChanged(ResourceSetChangeEvent event) { + if (active) { + toRefresh = Sets.newHashSet(); + toUpdate = Sets.newHashSet(); + super.resourceSetChanged(event); + refreshOutline(); + toRefresh = null; + toUpdate = null; + } else { + toRefresh = Sets.newHashSet(); + toUpdate = Sets.newHashSet(); + deferredRefresh = true; + super.resourceSetChanged(event); + } + } + + private void refreshOutline() { + if (!toRefresh.isEmpty()) { + refreshViewer(); + } else { + updateViewer(); + } + } + + private void refreshViewer() { + for (final DDiagram diagram : toRefresh) { + refreshViewer(diagram); + } + } + + private void updateViewer() { + for (final Object object : toUpdate) { + updateViewer(object); + } + } + + private void refreshViewer(final DDiagram diagram) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) { + viewer.refresh(diagram, true); + } + } + }); + } + + private void updateViewer(final Object object) { + Display.getDefault().asyncExec(new Runnable() { + public void run() { + if (viewer != null && viewer.getControl() != null && !viewer.getControl().isDisposed()) { + viewer.update(object, null); + } + } + }); + } + + private void addToRefresh(final DDiagram diagram) { + toRefresh.add(diagram); + } + + private void addToUpdate(final Object object) { + toUpdate.add(object); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.emf.transaction.DemultiplexingListener#handleNotification(org.eclipse.emf.transaction.TransactionalEditingDomain, + * org.eclipse.emf.common.notify.Notification) + */ + @Override + protected void handleNotification(TransactionalEditingDomain domain, Notification notification) { + final Object notifier = notification.getNotifier(); + + if (notifier instanceof DDiagramElement) { + + caseDDiagramElement(notification, (DDiagramElement) notifier); + + if (notifier instanceof AbstractDNode) { + caseAbstractDNode(notification, (AbstractDNode) notifier); + } + + if (notifier instanceof DEdge) { + caseDEdge(notification, (DEdge) notifier); + } + + if (notifier instanceof DNodeContainer) { + caseDNodeContainer(notification, (DNodeContainer) notifier); + } else if (notifier instanceof DNodeList) { + caseDNodeList(notification, (DNodeList) notifier); + } + + } else if (notifier instanceof DDiagram) { + caseDDiagram(notification, (DDiagram) notifier); + } + } + + private void caseDDiagramElement(final Notification n, final DDiagramElement diagramElement) { + final int featureID = n.getFeatureID(DDiagramElement.class); + + switch (featureID) { + case SiriusPackage.DDIAGRAM_ELEMENT__VISIBLE: + addToRefresh(SiriusUtil.findDiagram(diagramElement)); + break; + case SiriusPackage.DDIAGRAM_ELEMENT__NAME: + addToUpdate(diagramElement); + break; + default: + break; + } + } + + private void caseAbstractDNode(final Notification n, final AbstractDNode node) { + final int featureID = n.getFeatureID(AbstractDNode.class); + + switch (featureID) { + case SiriusPackage.ABSTRACT_DNODE__OWNED_BORDERED_NODES: + case SiriusPackage.ABSTRACT_DNODE__GRAPHICAL_FILTERS: + addToRefresh(SiriusUtil.findDiagram(node)); + break; + default: + break; + } + if (DNodeLabelItemProvider.hasRelevantLabelItem(node)) { + addToUpdate(new DNodeLabelItemProvider(getAdapterFactoryForNodeLabelItems(), (DNode) node)); + } + } + + private void caseDEdge(final Notification n, final DEdge edge) { + if (DEdgeLabelItemProvider.hasRelevantLabelItem(edge)) { + addToUpdate(new DEdgeLabelItemProvider(getAdapterFactoryForNodeLabelItems(), edge)); + } + // if (DEdgeBeginLabelItemProvider.hasRelevantLabelItem(edge)) { + // addToUpdate(new + // DEdgeBeginLabelItemProvider(getAdapterFactoryForNodeLabelItems(), + // (DEdge) edge)); + // } + // if (DEdgeEndLabelItemProvider.hasRelevantLabelItem(edge)) { + // addToUpdate(new + // DEdgeEndLabelItemProvider(getAdapterFactoryForNodeLabelItems(), + // (DEdge) edge)); + // } + } + + /** + * Returns the adapter factory to use to define labelItems. + * + * @return The adapter factory used to define labelItems. + */ + private AdapterFactory getAdapterFactoryForNodeLabelItems() { + List<AdapterFactory> factories = new ArrayList<AdapterFactory>(); + factories.add(new SiriusItemProviderAdapterFactory()); + factories.add(new ResourceItemProviderAdapterFactory()); + factories.add(new EcoreItemProviderAdapterFactory()); + factories.add(new ReflectiveItemProviderAdapterFactory()); + return new ComposedAdapterFactory(factories); + } + + private void caseDNodeContainer(final Notification n, final DNodeContainer nodeContainer) { + final int featureID = n.getFeatureID(DNodeContainer.class); + + switch (featureID) { + case SiriusPackage.DNODE_CONTAINER__OWNED_DIAGRAM_ELEMENTS: + addToRefresh(SiriusUtil.findDiagram(nodeContainer)); + break; + default: + break; + } + } + + private void caseDNodeList(final Notification n, final DNodeList nodeList) { + final int featureID = n.getFeatureID(DNodeList.class); + + switch (featureID) { + case SiriusPackage.DNODE_LIST__OWNED_ELEMENTS: + addToRefresh(SiriusUtil.findDiagram(nodeList)); + break; + default: + break; + } + } + + private void caseDDiagram(final Notification n, final DDiagram diagram) { + final int featureID = n.getFeatureID(DDiagram.class); + if (featureID == SiriusPackage.DDIAGRAM__OWNED_DIAGRAM_ELEMENTS) { + switch (n.getEventType()) { + case Notification.ADD: + case Notification.REMOVE: + case Notification.REMOVE_MANY: + addToRefresh(diagram); + break; + default: + break; + } + } else if (featureID == SiriusPackage.DDIAGRAM__ACTIVATED_FILTERS) { + switch (n.getEventType()) { + case Notification.ADD: + case Notification.REMOVE: + case Notification.REMOVE_MANY: + addToRefresh(diagram); + break; + default: + break; + } + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.common.ui.tools.api.outline.DiagramOutlinePageListener#activate(int) + */ + public void activate(int page) { + switch (page) { + case DiagramOutlinePageListener.OUTLINE: + active = true; + if (deferredRefresh) { + deferredRefresh = false; + refreshOutline(); + toRefresh = null; + toUpdate = null; + } + break; + default: + break; + } + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.sirius.common.ui.tools.api.outline.DiagramOutlinePageListener#deactivate(int) + */ + public void deactivate(int page) { + switch (page) { + case DiagramOutlinePageListener.OUTLINE: + active = false; + break; + default: + break; + } + } +} diff --git a/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineLabelProvider.java b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineLabelProvider.java new file mode 100644 index 0000000000..ff697e0544 --- /dev/null +++ b/plugins/org.eclipse.sirius.diagram/specific/org/eclipse/sirius/diagram/ui/tools/internal/views/providers/outline/OutlineLabelProvider.java @@ -0,0 +1,187 @@ +/******************************************************************************* + * Copyright (c) 2007, 2008 THALES GLOBAL SERVICES. + * 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: + * Obeo - initial API and implementation + *******************************************************************************/ +package org.eclipse.sirius.diagram.ui.tools.internal.views.providers.outline; + +import org.eclipse.emf.common.notify.AdapterFactory; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.edit.provider.IItemLabelProvider; +import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.resource.JFaceResources; +import org.eclipse.jface.viewers.DecorationOverlayIcon; +import org.eclipse.jface.viewers.IDecoration; +import org.eclipse.jface.viewers.IFontProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.Image; + +import org.eclipse.sirius.common.tools.api.util.Option; +import org.eclipse.sirius.DDiagramElement; +import org.eclipse.sirius.DEdge; +import org.eclipse.sirius.DSemanticDecorator; +import org.eclipse.sirius.business.api.query.DDiagramElementQuery; +import org.eclipse.sirius.diagram.part.SiriusDiagramEditorPlugin; +import org.eclipse.sirius.provider.SiriusEditPlugin; +import org.eclipse.sirius.ui.business.api.provider.AbstractDDiagramElementLabelItemProvider; +import org.eclipse.sirius.ui.tools.api.image.ImagesPath; + +/** + * Label provider for the diagram outline tree. + * + * @author Mariot Chauvin (mchauvin) + */ +public class OutlineLabelProvider extends LabelProvider implements IFontProvider { + + /** */ + private AdapterFactory factory; + + /** + * Constructor. + * + * @param adapterFactory + */ + public OutlineLabelProvider() { + this.factory = SiriusDiagramEditorPlugin.getInstance().getItemProvidersAdapterFactory(); + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.LabelProvider#getImage(java.lang.Object) + */ + @Override + public Image getImage(final Object element) { + Image result = null; + if (element instanceof DSemanticDecorator) { + result = getImage((DSemanticDecorator) element); + } else if (element instanceof AbstractDDiagramElementLabelItemProvider) { + result = getImage((AbstractDDiagramElementLabelItemProvider) element); + } + return result; + } + + /** + * Returns the image for the label of the given DNodeLabelItemProvider. + * + * @param element + * the element for which to provide the label image + * @return the image used to label the element, or <code>null</code> if + * there is no image for the given object + */ + private Image getImage(final AbstractDDiagramElementLabelItemProvider element) { + Image result; + ImageDescriptor descriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(element.getImage(element.getTarget())); + if (descriptor == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + } + result = SiriusDiagramEditorPlugin.getInstance().getImage(descriptor); + + Option<DDiagramElement> optionTarget = element.getDiagramElementTarget(); + if (optionTarget.some() && new DDiagramElementQuery(optionTarget.get()).isLabelHidden()) { + final ImageDescriptor decoratorDescriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(SiriusEditPlugin.INSTANCE.getImage(ImagesPath.HIDDEN_DECORATOR)); + final DecorationOverlayIcon finalDescriptor = new DecorationOverlayIcon(result, decoratorDescriptor, IDecoration.TOP_LEFT); + result = SiriusDiagramEditorPlugin.getInstance().getImage(finalDescriptor); + } + + return result; + } + + /** + * Returns the image for the label of the given DSemanticDecorator. + * + * @param element + * the element for which to provide the label image + * @return the image used to label the element, or <code>null</code> if + * there is no image for the given object + */ + private Image getImage(DSemanticDecorator element) { + Image result = null; + final EObject target = element.getTarget(); + if (target != null) { + final IItemLabelProvider labelProvider = (IItemLabelProvider) this.factory.adapt(target, IItemLabelProvider.class); + if (labelProvider != null) { + ImageDescriptor descriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(labelProvider.getImage(target)); + if (descriptor == null) { + descriptor = ImageDescriptor.getMissingImageDescriptor(); + } + result = SiriusDiagramEditorPlugin.getInstance().getImage(descriptor); + + if (element instanceof DEdge) { + final ImageDescriptor decoratorDescriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(SiriusEditPlugin.INSTANCE.getImage(ImagesPath.VIEW_EDGE_DECORATOR)); + final DecorationOverlayIcon finalDescriptor = new DecorationOverlayIcon(result, decoratorDescriptor, IDecoration.BOTTOM_LEFT); + result = SiriusDiagramEditorPlugin.getInstance().getImage(finalDescriptor); + + result = computeFoldDecorator(result, (DEdge) element); + } + + if (element instanceof DDiagramElement && new DDiagramElementQuery((DDiagramElement) element).isHidden()) { + final ImageDescriptor decoratorDescriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(SiriusEditPlugin.INSTANCE.getImage(ImagesPath.HIDDEN_DECORATOR)); + final DecorationOverlayIcon finalDescriptor = new DecorationOverlayIcon(result, decoratorDescriptor, IDecoration.TOP_LEFT); + result = SiriusDiagramEditorPlugin.getInstance().getImage(finalDescriptor); + + } + } + } + return result; + } + + private Image computeFoldDecorator(final Image baseImage, final DEdge edge) { + if (new DDiagramElementQuery(edge).isFolded()) { + final ImageDescriptor foldDescription = ExtendedImageRegistry.getInstance().getImageDescriptor(SiriusEditPlugin.INSTANCE.getImage(ImagesPath.FOLD_DECORATOR)); + final DecorationOverlayIcon finalFoldDescriptor = new DecorationOverlayIcon(baseImage, foldDescription, IDecoration.TOP_RIGHT); + return SiriusDiagramEditorPlugin.getInstance().getImage(finalFoldDescriptor); + } + return baseImage; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.LabelProvider#getText(java.lang.Object) + */ + @Override + public String getText(final Object element) { + String result = null; + if (element instanceof DSemanticDecorator) { + final EObject target = ((DSemanticDecorator) element).getTarget(); + if (target != null) { + final IItemLabelProvider labelProvider = (IItemLabelProvider) this.factory.adapt(target, IItemLabelProvider.class); + if (labelProvider != null) { + result = labelProvider.getText(target); + } + } + } else if (element instanceof AbstractDDiagramElementLabelItemProvider) { + result = ((AbstractDDiagramElementLabelItemProvider) element).getText(((AbstractDDiagramElementLabelItemProvider) element).getTarget()); + } + return result; + } + + /** + * {@inheritDoc} + * + * @see org.eclipse.jface.viewers.IFontProvider#getFont(java.lang.Object) + */ + public Font getFont(final Object element) { + Font result = JFaceResources.getDefaultFont(); + if (element instanceof DDiagramElement) { + final DDiagramElement vpe = (DDiagramElement) element; + if (!vpe.isVisible()) { + result = JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT); + } + } else if (element instanceof AbstractDDiagramElementLabelItemProvider) { + Option<DDiagramElement> optionTarget = ((AbstractDDiagramElementLabelItemProvider) element).getDiagramElementTarget(); + if (optionTarget.some() && new DDiagramElementQuery(optionTarget.get()).isLabelHidden()) { + result = JFaceResources.getFontRegistry().getItalic(JFaceResources.DEFAULT_FONT); + } + } + return result; + } +} |