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 }