/****************************************************************************** * Copyright (C) 2008 Low Heng Sin All Rights Reserved. * * 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; import java.io.IOException; import java.io.StringWriter; import java.util.ArrayList; import java.util.List; import org.adempiere.webui.component.Label; import org.adempiere.webui.component.Mask; import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.desktop.IDesktop; import org.adempiere.webui.session.SessionManager; import org.compiere.util.Util; import org.zkoss.zk.au.out.AuOuter; import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.ui.AbstractComponent; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.OpenEvent; import org.zkoss.zk.ui.util.Clients; import org.zkoss.zul.Cell; import org.zkoss.zul.Div; import org.zkoss.zul.Grid; import org.zkoss.zul.LayoutRegion; import org.zkoss.zul.Popup; import org.zkoss.zul.Row; import org.zkoss.zul.Rows; import org.zkoss.zul.Space; import org.zkoss.zul.Window; import org.zkoss.zul.Window.Mode; /** * Some static UI helper methods * @author Low Heng Sin * */ public final class LayoutUtils { public static final String ON_REDRAW_EVENT = "onRedraw"; /** * @param layout */ @Deprecated(forRemoval = true, since = "11") public static void sendDeferLayoutEvent(org.zkoss.zul.Borderlayout layout, int timeout) { /* this is not required anymore */ // StringBuilder content = new StringBuilder(); // content.append("ad_deferRenderBorderLayout('") // .append(layout.getUuid()) // .append("',").append(timeout).append(");"); // // AuScript as = new AuScript(null, content.toString()); // Clients.response("deferRenderBorderLayout", as); } /** * append cls to target's sclass property * @param cls * @param target */ public static void addSclass(String cls, HtmlBasedComponent target) { final String sclass = target.getSclass(); if (!hasSclass(cls, target)) target.setSclass(sclass == null ? cls : sclass + " " + cls); } /** * * @param cls * @param target * @return true if target's sclass property contain cls */ public static boolean hasSclass(String cls, HtmlBasedComponent target) { String sclass = target.getSclass(); if (sclass == null) sclass = ""; return cls == null || ((" " + sclass + " ").indexOf(" " + cls + " ") > -1); } /** * create right align label (wrapped in div) * @param label * @return right align label (wrapped in div) */ public static Component makeRightAlign(Label label) { Div div = new Div(); div.setStyle("text-align: right"); div.appendChild(label); return div; } /** * open popup window overlapping ref component * @param ref * @param window */ public static void openPopupWindow(Component ref, Window window) { openPopupWindow(ref, window, 0); } /** * open popup window overlapping ref component * @param ref * @param window * @param delayMs */ public static void openPopupWindow(Component ref, Window window, int delayMs) { openPopupWindow(ref, window, "overlap", delayMs); } /** * open popup window relative to ref component * @param ref * @param window * @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map- */ public static void openPopupWindow(Component ref, Window window, String position) { openPopupWindow(ref, window, position, 0); } /** * open popup window relative to ref component * @param ref * @param window * @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map- * @param delayMs */ public static void openPopupWindow(Component ref, Window window, String position, int delayMs) { if (window.getPage() == null) window.setPage(ref.getPage()); StringBuilder script = new StringBuilder(); if (delayMs > 0) { script.append("setTimeout(function() { "); } script.append("idempiere.show_popup_window('#") .append(ref.getUuid()) .append("','#") .append(window.getUuid()) .append("','") .append(position) .append("'); "); script.append("zk.Widget.$('#").append(window.getUuid()).append("').focus(); "); if (delayMs > 0) { script.append(" }, ").append(delayMs).append(");"); } window.doPopup(); Clients.response("_openPopupWindow_", new AuScript(window, script.toString())); window.focus(); } /** * open overlapped window (mode overlapped) relative to ref component * @param ref * @param window * @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map- */ public static void openOverlappedWindow(Component ref, Window window, String position) { if (window.getPage() == null) window.setPage(ref.getPage()); StringBuilder script = new StringBuilder(); script.append("idempiere.show_popup_window('#") .append(ref.getUuid()) .append("','#") .append(window.getUuid()) .append("','") .append(position) .append("');"); window.doOverlapped(); Clients.response("_openPopupWindow_", new AuScript(window, script.toString())); } /** * position opened window relative to ref component * @param ref * @param window * @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map- */ public static void positionWindow(Component ref, Window window, String position) { StringBuilder script = new StringBuilder(); script.append("idempiere.show_popup_window('#") .append(ref.getUuid()) .append("','#") .append(window.getUuid()) .append("','") .append(position) .append("');"); Clients.response("_openPopupWindow_", new AuScript(window, script.toString())); } /** * position component relative to ref component * @param ref * @param component * @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map- */ public static void positionComponent(Component ref, Component component, String position) { StringBuilder script = new StringBuilder(); script.append("(function() {let w = zk('#") .append(component.getUuid()) .append("'); ") .append("let ref=zk.Widget.$('#") .append(ref.getUuid()) .append("'); ") .append("w.position(") .append("ref.$n(),'") .append(position) .append("');})()"); Clients.response("_positionComponent_", new AuScript(component, script.toString())); } /** * open embedded window relative to ref component * @param ref * @param window * @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map- */ public static void openEmbeddedWindow(Component ref, Window window, String position) { StringBuilder script = new StringBuilder(); script.append("idempiere.show_popup_window('#") .append(ref.getUuid()) .append("','#") .append(window.getUuid()) .append("','") .append(position) .append("');"); window.setVisible(true); window.setMode(Mode.EMBEDDED); Clients.response("_openEmbeddedWindow_", new AuScript(window, script.toString())); } /** * open highlighted window relative to ref component * @param ref * @param window * @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map- */ public static void openHighlightedWindow(Component ref, Window window, String position) { StringBuilder script = new StringBuilder(); script.append("idempiere.show_popup_window('#") .append(ref.getUuid()) .append("','#") .append(window.getUuid()) .append("','") .append(position) .append("');"); window.setMode(Mode.HIGHLIGHTED); Clients.response("_openHighlightedWindow_", new AuScript(window, script.toString())); } /** * Force redraw of component * @param component */ public static void redraw(AbstractComponent component) { StringWriter writer = new StringWriter(1024); try { component.redraw(writer); Clients.response(new AuOuter(component, writer.toString())); } catch (IOException e) { e.printStackTrace(); } } /** * @param component * @return true if the component and all its parent are visible */ public static boolean isReallyVisible(Component component) { if (!component.isVisible()) return false; Component parent = component.getParent(); while (parent != null) { if (!parent.isVisible()) return false; parent = parent.getParent(); } return true; } /** * Remove cls from target's sclass property * @param cls * @param target */ public static void removeSclass(String cls, HtmlBasedComponent target) { String sclass = target.getSclass(); if (Util.isEmpty(sclass)) return; sclass = " " + sclass + " "; cls = " " + cls + " "; if (sclass.indexOf(cls) >= 0) { sclass = sclass.replace(cls, " "); sclass = sclass.trim(); target.setSclass(sclass); } } public static final int OVERLAP_TAB_PANEL = 1; public static final int OVERLAP_ALL_PAGE = 2; public static final int OVERLAP_PARENT = 3; public static final int OVERLAP_SELF = 4; /** * show window with a mask below. Depends on ownModel flag, mask is shown over tabPanel, all window or over a component. *
	 * when ownModel is
	 * - {@link #OVERLAP_SELF}, mask childOfOwn. if childOfOwn doesn't implement {@link ISupportMask}, make new {@link Mask} object to mask it
	 * - {@link #OVERLAP_ALL_PAGE}, window show overlap all page
	 * - {@link #OVERLAP_TAB_PANEL}, window show overlap tabPanel
	 * - {@link #OVERLAP_PARENT}, search nearest parent of childOfOwn that implement {@link ISupportMask}, if not found use as OVERLAP_ALL_PAGE
	 * 
	 * @param window 
	 * @param childOfOwn Component below window
	 * @param ownModel OVERLAP_TAB_PANEL, OVERLAP_ALL_PAGE, OVERLAP_PARENT or OVERLAP_SELF
	 * @return when show success return ISupportMask object, use {@link ISupportMask#hideMask()} to hide mask. 
	 */
	public static ISupportMask showWindowWithMask(Window window, Component childOfOwn, int ownModel){
		ISupportMask ownWindow = null;
		// search to top parent, capture parent with interface ISupportMask
		if (ownModel == OVERLAP_SELF && ISupportMask.class.isInstance(childOfOwn)){
			ownWindow = (ISupportMask) childOfOwn;			
		}else  if (ownModel == OVERLAP_TAB_PANEL){
			ownWindow = findMaskParent (childOfOwn, Tabpanel.class);
		}else  if (ownModel == OVERLAP_PARENT){
			ownWindow = findMaskParent (childOfOwn, null);
		}else if (ownModel == OVERLAP_ALL_PAGE){
			IDesktop desktop = SessionManager.getAppDesktop();
			if (desktop != null && ISupportMask.class.isInstance(desktop)){
				ownWindow = (ISupportMask)desktop;				
			}			
		}
		
		// show window
		if (ownWindow != null){
			showWindowWithMask (window, ownWindow);
		}
		
		return ownWindow;		 
	}
	
	/**
	 * Show window in center of mask
	 * @param window
	 * @param mask
	 */
	protected static void showWindowWithMask(Window window, ISupportMask mask){
		mask.showMask();
		mask.getMaskComponent().appendChild(window);
		
		StringBuilder script = new StringBuilder("(function(){let w=zk.Widget.$('#");
		script.append(mask.getMaskComponent().getUuid()).append("');");
		script.append("let d=zk.Widget.$('#").append(window.getUuid()).append("');w.busy=d;");
		script.append("})()");
		Clients.response(new AuScript(script.toString()));
		
		LayoutUtils.openOverlappedWindow(mask.getMaskComponent(), window, "middle_center");
	}
	
	/**
	 * Show window over ownWindow with a mask, use when ownWindow doesn't implement {@link ISupportMask}
	 * @param window
	 * @param ownWindow
	 * @param mask if mask = null, make new and return it
	 * @return {@link Mask} object for hidden mask when close window.
	 */
	public static Mask showWindowWithMask(Window window, Component ownWindow, Mask mask){
		if (mask == null){
			mask = new Mask();
		}		
		ownWindow.appendChild(mask);		
		
		ownWindow.appendChild(window);		
		LayoutUtils.openOverlappedWindow(ownWindow, window, "middle_center");
		
		return mask;
	}
	
	/**
	 * find nearest parent of child component that implement {@link ISupportMask}.