/*
* 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.tree.xml;
import java.util.List;
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.IC;
import org.gk.ui.client.com.form.gkMap;
import org.gk.ui.client.com.utils.StringUtils;
import com.extjs.gxt.ui.client.core.XDOM;
import com.extjs.gxt.ui.client.data.BaseTreeModel;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelIconProvider;
import com.extjs.gxt.ui.client.data.ModelKeyProvider;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.MenuEvent;
import com.extjs.gxt.ui.client.event.SelectionListener;
import com.extjs.gxt.ui.client.store.TreeStore;
import com.extjs.gxt.ui.client.util.IconHelper;
import com.extjs.gxt.ui.client.widget.ContentPanel;
import com.extjs.gxt.ui.client.widget.grid.ColumnConfig;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.EditorGrid.ClicksToEdit;
import com.extjs.gxt.ui.client.widget.grid.RowEditor;
import com.extjs.gxt.ui.client.widget.layout.FitLayout;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.menu.MenuItem;
import com.extjs.gxt.ui.client.widget.treegrid.EditorTreeGrid;
import com.extjs.gxt.ui.client.widget.treegrid.TreeGrid;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.xml.client.Document;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.XMLParser;
/**
* <title>XML樹控元件</title>
*
* <pre>
* 將XML轉成Grid樹控元件的呈現方式
* </pre>
*
* @author w10447
* @since 2011/02/14
*/
public abstract class gkXMLTreeGridIC extends ContentPanel implements IC {
protected CoreIC core;
protected TreeGrid tree;
protected TreeStore<ModelData> store;
protected ColumnModel cm;
// XML's Document
protected Document doc;
protected static final String NAME = "name"; // 顯示的節點名稱
protected static final String TEXT = "text"; //
protected static final String NODE = "node"; // XML節點屬性ID
protected static final String ID = "id"; // 唯一的识别ID
protected static final int KeyCodeF2 = 113;
protected static final int KeyCodeEnter = 13;
protected static final String EditTree = "editTree";
protected boolean inEdit = false; // 树节点是否为正在修改名字状态
public static interface Event {
public final static String setXMLInfo = ".setXMLInfo";
public final static String getXMLInfo = ".getXMLInfo";
public final static String onClick = ".onClick";
public final static String onDoubleClick = ".onDoubleClick";
public final static String afterLoad = ".afterLoad";
public final static String onEdit = ".onEdit";
}
public String evtSetXMLInfo() {
return getId() + Event.setXMLInfo;
}
public String evtGetXMLInfo() {
return getId() + Event.getXMLInfo;
}
public String evtOnClick() {
return getId() + Event.onClick;
}
public String evtOnDoubleClick() {
return getId() + Event.onDoubleClick;
}
public String evtAfterLoad() {
return getId() + Event.afterLoad;
}
public String evtOnEdit() {
return getId() + Event.onEdit;
}
@Override
public CoreIC core() {
return core;
}
public gkXMLTreeGridIC(String id) {
setId(id);
core = new CoreIC(this);
core.init();
createTree("");
setLayout(new FitLayout());
createTreeNodeIconProvider(tree);
add(tree);
}
public gkXMLTreeGridIC(String id, String treeType) {
setId(id);
core = new CoreIC(this);
core.init();
createTree(treeType);
setLayout(new FitLayout());
createTreeNodeIconProvider(tree);
add(tree);
}
public TreeGrid getTree() {
return tree;
}
@Override
public void setInfo(Object info) {
// 清掉所有節點
store.removeAll();
// 透過GWT API解析整個XML字串轉成doc物件
doc = XMLParser.parse("<root>" + info + "</root>");
XMLParser.removeWhitespace(doc.getFirstChild());
// 解析doc's node資訊放到store裡面
parseXmlDoc2Store(doc, store, cm);
}
@Override
public Object getInfo() {
return doc.toString();
}
@Override
public void bindEvent() {
// 訂閱XML更新資訊
core.subscribe(evtSetXMLInfo(), new EventProcess() {
@Override
public void execute(String eventId, EventObject eo) {
setInfo(eo.getInfoString());
}
});
}
/**
* 展開樹控元件所有節點
*/
protected void expandAll() {
// 必須在樹控元件繪製到畫面才能展開所有節點
// 因此必須透過監聽 Render事件
tree.addListener(Events.Render, new Listener<BaseEvent>() {
@Override
public void handleEvent(BaseEvent be) {
((TreeGrid) be.getSource()).expandAll();
}
});
}
protected abstract ColumnModel createColumnModel();
/**
* 建立樹控元件,使用EditTreeGrid才能編輯樹節點名稱
*
* @param treeType
*/
public void createTree(String treeType) {
store = new TreeStore<ModelData>();
store.setKeyProvider(new ModelKeyProvider<ModelData>() {
@Override
public String getKey(ModelData model) {
return model.get("name") + "";
}
});
cm = createColumnModel();
// 设定树的类型为可编辑树
if (treeType.equals(EditTree)) {
tree = new EditorTreeGrid(store, cm) {
@Override
protected boolean hasChildren(ModelData model) {
// 根據節點名稱來決定是否可包含其他節點
List list = ((BaseTreeModel) model).getChildren();
return (list == null || list.size() == 0) ? false : true;
}
};
((EditorTreeGrid) tree).setClicksToEdit(ClicksToEdit.TWO);
} else {
tree = new TreeGrid(store, cm) {
@Override
protected boolean hasChildren(ModelData model) {
// 根據節點名稱來決定是否可包含其他節點
List list = ((BaseTreeModel) model).getChildren();
return (list == null || list.size() == 0) ? false : true;
}
};
}
}
public void addRowEditorPlugin() {
RowEditor<ModelData> editor = new RowEditor<ModelData>();
tree.addPlugin(editor);
}
/**
* 建立根節點
*
* @param doc
* @param store
* @param cm
*/
protected void parseXmlDoc2Store(Document doc, TreeStore store,
ColumnModel cm) {
Node rootNode = doc.getFirstChild();
BaseTreeModel treeModel = getRootNodeInfo();
treeModel.set("node", rootNode);
preprocessNode(rootNode, treeModel, cm);
store.add(treeModel, true);
}
/**
*
* @param xmlNode
* @param treeModel
* @param cm
*/
public void preprocessNode(Node xmlNode, BaseTreeModel treeModel,
ColumnModel cm) {
NodeList xmlNodeList = xmlNode.getChildNodes();
// 將所有子節點加入
for (int i = 0; i < xmlNodeList.getLength(); i++) {
Node subXMLNode = xmlNodeList.item(i);
if (subXMLNode.getNodeType() == Node.COMMENT_NODE
|| subXMLNode.getNodeType() == Node.TEXT_NODE) {
continue;
}
BaseTreeModel subTreeNode = createTreeNode(subXMLNode, cm);
preprocessNode(subXMLNode, subTreeNode, cm);
treeModel.add(subTreeNode);
}
}
/**
* <pre>
* 取得根節點資訊,讓子類別決定根節點的名稱和顯示的文字
* 例如
* new dejgMap("name", "ui").fill("text", "GUL元件庫");
* name 節點名稱、text 節點顯示文字
* </pre>
*
* @return BaseTreeModel
*/
protected abstract BaseTreeModel getRootNodeInfo();
/**
* 定義XML哪些Node Name是容器,可以拖放節點進去 null表示都可以,new gkList()表示都不行
*
* @return List
*/
protected List getFolderNode() {
return null;
}
/**
* <pre>
* 準備放到TreeNode的資訊
* NAME 節點名稱
* NODE XML節點物件
* cm ColumnConfig
* </pre>
*
* @param xmlNode
* @param cm
* @return BaseTreeModel
*/
public BaseTreeModel createTreeNode(Node xmlNode, ColumnModel cm) {
BaseTreeModel tm = new BaseTreeModel();
String name = (xmlNode.getAttributes().getNamedItem("name") + "")
.equals("null") ? xmlNode.getNodeName() : (xmlNode
.getAttributes().getNamedItem("name") + "");
String id = (xmlNode.getAttributes().getNamedItem("id") + "")
.equals("null") ? DOM.createUniqueId() : (xmlNode
.getAttributes().getNamedItem("id") + "");
Node iconNode = xmlNode.getAttributes().getNamedItem("icon");
List<ColumnConfig> columns = cm.getColumns();
for (ColumnConfig cc : columns) {
String key = cc.getId();
String value = (xmlNode.getAttributes().getNamedItem(key) + "")
.equals("null") ? "" : (xmlNode.getAttributes()
.getNamedItem(key) + "");
tm.set(key, value);
}
tm.set(NAME, name);
tm.set(NODE, xmlNode);
tm.set(ID, id);
if (iconNode != null) {
tm.set("icon", iconNode.getNodeValue());
} else {
tm.set("icon", xmlNode.getNodeName());
}
return tm;
}
/**
* 幫忙處理拖拉Tree節點時,將樹節點裡面包含的XML Node也進行更新處理
*/
protected void createTreeHandler() {
}
protected void publishXmlUpdate() {
// 拖拉後,將整個XML字串整理好,發佈出去
String xmlInfo = StringUtils.xmlPretty(doc.getFirstChild() + "");
core.getBus().publish(new EventObject(evtGetXMLInfo(), xmlInfo));
}
public gkXMLTreeGridIC() {
this(XDOM.getUniqueId());
}
public void createTreeNodeIconProvider(TreeGrid tree) {
// 根據model裡面的icon屬性,設定圖示 (如果有的話)
tree.setIconProvider(new ModelIconProvider<ModelData>() {
@Override
public AbstractImagePrototype getIcon(ModelData model) {
String icon = model.get("icon");
return icon == null ? null : iconProvider(icon);
}
});
}
/**
* 讓子類別改寫提供icon
*
* @param name
* @return AbstractImagePrototype
*/
protected abstract AbstractImagePrototype iconProvider(String name);
/**
* 建立滑鼠右鍵選單,將原本按左鍵會隱藏視窗功能Disable
*
* @return Menu
*/
protected Menu createMenu() {
return new Menu() {
{
sinkEvents(com.google.gwt.user.client.Event.KEYEVENTS);
}
@Override
public void onBrowserEvent(com.google.gwt.user.client.Event event) {
if (event.getKeyCode() == 37) {
event.stopPropagation();
} else {
super.onBrowserEvent(event);
}
}
};
}
public void addMenuItem(String txt, ImageResource imgRes, EventProcess ep) {
addMenuItem(txt, AbstractImagePrototype.create(imgRes), ep);
}
/**
* 增加滑鼠右鍵選單項目
*
* @param txt
* @param iconStyle
* @param ep
*/
public void addMenuItem(String txt, String iconStyle, EventProcess ep) {
addMenuItem(txt, IconHelper.create(iconStyle), ep);
}
/**
* 增加滑鼠右鍵選單項目
*
* @param txt
* @param imgIcon
* @param ep
*/
protected void addMenuItem(final String txt,
AbstractImagePrototype imgIcon, final EventProcess ep) {
if (getContextMenu() == null) {
setContextMenu(createMenu());
}
Menu menu = getContextMenu();
MenuItem item = new MenuItem();
item.setText(txt);
item.setIcon(imgIcon);
item.addSelectionListener(new SelectionListener<MenuEvent>() {
@Override
public void componentSelected(MenuEvent ce) {
// 如果Tree沒有Item被點選,md將會是 null
ModelData md = getTree().getSelectionModel().getSelectedItem();
gkMap m = new gkMap();
if (md != null) {
m.putAll(md.getProperties());
}
ep.execute(txt, new EventObject(txt, m));
}
});
menu.add(item);
}
@Override
public void linkInfo(Object info) {
throw new RuntimeException("not implemented yet!");
}
}