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-2007 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: package org.netbeans.modules.visualweb.web.ui.dt.component;
042:
043: import java.util.regex.Pattern;
044: import javax.faces.component.UIPanel;
045: import javax.faces.component.UIComponent;
046:
047: import org.w3c.dom.DocumentFragment;
048: import org.w3c.dom.Element;
049: import org.w3c.dom.Node;
050:
051: import com.sun.rave.designtime.DesignBean;
052: import com.sun.rave.designtime.DesignContext;
053: import com.sun.rave.designtime.DesignProperty;
054: import com.sun.rave.designtime.Result;
055: import com.sun.rave.designtime.faces.FacesDesignProperty;
056: import com.sun.rave.designtime.markup.BasicMarkupMouseRegion;
057: import com.sun.rave.designtime.markup.MarkupDesignBean;
058: import com.sun.rave.designtime.markup.MarkupDesignInfo;
059: import com.sun.rave.designtime.markup.MarkupMouseRegion;
060: import com.sun.rave.designtime.markup.MarkupPosition;
061: import com.sun.rave.designtime.markup.MarkupRenderContext;
062: import com.sun.rave.web.ui.component.Body;
063: import com.sun.rave.web.ui.component.PanelGroup;
064: import com.sun.rave.web.ui.component.PanelLayout;
065: import com.sun.rave.web.ui.component.Tab;
066: import com.sun.rave.web.ui.component.TabSet;
067: import org.netbeans.modules.visualweb.web.ui.dt.component.util.DesignUtil;
068: import org.netbeans.modules.visualweb.web.ui.dt.AbstractDesignInfo;
069:
070: /**
071: * DesignInfo for the <code>Tab</code> component. The following behavior is
072: * implemented:
073: * <ul>
074: * <li>Upon component creation, pre-set <code>text</code> property to the
075: * component's display name, "Tab".</li>
076: * <li>Clicking on the tab names will switch the containing TabSet's selected
077: * property to the clicked tab, unless the selected property contains a value
078: * binding expression, in which case nothing happens.</li>
079: * </ul>
080: *
081: * @author gjmurphy
082: * @author Tor Norbye
083: */
084: public class TabDesignInfo extends AbstractDesignInfo implements
085: MarkupDesignInfo {
086:
087: public TabDesignInfo() {
088: super (Tab.class);
089: }
090:
091: public Result beanCreatedSetup(DesignBean bean) {
092: super .beanCreatedSetup(bean);
093: DesignContext context = bean.getDesignContext();
094: DesignProperty textProperty = bean.getProperty("text"); //NOI18N
095: String suffix = DesignUtil.getNumericalSuffix(bean
096: .getInstanceName());
097: textProperty.setValue(bean.getBeanInfo().getBeanDescriptor()
098: .getDisplayName()
099: + " " + suffix);
100: selectTab(bean);
101: if (context.canCreateBean(PanelLayout.class.getName(), bean,
102: null)) {
103: DesignBean panelBean = context.createBean(PanelLayout.class
104: .getName(), bean, null);
105: panelBean.getDesignInfo().beanCreatedSetup(panelBean);
106: panelBean.getProperty("panelLayout").setValue(
107: getParentLayout(bean));
108: }
109: DesignBean parentBean = bean.getBeanParent();
110: if (parentBean.getInstance() instanceof Tab) {
111: DesignBean childBean = parentBean.getChildBean(0);
112: if (childBean.getInstance() instanceof PanelLayout)
113: context.deleteBean(childBean);
114: }
115: return Result.SUCCESS;
116: }
117:
118: public Result beanPastedSetup(DesignBean bean) {
119: DesignContext context = bean.getDesignContext();
120: DesignBean parentBean = bean.getBeanParent();
121: if (parentBean.getInstance() instanceof Tab) {
122: DesignBean childBean = parentBean.getChildBean(0);
123: if (childBean.getInstance() instanceof PanelLayout)
124: context.deleteBean(childBean);
125: }
126: return Result.SUCCESS;
127: }
128:
129: public Result beanDeletedCleanup(DesignBean bean) {
130: unselectTab(bean);
131: return Result.SUCCESS;
132: }
133:
134: public boolean acceptChild(DesignBean parentBean,
135: DesignBean childBean, Class childClass) {
136: if (parentBean.getInstance() instanceof Tab) {
137: Tab tab = (Tab) parentBean.getInstance();
138: // Do not allow tabs, once added to a tabset, to be reparented to a
139: // different tab, as this requrires some property clean-up, but there
140: // is currently no way to handle the reparent event.
141: if (childBean != null && childBean.getBeanParent() != null)
142: return false;
143: // Allow tabs inside of childless tabs, tabs with only one panel child, or
144: // tabs with one or more tab children
145: if (childClass.equals(Tab.class)) {
146: if (tab.getChildCount() != 1)
147: return true;
148: UIComponent component = (UIComponent) tab.getChildren()
149: .get(0);
150: if (!(component instanceof PanelLayout)
151: || component.getChildCount() == 0)
152: return true;
153: }
154: // Allow panels inside of childless tabs only
155: if (UIPanel.class.isAssignableFrom(childClass)
156: || PanelGroup.class.isAssignableFrom(childClass)
157: || PanelLayout.class.isAssignableFrom(childClass)) {
158: if (tab.getChildCount() == 0)
159: return true;
160: }
161: }
162: return false;
163: }
164:
165: public boolean acceptParent(DesignBean parentBean,
166: DesignBean childBean, Class childClass) {
167: Object parent = parentBean.getInstance();
168: if (parent instanceof TabSet)
169: return true;
170: if (parent instanceof Tab
171: && !(parentBean.getBeanParent().getBeanParent()
172: .getInstance() instanceof Tab))
173: return true;
174: return false;
175: }
176:
177: public void instanceNameChanged(DesignBean bean,
178: String oldInstanceName) {
179: // If instance name is changed, check to see whether this tab is the tabSet's
180: // currently selected tab. If so, updated the tabSet's selected property.
181: DesignBean tabSetBean = bean.getBeanParent();
182: while (tabSetBean != null
183: && !(tabSetBean.getInstance() instanceof TabSet)) {
184: tabSetBean.getProperty("selectedChildId").setValue(null);
185: tabSetBean = tabSetBean.getBeanParent();
186: }
187: if (tabSetBean != null) {
188: DesignProperty selectedProperty = tabSetBean
189: .getProperty("selected"); //NOI18N
190: if (oldInstanceName != null
191: && oldInstanceName.equals(selectedProperty
192: .getValue()))
193: selectedProperty.setValue(bean.getInstanceName());
194: }
195: }
196:
197: public void customizeRender(final MarkupDesignBean bean,
198: MarkupRenderContext renderContext) {
199: DocumentFragment documentFragment = renderContext
200: .getDocumentFragment();
201: MarkupPosition begin = renderContext.getBeginPosition();
202: MarkupPosition end = renderContext.getEndPosition();
203:
204: if (begin == end) {
205: return;
206: }
207:
208: assert begin.getUnderParent() == end.getUnderParent();
209:
210: Node n = begin.getBeforeSibling();
211: Element tabElement = null;
212:
213: while (n != null && tabElement == null) {
214: if (n.getNodeType() == Node.ELEMENT_NODE
215: && n.getLocalName().equals("td"))
216: tabElement = (Element) n;
217: n = n.getParentNode();
218: }
219:
220: if (tabElement != null) {
221: FacesDesignProperty selectedProperty = getTabSetSelectedProperty(bean);
222: if (selectedProperty != null
223: && !bean.getProperty("id").getValue().equals(
224: selectedProperty.getValue()))
225: registerTab(bean, renderContext, tabElement);
226: }
227:
228: }
229:
230: private void registerTab(final DesignBean bean,
231: MarkupRenderContext context, Element e) {
232: String styleClass = e.getAttribute("class"); // NOI18N
233:
234: if (styleClass.indexOf("disabled") == -1) { // NOI18N
235: MarkupMouseRegion region = new BasicMarkupMouseRegion() {
236: public boolean isClickable() {
237: return true;
238: }
239:
240: public Result regionClicked(int clickCount) {
241: selectTab(bean);
242: return Result.SUCCESS;
243: }
244: };
245:
246: context.associateMouseRegion(e, region);
247:
248: return;
249: }
250: }
251:
252: /**
253: * "Selects" the tab wrapped by the bean specified, in the designer, by walking
254: * up the component tree and setting the selected property of the ancestor
255: * tabSet to the tab's id. If the "selected" property is value bound, then
256: * no changes are made.
257: */
258: private static void selectTab(DesignBean bean) {
259: FacesDesignProperty property = getTabSetSelectedProperty(bean);
260: assert property != null;
261: if (property != null && !property.isBound()) {
262: property.setValue(bean.getInstanceName());
263: }
264: }
265:
266: /**
267: * "De-selects" the tab wrapped by the bean specified, in the designer, by walking
268: * up the component tree and setting the selected property of the ancestor
269: * tabSet to the id of the next selectable sibling tab.
270: */
271: private static void unselectTab(DesignBean bean) {
272: // Find a suitable sibling tab to select
273: DesignBean tabSet = null;
274: String id = null;
275: DesignBean parentBean = bean.getBeanParent();
276: if (parentBean.getInstance() instanceof TabSet) {
277: tabSet = bean.getBeanParent();
278: } else {
279: tabSet = getTabSetBean(bean);
280: DesignBean[] childrenBeans = parentBean.getChildBeans();
281: int index = 0;
282: while (index < childrenBeans.length
283: && bean != childrenBeans[index])
284: index++;
285: if (index > 0) {
286: id = childrenBeans[index - 1].getInstanceName();
287: } else if (childrenBeans.length > 1) {
288: id = childrenBeans[1].getInstanceName();
289: }
290: }
291: assert tabSet != null;
292: if (tabSet != null) {
293: FacesDesignProperty property = (FacesDesignProperty) tabSet
294: .getProperty("selected"); // NOI18N
295: assert property != null;
296: if (property != null
297: && !property.isBound()
298: && property.getValue().equals(
299: bean.getInstanceName())) {
300: property.setValue(id);
301: }
302: }
303: }
304:
305: private static FacesDesignProperty getTabSetSelectedProperty(
306: DesignBean tabBean) {
307: DesignBean tabSetBean = getTabSetBean(tabBean);
308: if (tabSetBean == null)
309: return null;
310: return (FacesDesignProperty) tabSetBean.getProperty("selected");
311: }
312:
313: /**
314: * Walk up the component tree, starting with the tab bean specified, until
315: * the ancestor tabSet is found, and return it. If no parent tabSet is found
316: * returns null.
317: */
318: private static DesignBean getTabSetBean(DesignBean tabBean) {
319: DesignBean bean = tabBean.getBeanParent();
320: while (bean != null && !(bean.getInstance() instanceof TabSet))
321: bean = bean.getBeanParent();
322: return bean;
323: }
324:
325: private static Pattern gridPattern = Pattern
326: .compile(".*-rave-layout\\s*:\\s*grid.*");
327:
328: /**
329: * Walk up the component tree, starting with the tab bean specified, until
330: * the first enclosing body or div element with a "style" property is
331: * encountered. If there is a rave grid positioning style set, return a
332: * keyword indicating grid layout. Otherwise, returns a keyword indicating
333: * flow layout.
334: */
335: private static String getParentLayout(DesignBean bean) {
336: while (bean != null) {
337: if (bean.getInstance() instanceof Body
338: || (bean instanceof MarkupDesignBean && ((MarkupDesignBean) bean)
339: .getElement().getTagName().equals("div"))) {
340: String style = (String) bean.getProperty("style")
341: .getValue(); //NOI18N
342: if (style != null
343: && gridPattern.matcher(style).matches())
344: return PanelLayout.GRID_LAYOUT;
345: }
346: bean = bean.getBeanParent();
347: }
348: return PanelLayout.FLOW_LAYOUT;
349: }
350:
351: }
|