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 */ 171 public final int getMaxPageIndicatorCount() { 172 return maxPageIndicatorCount == null ? DEFAULT_MAX_PAGE_INDICATOR_COUNT : maxPageIndicatorCount.get(); 173 } 174 175 /** 176 * The maximum number of page indicators to use for this pagination control. 177 * The maximum number of pages indicators will remain unchanged if the value is less than 1 178 * or greater than the {@link #pageCount}. The number of page indicators will be 179 * reduced to fit the control if the {@code maxPageIndicatorCount} cannot fit. 180 * 181 * The default is 10 page indicators. 182 */ 183 public final IntegerProperty maxPageIndicatorCountProperty() { 184 if (maxPageIndicatorCount == null) { 185 maxPageIndicatorCount = new StyleableIntegerProperty(DEFAULT_MAX_PAGE_INDICATOR_COUNT) { 186 187 @Override protected void invalidated() { 188 if (!maxPageIndicatorCount.isBound()) { 189 if (getMaxPageIndicatorCount() < 1 || getMaxPageIndicatorCount() > getPageCount()) { 190 setMaxPageIndicatorCount(oldMaxPageIndicatorCount); 191 } 192 oldMaxPageIndicatorCount = getMaxPageIndicatorCount(); 193 } 194 } 195 196 @Override 197 public CssMetaData<Pagination,Number> getCssMetaData() { 198 return StyleableProperties.MAX_PAGE_INDICATOR_COUNT; 199 } 200 201 @Override 202 public Object getBean() { 203 return Pagination.this; 204 } 205 206 @Override 207 public String getName() { 208 return "maxPageIndicatorCount"; 209 } 210 }; 211 } 212 return maxPageIndicatorCount; 213 } 214 215 private int oldPageCount = INDETERMINATE; 216 private IntegerProperty pageCount = new SimpleIntegerProperty(this, "pageCount", INDETERMINATE) { 217 @Override protected void invalidated() { 218 if (!pageCount.isBound()) { 219 if (getPageCount() < 1) { 220 setPageCount(oldPageCount); 221 } 222 oldPageCount = getPageCount(); 223 } 224 } 225 }; 226 227 /** 228 * Sets the number of pages. 229 * 230 * @param value the number of pages 231 */ 232 public final void setPageCount(int value) { pageCount.set(value); } 233 234 /** 235 * Returns the number of pages. 236 */ 237 public final int getPageCount() { return pageCount.get(); } 238 239 /** 240 * The number of pages for this pagination control. This 241 * value must be greater than or equal to 1. {@link #INDETERMINATE} 242 * should be used as the page count if the total number of pages is unknown. 243 * 244 * The default is an {@link #INDETERMINATE} number of pages. 245 */ 246 public final IntegerProperty pageCountProperty() { return pageCount; } 247 248 private final IntegerProperty currentPageIndex = new SimpleIntegerProperty(this, "currentPageIndex", 0) { 249 @Override protected void invalidated() { 250 if (!currentPageIndex.isBound()) { 251 if (getCurrentPageIndex() < 0) { 252 setCurrentPageIndex(0); 253 } else if (getCurrentPageIndex() > getPageCount() - 1) { 254 setCurrentPageIndex(getPageCount() - 1); 255 } 256 } 257 } 258 259 @Override 260 public void bind(ObservableValue<? extends Number> rawObservable) { 261 throw new UnsupportedOperationException("currentPageIndex supports only bidirectional binding"); 262 } 263 }; 264 265 /** 266 * Sets the current page index. 267 * @param value the current page index. 268 */ 269 public final void setCurrentPageIndex(int value) { currentPageIndex.set(value); } 270 271 /** 272 * Returns the current page index. 273 */ 274 public final int getCurrentPageIndex() { return currentPageIndex.get(); } 275 276 /** 277 * The current page index to display for this pagination control. The first page will be 278 * the current page if the value is less than 0. Similarly the last page 279 * will be the current page if the value is greater than the {@link #pageCount} 280 * 281 * The default is 0 for the first page. 282 * <p> 283 * Because the page indicators set the current page index, the currentPageIndex property permits only 284 * bidirectional binding. 285 * The {@link javafx.beans.property.IntegerProperty#bind(javafx.beans.value.ObservableValue) bind} method 286 * throws an UnsupportedOperationException. 287 * </p> 288 */ 289 public final IntegerProperty currentPageIndexProperty() { return currentPageIndex; } 290 291 private ObjectProperty<Callback<Integer, Node>> pageFactory = 292 new SimpleObjectProperty<Callback<Integer, Node>>(this, "pageFactory"); 293 294 /** 295 * Sets the page factory callback function. 296 */ 297 public final void setPageFactory(Callback<Integer, Node> value) { pageFactory.set(value); } 298 299 /** 300 * Returns the page factory callback function. 301 */ 302 public final Callback<Integer, Node> getPageFactory() {return pageFactory.get(); } 303 304 /** 305 * The pageFactory callback function that is called when a page has been 306 * selected by the application or the user. 307 * 308 * This function is required for the functionality of the pagination 309 * control. The callback function should load and return the contents the page index. 310 * Null should be returned if the page index does not exist. The currentPageIndex 311 * will not change when null is returned. 312 * 313 * The default is null if there is no page factory set. 314 */ 315 public final ObjectProperty<Callback<Integer, Node>> pageFactoryProperty() { return pageFactory; } 316 317 318 /*************************************************************************** 319 * * 320 * Methods * 321 * * 322 **************************************************************************/ 323 324 /** {@inheritDoc} */ 325 @Override protected Skin<?> createDefaultSkin() { 326 return new PaginationSkin(this); 327 } 328 329 /*************************************************************************** 330 * * 331 * Stylesheet Handling * 332 * * 333 **************************************************************************/ 334 335 private static final String DEFAULT_STYLE_CLASS = "pagination"; 336 337 private static class StyleableProperties { 338 private static final CssMetaData<Pagination,Number> MAX_PAGE_INDICATOR_COUNT = 339 new CssMetaData<Pagination,Number>("-fx-max-page-indicator-count", 340 SizeConverter.getInstance(), DEFAULT_MAX_PAGE_INDICATOR_COUNT) { 341 342 @Override 343 public boolean isSettable(Pagination n) { 344 return n.maxPageIndicatorCount == null || !n.maxPageIndicatorCount.isBound(); 345 } 346 347 @Override 348 public StyleableProperty<Number> getStyleableProperty(Pagination n) { 349 return (StyleableProperty<Number>)(WritableValue<Number>)n.maxPageIndicatorCountProperty(); 350 } 351 }; 352 private static final List<CssMetaData<? extends Styleable, ?>> STYLEABLES; 353 static { 354 final List<CssMetaData<? extends Styleable, ?>> styleables = 355 new ArrayList<CssMetaData<? extends Styleable, ?>>(Control.getClassCssMetaData()); 356 styleables.add(MAX_PAGE_INDICATOR_COUNT); 357 STYLEABLES = Collections.unmodifiableList(styleables); 358 } 359 } 360 361 /** 362 * @return The CssMetaData associated with this class, which may include the 363 * CssMetaData of its superclasses. 364 * @since JavaFX 8.0 365 */ 366 public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() { 367 return StyleableProperties.STYLEABLES; 368 } 369 370 /** 371 * {@inheritDoc} 372 * @since JavaFX 8.0 373 */ 374 @Override 375 public List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() { 376 return getClassCssMetaData(); 377 } 378 379 }