iDempiere ERP. Workflow Editor 預設的編輯的欄位數為4個。 (圖 1)
在較複雜的Workflow設計圖時,可能就不敷使用。本篇文章說明如何修改Soruce code來增加欄位。
在 JIRA 提供 Patch檔或參考下列說明。
https://idempiere.atlassian.net/browse/IDEMPIERE-5431

圖 1

我們在工作列上增加一個 NumberBox,可以用來任意調整Workflow 畫布的欄位數。變更後按Refresh即可變更畫布欄位數。(圖 2)
畫布欄位數預設值為 4, 也可以透過System Configuration 來設定預設值 (關鍵字為 WORKFLOW_EDITOR_COLUMNS)

圖 2
org.adempiere.webui.apps.wf.WFNodeContainer 增加一個 setNoOfColumns method
        public int getNoOfColumns() {
		return noOfColumns;
	}

	public void setNoOfColumns(int noOfColumns) {
		this.noOfColumns = noOfColumns;
	}

org.adempiere.webui.apps.wf.WFEditor
增加一個 NumbreBox

private NumberBox columnsBox;

在initForm method中,將NumberBox 加入Toolbar
NumberBox預設值為4,

columnsBox = new NumberBox(true);
columnsBox.setValue(MSysConfig.getIntValue("WORKFLOW_EDITOR_COLUMNS", 4,Env.getAD_Client_ID(Env.getCtx())));
toolbar.appendChild(columnsBox);

在 load method 中加入

nodeContainer.setNoOfColumns(columnsBox.getValue().intValue());
for(int c = 0; c < 4; c++) {
改成下列程式碼
for(int c = 0; c < columnsBox.getValue().intValue(); c++) {

WFNodeContainer及WFEditor 完整程式如下。

/******************************************************************************
 * Copyright (C) 2008 Low Heng Sin                                            *
 * This program is free software; you can redistribute it and/or modify it    *
 * under the terms version 2 of the GNU General Public License as published   *
 * by the Free Software Foundation. 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, write to the Free Software Foundation, Inc.,    *
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.                     *
 *****************************************************************************/
package org.adempiere.webui.apps.wf;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;

import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.component.ConfirmPanel;
import org.adempiere.webui.component.ListItem;
import org.adempiere.webui.component.Listbox;
import org.adempiere.webui.component.ListboxFactory;
import org.adempiere.webui.component.NumberBox;
import org.adempiere.webui.component.Textbox;
import org.adempiere.webui.component.ToolBar;
import org.adempiere.webui.component.Window;
import org.adempiere.webui.event.DialogEvents;
import org.adempiere.webui.panel.ADForm;
import org.adempiere.webui.theme.ThemeManager;
import org.adempiere.webui.util.ZKUpdateUtil;
import org.compiere.apps.wf.WFGraphLayout;
import org.compiere.apps.wf.WFNodeWidget;
import org.compiere.model.MRole;
import org.compiere.model.MSysConfig;
import org.compiere.util.DB;
import org.compiere.util.Env;
import org.compiere.util.KeyNamePair;
import org.compiere.util.Msg;
import org.compiere.util.Util;
import org.compiere.wf.MWFNode;
import org.compiere.wf.MWFNodeNext;
import org.compiere.wf.MWorkflow;
import org.zkoss.zhtml.Table;
import org.zkoss.zhtml.Td;
import org.zkoss.zhtml.Tr;
import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.event.DropEvent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Borderlayout;
import org.zkoss.zul.Center;
import org.zkoss.zul.Div;
import org.zkoss.zul.Hbox;
import org.zkoss.zul.Label;
import org.zkoss.zul.Menupopup;
import org.zkoss.zul.North;
import org.zkoss.zul.Separator;
import org.zkoss.zul.South;
import org.zkoss.zul.Space;
import org.zkoss.zul.Toolbarbutton;
import org.zkoss.zul.Vbox;

/**
 *
 * @author Low Heng Sin
 *
 */
public class WFEditor extends ADForm {
	/**
	 * 
	 */
	private static final long serialVersionUID = 4293422396394778274L;

	private Listbox workflowList;
	private int m_workflowId = 0;
	private Toolbarbutton zoomButton;
	private Toolbarbutton refreshButton;
	private Toolbarbutton newButton;
	private Table table;
	private Center center;
	private MWorkflow m_wf;
	private WFNodeContainer nodeContainer;
	private NumberBox columnsBox;
	@Override
	protected void initForm() {
		ZKUpdateUtil.setHeight(this, "100%");
		Borderlayout layout = new Borderlayout();
		layout.setStyle("width: 100%; height: 100%; position: relative;");
		appendChild(layout);
		String sql;
		boolean isBaseLanguage = Env.isBaseLanguage(Env.getCtx(), "AD_Workflow");
		if (isBaseLanguage)
			sql = MRole.getDefault().addAccessSQL(
				"SELECT AD_Workflow_ID, Name FROM AD_Workflow WHERE IsActive='Y' ORDER BY 2",
				"AD_Workflow", MRole.SQL_NOTQUALIFIED, MRole.SQL_RO);	//	all
		else
			sql = MRole.getDefault().addAccessSQL(
					"SELECT AD_Workflow.AD_Workflow_ID, AD_Workflow_Trl.Name FROM AD_Workflow INNER JOIN AD_Workflow_Trl ON (AD_Workflow.AD_Workflow_ID=AD_Workflow_Trl.AD_Workflow_ID) "
					+ " WHERE AD_Workflow.IsActive='Y' AND AD_Workflow_Trl.AD_Language='"+Env.getAD_Language(Env.getCtx())+"' ORDER BY 2","AD_Workflow", MRole.SQL_FULLYQUALIFIED, MRole.SQL_RO);	//	all
		KeyNamePair[] pp = DB.getKeyNamePairs(sql, true);

		workflowList = ListboxFactory.newDropdownListbox();
		for (KeyNamePair knp : pp) {
			workflowList.addItem(knp);
		}
		workflowList.addEventListener(Events.ON_SELECT, this);

		North north = new North();
		layout.appendChild(north);
		ToolBar toolbar = new ToolBar();
		north.appendChild(toolbar);
		toolbar.appendChild(workflowList);
		// Zoom
		zoomButton = new Toolbarbutton();
		if (ThemeManager.isUseFontIconForImage())
			zoomButton.setIconSclass("z-icon-Zoom");
		else
			zoomButton.setImage(ThemeManager.getThemeResource("images/Zoom16.png"));
		toolbar.appendChild(zoomButton);
		zoomButton.addEventListener(Events.ON_CLICK, this);
		zoomButton.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Zoom")));
		// New Node
		newButton = new Toolbarbutton();
		if (ThemeManager.isUseFontIconForImage())
			newButton.setIconSclass("z-icon-New");
		else
			newButton.setImage(ThemeManager.getThemeResource("images/New16.png"));
		toolbar.appendChild(newButton);
		newButton.addEventListener(Events.ON_CLICK, this);
		newButton.setTooltiptext(Msg.getMsg(Env.getCtx(), "CreateNewNode"));
		// Refresh
		refreshButton = new Toolbarbutton();
		if (ThemeManager.isUseFontIconForImage())
			refreshButton.setIconSclass("z-icon-Refresh");
		else
			refreshButton.setImage(ThemeManager.getThemeResource("images/Refresh16.png"));
		toolbar.appendChild(refreshButton);
		refreshButton.addEventListener(Events.ON_CLICK, this);
		refreshButton.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Refresh")));
		ZKUpdateUtil.setHeight(north, "30px");
	
		columnsBox = new NumberBox(true);
	    columnsBox.setValue(MSysConfig.getIntValue("WORKFLOW_EDITOR_COLUMNS", 4,Env.getAD_Client_ID(Env.getCtx())));
		toolbar.appendChild(columnsBox);
		createTable();
		center = new Center();
		layout.appendChild(center);
		center.setAutoscroll(true);
		center.appendChild(table);

		ConfirmPanel confirmPanel = new ConfirmPanel(true);
		confirmPanel.addActionListener(this);
		South south = new South();
		layout.appendChild(south);
		south.appendChild(confirmPanel);
		ZKUpdateUtil.setHeight(south, "36px");
	}

	private void createTable() {
		table = new Table();
		table.setDynamicProperty("cellpadding", "0");
		table.setDynamicProperty("cellspacing", "0");
		table.setDynamicProperty("border", "none");
		table.setStyle("margin:0;padding:0");
	}

	@Override
	public void onEvent(Event event) throws Exception {
		if (event.getTarget().getId().equals(ConfirmPanel.A_CANCEL))
			this.detach();
		else if (event.getTarget().getId().equals(ConfirmPanel.A_OK))
			this.detach();
		else if (event.getTarget() == workflowList) {
			center.removeChild(table);
			createTable();
			center.appendChild(table);
			ListItem item = workflowList.getSelectedItem();
			KeyNamePair knp = item != null ? item.toKeyNamePair() : null;
			if (knp != null && knp.getKey() > 0) {
				load(knp.getKey(), true);
			}
		}
		else if (event.getTarget() == zoomButton) {
			if (workflowList.getSelectedIndex() > 0)
				zoom();
		}
		else if (event.getTarget() == refreshButton) {
			if (workflowList.getSelectedIndex() > 0)
				reload(m_workflowId, true);
		}
		else if (event.getTarget() == newButton) {
			if (workflowList.getSelectedIndex() > 0)
				createNewNode();
		}
		else if (event.getTarget() instanceof WFPopupItem) {
			WFPopupItem item = (WFPopupItem) event.getTarget();
			item.execute(this);
		}
		else if (event.getName().equals(Events.ON_DROP)) {
			DropEvent dropEvent = (DropEvent) event;
			Integer AD_WF_Node_ID = (Integer) dropEvent.getDragged().getAttribute("AD_WF_Node_ID");
			Integer xPosition = (Integer) event.getTarget().getAttribute("Node.XPosition");
			Integer yPosition = (Integer) event.getTarget().getAttribute("Node.YPosition");
			if (AD_WF_Node_ID != null) {
				WFNodeWidget widget = (WFNodeWidget) nodeContainer.getGraphScene().findWidget(AD_WF_Node_ID);
				if (widget != null) {
					widget.getModel().setXPosition(xPosition);
					widget.getModel().setYPosition(yPosition);
					widget.getModel().saveEx();
					reload(m_workflowId, true);
				}
			}
		}
	}

	private void createNewNode() {
		String nameLabel = Msg.getElement(Env.getCtx(), MWFNode.COLUMNNAME_Name);
		String title = Msg.getMsg(Env.getCtx(), "CreateNewNode");
		final Window w = new Window();
		w.setTitle(title);
		Vbox vbox = new Vbox();
		w.appendChild(vbox);
		vbox.appendChild(new Separator());
		Hbox hbox = new Hbox();
		hbox.appendChild(new Label(nameLabel));
		hbox.appendChild(new Space());
		final Textbox text = new Textbox();
		hbox.appendChild(text);
		vbox.appendChild(hbox);
		vbox.appendChild(new Separator());
		final ConfirmPanel panel = new ConfirmPanel(true, false, false, false, false, false, false);
		vbox.appendChild(panel);
		panel.addActionListener(Events.ON_CLICK, new EventListener<Event>() {

			public void onEvent(Event event) throws Exception {
				if (event.getTarget() == panel.getButton(ConfirmPanel.A_CANCEL)) {
					text.setText("");
				}
				w.onClose();
			}
		});
		
		ZKUpdateUtil.setWidth(w, "250px");
		w.setBorder("normal");
		w.setPage(this.getPage());
		w.addEventListener(DialogEvents.ON_WINDOW_CLOSE, new EventListener<Event>() {

			@Override
			public void onEvent(Event event) throws Exception {
				String name = text.getText();
				if (name != null && name.length() > 0)
				{
					int AD_Client_ID = Env.getAD_Client_ID(Env.getCtx());
					MWFNode node = new MWFNode(m_wf, name, name);
					node.setClientOrg(AD_Client_ID, 0);
					node.saveEx();
					reload(m_wf.getAD_Workflow_ID(), true);
				}
			}
		});
		w.doHighlighted();				
	}

	protected void reload(int workflowId, boolean reread) {
		center.removeChild(table);
		createTable();
		center.appendChild(table);
		load(workflowId, reread);
	}

	private void load(int workflowId, boolean reread) {
		//	Get Workflow
		m_wf = MWorkflow.get(Env.getCtx(), workflowId);
		m_workflowId = workflowId;
		nodeContainer = new WFNodeContainer();
		nodeContainer.setNoOfColumns(columnsBox.getValue().intValue());
		nodeContainer.setWorkflow(m_wf);
		
		if (reread) {
			m_wf.reloadNodes();
		}

		//	Add Nodes for Paint
		MWFNode[] nodes = m_wf.getNodes(true, Env.getAD_Client_ID(Env.getCtx()));
		List<Integer> added = new ArrayList<Integer>();
		for (int i = 0; i < nodes.length; i++)
		{
			if (!added.contains(nodes[i].getAD_WF_Node_ID()))
				nodeContainer.addNode(nodes[i]);
		}
		
		//  Add lines
		for (int i = 0; i < nodes.length; i++)
		{
			MWFNodeNext[] nexts = nodes[i].getTransitions(Env.getAD_Client_ID(Env.getCtx()));
			for (int j = 0; j < nexts.length; j++)
			{
				nodeContainer.addEdge(nexts[j]);
			}
		}

		Dimension dimension = nodeContainer.getDimension();
		BufferedImage bi = new BufferedImage (dimension.width, dimension.height, BufferedImage.TYPE_INT_ARGB);
		Graphics2D graphics = bi.createGraphics();
		nodeContainer.validate(graphics);
		nodeContainer.paint(graphics);

		try {
			int row = nodeContainer.getRowCount();
			for(int i = 0; i < row+1; i++) {
				Tr tr = new Tr();
				table.appendChild(tr);
				for(int c = 0; c < columnsBox.getValue().intValue(); c++) {
					BufferedImage t = new BufferedImage(WFGraphLayout.COLUMN_WIDTH, WFGraphLayout.ROW_HEIGHT, BufferedImage.TYPE_INT_ARGB);
					Graphics2D tg = t.createGraphics();
					Td td = new Td();
					td.setStyle("border: 1px dotted lightgray");
					tr.appendChild(td);
					
					if (i < row)
					{
						int x = c * WFGraphLayout.COLUMN_WIDTH;
						int y = i * WFGraphLayout.ROW_HEIGHT;

						tg.drawImage(bi.getSubimage(x, y, WFGraphLayout.COLUMN_WIDTH, WFGraphLayout.ROW_HEIGHT), 0, 0, null);
						org.zkoss.zul.Image image = new org.zkoss.zul.Image();
						image.setContent(t);
						td.appendChild(image);
						String imgStyle = "border:none;margin:0;padding:0";

						WFNodeWidget widget = nodeContainer.findWidget(i+1, c+1);
						if (widget != null)
						{
							MWFNode node = widget.getModel();
							if (node.getHelp(true) != null) {
								image.setTooltiptext(node.getHelp(true));
							}
							image.setAttribute("AD_WF_Node_ID", node.getAD_WF_Node_ID());
							image.addEventListener(Events.ON_CLICK, new EventListener<Event>() {

								public void onEvent(Event event) throws Exception {
									showNodeMenu(event.getTarget());
								}
							});
							image.setDraggable("WFNode");
							imgStyle = imgStyle + ";cursor:pointer";
						}
						else
						{
							image.setDroppable("WFNode");
							image.addEventListener(Events.ON_DROP, this);
							image.setAttribute("Node.XPosition", c+1);
							image.setAttribute("Node.YPosition", i+1);
						}
						image.setStyle(imgStyle);
					}
					else
					{
						Div div = new Div();
						ZKUpdateUtil.setWidth(div, (WFGraphLayout.COLUMN_WIDTH) + "px");
						ZKUpdateUtil.setHeight(div, (WFGraphLayout.ROW_HEIGHT) + "px");
						div.setAttribute("Node.XPosition", c+1);
						div.setAttribute("Node.YPosition", i+1);
						div.setDroppable("WFNode");
						div.addEventListener(Events.ON_DROP, this);
						td.appendChild(div);
					}

					tg.dispose();
				}
			}

		} catch (Exception e) {
			logger.log(Level.SEVERE, e.getLocalizedMessage(), e);
		}

	}

	protected void showNodeMenu(Component target) {
		Integer AD_WF_Node_ID = (Integer) target.getAttribute("AD_WF_Node_ID");
		if (AD_WF_Node_ID != null) {
			WFNodeWidget widget = (WFNodeWidget) nodeContainer.getGraphScene().findWidget(AD_WF_Node_ID);
			if (widget != null) {
				MWFNode node = widget.getModel();
				Menupopup popupMenu = new Menupopup();
				if (node.getAD_Client_ID() == Env.getAD_Client_ID(Env.getCtx()))
				{
					// Zoom
					addMenuItem(popupMenu, Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Zoom")), node, WFPopupItem.WFPOPUPITEM_ZOOM);
					// Properties
					addMenuItem(popupMenu, Msg.getMsg(Env.getCtx(), "Properties"), node, WFPopupItem.WFPOPUPITEM_PROPERTIES);
					// Delete node
					String title = Msg.getMsg(Env.getCtx(), "DeleteNode") +
						": " + node.getName(true);
					addMenuItem(popupMenu, title, node, WFPopupItem.WFPOPUPITEM_DELETENODE);
				}
				MWFNode[] nodes = m_wf.getNodes(true, Env.getAD_Client_ID(Env.getCtx()));
				MWFNodeNext[] lines = node.getTransitions(Env.getAD_Client_ID(Env.getCtx()));
				//	Add New Line
				for (MWFNode nn : nodes)
				{
					if (nn.getAD_WF_Node_ID() == node.getAD_WF_Node_ID())
						continue;	//	same
					if (nn.getAD_WF_Node_ID() == node.getAD_Workflow().getAD_WF_Node_ID())
						continue;	//	don't add line to starting node
					boolean found = false;
					for (MWFNodeNext line : lines)
					{
						if (nn.getAD_WF_Node_ID() == line.getAD_WF_Next_ID())
						{
							found = true; // line already exists
							break;
						}
					}
					if (!found) {
						// Check that inverse line doesn't exist
						for (MWFNodeNext revline : nn.getTransitions(Env.getAD_Client_ID(Env.getCtx()))) {
							if (node.getAD_WF_Node_ID() == revline.getAD_WF_Next_ID())
							{
								found = true; // inverse line already exists
								break;
							}
						}
					}
					if (!found)
					{
						String title = Msg.getMsg(Env.getCtx(), "AddLine")
							+ ": " + node.getName(true) + " -> " + nn.getName(true);
						addMenuItem(popupMenu, title, node, nn.getAD_WF_Node_ID());
					}
				}
				//	Delete Lines
				for (MWFNodeNext line : lines)
				{
					if (line.getAD_Client_ID() != Env.getAD_Client_ID(Env.getCtx()))
						continue;
					MWFNode next = MWFNode.get(Env.getCtx(), line.getAD_WF_Next_ID());
					String title = Msg.getMsg(Env.getCtx(), "DeleteLine")
						+ ": " + node.getName(true) + " -> " + next.getName(true);
					addMenuItem(popupMenu, title, line);
				}
				popupMenu.setPage(target.getPage());
				popupMenu.open(target);
			}

		}
	}

	/**
	 * 	Zoom to WorkFlow
	 */
	private void zoom()
	{
		if (m_workflowId > 0) {
			AEnv.zoom(MWorkflow.Table_ID, m_workflowId);
		}
	}	//	zoom

	/**
	 * 	Add Menu Item to - add new line to node
	 *	@param menu base menu
	 *	@param title title
	 */
	private void addMenuItem (Menupopup menu, String title, MWFNode node, int AD_WF_NodeTo_ID)
	{
		WFPopupItem item = new WFPopupItem (title, node, AD_WF_NodeTo_ID);
		menu.appendChild(item);
		item.addEventListener(Events.ON_CLICK, this);
	}	//	addMenuItem

	/**
	 * 	Add Menu Item to - delete line
	 *	@param menu base menu
	 *	@param title title
	 */
	private void addMenuItem (Menupopup menu, String title, MWFNodeNext line)
	{
		WFPopupItem item = new WFPopupItem (title, line);
		menu.appendChild(item);
		item.addEventListener(Events.ON_CLICK, this);
	}	//	addMenuItem
}
/******************************************************************************
 * Copyright (C) 2008 Low Heng Sin                                            *
 * This program is free software; you can redistribute it and/or modify it    *
 * under the terms version 2 of the GNU General Public License as published   *
 * by the Free Software Foundation. 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, write to the Free Software Foundation, Inc.,    *
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.                     *
 *****************************************************************************/
package org.adempiere.webui.apps.wf;

import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.util.HashMap;
import java.util.Map;

import org.compiere.apps.wf.WFGraphLayout;
import org.compiere.apps.wf.WFNodeWidget;
import org.compiere.apps.wf.WorkflowGraphScene;
import org.compiere.model.X_AD_Workflow;
import org.compiere.util.CLogger;
import org.compiere.wf.MWFNode;
import org.compiere.wf.MWFNodeNext;
import org.compiere.wf.MWorkflow;
import org.netbeans.api.visual.graph.GraphScene;
import org.netbeans.api.visual.graph.layout.GraphLayout;
import org.netbeans.api.visual.layout.LayoutFactory;
import org.netbeans.api.visual.layout.SceneLayout;

/**
 *
 * @author Low Heng Sin
 *
 */
public class WFNodeContainer
{
	/**	Logger			*/
	@SuppressWarnings("unused")
	private static final CLogger	log = CLogger.getCLogger(WFNodeContainer.class);

	/** The Workflow		*/
	private MWorkflow	m_wf = null;

	private int currentRow = 1;
	private int currentColumn = 0;
	private int noOfColumns = 8;
	private int maxColumn = 0;
	private int rowCount = 0;

	private WorkflowGraphScene graphScene = new WorkflowGraphScene();

	private Map<Integer, Integer[]> matrix = null;

	/**
	 * 	WFContentPanel
	 */
	public WFNodeContainer ()
	{
		matrix = new HashMap<Integer, Integer[]>();
	}	//	WFContentPanel

	/**
	 * 	Set Workflow
	 *	@param wf workflow
	 */
	public void setWorkflow (MWorkflow wf)
	{
		m_wf = wf;
	}	//	setWorkflow


	public int getNoOfColumns() {
		return noOfColumns;
	}

	public void setNoOfColumns(int noOfColumns) {
		this.noOfColumns = noOfColumns;
	}

	/**
	 * 	Remove All and their listeners
	 */
	public void removeAll ()
	{
		graphScene = new WorkflowGraphScene();
		currentColumn = 0;
		currentRow = 1;
		matrix = new HashMap<Integer, Integer[]>();
	}	//	removeAll


	public void addNode(MWFNode node) {
		int oldRow = currentRow;
		int oldColumn = currentColumn;
		if (node.getXPosition() > 0 && node.getYPosition() > 0) {
			currentColumn = node.getXPosition();
			currentRow = node.getYPosition();
			if (currentColumn > noOfColumns) {
				currentColumn = 1;
				currentRow ++;
			}
		} else if (currentColumn == noOfColumns) {
			currentColumn = 1;
			if (m_wf.getWorkflowType().equals(X_AD_Workflow.WORKFLOWTYPE_General)) {
				currentRow++;
			} else {
				currentRow = currentRow + 2;
			}
		} else {
			if (m_wf.getWorkflowType().equals(X_AD_Workflow.WORKFLOWTYPE_General) || currentColumn == 0) {
				currentColumn++;
			} else {
				currentColumn = currentColumn + 2;
				if (currentColumn > noOfColumns) {
					currentColumn = 1;
					currentRow = currentRow + 2;
				}
			}
		}

		if (currentRow > rowCount) {
			rowCount = currentRow;
		}

		Integer[] nodes = matrix.get(currentRow);
		if (nodes == null) {
			nodes = new Integer[noOfColumns];
			matrix.put(currentRow, nodes);
		} else {
			//detect collision
			while (nodes[currentColumn - 1] != null) {
				if (nodes[currentColumn - 1] == node.getAD_WF_Node_ID()) {
					break;
				} else if (currentColumn == noOfColumns) {
					currentColumn = 1;
					currentRow ++;
					nodes = matrix.get(currentRow);
					if (nodes == null) {
						nodes = new Integer[noOfColumns];
						matrix.put(currentRow, nodes);
					}
				} else {
					currentColumn ++;
				}
			}
		}

		WFNodeWidget w = (WFNodeWidget) graphScene.addNode(node.getAD_WF_Node_ID());
		w.setColumn(currentColumn);
		w.setRow(currentRow);

		nodes[currentColumn - 1] = node.getAD_WF_Node_ID();
		if (currentColumn > maxColumn) {
			maxColumn = currentColumn;
		}

		if (currentRow < oldRow) {
			currentRow = oldRow;
			currentColumn = oldColumn;
		} else if ( currentRow == oldRow && currentColumn < oldColumn) {
			currentColumn = oldColumn;
		}
	}

	public void addEdge(MWFNodeNext edge) {
		graphScene.addEdge(edge);
		graphScene.setEdgeSource(edge, edge.getAD_WF_Node_ID());
		graphScene.setEdgeTarget(edge, edge.getAD_WF_Next_ID());
	}

	/**
	 *
	 * @param row row #, starting from 1
	 * @param column column #, starting from 1
	 * @return WFNodeWidget
	 */
	public WFNodeWidget findWidget(int row, int column) {
		WFNodeWidget widget = null;
		Integer[] nodeRow = matrix.get(row);
		if (nodeRow != null && column <= nodeRow.length) {
			widget = (WFNodeWidget) graphScene.findWidget(nodeRow[column - 1]);
		}
		return widget;
	}

	/**
	 * 	Get Bounds of WF Node Icon
	 * 	@param AD_WF_Node_ID node id
	 * 	@return bounds of node with ID or null
	 */
	public Rectangle findBounds (int AD_WF_Node_ID)
	{
		WFNodeWidget widget = (WFNodeWidget) graphScene.findWidget(AD_WF_Node_ID);
		if (widget == null)
			return null;

		Point p = widget.getPreferredLocation();
		return new Rectangle(p.x, p.y, WFNodeWidget.NODE_WIDTH, WFNodeWidget.NODE_HEIGHT);
	}	//	findBounds

	public Dimension getDimension()
	{
		return new Dimension(noOfColumns * WFGraphLayout.COLUMN_WIDTH, currentRow * WFGraphLayout.ROW_HEIGHT);
	}

	public void validate(Graphics2D graphics)
	{
		GraphLayout<Integer, MWFNodeNext> graphLayout = new WFGraphLayout();
		graphLayout.setAnimated(false);
		SceneLayout sceneGraphLayout = LayoutFactory.createSceneGraphLayout (graphScene, graphLayout);
		sceneGraphLayout.invokeLayoutImmediately();

		graphScene.validate(graphics);
	}


	public void paint(Graphics2D graphics) {
		graphScene.paint(graphics);
	}

	public int getRowCount() {
		return rowCount;
	}

	public int getCurrentRow() {
		return currentRow;
	}

	public int getCurrentColumn() {
		return currentColumn;
	}

	public int getColumnCount() {
		return noOfColumns;
	}

	public int getMaxColumnWithNode() {
		return maxColumn;
	}

	public GraphScene<Integer, MWFNodeNext> getGraphScene() {
		return graphScene;
	}
}	//	WFContentPanel

By Ray Lee (System Analyst)

iDempeire ERP Contributor, 經濟部中小企業處財務管理顧問 李寶瑞

Leave a Reply

Your email address will not be published. Required fields are marked *