package org.activityinfo.ui.client.component.report.view; /* * #%L * ActivityInfo Server * %% * Copyright (C) 2009 - 2013 UNICEF * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #L% */ import com.extjs.gxt.ui.client.Style; import com.extjs.gxt.ui.client.data.BaseTreeModel; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.GridEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.store.ListStore; import com.extjs.gxt.ui.client.util.Format; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.ContentPanel; import com.extjs.gxt.ui.client.widget.grid.*; import com.extjs.gxt.ui.client.widget.layout.FitLayout; import org.activityinfo.i18n.shared.I18N; import org.activityinfo.legacy.client.Dispatcher; import org.activityinfo.legacy.client.type.IndicatorNumberFormat; import org.activityinfo.legacy.shared.reports.content.PivotTableData; import org.activityinfo.legacy.shared.reports.model.PivotReportElement; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class PivotGridPanel extends ContentPanel implements ReportView<PivotReportElement> { private static final int ROW_INDENT = 15; private Grid<PivotTableRow> grid; private ListStore<PivotTableRow> store; private Map<PivotTableData.Axis, String> propertyMap; private Map<Integer, PivotTableData.Axis> columnMap; private ColumnModel columnModel; private DrillDownEditor drillDownEditor; public PivotGridPanel(Dispatcher dispatcher) { this.drillDownEditor = new DrillDownEditor(dispatcher); setLayout(new FitLayout()); } @SuppressWarnings("GwtInconsistentSerializableClass") public class PivotTableRow extends BaseTreeModel { private PivotTableData.Axis rowAxis; private int depth; public PivotTableRow(PivotTableData.Axis axis, int depth) { this.rowAxis = axis; this.depth = depth; set("header", axis.getLabel()); for (Map.Entry<PivotTableData.Axis, PivotTableData.Cell> entry : axis.getCells().entrySet()) { set(propertyMap.get(entry.getKey()), entry.getValue().getValue()); } } public PivotTableData.Axis getRowAxis() { return rowAxis; } public int getDepth() { return depth; } } @Override public void show(final PivotReportElement element) { if (grid != null) { removeAll(); } PivotTableData data = element.getContent().getData(); propertyMap = new HashMap<>(); columnMap = new HashMap<>(); columnModel = createColumnModel(data); store = new ListStore<>(); addRows(data.getRootRow(), 0); grid = new Grid<>(store, columnModel); grid.setAutoExpandColumn("header"); grid.setAutoExpandMin(150); grid.setView(new PivotGridView()); grid.setSelectionModel(new CellSelectionModel<PivotGridPanel.PivotTableRow>()); grid.addListener(Events.CellDoubleClick, new Listener<GridEvent<PivotTableRow>>() { @Override public void handleEvent(GridEvent<PivotTableRow> ge) { if (ge.getColIndex() != 0) { PivotTableData.Axis row = ge.getModel().getRowAxis(); PivotTableData.Axis column = columnMap.get(ge.getColIndex()); if (row.getCell(column) != null) { drillDownEditor.drillDown(element, row, column); } } } }); add(grid); layout(); } private void addRows(PivotTableData.Axis parent, int depth) { for (PivotTableData.Axis axis : parent.getChildren()) { store.add(new PivotTableRow(axis, depth)); addRows(axis, depth + 1); } } private static class RowHeaderRenderer implements GridCellRenderer<PivotTableRow> { @Override public Object render(PivotTableRow model, String property, ColumnData config, int rowIndex, int colIndex, ListStore<PivotTableRow> store, Grid<PivotTableRow> grid) { String indent = (model.getDepth() * ROW_INDENT) + "px"; return "<span style=\"margin-left:" + indent + "\">" + Format.htmlEncode((String) model.get("header")) + "</span>"; } } protected ColumnModel createColumnModel(PivotTableData data) { List<ColumnConfig> config = new ArrayList<>(); ColumnConfig rowHeader = new ColumnConfig("header", "", 150); rowHeader.setRenderer(new RowHeaderRenderer()); rowHeader.setSortable(false); rowHeader.setMenuDisabled(true); config.add(rowHeader); int colIndex = 1; List<PivotTableData.Axis> leaves = data.getRootColumn().getLeaves(); for (PivotTableData.Axis axis : leaves) { String id = "col" + colIndex; String label = axis.getLabel(); if (label == null) { label = I18N.CONSTANTS.value(); } ColumnConfig column = new ColumnConfig(id, label, 75); column.setNumberFormat(IndicatorNumberFormat.INSTANCE); column.setAlignment(Style.HorizontalAlignment.RIGHT); column.setSortable(false); column.setMenuDisabled(true); column.setRenderer(new GridCellRenderer() { @Override public Object render(ModelData model, String property, ColumnData config, int rowIndex, int colIndex, ListStore store, Grid grid) { Double value = model.get(property); if (value == null) { config.cellAttr = ""; return null; } else { config.cellAttr = "data-pivot='value'"; return IndicatorNumberFormat.INSTANCE.format(value); } } }); propertyMap.put(axis, id); columnMap.put(colIndex, axis); config.add(column); colIndex++; } ColumnModel columnModel = new ColumnModel(config); int depth = data.getRootColumn().getDepth(); int row = 0; for (int d = 1; d <= depth; ++d) { List<PivotTableData.Axis> children = data.getRootColumn().getDescendantsAtDepth(d); if (d < depth) { int col = 1; for (PivotTableData.Axis child : children) { int colSpan = child.getLeaves().size(); columnModel.addHeaderGroup(row, col, new HeaderGroupConfig(child.getLabel(), 1, colSpan)); col += colSpan; } row++; } } return columnModel; } @Override public Component asComponent() { return this; } }