001/* 002 * Copyright 2013 GWT-Bootstrap 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package com.github.gwtbootstrap.client.ui; 017 018import com.github.gwtbootstrap.client.ui.base.HasId; 019import com.github.gwtbootstrap.client.ui.base.HasStyle; 020import com.github.gwtbootstrap.client.ui.base.IsResponsive; 021import com.github.gwtbootstrap.client.ui.constants.Device; 022import com.github.gwtbootstrap.client.ui.constants.IconPosition; 023import com.github.gwtbootstrap.client.ui.constants.IconSize; 024import com.github.gwtbootstrap.client.ui.constants.IconType; 025import com.google.gwt.event.dom.client.ClickEvent; 026import com.google.gwt.event.dom.client.ClickHandler; 027import com.google.gwt.user.cellview.client.AbstractPager; 028import com.google.gwt.view.client.HasRows; 029import com.google.web.bindery.event.shared.HandlerRegistration; 030 031import java.util.ArrayList; 032import java.util.List; 033 034/** 035 * A pager for controlling a {@link HasRows} using {@link Pagination}. 036 * Just bind the NumberedPager using {@code NumberedPager.setDisplay} to some {@link HasRows} implementation like 037 * {@link CellTable} or {@link DataGrid}, and the pager is automatically generated fully working. 038 * <p/> 039 * <p> 040 * <h3>Example</h3> 041 * <p/> 042 * <pre> 043 * <code> 044 * <b:NumberedPager display="{table}"/> 045 * <b:CellTable pageSize="10" ui:field="table" width="100%" /> 046 * </code> 047 * </pre> 048 * <p/> 049 * </p> 050 */ 051public class NumberedPager extends AbstractPager implements HasStyle, IsResponsive, HasId { 052 053 private final Pagination pagination = new Pagination(); 054 private final List<HandlerRegistration> handlerRegistrationList = new ArrayList<HandlerRegistration>(); 055 056 private int visiblePages = -1; 057 private String nextCustomStyleName; 058 private String previousCustomStyleName; 059 private NavLink nextLink = new NavLink(">"); 060 private NavLink previousLink = new NavLink("<"); 061 062 public NumberedPager() { 063 initWidget(pagination); 064 } 065 066 /** 067 * {@inheritDoc} 068 */ 069 public String getId() { 070 return pagination.getId(); 071 } 072 073 /** 074 * {@inheritDoc} 075 */ 076 public void setId(String id) { 077 pagination.setId(id); 078 } 079 080 /** 081 * Pulls the widget to the right side. 082 * 083 * @param pullRight <code>true</code> if the widget should be aligned right. 084 */ 085 public void setPullRight(boolean pullRight) { 086 pagination.setPullRight(pullRight); 087 } 088 089 public void setAlignment(String alignment) { 090 pagination.setAlignment(alignment); 091 } 092 093 /** 094 * Limit the number of visible pages in the Pagination widget. 095 * 096 * @param visiblePages number of visible pages 097 */ 098 public void setVisiblePages(int visiblePages) { 099 this.visiblePages = visiblePages; 100 if (visiblePages > 0) { 101 setRangeLimited(false); 102 }/* else { 103 setRangeLimited(true); 104 }*/ 105 } 106 107 /** 108 * {@inheritDoc} 109 */ 110 public void setShowOn(Device device) { 111 pagination.setShowOn(device); 112 } 113 114 /** 115 * {@inheritDoc} 116 */ 117 public void setHideOn(Device device) { 118 pagination.setHideOn(device); 119 } 120 121 public void setSize(Pagination.PaginationSize size) { 122 pagination.setSize(size); 123 } 124 125 /** 126 * {@inheritDoc} 127 */ 128 public void setStyle(com.github.gwtbootstrap.client.ui.base.Style style) { 129 pagination.setStyle(style); 130 } 131 132 /** 133 * {@inheritDoc} 134 */ 135 public void addStyle(com.github.gwtbootstrap.client.ui.base.Style style) { 136 pagination.addStyle(style); 137 } 138 139 /** 140 * {@inheritDoc} 141 */ 142 public void removeStyle(com.github.gwtbootstrap.client.ui.base.Style style) { 143 pagination.removeStyle(style); 144 } 145 146 public void setNextCustomStyleName(String nextCustomStyleName) { 147 this.nextCustomStyleName = nextCustomStyleName; 148 } 149 150 public void setNextText(String nextText) { 151 nextLink.setText(nextText); 152 } 153 154 public void setNextIcon(IconType type) { 155 nextLink.setIcon(type); 156 } 157 158 public void setNextCustomIconStyle(String customIconStyle) { 159 nextLink.setCustomIconStyle(customIconStyle); 160 } 161 162 public void setNextIconPosition(IconPosition position) { 163 nextLink.setIconPosition(position); 164 } 165 166 public void setNextIconSize(IconSize size) { 167 nextLink.setIconSize(size); 168 } 169 170 public void setPreviousCustomStyleName(String previousCustomStyleName) { 171 this.previousCustomStyleName = previousCustomStyleName; 172 } 173 174 public void setPreviousText(String previousText) { 175 previousLink.setText(previousText); 176 } 177 178 public void setPreviousIcon(IconType type) { 179 previousLink.setIcon(type); 180 } 181 182 public void setPreviousCustomIconStyle(String customIconStyle) { 183 previousLink.setCustomIconStyle(customIconStyle); 184 } 185 186 public void setPreviousIconPosition(IconPosition position) { 187 previousLink.setIconPosition(position); 188 } 189 190 public void setPreviousIconSize(IconSize size) { 191 previousLink.setIconSize(size); 192 } 193 194 @Override 195 protected void onRangeOrRowCountChanged() { 196 final HasRows display = super.getDisplay(); 197 final int pageSize = super.getPageSize(); 198 final int calculatedPage = display.getVisibleRange().getStart() / pageSize; 199 final int pageCount = super.getPageCount(); 200 201 // Workaround GWT misbehavior: 202 // Calculating wrong page when dynamically changing pageSize to a lower value and start index is between two pages. 203 // This logic aims to correct the start index. 204 if (pageCount > 0 && (calculatedPage != super.getPage())) { 205 display.setVisibleRange(pageSize * calculatedPage, pageSize); 206 } else { 207 // Proceed with normal paging logic 208 if (pageCount > 0) { 209 if (pagination.getWidgetCount() == 0) { 210 // Lazy init for not displaying back and forward buttons unnecessarily 211 initPagination(); 212 } 213 214 int widgetCount = pagination.getWidgetCount(); 215 if (pageCount + 2 > widgetCount) { 216 // If there are *more* pages then links, then add the remaining links 217 for (int i = widgetCount - 1; i <= pageCount; i++) { 218 NavLink page = pagination.addPageLink(i); 219 page.addClickHandler(createPageClickHandler(i - 1)); 220 } 221 } else if (pageCount + 2 < widgetCount) { 222 // If there are *less* pages then links, then remove the exceeding links 223 for (int i = widgetCount - 2; i > pageCount; i--) { 224 pagination.remove(i); 225 } 226 } 227 228 // Set the actual page link as disabled 229 updateButtonsState(); 230 } else { 231 if (pagination.getWidgetCount() > 0) { 232 resetPagination(); 233 } 234 } 235 } 236 } 237 238 private void initPagination() { 239 if (previousCustomStyleName != null) previousLink.addStyleName(previousCustomStyleName); 240 handlerRegistrationList.add(previousLink.addClickHandler(new ClickHandler() { 241 @Override 242 public void onClick(ClickEvent event) { 243 NumberedPager.this.previousPage(); 244 } 245 })); 246 pagination.add(previousLink); 247 248 NavLink page = pagination.addPageLink(1); 249 page.addClickHandler(new ClickHandler() { 250 @Override 251 public void onClick(ClickEvent event) { 252 NumberedPager.this.setPage(0); 253 } 254 }); 255 page.setDisabled(true); 256 257 if (nextCustomStyleName != null) nextLink.addStyleName(nextCustomStyleName); 258 handlerRegistrationList.add(nextLink.addClickHandler(new ClickHandler() { 259 @Override 260 public void onClick(ClickEvent event) { 261 NumberedPager.this.nextPage(); 262 } 263 })); 264 pagination.add(nextLink); 265 } 266 267 private void updateButtonsState() { 268 final int pageCount = getPageCount(); 269 final int currentPage = getPage(); 270 271 // If visiblePages is set, then treat circular exhibition 272 if (visiblePages > 0) { 273 // Calculate offsets 274 final int maxPages = visiblePages < pageCount ? visiblePages : pageCount; 275 final int pagesToShow = Math.min(pageCount, maxPages); 276 int firstVisibleIndex = 1; 277 278 if (currentPage >= pageCount - (maxPages / 2)) { 279 firstVisibleIndex = pageCount - maxPages + 1; 280 } else if (currentPage > maxPages / 2) { 281 firstVisibleIndex = currentPage - (maxPages / 2) + 1; 282 } 283 284 final int firstNotVisibleIndex = pagesToShow + firstVisibleIndex; 285 286 // Set out of range pages as invisible (before initial threshold) 287 for (int i = 1; i < firstVisibleIndex; i++) { 288 pagination.getWidget(i).setVisible(false); 289 } // (after final threshold) 290 for (int i = firstNotVisibleIndex; i < pagination.getWidgetCount() - 1; i++) { 291 pagination.getWidget(i).setVisible(false); 292 } 293 294 // Set in range pages as visible 295 for (int i = firstVisibleIndex; i < firstNotVisibleIndex; i++) { 296 pagination.getWidget(i).setVisible(true); 297 } 298 } 299 300 // Set all numbered buttons as enabled 301 for (int i = 1; i < pagination.getWidgetCount() - 1; i++) { 302 NavLink navLink = (NavLink) pagination.getWidget(i); 303 navLink.setDisabled(false); 304 navLink.setActive(false); 305 } 306 307 // Set the button of the current page as disable 308 NavLink navLink = (NavLink) pagination.getWidget(currentPage + 1); 309 navLink.setDisabled(true); 310 navLink.setActive(true); 311 312 // Update the state of previous and next buttons 313 updatePreviousAndNextButtons(); 314 } 315 316 private void updatePreviousAndNextButtons() { 317 previousLink.setDisabled(!super.hasPreviousPage()); 318 nextLink.setDisabled(!super.hasNextPage()); 319 } 320 321 private ClickHandler createPageClickHandler(final int page) { 322 return new ClickHandler() { 323 @Override 324 public void onClick(ClickEvent event) { 325 NumberedPager.this.setPage(page); 326 } 327 }; 328 } 329 330 private void resetPagination() { 331 // Deregister click handlers 332 for (HandlerRegistration handlerRegistration : handlerRegistrationList) { 333 handlerRegistration.removeHandler(); 334 } 335 pagination.clear(); 336 if (previousCustomStyleName != null) previousLink.removeStyleName(previousCustomStyleName); 337 if (nextCustomStyleName != null) nextLink.removeStyleName(nextCustomStyleName); 338 } 339}