1 /**********************************************
2 * Copyright (C) 2011 Lukas Laag
3 * This file is part of svgreal.
4 *
5 * svgreal is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * svgreal is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with svgreal. If not, see http://www.gnu.org/licenses/
17 **********************************************/
18 package org.vectomatic.svg.edit.client.command;
19
20 import java.util.List;
21
22 import org.vectomatic.svg.edit.client.command.dnd.DndCopyHandler;
23 import org.vectomatic.svg.edit.client.command.dnd.DndLinkHandler;
24 import org.vectomatic.svg.edit.client.command.dnd.DndMoveHandler;
25 import org.vectomatic.svg.edit.client.command.dnd.IDndHandler;
26 import org.vectomatic.svg.edit.client.event.KeyPressProcessor;
27 import org.vectomatic.svg.edit.client.event.KeyUpProcessor;
28 import org.vectomatic.svg.edit.client.gxt.widget.DNDGhost;
29 import org.vectomatic.svg.edit.client.model.ModelConstants;
30 import org.vectomatic.svg.edit.client.model.svg.SVGElementModel;
31
32 import com.extjs.gxt.ui.client.dnd.StatusProxy;
33 import com.extjs.gxt.ui.client.event.ComponentEvent;
34 import com.extjs.gxt.ui.client.event.DNDEvent;
35 import com.google.gwt.core.client.GWT;
36 import com.google.gwt.dom.client.Style;
37 import com.google.gwt.dom.client.Style.Position;
38 import com.google.gwt.dom.client.Style.Unit;
39
40 /**
41 * Base class for drag and drop command factories
42 * @author laaglu
43 */
44 public class DndCommandFactory extends CommandFactoryBase implements KeyPressProcessor, KeyUpProcessor {
45 /**
46 * Enum to represent the drop gestures
47 */
48 public enum DropGesture {
49 /**
50 * The source is released precisely on the target
51 */
52 OnNode,
53 /**
54 * The source is released slightly before the target
55 */
56 BeforeNode,
57 /**
58 * The source is released slightly after the target
59 */
60 AfterNode
61 }
62 /**
63 * Default handler for drag and drop events
64 */
65 private static IDndHandler defaultHandler = new DndMoveHandler();
66 /**
67 * Handlers for drag and drop events
68 */
69 private static IDndHandler[] handlers = new IDndHandler[] { new DndCopyHandler(), new DndLinkHandler(), defaultHandler};
70 /**
71 * The handler which currently processes the drag interactions
72 */
73 private IDndHandler currentHandler;
74 /**
75 * The drag and drop proxy
76 */
77 StatusProxy proxy;
78 /**
79 * To graphically represent the drag operation
80 */
81 private DNDGhost dndGhost;
82 /**
83 * The drag source elements
84 */
85 private List<SVGElementModel> sourceElements;
86 /**
87 * The drop target over which the user has last move the mouse
88 */
89 private SVGElementModel targetElement;
90 /**
91 * True if the current drop target is valid
92 */
93 boolean validTarget;
94
95
96 @SuppressWarnings("serial")
97 public static final IFactoryInstantiator<DndCommandFactory> INSTANTIATOR = new FactoryInstantiatorBase<DndCommandFactory>(ModelConstants.INSTANCE.dndCmdFactory(), ModelConstants.INSTANCE.dndCmdFactoryDesc()) {
98 @Override
99 public DndCommandFactory create() {
100 return new DndCommandFactory();
101 }
102 };
103 @Override
104 public IFactoryInstantiator<?> getInstantiator() {
105 return INSTANTIATOR;
106 }
107
108 @Override
109 public void start(Object requester) {
110 GWT.log("DndCommandFactory.start(" + requester + ")");
111 super.start(requester);
112 updateStatus(ModelConstants.INSTANCE.dndCmdFactory1());
113 }
114
115 /**
116 * Returns true if the drag start event comes from a valid drag source
117 * @param event a drag start event
118 * @return true if the drag start event comes from a valid drag source
119 */
120 public boolean isValidSource(DNDEvent event, List<SVGElementModel> sourceElements) {
121 targetElement = null;
122 validTarget = false;
123
124 this.sourceElements = sourceElements;
125 for (IDndHandler handler : handlers) {
126 if (handler.isValidSource(event, sourceElements)) {
127 dndGhost = new DNDGhost(sourceElements, event);
128 proxy = event.getStatus();
129 proxy.update(dndGhost.getElement());
130 targetElement = null;
131 setHandler(handler);
132
133 /*
134 * The drag and drop elements in GXT are positionned as follows:
135 * div container (x-dd-drag-proxy)
136 * div icon (x-dd-drop-icon)
137 * div proxy (x-dd-drag-ghost)
138 * div ghost (custom ghost)
139 *
140 * x-dd-drag-ghost needs to be altered to be made absolute
141 * in order to support custom ghost with absolute positioning
142 */
143 Style proxyStyle = dndGhost.getElement().getParentElement().getStyle();
144 proxyStyle.setPosition(Position.ABSOLUTE);
145 proxyStyle.setLeft(0, Unit.PX);
146 proxyStyle.setRight(0, Unit.PX);
147 proxyStyle.setTop(0, Unit.PX);
148 proxyStyle.setBottom(0, Unit.PX);
149
150 return true;
151 }
152 }
153 return false;
154 }
155
156 public boolean isValidDropTarget(SVGElementModel element) {
157 if (targetElement != element) {
158 targetElement = element;
159 validTarget = currentHandler.isValidTarget(sourceElements, targetElement);
160 GWT.log("isValidDropTarget(" + targetElement + ") = " + validTarget);
161 }
162 return validTarget;
163 }
164
165 public void processDragAndDrop(DropGesture dropGesture) {
166 GWT.log("processDragAndDrop(" + sourceElements + ", " + targetElement + "," + dropGesture + ") = " + validTarget);
167 // TODO
168 // ignore useless drag and drops:
169 // drag last item of a folder in its parent
170 // drag an item before of after itself
171 currentHandler.createCommands(sourceElements, targetElement, dropGesture);
172 currentHandler = null;
173 }
174
175 @Override
176 public boolean processKeyPress(ComponentEvent event) {
177 for (IDndHandler handler : handlers) {
178 if (handler.getKeyCode() == event.getKeyCode()) {
179 setHandler(handler);
180 return true;
181 }
182 }
183 return false;
184 }
185
186 @Override
187 public boolean processKeyUp(ComponentEvent event) {
188 for (IDndHandler handler : handlers) {
189 if (handler.getKeyCode() == event.getKeyCode()) {
190 setHandler(defaultHandler);
191 return true;
192 }
193 }
194 return false;
195 }
196
197 private void setHandler(IDndHandler handler) {
198 if (handler != currentHandler) {
199 currentHandler = handler;
200 dndGhost.update(currentHandler);
201 proxy.setStatus(currentHandler.isValidTarget(sourceElements, targetElement));
202 }
203 }
204
205 // /**
206 // * Extracts the SVG models from a drag and drop event
207 // * @param event the event
208 // * @return the SVG models associated to the event
209 // */
210 // public static List<SVGElementModel> getData(DNDEvent event) {
211 // List<TreeStoreModel> storeModels = event.getData();
212 // ArrayList<SVGElementModel> svgModels = new ArrayList<SVGElementModel>();
213 // for (TreeStoreModel storeModel : storeModels) {
214 // svgModels.add((SVGElementModel) storeModel.getModel());
215 // }
216 // return svgModels;
217 // }
218 //
219 //
220 // static private List<String> eventLog;
221 // static public void logDNDEvent(String origin, DNDEvent event) {
222 // List<String> strList = new ArrayList<String>();
223 // strList.add("=================== DNDListener =====================");
224 // strList.add("origin = " + origin);
225 // EventType type = event.getType();
226 // if (type == Events.DragStart) {
227 // strList.add("type = DragStart");
228 // } else if (type == Events.DragEnter) {
229 // strList.add("type = DragEnter");
230 // } else if (type == Events.DragLeave) {
231 // strList.add("type = DragLeave");
232 // } else if (type == Events.DragMove) {
233 // strList.add("type = DragMove");
234 // } else if (type == Events.Drop) {
235 // strList.add("type = Drop");
236 // }
237 // strList.add("component = " + event.getComponent());
238 // strList.add("data = " + DndHandlerBase.getSourceElementNames(getData(event)));
239 // strList.add("operation = " + event.getOperation());
240 // strList.add("source = " + event.getSource());
241 // DragSource dragSource = event.getDragSource();
242 // if (dragSource instanceof SVGTreePanelDragSource) {
243 // SVGTreePanelDragSource source = (SVGTreePanelDragSource)dragSource;
244 // strList.add("sourceModel = " + source.getSvgModel());
245 // }
246 // DropTarget dropTarget = event.getDropTarget();
247 // if (dropTarget instanceof SVGTreePanelDropTarget) {
248 // SVGTreePanelDropTarget target = (SVGTreePanelDropTarget)dropTarget;
249 // strList.add("targetModel = " + target.getSvgModel());
250 // SVGElementModel activeItem = target.getActiveItem();
251 // if (activeItem != null) {
252 // strList.add("activeItem = " + activeItem.get(SVGConstants.SVG_TITLE_TAG));
253 // }
254 // SVGElementModel appendItem = target.getActiveItem();
255 // if (appendItem != null) {
256 // strList.add("appendItem = " + appendItem.get(SVGConstants.SVG_TITLE_TAG));
257 // }
258 // }
259 // if (!strList.equals(eventLog)) {
260 // eventLog = strList;
261 // for(String str : eventLog) {
262 // GWT.log(str);
263 // }
264 // }
265 // }
266 }