/**
* Copyright 2014
*
* 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.
*
* @project loon
* @author cping
* @email:javachenpeng@yahoo.com
* @version 0.4.2
*/
package loon.component.table;
import loon.LSystem;
import loon.LTexture;
import loon.LTextures;
import loon.canvas.LColor;
import loon.component.LComponent;
import loon.component.LContainer;
import loon.component.skin.SkinManager;
import loon.component.skin.TableSkin;
import loon.font.FontSet;
import loon.font.IFont;
import loon.geom.Dimension;
import loon.opengl.GLEx;
import loon.utils.TArray;
import loon.utils.ArrayMap;
import loon.utils.MathUtils;
/*
*
* Example:
*
* TArray<ListItem> list=new TArray<ListItem>();
*
* ListItem item=new ListItem();
* item.name="test1";
* item.list.add("ffffff");
* item.list.add("gggggggg");
* item.list.add("hhhhhhhhh");
* list.add(item);
*
* ListItem item2=new ListItem();
* item2.name="test2";
* item2.list.add("ffffff");
* item2.list.add("gggggggg");
* item2.list.add("hhhhhhhhh");
* list.add(item2);
* LTable table=new LTable(IFont.getDefaultFont(), 60,60, 300, 300);
* table.setData(list, 100);
* add(table);
*
*/
public class LTable extends LContainer implements FontSet<LTable> {
private ITableModel model = null;
private TableColumn[] columns = null;
private boolean[] selected = null;
private int selectionCount = 0;
private int columnMinWidth = 15;
private boolean multipleSelection = false;
private boolean readOnly = false;
private HeaderControl header = new HeaderControl();
private ArrayMap bindIcons = new ArrayMap();
protected class BindIcon {
protected String name = "...";
protected LTexture texture;
BindIcon(String n, LTexture t) {
this.name = n;
this.texture = t;
}
}
class HeaderControl {
int headerY;
int mouseX = 0;
int columnResizeIndex = 0;
int columnWidthBuffer = 0;
}
private int cellHeight = 20;
private static final int OFFSET = 5;
private int cellSpacing = 0;
private boolean gridVisible = true;
private boolean tableHeaderVisible = true;
private LColor headerBackgroundColor = LColor.gray;
private LColor gridColor = LColor.gray;
private LColor textColor = LColor.white;
private LColor selectionColor = LColor.red.darker();
private LColor headTextColor = LColor.orange;
private IFont font;
private LTexture headerTexture;
private LTexture backgroundTexture;
public LTable(int x, int y) {
this(SkinManager.get().getTableSkin().getFont(), x, y, LSystem.viewSize
.getWidth(), LSystem.viewSize.getHeight());
}
public LTable(int x, int y, int width, int height) {
this(SkinManager.get().getTableSkin().getFont(), SkinManager.get()
.getTableSkin().getHeaderTexture(), SkinManager.get()
.getTableSkin().getBackgroundTexture(), x, y, width, height);
}
public LTable(IFont font, int x, int y, int width, int height) {
this(font, SkinManager.get().getTableSkin().getHeaderTexture(),
SkinManager.get().getTableSkin().getBackgroundTexture(), x, y,
width, height);
}
public LTable(LTexture headerTexture, LTexture backgroundTexture, int x,
int y, int width, int height) {
this(SkinManager.get().getTableSkin().getFont(), headerTexture,
backgroundTexture, x, y, width, height);
}
public LTable(IFont font, LTexture headerTexture,
LTexture backgroundTexture, int x, int y, int width, int height) {
this(font, headerTexture, backgroundTexture, x, y, width, height,
SkinManager.get().getTableSkin().getFontColor());
}
public LTable(TableSkin skin, int x, int y, int width, int height) {
this(skin.getFont(), skin.getHeaderTexture(), skin
.getBackgroundTexture(), x, y, width, height, skin
.getFontColor());
}
public LTable(IFont font, LTexture headerTexture,
LTexture backgroundTexture, int x, int y, int width, int height,
LColor fontColor) {
super(x, y, width, height);
this.font = font;
this.cellHeight = (int) (font.getHeight() + font.getAscent());
this.headerTexture = headerTexture;
this.backgroundTexture = backgroundTexture;
this.setElastic(false);
this.setLocked(true);
this.setLayer(0);
}
public void setData(TArray<ListItem> list, int width) {
setModel(new SimpleTableModel(list), width);
}
public void bindIcon(String name, LTexture texture) {
bindIcons.put(name, new BindIcon(name, texture));
}
public void bindIcon(String name, String fileName) {
bindIcons
.put(name, new BindIcon(name, LTextures.loadTexture(fileName)));
}
private BindIcon containsBindIcon(String name) {
for (int i = 0; i < bindIcons.size(); i++) {
BindIcon icon = (BindIcon) bindIcons.get(i);
if (name.equalsIgnoreCase(icon.name)) {
return icon;
}
}
return null;
}
public void removeIcon(String name) {
bindIcons.remove(name);
}
public void removeIcon(int idx) {
bindIcons.remove(idx);
}
public Dimension getContentMinSizeHint() {
int rowHeight = font.getHeight();
ITableModel model = getModel();
if (model == null) {
return new Dimension(0, 0);
}
int numberOfRows = model.getRowCount();
if (tableHeaderVisible) {
numberOfRows++;
}
return new Dimension(100, numberOfRows * rowHeight + numberOfRows
* cellSpacing);
}
public void mouseDragged(float x, float y) {
if (isTableHeadVisible()) {
if (header.columnResizeIndex > -1) {
int newWidth = (int) (header.columnWidthBuffer + (x - header.mouseX));
int sum = getColumnWidth(header.columnResizeIndex)
+ getColumnWidth(header.columnResizeIndex + 1);
if (newWidth < getColumnMinWidth()
|| sum - newWidth < getColumnMinWidth()) {
return;
}
columns[header.columnResizeIndex].setWidth(newWidth);
columns[header.columnResizeIndex + 1].setWidth(sum - newWidth);
}
}
}
public void mouseExited(float x, float y) {
if (header.columnResizeIndex > -1) {
header.columnResizeIndex = -1;
}
}
public void mouseMoved(float x, float y) {
if (!isTableHeadVisible()) {
return;
}
if (header.headerY < (y + getCellHeight() + getCellHeight())) {
int column = isOnColumn((int) x);
if (column >= 0) {
header.columnResizeIndex = column;
return;
} else if (header.columnResizeIndex > -1) {
header.columnResizeIndex = -1;
}
} else if (header.columnResizeIndex > -1) {
header.columnResizeIndex = -1;
}
}
public void mousePressed(float x, float y) {
if (isTableHeadVisible()) {
if (header.columnResizeIndex > -1) {
header.mouseX = (int) x;
header.columnWidthBuffer = getColumnWidth(header.columnResizeIndex);
return;
} else {
header.columnResizeIndex = 0;
}
}
if (readOnly) {
return;
}
assertSelectionArraySize();
int mouseY = (int) y;
if (!isTableHeadVisible()) {
mouseY -= getCellHeight();
}
mouseY += getCellSpacing();
int row = (mouseY / (getCellHeight() + getCellSpacing()));
if (isTableHeadVisible()) {
row--;
}
if (row < 0 || row >= selected.length) {
return;
}
if (!selected[row]) {
selectionCount++;
} else {
selectionCount--;
}
if (multipleSelection) {
selected[row] = !selected[row];
} else {
clearSelection();
selected[row] = !selected[row];
}
}
protected void processTouchDragged() {
mouseDragged(getUITouchX(), getUITouchY());
if (!locked) {
if (getContainer() != null) {
getContainer().sendToFront(this);
}
if (this.input != null) {
this.move(this.input.getTouchDX(), this.input.getTouchDY());
}
}
super.dragClick();
}
protected void processTouchPressed() {
super.processTouchPressed();
mousePressed(getUITouchX(), getUITouchY());
}
@Override
protected void processTouchReleased() {
super.processTouchReleased();
mouseExited(getUITouchX(), getUITouchY());
}
@Override
public float getHeight() {
if (model == null) {
return super.getHeight();
}
int height = 0;
for (int i = 0; i < model.getRowCount(); i++) {
height += (cellHeight + cellSpacing);
}
if (isTableHeadVisible()) {
height += (cellHeight + cellSpacing);
}
return height;
}
@Override
public float getWidth() {
if (model == null) {
return super.getWidth();
}
int width = 0;
for (int i = 0; i < model.getColumnCount(); i++) {
width += getColumnWidth(i);
}
return width;
}
@Override
public void createUI(GLEx g, int displayX, int displayY,
LComponent component, LTexture[] buttonImage) {
if (!isVisible()) {
return;
}
ITableModel model = getModel();
HeaderControl header = getHeader();
if (model == null) {
return;
}
try {
g.saveBrush();
int x = displayX;
int y = displayY;
y += cellHeight;
int size = (int) (getHeight() / (cellHeight + cellSpacing));
int wid = 0;
for (int i = 0; i < model.getColumnCount(); i++) {
wid += getColumnWidth(i);
}
int hei = 0;
for (int i = 0; i < model.getRowCount(); i++) {
hei += (cellHeight + cellSpacing);
}
if (wid != getWidth()
|| hei + (cellHeight + cellSpacing) != getHeight()) {
setSize(wid, hei + (cellHeight + cellSpacing));
}
if (gridVisible) {
g.setLineWidth(2f);
}
if (backgroundTexture != null) {
g.draw(backgroundTexture, x, y, wid, hei, LColor.white);
}
for (int row = 0; row < size && row < model.getRowCount(); row++) {
x = displayX;
if (isSelected(row)) {
g.setColor(selectionColor);
g.fillRect(x, y, wid, cellHeight);
g.setColor(LColor.white);
}
for (int columnIndex = 0; columnIndex < model.getColumnCount(); columnIndex++) {
g.setColor(textColor);
Object value = model.getValue(row, columnIndex);
if (value != null) {
ICellRenderer cellRenderer = getColumn(columnIndex)
.getCellRenderer();
Dimension contentDimension = cellRenderer
.getCellContentSize(value);
if (contentDimension == null) {
contentDimension = new Dimension(
getColumnWidth(columnIndex), cellHeight);
}
int alignedX = x
+ getColumn(columnIndex).getEntryAlignment()
.alignX(getColumnWidth(columnIndex),
contentDimension.getWidth());
int alignedY = y
+ getColumn(columnIndex).getEntryAlignment()
.alignY(cellHeight,
contentDimension.getHeight());
if (bindIcons.size() == 0) {
cellRenderer.paint(g, value, alignedX, alignedY,
getColumnWidth(columnIndex), cellHeight);
} else {
if (value instanceof String) {
String v = (String) value;
BindIcon icon = containsBindIcon(v);
if (icon != null) {
cellRenderer.paint(g, icon, alignedX,
alignedY,
getColumnWidth(columnIndex),
cellHeight);
} else {
cellRenderer.paint(g, value, alignedX,
alignedY,
getColumnWidth(columnIndex),
cellHeight);
}
} else {
cellRenderer.paint(g, value, alignedX,
alignedY, getColumnWidth(columnIndex),
cellHeight);
}
}
}
if (gridVisible) {
g.setColor(gridColor);
g.drawRect(x, y, getColumnWidth(columnIndex),
cellHeight);
g.setColor(LColor.white);
}
x += getColumnWidth(columnIndex) + cellSpacing;
}
y += (cellHeight + cellSpacing);
}
if (tableHeaderVisible) {
header.headerY = displayY;
if (headerTexture != null) {
g.draw(headerTexture, displayX, displayY, wid, cellHeight,
headerBackgroundColor);
if (gridVisible) {
g.setColor(gridColor);
g.drawRect(displayX, displayY, wid, cellHeight);
g.setColor(LColor.white);
}
} else {
g.setColor(headerBackgroundColor);
g.fillRect(displayX, displayY, wid, cellHeight);
g.setColor(LColor.white);
}
x = displayX;
for (int columnIndex = 0; columnIndex < model.getColumnCount(); columnIndex++) {
String s = model.getColumnName(columnIndex);
int columnWidth = getColumnWidth(columnIndex);
s = font.confineLength(s, columnWidth - OFFSET);
int entryOffset = OFFSET
+ getColumn(columnIndex).getHeaderAlignment()
.alignX(columnWidth - OFFSET,
font.stringWidth(s));
font.drawString(g, s, x + entryOffset, header.headerY
+ font.getAscent() / 2 - 4, headTextColor);
x += columnWidth + cellSpacing;
}
}
} finally {
g.restoreBrush();
}
}
public void setGridColor(LColor gridColor) {
this.gridColor = gridColor;
}
public void setTextColor(LColor textColor) {
this.textColor = textColor;
}
@Override
public LTable setFont(IFont font) {
this.font = font;
this.cellHeight = font.getHeight();
return this;
}
@Override
public IFont getFont() {
return font;
}
public LColor getHeadTextColor() {
return headTextColor;
}
public void setHeadTextColor(LColor headTextColor) {
this.headTextColor = headTextColor;
}
public LColor getSelectionColor() {
return selectionColor;
}
public void setSelectionColor(LColor selectionColor) {
this.selectionColor = selectionColor;
}
public LColor getGridColor() {
return gridColor;
}
public LColor getTextColor() {
return textColor;
}
public int getCellHeight() {
return cellHeight;
}
public void setCellHeight(int cellHeight) {
this.cellHeight = cellHeight;
}
public void setGridVisible(boolean gridVisible) {
this.gridVisible = gridVisible;
}
public boolean isTableHeadVisible() {
return tableHeaderVisible;
}
public boolean isReadOnly() {
return readOnly;
}
public void setHeaderVisible(boolean drawTableHead) {
this.tableHeaderVisible = drawTableHead;
}
public int getCellSpacing() {
return cellSpacing;
}
public void setCellSpacing(int cellSpacing) {
this.cellSpacing = cellSpacing;
}
public LColor getHeaderBackgroundColor() {
return headerBackgroundColor;
}
public void setHeaderBackgroundColor(LColor headerBackgroundColor) {
this.headerBackgroundColor = headerBackgroundColor;
}
protected HeaderControl getHeader() {
return header;
}
public void setSelected(int index, boolean b) {
assertModel();
assertSelectionArraySize();
if (index < 0 || index >= selected.length) {
return;
}
if (multipleSelection) {
if (selected[index] != b) {
selected[index] = b;
if (b) {
selectionCount++;
} else {
selectionCount--;
}
}
} else {
clearSelection();
selected[index] = b;
selectionCount = 1;
}
}
public void setModel(ITableModel m, int width) {
model = m;
columns = new TableColumn[m.getColumnCount()];
selected = new boolean[m.getRowCount()];
for (int i = 0; i < columns.length; i++) {
columns[i] = new TableColumn(m.getColumnName(i), width);
}
}
public ITableModel getModel() {
return model;
}
public boolean isSelected(int row) {
assertModel();
return row >= 0 && row < selected.length ? selected[row] : false;
}
public int getSelectionCount() {
return selectionCount;
}
public void distributeColumnWidthsEqually() {
if (model == null) {
throw new IllegalStateException("The table has no model!");
}
for (int i = 0; i < columns.length; i++) {
columns[i].setWidth((int) (getWidth() / columns.length));
}
}
public void setColumnWidth(int columnIndex, int widthInPixel) {
getColumn(columnIndex).setWidth(widthInPixel);
}
public void setColumnWidth(int columnIndex, float relativeWidth) {
getColumn(columnIndex).setRelativeWidth(relativeWidth);
}
public boolean isMultipleSelection() {
return multipleSelection;
}
public void setMultipleSelection(boolean multipleSelection) {
this.multipleSelection = multipleSelection;
}
public void clearSelection() {
for (int i = 0; i < selected.length; i++) {
selected[i] = false;
}
}
public void layout() {
if (model != null && columns.length > 0 && getColumnWidth(0) == -1) {
distributeColumnWidthsEqually();
}
}
public int getSelection() {
assertModel();
for (int i = 0; i < selected.length; i++) {
if (selected[i] == true)
return i;
}
return -1;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public TableColumn getColumn(int columnIndex) {
assertModel();
return columns[columnIndex];
}
public int isOnColumn(int x) {
float sum = 0;
for (int col = 0; col < columns.length - 1; col++) {
sum += getColumnWidth(col) + cellSpacing;
if (MathUtils.abs(sum - x) < 5)
return col;
}
return -1;
}
private void assertSelectionArraySize() {
if (selected.length == model.getRowCount()) {
return;
}
boolean[] newSelected = new boolean[model.getRowCount()];
for (int i = 0; i < selected.length && i < newSelected.length; i++) {
newSelected[i] = selected[i];
}
this.selected = newSelected;
}
private void assertModel() {
if (model == null) {
throw new IllegalStateException("No table model set!");
}
}
public int getColumnMinWidth() {
return columnMinWidth;
}
public void setColumnMinWidth(int columnMinWidth) {
this.columnMinWidth = columnMinWidth;
}
public int getColumnWidth(int columnIndex) {
TableColumn column = columns[columnIndex];
if (column != null) {
if (column.isRelative()) {
return (int) (getWidth() * column.getRelativeWidth());
}
return column.getWidth();
}
return 0;
}
public LTexture getHeaderTexture() {
return headerTexture;
}
public void setHeaderTexture(LTexture headerTexture) {
this.headerTexture = headerTexture;
}
public LTexture getBackgroundTexture() {
return backgroundTexture;
}
public void setBackgroundTexture(LTexture backgroundTexture) {
this.backgroundTexture = backgroundTexture;
}
@Override
public String getUIName() {
return "Table";
}
}