1 package sk.uniba.euromath.editor.xmlEditor.tools;
2
3 import java.util.Collections;
4
5 import org.eclipse.core.runtime.IStatus;
6 import org.eclipse.gef.DragTracker;
7 import org.eclipse.gef.EditPart;
8 import org.eclipse.gef.EditPartViewer;
9 import org.eclipse.gef.EditPartViewer.Conditional;
10 import org.eclipse.gef.tools.TargetingTool;
11 import org.eclipse.swt.events.KeyEvent;
12 import org.w3c.dom.DOMException;
13 import org.w3c.dom.Node;
14
15 import sk.baka.ikslibs.interval.DOMInterval;
16 import sk.baka.ikslibs.interval.DOMIntervalSet;
17 import sk.baka.ikslibs.ptr.DomPointer;
18 import sk.baka.ikslibs.ptr.DomPointerFactory;
19 import sk.uniba.euromath.EuroMath;
20 import sk.uniba.euromath.document.XMLAccess;
21 import sk.uniba.euromath.editor.xmlEditor.IXMLEditPart;
22 import sk.uniba.euromath.editor.xmlEditor.XMLEditDomain;
23 import sk.uniba.euromath.editor.xmlEditor.viewers.IXMLGraphicalViewer;
24
25 /***
26 * Drag tracker handling drags interpreting them as selections. Local selection
27 * is created translating start-point end-point to DOMPointers making
28 * DOMInterval. Depending on input is local selection combined with viewer's
29 * selection. <br>
30 * Start(end) point translation is always to DOMPinters pointing between nodes,
31 * not into.
32 *
33 * @author Martin Kollar 23.09.2005
34 */
35 public class XMLStructureDragTracker extends TargetingTool implements
36 DragTracker {
37
38 /***
39 * Holds selection of graphical viewer displayed before this drag
40 * started.
41 */
42 private DOMIntervalSet globalSelectionPriorDrag;
43
44 /***
45 * Source editpart;
46 */
47 private IXMLEditPart sourceEditPart;
48
49 /***
50 * Constructs new XMLDragTracker with the given XMLEditPart
51 *
52 * @param sourceEditPart
53 * source editPart where selection starts
54 */
55 public XMLStructureDragTracker(IXMLEditPart sourceEditPart) {
56 super();
57 this.sourceEditPart = sourceEditPart;
58 }
59
60 /***
61 * Retreives selection from viewer, before drag started.
62 */
63 @Override
64 public void setViewer(EditPartViewer viewer) {
65 super.setViewer(viewer);
66 if (this.globalSelectionPriorDrag == null) {
67 this.globalSelectionPriorDrag = getXMLGraphicalViewer()
68 .getDOMSelection();
69 }
70 }
71
72 @Override
73 protected boolean handleDragStarted() {
74 return stateTransition(STATE_DRAG, STATE_DRAG_IN_PROGRESS);
75 }
76
77 @Override
78 protected boolean handleDrag() {
79 updateTargetUnderMouse();
80 return true;
81 }
82
83 @Override
84 protected boolean handleDragInProgress() {
85 if (isInState(STATE_DRAG_IN_PROGRESS
86
87 }
88 return true;
89 }
90
91 @Override
92 protected boolean handleExitingEditPart() {
93 getXMLGraphicalViewer().setSelection(computeGlobalSelection());
94 return true;
95 }
96
97 @Override
98 protected boolean handleEnteredEditPart() {
99 getXMLGraphicalViewer().setSelection(computeGlobalSelection());
100 return true;
101 }
102
103 @Override
104 protected boolean handleButtonDown(int button) {
105 updateTargetUnderMouse();
106
107 if (button != 1) {
108
109 setState(STATE_INVALID);
110 return false;
111 }
112
113 stateTransition(STATE_INITIAL, STATE_DRAG);
114 return true;
115 }
116
117 /***
118 * Computes local selection corresponding to drag.
119 *
120 * @return selection made by this drag
121 */
122 protected DOMIntervalSet computeLocalSelection() {
123 if (!(getSourceEditPart() instanceof IXMLEditPart)
124 || (((IXMLEditPart) getSourceEditPart())
125 .getID() == null)) {
126 return new DOMIntervalSet();
127 }
128 if (!(getTargetEditPart() instanceof IXMLEditPart)
129 || (((IXMLEditPart) getTargetEditPart())
130 .getID() == null)) {
131 return new DOMIntervalSet();
132 }
133
134 Node node1 = null;
135 Node node2 = null;
136 try {
137 node1 = getXMLAccess().getIDManager().getNode(
138 ((IXMLEditPart) getSourceEditPart())
139 .getID()).item(0);
140 node2 = getXMLAccess().getIDManager().getNode(
141 ((IXMLEditPart) getTargetEditPart())
142 .getID()).item(0);
143 } catch (DOMException e) {
144 EuroMath.log(IStatus.ERROR, 0,
145 "Invalid state of Euromath.", e);
146 return new DOMIntervalSet();
147 }
148
149
150 int nodeCompare = node1.compareDocumentPosition(node2);
151
152 if ((nodeCompare & (Node.DOCUMENT_POSITION_CONTAINS | Node.DOCUMENT_POSITION_CONTAINED_BY)) != 0) {
153
154
155
156 DomPointer startPointer = DomPointerFactory
157 .create(node2);
158
159 DomPointer endPointer = DomPointerFactory.create(node2
160 .getParentNode(), node2
161 .getNextSibling());
162 return new DOMIntervalSet(new DOMInterval(startPointer,
163 endPointer));
164 }
165
166 if (node1.equals(node2)
167 || ((nodeCompare & Node.DOCUMENT_POSITION_FOLLOWING) != 0)) {
168
169
170
171 DomPointer startPointer = DomPointerFactory
172 .create(node1);
173
174 DomPointer endPointer = DomPointerFactory.create(node2
175 .getParentNode(), node2
176 .getNextSibling());
177 return new DOMIntervalSet(new DOMInterval(startPointer,
178 endPointer));
179 }
180
181 if ((nodeCompare & Node.DOCUMENT_POSITION_PRECEDING) != 0) {
182
183
184 DomPointer startPointer = DomPointerFactory
185 .create(node2);
186
187 DomPointer endPointer = DomPointerFactory.create(node1
188 .getParentNode(), node1
189 .getNextSibling());
190 return new DOMIntervalSet(new DOMInterval(startPointer,
191 endPointer));
192 }
193
194 throw new IllegalStateException();
195 }
196
197 /***
198 * If in the drag state, the tool selects the source edit part. If the
199 * edit part was already selected, {@link #performDirectEdit()} is
200 * called. If the edit part is newly selected and not completely
201 * visible, {@link EditPartViewer#reveal(EditPart)} is called to show
202 * the selected edit part.
203 *
204 * @see org.eclipse.gef.tools.AbstractTool#handleButtonUp(int)
205 */
206 protected boolean handleButtonUp(int button) {
207 if (isInState(STATE_DRAG | STATE_DRAG_IN_PROGRESS)) {
208 finishDrag();
209 setState(STATE_TERMINAL);
210 return true;
211 }
212
213 setState(STATE_TERMINAL);
214 return true;
215 }
216
217 /***
218 * Computes actual selection to set to viewer. Pressed ctrl and shift
219 * keys influence how selection is computed. The user needs 3 styles of
220 * selecting: simple interval (nothing pressed), union interval with
221 * current selection(shift pressed) and xor interval with current
222 * selection(ctrl pressed).
223 *
224 * @return global selection uctual to current state of drag, returned by
225 * copy
226 */
227 protected DOMIntervalSet computeGlobalSelection() {
228 DOMIntervalSet localSelection = computeLocalSelection();
229
230
231 if ((!getCurrentInput().isControlKeyDown())
232 && (!getCurrentInput().isShiftKeyDown())) {
233 return localSelection;
234 }
235
236 if (getCurrentInput().isControlKeyDown()
237 && !getCurrentInput().isShiftKeyDown()) {
238
239 return localSelection
240 .invert(this.globalSelectionPriorDrag);
241 }
242
243 if (getCurrentInput().isShiftKeyDown()
244 && !getCurrentInput().isControlKeyDown()) {
245
246 DOMIntervalSet result = new DOMIntervalSet(
247 localSelection);
248 result.union(this.globalSelectionPriorDrag, null);
249 return result;
250 }
251
252 return this.globalSelectionPriorDrag;
253 }
254
255 protected XMLAccess getXMLAccess() {
256 return ((XMLEditDomain) getDomain()).getXMLEditor()
257 .getXMLAccess();
258 }
259
260 protected IXMLGraphicalViewer getXMLGraphicalViewer() {
261 return (IXMLGraphicalViewer) getCurrentViewer();
262 }
263
264 @Override
265 protected String getCommandName() {
266 return "XML Structure Drag Tracker.";
267 }
268
269 protected EditPart getSourceEditPart() {
270 return this.sourceEditPart;
271 }
272
273 /***
274 * Simpler implementation than super, as target is directly taken
275 * editpart at mouse cursor location, which must be XMLEditPart with not
276 * null element id. In case when drag started at location where editpart
277 * hasn't id (source edit part hasn't id), source editpart is set to
278 * target, if not yet.
279 */
280 protected boolean updateTargetUnderMouse() {
281 if (!isTargetLocked()) {
282 EditPart editPart = getCurrentViewer()
283 .findObjectAtExcluding(getLocation(),
284 Collections.EMPTY_SET,
285 new Conditional() {
286 public boolean evaluate(
287 EditPart editpart) {
288 return XMLStructureDragTracker.this
289 .evaluate(editpart);
290 }
291 });
292 boolean changed = getTargetEditPart() != editPart;
293 setTargetEditPart(editPart);
294
295 if (!evaluate(getSourceEditPart()))
296 this.sourceEditPart = (IXMLEditPart)editPart;
297 return changed;
298 }
299 return false;
300 }
301
302 /***
303 * Test editpart, if is acceptable as end point of selection under
304 * mouse.
305 *
306 * @param editpart
307 * to test for potential target - end point of selection
308 * @return true if is suitable for end point of selection
309 */
310 protected boolean evaluate(EditPart editpart) {
311 return (editpart instanceof IXMLEditPart)
312 && (((IXMLEditPart) editpart).getID() != null)
313 && (((IXMLEditPart) editpart).getID().indexOf(
314 ';') < 0);
315 }
316
317 /***
318 * Is called at termination of drag to perfom and finish selection and
319 * drag related tasks.
320 */
321 protected void finishDrag() {
322 getXMLGraphicalViewer().setSelection(computeGlobalSelection());
323 }
324
325 @Override
326 protected boolean handleKeyDown(KeyEvent e) {
327 finishDrag();
328 setState(STATE_TERMINAL);
329 return true;
330 }
331
332 @Override
333 protected boolean handleKeyUp(KeyEvent e) {
334 finishDrag();
335 setState(STATE_TERMINAL);
336 return true;
337 }
338 }