1 /* 2 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javafx.scene.control; 27 28 import java.util.ArrayList; 29 import java.util.Collections; 30 import java.util.List; 31 import javafx.beans.DefaultProperty; 32 import javafx.beans.property.IntegerProperty; 33 import javafx.beans.property.ObjectProperty; 34 import javafx.beans.property.SimpleIntegerProperty; 35 import javafx.beans.property.SimpleObjectProperty; 36 import javafx.beans.value.ObservableValue; 37 import javafx.beans.value.WritableValue; 38 import javafx.css.CssMetaData; 39 import javafx.css.StyleableIntegerProperty; 40 import javafx.css.Styleable; 41 import javafx.css.StyleableProperty; 42 import javafx.scene.AccessibleRole; 43 import javafx.scene.Node; 44 import javafx.util.Callback; 45 import javafx.css.converter.SizeConverter; 46 import javafx.scene.control.skin.PaginationSkin; 47 48 /** 49 * <p> 50 * A Pagination control is used for navigation between pages of a single content, 51 * which has been divided into smaller parts. 52 * </p> 53 * 54 * <h3>Styling the page indicators</h3> 55 * <p> 56 * The control can be customized to display numeric page indicators or bullet style indicators by 57 * setting the style class {@link #STYLE_CLASS_BULLET}. The 58 * {@link #maxPageIndicatorCountProperty() maxPageIndicatorCountProperty} can be used to change 59 * the maximum number of page indicators. The property value can also be changed 60 * via CSS using -fx-max-page-indicator-count. 61 *</p> 62 * 63 * <h3>Page count</h3> 64 * <p> 65 * The {@link #pageCountProperty() pageCountProperty} controls the number of 66 * pages this pagination control has. If the page count is 67 * not known {@link #INDETERMINATE} should be used as the page count. 68 * </p> 69 * 70 * <h3>Page factory</h3> 71 * <p> 72 * The {@link #pageFactoryProperty() pageFactoryProperty} is a callback function 73 * that is called when a page has been selected by the application or 74 * the user. The function is required for the functionality of the pagination 75 * control. The callback function should load and return the contents of the selected page. 76 * Null should be returned if the selected page index does not exist. 77 * </p> 78 * 79 * <h3>Creating a Pagination control:</h3> 80 * <p> 81 * A simple example of how to create a pagination control with ten pages and 82 * each page containing ten hyperlinks. 83 * </p> 84 * 85 * <pre> 86 * {@code 87 * Pagination pagination = new Pagination(10, 0); 88 * pagination.setPageFactory(new Callback<Integer, Node>() { 89 * public Node call(Integer pageIndex) { 90 * VBox box = new VBox(5); 91 * for (int i = 0; i < pageIndex + 10; i++) { 92 * Hyperlink link = new Hyperlink(myurls[i]); 93 * box.getChildren().add(link); 94 * } 95 * return box; 96 * } 97 * }); 98 * }</pre> 99 * @since JavaFX 2.2 100 */ 101 @DefaultProperty("pages") 102 public class Pagination extends Control { 103 104 private static final int DEFAULT_MAX_PAGE_INDICATOR_COUNT = 10; 105 106 /** 107 * The style class to change the numeric page indicators to 108 * bullet indicators. 109 */ 110 public static final String STYLE_CLASS_BULLET = "bullet"; 111 112 /** 113 * Value for indicating that the page count is indeterminate. 114 * 115 * @see #setPageCount 116 */ 117 public static final int INDETERMINATE = Integer.MAX_VALUE; 118 119 /** 120 * Constructs a new Pagination control with the specified page count 121 * and page index. 122 * 123 * @param pageCount the number of pages for the pagination control 124 * @param pageIndex the index of the first page. 125 * 126 */ 127 public Pagination(int pageCount, int pageIndex) { 128 getStyleClass().setAll(DEFAULT_STYLE_CLASS); 129 setAccessibleRole(AccessibleRole.PAGINATION); 130 setPageCount(pageCount); 131 setCurrentPageIndex(pageIndex); 132 } 133 134 /** 135 * Constructs a new Pagination control with the specified page count. 136 * 137 * @param pageCount the number of pages for the pagination control 138 * 139 */ 140 public Pagination(int pageCount) { 141 this(pageCount, 0); 142 } 143 144 /** 145 * Constructs a Pagination control with an {@link #INDETERMINATE} page count 146 * and a page index equal to zero. 147 */ 148 public Pagination() { 149 this(INDETERMINATE, 0); 150 } 151 152 /*************************************************************************** 153 * * 154 * Properties * 155 * * 156 **************************************************************************/ 157 158 private int oldMaxPageIndicatorCount = DEFAULT_MAX_PAGE_INDICATOR_COUNT; 159 private IntegerProperty maxPageIndicatorCount; 160 161 /** 162 * Sets the maximum number of page indicators. 163 * 164 * @param value the number of page indicators. The default is 10. 165 */ 166 public final void setMaxPageIndicatorCount(int value) { maxPageIndicatorCountProperty().set(value); } 167 168 /** 169 * Returns the maximum number of page indicators. 170 * @return the maximum number of page indicators 171 */ 172 public final int getMaxPageIndicatorCount() { 173 return maxPageIndicatorCount == null ? DEFAULT_MAX_PAGE_INDICATOR_COUNT : maxPageIndicatorCount.get(); 174 } 175 176 /** 177 * The maximum number of page indicators to use for this pagination control. 178 * The maximum number of pages indicators will remain unchanged if the value is less than 1 179 * or greater than the {@link #pageCount}. The number of page indicators will be 180 * reduced to fit the control if the {@code maxPageIndicatorCount} cannot fit. 181 * 182 * The default is 10 page indicators. 183 * @return the maximum number of page indicators to use for this pagination control 184 */ 185 public final IntegerProperty maxPageIndicatorCountProperty() { 186 if (maxPageIndicatorCount == null) { 187 maxPageIndicatorCount = new StyleableIntegerProperty(DEFAULT_MAX_PAGE_INDICATOR_COUNT) { 188 189 @Override protected void invalidated() { 190 if (!maxPageIndicatorCount.isBound()) { 191 if (getMaxPageIndicatorCount() < 1 || getMaxPageIndicatorCount() > getPageCount()) { 192 setMaxPageIndicatorCount(oldMaxPageIndicatorCount); 193 } 194 oldMaxPageIndicatorCount = getMaxPageIndicatorCount(); 195 } 196 } 197 198 @Override 199 public CssMetaData<Pagination,Number> getCssMetaData() { 200 return StyleableProperties.MAX_PAGE_INDICATOR_COUNT; 201 } 202 203 @Override 204 public Object getBean() { 205 return Pagination.this; 206 } 207 208 @Override 209 public String getName() { 210 return "maxPageIndicatorCount"; 211 } 212 }; 213 } 214 return maxPageIndicatorCount; 215 } 216 217 private int oldPageCount = INDETERMINATE; 218 private IntegerProperty pageCount = new SimpleIntegerProperty(this, "pageCount", INDETERMINATE) { 219 @Override protected void invalidated() { 220 if (!pageCount.isBound()) { 221 if (getPageCount() < 1) { 222 setPageCount(oldPageCount); 223 } 224 oldPageCount = getPageCount(); 225 } 226 } 227 }; 228 229 /** 230 * Sets the number of pages. 231 * 232 * @param value the number of pages 233 */ 234 public final void setPageCount(int value) { pageCount.set(value); } 235 236 /** 237 * Returns the number of pages. 238 * @return the number of pages 239 */ 240 public final int getPageCount() { return pageCount.get(); } 241 242 /** 243 * The number of pages for this pagination control. This 244 * value must be greater than or equal to 1. {@link #INDETERMINATE} 245 * should be used as the page count if the total number of pages is unknown. 246 * 247 * The default is an {@link #INDETERMINATE} number of pages. 248 * @return the number of pages for this pagination control 249 */ 250 public final IntegerProperty pageCountProperty() { return pageCount; } 251 252 private final IntegerProperty currentPageIndex = new SimpleIntegerProperty(this, "currentPageIndex", 0) { 253 @Override protected void invalidated() { 254 if (!currentPageIndex.isBound()) { 255 if (getCurrentPageIndex() < 0) { 256 setCurrentPageIndex(0); 257 } else if (getCurrentPageIndex() > getPageCount() - 1) { 258 setCurrentPageIndex(getPageCount() - 1); 259 } 260 } 261 } 262 263 @Override 264 public void bind(ObservableValue<? extends Number> rawObservable) { 265 throw new UnsupportedOperationException("currentPageIndex supports only bidirectional binding"); 266 } 267 }; 268 269 /** 270 * Sets the current page index. 271 * @param value the current page index. 272 */ 273 public final void setCurrentPageIndex(int value) { currentPageIndex.set(value); } 274 275 /** 276 * Returns the current page index. 277 * @return the current page index 278 */ 279 public final int getCurrentPageIndex() { return currentPageIndex.get(); } 280 281 /** 282 * The current page index to display for this pagination control. The first page will be 283 * the current page if the value is less than 0. Similarly the last page 284 * will be the current page if the value is greater than the {@link #pageCount} 285 * 286 * The default is 0 for the first page. 287 * <p> 288 * Because the page indicators set the current page index, the currentPageIndex property permits only 289 * bidirectional binding. 290 * The {@link javafx.beans.property.IntegerProperty#bind(javafx.beans.value.ObservableValue) bind} method 291 * throws an UnsupportedOperationException. 292 * </p> 293 * @return the current page index property 294 */ 295 public final IntegerProperty currentPageIndexProperty() { return currentPageIndex; } 296 297 private ObjectProperty<Callback<Integer, Node>> pageFactory = 298 new SimpleObjectProperty<Callback<Integer, Node>>(this, "pageFactory"); 299 300 /** 301 * Sets the page factory callback function. 302 * @param value the page factory callback function 303 */ 304 public final void setPageFactory(Callback<Integer, Node> value) { pageFactory.set(value); } 305 306 /** 307 * Returns the page factory callback function. 308 * @return the page factory callback function 309 */ 310 public final Callback<Integer, Node> getPageFactory() {return pageFactory.get(); } 311 312 /** 313 * The pageFactory callback function that is called when a page has been 314 * selected by the application or the user. 315 * 316 * This function is required for the functionality of the pagination 317 * control. The callback function should load and return the contents the page index. 318 * Null should be returned if the page index does not exist. The currentPageIndex 319 * will not change when null is returned. 320 * 321 * The default is null if there is no page factory set. 322 * @return the page factory property 323 */ 324 public final ObjectProperty<Callback<Integer, Node>> pageFactoryProperty() { return pageFactory; } 325 326 327 /*************************************************************************** 328 * * 329 * Methods * 330 * * 331 **************************************************************************/ 332 333 /** {@inheritDoc} */ 334 @Override protected Skin<?> createDefaultSkin() { 335 return new PaginationSkin(this); 336 } 337 338 /*************************************************************************** 339 * * 340 * Stylesheet Handling * 341 * * 342 **************************************************************************/ 343 344 private static final String DEFAULT_STYLE_CLASS = "pagination"; 345 346 private static class StyleableProperties { 347 private static final CssMetaData<Pagination,Number> MAX_PAGE_INDICATOR_COUNT = 348 new CssMetaData<Pagination,Number>("-fx-max-page-indicator-count", 349 SizeConverter.getInstance(), DEFAULT_MAX_PAGE_INDICATOR_COUNT) { 350 351 @Override 352 public boolean isSettable(Pagination n) { 353 return n.maxPageIndicatorCount == null || !n.maxPageIndicatorCount.isBound(); 354 } 355 356 @Override 357 public StyleableProperty<Number> getStyleableProperty(Pagination n) { 358 return (StyleableProperty<Number>)(WritableValue<Number>)n.maxPageIndicatorCountProperty(); 359 } 360 }; 361 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 362 static { 363 final List<CssMetaData<? extends Styleable, ?>> styleables = 364 new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData()); 365 styleables.add(MAX_PAGE_INDICATOR_COUNT); 366 STYLEABLES = Collections.unmodifiableList(styleables); 367 } 368 } 369 370 /** 371 * @return The CssMetaData associated with this class, which may include the 372 * CssMetaData of its superclasses. 373 * @since JavaFX 8.0 374 */ 375 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 376 return StyleableProperties.STYLEABLES; 377 } 378 379 /** 380 * {@inheritDoc} 381 * @since JavaFX 8.0 382 */ 383 @Override 384 public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { 385 return getClassCssMetaData(); 386 } 387 388 }