package er.ajax; import java.math.BigDecimal; import com.webobjects.appserver.WOComponent; import com.webobjects.appserver.WOContext; import com.webobjects.appserver.WODisplayGroup; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSMutableDictionary; import er.extensions.foundation.ERXValueUtilities; /** * Abstract super class for a navigation bar that can be used with AjaxGrid. Use * of this is not mandatory, it is provided as a convenience. * <h3>Example Usage</h3> * This class has a symbiotic relationship with AjaxGrid. When this nav bar * changes the data (e.g. batch size, batch displayed) it updates the grid's * container <b>not</b> its own container. It needs to have the grid tell it to * update after the grid has refreshed. This allows the AjaxGrid to make any * needed changes to the display group before the contents of the nav bar are * updated. * * <pre> * Grid: AjaxGrid { * configurationData = configData; * displayGroup = displayGroup; * afterUpdate = "ajaxGridExampleNavBarUpdate();"; * } * * NavBar: AjaxGridExampleNavBar { * containerID = "ajaxGridExampleNavBar"; * displayGroup = displayGroup; * configurationData = configData; * } * </pre> * * <h3>Example Sub-class</h3> * * <pre> * <div class="ajaxGridNavBar"> * <webobject name="NavUpdater"> * <table><tr> * <td style="text-align:left;"><webobject name="PrevBatch"><<</webobject> * Page <b><span id="currentBatch"><webobject name="CurrentBatchIndex"/></span></b> of <b><webobject name="BatchCount"/></b> * <webobject name="NextBatch">>></webobject> * </td> * <td style="text-align:center;"> * Number of lines per page: <webobject name="BatchSizes"/><webobject name="UpdateBatchSize"/> * </td> * <td style="text-align:right;"> * Displaying <b><webobject name="FirstIndex"/></b> to <b><webobject name="LastIndex"/></b> of <b><webobject name="TotalCount"/></b> entries. * </td></tr></table> * <webobject name="BatchSlider"/> * </webobject name="NavUpdater"> * </div> * </pre> * * <pre> * NavUpdater: AjaxUpdateContainer { * id = containerID; * } * * BatchCount: WOString { * value = displayGroup.batchCount; * } * * BatchSlider: AjaxSlider { * orientation = "horizontal"; * value = currentBatchIndex; * minimum = 1; * maximum = displayGroup.batchCount; * onChangeServer = updateGridContainer; * onSlide = "function(v) { $('currentBatch').innerHTML = Math.round(v) }"; * onChange = "function(v) { $('currentBatch').innerHTML = Math.round(v) }"; * } * * CurrentBatchIndex : WOString { * value = displayGroup.currentBatchIndex; * } * * PrevBatch : AjaxUpdateLink { * action = previousBatch; * updateContainerID = gridContainerID; * } * * NextBatch : AjaxUpdateLink { * action = nextBatch; * updateContainerID = gridContainerID; * } * * BatchSizes: WOPopUpButton { * list = batchSizes; * item = batchSize; * selection = currentBatchSize; * onChange = updateBatchSizeOnChange; * } * * FirstIndex: WOString { * value = displayGroup.indexOfFirstDisplayedObject; * } * * LastIndex: WOString { * value = displayGroup.indexOfLastDisplayedObject; * } * * TotalCount: WOString { * value = displayGroup.allObjects.count; * } * * UpdateBatchSize: AjaxUpdateLink { * action = batchSizeUpdated; * functionName = updateBatchSizeName; * updateContainerID = gridContainerID; * } * </pre> * * @binding displayGroup the same WODisplayGroup passed to AjaxGrid * @binding configurationData the same NSMutableDictionary passed to AjaxGrid * @binding containerID unique ID for the AjaxUpdateContainer in this component. * * @author Chuck Hill */ public abstract class AjaxGridNavBar extends WOComponent { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; public static final String CONTAINER_ID_BINDING = "containerID"; public static final String DISPLAY_GROUP_BINDING = "displayGroup"; public static final String CONFIGURATION_DATA_BINDING = "configurationData"; public AjaxGridNavBar(WOContext context) { super(context); } @Override public boolean isStateless() { return true; } /** * Ajax action method to select the next batch. */ public void nextBatch() { displayGroup().displayNextBatch(); displayGroup().setSelectedObject(null); } /** * Ajax action method to select the previous batch. */ public void previousBatch() { displayGroup().displayPreviousBatch(); displayGroup().setSelectedObject(null); } /** * Intended to be bound to Ajax slider or selection of batch to display. * * @param newValue * new batch number from AjaxSlider */ public void setCurrentBatchIndex(Number newValue) { // Right now the value returned is a BigDecimal. KVC changes this to an // int by narrowing, so that 4.9876402 becomes 4. // If we round this instead, the slider movement is more inutuitive, // especially with smaller batch sizes. if (newValue instanceof BigDecimal) { int roundedIndex = Float.valueOf(((BigDecimal) newValue).floatValue() + 0.5f).intValue(); displayGroup().setCurrentBatchIndex(roundedIndex); } else { displayGroup().setCurrentBatchIndex(newValue.intValue()); } } /** * @return value for AjaxSlider */ public int currentBatchIndex() { return displayGroup().currentBatchIndex(); } /** * Returns JavaScript to update the AjaxUpdateContainer identified by * gridContainerID(). This is intended for use as onChangeServer binding for * a AjaxSlider. * * @return JavaScript calls to update the Ajax grid */ public String updateGridContainer() { return gridContainerID() + "Update(); "; } /** * @return JavaScript for option control to pass selected batch size when * changed */ public String updateBatchSizeOnChange() { return updateBatchSizeName() + "('batchSize=' + this.value);"; } /** * @return unique name for the AjaxUpdateContainer wrapping this * AjaxGridNavBar */ public String updateBatchSizeName() { return containerID() + "UpdateBatchSize"; } /** * Ajax action method for updates to batch size. Grabs batch size from * request and updates configuration. */ public void batchSizeUpdated() { String batchSizeString = (String) context().request().formValueForKey("batchSize"); int batchSizeIndex = Integer.parseInt(batchSizeString); configurationData().setObjectForKey(batchSizes().objectAtIndex(batchSizeIndex), AjaxGrid.BATCH_SIZE); // Keep display group in synch to avoid display update issues displayGroup().setNumberOfObjectsPerBatch(ERXValueUtilities.intValue(currentBatchSize())); } /** * @return the list of batch sizes to show in the popup */ public abstract NSArray batchSizes(); /** * @return displayGroup().numberOfObjectsPerBatch() as a String for the * option input */ public String currentBatchSize() { return (String) configurationData().objectForKey(AjaxGrid.BATCH_SIZE); } /** * @return UPDATE_CONTAINER_ID from configurationData(), the update * container ID from the AjaxGrid */ public String gridContainerID() { return (String) configurationData().objectForKey(AjaxGrid.UPDATE_CONTAINER_ID); } /** * @return TABLE_ID from configurationData(), the table ID from the AjaxGrid */ public String tableID() { return (String) configurationData().objectForKey(AjaxGrid.TABLE_ID); } /** * @return value bound to displayGroup */ public WODisplayGroup displayGroup() { return (WODisplayGroup) valueForBinding(DISPLAY_GROUP_BINDING); } /** * @return value bound to configurationData */ public NSMutableDictionary configurationData() { return (NSMutableDictionary) valueForBinding(CONFIGURATION_DATA_BINDING); } /** * @return value bound to containerID */ public String containerID() { return (String) valueForBinding(CONTAINER_ID_BINDING); } }