Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 77c3b6e855c1a25890115cc0683992b92d39b8eb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
/*****************************************************************************
 * Copyright (c) 2013 CEA LIST.
 *
 * 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:
 *
 *		CEA LIST - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.infra.gmfdiag.common.editpolicies;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.requests.ChangeBoundsRequest;
import org.eclipse.gef.tools.ResizeTracker;
import org.eclipse.gmf.runtime.diagram.ui.editpolicies.ResizableShapeEditPolicy;
import org.eclipse.papyrus.infra.gmfdiag.common.snap.ResizeTrackerWithPreferences;

/**
 *
 * See Bug 424943 ResizableEditPolicy#getResizeCommand duplicates request ignoring some request values
 *
 */
public class PapyrusResizableShapeEditPolicy extends ResizableShapeEditPolicy {

	/**
	 * The same {@link ChangeBoundsRequest} is sent to all moved edit parts,
	 * so we can cache the info about them in request potentially improving o(N^2) performance.
	 */
	private static final String PARAM_CACHED_EDIT_PARTS_SET = PapyrusResizableShapeEditPolicy.class.getName() + ":CachedMovedEPs";

	/**
	 * Tries to find the cached instance of {@link CachedEditPartsSet} in the request extended data map.
	 * If not found, initializes the new instance and caches it in request for other edit-policy instances.
	 * 
	 * @param req
	 * @return never returns <code>null</code>
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	protected static CachedEditPartsSet getMovedEditPartsSet(ChangeBoundsRequest req) {
		Map extData = req.getExtendedData();
		CachedEditPartsSet set = (CachedEditPartsSet) extData.get(PARAM_CACHED_EDIT_PARTS_SET);
		if (set == null) {
			set = new CachedEditPartsSet(req.getEditParts());
			extData.put(PARAM_CACHED_EDIT_PARTS_SET, set);
		}
		return set;
	}

	/**
	 * This implementation overrides {@link org.eclipse.gef.editpolicies.NonResizableEditPolicy#getMoveCommand} and keeps all parent's logic.
	 * Multi-selection request identified by the presence of {@link PapyrusResizableShapeEditPolicy#PARAM_CACHED_EDIT_PARTS_SET} key
	 * for request extended data
	 * see {@link org.eclipse.papyrus.infra.gmfdiag.common.editpolicies.XYLayoutWithConstrainedResizedEditPolicy#getConnectionsToElementsNotBeingMoved}
	 * 
	 * @param request
	 *            the change bounds request
	 * @return the command contribution to the request
	 */
	@SuppressWarnings("unchecked")
	@Override
	protected Command getMoveCommand(ChangeBoundsRequest request) {
		ChangeBoundsRequest req = new ChangeBoundsRequest(REQ_MOVE_CHILDREN);
		req.setEditParts(getHost());
		req.setMoveDelta(request.getMoveDelta());
		req.setSizeDelta(request.getSizeDelta());
		req.setLocation(request.getLocation());
		Map<Object, Object> extendedData = new HashMap<Object, Object>();
		extendedData.putAll(request.getExtendedData());
		CachedEditPartsSet set = new CachedEditPartsSet(request.getEditParts());
		extendedData.put(PARAM_CACHED_EDIT_PARTS_SET, set);
		req.setExtendedData(extendedData);
		return getHost().getParent().getCommand(req);
	}

	/**
	 * See Bug 424943 ResizableEditPolicy#getResizeCommand duplicates request ignoring some request values
	 * TODO : remove this override when the bug will be fixed
	 *
	 * Returns the command contribution for the given resize request. By
	 * default, the request is re-dispatched to the host's parent as a {@link org.eclipse.gef.RequestConstants#REQ_RESIZE_CHILDREN}. The
	 * parent's edit policies determine how to perform the resize based on the
	 * layout manager in use.
	 *
	 * @param request
	 *            the resize request
	 * @return the command contribution obtained from the parent
	 */

	@Override
	protected Command getResizeCommand(ChangeBoundsRequest request) {
		ChangeBoundsRequest req = new ChangeBoundsRequest(REQ_RESIZE_CHILDREN);
		req.setCenteredResize(request.isCenteredResize());
		req.setConstrainedMove(request.isConstrainedMove());
		req.setConstrainedResize(request.isConstrainedResize());
		req.setSnapToEnabled(request.isSnapToEnabled());
		req.setEditParts(getHost());

		req.setMoveDelta(request.getMoveDelta());
		req.setSizeDelta(request.getSizeDelta());
		req.setLocation(request.getLocation());
		req.setExtendedData(request.getExtendedData());
		req.setResizeDirection(request.getResizeDirection());

		if (getHost().getParent() == null) {
			return null;
		}

		return getHost().getParent().getCommand(req);
	}

	/**
	 *
	 * @see org.eclipse.gef.editpolicies.ResizableEditPolicy#getResizeTracker(int)
	 *
	 * @param direction
	 * @return
	 */
	@Override
	protected ResizeTracker getResizeTracker(int direction) {
		return new ResizeTrackerWithPreferences((GraphicalEditPart) getHost(), direction);
	}

	protected static enum MovedNodeKind {
		DIRECTLY, INDIRECTLY, NO
	}

	protected static class CachedEditPartsSet {

		private final Set<EditPart> myDirectlyMoved;

		private final Set<EditPart> myKnownIndirectlyYes;

		private final Set<EditPart> myKnownIndirectlyNo;

		public CachedEditPartsSet(List<EditPart> directlyMoved) {
			myDirectlyMoved = new HashSet<EditPart>(directlyMoved);
			myKnownIndirectlyNo = new HashSet<EditPart>(directlyMoved.size() * 5 + 1);
			myKnownIndirectlyYes = new HashSet<EditPart>(directlyMoved.size() * 5 + 1);
		}
		
		public boolean isMovedEditPart(EditPart ep) {
			return isMoved(ep) != MovedNodeKind.NO;
		}

		public MovedNodeKind isMoved(EditPart ep) {
			List<EditPart> chainUp = new LinkedList<EditPart>();
			EditPart cur = ep;
			MovedNodeKind kind = null;
			while (cur != null) {
				kind = getKnownKind(cur);
				if (kind != null) {
					break;
				}
				chainUp.add(cur);
				cur = cur.getParent();
			}
			if (cur == null || kind == null) {
				kind = MovedNodeKind.NO;
			} else if (kind == MovedNodeKind.DIRECTLY && cur != ep) {
				kind = MovedNodeKind.INDIRECTLY;
			}

			Set<EditPart> forKind;
			switch (kind) {
			case DIRECTLY:
				forKind = myDirectlyMoved;
				break;
			case INDIRECTLY:
				forKind = myKnownIndirectlyYes;
				break;
			case NO:
				forKind = myKnownIndirectlyNo;
				break;
			default:
				throw new IllegalArgumentException("Wow: " + kind);
			}

			if (kind != MovedNodeKind.DIRECTLY) {
				forKind.addAll(chainUp);
			}

			return kind;
		}

		private MovedNodeKind getKnownKind(EditPart ep) {
			if (myDirectlyMoved.contains(ep)) {
				return MovedNodeKind.DIRECTLY;
			}
			if (myKnownIndirectlyYes.contains(ep)) {
				return MovedNodeKind.INDIRECTLY;
			}
			if (myKnownIndirectlyNo.contains(ep)) {
				return MovedNodeKind.NO;
			}
			return null;
		}

	}

}

Back to the top