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";
/** default timeout of of 2 minutes **/
public static final int DEFAULT_TIMEOUT = 1000 * 60 * 2;
private final AtomicReference<Desktop> desktop = new AtomicReference<Desktop>();
private final Logger log = LoggerFactory.getLogger(this.getClass());
/** asynchronous request reference as AtmosphereResource **/
private final AtomicReference<AtmosphereResource> resource = new AtomicReference<AtmosphereResource>();
private final int timeout;
@ -66,6 +68,9 @@ public class AtmosphereServerPush implements ServerPush {
private final Object _mutex = new Object();
private List<Schedule<Event>> schedules = new ArrayList<>();
/**
* default constructor
*/
public AtmosphereServerPush() {
String timeoutString = Library.getProperty("fi.jawsy.jawwa.zk.atmosphere.timeout");
if (timeoutString == null || timeoutString.trim().length() == 0) {
@ -120,10 +125,19 @@ public class AtmosphereServerPush implements ServerPush {
return true;
}
/**
* release current AtmosphereResource
* @param resource
*/
public void clearResource(AtmosphereResource resource) {
this.resource.compareAndSet(resource, null);
}
/**
* commit/resume response for current AtmosphereResource
* @return true if resource is available
* @throws IOException
*/
private boolean commitResponse() throws IOException {
AtmosphereResource resource = this.resource.getAndSet(null);
if (resource != null) {
@ -224,6 +238,10 @@ public class AtmosphereServerPush implements ServerPush {
startClientPush(desktop);
}
/**
* start serverpush request at client side
* @param desktop
*/
private void startClientPush(Desktop desktop) {
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) {
if (log.isTraceEnabled()) {
log.trace(resource.transport().name());
@ -267,6 +289,7 @@ public class AtmosphereServerPush implements ServerPush {
return;
}
//suspend request for server push event
if (!resource.isSuspended()) {
//browser default timeout is 2 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;
/**
* class to hold two value, left and right
* @author hengsin
*
* @param <L>
* @param <R>
*/
public class Either<L, R> {
private L left;
private R right;
/**
* @param l left value
* @param r right value
*/
public Either(L l, R r) {
left = l;
right = r;
}
/**
* @return left value
*/
public L getLeftValue() {
return left;
}
/**
* @return right value
*/
public R getRightValue() {
return right;
}

View File

@ -51,6 +51,12 @@ public class ZkAtmosphereHandler implements AtmosphereHandler {
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) {
if (session.getWebApp() instanceof WebAppCtrl) {
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);
}
/**
* 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) {
String dtid = request.getParameter("dtid");
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) {
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) {
if (desktop instanceof DesktopCtrl) {
DesktopCtrl desktopCtrl = (DesktopCtrl) desktop;
@ -109,6 +130,12 @@ public class ZkAtmosphereHandler implements AtmosphereHandler {
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) {
Session session = WebManager.getSession(resource.getAtmosphereConfig().getServletContext(), request, false);
if (session == null) {

View File

@ -90,23 +90,31 @@ import org.zkoss.zul.Window;
*/
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";
/**
*
* generated serial id
*/
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 String APP_NAME = null;
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";
/** login and role selection window **/
private WLogin loginDesktop;
/** client info from browser **/
private ClientInfo clientInfo = new ClientInfo();
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);
@Deprecated(forRemoval = true, since = "11")
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";
/** the use of event thread have been deprecated, this should always be false **/
private static boolean eventThreadEnabled = false;
private ConcurrentMap<String, String[]> m_URLParameters;
/** Login completed event **/
private static final String ON_LOGIN_COMPLETED = "onLoginCompleted";
/**
* default constructor
*/
public AdempiereWebUI()
{
this.setVisible(false);
@ -140,8 +155,12 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
this.addEventListener(ON_LOGIN_COMPLETED, this);
}
/**
* Handle onCreate event from index.zul, don't call this directly.
*/
public void onCreate()
{
//handle ping request
String ping = Executions.getCurrent().getHeader("X-PING");
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();
}
/**
* perform clean up for ping request
*/
private void cleanupForPing() {
final Desktop desktop = Executions.getCurrent().getDesktop();
final WebApp wapp = desktop.getWebApp();
@ -208,10 +230,16 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
}, 1, TimeUnit.SECONDS);
}
/**
* Handle onOK (enter key) event
*/
public void onOk()
{
}
/**
* Handle onCancel(escape key) event
*/
public void onCancel()
{
}
@ -219,6 +247,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
/* (non-Javadoc)
* @see org.adempiere.webui.IWebClient#loginCompleted()
*/
@Override
public void loginCompleted()
{
if (loginDesktop != null)
@ -280,7 +309,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
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
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.setAutoBlur(false);
//create new desktop
//create IDesktop instance
IDesktop appDesktop = createDesktop();
appDesktop.setClientInfo(clientInfo);
appDesktop.createPart(this.getPage());
this.getPage().getDesktop().setAttribute(APPLICATION_DESKTOP_KEY, new WeakReference<IDesktop>(appDesktop));
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());
//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());
Clients.response(new AuScript("zAu.cmd0.clearBusy()"));
//add dynamic style
//add dynamic style for AD tab
StringBuilder cssContent = new StringBuilder();
cssContent.append(".adtab-form-borderlayout .z-south-collapsed:before { ");
cssContent.append("content: \"");
@ -347,6 +376,9 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
processParameters();
}
/**
* process URL parameters from browser
*/
private void processParameters() {
String action = getPrmString("Action");
if ("Zoom".equalsIgnoreCase(action)) {
@ -368,6 +400,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
m_URLParameters = null;
}
/**
* @param prm parameter name
* @return string value for parameter
*/
private String getPrmString(String prm) {
String retValue = "";
if (m_URLParameters != null) {
@ -378,6 +414,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
return retValue;
}
/**
* @param prm parameter name
* @return integer value for parameter
*/
private int getPrmInt(String prm) {
int retValue = 0;
String str = getPrmString(prm);
@ -398,6 +438,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
return keyListener;
}
/**
* Create IDesktop instance. Default is {@link DefaultDesktop}
* @return {@link IDesktop}
*/
private IDesktop createDesktop()
{
IDesktop appDesktop = null;
@ -424,6 +468,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
/* (non-Javadoc)
* @see org.adempiere.webui.IWebClient#logout()
*/
@Override
public void logout()
{
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) {
try {
((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(){
Desktop desktop = Executions.getCurrent().getDesktop();
@ -472,7 +521,10 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
afterLogout(session);
}
/**
* Logout current session
* @return {@link Session}
*/
protected Session logout0() {
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
*/
@Override
public IDesktop getAppDeskop()
{
Desktop desktop = Executions.getCurrent() != null ? Executions.getCurrent().getDesktop() : null;
@ -519,6 +572,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
return appDesktop;
}
@Override
public void onEvent(Event event) {
if (event instanceof ClientInfoEvent) {
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) {
SessionManager.setSessionApplication(this);
loginDesktop = new WLogin(this);
@ -576,6 +635,7 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
* @param userId
* @return UserPreference
*/
@Override
public UserPreference loadUserPreference(int userId) {
userPreference.loadPreference(userId);
return userPreference;
@ -584,10 +644,15 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
/**
* @return UserPrerence
*/
@Override
public UserPreference getUserPreference() {
return userPreference;
}
/**
* Should always return false
* @return true if event thread is enabled
*/
public static boolean isEventThreadEnabled() {
return eventThreadEnabled;
}
@ -635,6 +700,12 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
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) {
//stop key listener
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() {
StringBuilder uploadSetting = new StringBuilder("true,native");
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;
/**
*
* Model for client info from browser
* @author Low Heng Sin
*
*/
public class ClientInfo implements Serializable {
/**
*
* generated serial id
*/
private static final long serialVersionUID = -2686811277627911861L;
//values from browser
public int colorDepth;
public int desktopWidth;
public int desktopHeight;
@ -52,6 +53,7 @@ public class ClientInfo implements Serializable {
public boolean tablet;
public double devicePixelRatio;
//size constants for responsive layout
public static final int LARGE_WIDTH = 1200;
public static final int MEDIUM_WIDTH = 1000;
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
*
*/

View File

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

View File

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

View File

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

View File

@ -44,7 +44,7 @@ import org.zkoss.zul.Window;
import org.zkoss.zul.Window.Mode;
/**
*
* Some static UI helper methods
* @author Low Heng Sin
*
*/
@ -55,7 +55,7 @@ public final class LayoutUtils {
/**
* @param layout
*/
@Deprecated
@Deprecated(forRemoval = true, since = "11")
public static void sendDeferLayoutEvent(org.zkoss.zul.Borderlayout layout, int timeout) {
/* this is not required anymore */
// StringBuilder content = new StringBuilder();
@ -68,7 +68,7 @@ public final class LayoutUtils {
}
/**
*
* append cls to target's sclass property
* @param cls
* @param target
*/
@ -82,7 +82,7 @@ public final class LayoutUtils {
*
* @param cls
* @param target
* @return boolean
* @return true if target's sclass property contain cls
*/
public static boolean hasSclass(String cls, HtmlBasedComponent target) {
String sclass = target.getSclass();
@ -93,9 +93,9 @@ public final class LayoutUtils {
}
/**
*
* create right align label (wrapped in div)
* @param label
* @return wrapped label
* @return right align label (wrapped in div)
*/
public static Component makeRightAlign(Label label) {
Div div = new Div();
@ -105,28 +105,41 @@ public final class LayoutUtils {
return div;
}
/**
* open popup window overlapping ref component
* @param ref
* @param window
*/
public static void openPopupWindow(Component ref, Window window) {
openPopupWindow(ref, window, 0);
}
/**
* open popup window overlapping the ref component
* open popup window overlapping ref component
* @param ref
* @param window
* @param delayMs
*/
public static void openPopupWindow(Component ref, Window window, int delayMs) {
openPopupWindow(ref, window, "overlap", delayMs);
}
/**
* open popup window relative to ref component
* @param ref
* @param window
* @param position Refer to https://www.zkoss.org/javadoc/latest/jsdoc/_global_/jqzk.html#position-_global_.Dimension-_global_.String-_global_.Map-
*/
public static void openPopupWindow(Component ref, Window window, String position) {
openPopupWindow(ref, window, position, 0);
}
/**
* open popup window relative to the ref component
* open popup window relative to ref component
* @param ref
* @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) {
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 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) {
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 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) {
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 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) {
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 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) {
StringBuilder script = new StringBuilder();
@ -230,7 +243,7 @@ public final class LayoutUtils {
}
/**
*
* Force redraw of component
* @param component
*/
public static void redraw(AbstractComponent component) {
@ -259,6 +272,11 @@ public final class LayoutUtils {
return true;
}
/**
* Remove cls from target's sclass property
* @param cls
* @param target
*/
public static void removeSclass(String cls, HtmlBasedComponent target) {
String sclass = target.getSclass();
if (Util.isEmpty(sclass))
@ -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.
* 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_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
* @param window
* @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.
* 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 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 ownWindow
* @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
* @param child
* @param parentClass
* @return
* @return {@link ISupportMask}
*/
public static ISupportMask findMaskParent (Component child, Class<?> parentClass){
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) {
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) -> {
if (evt.isOpen())
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
* @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
*/
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 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;
import java.io.File;
@ -23,6 +45,9 @@ import org.compiere.util.DB;
import org.compiere.util.Ini;
import org.compiere.util.WebUtil;
/**
* Sync state of {@link HttpSession} and AD_Session
*/
public class LoggedSessionListener implements HttpSessionListener, ServletContextListener, ServerStateChangeListener{
private static Hashtable<String, HttpSession> AD_SessionList = new Hashtable<String, HttpSession>();
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())
{
Adempiere.addServerStateChangeListener(this);
@ -100,7 +125,7 @@ public class LoggedSessionListener implements HttpSessionListener, ServletContex
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'";
int no = DB.executeUpdate(sql, new Object[] {sessionID, serverName}, false, null);
if (no < 0) {

View File

@ -21,7 +21,7 @@ import org.zkoss.zk.ui.Component;
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
* @author hieplq
*
@ -39,7 +39,7 @@ public class ShowMaskWrapper implements ISupportMask {
private Mask maskObj;
/**
* comp is component want implement this interface
* comp is component that implement ISupportMask
* @param comp
*/
public ShowMaskWrapper (Component comp){

View File

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

View File

@ -68,12 +68,12 @@ import org.zkoss.zul.Vlayout;
public class ValuePreference extends Window implements EventListener<Event>
{
/**
*
* generated serial id
*/
private static final long serialVersionUID = 7594680475358417813L;
/**
* Factory
* Show value preference dialog
* @param ref
* @param mField field
* @param aValue value
@ -89,7 +89,7 @@ public class ValuePreference extends Window implements EventListener<Event>
} // start
/**
* Factory
* Show value preference dialog
* @param ref
* @param mField field
* @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_User_ID = Env.getAD_User_ID(Env.getCtx());
// Create Editor
// Create and show value preference dialog
@SuppressWarnings("unused")
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(),
@ -135,42 +135,8 @@ public class ValuePreference extends Window implements EventListener<Event>
displayType, AD_Reference_ID, ref);
} // create
/**
* 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 */
/** The Name of the Dialog */
public static final String NAME = "ValuePreference";
/** The Menu Icon */
//private static String ICON_URL = "images/VPreference16.png";
/** Logger */
private static final CLogger log = CLogger.getCLogger(ValuePreference.class);
private AbstractADWindowContent adwindowContent;
@ -189,7 +155,7 @@ public class ValuePreference extends Window implements EventListener<Event>
* @param DisplayValue value display
* @param displayType display type
* @param AD_Reference_ID reference
* @param ref
* @param ref
*/
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,
@ -439,7 +405,6 @@ public class ValuePreference extends Window implements EventListener<Event>
// ActionListener
cbClient.setEnabled(false);
cbClient.setChecked(true);
// cbClient.addActionListener(this);
// Can Change Org
if (MRole.PREFERENCETYPE_Client.equals(m_role.getPreferenceType()))
@ -498,7 +463,7 @@ public class ValuePreference extends Window implements EventListener<Event>
}
else
setExplanation();
} // actionPerformed
}
private void onCancel() {
this.detach();
@ -557,8 +522,6 @@ public class ValuePreference extends Window implements EventListener<Event>
*/
public int delete()
{
log.info("");
StringBuilder sql = new StringBuilder ("DELETE FROM AD_Preference WHERE ");
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);
@ -654,12 +617,10 @@ public class ValuePreference extends Window implements EventListener<Event>
} // getContextKey
/**
* Save to Disk
* Save to DB
*/
public void insert()
{
log.info("");
// --- Delete first
int no = delete();

View File

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

View File

@ -34,7 +34,7 @@ import org.zkoss.zul.West;
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 Low Heng Sin
* @date Mar 3, 2007
@ -42,17 +42,28 @@ import org.zkoss.zul.Window;
*/
public class WLogin extends AbstractUIPart
{
/** IWebClient instance ({@link AdempiereWebUI}) **/
private IWebClient app;
/** Main layout **/
private Borderlayout layout;
@Deprecated(forRemoval = true, since = "11")
private Window browserWarningWindow;
/** embedded window for login and role selection **/
private LoginWindow loginWindow;
/**
*
* @param app
*/
public WLogin(IWebClient 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)
{
PageDefinition pageDefintion = Executions.getCurrent().getPageDefinition(ThemeManager.getThemeResource("zul/login/login.zul"));
@ -107,6 +118,9 @@ public class WLogin extends AbstractUIPart
return layout;
}
/**
* detach/dispose window content
*/
public void detach() {
layout.detach();
layout = null;
@ -114,12 +128,15 @@ public class WLogin extends AbstractUIPart
browserWarningWindow.detach();
}
/**
* @return {@link Component}
*/
public Component getComponent() {
return layout;
}
/**
* Show change role window
* Show change role panel in {@link #loginWindow}
* @param locale
* @param properties env context
*/

View File

@ -36,8 +36,8 @@ import org.zkoss.zul.Menupopup;
import org.zkoss.zul.Popup;
/**
* Request Button Action.
* Popup Menu
* Handle Request Button Action.
* Show Popup Menu.
*
* @author Jorg Janke
* @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)
{
//open request window
MQuery query = null;
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) {
// New - set Table/Record
if (e.getTarget() == m_new)

View File

@ -37,11 +37,10 @@ import org.zkoss.zul.Menupopup;
import org.zkoss.zul.Popup;
/**
* Application Zoom Across Launcher.
* Called from APanel; Queries available Zoom Targets for Table.
* Handle Zoom Across button action.
*
* @author Jorg Janke
* @version $Id: AZoomAcross.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $
* @author Jorg Janke
* @version $Id: AZoomAcross.java,v 1.2 2006/07/30 00:51:27 jjanke Exp $
*
* @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) {
if (log.isLoggable(Level.CONFIG)) log.config("PO=" + po+", WindowID="+windowID);
@ -104,12 +109,18 @@ public class WZoomAcross
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 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) {
for (final ZoomInfoFactory.ZoomInfo zoomInfo : ZoomInfoFactory.retrieveZoomInfos(po,
@ -125,18 +136,18 @@ public class WZoomAcross
}
/**
* Launch Zoom
* @param pp KeyPair
* Zoom to destination window
* @param zoomInfo
*/
private void launchZoom (final ZoomInfoFactory.ZoomInfo zoomInfo)
{
final int AD_Window_ID = zoomInfo.windowId;
final MQuery query = zoomInfo.query;
log.info("AD_Window_ID=" + AD_Window_ID
+ " - " + query);
if (log.isLoggable(Level.INFO))
log.info("AD_Window_ID=" + AD_Window_ID + " - " + query);
AEnv.zoom(AD_Window_ID, query);
} // launchZoom
} // AZoom
}

View File

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

View File

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

View File

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

View File

@ -25,7 +25,7 @@ import org.compiere.util.CCache;
import org.zkoss.image.AImage;
/**
*
* Static methods to get {@link IAction} osgi service instance.
* @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
* @return {@link AImage}
*/

View File

@ -22,7 +22,7 @@ import org.zkoss.zul.Toolbarbutton;
*/
public interface IAction {
/**
*
* execute action
* @param 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
{
/**
*
* generated serial id
*/
private static final long serialVersionUID = 4302282658814599752L;
/**
* default constructor
*/
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 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);
}
};
SimpleListModel yesModel = new SimpleListModel();
Listbox noList = new Listbox();
Listbox yesList = new Listbox();
protected SimpleListModel yesModel = new SimpleListModel();
protected Listbox noList = new Listbox();
protected Listbox yesList = new Listbox();
private GridTab gridTab;
private boolean uiCreated;
/** true if tab have been activated **/
private boolean active = false;
private boolean isChanged;
private boolean detailPaneMode;
@ -277,7 +281,8 @@ public class ADSortTab extends Panel implements IADTabpanel
m_IdentifierSql = identifierSql.toString();
//
noLabel.setValue(Msg.getMsg(Env.getCtx(), "Available"));
log.fine(m_ColumnSortName);
if (log.isLoggable(Level.FINE))
log.fine(m_ColumnSortName);
} // dynInit
/**
@ -520,14 +525,19 @@ public class ADSortTab extends Panel implements IADTabpanel
}
}
/**
* @return true if tab has changes
*/
public boolean isChanged() {
return isChanged;
}
/**
* Move an item between yes and no list.
* Delegate to {@link #migrateLists(Listbox, Listbox, int)}
* @param event
*/
void migrateValueAcrossLists (Event event)
protected void migrateValueAcrossLists (Event event)
{
Object source = event.getTarget();
if (source instanceof ListItem) {
@ -544,7 +554,13 @@ public class ADSortTab extends Panel implements IADTabpanel
migrateLists (listFrom,listTo,endIndex);
} // 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;
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
*/
void migrateValueWithinYesList (Event event)
protected void migrateValueWithinYesList (Event event)
{
Object[] selObjects = yesList.getSelectedItems().toArray();
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
*/
void migrateValueWithinYesList (int endIndex, List<ListElement> selObjects)
protected void migrateValueWithinYesList (int endIndex, List<ListElement> selObjects)
{
int iniIndex =0;
Arrays.sort(selObjects.toArray());
@ -667,8 +683,9 @@ public class ADSortTab extends Panel implements IADTabpanel
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)
{
@ -676,14 +693,15 @@ public class ADSortTab extends Panel implements IADTabpanel
} // registerAPanel
/** (non-Javadoc)
/**
* Save changes to db.
*/
public void saveData()
{
if (!adWindowPanel.getToolbar().isSaveEnable())
return;
log.fine("");
boolean ok = true;
//TODO: should use model instead to enable change log and event handling
StringBuilder info = new StringBuilder();
StringBuffer sql = null;
// 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 {
if (event instanceof DropEvent)
{
@ -884,6 +903,7 @@ public class ADSortTab extends Panel implements IADTabpanel
}
}
@Override
public void activate(boolean b) {
if (b) {
if (getAttribute(ATTR_ON_ACTIVATE_POSTED) != null) {
@ -899,6 +919,7 @@ public class ADSortTab extends Panel implements IADTabpanel
Events.postEvent(event);
}
@Override
public void createUI() {
if (uiCreated) return;
try
@ -913,64 +934,80 @@ public class ADSortTab extends Panel implements IADTabpanel
uiCreated = true;
}
@Override
public void dynamicDisplay(int i) {
}
@Deprecated(forRemoval = true, since = "11")
public void editRecord(boolean b) {
}
@Override
public String getDisplayLogic() {
return gridTab.getDisplayLogic();
}
@Override
public GridTab getGridTab() {
return gridTab;
}
@Override
public int getTabLevel() {
return gridTab.getTabLevel();
}
@Override
public String getTableName()
{
return gridTab.getTableName();
}
@Override
public int getRecord_ID() {
return gridTab.getRecord_ID();
}
@Override
public String getTitle() {
return gridTab.getName();
}
@Override
public boolean isCurrent() {
return gridTab != null ? gridTab.isCurrent() : false;
}
@Override
public void query() {
loadData();
}
@Override
public void query(boolean currentRows, int currentDays, int i) {
loadData();
}
@Override
public void refresh() {
createUI();
loadData();
}
@Override
public void switchRowPresentation() {
}
@Override
public String get_ValueAsString(String variableName) {
return Env.getContext(Env.getCtx(), m_WindowNo, variableName);
}
@Override
public void afterSave(boolean onSaveEvent) {
}
@Override
public boolean onEnterKey() {
return false;
}
@ -991,6 +1028,7 @@ public class ADSortTab extends Panel implements IADTabpanel
ZKUpdateUtil.setVflex(this, "true");
}
@Override
public boolean isDetailPaneMode() {
return this.detailPaneMode;
}
@ -1053,6 +1091,7 @@ public class ADSortTab extends Panel implements IADTabpanel
noList.setModel(noModel);
}
@Override
public ADTreePanel getTreePanel() {
return null;
}

View File

@ -127,6 +127,7 @@ import org.zkoss.zul.West;
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.
* 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>,
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_OUT_CSS = "slide-left-out";
@ -151,19 +153,26 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
private static final String SLIDE_RIGHT_OUT_CSS = "slide-right-out";
/**
*
* generated serial id
*/
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";
/** post init event for tab panel **/
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";
/** Event after execution of {@link #dynamicDisplay(int)} **/
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";
/** 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 CLogger logger;
@ -177,68 +186,95 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
private GridWindow gridWindow;
/** AD Window content part that own this ADTabpanel instance **/
private AbstractADWindowContent windowPanel;
private int windowNo;
/** form view for center of {@link #formContainer} **/
private Grid form;
/** field editors **/
private ArrayList<WEditor> editors = new ArrayList<WEditor>();
/** components for field editors **/
private ArrayList<Component> editorComps = new ArrayList<Component>();
/** editor toolbar buttons**/
private ArrayList<WButtonEditor> toolbarButtonEditors = new ArrayList<WButtonEditor>();
/** toolbar buttons for AD_ToolBarButton **/
private ArrayList<ToolbarProcessButton> toolbarProcessButtons = new ArrayList<ToolbarProcessButton>();
/** true if UI have been created for form and list **/
private boolean uiCreated = false;
/** list view for center of {@link #formContainer} **/
private GridView listPanel;
/** content rows for group **/
private Map<String, List<Row>> fieldGroupContents;
/** header row for group **/
private Map<String, List<org.zkoss.zul.Row>> fieldGroupHeaders;
/** tabs for group (for tab type field group) **/
private Map<String, List<Tab>> fieldGroupTabHeaders;
/** all rows for current group (regardless of field group type) **/
private ArrayList<Row> rowList;
/** all collapsible groups **/
protected List<Group> allCollapsibleGroups;
/** main layout for header (center), tree (west) and detail pane (south) **/
private Borderlayout formContainer = null;
/** Tree panel for west of {@link #formContainer} **/
private ADTreePanel treePanel = null;
/** Sync field editor changes to GridField **/
private GridTabDataBinder dataBinder;
/** true if tab have been activated **/
protected boolean activated = false;
/**
* current group for collapsible type field group
*/
private Group currentGroup;
/** Panel for child tabs, south of {@link #formContainer} **/
private DetailPane detailPane;
/** true if this ADTabpanel instance is own by detail pane **/
private boolean detailPaneMode;
/** tab no within an AD Window (sequence start from 0) **/
private int tabNo;
/** DefaultFocusField */
/** Default focus field */
private WEditor defaultFocusField = null;
/** number of columns for {@link #form} **/
private int numberOfFormColumns;
/** event to toggle between form and list view **/
public static final String ON_TOGGLE_EVENT = "onToggle";
/** default width for west tree panel **/
private static final String DEFAULT_PANEL_WIDTH = "300px";
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();
/** List of Tab Group Grids */
private List<Grid> tabForms;
/** Current Tab Group Rows */
private Rows currentTabRows;
/** List of Grid/Form for tab type field group */
private List<Grid> tabGroupForms;
/** Current Rows for tab type field group */
private Rows currentTabGroupRows;
/** Event for south of {@link #formContainer} **/
private static enum SouthEvent {
SLIDE(),
OPEN(),
@ -247,11 +283,17 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
private SouthEvent() {}
}
/**
* default constructor
*/
public ADTabpanel()
{
init();
}
/**
* init components and event listeners
*/
private void init()
{
initComponents();
@ -270,6 +312,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
ClientInfo.onClientInfo(this, this::onClientInfo);
}
/**
* Create new {@link #form} and {@link #listPanel} instance.
*/
private void initComponents()
{
LayoutUtils.addSclass("adtab-content", this);
@ -282,6 +327,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
form.setVflex(false);
form.setSclass("grid-layout adwindow-form");
form.setWidgetAttribute(AdempiereWebUI.WIDGET_INSTANCE_NAME, "form");
//swipe listener for mobile
if (ClientInfo.isMobile())
{
form.addEventListener("onSwipeRight", e -> {
@ -313,6 +359,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
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() {
String uuid = form.getUuid();
StringBuilder script = new StringBuilder("(function(){let w=zk.Widget.$('")
@ -344,7 +394,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
public void setDetailPane(DetailPane component) {
detailPane = component;
Borderlayout borderLayout = (Borderlayout) formContainer;
Borderlayout borderLayout = formContainer;
South south = borderLayout.getSouth();
if (south == null) {
south = new South();
@ -375,7 +425,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
ZKUpdateUtil.setHeight(formContainer.getSouth(), height);
}
} 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 gridTab
*/
@ -487,7 +537,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
* Create UI components if not already created
* Create UI for AD_Fields
*/
@Override
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
*/
protected void createUI(boolean update)
@ -515,7 +565,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
fieldGroupHeaders = new HashMap<String, List<org.zkoss.zul.Row>>();
allCollapsibleGroups = new ArrayList<Group>();
tabForms = new ArrayList<Grid>();
tabGroupForms = new ArrayList<Grid>();
fieldGroupTabHeaders = new HashMap<String, List<Tab>>();
int numCols=gridTab.getNumColumns();
@ -610,8 +660,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (numCols - actualxpos + 1 > 0)
row.appendCellChild(createSpacer(), numCols - actualxpos + 1);
if(currentTabRows != null) {
currentTabRows.appendChild(row);
if (currentTabGroupRows != null) {
currentTabGroupRows.appendChild(row);
} else {
row.setGroup(currentGroup);
rows.appendChild(row);
@ -641,7 +691,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
rows.appendChild(row);
headerRows.add(row);
currentGroup = null;
currentTabRows = null;
currentTabGroupRows = null;
} else if(X_AD_FieldGroup.FIELDGROUPTYPE_Tab.equals(field.getFieldGroupType())) {
// Create New Tab for FieldGroup
List<Tab> headerTabs = new ArrayList<Tab>();
@ -658,7 +708,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
headerTabs.add(tab);
Grid tabForm = new Grid();
tabForms.add(tabForm);
tabGroupForms.add(tabForm);
ZKUpdateUtil.setHflex(tabForm, "1");
ZKUpdateUtil.setHeight(tabForm, null);
tabForm.setVflex(false);
@ -696,7 +746,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
tp.appendChild(tabForm);
currentGroup = null;
currentTabRows = tabRows;
currentTabGroupRows = tabRows;
}
else
{
@ -710,7 +760,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
{
rowg.setOpen(false);
}
currentTabRows = null;
currentTabGroupRows = null;
currentGroup = rowg;
rows.appendChild(rowg);
headerRows.add(rowg);
@ -738,8 +788,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (numCols - actualxpos + 1 > 0)
row.appendCellChild(createSpacer(), numCols - actualxpos + 1);
// Tab Group vs Grid Group
if(currentTabRows != null) {
currentTabRows.appendChild(row);
if (currentTabGroupRows != null) {
currentTabGroupRows.appendChild(row);
} else {
row.setGroup(currentGroup);
rows.appendChild(row);
@ -899,8 +949,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
if (numCols - actualxpos + 1 > 0)
row.appendCellChild(createSpacer(), numCols - actualxpos + 1);
// Tab Group vs Grid Group
if(currentTabRows != null) {
currentTabRows.appendChild(row);
if (currentTabGroupRows != null) {
currentTabGroupRows.appendChild(row);
} else {
row.setGroup(currentGroup);
rows.appendChild(row);
@ -936,6 +986,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
switchRowPresentation();
}
/**
* @param field
* @return {@link WEditor} or null if not found
*/
private WEditor findEditor(GridField field) {
for(WEditor editor : editors) {
if (editor.getGridField() == field)
@ -944,6 +998,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return null;
}
/**
* load toolbar buttons from AD_ToolBarButton
*/
private void loadToolbarButtons() {
//get extra toolbar process buttons
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
*/
@Override
@ -987,6 +1044,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return;
}
//css animation for slide
if (form.getSclass() != null && form.getSclass().contains(SLIDE_RIGHT_OUT_CSS)) {
Executions.schedule(getDesktop(), e -> {
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
for(Grid tabForm: tabForms) {
for(Grid tabForm: tabGroupForms) {
List<Component> tabrows = tabForm.getRows().getChildren();
for (Component comp : tabrows)
{
@ -1217,6 +1275,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
LayoutUtils.removeSclass(SLIDE_RIGHT_IN_CSS, form);
}
/**
* echo set selected node event for tree
*/
private void echoDeferSetSelectedNodeEvent() {
if (getAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR) == null) {
setAttribute(ON_DEFER_SET_SELECTED_NODE_ATTR, Boolean.TRUE);
@ -1225,7 +1286,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
* @return String
* @return display logic
*/
@Override
public String getDisplayLogic()
@ -1234,7 +1295,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
* @return String
* @return title of tab
*/
@Override
public String getTitle()
@ -1244,6 +1305,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/**
* @param variableName
* @return value
*/
@Override
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
public int getRecord_ID()
@ -1280,7 +1342,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/**
* Is panel need refresh
* @return boolean
* @return true if GridTab need refresh
*/
@Override
public boolean isCurrent()
@ -1298,7 +1360,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
* Retrieve from db
* Retrieve from db.
* Delegate to {@link GridTab#query(boolean)}
*/
@Override
public void query()
@ -1311,9 +1374,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/**
* Retrieve from db
* @param onlyCurrentRows
* @param onlyCurrentDays
* @param maxRows
* @param onlyCurrentRows True to show only unprocessed or the one updated within x days (default is 1 day before today)
* @param onlyCurrentDays if > 0, filter records with created >= current_date - onlyCurrentDays
* @param maxRows if > 0, maximum number of rows to load
*/
@Override
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
public void resetDetailForNewParentRecord ()
@ -1364,7 +1428,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
* @return TreePanel
* @return ADTreePanel
*/
public ADTreePanel getTreePanel()
{
@ -1372,7 +1436,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
* @return TreePanel
* @return master, detail or both
*/
public String getTreeDisplayedOn()
{
@ -1434,7 +1498,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
*
* Delegate to {@link #focusToEditor(WEditor, boolean)}
* @param 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) {
if (event == SouthEvent.OPEN || event == SouthEvent.CLOSE) {
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() {
if (isMobile())
return false;
@ -1578,6 +1649,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return open;
}
/**
* @return height of detail pane from user preference
*/
private String heigthDetailPane() {
String height = null;
int windowId = getGridTab().getAD_Window_ID();
@ -1588,6 +1662,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return height;
}
/**
* @return width of tree panel from user preference (or default if no user preference)
*/
private String widthTreePanel() {
String width = null;
int windowId = getGridTab().getAD_Window_ID();
@ -1597,6 +1674,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return Util.isEmpty(width) ? DEFAULT_PANEL_WIDTH : width;
}
/**
* Navigate to a tree node
* @param value
*/
private void navigateTo(DefaultTreeNode<MTreeNode> value) {
MTreeNode treeNode = value.getData();
// We Have a TreeNode
@ -1654,7 +1735,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
Dialog.error(windowNo, msg);
}
}
//if (col >= 0)
//update UI state
if (!uiCreated)
createUI();
dynamicDisplay(col);
@ -1750,6 +1832,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
treePanel.initTree(AD_Tree_ID, windowNo);
}
}
//update list view
if (listPanel.isVisible()) {
listPanel.updateListIndex();
listPanel.dynamicDisplay(col);
@ -1760,6 +1844,10 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
}
/**
* delete tree node by recordId
* @param recordId
*/
private void deleteNode(int recordId) {
if (recordId <= 0) return;
@ -1780,6 +1868,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
}
/**
* add new tree node for current row
*/
private void addNewNode() {
if (gridTab.getRecord_ID() > 0) {
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) {
if (recordId <= 0) return;
@ -1921,15 +2016,15 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
static class ZoomListener implements EventListener<Event> {
private IZoomableEditor searchEditor;
private IZoomableEditor zoomableEditor;
ZoomListener(IZoomableEditor editor) {
searchEditor = editor;
zoomableEditor = editor;
}
public void onEvent(Event event) throws Exception {
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
public boolean isGridView() {
@ -1989,7 +2084,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
/**
*
* @return GridPanel
* @return {@link GridView}
*/
@Override
public GridView getGridView() {
@ -2015,6 +2110,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
}
/**
* Show detail pane
*/
private void attachDetailPane() {
if (formContainer.getSouth() != null) {
formContainer.getSouth().setVisible(true);
@ -2029,6 +2127,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
}
/**
* Hide detail pane
*/
private void detachDetailPane() {
if (formContainer.getSouth() != null) {
formContainer.getSouth().setVisible(false);
@ -2039,8 +2140,8 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
* Get all visible button editors
* @return List<WButtonEditor>
* Get all visible toolbar button editors
* @return List<Button>
*/
public List<Button> getToolbarButtons() {
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() {
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
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() {
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() {
SimpleTreeModel model = (SimpleTreeModel)(TreeModel<?>) treePanel.getTree().getModel();
boolean retValue = false;
@ -2203,6 +2307,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
return retValue;
}
/**
* @return true if value is shown in tree
*/
private boolean isValueDisplayed() {
SimpleTreeModel model = (SimpleTreeModel)(TreeModel<?>) treePanel.getTree().getModel();
boolean retValue = false;
@ -2233,7 +2340,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
/**
*
* Save user preference for this AD Window
* @param attribute
* @param value
*/
@ -2287,6 +2394,9 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer
}
};
/**
* @return true if client is mobile
*/
protected boolean 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() {
boolean isGroupTabVisible = false;

View File

@ -35,29 +35,40 @@ import org.zkoss.zul.Tree;
/**
*
* Tree panel for AD_Tab with HasTree=Y
* @author hengsin
*
*/
public class ADTreePanel extends Panel implements EventListener<Event>
{
/**
*
* generated serial id
*/
private static final long serialVersionUID = 2718257463734592729L;
/** event to expand/collapse all tree nodes **/
private static final String ON_EXPAND_MENU_EVENT = "onExpandMenu";
/** search/lookup panel for tree **/
private TreeSearchPanel pnlSearch;
private Tree tree;
/** ToolBarButton to expand or collapse all tree nodes **/
private ToolBarButton expandToggle; // Elaine 2009/02/27 - expand tree
private int m_windowno = -1;
private int m_tabno = -1;
private int AD_Tree_ID = -1;
/**
* default constructor
*/
public ADTreePanel()
{
init();
}
/**
* @param windowno
* @param tabno
*/
public ADTreePanel(int windowno, int tabno)
{
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 windowNo
*/
@ -74,6 +86,14 @@ public class ADTreePanel extends Panel implements EventListener<Event>
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)
{
if (this.AD_Tree_ID != AD_Tree_ID)
@ -89,6 +109,9 @@ public class ADTreePanel extends Panel implements EventListener<Event>
return false;
}
/**
* Layout panel
*/
private void init()
{
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() {
this.AD_Tree_ID = -1;
}

View File

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

View File

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

View File

@ -77,7 +77,7 @@ import org.zkoss.zul.Toolbarbutton;
import org.zkoss.zul.impl.LabelImageElement;
/**
*
* Toolbar of AD_Window
* @author <a href="mailto:agramdass@gmail.com">Ashley G Ramdass</a>
* @date Feb 25, 2007
* @version $Revision: 0.10 $
@ -86,21 +86,29 @@ import org.zkoss.zul.impl.LabelImageElement;
* <li>FR [ 2076330 ] Add new methods in CWindowToolbar class
*/
public class ADWindowToolbar extends FToolbar implements EventListener<Event>
{
{
/**
*
* generated serial id
*/
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";
@Deprecated(forRemoval = true, since = "11")
public static final String MNITMPREFIX = "Mnitm";
private static final CLogger log = CLogger.getCLogger(ADWindowToolbar.class);
/** Search messages using translation */
private String m_sNew;
/** translated message for new query label (default for en is "** New Query **") */
private String m_sNew;
/** combobox to select user query **/
private Combobox fQueryName;
private MUserQuery[] userQueries;
private MUserQuery selectedUserQuery;
@ -136,25 +144,31 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
private ToolBarButton btnProcess;
private ToolBarButton btnQuickForm;
/** button to open overflow popup for toolbar buttons with IsShowMore=Y (for non-mobile client) **/
private ToolBarButton btnShowMore;
/** Button Name:ToolBarButton. Map for all buttons **/
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 ToolBarButton btnExit;
/** All register toolbar listener **/
private ArrayList<ToolbarListener> listeners = new ArrayList<ToolbarListener>();
/** current ON_Click event that's being handle **/
private Event event;
/** shortcut key map without ctrl and alt **/
private Map<Integer, ToolBarButton> keyMap = new HashMap<Integer, ToolBarButton>();
/** alt+key shortcut map **/
private Map<Integer, ToolBarButton> altKeyMap = new HashMap<Integer, ToolBarButton>();
/** ctrl+key shortcut map **/
private Map<Integer, ToolBarButton> ctrlKeyMap = new HashMap<Integer, ToolBarButton>();
/** list of custom toolbar button (IsCustomization=Y) **/
private List<ToolbarCustomButton> toolbarCustomButtons = new ArrayList<ToolbarCustomButton>();
/** Restriction list for window, loaded from AD_ToolBarButtonRestrict **/
private List<String> restrictionList;
/** List of toolbar button with IsAdvanced=Y **/
private List<String> advancedList;
// 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 int windowNo = 0;
/** previous key event time in ms **/
private long prevKeyEventTime = 0;
/**
* Previous key event.
* Use together with prevKeyEventTime to detect double fire of key event from browser
*/
private KeyEvent prevKeyEvent;
// Maintain hierarchical Quick form by its parent-child tab while open leaf
// tab once & dispose and doing same action
private int quickFormTabHrchyLevel = 0;
private A overflowButton;
/**
* Maintain hierarchical Quick form by its parent-child tab while open leaf
* tab once & dispose and doing same action
*/
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;
/** Popup for overflow/IsShowMore=Y toolbar buttons. Use for both mobile and desktop client. **/
private Popup overflowPopup;
/** width of toolbar from previous ON_AFTER_SIZE event **/
private int prevWidth;
/** AD Window content part that own this toolbar **/
private AbstractADWindowContent windowContent;
/** Last Modifier of Action Event */
// public int lastModifiers;
//
/**
* default constructor
*/
public ADWindowToolbar()
{
this(null, 0);
}
/**
* @param windowContent
* @param windowNo
*/
public ADWindowToolbar(AbstractADWindowContent windowContent, int windowNo) {
this.windowContent = windowContent;
setWindowNo(windowNo);
@ -200,6 +225,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}
}
/**
* Init components
*/
private void init()
{
LayoutUtils.addSclass("adwindow-toolbar", this);
@ -292,6 +320,8 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
if (!ClientInfo.isMobile())
overflows = new ArrayList<ToolBarButton>();
//Window toolbar buttons from AD_ToolBarButton
MToolBarButton[] officialButtons = MToolBarButton.getToolbarButtons("W", null);
for (MToolBarButton button : officialButtons) {
if (!button.isActive() || !hasAccess(BTNPREFIX+button.getComponentName())) {
@ -374,7 +404,13 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
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)
{
ToolBarButton btn = new ToolBarButton("");
@ -415,11 +451,21 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
return btn;
}
/**
* Get ToolBarButton by name
* @param name
* @return {@link ToolBarButton} or null
*/
public ToolBarButton getButton(String name)
{
return buttons.get(name);
}
/**
* Get ToolBarButton by name
* @param name
* @return {@link LabelImageElement} or null
*/
public LabelImageElement getToolbarItem(String 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_Z = 0x5A;
/**
* Configure shortcut key for each button
*/
private void configureKeyMap()
{
altKeyMap.put(VK_H, btnHelp);
@ -473,6 +522,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
altKeyMap.put(VK_L, btnCustomize);
}
/**
* Add separator/spacer between button
*/
protected void addSeparator()
{
Space s = new Space();
@ -481,16 +533,25 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
this.appendChild(s);
}
/**
* Add ToolbarListener
* @param toolbarListener
*/
public void addListener(ToolbarListener toolbarListener)
{
listeners.add(toolbarListener);
}
/**
* Remove ToolbarListener
* @param toolbarListener
*/
public void removeListener(ToolbarListener toolbarListener)
{
listeners.remove(toolbarListener);
}
@Override
public void onEvent(Event event)
{
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) {
this.event = event;
String compName;
@ -590,127 +656,216 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
this.event = null;
}
/**
* Enable/disable buttons for navigation between parent and detail tab
* @param enabled
*/
public void enableTabNavigation(boolean 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)
{
this.btnParentRecord.setDisabled(!enableParent);
this.btnDetailRecord.setDisabled(!enableDetail);
}
/**
* Enable/disable Refresh button
* @param enabled
*/
public void enableRefresh(boolean enabled)
{
this.btnRefresh.setDisabled(!enabled);
}
/**
* Enable/disable Save button
* @param enabled
*/
public void enableSave(boolean enabled)
{
this.btnSave.setDisabled(!enabled);
this.btnSaveAndCreate.setDisabled(!(isNewEnabled() || isSaveEnable()));
}
/**
* @return true if Save button is enable
*/
public boolean isSaveEnable() {
return !btnSave.isDisabled();
}
// public void enableExit(boolean enabled)
// {
// this.btnExit.setDisabled(!enabled);
// }
/**
* Enable/disable Delete button
* @param enabled
*/
public void enableDelete(boolean enabled)
{
this.btnDelete.setDisabled(!enabled);
}
/**
* @return true if Delete button is enable
*/
public boolean isDeleteEnable()
{
return !btnDelete.isDisabled();
}
/**
* @return true if New button is enable
*/
public boolean isNewEnabled() {
return !btnNew.isDisabled();
}
/**
* Enable/disable Ignore/Undo button
* @param enabled
*/
public void enableIgnore(boolean enabled)
{
this.btnIgnore.setDisabled(!enabled);
}
/**
* Enable/disable New button
* @param enabled
*/
public void enableNew(boolean enabled)
{
this.btnNew.setDisabled(!enabled);
this.btnSaveAndCreate.setDisabled(!(isNewEnabled() || isSaveEnable()));
}
/**
* Enable/disable Copy/Duplicate button
* @param enabled
*/
public void enableCopy(boolean enabled)
{
this.btnCopy.setDisabled(!enabled);
}
/**
* Enable/disable Attachment button
* @param enabled
*/
public void enableAttachment(boolean enabled)
{
this.btnAttachment.setDisabled(!enabled);
}
/**
* Enable/disable Chat button
* @param enabled
*/
public void enableChat(boolean enabled)
{
this.btnChat.setDisabled(!enabled);
}
/**
* Enable/disable Print button
* @param enabled
*/
public void enablePrint(boolean enabled)
{
this.btnPrint.setDisabled(!enabled);
}
/**
* Enable/disable Report button
* @param enabled
*/
public void enableReport(boolean enabled)
{
this.btnReport.setDisabled(!enabled);
}
/**
* Enable/disable Find/Query button
* @param enabled
*/
public void enableFind(boolean enabled)
{
this.btnFind.setDisabled(!enabled);
}
/**
* Enable/disable Toggle button
* @param enabled
*/
public void enableGridToggle(boolean enabled)
{
btnGridToggle.setDisabled(!enabled);
}
/**
* Enable/disable Customize Grid button
* @param enabled
*/
public void enableCustomize(boolean enabled)
{
btnCustomize.setDisabled(!enabled);
}
/**
* Enable/disable Archive button
* @param enabled
*/
public void enableArchive(boolean enabled)
{
btnArchive.setDisabled(!enabled);
}
/**
* Enable/disable Zoom Across button
* @param enabled
*/
public void enableZoomAcross(boolean enabled)
{
btnZoomAcross.setDisabled(!enabled);
}
/**
* Enable/disable Active Workflows button
* @param enabled
*/
public void enableActiveWorkflows(boolean enabled)
{
btnActiveWorkflows.setDisabled(!enabled);
}
/**
* Enable/disable Requests button
* @param enabled
*/
public void enableRequests(boolean enabled)
{
btnRequests.setDisabled(!enabled);
}
/**
* Enable/disable Quick Form button
* @param enabled
*/
public void enableQuickForm(boolean enabled)
{
btnQuickForm.setDisabled(!enabled);
}
/**
* Turn on/off Lock button (Pressed=On, Not Pressed=Off)
* @param enabled
*/
public void lock(boolean 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)
{
this.btnPostIt.setDisabled(!enabled);
}
/**
* Enable/disable the label button
* Enable/disable Label record button
* @param enabled
*/
public void enableLabel(boolean enabled)
@ -749,11 +908,18 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
this.btnLabel.setDisabled(!enabled);
}
/**
* @return ON_Click event that's being handle
*/
public Event getEvent()
{
return event;
}
/**
* Handle shortcut key event
* @param keyEvent
*/
private void onCtrlKeyEvent(KeyEvent keyEvent) {
if (windowContent != null && windowContent.isBlock())
return;
@ -810,6 +976,11 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
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)
{
if (btn != null) {
@ -823,7 +994,7 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}
/**
*
* Make all toolbar buttons visible
* @param visible
*/
public void setVisibleAll(boolean visible)
@ -835,7 +1006,6 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}
/**
*
* @param buttonName
* @param visible
*/
@ -849,7 +1019,6 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}
/**
*
* @param 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
*/
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
*/
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
*/
public void enableCSVImport(boolean b) {
@ -885,6 +1054,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
private boolean ToolBarMenuRestictionLoaded = false;
/**
* @param buttonName
* @return true if current login user has access to buttonName
*/
private boolean hasAccess(String buttonName) {
ADWindow adwindow = ADWindow.get(windowNo);
if (restrictionList == null)
@ -904,6 +1077,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
return true;
}
/**
* Initialise the accessibility state of toolbar buttons
*/
public void updateToolbarAccess() {
if (ToolBarMenuRestictionLoaded)
return;
@ -921,19 +1097,31 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
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,?)";
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) {
if (btnProcess != null) {
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() {
List<Toolbarbutton> customButtons = new ArrayList<Toolbarbutton>();
for(ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons) {
@ -995,6 +1183,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
readOnlyLogic();
}
/**
* Call {@link ToolbarCustomButton#pressedLogic()}
*/
private void pressedLogic()
{
for (ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons)
@ -1003,6 +1194,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}
}
/**
* Call {@link ToolbarCustomButton#readOnlyLogic()}
*/
private void readOnlyLogic()
{
for (ToolbarCustomButton toolbarCustomBtn : toolbarCustomButtons)
@ -1027,6 +1221,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}
}
/**
* Init for mobile client only.
*/
private void mobileInit() {
LayoutUtils.addSclass("mobile", this);
addEventListener("onOverflowButton", evt -> onOverflowButton(evt));
@ -1064,6 +1261,9 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
addCallback(AFTER_PAGE_ATTACHED, t -> afterPageAttached());
}
/**
* Mobile specific handling for AFTER_PAGE_ATTACHED callback.
*/
private void afterPageAttached() {
Component p = getParent();
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) {
int width = evt.getWidth();
if (width != prevWidth) {
prevWidth = width;
if (overflowButton != null)
overflowButton.detach();
if (mobileOverflowButton != null)
mobileOverflowButton.detach();
if (overflowPopup != null)
overflowPopup.detach();
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) {
overflows = new ArrayList<>();
String uuid = (String) evt.getData();
@ -1113,11 +1321,15 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
for (ToolBarButton toolbarButton : mobileShowMoreButtons)
overflows.add(toolbarButton);
if (overflows.size() > 0) {
createOverflowButton();
createOverflowButtonForMobile();
populateOverflowPopup();
}
}
/**
* Populate overflow popup.
* Use for both desktop and mobile client.
*/
private void populateOverflowPopup() {
boolean vertical = !ClientInfo.isMobile() && MSysConfig.getBooleanValue(MSysConfig.ZK_TOOLBAR_SHOW_MORE_VERTICAL, true, Env.getAD_Client_ID(Env.getCtx()));
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() {
this.appendChild(btnShowMore);
btnShowMore.setDisabled(false);
@ -1173,8 +1389,12 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
populateOverflowPopup();
}
/**
* Show overflow popup after {@link #btnShowMore}.
* For desktop client only.
*/
private void onShowMore() {
Long ts = (Long) overflowPopup.removeAttribute("popup.close");
Long ts = (Long) overflowPopup.removeAttribute(POPUP_CLOSE_TIMESTAMP_ATTR);
if (ts != null) {
if (System.currentTimeMillis() - ts.longValue() < 500) {
return;
@ -1183,30 +1403,37 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
overflowPopup.open(btnShowMore, "after_end");
}
private void createOverflowButton() {
overflowButton = new A();
overflowButton.setTooltiptext(Msg.getMsg(Env.getCtx(), "ShowMore"));
overflowButton.setIconSclass("z-icon-ShowMore");
overflowButton.setSclass("font-icon-toolbar-button toolbar-button mobile-overflow-link");
appendChild(overflowButton);
/**
* Create show more button for mobile client
*/
private void createOverflowButtonForMobile() {
mobileOverflowButton = new A();
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();
appendChild(overflowPopup);
overflowButton.addEventListener(Events.ON_CLICK, e -> {
Long ts = (Long) overflowPopup.removeAttribute("popup.close");
mobileOverflowButton.addEventListener(Events.ON_CLICK, e -> {
Long ts = (Long) overflowPopup.removeAttribute(POPUP_CLOSE_TIMESTAMP_ATTR);
if (ts != null) {
if (System.currentTimeMillis() - ts.longValue() < 500) {
return;
}
}
overflowPopup.open(overflowButton, "after_end");
overflowPopup.open(mobileOverflowButton, "after_end");
});
}
/**
* Create overflow popup.
* For both desktop and mobile client.
*/
private void newOverflowPopup() {
overflowPopup = new Popup();
overflowPopup.addEventListener(Events.ON_OPEN, (OpenEvent oe) -> {
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]);
for (Component child : childrens) {
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() {
if (this.getPage() != null) {
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) {
if (getButton(buttonName) != null)
getButton(buttonName).setPressed(pressed);
}
/**
* @return
* @return parent tab level for quick form
*/
public int getQuickFormTabHrchyLevel()
{
@ -1246,6 +1482,11 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
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) {
if (AEnv.getOrSetExecutionAttribute(getClass().getName()+".refreshUserQuery")) {
return;
@ -1269,6 +1510,10 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
fQueryName.setSelectedIndex(0);
}
/**
* Set selected user query
* @param AD_UserQuery_ID
*/
public void setSelectedUserQuery(int AD_UserQuery_ID) {
for (MUserQuery userQuery : userQueries) {
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) {
this.selectedUserQuery = selectedUserQuery;
if (selectedUserQuery != null)
fQueryName.setValue(selectedUserQuery.getName());
}
/**
* @return AD_UserQuery_ID of selected user query
*/
public int getAD_UserQuery_ID() {
if (selectedUserQuery == null)
return 0;
@ -1291,8 +1543,8 @@ public class ADWindowToolbar extends FToolbar implements EventListener<Event>
}
/**
* Init Default Query in Window Toolbar
* @return true if initialized
* Set selected user query to first default user query (if any)
* @return true if there's a default user query
*/
public boolean initDefaultQuery() {
if(userQueries != null) {

View File

@ -33,7 +33,7 @@ import org.compiere.util.Evaluator;
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:hengsin@gmail.com">Low Heng Sin</a>
* @date Feb 25, 2007
@ -46,16 +46,21 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
/** List of dependent Variables */
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>();
/** Parent part, the content part of AD Window **/
protected AbstractADWindowContent adWindowPanel;
/**
* default constructor
*/
public AbstractADTabbox()
{
}
/**
* Add Tab
* Add new tab(AD_Tab).
* Delegate to {@link #doAddTab(GridTab, IADTabpanel)}
* @param gTab grid tab model
* @param tabPanel
*/
@ -76,7 +81,8 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
}// addTab
/**
* handle add tab to tabbox
* Handle add new tab to UI.
* Override to implement add new tab to UI.
* @param tab
* @param tabPanel
*/
@ -84,19 +90,24 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
/**
* @param index of tab panel
* @return true if enable
* @return true if enable, false otherwise
*/
public boolean isEnabledAt(int index)
{
return true;
}// 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)
{
boolean display = Evaluator.evaluateLogic(newTab, logic);
boolean display = Evaluator.evaluateLogic(tabPanel, logic);
if (!display)
{
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 newIndex
* @return true if successfully switch to newIndex
*/
@Override
public boolean updateSelectedIndex(int oldIndex, int newIndex)
{
IADTabpanel newTab = tabPanelList.get(newIndex);
@ -136,6 +149,11 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
return canJump;
}
/**
* Prepare environment context for newTab.
* @param newIndex
* @param newTab
*/
private void prepareContext(int newIndex, IADTabpanel newTab) {
//update context
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 newIndex
*/
protected abstract void doTabSelectionChanged(int oldIndex, int newIndex);
/**
* @param index tab index
* @return true if tab is visible
* Evaluate display logic
* @param index
* @return true if visible, false otherwise
*/
public boolean isDisplay(int index) {
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 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) {
return canNavigateTo(fromIndex, toIndex, false);
}
/**
*
* @param fromIndex
* @param toIndex
* @param checkRecordID true to validate record id of fromIndex tab
* @return true if can navigate to toIndex tab
*/
/**
*
* @param fromIndex
* @param toIndex
* @param checkRecordID true to validate fromIndex has a valid record id
* @return true if can change selected tab from fromIndex to toIndex
*/
public boolean canNavigateTo(int fromIndex, int toIndex, boolean checkRecordID) {
IADTabpanel newTab = tabPanelList.get(toIndex);
if (newTab instanceof ADTabpanel)
@ -281,7 +303,7 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
}
/**
*
* Get break crumb path
* @return full path
*/
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
*/
public void evaluate (DataStatusEvent e)
@ -335,7 +358,7 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
} // 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();
@ -366,8 +389,10 @@ public abstract class AbstractADTabbox extends AbstractUIPart implements IADTabb
/**
* Set newIndex as selected tab
* Delegate to {@link #updateSelectedIndex(int, int)}
* @param newIndex
*/
@Override
public void setSelectedIndex(int newIndex) {
int oldIndex = getSelectedIndex();
updateSelectedIndex(oldIndex, newIndex);

View File

@ -52,13 +52,20 @@ import org.zkoss.zul.Menuitem;
import org.zkoss.zul.Window;
/**
* Bread crumb component for AD Window
* @author hengsin
*
*/
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";
/**
* 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";
/**
@ -68,29 +75,39 @@ public class BreadCrumb extends Div implements EventListener<Event> {
private static final String BTNPREFIX = "Btn";
/** west layout for paths to a tab (for e.g "Business Partner > Location") **/
private Hlayout layout;
/** record navigation buttons **/
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;
@SuppressWarnings("unused")
private int windowNo;
/** BtnName:ToolBarButton. Map of all toolbar buttons. **/
private HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>();
/** Last DataStatusEvent from {@link AbstractADWindowContent#dataStatusChanged(DataStatusEvent)} **/
private DataStatusEvent m_dse;
/** Last data status text from {@link AbstractADWindowContent#dataStatusChanged(DataStatusEvent)} **/
private String m_text;
/** register ToolbarListener **/
private ToolbarListener toolbarListener;
/** east layout for record navigation buttons **/
private Hlayout toolbarContainer;
/** popup for link to other tabs at same level **/
protected Menupopup linkPopup;
private GridTab m_gridTab;
/** AD Window content part that own this bread crumb **/
private AbstractADWindowContent windowContent;
/**
@ -140,7 +157,6 @@ public class BreadCrumb extends Div implements EventListener<Event> {
}
/**
*
* @param listener
*/
public void setToolbarListener(ToolbarListener listener) {
@ -148,10 +164,10 @@ public class BreadCrumb extends Div implements EventListener<Event> {
}
/**
*
* @param label
* @param id
* @param clickable
* Add path to tab
* @param label path label
* @param id path id
* @param clickable true to add clickable {@link BreadCrumbLink} false to add text label
*/
public void addPath(String label, String id, boolean clickable) {
if (clickable) {
@ -181,7 +197,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
}
/**
*
* Get parent BreadCrumbLinks
* @return list of parent links
*/
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
* @param links
* @param links Label:TabIndex map
*/
public void addLinks(LinkedHashMap<String, String> 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() {
layout.getChildren().clear();
@ -395,6 +411,13 @@ public class BreadCrumb extends Div implements EventListener<Event> {
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)
{
ToolBarButton btn = new ToolBarButton("");
@ -425,6 +448,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
}
/**
* Set record info text
* @param 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 gridTab
*/
@ -471,7 +496,7 @@ public class BreadCrumb extends Div implements EventListener<Event> {
}
/**
*
* Set visibility of record navigation toolbar
* @param 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() {
return !btnPrevious.isDisabled();
}
/**
* @return true if next button is enable
*/
public boolean isNextEnabled() {
return !btnNext.isDisabled();
}
/**
* @return next ToolBarButton
*/
public ToolBarButton getNextButton() {
return btnNext;
}
/**
* @return previous ToolBarButton
*/
public ToolBarButton getPreviousButton() {
return btnPrevious;
}
/**
* @return true if path/link is empty
*/
public boolean isEmpty() {
return layout == null || layout.getChildren().isEmpty();
}

View File

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

View File

@ -55,7 +55,10 @@ import org.zkoss.zul.RowRenderer;
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:hengsin@gmail.com">Low Heng Sin</a>
* @date Feb 25, 2007
@ -63,27 +66,45 @@ import org.zkoss.zul.Vlayout;
*/
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";
/** after tab selection change event **/
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";
/** tab selection change event **/
public static final String ON_SELECTION_CHANGED_EVENT = "onSelectionChanged";
/** List of all tab **/
private List<ADTabListModel.ADTabLabel> tabLabelList = new ArrayList<ADTabListModel.ADTabLabel>();
/** List of all tab panel **/
private List<IADTabpanel> tabPanelList = new ArrayList<IADTabpanel>();
/** main layout component **/
private Vlayout layout;
/** tab selection change listener **/
private EventListener<Event> selectionListener;
/** {@link IADTabpanel} instance for selected tab **/
private IADTabpanel headerTab;
/** Index of selected tab **/
private int selectedIndex = 0;
/**
* default constructor
*/
public CompositeADTabbox(){
}
@ -221,6 +242,9 @@ public class CompositeADTabbox extends AbstractADTabbox
}
}
/**
* Delete current row of selected detail tab
*/
private void onDelete() {
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) {
if (tabPanel == null || tabPanel.getGridTab() == null) return;
@ -283,6 +311,10 @@ public class CompositeADTabbox extends AbstractADTabbox
return detailPane;
}
/**
* defer execution of adTabPanel.focus()
* @param adTabPanel
*/
private void focusToTabpanel(IADTabpanel adTabPanel ) {
if (adTabPanel != null && adTabPanel instanceof HtmlBasedComponent) {
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 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
protected Component doCreatePart(Component parent)
{
@ -359,8 +396,9 @@ public class CompositeADTabbox extends AbstractADTabbox
breadCrumb.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
@Override
public void onEvent(Event event) throws Exception {
//send tab selection change event
int oldIndex = selectedIndex;
if (event.getTarget() instanceof BreadCrumbLink) {
if (event.getTarget() instanceof BreadCrumbLink) {
BreadCrumbLink link = (BreadCrumbLink) event.getTarget();
int newIndex = Integer.parseInt(link.getPathId());
@ -381,8 +419,8 @@ public class CompositeADTabbox extends AbstractADTabbox
@Override
protected void doAddTab(GridTab gTab, IADTabpanel tabPanel) {
ADTabListModel.ADTabLabel tabLabel = new ADTabListModel.ADTabLabel(gTab.getName(), gTab.getTabLevel(),gTab.getDescription(),
gTab.getWindowNo(),gTab.getAD_Tab_ID());
ADTabListModel.ADTabLabel tabLabel = new ADTabListModel.ADTabLabel(gTab.getName(), gTab.getTabLevel(), gTab.getDescription(),
gTab.getWindowNo(), gTab.getAD_Tab_ID());
tabLabelList.add(tabLabel);
tabPanelList.add(tabPanel);
@ -480,6 +518,7 @@ public class CompositeADTabbox extends AbstractADTabbox
});
}
//add to header or detail pane
if (layout.getChildren().isEmpty()) {
layout.appendChild(tabPanel);
headerTab = tabPanel;
@ -516,6 +555,9 @@ public class CompositeADTabbox extends AbstractADTabbox
return b;
}
/**
* Call {@link ADTabpanel#activateDetailIfVisible()}
*/
private void activateDetailIfVisible() {
if (headerTab instanceof ADTabpanel) {
((ADTabpanel)headerTab).activateDetailIfVisible();
@ -595,7 +637,7 @@ public class CompositeADTabbox extends AbstractADTabbox
//set state
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().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
*/
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[]>();
int tabIndex = -1;
int currentLevel = headerTab.getTabLevel();
@ -633,7 +677,7 @@ public class CompositeADTabbox extends AbstractADTabbox
if (detailPane == null) {
detailPane = createDetailPane();
}
detailPane.setAttribute("detailpane.tablist", list);
detailPane.setAttribute(DETAILPANE_TABLIST_ATTR, list);
ZKUpdateUtil.setVflex(detailPane, "true");
if (headerTab.getDetailPane() == null) {
@ -658,7 +702,8 @@ public class CompositeADTabbox extends AbstractADTabbox
//setup tabs of detail pane
if (detailPane != null) {
@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()) {
int currentLevel = headerTab.getTabLevel();
for (Object[] value : list) {
@ -747,6 +792,7 @@ public class CompositeADTabbox extends AbstractADTabbox
private void updateBreadCrumb() {
BreadCrumb breadCrumb = getBreadCrumb();
breadCrumb.reset();
//add parent path
if (selectedIndex > 0) {
List<ADTabLabel> parents = new ArrayList<ADTabListModel.ADTabLabel>();
List<Integer> parentIndex = new ArrayList<Integer>();
@ -771,6 +817,8 @@ public class CompositeADTabbox extends AbstractADTabbox
if (!breadCrumb.isVisible())
breadCrumb.setVisible(true);
//Links for other child tabs at same level
//Tab Index:Tab Label
LinkedHashMap<String, String> links = new LinkedHashMap<String, String>();
int parentIndex = 0;
if (headerTab.getTabLevel() > 1) {
@ -823,6 +871,9 @@ public class CompositeADTabbox extends AbstractADTabbox
}
}
/**
* @return {@link BreadCrumb}
*/
private BreadCrumb getBreadCrumb() {
ADWindowContent window = (ADWindowContent) adWindowPanel;
BreadCrumb breadCrumb = window.getBreadCrumb();
@ -844,6 +895,9 @@ public class CompositeADTabbox extends AbstractADTabbox
return null;
}
/**
* Notify selected detail tab after data status change of header tab
*/
class SyncDataStatusListener implements DataStatusListener {
private IADTabpanel tabPanel;
@ -867,7 +921,7 @@ public class CompositeADTabbox extends AbstractADTabbox
IADTabpanel detailTab = getSelectedDetailADTabpanel();
if (detailTab != null) {
//check data action
//check is data action from detail tab
String uuid = (String) execution.getAttribute(CompositeADTabbox.class.getName()+".dataAction");
if (uuid != null && uuid.equals(detailTab.getUuid()) && detailTab.getGridTab().isCurrent()) {
//refresh current row
@ -909,7 +963,6 @@ public class CompositeADTabbox extends AbstractADTabbox
}
/**
*
* @return true if selected detail tab have been activated
*/
public boolean isDetailActivated() {
@ -1074,6 +1127,10 @@ public class CompositeADTabbox extends AbstractADTabbox
}
}
/**
* force invalidate of tabPanel
* @param tabPanel
*/
private void invalidateTabPanel(IADTabpanel tabPanel) {
Center center = findCenter(tabPanel.getGridView());
if (center != null)
@ -1082,6 +1139,11 @@ public class CompositeADTabbox extends AbstractADTabbox
tabPanel.invalidate();
}
/**
* Find {@link Center} that own gridView
* @param gridView
* @return {@link Center}
*/
private Center findCenter(GridView gridView) {
if (gridView == null)
return null;

View File

@ -81,8 +81,10 @@ import org.zkoss.zul.Tabs;
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
*
*/
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";
/** Boolean execution attribute to indicate tabbox is handling ON_SELECT event **/
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";
/** Attribute use by {@link #messageContainers} to hold 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 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 TOGGLE_IMAGE = "images/Multi16.png";
/** Timestamp for previous key event **/
private long prevKeyEventTime = 0;
/**
* Previous KeyEvent reference.
* Use together with {@link #prevKeyEventTime} to detect double firing of key event by browser.
*/
private KeyEvent prevKeyEvent;
/** tabbox for AD_Tabs **/
private Tabbox tabbox;
/** Registered event listener for DetailPane events **/
private EventListener<Event> eventListener;
/** AD_Tab_ID:Hbox. Message (status, error) container for each tab. **/
private Map<Integer, Hbox> messageContainers = new HashMap<Integer, Hbox>();
/** content for message popup **/
private Div msgPopupCnt;
/** message popup window **/
private Window msgPopup;
/** last selected tab index **/
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";
/** on delete event for selected tab **/
public static final String ON_DELETE_EVENT = "onDelete";
/** on new event for selected tab **/
public static final String ON_NEW_EVENT = "onNew";
/** event to edit current row of selected tab **/
public static final String ON_EDIT_EVENT = "onEdit";
/** on save event for selected tab **/
public static final String ON_SAVE_EVENT = "onSave";
/** on quick form event for selected tab **/
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";
/**
@ -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 tabPanel
* @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 tabPanel
* @param tabLabel
@ -253,7 +282,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
*
* Add IADTabpanel to tabbox
* @param tabPanel
* @param tabLabel
*/
@ -262,7 +291,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
*
* Add IADTabpanel to tabbox
* @param tabPanel
* @param tabLabel
* @param enabled
@ -284,6 +313,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
tab.addEventListener(Events.ON_CLICK, new EventListener<Event>() {
@Override
public void onEvent(Event event) throws Exception {
//click on tab title trigger edit of current row
Tab tab = (Tab) event.getTarget();
if (!tab.isSelected())
return;
@ -313,8 +343,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
Tabpanel tp = new Tabpanel();
tabpanels.appendChild(tp);
ToolBar toolbar = tp.getToolbar();
//setup toolbar
ToolBar toolbar = tp.getToolbar();
HashMap<String, ToolBarButton> buttons = new HashMap<String, ToolBarButton>();
ToolBarButton button = new ToolBarButton();
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");
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);
for (MToolBarButton toolbarButton : officialButtons) {
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();
messageContainer.setPack("end");
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
*/
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
*/
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
*/
public void setEventListener(EventListener<Event> listener) {
@ -616,7 +649,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
/**
* @param index
* @return adtabpanel at index
* @return IADTabpanel at index
*/
public IADTabpanel getADTabpanel(int index) {
if (index < 0 || index >= tabbox.getTabpanels().getChildren().size())
@ -631,8 +664,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
*
* @return selected adtabpanel
* @return selected IADTabpanel
*/
public IADTabpanel getSelectedADTabpanel() {
org.zkoss.zul.Tabpanel selectedPanel = tabbox.getSelectedPanel();
@ -646,15 +678,14 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
*
* @return {@link Tabpanel}
* @return selected {@link Tabpanel}
*/
public Tabpanel getSelectedPanel() {
return (Tabpanel) tabbox.getSelectedPanel();
}
/**
*
* Set status and error text for selected tab.
* @param status
* @param error
*/
@ -676,6 +707,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
messageContainer.getChildren().clear();
//store in attribute for retrieval in ON_CLICK event
messageContainer.setAttribute(STATUS_ERROR_ATTRIBUTE, error);
messageContainer.setAttribute(STATUS_TEXT_ATTRIBUTE, status);
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) {
if (statusText == null)
return "";
@ -724,6 +761,11 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
return statusText.substring(0, 80);
}
/**
* Shorten notification text to a more presentable length.
* @param statusText
* @return shorten notification text
*/
private String buildNotificationText(String statusText) {
if (statusText == null)
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) {
Text t = new Text(status);
msgPopupCnt.getChildren().clear();
msgPopupCnt.appendChild(t);
}
/**
* Show notification popup using Clients.showNotification
* @param error
* @param msg
*/
private void showPopup(boolean error, String msg) {
Clients.showNotification(buildNotificationText(msg), "error", findTabpanel(this), "at_pointer", 3500, true);
}
/**
* Create message popup window
*/
private void createPopup() {
msgPopupCnt = new Div();
ZKUpdateUtil.setVflex(msgPopupCnt, "1");
msgPopup = new Window();
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);
}
/**
* Update state of Process toolbar button.
*/
private void updateProcessToolbar() {
int index = getSelectedIndex();
if (index < 0 || index >= getTabcount()) return;
@ -934,8 +990,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
* Edit current record
* @param formView
* Edit current record of selected tab.
* 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
*/
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() {
int index = tabbox.getSelectedIndex();
@ -972,7 +1029,6 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
*
* @param tabIndex
* @return true if tab at tabIndex is visible
*/
@ -996,7 +1052,6 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
*
* @param tabIndex
* @param enabled
*/
@ -1009,7 +1064,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
* disable toolbar
* Disable all toolbar buttons
*/
public void disableToolbar() {
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) {
Component parent = comp.getParent();
while (parent != null) {
@ -1037,7 +1097,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
}
/**
* add new record
* add new row
* @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_O = 0x4F;
private static final int VK_Q = 0x51;
/**
* Handle shortcut key event
* @param keyEvent
*/
private void onCtrlKeyEvent(KeyEvent keyEvent) {
ToolBarButton btn = null;
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
* @author hengsin
*
* Custom {@link org.adempiere.webui.component.Tabpanel} implementation for DetailPane.
*/
public static class Tabpanel extends org.adempiere.webui.component.Tabpanel {
/**
*
* generated serial id
*/
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
*/
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
*/
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() {
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() {
@ -1257,6 +1319,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
return list;
}
/**
* Create overflow button (show more) for mobile client.
*/
private void createOverflowButton() {
overflowButton = new A();
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() {
overflowPopup = new Popup();
overflowPopup.setHflex("min");
@ -1291,7 +1359,7 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
private static class RecordToolbar extends Hlayout {
/**
*
* generated serial id
*/
private static final long serialVersionUID = 5024630043211194429L;
private ToolBarButton btnFirst;
@ -1301,6 +1369,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
private ToolBarButton btnLast;
private GridTab gridTab;
/**
* @param gridTab
*/
private RecordToolbar(GridTab gridTab) {
this.gridTab = gridTab;
btnFirst = createButton("First", "First", "First");
@ -1350,6 +1421,13 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
this.setValign("middle");
}
/**
* Create toolbar button
* @param name
* @param image
* @param tooltip
* @return {@link ToolBarButton}
*/
private ToolBarButton createButton(String name, String image, String tooltip)
{
ToolBarButton btn = new ToolBarButton("");
@ -1372,6 +1450,9 @@ public class DetailPane extends Panel implements EventListener<Event>, IdSpace {
return btn;
}
/**
* Dynamic update state of toolbar buttons
*/
private void dynamicDisplay() {
int rowCount = gridTab.getRowCount();
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> {
/** 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";
//styles for grid cell
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_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 GridTab gridTab;
private int windowNo;
/** Sync field editor changes to GridField **/
private GridTabDataBinder dataBinder;
/** field editors **/
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 Paging paging;
/** internal listener for row event **/
private RowListener rowListener;
/** Grid that own this renderer **/
private Grid grid = null;
/** GridView that uses this renderer **/
private GridView gridPanel = null;
/** current focus row **/
private Row currentRow;
/** values of current row. updated in {@link #render(Row, Object[], int)}. **/
private Object[] currentValues;
/** true if currrent row is in edit mode **/
private boolean editing = false;
/** index of current row **/
private int currentRowIndex = -1;
/** AD window content part that own this renderer **/
private AbstractADWindowContent m_windowPanel;
/** internal listener for button ActionEvent **/
private ActionListener buttonListener;
/**
* Flag detect this view has customized column or not
@ -112,6 +137,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
/** DefaultFocusField */
private WEditor defaultFocusField = null;
/** editor configuration for readonly field editor **/
private final static IEditorConfiguration readOnlyEditorConfiguration = new IEditorConfiguration() {
@Override
public Boolean getReadonly() {
@ -135,6 +161,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
this.dataBinder = new GridTabDataBinder(gridTab);
}
/**
* Get editor for GridField.
* @param gridField
* @return {@link WEditor}
*/
private WEditor getEditorCell(GridField gridField) {
WEditor editor = editors.get(gridField);
if (editor != null) {
@ -147,6 +178,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
return editor;
}
/**
* Setup field editor
* @param gridField
* @param editor
*/
private void prepareFieldEditor(GridField gridField, WEditor editor) {
if (editor instanceof WButtonEditor)
{
@ -172,9 +208,8 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
}
/**
*
* @param field
* @return column index, -1 if not found
* @return column index for field, -1 if not found
*/
public int getColumnIndex(GridField field) {
GridField[] fields = gridPanel.getFields();
@ -185,6 +220,10 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
return -1;
}
/**
* @param value
* @return readonly checkbox component
*/
private Component createReadonlyCheckbox(Object value) {
Checkbox checkBox = new Checkbox();
if (value != null && "true".equalsIgnoreCase(value.toString()))
@ -195,6 +234,11 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
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() {
Textbox textBox = new Textbox();
textBox.setDisabled(true);
@ -236,7 +280,8 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
* @param value
* @param gridField
* @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
*/
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 value
* @param gridField
* @param isForceGetValue
* @return
* @param isForceGetValue true to return Component with value even if IsDisplay return false. This is to allow Grid customization
* preference to override IsDisplay result.
* @return {@link Component}
*/
private Component getDisplayComponent(int rowIndex, Object value, GridField gridField, boolean isForceGetValue) {
Component component;
@ -308,6 +354,12 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
return component;
}
/**
* Apply AD_Style to field.
* @param gridField
* @param rowIndex
* @param component
*/
private void applyFieldStyle(GridField gridField, int rowIndex,
HtmlBasedComponent component) {
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));
}
/**
* 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) {
if (style != null && style.startsWith(MStyle.SCLASS_PREFIX)) {
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 label
*/
@ -355,8 +413,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
}
/**
*
* @return active editor list
* @return field editor list
*/
public List<WEditor> getEditors() {
List<WEditor> editorList = new ArrayList<WEditor>();
@ -395,7 +452,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
else
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 (component instanceof Label) {
Label label = (Label)component;
@ -433,9 +490,10 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
}
/**
* Render data for row.
* @param row
* @param data
* @param index
* @param data Object[] values for row
* @param index row index within current page (i.e if page size is 25, index is one of 0 to 24).
*/
@Override
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()) {
Component component = getDisplayComponent(rowIndex, currentValues[i], gridPanelFields[i], isGridViewCustomized);
div.appendChild(component);
div.setAttribute("display.component", component);
div.setAttribute(DISPLAY_COMPONENT_ATTR, component);
if (gridPanelFields[i].isHeading()) {
component.setVisible(false);
}
@ -599,7 +657,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
}
div.setStyle(divStyle);
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_DOUBLE_CLICK, rowListener);
row.appendChild(div);
@ -693,7 +751,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
}
/**
* @return Row
* @return current {@link Row}
*/
public Row getCurrentRow() {
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() {
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() {
Component parent = grid.getParent();
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() {
if (currentRow != null && currentRow.getParent() != null) {
@ -861,7 +923,6 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
}
/**
*
* @param 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
*/
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
*/
public void setGridPanel(GridView gridPanel) {
this.gridPanel = gridPanel;
}
/**
* Internal listener for row event (ON_CLICK, ON_DOUBLE_CLICK and ON_OK).
*/
static class RowListener implements EventListener<Event> {
private Grid _grid;
@ -914,7 +978,7 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
public void onEvent(Event event) throws Exception {
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;
Event evt = new Event(Events.ON_CLICK, _grid, event.getTarget());
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() {
return editing;
}
/**
* Set AD window content part that own this renderer.
* {@link #buttonListener} need this to call {@link AbstractADWindowContent#actionPerformed(ActionEvent)}.
* @param windowPanel
*/
public void setADWindowPanel(AbstractADWindowContent windowPanel) {
@ -974,12 +1040,15 @@ public class GridTabRowRenderer implements RowRenderer<Object[]>, RowRendererExt
else
Events.sendEvent(event.getTarget().getParent(), event);
} 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();
Events.sendEvent(gridPanel, new Event("onSelectRow", gridPanel, checkBox));
}
}
/**
* @return {@link GridView#isShowCurrentRowIndicatorColumn}
*/
private boolean isShowCurrentRowIndicatorColumn() {
return gridPanel != null && gridPanel.isShowCurrentRowIndicatorColumn();
}

View File

@ -28,14 +28,14 @@ import org.zkoss.zul.event.ListDataEvent;
import org.zkoss.zul.ext.Sortable;
/**
*
* List model for {@link GridTable}
* @author Low Heng Sin
*
*/
public class GridTableListModel extends AbstractListModel<Object> implements TableModelListener, Sortable<Object> {
/**
*
* generated serial id
*/
private static final long serialVersionUID = 698185856751242764L;
private GridTable tableModel;
@ -47,10 +47,10 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
private int pageSize = -1;
private int pageNo = 0;
/** Edit mode flag. When editing is true, do not fire ListDataEvent.CONTENTS_CHANGED event. **/
private boolean editing = false;
/**
*
* @param tableModel
* @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
*/
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.
* Fire ListDataEvent.CONTENTS_CHANGED event for fromRow to toRow.
* @param fromRow
* @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 ascending
*/
@ -178,6 +180,7 @@ public class GridTableListModel extends AbstractListModel<Object> implements Tab
}
/**
* Handle TableModelEvent from GridTable.
* @param e
* @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
*/
public void setEditing(boolean b) {

View File

@ -70,95 +70,132 @@ import org.zkoss.zul.event.ZulEvents;
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
*
*/
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";
/** {@link Column} attribute to store grid field 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";
/** {@link Column} attribute to store initial/original column hflex value **/
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;
/**
*
* generated serial id
*/
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;";
/** default paging size when GridView is in DetailPane **/
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;
/** default paging size when GridView is in header panel **/
private static final int DEFAULT_PAGE_SIZE = 20;
/** minimum column width **/
private static final int MIN_COLUMN_WIDTH = 100;
/** maximum column width **/
private static final int MAX_COLUMN_WIDTH = 300;
/** minimum column width for combobox field **/
private static final int MIN_COMBOBOX_WIDTH = 160;
/** minimum column width for numeric field **/
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";
/** Static Logger */
private static CLogger s_log = CLogger.getCLogger (GridView.class);
/** data grid instance **/
private Grid listbox = null;
private int pageSize = DEFAULT_PAGE_SIZE;
/**
* 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 int numColumns = 5;
private int windowNo;
/** GridTab that back this GridView **/
private GridTab gridTab;
/** true if this GridView instance have been init with GridTab **/
private boolean init;
/** Zk List model for {@link #tableModel} **/
private GridTableListModel listModel;
private Paging paging;
/** Row renderer for this GridView instance **/
private GridTabRowRenderer renderer;
/** Footer for paging **/
private Div gridFooter;
/** true if current row is always in edit mode **/
private boolean modeless = true;
/** column click by user **/
private String columnOnClick;
/** AD window content part that own this GridView instance **/
private AbstractADWindowContent windowPanel;
/** true when grid is refreshing its data **/
private boolean refreshing;
/** AD_Field_ID:Column Width **/
private Map<Integer, String> columnWidthMap;
/** true if it is in DetailPane **/
private boolean detailPaneMode;
/** checkbox to select all row of current page **/
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;
/** true if auto hide empty column feature is enable **/
private String m_isAutoHideEmptyColumn;
/**
* default constructor
*/
public GridView()
{
this(0);
@ -214,6 +251,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
addEventListener("onCustomizeGrid", this);
}
/**
* create data grid instances
*/
protected void createListbox() {
listbox = new Grid();
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")));
}
/**
* turn on/off detail pane mode
* @param detailPaneMode
* @param gridTab
*/
public void setDetailPaneMode(boolean detailPaneMode, GridTab gridTab) {
if (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) {
int size = DEFAULT_DETAIL_PAGE_SIZE;
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 true if it is in detail pane mode
*/
public boolean isDetailPaneMode() {
return this.detailPaneMode;
}
/**
* Update paging component with new paging size and notify model if paging size has change.
*/
private void updatePaging() {
if (paging != null && paging.getPageSize() != pageSize) {
paging.setPageSize(pageSize);
@ -296,7 +350,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
}
/**
*
* Init data grid
* @param gridTab
*/
public void init(GridTab gridTab)
@ -324,6 +378,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
showRecordsCount();
}
/**
* Update {@link DetailPane} status with record count
*/
private void showRecordsCount() {
Component parent = this.getParent();
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) {
this.gridTab = gridTab;
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) {
String[] widths = customComponent[1].split("[,]");
for(int i = 0; i< gridField.length && i<widths.length; i++) {
columnWidthMap.put(gridField[i].getAD_Field_ID(), widths[i]);
for(int i = 0; i< gridFields.length && i<widths.length; i++) {
columnWidthMap.put(gridFields[i].getAD_Field_ID(), widths[i]);
}
}
m_isAutoHideEmptyColumn = tabCustomization.getIsAutoHideEmptyColumn();
@ -396,22 +457,22 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
}
});
gridField = new GridField[gridFieldList.size()];
gridFieldList.toArray(gridField);
gridFields = new GridField[gridFieldList.size()];
gridFieldList.toArray(gridFields);
}
numColumns = gridField.length;
numColumns = gridFields.length;
}
/**
*
* @return boolean
* @return true if data grid have been init with GridTab
*/
public boolean isInit() {
return init;
}
/**
* call when tab is activated
* Activate GridView (make visible or GridTab have been refreshed)
* @param 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
*/
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() {
return refreshing;
}
/**
* Update current row from model
* Update current row index from model
*/
public void updateListIndex() {
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() {
if (gridFooter.isVisible())
gridFooter.setVisible(false);
}
/**
* show paging component
*/
private void showPagingControl() {
if (!gridFooter.isVisible())
gridFooter.setVisible(true);
}
/**
*
* echo ON_POST_SELECTED_ROW_CHANGED_EVENT after current row index has changed
*/
protected void echoOnPostSelectedRowChanged() {
if (getAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED) == null) {
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;
}
/**
* remove all components
*/
public void clear()
{
this.getChildren().clear();
}
/**
* Setup {@link Columns} of data grid
*/
private void setupColumns()
{
if (init) return;
@ -595,36 +671,36 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
for (int i = 0; i < numColumns; i++)
{
// 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++;
org.zkoss.zul.Column column = new Column();
column.setAttribute(GRID_VIEW_GRID_FIELD_INDEX, i);
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.setSortDescending(new SortComparator(colindex, false, Env.getLanguage(Env.getCtx())));
//IDEMPIERE-2898 - UX: Field only showing title at header on grid
if( gridField[i].isFieldOnly() )
if( gridFields[i].isFieldOnly() )
column.setLabel("");
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("")) {
ZKUpdateUtil.setWidth(column, columnWidthMap.get(gridField[i].getAD_Field_ID()));
if (columnWidthMap != null && columnWidthMap.get(gridFields[i].getAD_Field_ID()) != null && !columnWidthMap.get(gridFields[i].getAD_Field_ID()).equals("")) {
ZKUpdateUtil.setWidth(column, columnWidthMap.get(gridFields[i].getAD_Field_ID()));
} else {
if (gridField[i].getDisplayType()==DisplayType.YesNo) {
if (gridFields[i].getDisplayType()==DisplayType.YesNo) {
if (i > 0) {
ZKUpdateUtil.setHflex(column, "min");
} else {
int estimatedWidth=60;
int headerWidth = (gridField[i].getHeader().length()+2) * 8;
int headerWidth = (gridFields[i].getHeader().length()+2) * 8;
if (headerWidth > estimatedWidth)
estimatedWidth = headerWidth;
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
if (i > 0)
ZKUpdateUtil.setHflex(column, "min");
@ -632,33 +708,33 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
ZKUpdateUtil.setWidth(column, "60px");
} else {
int estimatedWidth = 0;
if (DisplayType.isNumeric(gridField[i].getDisplayType()))
if (DisplayType.isNumeric(gridFields[i].getDisplayType()))
estimatedWidth = MIN_NUMERIC_COL_WIDTH;
else if (DisplayType.isLookup(gridField[i].getDisplayType()))
else if (DisplayType.isLookup(gridFields[i].getDisplayType()))
estimatedWidth = MIN_COMBOBOX_WIDTH;
else if (DisplayType.isText(gridField[i].getDisplayType()))
estimatedWidth = gridField[i].getDisplayLength() * 8;
else if (DisplayType.isText(gridFields[i].getDisplayType()))
estimatedWidth = gridFields[i].getDisplayLength() * 8;
else
estimatedWidth = MIN_COLUMN_WIDTH;
int headerWidth = (gridField[i].getHeader().length()+2) * 8;
int headerWidth = (gridFields[i].getHeader().length()+2) * 8;
if (headerWidth > estimatedWidth)
estimatedWidth = headerWidth;
//hflex=min for first column not working well
if (i > 0 && !ClientInfo.isMobile())
{
if (DisplayType.isLookup(gridField[i].getDisplayType()))
if (DisplayType.isLookup(gridFields[i].getDisplayType()))
{
if (headerWidth > MIN_COMBOBOX_WIDTH)
ZKUpdateUtil.setHflex(column, "min");
}
else if (DisplayType.isNumeric(gridField[i].getDisplayType()))
else if (DisplayType.isNumeric(gridFields[i].getDisplayType()))
{
if (headerWidth > MIN_NUMERIC_COL_WIDTH)
ZKUpdateUtil.setHflex(column, "min");
}
else if (!DisplayType.isText(gridField[i].getDisplayType()))
else if (!DisplayType.isText(gridFields[i].getDisplayType()))
{
if (headerWidth > MIN_COLUMN_WIDTH)
ZKUpdateUtil.setHflex(column, "min");
@ -689,6 +765,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
}
}
/**
* Render data grid
*/
private void render()
{
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() {
if (!isAutoHideEmptyColumns()) {
@ -748,7 +827,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
continue;
int index = (Integer)value;
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);
break;
}
@ -771,14 +850,14 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
if (paging != null && paging.getPageSize() > 0) {
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)) {
hideColumn = false;
break;
} else if (gridTab.getCurrentRow() == rowIndex && gridTab.isNew()) {
if (gridField[index].isEditable(false) && (gridField[index].isMandatory(false) || !Util.isEmpty(gridField[index].getVO().MandatoryLogic)
|| !Util.isEmpty(gridField[index].getVO().DisplayLogic)
|| !Util.isEmpty(gridField[index].getVO().ReadOnlyLogic))) {
if (gridFields[index].isEditable(false) && (gridFields[index].isMandatory(false) || !Util.isEmpty(gridFields[index].getVO().MandatoryLogic)
|| !Util.isEmpty(gridFields[index].getVO().DisplayLogic)
|| !Util.isEmpty(gridFields[index].getVO().ReadOnlyLogic))) {
hideColumn = false;
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() {
if (!Util.isEmpty(m_isAutoHideEmptyColumn, true))
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()));
}
/**
* Show zero records for processing message
*/
private void updateEmptyMessage() {
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() {
if (listModel != null)
((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() {
if (renderer != null && renderer.isEditing())
renderer.stopEditing(true);
}
@Override
public void onEvent(Event event) throws Exception
{
if (event == null)
@ -872,15 +962,15 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
if (cmp.getParent() instanceof org.zkoss.zul.Row)
{
row = (Row) cmp.getParent();
columnName = (String) cmp.getAttribute("columnName");
columnName = (String) cmp.getAttribute(GridTabRowRenderer.COLUMN_NAME_ATTR);
}
}
}
if (row != null)
{
//click on selected row to enter edit mode
{
if (row == renderer.getCurrentRow())
{
//click on selected row to enter edit mode
if (!renderer.isEditing())
{
renderer.editCurrentRow();
@ -892,6 +982,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
}
else
{
//change selection of current row
int index = listbox.getRows().getChildren().indexOf(row);
if (index >= 0 ) {
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) {
if (gridView == null)
return null;
@ -953,6 +1048,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
return null;
}
/**
* @return true if all row of current page is selected
*/
private boolean isAllSelected() {
org.zkoss.zul.Rows rows = listbox.getRows();
List<Component> childs = rows.getChildren();
@ -974,6 +1072,10 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
return all;
}
/**
* turn on/off select all rows for current page
* @param b
*/
private void toggleSelectionForAll(boolean b) {
org.zkoss.zul.Rows rows = listbox.getRows();
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) {
if (updateModelIndex(index)) {
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() {
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() {
ADTabpanel adtabpanel = null;
boolean setFocus = true;
@ -1095,6 +1204,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
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) {
if (renderer.isEditing()) {
if (columnOnClick != null && columnOnClick.trim().length() > 0) {
@ -1123,7 +1237,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
for(Object element : list) {
if (element instanceof Div) {
Div div = (Div) element;
if (columnOnClick.equals(div.getAttribute("columnName"))) {
if (columnOnClick.equals(div.getAttribute(GridTabRowRenderer.COLUMN_NAME_ATTR))) {
cmp = div.getFirstChild();
Clients.response(new AuScript(null, "idempiere.scrollToRow('" + cmp.getUuid() + "');"));
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) {
if (row.getChildren().size() == 0) {
return false;
@ -1146,6 +1265,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
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) {
if (pageSize > 0) {
int start = listModel.getPage() * listModel.getPageSize();
@ -1214,6 +1338,11 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
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) {
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() {
Component parent = this.getParent();
while (parent != null) {
@ -1261,6 +1393,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
this.windowNo = windowNo;
}
/**
* If current row is in edit mode, set focus to first field editor
*/
@Override
public void focus() {
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
*/
public void setADWindowPanel(AbstractADWindowContent winPanel) {
@ -1307,6 +1443,9 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
renderer.setADWindowPanel(windowPanel);
}
/**
* Re-Init GridView with cache gridTab.
*/
public void reInit() {
listbox.getChildren().clear();
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
* this list container only customize list.
* @return GridField[]
*/
public GridField[] getFields() {
return gridField;
return gridFields;
}
/**
* call {@link #onEditCurrentRow(Event)}
*/
public void onEditCurrentRow() {
onEditCurrentRow(null);
}
/**
* Edit current row
* @param event
*/
public void onEditCurrentRow(Event event) {
if (!renderer.isEditing()) {
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
public void focusToFirstEditor() {
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() {
positionPagingControl();
}
/**
* Set position of paging component depends on whether GridView is in header or DetailPane panel.
*/
private void positionPagingControl() {
if (isDetailPane()) {
Component parent = this.getParent();
@ -1411,6 +1569,7 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
}
parent = parent.getParent();
}
//use simplify paging presentation for DetailPane
if (paging != null)
paging.setDetailed(false);
}
@ -1426,12 +1585,18 @@ public class GridView extends Vlayout implements EventListener<Event>, IdSpace,
}
}
/**
* call editorTaverseCallback for all field editors.
*/
@Override
public void editorTraverse(Callback<WEditor> editorTaverseCallback) {
editorTraverse(editorTaverseCallback, renderer.getEditors());
}
/**
* @return true if current row indicator column is visible.
*/
public boolean isShowCurrentRowIndicatorColumn() {
return showCurrentRowIndicatorColumn;
}

View File

@ -19,54 +19,53 @@ import org.zkoss.zk.ui.event.Event;
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>
*
*/
public interface IADTabbox extends UIPart {
/**
*
* @return selected tab index
* @return selected header tab index
*/
public int getSelectedIndex();
/**
*
* set selected header tab
* @param i tab index
*/
public void setSelectedIndex(int i);
/**
* Change selected header tab index from oldTabIndex to newTabIndex
* @param oldTabIndex
* @param newTabIndex
* @return
* @return true if selected tab successfully change to newTabIndex
*/
public boolean updateSelectedIndex(int oldTabIndex, int newTabIndex);
/**
* @return selected tab panel reference
* @return selected header {@link IADTabpanel} instance
*/
public IADTabpanel getSelectedTabpanel();
/**
*
* @param fromIndex
* @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);
/**
* @param index
* @return boolean
* @return true if tab at index visible, false otherwise
*/
public boolean isDisplay(int index);
/**
*
* @param tab
* @param tabPanel
* add new tab (AD_Tab)
* @param tab {@link GridTab} for AD_Tab
* @param tabPanel {@link IADTabpanel} instance for AD_Tab
*/
public void addTab(GridTab tab, IADTabpanel tabPanel);
@ -76,41 +75,43 @@ public interface IADTabbox extends UIPart {
public int getTabCount();
/**
* Evaluate state of each tab after DataStatusEvent
* @param 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();
/**
*
* Set event listener for tab selection change event
* @param listener
*/
public void setSelectionEventListener(EventListener<Event> listener);
/**
* @param index
* @return IADTabpanel
* @param index tab index
* @return {@link IADTabpanel} instance at index
*/
public IADTabpanel getADTabpanel(int index);
/**
* @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);
/**
*
* Set AD Window content part that own this IADTabbox instance.
* @param 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();
@ -120,6 +121,7 @@ public interface IADTabbox extends UIPart {
public boolean isSortTab();
/**
* Call {@link IADTabpanel#needSave(boolean, boolean)}
* @param rowChange
* @param onlyRealChange
* @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);
/**
* ignore all pending changes
* ignore/undo all pending changes
*/
public void dataIgnore();
/**
* @return selected header grid tab
* @return {@link GridTab} instance of header tab
*/
public GridTab getSelectedGridTab();
/**
*
* Save changes
* @param onSaveEvent
* @return true if save is successfull
* @return true if save is successful
*/
public boolean dataSave(boolean onSaveEvent);
/**
*
* Update status text of {@link DetailPane}
* @param status
* @param error
*/
public void setDetailPaneStatusMessage(String status, boolean error);
/**
* @return the currently selected detail adtabpanel
* @return the selected detail {@link IADTabpanel} instance
*/
IADTabpanel getSelectedDetailADTabpanel();
/**
* @return dirty adtabpanel that need save ( if any )
* @return dirty {@link IADTabpanel} that need save ( if any )
*/
IADTabpanel getDirtyADTabpanel();
/**
*
* @param changed
* @param readOnly
* Call {@link DetailPane#updateToolbar(boolean, boolean)}
* @param changed true if header tab has changed
* @param readOnly true if header tab is readonly
*/
public void updateDetailPaneToolbar(boolean changed, boolean readOnly);
/**
* Set selected tab of {@link DetailPane} to tabIndex.
* @param tabIndex
* @param currentRow
* @param currentRow set current row of tab at tabIndex to 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();

View File

@ -21,13 +21,16 @@ import org.zkoss.zul.Button;
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
*
*/
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";
/** 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";
/**
@ -47,12 +50,12 @@ public interface IADTabpanel extends Component, Evaluatee {
public int getTabLevel();
/**
* @return tablename
* @return table name from GridTab
*/
public String getTableName();
/**
* @return record ID
* @return record ID of current row
*/
public int getRecord_ID();
@ -62,40 +65,39 @@ public interface IADTabpanel extends Component, Evaluatee {
public boolean isCurrent();
/**
*
* @return title
* @return title of tab
*/
public String getTitle();
/**
* Render the panel
* Layout fields of the tab panel
*/
public void createUI();
/**
*
* @return GridTab
* @return {@link GridTab} instance that back this IADTabpanel instance
*/
public GridTab getGridTab();
/**
* activate/deactivate the panel
* Activate/deactivate this IADTabpanel instance.
* Call by init or after tab selection changed.
* @param b
*/
public void activate(boolean b);
/**
* retrieve data from db
* Execute query through the backed {@link GridTab} instance.
*/
public void query();
/**
* Refresh from db
* Refresh data through the backed {@link GridTab} instance.
*/
public void refresh();
/**
* retrieve data from db
* Call {@link GridTab#query(boolean, int, int)}
* @param currentRows
* @param currentDays
* @param maxRows
@ -103,58 +105,56 @@ public interface IADTabpanel extends Component, Evaluatee {
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();
/**
* Dynamic update of field properties ( visibility, filter and mandatory )
* @param i
* Dynamic update of every field's UI properties ( visibility, filter and mandatory ).
* @param col optional column name
*/
public void dynamicDisplay(int i);
public void dynamicDisplay(int col);
/**
* After save event
* Handle after save event
* @param onSaveEvent
*/
public void afterSave(boolean onSaveEvent);
/**
* Enter key event
* Handle enter key event
* @return true if the event is process
*/
public boolean onEnterKey();
/**
* @return boolean
* @return true if current presentation of the tab panel is grid/list view
*/
public boolean isGridView();
/**
* @return true if the panel have been activated
* @return true if the tab panel have been activated
*/
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
*/
public void setDetailPaneMode(boolean detailMode);
/**
*
* @return true if the panel is in detailpane node
* @return true if the panel is in detail mode (i.e a detail tab in DetailPane)
*/
public boolean isDetailPaneMode();
/**
*
* @return gridview instance
* @return {@link GridView} instance
*/
public GridView getGridView();
/**
*
* Call {@link GridTab#needSave(boolean, boolean)}
* @param rowChange
* @param onlyRealChange
* @return true if there are pending changes
@ -162,70 +162,71 @@ public interface IADTabpanel extends Component, Evaluatee {
public boolean needSave(boolean rowChange, boolean onlyRealChange);
/**
* Save changes.
* Call {@link GridTab#dataSave(boolean)}
* @param onSaveEvent
* @return true if the save operation completed successfully
*/
public boolean dataSave(boolean onSaveEvent);
/**
*
* Set tab number/sequence within an AD_Window
* @param tabNo
*/
public void setTabNo(int tabNo);
/**
*
* @return tab no ( ad_tab.tabno )
*/
public int getTabNo();
/**
*
* Set the {@link DetailPane} part that own this IADTabpanel instance
* @param detailPane
*/
public void setDetailPane(DetailPane detailPane);
/**
*
* @return detailpane
* @return the {@link DetailPane} part that own this IADTabpanel instance
*/
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();
/**
* @return treepanel instance
* @return {@link ADTreePanel} instance
*/
public ADTreePanel getTreePanel();
/**
* @return Quick Form Button Enabled/Disabled
* @return true if Quick Form Button is Enabled
*/
public boolean isEnableQuickFormButton();
/**
* Get is detail pane visible
* @return boolean
* @return true if the containing {@link DetailPane} instance is visible
*/
public default boolean isDetailVisible() {
return false;
}
/**
* @return List of toolbar buttons
* @return List of toolbar buttons
*/
public List<Button> getToolbarButtons();
/**
* @return customization enabled/disabled for tab
* @return true if customize grid button is enabled
*/
public boolean isEnableCustomizeButton();
/**
* @return process Button Enabled/Disabled
* @return true if process Button is Enabled
*/
default public boolean isEnableProcessButton() {
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}
*/
public void updateToolbar(ADWindowToolbar toolbar);
/**
* Enabled/Disabled detail panel toolbar button
* Enabled/Disabled {@link DetailPane} toolbar buttons
*
* @param toolbar - {@link Toolbar}
*/

View File

@ -19,31 +19,30 @@ import org.adempiere.util.Callback;
import org.adempiere.webui.editor.WEditor;
/**
*
* Interface for container that host one or more field editors
* @author hengsin
*
*/
public interface IFieldEditorContainer {
/**
* focus to first field editor
* set focus to first field editor
*/
public void focusToFirstEditor();
/**
* focus to next field editor from ref
* set focus to next field editor from ref
* @param ref
*/
public void focusToNextEditor(WEditor ref);
/**
* helper method to loop thru editor collection of panel <br/>
* can use on callout to check relative editor
* Call editorTaverseCallback for all editors hosted by this container
* @param editorTaverseCallback
*/
public void editorTraverse (Callback<WEditor> editorTaverseCallback);
/**
* default implement for {@link #editorTraverse(Callback)}
* Default implementation for {@link #editorTraverse(Callback)}
* @param editorTaverseCallback
* @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;
import java.util.List;
@ -25,13 +46,16 @@ import org.zkoss.zul.Menuseparator;
/**
* @author hengsin
*
*/
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";
/** Menupopup/Menuitem attribute to store reference to 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";
/**
@ -39,6 +63,11 @@ public class ProcessButtonPopup extends Menupopup implements EventListener<Event
*/
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) {
this.setSclass("z-menu-noimage");
@ -88,7 +117,7 @@ public class ProcessButtonPopup extends Menupopup implements EventListener<Event
for(Listitem action : actions) {
Menuitem mi = new Menuitem(action.getLabel());
mi.setValue((String)action.getValue());
mi.setSclass(DOCUMENT_ACTION_MENUITEM_ATTRIBUTE);
mi.setSclass(DOCUMENT_ACTION_MENUITEM_SCLASS);
mi.addEventListener(Events.ON_CLICK, this);
popup.appendChild(mi);
}
@ -99,7 +128,7 @@ public class ProcessButtonPopup extends Menupopup implements EventListener<Event
@Override
public void onEvent(Event event) throws Exception {
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);
WDocActionPanel panel = (WDocActionPanel) mi.getParent().getAttribute(DOC_ACTION_PANEL_ATTRIBUTE);
panel.setSelectedItem(mi.getValue());

View File

@ -71,7 +71,7 @@ import org.zkoss.zul.RowRendererExt;
import org.zkoss.zul.Timebox;
/**
* Row renderer for Quick GridTab grid.
* Row renderer for Quick GridTab grid (Base on {@link GridTabRowRenderer})
*
* @author Logilite Technologies
* @since Nov 03, 2017
@ -79,7 +79,9 @@ import org.zkoss.zul.Timebox;
public class QuickGridTabRowRenderer
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";
/** Editor component attribute to store row index (absolute) **/
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_ALIGN_CENTER = CELL_DIV_STYLE + "text-align:center; ";
@ -90,18 +92,27 @@ public class QuickGridTabRowRenderer
private GridTab gridTab;
private int windowNo;
/** Sync field editor changes to GridField **/
private GridTabDataBinder dataBinder;
private Paging paging;
/** internal listener for row event **/
private RowListener rowListener;
/** Grid that own this renderer **/
private Grid grid = null;
/** QuickGridView that uses this renderer **/
private QuickGridView gridPanel = null;
/** current focus row **/
private Row currentRow;
/** values of current row. updated in {@link #render(Row, Object[], int)}. **/
private Object[] currentValues;
/** true if currrent row is in edit mode **/
private boolean editing = false;
public int currentRowIndex = -1;
/** AD window content part that own this renderer **/
private AbstractADWindowContent m_windowPanel;
/** internal listener for button ActionEvent **/
private ActionListener buttonListener;
// Row-wise Editors Map
public Map<Row, ArrayList<WEditor>> editorsListMap = new LinkedHashMap<Row, ArrayList<WEditor>>();
@ -128,6 +139,12 @@ public class QuickGridTabRowRenderer
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) {
WEditor editor = WebEditorFactory.getEditor(gridField, true);
if (editor != null) {
@ -137,6 +154,11 @@ public class QuickGridTabRowRenderer
return editor;
}
/**
* Setup field editor
* @param gridField
* @param editor
*/
private void prepareFieldEditor(GridField gridField, WEditor editor) {
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) {
GridField[] fields = gridPanel.getFields();
for(int i = 0; i < fields.length; i++) {
@ -178,6 +204,7 @@ public class QuickGridTabRowRenderer
}
/**
* Render data for row.
* @param row
* @param data
* @param index
@ -409,7 +436,7 @@ public class QuickGridTabRowRenderer
*
* @param component
* @param isDisable
* @return
* @return true if component is read only
*/
public boolean isDisableReadonlyComponent(Component component, boolean isDisable)
{
@ -548,7 +575,7 @@ public class QuickGridTabRowRenderer
*
* @param zclass
* @param isDisable
* @return
* @return modify zclass
*/
private String addOrRemoveCssClass(String zclass, boolean isDisable)
{
@ -568,14 +595,27 @@ public class QuickGridTabRowRenderer
private Cell currentCell = null;
/**
* @return current {@link Cell}
*/
public Cell getCurrentCell() {
return currentCell;
}
/**
* Set current cell
* @param currentCell
*/
public void setCurrentCell(Cell 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) {
if (col < 0 || row < 0)
return;
@ -660,7 +700,7 @@ public class QuickGridTabRowRenderer
}
/**
* Set Property change listener of editor field
* Add property change listener (WEditor) to GridField
*
* @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
*/
@ -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 col
@ -714,8 +754,8 @@ public class QuickGridTabRowRenderer
} // addRemovePropertyChangeListener
/**
* @param code
* @return
* @param code cell navigation code
* @return true to add property change listener, false otherwise
*/
public Boolean isAddRemoveListener(int code)
{
@ -726,7 +766,8 @@ public class QuickGridTabRowRenderer
} // isAddRemoveListener
/**
* @param row
* Set current row
* @param row absolute row index
*/
public void setRowTo(int row)
{
@ -736,6 +777,11 @@ public class QuickGridTabRowRenderer
setCurrentRow(currentRow);
}
/**
* @param row
* @param col
* @return true if cell is editable, false otherwise
*/
private boolean isEditable(int row, int col)
{
Cell cell = null;
@ -790,6 +836,9 @@ public class QuickGridTabRowRenderer
return false;
}
/**
* Set focus to {@link #currentCell}
*/
public void setFocusOnCurrentCell() {
if (currentCell == null || currentCell.getChildren().size() <= 0) {
return;
@ -845,6 +894,7 @@ public class QuickGridTabRowRenderer
} // setFocusOnCurrentCell
/**
* Set current focus row
* @param 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() {
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
*/
public void setGridPanel(QuickGridView gridPanel) {
this.gridPanel = gridPanel;
}
/**
* Internal listener for row event (ON_CLICK, ON_DOUBLE_CLICK and ON_OK).
*/
static class RowListener implements EventListener<Event> {
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() {
return editing;
}
/**
* Set AD window content part that own this renderer.
* {@link #buttonListener} need this to call {@link AbstractADWindowContent#actionPerformed(ActionEvent)}.
* @param windowPanel
*/
public void setADWindowPanel(AbstractADWindowContent windowPanel) {

View File

@ -73,7 +73,7 @@ import org.zkoss.zul.event.ZulEvents;
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
* @since Nov 03, 2017
@ -81,30 +81,39 @@ import org.zkoss.zul.impl.CustomGridDataLoader;
public class QuickGridView extends Vbox
implements EventListener<Event>, IdSpace, IFieldEditorContainer, StateChangeListener {
/**
*
* generated serial id
*/
private static final long serialVersionUID = 228387400133234920L;
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;";
/** default paging size **/
private static final int DEFAULT_PAGE_SIZE = 20;
/** default paging size for mobile client **/
private static final int DEFAULT_MOBILE_PAGE_SIZE = 20;
/** minimum column width **/
private static final int MIN_COLUMN_WIDTH = 100;
/** maximum column width **/
private static final int MAX_COLUMN_WIDTH = 300;
/** minimum column width for combobox field **/
private static final int MIN_COMBOBOX_WIDTH = 160;
/** minimum column width for numeric field **/
private static final int MIN_NUMERIC_COL_WIDTH = 120;
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";
/** shortcut key for cell navigation **/
public static final String CNTRL_KEYS = "#left#right#up#down#home@k@r";
// '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 FOCUS_CODE = 0;
/** data grid instance **/
private Grid listbox = null;
private int pageSize = DEFAULT_PAGE_SIZE;
/**
* 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;
/** GridTable model for GridTab **/
private AbstractTableModel tableModel;
private int numColumns = 5;
private int windowNo;
/** GridTab that back this QuickGridView **/
private GridTab gridTab;
/** true if this QuickGridView instance have been init with GridTab **/
private boolean init;
/** Zk List model for {@link #tableModel} **/
public GridTableListModel listModel;
public Paging paging;
/** Row renderer for this QuickGridView instance **/
private QuickGridTabRowRenderer renderer;
/** Footer for paging **/
private Div gridFooter;
/** true if current row is always in edit mode **/
private boolean modeless = true;
/** AD window content part that own this GridView instance **/
private AbstractADWindowContent windowPanel;
/** true when grid is refreshing its data **/
private boolean refreshing;
/** AD_Field_ID:Column Width **/
private Map<Integer, String> columnWidthMap;
/** checkbox to select all row of current page **/
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;
// 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.
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() {
return gridFields;
}
/**
* @param gridField
*/
public void setGridField(GridField[] gridField) {
this.gridFields = gridField;
}
/**
* @return {@link QuickGridTabRowRenderer}
*/
public QuickGridTabRowRenderer getRenderer() {
return renderer;
}
@ -239,6 +276,11 @@ public class QuickGridView extends Vbox
addEventListener(EVENT_ONFOCUS_AFTER_SAVE, this);
}
/**
* @param abstractADWindowContent
* @param gridTab
* @param wQuickForm
*/
public QuickGridView(AbstractADWindowContent abstractADWindowContent, GridTab gridTab, IQuickForm wQuickForm)
{
this(abstractADWindowContent.getWindowNo());
@ -247,6 +289,9 @@ public class QuickGridView extends Vbox
init(gridTab);
}
/**
* create data grid instances
*/
protected void createListbox()
{
listbox = new Grid();
@ -258,7 +303,7 @@ public class QuickGridView extends Vbox
}
/**
*
* Init data grid
* @param gridTab
*/
public void init(GridTab gridTab)
@ -278,6 +323,10 @@ public class QuickGridView extends Vbox
this.init = true;
}
/**
* Setup {@link #gridFields} from gridTab.
* @param gridTab
*/
private void setupFields(GridTab gridTab) {
this.gridTab = gridTab;
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() {
return init;
}
/**
* refresh after switching from form view
* Refresh data grid (int of quick form or column setup has change)
* @param 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() {
return refreshing;
}
/**
* Update current row from model
* Update current row index from model
*/
public void updateListIndex() {
if (gridTab == null || !gridTab.isOpen()) return;
@ -451,18 +503,24 @@ public class QuickGridView extends Vbox
}
}
/**
* hide paging component
*/
private void hidePagingControl() {
if (gridFooter.isVisible())
gridFooter.setVisible(false);
}
/**
* show paging component
*/
private void showPagingControl() {
if (!gridFooter.isVisible())
gridFooter.setVisible(true);
}
/**
*
* echo ON_POST_SELECTED_ROW_CHANGED_EVENT after current row index has changed
*/
protected void echoOnPostSelectedRowChanged() {
if (getAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED) == null) {
@ -480,11 +538,17 @@ public class QuickGridView extends Vbox
this.pageSize = pageSize;
}
/**
* remove all components
*/
public void clear()
{
this.getChildren().clear();
}
/**
* Setup {@link Columns} of data grid
*/
private void setupColumns()
{
if (init) return;
@ -608,6 +672,9 @@ public class QuickGridView extends Vbox
}
}
/**
* Render data grid
*/
private void render()
{
updateEmptyMessage();
@ -635,6 +702,9 @@ public class QuickGridView extends Vbox
}
}
/**
* Show zero records for processing message
*/
private void updateEmptyMessage() {
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() {
if (listModel != null)
((GridTable)tableModel).removeTableModelListener(listModel);
@ -1128,6 +1202,10 @@ public class QuickGridView extends Vbox
event.stopPropagation();
}
/**
* Set focus to cell (source)
* @param source
*/
private void setFocusOnDiv(Component source) {
int rowCount = gridTab.getTableModel().getRowCount();
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() {
org.zkoss.zul.Rows rows = listbox.getRows();
List<Component> childs = rows.getChildren();
@ -1162,6 +1243,10 @@ public class QuickGridView extends Vbox
return all;
}
/**
* turn on/off select all rows for current page
* @param b
*/
public void toggleSelectionForAll(boolean b) {
org.zkoss.zul.Rows rows = listbox.getRows();
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) {
if (updateModelIndex(index)) {
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)
{
@ -1211,6 +1305,9 @@ public class QuickGridView extends Vbox
return isSave;
}
/**
* Event after the current row index has changed.
*/
public void onPostSelectedRowChanged() {
removeAttribute(ATTR_ON_POST_SELECTED_ROW_CHANGED);
if (listbox.getRows() == null || listbox.getRows().getChildren().isEmpty())
@ -1244,6 +1341,11 @@ public class QuickGridView extends Vbox
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) {
if (row.getChildren().size() == 0) {
return false;
@ -1255,6 +1357,11 @@ public class QuickGridView extends Vbox
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) {
if (pageSize > 0) {
int start = listModel.getPage() * listModel.getPageSize();
@ -1309,9 +1416,8 @@ public class QuickGridView extends Vbox
}
/**
* Change display properties of current row
*
* @param noData
* 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) {
@ -1367,6 +1473,7 @@ public class QuickGridView extends Vbox
}
/**
* Set AD window content part that own this QuickGridView instance
* @param winPanel
*/
public void setADWindowPanel(AbstractADWindowContent winPanel) {
@ -1375,6 +1482,9 @@ public class QuickGridView extends Vbox
renderer.setADWindowPanel(windowPanel);
}
/**
* Re-Init QuickGridView with cache gridTab.
*/
public void reInit() {
listbox.getChildren().clear();
listbox.detach();
@ -1400,6 +1510,7 @@ public class QuickGridView extends Vbox
/**
* list field display in grid mode, in case user customize grid
* this list container only customize list.
* @return GridField[]
*/
public GridField[] getFields() {
return gridFields;
@ -1430,10 +1541,16 @@ public class QuickGridView extends Vbox
}
}
/**
* not used. candidate for removal.
*/
protected void onADTabPanelParentChanged() {
positionPagingControl();
}
/**
* not used. candidate for removal.
*/
private void positionPagingControl()
{
if (gridFooter.getParent() != this)
@ -1446,18 +1563,27 @@ public class QuickGridView extends Vbox
paging.setDetailed(true);
}
/**
* set status text
* @param text
* @param error
*/
public void setStatusLine(String text, boolean error)
{
windowPanel.getStatusBarQF().setStatusLine(text, error);
}
/**
* add new row to data grid
*/
public void createNewLine() {
isNewLineSaved = false;
gridTab.dataNew(false);
}
/**
* @param code
* Save changes
* @param code cell navigation code
*/
public boolean dataSave(int code) {
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)
{
int rowCount = gridTab.getTableModel().getRowCount();
@ -1516,6 +1646,9 @@ public class QuickGridView extends Vbox
return 0;
} // getFocusedRowIndex
/**
* @return sort column (if any)
*/
public Column findCurrentSortColumn()
{
if (listbox.getColumns() != null)

View File

@ -45,6 +45,8 @@ import org.zkoss.zul.Separator;
import org.zkoss.zul.Space;
/**
* Status bar component of AD Window.
*
* This class is based on org.compiere.apps.StatusBar written by Jorg Janke.
* @author Jorg Janke
*
@ -55,32 +57,45 @@ import org.zkoss.zul.Space;
public class StatusBar extends Panel implements EventListener<Event>
{
/**
*
* generated serial id
*/
private static final long serialVersionUID = 7091641684809092888L;
/** panel for record info text **/
private Panel infoPanel;
/** html content of {@link #infoPanel} **/
private Html infoLine;
/** west div for status text (info or error message) **/
private Div west;
/** east div for record info text **/
private Div east;
private ProcessInfoLog[] pInfoLogs;
/** current status text **/
private String m_statusText;
/** indicate current status text is info or error message */
private boolean m_statusError;
/** message popup **/
private Window msgPopup;
/** content div for {@link #msgPopup} **/
private Div msgPopupCnt;
/** layout for {@link #west} **/
private Hlayout messageContainer;
/** Caption for {@link #msgPopup} **/
private Caption msgPopupCaption;
/**
* Default constructor
*/
public StatusBar()
{
super();
@ -89,6 +104,10 @@ public class StatusBar extends Panel implements EventListener<Event>
createPopup();
}
/**
* Layout status bar.
* West is for message and East is for HTML record info (usually generated from AD_StatusLine).
*/
private void init()
{
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
*/
public void setInfo (String text)
@ -128,6 +147,7 @@ public class StatusBar extends Panel implements EventListener<Event>
} // setInfo
/**
* Call {@link #setStatusLine(String, boolean, ProcessInfoLog[])}.
* @param 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 error
*/
@ -145,6 +166,7 @@ public class StatusBar extends Panel implements EventListener<Event>
}
/**
* Set status message (west part) text.
* @param text
* @param error
* @param m_logs
@ -154,6 +176,7 @@ public class StatusBar extends Panel implements EventListener<Event>
pInfoLogs = m_logs;
Div div = null;
//detect duplicate call within the current execution cycle
Execution execution = Executions.getCurrent();
if (execution != null) {
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 )
return;
//show auto dismiss popup notification at top left of ancestor tab panel
String labelText = buildLabelText(m_statusText);
int duration = MSysConfig.getIntValue(MSysConfig.ZK_ERROR_MSG_LIFETIME_MILLISECONDS, 3500, Env.getAD_Client_ID(Env.getCtx()));
if (error) {
@ -206,6 +230,7 @@ public class StatusBar extends Panel implements EventListener<Event>
label.addEventListener(Events.ON_CLICK, this);
}
//add document/record link from ProcessInfoLog
if (m_logs != null) {
div = new Div();
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) {
if (statusText == null)
return "";
@ -240,12 +270,20 @@ public class StatusBar extends Panel implements EventListener<Event>
return statusText.substring(0, 80);
}
/**
* Create html content for {@link #msgPopupCnt}
*/
protected void createPopupContent() {
Html t = new Html(WTextEditorDialog.sanitize(m_statusText));
msgPopupCnt.getChildren().clear();
msgPopupCnt.appendChild(t);
}
/**
* Shorten statusText if length exceed the predefine max length of 140
* @param statusText
* @return shorten statusText
*/
private String buildNotificationText(String statusText) {
if (statusText == null)
return "";
@ -258,6 +296,11 @@ public class StatusBar extends Panel implements EventListener<Event>
return statusText.substring(0, 136) + " ...";
}
/**
* Find {@link Tabpanel} or {@link WQuickForm} that own comp
* @param comp
* @return
*/
private Component findTabpanel(Component comp) {
Component parent = comp.getParent();
while (parent != null) {
@ -278,13 +321,15 @@ public class StatusBar extends Panel implements EventListener<Event>
}
}
/**
* Show message popup ({@link #msgPopup})
*/
private void showPopup() {
appendChild(msgPopup);
LayoutUtils.openOverlappedWindow(messageContainer, msgPopup, "overlap_end");
}
/**
*
* @return process logs
*/
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() {
return m_statusText;
}
/**
* @return true if current status text is error text
*/
public boolean getStatusError() {
return m_statusError;
}
/**
* Create new message popup instance
*/
private void createPopup() {
msgPopupCnt = new Div();
ZKUpdateUtil.setVflex(msgPopupCnt, "1");
@ -321,6 +371,9 @@ public class StatusBar extends Panel implements EventListener<Event>
msgPopup.appendChild(msgPopupCaption);
}
/**
* handle onClientInfo event from browser
*/
protected void onClientInfo() {
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.zul.Toolbarbutton;
/**
* Model for AD_ToolBarButton with IsCustomization=Y
* @author hengsin
*/
public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
/** Toolbarbutton instance **/
private Toolbarbutton toolbarButton;
/** AD_ToolBarButton.ActionClassName **/
private String actionId;
private int windowNo;
private int tabNo = -1;
/** model instance for AD_ToolBarButton **/
private MToolBarButton mToolbarButton;
/**
* @param mToolbarButton
* @param btn
* @param actionId
* @param windowNo
*/
public ToolbarCustomButton(MToolBarButton mToolbarButton, Toolbarbutton btn, String actionId, int windowNo) {
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) {
toolbarButton = btn;
this.actionId = actionId;
@ -49,6 +69,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
toolbarButton.addEventListener(Events.ON_CLICK, this);
}
/**
* Call {@link IAction#execute(Object)}.
*/
@Override
public void onEvent(Event event) throws Exception {
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);
}
/**
* Delegate to {@link #dynamicDisplay(boolean)}
*/
public void dynamicDisplay() {
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) {
if (toolbarButton.getParent() == null && !forceValidation)
return;
@ -107,6 +137,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
toolbarButton.setVisible(visible);
}
/**
* Evaluate pressedLogic (if defined)
*/
public void pressedLogic() {
if (toolbarButton.getParent() == null)
return;
@ -130,6 +163,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
((ToolBarButton) toolbarButton).setPressed(isPressed);
}
/**
* Evaluate readOnlyLogic (if defined)
*/
public void readOnlyLogic() {
if (toolbarButton.getParent() == null)
return;
@ -154,6 +190,13 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
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) {
boolean isValid = false;
@ -169,6 +212,9 @@ public class ToolbarCustomButton implements EventListener<Event>, Evaluatee {
return isValid;
}
/**
* @return {@link Toolbarbutton}
*/
public Toolbarbutton getToolbarbutton() {
return toolbarButton;
}

View File

@ -28,22 +28,30 @@ import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Button;
/**
* Model for AD_ToolBarButton with AD_Process_ID > 0
* @author hengsin
*
*/
public class ToolbarProcessButton implements IProcessButton, Evaluatee {
/** model instance for AD_ToolBarButton **/
private MToolBarButton mToolbarButton;
private IADTabpanel adTabpanel;
/** {@link IADTabpanel} that own this ToolbarProcessButton instance **/
private IADTabpanel adTabpanel;
/** translated process name **/
private String name;
/** translated process description **/
private String description;
/** ActionListener for button **/
private ActionListener actionListener;
/** Button instance **/
private Button button;
private int windowNo;
/**
* @param windowNo
*
* @param mToolbarButton
* @param adTabpanel
* @param listener
* @param windowNo
*/
public ToolbarProcessButton(MToolBarButton mToolbarButton, IADTabpanel adTabpanel, ActionListener listener, int windowNo) {
this.mToolbarButton = mToolbarButton;
@ -118,10 +126,16 @@ public class ToolbarProcessButton implements IProcessButton, Evaluatee {
return name;
}
/**
* @return {@link Button}
*/
public Button getButton() {
return button;
}
/**
* Dynamic update of button state.
*/
public void dynamicDisplay() {
String displayLogic = mToolbarButton.getDisplayLogic();
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);
}
/**
* Evaluate readOnlyLogic (if defined)
*/
public void readOnlyLogic() {
String readOnlyLogic = mToolbarButton.getReadOnlyLogic();
@ -150,6 +167,9 @@ public class ToolbarProcessButton implements IProcessButton, Evaluatee {
button.setDisabled(disabled);
} // readOnlyLogic
/**
* Evaluate pressedLogic (if defined)
*/
public void pressedLogic() {
String pressedLogic = mToolbarButton.getPressedLogic();
@ -160,8 +180,14 @@ public class ToolbarProcessButton implements IProcessButton, Evaluatee {
button.setAttribute(ProcessButtonPopup.BUTTON_ATTRIBUTE_PRESSED, isPressed ? "Y" : "N");
} // 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) {
boolean isValid = false;
if (logic.startsWith("@SQL=")) {
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;
import org.adempiere.util.Callback;
/**
* Interface for AD Window event
* @author hengsin
*/
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);
}

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;
import org.adempiere.webui.adwindow.ADWindow;
/**
* Validation event for AD Window
* @author hengsin
*/
public class WindowValidatorEvent {
/** {@link ADWindow} instance **/
private ADWindow window;
/** Event name **/
private String name;
/**
* @param window
* @param name
*/
public WindowValidatorEvent(ADWindow window, String name) {
this.window = window;
this.name = name;
}
/**
* @return {@link ADWindow}
*/
public ADWindow getWindow() {
return this.window;
}
/**
* @return Event name
*/
public String getName() {
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;
/**
* Predefine AD Window validation event type
* @author hengsin
*
*/
public enum WindowValidatorEventType {
BEFORE_IGNORE("beforeIgnore"),
AFTER_IGNORE("afterIgnore"),
@ -14,12 +43,19 @@ public enum WindowValidatorEventType {
BEFORE_COPY("beforeCopy"),
AFTER_COPY("afterCopy");
/** Event name **/
private String name;
/**
* @param name
*/
private WindowValidatorEventType(String name) {
this.name = name;
}
/**
* @return Event name
*/
public String getName() {
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;
import java.util.ArrayList;
@ -13,14 +37,25 @@ import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
*
* Manage {@link WindowValidator} osgi services
* @author hengsin
*
*/
public class WindowValidatorManager implements BundleActivator, ServiceTrackerCustomizer<WindowValidator, WindowValidator> {
/** Singleton WindowValidatorManager instance **/
private static WindowValidatorManager instance = null;
/** {@link BundleContext} instance **/
private BundleContext context;
/** AD_Window_UU: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>();
/** WindowValidator osgi service tracker **/
private ServiceTracker<WindowValidator, WindowValidator> serviceTracker;
@Override
@ -46,7 +81,12 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
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);
if (list == null) {
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);
if (list != null) {
list.remove(service);
}
}
/**
* Create {@link #serviceTracker} and {@link #instance}
*/
@Override
public void start(BundleContext context) throws Exception {
this.context = context;
@ -97,6 +145,9 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
instance = this;
}
/**
* Close {@link #serviceTracker} and dispose {@link #instance}
*/
@Override
public void stop(BundleContext context) throws Exception {
serviceTracker.close();
@ -104,10 +155,19 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
instance = null;
}
/**
*
* @return {@link WindowValidatorManager}
*/
public static WindowValidatorManager getInstance() {
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) {
ADWindow window = event.getWindow();
String uuid = window.getAD_Window_UU();
@ -129,19 +189,32 @@ public class WindowValidatorManager implements BundleActivator, ServiceTrackerCu
chain.start();
}
/**
* class to call a list of {@link WindowValidator} through {@link Callback} chain.
*/
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 WindowValidator[] validators;
/** event for {@link #validators} **/
private WindowValidatorEvent event;
/** current index of {@link #validators} **/
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) {
this.event = event;
this.validators = validators;
this.callback = callback;
}
/**
* Start the @link {@link WindowValidator} callback chain.
*/
public void start() {
index = 0;
if (index < validators.length)

View File

@ -31,6 +31,7 @@ import java.util.Properties;
import java.util.logging.Level;
import org.adempiere.util.LogAuthFailure;
import org.adempiere.webui.AdempiereWebUI;
import org.adempiere.webui.LayoutUtils;
import org.adempiere.webui.apps.AEnv;
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?
// https://sourceforge.net/p/adempiere/zk-web-client/303/
// 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?
/* Check DB version */

View File

@ -30,6 +30,7 @@ import java.util.Properties;
import javax.servlet.http.HttpSession;
import org.adempiere.util.Callback;
import org.adempiere.webui.AdempiereWebUI;
import org.adempiere.webui.IWebClient;
import org.adempiere.webui.component.FWindow;
import org.adempiere.webui.component.Window;
@ -297,7 +298,7 @@ public class LoginWindow extends FWindow implements EventListener<Event>
else
loginName = user.getLDAPUser() != null ? user.getLDAPUser() : user.getName();
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.changeRole(ctx);
}