001: /*
002: * $Id: RadioChoice.java 5231 2006-04-01 23:34:49 +0000 (Sat, 01 Apr 2006)
003: * joco01 $ $Revision: 469206 $ $Date: 2006-04-01 23:34:49 +0000 (Sat, 01 Apr
004: * 2006) $
005: *
006: * ==============================================================================
007: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
008: * use this file except in compliance with the License. You may obtain a copy of
009: * the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
015: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
016: * License for the specific language governing permissions and limitations under
017: * the License.
018: */
019: package wicket.markup.html.form;
020:
021: import java.util.List;
022:
023: import wicket.Page;
024: import wicket.markup.ComponentTag;
025: import wicket.markup.MarkupStream;
026: import wicket.model.IModel;
027: import wicket.util.string.AppendingStringBuffer;
028: import wicket.util.string.Strings;
029: import wicket.version.undo.Change;
030:
031: /**
032: * A choice subclass that shows choices in radio style.
033: * <p>
034: * Java:
035: *
036: * <pre>
037: * List SITES = Arrays.asList(new String[] { "The Server Side", "Java Lobby", "Java.Net" });
038: * // Add a radio choice component that uses Input's 'site' property to designate the
039: * // current selection, and that uses the SITES list for the available options.
040: * form.add(new RadioChoice("site", SITES));
041: * </pre>
042: *
043: * HTML:
044: *
045: * <pre>
046: * <span valign="top" wicket:id="site">
047: * <input type="radio">site 1</input>
048: * <input type="radio">site 2</input>
049: * </span>
050: * </pre>
051: *
052: * </p>
053: *
054: * <p>
055: * You can can extend this class and override method
056: * wantOnSelectionChangedNotifications() to force server roundtrips on each
057: * selection change.
058: * </p>
059: *
060: * @author Jonathan Locke
061: * @author Igor Vaynberg (ivaynberg)
062: */
063: public class RadioChoice extends AbstractSingleSelectChoice implements
064: IOnChangeListener {
065: private static final long serialVersionUID = 1L;
066:
067: /** suffix change record. */
068: private class SuffixChange extends Change {
069: private static final long serialVersionUID = 1L;
070:
071: final String prevSuffix;
072:
073: SuffixChange(String prevSuffix) {
074: this .prevSuffix = prevSuffix;
075: }
076:
077: /**
078: * @see wicket.version.undo.Change#undo()
079: */
080: public void undo() {
081: setSuffix(prevSuffix);
082: }
083:
084: /**
085: * @see java.lang.Object#toString()
086: */
087: public String toString() {
088: return "SuffixChange[component: " + getPath()
089: + ", suffix: " + prevSuffix + "]";
090: }
091: }
092:
093: /**
094: * Prefix change record.
095: */
096: private class PrefixChange extends Change {
097: private static final long serialVersionUID = 1L;
098:
099: private final String prevPrefix;
100:
101: /**
102: * Construct.
103: *
104: * @param prevSuffix
105: */
106: PrefixChange(String prevSuffix) {
107: this .prevPrefix = prevSuffix;
108: }
109:
110: /**
111: * @see wicket.version.undo.Change#undo()
112: */
113: public void undo() {
114: setPrefix(prevPrefix);
115: }
116:
117: /**
118: * @see java.lang.Object#toString()
119: */
120: public String toString() {
121: return "PrefixChange[component: " + getPath()
122: + ", prefix: " + prevPrefix + "]";
123: }
124: }
125:
126: private String prefix = "";
127: private String suffix = "<br />\n";
128:
129: /**
130: * Constructor
131: *
132: * @param id
133: * See Component
134: * @see wicket.Component#Component(String)
135: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String)
136: */
137: public RadioChoice(final String id) {
138: super (id);
139: }
140:
141: /**
142: * Constructor
143: *
144: * @param id
145: * See Component
146: * @param choices
147: * The collection of choices in the radio choice
148: * @see wicket.Component#Component(String)
149: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String, List)
150: */
151: public RadioChoice(final String id, final List choices) {
152: super (id, choices);
153: }
154:
155: /**
156: * Constructor
157: *
158: * @param id
159: * See Component
160: * @param renderer
161: * The rendering engine
162: * @param choices
163: * The collection of choices in the radio choice
164: * @see wicket.Component#Component(String)
165: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
166: * List,IChoiceRenderer)
167: */
168: public RadioChoice(final String id, final List choices,
169: final IChoiceRenderer renderer) {
170: super (id, choices, renderer);
171: }
172:
173: /**
174: * Constructor
175: *
176: * @param id
177: * See Component
178: * @param model
179: * See Component
180: * @param choices
181: * The collection of choices in the radio choice
182: * @see wicket.Component#Component(String, IModel)
183: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
184: * IModel, List)
185: */
186: public RadioChoice(final String id, IModel model, final List choices) {
187: super (id, model, choices);
188: }
189:
190: /**
191: * Constructor
192: *
193: * @param id
194: * See Component
195: * @param model
196: * See Component
197: * @param choices
198: * The collection of choices in the radio choice
199: * @param renderer
200: * The rendering engine
201: * @see wicket.Component#Component(String, IModel)
202: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
203: * IModel, List,IChoiceRenderer)
204: */
205: public RadioChoice(final String id, IModel model,
206: final List choices, final IChoiceRenderer renderer) {
207: super (id, model, choices, renderer);
208: }
209:
210: /**
211: * Constructor
212: *
213: * @param id
214: * See Component
215: * @param choices
216: * The collection of choices in the radio choice
217: * @see wicket.Component#Component(String)
218: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
219: * IModel)
220: */
221: public RadioChoice(String id, IModel choices) {
222: super (id, choices);
223: }
224:
225: /**
226: * Constructor
227: *
228: * @param id
229: * See Component
230: * @param model
231: * The model that is updated with changes in this component. See
232: * Component
233: * @param choices
234: * The collection of choices in the radio choice
235: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
236: * IModel,IModel)
237: * @see wicket.Component#Component(String, IModel)
238: */
239: public RadioChoice(String id, IModel model, IModel choices) {
240: super (id, model, choices);
241: }
242:
243: /**
244: * Constructor
245: *
246: * @param id
247: * See Component
248: * @param choices
249: * The collection of choices in the radio choice
250: * @param renderer
251: * The rendering engine
252: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
253: * IModel,IChoiceRenderer)
254: * @see wicket.Component#Component(String)
255: */
256: public RadioChoice(String id, IModel choices,
257: IChoiceRenderer renderer) {
258: super (id, choices, renderer);
259: }
260:
261: /**
262: * Constructor
263: *
264: * @param id
265: * See Component
266: * @param model
267: * The model that is updated with changes in this component. See
268: * Component
269: * @param choices
270: * The collection of choices in the radio choice
271: * @param renderer
272: * The rendering engine
273: * @see wicket.Component#Component(String, IModel)
274: * @see wicket.markup.html.form.AbstractChoice#AbstractChoice(String,
275: * IModel, IModel,IChoiceRenderer)
276: */
277: public RadioChoice(String id, IModel model, IModel choices,
278: IChoiceRenderer renderer) {
279: super (id, model, choices, renderer);
280: }
281:
282: /**
283: * @see wicket.markup.html.form.IOnChangeListener#onSelectionChanged()
284: */
285: public void onSelectionChanged() {
286: convert();
287: updateModel();
288: onSelectionChanged(getModelObject());
289: }
290:
291: /**
292: * Template method that can be overriden by clients that implement
293: * IOnChangeListener to be notified by onChange events of a select element.
294: * This method does nothing by default.
295: * <p>
296: * Called when a option is selected of a dropdown list that wants to be
297: * notified of this event. This method is to be implemented by clients that
298: * want to be notified of selection events.
299: *
300: * @param newSelection
301: * The newly selected object of the backing model NOTE this is
302: * the same as you would get by calling getModelObject() if the
303: * new selection were current
304: */
305: protected void onSelectionChanged(Object newSelection) {
306: }
307:
308: /**
309: * Whether this component's onSelectionChanged event handler should called
310: * using javascript if the selection changes. If true, a roundtrip will be
311: * generated with each selection change, resulting in the model being
312: * updated (of just this component) and onSelectionChanged being called.
313: * This method returns false by default.
314: *
315: * @return True if this component's onSelectionChanged event handler should
316: * called using javascript if the selection changes
317: */
318: protected boolean wantOnSelectionChangedNotifications() {
319: return false;
320: }
321:
322: /**
323: * @return Prefix to use before choice
324: */
325: public final String getPrefix() {
326: return prefix;
327: }
328:
329: /**
330: * @param prefix
331: * Prefix to use before choice
332: * @return this
333: */
334: public final RadioChoice setPrefix(String prefix) {
335: // Tell the page that this component's prefix was changed
336: final Page page = findPage();
337: if (page != null) {
338: addStateChange(new PrefixChange(this .prefix));
339: }
340:
341: this .prefix = prefix;
342: return this ;
343: }
344:
345: /**
346: * @return Separator to use between radio options
347: */
348: public final String getSuffix() {
349: return suffix;
350: }
351:
352: /**
353: * @param suffix
354: * Separator to use between radio options
355: * @return this
356: */
357: public final RadioChoice setSuffix(String suffix) {
358: // Tell the page that this component's suffix was changed
359: final Page page = findPage();
360: if (page != null) {
361: addStateChange(new SuffixChange(this .suffix));
362: }
363:
364: this .suffix = suffix;
365: return this ;
366: }
367:
368: /**
369: * @see wicket.Component#onComponentTagBody(MarkupStream, ComponentTag)
370: */
371: protected final void onComponentTagBody(
372: final MarkupStream markupStream, final ComponentTag openTag) {
373: // Iterate through choices
374: final List choices = getChoices();
375:
376: // Buffer to hold generated body
377: final AppendingStringBuffer buffer = new AppendingStringBuffer(
378: (choices.size() + 1) * 70);
379:
380: // The selected value
381: final String selected = getValue();
382:
383: // Loop through choices
384: for (int index = 0; index < choices.size(); index++) {
385: // Get next choice
386: final Object choice = choices.get(index);
387:
388: // Get label for choice
389: final String label = (String) getConverter().convert(
390: getChoiceRenderer().getDisplayValue(choice),
391: String.class);
392:
393: // If there is a display value for the choice, then we know that the
394: // choice is automatic in some way. If label is /null/ then we know
395: // that the choice is a manually created radio tag at some random
396: // location in the page markup!
397: if (label != null) {
398: // Append option suffix
399: buffer.append(getPrefix());
400:
401: String id = getChoiceRenderer().getIdValue(choice,
402: index);
403: final String idAttr = getInputName() + "_" + id;
404:
405: // Add radio tag
406: buffer
407: .append("<input name=\"")
408: .append(getInputName())
409: .append("\"")
410: .append(" type=\"radio\"")
411: .append(
412: (isSelected(choice, index, selected) ? " checked=\"checked\""
413: : "")).append(
414: (isEnabled() ? ""
415: : " disabled=\"disabled\""))
416: .append(" value=\"").append(id).append(
417: "\" id=\"").append(idAttr).append("\"");
418:
419: // Should a roundtrip be made (have onSelectionChanged called)
420: // when the option is clicked?
421: if (wantOnSelectionChangedNotifications()) {
422: final CharSequence url = urlFor(IOnChangeListener.INTERFACE);
423:
424: Form form = (Form) findParent(Form.class);
425: if (form != null) {
426: buffer.append(" onclick=\"").append(
427: form.getJsForInterfaceUrl(url)).append(
428: ";\"");
429: } else {
430: // NOTE: do not encode the url as that would give
431: // invalid JavaScript
432: buffer.append(
433: " onclick=\"window.location.href='")
434: .append(url).append(
435: "&" + getInputName()).append(
436: "=").append(id).append("';\"");
437: }
438: }
439:
440: buffer.append("/>");
441:
442: // Add label for radio button
443: String display = label;
444: if (localizeDisplayValues()) {
445: display = getLocalizer().getString(label, this ,
446: label);
447: }
448: CharSequence escaped = Strings.escapeMarkup(display,
449: false, true);
450: buffer.append("<label for=\"").append(idAttr).append(
451: "\">").append(escaped).append("</label>");
452:
453: // Append option suffix
454: buffer.append(getSuffix());
455: }
456: }
457:
458: // Replace body
459: replaceComponentTagBody(markupStream, openTag, buffer);
460: }
461: }
|