/* MyMAM - Open Source Digital Media Asset Management. * http://www.mymam.net * * Copyright 2013, MyMAM contributors as indicated by the @author tag. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.mymam.controller; import javax.faces.bean.ManagedBean; import javax.faces.bean.ManagedProperty; import javax.faces.bean.RequestScoped; import javax.faces.bean.ViewScoped; import java.util.ArrayList; import java.util.List; /** * Backing bean for the paginator composite component. * <p/> * The paginator is based on * <a href="http://twitter.github.com/bootstrap/components.html#pagination">Bootstrap's pagination component</a>. * * @author fstab */ @ManagedBean @RequestScoped public class PaginatorBean { @ManagedProperty(value = "#{cc.attrs.paginatable}") private Paginatable paginatable; @ManagedProperty(value = "#{cc.attrs.prevLabel}") private String prevLabel; @ManagedProperty(value = "#{cc.attrs.nextLabel}") private String nextLabel; @ManagedProperty(value = "#{cc.attrs.size}") private int size = 9; // max number of page links to be displayed. /** * Must provide setter for {@link ManagedProperty}. * * @param paginatable the component to be paginated. */ public void setPaginatable(Paginatable paginatable) { this.paginatable = paginatable; } /** * Must provide setter for {@link ManagedProperty}. * * @param prevLabel internationalized label for the previous page link. */ public void setPrevLabel(String prevLabel) { this.prevLabel = prevLabel; } /** * Must provide setter for {@link ManagedProperty}. * * @param nextLabel internationalized label for the next page link. */ public void setNextLabel(String nextLabel) { this.nextLabel = nextLabel; } /** * Must provide setter for {@link ManagedProperty}. * * @param size maximum number of page links to be displayed. * Must be an odd number, because the link to the current page * is shown in the middle of the paginator. * * @throws IllegalArgumentException if size is even. */ public void setSize(int size) throws IllegalArgumentException { if ( size % 2 == 0 ) { throw new IllegalArgumentException("The paginator size must be an odd number."); } this.size = size; } /** * Create the backing beans for the page links to be displayed. * * @return the links to be displayed */ public List<PaginatorLinkBean> makeLinks() { int nPages = paginatable.getNumberOfPages(); int curPage = paginatable.getCurrentPage(); List<PaginatorLinkBean> result = new ArrayList<>(); result.add(makePrevLink()); Range range = Range.makeRange(curPage, nPages, size); for ( int target=range.first; target<=range.last; target++ ) { result.add(makeLink(target)); } result.add(makeNextLink(nPages)); return result; } /** * If there is only a single page, the paginator will not be displayed. * * @return true if there is more than one page. false otherwise. */ public boolean hasMultiplePages() { return paginatable.getNumberOfPages() > 1; } /** * Set the current page. This is called as the action method for the page links. * <p/> * Pages are numbered starting with 1, i.e. the first page is page 1, not page 0. * * @param page the number of the new page to be displayed. */ public void selectPage(int page) { paginatable.selectPage(page); } /** * Represents the range (interval) of links for the paginator: * from the first page link to the last page link. * <p/> * The size of the range (i.e. the number of links) is at max {@link PaginatorBean#size}. */ private static class Range { final int first; final int last; private Range(int first, int last) { this.first = first; this.last = last; } /** * The paginator displays max {@link PaginatorBean#size} links, with the * link to the current page in the middle of the range. * <p/> * This helper method calculates the range of page links for the paginator. * <dl> * <dt>Example 1</dt> * <dd>If there are 17 pages, the current page is 6, and size is 9, the range is [2, 10].</dd> * <dt>Example 2</dt> * <dd>If there are 17 pages, the current page is 3, and size is 9, the range is [1, 9]</dd> * <dt>Example 2</dt> * <dd>If there are 3 pages, the current page is 3, and size is 9, the range is [1, 3]</dd> * </dl> * * @param curPage the current page * @param nPages the total number of pages * @return the range of pages to be linked in the paginator. */ public static Range makeRange(int curPage, int nPages, int size) { int first = curPage > size /2 ? curPage - size /2 : 1; int last = curPage + size /2 <= nPages ? curPage + size /2 : nPages; if ( first == 1 ) { last += size - (last + 1 - first); if ( last >= nPages ) { last = nPages; } } if ( last == nPages ) { first -= size - (last + 1 - first); if ( first <= 1 ) { first = 1; } } return new Range(first, last); } } private PaginatorLinkBean makePrevLink() { if ( paginatable.getCurrentPage() == 1 ) { return new PaginatorLinkBean(prevLabel, 1, PaginatorLinkBean.State.DISABLED); } return new PaginatorLinkBean(prevLabel, paginatable.getCurrentPage() - 1); } private PaginatorLinkBean makeLink(int target) { if ( paginatable.getCurrentPage() == target ) { return new PaginatorLinkBean(target, PaginatorLinkBean.State.ACTIVE); } return new PaginatorLinkBean(target); } private PaginatorLinkBean makeNextLink(int nPages) { if ( paginatable.getCurrentPage() == nPages ) { return new PaginatorLinkBean(nextLabel, nPages, PaginatorLinkBean.State.DISABLED); } return new PaginatorLinkBean(nextLabel, paginatable.getCurrentPage() + 1); } }