KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tapestry > contrib > palette > Palette


1 // Copyright 2004, 2005 The Apache Software Foundation
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14

15 package org.apache.tapestry.contrib.palette;
16
17 import java.util.ArrayList JavaDoc;
18 import java.util.Collections JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Map JavaDoc;
23
24 import org.apache.tapestry.BaseComponent;
25 import org.apache.tapestry.IAsset;
26 import org.apache.tapestry.IForm;
27 import org.apache.tapestry.IMarkupWriter;
28 import org.apache.tapestry.IRequestCycle;
29 import org.apache.tapestry.IScript;
30 import org.apache.tapestry.PageRenderSupport;
31 import org.apache.tapestry.Tapestry;
32 import org.apache.tapestry.TapestryUtils;
33 import org.apache.tapestry.components.Block;
34 import org.apache.tapestry.form.IPropertySelectionModel;
35 import org.apache.tapestry.form.RequirableField;
36 import org.apache.tapestry.form.RequirableFieldSupport;
37 import org.apache.tapestry.html.Body;
38 import org.apache.tapestry.valid.IValidationDelegate;
39 import org.apache.tapestry.valid.ValidationStrings;
40 import org.apache.tapestry.valid.ValidatorException;
41
42 /**
43  * A component used to make a number of selections from a list. The general look is a pair of
44  * <select> elements. with a pair of buttons between them. The right element is a list of
45  * values that can be selected. The buttons move values from the right column ("available") to the
46  * left column ("selected").
47  * <p>
48  * This all takes a bit of JavaScript to accomplish (quite a bit), which means a {@link Body}
49  * component must wrap the Palette. If JavaScript is not enabled in the client browser, then the
50  * user will be unable to make (or change) any selections.
51  * <p>
52  * Cross-browser compatibility is not perfect. In some cases, the
53  * {@link org.apache.tapestry.contrib.form.MultiplePropertySelection}component may be a better
54  * choice.
55  * <p>
56  * <table border=1>
57  * <tr>
58  * <td>Parameter</td>
59  * <td>Type</td>
60  * <td>Direction</td>
61  * <td>Required</td>
62  * <td>Default</td>
63  * <td>Description</td>
64  * </tr>
65  * <tr>
66  * <td>selected</td>
67  * <td>{@link List}</td>
68  * <td>in</td>
69  * <td>yes</td>
70  * <td>&nbsp;</td>
71  * <td>A List of selected values. Possible selections are defined by the model; this should be a
72  * subset of the possible values. This may be null when the component is renderred. When the
73  * containing form is submitted, this parameter is updated with a new List of selected objects.
74  * <p>
75  * The order may be set by the user, as well, depending on the sortMode parameter.</td>
76  * </tr>
77  * <tr>
78  * <td>model</td>
79  * <td>{@link IPropertySelectionModel}</td>
80  * <td>in</td>
81  * <td>yes</td>
82  * <td>&nbsp;</td>
83  * <td>Works, as with a {@link org.apache.tapestry.form.PropertySelection}component, to define the
84  * possible values.</td>
85  * </tr>
86  * <tr>
87  * <td>sort</td>
88  * <td>string</td>
89  * <td>in</td>
90  * <td>no</td>
91  * <td>{@link SortMode#NONE}</td>
92  * <td>Controls automatic sorting of the options.</td>
93  * </tr>
94  * <tr>
95  * <td>rows</td>
96  * <td>int</td>
97  * <td>in</td>
98  * <td>no</td>
99  * <td>10</td>
100  * <td>The number of rows that should be visible in the Pallete's &lt;select&gt; elements.</td>
101  * </tr>
102  * <tr>
103  * <td>tableClass</td>
104  * <td>{@link String}</td>
105  * <td>in</td>
106  * <td>no</td>
107  * <td>tapestry-palette</td>
108  * <td>The CSS class for the table which surrounds the other elements of the Palette.</td>
109  * </tr>
110  * <tr>
111  * <td>selectedTitleBlock</td>
112  * <td>{@link Block}</td>
113  * <td>in</td>
114  * <td>no</td>
115  * <td>"Selected"</td>
116  * <td>If specified, allows a {@link Block}to be placed within the &lt;th&gt; reserved for the
117  * title above the selected items &lt;select&gt; (on the right). This allows for images or other
118  * components to be placed there. By default, the simple word <code>Selected</code> is used.</td>
119  * </tr>
120  * <tr>
121  * <td>availableTitleBlock</td>
122  * <td>{@link Block}</td>
123  * <td>in</td>
124  * <td>no</td>
125  * <td>"Available"</td>
126  * <td>As with selectedTitleBlock, but for the left column, of items which are available to be
127  * selected. The default is the word <code>Available</code>.</td>
128  * </tr>
129  * <tr>
130  * <td>selectImage <br>
131  * selectDisabledImage <br>
132  * deselectImage <br>
133  * deselectDisabledImage <br>
134  * upImage <br>
135  * upDisabledImage <br>
136  * downImage <br>
137  * downDisabledImage</td>
138  * <td>{@link IAsset}</td>
139  * <td>in</td>
140  * <td>no</td>
141  * <td>&nbsp;</td>
142  * <td>If any of these are specified then they override the default images provided with the
143  * component. This allows the look and feel to be customized relatively easily.
144  * <p>
145  * The most common reason to replace the images is to deal with backgrounds. The default images are
146  * anti-aliased against a white background. If a colored or patterned background is used, the
147  * default images will have an ugly white fringe. Until all browsers have full support for PNG
148  * (which has a true alpha channel), it is necessary to customize the images to match the
149  * background.</td>
150  * </tr>
151  * </table>
152  * <p>
153  * A Palette requires some CSS entries to render correctly ... especially the middle column, which
154  * contains the two or four buttons for moving selections between the two columns. The width and
155  * alignment of this column must be set using CSS. Additionally, CSS is commonly used to give the
156  * Palette columns a fixed width, and to dress up the titles. Here is an example of some CSS you can
157  * use to format the palette component:
158  *
159  * <pre>
160  *
161  *
162  *
163  *
164  *
165  *
166  *
167  * TABLE.tapestry-palette TH
168  * {
169  * font-size: 9pt;
170  * font-weight: bold;
171  * color: white;
172  * background-color: #330066;
173  * text-align: center;
174  * }
175  *
176  * TD.available-cell SELECT
177  * {
178  * font-weight: normal;
179  * background-color: #FFFFFF;
180  * width: 200px;
181  * }
182  *
183  * TD.selected-cell SELECT
184  * {
185  * font-weight: normal;
186  * background-color: #FFFFFF;
187  * width: 200px;
188  * }
189  *
190  * TABLE.tapestry-palette TD.controls
191  * {
192  * text-align: center;
193  * vertical-align: middle;
194  * width: 60px;
195  * }
196  *
197  *
198  *
199  *
200  *
201  *
202  *
203  * </pre>
204  *
205  * As of 4.0, Palette can indicate that it is required.
206  *
207  * @author Howard Lewis Ship
208  */

209
210 public abstract class Palette extends BaseComponent implements RequirableField
211 {
212     private static final int MAP_SIZE = 7;
213
214     /**
215      * A set of symbols produced by the Palette script. This is used to provide proper names for
216      * some of the HTML elements (&lt;select&gt; and &lt;button&gt; elements, etc.).
217      */

218     private Map JavaDoc _symbols;
219     
220     /** @since 3.0 * */
221     public abstract void setAvailableColumn(PaletteColumn column);
222
223     /** @since 3.0 * */
224     public abstract void setSelectedColumn(PaletteColumn column);
225
226     public abstract void setName(String JavaDoc name);
227
228     public abstract void setForm(IForm form);
229     
230     /** @since 4.0 */
231     public abstract void setRequiredMessage(String JavaDoc message);
232
233     protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle)
234     {
235         // Next few lines of code is similar to AbstractFormComponent (which, alas, extends from
236
// AbstractComponent, not from BaseComponent).
237

238         IForm form = TapestryUtils.getForm(cycle, this);
239
240         if (form.wasPrerendered(writer, this))
241             return;
242
243         setForm(form);
244
245         IValidationDelegate delegate = form.getDelegate();
246
247         delegate.setFormComponent(this);
248
249         form.getElementId(this);
250
251         RequirableFieldSupport requirableFieldSupport = getRequirableFieldSupport();
252         
253         if (form.isRewinding())
254             requirableFieldSupport.rewind(this, writer, cycle);
255
256         // Don't do any additional work if rewinding
257
// (some other action or form on the page).
258

259         if (!cycle.isRewinding())
260         {
261             // Lots of work to produce JavaScript and HTML for this sucker.
262

263             _symbols = new HashMap JavaDoc(MAP_SIZE);
264
265             runScript(cycle);
266
267             constructColumns();
268             
269             requirableFieldSupport.render(this, writer, cycle);
270         }
271
272         super.renderComponent(writer, cycle);
273     }
274
275     /**
276      * @see org.apache.tapestry.form.RequirableField#bind(org.apache.tapestry.IMarkupWriter, org.apache.tapestry.IRequestCycle)
277      */

278     public void bind(IMarkupWriter writer, IRequestCycle cycle) throws ValidatorException
279     {
280         handleSubmission(cycle);
281     }
282
283     /**
284      * @see org.apache.tapestry.AbstractComponent#finishLoad()
285      */

286     protected void finishLoad()
287     {
288         setRequiredMessage(ValidationStrings.getMessagePattern(ValidationStrings.REQUIRED_SELECT_FIELD, getPage().getLocale()));
289     }
290
291     /**
292      * @see org.apache.tapestry.form.RequirableField#getSubmittedValue(org.apache.tapestry.IRequestCycle)
293      */

294     public String JavaDoc getSubmittedValue(IRequestCycle cycle)
295     {
296         return cycle.getParameter(getName());
297     }
298
299     protected void cleanupAfterRender(IRequestCycle cycle)
300     {
301         _symbols = null;
302
303         setAvailableColumn(null);
304         setSelectedColumn(null);
305
306         super.cleanupAfterRender(cycle);
307     }
308
309     /**
310      * Executes the associated script, which generates all the JavaScript to support this Palette.
311      */

312     private void runScript(IRequestCycle cycle)
313     {
314         PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this);
315
316         setImage(pageRenderSupport, cycle, "selectImage", getSelectImage());
317         setImage(pageRenderSupport, cycle, "selectDisabledImage", getSelectDisabledImage());
318         setImage(pageRenderSupport, cycle, "deselectImage", getDeselectImage());
319         setImage(pageRenderSupport, cycle, "deselectDisabledImage", getDeselectDisabledImage());
320
321         if (isSortUser())
322         {
323             setImage(pageRenderSupport, cycle, "upImage", getUpImage());
324             setImage(pageRenderSupport, cycle, "upDisabledImage", getUpDisabledImage());
325             setImage(pageRenderSupport, cycle, "downImage", getDownImage());
326             setImage(pageRenderSupport, cycle, "downDisabledImage", getDownDisabledImage());
327         }
328
329         _symbols.put("palette", this);
330
331         getScript().execute(cycle, pageRenderSupport, _symbols);
332     }
333
334     /**
335      * Extracts its asset URL, sets it up for preloading, and assigns the preload reference as a
336      * script symbol.
337      */

338     private void setImage(PageRenderSupport pageRenderSupport, IRequestCycle cycle,
339             String JavaDoc symbolName, IAsset asset)
340     {
341         String JavaDoc URL = asset.buildURL(cycle);
342         String JavaDoc reference = pageRenderSupport.getPreloadedImageReference(URL);
343
344         _symbols.put(symbolName, reference);
345     }
346
347     public Map JavaDoc getSymbols()
348     {
349         return _symbols;
350     }
351
352     /**
353      * Constructs a pair of {@link PaletteColumn}s: the available and selected options.
354      */

355     private void constructColumns()
356     {
357         // Build a Set around the list of selected items.
358

359         List JavaDoc selected = getSelected();
360
361         if (selected == null)
362             selected = Collections.EMPTY_LIST;
363
364         String JavaDoc sortMode = getSort();
365
366         boolean sortUser = sortMode.equals(SortMode.USER);
367
368         List JavaDoc selectedOptions = null;
369
370         if (sortUser)
371         {
372             int count = selected.size();
373             selectedOptions = new ArrayList JavaDoc(count);
374
375             for (int i = 0; i < count; i++)
376                 selectedOptions.add(null);
377         }
378
379         PaletteColumn availableColumn = new PaletteColumn((String JavaDoc) _symbols.get("availableName"),
380                 getRows());
381         PaletteColumn selectedColumn = new PaletteColumn(getName(), getRows());
382
383         // Each value specified in the model will go into either the selected or available
384
// lists.
385

386         IPropertySelectionModel model = getModel();
387
388         int count = model.getOptionCount();
389
390         for (int i = 0; i < count; i++)
391         {
392             Object JavaDoc optionValue = model.getOption(i);
393
394             PaletteOption o = new PaletteOption(model.getValue(i), model.getLabel(i));
395
396             int index = selected.indexOf(optionValue);
397             boolean isSelected = index >= 0;
398
399             if (sortUser && isSelected)
400             {
401                 selectedOptions.set(index, o);
402                 continue;
403             }
404
405             PaletteColumn c = isSelected ? selectedColumn : availableColumn;
406
407             c.addOption(o);
408         }
409
410         if (sortUser)
411         {
412             Iterator JavaDoc i = selectedOptions.iterator();
413             while (i.hasNext())
414             {
415                 PaletteOption o = (PaletteOption) i.next();
416                 selectedColumn.addOption(o);
417             }
418         }
419
420         if (sortMode.equals(SortMode.VALUE))
421         {
422             availableColumn.sortByValue();
423             selectedColumn.sortByValue();
424         }
425         else if (sortMode.equals(SortMode.LABEL))
426         {
427             availableColumn.sortByLabel();
428             selectedColumn.sortByLabel();
429         }
430
431         setAvailableColumn(availableColumn);
432         setSelectedColumn(selectedColumn);
433     }
434
435     private void handleSubmission(IRequestCycle cycle)
436     {
437         String JavaDoc[] values = cycle.getParameters(getName());
438
439         int count = Tapestry.size(values);
440
441         if (count == 0)
442             return;
443
444         List JavaDoc selected = new ArrayList JavaDoc(count);
445         IPropertySelectionModel model = getModel();
446
447         for (int i = 0; i < count; i++)
448         {
449             String JavaDoc value = values[i];
450             Object JavaDoc option = model.translateValue(value);
451
452             selected.add(option);
453         }
454
455         setSelected(selected);
456     }
457
458     public boolean isSortUser()
459     {
460         return getSort() == SortMode.USER;
461     }
462
463     /**
464      * Returns null, but may make sense to implement a displayName parameter.
465      */

466     public String JavaDoc getDisplayName()
467     {
468         return null;
469     }
470
471     public abstract Block getAvailableTitleBlock();
472
473     public abstract IAsset getDeselectDisabledImage();
474
475     public abstract IAsset getDeselectImage();
476
477     public abstract IAsset getDownDisabledImage();
478
479     public abstract IAsset getDownImage();
480
481     public abstract IAsset getSelectDisabledImage();
482
483     public abstract IPropertySelectionModel getModel();
484
485     public abstract int getRows();
486
487     public abstract Block getSelectedTitleBlock();
488
489     public abstract IAsset getSelectImage();
490
491     public abstract String JavaDoc getSort();
492
493     public abstract IAsset getUpDisabledImage();
494
495     public abstract IAsset getUpImage();
496
497     /**
498      * Returns false. Palette components are never disabled.
499      *
500      * @since 2.2
501      */

502     public boolean isDisabled()
503     {
504         return false;
505     }
506
507     /** @since 2.2 * */
508
509     public abstract List JavaDoc getSelected();
510
511     /** @since 2.2 * */
512
513     public abstract void setSelected(List JavaDoc selected);
514
515     /**
516      * Injected.
517      * @since 4.0
518      */

519     public abstract IScript getScript();
520     
521     /**
522      * Injected.
523      * @since 4.0
524      */

525     public abstract RequirableFieldSupport getRequirableFieldSupport();
526 }
Popular Tags