diff --git a/migration/i8.2z/oracle/202102180540_IDEMPIERE-4705.sql b/migration/i8.2z/oracle/202102180540_IDEMPIERE-4705.sql new file mode 100644 index 0000000000..0f34e00ff3 --- /dev/null +++ b/migration/i8.2z/oracle/202102180540_IDEMPIERE-4705.sql @@ -0,0 +1,10 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + +-- Feb 16, 2021, 5:14:50 PM MYT +INSERT INTO AD_ToolBarButton (AD_Client_ID,AD_Org_ID,Created,CreatedBy,ComponentName,IsActive,AD_ToolBarButton_ID,Name,Updated,UpdatedBy,IsCustomization,KeyStroke_KeyCode,KeyStroke_Modifiers,AD_ToolBarButton_UU,Action,SeqNo,IsAdvancedButton,IsAddSeparator,EntityType,IsShowMore) VALUES (0,0,TO_DATE('2021-02-16 17:14:49','YYYY-MM-DD HH24:MI:SS'),100,'Toggle','Y',200112,'Detail - Multi',TO_DATE('2021-02-16 17:14:49','YYYY-MM-DD HH24:MI:SS'),100,'N',0,0,'2245209c-d9eb-4948-b622-4834281bc244','D',90,'N','N','D','N') +; + +SELECT register_migration_script('202102180540_IDEMPIERE-4705.sql') FROM dual +; + diff --git a/migration/i8.2z/postgresql/202102180540_IDEMPIERE-4705.sql b/migration/i8.2z/postgresql/202102180540_IDEMPIERE-4705.sql new file mode 100644 index 0000000000..808c279ccf --- /dev/null +++ b/migration/i8.2z/postgresql/202102180540_IDEMPIERE-4705.sql @@ -0,0 +1,7 @@ +-- Feb 16, 2021, 5:14:50 PM MYT +INSERT INTO AD_ToolBarButton (AD_Client_ID,AD_Org_ID,Created,CreatedBy,ComponentName,IsActive,AD_ToolBarButton_ID,Name,Updated,UpdatedBy,IsCustomization,KeyStroke_KeyCode,KeyStroke_Modifiers,AD_ToolBarButton_UU,"action",SeqNo,IsAdvancedButton,IsAddSeparator,EntityType,IsShowMore) VALUES (0,0,TO_TIMESTAMP('2021-02-16 17:14:49','YYYY-MM-DD HH24:MI:SS'),100,'Toggle','Y',200112,'Detail - Multi',TO_TIMESTAMP('2021-02-16 17:14:49','YYYY-MM-DD HH24:MI:SS'),100,'N',0,0,'2245209c-d9eb-4948-b622-4834281bc244','D',90,'N','N','D','N') +; + +SELECT register_migration_script('202102180540_IDEMPIERE-4705.sql') FROM dual +; + diff --git a/org.adempiere.base/src/org/compiere/model/GridTab.java b/org.adempiere.base/src/org/compiere/model/GridTab.java index b3ca4c18ea..30457a4001 100644 --- a/org.adempiere.base/src/org/compiere/model/GridTab.java +++ b/org.adempiere.base/src/org/compiere/model/GridTab.java @@ -2403,7 +2403,11 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable // log.fine("fini - " + e.toString()); } // fireDataStatusChanged - private void updateDataStatusEventProperties(DataStatusEvent e) { + /** + * update {@link DataStatusEvent} properties from gridTab + * @param e + */ + public void updateDataStatusEventProperties(DataStatusEvent e) { e.Created = (Timestamp)getValue("Created"); e.CreatedBy = (Integer)getValue("CreatedBy"); e.Updated = (Timestamp)getValue("Updated"); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java index ea1d2eec26..ddbb4c8eae 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java @@ -1387,7 +1387,11 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer tabPanel.getGridView().invalidateGridView(); } if (!tabPanel.isGridView()) { - tabPanel.switchRowPresentation(); + if (detailPane.getSelectedPanel().isToggleToFormView()) { + detailPane.getSelectedPanel().afterToggle(); + } else { + tabPanel.switchRowPresentation(); + } } } } @@ -1932,6 +1936,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer * * @return true if the detailpane is visible */ + @Override public boolean isDetailVisible() { if (formContainer.getSouth() == null || !formContainer.getSouth().isVisible() || !formContainer.getSouth().isOpen()) { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java index afc14f20bc..1e2a16d336 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java @@ -24,6 +24,7 @@ import java.util.LinkedHashMap; import java.util.List; import org.adempiere.util.Callback; +import org.adempiere.webui.adwindow.DetailPane.Tabpanel; import org.adempiere.webui.component.ADTabListModel; import org.adempiere.webui.component.ADTabListModel.ADTabLabel; import org.adempiere.webui.util.ZKUpdateUtil; @@ -123,20 +124,34 @@ public class CompositeADTabbox extends AbstractADTabbox public void onCallback(Boolean result) { if (result) { if (getSelectedDetailADTabpanel().getGridTab().isSingleRow()) { - onEditDetail(row, true); - if (!adWindowPanel.getActiveGridTab().isNew()) - adWindowPanel.onNew(); + if (headerTab.isDetailVisible() && headerTab.getDetailPane().getSelectedPanel().isToggleToFormView()) { + if (!getSelectedDetailADTabpanel().getGridTab().isNew()) { + getSelectedDetailADTabpanel().getGridTab().dataNew(false); + getSelectedDetailADTabpanel().dynamicDisplay(0); + focusToTabpanel(getSelectedDetailADTabpanel()); + } + } else { + onEditDetail(row, true); + if (!adWindowPanel.getActiveGridTab().isNew()) + adWindowPanel.onNew(); + } } else { if (!getSelectedDetailADTabpanel().getGridTab().isNew()) { getSelectedDetailADTabpanel().getGridTab().dataNew(false); - if (!((ADTabpanel)headerTab).isDetailVisible()) { + if (!headerTab.isDetailVisible()) { String uuid = headerTab.getDetailPane().getParent().getUuid(); String vid = getSelectedDetailADTabpanel().getGridView().getUuid(); String script = "setTimeout(function(){zk('#"+uuid+"').$().setOpen(true);setTimeout(function(){var v=zk('#" + vid + "').$();var e=new zk.Event(v,'onEditCurrentRow',null,{toServer:true});zAu.send(e);},200);},200)"; Clients.response(new AuScript(script)); } else { - getSelectedDetailADTabpanel().getGridView().onEditCurrentRow(); + boolean isFormView = headerTab.getDetailPane().getSelectedPanel().isToggleToFormView(); + if (isFormView) { + getSelectedDetailADTabpanel().dynamicDisplay(0); + focusToTabpanel(getSelectedDetailADTabpanel()); + } else { + getSelectedDetailADTabpanel().getGridView().onEditCurrentRow(); + } } } } @@ -178,6 +193,27 @@ public class CompositeADTabbox extends AbstractADTabbox } }); } + else if (DetailPane.ON_RECORD_NAVIGATE_EVENT.equals(event.getName())) { + final String action = (String) event.getData(); + adWindowPanel.saveAndNavigate(new Callback () { + @Override + public void onCallback(Boolean result) + { + if (result) + { + if ("first".equalsIgnoreCase(action)) { + getSelectedDetailADTabpanel().getGridTab().navigate(0); + } else if ("previous".equalsIgnoreCase(action)) { + getSelectedDetailADTabpanel().getGridTab().navigateRelative(-1); + } else if ("next".equalsIgnoreCase(action)) { + getSelectedDetailADTabpanel().getGridTab().navigateRelative(1); + } else if ("last".equalsIgnoreCase(action)) { + getSelectedDetailADTabpanel().getGridTab().navigate(getSelectedDetailADTabpanel().getGridTab().getRowCount()-1); + } + } + } + }); + } } private void onDelete() { @@ -242,6 +278,13 @@ public class CompositeADTabbox extends AbstractADTabbox return detailPane; } + private void focusToTabpanel(IADTabpanel adTabPanel ) { + if (adTabPanel != null && adTabPanel instanceof HtmlBasedComponent) { + final HtmlBasedComponent comp = (HtmlBasedComponent) adTabPanel; + Executions.schedule(layout.getDesktop(), e -> {comp.focus();}, new Event("onFocusDefer")); + } + } + protected void onEditDetail(int row, boolean formView) { int oldIndex = selectedIndex; @@ -604,13 +647,25 @@ public class CompositeADTabbox extends AbstractADTabbox detailPane.setSelectedIndex(0); activateDetailIfVisible(); } else { - if (((ADTabpanel) headerTab).isDetailVisible() && detailPane.getSelectedADTabpanel() != null) { + if (headerTab.isDetailVisible() && detailPane.getSelectedADTabpanel() != null) { IADTabpanel selectDetailPanel = detailPane.getSelectedADTabpanel(); - if (!selectDetailPanel.isVisible()) { + if (!selectDetailPanel.isVisible()) { selectDetailPanel.setVisible(true); } if (!selectDetailPanel.isGridView()) { - selectDetailPanel.switchRowPresentation(); + boolean switchToGrid = true; + Component parent = selectDetailPanel.getParent(); + while (parent != null) { + if (parent instanceof DetailPane.Tabpanel) { + DetailPane.Tabpanel dtp = (Tabpanel) parent; + switchToGrid = !dtp.isToggleToFormView(); + dtp.afterToggle(); + break; + } + parent = parent.getParent(); + } + if (switchToGrid) + selectDetailPanel.switchRowPresentation(); } if (selectDetailPanel instanceof ADTabpanel) { @@ -948,8 +1003,25 @@ public class CompositeADTabbox extends AbstractADTabbox if (row != null) gtr.setCurrentRow(row); } - if (wasForm && tabPanel.getTabLevel() == 0 && headerTab.getTabLevel() != 0) // maintain form on header when zooming to a detail tab - tabPanel.switchRowPresentation(); + if (wasForm) { + // maintain form on header when zooming to a detail tab + if (tabPanel.getTabLevel() == 0 && headerTab.getTabLevel() != 0) { + tabPanel.switchRowPresentation(); + } else { + Component parent = tabPanel.getParent(); + while (parent != null) { + if (parent instanceof DetailPane.Tabpanel) { + DetailPane.Tabpanel dtp = (Tabpanel) parent; + if (dtp.isToggleToFormView()) { + tabPanel.switchRowPresentation(); + dtp.afterToggle(); + } + break; + } + parent = parent.getParent(); + } + } + } } private void invalidateTabPanel(IADTabpanel tabPanel) { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java index 70bfa14ce9..9433635d46 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/DetailPane.java @@ -1,12 +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; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.adempiere.base.IServiceHolder; import org.adempiere.webui.ClientInfo; @@ -18,7 +40,6 @@ import org.adempiere.webui.component.Label; import org.adempiere.webui.component.Panel; import org.adempiere.webui.component.Tab; import org.adempiere.webui.component.Tabbox; -import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.ToolBar; import org.adempiere.webui.component.ToolBarButton; import org.adempiere.webui.component.Window; @@ -26,6 +47,9 @@ import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.ZKUpdateUtil; import org.adempiere.webui.window.CustomizeGridViewDialog; +import org.adempiere.webui.window.WRecordInfo; +import org.compiere.model.DataStatusEvent; +import org.compiere.model.GridTab; import org.compiere.model.MToolBarButton; import org.compiere.util.Env; import org.compiere.util.Msg; @@ -36,6 +60,7 @@ import org.zkoss.zk.au.out.AuScript; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; +import org.zkoss.zk.ui.HtmlBasedComponent; import org.zkoss.zk.ui.IdSpace; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.event.Event; @@ -44,8 +69,11 @@ import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.KeyEvent; import org.zkoss.zk.ui.sys.ExecutionCtrl; import org.zkoss.zk.ui.util.Clients; +import org.zkoss.zul.A; import org.zkoss.zul.Div; import org.zkoss.zul.Hbox; +import org.zkoss.zul.Hlayout; +import org.zkoss.zul.Popup; import org.zkoss.zul.Separator; import org.zkoss.zul.Space; import org.zkoss.zul.Tabpanels; @@ -57,10 +85,11 @@ import org.zkoss.zul.Toolbar; * */ public class DetailPane extends Panel implements EventListener, IdSpace { + /** - * + * generated serial id */ - private static final long serialVersionUID = 8807016961597158305L; + private static final long serialVersionUID = -6994505162094868814L; private static final String BTN_PROCESS_ID = "BtnProcess"; @@ -76,9 +105,11 @@ public class DetailPane extends Panel implements EventListener, IdSpace { private static final String BTN_CUSTOMIZE_ID = "BtnCustomize"; + private static final String BTN_TOGGLE_ID = "BtnToggle"; + private static final String TABBOX_ONSELECT_ATTRIBUTE = "detailpane.tabbox.onselect"; - public static final String ON_POST_SELECT_TAB_EVENT = "onPostSelectTab"; + private static final String ON_POST_SELECT_TAB_EVENT = "onPostSelectTab"; private static final String STATUS_TEXT_ATTRIBUTE = "status.text"; @@ -91,9 +122,9 @@ public class DetailPane extends Panel implements EventListener, IdSpace { private static final String PROCESS_IMAGE = "images/Process16.png"; private static final String SAVE_IMAGE = "images/Save16.png"; private static final String QUICK_FORM_IMAGE = "images/QuickForm16.png"; + private static final String TOGGLE_IMAGE = "images/Multi16.png"; - private ToolBarButton btnNew; private long prevKeyEventTime = 0; private KeyEvent prevKeyEvent; @@ -121,9 +152,11 @@ public class DetailPane extends Panel implements EventListener, IdSpace { public static final String ON_QUICK_FORM_EVENT = "onQuickForm"; - private HashMap buttons = new HashMap(); - private List toolbarCustomButtons = new ArrayList(); - + public static final String ON_RECORD_NAVIGATE_EVENT = "onRecordNavigate"; + + /** + * default constructor + */ public DetailPane() { tabbox = new Tabbox(); tabbox.setParent(this); @@ -280,25 +313,24 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } Tabpanel tp = new Tabpanel(); tabpanels.appendChild(tp); - tp.setSclass("adwindow-detailpane-tabpanel"); - ToolBar toolbar = new ToolBar(); - tp.appendChild(toolbar); - btnNew = new ToolBarButton(); + ToolBar toolbar = tp.getToolbar(); + + HashMap buttons = new HashMap(); + ToolBarButton button = new ToolBarButton(); if (ThemeManager.isUseFontIconForImage()) - btnNew.setIconSclass("z-icon-New"); + button.setIconSclass("z-icon-New"); else - btnNew.setImage(ThemeManager.getThemeResource(NEW_IMAGE)); - btnNew.setId(BTN_NEW_ID); - btnNew.addEventListener(Events.ON_CLICK, new EventListener() { + button.setImage(ThemeManager.getThemeResource(NEW_IMAGE)); + button.setId(BTN_NEW_ID); + button.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - onNew(); + if (event.getTarget().isVisible()) + onNew(); } }); - btnNew.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "New")) + " Shift+Alt+N"); - buttons.put(BTN_NEW_ID.substring(3, BTN_NEW_ID.length()), btnNew); - - ToolBarButton button = new ToolBarButton(); + button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "New")) + " Shift+Alt+N"); + buttons.put(BTN_NEW_ID.substring(3, BTN_NEW_ID.length()), button); button = new ToolBarButton(); if (ThemeManager.isUseFontIconForImage()) @@ -309,10 +341,11 @@ public class DetailPane extends Panel implements EventListener, IdSpace { button.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - onEdit(true); + if (event.getTarget().isVisible()) + onEdit(true); } }); - button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "EditRecord"))); + button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "EditRecord")) + " Shift+Alt+E"); buttons.put(BTN_EDIT_ID.substring(3, BTN_EDIT_ID.length()), button); button = new ToolBarButton(); @@ -324,11 +357,13 @@ public class DetailPane extends Panel implements EventListener, IdSpace { button.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - Event openEvent = new Event(ON_DELETE_EVENT, DetailPane.this); - eventListener.onEvent(openEvent); + if (event.getTarget().isVisible()) { + Event openEvent = new Event(ON_DELETE_EVENT, DetailPane.this); + eventListener.onEvent(openEvent); + } } }); - button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Delete"))); + button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Delete")) + " Shift+Alt+D"); buttons.put(BTN_DELETE_ID.substring(3, BTN_DELETE_ID.length()), button); button = new ToolBarButton(); @@ -340,13 +375,14 @@ public class DetailPane extends Panel implements EventListener, IdSpace { button.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - Event openEvent = new Event(ON_SAVE_EVENT, DetailPane.this); - eventListener.onEvent(openEvent); + if (event.getTarget().isVisible()) { + Event openEvent = new Event(ON_SAVE_EVENT, DetailPane.this); + eventListener.onEvent(openEvent); + } } }); - button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Save"))); + button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Save")) + " Shift+Alt+S"); buttons.put(BTN_SAVE_ID.substring(3, BTN_SAVE_ID.length()), button); - if (!tabPanel.getGridTab().isSortTab()) { button = new ToolBarButton(); @@ -358,10 +394,11 @@ public class DetailPane extends Panel implements EventListener, IdSpace { button.addEventListener(Events.ON_CLICK, new EventListener() { @Override public void onEvent(Event event) throws Exception { - onProcess(event.getTarget()); + if (event.getTarget().isVisible()) + onProcess(event.getTarget()); } }); - button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Process"))); + button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Process")) + " Shift+Alt+O"); buttons.put(BTN_PROCESS_ID.substring(3, BTN_PROCESS_ID.length()), button); } @@ -376,8 +413,10 @@ public class DetailPane extends Panel implements EventListener, IdSpace { @Override public void onEvent(Event event) throws Exception { - Event openEvent = new Event(ON_QUICK_FORM_EVENT, DetailPane.this); - eventListener.onEvent(openEvent); + if (event.getTarget().isVisible()) { + Event openEvent = new Event(ON_QUICK_FORM_EVENT, DetailPane.this); + eventListener.onEvent(openEvent); + } } }); button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "QuickForm"))); @@ -390,9 +429,20 @@ public class DetailPane extends Panel implements EventListener, IdSpace { else button.setImage(ThemeManager.getThemeResource(CUSTOMIZE_IMAGE)); button.setId(BTN_CUSTOMIZE_ID); - button.addEventListener(Events.ON_CLICK, e -> onCustomize(e)); + button.addEventListener(Events.ON_CLICK, e -> { if(e.getTarget().isVisible()) onCustomize(e); }); button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Customize"))); buttons.put(BTN_CUSTOMIZE_ID.substring(3, BTN_CUSTOMIZE_ID.length()), button); + + // ADD toggle grid button + button = new ToolBarButton(); + if (ThemeManager.isUseFontIconForImage()) + button.setIconSclass("z-icon-Multi"); + else + button.setImage(ThemeManager.getThemeResource(TOGGLE_IMAGE)); + button.setId(BTN_TOGGLE_ID); + button.addEventListener(Events.ON_CLICK, e -> { if(e.getTarget().isVisible()) onToggle(e); }); + button.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Toggle")) + " Shift+Alt+T"); + buttons.put(BTN_TOGGLE_ID.substring(3, BTN_TOGGLE_ID.length()), button); MToolBarButton[] officialButtons = MToolBarButton.getToolbarButtons("D", null); for (MToolBarButton toolbarButton : officialButtons) { @@ -428,7 +478,7 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } ToolbarCustomButton toolbarCustomBtn = new ToolbarCustomButton(toolbarButton, btn, actionId, tabPanel.getGridTab().getWindowNo()); - toolbarCustomButtons.add(toolbarCustomBtn); + tp.toolbarCustomButtons.add(toolbarCustomBtn); toolbar.appendChild(btn); } @@ -456,7 +506,15 @@ public class DetailPane extends Panel implements EventListener, IdSpace { messageContainers.put(tabLabel.AD_Tab_ID, messageContainer); tabPanel.setAttribute("AD_Tab_ID", tabLabel.AD_Tab_ID); - tp.appendChild(tabPanel); + if (ClientInfo.isMobile() && ClientInfo.maxWidth(ClientInfo.SMALL_WIDTH)) { + tp.createOverflowButton(); + } + + RecordToolbar recordToolbar = new RecordToolbar(tabPanel.getGridTab()); + recordToolbar.addEventListener(ON_RECORD_NAVIGATE_EVENT, eventListener); + tp.setRecordToolbar(recordToolbar); + tp.setADTabpanel(tabPanel); + if (tabPanel.getGridView() != null) { tabPanel.addEventListener(ADTabpanel.ON_DYNAMIC_DISPLAY_EVENT, this); tabPanel.getGridView().addEventListener(ON_EDIT_EVENT, new EventListener() { @@ -470,6 +528,30 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } } + /** + * toggle between grid and form view + * @param e + */ + protected void onToggle(Event e) { + var adTabPanel = getSelectedADTabpanel(); + if(!(adTabPanel instanceof ADSortTab)) { + adTabPanel.switchRowPresentation(); + getSelectedPanel().getToolbarButton(BTN_CUSTOMIZE_ID).setDisabled(!adTabPanel.isGridView()); + + Tabpanel tabPanel = (Tabpanel) tabbox.getSelectedTabpanel(); + tabPanel.setToggleToFormView(!adTabPanel.isGridView()); + tabPanel.afterToggle(); + + if (adTabPanel != null && adTabPanel instanceof HtmlBasedComponent) { + ((HtmlBasedComponent)adTabPanel).focus(); + } + } + } + + /** + * open customize grid dialog + * @param e + */ protected void onCustomize(Event e) { if (getSelectedADTabpanel() instanceof ADTabpanel) { ADTabpanel tabPanel = (ADTabpanel) getSelectedADTabpanel(); @@ -477,6 +559,10 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } } + /** + * open process dropdown + * @param button + */ protected void onProcess(Component button) { ProcessButtonPopup popup = new ProcessButtonPopup(); ADTabpanel adtab = (ADTabpanel) getSelectedADTabpanel(); @@ -539,6 +625,14 @@ public class DetailPane extends Panel implements EventListener, IdSpace { return null; } + /** + * + * @return {@link Tabpanel} + */ + public Tabpanel getSelectedPanel() { + return (Tabpanel) tabbox.getSelectedPanel(); + } + /** * * @param status @@ -589,6 +683,13 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } messageContainer.appendChild(new Space()); + + if (!tabPanel.isGridView()) { + Tabpanel tp = (Tabpanel) tabbox.getSelectedTabpanel(); + if (tp.getRecordToolbar() != null) { + tp.getRecordToolbar().dynamicDisplay(); + } + } } private String buildLabelText(String statusText) { @@ -721,8 +822,8 @@ public class DetailPane extends Panel implements EventListener, IdSpace { int index = getSelectedIndex(); if (index < 0 || index >= getTabcount()) return; - Tabpanel tabpanel = tabbox.getTabpanel(index); - Toolbar toolbar = (Toolbar) tabpanel.getFirstChild(); + Tabpanel tabpanel = (Tabpanel) tabbox.getTabpanel(index); + Toolbar toolbar = tabpanel.getToolbar(); IADTabpanel adtab = getADTabpanel(index); if (adtab == null) @@ -743,7 +844,7 @@ public class DetailPane extends Panel implements EventListener, IdSpace { deleteRecord = adtab.getGridTab().isDeleteRecord(); } boolean enableDelete = !changed && deleteRecord && !adtab.getGridTab().isSortTab(); - boolean enableCustomize = !adtab.getGridTab().isSortTab(); + boolean enableCustomize = !adtab.getGridTab().isSortTab() && adtab.isGridView(); ADWindow adwindow = ADWindow.findADWindow(this); if (adwindow == null) @@ -764,9 +865,10 @@ public class DetailPane extends Panel implements EventListener, IdSpace { btn.setDisabled(!adtab.needSave(true, false)); } else if (BTN_CUSTOMIZE_ID.equals(btn.getId())) { btn.setDisabled(!enableCustomize); - } - else if (BTN_QUICK_FORM_ID.equals(btn.getId())) { + } else if (BTN_QUICK_FORM_ID.equals(btn.getId())) { btn.setDisabled(!(adtab.isEnableQuickFormButton() && !adtab.getGridTab().isReadOnly())); + } else if (BTN_TOGGLE_ID.equals(btn.getId())) { + btn.setDisabled(adtab.getGridTab().isSortTab()); } if (windowRestrictList.contains(btn.getId())) { btn.setVisible(false); @@ -783,8 +885,8 @@ public class DetailPane extends Panel implements EventListener, IdSpace { int index = getSelectedIndex(); if (index < 0 || index >= getTabcount()) return; - Tabpanel tabpanel = tabbox.getTabpanel(index); - Toolbar toolbar = (Toolbar) tabpanel.getFirstChild(); + Tabpanel tabpanel = (Tabpanel) tabbox.getTabpanel(index); + Toolbar toolbar = tabpanel.getToolbar(); IADTabpanel adtab = getADTabpanel(index); if (adtab == null) return; @@ -886,8 +988,8 @@ public class DetailPane extends Panel implements EventListener, IdSpace { int index = getSelectedIndex(); if (index < 0 || index >= getTabcount()) return; - Tabpanel tabpanel = tabbox.getTabpanel(index); - Toolbar toolbar = (Toolbar) tabpanel.getFirstChild(); + Tabpanel tabpanel = (Tabpanel) tabbox.getTabpanel(index); + Toolbar toolbar = tabpanel.getToolbar(); for(Component c : toolbar.getChildren()) { if (c instanceof ToolBarButton) { ToolBarButton btn = (ToolBarButton) c; @@ -907,19 +1009,48 @@ public class DetailPane extends Panel implements EventListener, IdSpace { return null; } + /** + * add new record + * @throws Exception + */ public void onNew() throws Exception { Event openEvent = new Event(ON_NEW_EVENT, DetailPane.this); eventListener.onEvent(openEvent); } - public static final int VK_N = 0x4E; + private static final int VK_N = 0x4E; + private static final int VK_T = 0x54; + private static final int VK_E = 0x45; + private static final int VK_S = 0x53; + private static final int VK_D = 0x44; + private static final int VK_O = 0x4F; private void onCtrlKeyEvent(KeyEvent keyEvent) { ToolBarButton btn = null; if (keyEvent.isAltKey() && !keyEvent.isCtrlKey() && keyEvent.isShiftKey()) { // Shift+Alt key if (keyEvent.getKeyCode() == VK_N) { // Shift+Alt+N - btn = btnNew; + btn = getSelectedPanel().getToolbarButton(BTN_NEW_ID); + } else if (keyEvent.getKeyCode() == VK_T) { + btn = getSelectedPanel().getToolbarButton(BTN_TOGGLE_ID); + } else if (keyEvent.getKeyCode() == KeyEvent.HOME) { + btn = getSelectedPanel().getRecordToolbar().btnFirst; + } else if (keyEvent.getKeyCode() == KeyEvent.END) { + btn = getSelectedPanel().getRecordToolbar().btnLast; + } else if (keyEvent.getKeyCode() == KeyEvent.LEFT) { + btn = getSelectedPanel().getRecordToolbar().btnPrevious; + } else if (keyEvent.getKeyCode() == KeyEvent.RIGHT) { + btn = getSelectedPanel().getRecordToolbar().btnNext; + } else if (keyEvent.getKeyCode() == KeyEvent.HOME) { + btn = getSelectedPanel().getRecordToolbar().btnFirst; + } else if (keyEvent.getKeyCode() == VK_E) { + btn = getSelectedPanel().getToolbarButton(BTN_EDIT_ID); + } else if (keyEvent.getKeyCode() == VK_S) { + btn = getSelectedPanel().getToolbarButton(BTN_SAVE_ID); + } else if (keyEvent.getKeyCode() == VK_D) { + btn = getSelectedPanel().getToolbarButton(BTN_DELETE_ID); + } else if (keyEvent.getKeyCode() == VK_O) { + btn = getSelectedPanel().getToolbarButton(BTN_PROCESS_ID); } - } + } if (btn != null) { prevKeyEventTime = System.currentTimeMillis(); prevKeyEvent = keyEvent; @@ -934,4 +1065,278 @@ public class DetailPane extends Panel implements EventListener, IdSpace { } } + /** + * tabpanel for adtabpanel + * @author hengsin + * + */ + public static class Tabpanel extends org.adempiere.webui.component.Tabpanel { + + /** + * + */ + private static final long serialVersionUID = 8248794614430375822L; + + private ToolBar toolbar; + + private RecordToolbar recordToolBar; + + private Div pagingControl; + + private boolean toggleToFormView = false; + + private IADTabpanel adTabPanel; + + private List toolbarCustomButtons = new ArrayList(); + + private A overflowButton; + + private Popup overflowPopup; + + public Tabpanel() { + setSclass("adwindow-detailpane-tabpanel"); + toolbar = new ToolBar(); + appendChild(toolbar); + } + + /** + * update toolbar state after toggle between grid and form view + */ + public void afterToggle() { + if (getPagingControl() != null) + getPagingControl().setVisible(!toggleToFormView); + if (getRecordToolbar() != null) { + getRecordToolbar().setVisible(toggleToFormView); + if (getRecordToolbar().isVisible()) + getRecordToolbar().dynamicDisplay(); + } + boolean enableCustomize = !adTabPanel.getGridTab().isSortTab() && adTabPanel.isGridView(); + List btns = getToolbar().getChildren(); + Optional optional = btns.stream().filter(e -> BTN_CUSTOMIZE_ID.equals(e.getId())).findFirst(); + if (optional.isPresent()) + optional.get().setDisabled(!enableCustomize); + } + + /** + * set form view state + * @param b + */ + public void setToggleToFormView(boolean b) { + toggleToFormView = b; + } + + /** + * + * @return true if tab have been toggle to form view + */ + public boolean isToggleToFormView() { + return toggleToFormView; + } + + /** + * + * @param tabPanel + */ + public void setADTabpanel(IADTabpanel tabPanel) { + appendChild(tabPanel); + this.adTabPanel = tabPanel; + if (tabPanel instanceof ADTabpanel) { + tabPanel.addEventListener(ADTabpanel.ON_SWITCH_VIEW_EVENT, e -> { + if (recordToolBar != null && tabPanel.isGridView()) { + recordToolBar.setVisible(false); + } + }); + } + } + + /** + * Get toolbar of the tabpanel + * @return {@link ToolBar} + */ + public ToolBar getToolbar() { + return toolbar; + } + + /** + * set record navigation toolbar + * @param rtb + */ + public void setRecordToolbar(RecordToolbar rtb) { + recordToolBar = rtb; + Component parent = overflowPopup != null ? overflowPopup : toolbar; + parent.appendChild(rtb); + rtb.setVisible(false); + if (overflowPopup == null) + rtb.setSclass("adwindow-detailpane-adtab-grid-south"); + } + + /** + * + * @return {@link RecordToolbar} + */ + public RecordToolbar getRecordToolbar() { + return recordToolBar; + } + + /** + * set paging control container + * @param pagingControl + */ + public void setPagingControl(Div pagingControl) { + Component parent = overflowPopup != null ? overflowPopup : toolbar; + if ( pagingControl.getParent() != parent) { + parent.appendChild(pagingControl); + ZKUpdateUtil.setHflex(pagingControl, "0"); + if (overflowPopup == null) + pagingControl.setSclass("adwindow-detailpane-adtab-grid-south"); + } + this.pagingControl = pagingControl; + } + + /** + * + * @return paging control container + */ + public Div getPagingControl() { + return pagingControl; + } + + /** + * Get toolbar button by id + * @param id + * @return {@link ToolBarButton} + */ + public ToolBarButton getToolbarButton(String id) { + List list = toolbar.getChildren(); + Optional optional = list.stream().filter(e -> e.getId().equals(id)).findFirst(); + return optional.isPresent() ? optional.get() : null; + } + + 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"); + toolbar.appendChild(overflowButton); + newOverflowPopup(); + toolbar.appendChild(overflowPopup); + overflowButton.addEventListener(Events.ON_CLICK, e -> { + Long ts = (Long) overflowPopup.removeAttribute("popup.close"); + if (ts != null) { + if (System.currentTimeMillis() - ts.longValue() < 500) { + return; + } + } + overflowPopup.open(overflowButton, "after_end"); + }); + } + + private void newOverflowPopup() { + overflowPopup = new Popup(); + overflowPopup.setHflex("min"); + overflowPopup.setVflex("min"); + } + } + + /** + * record navigation toolbar + * @author hengsin + * + */ + private static class RecordToolbar extends Hlayout { + + /** + * + */ + private static final long serialVersionUID = 5024630043211194429L; + private ToolBarButton btnFirst; + private ToolBarButton btnPrevious; + private ToolBarButton btnRecordInfo; + private ToolBarButton btnNext; + private ToolBarButton btnLast; + private GridTab gridTab; + + private RecordToolbar(GridTab gridTab) { + this.gridTab = gridTab; + btnFirst = createButton("First", "First", "First"); + btnFirst.setTooltiptext(btnFirst.getTooltiptext()+" Shift+Alt+Home"); + appendChild(btnFirst); + btnFirst.addEventListener(Events.ON_CLICK, e -> { + Event ne = new Event(DetailPane.ON_RECORD_NAVIGATE_EVENT, this, "first"); + Events.sendEvent(this, ne); + }); + btnPrevious = createButton("Previous", "Previous", "Previous"); + btnPrevious.setTooltiptext(btnPrevious.getTooltiptext()+" Shift+Alt+Left"); + appendChild(btnPrevious); + btnPrevious.addEventListener(Events.ON_CLICK, e -> { + Event ne = new Event(DetailPane.ON_RECORD_NAVIGATE_EVENT, this, "previous"); + Events.sendEvent(this, ne); + }); + btnRecordInfo = new ToolBarButton(); + btnRecordInfo.setLabel(""); + btnRecordInfo.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Who"))); + btnRecordInfo.addEventListener(Events.ON_CLICK, e -> { + if (gridTab.isNew() || gridTab.getRowCount() == 0) + return; + DataStatusEvent dse = new DataStatusEvent(gridTab, gridTab.getRowCount(), gridTab.needSave(true, true), true, false); + gridTab.updateDataStatusEventProperties(dse); + dse.setCurrentRow(gridTab.getCurrentRow()); + String title = Msg.getMsg(Env.getCtx(), "Who") + btnRecordInfo.getLabel(); + new WRecordInfo(title, dse, gridTab); + }); + btnRecordInfo.setSclass("breadcrumb-record-info link"); + btnRecordInfo.setId("recordInfo"); + btnRecordInfo.setStyle("float: none; display: inline-flex; width: auto;"); + appendChild(btnRecordInfo); + btnNext = createButton("Next", "Next", "Next"); + btnNext.setTooltiptext(btnNext.getTooltiptext()+" Shift+Alt+Right"); + btnNext.addEventListener(Events.ON_CLICK, e -> { + Event ne = new Event(DetailPane.ON_RECORD_NAVIGATE_EVENT, this, "next"); + Events.sendEvent(this, ne); + }); + appendChild(btnNext); + btnLast = createButton("Last", "Last", "Last"); + btnLast.setTooltiptext(btnLast.getTooltiptext()+" Shift+Alt+End"); + btnLast.addEventListener(Events.ON_CLICK, e -> { + Event ne = new Event(DetailPane.ON_RECORD_NAVIGATE_EVENT, this, "last"); + Events.sendEvent(this, ne); + }); + appendChild(btnLast); + this.setValign("middle"); + } + + private ToolBarButton createButton(String name, String image, String tooltip) + { + ToolBarButton btn = new ToolBarButton(""); + btn.setName("Btn"+name); + btn.setId(name); + String suffix = "16.png"; + if (ThemeManager.isUseFontIconForImage()) + btn.setIconSclass("z-icon-"+image+"Record"); + else + btn.setImage(ThemeManager.getThemeResource("images/"+image + suffix)); + btn.setTooltiptext(Msg.getMsg(Env.getCtx(),tooltip)); + btn.setSclass("breadcrumb-toolbar-button"); + + this.appendChild(btn); + //make toolbar button last to receive focus + btn.setTabindex(0); + btn.setDisabled(true); + btn.setStyle("float: none"); + + return btn; + } + + private void dynamicDisplay() { + int rowCount = gridTab.getRowCount(); + int currentRow = gridTab.getCurrentRow()+1; + btnRecordInfo.setLabel(currentRow+"/"+rowCount); + btnFirst.setDisabled(currentRow<=1); + btnPrevious.setDisabled(currentRow<=1); + btnNext.setDisabled(currentRow==rowCount); + btnLast.setDisabled(currentRow==rowCount); + this.invalidate(); + } + } + } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java index 1843f96e16..3e0380b377 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/GridView.java @@ -63,7 +63,6 @@ import org.zkoss.zul.Column; import org.zkoss.zul.Div; import org.zkoss.zul.Paging; import org.zkoss.zul.Row; -import org.zkoss.zul.Tabpanel; import org.zkoss.zul.Vlayout; import org.zkoss.zul.event.ZulEvents; import org.zkoss.zul.impl.CustomGridDataLoader; @@ -1295,13 +1294,8 @@ public class GridView extends Vlayout implements EventListener, IdSpace, if (isDetailPane()) { Component parent = this.getParent(); while (parent != null) { - if (parent instanceof Tabpanel) { - Component firstChild = parent.getFirstChild(); - if ( gridFooter.getParent() != firstChild ) { - firstChild.appendChild(gridFooter); - ZKUpdateUtil.setHflex(gridFooter, "0"); - gridFooter.setSclass("adwindow-detailpane-adtab-grid-south"); - } + if (parent instanceof DetailPane.Tabpanel) { + ((DetailPane.Tabpanel) parent).setPagingControl(gridFooter); break; } parent = parent.getParent(); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java index 7d1911ab04..83c85e96fd 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/IADTabpanel.java @@ -195,4 +195,12 @@ public interface IADTabpanel extends Component, Evaluatee { * @return Quick Form Button Enabled/Disabled */ public abstract boolean isEnableQuickFormButton(); + + /** + * Get is detail pane visible + * @return boolean + */ + public default boolean isDetailVisible() { + return false; + } }