/* * 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.engine.client.build.grid; import java.util.Iterator; import java.util.List; import java.util.Map; import org.gk.engine.client.build.panel.XContentPanel; import org.gk.engine.client.event.EventCenter; import org.gk.engine.client.event.EventListener; import org.gk.engine.client.event.IEventConstants; import org.gk.engine.client.gen.UIGen; import org.gk.engine.client.utils.IRegExpUtils; import org.gk.ui.client.com.form.gkList; import org.gk.ui.client.com.form.gkMap; import org.gk.ui.client.com.grid.gkGridIC; import org.gk.ui.client.com.grid.gkListGridIC; import org.gk.ui.client.com.grid.gkPageGridIC; import org.gk.ui.client.com.grid.gkPageGridIC.BarPosition; import com.extjs.gxt.ui.client.Style.SelectionMode; import com.extjs.gxt.ui.client.data.BasePagingLoadConfig; import com.extjs.gxt.ui.client.data.BasePagingLoadResult; import com.extjs.gxt.ui.client.data.ModelData; import com.extjs.gxt.ui.client.dnd.DND.Feedback; import com.extjs.gxt.ui.client.dnd.DND.Operation; import com.extjs.gxt.ui.client.dnd.GridDragSource; import com.extjs.gxt.ui.client.dnd.GridDropTarget; import com.extjs.gxt.ui.client.event.BaseEvent; import com.extjs.gxt.ui.client.event.DNDEvent; import com.extjs.gxt.ui.client.event.Events; import com.extjs.gxt.ui.client.event.GridEvent; import com.extjs.gxt.ui.client.event.Listener; import com.extjs.gxt.ui.client.widget.Component; import com.extjs.gxt.ui.client.widget.grid.AggregationRowConfig; 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.HeaderGroupConfig; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.xml.client.Node; /** * Grid清單 * * <pre> * XGrid裡面放XGridField * </pre> * * @author I21890 2010/02/08 * @since 2010/7/26 */ public class XGrid extends XContentPanel { private final static String MODE = "single|simple|multi"; // 正整數的正規表示式 private final static String POSITIVE_INTEGER = "^[1-9]\\d*$"; protected String adjustForHScroll, autoNewRow, columnHeaderVisible; protected String feedback, limit, operation; protected String selMode, stripe, xTemplate; protected String page, pageSize; protected String checkBox, initRow, autoSelect; protected String seqPosition; protected String dragSource, dropTarget; protected String onRow; public XGrid(Node node, List<XGridField> widgets) { super(node, widgets); height = super.getAttribute("height", "300"); layout = super.getAttribute("layout", "fitlayout"); // 共通屬性 adjustForHScroll = super.getAttribute("adjustForHScroll", "true"); autoNewRow = super.getAttribute("autoNewRow", "false"); columnHeaderVisible = super.getAttribute("columnHeaderVisible", "true"); feedback = super.getAttribute("feedback", "insert"); limit = super.getAttribute("limit", ""); operation = super.getAttribute("operation", "move"); selMode = super.getAttribute("selMode", ""); stripe = super.getAttribute("stripe", "true"); xTemplate = super.getAttribute("xTemplate", ""); seqPosition = super.getAttribute("seqPosition", ""); // 分頁Grid屬性 page = super.getAttribute("page", "false"); pageSize = super.getAttribute("pageSize", ""); // 多筆編輯Grid屬性 checkBox = super.getAttribute("checkBox", ""); initRow = super.getAttribute("initRow", ""); autoSelect = super.getAttribute("autoSelect", "true"); // 事件屬性 dragSource = super.getAttribute("drag", "false"); dropTarget = super.getAttribute("drop", "false"); onRow = super.getAttribute("onRow", ""); } public String getAdjustForHScroll() { return adjustForHScroll; } public String getAutoNewRow() { return autoNewRow; } public String getColumnHeaderVisible() { return columnHeaderVisible; } public String getFeedback() { return feedback; } public String getLimit() { return limit; } public String getOperation() { return operation; } public String getSelMode() { return selMode; } public String getStripe() { return stripe; } public String getxTemplate() { return xTemplate; } public String getPage() { return page; } public boolean isPage() { return Boolean.parseBoolean(page) || page.matches("top|bottom"); } public String getPageSize() { return pageSize; } public String getCheckBox() { return checkBox; } public String getInitRow() { return initRow; } public String getAutoSelect() { return autoSelect; } public String getDragSource() { return dragSource; } public String getDropTarget() { return dropTarget; } public String getOnRow() { return onRow; } @Override public void init() { if (isPage() && init.startsWith(IEventConstants.HANDLER_BEAN)) { gkGridIC grid = (gkGridIC) super.getComponent(); if (grid != null) { grid.setInvokeBean(init); init = appendPageBar(init); } } super.init(); } @Override public Component build() { List fields = new gkList(); // HeaderGroup List header = new gkList(); // AggregationRow List aggRow = new gkList(); Iterator<UIGen> it = widgets.iterator(); // grid可放ColumnConfig或HeaderGroupConfig或AggregationRowConfig while (it.hasNext()) { UIGen ui = it.next(); Component com = ui.build(); Object obj = com.getData("columnConfig"); if (obj instanceof ColumnConfig) { fields.add(obj); } else if (obj instanceof HeaderGroupConfig) { header.add(obj); } else if (obj instanceof AggregationRowConfig) { aggRow.add(obj); } } // 如果是編輯模式,id改為innerGrid_$id gkGridIC grid = createGridIC(fields, header); final gkGridIC g = grid; // 設定是否要隱藏 ColumnHeader。 // 改用監聽Grid的render事件,因建構階段還取不到ColumnHeader grid.getGrid().addListener(Events.Render, new Listener<BaseEvent>() { @Override public void handleEvent(BaseEvent be) { g.getView().getHeader() .setVisible(Boolean.parseBoolean(columnHeaderVisible)); } }); // 第一次開啟分頁時Refresh Head,解決開啟關閉的摺頁後無法看到 Checkbox 問題 grid.addListener(Events.Expand, new Listener<BaseEvent>() { @Override public void handleEvent(BaseEvent be) { g.getGrid().getView().getHeader().refresh(); // 還原狀態 (當沒有任何資料時,Checkbox 會被勾選) g.getGrid().fireEvent(Events.ViewReady); g.removeListener(Events.Expand, this); } }); // 是否新增Aggregation Row if (!aggRow.isEmpty()) { attachAggregationRow(aggRow, grid); } // 設定是否自動增列 if (Boolean.parseBoolean(autoNewRow)) { grid.setAutoNewRow(true); } // 設定限制資料筆數 if (limit.matches(POSITIVE_INTEGER)) { grid.setLimit(Integer.parseInt(limit)); } // 對pageGrid設定每頁顯示資料筆數pageSize if (grid instanceof gkPageGridIC) { if (pageSize.matches(IRegExpUtils.POSITIVE_INTEGER)) { ((gkPageGridIC) grid).setPageSize(Integer.parseInt(pageSize)); } } // 設定Expander Row,若設定如果在放在new gkMultiEditorGridIC之前, // 則"+"符號在checkbox勾選框之後,反之其後 if (!xTemplate.equals("")) { grid.setRowExpander(xTemplate); } // 設定init時是否需要新增一空白行,"沿用舊的用法 type='edit'時預設要有initrow,但又可能會設定不要initRow" if (type.equals("edit")) { grid.setInitBlankRow(true); } if (!initRow.equals("")) { grid.setInitBlankRow(Boolean.parseBoolean(initRow)); } if (type.equals("edit") || checkBox.equals("true")) { if (!checkBox.equals("")) { grid.addCheckBox(Boolean.parseBoolean(checkBox), Boolean.parseBoolean(autoSelect)); } else { grid.addCheckBox(true, Boolean.parseBoolean(autoSelect)); } } // 是否新增流水號 if (seqPosition.matches("\\d+")) { grid.setSequence(Integer.parseInt(seqPosition)); } // 垂直scrollBar是否自動出現 grid.getView().setAdjustForHScroll( Boolean.parseBoolean(adjustForHScroll)); // 設定SelectionMode if (selMode.matches(MODE)) { grid.setSelectionMode(SelectionMode.valueOf(selMode.toUpperCase())); } // 設定每筆Row是否要用顏色區分 grid.getGrid().setStripeRows(Boolean.parseBoolean(stripe)); // 如果onRow有設定事件指令,當使用者點選清單中某筆資料時會觸發此事件 addEventListener(grid.getGrid(), Events.RowMouseDown, onRow); // 按上下方向鍵觸發onRow事件 addKeyListener(grid); grid.getView().setForceFit(true); initComponent(grid); return grid; } @Override protected void initComponent(Component com) { super.initComponent(com); gkGridIC gridIC = (gkGridIC) com; // 判斷設定grid是否為dragSource if (Boolean.parseBoolean(dragSource)) { final EventListener evtListener = new EventListener(gridIC.getId(), onDrag, XGrid.this); new GridDragSource(gridIC.getGrid()) { @Override protected void onDragDrop(DNDEvent e) { String dragSrcComId = e.getDragSource().getComponent() .getId() + ""; String dropTrgComId = e.getDropTarget().getComponent() .getId() + ""; // 判斷如果dragSource和dropTarget是同一Component,則設定operation=move // 否則依照gul語法中的參數來設定參數值 if (dragSrcComId.equals(dropTrgComId)) { e.setOperation(Operation.MOVE); } else { e.setOperation(Operation.valueOf(XGrid.this.operation .toUpperCase())); } // 這裡做延時處理是因為onDrop定義的事件有可能阻斷拖放的完成,如:show:grid // 拿掉延時測看看 final DNDEvent event = e; if (!onDrop.equals("")) { Scheduler.get().scheduleDeferred( new ScheduledCommand() { @Override public void execute() { evtListener.handleEvent(event); callSuperOnDragDrop(event); } }); } else { callSuperOnDragDrop(event); } } private void callSuperOnDragDrop(DNDEvent e) { super.onDragDrop(e); } }; } // 判斷設定grid是否為dropTarget if (Boolean.parseBoolean(dropTarget)) { final EventListener evtListener = new EventListener(gridIC.getId(), onDrop, XGrid.this); final EventListener evtListener2 = new EventListener( gridIC.getId(), onAfterDrop, XGrid.this); GridDropTarget target = new GridDropTarget(gridIC.getGrid()) { @Override protected void onDragDrop(DNDEvent e) { Object obj = e.getData(); // 複製一份新的data,然後設定給DNDEvent,避免operation=copy時rowIndex計算錯誤 if (XGrid.this.operation.toUpperCase().equals("COPY")) { if (obj instanceof List) { List<ModelData> models = new gkList<ModelData>(); List list = (List) obj; for (Iterator iterator = list.iterator(); iterator .hasNext();) { ModelData md = (ModelData) iterator.next(); gkMap map = new gkMap(md.getProperties()); models.add(map); } e.setData(models); } } final DNDEvent event = e; // 這裡做延時處理是因為onDrop定義的事件有可能阻斷拖放的完成,如:show:grid if (!onDrop.equals("")) { Scheduler.get().scheduleDeferred( new ScheduledCommand() { @Override public void execute() { evtListener.handleEvent(event); callSuperOnDragDrop(event); } }); } else if (onAfterDrop.equals("")) { callSuperOnDragDrop(event); } else { Scheduler.get().scheduleDeferred( new ScheduledCommand() { @Override public void execute() { callSuperOnDragDrop(event); evtListener2.handleEvent(event); } }); } } private void callSuperOnDragDrop(DNDEvent e) { super.onDragDrop(e); } }; target.setAllowSelfAsSource(Boolean.parseBoolean(dragSource) && Boolean.parseBoolean(dropTarget)); target.setFeedback(Feedback.valueOf(feedback.toUpperCase())); target.setAutoScroll(false); } // 初始時判斷是否需要initRow if (gridIC.isInitBlankRow() && gridIC.getGrid().getStore().getCount() == 0) { gridIC.addInitRow(); } } /** * 根據page屬性決定建立的Grid是不是分頁Grid * * @param fields * @param header * @return gkGridIC */ private gkGridIC createGridIC(final List fields, final List header) { gkGridIC grid; // 若page為true、top或bottom,則產生分頁Grid,若為false,則產生不分頁Grid if (isPage()) { grid = new gkPageGridIC(BarPosition.valueOf(page.toUpperCase())) { @Override public ColumnModel createColumnModel() { ColumnModel cm = new ColumnModel(fields); attachHeaderGroup(header, cm); return cm; } @Override protected void load(Object loadConfig, AsyncCallback callback) { if (isServerPaging()) { BasePagingLoadConfig config = (BasePagingLoadConfig) loadConfig; BasePagingLoadResult result; List datas = null; int total = 0; if (isLoadingPage()) { setPageOffset(config.getOffset()); result = new BasePagingLoadResult(datas, config.getOffset(), total); EventCenter.exec(getId(), appendPageBar(getInvokeBean()), XGrid.this, null); } else { Map info = core.getInfo(); total = (Integer) info.get(TOTALSIZE); datas = (List) info.get(DATA); result = new BasePagingLoadResult(datas, config.getOffset(), total); setLoadingPage(true); } callback.onSuccess(result); } else { super.load(loadConfig, callback); } } }; } else { grid = new gkListGridIC() { @Override public ColumnModel createColumnModel() { ColumnModel cm = new ColumnModel(fields); attachHeaderGroup(header, cm); return cm; } }; } return grid; } /** * 增加HeaderGroup * * @param header * @param cm */ private void attachHeaderGroup(List header, ColumnModel cm) { for (Iterator<HeaderGroupConfig> it = header.iterator(); it.hasNext();) { HeaderGroupConfig config = it.next(); cm.addHeaderGroup(config.getRow(), config.getColumn(), config); } } /** * 增加AggregationRow * * @param aggRow * @param grid */ private void attachAggregationRow(List aggRow, gkGridIC grid) { for (Iterator<AggregationRowConfig> it = aggRow.iterator(); it .hasNext();) { AggregationRowConfig config = it.next(); grid.getGrid().getColumnModel().addAggregationRow(config); } } /** * 按上下方向鍵觸發onRow事件 * * @param grid */ private void addKeyListener(final gkGridIC grid) { if (onRow.equals("")) { return; } final XGrid xGrid = this; grid.getGrid().addListener(Events.OnKeyUp, new Listener<GridEvent>() { @Override public void handleEvent(GridEvent ge) { int kc = ge.getKeyCode(); if (grid.getGrid().getSelectionModel() != null && (kc == KeyCodes.KEY_UP || kc == KeyCodes.KEY_DOWN)) { EventCenter.exec(getId(), onRow, xGrid, ge); } } }); } /** * 若為後端分頁的話,則需附加PageBar的資訊(limit與offset) * * @param bean * @return String */ private String appendPageBar(String bean) { StringBuffer append = new StringBuffer(bean); String[] colon = bean.split(IEventConstants.SPLIT_COLON); if (colon.length == 3) { append.append(IEventConstants.SPLIT_COMMA).append(id); append.append(".").append(IEventConstants.ATTRIB_PAGEBAR); } else if (colon.length == 2) { append.append(IEventConstants.SPLIT_COLON); append.append(id).append("."); append.append(IEventConstants.ATTRIB_PAGEBAR); } return append.toString(); } }