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;
043:
044: import java.awt.*;
045: import javax.swing.*;
046: import java.util.ArrayList;
047: import java.lang.reflect.Method;
048: import java.util.HashMap;
049: import java.util.Map;
050: import org.netbeans.modules.form.RADVisualComponent.MenuType;
051: import org.netbeans.modules.form.fakepeer.FakePeerSupport;
052:
053: import org.netbeans.modules.form.layoutsupport.*;
054: import org.openide.ErrorManager;
055:
056: public class RADVisualContainer extends RADVisualComponent implements
057: ComponentContainer {
058: private ArrayList<RADVisualComponent> subComponents = new ArrayList<RADVisualComponent>(
059: 10);
060: private LayoutSupportManager layoutSupport; // = new LayoutSupportManager();
061: private LayoutNode layoutNode; // [move to LayoutSupportManager?]
062:
063: private RADComponent containerMenu;
064:
065: private Method containerDelegateGetter;
066: private boolean noContainerDelegate;
067:
068: private static Map<MenuType, Class[]> supportedMenus;
069:
070: @Override
071: protected void setBeanInstance(Object beanInstance) {
072: containerDelegateGetter = null;
073: noContainerDelegate = false;
074:
075: super .setBeanInstance(beanInstance);
076:
077: if (layoutSupport != null) // need new layout support for new container bean
078: layoutSupport = new LayoutSupportManager(this ,
079: getFormModel().getCodeStructure());
080: }
081:
082: @Override
083: void setInModel(boolean in) {
084: boolean alreadyIn = isInModel();
085: super .setInModel(in);
086: if (in && !alreadyIn && layoutSupport != null) {
087: // deferred initialization from pre-creation
088: try {
089: layoutSupport.initializeLayoutDelegate();
090: } catch (Exception ex) {
091: // [not reported - but very unlikely to happen - only for new container with custom layout]
092: ErrorManager.getDefault().notify(
093: ErrorManager.INFORMATIONAL, ex);
094: layoutSupport.setUnknownLayoutDelegate(false);
095: }
096: }
097: }
098:
099: public void setLayoutSupportDelegate(
100: LayoutSupportDelegate layoutDelegate) throws Exception {
101: layoutSupport.setLayoutDelegate(layoutDelegate, false);
102: setLayoutNodeReference(null);
103: }
104:
105: public LayoutSupportManager getLayoutSupport() {
106: return layoutSupport;
107: }
108:
109: // public boolean isLayoutSupportSet() {
110: // return layoutSupport.getLayoutDelegate() != null;
111: // }
112:
113: public static boolean isFreeDesignContainer(RADComponent metacomp) {
114: return metacomp instanceof RADVisualContainer
115: && ((RADVisualContainer) metacomp).getLayoutSupport() == null;
116: }
117:
118: public static boolean isInFreeDesign(RADComponent metacomp) {
119: if (metacomp instanceof RADVisualComponent) {
120: RADVisualContainer parent = (RADVisualContainer) metacomp
121: .getParentComponent();
122: if (parent != null && parent.getLayoutSupport() == null
123: && metacomp != parent.getContainerMenu()) {
124: return true;
125: }
126: }
127: return false;
128: }
129:
130: void setOldLayoutSupport(boolean old) {
131: if (old) {
132: if (layoutSupport == null) {
133: layoutSupport = new LayoutSupportManager(this ,
134: getFormModel().getCodeStructure());
135: }
136: } else {
137: if (layoutSupport != null) { // clean the layout delegate and related code structre objects
138: try {
139: layoutSupport.setLayoutDelegate(null, false);
140: } catch (Exception ex) {
141: ErrorManager.getDefault().notify(
142: ErrorManager.INFORMATIONAL, ex);
143: }
144: refillContainerInstance();
145: }
146: layoutSupport = null;
147: setLayoutNodeReference(null);
148: }
149: }
150:
151: private void refillContainerInstance() {
152: Container cont = getContainerDelegate(getBeanInstance());
153: cont.removeAll();
154: if (!(cont instanceof ScrollPane)) { // Issue 128797
155: cont.setLayout(null); // Issue 77904
156: }
157: for (RADVisualComponent sub : subComponents) {
158: Component comp = (Component) sub.getBeanInstance();
159: FakePeerSupport.attachFakePeer(comp);
160: if (comp instanceof Container)
161: FakePeerSupport
162: .attachFakePeerRecursively((Container) comp);
163: cont.add(comp);
164: }
165: }
166:
167: public boolean hasDedicatedLayoutSupport() {
168: return layoutSupport != null && layoutSupport.isDedicated();
169: }
170:
171: /**
172: * @param container container.
173: * @return The JavaBean visual container represented by this
174: * RADVisualComponent
175: */
176: public Container getContainerDelegate(Object container) {
177: if (container instanceof RootPaneContainer
178: && container.getClass().getName().startsWith(
179: "javax.swing.")) // NOI18N
180: return ((RootPaneContainer) container).getContentPane();
181: if (container.getClass().equals(JRootPane.class))
182: return ((JRootPane) container).getContentPane();
183:
184: Container containerDelegate = (Container) container;
185: // Do not attempt to find container delegate if the classes
186: // don't match. This can happen when ViewConverter was used.
187: // Happens for JApplet, for example.
188: if (getBeanClass().isAssignableFrom(container.getClass())) {
189: Method m = getContainerDelegateMethod();
190: if (m != null) {
191: try {
192: containerDelegate = (Container) m.invoke(container,
193: new Object[0]);
194: if ((containerDelegate == null)
195: && (container instanceof JScrollPane)) {
196: JScrollPane scrollPane = (JScrollPane) container;
197: scrollPane.setViewportView(null); // force recreation of viewport
198: containerDelegate = (Container) m.invoke(
199: container, new Object[0]);
200: }
201: } catch (Exception ex) {
202: org.openide.ErrorManager.getDefault().notify(
203: org.openide.ErrorManager.INFORMATIONAL, ex);
204: }
205: }
206: }
207: return containerDelegate;
208: }
209:
210: public Method getContainerDelegateMethod() {
211: if (containerDelegateGetter == null && !noContainerDelegate) {
212: String delegateGetterName = getContainerDelegateGetterName();
213: if (delegateGetterName == null
214: && (RootPaneContainer.class
215: .isAssignableFrom(getBeanClass()) || JRootPane.class
216: .isAssignableFrom(getBeanClass())))
217: delegateGetterName = "getContentPane"; // NOI18N
218:
219: if (delegateGetterName != null) {
220: try {
221: containerDelegateGetter = getBeanClass().getMethod(
222: delegateGetterName, new Class[0]);
223: } catch (NoSuchMethodException ex) {
224: org.openide.ErrorManager.getDefault().notify(
225: org.openide.ErrorManager.INFORMATIONAL, ex);
226: }
227: } else
228: noContainerDelegate = true;
229: }
230: return containerDelegateGetter;
231: }
232:
233: String getContainerDelegateGetterName() {
234: Object value = getBeanInfo().getBeanDescriptor().getValue(
235: "containerDelegate"); // NOI18N
236:
237: if (value instanceof String)
238: return (String) value;
239: else
240: return null;
241: }
242:
243: public void setLayoutNodeReference(LayoutNode node) {
244: this .layoutNode = node;
245: }
246:
247: public LayoutNode getLayoutNodeReference() {
248: return layoutNode;
249: }
250:
251: boolean shouldHaveLayoutNode() {
252: return layoutSupport != null && layoutSupport.shouldHaveNode();
253: }
254:
255: public RADComponent getContainerMenu() {
256: return containerMenu;
257: }
258:
259: public boolean canAddComponent(Class compClass) {
260: if (isMenuTypeComponent()) {
261: // this is a menu container accepting certain types of menus
262: Class[] possibleClasses = getPossibleSubmenus(getMenuType(getBeanClass()));
263: if (possibleClasses != null) {
264: for (Class<?> cls : possibleClasses) {
265: if (cls.isAssignableFrom(compClass)) {
266: return true;
267: }
268: }
269: }
270: return false;
271: } else if (getContainerMenu() == null && canHaveMenu(compClass)) {
272: // visual container that can have a menubar
273: return true;
274: } else if (getMenuType(compClass) != null
275: && !JSeparator.class.isAssignableFrom(compClass)) {
276: // otherwise don't accept menu components
277: return false;
278: } else if (Component.class.isAssignableFrom(compClass)) {
279: // visual component can be added to visual container
280: // exception: avoid adding components to scroll pane that already contains something
281: if (JScrollPane.class.isAssignableFrom(getBeanClass())
282: && (((JScrollPane) getBeanInstance()).getViewport() != null)
283: && (((JScrollPane) getBeanInstance()).getViewport()
284: .getView() != null)) {
285: return false;
286: }
287: return true;
288: }
289: return false;
290: }
291:
292: boolean canHaveMenu(Class menuClass) {
293: return (JMenuBar.class.isAssignableFrom(menuClass) && RootPaneContainer.class
294: .isAssignableFrom(getBeanClass()))
295: || (MenuBar.class.isAssignableFrom(menuClass)
296: && Frame.class.isAssignableFrom(getBeanClass()) && !JFrame.class
297: .isAssignableFrom(getBeanClass()));
298: }
299:
300: private static Class[] getPossibleSubmenus(
301: MenuType menuContainerType) {
302: if (supportedMenus == null) {
303: supportedMenus = new HashMap<MenuType, Class[]>();
304: supportedMenus.put(MenuType.JMenuBar,
305: new Class[] { JMenu.class });
306: supportedMenus.put(MenuType.JMenu, new Class[] {
307: JMenuItem.class, JCheckBoxMenuItem.class,
308: JRadioButtonMenuItem.class, JMenu.class,
309: JSeparator.class });
310: supportedMenus.put(MenuType.JPopupMenu, new Class[] {
311: JMenuItem.class, JCheckBoxMenuItem.class,
312: JRadioButtonMenuItem.class, JMenu.class,
313: JSeparator.class });
314: }
315: return supportedMenus.get(menuContainerType);
316: }
317:
318: // -----------------------------------------------------------------------------
319: // SubComponents Management
320:
321: /** @return visual subcomponents (not the menu component) */
322: public RADVisualComponent[] getSubComponents() {
323: RADVisualComponent[] components = new RADVisualComponent[subComponents
324: .size()];
325: subComponents.toArray(components);
326: return components;
327: }
328:
329: public RADVisualComponent getSubComponent(int index) {
330: return subComponents.get(index);
331: }
332:
333: // the following methods implement ComponentContainer interface
334:
335: /** @return all subcomponents (including the menu component) */
336: public RADComponent[] getSubBeans() {
337: int n = subComponents.size();
338: if (containerMenu != null)
339: n++;
340:
341: RADComponent[] components = new RADComponent[n];
342: subComponents.toArray(components);
343: if (containerMenu != null)
344: components[n - 1] = containerMenu;
345:
346: return components;
347: }
348:
349: public void initSubComponents(RADComponent[] initComponents) {
350: if (subComponents == null)
351: subComponents = new ArrayList<RADVisualComponent>(
352: initComponents.length);
353: else {
354: subComponents.clear();
355: subComponents.ensureCapacity(initComponents.length);
356: }
357:
358: for (int i = 0; i < initComponents.length; i++) {
359: RADComponent metacomp = initComponents[i];
360: if (i == 0 && !isMenuTypeComponent()
361: && canHaveMenu(metacomp.getBeanClass())) {
362: containerMenu = metacomp;
363: } else {
364: subComponents.add((RADVisualComponent) metacomp);
365: }
366: metacomp.setParentComponent(this );
367: }
368:
369: if (layoutSupport == null)
370: refillContainerInstance();
371: }
372:
373: public void reorderSubComponents(int[] perm) {
374: RADVisualComponent[] components = new RADVisualComponent[subComponents
375: .size()];
376: LayoutConstraints[] constraints;
377: if (layoutSupport != null) {
378: layoutSupport.removeAll();
379: constraints = new LayoutConstraints[subComponents.size()];
380: } else
381: constraints = null;
382:
383: for (int i = 0; i < perm.length; i++) {
384: RADVisualComponent metacomp = subComponents.get(i);
385: components[perm[i]] = metacomp;
386: if (constraints != null)
387: constraints[perm[i]] = layoutSupport
388: .getStoredConstraints(metacomp);
389: }
390:
391: subComponents.clear();
392: subComponents.addAll(java.util.Arrays.asList(components));
393:
394: if (layoutSupport != null) {
395: layoutSupport.addComponents(components, constraints, 0);
396: } else {
397: refillContainerInstance();
398: }
399: }
400:
401: public void add(RADComponent comp) {
402: add(comp, -1);
403: }
404:
405: public void add(RADComponent metacomp, int index) {
406: RADVisualComponent visual;
407: if (index <= 0 && !isMenuTypeComponent()
408: && canHaveMenu(metacomp.getBeanClass())) {
409: containerMenu = metacomp;
410: visual = null;
411: } else {
412: visual = (RADVisualComponent) metacomp;
413: if (index == -1) {
414: index = subComponents.size();
415: subComponents.add(visual);
416: } else {
417: subComponents.add(index, visual);
418: }
419: if (layoutSupport == null) {
420: Component comp = (Component) visual.getBeanInstance();
421: FakePeerSupport.attachFakePeer(comp);
422: if (comp instanceof Container)
423: FakePeerSupport
424: .attachFakePeerRecursively((Container) comp);
425: getContainerDelegate(getBeanInstance())
426: .add(comp, index);
427: }
428: }
429:
430: metacomp.setParentComponent(this );
431: if (visual != null) { // force constraints properties creation
432: visual.getConstraintsProperties();
433: }
434: }
435:
436: public void remove(RADComponent comp) {
437: if (comp == containerMenu) {
438: containerMenu = null;
439: comp.setParentComponent(null);
440: } else if (comp instanceof RADVisualComponent) {
441: int index = subComponents.indexOf(comp);
442: if (layoutSupport != null) {
443: layoutSupport.removeComponent(
444: (RADVisualComponent) comp, index);
445: } else {
446: getContainerDelegate(getBeanInstance()).remove(index);
447: }
448: if (subComponents.remove(comp))
449: comp.setParentComponent(null);
450: }
451: }
452:
453: public int getIndexOf(RADComponent comp) {
454: if (comp != null && comp == containerMenu)
455: return subComponents.size();
456:
457: return subComponents.lastIndexOf(comp);
458: }
459: }
|