001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.form.layoutdesign.support;
043:
044: import java.awt.*;
045: import java.util.*;
046: import java.util.List;
047: import javax.swing.*;
048:
049: import org.jdesktop.layout.LayoutStyle;
050: import org.jdesktop.layout.GroupLayout;
051:
052: import org.netbeans.modules.form.layoutdesign.*;
053:
054: /**
055: * This class constructs real layout of AWT/Swing components based on the
056: * layout model.
057: *
058: * @author Jan Stola, Tomas Pavek
059: */
060: public class SwingLayoutBuilder {
061:
062: /**
063: * Default value for PADDING_SEPARATED type of gap.
064: */
065: public static final int PADDING_SEPARATE_VALUE = 18;
066:
067: private LayoutModel layoutModel;
068:
069: /**
070: * Container being layed out.
071: */
072: private Container container;
073:
074: /**
075: * LayoutComponent for the container.
076: */
077: private LayoutComponent containerLC;
078:
079: /**
080: * Maps from component ID to Component.
081: */
082: private Map<String, Component> componentIDMap;
083:
084: private boolean designMode;
085:
086: public SwingLayoutBuilder(LayoutModel layoutModel,
087: Container container, String containerId, boolean designMode) {
088: componentIDMap = new HashMap<String, Component>();
089: this .layoutModel = layoutModel;
090: this .container = container;
091: this .containerLC = layoutModel.getLayoutComponent(containerId);
092: this .designMode = designMode;
093: }
094:
095: /**
096: * Sets up layout of a container and adds all components to it according
097: * to the layout model. This method is used for initial construction of
098: * the layout visual representation (layout view).
099: */
100: public void setupContainerLayout(Component[] components,
101: String[] compIds) {
102: // addComponentsToContainer(components, compIds);
103: for (int counter = 0; counter < components.length; counter++) {
104: componentIDMap.put(compIds[counter], components[counter]);
105: }
106: createLayout();
107: }
108:
109: /**
110: * Adds new components to a container (according to the layout model).
111: * This method is used for incremental updates of the layout view.
112: */
113: // public void addComponentsToContainer(Component[] components, String[] compIds) {
114: // if (components.length != compIds.length) {
115: // throw new IllegalArgumentException("Sizes must match");
116: // }
117: // for (int counter = 0; counter < components.length; counter++) {
118: // componentIDMap.put(compIds[counter], components[counter]);
119: // }
120: // layout();
121: // }
122: /**
123: * Removes components from a container. This method is used for incremental
124: * updates of the layout view.
125: */
126: public void removeComponentsFromContainer(Component[] components,
127: String[] compIds) {
128: if (components.length != compIds.length) {
129: throw new IllegalArgumentException("Sizes must match"); // NOI18N
130: }
131: for (int counter = 0; counter < components.length; counter++) {
132: componentIDMap.remove(compIds[counter]);
133: }
134: createLayout();
135: }
136:
137: /**
138: * Clears given container - removes all components. This method is used
139: * for incremental updates of the layout view.
140: */
141: public void clearContainer() {
142: // Issue 121068 - componentResized event lost, but needed by JSlider
143: // forces new componentResized event when the container is laid out
144: issue121068Hack(container);
145: container.removeAll();
146: componentIDMap.clear();
147: }
148:
149: private void issue121068Hack(Component component) {
150: if (component instanceof JSlider) {
151: component.setBounds(0, 0, 0, 0);
152: }
153: if (component instanceof Container) {
154: Container cont = (Container) component;
155: for (int i = 0; i < cont.getComponentCount(); i++) {
156: issue121068Hack(cont.getComponent(i));
157: }
158: }
159: }
160:
161: public void createLayout() {
162: Throwable th = null;
163: boolean reset = true;
164: container.removeAll();
165: try {
166: GroupLayout layout = new GroupLayout(container);
167: container.setLayout(layout);
168: GroupLayout.Group[] layoutGroups = new GroupLayout.Group[2];
169: // with multiple roots add the highest roots first so the components appear at the top
170: for (int i = containerLC.getLayoutRootCount() - 1; i >= 0; i--) {
171: for (int dim = 0; dim < layoutGroups.length; dim++) {
172: LayoutInterval interval = containerLC
173: .getLayoutRoot(i, dim);
174: GroupLayout.Group group = composeGroup(layout,
175: interval, true, true);
176: if (layoutGroups[dim] == null) {
177: layoutGroups[dim] = group;
178: } else { // add multiple roots into one parallel group
179: GroupLayout.ParallelGroup parallel;
180: if (!(layoutGroups[dim] instanceof GroupLayout.ParallelGroup)) {
181: parallel = layout.createParallelGroup();
182: parallel.add(layoutGroups[dim]);
183: layoutGroups[dim] = parallel;
184: } else {
185: parallel = (GroupLayout.ParallelGroup) layoutGroups[dim];
186: }
187: parallel.add(group);
188: }
189: }
190: }
191: layout.setHorizontalGroup(layoutGroups[0]);
192: layout.setVerticalGroup(layoutGroups[1]);
193: composeLinks(layout);
194: // Try to create the layout (to be able to reset it in case of some problem)
195: layout.layoutContainer(container);
196: layout.invalidateLayout(container);
197: reset = false;
198: } finally {
199: if (reset) {
200: container.setLayout(null);
201: }
202: }
203: }
204:
205: public void doLayout() {
206: container.doLayout();
207: }
208:
209: public static boolean isRelevantContainer(Container cont) {
210: LayoutManager layoutManager = cont.getLayout();
211: String name = (layoutManager == null) ? null : layoutManager
212: .getClass().getName();
213: return "org.jdesktop.layout.GroupLayout".equals(name)
214: || "javax.swing.GroupLayout".equals(name); // NOI18N
215: }
216:
217: // -----
218:
219: private GroupLayout.Group composeGroup(GroupLayout layout,
220: LayoutInterval interval, boolean first, boolean last) {
221: GroupLayout.Group group = null;
222: if (interval.isGroup()) {
223: if (interval.isParallel()) {
224: int groupAlignment = convertAlignment(interval
225: .getGroupAlignment());
226: boolean notResizable = interval
227: .getMaximumSize(designMode) == LayoutConstants.USE_PREFERRED_SIZE;
228: group = layout.createParallelGroup(groupAlignment,
229: !notResizable);
230: } else if (interval.isSequential()) {
231: group = layout.createSequentialGroup();
232: } else {
233: assert false;
234: }
235: Iterator subIntervals = interval.getSubIntervals();
236: while (subIntervals.hasNext()) {
237: LayoutInterval subInterval = (LayoutInterval) subIntervals
238: .next();
239: fillGroup(layout, group, subInterval, first, last
240: && (!interval.isSequential() || !subIntervals
241: .hasNext()));
242: if (first && interval.isSequential()) {
243: first = false;
244: }
245: }
246: } else {
247: group = layout.createSequentialGroup();
248: fillGroup(layout, group, interval, true, true);
249: }
250: return group;
251: }
252:
253: private void fillGroup(GroupLayout layout, GroupLayout.Group group,
254: LayoutInterval interval, boolean first, boolean last) {
255: int alignment = getIntervalAlignment(interval);
256: if (interval.isGroup()) {
257: if (group instanceof GroupLayout.SequentialGroup) {
258: ((GroupLayout.SequentialGroup) group).add(composeGroup(
259: layout, interval, first, last));
260: } else {
261: ((GroupLayout.ParallelGroup) group).add(
262: convertAlignment(alignment), composeGroup(
263: layout, interval, first, last));
264: }
265: } else {
266: int minimum = interval.getMinimumSize(designMode);
267: int preferred = interval.getPreferredSize(designMode);
268: int min = convertSize(minimum, interval);
269: int pref = convertSize(preferred, interval);
270: int max = convertSize(interval.getMaximumSize(designMode),
271: interval);
272: if (interval.isComponent()) {
273: LayoutComponent layoutComp = interval.getComponent();
274: Component comp = componentIDMap.get(layoutComp.getId());
275: assert (comp != null);
276: if (minimum == LayoutConstants.NOT_EXPLICITLY_DEFINED) {
277: int dimension = (layoutComp
278: .getLayoutInterval(LayoutConstants.HORIZONTAL) == interval) ? LayoutConstants.HORIZONTAL
279: : LayoutConstants.VERTICAL;
280: if ((dimension == LayoutConstants.HORIZONTAL)
281: && comp.getClass().getName().equals(
282: "javax.swing.JComboBox")) { // Issue 68612 // NOI18N
283: min = 0;
284: } else if (preferred >= 0) {
285: Dimension minDim = comp.getMinimumSize();
286: int compMin = (dimension == LayoutConstants.HORIZONTAL) ? minDim.width
287: : minDim.height;
288: if (compMin > preferred) {
289: min = convertSize(
290: LayoutConstants.USE_PREFERRED_SIZE,
291: interval);
292: }
293: }
294: }
295: if (group instanceof GroupLayout.SequentialGroup) {
296: ((GroupLayout.SequentialGroup) group).add(comp,
297: min, pref, max);
298: } else {
299: GroupLayout.ParallelGroup pGroup = (GroupLayout.ParallelGroup) group;
300: pGroup.add(convertAlignment(alignment), comp, min,
301: pref, max);
302: }
303: } else {
304: assert interval.isEmptySpace();
305: if (interval.isDefaultPadding(designMode)) {
306: assert (group instanceof GroupLayout.SequentialGroup);
307: GroupLayout.SequentialGroup seqGroup = (GroupLayout.SequentialGroup) group;
308: if (first || last) {
309: seqGroup.addContainerGap(pref, max);
310: } else {
311: LayoutConstants.PaddingType paddingType = interval
312: .getPaddingType();
313: if (paddingType == null
314: || paddingType == LayoutConstants.PaddingType.RELATED) {
315: seqGroup.addPreferredGap(
316: LayoutStyle.RELATED, pref, max);
317: } else if (paddingType == LayoutConstants.PaddingType.UNRELATED) {
318: seqGroup.addPreferredGap(
319: LayoutStyle.UNRELATED, pref, max);
320: } else if (paddingType == LayoutConstants.PaddingType.SEPARATE) {
321: // special case - SEPARATE padding not known by LayoutStyle
322: if (pref == GroupLayout.DEFAULT_SIZE) {
323: pref = PADDING_SEPARATE_VALUE;
324: }
325: if (max == GroupLayout.DEFAULT_SIZE) {
326: max = PADDING_SEPARATE_VALUE;
327: }
328: seqGroup.add(PADDING_SEPARATE_VALUE, pref,
329: max);
330: } else {
331: assert paddingType == LayoutConstants.PaddingType.INDENT;
332: // TBD
333: }
334: }
335: } else {
336: if (min < 0)
337: min = pref; // min == GroupLayout.PREFERRED_SIZE
338: min = Math.min(pref, min);
339: max = Math.max(pref, max);
340: if (group instanceof GroupLayout.SequentialGroup) {
341: ((GroupLayout.SequentialGroup) group).add(min,
342: pref, max);
343: } else {
344: ((GroupLayout.ParallelGroup) group).add(min,
345: pref, max);
346: }
347: }
348: }
349: }
350: }
351:
352: /**
353: * Filters out invalid use of BASELINE alignment (see issue 78035).
354: * This method is a last resort to avoid failure in building the view.
355: * See also LayoutModel.checkAndFixGroup method.
356: */
357: private static int getIntervalAlignment(LayoutInterval interval) {
358: int alignment = interval.getAlignment();
359: LayoutInterval group = interval.getParent();
360: if (group.isParallel()) {
361: int groupAlignment = group.getGroupAlignment();
362: if ((alignment == LayoutConstants.BASELINE && groupAlignment != LayoutConstants.BASELINE)
363: || (alignment != LayoutConstants.BASELINE && groupAlignment == LayoutConstants.BASELINE)) { // illegal combination, follow the group alignment
364: alignment = groupAlignment;
365: System.err
366: .println("WARNING: Illegal use of baseline alignment, ignoring interval's alignment."); // NOI18N
367: // assert false;
368: }
369: } else if (alignment != LayoutConstants.DEFAULT) {
370: System.err
371: .println("WARNING: Ignoring non-default alignment of interval in sequential group."); // NOI18N
372: // assert false;
373: }
374:
375: return alignment;
376: }
377:
378: private static int convertAlignment(int alignment) {
379: int groupAlignment = 0;
380: switch (alignment) {
381: case LayoutConstants.DEFAULT:
382: groupAlignment = GroupLayout.LEADING;
383: break;
384: case LayoutConstants.LEADING:
385: groupAlignment = GroupLayout.LEADING;
386: break;
387: case LayoutConstants.TRAILING:
388: groupAlignment = GroupLayout.TRAILING;
389: break;
390: case LayoutConstants.CENTER:
391: groupAlignment = GroupLayout.CENTER;
392: break;
393: case LayoutConstants.BASELINE:
394: groupAlignment = GroupLayout.BASELINE;
395: break;
396: default:
397: assert false;
398: break;
399: }
400: return groupAlignment;
401: }
402:
403: private int convertSize(int size, LayoutInterval interval) {
404: int convertedSize;
405: switch (size) {
406: case LayoutConstants.NOT_EXPLICITLY_DEFINED:
407: convertedSize = GroupLayout.DEFAULT_SIZE;
408: break;
409: case LayoutConstants.USE_PREFERRED_SIZE:
410: convertedSize = interval.isEmptySpace() ? convertSize(
411: interval.getPreferredSize(designMode), interval)
412: : GroupLayout.PREFERRED_SIZE;
413: break;
414: default:
415: assert (size >= 0);
416: convertedSize = size;
417: break;
418: }
419: return convertedSize;
420: }
421:
422: private void composeLinks(GroupLayout layout) {
423: composeLinks(layout, LayoutConstants.HORIZONTAL);
424: composeLinks(layout, LayoutConstants.VERTICAL);
425: }
426:
427: private void composeLinks(GroupLayout layout, int dimension) {
428:
429: Map<Integer, List<String>> links = SwingLayoutUtils
430: .createLinkSizeGroups(containerLC, dimension);
431:
432: Set<Integer> linksSet = links.keySet();
433: Iterator<Integer> i = linksSet.iterator();
434: while (i.hasNext()) {
435: List<String> group = links.get(i.next());
436: List<Component> components = new ArrayList<Component>();
437: for (int j = 0; j < group.size(); j++) {
438: String compId = group.get(j);
439: LayoutComponent lc = layoutModel
440: .getLayoutComponent(compId);
441: if (lc != null) {
442: Component comp = componentIDMap.get(lc.getId());
443: if (comp == null) {
444: return;
445: } else {
446: components.add(comp);
447: }
448: }
449: }
450: Component[] compArray = components
451: .toArray(new Component[components.size()]);
452: if (compArray != null) {
453: if (dimension == LayoutConstants.HORIZONTAL) {
454: layout.linkSize(compArray, GroupLayout.HORIZONTAL);
455: }
456: if (dimension == LayoutConstants.VERTICAL) {
457: layout.linkSize(compArray, GroupLayout.VERTICAL);
458: }
459: }
460: }
461: }
462:
463: }
|