1 /*
2 Copyright 2002-2006 Martin van den Bemt
3
4 Licensed under the Apache License, Version 2.0 (the "License");
5 you may not use this file except in compliance with the License.
6 You may obtain a copy of the License at
7
8 http://www.apache.org/licenses/LICENSE-2.0
9
10 Unless required by applicable law or agreed to in writing, software
11 distributed under the License is distributed on an "AS IS" BASIS,
12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 See the License for the specific language governing permissions and
14 limitations under the License.
15 */
16 package org.xulux.guilayer.swing.layouts;
17
18 import java.awt.Component;
19 import java.awt.Container;
20 import java.awt.Dimension;
21 import java.awt.Insets;
22 import java.awt.LayoutManager2;
23 import java.awt.Rectangle;
24 import java.io.Serializable;
25 import java.util.HashMap;
26
27 import javax.swing.JComponent;
28
29 import org.xulux.api.gui.IWidget;
30
31 /**
32 * A layout manager that positions it's controls
33 * using the size and the position of the control
34 *
35 * @todo Fix insets problem when more native components are present than 1
36 * The insets seems to tamper with the bounds of native components.
37 * We should have a map or list to maintain processed components, so
38 * on first entry it doesn't do the bounds restore..
39 *
40 * @author <a href="mailto:martin@mvdb.net">Martin van den Bemt</a>
41 * @version $Id: XYLayout.java,v 1.8 2004/10/14 14:34:20 mvdb Exp $
42 */
43 public class XYLayout extends SwingLayoutAbstract implements LayoutManager2, Serializable {
44 /**
45 * the map with widgets
46 */
47 protected HashMap map;
48
49 /**
50 * Is this the first time the gui is layout?
51 */
52 private boolean firstLayout = true;
53
54 /**
55 *
56 */
57 public XYLayout() {
58 map = new HashMap();
59 }
60
61 /**
62 * Use this contructor if used inside nyx.
63 * @param parent the creator of the XYLayout
64 */
65 public XYLayout(IWidget parent) {
66 map = new HashMap();
67 setParent(parent);
68 }
69
70 /**
71 * The contraints is the widget itself.
72 * @see java.awt.LayoutManager2#addLayoutComponent(Component, Object)
73 */
74 public void addLayoutComponent(Component comp, Object constraints) {
75 map.put(comp, constraints);
76 }
77
78 /**
79 * @see java.awt.LayoutManager2#getLayoutAlignmentX(Container)
80 */
81 public float getLayoutAlignmentX(Container target) {
82 return 0.5F;
83 }
84
85 /**
86 * @see java.awt.LayoutManager2#getLayoutAlignmentY(Container)
87 */
88 public float getLayoutAlignmentY(Container target) {
89 return 0.5F;
90 }
91
92 /**
93 * @see java.awt.LayoutManager2#invalidateLayout(Container)
94 */
95 public void invalidateLayout(Container target) {
96 }
97
98 /**
99 * @see java.awt.LayoutManager2#maximumLayoutSize(Container)
100 */
101 public Dimension maximumLayoutSize(Container target) {
102 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
103 }
104
105 /**
106 * @see java.awt.LayoutManager#addLayoutComponent(String, Component)
107 */
108 public void addLayoutComponent(String name, Component comp) {
109 }
110
111 /**
112 * @see java.awt.LayoutManager#layoutContainer(Container)
113 */
114 public void layoutContainer(Container parent) {
115 if (parent == null) {
116 return;
117 }
118 synchronized (parent.getTreeLock()) {
119 Insets insets = parent.getInsets();
120 int count = parent.getComponentCount();
121 for (int i = 0; i < count; i++) {
122 Component component = parent.getComponent(i);
123 if (!component.isVisible()) {
124 continue;
125 }
126 IWidget widget = (IWidget) map.get(component);
127 Rectangle r = null;
128 if (widget != null && widget.isVisible()) {
129 r = getRectangle(widget, component);
130 if (component instanceof JComponent) {
131 ((JComponent) component).setPreferredSize(new Dimension(r.width, r.height));
132 }
133 component.setSize(r.width, r.height);
134 } else {
135 // if component is not a widget
136 // so layed on top of xulux, try to get all the info from
137 // the component. It's up to the component
138 // to set sizes etc and handle all other
139 // logic.
140 r = getRectangle(component, insets);
141 }
142 // r cannot be null in this scenario
143 component.setBounds(insets.left + r.x, insets.top + r.y, r.width, r.height);
144 }
145 }
146 }
147
148 /**
149 * The preferred size of the component is used, when the widget
150 * size isn't really useable...
151 *
152 * @param widget the widget to get the size from
153 * @param component the component to get the size from
154 * @return the rectangle of the component
155 */
156 public Rectangle getRectangle(IWidget widget, Component component) {
157 if (widget == null) {
158 return null;
159 }
160 Rectangle r = widget.getRectangle().getRectangle();
161 if (r.width <= 0 && r.height <= 0) {
162 Dimension d = component.getPreferredSize();
163 if (d != null) {
164 // prevent crashes on bad layoutmanagers.
165 // a layoutmanager which returns null on preferredLayoutSize
166 // would result in a crash here, preventing the a further layout..
167 r.width = d.width;
168 r.height = d.height;
169 widget.getRectangle().setSize(r.width, r.height);
170 }
171 }
172 return r;
173 }
174
175 /**
176 * This is mainly used for swing components that are
177 * layed "invisibly" on top of nyx.
178 * You should use setLocation(x,y) to position the
179 * Swing component correctly.
180 *
181 * @param component the component
182 * @param insets the insets
183 * @return the rectangle for a native swing component
184 */
185 public Rectangle getRectangle(Component component, Insets insets) {
186
187 if (component == null) {
188 return null;
189 }
190 Rectangle r = component.getBounds();
191 // we need to correct the insets when the first run alrady
192 // has taken place.
193 if (!isFirstLayout()) {
194 if (insets != null) {
195 r.x -= insets.left;
196 r.y -= insets.top;
197 component.setBounds(r);
198 }
199 } else {
200 setFirstLayout(false);
201 }
202 Dimension d = component.getPreferredSize();
203 r.width = d.width;
204 r.height = d.height;
205 return r;
206 }
207
208 /**
209 * @see java.awt.LayoutManager#minimumLayoutSize(Container)
210 */
211 public Dimension minimumLayoutSize(Container parent) {
212 return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
213 }
214
215 /**
216 * @see java.awt.LayoutManager#preferredLayoutSize(Container)
217 */
218 public Dimension preferredLayoutSize(Container parent) {
219 Dimension dim = getLayoutSize(parent);
220 return dim;
221 }
222
223 /**
224 * is almost the same as layoutContainer,
225 * except using setBounds.
226 * If the size of the widget is not set (0 or less than zero)
227 * it will use the preferredsize of the native component, which
228 * is gotten from getRectangle().
229 * @todo write a helper class to give some feedback to the developer
230 * on what sizes should be preferred, to have everything fit!
231 * now it just makes the sizes of eg a panel bigger if the insets
232 * are bigger than 0
233 * @todo dig into the insets stuff deeper and find scenarios where it can fail
234 * @param parent the parent container
235 * @return the layoutsize
236 */
237 protected Dimension getLayoutSize(Container parent) {
238 Dimension dim = new Dimension(0, 0);
239 IWidget pWidget = (IWidget) map.get(parent);
240 int maxX = 0;
241 int maxY = 0;
242 for (int i = 0; i < parent.getComponentCount(); i++) {
243 Component component = parent.getComponent(i);
244 IWidget widget = (IWidget) map.get(component);
245 if (widget != null && widget.isVisible()) {
246 Rectangle r = getRectangle(widget, component);
247 int tmpWidth = r.x + r.width;
248 if (tmpWidth > maxX) {
249 maxX = tmpWidth;
250 }
251 int tmpHeight = r.y + r.height;
252 if (tmpHeight > maxY) {
253 maxY = tmpHeight;
254 }
255 } else if (widget == null) { //if (widget == null) {
256 // only process when there is no widget
257 Dimension compDim = component.getPreferredSize();
258 maxX += compDim.width;
259 maxY += compDim.height;
260 }
261 }
262 dim.width = maxX;
263 dim.height = maxY;
264
265 Insets insets = parent.getInsets();
266 dim.width += insets.left + insets.right;
267 dim.height += insets.top + insets.bottom;
268 return dim;
269 }
270
271 /**
272 * Set if this is the first time that we layout or not
273 * @param firstLayout true or false
274 */
275 protected void setFirstLayout(boolean firstLayout) {
276 this.firstLayout = firstLayout;
277 }
278
279 /**
280 * @return if the layout still needs to process it's first layout
281 */
282 protected boolean isFirstLayout() {
283 return this.firstLayout;
284 }
285
286 /**
287 * @see java.awt.LayoutManager#removeLayoutComponent(Component)
288 */
289 public void removeLayoutComponent(Component comp) {
290 map.remove(comp);
291 }
292
293 /**
294 * @see org.xulux.gui.IXuluxLayout#destroy()
295 */
296 public void destroy() {
297 }
298
299 /**
300 * @see org.xulux.api.gui.IXuluxLayout#addWidget(org.xulux.api.gui.IWidget)
301 */
302 public void addWidget(IWidget widget) {
303 Object nativeWidget = widget.getNativeWidget();
304 if (nativeWidget instanceof Component) {
305 addLayoutComponent((Component) nativeWidget, widget);
306 }
307 }
308
309 /**
310 * @see org.xulux.api.gui.IXuluxLayout#removeWidget(org.xulux.api.gui.IWidget)
311 */
312 public void removeWidget(IWidget widget) {
313 Object nativeWidget = widget.getNativeWidget();
314 if (nativeWidget instanceof Component) {
315 removeLayoutComponent((Component) nativeWidget);
316 }
317 }
318 }