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.Button;
044: import com.sun.rave.web.ui.theme.Theme;
045: import com.sun.rave.web.ui.theme.ThemeStyles;
046: import com.sun.rave.web.ui.util.ConversionUtilities;
047: import com.sun.rave.web.ui.util.RenderingUtilities;
048: import com.sun.rave.web.ui.util.ThemeUtilities;
049:
050: import java.io.IOException;
051: import java.util.Map;
052:
053: import javax.faces.component.UIComponent;
054: import javax.faces.context.FacesContext;
055: import javax.faces.context.ResponseWriter;
056: import javax.faces.event.ActionEvent;
057:
058: /**
059: * <p>Renderer for a {@link Button} component.</p>
060: */
061: public class ButtonRenderer extends AbstractRenderer {
062: /**
063: * <p>The set of integer pass-through attributes to be rendered.</p>
064: */
065: private static final String integerAttributes[] = { "tabIndex" }; //NOI18N
066:
067: /**
068: * <p>The set of String pass-through attributes to be rendered.</p>
069: */
070: private static final String stringAttributes[] = { "dir", //NOI18N
071: "lang", //NOI18N
072: "onClick", //NOI18N
073: "onDblClick", //NOI18N
074: "onKeyDown", //NOI18N
075: "onKeyPress", //NOI18N
076: "onKeyUp", //NOI18N
077: "onMouseDown", //NOI18N
078: "onMouseUp", //NOI18N
079: "onMouseMove", //NOI18N
080: "style", //NOI18N
081: };
082:
083: /**
084: * The set of pass-through attributes rendered for input elements.
085: */
086: private static final String inputAttributes[] = { "alt", //NOI18N
087: "align" //NOI18N
088: };
089:
090: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
091: // Renderer Methods
092: // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
093:
094: /**
095: * <p>Determine if this was the component that submitted the form.</p>
096: *
097: * @param context <code>FacesContext</code> for the current request
098: * @param component <code>UIComponent</code> to be decoded
099: *
100: * @exception NullPointerException if <code>context</code> or
101: * <code>component</code> is <code>null</code>
102: */
103: public void decode(FacesContext context, UIComponent component) {
104: // Enforce NPE requirements in the Javadocs
105: if (context == null || component == null) {
106: throw new NullPointerException();
107: }
108:
109: Button button = (Button) component;
110:
111: // Do not process disabled or reset components.
112: if (button.isReset()) {
113: return;
114: }
115:
116: // Was our command the one that caused this submission?
117: String clientId = button.getClientId(context);
118: Map map = context.getExternalContext().getRequestParameterMap();
119:
120: if (map.containsKey(clientId)
121: || (map.containsKey(clientId + ".x") && map
122: .containsKey(clientId + ".y"))) {
123: button.queueEvent(new ActionEvent(button));
124:
125: // since this button submitted the form, we need to make it have
126: // focus next time through. To do this, we will set an attribute
127: // in the request map.
128: RenderingUtilities.setLastClientID(context, button
129: .getPrimaryElementID(context));
130: }
131: }
132:
133: /**
134: * <p>Render the appropriate element start, depending on the value of the
135: * <code>type</code> property.</p>
136: *
137: * @param context <code>FacesContext</code> for the current request
138: * @param component <code>UIComponent</code> to be rendered
139: * @param writer <code>ResponseWriter</code> to which the element
140: * start should be rendered
141: *
142: * @exception IOException if an input/output error occurs
143: */
144: protected void renderStart(FacesContext context,
145: UIComponent component, ResponseWriter writer)
146: throws IOException {
147: Button button = (Button) component;
148:
149: // Start the appropriate element
150: if (button.isEscape()) {
151: writer.startElement("input", button); //NOI18N
152: } else {
153: writer.startElement("button", button); //NOI18N
154: }
155: }
156:
157: /**
158: * <p>Render the appropriate element attributes depending on
159: * the value of the <code>imageURL</code> attribute.</p>
160: *
161: * @param context <code>FacesContext</code> for the current request
162: * @param component <code>UIComponent</code> to be rendered
163: * @param writer <code>ResponseWriter</code> to which the element
164: * attributes should be rendered
165: *
166: * @exception IOException if an input/output error occurs
167: */
168: protected void renderAttributes(FacesContext context,
169: UIComponent component, ResponseWriter writer)
170: throws IOException {
171: Button button = (Button) component;
172:
173: // Render client id and name.
174: //
175: // Note: Null is used when output is different than the original value
176: // of the component.
177: writer.writeAttribute("id", button.getClientId(context), null); //NOI18N
178: writer
179: .writeAttribute("name", button.getClientId(context),
180: null); //NOI18N
181:
182: // Render style classes.
183: String style = getStyle(button, ThemeUtilities
184: .getTheme(context));
185: RenderingUtilities.renderStyleClass(context, writer, button,
186: style);
187:
188: String js = getJavascript(button.getOnBlur(), "onblur"); //NOI18N
189: if (js != null) {
190: writer.writeAttribute("onblur", js, "onBlur"); //NOI18N
191: }
192:
193: js = getJavascript(button.getOnFocus(), "onfocus"); //NOI18N
194: if (js != null) {
195: writer.writeAttribute("onfocus", js, "onFocus"); //NOI18N
196: }
197:
198: js = getJavascript(button.getOnMouseOut(), "onmouseout"); //NOI18N
199: if (js != null) {
200: writer.writeAttribute("onmouseout", js, "onMouseOut"); //NOI18N
201: }
202:
203: js = getJavascript(button.getOnMouseOver(), "onmouseover"); //NOI18N
204: if (js != null) {
205: writer.writeAttribute("onmouseover", js, "onMouseOver"); //NOI18N
206: }
207:
208: // Render tooltip.
209: if (button.getToolTip() != null) {
210: writer.writeAttribute("title", button.getToolTip(),
211: "toolTip"); //NOI18N
212: }
213:
214: // Render disabled attribute.
215: if (button.isDisabled()) {
216: writer.writeAttribute("disabled", "disabled", null); //NOI18N
217: }
218:
219: // Render pass through attributes.
220: addIntegerAttributes(context, component, writer,
221: integerAttributes);
222: addStringAttributes(context, component, writer,
223: stringAttributes);
224:
225: // Render pass through attributes for input elements.
226: if (button.isEscape()) {
227: addStringAttributes(context, component, writer,
228: inputAttributes);
229: }
230:
231: // Note: Text attributes must be assigned last because the starting
232: // element may be closed here -- see bugtraq #6315893.
233: String url = button.getImageURL();
234: if (url != null) {
235: renderIconAttributes(context, component, writer, url);
236: } else {
237: renderTextAttributes(context, component, writer);
238: }
239: }
240:
241: /**
242: * <p>Render the appropriate element end, depending on the value of the
243: * <code>type</code> property.</p>
244: *
245: * @param context <code>FacesContext</code> for the current request
246: * @param component <code>UIComponent</code> to be rendered
247: * @param writer <code>ResponseWriter</code> to which the element
248: * end should be rendered
249: *
250: * @exception IOException if an input/output error occurs
251: */
252: protected void renderEnd(FacesContext context,
253: UIComponent component, ResponseWriter writer)
254: throws IOException {
255: Button button = (Button) component;
256:
257: // End the appropriate element
258: if (button.isEscape()) {
259: writer.endElement("input"); //NOI18N
260: } else {
261: writer.endElement("button"); //NOI18N
262: }
263:
264: // Initialize the JavaScript variable
265: StringBuffer jsBuffer = new StringBuffer(200);
266: jsBuffer.append("sjwuic_assign_button(");
267: jsBuffer.append("'"); //NOI18N
268: jsBuffer.append(button.getClientId(context));
269: jsBuffer.append("', "); //NOI18N
270: jsBuffer
271: .append((button.getImageURL() == null) ? "defaultButtonStrings" //NOI18N
272: : "defaultImgButtonStrings"); // NOI18N
273: jsBuffer.append(", "); //NOI18N
274: jsBuffer.append(!button.isPrimary());
275: jsBuffer.append(", "); //NOI18N
276: jsBuffer.append(button.isMini());
277: jsBuffer.append(", "); //NOI18N
278: jsBuffer.append(button.isDisabled());
279: jsBuffer.append(");"); //NOI18N
280:
281: writer.startElement("script", button); // NOI18N
282: writer.writeAttribute("type", "text/javascript", null); // NOI18N
283: writer.writeText(jsBuffer.toString(), null);
284: writer.endElement("script"); // NOI18N
285: }
286:
287: /**
288: * <p>Render the appropriate element attributes for an icon button.</p>
289: *
290: * @param context <code>FacesContext</code> for the current request
291: * @param component <code>UIComponent</code> to be rendered
292: * @param writer <code>ResponseWriter</code> to which the element
293: * attributes should be rendered
294: * @param url The image URL
295: *
296: * @exception IOException if an input/output error occurs
297: */
298: protected void renderIconAttributes(FacesContext context,
299: UIComponent component, ResponseWriter writer, String url)
300: throws IOException {
301: Button button = (Button) component;
302:
303: // Render type and source attributes.
304: writer.writeAttribute("type", "image", null); //NOI18N
305:
306: // <RAVE>
307: // Append context path to relative URLs
308: url = context.getApplication().getViewHandler().getResourceURL(
309: context, url);
310: // <RAVE>
311:
312: RenderingUtilities.renderURLAttribute(context, writer,
313: component, "src", //NOI18N
314: url, "imageURL"); //NOI18N
315: }
316:
317: /**
318: * <p>Render the appropriate element attributes for a text button.</p>
319: *
320: * @param context <code>FacesContext</code> for the current request
321: * @param component <code>UIComponent</code> to be rendered
322: * @param writer <code>ResponseWriter</code> to which the element
323: * attributes should be rendered
324: *
325: * @exception IOException if an input/output error occurs
326: */
327: protected void renderTextAttributes(FacesContext context,
328: UIComponent component, ResponseWriter writer)
329: throws IOException {
330: Button button = (Button) component;
331:
332: // Is this a reset button?
333: //
334: // Note: the button type is not supported for usability (isReset and
335: // isButton values would conflict).
336: if (button.isReset()) {
337: writer.writeAttribute("type", "reset", null); //NOI18N
338: } else {
339: writer.writeAttribute("type", "submit", null); //NOI18N
340: }
341:
342: // Get the textual label of the button.
343: String text = ConversionUtilities.convertValueToString(button,
344: button.getValue());
345: if (text == null || text.trim().length() == 0) {
346: return;
347: }
348:
349: // Pad the text if needed...
350: //
351: // Note: HCI_Admin used this code in their examples, but it may have
352: // been for Netscape 4.x. We may be able to do this with styles instead.
353: if (!button.isNoTextPadding()) {
354: if (text.trim().length() <= 3) {
355: text = " " + text + " "; //NOI18N
356: } else if (text.trim().length() == 4) {
357: text = " " + text + " "; //NOI18N
358: }
359: }
360:
361: // Render button text.
362: if (button.isEscape()) {
363: writer.writeAttribute("value", text, "text"); //NOI18N
364: } else {
365: // Note: This will close the starting tag -- see bugtraq #6315893.
366: writer.write(text);
367: }
368: }
369:
370: /**
371: * Get onblur style class.
372: *
373: * @param button <code>Button</code> to be rendered
374: * @param theme <code>Theme</code> for the component
375: */
376: protected String getOnBlurStyle(Button button, Theme theme) {
377: String style = null;
378:
379: if (button.getImageURL() != null) {
380: style = theme.getStyleClass(ThemeStyles.BUTTON3);
381: } else if (button.isMini() && !button.isPrimary()) {
382: style = theme.getStyleClass(ThemeStyles.BUTTON2_MINI);
383: } else if (button.isMini()) {
384: style = theme.getStyleClass(ThemeStyles.BUTTON1_MINI);
385: } else if (!button.isPrimary()) {
386: style = theme.getStyleClass(ThemeStyles.BUTTON2);
387: } else {
388: style = theme.getStyleClass(ThemeStyles.BUTTON1);
389: }
390:
391: return style;
392: }
393:
394: /**
395: * Get onfocus style class.
396: *
397: * @param button <code>Button</code> to be rendered
398: *@param theme <code>Theme</code> for the component
399: */
400: protected String getOnFocusStyle(Button button, Theme theme) {
401: String style = null;
402:
403: if (button.getImageURL() != null) {
404: style = theme.getStyleClass(ThemeStyles.BUTTON3_HOVER);
405: } else if (button.isMini() && !button.isPrimary()) {
406: style = theme.getStyleClass(ThemeStyles.BUTTON2_MINI_HOVER);
407: } else if (button.isMini()) {
408: style = theme.getStyleClass(ThemeStyles.BUTTON1_MINI_HOVER);
409: } else if (!button.isPrimary()) {
410: style = theme.getStyleClass(ThemeStyles.BUTTON2_HOVER);
411: } else {
412: style = theme.getStyleClass(ThemeStyles.BUTTON1_HOVER);
413: }
414:
415: return style;
416: }
417:
418: /**
419: * Get onmouseover style class.
420: *
421: * @param button <code>Button</code> to be rendered
422: * @param theme <code>Theme</code> for the component
423: */
424: protected String getOnMouseOverStyle(Button button, Theme theme) {
425: // The getOnfocusStyle method shares the same style classes.
426: return getOnFocusStyle(button, theme);
427: }
428:
429: /**
430: * Get onmouseout style class.
431: *
432: * @param button <code>Button</code> to be rendered
433: * @param theme <code>Theme</code> for the component
434: */
435: protected String getOnMouseOutStyle(Button button, Theme theme) {
436: // The getOnblurStyle method shares the same style classes.
437: return getOnBlurStyle(button, theme);
438: }
439:
440: /**
441: * Get style class.
442: *
443: * @param button <code>Button</code> to be rendered
444: * @param theme <code>Theme</code> for the component
445: */
446: protected String getStyle(Button button, Theme theme) {
447: // styles should always be appended
448: String style; // button style from theme.
449: if (button.getImageURL() != null) {
450: style = ThemeStyles.BUTTON3;
451: } else if (button.isMini() && !button.isPrimary()) {
452: style = button.isDisabled() ? theme
453: .getStyleClass(ThemeStyles.BUTTON2_MINI_DISABLED)
454: : theme.getStyleClass(ThemeStyles.BUTTON2_MINI);
455: } else if (button.isMini()) {
456: style = button.isDisabled() ? theme
457: .getStyleClass(ThemeStyles.BUTTON1_MINI_DISABLED)
458: : theme.getStyleClass(ThemeStyles.BUTTON1_MINI);
459: } else if (!button.isPrimary()) {
460: style = button.isDisabled() ? theme
461: .getStyleClass(ThemeStyles.BUTTON2_DISABLED)
462: : theme.getStyleClass(ThemeStyles.BUTTON2);
463: } else {
464: style = button.isDisabled() ? theme
465: .getStyleClass(ThemeStyles.BUTTON1_DISABLED)
466: : theme.getStyleClass(ThemeStyles.BUTTON1);
467: }
468:
469: return style;
470: }
471:
472: /**
473: * Helper method to set style classes during Javascript events such as
474: * onblur, onfocus, onmouseover, and onmouseout.
475: *
476: * @param value The existing attribute value to append Javascript to.
477: * @param jsmethod The JS event to invoke.
478: */
479: protected String getJavascript(String value, String jsmethod) {
480: if (jsmethod == null) {
481: return value;
482: }
483:
484: String event = "return this" + ".my" + jsmethod + "();"; //NOI18N
485:
486: return (value != null) ? value + ";" + event : event; //NOI18N
487: }
488: }
|