/* * Copyright (c) 2011 PonySDK * Owners: * Luciano Broussal <luciano.broussal AT gmail.com> * Mathieu Barbier <mathieu.barbier AT gmail.com> * Nicolas Ciaravola <nicolas.ciaravola.pro AT gmail.com> * * WebSite: * http://code.google.com/p/pony-sdk/ * * 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 com.ponysdk.core.ui.basic; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.TreeMap; import com.ponysdk.core.model.ServerToClientModel; import com.ponysdk.core.ui.model.ServerBinaryModel; /** * PHTMLTable contains the common table algorithms for {@link PGrid} and * {@link PFlexTable}. */ public abstract class PHTMLTable<T extends PCellFormatter> extends PPanel { private final TreeMap<Row, TreeMap<Integer, PWidget>> columnByRow = new TreeMap<>(); private final Map<PWidget, Cell> cellByWidget = new HashMap<>(); private final PColumnFormatter columnFormatter = new PColumnFormatter(); private final PRowFormatter rowFormatter = new PRowFormatter(); private T cellFormatter; private int cellPadding; private int cellSpacing; private int borderWidth; protected PHTMLTable() { } @Override protected void init0() { super.init0(); cellByWidget.keySet().forEach(widget -> widget.attach(window)); } public int getRowCount() { if (columnByRow.isEmpty()) return 0; else return columnByRow.lastKey().value + 1; } public int getCellCount(final int row) { final TreeMap<Integer, PWidget> cellByColumn = columnByRow.get(new Row(row)); if (cellByColumn == null || cellByColumn.isEmpty()) return 0; return cellByColumn.lastKey() + 1; } public void clearCell(final int row, final int col) { final PWidget widget = getWidgetFromMap(row, col); if (widget != null) { remove(widget); } } public T getCellFormatter() { return cellFormatter; } protected void setCellFormatter(final T cellFormatter) { this.cellFormatter = cellFormatter; } public PColumnFormatter getColumnFormatter() { return columnFormatter; } public int getCellPadding() { return cellPadding; } public void setCellPadding(final int padding) { if (Objects.equals(this.cellPadding, padding)) return; cellPadding = padding; saveUpdate(writer -> writer.write(ServerToClientModel.CELL_PADDING, padding)); } public int getCellSpacing() { return cellSpacing; } public void setCellSpacing(final int spacing) { if (Objects.equals(this.cellSpacing, spacing)) return; cellSpacing = spacing; saveUpdate(writer -> writer.write(ServerToClientModel.CELL_SPACING, spacing)); } public int getBorderWidth() { return borderWidth; } public void setBorderWidth(final int width) { if (Objects.equals(this.borderWidth, width)) return; this.borderWidth = width; saveUpdate(writer -> writer.write(ServerToClientModel.BORDER_WIDTH, width)); } public PWidget getWidget(final int row, final int column) { return getWidgetFromMap(row, column); } @Override public void clear() { final List<PWidget> values = new ArrayList<>(); columnByRow.values().forEach(widgetByColumn -> values.addAll(widgetByColumn.values())); values.forEach(widget -> remove(widget, false)); saveUpdate(writer -> writer.write(ServerToClientModel.CLEAR)); } public void removeRow(final int row) { final TreeMap<Integer, PWidget> widgetByColumn = columnByRow.remove(new Row(row)); if (widgetByColumn == null) return; getRowFormatter().removeRowStyle(row); final List<PWidget> values = new ArrayList<>(widgetByColumn.values()); values.forEach(widget -> remove(widget, false)); for (final Entry<Row, TreeMap<Integer, PWidget>> entry : columnByRow.entrySet()) { final Row irow = entry.getKey(); if (irow.value > row) { for (final PWidget widget : entry.getValue().values()) { final Cell cell = cellByWidget.get(widget); cell.row = cell.row - 1; } irow.value = irow.value - 1; } } saveUpdate(writer -> writer.write(ServerToClientModel.CLEAR_ROW, row)); } public void insertRow(final int row) { for (final Entry<Row, TreeMap<Integer, PWidget>> entry : columnByRow.entrySet()) { final Row irow = entry.getKey(); if (irow.value >= row) { for (final PWidget widget : entry.getValue().values()) { final Cell cell = cellByWidget.get(widget); cell.row = cell.row + 1; } irow.value = irow.value + 1; } } rowFormatter.insertRowStyle(row); saveUpdate(writer -> writer.write(ServerToClientModel.INSERT_ROW, row)); } @Override public boolean remove(final PWidget widget) { return remove(widget, true); } private boolean remove(final PWidget widget, final boolean physicalDetach) { // Validate. if (widget.getParent() != this) { return false; } // Orphan. try { orphan(widget); } finally { // Logical detach. if (removeWidgetFromMap(widget) != null) { // Physical detach. if (physicalDetach) { widget.saveRemove(widget.getID(), ID); } } } return true; } public void setWidget(final int row, final int column, final IsPWidget widget) { if (widget != null) { setWidget(row, column, widget.asWidget()); } } public void setWidget(final int row, final int column, final PWidget widget) { if (widget != null) { widget.removeFromParent(); // Removes any existing widget. clearCell(row, column); // Logical attach. addWidgetToMap(row, column, widget); adopt(widget); // Physical attach. widget.attach(window); widget.saveAdd(widget.getID(), ID, new ServerBinaryModel(ServerToClientModel.ROW, row), new ServerBinaryModel(ServerToClientModel.COLUMN, column)); } } private PWidget getWidgetFromMap(final int row, final int column) { final Map<Integer, PWidget> cellByColumn = columnByRow.get(new Row(row)); if (cellByColumn != null) { return cellByColumn.get(column); } return null; } private PWidget removeWidgetFromMap(final PWidget widget) { final Cell cell = cellByWidget.remove(widget); if (cell == null) return null; // already removed final Row row = new Row(cell.row); final Map<Integer, PWidget> cellByColumn = columnByRow.get(row); if (cellByColumn != null) { final PWidget w = cellByColumn.remove(cell.column); if (cellByColumn.isEmpty()) { columnByRow.remove(row); } return w; } return null; } private void addWidgetToMap(final int row, final int column, final PWidget widget) { final Row irow = new Row(row); final Cell cell = new Cell(row, column); cellByWidget.put(widget, cell); TreeMap<Integer, PWidget> cellByColumn = columnByRow.get(irow); if (cellByColumn == null) { cellByColumn = new TreeMap<>(); columnByRow.put(irow, cellByColumn); } cellByColumn.put(column, widget); } @Override public Iterator<PWidget> iterator() { return Collections.emptyIterator(); } public PRowFormatter getRowFormatter() { return rowFormatter; } class Row implements Comparable<Row> { private int value; protected Row(final int value) { this.value = value; } public void setValue(final int value) { this.value = value; } @Override public int compareTo(final Row row) { return value >= row.value ? value != row.value ? 1 : 0 : -1; } } protected class Cell { private int row; private int column; protected Cell(final int rowIndex, final int cellIndex) { this.column = cellIndex; this.row = rowIndex; } public void setRow(final int row) { this.row = row; } public void setColumn(final int column) { this.column = column; } } public class PRowFormatter { private Map<Integer, Set<String>> styleNames = new HashMap<>(); public void addStyleName(final int row, final String styleName) { Set<String> styles = styleNames.get(row); if (styles == null) { styles = new HashSet<>(); styleNames.put(row, styles); } if (styles.add(styleName)) { saveUpdate((writer) -> { writer.write(ServerToClientModel.ROW_FORMATTER_ADD_STYLE_NAME, styleName); writer.write(ServerToClientModel.ROW, row); }); } } public void removeStyleName(final int row, final String styleName) { final Set<String> styles = styleNames.get(row); if (styles == null) return; if (styles.remove(styleName)) { saveUpdate((writer) -> { writer.write(ServerToClientModel.ROW_FORMATTER_REMOVE_STYLE_NAME, styleName); writer.write(ServerToClientModel.ROW, row); }); } } public void setStyleName(final int row, final String styleName) { Set<String> styles = styleNames.get(row); if (styles == null) { styles = new HashSet<>(); styleNames.put(row, styles); } else { styles.clear(); } styles.add(styleName); saveUpdate((writer) -> { writer.write(ServerToClientModel.ROW_FORMATTER_SET_STYLE_NAME, styleName); writer.write(ServerToClientModel.ROW, row); }); } protected void insertRowStyle(final int row) { final Map<Integer, Set<String>> temp = new HashMap<>(); for (final Entry<Integer, Set<String>> entry : styleNames.entrySet()) { if (entry.getKey() >= row) { temp.put(entry.getKey() + 1, entry.getValue()); } else temp.put(entry.getKey(), entry.getValue()); } temp.put(row, new HashSet<>()); styleNames = temp; } protected void removeRowStyle(final int row) { styleNames.remove(row); final Map<Integer, Set<String>> temp = new HashMap<>(); for (final Entry<Integer, Set<String>> entry : styleNames.entrySet()) { if (entry.getKey() > row) { temp.put(entry.getKey() - 1, entry.getValue()); } else temp.put(entry.getKey(), entry.getValue()); } styleNames = temp; } } public class PColumnFormatter { public void setWidth(final int column, final String width) { saveUpdate((writer) -> { writer.write(ServerToClientModel.COLUMN_FORMATTER_COLUMN_WIDTH, width); writer.write(ServerToClientModel.COLUMN, column); }); } public void addStyleName(final int column, final String styleName) { saveUpdate((writer) -> { writer.write(ServerToClientModel.COLUMN_FORMATTER_ADD_STYLE_NAME, styleName); writer.write(ServerToClientModel.COLUMN, column); }); } public void removeStyleName(final int column, final String styleName) { saveUpdate((writer) -> { writer.write(ServerToClientModel.COLUMN_FORMATTER_REMOVE_STYLE_NAME, styleName); writer.write(ServerToClientModel.COLUMN, column); }); } public void setStyleName(final int column, final String styleName) { saveUpdate((writer) -> { writer.write(ServerToClientModel.COLUMN_FORMATTER_SET_STYLE_NAME, styleName); writer.write(ServerToClientModel.COLUMN, column); }); } } }