package org.sigmah.client.ui.widget.tab;
/*
* #%L
* Sigmah
* %%
* Copyright (C) 2010 - 2016 URD
* %%
* 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 3 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, see
* <http://www.gnu.org/licenses/gpl-3.0.html>.
* #L%
*/
import java.util.ArrayList;
import java.util.HashMap;
import com.google.gwt.animation.client.Animation;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Widget;
import java.util.List;
import java.util.Map;
/**
* This widget displays a bar of {@link Tab}.
*
* @author Raphaƫl Calabro (rcalabro@ideia.fr)
* @author Tom Miette (tmiette@ideia.fr)
*/
public class TabBar implements IsWidget {
// Widgets.
private final FlowPanel tabContainer;
private final AbsolutePanel scrollPanel;
// Stores the tabs.
private final List<Tab> tabs;
private final Map<TabId, Tab> tabsIds;
/**
* Tab currently displayed.
*/
private Tab activeTab;
private Tab previousTab;
// Animation.
private static final int ANIMATION_DURATION = 200;
private final Animation scrollAnimation;
private int direction;
private int initialPosition;
private double distance;
private int leftTabIndex;
// Listeners.
private final List<TabBarListener> listeners;
/**
* Tab bar events listener.
*
* @author Tom Miette (tmiette@ideia.fr)
*/
public static interface TabBarListener {
void tabShownOrHiddden(TabBar bar);
void tabAdded(TabBar bar, Tab tab);
void tabRemoved(TabBar bar, Tab tab);
void tabActivated(TabBar bar, Tab tab);
}
/**
* Creates a new TabBar using the given TabModel.
*/
public TabBar() {
// Widgets.
tabContainer = new FlowPanel();
tabContainer.setHeight("100%");
scrollPanel = new AbsolutePanel();
scrollPanel.setSize("100%", "100%");
scrollPanel.add(tabContainer, 0, 0);
// To store the tabs.
tabs = new ArrayList<Tab>();
tabsIds = new HashMap<TabId, Tab>();
// Animation.
scrollAnimation = new Animation() {
@Override
protected void onUpdate(double progress) {
int x = (int) (distance * progress) * direction + initialPosition;
if (x > 0) {
x = 0;
}
scrollPanel.setWidgetPosition(tabContainer, x, 0);
}
@Override
protected void onComplete() {
int x = (int) distance * direction + initialPosition;
if (x > 0) {
x = 0;
}
scrollPanel.setWidgetPosition(tabContainer, x, 0);
// Fires event.
fireTabShownOrHiddden(TabBar.this);
}
};
direction = 1;
initialPosition = 0;
distance = 0.0;
leftTabIndex = 0;
// Listeners.
listeners = new ArrayList<TabBarListener>();
}
/**
* {@inheritDoc}
*/
@Override
public Widget asWidget() {
return scrollPanel;
}
/**
* Gets the total number of tabs (hidden or not).
*
* @return the total number of tabs.
*/
public int getNumberOfTabs() {
return tabs.size();
}
/**
* Gets the numbers of tabs currently visible.
*
* @return the numbers of tabs currently visible.
*/
public int getNumberOfVisibleTabs() {
return getNumberOfTabs() - getNumberOfHiddenTabs();
}
/**
* Gets the number of tabs currently hidden.
*
* @return the number of tabs currently hidden.
*/
public int getNumberOfHiddenTabs() {
return leftTabIndex;
}
/**
* Hides the first displayed tab on the left.
*/
public void hideTab() {
int currentPosition = scrollPanel.getWidgetLeft(tabContainer);
distance = initialPosition + distance * direction - currentPosition;
if (distance < 0) {
distance = -distance;
}
if (leftTabIndex < tabs.size() - 1) {
distance += getTabWidth(tabs.get(leftTabIndex));
leftTabIndex++;
}
direction = -1;
initialPosition = currentPosition;
scrollAnimation.run(ANIMATION_DURATION);
}
/**
* Shows the first hidden tab on the left.
*/
public void showTab() {
int currentPosition = scrollPanel.getWidgetLeft(tabContainer);
distance = initialPosition + distance * direction - currentPosition;
if (distance < 0) {
distance = -distance;
}
if (leftTabIndex > 0) {
leftTabIndex--;
distance += getTabWidth(tabs.get(leftTabIndex));
}
direction = 1;
initialPosition = currentPosition;
scrollAnimation.run(ANIMATION_DURATION);
}
/**
* Adds and activates the given tab. If the tab already exists (based on its unique id), it is only activated.
*
* @param tab
* The tab.
*/
public void addTab(final Tab tab) {
// Nothing to do.
if (tab == null || tab.getId() == null) {
return;
}
// Builds a new tab if necessary.
Tab t = null;
if ((t = tabsIds.get(tab.getId())) == null) {
tab.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (isActive(tab)) {
// Active tab click action: nothing to do.
return;
}
activateTab(tab, false, true);
}
});
if (tab.isCloseable()) {
tab.addCloseHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
removeTab(tab);
}
});
}
// Stores the new tab.
t = tab;
tabContainer.add(t);
tabs.add(t);
tabsIds.put(t.getId(), t);
// Fires event.
fireTabAdded(tab);
}
// Or just update its title.
else {
updateTitle(t.getId(), tab.getTitle());
}
// Activates the tab
activateTab(t, true, false);
}
/**
* Updates the title of the tab with the given id.
*
* @param id
* The id.
* @param title
* The new title.
*/
public void updateTitle(TabId id, String title) {
final Tab tab = tabsIds.get(id);
if (tab != null) {
tab.setTabTitle(title);
}
}
/**
* Updates the title of the tab with the given id.
*
* @param id
* The id.
* @param title
* The new title.
*/
/**
* Updates the dom of the tab with the given id.
* @param id
* @param domId
*/
public void updateClosableTabDomId(TabId id, String domId) {
final Tab tab = tabsIds.get(id);
if (tab != null) {
if(tab.getClosePanel()!=null){
tab.getClosePanel().getElement().setId(domId);
}
}
}
/**
* Removes the given {@code id} corresponding tab.
*
* @param id
* The tab id.
*/
public void removeTab(final TabId id) {
removeTab(tabsIds.get(id));
}
/**
* Removes the tab currently displayed and activates to the previous tab
* (if not null).
*/
public void removeActiveTab() {
final Tab tabToRemove = this.activeTab;
activateTab(previousTab, true, true);
removeTab(tabToRemove);
this.previousTab = null;
}
/**
* Activates the given tab.
*
* @param tab
* The tab.
* @param updateStyle
* {@code true} to update tabs style (active), {@code false} to leave current styles.
* @param fireEvent
* If the {@link TabBarListener#tabActivated(TabBar, Tab)} event must be fired.
*/
private void activateTab(Tab tab, boolean updateStyle, boolean fireEvent) {
if (tab == null) {
return;
}
this.previousTab = activeTab;
this.activeTab = tab;
if (updateStyle) {
for (final Tab t : tabs) {
t.removeStyleName(Tab.CSS_TAB_ACTIVE);
}
tab.addStyleName(Tab.CSS_TAB_ACTIVE);
}
// Fires event.
if (fireEvent) {
fireTabActivated(tab);
}
}
/**
* Checks if the given tab is active.
*
* @param tab
* The tab.
* @return <code>true</code> if the tab is active, <code>false</code> otherwise.
*/
private boolean isActive(Tab tab) {
if (tab == null) {
return false;
}
return tab.getStyleName().contains(Tab.CSS_TAB_ACTIVE);
}
/**
* Removes the given tab.
*
* @param tab
* The tab
*/
private void removeTab(final Tab tab) {
// Nothing to do.
if (tab == null || tab.getId() == null) {
return;
}
// Is the tab active ?
final boolean active = isActive(tab);
// Removes the tab.
final int tabIndex = tabs.indexOf(tab);
tabContainer.remove(tab);
tabs.remove(tab);
tabsIds.remove(tab.getId());
// Fires event.
fireTabRemoved(tab);
// Activates the first tab ?
if (active) {
activateTab(tabs.get(tabIndex == tabs.size() ? tabIndex - 1 : tabIndex), true, true);
}
}
/**
* Gets the given tab total width (including borders, margins, paddings, etc.).
*
* @param tab
* The tab.
* @return The width.
*/
private int getTabWidth(Tab tab) {
return getTabWidth(tab.getElement());
}
private static final native int getTabWidth(com.google.gwt.dom.client.Element element) /*-{
var width = 0;
var style = $wnd.getComputedStyle(element, null);
width += parseInt(style.width) + parseInt(style.borderLeftWidth)
+ parseInt(style.borderRightWidth) + parseInt(style.marginLeft)
+ parseInt(style.marginRight) + parseInt(style.paddingLeft)
+ parseInt(style.paddingRight);
return width;
}-*/;
public void addListener(TabBarListener listener) {
if (listener != null) {
listeners.add(listener);
}
}
public void removeListener(TabBarListener listener) {
if (listener != null) {
listeners.remove(listener);
}
}
protected void fireTabAdded(Tab tab) {
for (final TabBarListener listener : listeners) {
listener.tabAdded(this, tab);
}
}
protected void fireTabRemoved(Tab tab) {
for (final TabBarListener listener : listeners) {
listener.tabRemoved(this, tab);
}
}
protected void fireTabActivated(Tab tab) {
for (final TabBarListener listener : listeners) {
listener.tabActivated(this, tab);
}
}
protected void fireTabShownOrHiddden(TabBar tab) {
for (final TabBarListener listener : listeners) {
listener.tabShownOrHiddden(this);
}
}
}