View Javadoc
1   /**
2    * 
3    * Copyright (c) 2013-2014, Openflexo
4    * Copyright (c) 2011-2012, AgileBirds
5    * 
6    * This file is part of Gina-swing-editor, a component of the software infrastructure 
7    * developed at Openflexo.
8    * 
9    * 
10   * Openflexo is dual-licensed under the European Union Public License (EUPL, either 
11   * version 1.1 of the License, or any later version ), which is available at 
12   * https://joinup.ec.europa.eu/software/page/eupl/licence-eupl
13   * and the GNU General Public License (GPL, either version 3 of the License, or any 
14   * later version), which is available at http://www.gnu.org/licenses/gpl.html .
15   * 
16   * You can redistribute it and/or modify under the terms of either of these licenses
17   * 
18   * If you choose to redistribute it and/or modify under the terms of the GNU GPL, you
19   * must include the following additional permission.
20   *
21   *          Additional permission under GNU GPL version 3 section 7
22   *
23   *          If you modify this Program, or any covered work, by linking or 
24   *          combining it with software containing parts covered by the terms 
25   *          of EPL 1.0, the licensors of this Program grant you additional permission
26   *          to convey the resulting work. * 
27   * 
28   * This software is distributed in the hope that it will be useful, but WITHOUT ANY 
29   * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 
30   * PARTICULAR PURPOSE. 
31   *
32   * See http://www.openflexo.org/license.html for details.
33   * 
34   * 
35   * Please contact Openflexo (openflexo-contacts@openflexo.org)
36   * or visit www.openflexo.org if you need additional information.
37   * 
38   */
39  
40  package org.openflexo.gina.swing.editor.palette;
41  
42  import java.awt.Component;
43  import java.awt.Container;
44  import java.awt.Dimension;
45  import java.awt.Point;
46  import java.awt.dnd.DnDConstants;
47  import java.awt.dnd.DragGestureEvent;
48  import java.awt.dnd.DragGestureListener;
49  import java.awt.dnd.DragGestureRecognizer;
50  import java.awt.dnd.DragSource;
51  import java.awt.dnd.DragSourceContext;
52  import java.awt.dnd.DragSourceDragEvent;
53  import java.awt.dnd.DragSourceDropEvent;
54  import java.awt.dnd.DragSourceEvent;
55  import java.awt.dnd.DragSourceListener;
56  import java.awt.event.MouseListener;
57  import java.util.Hashtable;
58  import java.util.logging.Level;
59  import java.util.logging.Logger;
60  
61  import javax.swing.JComponent;
62  import javax.swing.JOptionPane;
63  import javax.swing.SwingUtilities;
64  
65  import org.openflexo.gina.controller.FIBController;
66  import org.openflexo.gina.model.FIBComponent;
67  import org.openflexo.gina.model.FIBContainer;
68  import org.openflexo.gina.model.FIBOperator;
69  import org.openflexo.gina.model.container.FIBPanel;
70  import org.openflexo.gina.model.container.FIBPanel.Layout;
71  import org.openflexo.gina.model.container.FIBTab;
72  import org.openflexo.gina.model.container.FIBTabPanel;
73  import org.openflexo.gina.model.container.layout.ComponentConstraints;
74  import org.openflexo.gina.swing.editor.FIBEditor;
75  import org.openflexo.gina.swing.editor.view.FIBSwingEditableContainerView;
76  import org.openflexo.gina.swing.editor.view.FIBSwingEditableViewDelegate.FIBDropTarget;
77  import org.openflexo.gina.swing.editor.view.OperatorDecorator;
78  import org.openflexo.gina.swing.editor.view.PlaceHolder;
79  import org.openflexo.gina.swing.view.JFIBView;
80  import org.openflexo.gina.swing.view.SwingViewFactory;
81  import org.openflexo.gina.view.FIBContainerView;
82  import org.openflexo.gina.view.FIBOperatorView;
83  import org.openflexo.gina.view.FIBView;
84  import org.openflexo.logging.FlexoLogger;
85  
86  public class PaletteElement implements FIBDraggable /* implements Transferable */ {
87  	static final Logger logger = FlexoLogger.getLogger(PaletteElement.class.getPackage().getName());
88  
89  	private final FIBEditorPalette palette;
90  	private final FIBComponent modelComponent;
91  	private final FIBComponent representationComponent;
92  	private final FIBView<FIBComponent, ? extends JComponent> view;
93  
94  	private final DragSource dragSource;
95  	private final DragGestureListener dgListener;
96  	private final DragSourceListener dsListener;
97  	private final int dragAction = DnDConstants.ACTION_COPY;
98  	private final Hashtable<JComponent, DragGestureRecognizer> dgr;
99  
100 	// private static final DataFlavor DATA_FLAVOR = new
101 	// DataFlavor(PaletteElement.class, "PaletteElement");
102 
103 	public PaletteElement(FIBComponent modelComponent, FIBComponent representationComponent, FIBEditorPalette palette, Object dataObject) {
104 		this.modelComponent = modelComponent;
105 		this.representationComponent = representationComponent;
106 		this.palette = palette;
107 
108 		int x = Integer.parseInt(representationComponent.getParameter("x"));
109 		int y = Integer.parseInt(representationComponent.getParameter("y"));
110 
111 		view = FIBController.makeView(representationComponent, SwingViewFactory.INSTANCE, FIBEditor.EDITOR_LOCALIZATION, null, true);
112 		view.getController().setVariableValue("paletteData", dataObject);
113 
114 		if (view.getTechnologyComponent() != null) {
115 			Dimension size = view.getTechnologyComponent().getPreferredSize();
116 			view.getTechnologyComponent().setBounds(x, y, size.width, size.height);
117 		}
118 
119 		this.dgListener = new DGListener();
120 		this.dragSource = DragSource.getDefaultDragSource();
121 		this.dsListener = new DSListener();
122 
123 		dgr = new Hashtable<>();
124 
125 		if (view.getTechnologyComponent() != null) {
126 			recursivelyRemoveFocusableProperty(view.getTechnologyComponent());
127 			recursivelyAddDGR(view.getTechnologyComponent());
128 		}
129 
130 		enableDragging();
131 
132 	}
133 
134 	private void recursivelyRemoveFocusableProperty(JComponent c) {
135 		c.setRequestFocusEnabled(false);
136 		c.setFocusable(false);
137 
138 		if (c instanceof Container) {
139 			for (Component c2 : ((Container) c).getComponents()) {
140 				if (c2 instanceof JComponent) {
141 					recursivelyRemoveFocusableProperty((JComponent) c2);
142 				}
143 			}
144 		}
145 	}
146 
147 	private void recursivelyAddDGR(JComponent c) {
148 		for (MouseListener ml : c.getMouseListeners()) {
149 			c.removeMouseListener(ml);
150 		}
151 		DragGestureRecognizer newDGR = dragSource.createDefaultDragGestureRecognizer(c, dragAction, dgListener);
152 		dgr.put(c, newDGR);
153 
154 		if (c instanceof Container) {
155 			for (Component c2 : ((Container) c).getComponents()) {
156 				if (c2 instanceof JComponent) {
157 					recursivelyAddDGR((JComponent) c2);
158 				}
159 			}
160 		}
161 	}
162 
163 	public FIBView<FIBComponent, ? extends JComponent> getView() {
164 		return view;
165 	}
166 
167 	@Override
168 	final public void enableDragging() {
169 		for (JComponent j : dgr.keySet()) {
170 			dgr.get(j).setComponent(j);
171 		}
172 	}
173 
174 	@Override
175 	public void disableDragging() {
176 		for (JComponent j : dgr.keySet()) {
177 			dgr.get(j).setComponent(null);
178 		}
179 	}
180 
181 	@Override
182 	public boolean acceptDragging(FIBDropTarget target) {
183 		// logger.fine("acceptDragging ? for component: " +
184 		// target.getFIBComponent() + " place holder: " +
185 		// target.getPlaceHolder());
186 		return true;
187 	}
188 
189 	/*
190 	 * @Override public DataFlavor[] getTransferDataFlavors() { return new
191 	 * DataFlavor[] { DATA_FLAVOR }; }
192 	 * 
193 	 * @Override public boolean isDataFlavorSupported(DataFlavor flavor) {
194 	 * return true; }
195 	 * 
196 	 * @Override public Object getTransferData(DataFlavor flavor) throws
197 	 * UnsupportedFlavorException, IOException { return this; }
198 	 */
199 
200 	@Override
201 	public boolean elementDragged(FIBDropTarget target, DropListener dropListener, Point pt) {
202 
203 		// System.out.println("elementDragged(), dl=" + dropListener.getEditableView());
204 		// System.out.println("target=" + target);
205 
206 		PlaceHolder ph = target.getPlaceHolder(dropListener, pt);
207 
208 		/*System.out.println("ph=" + ph);
209 		System.out.println("modelComponent=" + modelComponent);
210 		System.out.println("target.getFIBComponent()=" + target.getFIBComponent());*/
211 
212 		FIBOperator targetOperator = null;
213 		FIBOperatorView<?, ?, ?> operatorView = null;
214 
215 		boolean isTabInsertion = modelComponent instanceof FIBPanel && target.getFIBComponent() instanceof FIBTabPanel;
216 		boolean componentInsertionInOperator = target.getFIBComponent() instanceof FIBOperator;
217 		if (componentInsertionInOperator) {
218 			targetOperator = (FIBOperator) target.getFIBComponent();
219 			operatorView = (FIBOperatorView<?, ?, ?>) target.getEditableView();
220 		}
221 
222 		// System.out.println("editable view = " + target.getEditableView());
223 		// System.out.println("delegate = " + target.getEditableView().getDelegate());
224 		// System.out.println("les operators = " + target.getEditableView().getDelegate());
225 
226 		if (!isTabInsertion && !componentInsertionInOperator && ph == null) {
227 			// May be this is is an operator area ???
228 
229 			// System.out.println("target.getEditableView().getParentView()=" + target.getEditableView().getParentView());
230 			FIBContainerView<?, ?, ?> parentView = target.getEditableView().getParentView();
231 			if (parentView instanceof FIBOperatorView) {
232 				operatorView = (FIBOperatorView<?, ?, ?>) parentView;
233 				parentView = operatorView.getConcreteContainerView();
234 			}
235 			if (parentView instanceof FIBSwingEditableContainerView) {
236 				Point ptInParent = SwingUtilities.convertPoint(target.getEditableView().getResultingJComponent(), pt,
237 						((JFIBView) parentView).getResultingJComponent());
238 				// Point ptInParent = new Point(pt.x + target.getEditableView().getResultingJComponent().getBounds().x,
239 				// pt.y + target.getEditableView().getResultingJComponent().getBounds().y);
240 				OperatorDecorator d = ((FIBSwingEditableContainerView<?, ?>) parentView).getDelegate().getOperatorDecorator(ptInParent);
241 				if (d != null) {
242 					componentInsertionInOperator = true;
243 					targetOperator = d.getOperator();
244 					operatorView = d.getOperatorView();
245 				}
246 			}
247 			/*boolean deleteIt = JOptionPane.showConfirmDialog(target.getFrame(),
248 					target.getFIBComponent() + ": really delete this component (undoable operation) ?", "information",
249 					JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.YES_OPTION;
250 			if (!deleteIt) {
251 				return false;
252 			}*/
253 		}
254 
255 		FIBComponent newComponent = (FIBComponent) modelComponent.cloneObject();
256 
257 		if (newComponent instanceof FIBPanel) {
258 			newComponent.setTemporarySize(representationComponent.getWidth(), representationComponent.getHeight());
259 		}
260 
261 		newComponent.setLocalizedDictionary(null);
262 		newComponent.clearParameters();
263 
264 		/*
265 		 * logger.info("Element dragged with component: " + newComponent +
266 		 * " place holder: " + ph);
267 		 * System.out.println(newComponent.getFactory().
268 		 * stringRepresentation(newComponent)); Thread.dumpStack();
269 		 */
270 
271 		try {
272 			FIBComponent targetComponent = target.getFIBComponent();
273 			FIBContainer containerComponent = targetComponent.getParent();
274 
275 			if (componentInsertionInOperator) {
276 				// Handle component insertion in operator
277 				System.out.println("Insert " + newComponent + " in operator " + targetOperator);
278 				ComponentConstraints constraints = operatorView.getConcreteContainerView().getLayoutManager().makeDefaultConstraints();
279 				// System.out.println("using constraints=" + targetOperator.getConstraints());
280 				targetOperator.addToSubComponents(newComponent, constraints);
281 				newComponent.translateNameWhenRequired();
282 				return true;
283 			}
284 			else if (isTabInsertion) {
285 				// Handle tab insertion
286 				if (targetComponent instanceof FIBTab && !(newComponent instanceof FIBPanel)) {
287 					return false;
288 				}
289 				if (containerComponent == null) {
290 					return false;
291 				}
292 				FIBTab newTabComponent = containerComponent.getModelFactory().newFIBTab();
293 				newTabComponent.setLayout(Layout.border);
294 				newTabComponent.setTitle("NewTab");
295 				newTabComponent.finalizeDeserialization();
296 				((FIBTabPanel) targetComponent).addToSubComponents(newTabComponent);
297 				newTabComponent.translateNameWhenRequired();
298 				return true;
299 			}
300 			else if (ph != null) {
301 				// Component inserted in placeholder
302 				System.out.println("Insert in placeholder " + ph);
303 				ph.willDelete();
304 				ph.insertComponent(newComponent, -1);
305 				newComponent.translateNameWhenRequired();
306 				ph.hasDeleted();
307 				return true;
308 			}
309 			else {
310 				// Normal case, we replace targetComponent by newComponent
311 				if (containerComponent == null) {
312 					return false;
313 				}
314 				boolean deleteIt = JOptionPane.showConfirmDialog(target.getFrame(),
315 						target.getFIBComponent() + ": really delete this component (undoable operation) ?", "information",
316 						JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.INFORMATION_MESSAGE) == JOptionPane.YES_OPTION;
317 				if (!deleteIt) {
318 					return false;
319 				}
320 				ComponentConstraints constraints = targetComponent.getConstraints();
321 				containerComponent.removeFromSubComponents(targetComponent);
322 				// WAS:
323 				// containerComponent.removeFromSubComponentsNoNotification(targetComponent);
324 				// WAS: No notification, we will do it later, to avoid
325 				// reindexing
326 				targetComponent.delete();
327 				containerComponent.addToSubComponents(newComponent, constraints);
328 				newComponent.translateNameWhenRequired();
329 				return true;
330 			}
331 		} finally {
332 			dropListener.getEditableView().getEditorController().setSelectedObject(newComponent);
333 		}
334 
335 	}
336 
337 	/**
338 	 * DGListener a listener that will start the drag. has access to top level's dsListener and dragSource
339 	 * 
340 	 * @see java.awt.dnd.DragGestureListener
341 	 * @see java.awt.dnd.DragSource
342 	 * @see java.awt.datatransfer.StringSelection
343 	 */
344 	class DGListener implements DragGestureListener {
345 		/**
346 		 * Start the drag if the operation is ok. uses java.awt.datatransfer.StringSelection to transfer the label's data
347 		 * 
348 		 * @param e
349 		 *            the event object
350 		 */
351 		@Override
352 		public void dragGestureRecognized(DragGestureEvent e) {
353 			logger.fine("dragGestureRecognized");
354 
355 			// if the action is ok we go ahead
356 			// otherwise we punt
357 			if ((e.getDragAction() & dragAction) == 0) {
358 				return;
359 				// get the label's text and put it inside a Transferable
360 				// Transferable transferable = new StringSelection(
361 				// DragLabel.this.getText() );
362 			}
363 
364 			PaletteElementDrag transferable = new PaletteElementDrag(PaletteElement.this, e.getDragOrigin());
365 
366 			try {
367 				// initial cursor, transferrable, dsource listener
368 				e.startDrag(FIBEditorPalette.dropKO, transferable, dsListener);
369 
370 				FIBEditorPalettes.logger.info("Starting drag for " + palette);
371 				// getDrawingView().captureDraggedNode(PaletteElementView.this,
372 				// e);
373 			} catch (Exception idoe) {
374 				idoe.printStackTrace();
375 				FIBEditorPalettes.logger.warning("Unexpected exception " + idoe);
376 			}
377 		}
378 
379 	}
380 
381 	/**
382 	 * DSListener a listener that will track the state of the DnD operation
383 	 * 
384 	 * @see java.awt.dnd.DragSourceListener
385 	 * @see java.awt.dnd.DragSource
386 	 * @see java.awt.datatransfer.StringSelection
387 	 */
388 	public class DSListener implements DragSourceListener {
389 
390 		/**
391 		 * @param e
392 		 *            the event
393 		 */
394 		@Override
395 		public void dragDropEnd(DragSourceDropEvent e) {
396 
397 			// getDrawingView().resetCapturedNode();
398 			if (e.getDropSuccess() == false) {
399 				if (FIBEditorPalettes.logger.isLoggable(Level.INFO)) {
400 					FIBEditorPalettes.logger.info("Dropping was not successful");
401 				}
402 				return;
403 			}
404 			/*
405 			 * the dropAction should be what the drop target specified in
406 			 * acceptDrop
407 			 */
408 			// this is the action selected by the drop target
409 			if (e.getDropAction() == DnDConstants.ACTION_MOVE) {
410 				System.out.println("Coucou, que se passe-t-il par ici ?");
411 			}
412 
413 		}
414 
415 		/**
416 		 * @param e
417 		 *            the event
418 		 */
419 		@Override
420 		public void dragEnter(DragSourceDragEvent e) {
421 			DragSourceContext context = e.getDragSourceContext();
422 			// System.out.println("dragEnter() with "+context+" component="+e.getSource());
423 			// intersection of the users selected action, and the source and
424 			// target actions
425 			int myaction = e.getDropAction();
426 			if ((myaction & dragAction) != 0) {
427 				context.setCursor(DragSource.DefaultCopyDrop);
428 			}
429 			else {
430 				context.setCursor(DragSource.DefaultCopyNoDrop);
431 			}
432 		}
433 
434 		/**
435 		 * @param e
436 		 *            the event
437 		 */
438 		@Override
439 		public void dragOver(DragSourceDragEvent e) {
440 			// interface
441 			// System.out.println("dragOver() with "+context+" component="+e.getSource());
442 		}
443 
444 		/**
445 		 * @param e
446 		 *            the event
447 		 */
448 		@Override
449 		public void dragExit(DragSourceEvent e) {
450 			// System.out.println("dragExit() with "+context+" component="+e.getSource());
451 			// interface
452 		}
453 
454 		/**
455 		 * for example, press shift during drag to change to a link action
456 		 * 
457 		 * @param e
458 		 *            the event
459 		 */
460 		@Override
461 		public void dropActionChanged(DragSourceDragEvent e) {
462 
463 			DragSourceContext context = e.getDragSourceContext();
464 			context.setCursor(DragSource.DefaultCopyNoDrop);
465 		}
466 	}
467 
468 }