/*
* Copyright (C) 2011 Jan Pokorsky
*
* 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/>.
*/
package cz.cas.lib.proarc.webapp.client.widget;
import com.google.gwt.core.client.JavaScriptObject;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.RecordList;
import com.smartgwt.client.types.BkgndRepeat;
import com.smartgwt.client.types.Cursor;
import com.smartgwt.client.types.ListGridEditEvent;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.types.RowEndEditAction;
import com.smartgwt.client.types.Visibility;
import com.smartgwt.client.util.JSOHelper;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.Img;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.ClickEvent;
import com.smartgwt.client.widgets.events.ClickHandler;
import com.smartgwt.client.widgets.events.FocusChangedEvent;
import com.smartgwt.client.widgets.events.FocusChangedHandler;
import com.smartgwt.client.widgets.events.KeyPressEvent;
import com.smartgwt.client.widgets.events.KeyPressHandler;
import com.smartgwt.client.widgets.form.fields.CanvasItem;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.HeaderItem;
import com.smartgwt.client.widgets.form.fields.StaticTextItem;
import com.smartgwt.client.widgets.form.fields.events.ChangedEvent;
import com.smartgwt.client.widgets.form.fields.events.ChangedHandler;
import com.smartgwt.client.widgets.form.fields.events.FormItemInitHandler;
import com.smartgwt.client.widgets.form.fields.events.ShowValueEvent;
import com.smartgwt.client.widgets.form.fields.events.ShowValueHandler;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.events.BodyKeyPressEvent;
import com.smartgwt.client.widgets.grid.events.BodyKeyPressHandler;
import com.smartgwt.client.widgets.grid.events.CellSavedEvent;
import com.smartgwt.client.widgets.grid.events.CellSavedHandler;
import com.smartgwt.client.widgets.layout.HLayout;
import com.smartgwt.client.widgets.layout.Layout;
import com.smartgwt.client.widgets.layout.VLayout;
import com.smartgwt.client.widgets.toolbar.ToolStrip;
import com.smartgwt.client.widgets.toolbar.ToolStripButton;
import cz.cas.lib.proarc.webapp.client.ClientUtils;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;
/**
* Grid as a FormItem.
*
* @author Jan Pokorsky
*/
public class ListFormItem extends CanvasItem {
private static final Logger LOG = Logger.getLogger(ListFormItem.class.getName());
private ListGrid listGrid;
private ListGridField[] fields;
private String help;
private ToolStripButton btnSwitch;
private ToolStripButton btnAdd;
public ListFormItem(String name, String title) {
super(name, title);
setup();
}
public ListFormItem(String name) {
super(name);
setup();
}
public ListFormItem() {
setup();
}
public void setFields(ListGridField... fields) {
this.fields = fields;
// setAttribute("fields", fields);
}
public ListGridField[] getFields() {
// JavaScriptObject fieldsJso = getAttributeAsJavaScriptObject("fields");
// if (fieldsJso == null) {
// return new ListGridField[0];
// }
// JavaScriptObject[] fieldsJsoArray = JSOHelper.toArray(fieldsJso);
// ListGridField[] fields = new ListGridField[fieldsJsoArray.length];
// for (int i = 0; i < fieldsJsoArray.length; i++) {
// JavaScriptObject fieldJso = fieldsJsoArray[i];
// fields[i] = new ListGridField(fieldJso);
// }
return fields;
}
public String getHelp() {
return help;
}
public void setHelp(String help) {
this.help = help;
}
public ToolStripButton getButtonSwitch() {
return btnSwitch;
}
public void setButtonSwitch(ToolStripButton btnSwitch) {
this.btnSwitch = btnSwitch;
}
public void changeState(boolean collapsed) {
// LOG.info("changeState: " + getName() + ", " + collapsed);
setCanFocus(!collapsed);
if (btnSwitch != null) {
String switchIcon = collapsed
? "[SKIN]SectionHeader/opener_closed.png"
: "[SKIN]SectionHeader/opener_opened.png";
btnSwitch.setIcon(switchIcon);
}
if (btnAdd != null) {
btnAdd.setDisabled(collapsed);
}
ListGrid grid = getGrid();
if (grid != null) {
Canvas gridCanvas = grid.getParentElement();
if (collapsed) {
gridCanvas.hide();
} else {
gridCanvas.show();
}
}
}
@Override
protected Canvas createCanvas() {
return super.createCanvas();
}
@Override
public void setCanFocus(Boolean canFocus) {
super.setCanFocus(canFocus);
Canvas canvas = getCanvas();
if (canvas != null) {
canvas.setCanFocus(canFocus);
}
}
private ListGrid getGrid() {
// Canvas canvas = getCanvas();
// for (Canvas child : canvas.getChildren()) {
// if (child instanceof ListGrid) {
// return (ListGrid) child;
// }
// }
// return null;
return this.listGrid;
}
private void setup() {
setWidth("*");
setHeight("*");
setColSpan("*");
setEndRow(true);
setStartRow(true);
setShouldSaveValue(true);
setCanFocus(false);
setInitHandler(new FormItemInitHandler() {
@Override
public void onInit(FormItem item) {
Canvas c = createCanvas(ListFormItem.this);
item.setCanFocus(false);
setCanvas(c);
}
});
addShowValueHandler(new ShowValueHandler() {
@Override
public void onShowValue(ShowValueEvent event) {
ListFormItem item = (ListFormItem) event.getSource();
ListGrid grid = item.getGrid();
// Object dataValue = event.getDataValue();
// if (dataValue instanceof Record) {
// setData(item, (Record) dataValue);
// } else if (dataValue instanceof Record[]) {
// setData(item, (Record[]) dataValue);
// } else if (dataValue instanceof List) {
// setData(item, (Record[]) ((List) dataValue).toArray(new Record[0]));
// } else if (dataValue instanceof RecordList) {
// setData(item, ((RecordList) dataValue).toArray());
// } else if (dataValue instanceof JavaScriptObject) {
// JavaScriptObject jso = (JavaScriptObject) dataValue;
// Record[] convertToRecordArray = Record.convertToRecordArray(jso);
// setData(item, convertToRecordArray);
// } else if (dataValue == null) {
// setData(item);
// } else {
// LOG.info(ClientUtils.format("onShowValue.name: %s, value: %s: , class: %s",
// item.getName(), dataValue, ClientUtils.safeGetClass(dataValue)));
// }
//// RecordList rl = event.getDataValueAsRecordList();
//// if (rl != null) {
//// Record[] toArray = rl.toArray();
//// setData(item, toArray);
//// } else {
//// setData(item);
//// }
// if (true) {
// return;
// }
RecordList values = event.getDataValueAsRecordList();
// Object dataValue = event.getDataValue();
// Class dataValueClass = (dataValue != null) ? dataValue.getClass() : null;
// LOG.info(ClientUtils.format("onShowValue.name: %s, value: %s: , class: %s", item.getName(), dataValue, dataValueClass));
//
// JavaScriptObject jsoValue = (JavaScriptObject) dataValue;
// Map convertToMap = JSOHelper.convertToMap(jsObj);
// LOG.info(
// convertToMap.toString()
//// ClientUtils.dump(event.getDataValueAsRecord(), "onShowValue.name: " + item.getName())
// );
//
// if (true) {
// setData(item, null);
// return ;
// }
if (values == null || values.isEmpty()) {
// hide grid in case of no records
item.changeState(true);
} else {
item.changeState(false);
}
setData(item, values);
}
});
}
private static void setData(ListFormItem item, Record... data) {
LOG.fine(ClientUtils.format("[%s] data: %s, class: %s",
item.getName(),
data,
ClientUtils.safeGetClass(data)
));
if (data != null) {
LOG.finest(ClientUtils.format("[%s] data.getLength: %s", item.getName(), data.length));
LOG.fine(ClientUtils.format("[%s] data.toArray: %s", item.getName(), Arrays.toString(data)));
}
ListGrid canvas = item.getGrid();
if (canvas != null) {
canvas.setData(data);
}
}
@Deprecated
private static void setData(ListFormItem item, RecordList data) {
LOG.fine(ClientUtils.format("[%s] data: %s, class: %s",
item.getName(),
data,
data != null ? data.getClass() : null));
if (data != null) {
LOG.finest(ClientUtils.format("[%s] data.getLength: %s", item.getName(), data.getLength()));
LOG.fine(ClientUtils.format("[%s] data.toArray: %s", item.getName(), Arrays.toString(data.toArray())));
}
ListGrid canvas = item.getGrid();
if (canvas != null) {
canvas.setData(data);
}
}
private static Canvas createCanvas(final ListFormItem item) {
final ListGrid grid = new ListGrid();
item.listGrid = grid;
// grid.setAutoWidth();
grid.setWidth100();
// grid.setHeight100();
grid.setCanEdit(true);
grid.setCanFocus(true);
grid.setSaveLocally(true);
grid.setShowEmptyMessage(false);
// grid.setAutoFitData(Autofit.VERTICAL);
grid.setAutoFitMaxRecords(10);
grid.setBodyOverflow(Overflow.VISIBLE);
grid.setOverflow(Overflow.VISIBLE);
// grid.setHeight(35);
grid.setShowAllRecords(true);
grid.setShowAllColumns(true);
grid.setListEndEditAction(RowEndEditAction.STOP);
grid.setCanRemoveRecords(true);
// grid.setRemoveIcon("minus16.png");
grid.setRemoveIcon("[SKIN]actions/remove_Disabled.png");
// grid.setRemoveIcon("[SKIN]DynamicForm/Remove_icon.png");
grid.setCanResizeFields(true);
// grid.setSelectionType(SelectionStyle.NONE);
grid.setWrapCells(true);
grid.setFixedRecordHeights(false);
// grid.setAlwaysShowEditors(true);
// grid.setCellPadding(4);
grid.setEditEvent(ListGridEditEvent.CLICK);
grid.setShowHeader(false);
grid.setLeaveScrollbarGap(false);
grid.setCanReorderRecords(true);
ListGridField[] fields = item.getFields();
grid.setFields(fields);
item.addChangedHandler(new ChangedHandler() {
@Override
public void onChanged(ChangedEvent event) {
System.out.println("---Item.onChanged: " + event.getValue() + ", item.getForm().valuesHaveChanged(): " +item.getForm().valuesHaveChanged());
}
});
grid.addCellSavedHandler(new CellSavedHandler() {
int state = 0;
int modulo = item.getFields().length;
@Override
public void onCellSaved(CellSavedEvent event) {
// state++;
// if (state % modulo != 0) {
// return ;
// }
// state = 0;
ListGrid grid = (ListGrid) event.getSource();
// item.storeValue(new RecordList(grid.getRecords()));
RecordList data = grid.getDataAsRecordList();
// System.out.println("###onCellSaved.grid: " + Arrays.toString(grid.getRecords()));
// System.out.println("###onCellSaved.list: " + Arrays.toString(data.toArray()));
Record[] toArray = data.toArray();
Record[] newArray = new Record[toArray.length];
Set<String> fieldNames = fieldNames(null, item.getFields());
for (int i = 0; i < toArray.length; i++) {
Record oldrec = toArray[i];
Map toMap = oldrec.toMap();
clearMap(toMap, fieldNames);
Record newrec = new Record(toMap);
newArray[i] = newrec;
}
// LOG.info(null)
// item.storeValue(newArray);
item.storeValue(new RecordList(newArray));
// item.storeValue(new RecordList(data.toArray()));
}
/** removes other than fields mapping */
private void clearMap(Map m, Set<String> fields) {
// Object[] keys = m.keySet().toArray();
Set<?> keySet = m.keySet();
keySet.retainAll(fields);
// for (Object key : keys) {
// if (!fields.contains(key)) {
// m.remove(key);
// }
// }
}
private Set<String> fieldNames(Map m, ListGridField[] fields) {
Set<String> fieldNames = new HashSet<String>();
for (ListGridField field : fields) {
fieldNames.add(field.getName());
}
return fieldNames;
}
});
final VLayout layout = new VLayout(0);
ToolStripButton btnAdd = new ToolStripButton();
item.btnAdd = btnAdd;
btnAdd.setIcon("[SKIN]actions/add.png");
// btnAdd.setIcon("plus16.png");
btnAdd.setIconSize(16);
btnAdd.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (grid.getRecordList().getLength() < 10) {
grid.startEditingNew();
}
}
});
final ToolStripButton btnSwitch = new ToolStripButton();
// btnSwitch.setIcon("[SKIN]actions/next.png");
btnSwitch.setIcon("[SKIN]SectionHeader/opener_opened.png");
btnSwitch.setIconSize(16);
btnSwitch.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
item.changeState(grid.isVisible());
}
});
item.setButtonSwitch(btnSwitch);
final ToolStripButton btnHelp = new ToolStripButton();
btnHelp.setIcon("[SKIN]actions/help.png");
btnHelp.setIconSize(16);
btnHelp.setCanFocus(false);
btnHelp.setCursor(Cursor.HELP);
btnHelp.setShowDown(false);
// btnHelp.setShowFocused(false);
// btnHelp.setShowSelectedIcon(false);
String helpHint = item.getHelp();
btnHelp.setTooltip(helpHint);
if (helpHint != null && helpHint.length() > 20) {
btnHelp.setHoverWidth(200);
}
ToolStrip toolStrip = new ToolStrip();
toolStrip.setCanFocus(false);
toolStrip.setWidth100();
toolStrip.setAutoHeight();
toolStrip.addButton(btnSwitch);
// HeaderItem headerItem = new HeaderItem("header", item.getTitle());
StaticTextItem headerItem = new StaticTextItem("header", item.getTitle());
headerItem.setTextBoxStyle("formTitle");
headerItem.setShowTitle(false);
headerItem.setDefaultValue(item.getTitle());
toolStrip.addFormItem(headerItem);
toolStrip.addFill();
toolStrip.addButton(btnAdd);
toolStrip.addButton(btnHelp);
// PickerIcon refreshPicker = new PickerIcon(PickerIcon.REFRESH);
// item.setIcons(refreshPicker);
HLayout gridLayout = new HLayout(0);
gridLayout.setCanFocus(true);
gridLayout.setAutoHeight();
gridLayout.setWidth100();
gridLayout.setVisibility(Visibility.VISIBLE);
gridLayout.addMember(grid);
layout.addMember(toolStrip);
layout.addMember(gridLayout);
layout.setAutoHeight();
layout.setVisibility(Visibility.VISIBLE);
layout.setCanFocus(item.getCanFocus());
layout.addFocusChangedHandler(new FocusChangedHandler() {
@Override
public void onFocusChanged(FocusChangedEvent event) {
boolean hasFocus = event.getHasFocus();
// LOG.info(ClientUtils.format("LFI.layout: item: %s, hasFocus: %s, containsFocus: %s",
// item.getName(), hasFocus, layout.containsFocus()));
if (!grid.getRecordList().isEmpty()) {
if (hasFocus) {
grid.selectRecord(0);
grid.focus();
// LOG.info("LFI.layout.gotFocus: grid.focus");
} else {
// LOG.info("LFI.layout.lostFocus");
}
}
}
});
// grid.addFocusChangedHandler(new FocusChangedHandler() {
//
// @Override
// public void onFocusChanged(FocusChangedEvent event) {
// boolean hasFocus = event.getHasFocus();
// if (hasFocus) {
//
// } else {
// LOG.info("LFI.grid: hasFocus: " + hasFocus + ", containsFocus: " + layout.containsFocus());
// grid.deselectAllRecords();
// Integer tabIndex = item.getTabIndex();
// item.getForm().focusInItem(tabIndex + 1);
// }
// }
// });
// helper widget to dispatch focus properly
final Canvas focusableBeforeGrid = new Img();
focusableBeforeGrid.setHeight(1);
focusableBeforeGrid.setWidth(1);
focusableBeforeGrid.setPadding(0);
focusableBeforeGrid.setMargin(0);
focusableBeforeGrid.setEdgeSize(0);
focusableBeforeGrid.setMinHeight(1);
focusableBeforeGrid.setMinWidth(1);
focusableBeforeGrid.setCanFocus(true);
focusableBeforeGrid.addFocusChangedHandler(new FocusChangedHandler() {
@Override
public void onFocusChanged(FocusChangedEvent event) {
boolean hasFocus = event.getHasFocus();
// LOG.info(ClientUtils.format("LFI.focusableBeforeGrid: item: %s, hasFocus: %s, containsFocus: %s",
// item.getName(), hasFocus, focusableBeforeGrid.containsFocus()));
if (hasFocus) {
Integer tabIndex = item.getTabIndex();
grid.deselectAllRecords();
FormItem[] items = item.getForm().getFields();
int itemIdx = findIndex(item, items);
int itemNextIdx = itemIdx - 1;
if (itemNextIdx < 0) {
// focus first item
itemNextIdx = items.length;
}
FormItem nextFocusItem = findItemWithDataReversely(itemNextIdx, items);
// LOG.info("LFI.labelAfterGrid: tabIndex: " + tabIndex + ", items.length: " + items.length
// + ", itemName: " + items[tabIndex].getName() + ", nextItemName: " + items[tabIndex + 1].getName()
// + "\n" + dump(items));
if (nextFocusItem != null) {
item.getForm().focusInItem(nextFocusItem);
}
}
}
});
gridLayout.addMember(focusableBeforeGrid, 0);
// helper widget to dispatch focus properly
// final Label labelAfterGrid = new Label("Focus dispather: " + item.getName());
final Canvas focusableAfterGrid = new Img();
// labelAfterGrid.setAutoHeight();
focusableAfterGrid.setHeight(1);
focusableAfterGrid.setWidth(1);
focusableAfterGrid.setPadding(0);
focusableAfterGrid.setMargin(0);
focusableAfterGrid.setEdgeSize(0);
focusableAfterGrid.setMinHeight(1);
focusableAfterGrid.setMinWidth(1);
focusableAfterGrid.setCanFocus(true);
focusableAfterGrid.addFocusChangedHandler(new FocusChangedHandler() {
@Override
public void onFocusChanged(FocusChangedEvent event) {
boolean hasFocus = event.getHasFocus();
// LOG.info(ClientUtils.format("LFI.focusableAfterGrid: item: %s, hasFocus: %s, containsFocus: %s",
// item.getName(), hasFocus, focusableAfterGrid.containsFocus()));
if (hasFocus) {
Integer tabIndex = item.getTabIndex();
grid.deselectAllRecords();
FormItem[] items = item.getForm().getFields();
int itemIdx = findIndex(item, items);
int itemNextIdx = itemIdx + 1;
if (itemNextIdx < 0 || itemNextIdx >= items.length) {
itemNextIdx = 0;
}
FormItem nextFocusItem = findItemWithData(itemNextIdx, items);
// LOG.info("LFI.focusableAfterGrid: tabIndex: " + tabIndex + ", items.length: " + items.length
// + ", itemName: " + items[tabIndex].getName() + ", nextItemName: " + items[tabIndex + 1].getName()
// + "\n" + dump(items));
if (nextFocusItem != null) {
item.getForm().focusInItem(nextFocusItem);
}
}
}
});
gridLayout.addMember(focusableAfterGrid);
return layout;
}
private static int findIndex(FormItem item, FormItem[] items) {
int itemIdx = -1;
for (int i = 0; i < items.length; i++) {
if (item == items[i]) {
itemIdx = i;
}
}
return itemIdx;
}
private static FormItem findItemWithDataReversely(int startIndex, FormItem... items) {
int mid = items.length >> 1;
if (startIndex != mid) {
startIndex = items.length - 1 - startIndex;
}
List<FormItem> itemReverseList = Arrays.asList(items);
Collections.reverse(itemReverseList);
return findItemWithData(startIndex, items);
}
private static FormItem findItemWithData(int startIndex, FormItem... items) {
return findItemWithData(startIndex, items.length, items);
}
private static FormItem findItemWithData(int startIndex, int stopIndex, FormItem... items) {
for (int i = startIndex; i < items.length && i < stopIndex; i++) {
FormItem item = items[i];
Boolean canFocus = item.getCanFocus();
if (canFocus == null || canFocus) {
return item;
}
}
if (startIndex <= 0) {
return null;
}
return findItemWithData(0, startIndex, items);
}
public static String dump(FormItem... fis) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < fis.length; i++) {
FormItem formItem = fis[i];
sb.append(ClientUtils.format("\n{%s: %s, tabIdx: %s, focus: %s%s}",
i, formItem.getName(), formItem.getTabIndex(), formItem.getCanFocus(), dumpChildren(formItem)));
}
return "FormItem[" + sb.toString() + ']';
}
private static String dumpChildren(FormItem formItem) {
if (formItem instanceof CanvasItem) {
CanvasItem canvasItem = (CanvasItem) formItem;
Canvas canvas = canvasItem.getCanvas();
return ClientUtils.format("\n {tab: %s, focus: %s, %s, %s, %s}",
canvas.getTabIndex(), canvas.getCanFocus(), canvas.getID(), canvas.getClass(), dumpChildren(canvas, " "));
} else {
return "";
}
}
private static String dumpChildren(Canvas canvasItem, String indent) {
StringBuilder sb = new StringBuilder();
Canvas[] children = (canvasItem instanceof Layout)
? ((Layout) canvasItem).getMembers()
: canvasItem.getChildren();
sb.append("children.length: ").append(children.length);
for (int i = 0; i < children.length; i++) {
Canvas child = children[i];
sb.append(ClientUtils.format("\n%s{%s, tab: %s, focus:%s, %s, %s, %s}",
indent, i, child.getTabIndex(), child.getCanFocus(), child.getID(),
child.getClass(), dumpChildren(child, indent + " ")));
}
return sb.toString();
}
}