All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.tapestry.contrib.palette.Palette Maven / Gradle / Ivy

There is a newer version: 4.1.6
Show newest version
// Copyright 2004, 2005 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry.contrib.palette;

import org.apache.tapestry.*;
import org.apache.tapestry.components.Block;
import org.apache.tapestry.form.FormComponentContributorContext;
import org.apache.tapestry.form.IPropertySelectionModel;
import org.apache.tapestry.form.ValidatableFieldExtension;
import org.apache.tapestry.form.ValidatableFieldSupport;
import org.apache.tapestry.form.validator.Required;
import org.apache.tapestry.form.validator.Validator;
import org.apache.tapestry.html.Body;
import org.apache.tapestry.json.JSONLiteral;
import org.apache.tapestry.json.JSONObject;
import org.apache.tapestry.valid.IValidationDelegate;
import org.apache.tapestry.valid.ValidationConstants;
import org.apache.tapestry.valid.ValidatorException;

import java.util.*;

/**
 * A component used to make a number of selections from a list. The general look is a pair of
 * <select> elements. with a pair of buttons between them. The right element is a list of
 * values that can be selected. The buttons move values from the right column ("available") to the
 * left column ("selected").
 * 

* This all takes a bit of JavaScript to accomplish (quite a bit), which means a {@link Body} * component must wrap the Palette. If JavaScript is not enabled in the client browser, then the * user will be unable to make (or change) any selections. *

* Cross-browser compatibility is not perfect. In some cases, the * {@link org.apache.tapestry.contrib.form.MultiplePropertySelection}component may be a better * choice. *

*

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ParameterTypeDirectionRequiredDefaultDescription
selected{@link List}inyes A List of selected values. Possible selections are defined by the model; this should be a * subset of the possible values. This may be null when the component is renderred. When the * containing form is submitted, this parameter is updated with a new List of selected objects. *

* The order may be set by the user, as well, depending on the sortMode parameter.

model{@link IPropertySelectionModel}inyes Works, as with a {@link org.apache.tapestry.form.PropertySelection}component, to define the * possible values.
sortstringinno{@link SortMode#NONE}Controls automatic sorting of the options.
rowsintinno10The number of rows that should be visible in the Pallete's <select> elements.
tableClass{@link String}innotapestry-paletteThe CSS class for the table which surrounds the other elements of the Palette.
selectedTitleBlock{@link Block}inno"Selected"If specified, allows a {@link Block}to be placed within the <th> reserved for the * title above the selected items <select> (on the right). This allows for images or other * components to be placed there. By default, the simple word Selected is used.
availableTitleBlock{@link Block}inno"Available"As with selectedTitleBlock, but for the left column, of items which are available to be * selected. The default is the word Available.
selectImage
* selectDisabledImage
* deselectImage
* deselectDisabledImage
* upImage
* upDisabledImage
* downImage
* downDisabledImage
{@link IAsset}inno If any of these are specified then they override the default images provided with the * component. This allows the look and feel to be customized relatively easily. *

* The most common reason to replace the images is to deal with backgrounds. The default images are * anti-aliased against a white background. If a colored or patterned background is used, the * default images will have an ugly white fringe. Until all browsers have full support for PNG * (which has a true alpha channel), it is necessary to customize the images to match the * background.

*

* A Palette requires some CSS entries to render correctly ... especially the middle column, which * contains the two or four buttons for moving selections between the two columns. The width and * alignment of this column must be set using CSS. Additionally, CSS is commonly used to give the * Palette columns a fixed width, and to dress up the titles. Here is an example of some CSS you can * use to format the palette component: * *

 *                             TABLE.tapestry-palette TH
 *                             {
 *                               font-size: 9pt;
 *                               font-weight: bold;
 *                               color: white;
 *                               background-color: #330066;
 *                               text-align: center;
 *                             }
 *                            
 *                             TD.available-cell SELECT
 *                             {
 *                               font-weight: normal;
 *                               background-color: #FFFFFF;
 *                               width: 200px;
 *                             }
 *                             
 *                             TD.selected-cell SELECT
 *                             {
 *                               font-weight: normal;
 *                               background-color: #FFFFFF;
 *                               width: 200px;
 *                             }
 *                             
 *                             TABLE.tapestry-palette TD.controls
 *                             {
 *                               text-align: center;
 *                               vertical-align: middle;
 *                               width: 60px;
 *                             }
 * 
* *

* As of 4.0, this component can be validated. *

* * @author Howard Lewis Ship */ public abstract class Palette extends BaseComponent implements ValidatableFieldExtension { private static final int MAP_SIZE = 7; /** * A set of symbols produced by the Palette script. This is used to provide proper names for * some of the HTML elements (<select> and <button> elements, etc.). */ private Map _symbols; /** @since 3.0 * */ public abstract void setAvailableColumn(PaletteColumn column); /** @since 3.0 * */ public abstract void setSelectedColumn(PaletteColumn column); public abstract void setName(String name); public abstract void setForm(IForm form); /** @since 4.0 */ public abstract void setRequiredMessage(String message); protected void renderComponent(IMarkupWriter writer, IRequestCycle cycle) { // Next few lines of code is similar to AbstractFormComponent (which, alas, extends from // AbstractComponent, not from BaseComponent). IForm form = TapestryUtils.getForm(cycle, this); setForm(form); if (form.wasPrerendered(writer, this)) return; IValidationDelegate delegate = form.getDelegate(); delegate.setFormComponent(this); form.getElementId(this); if (form.isRewinding()) { if (!isDisabled()) { rewindFormComponent(writer, cycle); } } else if (!cycle.isRewinding()) { if (!isDisabled()) delegate.registerForFocus(this, ValidationConstants.NORMAL_FIELD); renderFormComponent(writer, cycle); if (delegate.isInError()) delegate.registerForFocus(this, ValidationConstants.ERROR_FIELD); } super.renderComponent(writer, cycle); } protected void renderFormComponent(IMarkupWriter writer, IRequestCycle cycle) { _symbols = new HashMap(MAP_SIZE); getForm().getDelegate().writePrefix(writer, cycle, this, null); runScript(cycle); constructColumns(); getValidatableFieldSupport().renderContributions(this, writer, cycle); getForm().getDelegate().writeSuffix(writer, cycle, this, null); } protected void rewindFormComponent(IMarkupWriter writer, IRequestCycle cycle) { String[] values = cycle.getParameters(getName()); int count = Tapestry.size(values); List selected = new ArrayList(count); IPropertySelectionModel model = getModel(); for (int i = 0; i < count; i++) { String value = values[i]; Object option = model.translateValue(value); selected.add(option); } setSelected(selected); try { getValidatableFieldSupport().validate(this, writer, cycle, selected); } catch (ValidatorException e) { getForm().getDelegate().record(e); } } /** * {@inheritDoc} */ public void overrideContributions(Validator validator, FormComponentContributorContext context, IMarkupWriter writer, IRequestCycle cycle) { // we know this has to be a Required validator Required required = (Required)validator; JSONObject profile = context.getProfile(); if (!profile.has(ValidationConstants.CONSTRAINTS)) { profile.put(ValidationConstants.CONSTRAINTS, new JSONObject()); } JSONObject cons = profile.getJSONObject(ValidationConstants.CONSTRAINTS); required.accumulateProperty(cons, getClientId(), new JSONLiteral("[tapestry.form.validation.isPalleteSelected]")); required.accumulateProfileProperty(this, profile, ValidationConstants.CONSTRAINTS, required.buildMessage(context, this)); } /** * {@inheritDoc} */ public boolean overrideValidator(Validator validator, IRequestCycle cycle) { if (Required.class.isAssignableFrom(validator.getClass())) return true; return false; } protected void cleanupAfterRender(IRequestCycle cycle) { _symbols = null; setAvailableColumn(null); setSelectedColumn(null); super.cleanupAfterRender(cycle); } /** * Executes the associated script, which generates all the JavaScript to support this Palette. */ private void runScript(IRequestCycle cycle) { PageRenderSupport pageRenderSupport = TapestryUtils.getPageRenderSupport(cycle, this); setImage(pageRenderSupport, cycle, "selectImage", getSelectImage()); setImage(pageRenderSupport, cycle, "selectDisabledImage", getSelectDisabledImage()); setImage(pageRenderSupport, cycle, "deselectImage", getDeselectImage()); setImage(pageRenderSupport, cycle, "deselectDisabledImage", getDeselectDisabledImage()); if (isSortUser()) { setImage(pageRenderSupport, cycle, "upImage", getUpImage()); setImage(pageRenderSupport, cycle, "upDisabledImage", getUpDisabledImage()); setImage(pageRenderSupport, cycle, "downImage", getDownImage()); setImage(pageRenderSupport, cycle, "downDisabledImage", getDownDisabledImage()); } _symbols.put("palette", this); getScript().execute(this, cycle, pageRenderSupport, _symbols); } /** * Extracts its asset URL, sets it up for preloading, and assigns the preload reference as a * script symbol. */ private void setImage(PageRenderSupport pageRenderSupport, IRequestCycle cycle, String symbolName, IAsset asset) { String url = asset.buildURL(); String reference = pageRenderSupport.getPreloadedImageReference(this, url); _symbols.put(symbolName, reference); } public Map getSymbols() { return _symbols; } /** * Constructs a pair of {@link PaletteColumn}s: the available and selected options. */ private void constructColumns() { // Build a Set around the list of selected items. List selected = getSelected(); if (selected == null) selected = Collections.EMPTY_LIST; String sortMode = getSort(); boolean sortUser = sortMode.equals(SortMode.USER); List selectedOptions = null; if (sortUser) { int count = selected.size(); selectedOptions = new ArrayList(count); for (int i = 0; i < count; i++) selectedOptions.add(null); } PaletteColumn availableColumn = new PaletteColumn((String) _symbols.get("availableName"), (String)_symbols.get("availableName"), getRows()); PaletteColumn selectedColumn = new PaletteColumn(getName(), getClientId(), getRows()); // Each value specified in the model will go into either the selected or available // lists. IPropertySelectionModel model = getModel(); int count = model.getOptionCount(); for (int i = 0; i < count; i++) { Object optionValue = model.getOption(i); PaletteOption o = new PaletteOption(model.getValue(i), model.getLabel(i)); int index = selected.indexOf(optionValue); boolean isSelected = index >= 0; if (sortUser && isSelected) { selectedOptions.set(index, o); continue; } PaletteColumn c = isSelected ? selectedColumn : availableColumn; c.addOption(o); } if (sortUser) { Iterator i = selectedOptions.iterator(); while (i.hasNext()) { PaletteOption o = (PaletteOption) i.next(); selectedColumn.addOption(o); } } if (sortMode.equals(SortMode.VALUE)) { availableColumn.sortByValue(); selectedColumn.sortByValue(); } else if (sortMode.equals(SortMode.LABEL)) { availableColumn.sortByLabel(); selectedColumn.sortByLabel(); } setAvailableColumn(availableColumn); setSelectedColumn(selectedColumn); } public boolean isSortUser() { return getSort().equals(SortMode.USER); } public abstract Block getAvailableTitleBlock(); public abstract IAsset getDeselectDisabledImage(); public abstract IAsset getDeselectImage(); public abstract IAsset getDownDisabledImage(); public abstract IAsset getDownImage(); public abstract IAsset getSelectDisabledImage(); public abstract IPropertySelectionModel getModel(); public abstract int getRows(); public abstract Block getSelectedTitleBlock(); public abstract IAsset getSelectImage(); public abstract String getSort(); public abstract IAsset getUpDisabledImage(); public abstract IAsset getUpImage(); /** * Returns false. Palette components are never disabled. * * @since 2.2 */ public boolean isDisabled() { return false; } /** @since 2.2 * */ public abstract List getSelected(); /** @since 2.2 * */ public abstract void setSelected(List selected); /** * Injected. * * @since 4.0 */ public abstract IScript getScript(); /** * Injected. * * @since 4.0 */ public abstract ValidatableFieldSupport getValidatableFieldSupport(); /** * @see org.apache.tapestry.form.AbstractFormComponent#isRequired() */ public boolean isRequired() { return getValidatableFieldSupport().isRequired(this); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy