View Javadoc

1   /*
2    *   Magic-Project is a turn based strategy simulator
3    *   Copyright (C) 2003-2007 Fabrice Daugan
4    *
5    *   This program is free software; you can redistribute it and/or modify it 
6    * under the terms of the GNU General Public License as published by the Free 
7    * Software Foundation; either version 2 of the License, or (at your option) any
8    * later version.
9    *
10   *   This program is distributed in the hope that it will be useful, but WITHOUT 
11   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12   * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more 
13   * details.
14   *
15   *   You should have received a copy of the GNU General Public License along  
16   * with this program; if not, write to the Free Software Foundation, Inc., 
17   * 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18   */
19  package net.sf.magicproject.ui.layout;
20  
21  import java.awt.BorderLayout;
22  import java.awt.Component;
23  import java.awt.Container;
24  import java.awt.Dimension;
25  import java.awt.LayoutManager2;
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import net.sf.magicproject.clickable.targetable.card.AbstractCard;
32  import net.sf.magicproject.clickable.targetable.card.MCard;
33  import net.sf.magicproject.zone.ZoneSector;
34  
35  /***
36   * @author <a href="mailto:fabdouglas@users.sourceforge.net">Fabrice Daugan </a>
37   * @since 0.91
38   */
39  public class SectorLayout implements LayoutManager2, java.io.Serializable {
40  	/***
41  	 * Constructs a border layout with the horizontal gaps between components. The
42  	 * horizontal gap is specified by <code>hgap</code>.
43  	 * 
44  	 * @see #getHgap()
45  	 * @see #setHgap(int)
46  	 * @serial
47  	 */
48  	int hgap;
49  
50  	/***
51  	 * Constructs a border layout with the vertical gaps between components. The
52  	 * vertical gap is specified by <code>vgap</code>.
53  	 * 
54  	 * @see #getVgap()
55  	 * @see #setVgap(int)
56  	 * @serial
57  	 */
58  	int vgap;
59  
60  	private final Map<Object, ZoneSector> sectors;
61  
62  	private static final List<AbstractCard> EMPTY_LIST = new ArrayList<AbstractCard>(
63  			0);
64  
65  	/*
66  	 * JDK 1.1 serialVersionUID
67  	 */
68  	private static final long serialVersionUID = -8658291919501921765L;
69  
70  	/***
71  	 * Constructs a new border layout with no gaps between components.
72  	 * 
73  	 * @param sectors
74  	 *          defined sectors.
75  	 */
76  	public SectorLayout(List<ZoneSector> sectors) {
77  		this(5, 5, sectors);
78  	}
79  
80  	/***
81  	 * Constructs a border layout with the specified gaps between components. The
82  	 * horizontal gap is specified by <code>hgap</code> and the vertical gap is
83  	 * specified by <code>vgap</code>.
84  	 * 
85  	 * @param hgap
86  	 *          the horizontal gap.
87  	 * @param vgap
88  	 *          the vertical gap.
89  	 * @param sectors
90  	 *          defined sectors.
91  	 */
92  	public SectorLayout(int hgap, int vgap, List<ZoneSector> sectors) {
93  		this.hgap = hgap;
94  		this.vgap = vgap;
95  		this.sectors = new HashMap<Object, ZoneSector>();
96  		for (ZoneSector zoneSector : sectors) {
97  			this.sectors.put(zoneSector.getConstraint(), zoneSector);
98  		}
99  	}
100 
101 	/***
102 	 * Returns the horizontal gap between components.
103 	 * 
104 	 * @return the horizontal gap.
105 	 * @since JDK1.1
106 	 */
107 	public int getHgap() {
108 		return hgap;
109 	}
110 
111 	/***
112 	 * Sets the horizontal gap between components.
113 	 * 
114 	 * @param hgap
115 	 *          the horizontal gap between components
116 	 * @since JDK1.1
117 	 */
118 	public void setHgap(int hgap) {
119 		this.hgap = hgap;
120 	}
121 
122 	/***
123 	 * Returns the vertical gap between components.
124 	 * 
125 	 * @return the vertical gap.
126 	 * @since JDK1.1
127 	 */
128 	public int getVgap() {
129 		return vgap;
130 	}
131 
132 	/***
133 	 * Sets the vertical gap between components.
134 	 * 
135 	 * @param vgap
136 	 *          the vertical gap between components
137 	 * @since JDK1.1
138 	 */
139 	public void setVgap(int vgap) {
140 		this.vgap = vgap;
141 	}
142 
143 	public void addLayoutComponent(Component comp, Object constraints) {
144 		//
145 	}
146 
147 	public void addLayoutComponent(String name, Component comp) {
148 		//
149 	}
150 
151 	public void removeLayoutComponent(Component comp) {
152 		//
153 	}
154 
155 	/***
156 	 * Determines the minimum size of the <code>target</code> container using
157 	 * this layout manager.
158 	 * <p>
159 	 * This method is called when a container calls its
160 	 * <code>getMinimumSize</code> method. Most applications do not call this
161 	 * method directly.
162 	 * 
163 	 * @param target
164 	 *          the container in which to do the layout.
165 	 * @return the minimum dimensions needed to lay out the subcomponents of the
166 	 *         specified container.
167 	 * @see java.awt.Container
168 	 * @see net.sf.magicproject.ui.layout.SectorLayout#preferredLayoutSize
169 	 * @see java.awt.Container#getMinimumSize()
170 	 */
171 	public Dimension minimumLayoutSize(Container target) {
172 		return new Dimension(0, 0);
173 	}
174 
175 	/***
176 	 * Determines the preferred size of the <code>target</code> container using
177 	 * this layout manager, based on the components in the container.
178 	 * <p>
179 	 * Most applications do not call this method directly. This method is called
180 	 * when a container calls its <code>getPreferredSize</code> method.
181 	 * 
182 	 * @param target
183 	 *          the container in which to do the layout.
184 	 * @return the preferred dimensions to lay out the subcomponents of the
185 	 *         specified container.
186 	 * @see java.awt.Container
187 	 * @see net.sf.magicproject.ui.layout.SectorLayout#minimumLayoutSize
188 	 * @see java.awt.Container#getPreferredSize()
189 	 */
190 	public Dimension preferredLayoutSize(Container target) {
191 		return target.getSize();
192 	}
193 
194 	/***
195 	 * Returns the maximum dimensions for this layout given the components in the
196 	 * specified target container.
197 	 * 
198 	 * @param target
199 	 *          the component which needs to be laid out
200 	 * @return the maximum dimensions for this layout
201 	 * @see Container
202 	 * @see #minimumLayoutSize
203 	 * @see #preferredLayoutSize
204 	 */
205 	public Dimension maximumLayoutSize(Container target) {
206 		return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
207 	}
208 
209 	/***
210 	 * Returns the alignment along the x axis. This specifies how the component
211 	 * would like to be aligned relative to other components. The value should be
212 	 * a number between 0 and 1 where 0 represents alignment along the origin, 1
213 	 * is aligned the furthest away from the origin, 0.5 is centered, etc.
214 	 * 
215 	 * @param parent
216 	 * @return the alignment along the x axis.
217 	 */
218 	public float getLayoutAlignmentX(Container parent) {
219 		return 0.5f;
220 	}
221 
222 	/***
223 	 * Returns the alignment along the y axis. This specifies how the component
224 	 * would like to be aligned relative to other components. The value should be
225 	 * a number between 0 and 1 where 0 represents alignment along the origin, 1
226 	 * is aligned the furthest away from the origin, 0.5 is centered, etc.
227 	 * 
228 	 * @param parent
229 	 * @return the alignment along the y axis.
230 	 */
231 	public float getLayoutAlignmentY(Container parent) {
232 		return 0.5f;
233 	}
234 
235 	/***
236 	 * Invalidates the layout, indicating that if the layout manager has cached
237 	 * information it should be discarded.
238 	 * 
239 	 * @param target
240 	 */
241 	public void invalidateLayout(Container target) {
242 		//
243 	}
244 
245 	private List<AbstractCard> getCards(Object constraint) {
246 		ZoneSector sector = sectors.get(constraint);
247 		if (sector == null) {
248 			return EMPTY_LIST;
249 		}
250 		return sector.getCards();
251 	}
252 
253 	/***
254 	 * Lays out the container argument using this border layout.
255 	 * <p>
256 	 * This method actually reshapes the components in the specified container in
257 	 * order to satisfy the constraints of this <code>SectorLayout</code>
258 	 * object. The <code>NORTH</code> and <code>SOUTH</code> components, if
259 	 * any, are placed at the top and bottom of the container, respectively. The
260 	 * <code>WEST</code> and <code>EAST</code> components are then placed on
261 	 * the left and right, respectively. Finally, the <code>CENTER</code> object
262 	 * is placed in any remaining space in the middle.
263 	 * <p>
264 	 * Most applications do not call this method directly. This method is called
265 	 * when a container calls its <code>doLayout</code> method.
266 	 * 
267 	 * @param target
268 	 *          the container in which to do the layout.
269 	 * @see java.awt.Container
270 	 * @see java.awt.Container#doLayout()
271 	 */
272 	public void layoutContainer(Container target) {
273 		synchronized (target.getTreeLock()) {
274 			int height = target.getHeight();
275 			int width = target.getWidth();
276 
277 			// West part
278 			int yw = 0;
279 			int xw = 0;
280 			int maxWidth = 0;
281 			for (AbstractCard card : getCards(BorderLayout.WEST)) {
282 				if (card instanceof MCard && ((MCard) card).getMUI().isAutoAlign) {
283 					int cardHeight = card.getHeight();
284 					if (yw + cardHeight + vgap >= height) {
285 						yw = 0;
286 						xw += maxWidth + hgap;
287 						maxWidth = card.getWidth();
288 						card.setLocation(xw, yw);
289 					} else {
290 						if (card.getWidth() > maxWidth) {
291 							maxWidth = card.getWidth();
292 						}
293 						card.setLocation(xw, yw);
294 					}
295 					yw += cardHeight + vgap;
296 				}
297 			}
298 			xw += maxWidth + hgap;
299 
300 			// East part
301 			int ye = vgap;
302 			int xe = width - hgap;
303 			maxWidth = 0;
304 			for (AbstractCard card : getCards(BorderLayout.EAST)) {
305 				if (((MCard) card).getMUI().isAutoAlign) {
306 					int cardHeight = card.getHeight();
307 					if (ye + cardHeight + vgap >= height) {
308 						ye = 0;
309 						xe -= maxWidth + hgap;
310 						maxWidth = card.getWidth();
311 						card.setLocation(xe - card.getWidth(), ye);
312 					} else {
313 						if (card.getWidth() > maxWidth) {
314 							maxWidth = card.getWidth();
315 						}
316 						card.setLocation(xe - card.getWidth(), ye);
317 					}
318 					ye += cardHeight + vgap;
319 				}
320 			}
321 			xe -= maxWidth + hgap;
322 
323 			// North part
324 			int yn = vgap;
325 			int xn = (xe - xw) / 2;
326 			maxWidth = 0;
327 			int maxHeightN = 0;
328 			int count = 0;
329 			int requiredSpace = 0;
330 			int scaledHgap = hgap;
331 			for (AbstractCard card : getCards(BorderLayout.NORTH)) {
332 				if (((MCard) card).getMUI().isAutoAlign) {
333 					count++;
334 					requiredSpace += card.getWidth();
335 					if (card.getHeight() > maxHeightN)
336 						maxHeightN = card.getHeight();
337 				}
338 			}
339 			requiredSpace += count * hgap;
340 			if (count > 1) {
341 				if (requiredSpace + xw > xe) {
342 					scaledHgap = (xe - xw - requiredSpace) / (count - 1);
343 					xn = xw;
344 				} else {
345 					xn = (xe - xw - requiredSpace) / 2;
346 				}
347 			}
348 			for (AbstractCard card : getCards(BorderLayout.NORTH)) {
349 				if (((MCard) card).getMUI().isAutoAlign) {
350 					card.setLocation(xn, yn);
351 					xn += card.getWidth() + scaledHgap;
352 				}
353 			}
354 			yn += maxHeightN + vgap;
355 
356 			// South part
357 			int ys = height - vgap;
358 			int xs = (xe - xw) / 2;
359 			maxWidth = 0;
360 			int maxHeightS = 0;
361 			count = 0;
362 			requiredSpace = 0;
363 			scaledHgap = hgap;
364 			for (AbstractCard card : getCards(BorderLayout.SOUTH)) {
365 				if (((MCard) card).getMUI().isAutoAlign) {
366 					count++;
367 					requiredSpace += card.getWidth();
368 					if (card.getHeight() > maxHeightS)
369 						maxHeightS = card.getHeight();
370 				}
371 			}
372 			if (count > 1) {
373 				if (requiredSpace + xw > xe) {
374 					scaledHgap = (xe - xw - requiredSpace) / (count - 1);
375 					xs = xw;
376 				} else {
377 					xs = (xe - xw - requiredSpace) / 2;
378 				}
379 			}
380 			for (AbstractCard card : getCards(BorderLayout.SOUTH)) {
381 				if (((MCard) card).getMUI().isAutoAlign) {
382 					card.setLocation(xs, ys - card.getHeight());
383 					xs += card.getWidth() + scaledHgap;
384 				}
385 			}
386 			ys -= maxHeightS + vgap;
387 
388 			// Center part
389 			maxWidth = 0;
390 			int maxHeightC = 0;
391 			requiredSpace = 0;
392 			int requiredHeight = 0;
393 			scaledHgap = hgap;
394 			int tmpX = 0;
395 			List<Integer> lineWidths = new ArrayList<Integer>();
396 			for (AbstractCard card : getCards(BorderLayout.CENTER)) {
397 				if (((MCard) card).getMUI().isAutoAlign) {
398 					if (tmpX + card.getWidth() + hgap + xw >= xe) {
399 						// add new line to the center
400 						lineWidths.add(tmpX);
401 						tmpX = 0;
402 						// update the total lines height
403 						requiredHeight += maxHeightC + vgap;
404 						maxHeightC = card.getHeight();
405 					} else if (card.getHeight() > maxHeightC) {
406 						// update the line height
407 						maxHeightC = card.getHeight();
408 					}
409 					tmpX += card.getWidth() + hgap;
410 				}
411 			}
412 			lineWidths.add(tmpX);
413 
414 			if (!lineWidths.isEmpty()) {
415 				int yc = (ys - yn) / 2;
416 				if (yn + requiredHeight > ys) {
417 					// Not enougth space
418 					yc = yn;
419 				} else {
420 					yc = (ys - yn - requiredHeight) / 2;
421 				}
422 				int lastLine = 0;
423 				int lineWidth = lineWidths.get(0);
424 				int xc = (xe - xw - lineWidth) / 2;
425 				maxHeightC = 0;
426 				for (AbstractCard card : getCards(BorderLayout.CENTER)) {
427 					if (((MCard) card).getMUI().isAutoAlign) {
428 						if (xc + card.getWidth() + hgap + xw >= xe) {
429 							lineWidth = lineWidths.get(++lastLine);
430 							xc = (xe - xw - lineWidth) / 2;
431 							yc += maxHeightC + vgap;
432 							maxHeightC = card.getHeight();
433 						} else {
434 							if (card.getHeight() > maxHeightC) {
435 								maxHeightC = card.getHeight();
436 							}
437 						}
438 						card.setLocation(xc, yc);
439 						xc += card.getWidth() + hgap;
440 					}
441 				}
442 			}
443 		}
444 	}
445 
446 	/***
447 	 * Returns a string representation of the state of this border layout.
448 	 * 
449 	 * @return a string representation of this border layout.
450 	 */
451 	@Override
452 	public String toString() {
453 		return getClass().getName() + "[hgap=" + hgap + ",vgap=" + vgap + "]";
454 	}
455 
456 }