/******************************************************************************
* 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;
}
}