    001// Copyright 2007, 2008, 2009, 2010, 2011, 2012 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
    015package org.apache.tapestry5.corelib.components;
    016
    017import org.apache.tapestry5.*;
    018import org.apache.tapestry5.annotations.Events;
    019import org.apache.tapestry5.annotations.Parameter;
    020import org.apache.tapestry5.commons.Messages;
    021import org.apache.tapestry5.dom.Element;
    022import org.apache.tapestry5.grid.GridDataSource;
    023import org.apache.tapestry5.http.Link;
    024import org.apache.tapestry5.http.services.Request;
    025import org.apache.tapestry5.internal.InternalConstants;
    026import org.apache.tapestry5.ioc.annotations.Inject;
    027
    028/**
    029 * Generates a series of links used to jump to a particular page index within the overall data set.
    030 *
    031 * @tapestrydoc
    032 */
    033@Events(InternalConstants.GRID_INPLACE_UPDATE + " (internal event)")
    034public class GridPager
    035{
    036    /**
    037     * The source of the data displayed by the grid (this is used to determine {@link GridDataSource#getAvailableRows()
    038     * how many rows are available}, which in turn determines the page count).
    039     */
    040    @Parameter(required = true)
    041    private GridDataSource source;
    042
    043    /**
    044     * The number of rows displayed per page.
    045     */
    046    @Parameter(required = true)
    047    private int rowsPerPage;
    048
    049    /**
    050     * The current page number (indexed from 1).
    051     */
    052    @Parameter(required = true)
    053    private int currentPage;
    054
    055    /**
    056     * Number of pages before and after the current page in the range. The pager always displays links for 2 * range + 1
    057     * pages, unless that's more than the total number of available pages.
    058     */
    059    @Parameter(BindingConstants.SYMBOL + ":" + ComponentParameterConstants.GRIDPAGER_PAGE_RANGE)
    060    private int range;
    061
    062    /**
    063     * If not null, then each link is output as a link to update the specified zone.
    064     */
    065    @Parameter
    066    private String zone;
    067
    068    private int lastIndex;
    069
    070    private int maxPages;
    071
    072    @Inject
    073    private ComponentResources resources;
    074
    075    @Inject
    076    private Messages messages;
    077
    078    @Inject
    079    private Request request;
    080
    081    void beginRender(MarkupWriter writer)
    082    {
    083        int availableRows = source.getAvailableRows();
    084
    085        maxPages = ((availableRows - 1) / rowsPerPage) + 1;
    086
    087        if (maxPages < 2) return;
    088
    089        writer.element("ul", "class", "pagination");
    090
    091        if (zone != null)
    092        {
    093            writer.attributes("data-inplace-grid-links", true);
    094        }
    095
    096        lastIndex = 0;
    097
    098        for (int i = 1; i <= 2; i++)
    099            writePageLink(writer, i);
    100
    101        int low = currentPage - range;
    102        int high = currentPage + range;
    103
    104        if (low < 1)
    105        {
    106            low = 1;
    107            high = 2 * range + 1;
    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();    // ul
    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)
    133        {
    134            writer.element("li", "class", "disabled");
    135            writer.element("a", "href", "#");
    136            writer.write(" ... ");
    137            writer.end();
    138            writer.end();
    139        }
    140
    141        lastIndex = pageIndex;
    142
    143        if (pageIndex == currentPage)
    144        {
    145            writer.element("li", "class", "active");
    146            writer.element("a", "href", "#");
    147            writer.write(Integer.toString(pageIndex));
    148            writer.end();
    149            writer.end();
    150            return;
    151        }
    152
    153        writer.element("li");
    154
    155        Link link = resources.createEventLink(EventConstants.ACTION, pageIndex);
    156
    157        if (zone != null)
    158        {
    159            link.addParameter("t:inplace", "true");
    160        }
    161
    162        Element element = writer.element("a",
    163                "href", link,
    164                "data-update-zone", zone,
    165                "title", messages.format("core-goto-page", pageIndex));
    166
    167
    168        writer.write(Integer.toString(pageIndex));
    169
    170        writer.end();
    171
    172        writer.end();   // li
    173    }
    174
    175    /**
    176     * Repaging event handler.
    177     */
    178    boolean onAction(int newPage)
    179    {
    180        // TODO: Validate newPage in range
    181
    182        currentPage = newPage;
    183
    184        if (request.isXHR())
    185        {
    186            resources.triggerEvent(InternalConstants.GRID_INPLACE_UPDATE, null, null);
    187        }
    188
    189        return true;     // abort event
    190    }
    191}