/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 ro.nextreports.server.web.common.table; import java.util.ArrayList; import java.util.List; import org.apache.wicket.AttributeModifier; import org.apache.wicket.Component; import org.apache.wicket.ajax.AjaxRequestTarget; import org.apache.wicket.ajax.markup.html.form.AjaxCheckBox; import org.apache.wicket.extensions.markup.html.repeater.data.grid.ICellPopulator; import org.apache.wicket.extensions.markup.html.repeater.data.table.IColumn; import org.apache.wicket.extensions.markup.html.repeater.data.table.ISortableDataProvider; import org.apache.wicket.extensions.markup.html.repeater.data.table.PropertyColumn; import org.apache.wicket.markup.html.panel.EmptyPanel; import org.apache.wicket.markup.html.panel.GenericPanel; import org.apache.wicket.markup.html.panel.Panel; import org.apache.wicket.markup.repeater.Item; import org.apache.wicket.model.IModel; import org.apache.wicket.model.LoadableDetachableModel; import org.apache.wicket.model.Model; import org.apache.wicket.util.visit.IVisit; import org.apache.wicket.util.visit.IVisitor; import ro.nextreports.server.web.common.event.BulkMenuUpdateEvent; /** * User: mihai.panaitescu * Date: 26-May-2010 * Time: 17:13:33 */ public class AjaxCheckTablePanel<T> extends Panel { private static final long serialVersionUID = 1L; private static final String TABLE_ID = "table"; private BaseTable<T> dataTable; private AjaxCheckBox headerAjaxCheckBox; private List<T> selectedObjects; public AjaxCheckTablePanel(String id, List<IColumn<T, String>> columns, ISortableDataProvider<T, String> dataProvider, int rowsPerPage) { super(id); this.setOutputMarkupId(true); selectedObjects = new ArrayList<T>(); columns.add(0, new CheckableTableColumn(new Model<String>(), "id")); dataTable = new BaseTable<T>(TABLE_ID, columns, dataProvider, rowsPerPage) { private static final long serialVersionUID = 1L; @Override protected Item<T> newRowItem(String id, int index, IModel<T> model) { return newRowTableItem(model, new HighlitableDataItem(id, index, model)); } }; // ItemReuseStrategy is usefull to refresh only modified row, but it uses hash code and equals of the model // (EntityModel in this case which contains only the id) // So if we do a rename with this strategy, the change will not be seen (as when adding a new report for example) //dataTable.setItemReuseStrategy(ReuseIfModelsEqualStrategy.getInstance()); add(dataTable); } public List<T> getSelected() { return selectedObjects; } public BaseTable<T> getDataTable() { return dataTable; } public void unselectAll() { selectedObjects.clear(); } protected Item<T> newRowTableItem(IModel<T> entityIModel, Item<T> item) { return item; } private void onCheckChanged(HighlitableDataItem item, AjaxRequestTarget target) { // refresh row item.toggleHighlite(); target.add(item); // refresh first column header target.add(headerAjaxCheckBox); // refresh bulk menu updateSelectedObjects(); new BulkMenuUpdateEvent(this, target).fire(); } private void onHeaderCheckChanged(final boolean selectAll, final AjaxRequestTarget target) { // refresh rows affected by changing dataTable.visitChildren(HighlitableDataItem.class, new IVisitor<HighlitableDataItem, Void>() { @Override public void component(HighlitableDataItem object, IVisit<Void> visit) { if (object.isHighlite() != selectAll) { object.toggleHighlite(); target.add(object); } } }); // refresh bulk menu updateSelectedObjects(); new BulkMenuUpdateEvent(this, target).fire(); } private void updateSelectedObjects() { selectedObjects.clear(); dataTable.visitChildren(HighlitableDataItem.class, new IVisitor<HighlitableDataItem, Void>() { @Override public void component(HighlitableDataItem object, IVisit<Void> visit) { if (object.isHighlite()) { selectedObjects.add(object.getModelObject()); } visit.dontGoDeeper(); } }); } protected boolean isCheckable(IModel<T> rowModel) { return true; } private class CheckableTableColumn extends PropertyColumn<T, String> { private static final long serialVersionUID = 1L; public CheckableTableColumn(IModel<String> displayModel, String propertyExpressions) { super(displayModel, propertyExpressions); } @SuppressWarnings("unchecked") @Override public void populateItem(final Item<ICellPopulator<T>> item, String componentId, IModel<T> rowModel) { final HighlitableDataItem highlitableDataItem = item.findParent(HighlitableDataItem.class); IModel<Boolean> checkBoxModel = new LoadableDetachableModel<Boolean>() { private static final long serialVersionUID = 1L; @Override protected Boolean load() { return highlitableDataItem.isHighlite(); } }; if (isCheckable(rowModel)) { item.add(new CheckBoxColumnPanel(componentId, checkBoxModel)); item.add(AttributeModifier.replace("class", "checkboxColumn")); } else { item.add(new EmptyPanel(componentId)); } } @Override public Component getHeader(String componentId) { IModel<Boolean> checkBoxHeaderModel = new LoadableDetachableModel<Boolean>() { private static final long serialVersionUID = 1L; private boolean selected; @Override protected Boolean load() { selected = false; dataTable.visitChildren(HighlitableDataItem.class, new IVisitor<HighlitableDataItem, Void>() { @Override public void component(HighlitableDataItem object, IVisit<Void> visit) { if (!object.isHighlite()) { selected = false; return; } selected = true; visit.dontGoDeeper(); } }); return selected; } }; return new CheckBoxHeaderPanel(componentId, checkBoxHeaderModel); } } abstract class CheckBoxPanel extends GenericPanel<Boolean> { private static final long serialVersionUID = 1L; public CheckBoxPanel(String id) { super(id); this.createComponents(); } public CheckBoxPanel(String id, IModel<Boolean> model) { super(id, model); this.createComponents(); } protected abstract void createComponents(); } class CheckBoxHeaderPanel extends CheckBoxPanel { private static final long serialVersionUID = 1L; public CheckBoxHeaderPanel(String id, IModel<Boolean> model) { super(id, model); } protected void createComponents() { headerAjaxCheckBox = new AjaxCheckBox("select", getModel()) { private static final long serialVersionUID = 1L; @Override protected void onUpdate(AjaxRequestTarget target) { onHeaderCheckChanged(getModelObject(), target); } }; add(headerAjaxCheckBox); } } /* class CheckedHeaderModel extends AbstractCheckBoxModel { private static final long serialVersionUID = 1L; @Override public boolean isSelected() { dataTable.visitChildren(HighlitableDataItem.class, new IVisitor<HighlitableDataItem>() { public Object common(HighlitableDataItem common) { if (!common.isHighlite()) { return false; } return IVisitor.CONTINUE_TRAVERSAL_BUT_DONT_GO_DEEPER; } }); return true; } @Override public void select() { // TODO } @Override public void unselect() { // TODO } } */ class CheckBoxColumnPanel extends CheckBoxPanel { private static final long serialVersionUID = 1L; public CheckBoxColumnPanel(String id, IModel<Boolean> model) { super(id, model); } protected void createComponents() { AjaxCheckBox ajaxCheckBox = new AjaxCheckBox("select", getModel()) { private static final long serialVersionUID = 1L; @SuppressWarnings("unchecked") @Override protected void onUpdate(AjaxRequestTarget target) { HighlitableDataItem clickedItem = findParent(HighlitableDataItem.class); onCheckChanged(clickedItem, target); } }; add(ajaxCheckBox); } } /* class CheckedColumnModel extends AbstractCheckBoxModel { private static final long serialVersionUID = 1L; private HighlitableDataItem item; public CheckedColumnModel(HighlitableDataItem item) { this.item = item; } @Override public boolean isSelected() { return item.isHighlite(); } @Override public void select() { // TODO } @Override public void unselect() { // TODO } } */ class HighlitableDataItem extends Item<T> { private static final long serialVersionUID = 1L; private boolean highlite; public HighlitableDataItem(String id, int index, IModel<T> model) { super(id, index, model); setOutputMarkupId(true); if (isCheckable(model)) { add(new AttributeModifier("class", Model.of("tr-checked")) { private static final long serialVersionUID = 1L; @Override public boolean isEnabled(Component component) { return isHighlite(); } }); } } public void toggleHighlite() { highlite = !highlite; } public void setHighlite(boolean highlite) { this.highlite = highlite; } public boolean isHighlite() { return highlite; } } }