/*
* Copyright (C) 2000-2012 InfoChamp System Corporation
*
* 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 org.gk.ui.client.com.utils;
import java.util.Date;
import java.util.List;
import java.util.Map;
import jfreecode.gwt.event.client.bus.EventBus;
import jfreecode.gwt.event.client.bus.EventObject;
import jfreecode.gwt.event.client.bus.EventProcess;
import org.gk.ui.client.com.CoreIC;
import org.gk.ui.client.com.form.gkComboBox;
import org.gk.ui.client.com.form.gkDateField;
import org.gk.ui.client.com.form.gkLabelField;
import org.gk.ui.client.com.form.gkList;
import org.gk.ui.client.com.form.gkRadio;
import org.gk.ui.client.com.form.gkTimeField;
import org.gk.ui.client.com.form.gkYMField;
import org.gk.ui.client.com.panel.gkFieldAccessIfc;
import org.gk.ui.client.com.panel.gkFormPanelIC;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.EventType;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.FieldEvent;
import com.extjs.gxt.ui.client.event.KeyListener;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.SelectionChangedEvent;
import com.extjs.gxt.ui.client.event.SelectionChangedListener;
import com.extjs.gxt.ui.client.widget.LayoutContainer;
import com.extjs.gxt.ui.client.widget.form.AdapterField;
import com.extjs.gxt.ui.client.widget.form.CheckBox;
import com.extjs.gxt.ui.client.widget.form.ComboBox;
import com.extjs.gxt.ui.client.widget.form.Field;
import com.extjs.gxt.ui.client.widget.form.LabelField;
import com.extjs.gxt.ui.client.widget.form.NumberField;
import com.extjs.gxt.ui.client.widget.form.Radio;
import com.extjs.gxt.ui.client.widget.form.SliderField;
import com.extjs.gxt.ui.client.widget.form.TextField;
/**
* <pre>
* 公用工具類別,透過此工具類別所產生的GXT物件,將會和傳入
* Map物件中的指定key進行資料繫結。也就是說,當使用者修改了
* GXT物件的狀態,該狀態就會自動保留在Map。另外,GXT元件
* 也會訂閱指定事件,所以可以透過EventBus將Map資訊一次
* 發佈給所有的GXT物件進行變更,所以資料繫結是雙向的。
* </pre>
*
* @author I21890
* @since 2009/10/28
*/
public class BindingUtils {
/**
* 更新指定LayoutContainer的資訊
*
* @param lc
* LayoutContainer型別的物件
* @param info
* 要更新的資訊物件
*/
public static void update(LayoutContainer lc, Map info) {
update(lc.getId(), info);
}
/**
* 更新指定eventId的資訊
*
* @param eventId
* 發佈的EventId
* @param info
* 要更新的資訊物件
*/
public static void update(String eventId, Map info) {
EventBus.get().publish(new EventObject(eventId, info));
}
/**
* 建立Radio,並讓此Radio物件訂閱eventId事件,當收到此事件 將更新info物件和本身欄位顯示
*
* @param eventId
* @param info
* @param infoKey
* @param infoValue
* @param core
* @return Radio
*/
public static Radio createRadio(String eventId, final Map info,
final String infoKey, final String infoValue, CoreIC core) {
final Radio radio = new gkRadio() {
@Override
protected void onFocus(ComponentEvent ce) {
if (!hasFocus) {
if (!readOnly) {
info.put(infoKey, infoValue);
}
super.onFocus(ce);
}
}
};
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(infoKey)) {
Object value = infoMap.get(infoKey);
radio.setValue(infoValue.equals(value));
info.put(infoKey, value);
}
}
});
radio.addListener(Events.Change, new Listener<FieldEvent>() {
@Override
public void handleEvent(FieldEvent fe) {
updateDirtyField(infoKey, info);
Boolean value = (Boolean) fe.getField().getValue();
Object currentValue = info.get(infoKey);
if (value != null && value) {
if (!infoValue.equals(currentValue)) {
info.put(infoKey, infoValue);
}
} else {
if (infoValue.equals(currentValue)) {
info.put(infoKey, "");
}
}
}
});
if (info.get(infoKey) == null) {
initialInfoValue(radio, infoKey, info, "");
}
return radio;
}
/**
* 建立NumberField,此物件的資訊將放在info物件指定的infoKey
*
* @param eventId
* @param info
* @param infoKey
* @param core
* @return NumberField
*/
public static NumberField createNumberField(String eventId, final Map info,
final String infoKey, CoreIC core) {
final NumberField nf = new NumberField();
nf.addKeyListener(new KeyListener() {
@Override
public void componentKeyUp(ComponentEvent event) {
// 複製時非數字會顯示先前的value
if (nf.getValue() == null) {
if (nf.getRawValue().equals("")) {
info.put(infoKey, 0);
} else {
Number nv = (Number) info.get(infoKey);
nf.setValue(nv);
info.put(infoKey, nv);
}
} else {
info.put(infoKey, nf.getValue());
}
}
});
subscribe(eventId, nf, infoKey, info, core);
addListener(Events.Change, nf, infoKey, info);
initialInfoValue(nf, infoKey, info, "");
return nf;
}
/**
* 建立LabelField,此物件的資訊將放在info物件指定的infoKey
*
* @param eventId
* @param info
* @param infoKey
* @param core
* @return gkLabelField
*/
public static gkLabelField createLabelField(String eventId, Map info,
String infoKey, CoreIC core) {
gkLabelField label = new gkLabelField();
subscribe(eventId, label, infoKey, info, core);
addListener(Events.Change, label, infoKey, info);
initialInfoValue(label, infoKey, info, "");
return label;
}
public static ComboBox createComboBox(String eventId, final Map info,
final String infoKey, CoreIC core) {
final gkComboBox cb = new gkComboBox();
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(infoKey)) {
Object value = infoMap.get(infoKey);
ModelData md = null;
if (value instanceof ModelData) {
md = (ModelData) value;
} else {
if (value != null) {
md = cb.getPropertyEditor().convertStringValue(
value.toString());
}
}
cb.setValue(md);
info.put(infoKey, md == null ? "" : md.get("value"));
}
}
});
cb.addSelectionChangedListener(new SelectionChangedListener<ModelData>() {
@Override
public void selectionChanged(SelectionChangedEvent<ModelData> se) {
updateDirtyField(infoKey, info);
ModelData md = se.getSelectedItem();
info.put(infoKey, md == null ? "" : md.get("value"));
}
});
cb.addKeyListener(new KeyListener() {
@Override
public void componentKeyUp(ComponentEvent event) {
updateDirtyField(infoKey, info);
ModelData value = cb.getValue();
info.put(infoKey, value == null ? "" : value.get("value"));
}
});
initialInfoValue(cb, infoKey, info, "");
return cb;
}
/**
* 建立DateField,此物件的資訊將放在info物件指定的infoKey
*
* @param eventId
* @param info
* @param infoKey
* @param format
* @param core
* @return gkDateField
*/
public static gkDateField createDateField(String eventId, final Map info,
final String infoKey, String format, CoreIC core) {
gkDateField df = new gkDateField();
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(infoKey)) {
Object value = infoMap.get(infoKey);
info.put(infoKey, value == null ? "" : value);
}
}
});
addListener(Events.Change, df, infoKey, info);
initialInfoValue(df, infoKey, info, "");
return df;
}
public static gkYMField createYMField(String eventId, final Map info,
final String infoKey, String format, CoreIC core) {
gkYMField yf = new gkYMField();
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(infoKey)) {
Object value = infoMap.get(infoKey);
info.put(infoKey, value == null ? "" : value);
}
}
});
addListener(Events.Change, yf, infoKey, info);
initialInfoValue(yf, infoKey, info, "");
return yf;
}
/**
* 建立CheckBox,此物件的資訊將放在info物件指定的infoKey,value是用List存放已勾選的值
*
* @param eventId
* @param info
* @param infoKey
* @param infoValue
* @param core
* @return CheckBox
*/
public static CheckBox createCheckBox(String eventId, final Map info,
final String infoKey, final String infoValue, CoreIC core) {
final CheckBox cb = new CheckBox() {
@Override
public void setValue(Boolean b) {
super.setValue(b);
Object value = info.get(infoKey);
if (value instanceof List) {
List cbList = (List) value;
if (b != null && b) {
// 如果是true而且不在cbList裡面,表示狀態更新了
if (!cbList.contains(infoValue)) {
cbList.add(infoValue);
// 透過put發布InfoChange事件
info.put(infoKey, cbList);
}
} else {
// 如果在cbList裡面,表示狀態更新了
if (cbList.contains(infoValue)) {
cbList.remove(infoValue);
// 透過put發布InfoChange事件
info.put(infoKey, cbList);
}
}
}
}
@Override
protected void onClick(ComponentEvent ce) {
super.onClick(ce);
if ((boxLabelEl != null && boxLabelEl.dom.isOrHasChild(ce
.getTarget())) || readOnly) {
return;
}
fireEvent(Events.Select, ce);
}
};
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(infoKey)) {
Object value = infoMap.get(infoKey);
if (value != null && value instanceof List) {
List cbList = (List) value;
cb.setValue(cbList.contains(infoValue));
}
}
}
});
addListener(Events.Change, cb, infoKey, info);
initialInfoValue(cb, infoKey, info, new gkList());
return cb;
}
/**
* 建立TimeField,此物件的資訊將放在info物件指定的infoKey
*
* @param eventId
* @param info
* @param infoKey
* @param core
* @return gkTimeField
*/
public static gkTimeField createTimeField(String eventId, final Map info,
final String infoKey, CoreIC core) {
final gkTimeField tf = new gkTimeField();
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(infoKey)) {
String value = (String) infoMap.get(infoKey);
value = value.replaceAll(":", "");
tf.setTimeValue(value);
// 選項欄位value為空時清除欄位中的顯示(當通過事件設定欄位value為空時欄位無法自行設定顯示為空需通過此邏輯清除欄位中的顯示)
if (value.equals("")) {
tf.clearSelections();
}
info.put(infoKey, value);
}
}
});
tf.addSelectionChangedListener(new SelectionChangedListener<ModelData>() {
@Override
public void selectionChanged(SelectionChangedEvent<ModelData> se) {
updateDirtyField(infoKey, info);
ModelData md = se.getSelectedItem();
info.put(infoKey, md == null ? "" : md.get("timeValue"));
}
});
tf.addKeyListener(new KeyListener() {
@Override
public void componentKeyUp(ComponentEvent event) {
updateDirtyField(infoKey, info);
ModelData value = tf.getValue();
info.put(infoKey, value == null ? "" : value.get("timeValue"));
}
});
initialInfoValue(tf, infoKey, info, "");
return tf;
}
public static void binding(String eventId, Field field, final Map info,
final gkFieldAccessIfc access, CoreIC core) {
// 判斷是否為AdapterField
if (field instanceof AdapterField) {
LayoutContainer lc = (LayoutContainer) ((AdapterField) field)
.getWidget();
// 取出layoutConatainer裡面的物件
for (int i = 0; i < lc.getItemCount(); i++) {
Object obj = lc.getItem(i);
// 需要是field才作監聽動作
if (obj instanceof Field) {
final Field f = (Field) obj;
// 訂閱事件
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(f.getId())) {
access.setValue(infoMap);
Object value = infoMap.get(f.getId());
if (value != null) {
info.put(f.getId(), value);
}
}
}
});
// 監聽事件
f.addListener(Events.Change, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
updateDirtyField(f.getId(), info);
// .因應 eventId_infoChange 事件,應該設定 dirtyField
// 最後才info.put()
info.put(f.getId(), access.getValue(f.getId()));
}
});
// 填完值離開後(onblur)也要把值填回dirtyField
f.addListener(Events.OnBlur, new Listener() {
@Override
public void handleEvent(BaseEvent be) {
updateDirtyField(f.getId(), info);
info.put(f.getId(), access.getValue(f.getId()));
}
});
// 判斷widget component是否有預設值
Object filedValue = access.getValue(f.getId());
if (filedValue != null) {
info.put(f.getId(), filedValue);
}
}
}
}
}
/**
* 提供其他欄位binding
*
* @param eventId
* @param field
* @param infoKey
* @param info
* @param core
*/
public static void binding(String eventId, Field field, String infoKey,
Map info, CoreIC core) {
subscribe(eventId, field, infoKey, info, core);
addListener(Events.Change, field, infoKey, info);
addKeyListener(field, infoKey, info);
initialInfoValue(field, infoKey, info, "");
}
/**
* 訂閱事件
*
* @param eventId
* @param field
* @param infoKey
* @param info
* @param core
*/
public static void subscribe(String eventId, final Field field,
final String infoKey, final Map info, CoreIC core) {
core.subscribe(eventId, new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
Map infoMap = eo.getInfoMap();
if (infoMap.containsKey(infoKey)) {
Object value = infoMap.get(infoKey);
if (field instanceof NumberField) {
// 非空字串或非null才處理
if (value != null && !value.equals("")) {
field.setValue(field.getPropertyEditor()
.convertStringValue("" + value));
}
} else {
if (value instanceof String) {
field.setValue(field.getPropertyEditor()
.convertStringValue((String) value));
} else {
field.setValue(value);
}
}
info.put(infoKey, value == null ? "" : value);
}
}
});
}
/**
* 增加監聽事件
*
* @param eventType
* @param field
* @param infoKey
* @param info
*/
public static void addListener(EventType eventType, final Field field,
final String infoKey, final Map info) {
field.addListener(eventType, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
updateDirtyField(infoKey, info);
if (field instanceof TextField || field instanceof LabelField
|| field instanceof SliderField) {
Object value = field.getValue();
if (value != null) {
if (field instanceof gkDateField) {
value = DateTimeUtils.formatDate((Date) value);
}
}
// 因應eventId_infoChange事件,應該設定dirtyField最後才info.put()
info.put(infoKey, value == null ? "" : value);
}
}
});
}
/**
* 增加鍵盤的監聽事件
*
* @param field
* @param infoKey
* @param info
*/
public static void addKeyListener(final Field field, final String infoKey,
final Map info) {
field.addKeyListener(new KeyListener() {
@Override
public void componentKeyUp(ComponentEvent event) {
updateDirtyField(infoKey, info);
Object value = field.getValue();
info.put(infoKey, value == null ? "" : value);
}
});
}
/**
* 初始化info內的資料
*
* @param field
* @param infoKey
* @param info
* @param defaultValue
*/
public static void initialInfoValue(Field field, String infoKey, Map info,
Object defaultValue) {
info.put(infoKey, defaultValue);
}
/**
* 更新info內,dirtyList的infoKey
*
* @param infoKey
* @param info
*/
public static void updateDirtyField(String infoKey, Map info) {
if (info.containsKey(gkFormPanelIC.Event.DIRTY_FIELD)) {
List dirtyList = (List) info.get(gkFormPanelIC.Event.DIRTY_FIELD);
// 先remove, 在add, for 觸發dirtyField事件
dirtyList.remove(infoKey);
dirtyList.add(infoKey);
}
}
}