8721196bf59dd2943ff2b73ff942bd25828b5a6c
[scilab.git] / scilab / modules / xcos / src / java / org / scilab / modules / xcos / palette / PaletteBlockCtrl.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2009 - DIGITEO - Clement DAVID
4  *
5  * This file must be used under the terms of the CeCILL.
6  * This source file is licensed as described in the file COPYING, which
7  * you should have received as part of this distribution.  The terms
8  * are also available at
9  * http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.txt
10  *
11  */
12
13 package org.scilab.modules.xcos.palette;
14
15 import static org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.buildCall;
16 import static org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.synchronousScilabExec;
17
18 import java.awt.Point;
19 import java.awt.datatransfer.Transferable;
20 import java.awt.dnd.DnDConstants;
21 import java.awt.dnd.DragGestureEvent;
22 import java.awt.dnd.DragGestureListener;
23 import java.awt.dnd.DragSource;
24 import java.awt.dnd.InvalidDnDOperationException;
25 import java.awt.event.MouseListener;
26 import java.lang.ref.WeakReference;
27 import java.util.EnumSet;
28 import java.util.logging.Level;
29 import java.util.logging.Logger;
30
31 import org.scilab.modules.action_binding.highlevel.ScilabInterpreterManagement.InterpreterException;
32 import org.scilab.modules.gui.messagebox.ScilabModalDialog;
33 import org.scilab.modules.gui.messagebox.ScilabModalDialog.IconType;
34 import org.scilab.modules.localization.Messages;
35 import org.scilab.modules.xcos.JavaController;
36 import org.scilab.modules.xcos.Kind;
37 import org.scilab.modules.xcos.ObjectProperties;
38 import org.scilab.modules.xcos.UpdateStatus;
39 import org.scilab.modules.xcos.Xcos;
40 import org.scilab.modules.xcos.XcosView;
41 import org.scilab.modules.xcos.XcosViewListener;
42 import org.scilab.modules.xcos.block.BasicBlock;
43 import org.scilab.modules.xcos.graph.XcosDiagram;
44 import org.scilab.modules.xcos.graph.model.XcosCellFactory;
45 import org.scilab.modules.xcos.io.scicos.ScicosFormatException;
46 import org.scilab.modules.xcos.io.scicos.ScilabDirectHandler;
47 import org.scilab.modules.xcos.palette.listener.PaletteBlockMouseListener;
48 import org.scilab.modules.xcos.palette.model.PaletteBlock;
49 import org.scilab.modules.xcos.palette.view.PaletteBlockView;
50 import org.scilab.modules.xcos.palette.view.PaletteManagerView;
51 import org.scilab.modules.xcos.utils.BlockPositioning;
52 import org.scilab.modules.xcos.utils.XcosMessages;
53
54 import com.mxgraph.swing.handler.mxGraphTransferHandler;
55 import com.mxgraph.swing.util.mxGraphTransferable;
56
57 /**
58  * A palette block is the representation of the block in the palette. All the
59  * operations there are used to render, load and put (on a diagram) a block.
60  */
61 public final class PaletteBlockCtrl {
62     /**
63      * Internal graph used to render each block.
64      */
65     public static final XcosDiagram INTERNAL_GRAPH;
66     static {
67         JavaController controller = new JavaController();
68         INTERNAL_GRAPH = new XcosDiagram(controller.createObject(Kind.DIAGRAM), Kind.DIAGRAM);
69         INTERNAL_GRAPH.installListeners();
70     }
71
72     private static final double BLOCK_DEFAULT_POSITION = 10.0;
73     private static final MouseListener MOUSE_LISTENER = new PaletteBlockMouseListener();
74     private static final Logger LOG = Logger.getLogger(PaletteBlockCtrl.class.getName());
75
76     private static final String UNABLE_TO_LOAD_BLOCK = Messages.gettext("Unable to load block from %s .");
77     private static final String LOADING_THE_BLOCK = Messages.gettext("Loading the block") + XcosMessages.DOTS;
78
79     private static PaletteBlockCtrl previouslySelected;
80
81     private final PaletteBlock model;
82     private final PaletteBlockView view;
83
84     private transient WeakReference<Transferable> transferable = new WeakReference<Transferable>(null);
85
86     /**
87      * Default constructor
88      *
89      * @param model
90      *            the block data
91      */
92     public PaletteBlockCtrl(PaletteBlock model) {
93         this.model = model;
94         this.view = new PaletteBlockView(this);
95         installListeners(this.view);
96     }
97
98     /**
99      * @param view
100      *            The view to setup
101      */
102     private void installListeners(PaletteBlockView view) {
103         view.addMouseListener(MOUSE_LISTENER);
104         installDnd();
105     }
106
107     /**
108      * @return the view
109      */
110     public PaletteBlockView getView() {
111         return view;
112     }
113
114     /**
115      * @return the model
116      */
117     public PaletteBlock getModel() {
118         return model;
119     }
120
121     /**
122      * This function is the only access to get the block.
123      *
124      * @return the transferable object
125      * @throws ScicosFormatException
126      *             on decoding error
127      */
128     public synchronized Transferable getTransferable() throws ScicosFormatException {
129         Transferable transfer = transferable.get();
130         if (transfer == null) {
131             BasicBlock block;
132             try {
133                 block = loadBlock();
134             } catch (ScicosFormatException ex) {
135                 getView().setEnabled(false);
136                 throw ex;
137             }
138             if (block == null) {
139                 if (LOG.isLoggable(Level.FINEST)) {
140                     LOG.finest(String.format(UNABLE_TO_LOAD_BLOCK, getModel().getData().getEvaluatedPath()));
141                 }
142                 getView().setEnabled(false);
143                 throw new InvalidDnDOperationException();
144             }
145             getView().setEnabled(true);
146
147             /* Render it and export it */
148             block.getGeometry().setX(BLOCK_DEFAULT_POSITION);
149             block.getGeometry().setY(BLOCK_DEFAULT_POSITION);
150
151             INTERNAL_GRAPH.addCell(block);
152             INTERNAL_GRAPH.selectAll();
153
154             BlockPositioning.updateBlockView(INTERNAL_GRAPH, block);
155
156             mxGraphTransferHandler handler = ((mxGraphTransferHandler) INTERNAL_GRAPH.getAsComponent().getTransferHandler());
157             Object[] cells = new Object[] {block};
158             transfer = new mxGraphTransferable(cells, INTERNAL_GRAPH.getPaintBounds(cells), handler.createTransferableImage(INTERNAL_GRAPH.getAsComponent(), cells));
159             transferable = new WeakReference<Transferable>(transfer);
160
161             INTERNAL_GRAPH.removeCells();
162         }
163         return transfer;
164     }
165
166     private static class BlockLoadedListener extends XcosViewListener {
167         private long uid;
168
169         public BlockLoadedListener() {
170             uid = 0;
171         }
172
173         public long getUID() {
174             return uid;
175         }
176
177         /**
178          * When a unique block is created then store it for later use.
179          */
180         @Override
181         public void objectCreated(long uid, Kind kind) {
182             if (!EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(kind)) {
183                 return;
184             }
185
186             this.uid = uid;
187         }
188
189         /**
190          * When a composite block is created we track the PARENT_BLOCK / CHILDREN association to store the parent.
191          */
192         @Override
193         public void propertyUpdated(long uid, Kind kind, ObjectProperties property, UpdateStatus status) {
194             if (status != UpdateStatus.SUCCESS || property != ObjectProperties.CHILDREN) {
195                 return;
196             }
197             if (!EnumSet.of(Kind.BLOCK, Kind.ANNOTATION).contains(kind)) {
198                 return;
199             }
200
201             this.uid = uid;
202         }
203     }
204
205
206     /**
207      * @return the loaded block.
208      * @throws ScicosFormatException
209      *             on error
210      */
211     private BasicBlock loadBlock() throws ScicosFormatException {
212         XcosView view = (XcosView) JavaController.lookup_view(Xcos.class.getSimpleName());
213
214         BlockLoadedListener blockLoaded = new BlockLoadedListener();
215         view.addXcosViewListener(blockLoaded, EnumSet.allOf(Kind.class), true, EnumSet.of(ObjectProperties.CHILDREN));
216
217         BasicBlock block;
218         try {
219             synchronousScilabExec(ScilabDirectHandler.BLK + " = " + buildCall(model.getName(), "define"));
220             block = XcosCellFactory.createBlock(blockLoaded.getUID());
221         } catch (InterpreterException e1) {
222             LOG.severe(e1.toString());
223             block = null;
224         } finally {
225             view.removeXcosViewListener(blockLoaded);
226         }
227
228         return block;
229     }
230
231     /**
232      * This function load the block and render it on the hidden diagram. This
233      * can be time-consuming and each block should be cached on the caller when
234      * possible.
235      *
236      * @return a rendered block
237      */
238     public BasicBlock getBlock() {
239         try {
240             return (BasicBlock) ((mxGraphTransferable) getTransferable()).getCells()[0];
241         } catch (ScicosFormatException e) {
242             LOG.severe(e.toString());
243             return null;
244         }
245     }
246
247     /**
248      * @return true if it is selected, false otherwise
249      */
250     public boolean isSelected() {
251         return this == previouslySelected;
252     }
253
254     /**
255      * @param selected
256      *            the selected state to set
257      */
258     public void setSelected(boolean selected) {
259         if (selected) {
260             if (previouslySelected != null) {
261                 previouslySelected.setSelected(false);
262             }
263             previouslySelected = this;
264         }
265         getView().setSelectedUI(selected);
266     }
267
268     /**
269      * Install the Drag'n'Drop on this instance.
270      */
271     public void installDnd() {
272         // Install the handler for dragging nodes into a graph
273         DragGestureListener dragGestureListener = new DragGestureListener() {
274             @Override
275             public void dragGestureRecognized(DragGestureEvent e) {
276                 if (PaletteManagerView.get() == null) {
277                     PaletteManagerView.restore(null);
278                 }
279                 final PaletteManagerView winView = PaletteManagerView.get();
280                 final DragGestureEvent event = e;
281                 final String msg = String.format(UNABLE_TO_LOAD_BLOCK, getModel().getName());
282
283                 winView.setInfo(LOADING_THE_BLOCK);
284                 try {
285                     Transferable transfer = getTransferable();
286
287                     if (transfer != null) {
288                         event.startDrag(null, null, new Point(), transfer, null);
289                     } else {
290                         throw new InvalidDnDOperationException();
291                     }
292                 } catch (InvalidDnDOperationException exception) {
293                     ScilabModalDialog.show(winView, msg, XcosMessages.XCOS_ERROR, IconType.ERROR_ICON);
294                 } catch (ScicosFormatException ex) {
295                     ScilabModalDialog.show(winView, ex.getMessage(), XcosMessages.XCOS_ERROR, IconType.ERROR_ICON);
296                 } finally {
297                     winView.setInfo(XcosMessages.EMPTY_INFO);
298                 }
299             }
300
301         };
302
303         DragSource dragSource = DragSource.getDefaultDragSource();
304         dragSource.createDefaultDragGestureRecognizer(this.getView(), DnDConstants.ACTION_COPY, dragGestureListener);
305     }
306 }