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}