001: /*
002: * ToolBarLayout.java
003: *
004: * Copyright (C) 2002, 2003, 2004, 2005, 2006 Takis Diakoumis
005: *
006: * This program is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU General Public License
008: * as published by the Free Software Foundation; either version 2
009: * of the License, or any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
019: *
020: */
021:
022: package org.underworldlabs.swing.toolbar;
023:
024: import java.awt.Component;
025: import java.awt.Container;
026: import java.awt.Dimension;
027: import java.awt.Insets;
028: import java.awt.LayoutManager2;
029: import java.awt.Rectangle;
030:
031: import java.util.ArrayList;
032: import java.util.Collections;
033: import java.util.Comparator;
034: import java.util.Enumeration;
035: import java.util.Hashtable;
036: import java.util.Vector;
037:
038: /* ----------------------------------------------------------
039: * CVS NOTE: Changes to the CVS repository prior to the
040: * release of version 3.0.0beta1 has meant a
041: * resetting of CVS revision numbers.
042: * ----------------------------------------------------------
043: */
044:
045: /**
046: *
047: * @author Takis Diakoumis
048: * @version $Revision: 1.5 $
049: * @date $Date: 2006/07/15 12:50:56 $
050: */
051: public class ToolBarLayout implements LayoutManager2 {
052:
053: /** The default tool bar row height */
054: private static final int ROW_HEIGHT = 30;
055:
056: /** The bottom border offset */
057: public static final int BORDER_OFFSET = 2;
058:
059: /** the applied row height */
060: private int rowHeight = -1;
061:
062: /** The number of rows currently displayed */
063: private int toolBarRows;
064:
065: /** The comparator for tool bar ordering */
066: private ToolsPositionComparator comparator;
067:
068: /** The constraints/component pairs */
069: private Hashtable componentsMap;
070:
071: /** The tool bar constraints */
072: private ArrayList constraintsList;
073:
074: /** <p>Constructs a new layout instance initialised
075: * to the specified number of rows.
076: *
077: * @param the initial number of rows
078: */
079: public ToolBarLayout(int toolBarRows) {
080: constraintsList = new ArrayList();
081: componentsMap = new Hashtable();
082: comparator = new ToolsPositionComparator();
083: this .toolBarRows = toolBarRows;
084: }
085:
086: /** <p>Lays out the container in the specified container.
087: *
088: * @param the component which needs to be laid out
089: */
090: public void layoutContainer(Container parent) {
091:
092: Dimension parentDim = parent.getSize();
093: Collections.sort(constraintsList, comparator);
094:
095: // the x and y position
096: int locX = 0, locY = 0;
097: // the resize offset x position
098: int resizeOffsetX = 0;
099: // the last and current row number
100: int previousRow = 0, currentRow = 0;
101: // the widths to be set
102: int width = 0, preferredWidth = 0, minWidth = 0;
103: // component count
104: int compCount = constraintsList.size();
105: // whether the row count changed
106: boolean rowAdded = false;
107:
108: ToolBarConstraints constraints = null;
109: Component component = null;
110:
111: for (int i = 0; i < compCount; i++) {
112: constraints = (ToolBarConstraints) constraintsList.get(i);
113: component = (Component) componentsMap.get(constraints);
114: if (!component.isVisible()) {
115: continue;
116: }
117:
118: if (rowHeight <= 0) {
119: rowHeight = component.getPreferredSize().height;
120: }
121:
122: currentRow = constraints.getRow();
123: if (currentRow > toolBarRows) {
124: toolBarRows = currentRow;
125: rowAdded = true;
126: }
127:
128: minWidth = constraints.getMinimumWidth();
129: preferredWidth = constraints.getPreferredWidth();
130:
131: // reset the x location if new row
132: if (currentRow != previousRow) {
133: locX = 0;
134: locY = currentRow * getRowHeight();
135: }
136:
137: // check if we stretch this tool bar to fill the width
138: if (i != compCount - 1) {
139:
140: // check the next component
141: ToolBarConstraints _constraints = (ToolBarConstraints) constraintsList
142: .get(i + 1);
143: resizeOffsetX = _constraints.getResizeOffsetX();
144:
145: // if the next component is on a new row,
146: // fill the current row
147: if (_constraints.getRow() == currentRow + 1) {
148: width = parentDim.width - locX;
149: }
150: // if the component is resizing
151: else if (resizeOffsetX != -1) {
152:
153: ToolBarConstraints[] tbca = getToolBars(currentRow);
154:
155: int maxWidth = parentDim.width - locX;
156: int _width = resizeOffsetX - locX;
157: int _minWidth = 0;
158: int start = 0;
159:
160: // determine the components position in the row
161: for (int k = 0; k < tbca.length; k++) {
162:
163: if (tbca[k] == constraints) {
164: start = k + 1;
165: break;
166: }
167:
168: }
169:
170: for (int j = start; j < tbca.length; j++) {
171:
172: _minWidth = tbca[j].getMinimumWidth();
173: if (_minWidth == -1) {
174: _minWidth = ((Component) componentsMap
175: .get(constraints)).getMinimumSize().width;
176: }
177: maxWidth -= _minWidth;
178:
179: }
180:
181: if (_width < minWidth) {
182: width = minWidth;
183: } else if (_width > maxWidth) {
184: width = maxWidth;
185: } else {
186: width = _width;
187: }
188:
189: }
190: // if the component has a preferred width
191: else if (preferredWidth != -1) {
192: width = preferredWidth;
193: }
194: // otherwise use the minimum
195: else {
196: width = minWidth;
197: }
198:
199: }
200:
201: // otherwise the component should fill its row
202: else {
203: minWidth = constraints.getMinimumWidth();
204: if (minWidth == -1) {
205: minWidth = component.getMinimumSize().width;
206: }
207:
208: width = parentDim.width - locX;
209: if (width < minWidth) {
210: width = minWidth;
211: }
212: locX = parentDim.width - width;
213: }
214:
215: component.setBounds(locX, locY, width, getRowHeight());
216:
217: // reset the component's position
218: constraints.setLocX(locX);
219:
220: // reset values
221: previousRow = currentRow;
222: locX += width;
223: minWidth = 0;
224: resizeOffsetX = -1;
225:
226: }
227: comparator.setFirstLayout(false);
228: }
229:
230: /** <p>Returns all the toolbars on the specified row.
231: *
232: * @parm the tool bar row
233: */
234: private ToolBarConstraints[] getToolBars(int row) {
235: int _row = -1;
236: ToolBarConstraints tbc = null;
237: Vector _toolBars = new Vector();
238:
239: for (int i = 0, k = constraintsList.size(); i < k; i++) {
240: tbc = (ToolBarConstraints) constraintsList.get(i);
241: _row = tbc.getRow();
242:
243: if (_row > row) {
244: break;
245: }
246:
247: if (_row == row) {
248: _toolBars.add(tbc);
249: }
250:
251: }
252:
253: return (ToolBarConstraints[]) _toolBars
254: .toArray(new ToolBarConstraints[] {});
255: }
256:
257: /** <p>Returns the currently displayed tool bar row count.
258: *
259: * @return the current row count
260: */
261: public int getRowCount() {
262: return toolBarRows;
263: }
264:
265: public void setRows(int rows) {
266: toolBarRows = rows;
267: }
268:
269: /** <p>Retrieves the specified components constraints.
270: *
271: * @param the component
272: * @return the component's constraints
273: */
274: public ToolBarConstraints getConstraint(Component comp) {
275: ToolBarConstraints constraint = null;
276: Enumeration consEnum = componentsMap.keys();
277:
278: while (consEnum.hasMoreElements()) {
279: Object element = consEnum.nextElement();
280:
281: if (componentsMap.get(element) == comp) {
282: constraint = (ToolBarConstraints) element;
283: break;
284: }
285:
286: }
287:
288: return constraint;
289:
290: }
291:
292: /** <p>Determines the component count for the
293: * specified row.
294: *
295: * @param the row
296: * @return the component count for the row
297: */
298: private int getComponentCount(int row) {
299: int count = 0;
300: int size = constraintsList.size();
301: ToolBarConstraints constraint = null;
302:
303: for (int i = 0; i < size; i++) {
304: constraint = (ToolBarConstraints) constraintsList.get(i);
305:
306: if (constraint.getRow() == row) {
307: Component c = (Component) componentsMap.get(constraint);
308: if (c.isVisible()) {
309: count++;
310: }
311: }
312:
313: }
314:
315: return count;
316: }
317:
318: private boolean maybeRemoveRow() {
319: int removeRow = -1;
320:
321: for (int i = 0; i < toolBarRows; i++) {
322:
323: if (getComponentCount(i) == 0) {
324: removeRow = i;
325: break;
326: }
327:
328: }
329:
330: if (removeRow == -1)
331: return false;
332:
333: int currentRow = -1;
334: int size = constraintsList.size();
335: ToolBarConstraints constraint = null;
336:
337: for (int i = 0; i < size; i++) {
338: constraint = (ToolBarConstraints) constraintsList.get(i);
339: currentRow = constraint.getRow();
340: if (constraint.getRow() > removeRow) {
341: constraint.setRow(currentRow - 1);
342: }
343: }
344:
345: toolBarRows--;
346: return true;
347: }
348:
349: public int getRowHeight() {
350: if (rowHeight <= 0) {
351: return ROW_HEIGHT;
352: }
353: return rowHeight;
354: }
355:
356: public boolean maybeAddRow(Component comp) {
357: ToolBarConstraints constraint = getConstraint(comp);
358: int currentRow = constraint.getRow();
359:
360: if (getComponentCount(currentRow) == 1)
361: return false;
362:
363: int newRow = (int) Math.round((double) comp.getY()
364: / (double) getRowHeight());
365: constraint.setRow(newRow);
366:
367: if (newRow + 1 > toolBarRows)
368: toolBarRows++;
369:
370: return true;
371: }
372:
373: public void componentResized(Component comp, int locX) {
374: ToolBarConstraints constraint = getConstraint(comp);
375: constraint.setResizeOffsetX(locX);
376: constraint.setLocX(locX);
377: }
378:
379: public void componentMoved(Component comp, int locX, int locY) {
380: ToolBarConstraints constraint = getConstraint(comp);
381:
382: // determine the new row (if changed)
383: int currentRow = constraint.getRow();
384: int newRow = (int) Math.round((double) locY
385: / (double) getRowHeight());
386:
387: if (newRow != currentRow)
388: constraint.setResizeOffsetX(-1);
389:
390: if (newRow > toolBarRows)
391: newRow = toolBarRows - 1;
392:
393: constraint.setLocX(locX);
394: constraint.setRow(newRow);
395: maybeRemoveRow();
396: }
397:
398: /** <p>Adds the specified component to the layout, using the specified
399: * constraint object.
400: *
401: * @param the component to be added
402: * @param where/how the component is added to the layout.
403: */
404: public void addLayoutComponent(Component comp, Object cons) {
405:
406: if (cons instanceof ToolBarConstraints) {
407:
408: ToolBarConstraints _cons = (ToolBarConstraints) ((ToolBarConstraints) cons)
409: .clone();
410:
411: if (_cons.getMinimumWidth() == -1)
412: _cons.setMinimumWidth(comp.getMinimumSize().width);
413:
414: componentsMap.put(_cons, comp);
415: constraintsList.add(_cons);
416:
417: }
418:
419: else if (cons != null) {
420: throw new IllegalArgumentException(
421: "cannot add to layout: constraints must be a ToolBarConstraints");
422: }
423:
424: }
425:
426: /** <p>Returns the maximum size of this component.
427: *
428: * @param the target container
429: */
430: public Dimension maximumLayoutSize(Container target) {
431: return new Dimension(Integer.MAX_VALUE, Integer.MAX_VALUE);
432: }
433:
434: /** <p>Calculates the minimum size dimensions for the specified
435: * panel given the components in the specified parent container.
436: *
437: * @param parent the component to be laid out
438: * @see #preferredLayoutSize
439: */
440: public Dimension minimumLayoutSize(Container parent) {
441: return getLayoutSize(parent, false);
442: }
443:
444: private Rectangle getComponentBounds(Component comp,
445: boolean isPreferred) {
446: Rectangle rectangle = comp.getBounds();
447:
448: if (rectangle.height == 0) {
449: ToolBarConstraints constraints = getConstraint(comp);
450: rectangle.height = getRowHeight();
451: rectangle.y = getRowHeight() * constraints.getRow();
452: }
453:
454: if (rectangle.width <= 0 || rectangle.height <= 0) {
455: Dimension dimension = isPreferred ? comp.getPreferredSize()
456: : comp.getMinimumSize();
457:
458: if (rectangle.width <= 0)
459: rectangle.width = dimension.width;
460: if (rectangle.height <= 0)
461: rectangle.height = dimension.height;
462:
463: }
464:
465: return rectangle;
466: }
467:
468: protected Dimension getLayoutSize(Container parent,
469: boolean isPreferred) {
470: Dimension dimension = new Dimension(0, 0);
471: int width = 0;
472: int height = 0;
473: int i = parent.getComponentCount();
474:
475: for (int j = 0; j < i; j++) {
476: Component component = parent.getComponent(j);
477: if (component.isVisible()) {
478: Rectangle rectangle = getComponentBounds(component,
479: isPreferred);
480: dimension.width = Math.max(dimension.width, rectangle.x
481: + rectangle.width);
482: }
483:
484: }
485:
486: Insets insets = parent.getInsets();
487: dimension.width += insets.left + insets.right;
488: dimension.height = (getRowHeight() * toolBarRows)
489: + BORDER_OFFSET + insets.top + insets.bottom;
490: return dimension;
491: }
492:
493: /** <p>Calculates the preferred size dimensions for the specified
494: * panel given the components in the specified parent container.
495: *
496: * @param parent the component to be laid out
497: * @see #minimumLayoutSize
498: */
499: public Dimension preferredLayoutSize(Container parent) {
500: return getLayoutSize(parent, true);
501: }
502:
503: /** <p>Removes the specified component from the layout.
504: *
505: * @param the component to be removed
506: */
507: public void removeLayoutComponent(Component comp) {
508: }
509:
510: /** <p>Removes all components from this layout. */
511: public void removeComponents() {
512: componentsMap.clear();
513: constraintsList.clear();
514: comparator.setFirstLayout(true);
515: }
516:
517: /** <p>Invalidates the layout, indicating that if the layout manager
518: * has cached information it should be discarded.
519: *
520: * @param the target container
521: */
522: public void invalidateLayout(Container target) {
523: }
524:
525: /** <p>Returns the alignment along the x axis. This specifies how
526: * the component would like to be aligned relative to other
527: * components. The value should be a number between 0 and 1
528: * where 0 represents alignment along the origin, 1 is aligned
529: * the furthest away from the origin, 0.5 is centered, etc.
530: *
531: * @param the target container
532: */
533: public float getLayoutAlignmentX(Container target) {
534: return 0.5f;
535: }
536:
537: /** <p>Returns the alignment along the y axis. This specifies how
538: * the component would like to be aligned relative to other
539: * components. The value should be a number between 0 and 1
540: * where 0 represents alignment along the origin, 1 is aligned
541: * the furthest away from the origin, 0.5 is centered, etc.
542: *
543: * @param the target container
544: */
545: public float getLayoutAlignmentY(Container target) {
546: return 0.5f;
547: }
548:
549: /** <p>Adds the specified component with the specified name to
550: * the layout. This does nothing in ToolBarLayout - constraints
551: * are required.
552: */
553: public void addLayoutComponent(String name, Component comp) {
554: }
555:
556: // reorders the constraints depending on component placement
557: class ToolsPositionComparator implements Comparator {
558:
559: private boolean firstLayout = true;
560:
561: /** <p>Compares the two objects. */
562: public int compare(Object obj1, Object obj2) {
563: ToolBarConstraints cons1 = (ToolBarConstraints) obj1;
564: ToolBarConstraints cons2 = (ToolBarConstraints) obj2;
565:
566: int halfWidth = ((Component) componentsMap.get(cons1))
567: .getWidth() / 2;
568:
569: if (firstLayout) {
570: halfWidth = 0;
571: }
572:
573: int firstX = cons1.getLocX() + halfWidth;
574: int secondX = cons2.getLocX();
575:
576: int firstY = cons1.getRow();
577: int secondY = cons2.getRow();
578:
579: if (firstX < secondX) {
580: if (firstY > secondY) {
581: return 1;
582: } else {
583: return -1;
584: }
585: } else if (firstX > secondX) {
586: if (firstY < secondY) {
587: return -1;
588: } else {
589: return 1;
590: }
591: } else {
592: return 0;
593: }
594: }
595:
596: public boolean equals(Object obj) {
597: return this .equals(obj);
598: }
599:
600: public void setFirstLayout(boolean firstLayout) {
601: this .firstLayout = firstLayout;
602: }
603:
604: } // class ToolsPositionComparator
605:
606: }
|