001package gwt.material.design.client.ui;
002/*
003 * #%L
004 * GwtMaterial
005 * %%
006 * Copyright (C) 2015 GwtMaterialDesign
007 * %%
008 * Licensed under the Apache License, Version 2.0 (the "License");
009 * you may not use this file except in compliance with the License.
010 * You may obtain a copy of the License at
011 * 
012 *      http://www.apache.org/licenses/LICENSE-2.0
013 * 
014 * Unless required by applicable law or agreed to in writing, software
015 * distributed under the License is distributed on an "AS IS" BASIS,
016 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017 * See the License for the specific language governing permissions and
018 * limitations under the License.
019 * #L%
020 */
021
022import com.google.gwt.core.client.Scheduler;
023import com.google.gwt.dom.client.Document;
024import com.google.gwt.event.dom.client.ClickEvent;
025import com.google.gwt.event.dom.client.ClickHandler;
026import com.google.gwt.user.client.Command;
027import com.google.gwt.user.client.Event;
028import gwt.material.design.client.base.MaterialWidget;
029import gwt.material.design.client.constants.IconPosition;
030import gwt.material.design.client.constants.IconType;
031import gwt.material.design.client.constants.WavesType;
032import gwt.material.design.client.events.PageSelectionEvent;
033import gwt.material.design.client.ui.html.ListItem;
034
035import static gwt.material.design.client.events.PageSelectionEvent.PageSelectionHandler;
036import static gwt.material.design.client.events.PageSelectionEvent.TYPE;
037
038//@formatter:off
039
040/**
041 * Material Pager with page event
042 * <h3>UiBinder Usage:</h3>
043 * <pre>
044 * {@code<m:MaterialPager  ui:field='pager' />}
045 * </pre>
046 *
047 * @author Guaido79
048 */
049public class MaterialPager extends MaterialWidget {
050
051    private int total;
052    private int pageSize = 10;
053    private int currentPage = 1;
054    private int maxPageLinksShown = 10;
055    private boolean enableIndicator;
056    private String indicatorTemplate = "Page {page} of {total}";
057
058    private int calcTotalPages;
059    private int calcShowingPageFrom;
060    private int calcShowingPageTo;
061    private boolean calcInitialized;
062
063    private PagerListItem linkLeft;
064    private PagerListItem linkRight;
065
066    private MaterialChip indicator;
067
068    public MaterialPager() {
069        super(Document.get().createULElement(), "pagination");
070        setWaves(WavesType.DEFAULT);
071        removeStyleName("waves-effect");
072    }
073
074    public MaterialPager(int total, int pageSize) {
075        this();
076        this.total = total;
077        this.pageSize = pageSize;
078    }
079
080    public MaterialPager(int total, int pageSize, int currentPage) {
081        this(total, pageSize);
082        this.currentPage = currentPage;
083    }
084
085    @Override
086    protected void onLoad() {
087        super.onLoad();
088        init();
089    }
090
091    private void init() {
092        if (!calcInitialized) {
093            calcTotalPages = total / pageSize + (((double) total % (double) pageSize) > 0 ? 1 : 0);
094
095            add(getOrCreateLiElementLeft());
096            moveNextPagesRange();
097            add(getOrCreateLiElementRight());
098            if (enableIndicator) {
099                add(createLiElementIndicator());
100            }
101            onPageSelection(1);
102
103            calcInitialized = true;
104        }
105    }
106
107    protected void moveNextPagesRange() {
108        calcShowingPageFrom = currentPage;
109        calcShowingPageTo = Math.min(currentPage + maxPageLinksShown - 1, calcTotalPages);
110        createPageNumberLinks();
111    }
112
113    protected void movePreviousPagesRange() {
114        calcShowingPageFrom = currentPage - maxPageLinksShown + 1;
115        calcShowingPageTo = currentPage;
116        createPageNumberLinks();
117    }
118
119    protected void createPageNumberLinks() {
120
121        for (int i = 0; i < getWidgetCount(); i++) {
122            final PagerListItem widget = (PagerListItem) getWidget(i);
123            if (!widget.isFixed()) {
124                Scheduler.get().scheduleDeferred(new Command() {
125                    @Override
126                    public void execute() {
127                        widget.removeFromParent();
128                    }
129                });
130            }
131        }
132        int insertionIndex = 1;
133        for (int i = calcShowingPageFrom; i <= calcShowingPageTo; i++) {
134            final PagerListItem liElementForPage = createLiElementForPage(i);
135
136            Scheduler.get().scheduleDeferred(new InsertElementAtPositionCommand(insertionIndex++) {
137                @Override
138                public void execute() {
139                    insert(liElementForPage, insertionIndex);
140                }
141            });
142
143        }
144    }
145
146    protected PagerListItem createLiElementForPage(final int page) {
147        final PagerListItem pageLiElement = new PagerListItem();
148        pageLiElement.setFixed(false);
149        pageLiElement.add(createLinkPage(page));
150
151        addPageSelectionHandler(new PageSelectionHandler() {
152            @Override
153            public void onPageSelected(PageSelectionEvent event) {
154                pageLiElement.setActive(event.getPageTo() == page);
155            }
156        });
157
158        pageLiElement.addHandler(new ClickHandler() {
159            @Override
160            public void onClick(ClickEvent event) {
161                onPageSelection(page);
162                event.preventDefault();
163                event.stopPropagation();
164            }
165        }, ClickEvent.getType());
166
167        return pageLiElement;
168    }
169
170    protected PagerListItem getOrCreateLiElementLeft() {
171        linkLeft = new PagerListItem();
172        linkLeft.setFixed(true);
173        MaterialLink mLink = createLinkLeft();
174        linkLeft.addHandler(new ClickHandler() {
175            @Override
176            public void onClick(ClickEvent event) {
177                if (linkLeft.isEnabled())
178                    onPageSelection(currentPage - 1);
179                event.preventDefault();
180                event.stopPropagation();
181            }
182        }, ClickEvent.getType());
183        this.linkLeft.add(mLink);
184
185        addPageSelectionHandler(new PageSelectionHandler() {
186            @Override
187            public void onPageSelected(PageSelectionEvent event) {
188                MaterialPager.this.linkLeft.setEnabled(event.getPageTo() > 1);
189            }
190        });
191
192        return this.linkLeft;
193    }
194
195    protected PagerListItem getOrCreateLiElementRight() {
196        linkRight = new PagerListItem();
197        linkRight.setFixed(true);
198        MaterialLink mLink = createLinkRight();
199        linkRight.addHandler(new ClickHandler() {
200            @Override
201            public void onClick(ClickEvent event) {
202                if (linkRight.isEnabled())
203                    onPageSelection(currentPage + 1);
204                event.stopPropagation();
205                event.preventDefault();
206            }
207        }, ClickEvent.getType());
208        this.linkRight.add(mLink);
209
210        addPageSelectionHandler(new PageSelectionHandler() {
211            @Override
212            public void onPageSelected(PageSelectionEvent event) {
213                MaterialPager.this.linkRight.setEnabled(event.getPageTo() < calcTotalPages);
214            }
215        });
216
217        return this.linkRight;
218    }
219
220    protected PagerListItem createLiElementIndicator() {
221
222        PagerListItem indicatorLi = new PagerListItem(false);
223        indicatorLi.setFixed(true);
224        indicatorLi.add(getOrCreateIndicator());
225        return indicatorLi;
226    }
227
228    protected MaterialChip getOrCreateIndicator() {
229        indicator = new MaterialChip();
230        indicator.getElement().getStyle().setBackgroundColor("inherit");
231        addPageSelectionHandler(new PageSelectionHandler() {
232            @Override
233            public void onPageSelected(PageSelectionEvent event) {
234                indicator.setText(
235                    indicatorTemplate
236                        .replaceAll("\\{page\\}", String.valueOf(event.getPageTo()))
237                        .replaceAll("\\{total\\}", String.valueOf(event.getTotalPage()))
238                );
239            }
240        });
241
242        return indicator;
243    }
244
245    protected MaterialLink createLinkPage(final int page) {
246        MaterialLink link = new MaterialLink(String.valueOf(page));
247
248        return link;
249    }
250
251    protected MaterialLink createLinkLeft() {
252        final MaterialLink linkLeft = new MaterialLink(IconType.CHEVRON_LEFT);
253        linkLeft.setIconPosition(IconPosition.NONE);
254        return linkLeft;
255    }
256
257    protected MaterialLink createLinkRight() {
258        final MaterialLink linkRight = new MaterialLink(IconType.CHEVRON_RIGHT);
259        linkRight.setIconPosition(IconPosition.NONE);
260        return linkRight;
261    }
262
263    protected void onPageSelection(int page) {
264        this.currentPage = page;
265
266        if (this.currentPage > calcShowingPageTo) {
267            moveNextPagesRange();
268        }
269        if (this.currentPage < calcShowingPageFrom) {
270            movePreviousPagesRange();
271        }
272
273        PageSelectionEvent event = new PageSelectionEvent();
274        event.setPageFrom(this.currentPage);
275        event.setPageTo(page);
276        event.setTotalPage(this.calcTotalPages);
277
278        fireEvent(event);
279    }
280
281    public void addPageSelectionHandler(PageSelectionHandler handler) {
282        this.addHandler(handler, TYPE);
283    }
284
285    public boolean isEnableIndicator() {
286        return enableIndicator;
287    }
288
289    public void setEnableIndicator(boolean enableIndicator) {
290        this.enableIndicator = enableIndicator;
291    }
292
293    public int getTotal() {
294        return total;
295    }
296
297    public void setTotal(final int total) {
298        boolean needToClear = total != this.total;
299        this.total = total;
300        currentPage = 1;
301        if (calcInitialized && needToClear) {
302            this.clear();
303            init();
304        }
305    }
306
307    public int getPageSize() {
308        return pageSize;
309    }
310
311    public void setPageSize(int pageSize) {
312        this.pageSize = pageSize;
313    }
314
315    public int getCurrentPage() {
316        return currentPage;
317    }
318
319    public void setCurrentPage(int currentPage) {
320        this.currentPage = currentPage;
321    }
322
323    public int getMaxPageLinksShown() {
324        return maxPageLinksShown;
325    }
326
327    public void setMaxPageLinksShown(int maxPageLinksShown) {
328        this.maxPageLinksShown = maxPageLinksShown;
329    }
330
331    public String getIndicatorTemplate() {
332        return indicatorTemplate;
333    }
334
335    /**
336     * Set the paging indicator label with a custom template
337     * <ul>
338     * <li><strong>{page}</strong> is the current page</li>
339     * <li><strong>{total}</strong> is the total page</li>
340     * </ul>
341     * Example
342     * <pre>
343     *{@code
344     * Page {page} of {total}
345     * }</pre>
346     *
347     * @param indicatorTemplate
348     */
349    public void setIndicatorTemplate(String indicatorTemplate) {
350        this.indicatorTemplate = indicatorTemplate;
351    }
352
353    static abstract class InsertElementAtPositionCommand implements Scheduler.ScheduledCommand {
354        protected int insertionIndex;
355
356        InsertElementAtPositionCommand(int insertionIndex) {
357            this.insertionIndex = insertionIndex;
358        }
359    }
360
361    static class PagerListItem extends ListItem {
362
363        private boolean fixed;
364        private boolean enabled;
365
366        public PagerListItem() {
367            this(true);
368        }
369
370        public PagerListItem(boolean clickable) {
371            if (clickable) {
372                addStyleName("waves-effect");
373                sinkEvents(Event.ONCLICK | Event.TOUCHEVENTS);
374            }
375        }
376
377        public boolean isFixed() {
378            return fixed;
379        }
380
381        public void setFixed(boolean fixed) {
382            this.fixed = fixed;
383        }
384
385        public void setActive(boolean active) {
386            if (active) {
387                addStyleName("active");
388            } else {
389                removeStyleName("active");
390            }
391        }
392
393        @Override
394        public boolean isEnabled() {
395            return enabled;
396        }
397
398        @Override
399        public void setEnabled(boolean enabled) {
400            this.enabled = enabled;
401            if (!enabled) {
402                addStyleName("disabled");
403            } else {
404                removeStyleName("disabled");
405            }
406        }
407    }
408}