package com.eas.grid.columns;
import java.util.Comparator;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.eas.bound.ModelCheck;
import com.eas.bound.ModelCombo;
import com.eas.bound.ModelDecoratorBox;
import com.eas.core.HasPublished;
import com.eas.core.Utils;
import com.eas.core.XElement;
import com.eas.core.Utils.JsObject;
import com.eas.grid.GridColumn;
import com.eas.grid.GridSection;
import com.eas.grid.ModelGrid;
import com.eas.grid.RenderedCellContext;
import com.eas.grid.cells.CellHasReadonly;
import com.eas.grid.cells.CellRenderer;
import com.eas.grid.cells.CheckBoxCell;
import com.eas.grid.cells.RenderedEditorCell;
import com.eas.grid.cells.TreeExpandableCell;
import com.eas.grid.rows.PathComparator;
import com.eas.ui.PublishedCell;
import com.eas.ui.EventsPublisher;
import com.eas.widgets.WidgetsUtils;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.cell.client.Cell.Context;
import com.google.gwt.cell.client.FieldUpdater;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArrayMixed;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.ImageElement;
import com.google.gwt.dom.client.InputElement;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
public class ModelColumn extends GridColumn<JavaScriptObject, Object> implements FieldUpdater<JavaScriptObject, Object>, ChangesHost, HasPublished {
protected String field;
protected String sortField;
protected ModelDecoratorBox<? extends Object> editor;
protected ModelGrid grid;
protected double minWidth = 15;
protected double maxWidth = Integer.MAX_VALUE;
protected double designedWidth = 75;
protected double widthDelta;
protected boolean readonly;
protected boolean visible = true;
protected boolean selectOnly;
protected Comparator<JavaScriptObject> comparator;
protected JavaScriptObject published;
protected JavaScriptObject onRender;
protected JavaScriptObject onSelect;
protected ModelColumn(Cell<Object> aCell) {
super(aCell);
}
public ModelColumn() {
super(new TreeExpandableCell<JavaScriptObject, Object>(null));
setFieldUpdater(this);
setDefaultSortAscending(true);
setPublished(JavaScriptObject.createObject());
}
public static String renderDecorated(SafeHtmlBuilder rendered, String aId, PublishedCell aCell, SafeHtmlBuilder sb) {
return StyleIconDecorator.decorate(rendered.toSafeHtml(), aId, aCell, HasVerticalAlignment.ALIGN_MIDDLE, sb);
}
protected Cell<Object> getTargetCell() {
return ((TreeExpandableCell<JavaScriptObject, Object>) getCell()).getCell();
}
public Comparator<JavaScriptObject> getComparator() {
return comparator;
}
public ModelGrid getGrid() {
return grid;
}
public void setGrid(ModelGrid aValue) {
if (grid != aValue) {
if (grid != null && grid.getSortHandler() != null) {
grid.getSortHandler().setComparator(this, null);
}
grid = aValue;
if (grid != null) {
if (grid.getSortHandler() != null) {
if (isSortable()) {
grid.getSortHandler().setComparator(this, comparator);
} else {
grid.getSortHandler().setComparator(this, null);
}
}
grid.setColumnWidth(this, getWidth(), Style.Unit.PX);
if (visible)
grid.showColumn(this);
else
grid.hideColumn(this);
}
}
}
public String getField() {
return field;
}
public void setField(String aValue) {
if (field == null ? aValue != null : !field.equals(aValue)) {
field = aValue;
comparator = new PathComparator(sortField != null && !sortField.isEmpty() ? sortField : field, true);
if (grid != null && grid.getSortHandler() != null) {
grid.getSortHandler().setComparator(this, comparator);
}
}
}
public String getSortField() {
return sortField;
}
public void setSortField(String aValue) {
if (sortField == null ? aValue != null : !sortField.equals(aValue)) {
sortField = aValue;
comparator = new PathComparator(sortField != null && !sortField.isEmpty() ? sortField : field, true);
if (grid != null && grid.getSortHandler() != null) {
grid.getSortHandler().setComparator(this, comparator);
}
}
}
public double getMinWidth() {
return minWidth;
}
public void setMinWidth(double aValue) {
minWidth = aValue;
}
public double getMaxWidth() {
return maxWidth;
}
public void setMaxWidth(double aValue) {
maxWidth = aValue;
}
@Override
public boolean isChanged(JavaScriptObject anElement) {
return false;
}
@Override
public Object getValue(JavaScriptObject anElement) {
if (anElement != null && field != null && !field.isEmpty()) {
return Utils.getPathData(anElement, field);
} else
return null;
}
@Override
public void update(int aIndex, JavaScriptObject anElement, Object value) {
if (anElement != null && field != null && !field.isEmpty() && !readonly && grid.isEditable()) {
Utils.setPathData(anElement, field, Utils.toJs(value));
}
}
public boolean isVisible() {
return visible;
}
public void setVisible(boolean aValue) {
if (visible != aValue) {
visible = aValue;
((TreeExpandableCell<JavaScriptObject, Object>) getCell()).setVisible(aValue);
if (grid != null) {
if (visible) {
grid.showColumn(this);
} else {
grid.hideColumn(this);
}
grid.onResize();
}
}
}
public void updateVisible(boolean aValue) {
if (visible != aValue) {
visible = aValue;
((TreeExpandableCell<JavaScriptObject, Object>) getCell()).setVisible(aValue);
}
}
public double getDesignedWidth() {
return designedWidth;
}
public double getWidth() {
return designedWidth + widthDelta;
}
public void setWidth(double aValue) {
if (getWidth() != aValue) {
designedWidth = aValue;
widthDelta = 0;
if (grid != null) {
grid.setColumnWidth(this, getWidth(), Style.Unit.PX);
}
}
}
public void updateWidth(double aValue) {
if (getWidth() != aValue) {
widthDelta = Math.max(0, aValue - designedWidth);
}
}
public boolean isReadonly() {
return readonly;
}
public void setReadonly(boolean aValue) {
readonly = aValue;
}
@Override
public boolean isSortable() {
return super.isSortable();
}
@Override
public void setSortable(boolean aValue) {
super.setSortable(aValue);
if (grid != null && grid.getSortHandler() != null) {
if (aValue) {
grid.getSortHandler().setComparator(this, comparator);
} else {
grid.getSortHandler().setComparator(this, null);
}
}
}
public boolean isSelectOnly() {
return selectOnly;
}
public void setSelectOnly(boolean aValue) {
selectOnly = aValue;
}
public JavaScriptObject getOnRender() {
return onRender;
}
public void setOnRender(JavaScriptObject aValue) {
if (onRender != aValue) {
onRender = aValue;
if (editor != null) {
editor.setOnRender(onRender);
}
}
}
public JavaScriptObject getOnSelect() {
return onSelect;
}
public void setOnSelect(JavaScriptObject aValue) {
if (onSelect != aValue) {
onSelect = aValue;
if (editor != null) {
editor.setOnSelect(onSelect);
}
}
}
public ModelDecoratorBox<? extends Object> getEditor() {
return editor;
}
protected boolean gridRedrawQueued;
protected void enqueueGridRedraw() {
gridRedrawQueued = true;
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
if (gridRedrawQueued) {
gridRedrawQueued = false;
if (grid != null) {
grid.redraw();
}
}
}
});
}
public void setEditor(ModelDecoratorBox<? extends Object> aEditor) {
if (editor != aEditor) {
if (editor != null) {
editor.setOnRender(null);
editor.setOnSelect(null);
if (editor instanceof ModelCombo) {
((ModelCombo) editor).setOnRedraw(null);
}
}
editor = aEditor;
if (editor != null) {
editor.setOnRender(onRender);
editor.setOnSelect(onSelect);
if (editor instanceof ModelCombo) {
((ModelCombo) editor).setOnRedraw(new Runnable() {
@Override
public void run() {
enqueueGridRedraw();
}
});
}
}
if (editor instanceof ModelCheck) {
((TreeExpandableCell<JavaScriptObject, Object>) getCell()).setCell(new CheckBoxCell() {
@Override
public void onBrowserEvent(com.google.gwt.cell.client.Cell.Context context, Element parent, Object value, NativeEvent event, ValueUpdater<Object> valueUpdater) {
String type = event.getType();
if (BrowserEvents.CHANGE.equals(type) && (readonly || !grid.isEditable())) {
InputElement input = parent.<XElement>cast().firstChildByTagName("input").cast();
boolean checked = input.isChecked();
input.setChecked(!checked);
} else
super.onBrowserEvent(context, parent, value, event, valueUpdater);
}
@Override
public void render(com.google.gwt.cell.client.Cell.Context context, Object aValue, SafeHtmlBuilder sb) {
try {
if (getEditor() instanceof ModelDecoratorBox<?>) {
ModelDecoratorBox<Object> modelEditor = (ModelDecoratorBox<Object>) getEditor();
aValue = modelEditor.convert(Utils.toJava(aValue));
}
super.render(context, aValue, sb);
} catch (Exception ex) {
Logger.getLogger(ModelColumn.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
} else {
((TreeExpandableCell<JavaScriptObject, Object>) getCell()).setCell(new RenderedEditorCell<Object>(editor) {
@Override
protected void renderCell(com.google.gwt.cell.client.Cell.Context context, Object value, SafeHtmlBuilder sb) {
try {
if (editor instanceof ModelDecoratorBox<?>) {
ModelDecoratorBox<Object> modelEditor = (ModelDecoratorBox<Object>) getEditor();
value = modelEditor.convert(Utils.toJava(value));
}
super.renderCell(context, value, sb);
} catch (Exception ex) {
Logger.getLogger(ModelColumn.class.getName()).log(Level.SEVERE, null, ex);
}
}
@Override
public void startEditing(com.google.gwt.cell.client.Cell.Context context, Element aBoxPositionTemplate, Element aBoxParent, Object value, ValueUpdater<Object> valueUpdater,
Runnable onEditorClose) {
try {
if (getEditor() instanceof ModelDecoratorBox<?>) {
ModelDecoratorBox<Object> modelEditor = (ModelDecoratorBox<Object>) getEditor();
value = modelEditor.convert(Utils.toJava(value));
}
grid.setActiveEditor(getEditor());
super.startEditing(context, aBoxPositionTemplate, aBoxParent, value, valueUpdater, onEditorClose);
} catch (Exception ex) {
Logger.getLogger(ModelColumn.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
RenderedEditorCell<Object> cell = (RenderedEditorCell<Object>) getTargetCell();
cell.setReadonly(new CellHasReadonly() {
@Override
public boolean isReadonly() {
return readonly || !grid.isEditable();
}
});
cell.setOnEditorClose(new RenderedEditorCell.EditorCloser() {
@Override
public void closed(Element aTable) {
grid.setActiveEditor(null);
final GridSection<?> toFocus = GridSection.getInstance(aTable);
if (toFocus != null) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
toFocus.setFocus(true);
}
});
}
}
});
cell.setRenderer(new CellRenderer<Object>() {
@Override
public boolean render(Context context, String aId, Object value, SafeHtmlBuilder sb) {
JavaScriptObject onRender = ModelColumn.this.getOnRender() != null ? ModelColumn.this.getOnRender() : ModelColumn.this.getGrid().getOnRender();
if (onRender != null) {
if (editor instanceof ModelDecoratorBox<?>) {
ModelDecoratorBox<Object> modelEditor = (ModelDecoratorBox<Object>) getEditor();
value = modelEditor.convert(Utils.toJava(value));
}
String display = null;
Object oldValue = ((HasValue<Object>) editor).getValue();
((HasValue<Object>) editor).setValue(value);
try {
display = ((HasText) editor).getText();
} finally {
// We have to take care about old value because
// of edited cell while grid rendering.
((HasValue<Object>) editor).setValue(oldValue);
}
SafeHtmlBuilder lsb = new SafeHtmlBuilder();
PublishedCell cellToRender = calcContextPublishedCell(ModelColumn.this.getPublished(), onRender, context, ModelColumn.this.getField(), display);
if (cellToRender != null) {
if (cellToRender.getDisplay() != null)
display = cellToRender.getDisplay();
}
if (display == null)
lsb.append(SafeHtmlUtils.fromTrustedString(" "));
else
lsb.append(SafeHtmlUtils.fromString(display));
grid.complementPublishedStyle(cellToRender);
String decorId = renderDecorated(lsb, aId, cellToRender, sb);
if (cellToRender != null) {
if (context instanceof RenderedCellContext) {
((RenderedCellContext) context).setPublishedCell(cellToRender);
}
ModelColumn.bindDisplayCallback(cellToRender, decorId);
ModelColumn.bindIconCallback(cellToRender, decorId);
}
return true;
} else {
return false;
}
}
});
}
}
}
public static PublishedCell calcContextPublishedCell(JavaScriptObject aThis, JavaScriptObject aOnRender, com.google.gwt.cell.client.Cell.Context context, String aField, String aDisplay) {
if (aOnRender != null) {
Object key = context.getKey();
JavaScriptObject renderedElement = key instanceof JavaScriptObject ? (JavaScriptObject) key : null;
if (renderedElement != null) {
Object data = aField != null && !aField.isEmpty() ? Utils.getPathData(renderedElement, aField) : null;
PublishedCell cell = WidgetsUtils.publishCell(data, aDisplay);
JsArrayMixed args = JavaScriptObject.createArray().cast();
args.push(EventsPublisher.publishOnRenderEvent(aThis, null, null, renderedElement, cell));
aOnRender.<JsObject> cast().apply(aThis, args);
return cell;
}
}
return null;
}
protected static void bindDisplayCallback(final PublishedCell aCell, final String aTargetElementId) {
aCell.setDisplayCallback(new Runnable() {
@Override
public void run() {
Element identifiedTextSection = Document.get().getElementById(aTargetElementId);
if (identifiedTextSection != null) {
aCell.styleToElementBackgroundToTd(identifiedTextSection);
String toRender = aCell.getDisplay();
if (toRender == null)
toRender = " ";
identifiedTextSection.setInnerSafeHtml(SafeHtmlUtils.fromTrustedString(toRender));
}
}
});
}
protected static void bindIconCallback(final PublishedCell aCell, final String aTargetElementId) {
aCell.setIconCallback(new Runnable() {
@Override
public void run() {
Element identifiedTextSection = Document.get().getElementById(aTargetElementId);
if (identifiedTextSection != null) {
ImageElement iconSection = (ImageElement) identifiedTextSection.getPreviousSiblingElement();
if (iconSection != null) {
if (aCell.getIcon() != null) {
iconSection.setSrc(aCell.getIcon().getSafeUri().asString());
} else {
iconSection.setSrc(null);
}
}
}
}
});
}
@Override
public JavaScriptObject getPublished() {
return published;
}
@Override
public void setPublished(JavaScriptObject aValue) {
if (published != aValue) {
published = aValue;
}
}
public void sort() {
grid.addSort(this, true);
}
public void sortDesc() {
grid.addSort(this, false);
}
public void unsort() {
grid.unsortColumn(this);
}
}