IDEMPIERE-5570 Zk: Improve readability of code (#1666)

This commit is contained in:
hengsin 2023-02-09 21:39:59 +08:00 committed by GitHub
parent 801d1fb898
commit 19459be87b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
53 changed files with 2647 additions and 736 deletions

View File

@ -53,11 +53,13 @@ public class AtmosphereServerPush implements ServerPush {
private static final String ON_ACTIVATE_DESKTOP = "onActivateDesktop"; private static final String ON_ACTIVATE_DESKTOP = "onActivateDesktop";
/** default timeout of of 2 minutes **/
public static final int DEFAULT_TIMEOUT = 1000 * 60 * 2; public static final int DEFAULT_TIMEOUT = 1000 * 60 * 2;
private final AtomicReference<Desktop> desktop = new AtomicReference<Desktop>(); private final AtomicReference<Desktop> desktop = new AtomicReference<Desktop>();
private final Logger log = LoggerFactory.getLogger(this.getClass()); private final Logger log = LoggerFactory.getLogger(this.getClass());
/** asynchronous request reference as AtmosphereResource **/
private final AtomicReference<AtmosphereResource> resource = new AtomicReference<AtmosphereResource>(); private final AtomicReference<AtmosphereResource> resource = new AtomicReference<AtmosphereResource>();
private final int timeout; private final int timeout;
@ -66,6 +68,9 @@ public class AtmosphereServerPush implements ServerPush {
private final Object _mutex = new Object(); private final Object _mutex = new Object();
private List<Schedule<Event>> schedules = new ArrayList<>(); private List<Schedule<Event>> schedules = new ArrayList<>();
/**
* default constructor
*/
public AtmosphereServerPush() { public AtmosphereServerPush() {
String timeoutString = Library.getProperty("fi.jawsy.jawwa.zk.atmosphere.timeout"); String timeoutString = Library.getProperty("fi.jawsy.jawwa.zk.atmosphere.timeout");
if (timeoutString == null || timeoutString.trim().length() == 0) { if (timeoutString == null || timeoutString.trim().length() == 0) {
@ -120,10 +125,19 @@ public class AtmosphereServerPush implements ServerPush {
return true; return true;
} }
/**
* release current AtmosphereResource
* @param resource
*/
public void clearResource(AtmosphereResource resource) { public void clearResource(AtmosphereResource resource) {
this.resource.compareAndSet(resource, null); this.resource.compareAndSet(resource, null);
} }
/**
* commit/resume response for current AtmosphereResource
* @return true if resource is available
* @throws IOException
*/
private boolean commitResponse() throws IOException { private boolean commitResponse() throws IOException {
AtmosphereResource resource = this.resource.getAndSet(null); AtmosphereResource resource = this.resource.getAndSet(null);
if (resource != null) { if (resource != null) {
@ -224,6 +238,10 @@ public class AtmosphereServerPush implements ServerPush {
startClientPush(desktop); startClientPush(desktop);
} }
/**
* start serverpush request at client side
* @param desktop
*/
private void startClientPush(Desktop desktop) { private void startClientPush(Desktop desktop) {
Clients.response("jawwa.atmosphere.serverpush", new AuScript(null, "jawwa.atmosphere.startServerPush('" + desktop.getId() + "', " + timeout Clients.response("jawwa.atmosphere.serverpush", new AuScript(null, "jawwa.atmosphere.startServerPush('" + desktop.getId() + "', " + timeout
+ ");")); + ");"));
@ -256,6 +274,10 @@ public class AtmosphereServerPush implements ServerPush {
} }
} }
/**
* handle asynchronous server push request (long polling request)
* @param resource
*/
public void onRequest(AtmosphereResource resource) { public void onRequest(AtmosphereResource resource) {
if (log.isTraceEnabled()) { if (log.isTraceEnabled()) {
log.trace(resource.transport().name()); log.trace(resource.transport().name());
@ -267,6 +289,7 @@ public class AtmosphereServerPush implements ServerPush {
return; return;
} }
//suspend request for server push event
if (!resource.isSuspended()) { if (!resource.isSuspended()) {
//browser default timeout is 2 minutes //browser default timeout is 2 minutes
resource.suspend(5, TimeUnit.MINUTES); resource.suspend(5, TimeUnit.MINUTES);

View File

@ -1,18 +1,59 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* 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 2 *
* 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, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package fi.jawsy.jawwa.zk.atmosphere; package fi.jawsy.jawwa.zk.atmosphere;
/**
* class to hold two value, left and right
* @author hengsin
*
* @param <L>
* @param <R>
*/
public class Either<L, R> { public class Either<L, R> {
private L left; private L left;
private R right; private R right;
/**
* @param l left value
* @param r right value
*/
public Either(L l, R r) { public Either(L l, R r) {
left = l; left = l;
right = r; right = r;
} }
/**
* @return left value
*/
public L getLeftValue() { public L getLeftValue() {
return left; return left;
} }
/**
* @return right value
*/
public R getRightValue() { public R getRightValue() {
return right; return right;
} }

View File

@ -51,6 +51,12 @@ public class ZkAtmosphereHandler implements AtmosphereHandler {
public void destroy() { public void destroy() {
} }
/**
* Get Zk {@link Desktop} instance from session by desktop id
* @param session
* @param dtid desktop id
* @return left as error message and right as {@link Desktop} reference
*/
private Either<String, Desktop> getDesktop(Session session, String dtid) { private Either<String, Desktop> getDesktop(Session session, String dtid) {
if (session.getWebApp() instanceof WebAppCtrl) { if (session.getWebApp() instanceof WebAppCtrl) {
WebAppCtrl webAppCtrl = (WebAppCtrl) session.getWebApp(); WebAppCtrl webAppCtrl = (WebAppCtrl) session.getWebApp();
@ -64,11 +70,21 @@ public class ZkAtmosphereHandler implements AtmosphereHandler {
return new Either<String, Desktop>("Webapp does not implement WebAppCtrl", null); return new Either<String, Desktop>("Webapp does not implement WebAppCtrl", null);
} }
/**
* Get Zk {@link Desktop} id from HttpServletRequest parameter (dtid)
* @param request
* @return left as desktop id and right as error message
*/
private Either<String, String> getDesktopId(HttpServletRequest request) { private Either<String, String> getDesktopId(HttpServletRequest request) {
String dtid = request.getParameter("dtid"); String dtid = request.getParameter("dtid");
return new Either<String, String>(dtid, DESKTOP_NOT_FOUND); return new Either<String, String>(dtid, DESKTOP_NOT_FOUND);
} }
/**
* Get {@link AtmosphereServerPush} instance from {@link AtmosphereResource}
* @param resource {@link AtmosphereResource}
* @return left as error message and right as AtmosphereServerPush reference
*/
private Either<String, AtmosphereServerPush> getServerPush(AtmosphereResource resource) { private Either<String, AtmosphereServerPush> getServerPush(AtmosphereResource resource) {
AtmosphereRequest request = resource.getRequest(); AtmosphereRequest request = resource.getRequest();
@ -96,6 +112,11 @@ public class ZkAtmosphereHandler implements AtmosphereHandler {
} }
} }
/**
* Get {@link AtmosphereServerPush} instance from {@link Desktop}
* @param desktop
* @return left as error message and right as {@link AtmosphereServerPush} reference
*/
private Either<String, AtmosphereServerPush> getServerPush(Desktop desktop) { private Either<String, AtmosphereServerPush> getServerPush(Desktop desktop) {
if (desktop instanceof DesktopCtrl) { if (desktop instanceof DesktopCtrl) {
DesktopCtrl desktopCtrl = (DesktopCtrl) desktop; DesktopCtrl desktopCtrl = (DesktopCtrl) desktop;
@ -109,6 +130,12 @@ public class ZkAtmosphereHandler implements AtmosphereHandler {
return new Either<String, AtmosphereServerPush>("Desktop does not implement DesktopCtrl", null); return new Either<String, AtmosphereServerPush>("Desktop does not implement DesktopCtrl", null);
} }
/**
* Get {@link Session} from request
* @param resource
* @param request
* @return left as error message and right as Session reference
*/
private Either<String, Session> getSession(AtmosphereResource resource, HttpServletRequest request) { private Either<String, Session> getSession(AtmosphereResource resource, HttpServletRequest request) {
Session session = WebManager.getSession(resource.getAtmosphereConfig().getServletContext(), request, false); Session session = WebManager.getSession(resource.getAtmosphereConfig().getServletContext(), request, false);
if (session == null) { if (session == null) {

View File

@ -90,23 +90,31 @@ import org.zkoss.zul.Window;
*/ */
public class AdempiereWebUI extends Window implements EventListener<Event>, IWebClient public class AdempiereWebUI extends Window implements EventListener<Event>, IWebClient
{ {
/** {@link Session} attribute to hold current login user id value **/
public static final String CHECK_AD_USER_ID_ATTR = "Check_AD_User_ID";
/** Boolean attribute to indicate the HTTP session of a Desktop have been invalidated **/
public static final String DESKTOP_SESSION_INVALIDATED_ATTR = "DesktopSessionInvalidated"; public static final String DESKTOP_SESSION_INVALIDATED_ATTR = "DesktopSessionInvalidated";
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = -6725805283410008847L; private static final long serialVersionUID = -6725805283410008847L;
/** {@link Desktop} attribute to hold {@link IDesktop} reference **/
public static final String APPLICATION_DESKTOP_KEY = "application.desktop"; public static final String APPLICATION_DESKTOP_KEY = "application.desktop";
public static String APP_NAME = null; public static String APP_NAME = null;
public static final String UID = "1.0.0"; public static final String UID = "1.0.0";
/** Attribute for widget instance name, use for Selenium test **/
public static final String WIDGET_INSTANCE_NAME = "instanceName"; public static final String WIDGET_INSTANCE_NAME = "instanceName";
/** login and role selection window **/
private WLogin loginDesktop; private WLogin loginDesktop;
/** client info from browser **/
private ClientInfo clientInfo = new ClientInfo(); private ClientInfo clientInfo = new ClientInfo();
private String langSession; private String langSession;
@ -119,16 +127,23 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
private static final CLogger logger = CLogger.getCLogger(AdempiereWebUI.class); private static final CLogger logger = CLogger.getCLogger(AdempiereWebUI.class);
@Deprecated(forRemoval = true, since = "11")
public static final String EXECUTION_CARRYOVER_SESSION_KEY = "execution.carryover"; public static final String EXECUTION_CARRYOVER_SESSION_KEY = "execution.carryover";
/** Session attribute to hold {@link ClientInfo} reference **/
private static final String CLIENT_INFO = "client.info"; private static final String CLIENT_INFO = "client.info";
/** the use of event thread have been deprecated, this should always be false **/
private static boolean eventThreadEnabled = false; private static boolean eventThreadEnabled = false;
private ConcurrentMap<String, String[]> m_URLParameters; private ConcurrentMap<String, String[]> m_URLParameters;
/** Login completed event **/
private static final String ON_LOGIN_COMPLETED = "onLoginCompleted"; private static final String ON_LOGIN_COMPLETED = "onLoginCompleted";
/**
* default constructor
*/
public AdempiereWebUI() public AdempiereWebUI()
{ {
this.setVisible(false); this.setVisible(false);
@ -140,8 +155,12 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
this.addEventListener(ON_LOGIN_COMPLETED, this); this.addEventListener(ON_LOGIN_COMPLETED, this);
} }
/**
* Handle onCreate event from index.zul, don't call this directly.
*/
public void onCreate() public void onCreate()
{ {
//handle ping request
String ping = Executions.getCurrent().getHeader("X-PING"); String ping = Executions.getCurrent().getHeader("X-PING");
if (!Util.isEmpty(ping, true)) if (!Util.isEmpty(ping, true))
{ {
@ -183,6 +202,9 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
eventThreadEnabled = Executions.getCurrent().getDesktop().getWebApp().getConfiguration().isEventThreadEnabled(); eventThreadEnabled = Executions.getCurrent().getDesktop().getWebApp().getConfiguration().isEventThreadEnabled();
} }
/**
* perform clean up for ping request
*/
private void cleanupForPing() { private void cleanupForPing() {
final Desktop desktop = Executions.getCurrent().getDesktop(); final Desktop desktop = Executions.getCurrent().getDesktop();
final WebApp wapp = desktop.getWebApp(); final WebApp wapp = desktop.getWebApp();
@ -208,10 +230,16 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
}, 1, TimeUnit.SECONDS); }, 1, TimeUnit.SECONDS);
} }
/**
* Handle onOK (enter key) event
*/
public void onOk() public void onOk()
{ {
} }
/**
* Handle onCancel(escape key) event
*/
public void onCancel() public void onCancel()
{ {
} }
@ -219,6 +247,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
/* (non-Javadoc) /* (non-Javadoc)
* @see org.adempiere.webui.IWebClient#loginCompleted() * @see org.adempiere.webui.IWebClient#loginCompleted()
*/ */
@Override
public void loginCompleted() public void loginCompleted()
{ {
if (loginDesktop != null) if (loginDesktop != null)
@ -280,7 +309,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
mSession.saveEx(); mSession.saveEx();
} }
currSess.setAttribute("Check_AD_User_ID", Env.getAD_User_ID(ctx)); currSess.setAttribute(CHECK_AD_USER_ID_ATTR, Env.getAD_User_ID(ctx));
//enable full interface, relook into this when doing preference //enable full interface, relook into this when doing preference
Env.setContext(ctx, Env.SHOW_TRANSLATION, true); Env.setContext(ctx, Env.SHOW_TRANSLATION, true);
@ -298,14 +327,14 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
keyListener.setCtrlKeys("@a@c@d@e@f@h@l@m@n@o@p@q@r@s@t@w@x@z@#left@#right@#up@#down@#home@#end#enter^u@u@#pgdn@#pgup$#f2^#f2"); keyListener.setCtrlKeys("@a@c@d@e@f@h@l@m@n@o@p@q@r@s@t@w@x@z@#left@#right@#up@#down@#home@#end#enter^u@u@#pgdn@#pgup$#f2^#f2");
keyListener.setAutoBlur(false); keyListener.setAutoBlur(false);
//create new desktop //create IDesktop instance
IDesktop appDesktop = createDesktop(); IDesktop appDesktop = createDesktop();
appDesktop.setClientInfo(clientInfo); appDesktop.setClientInfo(clientInfo);
appDesktop.createPart(this.getPage()); appDesktop.createPart(this.getPage());
this.getPage().getDesktop().setAttribute(APPLICATION_DESKTOP_KEY, new WeakReference<IDesktop>(appDesktop)); this.getPage().getDesktop().setAttribute(APPLICATION_DESKTOP_KEY, new WeakReference<IDesktop>(appDesktop));
appDesktop.getComponent().getRoot().addEventListener(Events.ON_CLIENT_INFO, this); appDesktop.getComponent().getRoot().addEventListener(Events.ON_CLIENT_INFO, this);
//track browser tab per session //track browser tab per session, 1 browser tab = 1 zk Desktop
SessionContextListener.addDesktopId(mSession.getAD_Session_ID(), getPage().getDesktop().getId()); SessionContextListener.addDesktopId(mSession.getAD_Session_ID(), getPage().getDesktop().getId());
//ensure server push is on //ensure server push is on
@ -330,7 +359,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
Env.setContext(ctx, Env.IS_CAN_APPROVE_OWN_DOC, MRole.getDefault().isCanApproveOwnDoc()); Env.setContext(ctx, Env.IS_CAN_APPROVE_OWN_DOC, MRole.getDefault().isCanApproveOwnDoc());
Clients.response(new AuScript("zAu.cmd0.clearBusy()")); Clients.response(new AuScript("zAu.cmd0.clearBusy()"));
//add dynamic style //add dynamic style for AD tab
StringBuilder cssContent = new StringBuilder(); StringBuilder cssContent = new StringBuilder();
cssContent.append(".adtab-form-borderlayout .z-south-collapsed:before { "); cssContent.append(".adtab-form-borderlayout .z-south-collapsed:before { ");
cssContent.append("content: \""); cssContent.append("content: \"");
@ -347,6 +376,9 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
processParameters(); processParameters();
} }
/**
* process URL parameters from browser
*/
private void processParameters() { private void processParameters() {
String action = getPrmString("Action"); String action = getPrmString("Action");
if ("Zoom".equalsIgnoreCase(action)) { if ("Zoom".equalsIgnoreCase(action)) {
@ -368,6 +400,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
m_URLParameters = null; m_URLParameters = null;
} }
/**
* @param prm parameter name
* @return string value for parameter
*/
private String getPrmString(String prm) { private String getPrmString(String prm) {
String retValue = ""; String retValue = "";
if (m_URLParameters != null) { if (m_URLParameters != null) {
@ -378,6 +414,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
return retValue; return retValue;
} }
/**
* @param prm parameter name
* @return integer value for parameter
*/
private int getPrmInt(String prm) { private int getPrmInt(String prm) {
int retValue = 0; int retValue = 0;
String str = getPrmString(prm); String str = getPrmString(prm);
@ -398,6 +438,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
return keyListener; return keyListener;
} }
/**
* Create IDesktop instance. Default is {@link DefaultDesktop}
* @return {@link IDesktop}
*/
private IDesktop createDesktop() private IDesktop createDesktop()
{ {
IDesktop appDesktop = null; IDesktop appDesktop = null;
@ -424,6 +468,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
/* (non-Javadoc) /* (non-Javadoc)
* @see org.adempiere.webui.IWebClient#logout() * @see org.adempiere.webui.IWebClient#logout()
*/ */
@Override
public void logout() public void logout()
{ {
final Desktop desktop = Executions.getCurrent().getDesktop(); final Desktop desktop = Executions.getCurrent().getDesktop();
@ -447,6 +492,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
} }
} }
/**
* after logout action for Session
* @param session
*/
private void afterLogout(final Session session) { private void afterLogout(final Session session) {
try { try {
((SessionCtrl)session).onDestroyed(); ((SessionCtrl)session).onDestroyed();
@ -457,7 +506,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
} }
/** /**
* Perform logout after user close a browser tab without first logging out * Auto logout after user close browser tab without first logging out
*/ */
public void logoutAfterTabDestroyed(){ public void logoutAfterTabDestroyed(){
Desktop desktop = Executions.getCurrent().getDesktop(); Desktop desktop = Executions.getCurrent().getDesktop();
@ -472,7 +521,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
afterLogout(session); afterLogout(session);
} }
/**
* Logout current session
* @return {@link Session}
*/
protected Session logout0() { protected Session logout0() {
Session session = Executions.getCurrent() != null ? Executions.getCurrent().getDesktop().getSession() : null; Session session = Executions.getCurrent() != null ? Executions.getCurrent().getDesktop().getSession() : null;
@ -502,6 +554,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
/** /**
* @return IDesktop * @return IDesktop
*/ */
@Override
public IDesktop getAppDeskop() public IDesktop getAppDeskop()
{ {
Desktop desktop = Executions.getCurrent() != null ? Executions.getCurrent().getDesktop() : null; Desktop desktop = Executions.getCurrent() != null ? Executions.getCurrent().getDesktop() : null;
@ -519,6 +572,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
return appDesktop; return appDesktop;
} }
@Override
public void onEvent(Event event) { public void onEvent(Event event) {
if (event instanceof ClientInfoEvent) { if (event instanceof ClientInfoEvent) {
ClientInfoEvent c = (ClientInfoEvent)event; ClientInfoEvent c = (ClientInfoEvent)event;
@ -564,6 +618,11 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
} }
/**
* handle change role event
* @param locale
* @param context
*/
private void onChangeRole(Locale locale, Properties context) { private void onChangeRole(Locale locale, Properties context) {
SessionManager.setSessionApplication(this); SessionManager.setSessionApplication(this);
loginDesktop = new WLogin(this); loginDesktop = new WLogin(this);
@ -576,6 +635,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
* @param userId * @param userId
* @return UserPreference * @return UserPreference
*/ */
@Override
public UserPreference loadUserPreference(int userId) { public UserPreference loadUserPreference(int userId) {
userPreference.loadPreference(userId); userPreference.loadPreference(userId);
return userPreference; return userPreference;
@ -584,10 +644,15 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
/** /**
* @return UserPrerence * @return UserPrerence
*/ */
@Override
public UserPreference getUserPreference() { public UserPreference getUserPreference() {
return userPreference; return userPreference;
} }
/**
* Should always return false
* @return true if event thread is enabled
*/
public static boolean isEventThreadEnabled() { public static boolean isEventThreadEnabled() {
return eventThreadEnabled; return eventThreadEnabled;
} }
@ -635,6 +700,12 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
appDesktop.logout(T -> {if (T) asyncChangeRole(session, locale, properties);}); appDesktop.logout(T -> {if (T) asyncChangeRole(session, locale, properties);});
} }
/**
* change role, asynchronous callback from {@link IDesktop#logout(org.adempiere.util.Callback)}
* @param httpSession
* @param locale
* @param properties
*/
private void asyncChangeRole(HttpSession httpSession, Locale locale, Properties properties) { private void asyncChangeRole(HttpSession httpSession, Locale locale, Properties properties) {
//stop key listener //stop key listener
if (keyListener != null) { if (keyListener != null) {
@ -675,8 +746,8 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
} }
/** /**
* @return string for setupload * @return setting for setUpload call
*/ */
public static String getUploadSetting() { public static String getUploadSetting() {
StringBuilder uploadSetting = new StringBuilder("true,native"); StringBuilder uploadSetting = new StringBuilder("true,native");
int size = MSysConfig.getIntValue(MSysConfig.ZK_MAX_UPLOAD_SIZE, 0); int size = MSysConfig.getIntValue(MSysConfig.ZK_MAX_UPLOAD_SIZE, 0);

View File

@ -29,16 +29,17 @@ import org.zkoss.zk.ui.event.Events;
import org.zkoss.zk.ui.sys.ComponentCtrl; import org.zkoss.zk.ui.sys.ComponentCtrl;
/** /**
* * Model for client info from browser
* @author Low Heng Sin * @author Low Heng Sin
* *
*/ */
public class ClientInfo implements Serializable { public class ClientInfo implements Serializable {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = -2686811277627911861L; private static final long serialVersionUID = -2686811277627911861L;
//values from browser
public int colorDepth; public int colorDepth;
public int desktopWidth; public int desktopWidth;
public int desktopHeight; public int desktopHeight;
@ -52,6 +53,7 @@ public class ClientInfo implements Serializable {
public boolean tablet; public boolean tablet;
public double devicePixelRatio; public double devicePixelRatio;
//size constants for responsive layout
public static final int LARGE_WIDTH = 1200; public static final int LARGE_WIDTH = 1200;
public static final int MEDIUM_WIDTH = 1000; public static final int MEDIUM_WIDTH = 1000;
public static final int SMALL_WIDTH = 700; public static final int SMALL_WIDTH = 700;

View File

@ -67,7 +67,7 @@ public class DefaultWebAppInit implements WebAppInit {
} }
/** /**
* Process modle event of table system config * Process model event of table system config
* @author hieplq * @author hieplq
* *
*/ */

View File

@ -10,6 +10,7 @@ import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
@Deprecated(forRemoval = true, since = "11")
public class DelegatingServlet extends HttpServlet { public class DelegatingServlet extends HttpServlet {
/** /**

View File

@ -50,7 +50,7 @@ import org.idempiere.ui.zk.media.IMediaViewProvider;
import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Component;
/** /**
* * Entry point to get implementation instance for UI extensions (through OSGI service or Equinox extension).
* @author viola * @author viola
* @author hengsin * @author hengsin
* *
@ -372,8 +372,8 @@ public class Extensions {
} }
/** /**
* @param tabType * @param tabType build in - FORM or SORT, custom - through IADTabPanelFactory extension
* @return {@link IADTabpanel} * @return {@link IADTabpanel}
*/ */
public static IADTabpanel getADTabPanel(String tabType) public static IADTabpanel getADTabPanel(String tabType)
{ {

View File

@ -18,56 +18,61 @@ import org.compiere.model.MUser;
import org.zkforge.keylistener.Keylistener; import org.zkforge.keylistener.Keylistener;
/** /**
* * Interface for web client
* @author hengsin * @author hengsin
* *
*/ */
public interface IWebClient { public interface IWebClient {
/** /**
* login completed * handle login completed
*/ */
public void loginCompleted(); public void loginCompleted();
/** /**
* logout * handle logout
*/ */
public void logout(); public void logout();
/** /**
* logout after browser destroyed * auto logout after close of browser tab
*/ */
public void logoutAfterTabDestroyed(); public void logoutAfterTabDestroyed();
/** /**
* * Get {@link IDesktop} instance
* @return IDesktop * @return IDesktop
*/ */
public IDesktop getAppDeskop(); public IDesktop getAppDeskop();
/** /**
* * load user preference by user id
* @param userId * @param userId
* @return UserPreference * @return UserPreference
*/ */
public UserPreference loadUserPreference(int userId); public UserPreference loadUserPreference(int userId);
/** /**
* * Get current user preference
* @return UserPreference * @return UserPreference
*/ */
public UserPreference getUserPreference(); public UserPreference getUserPreference();
/** /**
* change Role * handle change Role
*/ */
public void changeRole(MUser user); public void changeRole(MUser user);
/** /**
* @return keylistener * Get global key listener
* @return {@link Keylistener}
*/ */
public abstract Keylistener getKeylistener(); public abstract Keylistener getKeylistener();
/**
* Get current ClientInfo
* @return {@link ClientInfo}
*/
default ClientInfo getClientInfo() { default ClientInfo getClientInfo() {
return getAppDeskop().getClientInfo(); return getAppDeskop().getClientInfo();
} }

View File

@ -44,7 +44,7 @@ import org.zkoss.zul.Window;
import org.zkoss.zul.Window.Mode; import org.zkoss.zul.Window.Mode;
/** /**
* * Some static UI helper methods
* @author Low Heng Sin * @author Low Heng Sin
* *
*/ */
@ -55,7 +55,7 @@ public final class LayoutUtils {
/** /**
* @param layout * @param layout
*/ */
@Deprecated @Deprecated(forRemoval = true, since = "11")
public static void sendDeferLayoutEvent(org.zkoss.zul.Borderlayout layout, int timeout) { public static void sendDeferLayoutEvent(org.zkoss.zul.Borderlayout layout, int timeout) {
/* this is not required anymore */ /* this is not required anymore */
// StringBuilder content = new StringBuilder(); // StringBuilder content = new StringBuilder();
@ -68,7 +68,7 @@ public final class LayoutUtils {
} }
/** /**
* * append cls to target's sclass property
* @param cls * @param cls
* @param target * @param target
*/ */
@ -82,7 +82,7 @@ public final class LayoutUtils {
* *
* @param cls * @param cls
* @param target * @param target
* @return boolean * @return true if target's sclass property contain cls
*/ */
public static boolean hasSclass(String cls, HtmlBasedComponent target) { public static boolean hasSclass(String cls, HtmlBasedComponent target) {
String sclass = target.getSclass(); String sclass = target.getSclass();
@ -93,9 +93,9 @@ public final class LayoutUtils {
} }
/** /**
* * create right align label (wrapped in div)
* @param label * @param label
* @return wrapped label * @return right align label (wrapped in div)
*/ */
public static Component makeRightAlign(Label label) { public static Component makeRightAlign(Label label) {
Div div = new Div(); Div div = new Div();
@ -105,28 +105,41 @@ public final class LayoutUtils {
return div; return div;
} }
/**
* open popup window overlapping ref component
* @param ref
* @param window
*/
public static void openPopupWindow(Component ref, Window window) { public static void openPopupWindow(Component ref, Window window) {
openPopupWindow(ref, window, 0); openPopupWindow(ref, window, 0);
} }
/** /**
* open popup window overlapping the ref component * open popup window overlapping ref component
* @param ref * @param ref
* @param window * @param window
* @param delayMs
*/ */
public static void openPopupWindow(Component ref, Window window, int delayMs) { public static void openPopupWindow(Component ref, Window window, int delayMs) {
openPopupWindow(ref, window, "overlap", 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) { public static void openPopupWindow(Component ref, Window window, String position) {
openPopupWindow(ref, window, position, 0); openPopupWindow(ref, window, position, 0);
} }
/** /**
* open popup window relative to the ref component * open popup window relative to ref component
* @param ref * @param ref
* @param window * @param window
* @param position * @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) { public static void openPopupWindow(Component ref, Window window, String position, int delayMs) {
if (window.getPage() == null) if (window.getPage() == null)
@ -152,10 +165,10 @@ public final class LayoutUtils {
} }
/** /**
* open popup window relative to the ref component * open overlapped window (mode overlapped) relative to ref component
* @param ref * @param ref
* @param window * @param window
* @param position * @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) { public static void openOverlappedWindow(Component ref, Window window, String position) {
if (window.getPage() == null) if (window.getPage() == null)
@ -173,10 +186,10 @@ public final class LayoutUtils {
} }
/** /**
* position opened window relative to the ref component * position opened window relative to ref component
* @param ref * @param ref
* @param window * @param window
* @param position * @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) { public static void positionWindow(Component ref, Window window, String position) {
StringBuilder script = new StringBuilder(); StringBuilder script = new StringBuilder();
@ -191,10 +204,10 @@ public final class LayoutUtils {
} }
/** /**
* open embedded window relative to the ref component * open embedded window relative to ref component
* @param ref * @param ref
* @param window * @param window
* @param position * @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) { public static void openEmbeddedWindow(Component ref, Window window, String position) {
StringBuilder script = new StringBuilder(); StringBuilder script = new StringBuilder();
@ -211,10 +224,10 @@ public final class LayoutUtils {
} }
/** /**
* open highlighted window relative to the ref component * open highlighted window relative to ref component
* @param ref * @param ref
* @param window * @param window
* @param position * @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) { public static void openHighlightedWindow(Component ref, Window window, String position) {
StringBuilder script = new StringBuilder(); StringBuilder script = new StringBuilder();
@ -230,7 +243,7 @@ public final class LayoutUtils {
} }
/** /**
* * Force redraw of component
* @param component * @param component
*/ */
public static void redraw(AbstractComponent component) { public static void redraw(AbstractComponent component) {
@ -259,6 +272,11 @@ public final class LayoutUtils {
return true; return true;
} }
/**
* Remove cls from target's sclass property
* @param cls
* @param target
*/
public static void removeSclass(String cls, HtmlBasedComponent target) { public static void removeSclass(String cls, HtmlBasedComponent target) {
String sclass = target.getSclass(); String sclass = target.getSclass();
if (Util.isEmpty(sclass)) if (Util.isEmpty(sclass))
@ -281,13 +299,13 @@ public final class LayoutUtils {
/** /**
* show window with a mask below. mask over tabPanel, all window or only over a control, dependency ownModel flag. * show window with a mask below. mask over tabPanel, all window or only over a control, dependency ownModel flag.
* when ownModel == {@link #OVERLAP_SELF}, window show overlap childOfOwn, * when ownModel == {@link #OVERLAP_SELF}, window show overlap childOfOwn,
* when childOfOwn isn't implement {@link ISupportMask} make new {@link Mask} object to make mask layout * when childOfOwn doesn't implement {@link ISupportMask} make new {@link Mask} object to make mask layout
* ownModel == {@link #OVERLAP_ALL_PAGE}, window show overlap all page * ownModel == {@link #OVERLAP_ALL_PAGE}, window show overlap all page
* ownModel == {@link #OVERLAP_TAB_PANEL}, window show overlap tabPanel * ownModel == {@link #OVERLAP_TAB_PANEL}, window show overlap tabPanel
* ownModel == {@link #OVERLAP_PARENT}, search near parent of childOfOwn implement {@link ISupportMask} if not exist user as OVERLAP_ALL_PAGE * ownModel == {@link #OVERLAP_PARENT}, search near parent of childOfOwn implement {@link ISupportMask} if not exist user as OVERLAP_ALL_PAGE
* @param window * @param window
* @param childOfOwn * @param childOfOwn
* @param ownModel * @param ownModel OVERLAP_TAB_PANEL, OVERLAP_ALL_PAGE, OVERLAP_PARENT or OVERLAP_SELF
* @return when show success return IMask object, it is own window, use {@link ISupportMask#hideMask()} to hidden mask. * @return when show success return IMask object, it is own window, use {@link ISupportMask#hideMask()} to hidden mask.
* other return null. * other return null.
*/ */
@ -316,7 +334,7 @@ public final class LayoutUtils {
} }
/** /**
* Show window in center of component * Show window in center of mask
* @param window * @param window
* @param mask * @param mask
*/ */
@ -334,7 +352,7 @@ public final class LayoutUtils {
} }
/** /**
* Show window over ownWindow with a mask, use when ownWindow isn't implement {@link ISupportMask} * Show window over ownWindow with a mask, use when ownWindow doesn't implement {@link ISupportMask}
* @param window * @param window
* @param ownWindow * @param ownWindow
* @param mask if mask = null, make new and return it * @param mask if mask = null, make new and return it
@ -353,11 +371,11 @@ public final class LayoutUtils {
} }
/** /**
* find parent control of child control, parent must implement {@link ISupportMask} * find parent of child component, parent must implement {@link ISupportMask}
* if parentClass != null, parent class must extends parentClass * if parentClass != null, parent class must extends parentClass
* @param child * @param child
* @param parentClass * @param parentClass
* @return * @return {@link ISupportMask}
*/ */
public static ISupportMask findMaskParent (Component child, Class<?> parentClass){ public static ISupportMask findMaskParent (Component child, Class<?> parentClass){
Component parent = child; Component parent = child;
@ -429,6 +447,11 @@ public final class LayoutUtils {
} }
} }
/**
* Expand number of grid column to min (for e.g, to min of 2 column)
* @param grid
* @param min
*/
public static void expandTo(Grid grid, int min) { public static void expandTo(Grid grid, int min) {
expandTo(grid, min, false); expandTo(grid, min, false);
} }
@ -488,6 +511,7 @@ public final class LayoutUtils {
} }
} }
/** Event listener to add/remove slide from target component's sclass property **/
private static final EventListener<OpenEvent> addSlideEventListener = (OpenEvent evt) -> { private static final EventListener<OpenEvent> addSlideEventListener = (OpenEvent evt) -> {
if (evt.isOpen()) if (evt.isOpen())
LayoutUtils.removeSclass("slide", (HtmlBasedComponent) evt.getTarget()); LayoutUtils.removeSclass("slide", (HtmlBasedComponent) evt.getTarget());
@ -504,7 +528,7 @@ public final class LayoutUtils {
} }
/** /**
* find popup ancestor of comp * find first popup ancestor of comp
* @param comp * @param comp
* @return {@link Popup} if comp or one of its ancestor is Popup * @return {@link Popup} if comp or one of its ancestor is Popup
*/ */
@ -519,7 +543,7 @@ public final class LayoutUtils {
} }
/** /**
* call popup.detach when it is close * Auto call popup.detach when popup is close
* @param popup * @param popup
*/ */
public static void autoDetachOnClose(Popup popup) { public static void autoDetachOnClose(Popup popup) {
@ -531,7 +555,7 @@ public final class LayoutUtils {
} }
/** /**
* set target same width as ref using client side script * Make target same width as ref using client side script
* @param target * @param target
* @param ref * @param ref
*/ */

View File

@ -1,3 +1,25 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* 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 2 *
* 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, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
**********************************************************************/
package org.adempiere.webui; package org.adempiere.webui;
import java.io.File; import java.io.File;
@ -23,6 +45,9 @@ import org.compiere.util.DB;
import org.compiere.util.Ini; import org.compiere.util.Ini;
import org.compiere.util.WebUtil; import org.compiere.util.WebUtil;
/**
* Sync state of {@link HttpSession} and AD_Session
*/
public class LoggedSessionListener implements HttpSessionListener, ServletContextListener, ServerStateChangeListener{ public class LoggedSessionListener implements HttpSessionListener, ServletContextListener, ServerStateChangeListener{
private static Hashtable<String, HttpSession> AD_SessionList = new Hashtable<String, HttpSession>(); private static Hashtable<String, HttpSession> AD_SessionList = new Hashtable<String, HttpSession>();
private static final CLogger logger = CLogger.getCLogger(LoggedSessionListener.class); private static final CLogger logger = CLogger.getCLogger(LoggedSessionListener.class);
@ -83,7 +108,7 @@ public class LoggedSessionListener implements HttpSessionListener, ServletContex
*/ */
} }
public void DestroyAllSession() { private void DestroyAllSession() {
if (!Adempiere.isStarted()) if (!Adempiere.isStarted())
{ {
Adempiere.addServerStateChangeListener(this); Adempiere.addServerStateChangeListener(this);
@ -100,7 +125,7 @@ public class LoggedSessionListener implements HttpSessionListener, ServletContex
Adempiere.removeServerStateChangeListener(this); Adempiere.removeServerStateChangeListener(this);
} }
public void removeADSession(String sessionID, String serverName) { private void removeADSession(String sessionID, String serverName) {
String sql = "UPDATE AD_Session SET Processed='Y' WHERE WebSession=? AND ServerName=? AND Processed='N'"; String sql = "UPDATE AD_Session SET Processed='Y' WHERE WebSession=? AND ServerName=? AND Processed='N'";
int no = DB.executeUpdate(sql, new Object[] {sessionID, serverName}, false, null); int no = DB.executeUpdate(sql, new Object[] {sessionID, serverName}, false, null);
if (no < 0) { if (no < 0) {

View File

@ -21,7 +21,7 @@ import org.zkoss.zk.ui.Component;
import org.zkoss.zk.ui.util.Clients; import org.zkoss.zk.ui.util.Clients;
/** /**
* Helper class for any component want implement {@link ISupportMask} * Helper class for {@link ISupportMask} implementation
* Just make a instance of this class and let it do everything * Just make a instance of this class and let it do everything
* @author hieplq * @author hieplq
* *
@ -39,7 +39,7 @@ public class ShowMaskWrapper implements ISupportMask {
private Mask maskObj; private Mask maskObj;
/** /**
* comp is component want implement this interface * comp is component that implement ISupportMask
* @param comp * @param comp
*/ */
public ShowMaskWrapper (Component comp){ public ShowMaskWrapper (Component comp){

View File

@ -20,8 +20,8 @@ import org.zkoss.zk.ui.ShadowElement;
import org.zkoss.zk.ui.util.UiLifeCycle; import org.zkoss.zk.ui.util.UiLifeCycle;
/** /**
* Utility class for selenium support
* @author hengsin * @author hengsin
*
*/ */
public class UiLifeCycleListener implements UiLifeCycle { public class UiLifeCycleListener implements UiLifeCycle {

View File

@ -68,12 +68,12 @@ import org.zkoss.zul.Vlayout;
public class ValuePreference extends Window implements EventListener<Event> public class ValuePreference extends Window implements EventListener<Event>
{ {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 7594680475358417813L; private static final long serialVersionUID = 7594680475358417813L;
/** /**
* Factory * Show value preference dialog
* @param ref * @param ref
* @param mField field * @param mField field
* @param aValue value * @param aValue value
@ -89,7 +89,7 @@ public class ValuePreference extends Window implements EventListener<Event>
} // start } // start
/** /**
* Factory * Show value preference dialog
* @param ref * @param ref
* @param mField field * @param mField field
* @param aValue value * @param aValue value
@ -127,7 +127,7 @@ public class ValuePreference extends Window implements EventListener<Event>
int AD_Org_ID = Env.getContextAsInt(Env.getCtx(), WindowNo, "AD_Org_ID"); int AD_Org_ID = Env.getContextAsInt(Env.getCtx(), WindowNo, "AD_Org_ID");
int AD_User_ID = Env.getAD_User_ID(Env.getCtx()); int AD_User_ID = Env.getAD_User_ID(Env.getCtx());
// Create Editor // Create and show value preference dialog
@SuppressWarnings("unused") @SuppressWarnings("unused")
ValuePreference vp = new ValuePreference (WindowNo, ValuePreference vp = new ValuePreference (WindowNo,
AD_Client_ID, AD_Org_ID, AD_User_ID, AD_Window_ID, mField.getAD_Process_ID_Of_Panel(), mField.getAD_InfoWindow_ID_of_Panel(), AD_Client_ID, AD_Org_ID, AD_User_ID, AD_Window_ID, mField.getAD_Process_ID_Of_Panel(), mField.getAD_InfoWindow_ID_of_Panel(),
@ -135,42 +135,8 @@ public class ValuePreference extends Window implements EventListener<Event>
displayType, AD_Reference_ID, ref); displayType, AD_Reference_ID, ref);
} // create } // create
/** /** The Name of the Dialog */
* Create the popup menu item to start the ValuePreference editor.
* <code>
* .. add method
* public void setField (MField mField)
* {
* m_mField = mField;
* if (m_mField != null)
* ValuePreference.addMenu (this, m_popupMenu);
* } // setField
*
* .. in actionPerformed add ..
* if (e.getActionCommand().equals(ValuePreference.NAME))
* {
* ValuePreference.start (m_mField, getValue(), DisplayValue);
* return;
* }
* </code>
* @param l listener
* @param popupMenu menu
* @return JMenuItem
*/
/*
public static CMenuItem addMenu (ActionListener l, JPopupMenu popupMenu)
{
CMenuItem mi = new CMenuItem (Msg.getMsg(Env.getCtx(), NAME), s_icon);
mi.setActionCommand(NAME);
mi.addActionListener(l);
popupMenu.add(mi);
return mi;
}*/ // addMenu
/** The Name of the Editor */
public static final String NAME = "ValuePreference"; public static final String NAME = "ValuePreference";
/** The Menu Icon */
//private static String ICON_URL = "images/VPreference16.png";
/** Logger */ /** Logger */
private static final CLogger log = CLogger.getCLogger(ValuePreference.class); private static final CLogger log = CLogger.getCLogger(ValuePreference.class);
private AbstractADWindowContent adwindowContent; private AbstractADWindowContent adwindowContent;
@ -189,7 +155,7 @@ public class ValuePreference extends Window implements EventListener<Event>
* @param DisplayValue value display * @param DisplayValue value display
* @param displayType display type * @param displayType display type
* @param AD_Reference_ID reference * @param AD_Reference_ID reference
* @param ref * @param ref
*/ */
public ValuePreference (int WindowNo, public ValuePreference (int WindowNo,
int AD_Client_ID, int AD_Org_ID, int AD_User_ID, int AD_Window_ID, int AD_Process_ID_Of_Panel, int AD_Infowindow_ID, int AD_Client_ID, int AD_Org_ID, int AD_User_ID, int AD_Window_ID, int AD_Process_ID_Of_Panel, int AD_Infowindow_ID,
@ -439,7 +405,6 @@ public class ValuePreference extends Window implements EventListener<Event>
// ActionListener // ActionListener
cbClient.setEnabled(false); cbClient.setEnabled(false);
cbClient.setChecked(true); cbClient.setChecked(true);
// cbClient.addActionListener(this);
// Can Change Org // Can Change Org
if (MRole.PREFERENCETYPE_Client.equals(m_role.getPreferenceType())) if (MRole.PREFERENCETYPE_Client.equals(m_role.getPreferenceType()))
@ -498,7 +463,7 @@ public class ValuePreference extends Window implements EventListener<Event>
} }
else else
setExplanation(); setExplanation();
} // actionPerformed }
private void onCancel() { private void onCancel() {
this.detach(); this.detach();
@ -557,8 +522,6 @@ public class ValuePreference extends Window implements EventListener<Event>
*/ */
public int delete() public int delete()
{ {
log.info("");
StringBuilder sql = new StringBuilder ("DELETE FROM AD_Preference WHERE "); StringBuilder sql = new StringBuilder ("DELETE FROM AD_Preference WHERE ");
sql.append("AD_Client_ID=").append(cbClient.isChecked() ? m_AD_Client_ID : 0); sql.append("AD_Client_ID=").append(cbClient.isChecked() ? m_AD_Client_ID : 0);
sql.append(" AND AD_Org_ID=").append(cbOrg.isChecked() ? m_AD_Org_ID : 0); sql.append(" AND AD_Org_ID=").append(cbOrg.isChecked() ? m_AD_Org_ID : 0);
@ -654,12 +617,10 @@ public class ValuePreference extends Window implements EventListener<Event>
} // getContextKey } // getContextKey
/** /**
* Save to Disk * Save to DB
*/ */
public void insert() public void insert()
{ {
log.info("");
// --- Delete first // --- Delete first
int no = delete(); int no = delete();

View File

@ -143,7 +143,7 @@ public class WArchive implements EventListener<Event>
} // getZoomTargets } // getZoomTargets
/** /**
* Listner * Listener
* @param e event * @param e event
*/ */
@Override @Override
@ -151,6 +151,7 @@ public class WArchive implements EventListener<Event>
{ {
if (e.getTarget() instanceof Menuitem) if (e.getTarget() instanceof Menuitem)
{ {
//open archive viewer
int AD_Form_ID = FORM_ARCHIVEVIEWER; // ArchiveViewer int AD_Form_ID = FORM_ARCHIVEVIEWER; // ArchiveViewer
ADForm form = ADForm.openForm(AD_Form_ID); ADForm form = ADForm.openForm(AD_Form_ID);

View File

@ -34,7 +34,7 @@ import org.zkoss.zul.West;
import org.zkoss.zul.Window; import org.zkoss.zul.Window;
/** /**
* * Manage login window for login and role selection
* @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a> * @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a>
* @author Low Heng Sin * @author Low Heng Sin
* @date Mar 3, 2007 * @date Mar 3, 2007
@ -42,17 +42,28 @@ import org.zkoss.zul.Window;
*/ */
public class WLogin extends AbstractUIPart public class WLogin extends AbstractUIPart
{ {
/** IWebClient instance ({@link AdempiereWebUI}) **/
private IWebClient app; private IWebClient app;
/** Main layout **/
private Borderlayout layout; private Borderlayout layout;
@Deprecated(forRemoval = true, since = "11")
private Window browserWarningWindow; private Window browserWarningWindow;
/** embedded window for login and role selection **/
private LoginWindow loginWindow; private LoginWindow loginWindow;
/**
*
* @param app
*/
public WLogin(IWebClient app) public WLogin(IWebClient app)
{ {
this.app = app; this.app = app;
} }
/**
* Create UI from login.zul file. The main layout component ("layout") must be instance of {@link Borderlayout}.
*/
@Override
protected Component doCreatePart(Component parent) protected Component doCreatePart(Component parent)
{ {
PageDefinition pageDefintion = Executions.getCurrent().getPageDefinition(ThemeManager.getThemeResource("zul/login/login.zul")); PageDefinition pageDefintion = Executions.getCurrent().getPageDefinition(ThemeManager.getThemeResource("zul/login/login.zul"));
@ -107,6 +118,9 @@ public class WLogin extends AbstractUIPart
return layout; return layout;
} }
/**
* detach/dispose window content
*/
public void detach() { public void detach() {
layout.detach(); layout.detach();
layout = null; layout = null;
@ -114,12 +128,15 @@ public class WLogin extends AbstractUIPart
browserWarningWindow.detach(); browserWarningWindow.detach();
} }
/**
* @return {@link Component}
*/
public Component getComponent() { public Component getComponent() {
return layout; return layout;
} }
/** /**
* Show change role window * Show change role panel in {@link #loginWindow}
* @param locale * @param locale
* @param properties env context * @param properties env context
*/ */

View File

@ -36,8 +36,8 @@ import org.zkoss.zul.Menupopup;
import org.zkoss.zul.Popup; import org.zkoss.zul.Popup;
/** /**
* Request Button Action. * Handle Request Button Action.
* Popup Menu * Show Popup Menu.
* *
* @author Jorg Janke * @author Jorg Janke
* @version $Id: ARequest.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $ * @version $Id: ARequest.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $
@ -133,6 +133,7 @@ public class WRequest implements EventListener<Event>
{ {
if (e.getTarget() instanceof Menuitem) if (e.getTarget() instanceof Menuitem)
{ {
//open request window
MQuery query = null; MQuery query = null;
if (e.getTarget() == m_active) if (e.getTarget() == m_active)
{ {
@ -171,6 +172,11 @@ public class WRequest implements EventListener<Event>
} }
} }
/**
* Set initial values for new request record
* @param e
* @param frame
*/
private void onNew(Event e, ADWindow frame) { private void onNew(Event e, ADWindow frame) {
// New - set Table/Record // New - set Table/Record
if (e.getTarget() == m_new) if (e.getTarget() == m_new)

View File

@ -37,11 +37,10 @@ import org.zkoss.zul.Menupopup;
import org.zkoss.zul.Popup; import org.zkoss.zul.Popup;
/** /**
* Application Zoom Across Launcher. * Handle Zoom Across button action.
* Called from APanel; Queries available Zoom Targets for Table.
* *
* @author Jorg Janke * @author Jorg Janke
* @version $Id: AZoomAcross.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $ * @version $Id: AZoomAcross.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $
* *
* @author Teo Sarca, SC ARHIPAC SERVICE SRL - FR [ 1762465 ] * @author Teo Sarca, SC ARHIPAC SERVICE SRL - FR [ 1762465 ]
* *
@ -64,6 +63,12 @@ public class WZoomAcross
} }
/**
* show zoom across popup menu
* @param invoker
* @param po
* @param windowID
*/
public WZoomAcross(Component invoker, PO po, final int windowID) { public WZoomAcross(Component invoker, PO po, final int windowID) {
if (log.isLoggable(Level.CONFIG)) log.config("PO=" + po+", WindowID="+windowID); if (log.isLoggable(Level.CONFIG)) log.config("PO=" + po+", WindowID="+windowID);
@ -104,12 +109,18 @@ public class WZoomAcross
m_popup.open(invoker, "after_start"); m_popup.open(invoker, "after_start");
} }
private Menupopup m_popup = new Menupopup(); //"ZoomMenu" /** popup menu for zoom across targets **/
private Menupopup m_popup = new Menupopup();
private static final CLogger log = CLogger.getCLogger(WZoomAcross.class); private static final CLogger log = CLogger.getCLogger(WZoomAcross.class);
private final List<ZoomInfoFactory.ZoomInfo> zoomInfos = new ArrayList<ZoomInfoFactory.ZoomInfo>(); private final List<ZoomInfoFactory.ZoomInfo> zoomInfos = new ArrayList<ZoomInfoFactory.ZoomInfo>();
/**
* find zoom across targets
* @param po
* @param windowID
*/
private void mkZoomTargets(final PO po, final int windowID) { private void mkZoomTargets(final PO po, final int windowID) {
for (final ZoomInfoFactory.ZoomInfo zoomInfo : ZoomInfoFactory.retrieveZoomInfos(po, for (final ZoomInfoFactory.ZoomInfo zoomInfo : ZoomInfoFactory.retrieveZoomInfos(po,
@ -125,18 +136,18 @@ public class WZoomAcross
} }
/** /**
* Launch Zoom * Zoom to destination window
* @param pp KeyPair * @param zoomInfo
*/ */
private void launchZoom (final ZoomInfoFactory.ZoomInfo zoomInfo) private void launchZoom (final ZoomInfoFactory.ZoomInfo zoomInfo)
{ {
final int AD_Window_ID = zoomInfo.windowId; final int AD_Window_ID = zoomInfo.windowId;
final MQuery query = zoomInfo.query; final MQuery query = zoomInfo.query;
log.info("AD_Window_ID=" + AD_Window_ID if (log.isLoggable(Level.INFO))
+ " - " + query); log.info("AD_Window_ID=" + AD_Window_ID + " - " + query);
AEnv.zoom(AD_Window_ID, query); AEnv.zoom(AD_Window_ID, query);
} // launchZoom } // launchZoom
} // AZoom }

View File

@ -18,8 +18,9 @@ import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext; import org.osgi.framework.BundleContext;
/** /**
* {@link BundleActivator} for web UI
* Start {@link WindowValidatorManager}
* @author hengsin * @author hengsin
*
*/ */
public class WebUIActivator implements BundleActivator { public class WebUIActivator implements BundleActivator {
@ -44,6 +45,10 @@ public class WebUIActivator implements BundleActivator {
WindowValidatorManager.getInstance().stop(context); WindowValidatorManager.getInstance().stop(context);
} }
/**
*
* @return {@link BundleContext}
*/
public static BundleContext getBundleContext() { public static BundleContext getBundleContext() {
return bundleContext; return bundleContext;
} }

View File

@ -104,11 +104,10 @@ import org.zkoss.zul.Space;
* @author Elaine Tan * @author Elaine Tan
* @author Low Heng Sin * @author Low Heng Sin
*/ */
public class WAcctViewer extends Window implements EventListener<Event> public class WAcctViewer extends Window implements EventListener<Event>
{ {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 3440375640756094077L; private static final long serialVersionUID = 3440375640756094077L;
@ -207,7 +206,6 @@ public class WAcctViewer extends Window implements EventListener<Event>
/** /**
* Default constructor * Default constructor
*/ */
public WAcctViewer() public WAcctViewer()
{ {
this (0, 0, 0); this (0, 0, 0);
@ -220,14 +218,13 @@ public class WAcctViewer extends Window implements EventListener<Event>
* @param AD_Table_ID Table * @param AD_Table_ID Table
* @param Record_ID Record * @param Record_ID Record
*/ */
public WAcctViewer(int AD_Client_ID, int AD_Table_ID, int Record_ID) public WAcctViewer(int AD_Client_ID, int AD_Table_ID, int Record_ID)
{ {
super (); super ();
log.info("AD_Table_ID=" + AD_Table_ID + ", Record_ID=" + Record_ID); if (log.isLoggable(Level.INFO))
log.info("AD_Table_ID=" + AD_Table_ID + ", Record_ID=" + Record_ID);
//setDefaultCloseOperation(DISPOSE_ON_CLOSE);
m_windowNo = SessionManager.getAppDesktop().registerWindow(this); m_windowNo = SessionManager.getAppDesktop().registerWindow(this);
m_data = new WAcctViewerData (Env.getCtx(), m_windowNo, AD_Client_ID, AD_Table_ID); m_data = new WAcctViewerData (Env.getCtx(), m_windowNo, AD_Client_ID, AD_Table_ID);
@ -242,9 +239,8 @@ public class WAcctViewer extends Window implements EventListener<Event>
catch(Exception e) catch(Exception e)
{ {
log.log(Level.SEVERE, "", e); log.log(Level.SEVERE, "", e);
//dispose();
} }
} // AcctViewer }
/** /**
* Static Init. * Static Init.
@ -257,7 +253,6 @@ public class WAcctViewer extends Window implements EventListener<Event>
* </pre> * </pre>
* @throws Exception * @throws Exception
*/ */
private void init() throws Exception private void init() throws Exception
{ {
// Selection Panel // Selection Panel
@ -450,8 +445,6 @@ public class WAcctViewer extends Window implements EventListener<Event>
ZKUpdateUtil.setHflex(sortBy4, "1"); ZKUpdateUtil.setHflex(sortBy4, "1");
row.appendChild(group4); row.appendChild(group4);
//"images/InfoAccount16.png"
Groupbox groupDisplay = new Groupbox(); Groupbox groupDisplay = new Groupbox();
Caption capDisplay = new Caption(Msg.getMsg(Env.getCtx(), "Display")); Caption capDisplay = new Caption(Msg.getMsg(Env.getCtx(), "Display"));
groupDisplay.appendChild(capDisplay); groupDisplay.appendChild(capDisplay);
@ -549,11 +542,8 @@ public class WAcctViewer extends Window implements EventListener<Event>
resultPanel.appendChild(resultCenter); resultPanel.appendChild(resultCenter);
ZKUpdateUtil.setHflex(table, "1"); ZKUpdateUtil.setHflex(table, "1");
ZKUpdateUtil.setVflex(table, true); ZKUpdateUtil.setVflex(table, true);
//ZKUpdateUtil.setHeight(table, "99%");
//table.setStyle("position: absolute;");
resultCenter.appendChild(table); resultCenter.appendChild(table);
ZKUpdateUtil.setHflex(table, "1"); ZKUpdateUtil.setHflex(table, "1");
//ZKUpdateUtil.setVflex(table, "1");
table.addEventListener(Events.ON_DOUBLE_CLICK, this); table.addEventListener(Events.ON_DOUBLE_CLICK, this);
if (ClientInfo.isMobile()) if (ClientInfo.isMobile())
table.setSizedByContent(true); table.setSizedByContent(true);
@ -599,7 +589,7 @@ public class WAcctViewer extends Window implements EventListener<Event>
layout.setParent(this); layout.setParent(this);
ZKUpdateUtil.setHeight(layout, "100%"); ZKUpdateUtil.setHeight(layout, "100%");
ZKUpdateUtil.setWidth(layout, "100%"); ZKUpdateUtil.setWidth(layout, "100%");
layout.setStyle("background-color: transparent; margin: 0; position: absolute; padding: 0;"); layout.setStyle("background-color: transparent; margin: 0; position: relative; padding: 0;");
Center center = new Center(); Center center = new Center();
center.setParent(layout); center.setParent(layout);
@ -618,7 +608,7 @@ public class WAcctViewer extends Window implements EventListener<Event>
this.setTitle(Msg.getMsg(Env.getCtx(), TITLE)); this.setTitle(Msg.getMsg(Env.getCtx(), TITLE));
this.setClosable(true); this.setClosable(true);
this.setStyle("position: absolute; width: 100%; height: 100%;"); this.setStyle("position: relative; width: 100%; height: 100%;");
this.setSizable(true); this.setSizable(true);
this.setMaximizable(true); this.setMaximizable(true);
} }
@ -629,7 +619,6 @@ public class WAcctViewer extends Window implements EventListener<Event>
* @param AD_Table_ID table * @param AD_Table_ID table
* @param Record_ID record * @param Record_ID record
*/ */
private void dynInit (int AD_Table_ID, int Record_ID) private void dynInit (int AD_Table_ID, int Record_ID)
{ {
m_data.validateAcctSchemas(Record_ID); m_data.validateAcctSchemas(Record_ID);
@ -702,6 +691,12 @@ public class WAcctViewer extends Window implements EventListener<Event>
stateChanged(); stateChanged();
} // dynInit } // dynInit
/**
* set selected table and record id
* @param AD_Table_ID
* @param Record_ID
* @return true if AD_Table_ID is found, false otherwise
*/
private boolean setSelectedTable(int AD_Table_ID, int Record_ID) private boolean setSelectedTable(int AD_Table_ID, int Record_ID)
{ {
int cnt = selTable.getItemCount(); int cnt = selTable.getItemCount();
@ -727,9 +722,8 @@ public class WAcctViewer extends Window implements EventListener<Event>
} }
/** /**
* Dispose * Dispose window
*/ */
public void dispose() public void dispose()
{ {
m_data.dispose(); m_data.dispose();
@ -738,11 +732,10 @@ public class WAcctViewer extends Window implements EventListener<Event>
} // dispose; } // dispose;
/************************************************************************** /**************************************************************************
* Tab Changed * After Tab Selection Changed
*/ */
public void stateChanged() public void stateChanged()
{ {
// log.info( "AcctViewer.stateChanged");
boolean visible = m_data.documentQuery && tabResult.isSelected(); boolean visible = m_data.documentQuery && tabResult.isSelected();
bRePost.setVisible(visible); bRePost.setVisible(visible);
@ -756,11 +749,8 @@ public class WAcctViewer extends Window implements EventListener<Event>
* Event Performed (Event Listener) * Event Performed (Event Listener)
* @param e Event * @param e Event
*/ */
public void onEvent(Event e) throws Exception public void onEvent(Event e) throws Exception
{ {
// log.info(e.getActionCommand());
Object source = e.getTarget(); Object source = e.getTarget();
if (source == tabResult) if (source == tabResult)
@ -802,6 +792,10 @@ public class WAcctViewer extends Window implements EventListener<Event>
} }
} // onEvent } // onEvent
/**
* export to excel
* show excel viewer if available
*/
private void actionExport() { private void actionExport() {
if (m_rmodel != null && m_rmodel.getRowCount() > 0) { if (m_rmodel != null && m_rmodel.getRowCount() > 0) {
RModelExcelExporter exporter = new RModelExcelExporter(m_rmodel); RModelExcelExporter exporter = new RModelExcelExporter(m_rmodel);
@ -835,9 +829,8 @@ public class WAcctViewer extends Window implements EventListener<Event>
} }
/** /**
* New Acct Schema * Handle Acct Schema selection
*/ */
private void actionAcctSchema() private void actionAcctSchema()
{ {
Listitem listitem = selAcctSchema.getSelectedItem(); Listitem listitem = selAcctSchema.getSelectedItem();
@ -853,7 +846,8 @@ public class WAcctViewer extends Window implements EventListener<Event>
m_data.C_AcctSchema_ID = kp.getKey(); m_data.C_AcctSchema_ID = kp.getKey();
m_data.ASchema = MAcctSchema.get(Env.getCtx(), m_data.C_AcctSchema_ID); m_data.ASchema = MAcctSchema.get(Env.getCtx(), m_data.C_AcctSchema_ID);
log.info(m_data.ASchema.toString()); if (log.isLoggable(Level.INFO))
log.info(m_data.ASchema.toString());
// Sort Options // Sort Options
@ -916,7 +910,6 @@ public class WAcctViewer extends Window implements EventListener<Event>
* Add to Sort * Add to Sort
* @param vn name pair * @param vn name pair
*/ */
private void sortAddItem(ValueNamePair vn) private void sortAddItem(ValueNamePair vn)
{ {
sortBy1.appendItem(vn.getName(), vn); sortBy1.appendItem(vn.getName(), vn);
@ -926,9 +919,9 @@ public class WAcctViewer extends Window implements EventListener<Event>
} // sortAddItem } // sortAddItem
/** /**
* Query * Query.
* Delegate to {@link WAcctViewerData#query()}
*/ */
private void actionQuery() private void actionQuery()
{ {
// Parameter Info // Parameter Info
@ -1171,7 +1164,6 @@ public class WAcctViewer extends Window implements EventListener<Event>
/** /**
* Document selection * Document selection
*/ */
private void actionDocument() private void actionDocument()
{ {
boolean doc = selDocument.isChecked(); boolean doc = selDocument.isChecked();
@ -1193,9 +1185,8 @@ public class WAcctViewer extends Window implements EventListener<Event>
} // actionDocument } // actionDocument
/** /**
* Save Table selection (reset Record selection) * Handle Table selection (reset Record selection)
*/ */
private void actionTable() private void actionTable()
{ {
Listitem listitem = selTable.getSelectedItem(); Listitem listitem = selTable.getSelectedItem();
@ -1217,17 +1208,15 @@ public class WAcctViewer extends Window implements EventListener<Event>
} // actionTable } // actionTable
/** /**
* Action Button * Handle Info Button action
* Show info window
* *
* @param button pressed button * @param button pressed button
* @return ID * @throws Exception
* @throws Exception
*/ */
private void actionButton(final Button button) throws Exception private void actionButton(final Button button) throws Exception
{ {
final String keyColumn = button.getName(); final String keyColumn = button.getName();
log.info(keyColumn);
String whereClause = "(IsSummary='N' OR IsSummary IS NULL)"; String whereClause = "(IsSummary='N' OR IsSummary IS NULL)";
String lookupColumn = keyColumn; String lookupColumn = keyColumn;
@ -1321,14 +1310,12 @@ public class WAcctViewer extends Window implements EventListener<Event>
} }
}); });
AEnv.showWindow(info); AEnv.showWindow(info);
} // actionButton } // actionButton
/** /**
* RePost Record * RePost Record
*/ */
private void actionRePost() private void actionRePost()
{ {
if (m_data.documentQuery if (m_data.documentQuery
@ -1362,7 +1349,9 @@ public class WAcctViewer extends Window implements EventListener<Event>
} }
} // actionRePost } // actionRePost
// Elaine 2009/07/29 /**
* zoom to table id + record id
*/
private void actionZoom() private void actionZoom()
{ {
int selected = table.getSelectedIndex(); int selected = table.getSelectedIndex();
@ -1380,8 +1369,10 @@ public class WAcctViewer extends Window implements EventListener<Event>
AEnv.zoom(AD_Table_ID, Record_ID); AEnv.zoom(AD_Table_ID, Record_ID);
} }
} }
//
/**
* zoom to fact acct window (double click action)
*/
private void actionZoomFactAcct() { private void actionZoomFactAcct() {
int selected = table.getSelectedIndex(); int selected = table.getSelectedIndex();
if(selected == -1) return; if(selected == -1) return;

View File

@ -41,6 +41,7 @@ import org.compiere.model.MFactAcct;
import org.compiere.model.MJournal; import org.compiere.model.MJournal;
import org.compiere.model.MLookupFactory; import org.compiere.model.MLookupFactory;
import org.compiere.model.MRefList; import org.compiere.model.MRefList;
import org.compiere.model.SystemIDs;
import org.compiere.report.core.RColumn; import org.compiere.report.core.RColumn;
import org.compiere.report.core.RModel; import org.compiere.report.core.RModel;
import org.compiere.util.CLogger; import org.compiere.util.CLogger;
@ -53,8 +54,7 @@ import org.compiere.util.Msg;
import org.compiere.util.ValueNamePair; import org.compiere.util.ValueNamePair;
/** /**
* Account Viewer State - maintains State information for the Account Viewer * State and data access helper for {@link WAcctViewer}
* Based on class AcctViewerData
* *
* @author Niraj Sohun * @author Niraj Sohun
* July 27, 2007 * July 27, 2007
@ -68,68 +68,70 @@ public class WAcctViewerData
/** Client */ /** Client */
public int AD_Client_ID; public int AD_Client_ID;
/** All Acct Schema */ /** All Accounting Schemas for client */
public MAcctSchema[] ASchemas = null; public MAcctSchema[] ASchemas = null;
/** This Acct Schema */ /** Selected Accounting Schema */
public MAcctSchema ASchema = null; public MAcctSchema ASchema = null;
// Selection Info // Selection Info
/** Document Query */ /** Document Query - query with {@link #AD_Table_ID} and {@link #Record_ID} */
public boolean documentQuery = false; public boolean documentQuery = false;
/** Acct Schema */ /** Selected Accounting Schema ID */
public int C_AcctSchema_ID = 0; public int C_AcctSchema_ID = 0;
/** Posting Type */ /** Selected Posting Type */
public String PostingType = ""; public String PostingType = "";
/** Organization */ /** Selected Organization ID */
public int AD_Org_ID = 0; public int AD_Org_ID = 0;
/** Date From */ /** Date From, for DateAcct filter */
public Timestamp DateFrom = null; public Timestamp DateFrom = null;
/** Date To */ /** Date To, for DateAcct filter */
public Timestamp DateTo = null; public Timestamp DateTo = null;
// Document Table Selection Info // Document Table Selection Info
/** Table ID */ /** Selected Table ID for {@link #documentQuery} */
public int AD_Table_ID; public int AD_Table_ID;
/** Record */ /** Selected Record ID for {@link #documentQuery} */
public int Record_ID; public int Record_ID;
/** Containing Column and Query */ /** ColumnName:Filter */
public HashMap<String,String> whereInfo = new HashMap<String,String>(); public HashMap<String,String> whereInfo = new HashMap<String,String>();
/** Containing TableName and AD_Table_ID */ /** TableName:AD_Table_ID */
public HashMap<String,Integer> tableInfo = new HashMap<String,Integer>(); public HashMap<String,Integer> tableInfo = new HashMap<String,Integer>();
// Display Info // Display Info
/** Display Qty */ /** Display Qty Columns */
boolean displayQty = false; protected boolean displayQty = false;
/** Display Source Surrency */ /** Display Source Amount Columns */
boolean displaySourceAmt = false; protected boolean displaySourceAmt = false;
/** Display Document info */ /** Display Document info */
boolean displayDocumentInfo = false; protected boolean displayDocumentInfo = false;
String sortBy1 = ""; //order by
String sortBy2 = ""; protected String sortBy1 = "";
String sortBy3 = ""; protected String sortBy2 = "";
String sortBy4 = ""; protected String sortBy3 = "";
protected String sortBy4 = "";
boolean group1 = false; //group by
boolean group2 = false; protected boolean group1 = false;
boolean group3 = false; protected boolean group2 = false;
boolean group4 = false; protected boolean group3 = false;
protected boolean group4 = false;
/** Leasing Columns */ /** Leading Columns. Number of columns shown before Accounted and Source Amount Columns. */
private int m_leadingColumns = 0; private int m_leadingColumns = 0;
/** UserElement1 Reference */ /** UserElement1 Reference */
@ -148,7 +150,6 @@ public class WAcctViewerData
* @param ad_Client_ID client * @param ad_Client_ID client
* @param ad_Table_ID table * @param ad_Table_ID table
*/ */
public WAcctViewerData (Properties ctx, int windowNo, int ad_Client_ID, int ad_Table_ID) public WAcctViewerData (Properties ctx, int windowNo, int ad_Client_ID, int ad_Table_ID)
{ {
WindowNo = windowNo; WindowNo = windowNo;
@ -168,8 +169,7 @@ public class WAcctViewerData
/** /**
* Dispose * Dispose
*/ */
public void dispose() public void dispose()
{ {
ASchemas = null; ASchemas = null;
@ -182,8 +182,8 @@ public class WAcctViewerData
} // dispose } // dispose
/** /**
* GL Journal only posts in one Accounting Schema * GL Journal only posts in one Accounting Schema.
* if the record is a GL Journal, remove the others from the array * If the record is a GL Journal, remove the other accounting schema from {@link #ASchemas}
* @param Record_ID * @param Record_ID
*/ */
protected void validateAcctSchemas(int Record_ID) protected void validateAcctSchemas(int Record_ID)
@ -191,7 +191,7 @@ public class WAcctViewerData
if (Record_ID > 0 && AD_Table_ID == MJournal.Table_ID) { if (Record_ID > 0 && AD_Table_ID == MJournal.Table_ID) {
MJournal journal = new MJournal(Env.getCtx(), Record_ID, null); MJournal journal = new MJournal(Env.getCtx(), Record_ID, null);
if (journal != null) { if (journal.getGL_Journal_ID() == Record_ID) {
ASchemas = new MAcctSchema[1]; ASchemas = new MAcctSchema[1];
ASchemas[0] = MAcctSchema.get(Env.getCtx(), journal.getC_AcctSchema_ID()); ASchemas[0] = MAcctSchema.get(Env.getCtx(), journal.getC_AcctSchema_ID());
ASchema = ASchemas[0]; ASchema = ASchemas[0];
@ -199,11 +199,10 @@ public class WAcctViewerData
} }
} // validateAcctSchemas } // validateAcctSchemas
/************************************************************************** /**
* Fill Accounting Schema * Fill Accounting Schema
* @param cb Listbox to be filled * @param cb Listbox to be filled
*/ */
protected void fillAcctSchema (Listbox cb) protected void fillAcctSchema (Listbox cb)
{ {
for (int i = 0; i < ASchemas.length; i++) for (int i = 0; i < ASchemas.length; i++)
@ -214,10 +213,9 @@ public class WAcctViewerData
} // fillAcctSchema } // fillAcctSchema
/** /**
* Fill Posting Type * Fill Posting Type from {@link SystemIDs#REFERENCE_POSTING_TYPE} list.
* @param cb Listox to be filled * @param cb Listbox to be filled
*/ */
protected void fillPostingType (Listbox cb) protected void fillPostingType (Listbox cb)
{ {
int AD_Reference_ID = REFERENCE_POSTING_TYPE; int AD_Reference_ID = REFERENCE_POSTING_TYPE;
@ -230,18 +228,15 @@ public class WAcctViewerData
} // fillPostingType } // fillPostingType
/** /**
* Fill Table with * Fill Listbox with
* ValueNamePair (TableName, translatedKeyColumnName) * ValueNamePair (TableName, translatedKeyColumnName)
* and tableInfo with (TableName, AD_Table_ID) * and {@link #tableInfo} with (TableName, AD_Table_ID)
* and select the entry for AD_Table_ID * and select the entry for AD_Table_ID
* *
* @param cb Listbox to be filled * @param cb Listbox to be filled
*/ */
protected void fillTable (Listbox cb) protected void fillTable (Listbox cb)
{ {
ValueNamePair select = null;
String sql = "SELECT AD_Table_ID, TableName FROM AD_Table t " String sql = "SELECT AD_Table_ID, TableName FROM AD_Table t "
+ "WHERE EXISTS (SELECT * FROM AD_Column c" + "WHERE EXISTS (SELECT * FROM AD_Column c"
+ " WHERE t.AD_Table_ID=c.AD_Table_ID AND c.ColumnName='Posted')" + " WHERE t.AD_Table_ID=c.AD_Table_ID AND c.ColumnName='Posted')"
@ -261,10 +256,7 @@ public class WAcctViewerData
ValueNamePair pp = new ValueNamePair(tableName, name); ValueNamePair pp = new ValueNamePair(tableName, name);
cb.appendItem(pp.getName(),pp); cb.appendItem(pp.getName(),pp);
tableInfo.put (tableName, Integer.valueOf(id)); tableInfo.put (tableName, Integer.valueOf(id));
if (id == AD_Table_ID)
select = pp;
} }
} }
catch (SQLException e) catch (SQLException e)
@ -276,18 +268,14 @@ public class WAcctViewerData
DB.close(rs, pstmt); DB.close(rs, pstmt);
rs = null; rs = null;
pstmt = null; pstmt = null;
} }
if (select != null)
;//cb.setSelectedItem(select);
} // fillTable } // fillTable
/** /**
* Fill Org * Fill Org
* *
* @param cb Listbox to be filled * @param cb Listbox to be filled
*/ */
protected void fillOrg (Listbox cb) protected void fillOrg (Listbox cb)
{ {
KeyNamePair pp = new KeyNamePair(0, ""); KeyNamePair pp = new KeyNamePair(0, "");
@ -320,7 +308,7 @@ public class WAcctViewerData
} // fillOrg } // fillOrg
/** /**
* Get Button Text * Get Info Button Text
* *
* @param tableName table * @param tableName table
* @param columnName column * @param columnName column
@ -362,10 +350,9 @@ public class WAcctViewerData
return retValue; return retValue;
} // getButtonText } // getButtonText
/**************************************************************************
/** /**
* Create Query and submit * Create query and execute
* @return Report Model * @return {@link RModel} query result
*/ */
protected RModel query() protected RModel query()
@ -502,10 +489,9 @@ public class WAcctViewerData
} // query } // query
/** /**
* Create Report Model (Columns) * Create new Report Model (Setup Columns) instance
* @return Report Model * @return {@link RModel}
*/ */
private RModel getRModel() private RModel getRModel()
{ {
Properties ctx = Env.getCtx(); Properties ctx = Env.getCtx();
@ -596,10 +582,9 @@ public class WAcctViewerData
} // createRModel } // createRModel
/** /**
* Create the key columns in sequence * Create the list of key/mandatory columns to display in viewer
* @return List of Key Columns * @return List of Key Columns
*/ */
private ArrayList<String> createKeyColumns() private ArrayList<String> createKeyColumns()
{ {
ArrayList<String> columns = new ArrayList<String>(); ArrayList<String> columns = new ArrayList<String>();

View File

@ -25,7 +25,7 @@ import org.compiere.util.CCache;
import org.zkoss.image.AImage; import org.zkoss.image.AImage;
/** /**
* * Static methods to get {@link IAction} osgi service instance.
* @author hengsin * @author hengsin
* *
*/ */
@ -58,7 +58,10 @@ public class Actions {
} }
/** /**
* * Image name: actionId+"24.png".
* Get image from current theme or plugin resource.
* For plugin resource, it will try the path /action/images/{theme}/{image name}, /action/images/default/{image name} and
* /action/images/{image name}
* @param actionId * @param actionId
* @return {@link AImage} * @return {@link AImage}
*/ */

View File

@ -22,7 +22,7 @@ import org.zkoss.zul.Toolbarbutton;
*/ */
public interface IAction { public interface IAction {
/** /**
* * execute action
* @param target * @param target
*/ */
public void execute(Object target); public void execute(Object target);

View File

@ -78,10 +78,13 @@ import org.zkoss.zul.event.ListDataEvent;
public class ADSortTab extends Panel implements IADTabpanel public class ADSortTab extends Panel implements IADTabpanel
{ {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 4302282658814599752L; private static final long serialVersionUID = 4302282658814599752L;
/**
* default constructor
*/
public ADSortTab() public ADSortTab()
{ {
} }
@ -133,7 +136,7 @@ public class ADSortTab extends Panel implements IADTabpanel
private Button bUp = ButtonFactory.createButton(null, ThemeManager.getThemeResource("images/MoveUp16.png"), null); private Button bUp = ButtonFactory.createButton(null, ThemeManager.getThemeResource("images/MoveUp16.png"), null);
private Button bDown = ButtonFactory.createButton(null, ThemeManager.getThemeResource("images/MoveDown16.png"), null); private Button bDown = ButtonFactory.createButton(null, ThemeManager.getThemeResource("images/MoveDown16.png"), null);
// //
SimpleListModel noModel = new SimpleListModel() { protected SimpleListModel noModel = new SimpleListModel() {
/** /**
* *
*/ */
@ -153,12 +156,13 @@ public class ADSortTab extends Panel implements IADTabpanel
fireEvent(ListDataEvent.INTERVAL_ADDED, index, index); fireEvent(ListDataEvent.INTERVAL_ADDED, index, index);
} }
}; };
SimpleListModel yesModel = new SimpleListModel(); protected SimpleListModel yesModel = new SimpleListModel();
Listbox noList = new Listbox(); protected Listbox noList = new Listbox();
Listbox yesList = new Listbox(); protected Listbox yesList = new Listbox();
private GridTab gridTab; private GridTab gridTab;
private boolean uiCreated; private boolean uiCreated;
/** true if tab have been activated **/
private boolean active = false; private boolean active = false;
private boolean isChanged; private boolean isChanged;
private boolean detailPaneMode; private boolean detailPaneMode;
@ -277,7 +281,8 @@ public class ADSortTab extends Panel implements IADTabpanel
m_IdentifierSql = identifierSql.toString(); m_IdentifierSql = identifierSql.toString();
// //
noLabel.setValue(Msg.getMsg(Env.getCtx(), "Available")); noLabel.setValue(Msg.getMsg(Env.getCtx(), "Available"));
log.fine(m_ColumnSortName); if (log.isLoggable(Level.FINE))
log.fine(m_ColumnSortName);
} // dynInit } // dynInit
/** /**
@ -520,14 +525,19 @@ public class ADSortTab extends Panel implements IADTabpanel
} }
} }
/**
* @return true if tab has changes
*/
public boolean isChanged() { public boolean isChanged() {
return isChanged; return isChanged;
} }
/** /**
* Move an item between yes and no list.
* Delegate to {@link #migrateLists(Listbox, Listbox, int)}
* @param event * @param event
*/ */
void migrateValueAcrossLists (Event event) protected void migrateValueAcrossLists (Event event)
{ {
Object source = event.getTarget(); Object source = event.getTarget();
if (source instanceof ListItem) { if (source instanceof ListItem) {
@ -544,7 +554,13 @@ public class ADSortTab extends Panel implements IADTabpanel
migrateLists (listFrom,listTo,endIndex); migrateLists (listFrom,listTo,endIndex);
} // migrateValueAcrossLists } // migrateValueAcrossLists
void migrateLists (Listbox listFrom , Listbox listTo , int endIndex) /**
* Move an item from listFrom to listTo.
* @param listFrom
* @param listTo
* @param endIndex destination index
*/
protected void migrateLists (Listbox listFrom , Listbox listTo , int endIndex)
{ {
int index = 0; int index = 0;
SimpleListModel lmFrom = (listFrom == yesList) ? yesModel:noModel; SimpleListModel lmFrom = (listFrom == yesList) ? yesModel:noModel;
@ -577,10 +593,10 @@ public class ADSortTab extends Panel implements IADTabpanel
} }
/** /**
* Move within Yes List * Move an item within Yes List
* @param event event * @param event event
*/ */
void migrateValueWithinYesList (Event event) protected void migrateValueWithinYesList (Event event)
{ {
Object[] selObjects = yesList.getSelectedItems().toArray(); Object[] selObjects = yesList.getSelectedItems().toArray();
if (selObjects == null) if (selObjects == null)
@ -643,10 +659,10 @@ public class ADSortTab extends Panel implements IADTabpanel
/** /**
* Move within Yes List with Drag Event and Multiple Choice * Move items within Yes List with Drag Event and Multiple Choice
* @param event event * @param event event
*/ */
void migrateValueWithinYesList (int endIndex, List<ListElement> selObjects) protected void migrateValueWithinYesList (int endIndex, List<ListElement> selObjects)
{ {
int iniIndex =0; int iniIndex =0;
Arrays.sort(selObjects.toArray()); Arrays.sort(selObjects.toArray());
@ -667,8 +683,9 @@ public class ADSortTab extends Panel implements IADTabpanel
setIsChanged(true); setIsChanged(true);
} }
/* (non-Javadoc) /**
* @see org.compiere.grid.APanelTab#registerAPanel(APanel) * Set AD Window content part that own this ADSortTab instance.
* @param panel
*/ */
public void registerAPanel (AbstractADWindowContent panel) public void registerAPanel (AbstractADWindowContent panel)
{ {
@ -676,14 +693,15 @@ public class ADSortTab extends Panel implements IADTabpanel
} // registerAPanel } // registerAPanel
/** (non-Javadoc) /**
* Save changes to db.
*/ */
public void saveData() public void saveData()
{ {
if (!adWindowPanel.getToolbar().isSaveEnable()) if (!adWindowPanel.getToolbar().isSaveEnable())
return; return;
log.fine("");
boolean ok = true; boolean ok = true;
//TODO: should use model instead to enable change log and event handling
StringBuilder info = new StringBuilder(); StringBuilder info = new StringBuilder();
StringBuffer sql = null; StringBuffer sql = null;
// noList - Set SortColumn to null and optional YesNo Column to 'N' // noList - Set SortColumn to null and optional YesNo Column to 'N'
@ -851,6 +869,7 @@ public class ADSortTab extends Panel implements IADTabpanel
{ {
} }
@Override
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
if (event instanceof DropEvent) if (event instanceof DropEvent)
{ {
@ -884,6 +903,7 @@ public class ADSortTab extends Panel implements IADTabpanel
} }
} }
@Override
public void activate(boolean b) { public void activate(boolean b) {
if (b) { if (b) {
if (getAttribute(ATTR_ON_ACTIVATE_POSTED) != null) { if (getAttribute(ATTR_ON_ACTIVATE_POSTED) != null) {
@ -899,6 +919,7 @@ public class ADSortTab extends Panel implements IADTabpanel
Events.postEvent(event); Events.postEvent(event);
} }
@Override
public void createUI() { public void createUI() {
if (uiCreated) return; if (uiCreated) return;
try try
@ -913,64 +934,80 @@ public class ADSortTab extends Panel implements IADTabpanel
uiCreated = true; uiCreated = true;
} }
@Override
public void dynamicDisplay(int i) { public void dynamicDisplay(int i) {
} }
@Deprecated(forRemoval = true, since = "11")
public void editRecord(boolean b) { public void editRecord(boolean b) {
} }
@Override
public String getDisplayLogic() { public String getDisplayLogic() {
return gridTab.getDisplayLogic(); return gridTab.getDisplayLogic();
} }
@Override
public GridTab getGridTab() { public GridTab getGridTab() {
return gridTab; return gridTab;
} }
@Override
public int getTabLevel() { public int getTabLevel() {
return gridTab.getTabLevel(); return gridTab.getTabLevel();
} }
@Override
public String getTableName() public String getTableName()
{ {
return gridTab.getTableName(); return gridTab.getTableName();
} }
@Override
public int getRecord_ID() { public int getRecord_ID() {
return gridTab.getRecord_ID(); return gridTab.getRecord_ID();
} }
@Override
public String getTitle() { public String getTitle() {
return gridTab.getName(); return gridTab.getName();
} }
@Override
public boolean isCurrent() { public boolean isCurrent() {
return gridTab != null ? gridTab.isCurrent() : false; return gridTab != null ? gridTab.isCurrent() : false;
} }
@Override
public void query() { public void query() {
loadData(); loadData();
} }
@Override
public void query(boolean currentRows, int currentDays, int i) { public void query(boolean currentRows, int currentDays, int i) {
loadData(); loadData();
} }
@Override
public void refresh() { public void refresh() {
createUI(); createUI();
loadData(); loadData();
} }
@Override
public void switchRowPresentation() { public void switchRowPresentation() {
} }
@Override
public String get_ValueAsString(String variableName) { public String get_ValueAsString(String variableName) {
return Env.getContext(Env.getCtx(), m_WindowNo, variableName); return Env.getContext(Env.getCtx(), m_WindowNo, variableName);
} }
@Override
public void afterSave(boolean onSaveEvent) { public void afterSave(boolean onSaveEvent) {
} }
@Override
public boolean onEnterKey() { public boolean onEnterKey() {
return false; return false;
} }
@ -991,6 +1028,7 @@ public class ADSortTab extends Panel implements IADTabpanel
ZKUpdateUtil.setVflex(this, "true"); ZKUpdateUtil.setVflex(this, "true");
} }
@Override
public boolean isDetailPaneMode() { public boolean isDetailPaneMode() {
return this.detailPaneMode; return this.detailPaneMode;
} }
@ -1053,6 +1091,7 @@ public class ADSortTab extends Panel implements IADTabpanel
noList.setModel(noModel); noList.setModel(noModel);
} }
@Override
public ADTreePanel getTreePanel() { public ADTreePanel getTreePanel() {
return null; return null;
} }

View File

@ -127,6 +127,7 @@ import org.zkoss.zul.West;
import org.zkoss.zul.impl.XulElement; import org.zkoss.zul.impl.XulElement;
/** /**
* UI for an AD_Tab content (AD_Tab + AD_Fields).
* *
* This class is based on org.compiere.grid.GridController written by Jorg Janke. * This class is based on org.compiere.grid.GridController written by Jorg Janke.
* Changes have been brought for UI compatibility. * Changes have been brought for UI compatibility.
@ -142,6 +143,7 @@ import org.zkoss.zul.impl.XulElement;
public class ADTabpanel extends Div implements Evaluatee, EventListener<Event>, public class ADTabpanel extends Div implements Evaluatee, EventListener<Event>,
DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
{ {
//css for slide animation
private static final String SLIDE_LEFT_IN_CSS = "slide-left-in"; private static final String SLIDE_LEFT_IN_CSS = "slide-left-in";
private static final String SLIDE_LEFT_OUT_CSS = "slide-left-out"; private static final String SLIDE_LEFT_OUT_CSS = "slide-left-out";
@ -151,19 +153,26 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
private static final String SLIDE_RIGHT_OUT_CSS = "slide-right-out"; private static final String SLIDE_RIGHT_OUT_CSS = "slide-right-out";
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = -5335610241895151024L; private static final long serialVersionUID = -5335610241895151024L;
/** event to save open/close state of detail pane as user preference for ad window **/
private static final String ON_SAVE_OPEN_PREFERENCE_EVENT = "onSaveOpenPreference"; private static final String ON_SAVE_OPEN_PREFERENCE_EVENT = "onSaveOpenPreference";
/** post init event for tab panel **/
public static final String ON_POST_INIT_EVENT = "onPostInit"; public static final String ON_POST_INIT_EVENT = "onPostInit";
/** event after tab panel had switch presentation between form and list view **/
public static final String ON_SWITCH_VIEW_EVENT = "onSwitchView"; public static final String ON_SWITCH_VIEW_EVENT = "onSwitchView";
/** Event after execution of {@link #dynamicDisplay(int)} **/
public static final String ON_DYNAMIC_DISPLAY_EVENT = "onDynamicDisplay"; public static final String ON_DYNAMIC_DISPLAY_EVENT = "onDynamicDisplay";
/** defer event to set selected tree node **/
private static final String ON_DEFER_SET_SELECTED_NODE = "onDeferSetSelectedNode"; private static final String ON_DEFER_SET_SELECTED_NODE = "onDeferSetSelectedNode";
/** ADTabpanel attribute to prevent ON_DEFER_SET_SELECTED_NODE event posted twice within 1 execution **/
private static final String ON_DEFER_SET_SELECTED_NODE_ATTR = "onDeferSetSelectedNode.Event.Posted"; private static final String ON_DEFER_SET_SELECTED_NODE_ATTR = "onDeferSetSelectedNode.Event.Posted";
private static final CLogger logger; private static final CLogger logger;
@ -177,68 +186,95 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
private GridWindow gridWindow; private GridWindow gridWindow;
/** AD Window content part that own this ADTabpanel instance **/
private AbstractADWindowContent windowPanel; private AbstractADWindowContent windowPanel;
private int windowNo; private int windowNo;
/** form view for center of {@link #formContainer} **/
private Grid form; private Grid form;
/** field editors **/
private ArrayList<WEditor> editors = new ArrayList<WEditor>(); private ArrayList<WEditor> editors = new ArrayList<WEditor>();
/** components for field editors **/
private ArrayList<Component> editorComps = new ArrayList<Component>(); private ArrayList<Component> editorComps = new ArrayList<Component>();
/** editor toolbar buttons**/
private ArrayList<WButtonEditor> toolbarButtonEditors = new ArrayList<WButtonEditor>(); private ArrayList<WButtonEditor> toolbarButtonEditors = new ArrayList<WButtonEditor>();
/** toolbar buttons for AD_ToolBarButton **/
private ArrayList<ToolbarProcessButton> toolbarProcessButtons = new ArrayList<ToolbarProcessButton>(); private ArrayList<ToolbarProcessButton> toolbarProcessButtons = new ArrayList<ToolbarProcessButton>();
/** true if UI have been created for form and list **/
private boolean uiCreated = false; private boolean uiCreated = false;
/** list view for center of {@link #formContainer} **/
private GridView listPanel; private GridView listPanel;
/** content rows for group **/
private Map<String, List<Row>> fieldGroupContents; private Map<String, List<Row>> fieldGroupContents;
/** header row for group **/
private Map<String, List<org.zkoss.zul.Row>> fieldGroupHeaders; private Map<String, List<org.zkoss.zul.Row>> fieldGroupHeaders;
/** tabs for group (for tab type field group) **/
private Map<String, List<Tab>> fieldGroupTabHeaders; private Map<String, List<Tab>> fieldGroupTabHeaders;
/** all rows for current group (regardless of field group type) **/
private ArrayList<Row> rowList; private ArrayList<Row> rowList;
/** all collapsible groups **/
protected List<Group> allCollapsibleGroups; protected List<Group> allCollapsibleGroups;
/** main layout for header (center), tree (west) and detail pane (south) **/
private Borderlayout formContainer = null; private Borderlayout formContainer = null;
/** Tree panel for west of {@link #formContainer} **/
private ADTreePanel treePanel = null; private ADTreePanel treePanel = null;
/** Sync field editor changes to GridField **/
private GridTabDataBinder dataBinder; private GridTabDataBinder dataBinder;
/** true if tab have been activated **/
protected boolean activated = false; protected boolean activated = false;
/**
* current group for collapsible type field group
*/
private Group currentGroup; private Group currentGroup;
/** Panel for child tabs, south of {@link #formContainer} **/
private DetailPane detailPane; private DetailPane detailPane;
/** true if this ADTabpanel instance is own by detail pane **/
private boolean detailPaneMode; private boolean detailPaneMode;
/** tab no within an AD Window (sequence start from 0) **/
private int tabNo; private int tabNo;
/** DefaultFocusField */ /** Default focus field */
private WEditor defaultFocusField = null; private WEditor defaultFocusField = null;
/** number of columns for {@link #form} **/
private int numberOfFormColumns; private int numberOfFormColumns;
/** event to toggle between form and list view **/
public static final String ON_TOGGLE_EVENT = "onToggle"; public static final String ON_TOGGLE_EVENT = "onToggle";
/** default width for west tree panel **/
private static final String DEFAULT_PANEL_WIDTH = "300px"; private static final String DEFAULT_PANEL_WIDTH = "300px";
private static CCache<Integer, Boolean> quickFormCache = new CCache<Integer, Boolean>(null, "QuickForm", 20, false); private static CCache<Integer, Boolean> quickFormCache = new CCache<Integer, Boolean>(null, "QuickForm", 20, false);
/** Tab Box for Tab Field Groups */ /** Tab Box for Tab Type Field Groups */
private Tabbox tabbox = new Tabbox(); private Tabbox tabbox = new Tabbox();
/** List of Tab Group Grids */ /** List of Grid/Form for tab type field group */
private List<Grid> tabForms; private List<Grid> tabGroupForms;
/** Current Tab Group Rows */ /** Current Rows for tab type field group */
private Rows currentTabRows; private Rows currentTabGroupRows;
/** Event for south of {@link #formContainer} **/
private static enum SouthEvent { private static enum SouthEvent {
SLIDE(), SLIDE(),
OPEN(), OPEN(),
@ -247,11 +283,17 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
private SouthEvent() {} private SouthEvent() {}
} }
/**
* default constructor
*/
public ADTabpanel() public ADTabpanel()
{ {
init(); init();
} }
/**
* init components and event listeners
*/
private void init() private void init()
{ {
initComponents(); initComponents();
@ -270,6 +312,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
ClientInfo.onClientInfo(this, this::onClientInfo); ClientInfo.onClientInfo(this, this::onClientInfo);
} }
/**
* Create new {@link #form} and {@link #listPanel} instance.
*/
private void initComponents() private void initComponents()
{ {
LayoutUtils.addSclass("adtab-content", this); LayoutUtils.addSclass("adtab-content", this);
@ -282,6 +327,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
form.setVflex(false); form.setVflex(false);
form.setSclass("grid-layout adwindow-form"); form.setSclass("grid-layout adwindow-form");
form.setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "form"); form.setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "form");
//swipe listener for mobile
if (ClientInfo.isMobile()) if (ClientInfo.isMobile())
{ {
form.addEventListener("onSwipeRight", e -> { form.addEventListener("onSwipeRight", e -> {
@ -313,6 +359,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
listPanel.getListbox().addEventListener(Events.ON_DOUBLE_CLICK, this); listPanel.getListbox().addEventListener(Events.ON_DOUBLE_CLICK, this);
} }
/**
* Setup client side form swipe listener for mobile.
* Send onSwipeRight and onSwipeLeft event to server.
*/
private void setupFormSwipeListener() { private void setupFormSwipeListener() {
String uuid = form.getUuid(); String uuid = form.getUuid();
StringBuilder script = new StringBuilder("(function(){let w=zk.Widget.$('") StringBuilder script = new StringBuilder("(function(){let w=zk.Widget.$('")
@ -344,7 +394,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
public void setDetailPane(DetailPane component) { public void setDetailPane(DetailPane component) {
detailPane = component; detailPane = component;
Borderlayout borderLayout = (Borderlayout) formContainer; Borderlayout borderLayout = formContainer;
South south = borderLayout.getSouth(); South south = borderLayout.getSouth();
if (south == null) { if (south == null) {
south = new South(); south = new South();
@ -375,7 +425,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
ZKUpdateUtil.setHeight(formContainer.getSouth(), height); ZKUpdateUtil.setHeight(formContainer.getSouth(), height);
} }
} catch (Exception e) { } catch (Exception e) {
// just ignore exception is harmless here, consequence is just not setting height so it will assume the default of theme // just ignore, exception is harmless here, consequence is just not setting height so it will assume the default of theme
} }
} }
} }
@ -386,7 +436,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* * init tab panel layout ({@link #formContainer} and listeners
* @param winPanel * @param winPanel
* @param gridTab * @param gridTab
*/ */
@ -487,7 +537,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* Create UI components if not already created * Create UI for AD_Fields
*/ */
@Override @Override
public void createUI() public void createUI()
@ -496,7 +546,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* * Create UI for AD_Fields
* @param update true if it is update instead of create new * @param update true if it is update instead of create new
*/ */
protected void createUI(boolean update) protected void createUI(boolean update)
@ -515,7 +565,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
fieldGroupHeaders = new HashMap<String, List<org.zkoss.zul.Row>>(); fieldGroupHeaders = new HashMap<String, List<org.zkoss.zul.Row>>();
allCollapsibleGroups = new ArrayList<Group>(); allCollapsibleGroups = new ArrayList<Group>();
tabForms = new ArrayList<Grid>(); tabGroupForms = new ArrayList<Grid>();
fieldGroupTabHeaders = new HashMap<String, List<Tab>>(); fieldGroupTabHeaders = new HashMap<String, List<Tab>>();
int numCols=gridTab.getNumColumns(); int numCols=gridTab.getNumColumns();
@ -610,8 +660,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (numCols - actualxpos + 1 > 0) if (numCols - actualxpos + 1 > 0)
row.appendCellChild(createSpacer(), numCols - actualxpos + 1); row.appendCellChild(createSpacer(), numCols - actualxpos + 1);
if(currentTabRows != null) { if (currentTabGroupRows != null) {
currentTabRows.appendChild(row); currentTabGroupRows.appendChild(row);
} else { } else {
row.setGroup(currentGroup); row.setGroup(currentGroup);
rows.appendChild(row); rows.appendChild(row);
@ -641,7 +691,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
rows.appendChild(row); rows.appendChild(row);
headerRows.add(row); headerRows.add(row);
currentGroup = null; currentGroup = null;
currentTabRows = null; currentTabGroupRows = null;
} else if(X_AD_FieldGroup.FIELDGROUPTYPE_Tab.equals(field.getFieldGroupType())) { } else if(X_AD_FieldGroup.FIELDGROUPTYPE_Tab.equals(field.getFieldGroupType())) {
// Create New Tab for FieldGroup // Create New Tab for FieldGroup
List<Tab> headerTabs = new ArrayList<Tab>(); List<Tab> headerTabs = new ArrayList<Tab>();
@ -658,7 +708,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
headerTabs.add(tab); headerTabs.add(tab);
Grid tabForm = new Grid(); Grid tabForm = new Grid();
tabForms.add(tabForm); tabGroupForms.add(tabForm);
ZKUpdateUtil.setHflex(tabForm, "1"); ZKUpdateUtil.setHflex(tabForm, "1");
ZKUpdateUtil.setHeight(tabForm, null); ZKUpdateUtil.setHeight(tabForm, null);
tabForm.setVflex(false); tabForm.setVflex(false);
@ -696,7 +746,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
tp.appendChild(tabForm); tp.appendChild(tabForm);
currentGroup = null; currentGroup = null;
currentTabRows = tabRows; currentTabGroupRows = tabRows;
} }
else else
{ {
@ -710,7 +760,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
{ {
rowg.setOpen(false); rowg.setOpen(false);
} }
currentTabRows = null; currentTabGroupRows = null;
currentGroup = rowg; currentGroup = rowg;
rows.appendChild(rowg); rows.appendChild(rowg);
headerRows.add(rowg); headerRows.add(rowg);
@ -738,8 +788,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (numCols - actualxpos + 1 > 0) if (numCols - actualxpos + 1 > 0)
row.appendCellChild(createSpacer(), numCols - actualxpos + 1); row.appendCellChild(createSpacer(), numCols - actualxpos + 1);
// Tab Group vs Grid Group // Tab Group vs Grid Group
if(currentTabRows != null) { if (currentTabGroupRows != null) {
currentTabRows.appendChild(row); currentTabGroupRows.appendChild(row);
} else { } else {
row.setGroup(currentGroup); row.setGroup(currentGroup);
rows.appendChild(row); rows.appendChild(row);
@ -899,8 +949,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (numCols - actualxpos + 1 > 0) if (numCols - actualxpos + 1 > 0)
row.appendCellChild(createSpacer(), numCols - actualxpos + 1); row.appendCellChild(createSpacer(), numCols - actualxpos + 1);
// Tab Group vs Grid Group // Tab Group vs Grid Group
if(currentTabRows != null) { if (currentTabGroupRows != null) {
currentTabRows.appendChild(row); currentTabGroupRows.appendChild(row);
} else { } else {
row.setGroup(currentGroup); row.setGroup(currentGroup);
rows.appendChild(row); rows.appendChild(row);
@ -936,6 +986,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
switchRowPresentation(); switchRowPresentation();
} }
/**
* @param field
* @return {@link WEditor} or null if not found
*/
private WEditor findEditor(GridField field) { private WEditor findEditor(GridField field) {
for(WEditor editor : editors) { for(WEditor editor : editors) {
if (editor.getGridField() == field) if (editor.getGridField() == field)
@ -944,6 +998,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return null; return null;
} }
/**
* load toolbar buttons from AD_ToolBarButton
*/
private void loadToolbarButtons() { private void loadToolbarButtons() {
//get extra toolbar process buttons //get extra toolbar process buttons
MToolBarButton[] mToolbarButtons = MToolBarButton.getProcessButtonOfTab(gridTab.getAD_Tab_ID(), null); MToolBarButton[] mToolbarButtons = MToolBarButton.getProcessButtonOfTab(gridTab.getAD_Tab_ID(), null);
@ -976,7 +1033,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* Validate display properties of fields of current row. * Update state of fields (visibility, style, writable, etc)
* @param col * @param col
*/ */
@Override @Override
@ -987,6 +1044,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return; return;
} }
//css animation for slide
if (form.getSclass() != null && form.getSclass().contains(SLIDE_RIGHT_OUT_CSS)) { if (form.getSclass() != null && form.getSclass().contains(SLIDE_RIGHT_OUT_CSS)) {
Executions.schedule(getDesktop(), e -> { Executions.schedule(getDesktop(), e -> {
LayoutUtils.removeSclass(SLIDE_RIGHT_OUT_CSS, form); LayoutUtils.removeSclass(SLIDE_RIGHT_OUT_CSS, form);
@ -1104,7 +1162,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
//hide row if all editor within the row is invisible in Tabbox grid //hide row if all editor within the row is invisible in Tabbox grid
for(Grid tabForm: tabForms) { for(Grid tabForm: tabGroupForms) {
List<Component> tabrows = tabForm.getRows().getChildren(); List<Component> tabrows = tabForm.getRows().getChildren();
for (Component comp : tabrows) for (Component comp : tabrows)
{ {
@ -1217,6 +1275,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
LayoutUtils.removeSclass(SLIDE_RIGHT_IN_CSS, form); LayoutUtils.removeSclass(SLIDE_RIGHT_IN_CSS, form);
} }
/**
* echo set selected node event for tree
*/
private void echoDeferSetSelectedNodeEvent() { private void echoDeferSetSelectedNodeEvent() {
if (getAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR) == null) { if (getAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR) == null) {
setAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR, Boolean.TRUE); setAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR, Boolean.TRUE);
@ -1225,7 +1286,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* @return String * @return display logic
*/ */
@Override @Override
public String getDisplayLogic() public String getDisplayLogic()
@ -1234,7 +1295,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* @return String * @return title of tab
*/ */
@Override @Override
public String getTitle() public String getTitle()
@ -1244,6 +1305,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/** /**
* @param variableName * @param variableName
* @return value
*/ */
@Override @Override
public String get_ValueAsString(String variableName) public String get_ValueAsString(String variableName)
@ -1270,7 +1332,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* @return The record ID of this Tabpanel * @return The record ID of current row
*/ */
@Override @Override
public int getRecord_ID() public int getRecord_ID()
@ -1280,7 +1342,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/** /**
* Is panel need refresh * Is panel need refresh
* @return boolean * @return true if GridTab need refresh
*/ */
@Override @Override
public boolean isCurrent() public boolean isCurrent()
@ -1298,7 +1360,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* Retrieve from db * Retrieve from db.
* Delegate to {@link GridTab#query(boolean)}
*/ */
@Override @Override
public void query() public void query()
@ -1311,9 +1374,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/** /**
* Retrieve from db * Retrieve from db
* @param onlyCurrentRows * @param onlyCurrentRows True to show only unprocessed or the one updated within x days (default is 1 day before today)
* @param onlyCurrentDays * @param onlyCurrentDays if > 0, filter records with created >= current_date - onlyCurrentDays
* @param maxRows * @param maxRows if > 0, maximum number of rows to load
*/ */
@Override @Override
public void query (boolean onlyCurrentRows, int onlyCurrentDays, int maxRows) public void query (boolean onlyCurrentRows, int onlyCurrentDays, int maxRows)
@ -1339,7 +1402,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* reset detail data grid for new parent record that's not saved yet * Reset detail data grid for new parent record that's not saved yet.
* Delegate to {@link GridTab#resetDetailForNewParentRecord()}.
*/ */
@Override @Override
public void resetDetailForNewParentRecord () public void resetDetailForNewParentRecord ()
@ -1364,7 +1428,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* @return TreePanel * @return ADTreePanel
*/ */
public ADTreePanel getTreePanel() public ADTreePanel getTreePanel()
{ {
@ -1372,7 +1436,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* @return TreePanel * @return master, detail or both
*/ */
public String getTreeDisplayedOn() public String getTreeDisplayedOn()
{ {
@ -1434,7 +1498,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* * Delegate to {@link #focusToEditor(WEditor, boolean)}
* @param checkCurrent * @param checkCurrent
*/ */
public void focusToFirstEditor(boolean checkCurrent) { public void focusToFirstEditor(boolean checkCurrent) {
@ -1535,6 +1599,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* handle open/close event of south panel (detail pane)
* @param event
*/
private void onSouthEvent(SouthEvent event) { private void onSouthEvent(SouthEvent event) {
if (event == SouthEvent.OPEN || event == SouthEvent.CLOSE) { if (event == SouthEvent.OPEN || event == SouthEvent.CLOSE) {
boolean open = event == SouthEvent.OPEN ? true : false; boolean open = event == SouthEvent.OPEN ? true : false;
@ -1563,6 +1631,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* @return true if detail pane is open/visible
*/
private boolean isOpenDetailPane() { private boolean isOpenDetailPane() {
if (isMobile()) if (isMobile())
return false; return false;
@ -1578,6 +1649,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return open; return open;
} }
/**
* @return height of detail pane from user preference
*/
private String heigthDetailPane() { private String heigthDetailPane() {
String height = null; String height = null;
int windowId = getGridTab().getAD_Window_ID(); int windowId = getGridTab().getAD_Window_ID();
@ -1588,6 +1662,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return height; return height;
} }
/**
* @return width of tree panel from user preference (or default if no user preference)
*/
private String widthTreePanel() { private String widthTreePanel() {
String width = null; String width = null;
int windowId = getGridTab().getAD_Window_ID(); int windowId = getGridTab().getAD_Window_ID();
@ -1597,6 +1674,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return Util.isEmpty(width) ? DEFAULT_PANEL_WIDTH : width; return Util.isEmpty(width) ? DEFAULT_PANEL_WIDTH : width;
} }
/**
* Navigate to a tree node
* @param value
*/
private void navigateTo(DefaultTreeNode<MTreeNode> value) { private void navigateTo(DefaultTreeNode<MTreeNode> value) {
MTreeNode treeNode = value.getData(); MTreeNode treeNode = value.getData();
// We Have a TreeNode // We Have a TreeNode
@ -1654,7 +1735,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
Dialog.error(windowNo, msg); Dialog.error(windowNo, msg);
} }
} }
//if (col >= 0)
//update UI state
if (!uiCreated) if (!uiCreated)
createUI(); createUI();
dynamicDisplay(col); dynamicDisplay(col);
@ -1750,6 +1832,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
treePanel.initTree(AD_Tree_ID, windowNo); treePanel.initTree(AD_Tree_ID, windowNo);
} }
} }
//update list view
if (listPanel.isVisible()) { if (listPanel.isVisible()) {
listPanel.updateListIndex(); listPanel.updateListIndex();
listPanel.dynamicDisplay(col); listPanel.dynamicDisplay(col);
@ -1760,6 +1844,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* delete tree node by recordId
* @param recordId
*/
private void deleteNode(int recordId) { private void deleteNode(int recordId) {
if (recordId <= 0) return; if (recordId <= 0) return;
@ -1780,6 +1868,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* add new tree node for current row
*/
private void addNewNode() { private void addNewNode() {
if (gridTab.getRecord_ID() > 0) { if (gridTab.getRecord_ID() > 0) {
String name = (String)gridTab.getValue("Name"); String name = (String)gridTab.getValue("Name");
@ -1817,6 +1908,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* set selected tree node by recordId
* @param recordId
*/
private void setSelectedNode(int recordId) { private void setSelectedNode(int recordId) {
if (recordId <= 0) return; if (recordId <= 0) return;
@ -1921,15 +2016,15 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
static class ZoomListener implements EventListener<Event> { static class ZoomListener implements EventListener<Event> {
private IZoomableEditor searchEditor; private IZoomableEditor zoomableEditor;
ZoomListener(IZoomableEditor editor) { ZoomListener(IZoomableEditor editor) {
searchEditor = editor; zoomableEditor = editor;
} }
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
if (Events.ON_CLICK.equals(event.getName())) { if (Events.ON_CLICK.equals(event.getName())) {
searchEditor.actionZoom(); zoomableEditor.actionZoom();
} }
} }
@ -1980,7 +2075,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* @return boolean * @return true if list/grid view is visible
*/ */
@Override @Override
public boolean isGridView() { public boolean isGridView() {
@ -1989,7 +2084,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/** /**
* *
* @return GridPanel * @return {@link GridView}
*/ */
@Override @Override
public GridView getGridView() { public GridView getGridView() {
@ -2015,6 +2110,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* Show detail pane
*/
private void attachDetailPane() { private void attachDetailPane() {
if (formContainer.getSouth() != null) { if (formContainer.getSouth() != null) {
formContainer.getSouth().setVisible(true); formContainer.getSouth().setVisible(true);
@ -2029,6 +2127,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* Hide detail pane
*/
private void detachDetailPane() { private void detachDetailPane() {
if (formContainer.getSouth() != null) { if (formContainer.getSouth() != null) {
formContainer.getSouth().setVisible(false); formContainer.getSouth().setVisible(false);
@ -2039,8 +2140,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* Get all visible button editors * Get all visible toolbar button editors
* @return List<WButtonEditor> * @return List<Button>
*/ */
public List<Button> getToolbarButtons() { public List<Button> getToolbarButtons() {
List<Button> buttonList = new ArrayList<Button>(); List<Button> buttonList = new ArrayList<Button>();
@ -2085,7 +2186,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* activate current selected detail tab if it is visible * activate selected detail tab if it is visible
*/ */
public void activateDetailIfVisible() { public void activateDetailIfVisible() {
if (isDetailVisible()) { if (isDetailVisible()) {
@ -2110,7 +2211,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/** /**
* *
* @return true if the detailpane is visible * @return true if {@link DetailPane} is visible
*/ */
@Override @Override
public boolean isDetailVisible() { public boolean isDetailVisible() {
@ -2124,7 +2225,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/** /**
* *
* @return true if have one or more detail tabs * @return true if selected tab has one or more detail/child tab
*/ */
public boolean hasDetailTabs() { public boolean hasDetailTabs() {
if (formContainer.getSouth() == null || !formContainer.getSouth().isVisible()) { if (formContainer.getSouth() == null || !formContainer.getSouth().isVisible()) {
@ -2196,6 +2297,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
} }
/**
* @return true if tree is order by value
*/
private boolean isTreeDrivenByValue() { private boolean isTreeDrivenByValue() {
SimpleTreeModel model = (SimpleTreeModel)(TreeModel<?>) treePanel.getTree().getModel(); SimpleTreeModel model = (SimpleTreeModel)(TreeModel<?>) treePanel.getTree().getModel();
boolean retValue = false; boolean retValue = false;
@ -2203,6 +2307,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return retValue; return retValue;
} }
/**
* @return true if value is shown in tree
*/
private boolean isValueDisplayed() { private boolean isValueDisplayed() {
SimpleTreeModel model = (SimpleTreeModel)(TreeModel<?>) treePanel.getTree().getModel(); SimpleTreeModel model = (SimpleTreeModel)(TreeModel<?>) treePanel.getTree().getModel();
boolean retValue = false; boolean retValue = false;
@ -2233,7 +2340,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* * Save user preference for this AD Window
* @param attribute * @param attribute
* @param value * @param value
*/ */
@ -2287,6 +2394,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
}; };
/**
* @return true if client is mobile
*/
protected boolean isMobile() { protected boolean isMobile() {
return ClientInfo.isMobile(); return ClientInfo.isMobile();
} }
@ -2323,7 +2433,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
} }
/** /**
* Set Visibility for Tabbox based on Children and Form Visibility * Set Visibility for {@link #tabbox} based on {@link #form} Visibility and whether {@link #tabbox} is empty
*/ */
private void setGroupTabboxVisibility() { private void setGroupTabboxVisibility() {
boolean isGroupTabVisible = false; boolean isGroupTabVisible = false;

View File

@ -35,29 +35,40 @@ import org.zkoss.zul.Tree;
/** /**
* *
* Tree panel for AD_Tab with HasTree=Y
* @author hengsin * @author hengsin
* *
*/ */
public class ADTreePanel extends Panel implements EventListener<Event> public class ADTreePanel extends Panel implements EventListener<Event>
{ {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 2718257463734592729L; private static final long serialVersionUID = 2718257463734592729L;
/** event to expand/collapse all tree nodes **/
private static final String ON_EXPAND_MENU_EVENT = "onExpandMenu"; private static final String ON_EXPAND_MENU_EVENT = "onExpandMenu";
/** search/lookup panel for tree **/
private TreeSearchPanel pnlSearch; private TreeSearchPanel pnlSearch;
private Tree tree; private Tree tree;
/** ToolBarButton to expand or collapse all tree nodes **/
private ToolBarButton expandToggle; // Elaine 2009/02/27 - expand tree private ToolBarButton expandToggle; // Elaine 2009/02/27 - expand tree
private int m_windowno = -1; private int m_windowno = -1;
private int m_tabno = -1; private int m_tabno = -1;
private int AD_Tree_ID = -1; private int AD_Tree_ID = -1;
/**
* default constructor
*/
public ADTreePanel() public ADTreePanel()
{ {
init(); init();
} }
/**
* @param windowno
* @param tabno
*/
public ADTreePanel(int windowno, int tabno) public ADTreePanel(int windowno, int tabno)
{ {
m_windowno = windowno; m_windowno = windowno;
@ -66,6 +77,7 @@ public class ADTreePanel extends Panel implements EventListener<Event>
} }
/** /**
* Delegate to {@link #initTree(int, int, String, int)}
* @param AD_Tree_ID * @param AD_Tree_ID
* @param windowNo * @param windowNo
*/ */
@ -74,6 +86,14 @@ public class ADTreePanel extends Panel implements EventListener<Event>
return initTree(AD_Tree_ID, windowNo, null, 0); return initTree(AD_Tree_ID, windowNo, null, 0);
} }
/**
* Init and load tree
* @param AD_Tree_ID
* @param windowNo
* @param linkColName
* @param linkID
* @return true if a new tree have been created and loaded, false if AD_Tree_ID have already been loaded
*/
public boolean initTree(int AD_Tree_ID, int windowNo, String linkColName, int linkID) public boolean initTree(int AD_Tree_ID, int windowNo, String linkColName, int linkID)
{ {
if (this.AD_Tree_ID != AD_Tree_ID) if (this.AD_Tree_ID != AD_Tree_ID)
@ -89,6 +109,9 @@ public class ADTreePanel extends Panel implements EventListener<Event>
return false; return false;
} }
/**
* Layout panel
*/
private void init() private void init()
{ {
setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "treepanel"); setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "treepanel");
@ -191,6 +214,9 @@ public class ADTreePanel extends Panel implements EventListener<Event>
} }
// //
/**
* Reset AD_Tree_ID to create a new Tree in next {@link #initTree(int, int, String, int)} call
*/
public void prepareForRefresh() { public void prepareForRefresh() {
this.AD_Tree_ID = -1; this.AD_Tree_ID = -1;
} }

View File

@ -38,30 +38,37 @@ import org.compiere.util.Env;
import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Component;
/** /**
* * UI part for AD_Window
* @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a> * @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a>
* @date Feb 25, 2007 * @date Feb 25, 2007
* @version $Revision: 0.10 $ * @version $Revision: 0.10 $
*/ */
public class ADWindow extends AbstractUIPart public class ADWindow extends AbstractUIPart
{ {
/** Component attribute to hold reference to ancestor ADWindow instance **/
public static final String AD_WINDOW_ATTRIBUTE_KEY = "org.adempiere.webui.adwindow"; public static final String AD_WINDOW_ATTRIBUTE_KEY = "org.adempiere.webui.adwindow";
/** Content part for AD_Window (toolbar, tabbox, statusbar, etc) **/
private ADWindowContent windowContent; private ADWindowContent windowContent;
/** Environment Context **/
private Properties ctx; private Properties ctx;
/** AD_Window_ID **/
private int adWindowId; private int adWindowId;
private String _title; private String windowTitle;
private int windowNo; private int windowNo;
/** initial query when AD Window is first open **/
private MQuery query; private MQuery query;
/** main component of ADWindowContent **/
private Component windowPanelComponent; private Component windowPanelComponent;
/** image for window (desktop tab) title **/
private MImage image; private MImage image;
/** AD_Tab_ID:BtnComponentName. List of toolbar buttons to exclude, loaded from AD_ToolBarButtonRestrict **/
private Map<Integer, List<String>> tabToolbarRestricMap = new HashMap<Integer, List<String>>(); private Map<Integer, List<String>> tabToolbarRestricMap = new HashMap<Integer, List<String>>();
/** List of BtnComponentName to exclude, loaded from AD_ToolBarButtonRestrict **/
private List<String> windowToolbarRestrictList = null; private List<String> windowToolbarRestrictList = null;
/** List of advanced (IsAdvancedButton=Y) window toolbar buttons. Accessible by advanced role only. **/
private List<String> windowToolbarAdvancedList = null; private List<String> windowToolbarAdvancedList = null;
/** AD_Window_UU value **/
private String adWindowUUID; private String adWindowUUID;
/** /**
@ -100,11 +107,14 @@ public class ADWindow extends AbstractUIPart
} }
} }
/**
* Init ADWindowContent
*/
private void init() private void init()
{ {
windowContent = new ADWindowContent(ctx, windowNo, adWindowId); windowContent = new ADWindowContent(ctx, windowNo, adWindowId);
windowContent.setADWindow(this); windowContent.setADWindow(this);
_title = windowContent.getTitle(); windowTitle = windowContent.getTitle();
image = windowContent.getImage(); image = windowContent.getImage();
} }
@ -114,18 +124,22 @@ public class ADWindow extends AbstractUIPart
*/ */
public String getTitle() public String getTitle()
{ {
return _title; return windowTitle;
} }
/** /**
* * @return image for window title
* @return image for the country
*/ */
public MImage getMImage() public MImage getMImage()
{ {
return image; return image;
} }
/**
* Create component for content part (ADWindowContent).
* @see ADWindowContent#createPart(Object)
*/
@Override
protected Component doCreatePart(Component parent) protected Component doCreatePart(Component parent)
{ {
windowPanelComponent = windowContent.createPart(parent); windowPanelComponent = windowContent.createPart(parent);
@ -154,6 +168,10 @@ public class ADWindow extends AbstractUIPart
return windowContent; return windowContent;
} }
/**
* @param AD_Tab_ID
* @return list of toolbar button to exclude/restrict for current login role
*/
public List<String> getTabToolbarRestrictList(int AD_Tab_ID) { public List<String> getTabToolbarRestrictList(int AD_Tab_ID) {
List<String> tabRestrictList = tabToolbarRestricMap.get(AD_Tab_ID); List<String> tabRestrictList = tabToolbarRestricMap.get(AD_Tab_ID);
if (tabRestrictList == null) { if (tabRestrictList == null) {
@ -174,6 +192,9 @@ public class ADWindow extends AbstractUIPart
return tabRestrictList; return tabRestrictList;
} }
/**
* @return list of window toolbar button to exclude/restrict for current login role
*/
public List<String> getWindowToolbarRestrictList() { public List<String> getWindowToolbarRestrictList() {
if (windowToolbarRestrictList == null) { if (windowToolbarRestrictList == null) {
//load window restriction //load window restriction
@ -192,6 +213,9 @@ public class ADWindow extends AbstractUIPart
return windowToolbarRestrictList; return windowToolbarRestrictList;
} }
/**
* @return list of advance (IsAdvancedButton=Y) toolbar buttons for window
*/
public List<String> getWindowAdvancedButtonList() { public List<String> getWindowAdvancedButtonList() {
if (windowToolbarAdvancedList == null) { if (windowToolbarAdvancedList == null) {
//load window advance buttons //load window advance buttons
@ -207,18 +231,23 @@ public class ADWindow extends AbstractUIPart
return windowToolbarAdvancedList; return windowToolbarAdvancedList;
} }
/**
* @return AD_Window_ID
*/
public int getAD_Window_ID() { public int getAD_Window_ID() {
return adWindowId; return adWindowId;
} }
/**
* @return AD_Window_UU
*/
public String getAD_Window_UU() { public String getAD_Window_UU() {
return adWindowUUID; return adWindowUUID;
} }
/** /**
*
* @param windowNo * @param windowNo
* @return adwindow instance for windowNo ( if any ) * @return {@link ADWindow} instance for windowNo ( if any )
*/ */
public static ADWindow get(int windowNo) { public static ADWindow get(int windowNo) {
Object window = SessionManager.getAppDesktop().findWindow(windowNo); Object window = SessionManager.getAppDesktop().findWindow(windowNo);
@ -229,8 +258,9 @@ public class ADWindow extends AbstractUIPart
} }
/** /**
* Find ADWindow instance that's the ancestor of comp
* @param comp * @param comp
* @return adwindow instance if found, null otherwise * @return {@link ADWindow} instance if found, null otherwise
*/ */
public static ADWindow findADWindow(Component comp) { public static ADWindow findADWindow(Component comp) {
Component parent = comp; Component parent = comp;

View File

@ -46,7 +46,8 @@ import org.zkoss.zul.Tab;
import org.zkoss.zul.Vlayout; import org.zkoss.zul.Vlayout;
/** /**
* * Content area of {@link ADWindow}.
*
* This class is based on org.compiere.apps.APanel written by Jorg Janke. * This class is based on org.compiere.apps.APanel written by Jorg Janke.
* @author Jorg Janke * @author Jorg Janke
* *
@ -60,15 +61,26 @@ public class ADWindowContent extends AbstractADWindowContent
@SuppressWarnings("unused") @SuppressWarnings("unused")
private static final CLogger logger = CLogger.getCLogger(ADWindowContent.class); private static final CLogger logger = CLogger.getCLogger(ADWindowContent.class);
/** Main layout component **/
private Vlayout layout; private Vlayout layout;
/** Center Div of {@link #layout}, host {@link CompositeADTabbox} **/
private Div contentArea; private Div contentArea;
/**
* @param ctx
* @param windowNo
* @param adWindowId
*/
public ADWindowContent(Properties ctx, int windowNo, int adWindowId) public ADWindowContent(Properties ctx, int windowNo, int adWindowId)
{ {
super(ctx, windowNo, adWindowId); super(ctx, windowNo, adWindowId);
} }
/**
* Layout UI.
* Vertical layout of toolbar, breadCrumb, statusBar and {@link #contentArea}.
*/
@Override
protected Component doCreatePart(Component parent) protected Component doCreatePart(Component parent)
{ {
layout = new ADWindowVlayout(this); layout = new ADWindowVlayout(this);
@ -100,6 +112,7 @@ public class ADWindowContent extends AbstractADWindowContent
LayoutUtils.addSclass("adwindow-status", statusBar); LayoutUtils.addSclass("adwindow-status", statusBar);
//IADTabbox
contentArea = new Div(); contentArea = new Div();
contentArea.setParent(layout); contentArea.setParent(layout);
ZKUpdateUtil.setVflex(contentArea, "1"); ZKUpdateUtil.setVflex(contentArea, "1");
@ -119,12 +132,21 @@ public class ADWindowContent extends AbstractADWindowContent
return layout; return layout;
} }
/**
* Create {@link CompositeADTabbox}
*/
@Override
protected IADTabbox createADTab() protected IADTabbox createADTab()
{ {
CompositeADTabbox composite = new CompositeADTabbox(); CompositeADTabbox composite = new CompositeADTabbox();
return composite; return composite;
} }
/**
* Get main layout component
* @return {@link Vlayout}
*/
@Override
public Vlayout getComponent() { public Vlayout getComponent() {
return layout; return layout;
} }
@ -133,6 +155,7 @@ public class ADWindowContent extends AbstractADWindowContent
* @param event * @param event
* @see EventListener#onEvent(Event) * @see EventListener#onEvent(Event)
*/ */
@Override
public void onEvent(Event event) { public void onEvent(Event event) {
if (Events.ON_CTRL_KEY.equals(event.getName())) { if (Events.ON_CTRL_KEY.equals(event.getName())) {
KeyEvent keyEvent = (KeyEvent) event; KeyEvent keyEvent = (KeyEvent) event;
@ -154,6 +177,9 @@ public class ADWindowContent extends AbstractADWindowContent
} }
} }
/**
* ITabOnCloseHandler to call {@link ADWindowContent#onExit(Callback)} when user wants to close an AD Window
*/
class TabOnCloseHanlder implements ITabOnCloseHandler, Callback<Boolean> { class TabOnCloseHanlder implements ITabOnCloseHandler, Callback<Boolean> {
private Tabpanel tabPanel; private Tabpanel tabPanel;
public void onClose(Tabpanel tabPanel) { public void onClose(Tabpanel tabPanel) {
@ -170,8 +196,8 @@ public class ADWindowContent extends AbstractADWindowContent
} }
/** /**
* close tab contain this window * Close tab related to tabPanel
* @param tabPanel * @param tabPanel Tabpanel that represent AD_Window
*/ */
protected void closeTab (Tabpanel tabPanel) { protected void closeTab (Tabpanel tabPanel) {
Tab tab = tabPanel.getLinkedTab(); Tab tab = tabPanel.getLinkedTab();
@ -180,6 +206,9 @@ public class ADWindowContent extends AbstractADWindowContent
SessionManager.getAppDesktop().unregisterWindow(getWindowNo()); SessionManager.getAppDesktop().unregisterWindow(getWindowNo());
} }
/**
* Vlayout subclass to override onPageDetached.
*/
public static class ADWindowVlayout extends Vlayout implements IHelpContext { public static class ADWindowVlayout extends Vlayout implements IHelpContext {
/** /**
* generated serial id * generated serial id
@ -192,6 +221,9 @@ public class ADWindowContent extends AbstractADWindowContent
this.content = content; this.content = content;
} }
/**
* clean up listeners
*/
@Override @Override
public void onPageDetached(Page page) { public void onPageDetached(Page page) {
super.onPageDetached(page); super.onPageDetached(page);

View File

@ -77,7 +77,7 @@ import org.zkoss.zul.Toolbarbutton;
import org.zkoss.zul.impl.LabelImageElement; import org.zkoss.zul.impl.LabelImageElement;
/** /**
* * Toolbar of AD_Window
* @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a> * @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a>
* @date Feb 25, 2007 * @date Feb 25, 2007
* @version $Revision: 0.10 $ * @version $Revision: 0.10 $
@ -86,21 +86,29 @@ import org.zkoss.zul.impl.LabelImageElement;
* <li>FR [ 2076330 ] Add new methods in CWindowToolbar class * <li>FR [ 2076330 ] Add new methods in CWindowToolbar class
*/ */
public class ADWindowToolbar extends FToolbar implements EventListener<Event> public class ADWindowToolbar extends FToolbar implements EventListener<Event>
{ {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = -5151981978053022864L; private static final long serialVersionUID = -5151981978053022864L;
/**
* Attribute for {@link #overflowPopup} to store the last close timestamp in ms.
* Use to prevent too rapid open and close of overflow popup.
*/
private static final String POPUP_CLOSE_TIMESTAMP_ATTR = "popup.close";
/** Prefix for Button Name **/
public static final String BTNPREFIX = "Btn"; public static final String BTNPREFIX = "Btn";
@Deprecated(forRemoval = true, since = "11")
public static final String MNITMPREFIX = "Mnitm"; public static final String MNITMPREFIX = "Mnitm";
private static final CLogger log = CLogger.getCLogger(ADWindowToolbar.class); private static final CLogger log = CLogger.getCLogger(ADWindowToolbar.class);
/** Search messages using translation */ /** translated message for new query label (default for en is "** New Query **") */
private String m_sNew; private String m_sNew;
/** combobox to select user query **/
private Combobox fQueryName; private Combobox fQueryName;
private MUserQuery[] userQueries; private MUserQuery[] userQueries;
private MUserQuery selectedUserQuery; private MUserQuery selectedUserQuery;
@ -136,25 +144,31 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
private ToolBarButton btnProcess; private ToolBarButton btnProcess;
private ToolBarButton btnQuickForm; private ToolBarButton btnQuickForm;
/** button to open overflow popup for toolbar buttons with IsShowMore=Y (for non-mobile client) **/
private ToolBarButton btnShowMore; private ToolBarButton btnShowMore;
/** Button Name:ToolBarButton. Map for all buttons **/
private HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>(); private HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>();
/**
* For mobile client, the list of toolbar button with IsShowMore=Y is first keep here
* and move to {@link #overflows} in {@link #onOverflowButton(Event) event}
* */
private ArrayList<ToolBarButton> mobileShowMoreButtons = new ArrayList<ToolBarButton>(); private ArrayList<ToolBarButton> mobileShowMoreButtons = new ArrayList<ToolBarButton>();
/** All register toolbar listener **/
// private ToolBarButton btnExit;
private ArrayList<ToolbarListener> listeners = new ArrayList<ToolbarListener>(); private ArrayList<ToolbarListener> listeners = new ArrayList<ToolbarListener>();
/** current ON_Click event that's being handle **/
private Event event; private Event event;
/** shortcut key map without ctrl and alt **/
private Map<Integer, ToolBarButton> keyMap = new HashMap<Integer, ToolBarButton>(); private Map<Integer, ToolBarButton> keyMap = new HashMap<Integer, ToolBarButton>();
/** alt+key shortcut map **/
private Map<Integer, ToolBarButton> altKeyMap = new HashMap<Integer, ToolBarButton>(); private Map<Integer, ToolBarButton> altKeyMap = new HashMap<Integer, ToolBarButton>();
/** ctrl+key shortcut map **/
private Map<Integer, ToolBarButton> ctrlKeyMap = new HashMap<Integer, ToolBarButton>(); private Map<Integer, ToolBarButton> ctrlKeyMap = new HashMap<Integer, ToolBarButton>();
/** list of custom toolbar button (IsCustomization=Y) **/
private List<ToolbarCustomButton> toolbarCustomButtons = new ArrayList<ToolbarCustomButton>(); private List<ToolbarCustomButton> toolbarCustomButtons = new ArrayList<ToolbarCustomButton>();
/** Restriction list for window, loaded from AD_ToolBarButtonRestrict **/
private List<String> restrictionList; private List<String> restrictionList;
/** List of toolbar button with IsAdvanced=Y **/
private List<String> advancedList; private List<String> advancedList;
// Elaine 2008/12/04 // Elaine 2008/12/04
@ -163,34 +177,45 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
private boolean isAllowProductInfo = MRole.getDefault().canAccess_Info_Product(); private boolean isAllowProductInfo = MRole.getDefault().canAccess_Info_Product();
private int windowNo = 0; private int windowNo = 0;
/** previous key event time in ms **/
private long prevKeyEventTime = 0; private long prevKeyEventTime = 0;
/**
* Previous key event.
* Use together with prevKeyEventTime to detect double fire of key event from browser
*/
private KeyEvent prevKeyEvent; private KeyEvent prevKeyEvent;
// Maintain hierarchical Quick form by its parent-child tab while open leaf /**
// tab once & dispose and doing same action * Maintain hierarchical Quick form by its parent-child tab while open leaf
private int quickFormTabHrchyLevel = 0; * tab once & dispose and doing same action
*/
private A overflowButton; private int quickFormTabHrchyLevel = 0;
/** show more button for mobile client **/
private A mobileOverflowButton;
/**
* list of toolbar button overflow to show more popup (or with IsShowMore=Y).
* Use for both mobile and desktop client.
* */
private ArrayList<ToolBarButton> overflows; private ArrayList<ToolBarButton> overflows;
/** Popup for overflow/IsShowMore=Y toolbar buttons. Use for both mobile and desktop client. **/
private Popup overflowPopup; private Popup overflowPopup;
/** width of toolbar from previous ON_AFTER_SIZE event **/
private int prevWidth; private int prevWidth;
/** AD Window content part that own this toolbar **/
private AbstractADWindowContent windowContent; private AbstractADWindowContent windowContent;
/** Last Modifier of Action Event */ /**
// public int lastModifiers; * default constructor
// */
public ADWindowToolbar() public ADWindowToolbar()
{ {
this(null, 0); this(null, 0);
} }
/**
* @param windowContent
* @param windowNo
*/
public ADWindowToolbar(AbstractADWindowContent windowContent, int windowNo) { public ADWindowToolbar(AbstractADWindowContent windowContent, int windowNo) {
this.windowContent = windowContent; this.windowContent = windowContent;
setWindowNo(windowNo); setWindowNo(windowNo);
@ -200,6 +225,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Init components
*/
private void init() private void init()
{ {
LayoutUtils.addSclass("adwindow-toolbar", this); LayoutUtils.addSclass("adwindow-toolbar", this);
@ -292,6 +320,8 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
if (!ClientInfo.isMobile()) if (!ClientInfo.isMobile())
overflows = new ArrayList<ToolBarButton>(); overflows = new ArrayList<ToolBarButton>();
//Window toolbar buttons from AD_ToolBarButton
MToolBarButton[] officialButtons = MToolBarButton.getToolbarButtons("W", null); MToolBarButton[] officialButtons = MToolBarButton.getToolbarButtons("W", null);
for (MToolBarButton button : officialButtons) { for (MToolBarButton button : officialButtons) {
if (!button.isActive() || !hasAccess(BTNPREFIX+button.getComponentName())) { if (!button.isActive() || !hasAccess(BTNPREFIX+button.getComponentName())) {
@ -374,7 +404,13 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
ZKUpdateUtil.setWidth(this, "100%"); ZKUpdateUtil.setWidth(this, "100%");
} }
/**
* Create new toolbar button
* @param name button name (BTNPREFIX+name)
* @param image image name (images/image+suffix) or iconSclass name (z-icon-image)
* @param tooltip
* @return {@link ToolBarButton}
*/
private ToolBarButton createButton(String name, String image, String tooltip) private ToolBarButton createButton(String name, String image, String tooltip)
{ {
ToolBarButton btn = new ToolBarButton(""); ToolBarButton btn = new ToolBarButton("");
@ -415,11 +451,21 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
return btn; return btn;
} }
/**
* Get ToolBarButton by name
* @param name
* @return {@link ToolBarButton} or null
*/
public ToolBarButton getButton(String name) public ToolBarButton getButton(String name)
{ {
return buttons.get(name); return buttons.get(name);
} }
/**
* Get ToolBarButton by name
* @param name
* @return {@link LabelImageElement} or null
*/
public LabelImageElement getToolbarItem(String name) public LabelImageElement getToolbarItem(String name)
{ {
return buttons.get(name); return buttons.get(name);
@ -453,6 +499,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
public static final int VK_Y = 0x59; public static final int VK_Y = 0x59;
public static final int VK_Z = 0x5A; public static final int VK_Z = 0x5A;
/**
* Configure shortcut key for each button
*/
private void configureKeyMap() private void configureKeyMap()
{ {
altKeyMap.put(VK_H, btnHelp); altKeyMap.put(VK_H, btnHelp);
@ -473,6 +522,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
altKeyMap.put(VK_L, btnCustomize); altKeyMap.put(VK_L, btnCustomize);
} }
/**
* Add separator/spacer between button
*/
protected void addSeparator() protected void addSeparator()
{ {
Space s = new Space(); Space s = new Space();
@ -481,16 +533,25 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
this.appendChild(s); this.appendChild(s);
} }
/**
* Add ToolbarListener
* @param toolbarListener
*/
public void addListener(ToolbarListener toolbarListener) public void addListener(ToolbarListener toolbarListener)
{ {
listeners.add(toolbarListener); listeners.add(toolbarListener);
} }
/**
* Remove ToolbarListener
* @param toolbarListener
*/
public void removeListener(ToolbarListener toolbarListener) public void removeListener(ToolbarListener toolbarListener)
{ {
listeners.remove(toolbarListener); listeners.remove(toolbarListener);
} }
@Override
public void onEvent(Event event) public void onEvent(Event event)
{ {
String eventName = event.getName(); String eventName = event.getName();
@ -547,6 +608,11 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Handle ON_Click event for button.
* Call register {@link ToolbarListener}.
* @param event
*/
private void doOnClick(Event event) { private void doOnClick(Event event) {
this.event = event; this.event = event;
String compName; String compName;
@ -590,127 +656,216 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
this.event = null; this.event = null;
} }
/**
* Enable/disable buttons for navigation between parent and detail tab
* @param enabled
*/
public void enableTabNavigation(boolean enabled) public void enableTabNavigation(boolean enabled)
{ {
enableTabNavigation(enabled, enabled); enableTabNavigation(enabled, enabled);
} }
/**
* Enable/disable buttons for navigation between parent and detail tab
* @param enableParent
* @param enableDetail
*/
public void enableTabNavigation(boolean enableParent, boolean enableDetail) public void enableTabNavigation(boolean enableParent, boolean enableDetail)
{ {
this.btnParentRecord.setDisabled(!enableParent); this.btnParentRecord.setDisabled(!enableParent);
this.btnDetailRecord.setDisabled(!enableDetail); this.btnDetailRecord.setDisabled(!enableDetail);
} }
/**
* Enable/disable Refresh button
* @param enabled
*/
public void enableRefresh(boolean enabled) public void enableRefresh(boolean enabled)
{ {
this.btnRefresh.setDisabled(!enabled); this.btnRefresh.setDisabled(!enabled);
} }
/**
* Enable/disable Save button
* @param enabled
*/
public void enableSave(boolean enabled) public void enableSave(boolean enabled)
{ {
this.btnSave.setDisabled(!enabled); this.btnSave.setDisabled(!enabled);
this.btnSaveAndCreate.setDisabled(!(isNewEnabled() || isSaveEnable())); this.btnSaveAndCreate.setDisabled(!(isNewEnabled() || isSaveEnable()));
} }
/**
* @return true if Save button is enable
*/
public boolean isSaveEnable() { public boolean isSaveEnable() {
return !btnSave.isDisabled(); return !btnSave.isDisabled();
} }
// public void enableExit(boolean enabled) /**
// { * Enable/disable Delete button
// this.btnExit.setDisabled(!enabled); * @param enabled
// } */
public void enableDelete(boolean enabled) public void enableDelete(boolean enabled)
{ {
this.btnDelete.setDisabled(!enabled); this.btnDelete.setDisabled(!enabled);
} }
/**
* @return true if Delete button is enable
*/
public boolean isDeleteEnable() public boolean isDeleteEnable()
{ {
return !btnDelete.isDisabled(); return !btnDelete.isDisabled();
} }
/**
* @return true if New button is enable
*/
public boolean isNewEnabled() { public boolean isNewEnabled() {
return !btnNew.isDisabled(); return !btnNew.isDisabled();
} }
/**
* Enable/disable Ignore/Undo button
* @param enabled
*/
public void enableIgnore(boolean enabled) public void enableIgnore(boolean enabled)
{ {
this.btnIgnore.setDisabled(!enabled); this.btnIgnore.setDisabled(!enabled);
} }
/**
* Enable/disable New button
* @param enabled
*/
public void enableNew(boolean enabled) public void enableNew(boolean enabled)
{ {
this.btnNew.setDisabled(!enabled); this.btnNew.setDisabled(!enabled);
this.btnSaveAndCreate.setDisabled(!(isNewEnabled() || isSaveEnable())); this.btnSaveAndCreate.setDisabled(!(isNewEnabled() || isSaveEnable()));
} }
/**
* Enable/disable Copy/Duplicate button
* @param enabled
*/
public void enableCopy(boolean enabled) public void enableCopy(boolean enabled)
{ {
this.btnCopy.setDisabled(!enabled); this.btnCopy.setDisabled(!enabled);
} }
/**
* Enable/disable Attachment button
* @param enabled
*/
public void enableAttachment(boolean enabled) public void enableAttachment(boolean enabled)
{ {
this.btnAttachment.setDisabled(!enabled); this.btnAttachment.setDisabled(!enabled);
} }
/**
* Enable/disable Chat button
* @param enabled
*/
public void enableChat(boolean enabled) public void enableChat(boolean enabled)
{ {
this.btnChat.setDisabled(!enabled); this.btnChat.setDisabled(!enabled);
} }
/**
* Enable/disable Print button
* @param enabled
*/
public void enablePrint(boolean enabled) public void enablePrint(boolean enabled)
{ {
this.btnPrint.setDisabled(!enabled); this.btnPrint.setDisabled(!enabled);
} }
/**
* Enable/disable Report button
* @param enabled
*/
public void enableReport(boolean enabled) public void enableReport(boolean enabled)
{ {
this.btnReport.setDisabled(!enabled); this.btnReport.setDisabled(!enabled);
} }
/**
* Enable/disable Find/Query button
* @param enabled
*/
public void enableFind(boolean enabled) public void enableFind(boolean enabled)
{ {
this.btnFind.setDisabled(!enabled); this.btnFind.setDisabled(!enabled);
} }
/**
* Enable/disable Toggle button
* @param enabled
*/
public void enableGridToggle(boolean enabled) public void enableGridToggle(boolean enabled)
{ {
btnGridToggle.setDisabled(!enabled); btnGridToggle.setDisabled(!enabled);
} }
/**
* Enable/disable Customize Grid button
* @param enabled
*/
public void enableCustomize(boolean enabled) public void enableCustomize(boolean enabled)
{ {
btnCustomize.setDisabled(!enabled); btnCustomize.setDisabled(!enabled);
} }
/**
* Enable/disable Archive button
* @param enabled
*/
public void enableArchive(boolean enabled) public void enableArchive(boolean enabled)
{ {
btnArchive.setDisabled(!enabled); btnArchive.setDisabled(!enabled);
} }
/**
* Enable/disable Zoom Across button
* @param enabled
*/
public void enableZoomAcross(boolean enabled) public void enableZoomAcross(boolean enabled)
{ {
btnZoomAcross.setDisabled(!enabled); btnZoomAcross.setDisabled(!enabled);
} }
/**
* Enable/disable Active Workflows button
* @param enabled
*/
public void enableActiveWorkflows(boolean enabled) public void enableActiveWorkflows(boolean enabled)
{ {
btnActiveWorkflows.setDisabled(!enabled); btnActiveWorkflows.setDisabled(!enabled);
} }
/**
* Enable/disable Requests button
* @param enabled
*/
public void enableRequests(boolean enabled) public void enableRequests(boolean enabled)
{ {
btnRequests.setDisabled(!enabled); btnRequests.setDisabled(!enabled);
} }
/**
* Enable/disable Quick Form button
* @param enabled
*/
public void enableQuickForm(boolean enabled) public void enableQuickForm(boolean enabled)
{ {
btnQuickForm.setDisabled(!enabled); btnQuickForm.setDisabled(!enabled);
} }
/**
* Turn on/off Lock button (Pressed=On, Not Pressed=Off)
* @param enabled
*/
public void lock(boolean locked) public void lock(boolean locked)
{ {
setPressed("Lock", locked); setPressed("Lock", locked);
@ -735,13 +890,17 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Enable/disable Post It Note button
* @param enabled
*/
public void enablePostIt(boolean enabled) public void enablePostIt(boolean enabled)
{ {
this.btnPostIt.setDisabled(!enabled); this.btnPostIt.setDisabled(!enabled);
} }
/** /**
* Enable/disable the label button * Enable/disable Label record button
* @param enabled * @param enabled
*/ */
public void enableLabel(boolean enabled) public void enableLabel(boolean enabled)
@ -749,11 +908,18 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
this.btnLabel.setDisabled(!enabled); this.btnLabel.setDisabled(!enabled);
} }
/**
* @return ON_Click event that's being handle
*/
public Event getEvent() public Event getEvent()
{ {
return event; return event;
} }
/**
* Handle shortcut key event
* @param keyEvent
*/
private void onCtrlKeyEvent(KeyEvent keyEvent) { private void onCtrlKeyEvent(KeyEvent keyEvent) {
if (windowContent != null && windowContent.isBlock()) if (windowContent != null && windowContent.isBlock())
return; return;
@ -810,6 +976,11 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
fireButtonClickEvent(keyEvent, btn); fireButtonClickEvent(keyEvent, btn);
} }
/**
* Fire ON_Click event for button, trigger by shortcut key event.
* @param keyEvent source shortcut key event
* @param btn
*/
private void fireButtonClickEvent(KeyEvent keyEvent, ToolBarButton btn) private void fireButtonClickEvent(KeyEvent keyEvent, ToolBarButton btn)
{ {
if (btn != null) { if (btn != null) {
@ -823,7 +994,7 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
/** /**
* * Make all toolbar buttons visible
* @param visible * @param visible
*/ */
public void setVisibleAll(boolean visible) public void setVisibleAll(boolean visible)
@ -835,7 +1006,6 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
/** /**
*
* @param buttonName * @param buttonName
* @param visible * @param visible
*/ */
@ -849,7 +1019,6 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
/** /**
*
* @param windowNo * @param windowNo
*/ */
public void setWindowNo(int windowNo) { public void setWindowNo(int windowNo) {
@ -857,7 +1026,7 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
/** /**
* Enable/disable export button * Enable/disable Export button
* @param b * @param b
*/ */
public void enableExport(boolean b) { public void enableExport(boolean b) {
@ -866,7 +1035,7 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
/** /**
* Enable/disable file import button * Enable/disable File Import button
* @param b * @param b
*/ */
public void enableFileImport(boolean b) { public void enableFileImport(boolean b) {
@ -875,7 +1044,7 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
/** /**
* Enable/disable CSV import button * Enable/disable CSV Import button
* @param b * @param b
*/ */
public void enableCSVImport(boolean b) { public void enableCSVImport(boolean b) {
@ -885,6 +1054,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
private boolean ToolBarMenuRestictionLoaded = false; private boolean ToolBarMenuRestictionLoaded = false;
/**
* @param buttonName
* @return true if current login user has access to buttonName
*/
private boolean hasAccess(String buttonName) { private boolean hasAccess(String buttonName) {
ADWindow adwindow = ADWindow.get(windowNo); ADWindow adwindow = ADWindow.get(windowNo);
if (restrictionList == null) if (restrictionList == null)
@ -904,6 +1077,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
return true; return true;
} }
/**
* Initialise the accessibility state of toolbar buttons
*/
public void updateToolbarAccess() { public void updateToolbarAccess() {
if (ToolBarMenuRestictionLoaded) if (ToolBarMenuRestictionLoaded)
return; return;
@ -921,19 +1097,31 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
ToolBarMenuRestictionLoaded = true; ToolBarMenuRestictionLoaded = true;
} }
/** btnActiveWorkflow should be disabled when table has not workflow defined */ /**
boolean hasWorkflow(GridTab gridTab) * btnActiveWorkflow should be disabled when table has no workflow defined
* @param gridTab
* @return true if has workflow define for gridTab
*/
protected boolean hasWorkflow(GridTab gridTab)
{ {
String sql = "SELECT COUNT(*) FROM AD_Workflow WHERE IsActive='Y' AND AD_Table_ID=? AND AD_Client_ID IN (0,?)"; String sql = "SELECT COUNT(*) FROM AD_Workflow WHERE IsActive='Y' AND AD_Table_ID=? AND AD_Client_ID IN (0,?)";
return (DB.getSQLValueEx(null, sql, gridTab.getAD_Table_ID(), Env.getAD_Client_ID(Env.getCtx())) > 0); return (DB.getSQLValueEx(null, sql, gridTab.getAD_Table_ID(), Env.getAD_Client_ID(Env.getCtx())) > 0);
} }
/**
* Enable/disable Process button
* @param enabled
*/
public void enableProcessButton(boolean b) { public void enableProcessButton(boolean b) {
if (btnProcess != null) { if (btnProcess != null) {
btnProcess.setDisabled(!b); btnProcess.setDisabled(!b);
} }
} }
/**
* Dynamic update of each toolbar button state (Check restrictions).
* For custom button, call {@link ToolbarCustomButton#dynamicDisplay()}, process pressedLogic and readOnlyLogic.
*/
public void dynamicDisplay() { public void dynamicDisplay() {
List<Toolbarbutton> customButtons = new ArrayList<Toolbarbutton>(); List<Toolbarbutton> customButtons = new ArrayList<Toolbarbutton>();
for(ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons) { for(ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons) {
@ -995,6 +1183,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
readOnlyLogic(); readOnlyLogic();
} }
/**
* Call {@link ToolbarCustomButton#pressedLogic()}
*/
private void pressedLogic() private void pressedLogic()
{ {
for (ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons) for (ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons)
@ -1003,6 +1194,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Call {@link ToolbarCustomButton#readOnlyLogic()}
*/
private void readOnlyLogic() private void readOnlyLogic()
{ {
for (ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons) for (ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons)
@ -1027,6 +1221,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Init for mobile client only.
*/
private void mobileInit() { private void mobileInit() {
LayoutUtils.addSclass("mobile", this); LayoutUtils.addSclass("mobile", this);
addEventListener("onOverflowButton", evt -> onOverflowButton(evt)); addEventListener("onOverflowButton", evt -> onOverflowButton(evt));
@ -1064,6 +1261,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
addCallback(AFTER_PAGE_ATTACHED, t -> afterPageAttached()); addCallback(AFTER_PAGE_ATTACHED, t -> afterPageAttached());
} }
/**
* Mobile specific handling for AFTER_PAGE_ATTACHED callback.
*/
private void afterPageAttached() { private void afterPageAttached() {
Component p = getParent(); Component p = getParent();
while (p != null) { while (p != null) {
@ -1075,12 +1275,16 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Mobile specific event handling for ON_AFTER_SIZE event.
* @param evt
*/
private void onAfterSize(AfterSizeEvent evt) { private void onAfterSize(AfterSizeEvent evt) {
int width = evt.getWidth(); int width = evt.getWidth();
if (width != prevWidth) { if (width != prevWidth) {
prevWidth = width; prevWidth = width;
if (overflowButton != null) if (mobileOverflowButton != null)
overflowButton.detach(); mobileOverflowButton.detach();
if (overflowPopup != null) if (overflowPopup != null)
overflowPopup.detach(); overflowPopup.detach();
if (overflows != null) { if (overflows != null) {
@ -1093,6 +1297,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Handle onOverflowButton event from mobile client.
* @param evt
*/
private void onOverflowButton(Event evt) { private void onOverflowButton(Event evt) {
overflows = new ArrayList<>(); overflows = new ArrayList<>();
String uuid = (String) evt.getData(); String uuid = (String) evt.getData();
@ -1113,11 +1321,15 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
for (ToolBarButton toolbarButton : mobileShowMoreButtons) for (ToolBarButton toolbarButton : mobileShowMoreButtons)
overflows.add(toolbarButton); overflows.add(toolbarButton);
if (overflows.size() > 0) { if (overflows.size() > 0) {
createOverflowButton(); createOverflowButtonForMobile();
populateOverflowPopup(); populateOverflowPopup();
} }
} }
/**
* Populate overflow popup.
* Use for both desktop and mobile client.
*/
private void populateOverflowPopup() { private void populateOverflowPopup() {
boolean vertical = !ClientInfo.isMobile() && MSysConfig.getBooleanValue(MSysConfig.ZK_TOOLBAR_SHOW_MORE_VERTICAL, true, Env.getAD_Client_ID(Env.getCtx())); boolean vertical = !ClientInfo.isMobile() && MSysConfig.getBooleanValue(MSysConfig.ZK_TOOLBAR_SHOW_MORE_VERTICAL, true, Env.getAD_Client_ID(Env.getCtx()));
if (vertical) { if (vertical) {
@ -1164,6 +1376,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Enable show more feature for desktop client.
* Overflow for mobile client is initialise differently in {@link #mobileInit()}.
*/
private void enableShowMore() { private void enableShowMore() {
this.appendChild(btnShowMore); this.appendChild(btnShowMore);
btnShowMore.setDisabled(false); btnShowMore.setDisabled(false);
@ -1173,8 +1389,12 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
populateOverflowPopup(); populateOverflowPopup();
} }
/**
* Show overflow popup after {@link #btnShowMore}.
* For desktop client only.
*/
private void onShowMore() { private void onShowMore() {
Long ts = (Long) overflowPopup.removeAttribute("popup.close"); Long ts = (Long) overflowPopup.removeAttribute(POPUP_CLOSE_TIMESTAMP_ATTR);
if (ts != null) { if (ts != null) {
if (System.currentTimeMillis() - ts.longValue() < 500) { if (System.currentTimeMillis() - ts.longValue() < 500) {
return; return;
@ -1183,30 +1403,37 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
overflowPopup.open(btnShowMore, "after_end"); overflowPopup.open(btnShowMore, "after_end");
} }
private void createOverflowButton() { /**
overflowButton = new A(); * Create show more button for mobile client
overflowButton.setTooltiptext(Msg.getMsg(Env.getCtx(), "ShowMore")); */
overflowButton.setIconSclass("z-icon-ShowMore"); private void createOverflowButtonForMobile() {
overflowButton.setSclass("font-icon-toolbar-button toolbar-button mobile-overflow-link"); mobileOverflowButton = new A();
appendChild(overflowButton); mobileOverflowButton.setTooltiptext(Msg.getMsg(Env.getCtx(), "ShowMore"));
mobileOverflowButton.setIconSclass("z-icon-ShowMore");
mobileOverflowButton.setSclass("font-icon-toolbar-button toolbar-button mobile-overflow-link");
appendChild(mobileOverflowButton);
newOverflowPopup(); newOverflowPopup();
appendChild(overflowPopup); appendChild(overflowPopup);
overflowButton.addEventListener(Events.ON_CLICK, e -> { mobileOverflowButton.addEventListener(Events.ON_CLICK, e -> {
Long ts = (Long) overflowPopup.removeAttribute("popup.close"); Long ts = (Long) overflowPopup.removeAttribute(POPUP_CLOSE_TIMESTAMP_ATTR);
if (ts != null) { if (ts != null) {
if (System.currentTimeMillis() - ts.longValue() < 500) { if (System.currentTimeMillis() - ts.longValue() < 500) {
return; return;
} }
} }
overflowPopup.open(overflowButton, "after_end"); overflowPopup.open(mobileOverflowButton, "after_end");
}); });
} }
/**
* Create overflow popup.
* For both desktop and mobile client.
*/
private void newOverflowPopup() { private void newOverflowPopup() {
overflowPopup = new Popup(); overflowPopup = new Popup();
overflowPopup.addEventListener(Events.ON_OPEN, (OpenEvent oe) -> { overflowPopup.addEventListener(Events.ON_OPEN, (OpenEvent oe) -> {
if (!oe.isOpen()) { if (!oe.isOpen()) {
overflowPopup.setAttribute("popup.close", System.currentTimeMillis()); overflowPopup.setAttribute(POPUP_CLOSE_TIMESTAMP_ATTR, System.currentTimeMillis());
Component[] childrens = overflowPopup.getChildren().toArray(new Component[0]); Component[] childrens = overflowPopup.getChildren().toArray(new Component[0]);
for (Component child : childrens) { for (Component child : childrens) {
if (child instanceof Grid || child instanceof Toolbarbutton) if (child instanceof Grid || child instanceof Toolbarbutton)
@ -1217,6 +1444,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}); });
} }
/**
* Post after size event handler for mobile client.
* Calculate which toolbar buttons should overflow to show more popup.
*/
public void onPostAfterSize() { public void onPostAfterSize() {
if (this.getPage() != null) { if (this.getPage() != null) {
String script = "(function(){let w = zk.Widget.$('#" + getUuid() + "'); w.toolbarScrollable(w);"; String script = "(function(){let w = zk.Widget.$('#" + getUuid() + "'); w.toolbarScrollable(w);";
@ -1225,13 +1456,18 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Set button to pressed/not pressed state
* @param buttonName
* @param pressed
*/
public void setPressed(String buttonName, boolean pressed) { public void setPressed(String buttonName, boolean pressed) {
if (getButton(buttonName) != null) if (getButton(buttonName) != null)
getButton(buttonName).setPressed(pressed); getButton(buttonName).setPressed(pressed);
} }
/** /**
* @return * @return parent tab level for quick form
*/ */
public int getQuickFormTabHrchyLevel() public int getQuickFormTabHrchyLevel()
{ {
@ -1246,6 +1482,11 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
this.quickFormTabHrchyLevel = quickFormHrchyTabLevel; this.quickFormTabHrchyLevel = quickFormHrchyTabLevel;
} }
/**
* Reload user queries and set selected item to AD_UserQuery_ID
* @param AD_Tab_ID
* @param AD_UserQuery_ID
*/
public void refreshUserQuery(int AD_Tab_ID, int AD_UserQuery_ID) { public void refreshUserQuery(int AD_Tab_ID, int AD_UserQuery_ID) {
if (AEnv.getOrSetExecutionAttribute(getClass().getName()+".refreshUserQuery")) { if (AEnv.getOrSetExecutionAttribute(getClass().getName()+".refreshUserQuery")) {
return; return;
@ -1269,6 +1510,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
fQueryName.setSelectedIndex(0); fQueryName.setSelectedIndex(0);
} }
/**
* Set selected user query
* @param AD_UserQuery_ID
*/
public void setSelectedUserQuery(int AD_UserQuery_ID) { public void setSelectedUserQuery(int AD_UserQuery_ID) {
for (MUserQuery userQuery : userQueries) { for (MUserQuery userQuery : userQueries) {
if (AD_UserQuery_ID == userQuery.getAD_UserQuery_ID()) { if (AD_UserQuery_ID == userQuery.getAD_UserQuery_ID()) {
@ -1278,12 +1523,19 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
} }
/**
* Set selected user query
* @param selectedUserQuery
*/
public void setSelectedUserQuery(MUserQuery selectedUserQuery) { public void setSelectedUserQuery(MUserQuery selectedUserQuery) {
this.selectedUserQuery = selectedUserQuery; this.selectedUserQuery = selectedUserQuery;
if (selectedUserQuery != null) if (selectedUserQuery != null)
fQueryName.setValue(selectedUserQuery.getName()); fQueryName.setValue(selectedUserQuery.getName());
} }
/**
* @return AD_UserQuery_ID of selected user query
*/
public int getAD_UserQuery_ID() { public int getAD_UserQuery_ID() {
if (selectedUserQuery == null) if (selectedUserQuery == null)
return 0; return 0;
@ -1291,8 +1543,8 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
} }
/** /**
* Init Default Query in Window Toolbar * Set selected user query to first default user query (if any)
* @return true if initialized * @return true if there's a default user query
*/ */
public boolean initDefaultQuery() { public boolean initDefaultQuery() {
if(userQueries != null) { if(userQueries != null) {

View File

@ -33,7 +33,7 @@ import org.compiere.util.Evaluator;
import org.compiere.util.Util; import org.compiere.util.Util;
/** /**
* * Abstract model and controller for AD_Tab+AD_Field. UI part is implemented in sub class.
* @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a> * @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a>
* @author <a href="mailto:hengsin@gmail.com">Low Heng Sin</a> * @author <a href="mailto:hengsin@gmail.com">Low Heng Sin</a>
* @date Feb 25, 2007 * @date Feb 25, 2007
@ -46,16 +46,21 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
/** List of dependent Variables */ /** List of dependent Variables */
private ArrayList<String> m_dependents = new ArrayList<String>(); private ArrayList<String> m_dependents = new ArrayList<String>();
/** Tabs associated to this tab box */ /** AD tab panels associated to this tab box */
protected List<IADTabpanel> tabPanelList = new ArrayList<IADTabpanel>(); protected List<IADTabpanel> tabPanelList = new ArrayList<IADTabpanel>();
/** Parent part, the content part of AD Window **/
protected AbstractADWindowContent adWindowPanel; protected AbstractADWindowContent adWindowPanel;
/**
* default constructor
*/
public AbstractADTabbox() public AbstractADTabbox()
{ {
} }
/** /**
* Add Tab * Add new tab(AD_Tab).
* Delegate to {@link #doAddTab(GridTab, IADTabpanel)}
* @param gTab grid tab model * @param gTab grid tab model
* @param tabPanel * @param tabPanel
*/ */
@ -76,7 +81,8 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
}// addTab }// addTab
/** /**
* handle add tab to tabbox * Handle add new tab to UI.
* Override to implement add new tab to UI.
* @param tab * @param tab
* @param tabPanel * @param tabPanel
*/ */
@ -84,19 +90,24 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
/** /**
* @param index of tab panel * @param index of tab panel
* @return true if enable * @return true if enable, false otherwise
*/ */
public boolean isEnabledAt(int index) public boolean isEnabledAt(int index)
{ {
return true; return true;
}// isEnabledAt }// isEnabledAt
private boolean isDisplay(IADTabpanel newTab) /**
* Evaluate display logic
* @param tabPanel
* @return true if visible, false otherwise
*/
private boolean isDisplay(IADTabpanel tabPanel)
{ {
String logic = newTab.getDisplayLogic(); String logic = tabPanel.getDisplayLogic();
if (logic != null && logic.length() > 0) if (logic != null && logic.length() > 0)
{ {
boolean display = Evaluator.evaluateLogic(newTab, logic); boolean display = Evaluator.evaluateLogic(tabPanel, logic);
if (!display) if (!display)
{ {
log.info("Not displayed - " + logic); log.info("Not displayed - " + logic);
@ -107,11 +118,13 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
} }
/** /**
* Updated selected tab index * Change selected tab index from oldIndex to newIndex.
* Delegate to {@link #doTabSelectionChanged(int, int)}.
* @param oldIndex * @param oldIndex
* @param newIndex * @param newIndex
* @return true if successfully switch to newIndex * @return true if successfully switch to newIndex
*/ */
@Override
public boolean updateSelectedIndex(int oldIndex, int newIndex) public boolean updateSelectedIndex(int oldIndex, int newIndex)
{ {
IADTabpanel newTab = tabPanelList.get(newIndex); IADTabpanel newTab = tabPanelList.get(newIndex);
@ -136,6 +149,11 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
return canJump; return canJump;
} }
/**
* Prepare environment context for newTab.
* @param newIndex
* @param newTab
*/
private void prepareContext(int newIndex, IADTabpanel newTab) { private void prepareContext(int newIndex, IADTabpanel newTab) {
//update context //update context
if (newTab != null) if (newTab != null)
@ -192,15 +210,17 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
} }
/** /**
* handle tab selection changed event * Handle tab selection change event.
* Override to update UI for tab selection change.
* @param oldIndex * @param oldIndex
* @param newIndex * @param newIndex
*/ */
protected abstract void doTabSelectionChanged(int oldIndex, int newIndex); protected abstract void doTabSelectionChanged(int oldIndex, int newIndex);
/** /**
* @param index tab index * Evaluate display logic
* @return true if tab is visible * @param index
* @return true if visible, false otherwise
*/ */
public boolean isDisplay(int index) { public boolean isDisplay(int index) {
if (index >= tabPanelList.size()) if (index >= tabPanelList.size())
@ -218,21 +238,23 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
} }
/** /**
* Delegate to {@link #canNavigateTo(int, int, boolean)}
* @param fromIndex * @param fromIndex
* @param toIndex * @param toIndex
* @return true if can navigate to toIndex * @return true if can change selected tab from fromIndex to toIndex
*/ */
@Override
public boolean canNavigateTo(int fromIndex, int toIndex) { public boolean canNavigateTo(int fromIndex, int toIndex) {
return canNavigateTo(fromIndex, toIndex, false); return canNavigateTo(fromIndex, toIndex, false);
} }
/** /**
* *
* @param fromIndex * @param fromIndex
* @param toIndex * @param toIndex
* @param checkRecordID true to validate record id of fromIndex tab * @param checkRecordID true to validate fromIndex has a valid record id
* @return true if can navigate to toIndex tab * @return true if can change selected tab from fromIndex to toIndex
*/ */
public boolean canNavigateTo(int fromIndex, int toIndex, boolean checkRecordID) { public boolean canNavigateTo(int fromIndex, int toIndex, boolean checkRecordID) {
IADTabpanel newTab = tabPanelList.get(toIndex); IADTabpanel newTab = tabPanelList.get(toIndex);
if (newTab instanceof ADTabpanel) if (newTab instanceof ADTabpanel)
@ -281,7 +303,7 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
} }
/** /**
* * Get break crumb path
* @return full path * @return full path
*/ */
public String getPath() { public String getPath() {
@ -310,7 +332,8 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
} }
/** /**
* Evaluate Tab Logic * Handle DataStatusEvent.
* Delegate to {@link #updateTabState()}.
* @param e event * @param e event
*/ */
public void evaluate (DataStatusEvent e) public void evaluate (DataStatusEvent e)
@ -335,7 +358,7 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
} // evaluate } // evaluate
/** /**
* Update display state of tab (visibility, activation and if need invalidate) * Update UI state of tab (visibility, activation and if need invalidate)
*/ */
protected abstract void updateTabState(); protected abstract void updateTabState();
@ -366,8 +389,10 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
/** /**
* Set newIndex as selected tab * Set newIndex as selected tab
* Delegate to {@link #updateSelectedIndex(int, int)}
* @param newIndex * @param newIndex
*/ */
@Override
public void setSelectedIndex(int newIndex) { public void setSelectedIndex(int newIndex) {
int oldIndex = getSelectedIndex(); int oldIndex = getSelectedIndex();
updateSelectedIndex(oldIndex, newIndex); updateSelectedIndex(oldIndex, newIndex);

View File

@ -52,13 +52,20 @@ import org.zkoss.zul.Menuitem;
import org.zkoss.zul.Window; import org.zkoss.zul.Window;
/** /**
* Bread crumb component for AD Window
* @author hengsin * @author hengsin
*
*/ */
public class BreadCrumb extends Div implements EventListener<Event> { public class BreadCrumb extends Div implements EventListener<Event> {
/**
* Event echo after ON_MOUSE_OVER event.
*/
private static final String ON_MOUSE_OVER_ECHO_EVENT = "onMouseOverEcho"; private static final String ON_MOUSE_OVER_ECHO_EVENT = "onMouseOverEcho";
/**
* This is echo after some delay after ON_MOUSE_OUT event (to close linkPopup).
* Also use as attribute to allow a rapid ON_MOUSE_OVER event to cancel the delay ON_MOUSE_OUT_ECHO_EVENT, thus keeping linkPopup open.
**/
private static final String ON_MOUSE_OUT_ECHO_EVENT = "onMouseOutEcho"; private static final String ON_MOUSE_OUT_ECHO_EVENT = "onMouseOutEcho";
/** /**
@ -68,29 +75,39 @@ public class BreadCrumb extends Div implements EventListener<Event> {
private static final String BTNPREFIX = "Btn"; private static final String BTNPREFIX = "Btn";
/** west layout for paths to a tab (for e.g "Business Partner > Location") **/
private Hlayout layout; private Hlayout layout;
/** record navigation buttons **/
private ToolBarButton btnFirst, btnPrevious, btnNext, btnLast, btnRecordInfo; private ToolBarButton btnFirst, btnPrevious, btnNext, btnLast, btnRecordInfo;
/** Label:TabIndex. Link to other tabs at same level (i.e other child tabs of the same parent tab). **/
private LinkedHashMap<String, String> links; private LinkedHashMap<String, String> links;
@SuppressWarnings("unused") @SuppressWarnings("unused")
private int windowNo; private int windowNo;
/** BtnName:ToolBarButton. Map of all toolbar buttons. **/
private HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>(); private HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>();
/** Last DataStatusEvent from {@link AbstractADWindowContent#dataStatusChanged(DataStatusEvent)} **/
private DataStatusEvent m_dse; private DataStatusEvent m_dse;
/** Last data status text from {@link AbstractADWindowContent#dataStatusChanged(DataStatusEvent)} **/
private String m_text; private String m_text;
/** register ToolbarListener **/
private ToolbarListener toolbarListener; private ToolbarListener toolbarListener;
/** east layout for record navigation buttons **/
private Hlayout toolbarContainer; private Hlayout toolbarContainer;
/** popup for link to other tabs at same level **/
protected Menupopup linkPopup; protected Menupopup linkPopup;
private GridTab m_gridTab; private GridTab m_gridTab;
/** AD Window content part that own this bread crumb **/
private AbstractADWindowContent windowContent; private AbstractADWindowContent windowContent;
/** /**
@ -140,7 +157,6 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
/** /**
*
* @param listener * @param listener
*/ */
public void setToolbarListener(ToolbarListener listener) { public void setToolbarListener(ToolbarListener listener) {
@ -148,10 +164,10 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
/** /**
* * Add path to tab
* @param label * @param label path label
* @param id * @param id path id
* @param clickable * @param clickable true to add clickable {@link BreadCrumbLink} false to add text label
*/ */
public void addPath(String label, String id, boolean clickable) { public void addPath(String label, String id, boolean clickable) {
if (clickable) { if (clickable) {
@ -181,7 +197,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
/** /**
* * Get parent BreadCrumbLinks
* @return list of parent links * @return list of parent links
*/ */
public List<BreadCrumbLink> getParentLinks() { public List<BreadCrumbLink> getParentLinks() {
@ -195,7 +211,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
/** /**
* add links to other tabs at the same level * add links to other tabs at the same level
* @param links * @param links Label:TabIndex map
*/ */
public void addLinks(LinkedHashMap<String, String> links) { public void addLinks(LinkedHashMap<String, String> links) {
this.links = links; this.links = links;
@ -368,7 +384,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
/** /**
* remove all links * remove all path and links
*/ */
public void reset() { public void reset() {
layout.getChildren().clear(); layout.getChildren().clear();
@ -395,6 +411,13 @@ public class BreadCrumb extends Div implements EventListener<Event> {
this.btnNext.setDisabled(!enabled); this.btnNext.setDisabled(!enabled);
} }
/**
* Create toolbar button.
* @param name
* @param image
* @param tooltip
* @return {@link ToolBarButton}
*/
private ToolBarButton createButton(String name, String image, String tooltip) private ToolBarButton createButton(String name, String image, String tooltip)
{ {
ToolBarButton btn = new ToolBarButton(""); ToolBarButton btn = new ToolBarButton("");
@ -425,6 +448,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
/** /**
* Set record info text
* @param text * @param text
*/ */
public void setStatusDB (String text) public void setStatusDB (String text)
@ -433,7 +457,8 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
/** /**
* @param text * Data status from {@link AbstractADWindowContent#dataStatusChanged(DataStatusEvent)}
* @param text record info text (for e.g 1/1)
* @param dse * @param dse
* @param gridTab * @param gridTab
*/ */
@ -471,7 +496,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
/** /**
* * Set visibility of record navigation toolbar
* @param visible * @param visible
*/ */
public void setNavigationToolbarVisibility(boolean visible) { public void setNavigationToolbarVisibility(boolean visible) {
@ -498,22 +523,37 @@ public class BreadCrumb extends Div implements EventListener<Event> {
} }
} }
/**
* @return true if previous button is enable
*/
public boolean isPreviousEnabled() { public boolean isPreviousEnabled() {
return !btnPrevious.isDisabled(); return !btnPrevious.isDisabled();
} }
/**
* @return true if next button is enable
*/
public boolean isNextEnabled() { public boolean isNextEnabled() {
return !btnNext.isDisabled(); return !btnNext.isDisabled();
} }
/**
* @return next ToolBarButton
*/
public ToolBarButton getNextButton() { public ToolBarButton getNextButton() {
return btnNext; return btnNext;
} }
/**
* @return previous ToolBarButton
*/
public ToolBarButton getPreviousButton() { public ToolBarButton getPreviousButton() {
return btnPrevious; return btnPrevious;
} }
/**
* @return true if path/link is empty
*/
public boolean isEmpty() { public boolean isEmpty() {
return layout == null || layout.getChildren().isEmpty(); return layout == null || layout.getChildren().isEmpty();
} }

View File

@ -16,8 +16,8 @@ package org.adempiere.webui.adwindow;
import org.zkoss.zul.A; import org.zkoss.zul.A;
/** /**
* Link component for {@link BreadCrumb}
* @author hengsin * @author hengsin
*
*/ */
public class BreadCrumbLink extends A { public class BreadCrumbLink extends A {
@ -26,12 +26,19 @@ public class BreadCrumbLink extends A {
*/ */
private static final long serialVersionUID = 170361731431877695L; private static final long serialVersionUID = 170361731431877695L;
/** id for path (tab index) **/
private String pathId; private String pathId;
/**
* @return path id
*/
public String getPathId() { public String getPathId() {
return pathId; return pathId;
} }
/**
* @param pathId
*/
public void setPathId(String pathId) { public void setPathId(String pathId) {
this.pathId = pathId; this.pathId = pathId;
} }

View File

@ -55,7 +55,10 @@ import org.zkoss.zul.RowRenderer;
import org.zkoss.zul.Vlayout; import org.zkoss.zul.Vlayout;
/** /**
* * Header and detail UI for AD_Tabs.
* This class manage a list of tabs with the current selected tab as the attached and visible {@link ADTabpanel} instance.
* Child tabs of selected tab is shown in {@link DetailPane} inside {@link ADTabpanel}.
*
* @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a> * @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a>
* @author <a href="mailto:hengsin@gmail.com">Low Heng Sin</a> * @author <a href="mailto:hengsin@gmail.com">Low Heng Sin</a>
* @date Feb 25, 2007 * @date Feb 25, 2007
@ -63,27 +66,45 @@ import org.zkoss.zul.Vlayout;
*/ */
public class CompositeADTabbox extends AbstractADTabbox public class CompositeADTabbox extends AbstractADTabbox
{ {
/**
* DetailPane attribute to hold list of child tabs.
* List of Object[] of tabIndex, tabPanel, tabLabel, enable.
*/
private static final String DETAILPANE_TABLIST_ATTR = "detailpane.tablist";
/** Execution attribute to hold reference to detail ADTabpanel that's handling onEditDetail event **/
public static final String AD_TABBOX_ON_EDIT_DETAIL_ATTRIBUTE = "ADTabbox.onEditDetail"; public static final String AD_TABBOX_ON_EDIT_DETAIL_ATTRIBUTE = "ADTabbox.onEditDetail";
/** after tab selection change event **/
private static final String ON_POST_TAB_SELECTION_CHANGED_EVENT = "onPostTabSelectionChanged"; private static final String ON_POST_TAB_SELECTION_CHANGED_EVENT = "onPostTabSelectionChanged";
/** event echo from ON_POST_TAB_SELECTION_CHANGED_EVENT handler **/
private static final String ON_TAB_SELECTION_CHANGED_ECHO_EVENT = "onTabSelectionChangedEcho"; private static final String ON_TAB_SELECTION_CHANGED_ECHO_EVENT = "onTabSelectionChangedEcho";
/** tab selection change event **/
public static final String ON_SELECTION_CHANGED_EVENT = "onSelectionChanged"; public static final String ON_SELECTION_CHANGED_EVENT = "onSelectionChanged";
/** List of all tab **/
private List<ADTabListModel.ADTabLabel> tabLabelList = new ArrayList<ADTabListModel.ADTabLabel>(); private List<ADTabListModel.ADTabLabel> tabLabelList = new ArrayList<ADTabListModel.ADTabLabel>();
/** List of all tab panel **/
private List<IADTabpanel> tabPanelList = new ArrayList<IADTabpanel>(); private List<IADTabpanel> tabPanelList = new ArrayList<IADTabpanel>();
/** main layout component **/
private Vlayout layout; private Vlayout layout;
/** tab selection change listener **/
private EventListener<Event> selectionListener; private EventListener<Event> selectionListener;
/** {@link IADTabpanel} instance for selected tab **/
private IADTabpanel headerTab; private IADTabpanel headerTab;
/** Index of selected tab **/
private int selectedIndex = 0; private int selectedIndex = 0;
/**
* default constructor
*/
public CompositeADTabbox(){ public CompositeADTabbox(){
} }
@ -221,6 +242,9 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
} }
/**
* Delete current row of selected detail tab
*/
private void onDelete() { private void onDelete() {
if (headerTab.getGridTab().isNew()) return; if (headerTab.getGridTab().isNew()) return;
@ -245,6 +269,10 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
} }
/**
* Delete selected rows of selected detail tab
* @param tabPanel
*/
private void onDeleteSelected(final IADTabpanel tabPanel) { private void onDeleteSelected(final IADTabpanel tabPanel) {
if (tabPanel == null || tabPanel.getGridTab() == null) return; if (tabPanel == null || tabPanel.getGridTab() == null) return;
@ -283,6 +311,10 @@ public class CompositeADTabbox extends AbstractADTabbox
return detailPane; return detailPane;
} }
/**
* defer execution of adTabPanel.focus()
* @param adTabPanel
*/
private void focusToTabpanel(IADTabpanel adTabPanel ) { private void focusToTabpanel(IADTabpanel adTabPanel ) {
if (adTabPanel != null && adTabPanel instanceof HtmlBasedComponent) { if (adTabPanel != null && adTabPanel instanceof HtmlBasedComponent) {
final HtmlBasedComponent comp = (HtmlBasedComponent) adTabPanel; final HtmlBasedComponent comp = (HtmlBasedComponent) adTabPanel;
@ -291,7 +323,8 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
/** /**
* Edit selected detail tab * Edit current row of selected detail tab.
* Make selected detail tab the new header tab.
* @param row * @param row
* @param formView * @param formView
*/ */
@ -328,6 +361,10 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
} }
/**
* Create layout and setup listeners for bread crumb.
* Vertical layout with {@link ADTabpanel} as the only child component.
*/
@Override @Override
protected Component doCreatePart(Component parent) protected Component doCreatePart(Component parent)
{ {
@ -359,8 +396,9 @@ public class CompositeADTabbox extends AbstractADTabbox
breadCrumb.addEventListener(Events.ON_CLICK, new EventListener<Event>() { breadCrumb.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
@Override @Override
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
//send tab selection change event
int oldIndex = selectedIndex; int oldIndex = selectedIndex;
if (event.getTarget() instanceof BreadCrumbLink) { if (event.getTarget() instanceof BreadCrumbLink) {
BreadCrumbLink link = (BreadCrumbLink) event.getTarget(); BreadCrumbLink link = (BreadCrumbLink) event.getTarget();
int newIndex = Integer.parseInt(link.getPathId()); int newIndex = Integer.parseInt(link.getPathId());
@ -381,8 +419,8 @@ public class CompositeADTabbox extends AbstractADTabbox
@Override @Override
protected void doAddTab(GridTab gTab, IADTabpanel tabPanel) { protected void doAddTab(GridTab gTab, IADTabpanel tabPanel) {
ADTabListModel.ADTabLabel tabLabel = new ADTabListModel.ADTabLabel(gTab.getName(), gTab.getTabLevel(),gTab.getDescription(), ADTabListModel.ADTabLabel tabLabel = new ADTabListModel.ADTabLabel(gTab.getName(), gTab.getTabLevel(), gTab.getDescription(),
gTab.getWindowNo(),gTab.getAD_Tab_ID()); gTab.getWindowNo(), gTab.getAD_Tab_ID());
tabLabelList.add(tabLabel); tabLabelList.add(tabLabel);
tabPanelList.add(tabPanel); tabPanelList.add(tabPanel);
@ -480,6 +518,7 @@ public class CompositeADTabbox extends AbstractADTabbox
}); });
} }
//add to header or detail pane
if (layout.getChildren().isEmpty()) { if (layout.getChildren().isEmpty()) {
layout.appendChild(tabPanel); layout.appendChild(tabPanel);
headerTab = tabPanel; headerTab = tabPanel;
@ -516,6 +555,9 @@ public class CompositeADTabbox extends AbstractADTabbox
return b; return b;
} }
/**
* Call {@link ADTabpanel#activateDetailIfVisible()}
*/
private void activateDetailIfVisible() { private void activateDetailIfVisible() {
if (headerTab instanceof ADTabpanel) { if (headerTab instanceof ADTabpanel) {
((ADTabpanel)headerTab).activateDetailIfVisible(); ((ADTabpanel)headerTab).activateDetailIfVisible();
@ -595,7 +637,7 @@ public class CompositeADTabbox extends AbstractADTabbox
//set state //set state
headerTab.setDetailPaneMode(false); headerTab.setDetailPaneMode(false);
//show empty path, update later with actual path in onTabSelectionChangedEcho //show empty path, update later with actual path in onPostTabSelectionChanged
getBreadCrumb().getFirstChild().getChildren().clear(); getBreadCrumb().getFirstChild().getChildren().clear();
getBreadCrumb().getFirstChild().appendChild(new Label("")); getBreadCrumb().getFirstChild().appendChild(new Label(""));
@ -603,11 +645,13 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
/** /**
* first after tab selection change event, follow by onTabSelectionChangedEcho event * Handle after tab selection change event, echo onTabSelectionChangedEcho event.
* @param back * @param back
*/ */
private void onPostTabSelectionChanged(Boolean back) { private void onPostTabSelectionChanged(Boolean back) {
if (headerTab instanceof ADTabpanel && !headerTab.getGridTab().isSortTab()) { if (headerTab instanceof ADTabpanel && !headerTab.getGridTab().isSortTab()) {
//gather all child tabs (both immediate and not immediate)
//Object[]: tabIndex, tabPanel, tabLabel, enable
List<Object[]> list = new ArrayList<Object[]>(); List<Object[]> list = new ArrayList<Object[]>();
int tabIndex = -1; int tabIndex = -1;
int currentLevel = headerTab.getTabLevel(); int currentLevel = headerTab.getTabLevel();
@ -633,7 +677,7 @@ public class CompositeADTabbox extends AbstractADTabbox
if (detailPane == null) { if (detailPane == null) {
detailPane = createDetailPane(); detailPane = createDetailPane();
} }
detailPane.setAttribute("detailpane.tablist", list); detailPane.setAttribute(DETAILPANE_TABLIST_ATTR, list);
ZKUpdateUtil.setVflex(detailPane, "true"); ZKUpdateUtil.setVflex(detailPane, "true");
if (headerTab.getDetailPane() == null) { if (headerTab.getDetailPane() == null) {
@ -658,7 +702,8 @@ public class CompositeADTabbox extends AbstractADTabbox
//setup tabs of detail pane //setup tabs of detail pane
if (detailPane != null) { if (detailPane != null) {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
List<Object[]> list = (List<Object[]>) detailPane.removeAttribute("detailpane.tablist"); //tabIndex, tabPanel, tabLabel, enable
List<Object[]> list = (List<Object[]>) detailPane.removeAttribute(DETAILPANE_TABLIST_ATTR);
if (list != null && !list.isEmpty()) { if (list != null && !list.isEmpty()) {
int currentLevel = headerTab.getTabLevel(); int currentLevel = headerTab.getTabLevel();
for (Object[] value : list) { for (Object[] value : list) {
@ -747,6 +792,7 @@ public class CompositeADTabbox extends AbstractADTabbox
private void updateBreadCrumb() { private void updateBreadCrumb() {
BreadCrumb breadCrumb = getBreadCrumb(); BreadCrumb breadCrumb = getBreadCrumb();
breadCrumb.reset(); breadCrumb.reset();
//add parent path
if (selectedIndex > 0) { if (selectedIndex > 0) {
List<ADTabLabel> parents = new ArrayList<ADTabListModel.ADTabLabel>(); List<ADTabLabel> parents = new ArrayList<ADTabListModel.ADTabLabel>();
List<Integer> parentIndex = new ArrayList<Integer>(); List<Integer> parentIndex = new ArrayList<Integer>();
@ -771,6 +817,8 @@ public class CompositeADTabbox extends AbstractADTabbox
if (!breadCrumb.isVisible()) if (!breadCrumb.isVisible())
breadCrumb.setVisible(true); breadCrumb.setVisible(true);
//Links for other child tabs at same level
//Tab Index:Tab Label
LinkedHashMap<String, String> links = new LinkedHashMap<String, String>(); LinkedHashMap<String, String> links = new LinkedHashMap<String, String>();
int parentIndex = 0; int parentIndex = 0;
if (headerTab.getTabLevel() > 1) { if (headerTab.getTabLevel() > 1) {
@ -823,6 +871,9 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
} }
/**
* @return {@link BreadCrumb}
*/
private BreadCrumb getBreadCrumb() { private BreadCrumb getBreadCrumb() {
ADWindowContent window = (ADWindowContent) adWindowPanel; ADWindowContent window = (ADWindowContent) adWindowPanel;
BreadCrumb breadCrumb = window.getBreadCrumb(); BreadCrumb breadCrumb = window.getBreadCrumb();
@ -844,6 +895,9 @@ public class CompositeADTabbox extends AbstractADTabbox
return null; return null;
} }
/**
* Notify selected detail tab after data status change of header tab
*/
class SyncDataStatusListener implements DataStatusListener { class SyncDataStatusListener implements DataStatusListener {
private IADTabpanel tabPanel; private IADTabpanel tabPanel;
@ -867,7 +921,7 @@ public class CompositeADTabbox extends AbstractADTabbox
IADTabpanel detailTab = getSelectedDetailADTabpanel(); IADTabpanel detailTab = getSelectedDetailADTabpanel();
if (detailTab != null) { if (detailTab != null) {
//check data action //check is data action from detail tab
String uuid = (String) execution.getAttribute(CompositeADTabbox.class.getName()+".dataAction"); String uuid = (String) execution.getAttribute(CompositeADTabbox.class.getName()+".dataAction");
if (uuid != null && uuid.equals(detailTab.getUuid()) && detailTab.getGridTab().isCurrent()) { if (uuid != null && uuid.equals(detailTab.getUuid()) && detailTab.getGridTab().isCurrent()) {
//refresh current row //refresh current row
@ -909,7 +963,6 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
/** /**
*
* @return true if selected detail tab have been activated * @return true if selected detail tab have been activated
*/ */
public boolean isDetailActivated() { public boolean isDetailActivated() {
@ -1074,6 +1127,10 @@ public class CompositeADTabbox extends AbstractADTabbox
} }
} }
/**
* force invalidate of tabPanel
* @param tabPanel
*/
private void invalidateTabPanel(IADTabpanel tabPanel) { private void invalidateTabPanel(IADTabpanel tabPanel) {
Center center = findCenter(tabPanel.getGridView()); Center center = findCenter(tabPanel.getGridView());
if (center != null) if (center != null)
@ -1082,6 +1139,11 @@ public class CompositeADTabbox extends AbstractADTabbox
tabPanel.invalidate(); tabPanel.invalidate();
} }
/**
* Find {@link Center} that own gridView
* @param gridView
* @return {@link Center}
*/
private Center findCenter(GridView gridView) { private Center findCenter(GridView gridView) {
if (gridView == null) if (gridView == null)
return null; return null;

View File

@ -81,8 +81,10 @@ import org.zkoss.zul.Tabs;
import org.zkoss.zul.Toolbar; import org.zkoss.zul.Toolbar;
/** /**
* Detail panel that display the child tabs of a parent {@link ADTabpanel} tab.
* Implemented as a panel with {@link Tabbox}.
*
* @author hengsin * @author hengsin
*
*/ */
public class DetailPane extends Panel implements EventListener<Event>, IdSpace { public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
@ -107,12 +109,16 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
private static final String BTN_TOGGLE_ID = "BtnToggle"; private static final String BTN_TOGGLE_ID = "BtnToggle";
/** Boolean execution attribute to indicate tabbox is handling ON_SELECT event **/
private static final String TABBOX_ONSELECT_ATTRIBUTE = "detailpane.tabbox.onselect"; private static final String TABBOX_ONSELECT_ATTRIBUTE = "detailpane.tabbox.onselect";
/** event after handling of ON_SElECT event of a detail tab */
private static final String ON_POST_SELECT_TAB_EVENT = "onPostSelectTab"; private static final String ON_POST_SELECT_TAB_EVENT = "onPostSelectTab";
/** Attribute use by {@link #messageContainers} to hold status text **/
private static final String STATUS_TEXT_ATTRIBUTE = "status.text"; private static final String STATUS_TEXT_ATTRIBUTE = "status.text";
/** Attribute use by {@link #messageContainers} to hold error text **/
private static final String STATUS_ERROR_ATTRIBUTE = "status.error"; private static final String STATUS_ERROR_ATTRIBUTE = "status.error";
private static final String CUSTOMIZE_IMAGE = "images/Customize16.png"; private static final String CUSTOMIZE_IMAGE = "images/Customize16.png";
@ -124,34 +130,57 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
private static final String QUICK_FORM_IMAGE = "images/QuickForm16.png"; private static final String QUICK_FORM_IMAGE = "images/QuickForm16.png";
private static final String TOGGLE_IMAGE = "images/Multi16.png"; private static final String TOGGLE_IMAGE = "images/Multi16.png";
/** Timestamp for previous key event **/
private long prevKeyEventTime = 0; private long prevKeyEventTime = 0;
/**
* Previous KeyEvent reference.
* Use together with {@link #prevKeyEventTime} to detect double firing of key event by browser.
*/
private KeyEvent prevKeyEvent; private KeyEvent prevKeyEvent;
/** tabbox for AD_Tabs **/
private Tabbox tabbox; private Tabbox tabbox;
/** Registered event listener for DetailPane events **/
private EventListener<Event> eventListener; private EventListener<Event> eventListener;
/** AD_Tab_ID:Hbox. Message (status, error) container for each tab. **/
private Map<Integer, Hbox> messageContainers = new HashMap<Integer, Hbox>(); private Map<Integer, Hbox> messageContainers = new HashMap<Integer, Hbox>();
/** content for message popup **/
private Div msgPopupCnt; private Div msgPopupCnt;
/** message popup window **/
private Window msgPopup; private Window msgPopup;
/** last selected tab index **/
private int prevSelectedIndex = 0; private int prevSelectedIndex = 0;
/**
* On activate event for detail tab.
* Use to activate detail tab or notify detail tab after header tab change.
*/
public static final String ON_ACTIVATE_DETAIL_EVENT = "onActivateDetail"; public static final String ON_ACTIVATE_DETAIL_EVENT = "onActivateDetail";
/** on delete event for selected tab **/
public static final String ON_DELETE_EVENT = "onDelete"; public static final String ON_DELETE_EVENT = "onDelete";
/** on new event for selected tab **/
public static final String ON_NEW_EVENT = "onNew"; public static final String ON_NEW_EVENT = "onNew";
/** event to edit current row of selected tab **/
public static final String ON_EDIT_EVENT = "onEdit"; public static final String ON_EDIT_EVENT = "onEdit";
/** on save event for selected tab **/
public static final String ON_SAVE_EVENT = "onSave"; public static final String ON_SAVE_EVENT = "onSave";
/** on quick form event for selected tab **/
public static final String ON_QUICK_FORM_EVENT = "onQuickForm"; public static final String ON_QUICK_FORM_EVENT = "onQuickForm";
/**
* Record navigation event for selected tab.
* Event data is the navigation action (previous, next, first and last).
*/
public static final String ON_RECORD_NAVIGATE_EVENT = "onRecordNavigate"; public static final String ON_RECORD_NAVIGATE_EVENT = "onRecordNavigate";
/** /**
@ -224,7 +253,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* replace of add * Replace or add IADTabpanel to tabbox.
* @param index * @param index
* @param tabPanel * @param tabPanel
* @param tabLabel * @param tabLabel
@ -238,7 +267,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* replace or add * Replace or add IADTabpanel to tabbox.
* @param index * @param index
* @param tabPanel * @param tabPanel
* @param tabLabel * @param tabLabel
@ -253,7 +282,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* * Add IADTabpanel to tabbox
* @param tabPanel * @param tabPanel
* @param tabLabel * @param tabLabel
*/ */
@ -262,7 +291,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* * Add IADTabpanel to tabbox
* @param tabPanel * @param tabPanel
* @param tabLabel * @param tabLabel
* @param enabled * @param enabled
@ -284,6 +313,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
tab.addEventListener(Events.ON_CLICK, new EventListener<Event>() { tab.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
@Override @Override
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
//click on tab title trigger edit of current row
Tab tab = (Tab) event.getTarget(); Tab tab = (Tab) event.getTarget();
if (!tab.isSelected()) if (!tab.isSelected())
return; return;
@ -313,8 +343,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
Tabpanel tp = new Tabpanel(); Tabpanel tp = new Tabpanel();
tabpanels.appendChild(tp); tabpanels.appendChild(tp);
ToolBar toolbar = tp.getToolbar();
//setup toolbar
ToolBar toolbar = tp.getToolbar();
HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>(); HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>();
ToolBarButton button = new ToolBarButton(); ToolBarButton button = new ToolBarButton();
if (ThemeManager.isUseFontIconForImage()) if (ThemeManager.isUseFontIconForImage())
@ -444,6 +475,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Toggle")) + " Shift+Alt+T"); button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Toggle")) + " Shift+Alt+T");
buttons.put(BTN_TOGGLE_ID.substring(3, BTN_TOGGLE_ID.length()), button); buttons.put(BTN_TOGGLE_ID.substring(3, BTN_TOGGLE_ID.length()), button);
//Detail toolbar button configure at AD_ToolBarButton
MToolBarButton[] officialButtons = MToolBarButton.getToolbarButtons("D", null); MToolBarButton[] officialButtons = MToolBarButton.getToolbarButtons("D", null);
for (MToolBarButton toolbarButton : officialButtons) { for (MToolBarButton toolbarButton : officialButtons) {
if ( !toolbarButton.isActive() ) { if ( !toolbarButton.isActive() ) {
@ -505,6 +537,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
} }
//container for status and error text
Hbox messageContainer = new Hbox(); Hbox messageContainer = new Hbox();
messageContainer.setPack("end"); messageContainer.setPack("end");
messageContainer.setAlign("center"); messageContainer.setAlign("center");
@ -564,7 +597,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* open customize grid dialog * Open customize grid view dialog.
* @param e * @param e
*/ */
protected void onCustomize(Event e) { protected void onCustomize(Event e) {
@ -579,7 +612,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* open process dropdown * open process list popup
* @param button * @param button
*/ */
protected void onProcess(Component button) { protected void onProcess(Component button) {
@ -594,7 +627,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* * Set event listener for DetailPane events
* @param listener * @param listener
*/ */
public void setEventListener(EventListener<Event> listener) { public void setEventListener(EventListener<Event> listener) {
@ -616,7 +649,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
/** /**
* @param index * @param index
* @return adtabpanel at index * @return IADTabpanel at index
*/ */
public IADTabpanel getADTabpanel(int index) { public IADTabpanel getADTabpanel(int index) {
if (index < 0 || index >= tabbox.getTabpanels().getChildren().size()) if (index < 0 || index >= tabbox.getTabpanels().getChildren().size())
@ -631,8 +664,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* * @return selected IADTabpanel
* @return selected adtabpanel
*/ */
public IADTabpanel getSelectedADTabpanel() { public IADTabpanel getSelectedADTabpanel() {
org.zkoss.zul.Tabpanel selectedPanel = tabbox.getSelectedPanel(); org.zkoss.zul.Tabpanel selectedPanel = tabbox.getSelectedPanel();
@ -646,15 +678,14 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* * @return selected {@link Tabpanel}
* @return {@link Tabpanel}
*/ */
public Tabpanel getSelectedPanel() { public Tabpanel getSelectedPanel() {
return (Tabpanel) tabbox.getSelectedPanel(); return (Tabpanel) tabbox.getSelectedPanel();
} }
/** /**
* * Set status and error text for selected tab.
* @param status * @param status
* @param error * @param error
*/ */
@ -676,6 +707,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
messageContainer.getChildren().clear(); messageContainer.getChildren().clear();
//store in attribute for retrieval in ON_CLICK event
messageContainer.setAttribute(STATUS_ERROR_ATTRIBUTE, error); messageContainer.setAttribute(STATUS_ERROR_ATTRIBUTE, error);
messageContainer.setAttribute(STATUS_TEXT_ATTRIBUTE, status); messageContainer.setAttribute(STATUS_TEXT_ATTRIBUTE, status);
messageContainer.setSclass(error ? "docstatus-error" : "docstatus-normal"); messageContainer.setSclass(error ? "docstatus-error" : "docstatus-normal");
@ -712,6 +744,11 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
} }
/**
* Shorten status text to a more presentable length.
* @param statusText
* @return shorten status text
*/
private String buildLabelText(String statusText) { private String buildLabelText(String statusText) {
if (statusText == null) if (statusText == null)
return ""; return "";
@ -724,6 +761,11 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
return statusText.substring(0, 80); return statusText.substring(0, 80);
} }
/**
* Shorten notification text to a more presentable length.
* @param statusText
* @return shorten notification text
*/
private String buildNotificationText(String statusText) { private String buildNotificationText(String statusText) {
if (statusText == null) if (statusText == null)
return ""; return "";
@ -777,20 +819,31 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
} }
/**
* Create popup content for message popup window
* @param status
*/
protected void createPopupContent(String status) { protected void createPopupContent(String status) {
Text t = new Text(status); Text t = new Text(status);
msgPopupCnt.getChildren().clear(); msgPopupCnt.getChildren().clear();
msgPopupCnt.appendChild(t); msgPopupCnt.appendChild(t);
} }
/**
* Show notification popup using Clients.showNotification
* @param error
* @param msg
*/
private void showPopup(boolean error, String msg) { private void showPopup(boolean error, String msg) {
Clients.showNotification(buildNotificationText(msg), "error", findTabpanel(this), "at_pointer", 3500, true); Clients.showNotification(buildNotificationText(msg), "error", findTabpanel(this), "at_pointer", 3500, true);
} }
/**
* Create message popup window
*/
private void createPopup() { private void createPopup() {
msgPopupCnt = new Div(); msgPopupCnt = new Div();
ZKUpdateUtil.setVflex(msgPopupCnt, "1"); ZKUpdateUtil.setVflex(msgPopupCnt, "1");
msgPopup = new Window(); msgPopup = new Window();
msgPopup.setVisible(false); msgPopup.setVisible(false);
@ -903,10 +956,13 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
} }
// update from customized implementation //Not use by ADTabpanel, for custom IADTabpanel implementation.
adtab.updateDetailToolbar(toolbar); adtab.updateDetailToolbar(toolbar);
} }
/**
* Update state of Process toolbar button.
*/
private void updateProcessToolbar() { private void updateProcessToolbar() {
int index = getSelectedIndex(); int index = getSelectedIndex();
if (index < 0 || index >= getTabcount()) return; if (index < 0 || index >= getTabcount()) return;
@ -934,8 +990,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* Edit current record * Edit current record of selected tab.
* @param formView * This event will make the selected tab becomes the new header tab, i.e become the selected tab of {@link CompositeADTabbox}.
* @param formView true to force form view.
* @throws Exception * @throws Exception
*/ */
public void onEdit(boolean formView) throws Exception { public void onEdit(boolean formView) throws Exception {
@ -944,7 +1001,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* fire the on activate detail event * Fire ON_ACTIVATE_DETAIL_EVENT for selected tab.
*/ */
public void fireActivateDetailEvent() { public void fireActivateDetailEvent() {
int index = tabbox.getSelectedIndex(); int index = tabbox.getSelectedIndex();
@ -972,7 +1029,6 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
*
* @param tabIndex * @param tabIndex
* @return true if tab at tabIndex is visible * @return true if tab at tabIndex is visible
*/ */
@ -996,7 +1052,6 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
*
* @param tabIndex * @param tabIndex
* @param enabled * @param enabled
*/ */
@ -1009,7 +1064,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* disable toolbar * Disable all toolbar buttons
*/ */
public void disableToolbar() { public void disableToolbar() {
int index = getSelectedIndex(); int index = getSelectedIndex();
@ -1025,6 +1080,11 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
} }
/**
* Find first {@link Tabpanel} that own comp.
* @param comp
* @return {@link Component}
*/
private Component findTabpanel(Component comp) { private Component findTabpanel(Component comp) {
Component parent = comp.getParent(); Component parent = comp.getParent();
while (parent != null) { while (parent != null) {
@ -1037,7 +1097,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* add new record * add new row
* @throws Exception * @throws Exception
*/ */
public void onNew() throws Exception { public void onNew() throws Exception {
@ -1052,6 +1112,11 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
private static final int VK_D = 0x44; private static final int VK_D = 0x44;
private static final int VK_O = 0x4F; private static final int VK_O = 0x4F;
private static final int VK_Q = 0x51; private static final int VK_Q = 0x51;
/**
* Handle shortcut key event
* @param keyEvent
*/
private void onCtrlKeyEvent(KeyEvent keyEvent) { private void onCtrlKeyEvent(KeyEvent keyEvent) {
ToolBarButton btn = null; ToolBarButton btn = null;
if (keyEvent.isAltKey() && !keyEvent.isCtrlKey() && keyEvent.isShiftKey()) { // Shift+Alt key if (keyEvent.isAltKey() && !keyEvent.isCtrlKey() && keyEvent.isShiftKey()) { // Shift+Alt key
@ -1096,14 +1161,12 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* tabpanel for adtabpanel * Custom {@link org.adempiere.webui.component.Tabpanel} implementation for DetailPane.
* @author hengsin
*
*/ */
public static class Tabpanel extends org.adempiere.webui.component.Tabpanel { public static class Tabpanel extends org.adempiere.webui.component.Tabpanel {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 8248794614430375822L; private static final long serialVersionUID = 8248794614430375822L;
@ -1156,7 +1219,6 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
*
* @return true if tab have been toggle to form view * @return true if tab have been toggle to form view
*/ */
public boolean isToggleToFormView() { public boolean isToggleToFormView() {
@ -1209,7 +1271,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
} }
/** /**
* set paging control container * Set paging control
* @param pagingControl * @param pagingControl
*/ */
public void setPagingControl(Div pagingControl) { public void setPagingControl(Div pagingControl) {
@ -1225,7 +1287,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
/** /**
* *
* @return paging control container * @return paging control
*/ */
public Div getPagingControl() { public Div getPagingControl() {
return pagingControl; return pagingControl;
@ -1243,7 +1305,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
/** /**
* *
* @return buttons from the detail toolbar * @return toolbar buttons from the detail toolbar
*/ */
private List<ToolBarButton> getToolbarButtons() { private List<ToolBarButton> getToolbarButtons() {
@ -1257,6 +1319,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
return list; return list;
} }
/**
* Create overflow button (show more) for mobile client.
*/
private void createOverflowButton() { private void createOverflowButton() {
overflowButton = new A(); overflowButton = new A();
overflowButton.setTooltiptext(Msg.getMsg(Env.getCtx(), "ShowMore")); overflowButton.setTooltiptext(Msg.getMsg(Env.getCtx(), "ShowMore"));
@ -1276,6 +1341,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}); });
} }
/**
* Create new overflow popup
*/
private void newOverflowPopup() { private void newOverflowPopup() {
overflowPopup = new Popup(); overflowPopup = new Popup();
overflowPopup.setHflex("min"); overflowPopup.setHflex("min");
@ -1291,7 +1359,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
private static class RecordToolbar extends Hlayout { private static class RecordToolbar extends Hlayout {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 5024630043211194429L; private static final long serialVersionUID = 5024630043211194429L;
private ToolBarButton btnFirst; private ToolBarButton btnFirst;
@ -1301,6 +1369,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
private ToolBarButton btnLast; private ToolBarButton btnLast;
private GridTab gridTab; private GridTab gridTab;
/**
* @param gridTab
*/
private RecordToolbar(GridTab gridTab) { private RecordToolbar(GridTab gridTab) {
this.gridTab = gridTab; this.gridTab = gridTab;
btnFirst = createButton("First", "First", "First"); btnFirst = createButton("First", "First", "First");
@ -1350,6 +1421,13 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
this.setValign("middle"); this.setValign("middle");
} }
/**
* Create toolbar button
* @param name
* @param image
* @param tooltip
* @return {@link ToolBarButton}
*/
private ToolBarButton createButton(String name, String image, String tooltip) private ToolBarButton createButton(String name, String image, String tooltip)
{ {
ToolBarButton btn = new ToolBarButton(""); ToolBarButton btn = new ToolBarButton("");
@ -1372,6 +1450,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
return btn; return btn;
} }
/**
* Dynamic update state of toolbar buttons
*/
private void dynamicDisplay() { private void dynamicDisplay() {
int rowCount = gridTab.getRowCount(); int rowCount = gridTab.getRowCount();
int currentRow = gridTab.getCurrentRow()+1; int currentRow = gridTab.getCurrentRow()+1;

View File

@ -81,28 +81,53 @@ import org.zkoss.zul.impl.XulElement;
*/ */
public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt, RendererCtrl, EventListener<Event> { public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt, RendererCtrl, EventListener<Event> {
/** Cell div component attribute to hold field column name value **/
protected static final String COLUMN_NAME_ATTR = "columnName";
/** Cell div component attribute to hold reference to editor component use to create display text for field **/
private static final String DISPLAY_COMPONENT_ATTR = "display.component";
/** Boolean execution attribute to indicate execution is handling the "Select" checkbox's ON_CHECK event **/
private static final String GRID_VIEW_ON_SELECT_ROW_ATTR = "gridView.onSelectRow";
/** Editor component attribute to store row index (absolute) **/
public static final String GRID_ROW_INDEX_ATTR = "grid.row.index"; public static final String GRID_ROW_INDEX_ATTR = "grid.row.index";
//styles for grid cell
private static final String CELL_DIV_STYLE = "height: 100%; cursor: pointer; "; private static final String CELL_DIV_STYLE = "height: 100%; cursor: pointer; ";
private static final String CELL_DIV_STYLE_ALIGN_CENTER = CELL_DIV_STYLE + "text-align:center; "; private static final String CELL_DIV_STYLE_ALIGN_CENTER = CELL_DIV_STYLE + "text-align:center; ";
private static final String CELL_DIV_STYLE_ALIGN_RIGHT = CELL_DIV_STYLE + "text-align:right; "; private static final String CELL_DIV_STYLE_ALIGN_RIGHT = CELL_DIV_STYLE + "text-align:right; ";
/** default max length for display text for field **/
private static final int MAX_TEXT_LENGTH_DEFAULT = 60; private static final int MAX_TEXT_LENGTH_DEFAULT = 60;
private GridTab gridTab; private GridTab gridTab;
private int windowNo; private int windowNo;
/** Sync field editor changes to GridField **/
private GridTabDataBinder dataBinder; private GridTabDataBinder dataBinder;
/** field editors **/
private Map<GridField, WEditor> editors = new LinkedHashMap<GridField, WEditor>(); private Map<GridField, WEditor> editors = new LinkedHashMap<GridField, WEditor>();
/** readonly field editors to get display text for field value **/
private Map<GridField, WEditor> readOnlyEditors = new LinkedHashMap<GridField, WEditor>(); private Map<GridField, WEditor> readOnlyEditors = new LinkedHashMap<GridField, WEditor>();
private Paging paging; private Paging paging;
/** internal listener for row event **/
private RowListener rowListener; private RowListener rowListener;
/** Grid that own this renderer **/
private Grid grid = null; private Grid grid = null;
/** GridView that uses this renderer **/
private GridView gridPanel = null; private GridView gridPanel = null;
/** current focus row **/
private Row currentRow; private Row currentRow;
/** values of current row. updated in {@link #render(Row, Object[], int)}. **/
private Object[] currentValues; private Object[] currentValues;
/** true if currrent row is in edit mode **/
private boolean editing = false; private boolean editing = false;
/** index of current row **/
private int currentRowIndex = -1; private int currentRowIndex = -1;
/** AD window content part that own this renderer **/
private AbstractADWindowContent m_windowPanel; private AbstractADWindowContent m_windowPanel;
/** internal listener for button ActionEvent **/
private ActionListener buttonListener; private ActionListener buttonListener;
/** /**
* Flag detect this view has customized column or not * Flag detect this view has customized column or not
@ -112,6 +137,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
/** DefaultFocusField */ /** DefaultFocusField */
private WEditor defaultFocusField = null; private WEditor defaultFocusField = null;
/** editor configuration for readonly field editor **/
private final static IEditorConfiguration readOnlyEditorConfiguration = new IEditorConfiguration() { private final static IEditorConfiguration readOnlyEditorConfiguration = new IEditorConfiguration() {
@Override @Override
public Boolean getReadonly() { public Boolean getReadonly() {
@ -135,6 +161,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
this.dataBinder = new GridTabDataBinder(gridTab); this.dataBinder = new GridTabDataBinder(gridTab);
} }
/**
* Get editor for GridField.
* @param gridField
* @return {@link WEditor}
*/
private WEditor getEditorCell(GridField gridField) { private WEditor getEditorCell(GridField gridField) {
WEditor editor = editors.get(gridField); WEditor editor = editors.get(gridField);
if (editor != null) { if (editor != null) {
@ -147,6 +178,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
return editor; return editor;
} }
/**
* Setup field editor
* @param gridField
* @param editor
*/
private void prepareFieldEditor(GridField gridField, WEditor editor) { private void prepareFieldEditor(GridField gridField, WEditor editor) {
if (editor instanceof WButtonEditor) if (editor instanceof WButtonEditor)
{ {
@ -172,9 +208,8 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
*
* @param field * @param field
* @return column index, -1 if not found * @return column index for field, -1 if not found
*/ */
public int getColumnIndex(GridField field) { public int getColumnIndex(GridField field) {
GridField[] fields = gridPanel.getFields(); GridField[] fields = gridPanel.getFields();
@ -185,6 +220,10 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
return -1; return -1;
} }
/**
* @param value
* @return readonly checkbox component
*/
private Component createReadonlyCheckbox(Object value) { private Component createReadonlyCheckbox(Object value) {
Checkbox checkBox = new Checkbox(); Checkbox checkBox = new Checkbox();
if (value != null && "true".equalsIgnoreCase(value.toString())) if (value != null && "true".equalsIgnoreCase(value.toString()))
@ -195,6 +234,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
return checkBox; return checkBox;
} }
/**
* Create invisible component for GridField with IsHeading=Y.
* To fill up space allocated for field component.
* @return invisible text box component
*/
private Component createInvisibleComponent() { private Component createInvisibleComponent() {
Textbox textBox = new Textbox(); Textbox textBox = new Textbox();
textBox.setDisabled(true); textBox.setDisabled(true);
@ -236,7 +280,8 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
* @param value * @param value
* @param gridField * @param gridField
* @param rowIndex * @param rowIndex
* @param isForceGetValue * @param isForceGetValue true to return text for field value even if IsDisplay return false. This is to allow Grid customization
* to override IsDisplay result.
* @return display text * @return display text
*/ */
private String getDisplayText(Object value, GridField gridField, int rowIndex, boolean isForceGetValue) private String getDisplayText(Object value, GridField gridField, int rowIndex, boolean isForceGetValue)
@ -271,8 +316,9 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
* @param rowIndex * @param rowIndex
* @param value * @param value
* @param gridField * @param gridField
* @param isForceGetValue * @param isForceGetValue true to return Component with value even if IsDisplay return false. This is to allow Grid customization
* @return * preference to override IsDisplay result.
* @return {@link Component}
*/ */
private Component getDisplayComponent(int rowIndex, Object value, GridField gridField, boolean isForceGetValue) { private Component getDisplayComponent(int rowIndex, Object value, GridField gridField, boolean isForceGetValue) {
Component component; Component component;
@ -308,6 +354,12 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
return component; return component;
} }
/**
* Apply AD_Style to field.
* @param gridField
* @param rowIndex
* @param component
*/
private void applyFieldStyle(GridField gridField, int rowIndex, private void applyFieldStyle(GridField gridField, int rowIndex,
HtmlBasedComponent component) { HtmlBasedComponent component) {
int AD_Style_ID = gridField.getAD_FieldStyle_ID(); int AD_Style_ID = gridField.getAD_FieldStyle_ID();
@ -319,6 +371,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
setComponentStyle(component, style.buildStyle(ThemeManager.getTheme(), gridRowCtx)); setComponentStyle(component, style.buildStyle(ThemeManager.getTheme(), gridRowCtx));
} }
/**
* Set component's style, sclass or zclass property
* @param component
* @param style "@sclass=" for sclass for "@zclass=" for zclass. default to style if there's no prefix.
*/
protected void setComponentStyle(HtmlBasedComponent component, String style) { protected void setComponentStyle(HtmlBasedComponent component, String style) {
if (style != null && style.startsWith(MStyle.SCLASS_PREFIX)) { if (style != null && style.startsWith(MStyle.SCLASS_PREFIX)) {
String sclass = style.substring(MStyle.SCLASS_PREFIX.length()); String sclass = style.substring(MStyle.SCLASS_PREFIX.length());
@ -341,6 +398,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* set label text, shorten text if length exceed define max length.
* @param text * @param text
* @param label * @param label
*/ */
@ -355,8 +413,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* * @return field editor list
* @return active editor list
*/ */
public List<WEditor> getEditors() { public List<WEditor> getEditors() {
List<WEditor> editorList = new ArrayList<WEditor>(); List<WEditor> editorList = new ArrayList<WEditor>();
@ -395,7 +452,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
else else
child = parent; child = parent;
} }
Component component = div!=null ? (Component) div.getAttribute("display.component") : null; Component component = div!=null ? (Component) div.getAttribute(DISPLAY_COMPONENT_ATTR) : null;
if (updateCellLabel) { if (updateCellLabel) {
if (component instanceof Label) { if (component instanceof Label) {
Label label = (Label)component; Label label = (Label)component;
@ -433,9 +490,10 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* Render data for row.
* @param row * @param row
* @param data * @param data Object[] values for row
* @param index * @param index row index within current page (i.e if page size is 25, index is one of 0 to 24).
*/ */
@Override @Override
public void render(Row row, Object[] data, int index) throws Exception { public void render(Row row, Object[] data, int index) throws Exception {
@ -579,7 +637,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
if (column.isVisible()) { if (column.isVisible()) {
Component component = getDisplayComponent(rowIndex, currentValues[i], gridPanelFields[i], isGridViewCustomized); Component component = getDisplayComponent(rowIndex, currentValues[i], gridPanelFields[i], isGridViewCustomized);
div.appendChild(component); div.appendChild(component);
div.setAttribute("display.component", component); div.setAttribute(DISPLAY_COMPONENT_ATTR, component);
if (gridPanelFields[i].isHeading()) { if (gridPanelFields[i].isHeading()) {
component.setVisible(false); component.setVisible(false);
} }
@ -599,7 +657,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
div.setStyle(divStyle); div.setStyle(divStyle);
ZKUpdateUtil.setWidth(div, "100%"); ZKUpdateUtil.setWidth(div, "100%");
div.setAttribute("columnName", gridPanelFields[i].getColumnName()); div.setAttribute(COLUMN_NAME_ATTR, gridPanelFields[i].getColumnName());
div.addEventListener(Events.ON_CLICK, rowListener); div.addEventListener(Events.ON_CLICK, rowListener);
div.addEventListener(Events.ON_DOUBLE_CLICK, rowListener); div.addEventListener(Events.ON_DOUBLE_CLICK, rowListener);
row.appendChild(div); row.appendChild(div);
@ -693,7 +751,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* @return Row * @return current {@link Row}
*/ */
public Row getCurrentRow() { public Row getCurrentRow() {
return currentRow; return currentRow;
@ -707,7 +765,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* Enter edit mode * Enter edit mode for current focus row.
*/ */
public void editCurrentRow() { public void editCurrentRow() {
if (ClientInfo.isMobile()) { if (ClientInfo.isMobile()) {
@ -771,6 +829,9 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
} }
/**
* @return true if it is own by {@link DetailPane}.
*/
private boolean isDetailPane() { private boolean isDetailPane() {
Component parent = grid.getParent(); Component parent = grid.getParent();
while (parent != null) { while (parent != null) {
@ -828,7 +889,8 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* set focus to first active editor * Set focus to first writable field editor (or default focus field editor if it is writable).
* If no field editor is writable, set focus to first visible field editor.
*/ */
public void focusToFirstEditor() { public void focusToFirstEditor() {
if (currentRow != null && currentRow.getParent() != null) { if (currentRow != null && currentRow.getParent() != null) {
@ -861,7 +923,6 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
*
* @param toFocus * @param toFocus
*/ */
protected void focusToEditor(WEditor toFocus) { protected void focusToEditor(WEditor toFocus) {
@ -877,7 +938,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* set focus to next readwrite editor from ref * set focus to next writable editor from ref
* @param ref * @param ref
*/ */
public void focusToNextEditor(WEditor ref) { public void focusToNextEditor(WEditor ref) {
@ -897,13 +958,16 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* * Set {@link GridView} that own this renderer.
* @param gridPanel * @param gridPanel
*/ */
public void setGridPanel(GridView gridPanel) { public void setGridPanel(GridView gridPanel) {
this.gridPanel = gridPanel; this.gridPanel = gridPanel;
} }
/**
* Internal listener for row event (ON_CLICK, ON_DOUBLE_CLICK and ON_OK).
*/
static class RowListener implements EventListener<Event> { static class RowListener implements EventListener<Event> {
private Grid _grid; private Grid _grid;
@ -914,7 +978,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
if (Events.ON_CLICK.equals(event.getName())) { if (Events.ON_CLICK.equals(event.getName())) {
if (Executions.getCurrent().getAttribute("gridView.onSelectRow") != null) if (Executions.getCurrent().getAttribute(GRID_VIEW_ON_SELECT_ROW_ATTR) != null)
return; return;
Event evt = new Event(Events.ON_CLICK, _grid, event.getTarget()); Event evt = new Event(Events.ON_CLICK, _grid, event.getTarget());
Events.sendEvent(_grid, evt); Events.sendEvent(_grid, evt);
@ -932,13 +996,15 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
} }
/** /**
* @return true if it is in edit mode, false otherwise * @return true if current row is in edit mode, false otherwise
*/ */
public boolean isEditing() { public boolean isEditing() {
return editing; return editing;
} }
/** /**
* Set AD window content part that own this renderer.
* {@link #buttonListener} need this to call {@link AbstractADWindowContent#actionPerformed(ActionEvent)}.
* @param windowPanel * @param windowPanel
*/ */
public void setADWindowPanel(AbstractADWindowContent windowPanel) { public void setADWindowPanel(AbstractADWindowContent windowPanel) {
@ -974,12 +1040,15 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
else else
Events.sendEvent(event.getTarget().getParent(), event); Events.sendEvent(event.getTarget().getParent(), event);
} else if (event.getTarget() instanceof Checkbox) { } else if (event.getTarget() instanceof Checkbox) {
Executions.getCurrent().setAttribute("gridView.onSelectRow", Boolean.TRUE); Executions.getCurrent().setAttribute(GRID_VIEW_ON_SELECT_ROW_ATTR, Boolean.TRUE);
Checkbox checkBox = (Checkbox) event.getTarget(); Checkbox checkBox = (Checkbox) event.getTarget();
Events.sendEvent(gridPanel, new Event("onSelectRow", gridPanel, checkBox)); Events.sendEvent(gridPanel, new Event("onSelectRow", gridPanel, checkBox));
} }
} }
/**
* @return {@link GridView#isShowCurrentRowIndicatorColumn}
*/
private boolean isShowCurrentRowIndicatorColumn() { private boolean isShowCurrentRowIndicatorColumn() {
return gridPanel != null && gridPanel.isShowCurrentRowIndicatorColumn(); return gridPanel != null && gridPanel.isShowCurrentRowIndicatorColumn();
} }

View File

@ -28,14 +28,14 @@ import org.zkoss.zul.event.ListDataEvent;
import org.zkoss.zul.ext.Sortable; import org.zkoss.zul.ext.Sortable;
/** /**
* * List model for {@link GridTable}
* @author Low Heng Sin * @author Low Heng Sin
* *
*/ */
public class GridTableListModel extends AbstractListModel<Object> implements TableModelListener, Sortable<Object> { public class GridTableListModel extends AbstractListModel<Object> implements TableModelListener, Sortable<Object> {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 698185856751242764L; private static final long serialVersionUID = 698185856751242764L;
private GridTable tableModel; private GridTable tableModel;
@ -47,10 +47,10 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
private int pageSize = -1; private int pageSize = -1;
private int pageNo = 0; private int pageNo = 0;
/** Edit mode flag. When editing is true, do not fire ListDataEvent.CONTENTS_CHANGED event. **/
private boolean editing = false; private boolean editing = false;
/** /**
*
* @param tableModel * @param tableModel
* @param windowNo * @param windowNo
*/ */
@ -142,7 +142,7 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
} }
/** /**
* Request components that attached to this model to re-render a row. * Delegate to {@link #updateComponent(int, int)}.
* @param row * @param row
*/ */
public void updateComponent(int row) { public void updateComponent(int row) {
@ -151,6 +151,7 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
/** /**
* Request components that attached to this model to re-render a range of row. * Request components that attached to this model to re-render a range of row.
* Fire ListDataEvent.CONTENTS_CHANGED event for fromRow to toRow.
* @param fromRow * @param fromRow
* @param toRow * @param toRow
*/ */
@ -162,6 +163,7 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
} }
/** /**
* Sort and fire ListDataEvent.CONTENTS_CHANGED event to notify UI component.
* @param cmpr * @param cmpr
* @param ascending * @param ascending
*/ */
@ -178,6 +180,7 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
} }
/** /**
* Handle TableModelEvent from GridTable.
* @param e * @param e
* @see TableModelListener#tableChanged(TableModelEvent) * @see TableModelListener#tableChanged(TableModelEvent)
*/ */
@ -216,6 +219,8 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
} }
/** /**
* Set editing to true/false.
* When editing is true, do not fire ListDataEvent.CONTENTS_CHANGED event.
* @param b * @param b
*/ */
public void setEditing(boolean b) { public void setEditing(boolean b) {

View File

@ -70,95 +70,132 @@ import org.zkoss.zul.event.ZulEvents;
import org.zkoss.zul.impl.CustomGridDataLoader; import org.zkoss.zul.impl.CustomGridDataLoader;
/** /**
* Grid view implemented using the Grid component. * Grid/List view implemented using the Grid component.
* @author Low Heng Sin * @author Low Heng Sin
* *
*/ */
public class GridView extends Vlayout implements EventListener<Event>, IdSpace, IFieldEditorContainer, StateChangeListener public class GridView extends Vlayout implements EventListener<Event>, IdSpace, IFieldEditorContainer, StateChangeListener
{ {
/** Event after the current row index has changed. **/
private static final String ON_POST_SELECTED_ROW_CHANGED_EVENT = "onPostSelectedRowChanged";
public static final String ZERO_PX_WIDTH = "0px"; public static final String ZERO_PX_WIDTH = "0px";
/** {@link Column} attribute to store grid field index **/
private static final String GRID_VIEW_GRID_FIELD_INDEX = "gridView.gridField.index"; private static final String GRID_VIEW_GRID_FIELD_INDEX = "gridView.gridField.index";
/** {@link Column} attribute to store initial/original column width value **/
public static final String COLUMN_WIDTH_ORIGINAL = "column.width.original"; public static final String COLUMN_WIDTH_ORIGINAL = "column.width.original";
/** {@link Column} attribute to store initial/original column hflex value **/
private static final String COLUMN_HFLEX_ORIGINAL = "column.hflex.original"; private static final String COLUMN_HFLEX_ORIGINAL = "column.hflex.original";
/** minimum column width for mobile client **/
private static final int MIN_COLUMN_MOBILE_WIDTH = 100; private static final int MIN_COLUMN_MOBILE_WIDTH = 100;
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 3995829393137424527L; private static final long serialVersionUID = 3995829393137424527L;
/** Style for Grid and Grid Footer **/
private static final String HEADER_GRID_STYLE = "border: none; margin:0; padding: 0;"; private static final String HEADER_GRID_STYLE = "border: none; margin:0; padding: 0;";
/** default paging size when GridView is in DetailPane **/
private static final int DEFAULT_DETAIL_PAGE_SIZE = 10; private static final int DEFAULT_DETAIL_PAGE_SIZE = 10;
/** default paging size for mobile client when GridView is in header panel **/
private static final int DEFAULT_MOBILE_PAGE_SIZE = 20; private static final int DEFAULT_MOBILE_PAGE_SIZE = 20;
/** default paging size when GridView is in header panel **/
private static final int DEFAULT_PAGE_SIZE = 20; private static final int DEFAULT_PAGE_SIZE = 20;
/** minimum column width **/
private static final int MIN_COLUMN_WIDTH = 100; private static final int MIN_COLUMN_WIDTH = 100;
/** maximum column width **/
private static final int MAX_COLUMN_WIDTH = 300; private static final int MAX_COLUMN_WIDTH = 300;
/** minimum column width for combobox field **/
private static final int MIN_COMBOBOX_WIDTH = 160; private static final int MIN_COMBOBOX_WIDTH = 160;
/** minimum column width for numeric field **/
private static final int MIN_NUMERIC_COL_WIDTH = 120; private static final int MIN_NUMERIC_COL_WIDTH = 120;
/** GridView boolean attribute to indicate ON_POST_SELECTED_ROW_CHANGED_EVENT have been posted in current execution cycle **/
private static final String ATTR_ON_POST_SELECTED_ROW_CHANGED = "org.adempiere.webui.adwindow.GridView.onPostSelectedRowChanged"; private static final String ATTR_ON_POST_SELECTED_ROW_CHANGED = "org.adempiere.webui.adwindow.GridView.onPostSelectedRowChanged";
/** Static Logger */ /** Static Logger */
private static CLogger s_log = CLogger.getCLogger (GridView.class); private static CLogger s_log = CLogger.getCLogger (GridView.class);
/** data grid instance **/
private Grid listbox = null; private Grid listbox = null;
private int pageSize = DEFAULT_PAGE_SIZE; private int pageSize = DEFAULT_PAGE_SIZE;
/** /**
* list field display in grid mode, in case user customize grid * list field display in grid mode, in case user customize grid
* this list container only customize list. * this list container only display list.
*/ */
private GridField[] gridField; private GridField[] gridFields;
/** GridTable model for GridTab **/
private AbstractTableModel tableModel; private AbstractTableModel tableModel;
private int numColumns = 5; private int numColumns = 5;
private int windowNo; private int windowNo;
/** GridTab that back this GridView **/
private GridTab gridTab; private GridTab gridTab;
/** true if this GridView instance have been init with GridTab **/
private boolean init; private boolean init;
/** Zk List model for {@link #tableModel} **/
private GridTableListModel listModel; private GridTableListModel listModel;
private Paging paging; private Paging paging;
/** Row renderer for this GridView instance **/
private GridTabRowRenderer renderer; private GridTabRowRenderer renderer;
/** Footer for paging **/
private Div gridFooter; private Div gridFooter;
/** true if current row is always in edit mode **/
private boolean modeless = true; private boolean modeless = true;
/** column click by user **/
private String columnOnClick; private String columnOnClick;
/** AD window content part that own this GridView instance **/
private AbstractADWindowContent windowPanel; private AbstractADWindowContent windowPanel;
/** true when grid is refreshing its data **/
private boolean refreshing; private boolean refreshing;
/** AD_Field_ID:Column Width **/
private Map<Integer, String> columnWidthMap; private Map<Integer, String> columnWidthMap;
/** true if it is in DetailPane **/
private boolean detailPaneMode; private boolean detailPaneMode;
/** checkbox to select all row of current page **/
protected Checkbox selectAll; protected Checkbox selectAll;
boolean isHasCustomizeData = false; /** true if there are AD_Tab_Customization for GridTab **/
protected boolean isHasCustomizeData = false;
/** true to add row indicator column after selection column (i.e second column) **/
private boolean showCurrentRowIndicatorColumn = true; private boolean showCurrentRowIndicatorColumn = true;
/** true if auto hide empty column feature is enable **/
private String m_isAutoHideEmptyColumn; private String m_isAutoHideEmptyColumn;
/**
* default constructor
*/
public GridView() public GridView()
{ {
this(0); this(0);
@ -214,6 +251,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
addEventListener("onCustomizeGrid", this); addEventListener("onCustomizeGrid", this);
} }
/**
* create data grid instances
*/
protected void createListbox() { protected void createListbox() {
listbox = new Grid(); listbox = new Grid();
listbox.setSizedByContent(false); listbox.setSizedByContent(false);
@ -223,6 +263,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Processing"))); listbox.setEmptyMessage(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Processing")));
} }
/**
* turn on/off detail pane mode
* @param detailPaneMode
* @param gridTab
*/
public void setDetailPaneMode(boolean detailPaneMode, GridTab gridTab) { public void setDetailPaneMode(boolean detailPaneMode, GridTab gridTab) {
if (this.detailPaneMode != detailPaneMode) { if (this.detailPaneMode != detailPaneMode) {
this.detailPaneMode = detailPaneMode; this.detailPaneMode = detailPaneMode;
@ -231,7 +276,10 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/** Returns the number of records to be displayed in detail grid */ /**
* @param gridTab
* @return the number of records to be displayed in detail grid
*/
private int getDetailPageSize(GridTab gridTab) { private int getDetailPageSize(GridTab gridTab) {
int size = DEFAULT_DETAIL_PAGE_SIZE; int size = DEFAULT_DETAIL_PAGE_SIZE;
String pageDetailSizes = MSysConfig.getValue(MSysConfig.ZK_PAGING_DETAIL_SIZE, Env.getAD_Client_ID(Env.getCtx())); String pageDetailSizes = MSysConfig.getValue(MSysConfig.ZK_PAGING_DETAIL_SIZE, Env.getAD_Client_ID(Env.getCtx()));
@ -279,10 +327,16 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
return size; return size;
} }
/**
* @return true if it is in detail pane mode
*/
public boolean isDetailPaneMode() { public boolean isDetailPaneMode() {
return this.detailPaneMode; return this.detailPaneMode;
} }
/**
* Update paging component with new paging size and notify model if paging size has change.
*/
private void updatePaging() { private void updatePaging() {
if (paging != null && paging.getPageSize() != pageSize) { if (paging != null && paging.getPageSize() != pageSize) {
paging.setPageSize(pageSize); paging.setPageSize(pageSize);
@ -296,7 +350,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
/** /**
* * Init data grid
* @param gridTab * @param gridTab
*/ */
public void init(GridTab gridTab) public void init(GridTab gridTab)
@ -324,6 +378,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
showRecordsCount(); showRecordsCount();
} }
/**
* Update {@link DetailPane} status with record count
*/
private void showRecordsCount() { private void showRecordsCount() {
Component parent = this.getParent(); Component parent = this.getParent();
while (parent != null) { while (parent != null) {
@ -337,6 +394,10 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* Setup {@link #gridFields} from gridTab.
* @param gridTab
*/
private void setupFields(GridTab gridTab) { private void setupFields(GridTab gridTab) {
this.gridTab = gridTab; this.gridTab = gridTab;
gridTab.addStateChangeListener(this); gridTab.addStateChangeListener(this);
@ -366,11 +427,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
} }
gridField = fieldList.toArray(new GridField[0]); gridFields = fieldList.toArray(new GridField[0]);
if (customComponent.length == 2) { if (customComponent.length == 2) {
String[] widths = customComponent[1].split("[,]"); String[] widths = customComponent[1].split("[,]");
for(int i = 0; i< gridField.length && i<widths.length; i++) { for(int i = 0; i< gridFields.length && i<widths.length; i++) {
columnWidthMap.put(gridField[i].getAD_Field_ID(), widths[i]); columnWidthMap.put(gridFields[i].getAD_Field_ID(), widths[i]);
} }
} }
m_isAutoHideEmptyColumn = tabCustomization.getIsAutoHideEmptyColumn(); m_isAutoHideEmptyColumn = tabCustomization.getIsAutoHideEmptyColumn();
@ -396,22 +457,22 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
}); });
gridField = new GridField[gridFieldList.size()]; gridFields = new GridField[gridFieldList.size()];
gridFieldList.toArray(gridField); gridFieldList.toArray(gridFields);
} }
numColumns = gridField.length; numColumns = gridFields.length;
} }
/** /**
* *
* @return boolean * @return true if data grid have been init with GridTab
*/ */
public boolean isInit() { public boolean isInit() {
return init; return init;
} }
/** /**
* call when tab is activated * Activate GridView (make visible or GridTab have been refreshed)
* @param gridTab * @param gridTab
*/ */
public void activate(GridTab gridTab) { public void activate(GridTab gridTab) {
@ -425,7 +486,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
/** /**
* refresh after switching from form view * Refresh data grid (after switching from form view or column setup has change)
* @param gridTab * @param gridTab
*/ */
public void refresh(GridTab gridTab) { public void refresh(GridTab gridTab) {
@ -445,12 +506,15 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* @return true if data grid is refreshing data from GridTab
*/
public boolean isRefreshing() { public boolean isRefreshing() {
return refreshing; return refreshing;
} }
/** /**
* Update current row from model * Update current row index from model
*/ */
public void updateListIndex() { public void updateListIndex() {
if (gridTab == null || !gridTab.isOpen()) return; if (gridTab == null || !gridTab.isOpen()) return;
@ -506,23 +570,29 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* hide paging component
*/
private void hidePagingControl() { private void hidePagingControl() {
if (gridFooter.isVisible()) if (gridFooter.isVisible())
gridFooter.setVisible(false); gridFooter.setVisible(false);
} }
/**
* show paging component
*/
private void showPagingControl() { private void showPagingControl() {
if (!gridFooter.isVisible()) if (!gridFooter.isVisible())
gridFooter.setVisible(true); gridFooter.setVisible(true);
} }
/** /**
* * echo ON_POST_SELECTED_ROW_CHANGED_EVENT after current row index has changed
*/ */
protected void echoOnPostSelectedRowChanged() { protected void echoOnPostSelectedRowChanged() {
if (getAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED) == null) { if (getAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED) == null) {
setAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED, Boolean.TRUE); setAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED, Boolean.TRUE);
Events.echoEvent("onPostSelectedRowChanged", this, null); Events.echoEvent(ON_POST_SELECTED_ROW_CHANGED_EVENT, this, null);
} }
} }
@ -535,11 +605,17 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
this.pageSize = pageSize; this.pageSize = pageSize;
} }
/**
* remove all components
*/
public void clear() public void clear()
{ {
this.getChildren().clear(); this.getChildren().clear();
} }
/**
* Setup {@link Columns} of data grid
*/
private void setupColumns() private void setupColumns()
{ {
if (init) return; if (init) return;
@ -595,36 +671,36 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
for (int i = 0; i < numColumns; i++) for (int i = 0; i < numColumns; i++)
{ {
// IDEMPIERE-2148: when has tab customize, ignore check properties isDisplayedGrid // IDEMPIERE-2148: when has tab customize, ignore check properties isDisplayedGrid
if ((isHasCustomizeData || gridField[i].isDisplayedGrid()) && !gridField[i].isToolbarOnlyButton()) if ((isHasCustomizeData || gridFields[i].isDisplayedGrid()) && !gridFields[i].isToolbarOnlyButton())
{ {
colnames.put(index, gridField[i].getHeader()); colnames.put(index, gridFields[i].getHeader());
index++; index++;
org.zkoss.zul.Column column = new Column(); org.zkoss.zul.Column column = new Column();
column.setAttribute(GRID_VIEW_GRID_FIELD_INDEX, i); column.setAttribute(GRID_VIEW_GRID_FIELD_INDEX, i);
column.setHeight("2em"); column.setHeight("2em");
int colindex =tableModel.findColumn(gridField[i].getColumnName()); int colindex =tableModel.findColumn(gridFields[i].getColumnName());
column.setSortAscending(new SortComparator(colindex, true, Env.getLanguage(Env.getCtx()))); column.setSortAscending(new SortComparator(colindex, true, Env.getLanguage(Env.getCtx())));
column.setSortDescending(new SortComparator(colindex, false, Env.getLanguage(Env.getCtx()))); column.setSortDescending(new SortComparator(colindex, false, Env.getLanguage(Env.getCtx())));
//IDEMPIERE-2898 - UX: Field only showing title at header on grid //IDEMPIERE-2898 - UX: Field only showing title at header on grid
if( gridField[i].isFieldOnly() ) if( gridFields[i].isFieldOnly() )
column.setLabel(""); column.setLabel("");
else else
column.setLabel(gridField[i].getHeader()); column.setLabel(gridFields[i].getHeader());
if (columnWidthMap != null && columnWidthMap.get(gridField[i].getAD_Field_ID()) != null && !columnWidthMap.get(gridField[i].getAD_Field_ID()).equals("")) { if (columnWidthMap != null && columnWidthMap.get(gridFields[i].getAD_Field_ID()) != null && !columnWidthMap.get(gridFields[i].getAD_Field_ID()).equals("")) {
ZKUpdateUtil.setWidth(column, columnWidthMap.get(gridField[i].getAD_Field_ID())); ZKUpdateUtil.setWidth(column, columnWidthMap.get(gridFields[i].getAD_Field_ID()));
} else { } else {
if (gridField[i].getDisplayType()==DisplayType.YesNo) { if (gridFields[i].getDisplayType()==DisplayType.YesNo) {
if (i > 0) { if (i > 0) {
ZKUpdateUtil.setHflex(column, "min"); ZKUpdateUtil.setHflex(column, "min");
} else { } else {
int estimatedWidth=60; int estimatedWidth=60;
int headerWidth = (gridField[i].getHeader().length()+2) * 8; int headerWidth = (gridFields[i].getHeader().length()+2) * 8;
if (headerWidth > estimatedWidth) if (headerWidth > estimatedWidth)
estimatedWidth = headerWidth; estimatedWidth = headerWidth;
ZKUpdateUtil.setWidth(column, estimatedWidth+"px"); ZKUpdateUtil.setWidth(column, estimatedWidth+"px");
} }
} else if (DisplayType.isNumeric(gridField[i].getDisplayType()) && "Line".equals(gridField[i].getColumnName())) { } else if (DisplayType.isNumeric(gridFields[i].getDisplayType()) && "Line".equals(gridFields[i].getColumnName())) {
//special treatment for line //special treatment for line
if (i > 0) if (i > 0)
ZKUpdateUtil.setHflex(column, "min"); ZKUpdateUtil.setHflex(column, "min");
@ -632,33 +708,33 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
ZKUpdateUtil.setWidth(column, "60px"); ZKUpdateUtil.setWidth(column, "60px");
} else { } else {
int estimatedWidth = 0; int estimatedWidth = 0;
if (DisplayType.isNumeric(gridField[i].getDisplayType())) if (DisplayType.isNumeric(gridFields[i].getDisplayType()))
estimatedWidth = MIN_NUMERIC_COL_WIDTH; estimatedWidth = MIN_NUMERIC_COL_WIDTH;
else if (DisplayType.isLookup(gridField[i].getDisplayType())) else if (DisplayType.isLookup(gridFields[i].getDisplayType()))
estimatedWidth = MIN_COMBOBOX_WIDTH; estimatedWidth = MIN_COMBOBOX_WIDTH;
else if (DisplayType.isText(gridField[i].getDisplayType())) else if (DisplayType.isText(gridFields[i].getDisplayType()))
estimatedWidth = gridField[i].getDisplayLength() * 8; estimatedWidth = gridFields[i].getDisplayLength() * 8;
else else
estimatedWidth = MIN_COLUMN_WIDTH; estimatedWidth = MIN_COLUMN_WIDTH;
int headerWidth = (gridField[i].getHeader().length()+2) * 8; int headerWidth = (gridFields[i].getHeader().length()+2) * 8;
if (headerWidth > estimatedWidth) if (headerWidth > estimatedWidth)
estimatedWidth = headerWidth; estimatedWidth = headerWidth;
//hflex=min for first column not working well //hflex=min for first column not working well
if (i > 0 && !ClientInfo.isMobile()) if (i > 0 && !ClientInfo.isMobile())
{ {
if (DisplayType.isLookup(gridField[i].getDisplayType())) if (DisplayType.isLookup(gridFields[i].getDisplayType()))
{ {
if (headerWidth > MIN_COMBOBOX_WIDTH) if (headerWidth > MIN_COMBOBOX_WIDTH)
ZKUpdateUtil.setHflex(column, "min"); ZKUpdateUtil.setHflex(column, "min");
} }
else if (DisplayType.isNumeric(gridField[i].getDisplayType())) else if (DisplayType.isNumeric(gridFields[i].getDisplayType()))
{ {
if (headerWidth > MIN_NUMERIC_COL_WIDTH) if (headerWidth > MIN_NUMERIC_COL_WIDTH)
ZKUpdateUtil.setHflex(column, "min"); ZKUpdateUtil.setHflex(column, "min");
} }
else if (!DisplayType.isText(gridField[i].getDisplayType())) else if (!DisplayType.isText(gridFields[i].getDisplayType()))
{ {
if (headerWidth > MIN_COLUMN_WIDTH) if (headerWidth > MIN_COLUMN_WIDTH)
ZKUpdateUtil.setHflex(column, "min"); ZKUpdateUtil.setHflex(column, "min");
@ -689,6 +765,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* Render data grid
*/
private void render() private void render()
{ {
updateEmptyMessage(); updateEmptyMessage();
@ -722,7 +801,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
/** /**
* auto hide empty columns * Auto hide empty columns (if auto hide empty column feature have been turned on)
*/ */
protected void autoHideEmptyColumns() { protected void autoHideEmptyColumns() {
if (!isAutoHideEmptyColumns()) { if (!isAutoHideEmptyColumns()) {
@ -748,7 +827,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
continue; continue;
int index = (Integer)value; int index = (Integer)value;
for(int i = 0; i < gridTabFields.length; i++) { for(int i = 0; i < gridTabFields.length; i++) {
if (gridField[index].getAD_Field_ID() == gridTabFields[i].getAD_Field_ID()) { if (gridFields[index].getAD_Field_ID() == gridTabFields[i].getAD_Field_ID()) {
indexMap.put(index, i); indexMap.put(index, i);
break; break;
} }
@ -771,14 +850,14 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
if (paging != null && paging.getPageSize() > 0) { if (paging != null && paging.getPageSize() > 0) {
rowIndex = (paging.getActivePage() * paging.getPageSize()) + rowIndex; rowIndex = (paging.getActivePage() * paging.getPageSize()) + rowIndex;
} }
String display = renderer.getDisplayTextWithEditorCheck(values[valueIndex], gridField[index], rowIndex); String display = renderer.getDisplayTextWithEditorCheck(values[valueIndex], gridFields[index], rowIndex);
if (!Util.isEmpty(display, true)) { if (!Util.isEmpty(display, true)) {
hideColumn = false; hideColumn = false;
break; break;
} else if (gridTab.getCurrentRow() == rowIndex && gridTab.isNew()) { } else if (gridTab.getCurrentRow() == rowIndex && gridTab.isNew()) {
if (gridField[index].isEditable(false) && (gridField[index].isMandatory(false) || !Util.isEmpty(gridField[index].getVO().MandatoryLogic) if (gridFields[index].isEditable(false) && (gridFields[index].isMandatory(false) || !Util.isEmpty(gridFields[index].getVO().MandatoryLogic)
|| !Util.isEmpty(gridField[index].getVO().DisplayLogic) || !Util.isEmpty(gridFields[index].getVO().DisplayLogic)
|| !Util.isEmpty(gridField[index].getVO().ReadOnlyLogic))) { || !Util.isEmpty(gridFields[index].getVO().ReadOnlyLogic))) {
hideColumn = false; hideColumn = false;
break; break;
} }
@ -808,6 +887,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* @return if auto hide empty columns feature have been turned on
*/
private boolean isAutoHideEmptyColumns() { private boolean isAutoHideEmptyColumns() {
if (!Util.isEmpty(m_isAutoHideEmptyColumn, true)) if (!Util.isEmpty(m_isAutoHideEmptyColumn, true))
return "Y".equalsIgnoreCase(m_isAutoHideEmptyColumn); return "Y".equalsIgnoreCase(m_isAutoHideEmptyColumn);
@ -815,6 +897,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
return MSysConfig.getBooleanValue(MSysConfig.ZK_GRID_AUTO_HIDE_EMPTY_COLUMNS, false, Env.getAD_Client_ID(Env.getCtx())); return MSysConfig.getBooleanValue(MSysConfig.ZK_GRID_AUTO_HIDE_EMPTY_COLUMNS, false, Env.getAD_Client_ID(Env.getCtx()));
} }
/**
* Show zero records for processing message
*/
private void updateEmptyMessage() { private void updateEmptyMessage() {
if (gridTab.getRowCount() == 0) if (gridTab.getRowCount() == 0)
{ {
@ -826,6 +911,10 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* Update {@link #listModel} with {@link #tableModel} changes.
* Re-create {@link #renderer}.
*/
private void updateModel() { private void updateModel() {
if (listModel != null) if (listModel != null)
((GridTable)tableModel).removeTableModelListener(listModel); ((GridTable)tableModel).removeTableModelListener(listModel);
@ -846,13 +935,14 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
/** /**
* deactivate panel * Deactivate Grid View. Stop editing if current row is in edit mode.
*/ */
public void deactivate() { public void deactivate() {
if (renderer != null && renderer.isEditing()) if (renderer != null && renderer.isEditing())
renderer.stopEditing(true); renderer.stopEditing(true);
} }
@Override
public void onEvent(Event event) throws Exception public void onEvent(Event event) throws Exception
{ {
if (event == null) if (event == null)
@ -872,15 +962,15 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
if (cmp.getParent() instanceof org.zkoss.zul.Row) if (cmp.getParent() instanceof org.zkoss.zul.Row)
{ {
row = (Row) cmp.getParent(); row = (Row) cmp.getParent();
columnName = (String) cmp.getAttribute("columnName"); columnName = (String) cmp.getAttribute(GridTabRowRenderer.COLUMN_NAME_ATTR);
} }
} }
} }
if (row != null) if (row != null)
{ {
//click on selected row to enter edit mode
if (row == renderer.getCurrentRow()) if (row == renderer.getCurrentRow())
{ {
//click on selected row to enter edit mode
if (!renderer.isEditing()) if (!renderer.isEditing())
{ {
renderer.editCurrentRow(); renderer.editCurrentRow();
@ -892,6 +982,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
else else
{ {
//change selection of current row
int index = listbox.getRows().getChildren().indexOf(row); int index = listbox.getRows().getChildren().indexOf(row);
if (index >= 0 ) { if (index >= 0 ) {
columnOnClick = columnName; columnOnClick = columnName;
@ -941,6 +1032,10 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* @param gridView
* @return {@link Center} that own this GridView instance
*/
private Center findCenter(GridView gridView) { private Center findCenter(GridView gridView) {
if (gridView == null) if (gridView == null)
return null; return null;
@ -953,6 +1048,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
return null; return null;
} }
/**
* @return true if all row of current page is selected
*/
private boolean isAllSelected() { private boolean isAllSelected() {
org.zkoss.zul.Rows rows = listbox.getRows(); org.zkoss.zul.Rows rows = listbox.getRows();
List<Component> childs = rows.getChildren(); List<Component> childs = rows.getChildren();
@ -974,6 +1072,10 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
return all; return all;
} }
/**
* turn on/off select all rows for current page
* @param b
*/
private void toggleSelectionForAll(boolean b) { private void toggleSelectionForAll(boolean b) {
org.zkoss.zul.Rows rows = listbox.getRows(); org.zkoss.zul.Rows rows = listbox.getRows();
List<Component> childs = rows.getChildren(); List<Component> childs = rows.getChildren();
@ -995,6 +1097,10 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* Update list model and data grid with new current row index
* @param index
*/
private void onSelectedRowChange(int index) { private void onSelectedRowChange(int index) {
if (updateModelIndex(index)) { if (updateModelIndex(index)) {
updateListIndex(); updateListIndex();
@ -1002,7 +1108,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
/** /**
* Event after the current selected row change * Event after the current row index has changed.
*/ */
public void onPostSelectedRowChanged() { public void onPostSelectedRowChanged() {
removeAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED); removeAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED);
@ -1060,6 +1166,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* Focus to first editor field if GridView instance is not own by the selected detail tab panel.
*/
private void focusToFirstEditorIfNotDetailTab() { private void focusToFirstEditorIfNotDetailTab() {
ADTabpanel adtabpanel = null; ADTabpanel adtabpanel = null;
boolean setFocus = true; boolean setFocus = true;
@ -1095,6 +1204,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
onPostSelectedRowChanged(); onPostSelectedRowChanged();
} }
/**
* Focus to row.
* If it is in edit mode, assume row is the current editing row.
* @param row
*/
private void focusToRow(org.zkoss.zul.Row row) { private void focusToRow(org.zkoss.zul.Row row) {
if (renderer.isEditing()) { if (renderer.isEditing()) {
if (columnOnClick != null && columnOnClick.trim().length() > 0) { if (columnOnClick != null && columnOnClick.trim().length() > 0) {
@ -1123,7 +1237,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
for(Object element : list) { for(Object element : list) {
if (element instanceof Div) { if (element instanceof Div) {
Div div = (Div) element; Div div = (Div) element;
if (columnOnClick.equals(div.getAttribute("columnName"))) { if (columnOnClick.equals(div.getAttribute(GridTabRowRenderer.COLUMN_NAME_ATTR))) {
cmp = div.getFirstChild(); cmp = div.getFirstChild();
Clients.response(new AuScript(null, "idempiere.scrollToRow('" + cmp.getUuid() + "');")); Clients.response(new AuScript(null, "idempiere.scrollToRow('" + cmp.getUuid() + "');"));
break; break;
@ -1135,6 +1249,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* @param row
* @param index
* @return true if row have been rendered by row renderer
*/
private boolean isRowRendered(org.zkoss.zul.Row row, int index) { private boolean isRowRendered(org.zkoss.zul.Row row, int index) {
if (row.getChildren().size() == 0) { if (row.getChildren().size() == 0) {
return false; return false;
@ -1146,6 +1265,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
return true; return true;
} }
/**
* Update gridTab current row index.
* @param rowIndex row index of current page
* @return true if gridTab current row index has change
*/
private boolean updateModelIndex(int rowIndex) { private boolean updateModelIndex(int rowIndex) {
if (pageSize > 0) { if (pageSize > 0) {
int start = listModel.getPage() * listModel.getPageSize(); int start = listModel.getPage() * listModel.getPageSize();
@ -1214,6 +1338,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
selectAll.setChecked(false); selectAll.setChecked(false);
} }
/**
* Perform dynamic display for editors in list
* @param noData true if data grid is empty
* @param list
*/
private void dynamicDisplayEditors(boolean noData, List<WEditor> list) { private void dynamicDisplayEditors(boolean noData, List<WEditor> list) {
for (WEditor comp : list) for (WEditor comp : list)
{ {
@ -1242,6 +1371,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* @return true if this GridView instance is own by DetailPane
*/
private boolean isDetailPane() { private boolean isDetailPane() {
Component parent = this.getParent(); Component parent = this.getParent();
while (parent != null) { while (parent != null) {
@ -1261,6 +1393,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
this.windowNo = windowNo; this.windowNo = windowNo;
} }
/**
* If current row is in edit mode, set focus to first field editor
*/
@Override @Override
public void focus() { public void focus() {
if (renderer != null && renderer.isEditing()) { if (renderer != null && renderer.isEditing()) {
@ -1299,6 +1434,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
/** /**
* Set AD window content part that own this GridView instance
* @param winPanel * @param winPanel
*/ */
public void setADWindowPanel(AbstractADWindowContent winPanel) { public void setADWindowPanel(AbstractADWindowContent winPanel) {
@ -1307,6 +1443,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
renderer.setADWindowPanel(windowPanel); renderer.setADWindowPanel(windowPanel);
} }
/**
* Re-Init GridView with cache gridTab.
*/
public void reInit() { public void reInit() {
listbox.getChildren().clear(); listbox.getChildren().clear();
listbox.detach(); listbox.detach();
@ -1343,15 +1482,23 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
/** /**
* list field display in grid mode, in case user customize grid * list field display in grid mode, in case user customize grid
* this list container only customize list. * this list container only customize list.
* @return GridField[]
*/ */
public GridField[] getFields() { public GridField[] getFields() {
return gridField; return gridFields;
} }
/**
* call {@link #onEditCurrentRow(Event)}
*/
public void onEditCurrentRow() { public void onEditCurrentRow() {
onEditCurrentRow(null); onEditCurrentRow(null);
} }
/**
* Edit current row
* @param event
*/
public void onEditCurrentRow(Event event) { public void onEditCurrentRow(Event event) {
if (!renderer.isEditing()) { if (!renderer.isEditing()) {
Row currentRow = renderer.getCurrentRow(); Row currentRow = renderer.getCurrentRow();
@ -1366,6 +1513,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* If current row is in edit mode, set focus to first writable field editor.
*/
@Override @Override
public void focusToFirstEditor() { public void focusToFirstEditor() {
if (renderer.isEditing()) { if (renderer.isEditing()) {
@ -1397,10 +1547,18 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* Parent component change notification from ADTabpanel that own this GridView instance (Usually
* after movement between Header and DetailPane panel).
* Re-position paging component.
*/
protected void onADTabPanelParentChanged() { protected void onADTabPanelParentChanged() {
positionPagingControl(); positionPagingControl();
} }
/**
* Set position of paging component depends on whether GridView is in header or DetailPane panel.
*/
private void positionPagingControl() { private void positionPagingControl() {
if (isDetailPane()) { if (isDetailPane()) {
Component parent = this.getParent(); Component parent = this.getParent();
@ -1411,6 +1569,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
parent = parent.getParent(); parent = parent.getParent();
} }
//use simplify paging presentation for DetailPane
if (paging != null) if (paging != null)
paging.setDetailed(false); paging.setDetailed(false);
} }
@ -1426,12 +1585,18 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
} }
} }
/**
* call editorTaverseCallback for all field editors.
*/
@Override @Override
public void editorTraverse(Callback<WEditor> editorTaverseCallback) { public void editorTraverse(Callback<WEditor> editorTaverseCallback) {
editorTraverse(editorTaverseCallback, renderer.getEditors()); editorTraverse(editorTaverseCallback, renderer.getEditors());
} }
/**
* @return true if current row indicator column is visible.
*/
public boolean isShowCurrentRowIndicatorColumn() { public boolean isShowCurrentRowIndicatorColumn() {
return showCurrentRowIndicatorColumn; return showCurrentRowIndicatorColumn;
} }

View File

@ -19,54 +19,53 @@ import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.EventListener;
/** /**
* * Interface for header+details AD_Tabs UI for AD_Window.
* @author <a href="mailto:hengsin@gmail.com">Low Heng Sin</a> * @author <a href="mailto:hengsin@gmail.com">Low Heng Sin</a>
* *
*/ */
public interface IADTabbox extends UIPart { public interface IADTabbox extends UIPart {
/** /**
* * @return selected header tab index
* @return selected tab index
*/ */
public int getSelectedIndex(); public int getSelectedIndex();
/** /**
* * set selected header tab
* @param i tab index * @param i tab index
*/ */
public void setSelectedIndex(int i); public void setSelectedIndex(int i);
/** /**
* Change selected header tab index from oldTabIndex to newTabIndex
* @param oldTabIndex * @param oldTabIndex
* @param newTabIndex * @param newTabIndex
* @return * @return true if selected tab successfully change to newTabIndex
*/ */
public boolean updateSelectedIndex(int oldTabIndex, int newTabIndex); public boolean updateSelectedIndex(int oldTabIndex, int newTabIndex);
/** /**
* @return selected tab panel reference * @return selected header {@link IADTabpanel} instance
*/ */
public IADTabpanel getSelectedTabpanel(); public IADTabpanel getSelectedTabpanel();
/** /**
*
* @param fromIndex * @param fromIndex
* @param toIndex * @param toIndex
* @return boolean * @return true if user can change selected tab from fromIndex to toIndex, false otherwise
*/ */
public boolean canNavigateTo(int fromIndex, int toIndex); public boolean canNavigateTo(int fromIndex, int toIndex);
/** /**
* @param index * @param index
* @return boolean * @return true if tab at index visible, false otherwise
*/ */
public boolean isDisplay(int index); public boolean isDisplay(int index);
/** /**
* * add new tab (AD_Tab)
* @param tab * @param tab {@link GridTab} for AD_Tab
* @param tabPanel * @param tabPanel {@link IADTabpanel} instance for AD_Tab
*/ */
public void addTab(GridTab tab, IADTabpanel tabPanel); public void addTab(GridTab tab, IADTabpanel tabPanel);
@ -76,41 +75,43 @@ public interface IADTabbox extends UIPart {
public int getTabCount(); public int getTabCount();
/** /**
* Evaluate state of each tab after DataStatusEvent
* @param e * @param e
*/ */
public void evaluate(DataStatusEvent e); public void evaluate(DataStatusEvent e);
/** /**
* @return path to the active tab * @return folder like parent/child path to the selected tab (for e.g Business Partner > Location)
*/ */
public String getPath(); public String getPath();
/** /**
* * Set event listener for tab selection change event
* @param listener * @param listener
*/ */
public void setSelectionEventListener(EventListener<Event> listener); public void setSelectionEventListener(EventListener<Event> listener);
/** /**
* @param index * @param index tab index
* @return IADTabpanel * @return {@link IADTabpanel} instance at index
*/ */
public IADTabpanel getADTabpanel(int index); public IADTabpanel getADTabpanel(int index);
/** /**
* @param gTab * @param gTab
* @return IADTabpanel or null if not found * @return {@link IADTabpanel} instance for gTab or null if not found
*/ */
public IADTabpanel findADTabpanel(GridTab gTab); public IADTabpanel findADTabpanel(GridTab gTab);
/** /**
* * Set AD Window content part that own this IADTabbox instance.
* @param abstractADWindowPanel * @param abstractADWindowPanel
*/ */
public void setADWindowPanel(AbstractADWindowContent abstractADWindowPanel); public void setADWindowPanel(AbstractADWindowContent abstractADWindowPanel);
/** /**
* drill down to the current selected adtabpanel * Edit current row of selected detail tab.
* The selected detail tab will become the new header tab.
*/ */
public void onDetailRecord(); public void onDetailRecord();
@ -120,6 +121,7 @@ public interface IADTabbox extends UIPart {
public boolean isSortTab(); public boolean isSortTab();
/** /**
* Call {@link IADTabpanel#needSave(boolean, boolean)}
* @param rowChange * @param rowChange
* @param onlyRealChange * @param onlyRealChange
* @return true if there are changes pending to be save * @return true if there are changes pending to be save
@ -127,54 +129,55 @@ public interface IADTabbox extends UIPart {
public boolean needSave(boolean rowChange, boolean onlyRealChange); public boolean needSave(boolean rowChange, boolean onlyRealChange);
/** /**
* ignore all pending changes * ignore/undo all pending changes
*/ */
public void dataIgnore(); public void dataIgnore();
/** /**
* @return selected header grid tab * @return {@link GridTab} instance of header tab
*/ */
public GridTab getSelectedGridTab(); public GridTab getSelectedGridTab();
/** /**
* * Save changes
* @param onSaveEvent * @param onSaveEvent
* @return true if save is successfull * @return true if save is successful
*/ */
public boolean dataSave(boolean onSaveEvent); public boolean dataSave(boolean onSaveEvent);
/** /**
* * Update status text of {@link DetailPane}
* @param status * @param status
* @param error * @param error
*/ */
public void setDetailPaneStatusMessage(String status, boolean error); public void setDetailPaneStatusMessage(String status, boolean error);
/** /**
* @return the currently selected detail adtabpanel * @return the selected detail {@link IADTabpanel} instance
*/ */
IADTabpanel getSelectedDetailADTabpanel(); IADTabpanel getSelectedDetailADTabpanel();
/** /**
* @return dirty adtabpanel that need save ( if any ) * @return dirty {@link IADTabpanel} that need save ( if any )
*/ */
IADTabpanel getDirtyADTabpanel(); IADTabpanel getDirtyADTabpanel();
/** /**
* * Call {@link DetailPane#updateToolbar(boolean, boolean)}
* @param changed * @param changed true if header tab has changed
* @param readOnly * @param readOnly true if header tab is readonly
*/ */
public void updateDetailPaneToolbar(boolean changed, boolean readOnly); public void updateDetailPaneToolbar(boolean changed, boolean readOnly);
/** /**
* Set selected tab of {@link DetailPane} to tabIndex.
* @param tabIndex * @param tabIndex
* @param currentRow * @param currentRow set current row of tab at tabIndex to currentRow
*/ */
public void setDetailPaneSelectedTab(int tabIndex, int currentRow); public void setDetailPaneSelectedTab(int tabIndex, int currentRow);
/** /**
* @return true if all the tabs of detail pane have been linked up with adtabpanel * @return true if all the tabs of detail pane have been linked up with header tab
*/ */
public boolean isDetailPaneLoaded(); public boolean isDetailPaneLoaded();

View File

@ -21,13 +21,16 @@ import org.zkoss.zul.Button;
import org.zkoss.zul.Toolbar; import org.zkoss.zul.Toolbar;
/** /**
* Interface for UI component that edit/display record using ad_tab definitions * Interface for AD_Tab UI (with all the AD_Fields definition)
* @author Low Heng Sin * @author Low Heng Sin
* *
*/ */
public interface IADTabpanel extends Component, Evaluatee { public interface IADTabpanel extends Component, Evaluatee {
/** Activate/Deactivate event for IADTabpanel. Fire for init or after tab selection changed. **/
public static final String ON_ACTIVATE_EVENT = "onActivate"; public static final String ON_ACTIVATE_EVENT = "onActivate";
/** Component boolean attribute to indicate ON_ACTIVATE_EVENT have been posted for the current execution cycle **/
public static final String ATTR_ON_ACTIVATE_POSTED = "org.adempiere.webui.adwindow.IADTabpanel.onActivatePosted"; public static final String ATTR_ON_ACTIVATE_POSTED = "org.adempiere.webui.adwindow.IADTabpanel.onActivatePosted";
/** /**
@ -47,12 +50,12 @@ public interface IADTabpanel extends Component, Evaluatee {
public int getTabLevel(); public int getTabLevel();
/** /**
* @return tablename * @return table name from GridTab
*/ */
public String getTableName(); public String getTableName();
/** /**
* @return record ID * @return record ID of current row
*/ */
public int getRecord_ID(); public int getRecord_ID();
@ -62,40 +65,39 @@ public interface IADTabpanel extends Component, Evaluatee {
public boolean isCurrent(); public boolean isCurrent();
/** /**
* * @return title of tab
* @return title
*/ */
public String getTitle(); public String getTitle();
/** /**
* Render the panel * Layout fields of the tab panel
*/ */
public void createUI(); public void createUI();
/** /**
* * @return {@link GridTab} instance that back this IADTabpanel instance
* @return GridTab
*/ */
public GridTab getGridTab(); public GridTab getGridTab();
/** /**
* activate/deactivate the panel * Activate/deactivate this IADTabpanel instance.
* Call by init or after tab selection changed.
* @param b * @param b
*/ */
public void activate(boolean b); public void activate(boolean b);
/** /**
* retrieve data from db * Execute query through the backed {@link GridTab} instance.
*/ */
public void query(); public void query();
/** /**
* Refresh from db * Refresh data through the backed {@link GridTab} instance.
*/ */
public void refresh(); public void refresh();
/** /**
* retrieve data from db * Call {@link GridTab#query(boolean, int, int)}
* @param currentRows * @param currentRows
* @param currentDays * @param currentDays
* @param maxRows * @param maxRows
@ -103,58 +105,56 @@ public interface IADTabpanel extends Component, Evaluatee {
public void query(boolean currentRows, int currentDays, int maxRows); public void query(boolean currentRows, int currentDays, int maxRows);
/** /**
* Toggle between grid and form view * Switch between grid/list and form view
*/ */
public void switchRowPresentation(); public void switchRowPresentation();
/** /**
* Dynamic update of field properties ( visibility, filter and mandatory ) * Dynamic update of every field's UI properties ( visibility, filter and mandatory ).
* @param i * @param col optional column name
*/ */
public void dynamicDisplay(int i); public void dynamicDisplay(int col);
/** /**
* After save event * Handle after save event
* @param onSaveEvent * @param onSaveEvent
*/ */
public void afterSave(boolean onSaveEvent); public void afterSave(boolean onSaveEvent);
/** /**
* Enter key event * Handle enter key event
* @return true if the event is process * @return true if the event is process
*/ */
public boolean onEnterKey(); public boolean onEnterKey();
/** /**
* @return boolean * @return true if current presentation of the tab panel is grid/list view
*/ */
public boolean isGridView(); public boolean isGridView();
/** /**
* @return true if the panel have been activated * @return true if the tab panel have been activated
*/ */
public boolean isActivated(); public boolean isActivated();
/** /**
* * Turn on/off detail mode, i.e either tab panel is currently a header or detail tab of the UI.
* @param detailMode * @param detailMode
*/ */
public void setDetailPaneMode(boolean detailMode); public void setDetailPaneMode(boolean detailMode);
/** /**
* * @return true if the panel is in detail mode (i.e a detail tab in DetailPane)
* @return true if the panel is in detailpane node
*/ */
public boolean isDetailPaneMode(); public boolean isDetailPaneMode();
/** /**
* * @return {@link GridView} instance
* @return gridview instance
*/ */
public GridView getGridView(); public GridView getGridView();
/** /**
* * Call {@link GridTab#needSave(boolean, boolean)}
* @param rowChange * @param rowChange
* @param onlyRealChange * @param onlyRealChange
* @return true if there are pending changes * @return true if there are pending changes
@ -162,70 +162,71 @@ public interface IADTabpanel extends Component, Evaluatee {
public boolean needSave(boolean rowChange, boolean onlyRealChange); public boolean needSave(boolean rowChange, boolean onlyRealChange);
/** /**
* Save changes.
* Call {@link GridTab#dataSave(boolean)}
* @param onSaveEvent * @param onSaveEvent
* @return true if the save operation completed successfully * @return true if the save operation completed successfully
*/ */
public boolean dataSave(boolean onSaveEvent); public boolean dataSave(boolean onSaveEvent);
/** /**
* * Set tab number/sequence within an AD_Window
* @param tabNo * @param tabNo
*/ */
public void setTabNo(int tabNo); public void setTabNo(int tabNo);
/** /**
*
* @return tab no ( ad_tab.tabno ) * @return tab no ( ad_tab.tabno )
*/ */
public int getTabNo(); public int getTabNo();
/** /**
* * Set the {@link DetailPane} part that own this IADTabpanel instance
* @param detailPane * @param detailPane
*/ */
public void setDetailPane(DetailPane detailPane); public void setDetailPane(DetailPane detailPane);
/** /**
* * @return the {@link DetailPane} part that own this IADTabpanel instance
* @return detailpane
*/ */
public DetailPane getDetailPane(); public DetailPane getDetailPane();
/** /**
* reset detail data grid when parent tab current record is new and not saved yet * Reset detail data grid when parent tab current record is new and not saved yet.
* Call {@link GridTab#resetDetailForNewParentRecord()}
*/ */
public void resetDetailForNewParentRecord(); public void resetDetailForNewParentRecord();
/** /**
* @return treepanel instance * @return {@link ADTreePanel} instance
*/ */
public ADTreePanel getTreePanel(); public ADTreePanel getTreePanel();
/** /**
* @return Quick Form Button Enabled/Disabled * @return true if Quick Form Button is Enabled
*/ */
public boolean isEnableQuickFormButton(); public boolean isEnableQuickFormButton();
/** /**
* Get is detail pane visible * Get is detail pane visible
* @return boolean * @return true if the containing {@link DetailPane} instance is visible
*/ */
public default boolean isDetailVisible() { public default boolean isDetailVisible() {
return false; return false;
} }
/** /**
* @return List of toolbar buttons * @return List of toolbar buttons
*/ */
public List<Button> getToolbarButtons(); public List<Button> getToolbarButtons();
/** /**
* @return customization enabled/disabled for tab * @return true if customize grid button is enabled
*/ */
public boolean isEnableCustomizeButton(); public boolean isEnableCustomizeButton();
/** /**
* @return process Button Enabled/Disabled * @return true if process Button is Enabled
*/ */
default public boolean isEnableProcessButton() { default public boolean isEnableProcessButton() {
boolean isNewRow = getGridTab().getRowCount() == 0 || getGridTab().isNew(); boolean isNewRow = getGridTab().getRowCount() == 0 || getGridTab().isNew();
@ -233,14 +234,14 @@ public interface IADTabpanel extends Component, Evaluatee {
} }
/** /**
* Enabled/Disabled tab toolbar button * Enabled/Disabled ADWindowToolbar buttons
* *
* @param toolbar - {@link ADWindowToolbar} * @param toolbar - {@link ADWindowToolbar}
*/ */
public void updateToolbar(ADWindowToolbar toolbar); public void updateToolbar(ADWindowToolbar toolbar);
/** /**
* Enabled/Disabled detail panel toolbar button * Enabled/Disabled {@link DetailPane} toolbar buttons
* *
* @param toolbar - {@link Toolbar} * @param toolbar - {@link Toolbar}
*/ */

View File

@ -19,31 +19,30 @@ import org.adempiere.util.Callback;
import org.adempiere.webui.editor.WEditor; import org.adempiere.webui.editor.WEditor;
/** /**
* * Interface for container that host one or more field editors
* @author hengsin * @author hengsin
* *
*/ */
public interface IFieldEditorContainer { public interface IFieldEditorContainer {
/** /**
* focus to first field editor * set focus to first field editor
*/ */
public void focusToFirstEditor(); public void focusToFirstEditor();
/** /**
* focus to next field editor from ref * set focus to next field editor from ref
* @param ref * @param ref
*/ */
public void focusToNextEditor(WEditor ref); public void focusToNextEditor(WEditor ref);
/** /**
* helper method to loop thru editor collection of panel <br/> * Call editorTaverseCallback for all editors hosted by this container
* can use on callout to check relative editor
* @param editorTaverseCallback * @param editorTaverseCallback
*/ */
public void editorTraverse (Callback<WEditor> editorTaverseCallback); public void editorTraverse (Callback<WEditor> editorTaverseCallback);
/** /**
* default implement for {@link #editorTraverse(Callback)} * Default implementation for {@link #editorTraverse(Callback)}
* @param editorTaverseCallback * @param editorTaverseCallback
* @param editors * @param editors
*/ */

View File

@ -1,6 +1,27 @@
/** /***********************************************************************
* * This file is part of iDempiere ERP Open Source *
*/ * http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* 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 2 *
* 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, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.adwindow; package org.adempiere.webui.adwindow;
import java.util.List; import java.util.List;
@ -25,13 +46,16 @@ import org.zkoss.zul.Menuseparator;
/** /**
* @author hengsin * @author hengsin
*
*/ */
public class ProcessButtonPopup extends Menupopup implements EventListener<Event> { public class ProcessButtonPopup extends Menupopup implements EventListener<Event> {
private static final String DOCUMENT_ACTION_MENUITEM_ATTRIBUTE = "document-action-menuitem"; /** sclass for document action menu items **/
private static final String DOCUMENT_ACTION_MENUITEM_SCLASS = "document-action-menuitem";
/** Menupopup attribute to store reference to WDocActionPanel **/
private static final String DOC_ACTION_PANEL_ATTRIBUTE = "doc-action-panel"; private static final String DOC_ACTION_PANEL_ATTRIBUTE = "doc-action-panel";
/** Menupopup/Menuitem attribute to store reference to Button **/
private static final String BUTTON_ATTRIBUTE = "button"; private static final String BUTTON_ATTRIBUTE = "button";
/** Button yes/no attribute (Y/N) to store whether button is pressed **/
public static final String BUTTON_ATTRIBUTE_PRESSED = "buttonPressed"; public static final String BUTTON_ATTRIBUTE_PRESSED = "buttonPressed";
/** /**
@ -39,6 +63,11 @@ public class ProcessButtonPopup extends Menupopup implements EventListener<Event
*/ */
private static final long serialVersionUID = 304878472233552113L; private static final long serialVersionUID = 304878472233552113L;
/**
* Render buttons as menu items.
* Special treatment for DocAction - render each available document action as a sub menu item.
* @param buttons
*/
public void render(List<Button> buttons) { public void render(List<Button> buttons) {
this.setSclass("z-menu-noimage"); this.setSclass("z-menu-noimage");
@ -88,7 +117,7 @@ public class ProcessButtonPopup extends Menupopup implements EventListener<Event
for(Listitem action : actions) { for(Listitem action : actions) {
Menuitem mi = new Menuitem(action.getLabel()); Menuitem mi = new Menuitem(action.getLabel());
mi.setValue((String)action.getValue()); mi.setValue((String)action.getValue());
mi.setSclass(DOCUMENT_ACTION_MENUITEM_ATTRIBUTE); mi.setSclass(DOCUMENT_ACTION_MENUITEM_SCLASS);
mi.addEventListener(Events.ON_CLICK, this); mi.addEventListener(Events.ON_CLICK, this);
popup.appendChild(mi); popup.appendChild(mi);
} }
@ -99,7 +128,7 @@ public class ProcessButtonPopup extends Menupopup implements EventListener<Event
@Override @Override
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
Menuitem mi = (Menuitem) event.getTarget(); Menuitem mi = (Menuitem) event.getTarget();
if (DOCUMENT_ACTION_MENUITEM_ATTRIBUTE.equals(mi.getSclass())) { if (DOCUMENT_ACTION_MENUITEM_SCLASS.equals(mi.getSclass())) {
final Button button = (Button) mi.getParent().getAttribute(BUTTON_ATTRIBUTE); final Button button = (Button) mi.getParent().getAttribute(BUTTON_ATTRIBUTE);
WDocActionPanel panel = (WDocActionPanel) mi.getParent().getAttribute(DOC_ACTION_PANEL_ATTRIBUTE); WDocActionPanel panel = (WDocActionPanel) mi.getParent().getAttribute(DOC_ACTION_PANEL_ATTRIBUTE);
panel.setSelectedItem(mi.getValue()); panel.setSelectedItem(mi.getValue());

View File

@ -71,7 +71,7 @@ import org.zkoss.zul.RowRendererExt;
import org.zkoss.zul.Timebox; import org.zkoss.zul.Timebox;
/** /**
* Row renderer for Quick GridTab grid. * Row renderer for Quick GridTab grid (Base on {@link GridTabRowRenderer})
* *
* @author Logilite Technologies * @author Logilite Technologies
* @since Nov 03, 2017 * @since Nov 03, 2017
@ -79,7 +79,9 @@ import org.zkoss.zul.Timebox;
public class QuickGridTabRowRenderer public class QuickGridTabRowRenderer
implements RowRenderer<Object[]>, RowRendererExt, RendererCtrl, EventListener<Event> { implements RowRenderer<Object[]>, RowRendererExt, RendererCtrl, EventListener<Event> {
/** Component boolean attribute to indicate this component is own by QuickGridView **/
public static final String IS_QUICK_FORM_COMPONENT = "IS_QUICK_FORM_COMPONENT"; public static final String IS_QUICK_FORM_COMPONENT = "IS_QUICK_FORM_COMPONENT";
/** Editor component attribute to store row index (absolute) **/
public static final String GRID_ROW_INDEX_ATTR = "grid.row.index"; public static final String GRID_ROW_INDEX_ATTR = "grid.row.index";
private static final String CELL_DIV_STYLE = "height: 100%; cursor: pointer; "; private static final String CELL_DIV_STYLE = "height: 100%; cursor: pointer; ";
private static final String CELL_DIV_STYLE_ALIGN_CENTER = CELL_DIV_STYLE + "text-align:center; "; private static final String CELL_DIV_STYLE_ALIGN_CENTER = CELL_DIV_STYLE + "text-align:center; ";
@ -90,18 +92,27 @@ public class QuickGridTabRowRenderer
private GridTab gridTab; private GridTab gridTab;
private int windowNo; private int windowNo;
/** Sync field editor changes to GridField **/
private GridTabDataBinder dataBinder; private GridTabDataBinder dataBinder;
private Paging paging; private Paging paging;
/** internal listener for row event **/
private RowListener rowListener; private RowListener rowListener;
/** Grid that own this renderer **/
private Grid grid = null; private Grid grid = null;
/** QuickGridView that uses this renderer **/
private QuickGridView gridPanel = null; private QuickGridView gridPanel = null;
/** current focus row **/
private Row currentRow; private Row currentRow;
/** values of current row. updated in {@link #render(Row, Object[], int)}. **/
private Object[] currentValues; private Object[] currentValues;
/** true if currrent row is in edit mode **/
private boolean editing = false; private boolean editing = false;
public int currentRowIndex = -1; public int currentRowIndex = -1;
/** AD window content part that own this renderer **/
private AbstractADWindowContent m_windowPanel; private AbstractADWindowContent m_windowPanel;
/** internal listener for button ActionEvent **/
private ActionListener buttonListener; private ActionListener buttonListener;
// Row-wise Editors Map // Row-wise Editors Map
public Map<Row, ArrayList<WEditor>> editorsListMap = new LinkedHashMap<Row, ArrayList<WEditor>>(); public Map<Row, ArrayList<WEditor>> editorsListMap = new LinkedHashMap<Row, ArrayList<WEditor>>();
@ -128,6 +139,12 @@ public class QuickGridTabRowRenderer
this.dataBinder = new GridTabDataBinder(gridTab); this.dataBinder = new GridTabDataBinder(gridTab);
} }
/**
* Get editor for GridField and set value to object parameter.
* @param gridField
* @param object
* @return {@link WEditor}
*/
private WEditor getEditorCell(GridField gridField, Object object) { private WEditor getEditorCell(GridField gridField, Object object) {
WEditor editor = WebEditorFactory.getEditor(gridField, true); WEditor editor = WebEditorFactory.getEditor(gridField, true);
if (editor != null) { if (editor != null) {
@ -137,6 +154,11 @@ public class QuickGridTabRowRenderer
return editor; return editor;
} }
/**
* Setup field editor
* @param gridField
* @param editor
*/
private void prepareFieldEditor(GridField gridField, WEditor editor) { private void prepareFieldEditor(GridField gridField, WEditor editor) {
if (editor instanceof WButtonEditor) if (editor instanceof WButtonEditor)
{ {
@ -161,6 +183,10 @@ public class QuickGridTabRowRenderer
} }
} }
/**
* @param field
* @return column index for field, -1 if not found
*/
public int getColumnIndex(GridField field) { public int getColumnIndex(GridField field) {
GridField[] fields = gridPanel.getFields(); GridField[] fields = gridPanel.getFields();
for(int i = 0; i < fields.length; i++) { for(int i = 0; i < fields.length; i++) {
@ -178,6 +204,7 @@ public class QuickGridTabRowRenderer
} }
/** /**
* Render data for row.
* @param row * @param row
* @param data * @param data
* @param index * @param index
@ -409,7 +436,7 @@ public class QuickGridTabRowRenderer
* *
* @param component * @param component
* @param isDisable * @param isDisable
* @return * @return true if component is read only
*/ */
public boolean isDisableReadonlyComponent(Component component, boolean isDisable) public boolean isDisableReadonlyComponent(Component component, boolean isDisable)
{ {
@ -548,7 +575,7 @@ public class QuickGridTabRowRenderer
* *
* @param zclass * @param zclass
* @param isDisable * @param isDisable
* @return * @return modify zclass
*/ */
private String addOrRemoveCssClass(String zclass, boolean isDisable) private String addOrRemoveCssClass(String zclass, boolean isDisable)
{ {
@ -568,14 +595,27 @@ public class QuickGridTabRowRenderer
private Cell currentCell = null; private Cell currentCell = null;
/**
* @return current {@link Cell}
*/
public Cell getCurrentCell() { public Cell getCurrentCell() {
return currentCell; return currentCell;
} }
/**
* Set current cell
* @param currentCell
*/
public void setCurrentCell(Cell currentCell) { public void setCurrentCell(Cell currentCell) {
this.currentCell = currentCell; this.currentCell = currentCell;
} }
/**
* Set current cell
* @param row
* @param col
* @param code cell navigation code (right, left, down, up, next)
*/
public void setCurrentCell(int row, int col, int code) { public void setCurrentCell(int row, int col, int code) {
if (col < 0 || row < 0) if (col < 0 || row < 0)
return; return;
@ -660,7 +700,7 @@ public class QuickGridTabRowRenderer
} }
/** /**
* Set Property change listener of editor field * Add property change listener (WEditor) to GridField
* *
* @param editorsList * @param editorsList
*/ */
@ -676,7 +716,7 @@ public class QuickGridTabRowRenderer
} }
/** /**
* Remove Property change listener of editor field * Remove property change listener (WEditor) from GridField
* *
* @param editorsList * @param editorsList
*/ */
@ -691,7 +731,7 @@ public class QuickGridTabRowRenderer
} }
/** /**
* If true add Property Change Listener, a false Remove Property Change Listener * If isAddListener is true add Property Change Listener, otherwise Remove Property Change Listener
* *
* @param isAddListener * @param isAddListener
* @param col * @param col
@ -714,8 +754,8 @@ public class QuickGridTabRowRenderer
} // addRemovePropertyChangeListener } // addRemovePropertyChangeListener
/** /**
* @param code * @param code cell navigation code
* @return * @return true to add property change listener, false otherwise
*/ */
public Boolean isAddRemoveListener(int code) public Boolean isAddRemoveListener(int code)
{ {
@ -726,7 +766,8 @@ public class QuickGridTabRowRenderer
} // isAddRemoveListener } // isAddRemoveListener
/** /**
* @param row * Set current row
* @param row absolute row index
*/ */
public void setRowTo(int row) public void setRowTo(int row)
{ {
@ -736,6 +777,11 @@ public class QuickGridTabRowRenderer
setCurrentRow(currentRow); setCurrentRow(currentRow);
} }
/**
* @param row
* @param col
* @return true if cell is editable, false otherwise
*/
private boolean isEditable(int row, int col) private boolean isEditable(int row, int col)
{ {
Cell cell = null; Cell cell = null;
@ -790,6 +836,9 @@ public class QuickGridTabRowRenderer
return false; return false;
} }
/**
* Set focus to {@link #currentCell}
*/
public void setFocusOnCurrentCell() { public void setFocusOnCurrentCell() {
if (currentCell == null || currentCell.getChildren().size() <= 0) { if (currentCell == null || currentCell.getChildren().size() <= 0) {
return; return;
@ -845,6 +894,7 @@ public class QuickGridTabRowRenderer
} // setFocusOnCurrentCell } // setFocusOnCurrentCell
/** /**
* Set current focus row
* @param row * @param row
*/ */
public void setCurrentRow(Row row) public void setCurrentRow(Row row)
@ -876,7 +926,7 @@ public class QuickGridTabRowRenderer
} }
/** /**
* Enter edit mode * Enter edit mode for current focus row.
*/ */
public void editCurrentRow() { public void editCurrentRow() {
if (currentRow != null && currentRow.getParent() != null && currentRow.isVisible() && grid != null if (currentRow != null && currentRow.getParent() != null && currentRow.isVisible() && grid != null
@ -929,13 +979,16 @@ public class QuickGridTabRowRenderer
} }
/** /**
* * Set {@link QuickGridView} that own this renderer.
* @param gridPanel * @param gridPanel
*/ */
public void setGridPanel(QuickGridView gridPanel) { public void setGridPanel(QuickGridView gridPanel) {
this.gridPanel = gridPanel; this.gridPanel = gridPanel;
} }
/**
* Internal listener for row event (ON_CLICK, ON_DOUBLE_CLICK and ON_OK).
*/
static class RowListener implements EventListener<Event> { static class RowListener implements EventListener<Event> {
private Grid _grid; private Grid _grid;
@ -964,13 +1017,15 @@ public class QuickGridTabRowRenderer
} }
/** /**
* @return boolean * @return true if current row is in edit mode, false otherwise
*/ */
public boolean isEditing() { public boolean isEditing() {
return editing; return editing;
} }
/** /**
* Set AD window content part that own this renderer.
* {@link #buttonListener} need this to call {@link AbstractADWindowContent#actionPerformed(ActionEvent)}.
* @param windowPanel * @param windowPanel
*/ */
public void setADWindowPanel(AbstractADWindowContent windowPanel) { public void setADWindowPanel(AbstractADWindowContent windowPanel) {

View File

@ -73,7 +73,7 @@ import org.zkoss.zul.event.ZulEvents;
import org.zkoss.zul.impl.CustomGridDataLoader; import org.zkoss.zul.impl.CustomGridDataLoader;
/** /**
* Quick Grid view implemented using the Grid component. * Quick Grid view implemented using the Grid component (Base on {@link GridView}).
* *
* @author Logilite Technologies * @author Logilite Technologies
* @since Nov 03, 2017 * @since Nov 03, 2017
@ -81,30 +81,39 @@ import org.zkoss.zul.impl.CustomGridDataLoader;
public class QuickGridView extends Vbox public class QuickGridView extends Vbox
implements EventListener<Event>, IdSpace, IFieldEditorContainer, StateChangeListener { implements EventListener<Event>, IdSpace, IFieldEditorContainer, StateChangeListener {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 228387400133234920L; private static final long serialVersionUID = 228387400133234920L;
static CLogger log = CLogger.getCLogger(QuickGridView.class); static CLogger log = CLogger.getCLogger(QuickGridView.class);
/** Style for Grid and Grid Footer **/
private static final String HEADER_GRID_STYLE = "border: none; margin:0; padding: 0;"; private static final String HEADER_GRID_STYLE = "border: none; margin:0; padding: 0;";
/** default paging size **/
private static final int DEFAULT_PAGE_SIZE = 20; private static final int DEFAULT_PAGE_SIZE = 20;
/** default paging size for mobile client **/
private static final int DEFAULT_MOBILE_PAGE_SIZE = 20; private static final int DEFAULT_MOBILE_PAGE_SIZE = 20;
/** minimum column width **/
private static final int MIN_COLUMN_WIDTH = 100; private static final int MIN_COLUMN_WIDTH = 100;
/** maximum column width **/
private static final int MAX_COLUMN_WIDTH = 300; private static final int MAX_COLUMN_WIDTH = 300;
/** minimum column width for combobox field **/
private static final int MIN_COMBOBOX_WIDTH = 160; private static final int MIN_COMBOBOX_WIDTH = 160;
/** minimum column width for numeric field **/
private static final int MIN_NUMERIC_COL_WIDTH = 120; private static final int MIN_NUMERIC_COL_WIDTH = 120;
public static final int SALES_ORDER_LINE_TAB_ID = 187; public static final int SALES_ORDER_LINE_TAB_ID = 187;
/** GridView boolean attribute to indicate ON_POST_SELECTED_ROW_CHANGED_EVENT have been posted in current execution cycle **/
private static final String ATTR_ON_POST_SELECTED_ROW_CHANGED = "org.adempiere.webui.adwindow.GridView.onPostSelectedRowChanged"; private static final String ATTR_ON_POST_SELECTED_ROW_CHANGED = "org.adempiere.webui.adwindow.GridView.onPostSelectedRowChanged";
/** shortcut key for cell navigation **/
public static final String CNTRL_KEYS = "#left#right#up#down#home@k@r"; public static final String CNTRL_KEYS = "#left#right#up#down#home@k@r";
// 'Enter' Work as Down key // 'Enter' Work as Down key
@ -123,47 +132,63 @@ public class QuickGridView extends Vbox
public static final int NAVIGATE_CODE = 1; public static final int NAVIGATE_CODE = 1;
public static final int FOCUS_CODE = 0; public static final int FOCUS_CODE = 0;
/** data grid instance **/
private Grid listbox = null; private Grid listbox = null;
private int pageSize = DEFAULT_PAGE_SIZE; private int pageSize = DEFAULT_PAGE_SIZE;
/** /**
* list field display in grid mode, in case user customize grid * list field display in grid mode, in case user customize grid
* this list container only customize list. * this list container only display list.
*/ */
private GridField[] gridFields; private GridField[] gridFields;
/** GridTable model for GridTab **/
private AbstractTableModel tableModel; private AbstractTableModel tableModel;
private int numColumns = 5; private int numColumns = 5;
private int windowNo; private int windowNo;
/** GridTab that back this QuickGridView **/
private GridTab gridTab; private GridTab gridTab;
/** true if this QuickGridView instance have been init with GridTab **/
private boolean init; private boolean init;
/** Zk List model for {@link #tableModel} **/
public GridTableListModel listModel; public GridTableListModel listModel;
public Paging paging; public Paging paging;
/** Row renderer for this QuickGridView instance **/
private QuickGridTabRowRenderer renderer; private QuickGridTabRowRenderer renderer;
/** Footer for paging **/
private Div gridFooter; private Div gridFooter;
/** true if current row is always in edit mode **/
private boolean modeless = true; private boolean modeless = true;
/** AD window content part that own this GridView instance **/
private AbstractADWindowContent windowPanel; private AbstractADWindowContent windowPanel;
/** true when grid is refreshing its data **/
private boolean refreshing; private boolean refreshing;
/** AD_Field_ID:Column Width **/
private Map<Integer, String> columnWidthMap; private Map<Integer, String> columnWidthMap;
/** checkbox to select all row of current page **/
protected Checkbox selectAll; protected Checkbox selectAll;
boolean isHasCustomizeData = false; /** true if there are AD_Tab_Customization for GridTab **/
protected boolean isHasCustomizeData = false;
Keylistener keyListener; /** reference to desktop key listener **/
protected Keylistener keyListener;
/** true if no new row or new row have been saved **/
public boolean isNewLineSaved = true; public boolean isNewLineSaved = true;
// Prevent focus change until render is complete // Prevent focus change until render is complete
@ -171,16 +196,28 @@ public class QuickGridView extends Vbox
// To prevent 'onFocus' event fire twice on same component. // To prevent 'onFocus' event fire twice on same component.
private Component preEventComponent; private Component preEventComponent;
public IQuickForm quickForm; /** IQuickForm that own this QuickGridView instance **/
public IQuickForm quickForm;
/**
* list field display in grid mode, in case user customize grid
* this list container only customize list.
* @return GridField[]
*/
public GridField[] getGridField() { public GridField[] getGridField() {
return gridFields; return gridFields;
} }
/**
* @param gridField
*/
public void setGridField(GridField[] gridField) { public void setGridField(GridField[] gridField) {
this.gridFields = gridField; this.gridFields = gridField;
} }
/**
* @return {@link QuickGridTabRowRenderer}
*/
public QuickGridTabRowRenderer getRenderer() { public QuickGridTabRowRenderer getRenderer() {
return renderer; return renderer;
} }
@ -239,6 +276,11 @@ public class QuickGridView extends Vbox
addEventListener(EVENT_ONFOCUS_AFTER_SAVE, this); addEventListener(EVENT_ONFOCUS_AFTER_SAVE, this);
} }
/**
* @param abstractADWindowContent
* @param gridTab
* @param wQuickForm
*/
public QuickGridView(AbstractADWindowContent abstractADWindowContent, GridTab gridTab, IQuickForm wQuickForm) public QuickGridView(AbstractADWindowContent abstractADWindowContent, GridTab gridTab, IQuickForm wQuickForm)
{ {
this(abstractADWindowContent.getWindowNo()); this(abstractADWindowContent.getWindowNo());
@ -247,6 +289,9 @@ public class QuickGridView extends Vbox
init(gridTab); init(gridTab);
} }
/**
* create data grid instances
*/
protected void createListbox() protected void createListbox()
{ {
listbox = new Grid(); listbox = new Grid();
@ -258,7 +303,7 @@ public class QuickGridView extends Vbox
} }
/** /**
* * Init data grid
* @param gridTab * @param gridTab
*/ */
public void init(GridTab gridTab) public void init(GridTab gridTab)
@ -278,6 +323,10 @@ public class QuickGridView extends Vbox
this.init = true; this.init = true;
} }
/**
* Setup {@link #gridFields} from gridTab.
* @param gridTab
*/
private void setupFields(GridTab gridTab) { private void setupFields(GridTab gridTab) {
this.gridTab = gridTab; this.gridTab = gridTab;
gridTab.addStateChangeListener(this); gridTab.addStateChangeListener(this);
@ -370,14 +419,14 @@ public class QuickGridView extends Vbox
/** /**
* *
* @return boolean * @return true if data grid have been init with GridTab
*/ */
public boolean isInit() { public boolean isInit() {
return init; return init;
} }
/** /**
* refresh after switching from form view * Refresh data grid (int of quick form or column setup has change)
* @param gridTab * @param gridTab
*/ */
public void refresh(GridTab gridTab) { public void refresh(GridTab gridTab) {
@ -397,12 +446,15 @@ public class QuickGridView extends Vbox
} }
} }
/**
* @return true if data grid is refreshing data from GridTab
*/
public boolean isRefreshing() { public boolean isRefreshing() {
return refreshing; return refreshing;
} }
/** /**
* Update current row from model * Update current row index from model
*/ */
public void updateListIndex() { public void updateListIndex() {
if (gridTab == null || !gridTab.isOpen()) return; if (gridTab == null || !gridTab.isOpen()) return;
@ -451,18 +503,24 @@ public class QuickGridView extends Vbox
} }
} }
/**
* hide paging component
*/
private void hidePagingControl() { private void hidePagingControl() {
if (gridFooter.isVisible()) if (gridFooter.isVisible())
gridFooter.setVisible(false); gridFooter.setVisible(false);
} }
/**
* show paging component
*/
private void showPagingControl() { private void showPagingControl() {
if (!gridFooter.isVisible()) if (!gridFooter.isVisible())
gridFooter.setVisible(true); gridFooter.setVisible(true);
} }
/** /**
* * echo ON_POST_SELECTED_ROW_CHANGED_EVENT after current row index has changed
*/ */
protected void echoOnPostSelectedRowChanged() { protected void echoOnPostSelectedRowChanged() {
if (getAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED) == null) { if (getAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED) == null) {
@ -480,11 +538,17 @@ public class QuickGridView extends Vbox
this.pageSize = pageSize; this.pageSize = pageSize;
} }
/**
* remove all components
*/
public void clear() public void clear()
{ {
this.getChildren().clear(); this.getChildren().clear();
} }
/**
* Setup {@link Columns} of data grid
*/
private void setupColumns() private void setupColumns()
{ {
if (init) return; if (init) return;
@ -608,6 +672,9 @@ public class QuickGridView extends Vbox
} }
} }
/**
* Render data grid
*/
private void render() private void render()
{ {
updateEmptyMessage(); updateEmptyMessage();
@ -635,6 +702,9 @@ public class QuickGridView extends Vbox
} }
} }
/**
* Show zero records for processing message
*/
private void updateEmptyMessage() { private void updateEmptyMessage() {
if (gridTab.getRowCount() == 0) if (gridTab.getRowCount() == 0)
{ {
@ -646,6 +716,10 @@ public class QuickGridView extends Vbox
} }
} }
/**
* Update {@link #listModel} with {@link #tableModel} changes.
* Re-create {@link #renderer}.
*/
private void updateModel() { private void updateModel() {
if (listModel != null) if (listModel != null)
((GridTable)tableModel).removeTableModelListener(listModel); ((GridTable)tableModel).removeTableModelListener(listModel);
@ -1128,6 +1202,10 @@ public class QuickGridView extends Vbox
event.stopPropagation(); event.stopPropagation();
} }
/**
* Set focus to cell (source)
* @param source
*/
private void setFocusOnDiv(Component source) { private void setFocusOnDiv(Component source) {
int rowCount = gridTab.getTableModel().getRowCount(); int rowCount = gridTab.getTableModel().getRowCount();
int colCount = renderer.getCurrentRow().getChildren().size(); int colCount = renderer.getCurrentRow().getChildren().size();
@ -1141,6 +1219,9 @@ public class QuickGridView extends Vbox
} }
} }
/**
* @return true if all row of current page is selected
*/
public boolean isAllSelected() { public boolean isAllSelected() {
org.zkoss.zul.Rows rows = listbox.getRows(); org.zkoss.zul.Rows rows = listbox.getRows();
List<Component> childs = rows.getChildren(); List<Component> childs = rows.getChildren();
@ -1162,6 +1243,10 @@ public class QuickGridView extends Vbox
return all; return all;
} }
/**
* turn on/off select all rows for current page
* @param b
*/
public void toggleSelectionForAll(boolean b) { public void toggleSelectionForAll(boolean b) {
org.zkoss.zul.Rows rows = listbox.getRows(); org.zkoss.zul.Rows rows = listbox.getRows();
List<Component> childs = rows.getChildren(); List<Component> childs = rows.getChildren();
@ -1188,6 +1273,10 @@ public class QuickGridView extends Vbox
} }
} }
/**
* Update list model and data grid with new current row index
* @param index
*/
private void onSelectedRowChange(int index) { private void onSelectedRowChange(int index) {
if (updateModelIndex(index)) { if (updateModelIndex(index)) {
updateListIndex(); updateListIndex();
@ -1195,7 +1284,12 @@ public class QuickGridView extends Vbox
} }
/** /**
* Event after the current selected row change * Save changes.
* Call {@link #dataSave(int)}
* @param code cell navigation code
* @param row
* @param col
* @return true if save succesfully
*/ */
private boolean save(int code, int row, int col) private boolean save(int code, int row, int col)
{ {
@ -1211,6 +1305,9 @@ public class QuickGridView extends Vbox
return isSave; return isSave;
} }
/**
* Event after the current row index has changed.
*/
public void onPostSelectedRowChanged() { public void onPostSelectedRowChanged() {
removeAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED); removeAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED);
if (listbox.getRows() == null || listbox.getRows().getChildren().isEmpty()) if (listbox.getRows() == null || listbox.getRows().getChildren().isEmpty())
@ -1244,6 +1341,11 @@ public class QuickGridView extends Vbox
onPostSelectedRowChanged(); onPostSelectedRowChanged();
} }
/**
* @param row
* @param index
* @return true if row have been rendered by row renderer
*/
private boolean isRowRendered(org.zkoss.zul.Row row, int index) { private boolean isRowRendered(org.zkoss.zul.Row row, int index) {
if (row.getChildren().size() == 0) { if (row.getChildren().size() == 0) {
return false; return false;
@ -1255,6 +1357,11 @@ public class QuickGridView extends Vbox
return true; return true;
} }
/**
* Update gridTab current row index.
* @param rowIndex row index of current page
* @return true if gridTab current row index has change
*/
public boolean updateModelIndex(int rowIndex) { public boolean updateModelIndex(int rowIndex) {
if (pageSize > 0) { if (pageSize > 0) {
int start = listModel.getPage() * listModel.getPageSize(); int start = listModel.getPage() * listModel.getPageSize();
@ -1309,9 +1416,8 @@ public class QuickGridView extends Vbox
} }
/** /**
* Change display properties of current row * Perform dynamic display for editors in list
* * @param noData true if data grid is empty
* @param noData
* @param list * @param list
*/ */
private void dynamicDisplayEditors(boolean noData, List<WEditor> list) { private void dynamicDisplayEditors(boolean noData, List<WEditor> list) {
@ -1367,6 +1473,7 @@ public class QuickGridView extends Vbox
} }
/** /**
* Set AD window content part that own this QuickGridView instance
* @param winPanel * @param winPanel
*/ */
public void setADWindowPanel(AbstractADWindowContent winPanel) { public void setADWindowPanel(AbstractADWindowContent winPanel) {
@ -1375,6 +1482,9 @@ public class QuickGridView extends Vbox
renderer.setADWindowPanel(windowPanel); renderer.setADWindowPanel(windowPanel);
} }
/**
* Re-Init QuickGridView with cache gridTab.
*/
public void reInit() { public void reInit() {
listbox.getChildren().clear(); listbox.getChildren().clear();
listbox.detach(); listbox.detach();
@ -1400,6 +1510,7 @@ public class QuickGridView extends Vbox
/** /**
* list field display in grid mode, in case user customize grid * list field display in grid mode, in case user customize grid
* this list container only customize list. * this list container only customize list.
* @return GridField[]
*/ */
public GridField[] getFields() { public GridField[] getFields() {
return gridFields; return gridFields;
@ -1430,10 +1541,16 @@ public class QuickGridView extends Vbox
} }
} }
/**
* not used. candidate for removal.
*/
protected void onADTabPanelParentChanged() { protected void onADTabPanelParentChanged() {
positionPagingControl(); positionPagingControl();
} }
/**
* not used. candidate for removal.
*/
private void positionPagingControl() private void positionPagingControl()
{ {
if (gridFooter.getParent() != this) if (gridFooter.getParent() != this)
@ -1446,18 +1563,27 @@ public class QuickGridView extends Vbox
paging.setDetailed(true); paging.setDetailed(true);
} }
/**
* set status text
* @param text
* @param error
*/
public void setStatusLine(String text, boolean error) public void setStatusLine(String text, boolean error)
{ {
windowPanel.getStatusBarQF().setStatusLine(text, error); windowPanel.getStatusBarQF().setStatusLine(text, error);
} }
/**
* add new row to data grid
*/
public void createNewLine() { public void createNewLine() {
isNewLineSaved = false; isNewLineSaved = false;
gridTab.dataNew(false); gridTab.dataNew(false);
} }
/** /**
* @param code * Save changes
* @param code cell navigation code
*/ */
public boolean dataSave(int code) { public boolean dataSave(int code) {
boolean isSave = false; boolean isSave = false;
@ -1501,6 +1627,10 @@ public class QuickGridView extends Vbox
} }
/**
* @param source
* @return row that own source cell
*/
private int getFocusedRowIndex(Component source) private int getFocusedRowIndex(Component source)
{ {
int rowCount = gridTab.getTableModel().getRowCount(); int rowCount = gridTab.getTableModel().getRowCount();
@ -1516,6 +1646,9 @@ public class QuickGridView extends Vbox
return 0; return 0;
} // getFocusedRowIndex } // getFocusedRowIndex
/**
* @return sort column (if any)
*/
public Column findCurrentSortColumn() public Column findCurrentSortColumn()
{ {
if (listbox.getColumns() != null) if (listbox.getColumns() != null)

View File

@ -45,6 +45,8 @@ import org.zkoss.zul.Separator;
import org.zkoss.zul.Space; import org.zkoss.zul.Space;
/** /**
* Status bar component of AD Window.
*
* This class is based on org.compiere.apps.StatusBar written by Jorg Janke. * This class is based on org.compiere.apps.StatusBar written by Jorg Janke.
* @author Jorg Janke * @author Jorg Janke
* *
@ -55,32 +57,45 @@ import org.zkoss.zul.Space;
public class StatusBar extends Panel implements EventListener<Event> public class StatusBar extends Panel implements EventListener<Event>
{ {
/** /**
* * generated serial id
*/ */
private static final long serialVersionUID = 7091641684809092888L; private static final long serialVersionUID = 7091641684809092888L;
/** panel for record info text **/
private Panel infoPanel; private Panel infoPanel;
/** html content of {@link #infoPanel} **/
private Html infoLine; private Html infoLine;
/** west div for status text (info or error message) **/
private Div west; private Div west;
/** east div for record info text **/
private Div east; private Div east;
private ProcessInfoLog[] pInfoLogs; private ProcessInfoLog[] pInfoLogs;
/** current status text **/
private String m_statusText; private String m_statusText;
/** indicate current status text is info or error message */
private boolean m_statusError; private boolean m_statusError;
/** message popup **/
private Window msgPopup; private Window msgPopup;
/** content div for {@link #msgPopup} **/
private Div msgPopupCnt; private Div msgPopupCnt;
/** layout for {@link #west} **/
private Hlayout messageContainer; private Hlayout messageContainer;
/** Caption for {@link #msgPopup} **/
private Caption msgPopupCaption; private Caption msgPopupCaption;
/**
* Default constructor
*/
public StatusBar() public StatusBar()
{ {
super(); super();
@ -89,6 +104,10 @@ public class StatusBar extends Panel implements EventListener<Event>
createPopup(); createPopup();
} }
/**
* Layout status bar.
* West is for message and East is for HTML record info (usually generated from AD_StatusLine).
*/
private void init() private void init()
{ {
infoPanel = new Panel(); infoPanel = new Panel();
@ -114,7 +133,7 @@ public class StatusBar extends Panel implements EventListener<Event>
} }
/** /**
* Set Info Line * Set record Info Line
* @param text text * @param text text
*/ */
public void setInfo (String text) public void setInfo (String text)
@ -128,6 +147,7 @@ public class StatusBar extends Panel implements EventListener<Event>
} // setInfo } // setInfo
/** /**
* Call {@link #setStatusLine(String, boolean, ProcessInfoLog[])}.
* @param text * @param text
*/ */
public void setStatusLine (String text) public void setStatusLine (String text)
@ -136,6 +156,7 @@ public class StatusBar extends Panel implements EventListener<Event>
} }
/** /**
* Call {@link #setStatusLine(String, boolean, ProcessInfoLog[])}.
* @param text * @param text
* @param error * @param error
*/ */
@ -145,6 +166,7 @@ public class StatusBar extends Panel implements EventListener<Event>
} }
/** /**
* Set status message (west part) text.
* @param text * @param text
* @param error * @param error
* @param m_logs * @param m_logs
@ -154,6 +176,7 @@ public class StatusBar extends Panel implements EventListener<Event>
pInfoLogs = m_logs; pInfoLogs = m_logs;
Div div = null; Div div = null;
//detect duplicate call within the current execution cycle
Execution execution = Executions.getCurrent(); Execution execution = Executions.getCurrent();
if (execution != null) { if (execution != null) {
String key = this.getClass().getName()+"."+getUuid(); String key = this.getClass().getName()+"."+getUuid();
@ -174,6 +197,7 @@ public class StatusBar extends Panel implements EventListener<Event>
if (text == null || text.trim().length() == 0 ) if (text == null || text.trim().length() == 0 )
return; return;
//show auto dismiss popup notification at top left of ancestor tab panel
String labelText = buildLabelText(m_statusText); String labelText = buildLabelText(m_statusText);
int duration = MSysConfig.getIntValue(MSysConfig.ZK_ERROR_MSG_LIFETIME_MILLISECONDS, 3500, Env.getAD_Client_ID(Env.getCtx())); int duration = MSysConfig.getIntValue(MSysConfig.ZK_ERROR_MSG_LIFETIME_MILLISECONDS, 3500, Env.getAD_Client_ID(Env.getCtx()));
if (error) { if (error) {
@ -206,6 +230,7 @@ public class StatusBar extends Panel implements EventListener<Event>
label.addEventListener(Events.ON_CLICK, this); label.addEventListener(Events.ON_CLICK, this);
} }
//add document/record link from ProcessInfoLog
if (m_logs != null) { if (m_logs != null) {
div = new Div(); div = new Div();
for (int i = 0; i < m_logs.length; i++) { for (int i = 0; i < m_logs.length; i++) {
@ -228,6 +253,11 @@ public class StatusBar extends Panel implements EventListener<Event>
} }
} }
/**
* shorten statusText if exceed predefine max length of 80
* @param statusText
* @return shorten statusText
*/
private String buildLabelText(String statusText) { private String buildLabelText(String statusText) {
if (statusText == null) if (statusText == null)
return ""; return "";
@ -240,12 +270,20 @@ public class StatusBar extends Panel implements EventListener<Event>
return statusText.substring(0, 80); return statusText.substring(0, 80);
} }
/**
* Create html content for {@link #msgPopupCnt}
*/
protected void createPopupContent() { protected void createPopupContent() {
Html t = new Html(WTextEditorDialog.sanitize(m_statusText)); Html t = new Html(WTextEditorDialog.sanitize(m_statusText));
msgPopupCnt.getChildren().clear(); msgPopupCnt.getChildren().clear();
msgPopupCnt.appendChild(t); msgPopupCnt.appendChild(t);
} }
/**
* Shorten statusText if length exceed the predefine max length of 140
* @param statusText
* @return shorten statusText
*/
private String buildNotificationText(String statusText) { private String buildNotificationText(String statusText) {
if (statusText == null) if (statusText == null)
return ""; return "";
@ -258,6 +296,11 @@ public class StatusBar extends Panel implements EventListener<Event>
return statusText.substring(0, 136) + " ..."; return statusText.substring(0, 136) + " ...";
} }
/**
* Find {@link Tabpanel} or {@link WQuickForm} that own comp
* @param comp
* @return
*/
private Component findTabpanel(Component comp) { private Component findTabpanel(Component comp) {
Component parent = comp.getParent(); Component parent = comp.getParent();
while (parent != null) { while (parent != null) {
@ -278,13 +321,15 @@ public class StatusBar extends Panel implements EventListener<Event>
} }
} }
/**
* Show message popup ({@link #msgPopup})
*/
private void showPopup() { private void showPopup() {
appendChild(msgPopup); appendChild(msgPopup);
LayoutUtils.openOverlappedWindow(messageContainer, msgPopup, "overlap_end"); LayoutUtils.openOverlappedWindow(messageContainer, msgPopup, "overlap_end");
} }
/** /**
*
* @return process logs * @return process logs
*/ */
public ProcessInfoLog[] getPLogs() { public ProcessInfoLog[] getPLogs() {
@ -292,17 +337,22 @@ public class StatusBar extends Panel implements EventListener<Event>
} }
/** /**
* * @return current status line text
* @return current status line text */
*/
public String getStatusLine() { public String getStatusLine() {
return m_statusText; return m_statusText;
} }
/**
* @return true if current status text is error text
*/
public boolean getStatusError() { public boolean getStatusError() {
return m_statusError; return m_statusError;
} }
/**
* Create new message popup instance
*/
private void createPopup() { private void createPopup() {
msgPopupCnt = new Div(); msgPopupCnt = new Div();
ZKUpdateUtil.setVflex(msgPopupCnt, "1"); ZKUpdateUtil.setVflex(msgPopupCnt, "1");
@ -321,6 +371,9 @@ public class StatusBar extends Panel implements EventListener<Event>
msgPopup.appendChild(msgPopupCaption); msgPopup.appendChild(msgPopupCaption);
} }
/**
* handle onClientInfo event from browser
*/
protected void onClientInfo() { protected void onClientInfo() {
ZKUpdateUtil.setWindowWidthX(msgPopup, 500); ZKUpdateUtil.setWindowWidthX(msgPopup, 500);
} }

View File

@ -27,18 +27,38 @@ import org.zkoss.zk.ui.event.EventListener;
import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Toolbarbutton; import org.zkoss.zul.Toolbarbutton;
/**
* Model for AD_ToolBarButton with IsCustomization=Y
* @author hengsin
*/
public class ToolbarCustomButton implements EventListener<Event>, Evaluatee { public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
/** Toolbarbutton instance **/
private Toolbarbutton toolbarButton; private Toolbarbutton toolbarButton;
/** AD_ToolBarButton.ActionClassName **/
private String actionId; private String actionId;
private int windowNo; private int windowNo;
private int tabNo = -1; private int tabNo = -1;
/** model instance for AD_ToolBarButton **/
private MToolBarButton mToolbarButton; private MToolBarButton mToolbarButton;
/**
* @param mToolbarButton
* @param btn
* @param actionId
* @param windowNo
*/
public ToolbarCustomButton(MToolBarButton mToolbarButton, Toolbarbutton btn, String actionId, int windowNo) { public ToolbarCustomButton(MToolBarButton mToolbarButton, Toolbarbutton btn, String actionId, int windowNo) {
this(mToolbarButton, btn, actionId, windowNo, -1); this(mToolbarButton, btn, actionId, windowNo, -1);
} }
/**
* @param mToolbarButton
* @param btn
* @param actionId
* @param windowNo
* @param tabNo
*/
public ToolbarCustomButton(MToolBarButton mToolbarButton, Toolbarbutton btn, String actionId, int windowNo, int tabNo) { public ToolbarCustomButton(MToolBarButton mToolbarButton, Toolbarbutton btn, String actionId, int windowNo, int tabNo) {
toolbarButton = btn; toolbarButton = btn;
this.actionId = actionId; this.actionId = actionId;
@ -49,6 +69,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
toolbarButton.addEventListener(Events.ON_CLICK, this); toolbarButton.addEventListener(Events.ON_CLICK, this);
} }
/**
* Call {@link IAction#execute(Object)}.
*/
@Override @Override
public void onEvent(Event event) throws Exception { public void onEvent(Event event) throws Exception {
IServiceHolder<IAction> serviceHolder = Actions.getAction(actionId); IServiceHolder<IAction> serviceHolder = Actions.getAction(actionId);
@ -77,10 +100,17 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
return Env.getContext (Env.getCtx(), windowNo, tabNo, variableName, false, true); return Env.getContext (Env.getCtx(), windowNo, tabNo, variableName, false, true);
} }
/**
* Delegate to {@link #dynamicDisplay(boolean)}
*/
public void dynamicDisplay() { public void dynamicDisplay() {
dynamicDisplay(false); dynamicDisplay(false);
} }
/**
* Dynamic update of button state.
* @param forceValidation if true, execute dynamic update event if button is in detached state
*/
public void dynamicDisplay(boolean forceValidation) { public void dynamicDisplay(boolean forceValidation) {
if (toolbarButton.getParent() == null && !forceValidation) if (toolbarButton.getParent() == null && !forceValidation)
return; return;
@ -107,6 +137,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
toolbarButton.setVisible(visible); toolbarButton.setVisible(visible);
} }
/**
* Evaluate pressedLogic (if defined)
*/
public void pressedLogic() { public void pressedLogic() {
if (toolbarButton.getParent() == null) if (toolbarButton.getParent() == null)
return; return;
@ -130,6 +163,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
((ToolBarButton) toolbarButton).setPressed(isPressed); ((ToolBarButton) toolbarButton).setPressed(isPressed);
} }
/**
* Evaluate readOnlyLogic (if defined)
*/
public void readOnlyLogic() { public void readOnlyLogic() {
if (toolbarButton.getParent() == null) if (toolbarButton.getParent() == null)
return; return;
@ -154,6 +190,13 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
toolbarButton.setDisabled(isReadOnly); toolbarButton.setDisabled(isReadOnly);
} }
/**
* Evaluate SQL or boolean logic expression.
* For SQL expression, return true if the SQL expression has result (it doesn't check the return value of the SQL statement).
* @param logic
* @param tabNo
* @return result of evaluation of logic
*/
private boolean validateLogic(String logic, int tabNo) { private boolean validateLogic(String logic, int tabNo) {
boolean isValid = false; boolean isValid = false;
@ -169,6 +212,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
return isValid; return isValid;
} }
/**
* @return {@link Toolbarbutton}
*/
public Toolbarbutton getToolbarbutton() { public Toolbarbutton getToolbarbutton() {
return toolbarButton; return toolbarButton;
} }

View File

@ -28,22 +28,30 @@ import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Button; import org.zkoss.zul.Button;
/** /**
* Model for AD_ToolBarButton with AD_Process_ID > 0
* @author hengsin * @author hengsin
*
*/ */
public class ToolbarProcessButton implements IProcessButton, Evaluatee { public class ToolbarProcessButton implements IProcessButton, Evaluatee {
/** model instance for AD_ToolBarButton **/
private MToolBarButton mToolbarButton; private MToolBarButton mToolbarButton;
private IADTabpanel adTabpanel; /** {@link IADTabpanel} that own this ToolbarProcessButton instance **/
private IADTabpanel adTabpanel;
/** translated process name **/
private String name; private String name;
/** translated process description **/
private String description; private String description;
/** ActionListener for button **/
private ActionListener actionListener; private ActionListener actionListener;
/** Button instance **/
private Button button; private Button button;
private int windowNo; private int windowNo;
/** /**
* @param windowNo * @param mToolbarButton
* * @param adTabpanel
* @param listener
* @param windowNo
*/ */
public ToolbarProcessButton(MToolBarButton mToolbarButton, IADTabpanel adTabpanel, ActionListener listener, int windowNo) { public ToolbarProcessButton(MToolBarButton mToolbarButton, IADTabpanel adTabpanel, ActionListener listener, int windowNo) {
this.mToolbarButton = mToolbarButton; this.mToolbarButton = mToolbarButton;
@ -118,10 +126,16 @@ public class ToolbarProcessButton implements IProcessButton, Evaluatee {
return name; return name;
} }
/**
* @return {@link Button}
*/
public Button getButton() { public Button getButton() {
return button; return button;
} }
/**
* Dynamic update of button state.
*/
public void dynamicDisplay() { public void dynamicDisplay() {
String displayLogic = mToolbarButton.getDisplayLogic(); String displayLogic = mToolbarButton.getDisplayLogic();
if (displayLogic == null || displayLogic.trim().length() == 0) if (displayLogic == null || displayLogic.trim().length() == 0)
@ -140,6 +154,9 @@ public class ToolbarProcessButton implements IProcessButton, Evaluatee {
return Env.getContext (Env.getCtx(), windowNo, tabNo, variableName, false, true); return Env.getContext (Env.getCtx(), windowNo, tabNo, variableName, false, true);
} }
/**
* Evaluate readOnlyLogic (if defined)
*/
public void readOnlyLogic() { public void readOnlyLogic() {
String readOnlyLogic = mToolbarButton.getReadOnlyLogic(); String readOnlyLogic = mToolbarButton.getReadOnlyLogic();
@ -150,6 +167,9 @@ public class ToolbarProcessButton implements IProcessButton, Evaluatee {
button.setDisabled(disabled); button.setDisabled(disabled);
} // readOnlyLogic } // readOnlyLogic
/**
* Evaluate pressedLogic (if defined)
*/
public void pressedLogic() { public void pressedLogic() {
String pressedLogic = mToolbarButton.getPressedLogic(); String pressedLogic = mToolbarButton.getPressedLogic();
@ -160,8 +180,14 @@ public class ToolbarProcessButton implements IProcessButton, Evaluatee {
button.setAttribute(ProcessButtonPopup.BUTTON_ATTRIBUTE_PRESSED, isPressed ? "Y" : "N"); button.setAttribute(ProcessButtonPopup.BUTTON_ATTRIBUTE_PRESSED, isPressed ? "Y" : "N");
} // pressedLogic } // pressedLogic
/**
* Evaluate SQL or boolean logic expression.
* For SQL expression, return true if the SQL expression has result (it doesn't check the return value of the SQL statement).
* @param logic SQL (@SQL=) or boolean expression
* @param tabNo
* @return result of evaluation of logic
*/
private boolean validateLogic(String logic, int tabNo) { private boolean validateLogic(String logic, int tabNo) {
boolean isValid = false; boolean isValid = false;
if (logic.startsWith("@SQL=")) { if (logic.startsWith("@SQL=")) {
isValid = Evaluator.parseSQLLogic(logic, Env.getCtx(), windowNo, tabNo, getColumnName()); isValid = Evaluator.parseSQLLogic(logic, Env.getCtx(), windowNo, tabNo, getColumnName());

View File

@ -1,7 +1,41 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* 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 2 *
* 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, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.adwindow.validator; package org.adempiere.webui.adwindow.validator;
import org.adempiere.util.Callback; import org.adempiere.util.Callback;
/**
* Interface for AD Window event
* @author hengsin
*/
public interface WindowValidator { public interface WindowValidator {
/**
* Handle {@link WindowValidatorEvent}.
* Call callback with the result of the event (success or error).
* @param event
* @param callback optional callback
*/
public void onWindowEvent(WindowValidatorEvent event, Callback<Boolean> callback); public void onWindowEvent(WindowValidatorEvent event, Callback<Boolean> callback);
} }

View File

@ -1,20 +1,60 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* 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 2 *
* 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, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.adwindow.validator; package org.adempiere.webui.adwindow.validator;
import org.adempiere.webui.adwindow.ADWindow; import org.adempiere.webui.adwindow.ADWindow;
/**
* Validation event for AD Window
* @author hengsin
*/
public class WindowValidatorEvent { public class WindowValidatorEvent {
/** {@link ADWindow} instance **/
private ADWindow window; private ADWindow window;
/** Event name **/
private String name; private String name;
/**
* @param window
* @param name
*/
public WindowValidatorEvent(ADWindow window, String name) { public WindowValidatorEvent(ADWindow window, String name) {
this.window = window; this.window = window;
this.name = name; this.name = name;
} }
/**
* @return {@link ADWindow}
*/
public ADWindow getWindow() { public ADWindow getWindow() {
return this.window; return this.window;
} }
/**
* @return Event name
*/
public String getName() { public String getName() {
return this.name; return this.name;
} }

View File

@ -1,5 +1,34 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* 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 2 *
* 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, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.adwindow.validator; package org.adempiere.webui.adwindow.validator;
/**
* Predefine AD Window validation event type
* @author hengsin
*
*/
public enum WindowValidatorEventType { public enum WindowValidatorEventType {
BEFORE_IGNORE("beforeIgnore"), BEFORE_IGNORE("beforeIgnore"),
AFTER_IGNORE("afterIgnore"), AFTER_IGNORE("afterIgnore"),
@ -14,12 +43,19 @@ public enum WindowValidatorEventType {
BEFORE_COPY("beforeCopy"), BEFORE_COPY("beforeCopy"),
AFTER_COPY("afterCopy"); AFTER_COPY("afterCopy");
/** Event name **/
private String name; private String name;
/**
* @param name
*/
private WindowValidatorEventType(String name) { private WindowValidatorEventType(String name) {
this.name = name; this.name = name;
} }
/**
* @return Event name
*/
public String getName() { public String getName() {
return name; return name;
} }

View File

@ -1,3 +1,27 @@
/***********************************************************************
* This file is part of iDempiere ERP Open Source *
* http://www.idempiere.org *
* *
* Copyright (C) Contributors *
* *
* 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 2 *
* 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, write to the Free Software *
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, *
* MA 02110-1301, USA. *
* *
* Contributors: *
* - hengsin *
**********************************************************************/
package org.adempiere.webui.adwindow.validator; package org.adempiere.webui.adwindow.validator;
import java.util.ArrayList; import java.util.ArrayList;
@ -13,14 +37,25 @@ import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer; import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
*
* Manage {@link WindowValidator} osgi services
* @author hengsin
*
*/
public class WindowValidatorManager implements BundleActivator, ServiceTrackerCustomizer<WindowValidator, WindowValidator> { public class WindowValidatorManager implements BundleActivator, ServiceTrackerCustomizer<WindowValidator, WindowValidator> {
/** Singleton WindowValidatorManager instance **/
private static WindowValidatorManager instance = null; private static WindowValidatorManager instance = null;
/** {@link BundleContext} instance **/
private BundleContext context; private BundleContext context;
/** AD_Window_UU:List<WindowValidator> **/
private Map<String, List<WindowValidator>> validatorMap = new HashMap<String, List<WindowValidator>>(); private Map<String, List<WindowValidator>> validatorMap = new HashMap<String, List<WindowValidator>>();
/** WindowValidator for all AD Window **/
private List<WindowValidator> globalValidators = new ArrayList<WindowValidator>(); private List<WindowValidator> globalValidators = new ArrayList<WindowValidator>();
/** WindowValidator osgi service tracker **/
private ServiceTracker<WindowValidator, WindowValidator> serviceTracker; private ServiceTracker<WindowValidator, WindowValidator> serviceTracker;
@Override @Override
@ -46,7 +81,12 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
return service; return service;
} }
void addService(WindowValidator service, String uuid) { /**
* Add {@link WindowValidator} service for an AD Window
* @param service
* @param uuid AD_Window_UU
*/
protected void addService(WindowValidator service, String uuid) {
List<WindowValidator> list = validatorMap.get(uuid); List<WindowValidator> list = validatorMap.get(uuid);
if (list == null) { if (list == null) {
list = new ArrayList<WindowValidator>(); list = new ArrayList<WindowValidator>();
@ -81,13 +121,21 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
} }
} }
void removeService(WindowValidator service, String uuid) { /**
*
* @param service
* @param uuid
*/
protected void removeService(WindowValidator service, String uuid) {
List<WindowValidator> list = validatorMap.get(uuid); List<WindowValidator> list = validatorMap.get(uuid);
if (list != null) { if (list != null) {
list.remove(service); list.remove(service);
} }
} }
/**
* Create {@link #serviceTracker} and {@link #instance}
*/
@Override @Override
public void start(BundleContext context) throws Exception { public void start(BundleContext context) throws Exception {
this.context = context; this.context = context;
@ -97,6 +145,9 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
instance = this; instance = this;
} }
/**
* Close {@link #serviceTracker} and dispose {@link #instance}
*/
@Override @Override
public void stop(BundleContext context) throws Exception { public void stop(BundleContext context) throws Exception {
serviceTracker.close(); serviceTracker.close();
@ -104,10 +155,19 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
instance = null; instance = null;
} }
/**
*
* @return {@link WindowValidatorManager}
*/
public static WindowValidatorManager getInstance() { public static WindowValidatorManager getInstance() {
return instance; return instance;
} }
/**
* fire window validator event for all register {@link WindowValidator} service
* @param event
* @param callback optional callback
*/
public void fireWindowValidatorEvent(WindowValidatorEvent event, Callback<Boolean> callback) { public void fireWindowValidatorEvent(WindowValidatorEvent event, Callback<Boolean> callback) {
ADWindow window = event.getWindow(); ADWindow window = event.getWindow();
String uuid = window.getAD_Window_UU(); String uuid = window.getAD_Window_UU();
@ -129,19 +189,32 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
chain.start(); chain.start();
} }
/**
* class to call a list of {@link WindowValidator} through {@link Callback} chain.
*/
private static class ChainCallback implements Callback<Boolean> { private static class ChainCallback implements Callback<Boolean> {
/** optional callback to invoke after execution of all {@link #validators} or when there's error **/
private Callback<Boolean> callback; private Callback<Boolean> callback;
private WindowValidator[] validators; private WindowValidator[] validators;
/** event for {@link #validators} **/
private WindowValidatorEvent event; private WindowValidatorEvent event;
/** current index of {@link #validators} **/
private int index = -1; private int index = -1;
/**
* @param event
* @param validators
* @param callback optional callback to invoke after execution of all {@link #validators} or when there's error
*/
public ChainCallback(WindowValidatorEvent event, WindowValidator[] validators, Callback<Boolean> callback) { public ChainCallback(WindowValidatorEvent event, WindowValidator[] validators, Callback<Boolean> callback) {
this.event = event; this.event = event;
this.validators = validators; this.validators = validators;
this.callback = callback; this.callback = callback;
} }
/**
* Start the @link {@link WindowValidator} callback chain.
*/
public void start() { public void start() {
index = 0; index = 0;
if (index < validators.length) if (index < validators.length)

View File

@ -31,6 +31,7 @@ import java.util.Properties;
import java.util.logging.Level; import java.util.logging.Level;
import org.adempiere.util.LogAuthFailure; import org.adempiere.util.LogAuthFailure;
import org.adempiere.webui.AdempiereWebUI;
import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.LayoutUtils;
import org.adempiere.webui.apps.AEnv; import org.adempiere.webui.apps.AEnv;
import org.adempiere.webui.component.Button; import org.adempiere.webui.component.Button;
@ -660,7 +661,7 @@ public class LoginPanel extends Window implements EventListener<Event>
// [ adempiere-ZK Web Client-2832968 ] User context lost? // [ adempiere-ZK Web Client-2832968 ] User context lost?
// https://sourceforge.net/p/adempiere/zk-web-client/303/ // https://sourceforge.net/p/adempiere/zk-web-client/303/
// it's harmless, if there is no bug then this must never fail // it's harmless, if there is no bug then this must never fail
currSess.setAttribute("Check_AD_User_ID", Env.getAD_User_ID(ctx)); currSess.setAttribute(AdempiereWebUI.CHECK_AD_USER_ID_ATTR, Env.getAD_User_ID(ctx));
// End of temporary code for [ adempiere-ZK Web Client-2832968 ] User context lost? // End of temporary code for [ adempiere-ZK Web Client-2832968 ] User context lost?
/* Check DB version */ /* Check DB version */

View File

@ -30,6 +30,7 @@ import java.util.Properties;
import javax.servlet.http.HttpSession; import javax.servlet.http.HttpSession;
import org.adempiere.util.Callback; import org.adempiere.util.Callback;
import org.adempiere.webui.AdempiereWebUI;
import org.adempiere.webui.IWebClient; import org.adempiere.webui.IWebClient;
import org.adempiere.webui.component.FWindow; import org.adempiere.webui.component.FWindow;
import org.adempiere.webui.component.Window; import org.adempiere.webui.component.Window;
@ -297,7 +298,7 @@ public class LoginWindow extends FWindow implements EventListener<Event>
else else
loginName = user.getLDAPUser() != null ? user.getLDAPUser() : user.getName(); loginName = user.getLDAPUser() != null ? user.getLDAPUser() : user.getName();
loginOk(loginName, true, login.getClients()); loginOk(loginName, true, login.getClients());
getDesktop().getSession().setAttribute("Check_AD_User_ID", Env.getAD_User_ID(ctx)); getDesktop().getSession().setAttribute(AdempiereWebUI.CHECK_AD_USER_ID_ATTR, Env.getAD_User_ID(ctx));
pnlRole.setChangeRole(true); pnlRole.setChangeRole(true);
pnlRole.changeRole(ctx); pnlRole.changeRole(ctx);
} }