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