From ca98a81671c1e810e80ae32da587d297dfd00c1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Peter=20Tak=C3=A1cs?= <93127072+PeterTakacs300@users.noreply.github.com> Date: Sun, 16 Jul 2023 17:33:58 +0200 Subject: [PATCH] IDEMPIERE-5772 - Quick Info Widget Support for Info Window (#1899) * IDEMPIERE-5772 - Quick Info Widget Support for Info Window - initial commit * IDEMPIERE-5772 - manage context variables in info window Put the following values into the context: - query criteria - values of the selected row * IDEMPIERE-5772 - fixes - info window should not change context if isLookup() = true (popup window mode) - parameter values should be put into context only on re-query - values of KeyNamePair type should use the "key" in context (practically tat means the ID) instead of name (display value) * IDEMPIERE-5772 - add prefix to row variables and Selected_ID to ctx * IDEMPIERE-5772 - rename variable name Selected_ID -> ID_Selection * IDEMPIERE-5772 - patch by Carlos * IDEMPIERE-5772 - fixed reported issues - support ctx variables in info windows without process (no multiselection) - change ctx variable syntax/prefix - fix issue with type IDColumn --- .../oracle/202306190914_IDEMPIERE-5772.sql | 53 +++++ .../202306190914_IDEMPIERE-5772.sql | 50 +++++ .../compiere/model/I_AD_StatusLineUsedIn.java | 15 ++ .../src/org/compiere/model/MStatusLine.java | 21 +- .../org/compiere/model/MStatusLineUsedIn.java | 20 +- .../compiere/model/X_AD_StatusLineUsedIn.java | 30 ++- .../webui/desktop/DefaultDesktop.java | 20 +- .../org/adempiere/webui/desktop/IDesktop.java | 14 ++ .../org/adempiere/webui/editor/WEditor.java | 2 +- .../org/adempiere/webui/info/InfoWindow.java | 6 + .../adempiere/webui/panel/HelpController.java | 17 +- .../org/adempiere/webui/panel/InfoPanel.java | 183 ++++++++++++++++-- 12 files changed, 404 insertions(+), 27 deletions(-) create mode 100644 migration/iD11/oracle/202306190914_IDEMPIERE-5772.sql create mode 100644 migration/iD11/postgresql/202306190914_IDEMPIERE-5772.sql diff --git a/migration/iD11/oracle/202306190914_IDEMPIERE-5772.sql b/migration/iD11/oracle/202306190914_IDEMPIERE-5772.sql new file mode 100644 index 0000000000..c452ec652a --- /dev/null +++ b/migration/iD11/oracle/202306190914_IDEMPIERE-5772.sql @@ -0,0 +1,53 @@ +-- IDEMPIERE-5772 +SELECT register_migration_script('202306190914_IDEMPIERE-5772.sql') FROM dual; + +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- Jun 19, 2023, 9:14:41 AM CEST +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,Help,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (215853,0,'Info Window','Info and search/select Window','The Info window is used to search and select records as well as display information relevant to the selection.',200108,'AD_InfoWindow_ID',22,'N','N','N','N','N',0,'N',19,0,0,'Y',TO_TIMESTAMP('2023-06-19 09:14:40','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-06-19 09:14:40','YYYY-MM-DD HH24:MI:SS'),100,3068,'Y','N','D','N','N','N','Y','78d7ed91-8563-461a-ab16-39e62c393e78','Y',0,'N','N','C','N') +; + +-- Jun 19, 2023, 9:14:44 AM CEST +UPDATE AD_Column SET FKConstraintName='ADInfoWindow_ADStatusLineUsedI', FKConstraintType='C',Updated=TO_TIMESTAMP('2023-06-19 09:14:44','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=215853 +; + +-- Jun 19, 2023, 9:14:44 AM CEST +ALTER TABLE AD_StatusLineUsedIn ADD AD_InfoWindow_ID NUMBER(10) DEFAULT NULL +; + +-- Jun 19, 2023, 9:14:44 AM CEST +ALTER TABLE AD_StatusLineUsedIn ADD CONSTRAINT ADInfoWindow_ADStatusLineUsedI FOREIGN KEY (AD_InfoWindow_ID) REFERENCES ad_infowindow(ad_infowindow_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED +; + +-- Jun 19, 2023, 9:15:04 AM CEST +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (207654,'Info Window','Info and search/select Window','The Info window is used to search and select records as well as display information relevant to the selection.',200115,215853,'Y',22,110,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2023-06-19 09:15:03','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-06-19 09:15:03','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','2ab90c17-a77d-4b89-bc17-17dc3f7b775d','Y',80,2) +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET DisplayLogic='@AD_Window_ID@=0 & @AD_InfoWindow_ID@=0', SeqNo=40,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202573 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET DisplayLogic='@AD_Table_ID@=0 & @AD_InfoWindow_ID@=0', SeqNo=60,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202574 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET DisplayLogic='@AD_Table_ID@=0 & @AD_InfoWindow_ID@=0', SeqNo=70,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202575 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET IsDisplayed='Y', DisplayLogic='@AD_Table_ID@=0 & @AD_Window_ID@=0', SeqNo=80, XPosition=1,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207654 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET SeqNo=100,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202576 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET SeqNo=110,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202578 +; + +-- Jul 13, 2023, 6:38:30 PM CEST +UPDATE AD_Column SET DefaultValue='NULL',Updated=TO_TIMESTAMP('2023-07-13 18:38:30','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=215853 +; diff --git a/migration/iD11/postgresql/202306190914_IDEMPIERE-5772.sql b/migration/iD11/postgresql/202306190914_IDEMPIERE-5772.sql new file mode 100644 index 0000000000..fa7bbb9875 --- /dev/null +++ b/migration/iD11/postgresql/202306190914_IDEMPIERE-5772.sql @@ -0,0 +1,50 @@ +-- IDEMPIERE-5772 +SELECT register_migration_script('202306190914_IDEMPIERE-5772.sql') FROM dual; + +-- Jun 19, 2023, 9:14:41 AM CEST +INSERT INTO AD_Column (AD_Column_ID,Version,Name,Description,Help,AD_Table_ID,ColumnName,FieldLength,IsKey,IsParent,IsMandatory,IsTranslated,IsIdentifier,SeqNo,IsEncrypted,AD_Reference_ID,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,AD_Element_ID,IsUpdateable,IsSelectionColumn,EntityType,IsSyncDatabase,IsAlwaysUpdateable,IsAutocomplete,IsAllowLogging,AD_Column_UU,IsAllowCopy,SeqNoSelection,IsToolbarButton,IsSecure,FKConstraintType,IsHtml) VALUES (215853,0,'Info Window','Info and search/select Window','The Info window is used to search and select records as well as display information relevant to the selection.',200108,'AD_InfoWindow_ID',22,'N','N','N','N','N',0,'N',19,0,0,'Y',TO_TIMESTAMP('2023-06-19 09:14:40','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-06-19 09:14:40','YYYY-MM-DD HH24:MI:SS'),100,3068,'Y','N','D','N','N','N','Y','78d7ed91-8563-461a-ab16-39e62c393e78','Y',0,'N','N','C','N') +; + +-- Jun 19, 2023, 9:14:44 AM CEST +UPDATE AD_Column SET FKConstraintName='ADInfoWindow_ADStatusLineUsedI', FKConstraintType='C',Updated=TO_TIMESTAMP('2023-06-19 09:14:44','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=215853 +; + +-- Jun 19, 2023, 9:14:44 AM CEST +ALTER TABLE AD_StatusLineUsedIn ADD COLUMN AD_InfoWindow_ID NUMERIC(10) DEFAULT NULL +; + +-- Jun 19, 2023, 9:14:44 AM CEST +ALTER TABLE AD_StatusLineUsedIn ADD CONSTRAINT ADInfoWindow_ADStatusLineUsedI FOREIGN KEY (AD_InfoWindow_ID) REFERENCES ad_infowindow(ad_infowindow_id) ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED +; + +-- Jun 19, 2023, 9:15:04 AM CEST +INSERT INTO AD_Field (AD_Field_ID,Name,Description,Help,AD_Tab_ID,AD_Column_ID,IsDisplayed,DisplayLength,SeqNo,IsSameLine,IsHeading,IsFieldOnly,IsEncrypted,AD_Client_ID,AD_Org_ID,IsActive,Created,CreatedBy,Updated,UpdatedBy,IsReadOnly,IsCentrallyMaintained,EntityType,AD_Field_UU,IsDisplayedGrid,SeqNoGrid,ColumnSpan) VALUES (207654,'Info Window','Info and search/select Window','The Info window is used to search and select records as well as display information relevant to the selection.',200115,215853,'Y',22,110,'N','N','N','N',0,0,'Y',TO_TIMESTAMP('2023-06-19 09:15:03','YYYY-MM-DD HH24:MI:SS'),100,TO_TIMESTAMP('2023-06-19 09:15:03','YYYY-MM-DD HH24:MI:SS'),100,'N','Y','D','2ab90c17-a77d-4b89-bc17-17dc3f7b775d','Y',80,2) +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET DisplayLogic='@AD_Window_ID@=0 & @AD_InfoWindow_ID@=0', SeqNo=40,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202573 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET DisplayLogic='@AD_Table_ID@=0 & @AD_InfoWindow_ID@=0', SeqNo=60,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202574 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET DisplayLogic='@AD_Table_ID@=0 & @AD_InfoWindow_ID@=0', SeqNo=70,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202575 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET IsDisplayed='Y', DisplayLogic='@AD_Table_ID@=0 & @AD_Window_ID@=0', SeqNo=80, XPosition=1,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=207654 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET SeqNo=100,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202576 +; + +-- Jun 19, 2023, 9:22:16 AM CEST +UPDATE AD_Field SET SeqNo=110,Updated=TO_TIMESTAMP('2023-06-19 09:22:16','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Field_ID=202578 +; + +-- Jul 13, 2023, 6:38:30 PM CEST +UPDATE AD_Column SET DefaultValue='NULL',Updated=TO_TIMESTAMP('2023-07-13 18:38:30','YYYY-MM-DD HH24:MI:SS'),UpdatedBy=100 WHERE AD_Column_ID=215853 +; diff --git a/org.adempiere.base/src/org/compiere/model/I_AD_StatusLineUsedIn.java b/org.adempiere.base/src/org/compiere/model/I_AD_StatusLineUsedIn.java index 05177a2dd6..a8b45e9dde 100644 --- a/org.adempiere.base/src/org/compiere/model/I_AD_StatusLineUsedIn.java +++ b/org.adempiere.base/src/org/compiere/model/I_AD_StatusLineUsedIn.java @@ -49,6 +49,21 @@ public interface I_AD_StatusLineUsedIn */ public int getAD_Client_ID(); + /** Column name AD_InfoWindow_ID */ + public static final String COLUMNNAME_AD_InfoWindow_ID = "AD_InfoWindow_ID"; + + /** Set Info Window. + * Info and search/select Window + */ + public void setAD_InfoWindow_ID (int AD_InfoWindow_ID); + + /** Get Info Window. + * Info and search/select Window + */ + public int getAD_InfoWindow_ID(); + + public org.compiere.model.I_AD_InfoWindow getAD_InfoWindow() throws RuntimeException; + /** Column name AD_Org_ID */ public static final String COLUMNNAME_AD_Org_ID = "AD_Org_ID"; diff --git a/org.adempiere.base/src/org/compiere/model/MStatusLine.java b/org.adempiere.base/src/org/compiere/model/MStatusLine.java index 76d0ddaf6e..8d48a3991a 100644 --- a/org.adempiere.base/src/org/compiere/model/MStatusLine.java +++ b/org.adempiere.base/src/org/compiere/model/MStatusLine.java @@ -189,7 +189,19 @@ public class MStatusLine extends X_AD_StatusLine implements ImmutablePOSupport * @return array of widget lines discovered for table or specific tab or general window */ public static MStatusLine[] getStatusLinesWidget(int window_ID, int tab_ID, int table_ID) { - StringBuilder key = new StringBuilder().append(window_ID).append("|").append(tab_ID).append("|").append(table_ID); + return getStatusLinesWidget(window_ID, tab_ID, table_ID, 0); + } + + /** + * Get the widget lines defined for the window and tab and table (immutable) + * @param window_ID + * @param tab_ID + * @param table_ID + * @param infoWindow_ID + * @return array of widget lines discovered for table or specific tab or general window + */ + public static MStatusLine[] getStatusLinesWidget(int window_ID, int tab_ID, int table_ID, int infoWindow_ID) { + StringBuilder key = new StringBuilder().append(window_ID).append("|").append(tab_ID).append("|").append(table_ID).append("|").append(infoWindow_ID); MStatusLine[] retValue = null; if (s_cachew.containsKey(key.toString())) { @@ -205,9 +217,12 @@ public class MStatusLine extends X_AD_StatusLine implements ImmutablePOSupport + "WHERE slu.IsActive = 'Y' " + " AND sl.IsActive = 'Y' " + " AND slu.IsStatusLine = 'N' " - + " AND (slu.AD_Table_ID = ? OR (slu.AD_Window_ID=? AND slu.AD_Tab_ID=?) OR (slu.AD_Window_ID=? AND slu.AD_Tab_ID IS NULL)) " + + " AND (slu.AD_Table_ID = ? " + + " OR (slu.AD_Window_ID=? AND slu.AD_Tab_ID=?) " + + " OR (slu.AD_Window_ID=? AND slu.AD_Tab_ID IS NULL)" + + " OR slu.AD_InfoWindow_ID=?) " + "ORDER BY slu.SeqNo"; - int[] wlids = DB.getIDsEx(null, sql, table_ID, window_ID, tab_ID, window_ID); + int[] wlids = DB.getIDsEx(null, sql, table_ID, window_ID, tab_ID, window_ID, infoWindow_ID); if (wlids.length > 0) { ArrayList list = new ArrayList(); for (int wlid : wlids) { diff --git a/org.adempiere.base/src/org/compiere/model/MStatusLineUsedIn.java b/org.adempiere.base/src/org/compiere/model/MStatusLineUsedIn.java index 90d50018f4..6f96cfb1b4 100644 --- a/org.adempiere.base/src/org/compiere/model/MStatusLineUsedIn.java +++ b/org.adempiere.base/src/org/compiere/model/MStatusLineUsedIn.java @@ -71,11 +71,21 @@ public class MStatusLineUsedIn extends X_AD_StatusLineUsedIn if (getAD_Table_ID() > 0) { setAD_Window_ID(0); setAD_Tab_ID(0); - } else { - if (getAD_Window_ID() <= 0) { - log.saveError("SaveError", Msg.parseTranslation(getCtx(), "@FillMandatory@ @AD_Table_ID@ @AD_Window_ID@")); - return false; - } + setAD_InfoWindow_ID(0); + } + else if (getAD_Window_ID() > 0) { + setAD_Table_ID(0); + setAD_InfoWindow_ID(0); + } + else if (getAD_InfoWindow_ID() > 0) { + setAD_Table_ID(0); + setAD_Window_ID(0); + setAD_Tab_ID(0); + } + else { + log.saveError("SaveError", Msg.parseTranslation(getCtx(), "@FillMandatory@ @AD_Table_ID@ @AD_Window_ID@ @AD_InfoWindow_ID@")); + return false; + } return true; } diff --git a/org.adempiere.base/src/org/compiere/model/X_AD_StatusLineUsedIn.java b/org.adempiere.base/src/org/compiere/model/X_AD_StatusLineUsedIn.java index 64e5b67d0d..e4b7b60337 100644 --- a/org.adempiere.base/src/org/compiere/model/X_AD_StatusLineUsedIn.java +++ b/org.adempiere.base/src/org/compiere/model/X_AD_StatusLineUsedIn.java @@ -30,7 +30,7 @@ public class X_AD_StatusLineUsedIn extends PO implements I_AD_StatusLineUsedIn, /** * */ - private static final long serialVersionUID = 20230409L; + private static final long serialVersionUID = 20230619L; /** Standard Constructor */ public X_AD_StatusLineUsedIn (Properties ctx, int AD_StatusLineUsedIn_ID, String trxName) @@ -147,6 +147,34 @@ public class X_AD_StatusLineUsedIn extends PO implements I_AD_StatusLineUsedIn, return ii.intValue(); } + public org.compiere.model.I_AD_InfoWindow getAD_InfoWindow() throws RuntimeException + { + return (org.compiere.model.I_AD_InfoWindow)MTable.get(getCtx(), org.compiere.model.I_AD_InfoWindow.Table_ID) + .getPO(getAD_InfoWindow_ID(), get_TrxName()); + } + + /** Set Info Window. + @param AD_InfoWindow_ID Info and search/select Window + */ + public void setAD_InfoWindow_ID (int AD_InfoWindow_ID) + { + if (AD_InfoWindow_ID < 1) + set_Value (COLUMNNAME_AD_InfoWindow_ID, null); + else + set_Value (COLUMNNAME_AD_InfoWindow_ID, Integer.valueOf(AD_InfoWindow_ID)); + } + + /** Get Info Window. + @return Info and search/select Window + */ + public int getAD_InfoWindow_ID() + { + Integer ii = (Integer)get_Value(COLUMNNAME_AD_InfoWindow_ID); + if (ii == null) + return 0; + return ii.intValue(); + } + /** Set AD_StatusLineUsedIn. @param AD_StatusLineUsedIn_ID AD_StatusLineUsedIn */ diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java index 9e3e52f77c..84fcc975ec 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java @@ -49,6 +49,7 @@ import org.adempiere.webui.panel.ADForm; import org.adempiere.webui.panel.BroadcastMessageWindow; import org.adempiere.webui.panel.HeaderPanel; import org.adempiere.webui.panel.HelpController; +import org.adempiere.webui.panel.InfoPanel; import org.adempiere.webui.panel.TimeoutPanel; import org.adempiere.webui.part.ITabOnSelectHandler; import org.adempiere.webui.session.SessionManager; @@ -1034,6 +1035,11 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria @Override public void updateHelpContext(String ctxType, int recordId) { + this.updateHelpContext(ctxType, recordId, null); + } + + @Override + public void updateHelpContext(String ctxType, int recordId, InfoPanel infoPanel) { // don't show context for SetupWizard Form, is managed internally using wf and node ctxhelp if (recordId == SystemIDs.FORM_SETUP_WIZARD && X_AD_CtxHelp.CTXTYPE_Form.equals(ctxType)) return; @@ -1047,7 +1053,10 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria if (adwindow != null) { gridTab = adwindow.getADWindowContent().getActiveGridTab(); } - updateHelpQuickInfo(gridTab); + if(X_AD_CtxHelp.CTXTYPE_Info.equals(ctxType)) + updateHelpQuickInfo(infoPanel); + else + updateHelpQuickInfo(gridTab); } @Override @@ -1060,6 +1069,12 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria helpController.renderToolTip(hdr, desc, help, otherContent); } + @Override + public void updateHelpQuickInfo(InfoPanel infoPanel) { + if (isQuickInfoOpen) + helpController.renderQuickInfo(infoPanel); + } + @Override public void updateHelpQuickInfo(GridTab gridTab) { this.gridTab = gridTab; @@ -1084,7 +1099,8 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria @Override public void openInfo(int infoId) { super.openInfo(infoId); - updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoId); + // updateHelpContext is already called in InfoPanel onPageAttached method - IDEMPIERE-5772 +// updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoId); } @Override diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java index 490822bfd8..f21f9e55a6 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java @@ -19,6 +19,7 @@ import org.adempiere.webui.adwindow.ADWindow; import org.adempiere.webui.apps.ProcessDialog; import org.adempiere.webui.component.Window; import org.adempiere.webui.panel.ADForm; +import org.adempiere.webui.panel.InfoPanel; import org.adempiere.webui.part.UIPart; import org.compiere.model.GridField; import org.compiere.model.GridTab; @@ -220,6 +221,13 @@ public interface IDesktop extends UIPart { } } + /** + * update help content in help/info panel + * @param infoWindowId + * @param infoPanel + */ + public void updateHelpContext(String ctxType, int infoWindowId, InfoPanel infoPanel); + /** * update help content in help/info panel * @param ctxTypes @@ -247,6 +255,12 @@ public interface IDesktop extends UIPart { * @param gridTab */ public void updateHelpQuickInfo(GridTab gridTab); + + /** + * update quick info (status line) in help/info panel + * @param infoPanel + */ + public void updateHelpQuickInfo(InfoPanel infoPanel); /** * diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java index 207523cb28..dfefa9a565 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/editor/WEditor.java @@ -480,7 +480,7 @@ public abstract class WEditor implements EventListener, PropertyChangeLis * Fire ValueChangeEvent to ValueChangeListener in {@link #listeners} * @param event */ - protected void fireValueChange(ValueChangeEvent event) + public void fireValueChange(ValueChangeEvent event) { //copy to array to avoid concurrent modification exception ValueChangeListener[] vcl = new ValueChangeListener[listeners.size()]; diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java index b32f858e98..f37568df7e 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/info/InfoWindow.java @@ -1904,10 +1904,12 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL mField.addPropertyChangeListener(editor); mField.setValue(mField.getDefaultForPanel(), true); + editor.fireValueChange(new ValueChangeEvent(editor, mField.getColumnName(), mField.getOldValue(), mField.getValue())); if(infoColumn.isRange()) { mField2.addPropertyChangeListener(editor2); mField2.setValue(mField2.getDefaultForPanel(), true); + editor2.fireValueChange(new ValueChangeEvent(editor2, mField2.getColumnName(), mField2.getOldValue(), mField2.getValue())); } } // addSelectionColumn @@ -2199,15 +2201,19 @@ public class InfoWindow extends InfoPanel implements ValueChangeListener, EventL if (evt.getNewValue() == null) { Env.setContext(infoContext, p_WindowNo, editor.getColumnName(), ""); Env.setContext(infoContext, p_WindowNo, Env.TAB_INFO, editor.getColumnName(), ""); + paraCtxValues.put(editor.getColumnName(), ""); } else if (evt.getNewValue() instanceof Boolean) { Env.setContext(infoContext, p_WindowNo, editor.getColumnName(), (Boolean)evt.getNewValue()); Env.setContext(infoContext, p_WindowNo, Env.TAB_INFO, editor.getColumnName(), (Boolean)evt.getNewValue()); + paraCtxValues.put(editor.getColumnName(), (Boolean)evt.getNewValue()); } else if (evt.getNewValue() instanceof Timestamp) { Env.setContext(infoContext, p_WindowNo, editor.getColumnName(), (Timestamp)evt.getNewValue()); Env.setContext(infoContext, p_WindowNo, Env.TAB_INFO+"|"+editor.getColumnName(), (Timestamp)evt.getNewValue()); + paraCtxValues.put(editor.getColumnName(), (Timestamp)evt.getNewValue()); } else { Env.setContext(infoContext, p_WindowNo, editor.getColumnName(), evt.getNewValue().toString()); Env.setContext(infoContext, p_WindowNo, Env.TAB_INFO, editor.getColumnName(), evt.getNewValue().toString()); + paraCtxValues.put(editor.getColumnName(), evt.getNewValue().toString()); } dynamicDisplay(editor); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/HelpController.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/HelpController.java index 592fc61327..60a3f75c09 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/HelpController.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/HelpController.java @@ -662,11 +662,22 @@ public class HelpController } } - public void renderQuickInfo(GridTab gridTab) { - if (gridTab == null) { + public void renderQuickInfo(Object obj) { + if (obj == null) { pnlQuickInfo.setVisible(false); } else { - String widget = gridTab.getStatusLinesWidget(); + String widget = ""; + if(obj instanceof GridTab) { + widget = ((GridTab)obj).getStatusLinesWidget(); + } + else if(obj instanceof InfoPanel) { + widget = ((InfoPanel)obj).getStatusLinesWidget(); + } + else { + pnlQuickInfo.setVisible(false); + return; + } + if (widget == null) { pnlQuickInfo.setVisible(false); } else { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java index c3e23af21b..0ca6dc220a 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/panel/InfoPanel.java @@ -35,6 +35,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Vector; import java.util.logging.Level; @@ -73,6 +74,7 @@ import org.adempiere.webui.window.Dialog; import org.compiere.minigrid.ColumnInfo; import org.compiere.minigrid.IDColumn; import org.compiere.minigrid.UUIDColumn; +import org.compiere.model.AccessSqlParser.TableInfo; import org.compiere.model.GridField; import org.compiere.model.InfoColumnVO; import org.compiere.model.InfoRelatedVO; @@ -82,10 +84,10 @@ import org.compiere.model.MPInstance; import org.compiere.model.MProcess; import org.compiere.model.MRefTable; import org.compiere.model.MRole; +import org.compiere.model.MStatusLine; import org.compiere.model.MSysConfig; import org.compiere.model.MTable; import org.compiere.model.X_AD_CtxHelp; -import org.compiere.model.AccessSqlParser.TableInfo; import org.compiere.process.ProcessInfo; import org.compiere.process.ProcessInfoLog; import org.compiere.process.ProcessInfoUtil; @@ -141,6 +143,8 @@ public abstract class InfoPanel extends Window implements EventListener, protected static final String ON_USER_QUERY_ATTR = "ON_USER_QUERY"; protected static final String INFO_QUERY_TIME_OUT_ERROR = "InfoQueryTimeOutError"; protected static final String COLUMN_VISIBLE_ORIGINAL = "column.visible.original"; + protected static final String ROW_CTX_VARIABLE_PREFIX = "_IWInfo_"; + protected static final String ROW_ID_CTX_VARIABLE_NAME = "_IWInfoIDs_Selected"; private final static int DEFAULT_PAGE_SIZE = 100; private final static int DEFAULT_PAGE_PRELOAD = 4; @@ -614,6 +618,19 @@ public abstract class InfoPanel extends Window implements EventListener, */ private boolean isUseEscForTabClosing = MSysConfig.getBooleanValue(MSysConfig.USE_ESC_FOR_TAB_CLOSING, false, Env.getAD_Client_ID(Env.getCtx())); + /** + * Contains the indexes of selected row, maintains the selection order + */ + protected ArrayList m_rowSelectionOrder = new ArrayList(); + /** + * Number of selected rows + */ + protected int m_selectedCount = 0; + /** + * Values that will be put into the context on re-query + */ + protected HashMap paraCtxValues = new HashMap(); + /** * Loaded correctly * @return true if loaded OK @@ -647,10 +664,7 @@ public abstract class InfoPanel extends Window implements EventListener, */ public void setStatusSelected () { - if (!p_multipleSelection) - return; - - int selectedCount = recordSelectedData.size(); + int selectedCount = p_multipleSelection ? recordSelectedData.size() : 0; for (int rowIndex = 0; rowIndex < contentPanel.getModel().getRowCount(); rowIndex++){ Object keyCandidate = getColumnValue(rowIndex); @@ -659,16 +673,20 @@ public abstract class InfoPanel extends Window implements EventListener, List candidateRecord = (List)contentPanel.getModel().get(rowIndex); if (contentPanel.getModel().isSelected(candidateRecord)){ - if (!recordSelectedData.containsKey(keyCandidate)){ + if(!p_multipleSelection) { + selectedCount++; + break; + } + else if (!recordSelectedData.containsKey(keyCandidate)){ selectedCount++; } - }else{ + }else if (p_multipleSelection){ if (recordSelectedData.containsKey(keyCandidate)){// unselected record selectedCount--; } } } - + m_selectedCount = selectedCount; String msg = Msg.getMsg(Env.getCtx(), "IWStatusSelected", new Object [] {String.valueOf(selectedCount)}); statusBar.setSelectedRowNumber(msg); btnSelectAll.setEnabled(m_count > 0 && selectedCount != m_count); @@ -2218,6 +2236,10 @@ public abstract class InfoPanel extends Window implements EventListener, } enableButtons(); + if(!isLookup()) { + updateRowSelectionOrder(); + updateContext(false); + } }else if (event.getTarget() == contentPanel && event.getName().equals("onAfterRender")){ //IDEMPIERE-1334 at this event selected item from listBox and model is sync @@ -2260,6 +2282,7 @@ public abstract class InfoPanel extends Window implements EventListener, else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_REFRESH))) { recordSelectedData.clear(); + setStatusSelected(); onUserQuery(); } else if (event.getTarget().equals(confirmPanel.getButton(ConfirmPanel.A_CANCEL))) @@ -2307,10 +2330,18 @@ public abstract class InfoPanel extends Window implements EventListener, else if (ON_SELECT_ALL_RECORDS.equals(event.getName())) { selectAllRecords(); + if(!isLookup()) { + updateRowSelectionOrder(); + updateContext(false); + } } else if (event.getTarget().equals(btnDeSelectAll)) { deSelectAllRecords(); + if(!isLookup()) { + updateRowSelectionOrder(); + updateContext(false); + } } // IDEMPIERE-1334 handle event click into process button start else if (ON_RUN_PROCESS.equals(event.getName())){ @@ -2380,7 +2411,7 @@ public abstract class InfoPanel extends Window implements EventListener, else if (event.getName().equals(WindowContainer.ON_WINDOW_CONTAINER_SELECTION_CHANGED_EVENT)) { if (infoWindow != null) - SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID()); + SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID(), this); else SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Home, 0); } @@ -2859,6 +2890,10 @@ public abstract class InfoPanel extends Window implements EventListener, isQueryByUser = false; hideBusyDialog(); } + if(!isLookup()) { + updateRowSelectionOrder(); + updateContext(true); + } } /** @@ -3222,11 +3257,11 @@ public abstract class InfoPanel extends Window implements EventListener, @Override public void onPageAttached(Page newpage, Page oldpage) { super.onPageAttached(newpage, oldpage); - if (newpage != null) { + if (newpage != null && !isLookup()) { if (infoWindow != null) - SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID()); + SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID(), this); else - SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Home, 0); + SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Home, 0, this); } SessionManager.getSessionApplication().getKeylistener().addEventListener(Events.ON_CTRL_KEY, this); } @@ -3299,5 +3334,129 @@ public abstract class InfoPanel extends Window implements EventListener, if (btnDeSelectAll != null) btnDeSelectAll.setVisible(multipleSelection); } + + + /** + * Widget support + * Depending on Window/Tab returns widget lines info + * @return info + */ + public String getStatusLinesWidget() { + if(infoWindow == null) + return null; + MStatusLine[] wls = MStatusLine.getStatusLinesWidget(0, 0, 0, infoWindow.getAD_InfoWindow_ID()); + if (wls != null && wls.length > 0) + { + StringBuilder lines = new StringBuilder(); + for (MStatusLine wl : wls) { + String line = wl.parseLine(getWindowNo()); + if (line != null) { + lines.append(line).append("
"); + } + } + if (lines.length() > 0) + return lines.toString(); + } + return null; + } // getWidgetLines + + /** + * Update row selection order + */ + protected void updateRowSelectionOrder() { + if(m_selectedCount == m_count) { + for(int rowIdx = 0; rowIdx < m_count; rowIdx++) { + if(!m_rowSelectionOrder.contains(rowIdx)) + m_rowSelectionOrder.add((Integer)rowIdx); + } + } + else if(m_selectedCount == 0) { + m_rowSelectionOrder.clear(); + } + else { + if(!p_multipleSelection) { + m_rowSelectionOrder.clear(); + m_rowSelectionOrder.add(m_lastSelectedIndex); + } + else { + if(m_rowSelectionOrder.contains(m_lastSelectedIndex)) + m_rowSelectionOrder.remove((Integer)m_lastSelectedIndex); + else + m_rowSelectionOrder.add(m_lastSelectedIndex); + } + } + } // updateRowSelectionOrder + + /** + * Put values from the selected row into the context + */ + protected void updateContext(boolean checkQueryCriteria) { + Map> rowInfo = getSelectedRowInfo(); + List lastSelectedRow = m_rowSelectionOrder.size() > 0 ? rowInfo.get(getRowKeyAt(m_rowSelectionOrder.get(m_rowSelectionOrder.size() - 1))) : null; + + if(checkQueryCriteria) { + // put parameter values into the context + for(Map.Entry e : paraCtxValues.entrySet()) { + String columnName = e.getKey(); + Object value = e.getValue(); + setContext(columnName, value); + } + } + + // put the values of the last selected row into the context + for(int i = 0; i < p_layout.length; i++) { + String columnName = p_layout[i].getColumnName(); + Object value = lastSelectedRow != null ? lastSelectedRow.get(i) : null; + setContext(ROW_CTX_VARIABLE_PREFIX + columnName, value); + } + // add selected IDs to the context + setContext(ROW_ID_CTX_VARIABLE_NAME, getSelectedIDsForCtx()); + + // update Quick Info widget + if (infoWindow != null) + SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Info, infoWindow.getAD_InfoWindow_ID(), this); + else + SessionManager.getAppDesktop().updateHelpContext(X_AD_CtxHelp.CTXTYPE_Home, 0, this); + } // updateContext + + /** + * Set context + * @param columnName + * @param value + */ + protected void setContext(String columnName, Object value) { + if(value instanceof KeyNamePair) + value = ((KeyNamePair)value).getKey(); + else if(value instanceof IDColumn) + value = ((IDColumn)value).getRecord_ID(); + + if (value == null) { + Env.setContext(Env.getCtx(), p_WindowNo, columnName, ""); + } else if (value instanceof Boolean) { + Env.setContext(Env.getCtx(), p_WindowNo, columnName, (Boolean)value); + } else if (value instanceof Timestamp) { + Env.setContext(Env.getCtx(), p_WindowNo, columnName, (Timestamp)value); + } else { + Env.setContext(Env.getCtx(), p_WindowNo, columnName, value.toString()); + } + } + + /** + * Get a comma-separated string of selected IDs + * @return String ctx value + */ + protected String getSelectedIDsForCtx() { + String returnVal = null; + + for(int idx : m_rowSelectionOrder) { + String selectedID = Objects.toString(getRowKeyAt(idx)); + if(returnVal == null) + returnVal = selectedID; + else + returnVal += "," + selectedID; + } + return returnVal; + } + } // Info