001/* 002 * Copyright 2010 Google Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 005 * use this file except in compliance with the License. You may obtain a copy of 006 * 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, WITHOUT 012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 013 * License for the specific language governing permissions and limitations under 014 * the License. 015 */ 016package com.github.gwtbootstrap.client.ui; 017 018import com.github.gwtbootstrap.client.ui.constants.ButtonType; 019import com.github.gwtbootstrap.client.ui.constants.IconType; 020import com.github.gwtbootstrap.client.ui.constants.Placement; 021import com.google.gwt.core.client.GWT; 022import com.google.gwt.event.dom.client.ClickEvent; 023import com.google.gwt.event.dom.client.ClickHandler; 024import com.google.gwt.i18n.client.Constants; 025import com.google.gwt.i18n.client.LocalizableResource.DefaultLocale; 026import com.google.gwt.i18n.client.NumberFormat; 027import com.google.gwt.resources.client.ClientBundle; 028import com.google.gwt.uibinder.client.UiConstructor; 029import com.google.gwt.user.cellview.client.AbstractPager; 030import com.google.gwt.user.cellview.client.SimplePager.Style; 031import com.google.gwt.user.client.ui.HTML; 032import com.google.gwt.user.client.ui.HasVerticalAlignment; 033import com.google.gwt.user.client.ui.HorizontalPanel; 034import com.google.gwt.view.client.HasRows; 035import com.google.gwt.view.client.Range; 036 037/** 038 * A pager for controlling a {@link HasRows} that only supports simple page 039 * navigation. 040 * 041 * <p> 042 * <h3>Example</h3> 043 * 044 * <pre> 045 * <b:CellTable pageSize="10" ui:field="table" width="100%" /> 046 * <div> 047 * <b:SimplePager display="{table}" location="RIGHT" fastForwardRows="50"/> 048 * </div> 049 * </pre> 050 * 051 * </p> 052 */ 053public class SimplePager extends AbstractPager { 054 055 /** 056 * Constant for labeling the simple pager navigational {@link ImageButton}s 057 */ 058 @DefaultLocale("en_US") 059 public interface ImageButtonsConstants extends Constants { 060 @DefaultStringValue("Fast forward") 061 String fastForward(); 062 063 @DefaultStringValue("First page") 064 String firstPage(); 065 066 @DefaultStringValue("Last page") 067 String lastPage(); 068 069 @DefaultStringValue("Next page") 070 String nextPage(); 071 072 @DefaultStringValue("Previous page") 073 String prevPage(); 074 } 075 076 /** 077 * A ClientBundle that provides styles for this widget. 078 */ 079 public static interface Resources extends ClientBundle { 080 081 /** 082 * The styles used in this widget. 083 */ 084 @Source("GwtBootstrapSimplePager.css") 085 Style simplePagerStyle(); 086 } 087 088 /** 089 * The location of the text relative to the paging buttons. 090 */ 091 public static enum TextLocation { 092 CENTER, LEFT, RIGHT; 093 } 094 095 private static final int DEFAULT_FAST_FORWARD_ROWS = 100; 096 private static Resources DEFAULT_RESOURCES; 097 098 private static Resources getDefaultResources() { 099 if (DEFAULT_RESOURCES == null) { 100 DEFAULT_RESOURCES = GWT.create(Resources.class); 101 } 102 return DEFAULT_RESOURCES; 103 } 104 105 private final int tooltipDelay = 1000; 106 private final Placement tooltipPlacement = Placement.BOTTOM; 107 108 private final Button fastForward; 109 private final Tooltip fastForwardTooltip; 110 111 private int fastForwardRows; 112 113 private final Button firstPage; 114 private final Tooltip firstPageTooltip; 115 116 /** 117 * We use an {@link HTML} so we can embed the loading image. 118 */ 119 private final HTML label = new HTML(); 120 121 private final Button lastPage; 122 private final Button nextPage; 123 private final Button prevPage; 124 125 private final Tooltip lastPageTooltip; 126 private final Tooltip nextPageTooltip; 127 private final Tooltip prevPageTooltip; 128 129 /** 130 * The {@link Resources} used by this widget. 131 */ 132 private final Resources resources; 133 134 /** 135 * The {@link Style} used by this widget. 136 */ 137 private final Style style; 138 139 /** 140 * Construct a {@link SimplePager} with the default text location. 141 */ 142 public SimplePager() { 143 this(TextLocation.CENTER); 144 } 145 146 /** 147 * Construct a {@link SimplePager} with the specified text location. 148 * 149 * @param location 150 * the location of the text relative to the buttons 151 */ 152 @UiConstructor 153 // Hack for Google I/O demo 154 public SimplePager(TextLocation location) { 155 this(location, getDefaultResources(), true, DEFAULT_FAST_FORWARD_ROWS, false); 156 } 157 158 /** 159 * Construct a {@link SimplePager} with the default resources, fast forward 160 * rows and default image button names. 161 * 162 * @param location 163 * the location of the text relative to the buttons 164 * @param showFastForwardButton 165 * if true, show a fast-forward button that advances by a larger 166 * increment than a single page 167 * @param showLastPageButton 168 * if true, show a button to go the the last page 169 */ 170 public SimplePager(TextLocation location, boolean showFastForwardButton, boolean showLastPageButton) { 171 this(location, showFastForwardButton, DEFAULT_FAST_FORWARD_ROWS, showLastPageButton); 172 } 173 174 /** 175 * Construct a {@link SimplePager} with the default resources and default 176 * image button names. 177 * 178 * @param location 179 * the location of the text relative to the buttons 180 * @param showFastForwardButton 181 * if true, show a fast-forward button that advances by a larger 182 * increment than a single page 183 * @param fastForwardRows 184 * the number of rows to jump when fast forwarding 185 * @param showLastPageButton 186 * if true, show a button to go the the last page 187 */ 188 public SimplePager(TextLocation location, boolean showFastForwardButton, final int fastForwardRows, boolean showLastPageButton) { 189 this(location, getDefaultResources(), showFastForwardButton, fastForwardRows, showLastPageButton); 190 } 191 192 /** 193 * Construct a {@link SimplePager} with the specified resources. 194 * 195 * @param location 196 * the location of the text relative to the buttons 197 * @param resources 198 * the {@link Resources} to use 199 * @param showFastForwardButton 200 * if true, show a fast-forward button that advances by a larger 201 * increment than a single page 202 * @param fastForwardRows 203 * the number of rows to jump when fast forwarding 204 * @param showLastPageButton 205 * if true, show a button to go the the last page 206 * @param imageButtonConstants 207 * Constants that contain the image button names 208 */ 209 public SimplePager(TextLocation location, Resources resources, boolean showFastForwardButton, final int fastForwardRows, boolean showLastPageButton, ImageButtonsConstants imageButtonConstants) { 210 this.resources = resources; 211 this.fastForwardRows = fastForwardRows; 212 this.style = this.resources.simplePagerStyle(); 213 this.style.ensureInjected(); 214 215 // Create the buttons. 216 firstPage = new Button(); 217 firstPage.setType(ButtonType.LINK); 218 firstPage.setIcon(IconType.FAST_BACKWARD); 219 firstPage.addClickHandler(new ClickHandler() { 220 @Override 221 public void onClick(ClickEvent event) { 222 firstPage(); 223 } 224 }); 225 firstPageTooltip = new Tooltip(imageButtonConstants.firstPage()); 226 firstPageTooltip.setWidget(firstPage); 227 firstPageTooltip.setPlacement(tooltipPlacement); 228 firstPageTooltip.setShowDelay(tooltipDelay); 229 230 nextPage = new Button(); 231 nextPage.setType(ButtonType.LINK); 232 nextPage.setIcon(IconType.STEP_FORWARD); 233 nextPage.addClickHandler(new ClickHandler() { 234 @Override 235 public void onClick(ClickEvent event) { 236 nextPage(); 237 } 238 }); 239 nextPageTooltip = new Tooltip(imageButtonConstants.nextPage()); 240 nextPageTooltip.setWidget(nextPage); 241 nextPageTooltip.setPlacement(tooltipPlacement); 242 nextPageTooltip.setShowDelay(tooltipDelay); 243 244 prevPage = new Button(); 245 prevPage.setType(ButtonType.LINK); 246 prevPage.setIcon(IconType.STEP_BACKWARD); 247 prevPage.addClickHandler(new ClickHandler() { 248 @Override 249 public void onClick(ClickEvent event) { 250 previousPage(); 251 } 252 }); 253 prevPageTooltip = new Tooltip(imageButtonConstants.prevPage()); 254 prevPageTooltip.setWidget(prevPage); 255 prevPageTooltip.setPlacement(tooltipPlacement); 256 prevPageTooltip.setShowDelay(tooltipDelay); 257 258 if (showLastPageButton) { 259 lastPage = new Button(); 260 lastPage.setType(ButtonType.LINK); 261 lastPage.setIcon(IconType.FAST_FORWARD); 262 lastPage.addClickHandler(new ClickHandler() { 263 @Override 264 public void onClick(ClickEvent event) { 265 lastPage(); 266 } 267 }); 268 lastPageTooltip = new Tooltip(imageButtonConstants.lastPage()); 269 lastPageTooltip.setWidget(lastPage); 270 lastPageTooltip.setPlacement(tooltipPlacement); 271 lastPageTooltip.setShowDelay(tooltipDelay); 272 } else { 273 lastPage = null; 274 lastPageTooltip = null; 275 } 276 if (showFastForwardButton) { 277 fastForward = new Button(); 278 fastForward.setType(ButtonType.LINK); 279 fastForward.setIcon(IconType.FORWARD); 280 fastForward.addClickHandler(new ClickHandler() { 281 @Override 282 public void onClick(ClickEvent event) { 283 setPage(getPage() + getFastForwardPages()); 284 } 285 }); 286 fastForwardTooltip = new Tooltip(imageButtonConstants.fastForward()); 287 fastForwardTooltip.setWidget(fastForward); 288 fastForwardTooltip.setPlacement(tooltipPlacement); 289 fastForwardTooltip.setShowDelay(tooltipDelay); 290 } else { 291 fastForward = null; 292 fastForwardTooltip = null; 293 } 294 295 // Construct the widget. 296 HorizontalPanel layout = new HorizontalPanel(); 297 layout.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE); 298 initWidget(layout); 299 if (location == TextLocation.LEFT) { 300 layout.add(label); 301 } 302 layout.add(firstPage); 303 layout.add(firstPageTooltip); 304 layout.add(prevPage); 305 layout.add(prevPageTooltip); 306 if (location == TextLocation.CENTER) { 307 layout.add(label); 308 } 309 layout.add(nextPage); 310 layout.add(nextPageTooltip); 311 if (showFastForwardButton) { 312 layout.add(fastForward); 313 layout.add(fastForwardTooltip); 314 } 315 if (showLastPageButton) { 316 layout.add(lastPage); 317 layout.add(lastPageTooltip); 318 } 319 320 if (location == TextLocation.RIGHT) { 321 layout.add(label); 322 } 323 324 // Add style names to the cells. 325 firstPage.getElement().getParentElement().addClassName(style.button()); 326 prevPage.getElement().getParentElement().addClassName(style.button()); 327 label.getElement().getParentElement().addClassName(style.pageDetails()); 328 nextPage.getElement().getParentElement().addClassName(style.button()); 329 if (showFastForwardButton) { 330 fastForward.getElement().getParentElement().addClassName(style.button()); 331 } 332 if (showLastPageButton) { 333 lastPage.getElement().getParentElement().addClassName(style.button()); 334 } 335 336 // Disable the buttons by default. 337 setDisplay(null); 338 } 339 340 /** 341 * Construct a {@link SimplePager} with the specified resources and default 342 * image button names. 343 * 344 * @param location 345 * the location of the text relative to the buttons 346 * @param resources 347 * the {@link Resources} to use 348 * @param showFastForwardButton 349 * if true, show a fast-forward button that advances by a larger 350 * increment than a single page 351 * @param fastForwardRows 352 * the number of rows to jump when fast forwarding 353 * @param showLastPageButton 354 * if true, show a button to go the the last page 355 */ 356 public SimplePager(TextLocation location, Resources resources, boolean showFastForwardButton, final int fastForwardRows, boolean showLastPageButton) { 357 this(location, resources, showFastForwardButton, fastForwardRows, showLastPageButton, GWT.<ImageButtonsConstants> create(ImageButtonsConstants.class)); 358 } 359 360 @Override 361 public void firstPage() { 362 super.firstPage(); 363 } 364 365 @Override 366 public int getPage() { 367 return super.getPage(); 368 } 369 370 @Override 371 public int getPageCount() { 372 return super.getPageCount(); 373 } 374 375 @Override 376 public boolean hasNextPage() { 377 return super.hasNextPage(); 378 } 379 380 @Override 381 public boolean hasNextPages(int pages) { 382 return super.hasNextPages(pages); 383 } 384 385 @Override 386 public boolean hasPage(int index) { 387 return super.hasPage(index); 388 } 389 390 @Override 391 public boolean hasPreviousPage() { 392 return super.hasPreviousPage(); 393 } 394 395 @Override 396 public boolean hasPreviousPages(int pages) { 397 return super.hasPreviousPages(pages); 398 } 399 400 @Override 401 public void lastPage() { 402 super.lastPage(); 403 } 404 405 @Override 406 public void lastPageStart() { 407 super.lastPageStart(); 408 } 409 410 @Override 411 public void nextPage() { 412 super.nextPage(); 413 } 414 415 @Override 416 public void previousPage() { 417 super.previousPage(); 418 } 419 420 @Override 421 public void setDisplay(HasRows display) { 422 // Enable or disable all buttons. 423 boolean disableButtons = (display == null); 424 setFastForwardDisabled(disableButtons); 425 setNextPageButtonsDisabled(disableButtons); 426 setPrevPageButtonsDisabled(disableButtons); 427 super.setDisplay(display); 428 } 429 430 @Override 431 public void setPage(int index) { 432 super.setPage(index); 433 } 434 435 @Override 436 public void setPageSize(int pageSize) { 437 super.setPageSize(pageSize); 438 } 439 440 @Override 441 public void setPageStart(int index) { 442 super.setPageStart(index); 443 } 444 445 /** 446 * Let the page know that the table is loading. Call this method to clear 447 * all data from the table and hide the current range when new data is being 448 * loaded into the table. 449 */ 450 public void startLoading() { 451 getDisplay().setRowCount(0, true); 452 label.setHTML(""); 453 } 454 455 /** 456 * Get the text to display in the pager that reflects the state of the 457 * pager. 458 * 459 * @return the text 460 */ 461 protected String createText() { 462 // Default text is 1 based. 463 NumberFormat formatter = NumberFormat.getFormat("#,###"); 464 HasRows display = getDisplay(); 465 Range range = display.getVisibleRange(); 466 int pageStart = range.getStart() + 1; 467 int pageSize = range.getLength(); 468 int dataSize = display.getRowCount(); 469 int endIndex = Math.min(dataSize, pageStart + pageSize - 1); 470 endIndex = Math.max(pageStart, endIndex); 471 boolean exact = display.isRowCountExact(); 472 return formatter.format(pageStart) + "-" + formatter.format(endIndex) + (exact ? " of " : " of over ") + formatter.format(dataSize); 473 } 474 475 @Override 476 protected void onRangeOrRowCountChanged() { 477 HasRows display = getDisplay(); 478 label.setText(createText()); 479 480 // Update the prev and first buttons. 481 setPrevPageButtonsDisabled(!hasPreviousPage()); 482 483 // Update the next and last buttons. 484 if (isRangeLimited() || !display.isRowCountExact()) { 485 setNextPageButtonsDisabled(!hasNextPage()); 486 setFastForwardDisabled(!hasNextPages(getFastForwardPages())); 487 } 488 } 489 490 /** 491 * Check if the next button is disabled. Visible for testing. 492 */ 493 boolean isNextButtonDisabled() { 494 return !nextPage.isEnabled(); 495 } 496 497 /** 498 * Check if the previous button is disabled. Visible for testing. 499 */ 500 boolean isPreviousButtonDisabled() { 501 return !prevPage.isEnabled(); 502 } 503 504 /** 505 * Get the number of pages to fast forward based on the current page size. 506 * 507 * @return the number of pages to fast forward 508 */ 509 private int getFastForwardPages() { 510 int pageSize = getPageSize(); 511 return pageSize > 0 ? fastForwardRows / pageSize : 0; 512 } 513 514 public int getFastForwardRows() { 515 return fastForwardRows; 516 } 517 518 public void setFastForwardRows(int fastForwardRows) { 519 this.fastForwardRows = fastForwardRows; 520 } 521 522 /** 523 * Enable or disable the fast forward button. 524 * 525 * @param disabled 526 * true to disable, false to enable 527 */ 528 private void setFastForwardDisabled(boolean disabled) { 529 if (fastForward != null) { 530 fastForward.setEnabled(!disabled); 531 if (disabled) { 532 fastForward.getElement().addClassName(style.disabledButton()); 533 } else { 534 fastForward.getElement().removeClassName(style.disabledButton()); 535 } 536 } 537 } 538 539 /** 540 * Enable or disable the next page buttons. 541 * 542 * @param disabled 543 * true to disable, false to enable 544 */ 545 private void setNextPageButtonsDisabled(boolean disabled) { 546 nextPage.setEnabled(!disabled); 547 if (disabled) { 548 nextPage.getElement().addClassName(style.disabledButton()); 549 } else { 550 nextPage.getElement().removeClassName(style.disabledButton()); 551 } 552 if (lastPage != null) { 553 lastPage.setEnabled(!disabled); 554 if (disabled) { 555 lastPage.getElement().addClassName(style.disabledButton()); 556 } else { 557 lastPage.getElement().removeClassName(style.disabledButton()); 558 } 559 } 560 } 561 562 /** 563 * Enable or disable the previous page buttons. 564 * 565 * @param disabled 566 * true to disable, false to enable 567 */ 568 private void setPrevPageButtonsDisabled(boolean disabled) { 569 firstPage.setEnabled(!disabled); 570 prevPage.setEnabled(!disabled); 571 if (disabled) { 572 firstPage.getElement().addClassName(style.disabledButton()); 573 prevPage.getElement().addClassName(style.disabledButton()); 574 } else { 575 firstPage.getElement().removeClassName(style.disabledButton()); 576 prevPage.getElement().removeClassName(style.disabledButton()); 577 } 578 } 579}