    001    // Copyright 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
    002    //
    003    // Licensed under the Apache License, Version 2.0 (the "License");
    004    // you may not use this file except in compliance with the License.
    005    // You may obtain a copy of the License at
    006    //
    007    //     http://www.apache.org/licenses/LICENSE-2.0
    008    //
    009    // Unless required by applicable law or agreed to in writing, software
    010    // distributed under the License is distributed on an "AS IS" BASIS,
    011    // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    012    // See the License for the specific language governing permissions and
    013    // limitations under the License.
    014
    015    package org.apache.tapestry5.corelib.components;
    016
    017    import org.apache.tapestry5.*;
    018    import org.apache.tapestry5.annotations.Environmental;
    019    import org.apache.tapestry5.annotations.Events;
    020    import org.apache.tapestry5.annotations.Parameter;
    021    import org.apache.tapestry5.dom.Element;
    022    import org.apache.tapestry5.grid.GridDataSource;
    023    import org.apache.tapestry5.internal.InternalConstants;
    024    import org.apache.tapestry5.ioc.Messages;
    025    import org.apache.tapestry5.ioc.annotations.Inject;
    026    import org.apache.tapestry5.services.ClientBehaviorSupport;
    027    import org.apache.tapestry5.services.javascript.JavaScriptSupport;
    028
    029    /**
    030     * Generates a series of links used to jump to a particular page index within the overall data set.
    031     *
    032     * @tapestrydoc
    033     */
    034    @Events(InternalConstants.GRID_INPLACE_UPDATE + " (internal event)")
    035    public class GridPager
    036    {
    037        /**
    038         * The source of the data displayed by the grid (this is used to determine {@link GridDataSource#getAvailableRows()
    039         * how many rows are available}, which in turn determines the page count).
    040         */
    041        @Parameter(required = true)
    042        private GridDataSource source;
    043
    044        /**
    045         * The number of rows displayed per page.
    046         */
    047        @Parameter(required = true)
    048        private int rowsPerPage;
    049
    050        /**
    051         * The current page number (indexed from 1).
    052         */
    053        @Parameter(required = true)
    054        private int currentPage;
    055
    056        /**
    057         * Number of pages before and after the current page in the range. The pager always displays links for 2 * range + 1
    058         * pages, unless that's more than the total number of available pages.
    059         */
    060        @Parameter(BindingConstants.SYMBOL + ":" + ComponentParameterConstants.GRIDPAGER_PAGE_RANGE)
    061        private int range;
    062
    063        /**
    064         * If not null, then each link is output as a link to update the specified zone.
    065         */
    066        @Parameter
    067        private String zone;
    068
    069        private int lastIndex;
    070
    071        private int maxPages;
    072
    073        @Inject
    074        private ComponentResources resources;
    075
    076        @Inject
    077        private Messages messages;
    078
    079        @Environmental
    080        private ClientBehaviorSupport clientBehaviorSupport;
    081
    082        @Environmental
    083        private JavaScriptSupport jsSupport;
    084
    085        void beginRender(MarkupWriter writer)
    086        {
    087            int availableRows = source.getAvailableRows();
    088
    089            maxPages = ((availableRows - 1) / rowsPerPage) + 1;
    090
    091            if (maxPages < 2) return;
    092
    093            writer.element("div", "class", "t-data-grid-pager");
    094
    095            lastIndex = 0;
    096
    097            for (int i = 1; i <= 2; i++)
    098                writePageLink(writer, i);
    099
    100            int low = currentPage - range;
    101            int high = currentPage + range;
    102
    103            if (low < 1)
    104            {
    105                low = 1;
    106                high = 2 * range + 1;
    107            }
    108            else
    109            {
    110                if (high > maxPages)
    111                {
    112                    high = maxPages;
    113                    low = high - 2 * range;
    114                }
    115            }
    116
    117            for (int i = low; i <= high; i++)
    118                writePageLink(writer, i);
    119
    120            for (int i = maxPages - 1; i <= maxPages; i++)
    121                writePageLink(writer, i);
    122
    123            writer.end();
    124        }
    125
    126        private void writePageLink(MarkupWriter writer, int pageIndex)
    127        {
    128            if (pageIndex < 1 || pageIndex > maxPages) return;
    129
    130            if (pageIndex <= lastIndex) return;
    131
    132            if (pageIndex != lastIndex + 1) writer.write(" ... ");
    133
    134            lastIndex = pageIndex;
    135
    136            if (pageIndex == currentPage)
    137            {
    138                writer.element("span", "class", "current");
    139                writer.write(Integer.toString(pageIndex));
    140                writer.end();
    141                return;
    142            }
    143
    144            Object[] context = zone == null
    145                               ? new Object[] { pageIndex }
    146                               : new Object[] { pageIndex, zone };
    147
    148            Link link = resources.createEventLink(EventConstants.ACTION, context);
    149
    150            Element element = writer.element("a",
    151                                             "href", zone == null ? link : "#",
    152                                             "title", messages.format("goto-page", pageIndex));
    153
    154            writer.write(Integer.toString(pageIndex));
    155            writer.end();
    156
    157            if (zone != null)
    158            {
    159                String id = jsSupport.allocateClientId(resources);
    160
    161                element.attribute("id", id);
    162
    163                clientBehaviorSupport.linkZone(id, zone, link);
    164            }
    165        }
    166
    167        /**
    168         * Normal, non-Ajax event handler.
    169         */
    170        void onAction(int newPage)
    171        {
    172            // TODO: Validate newPage in range
    173
    174            currentPage = newPage;
    175        }
    176
    177        /**
    178         * Akjax event handler, passing the zone along.
    179         */
    180        boolean onAction(int newPage, String zone)
    181        {
    182            onAction(newPage);
    183
    184            resources.triggerEvent(InternalConstants.GRID_INPLACE_UPDATE, new Object[] { zone }, null);
    185
    186            return true; // abort event
    187        }
    188    }