15447e9644214bdfe306301dbd501cf5a923f33b
[scilab.git] / scilab / modules / graph / src / java / org / scilab / modules / graph / ScilabComponent.java
1 /*
2  * Scilab ( http://www.scilab.org/ ) - This file is part of Scilab
3  * Copyright (C) 2010 - 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.graph;
14
15 import java.awt.Color;
16 import java.awt.Dimension;
17 import java.awt.Graphics;
18 import java.awt.GraphicsEnvironment;
19
20 import com.mxgraph.model.mxICell;
21 import com.mxgraph.model.mxIGraphModel;
22 import com.mxgraph.swing.mxGraphComponent;
23 import com.mxgraph.util.mxEvent;
24 import com.mxgraph.util.mxEventObject;
25 import com.mxgraph.util.mxEventSource;
26 import com.mxgraph.util.mxRectangle;
27 import com.mxgraph.view.mxCellState;
28 import com.mxgraph.view.mxGraphView;
29
30 /**
31  * Implement the default component for the {@link ScilabGraph}.
32  */
33 @SuppressWarnings(value = { "serial" })
34 public class ScilabComponent extends mxGraphComponent {
35     /**
36      * Color use to mask the graph when the graph is locked
37      */
38     private static final Color MASK_COLOR = new Color(240, 240, 240, 100);
39
40     private static final double SCALE_MULTIPLIER = 1.1;
41
42     /**
43      * Construct the component with the associated graph
44      *
45      * @param graph
46      *            The associated graph
47      */
48     public ScilabComponent(ScilabGraph graph) {
49         super(graph);
50     }
51
52     /**
53      * @return the associated graph control
54      * @see com.mxgraph.swing.mxGraphComponent#createGraphControl()
55      */
56     @Override
57     protected mxGraphControl createGraphControl() {
58         return new ScilabGraphControl();
59     }
60
61     /**
62      * Create the associated canvas
63      *
64      * @return the canvas
65      */
66     @Override
67     public ScilabCanvas createCanvas() {
68         return new ScilabCanvas();
69     }
70
71     /**
72      * Zoom the whole graph and center the view on it.
73      *
74      * @param cells
75      *            the cells to center on
76      */
77     public void zoomAndCenterToCells(final Object[] cells) {
78         final mxRectangle preference = zoomBounds(cells);
79         final Dimension actual = getViewport().getSize();
80
81         final double newScale;
82         final double heightScale = actual.getHeight() / preference.getHeight();
83         final double widthScale = actual.getWidth() / preference.getWidth();
84
85         if (heightScale > 1.0) {
86             if (widthScale > 1.0) {
87                 // We need to zoom in (the max applicable zoom is the lowest)
88                 newScale = Math.min(heightScale, widthScale);
89             } else {
90                 // we need to zoom out (only widthScale is < 1.0)
91                 newScale = widthScale;
92             }
93         } else {
94             if (widthScale > 1.0) {
95                 // we need to zoom out (only heightScale is < 1.0)
96                 newScale = heightScale;
97             } else {
98                 // We need to zoom out (the max applicable zoom is the lowest)
99                 newScale = Math.min(heightScale, widthScale);
100             }
101         }
102
103         // do not apply small zoom values
104         if (Math.abs(1.0 - newScale) < 0.2) {
105             getGraphControl().scrollRectToVisible(zoomBounds(cells).getRectangle(), true);
106             return;
107         }
108
109         zoom(newScale / SCALE_MULTIPLIER);
110         getGraphControl().scrollRectToVisible(zoomBounds(cells).getRectangle(), true);
111     }
112
113     private final mxRectangle zoomBounds(final Object[] cells) {
114         final mxRectangle preference;
115         final Object[] c;
116         if (cells == null || cells.length == 0) {
117             c = graph.getChildCells(graph.getDefaultParent());
118         } else {
119             c = cells;
120         }
121         preference = getChildrenBounds(c);
122
123         return preference;
124     }
125
126     /**
127      * Get the children bound for the cells
128      *
129      * @param cells
130      *            the root of the graph
131      * @return the rectangle or null if not applicable
132      */
133     private mxRectangle getChildrenBounds(final Object[] cells) {
134         mxRectangle result = null;
135
136         if (cells != null && cells.length > 0) {
137             final mxGraphView view = graph.getView();
138             final mxIGraphModel model = graph.getModel();
139
140             for (int i = 0; i < cells.length; i++) {
141                 if (model.isVertex(cells[i]) || model.isEdge(cells[i])) {
142                     final mxICell parent = ((mxICell) cells[i]);
143                     final int childCount = parent.getChildCount();
144
145                     for (int j = 0; j < childCount; j++) {
146                         final mxICell child = parent.getChildAt(j);
147
148                         result = updateRectangle(result, view, child);
149                     }
150
151                     result = updateRectangle(result, view, parent);
152                 }
153             }
154         }
155
156         return result;
157     }
158
159     /**
160      * Update the rectangle parameter with the cell status
161      *
162      * @param result
163      *            the previous result
164      * @param view
165      *            the current view
166      * @param child
167      *            the child we have to work on
168      * @return the updated rectangle
169      */
170     private mxRectangle updateRectangle(mxRectangle result, final mxGraphView view, final mxICell child) {
171         final mxCellState state = view.getState(child);
172         mxRectangle rect = result;
173
174         if (state != null) {
175             if (rect == null) {
176                 rect = new mxRectangle(state);
177             } else {
178                 rect.add(state);
179             }
180         }
181         return rect;
182     }
183
184     /**
185      * Implement a graph control which paint a foreground on top of the view
186      * when the graph is locked.
187      */
188     @SuppressWarnings(value = { "serial" })
189     public class ScilabGraphControl extends mxGraphControl {
190
191         /**
192          * Default constructor
193          */
194         public ScilabGraphControl() {
195             super();
196
197             // Paint the foreground color after the real paint
198             addListener(mxEvent.AFTER_PAINT, new mxEventSource.mxIEventListener() {
199                 @Override
200                 public void invoke(Object sender, mxEventObject evt) {
201
202                     Graphics g = (Graphics) evt.getProperty("g");
203                     if (getGraph().isCellsLocked()) {
204                         g.setColor(MASK_COLOR);
205
206                         Dimension b = getGraphControl().getSize();
207
208                         g.fillRect(0, 0, b.width, b.height);
209                     }
210                 }
211             });
212         }
213     }
214
215     /*
216      * Disable some handlers in case of an headless env.
217      */
218
219     @Override
220     protected void createHandlers() {
221         if (GraphicsEnvironment.isHeadless()) {
222             return;
223         }
224
225         super.createHandlers();
226     }
227 }