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 com.sun.rave.web.ui.renderer;
042:
043: import com.sun.rave.web.ui.component.Anchor;
044: import com.sun.rave.web.ui.component.Icon;
045: import com.sun.rave.web.ui.component.IconHyperlink;
046: import com.sun.rave.web.ui.component.Legend;
047: import com.sun.rave.web.ui.component.PropertySheet;
048: import com.sun.rave.web.ui.component.PropertySheetSection;
049: import com.sun.rave.web.ui.theme.Theme;
050: import com.sun.rave.web.ui.theme.ThemeImages;
051: import com.sun.rave.web.ui.theme.ThemeStyles;
052: import com.sun.rave.web.ui.util.RenderingUtilities;
053: import com.sun.rave.web.ui.util.ThemeUtilities;
054: import java.io.IOException;
055: import java.util.Iterator;
056: import java.util.List;
057:
058: import javax.faces.component.UIComponent;
059: import javax.faces.context.FacesContext;
060: import javax.faces.context.ResponseWriter;
061:
062: /**
063: * Renders a PropertySheet component.
064: */
065: public class PropertySheetRenderer extends javax.faces.render.Renderer {
066:
067: public static final String JUMPTOSECTIONTOOLTIP = "propertySheet.jumpToSectionTooltip";
068:
069: public static final String JUMPTOTOPTOOLTIP = "propertySheet.jumpToTopTooltip";
070:
071: public static final String JUMPTOTOP = "propertySheet.jumpToTop";
072:
073: /**
074: * Creates a new instance of PropertySheetRenderer.
075: */
076: public PropertySheetRenderer() {
077: }
078:
079: /**
080: * This renderer renders the component's children.
081: * @returns true
082: */
083: public boolean getRendersChildren() {
084: return true;
085: }
086:
087: /**
088: * Render a property sheet.
089: *
090: * @param context The current FacesContext
091: * @param component The PropertySheet object to render
092: * @param writer The current ResponseWriter
093: *
094: * @exception IOException if an input/output error occurs
095: */
096: public void encodeEnd(FacesContext context, UIComponent component)
097: throws IOException {
098: if (context == null || component == null) {
099: throw new NullPointerException();
100: }
101: if (!component.isRendered()) {
102: return;
103: }
104:
105: ResponseWriter writer = context.getResponseWriter();
106:
107: PropertySheet propertySheet = (PropertySheet) component;
108:
109: // Get the theme
110: //
111: Theme theme = ThemeUtilities.getTheme(context);
112:
113: writer.startElement("div", propertySheet);
114: writer.writeAttribute("id", component.getClientId(context),
115: "id");//NOI18N
116:
117: String propValue = RenderingUtilities.getStyleClasses(context,
118: component, theme
119: .getStyleClass(ThemeStyles.PROPERTY_SHEET));
120: writer.writeAttribute("class", propValue, null);
121:
122: propValue = propertySheet.getStyle();
123: if (propValue != null) {
124: writer.writeAttribute("style", propValue, "style");
125: }
126:
127: renderJumpLinks(context, propertySheet, theme, writer);
128: renderRequiredFieldsLegend(context, propertySheet, theme,
129: writer);
130: renderPropertySheetSections(context, propertySheet, theme,
131: writer);
132:
133: writer.endElement("div");
134: }
135:
136: /**
137: * Render the property sheet sections.
138: *
139: * @param context The current FacesContext
140: * @param propertySheet The PropertySheet object to render
141: * @param theme The Theme to reference.
142: * @param writer The current ResponseWriter
143: *
144: * @exception IOException if an input/output error occurs
145: */
146: protected void renderPropertySheetSections(FacesContext context,
147: PropertySheet propertySheet, Theme theme,
148: ResponseWriter writer) throws IOException {
149:
150: // From propertysheetsection template
151: //
152: // <!-- Separator before Section (except first one, unless jumplinks
153: // are rendered) -->
154: // This has to be done here since we know if jumpllinks were
155: // rendered, not the section. There was probably a request map
156: // attribute set to convey this. We control the spacer too.
157: //
158: List sections = propertySheet.getVisibleSections();
159: boolean haveJumpLinks = propertySheet.isJumpLinks()
160: && sections.size() > 1;
161: boolean renderSpacer = false;
162: Iterator sectionsIterator = sections.iterator();
163: while (sectionsIterator.hasNext()) {
164: PropertySheetSection section = (PropertySheetSection) sectionsIterator
165: .next();
166: renderAnchor(context, section, writer);
167:
168: // Orginally the spacer came after the section's
169: // opening div. Let's do it before. It should be equivalent.
170: // And the PropertySheet should control separators.
171: //
172: // If there are jumplinks render a spacer if there is more
173: // than one section.
174: // If there are no jumplinks, render a spacer unless
175: // it is the first section.
176: //
177: if (haveJumpLinks || renderSpacer) {
178: renderSpacer(context, section, theme, writer);
179: } else {
180: renderSpacer = true;
181: }
182: RenderingUtilities.renderComponent((UIComponent) section,
183: context);
184:
185: if (haveJumpLinks && sections.size() > 1) {
186: renderJumpToTopLink(context, propertySheet, theme,
187: writer);
188: }
189: }
190: }
191:
192: /**
193: * Render a required fields legend.
194: * If <code>propertySheet.getRequiredFields</code> returns null
195: * a spacer is rendered.
196: *
197: * @param context The current FacesContext
198: * @param propertySheet The PropertySheet object to render
199: * @param theme The Theme to reference.
200: * @param writer The current ResponseWriter
201: *
202: * @exception IOException if an input/output error occurs
203: */
204: protected void renderRequiredFieldsLegend(FacesContext context,
205: PropertySheet propertySheet, Theme theme,
206: ResponseWriter writer) throws IOException {
207:
208: // This should be a facet.
209: //
210: String requiredFields = propertySheet.getRequiredFields();
211: // Why isn't this boolean ?
212: //
213: if (requiredFields != null
214: && requiredFields.equalsIgnoreCase("true")) {//NOI18N
215: Legend legend = new Legend();
216: legend.setId(propertySheet.getId() + "_legend"); //NOI18N
217: //legend.setText(requiredFields);
218: // FIXME : This MUST become a CSS selector.
219: //
220: legend.setStyle("margin:0pt 10px 0pt 0px"); //NOI18N
221: RenderingUtilities.renderComponent(legend, context);
222: } else {
223: // FIXME : Needs to be theme.
224: //
225: Icon spacer = theme.getIcon(ThemeImages.DOT);
226: spacer.setHeight(20);
227: spacer.setWidth(1);
228: RenderingUtilities.renderComponent(spacer, context);
229: }
230: }
231:
232: /**
233: * Render a set of jump links.
234: *
235: * @param context The current FacesContext
236: * @param propertySheet The PropertySheet object to render
237: * @param theme The Theme to reference.
238: * @param writer The current ResponseWriter
239: *
240: * @exception IOException if an input/output error occurs
241: */
242: protected void renderJumpLinks(FacesContext context,
243: PropertySheet propertySheet, Theme theme,
244: ResponseWriter writer) throws IOException {
245:
246: // Don't render any jump links if they are not requested.
247: //
248: if (!propertySheet.isJumpLinks()) {
249: return;
250: }
251: // Or if there are no visible sections
252: //
253: List sections = propertySheet.getVisibleSections();
254: int numSections = sections.size();
255: if (numSections <= 1) {
256: return;
257: }
258:
259: // There seems to be a distinction if there are 4, 5 to 9,
260: // and greater than 9 property sheet sections. This should be a
261: // theme configurable parameter.
262: //
263: // If there are less than 5 sections, there will be
264: // 2 jump links per row.
265: // If there are greater than 5 sections there will be
266: // 3 junmp links per row.
267: // If there are more than 10 sections, there will be
268: // 4 jump links per row.
269: //
270: // Determine the number of sections
271: //
272: // Start the layout for the property sheet sections
273: // jump link area
274: //
275: int jumpLinksPerRow = numSections < 5 ? 2 : (numSections > 4
276: && numSections < 10 ? 3 : 4);
277:
278: // Start a div for the jump links table
279: //
280: writer.startElement("div", propertySheet);
281: writer.writeAttribute("class", theme
282: .getStyleClass(ThemeStyles.CONTENT_JUMP_SECTION_DIV),
283: null);
284:
285: writer.startElement("table", propertySheet);
286: writer.writeAttribute("border", "0", null);
287: writer.writeAttribute("cellspacing", "0", null);
288: writer.writeAttribute("cellpadding", "0", null);
289: writer.writeAttribute("title", "", null); //NOI18N
290:
291: // Optimize, just get the needed selectors once.
292: //
293: String jumpLinkDivStyle = theme
294: .getStyleClass(ThemeStyles.CONTENT_JUMP_LINK_DIV);
295: String jumpLinkStyle = theme
296: .getStyleClass(ThemeStyles.JUMP_LINK);
297:
298: Iterator sectionIterator = sections.iterator();
299: while (sectionIterator.hasNext()) {
300:
301: writer.startElement("tr", propertySheet);
302:
303: for (int i = 0; i < jumpLinksPerRow; ++i) {
304:
305: PropertySheetSection section = (PropertySheetSection) sectionIterator
306: .next();
307:
308: writer.startElement("td", propertySheet);
309: writer.startElement("span", propertySheet);
310: writer.writeAttribute("class", jumpLinkDivStyle, null);
311:
312: IconHyperlink jumpLink = new IconHyperlink();
313: jumpLink.setId(section.getId() + "_jumpLink");//NOI18N
314: jumpLink.setParent(propertySheet);
315: jumpLink.setIcon(ThemeImages.HREF_ANCHOR);
316: jumpLink.setBorder(0);
317: // Shouldn't this come from the section ?
318: //
319: String propValue = theme
320: .getMessage(JUMPTOSECTIONTOOLTIP);
321: if (propValue != null) {
322: jumpLink.setAlt(propValue);
323: jumpLink.setToolTip(propValue);
324: }
325:
326: propValue = section.getLabel();
327: if (propValue != null) {
328: jumpLink.setText(propValue);
329: }
330: jumpLink.setUrl("#_" + section.getId());
331: jumpLink.setStyleClass(jumpLinkStyle);
332:
333: // Render the jump link
334: //
335: RenderingUtilities.renderComponent(jumpLink, context);
336:
337: writer.endElement("span");
338: writer.endElement("td");
339:
340: // If we haven't created enough cells, we should.
341: //
342: if (!sectionIterator.hasNext()) {
343: while (++i < jumpLinksPerRow) {
344: writer.startElement("td", propertySheet);
345: writer.startElement("span", propertySheet);
346: writer.writeAttribute("class",
347: jumpLinkDivStyle, null);
348: writer.endElement("span");
349: writer.endElement("td");
350: }
351: break;
352: }
353: }
354: writer.endElement("tr");
355: }
356: writer.endElement("table");
357: writer.endElement("div");
358: }
359:
360: /**
361: * Does not participate in rendering a PropertySheet.
362: *
363: * @param context The current FacesContext
364: * @param component The PropertySheet object to render
365: * @param writer The current ResponseWriter
366: *
367: * @exception IOException if an input/output error occurs
368: */
369: public void encodeChildren(FacesContext context,
370: UIComponent component) throws IOException {
371: if (context == null || component == null) {
372: throw new NullPointerException();
373: }
374: }
375:
376: /**
377: * Render an anchor to the section.
378: *
379: * @param context The current FacesContext
380: * @param propertySheetSection The PropertySheetSection about to be rendered.
381: * @param writer The current ResponseWriter
382: *
383: * @exception IOException if an input/output error occurs
384: */
385: private void renderAnchor(FacesContext context,
386: PropertySheetSection propertySheetSection,
387: ResponseWriter writer) throws IOException {
388:
389: Anchor anchor = new Anchor();
390: anchor.setParent(propertySheetSection);
391: anchor.setId("_" + propertySheetSection.getId());//NOI18N
392: RenderingUtilities.renderComponent(anchor, context);
393: }
394:
395: /**
396: * Render a spacer before the section.
397: *
398: * @param context The current FacesContext
399: * @param propertySheet The PropertySheet being rendered
400: * @param propertySheetSection The PropertySheetSection about to be rendered.
401: * @param theme The Theme to reference.
402: * @param writer The current ResponseWriter
403: *
404: * @exception IOException if an input/output error occurs
405: */
406: private void renderSpacer(FacesContext context,
407: PropertySheetSection propertySheetSection, Theme theme,
408: ResponseWriter writer) throws IOException {
409:
410: Icon spacer = theme.getIcon(ThemeImages.DOT);
411: writer.startElement("div", null);
412: writer.writeAttribute("class", theme
413: .getStyleClass(ThemeStyles.CONTENT_LIN), null);
414:
415: spacer.setId(propertySheetSection.getId() + "_dot1"); //NOI18N
416: spacer.setParent(propertySheetSection);
417: spacer.setHeight(1);
418: spacer.setWidth(1);
419: RenderingUtilities.renderComponent(spacer, context);
420: writer.endElement("div");
421: }
422:
423: /**
424: * Render the back to top link
425: *
426: * @param context The current FacesContext
427: * @param propertySheet The PropertySheet being rendered
428: * @param propertySheetSection The PropertySheetSection about to be rendered.
429: * @param theme The Theme to reference.
430: * @param writer The current ResponseWriter
431: *
432: * @exception IOException if an input/output error occurs
433: */
434: private void renderJumpToTopLink(FacesContext context,
435: PropertySheet propertySheet, Theme theme,
436: ResponseWriter writer) throws IOException {
437:
438: writer.startElement("div", propertySheet);
439: writer.writeAttribute("class", theme
440: .getStyleClass(ThemeStyles.CONTENT_JUMP_TOP_DIV), null);
441:
442: // Should be facets ?
443: //
444: IconHyperlink jumpLink = new IconHyperlink();
445: jumpLink.setIcon(ThemeImages.HREF_TOP);
446: jumpLink.setBorder(0);
447: // Shouldn't this come from the section ?
448: //
449: String propValue = theme.getMessage(JUMPTOTOPTOOLTIP);
450: if (propValue != null) {
451: jumpLink.setAlt(propValue);
452: jumpLink.setToolTip(propValue);
453: }
454:
455: propValue = theme.getMessage(JUMPTOTOP);
456: if (propValue != null) {
457: jumpLink.setText(propValue);
458: }
459: jumpLink.setUrl("#"); //NOI18N
460: jumpLink.setStyleClass(theme
461: .getStyleClass(ThemeStyles.JUMP_TOP_LINK));
462:
463: // Render the jump link
464: //
465: RenderingUtilities.renderComponent(jumpLink, context);
466:
467: writer.endElement("div");
468: }
469: }
|