001/*
002 * #%L
003 * GwtMaterial
004 * %%
005 * Copyright (C) 2015 - 2016 GwtMaterialDesign
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 * 
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 * 
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package gwt.material.design.client.ui.pager;
021
022import com.google.gwt.core.client.GWT;
023import com.google.gwt.dom.client.Document;
024import gwt.material.design.client.base.MaterialWidget;
025import gwt.material.design.client.base.constants.TableCssName;
026import gwt.material.design.client.data.DataSource;
027import gwt.material.design.client.data.loader.LoadCallback;
028import gwt.material.design.client.data.loader.LoadConfig;
029import gwt.material.design.client.data.loader.LoadResult;
030import gwt.material.design.client.ui.MaterialToast;
031import gwt.material.design.client.ui.table.MaterialDataTable;
032
033/**
034 * Material Data Pager - a simple pager for Material Data Table component
035 *
036 * @author kevzlou7979
037 */
038public class MaterialDataPager<T> extends MaterialWidget implements HasPager {
039
040    private MaterialDataTable<T> table;
041    private DataSource<T> dataSource;
042
043    private int offset = 0;
044    private int limit = 0;
045    private int currentPage = 1;
046    private int totalRows = 0;
047    private int[] limitOptions = new int[]{5, 10, 20};
048
049    private PageActionsPanel actionsPanel = new PageActionsPanel(this);
050    private PageNumberSelection pageNumberSelection = new PageNumberSelection(this);
051    private PageRowSelection pageRowSelection = new PageRowSelection(this);
052
053    public MaterialDataPager() {
054        super(Document.get().createDivElement(), TableCssName.DATA_PAGER, TableCssName.ROW);
055    }
056
057    public MaterialDataPager(MaterialDataTable<T> table, DataSource<T> dataSource) {
058        this();
059        this.table = table;
060        this.dataSource = dataSource;
061    }
062
063    /**
064     * Initialize the data pager for navigation
065     */
066    @Override
067    protected void onLoad() {
068        super.onLoad();
069
070        limit = limitOptions[0];
071
072        add(pageNumberSelection);
073        add(pageRowSelection);
074        add(actionsPanel);
075
076        firstPage();
077    }
078
079    public void updateRowsPerPage(int limit) {
080        if ((totalRows / currentPage) < limit) {
081            lastPage();
082            return;
083        }
084        gotoPage(pageNumberSelection.getValue());
085    }
086
087    @Override
088    public void next() {
089        currentPage++;
090        gotoPage(currentPage);
091    }
092
093    @Override
094    public void previous() {
095        currentPage--;
096        gotoPage(currentPage);
097    }
098
099    @Override
100    public void lastPage() {
101        if (isExcess()) {
102            gotoPage((totalRows / limit) + 1);
103        }else {
104            gotoPage(totalRows / limit);
105        }
106
107        pageNumberSelection.setSelectedIndex(currentPage - 1);
108    }
109
110    @Override
111    public void firstPage() {
112        gotoPage(1);
113    }
114
115    @Override
116    public void gotoPage(int page) {
117        this.currentPage = page;
118        doLoad((page * limit) - limit, limit);
119    }
120
121    @Override
122    public int getCurrentPage() {
123        return currentPage;
124    }
125
126    @Override
127    public void setLimit(int limit) {
128        this.limit = limit;
129    }
130
131    @Override
132    public boolean isNext() {
133        return offset + limit < totalRows;
134    }
135
136    @Override
137    public boolean isPrevious() {
138        return offset - limit >= 0;
139    }
140
141    /**
142     * Set the limit as an array of options to be populated inside the rows per page listbox
143     */
144    public void setLimitOptions(int... limitOptions) {
145        this.limitOptions = limitOptions;
146    }
147
148    /**
149     * Check whether there are excess rows to be rendered with given limit
150     */
151    public boolean isExcess() {
152        return totalRows % limit > 0;
153    }
154
155    /**
156     * Check whether the pager is on the last currentPage.
157     */
158    public boolean isLastPage() {
159        return currentPage == (totalRows / limit) + 1;
160    }
161
162    /**
163     * Load the datasource within a given offset and limit
164     */
165    protected void doLoad(int offset, int limit) {
166        this.offset = offset;
167
168        // Check whether the pager has excess rows with given limit
169        if (isLastPage() & isExcess()) {
170            // Get the difference between total rows and excess rows
171            limit = totalRows - offset;
172        }
173
174        int finalLimit = limit;
175        dataSource.load(new LoadConfig<>(offset, limit, table.getView().getSortContext(),
176                table.getView().getOpenCategories()), new LoadCallback<T>() {
177            @Override
178            public void onSuccess(LoadResult<T> loadResult) {
179                totalRows = loadResult.getTotalLength();
180                table.setVisibleRange(offset, finalLimit);
181                table.loaded(loadResult.getOffset(), loadResult.getData());
182                updateUi();
183            }
184
185            @Override
186            public void onFailure(Throwable caught) {
187                GWT.log("Load failure", caught);
188                //TODO: What we need to do on failure? May be clear table?
189            }
190        });
191    }
192
193    /**
194     * Set and update the ui fields of the pager after the datasource load callback
195     */
196    protected void updateUi() {
197        pageNumberSelection.updatePageNumber(totalRows, limit, currentPage);
198
199        // Action label (current selection) in either the form "x-y of z" or "y of z" (when page has only 1 record)
200        int firstRow = offset + 1;
201        int lastRow = (isExcess() & isLastPage()) ? totalRows : (offset + limit);
202        actionsPanel.getActionLabel().setText((firstRow == lastRow ? lastRow : firstRow + "-" + lastRow) + " of " + totalRows);
203
204        actionsPanel.getIconNext().setEnabled(true);
205        actionsPanel.getIconPrev().setEnabled(true);
206
207        if (!isNext()) {
208            actionsPanel.getIconNext().setEnabled(false);
209        }
210
211        if (!isPrevious()) {
212            actionsPanel.getIconPrev().setEnabled(false);
213        }
214    }
215
216    public MaterialDataTable<T> getTable() {
217        return table;
218    }
219
220    public void setTable(MaterialDataTable<T> table) {
221        this.table = table;
222    }
223
224    public DataSource<T> getDataSource() {
225        return dataSource;
226    }
227
228    public void setDataSource(DataSource<T> dataSource) {
229        this.dataSource = dataSource;
230    }
231
232    public int getOffset() {
233        return offset;
234    }
235
236    public int getLimit() {
237        return limit;
238    }
239
240    public int getTotalRows() {
241        return totalRows;
242    }
243
244    public int[] getLimitOptions() {
245        return limitOptions;
246    }
247}