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.layoutsupport.delegates;
043:
044: import java.awt.*;
045: import javax.swing.*;
046: import java.lang.reflect.Method;
047:
048: import org.openide.nodes.Node;
049:
050: import org.netbeans.modules.form.layoutsupport.*;
051: import org.netbeans.modules.form.codestructure.*;
052: import org.netbeans.modules.form.*;
053:
054: /**
055: * Dedicated layout support class for JTabbedPane.
056: *
057: * @author Tomas Pavek
058: */
059:
060: public class JTabbedPaneSupport extends AbstractLayoutSupport {
061:
062: private int selectedTab = -1;
063:
064: private static Method addTabMethod1;
065: private static Method addTabMethod2;
066: private static Method addTabMethod3;
067:
068: /** Gets the supported layout manager class - JTabbedPane.
069: * @return the class supported by this delegate
070: */
071: public Class getSupportedClass() {
072: return JTabbedPane.class;
073: }
074:
075: /** Removes one component from the layout (at metadata level).
076: * The code structures describing the layout is updated immediately.
077: * @param index index of the component in the layout
078: */
079: @Override
080: public void removeComponent(int index) {
081: super .removeComponent(index);
082: if (selectedTab >= getComponentCount())
083: selectedTab = getComponentCount() - 1;
084: }
085:
086: /** This method is called when user clicks on the container in form
087: * designer. For JTabbedPane, we it switch the selected TAB.
088: * @param p Point of click in the container
089: * @param real instance of the container when the click occurred
090: * @param containerDelegate effective container delegate of the container
091: */
092: @Override
093: public void processMouseClick(Point p, Container container,
094: Container containerDelegate) {
095: if (!(container instanceof JTabbedPane))
096: return;
097:
098: JTabbedPane tabbedPane = (JTabbedPane) container;
099: int n = tabbedPane.getTabCount();
100: for (int i = 0; i < n; i++) {
101: if (tabbedPane.getBoundsAt(i).contains(p)) {
102: selectedTab = i;
103: tabbedPane.setSelectedIndex(i);
104: break;
105: }
106: }
107: }
108:
109: /** This method is called when a component is selected in Component
110: * Inspector.
111: * @param index position (index) of the selected component in container
112: */
113: @Override
114: public void selectComponent(int index) {
115: selectedTab = index; // remember as selected tab
116: }
117:
118: /** In this method, the layout delegate has a chance to "arrange" real
119: * container instance additionally - some other way that cannot be
120: * done through layout properties and added components.
121: * @param container instance of a real container to be arranged
122: * @param containerDelegate effective container delegate of the container
123: */
124: @Override
125: public void arrangeContainer(Container container,
126: Container containerDelegate) {
127: if (!(container instanceof JTabbedPane))
128: return;
129:
130: JTabbedPane tabbedPane = (JTabbedPane) container;
131: if (selectedTab >= 0) {
132: if (tabbedPane.getTabCount() > selectedTab) {
133: // select the tab
134: tabbedPane.setSelectedIndex(selectedTab);
135:
136: // workaround for JTabbedPane bug 4190719
137: Component comp = tabbedPane.getSelectedComponent();
138: if (comp != null)
139: comp.setVisible(true);
140: tabbedPane.repaint();
141: }
142: } else if (tabbedPane.getTabCount() > 0) {
143: // workaround for JTabbedPane bug 4190719
144: tabbedPane.getComponentAt(0).setVisible(true);
145: }
146: }
147:
148: /** This method should calculate position (index) for a component dragged
149: * over a container (or just for mouse cursor being moved over container,
150: * without any component).
151: * @param container instance of a real container over/in which the
152: * component is dragged
153: * @param containerDelegate effective container delegate of the container
154: * @param component the real component being dragged; not needed here
155: * @param index position (index) of the component in its current container;
156: * not needed here
157: * @param posInCont position of mouse in the container delegate; not needed
158: * @param posInComp position of mouse in the dragged component; not needed
159: * @return index corresponding to the position of the component in the
160: * container
161: */
162: @Override
163: public int getNewIndex(Container container,
164: Container containerDelegate, Component component,
165: int index, Point posInCont, Point posInComp) {
166: if (!(container instanceof JTabbedPane))
167: return -1;
168: return ((JTabbedPane) container).getTabCount();
169: }
170:
171: @Override
172: public String getAssistantContext() {
173: return "tabbedPaneLayout"; // NOI18N
174: }
175:
176: /** This method paints a dragging feedback for a component dragged over
177: * a container (or just for mouse cursor being moved over container,
178: * without any component).
179: * @param container instance of a real container over/in which the
180: * component is dragged
181: * @param containerDelegate effective container delegate of the container
182: * @param component the real component being dragged; not needed here
183: * @param newConstraints component layout constraints to be presented;
184: * not used for JTabbedPane
185: * @param newIndex component's index position to be presented; not needed
186: * @param g Graphics object for painting (with color and line style set)
187: * @return whether any feedback was painted (true in this case)
188: */
189: @Override
190: public boolean paintDragFeedback(Container container,
191: Container containerDelegate, Component component,
192: LayoutConstraints newConstraints, int newIndex, Graphics g) {
193: if (!(container instanceof JTabbedPane))
194: return false;
195:
196: JTabbedPane tabbedPane = (JTabbedPane) container;
197: if ((tabbedPane.getTabCount() == 0)
198: || (component == tabbedPane.getComponentAt(0))) {
199: Dimension sz = container.getSize();
200: Insets insets = container.getInsets();
201: sz.width -= insets.left + insets.right;
202: sz.height -= insets.top + insets.bottom;
203: g.drawRect(0, 0, sz.width, sz.height);
204: } else {
205: Rectangle rect = tabbedPane.getComponentAt(0).getBounds();
206: g.drawRect(rect.x, rect.y, rect.width, rect.height);
207: }
208: return true;
209: }
210:
211: /** Adds real components to given container (according to layout
212: * constraints stored for the components).
213: * @param container instance of a real container to be added to
214: * @param containerDelegate effective container delegate of the container
215: * @param components components to be added
216: * @param index position at which to add the components to container
217: */
218: @Override
219: public void addComponentsToContainer(Container container,
220: Container containerDelegate, Component[] components,
221: int index) {
222: if (!(container instanceof JTabbedPane))
223: return;
224:
225: for (int i = 0; i < components.length; i++) {
226: LayoutConstraints constraints = getConstraints(i + index);
227: if (constraints instanceof TabConstraints) {
228: JTabbedPane tabbedPane = (JTabbedPane) container;
229: try {
230: Object title = ((FormProperty) constraints
231: .getProperties()[0]).getRealValue();
232: Object icon = ((FormProperty) constraints
233: .getProperties()[1]).getRealValue();
234: Object tooltip = ((FormProperty) constraints
235: .getProperties()[2]).getRealValue();
236:
237: tabbedPane
238: .insertTab(
239: title instanceof String ? (String) title
240: : null,
241: icon instanceof Icon ? (Icon) icon
242: : null,
243: components[i],
244: tooltip instanceof String ? (String) tooltip
245: : null, index + i);
246: } catch (Exception ex) {
247: org.openide.ErrorManager.getDefault().notify(
248: org.openide.ErrorManager.INFORMATIONAL, ex);
249: }
250: }
251: }
252: }
253:
254: // ---------
255:
256: /** This method is used for scanning code structures and recognizing
257: * components added to containers and their constraints. It's called from
258: * initialize method. When a relevant code statement is found, then the
259: * CodeExpression of component is get and added to component, and also the
260: * layout constraints information is read.
261: * @param statement CodeStatement to be tested if it contains relevant code
262: * @param componentCode CodeGroup to be filled with all component code
263: * @return CodeExpression representing found component; null if the
264: * statement is not relevant
265: */
266: @Override
267: protected CodeExpression readComponentCode(CodeStatement statement,
268: CodeGroup componentCode) {
269: CodeExpression compExp;
270: int[] constrPropsIndices;
271: CodeExpression[] params = statement.getStatementParameters();
272:
273: Object connectingObject = statement.getMetaObject();
274: if (getAddTabMethod1().equals(connectingObject)) {
275: compExp = params[2];
276: constrPropsIndices = new int[] { 0, 1, -1, 2 }; // tab, icon, tooltip
277: } else if (getAddTabMethod2().equals(connectingObject)) {
278: compExp = params[2];
279: constrPropsIndices = new int[] { 0, 1, -1 }; // tab, icon
280: } else if (getAddTabMethod3().equals(connectingObject)) {
281: compExp = params[1];
282: constrPropsIndices = new int[] { 0, -1 }; // tab
283: } else
284: return null;
285:
286: TabConstraints constr = new TabConstraints("tab"); // NOI18N
287: Node.Property[] props = constr.getProperties();
288: for (int i = 0; i < params.length; i++) {
289: if (params[i] != compExp) {
290: Node.Property prop = props[constrPropsIndices[i]];
291: Object comp = compExp.getOrigin().getMetaObject();
292: if ((prop instanceof FormProperty)
293: && (comp instanceof RADComponent)) {
294: // Issue 124533
295: FormProperty fprop = (FormProperty) prop;
296: RADComponent metacomp = (RADComponent) comp;
297: fprop
298: .setPropertyContext(new FormPropertyContext.Component(
299: metacomp));
300: }
301: FormCodeSupport.readPropertyExpression(params[i], prop,
302: false);
303: }
304: }
305: getConstraintsList().add(constr);
306:
307: componentCode.addStatement(statement);
308:
309: return compExp;
310: }
311:
312: /** Creates code for a component added to the layout (opposite to
313: * readComponentCode method).
314: * @param componentCode CodeGroup to be filled with complete component code
315: * (code for initializing the layout constraints and adding the
316: * component to the layout)
317: * @param compExp CodeExpression object representing component
318: * @param index position of the component in the layout
319: */
320: @Override
321: protected void createComponentCode(CodeGroup componentCode,
322: CodeExpression componentExpression, int index) {
323: LayoutConstraints constr = getConstraints(index);
324: if (!(constr instanceof TabConstraints))
325: return; // should not happen
326:
327: ((TabConstraints) constr).createComponentCode(componentCode,
328: getLayoutContext().getContainerCodeExpression(),
329: componentExpression);
330: }
331:
332: /** This method is called to get a default component layout constraints
333: * metaobject in case it is not provided (e.g. in addComponents method).
334: * @return the default LayoutConstraints object for the supported layout;
335: * null if no component constraints are used
336: */
337: @Override
338: protected LayoutConstraints createDefaultConstraints() {
339: return new TabConstraints("tab" + (getComponentCount())); // NOI18N
340: }
341:
342: // ----------
343:
344: // tab, icon, component, tooltip
345: private static Method getAddTabMethod1() {
346: if (addTabMethod1 == null) {
347: try {
348: addTabMethod1 = JTabbedPane.class.getMethod("addTab", // NOI18N
349: new Class[] { String.class, Icon.class,
350: Component.class, String.class });
351: } catch (NoSuchMethodException ex) { // should not happen
352: ex.printStackTrace();
353: }
354: }
355: return addTabMethod1;
356: }
357:
358: // tab, icon, component
359: private static Method getAddTabMethod2() {
360: if (addTabMethod2 == null) {
361: try {
362: addTabMethod2 = JTabbedPane.class.getMethod("addTab", // NOI18N
363: new Class[] { String.class, Icon.class,
364: Component.class });
365: } catch (NoSuchMethodException ex) { // should not happen
366: ex.printStackTrace();
367: }
368: }
369: return addTabMethod2;
370: }
371:
372: // tab, component
373: private static Method getAddTabMethod3() {
374: if (addTabMethod3 == null) {
375: try {
376: addTabMethod3 = JTabbedPane.class.getMethod("addTab", // NOI18N
377: new Class[] { String.class, Component.class });
378: } catch (NoSuchMethodException ex) { // should not happen
379: ex.printStackTrace();
380: }
381: }
382: return addTabMethod3;
383: }
384:
385: // ----------
386:
387: /** LayoutConstraints implementation for managing JTabbedPane tab
388: * parameters.
389: */
390: public static class TabConstraints implements LayoutConstraints {
391: private String title;
392: private Icon icon;
393: private String toolTip;
394:
395: private FormProperty[] properties;
396:
397: private CodeExpression containerExpression;
398: private CodeExpression componentExpression;
399: private CodeGroup componentCode;
400: private CodeExpression[] propertyExpressions;
401:
402: public TabConstraints(String title) {
403: this .title = title;
404: }
405:
406: public TabConstraints(String title, Icon icon, String toolTip) {
407: this .title = title;
408: this .icon = icon;
409: this .toolTip = toolTip;
410: }
411:
412: public String getTitle() {
413: return title;
414: }
415:
416: public Icon getIcon() {
417: return icon;
418: }
419:
420: public String getToolTip() {
421: return toolTip;
422: }
423:
424: // -----------
425:
426: public Node.Property[] getProperties() {
427: if (properties == null) {
428: properties = new FormProperty[] {
429: new FormProperty("TabConstraints.tabTitle", // NOI18N
430: String.class, getBundle().getString(
431: "PROP_tabTitle"), // NOI18N
432: getBundle().getString("HINT_tabTitle")) { // NOI18N
433:
434: public Object getTargetValue() {
435: return title;
436: }
437:
438: public void setTargetValue(Object value) {
439: title = (String) value;
440: }
441:
442: @Override
443: protected Object getRealValue(Object value) {
444: Object realValue = super
445: .getRealValue(value);
446: if (realValue == FormDesignValue.IGNORED_VALUE)
447: realValue = ((FormDesignValue) value)
448: .getDescription();
449: return realValue;
450: }
451:
452: @Override
453: protected void propertyValueChanged(
454: Object old, Object current) {
455: if (isChangeFiring())
456: updateCode();
457: super
458: .propertyValueChanged(old,
459: current);
460: }
461: },
462:
463: new FormProperty("TabConstraints.tabIcon", // NOI18N
464: Icon.class, getBundle().getString(
465: "PROP_tabIcon"), // NOI18N
466: getBundle().getString("HINT_tabIcon")) { // NOI18N
467:
468: public Object getTargetValue() {
469: return icon;
470: }
471:
472: public void setTargetValue(Object value) {
473: icon = (Icon) value;
474: }
475:
476: @Override
477: public boolean supportsDefaultValue() {
478: return true;
479: }
480:
481: @Override
482: public Object getDefaultValue() {
483: return null;
484: }
485:
486: @Override
487: protected void propertyValueChanged(
488: Object old, Object current) {
489: if (isChangeFiring())
490: updateCode();
491: super
492: .propertyValueChanged(old,
493: current);
494: }
495: },
496:
497: new FormProperty("TabConstraints.tabToolTip", // NOI18N
498: String.class, getBundle().getString(
499: "PROP_tabToolTip"), // NOI18N
500: getBundle()
501: .getString("HINT_tabToolTip")) { // NOI18N
502:
503: public Object getTargetValue() {
504: return toolTip;
505: }
506:
507: public void setTargetValue(Object value) {
508: toolTip = (String) value;
509: }
510:
511: @Override
512: protected Object getRealValue(Object value) {
513: Object realValue = super
514: .getRealValue(value);
515: if (realValue == FormDesignValue.IGNORED_VALUE)
516: realValue = ((FormDesignValue) value)
517: .getDescription();
518: return realValue;
519: }
520:
521: @Override
522: public boolean supportsDefaultValue() {
523: return true;
524: }
525:
526: @Override
527: public Object getDefaultValue() {
528: return null;
529: }
530:
531: @Override
532: protected void propertyValueChanged(
533: Object old, Object current) {
534: if (isChangeFiring())
535: updateCode();
536: super
537: .propertyValueChanged(old,
538: current);
539: }
540: } };
541:
542: properties[0].setChanged(true);
543: }
544:
545: return properties;
546: }
547:
548: public Object getConstraintsObject() {
549: return title;
550: }
551:
552: public LayoutConstraints cloneConstraints() {
553: LayoutConstraints constr = new TabConstraints(title);
554: org.netbeans.modules.form.FormUtils.copyProperties(
555: getProperties(), constr.getProperties(),
556: FormUtils.CHANGED_ONLY
557: | FormUtils.DISABLE_CHANGE_FIRING);
558: return constr;
559: }
560:
561: // --------
562:
563: private void createComponentCode(CodeGroup compCode,
564: CodeExpression contExp, CodeExpression compExp) {
565: this .componentCode = compCode;
566: this .containerExpression = contExp;
567: this .componentExpression = compExp;
568: this .propertyExpressions = null;
569: updateCode();
570: }
571:
572: private void updateCode() {
573: if (componentCode == null)
574: return;
575:
576: CodeStructure.removeStatements(componentCode
577: .getStatementsIterator());
578: componentCode.removeAll();
579:
580: getProperties();
581:
582: Method addTabMethod;
583: CodeExpression[] params;
584:
585: if (properties[2].isChanged()) {
586: addTabMethod = getAddTabMethod1();
587: params = new CodeExpression[] {
588: getPropertyExpression(0), // tab
589: getPropertyExpression(1), // icon
590: componentExpression, getPropertyExpression(2) }; // tooltip
591: } else if (properties[1].isChanged()) {
592: addTabMethod = getAddTabMethod2();
593: params = new CodeExpression[] {
594: getPropertyExpression(0), // tab
595: getPropertyExpression(1), // icon
596: componentExpression };
597: } else { // tab
598: addTabMethod = getAddTabMethod3();
599: params = new CodeExpression[] {
600: getPropertyExpression(0), // tab
601: componentExpression };
602: }
603:
604: CodeStatement addTabStatement = CodeStructure
605: .createStatement(containerExpression, addTabMethod,
606: params);
607: componentCode.addStatement(addTabStatement);
608: }
609:
610: private CodeExpression getPropertyExpression(int index) {
611: if (propertyExpressions == null) {
612: propertyExpressions = new CodeExpression[properties.length];
613: for (int i = 0; i < properties.length; i++) {
614: propertyExpressions[i] = componentExpression
615: .getCodeStructure()
616: .createExpression(
617: FormCodeSupport
618: .createOrigin(properties[i]));
619: }
620: }
621: return propertyExpressions[index];
622: }
623: }
624: }
|