/****************************************************************************** * Product: Posterita Ajax UI * * Copyright (C) 2007 Posterita Ltd. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify it * * under the terms version 2 of the GNU General Public License as published * * by the Free Software Foundation. This program is distributed in the hope * * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * * with this program; if not, write to the Free Software Foundation, Inc., * * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * For the text or an alternative of this public license, you may reach us * * Posterita Ltd., 3, Draper Avenue, Quatre Bornes, Mauritius * * or via info@posterita.org or http://www.posterita.org/ * *****************************************************************************/ package org.adempiere.webui.component; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.SelectEvent; import org.zkoss.zul.Tab; import org.zkoss.zul.Tabpanels; import org.zkoss.zul.Tabs; /** * Extend {@link org.zkoss.zul.Tabbox} * @author Ashley G Ramdass * @date Feb 25, 2007 */ public class Tabbox extends org.zkoss.zul.Tabbox implements EventListener { /** * generated serial id */ private static final long serialVersionUID = 1400484283064851775L; private boolean isSupportTabDragDrop = false; private boolean isActiveBySeq = false; private boolean isCheckVisibleOnlyForNextActive = true; /** * Deque of tab by selection sequence. Each time when a Tab is selected by user, the Tab is push to front of queue. */ private Deque activeTabSeq = new ArrayDeque<>(); /** * Default constructor */ public Tabbox () { super(); this.addEventListener(Events.ON_SELECT, this); } /** * Get tab panel at index * @param index * @return Tabpanel. Throw IndexOutOfBoundsException if index is invalid. */ public Tabpanel getTabpanel(int index) { try { Tabpanels tabpanels = this.getTabpanels(); Tabpanel tabPanel = (Tabpanel)tabpanels.getChildren().get(index); return tabPanel; } catch (Exception ex) { throw new IndexOutOfBoundsException(ex.getMessage()); } } /** * Get selected tab panel * @return selected Tabpanel */ public Tabpanel getSelectedTabpanel() { return getTabpanel(this.getSelectedIndex()); } /** * @return true if drag and drop of tab is enable */ public boolean isSupportTabDragDrop() { return isSupportTabDragDrop; } /** * Set enable/disable the drag and drop of {@link Tab} * @param isSupportTabDragDrop */ public void setSupportTabDragDrop(boolean isSupportTabDragDrop) { this.isSupportTabDragDrop = isSupportTabDragDrop; } /** * Send onPageAttached event */ @Override public void onPageAttached(Page newpage, Page oldpage) { super.onPageAttached(newpage, oldpage); Events.sendEvent("onPageAttached", this, null); } /** * Send onPageDetached event */ @Override public void onPageDetached(Page page) { super.onPageDetached(page); Events.sendEvent("onPageDetached", this, null); } /** * Add current selected tab to stack when a new tab is selected */ @Override public void onEvent(Event event) throws Exception { if (event instanceof SelectEvent selectEvent && isActiveBySeq()) { @SuppressWarnings("rawtypes") Iterator itPrevSelectedTab = selectEvent.getPreviousSelectedItems().iterator(); if (itPrevSelectedTab.hasNext()) { activeTabSeq.push((Tab)itPrevSelectedTab.next()); } } } /** * Override to add active tab to stack when tab is selected by add to {@link Tabs} */ @Override public void setSelectedTab(Tab tab) { if (getSelectedTab() != null && getSelectedTab() != tab && isActiveBySeq()) {//DEFER SET SELECTED TAB activeTabSeq.push(getSelectedTab()); } super.setSelectedTab(tab); } /** * @return true if set next active tab using {@link #activeTabSeq} */ public boolean isActiveBySeq() { return isActiveBySeq; } /** * Enable/disable the use of {@link #activeTabSeq} for the setting of next active tab. * @param isActiveBySeq */ public void setActiveBySeq(boolean isActiveBySeq) { this.isActiveBySeq = isActiveBySeq; } /** * Get next active tab by selection sequence store on {@link #activeTabSeq}. * @param currentTab current Tab to skip/ignore * @return Tab or null if no valid candidate from {@link #activeTabSeq}. */ public Tab getNextActiveBySeq (Tab currentTab) { Tab cadidateTabActive = null; while ((cadidateTabActive = activeTabSeq.peek()) != null) { boolean canNotActive = cadidateTabActive.isDisabled() || (!cadidateTabActive.isVisible() && isCheckVisibleOnlyForNextActive()); if (canNotActive) { // move disable item to last stack it can be active late cadidateTabActive = activeTabSeq.pop(); activeTabSeq.addLast(cadidateTabActive); }else if (cadidateTabActive.getParent() == null || currentTab == cadidateTabActive) { activeTabSeq.pop(); //not attached to page or == currentTab, remove }else { return activeTabSeq.pop(); } } return null; } /** * {@link #activeTabSeq} is maintain by selected tab event, so when a tab is close, code should call this function to save memory
* @param closeTab */ public void removeTabFromActiveSeq (Tab closeTab) { activeTabSeq.remove(closeTab); } /** * @return true if {@link #activeTabSeq} should ignore invisible tab. */ public boolean isCheckVisibleOnlyForNextActive() { return isCheckVisibleOnlyForNextActive; } /** * Ignore invisible tab for next active by selection sequence * @param isVisibleOnly */ public void setCheckVisibleOnlyForNextActive(boolean isVisibleOnly) { isCheckVisibleOnlyForNextActive = isVisibleOnly; } }