/*******************************************************************************
*
* Copyright 2011-2014 Spiffy UI Team
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
******************************************************************************/
package org.spiffyui.client.nav;
import java.util.ArrayList;
import java.util.List;
import org.spiffyui.client.HistoryCallback;
import org.spiffyui.client.JSUtil;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
/**
* This is the navigation bar for the main page.
*
*/
public class MainNavBar extends HasNavBarListenersPanel implements ClickHandler, HistoryCallback
{
private static final String TITLE = Window.getTitle();
private final List<NavItem> m_items = new ArrayList<NavItem>();
private NavItem m_selectedItem = null;
private boolean m_bookmarkable = false;
/**
* The class used for selected navigation items
*/
protected static final String SELECTED_CLASS = "navItemSelected";
/**
* Create a new MainNavBar
*/
public MainNavBar()
{
getElement().setId("mainNavContainer");
if (RootPanel.get("mainNavigation") != null) {
RootPanel.get("mainNavigation").add(this);
} else {
throw new IllegalStateException("Unable to locate the mainNavigation element. You must import spiffyui.min.js before using this widget.");
}
}
@Override
public void add(Widget w)
{
if (w instanceof NavWidget) {
super.add(w);
if (w instanceof NavItem) {
m_items.add((NavItem) w);
((NavItem) w).getAnchor().addClickHandler(this);
} else if (w instanceof NavSection) {
((NavSection) w).setNavBar(this);
}
} else {
throw new IllegalArgumentException("You can only add NavItem, NavHeader, NavSection, NavPanel, or NavSeparator to this class");
}
}
/**
* Addes a nav item to the list of anchors to listen to
*
* @param item the nav item to add
*/
protected void addNavItem(NavItem item)
{
if (!m_items.contains(item)) {
item.getAnchor().addClickHandler(this);
m_items.add(item);
}
}
@Override
public void onClick(ClickEvent event)
{
event.preventDefault();
if (!isEnabled()) {
return;
}
NavItem navItem = null;
for (Widget w : getChildren()) {
if (w instanceof NavItem) {
NavItem item = (NavItem) w;
if (item.getAnchor() == event.getSource()) {
navItem = item;
break;
}
} else if (w instanceof NavSection) {
NavItem item = ((NavSection) w).getNavItem((Anchor) event.getSource());
if (item != null) {
navItem = item;
break;
}
}
}
doFireEvent(event, navItem);
}
/**
* Call-back when the history item is retrieved after a browser back or forward
* button is pressed. The default implementation will be to treat the supplied id
* as a {@link NavItem} reference and select the one with the same ID.
*
* If you subclass and overload the {@link #addHistoryItem(String,String)} routine, you
* will probably need to overload this method as well, to ensure that the supplied id
* (or history token) is translated correctly for your subclass.
*
* @param id The history token previously stored on the history stack.
*/
@Override
public void historyChanged(String id)
{
NavItem item = getItem(id);
if (item != null) {
selectItem(item, false, true);
}
}
private void doFireEvent(ClickEvent event, NavItem navItem)
{
//if any listener wants to cancel the event
//then do not continue
if (!firePreEvent(navItem)) {
return;
}
//continue if no listener returned false on the pre-event
for (Widget w : getChildren()) {
if (w instanceof NavItem) {
NavItem item = (NavItem) w;
if (item.getAnchor() == event.getSource()) {
item.addStyleName(SELECTED_CLASS);
m_selectedItem = item;
fireEvent(item);
} else {
item.removeStyleName(SELECTED_CLASS);
}
} else if (w instanceof NavSection) {
((NavSection) w).updateSelectedState((Anchor) event.getSource());
}
}
}
/**
* Selects the specified navigation item and fires the navigation event to
* let all listeners know it was selected. This intentionally does not
* fire a pre-event so that it cannot be intercepted. Returns true if NavItem found, false otherwise
*
* @param item the item to select
* @return true if the item is one of the NavItems of the NavBar, false if not a member such as logout
*/
public boolean selectItem(NavItem item)
{
return selectItem(item, true, false);
}
/**
* Selects the specified navigation item and fires the navigation event to
* let all listeners know it was selected. Returns true if NavItem found, false otherwise
*
* @param item the item to select
* @param addToHistory
* true if this item should be added to the browser's history and false otherwise
* @param doFirePreEvent
* true to allow interception and cancelling of the event, false to not fire the pre-event
*
* @return true if the item is one of the NavItems of the NavBar, false if not a member such as logout
*/
public boolean selectItem(NavItem item, boolean addToHistory, boolean doFirePreEvent)
{
return selectItem(item, addToHistory, doFirePreEvent, true);
}
/**
* Selects the specified navigation item and can fire the navigation event to
* let all listeners know it was selected, if the doFireSelectedEvent parameter is set accordingly.
* Returns true if NavItem found, false otherwise
*
* @param item the item to select
* @param addToHistory
* true if this item should be added to the browser's history and false otherwise
* @param doFirePreEvent
* true to allow interception and cancelling of the event, false to not fire the pre-event
* @param doFireSelectEvent
* true to fire the selection event to navbar listeners, false to not fire the event
*
* @return true if the item is one of the NavItems of the NavBar, false if not a member such as logout
*/
public boolean selectItem(NavItem item, boolean addToHistory, boolean doFirePreEvent, boolean doFireSelectEvent)
{
//if any listener wants to cancel the event
//then do not continue
if (doFirePreEvent && !firePreEvent(item)) {
return false;
}
boolean found = false;
for (NavItem ni : m_items) {
if (ni == item) {
ni.addStyleName(SELECTED_CLASS);
m_selectedItem = ni;
if (doFireSelectEvent) {
fireEvent(item, addToHistory);
}
found = true;
} else {
ni.removeStyleName(SELECTED_CLASS);
}
}
return found;
}
/**
* Get the currently selected navigation item in this navigation bar.
*
* @return the currently selected navigation item or null if no items are selected
*/
public NavItem getSelectedItem()
{
return m_selectedItem;
}
/**
* Gets a navigation item from the navigation menu.
*
* @param id The id of the item to get
*
* @return the item with that id or null if it doesn't exist
*/
public NavItem getItem(String id)
{
for (NavItem item : m_items) {
if (item.getElement().getId().equals(id)) {
return item;
}
}
return null;
}
/**
* Set the navigation bar to be enabled or disabled. A disabled navigation bar
* is gray and never fires selection events.
*
* @param enabled true for enabled and false for disabled
*/
@Override
public void setEnabled(boolean enabled)
{
super.setEnabled(enabled);
if (enabled) {
removeStyleName("disabled");
} else {
addStyleName("disabled");
}
}
@Override
public void fireEvent(NavItem item, boolean addToHistory)
{
super.fireEvent(item, addToHistory);
if (isEnabled() && addToHistory) {
addHistoryItem(item.getElement().getId(), TITLE + " - " + item.getDisplayName());
}
}
/**
* A routine to store a history token that this Navbar can use when called
* in the future when the forward or back button's are called.<p>
* This is available for subclasses to overload if they wish to store a token
* that is not based on the NavItem's ID. For instance the situation where
* there are multiple types of identifiers being used to reconstitute a past application
* state.<p>
* Please note that if you overload this method you will need to provide an alternative implementation
* of the history callback routine {@link #historyChanged(String)}. That will need to translate the
* stored token that your overloaded addHistoryItem routine stored.
*
* @param historyToken
* A string value that will server as a token about the applications state
*
* @see MainNavBar.addHistoryItem
* @deprecated This method is deprecated and will not be called.
*/
@Deprecated
protected void addHistoryItem(String historyToken)
{
/*
This method is a no-op and is just kept for backward compatability
*/
}
/**
* A routine to store a history token that this Navbar can use when called
* in the future when the forward or back button's are called.<p>
* This is available for subclasses to overload if they wish to store a token
* that is not based on the NavItem's ID. For instance the situation where
* there are multiple types of identifiers being used to reconstitute a past application
* state.<p>
* Please note that if you overload this method you will need to provide an alternative implementation
* of the history callback routine {@link #historyChanged(String)}. That will need to translate the
* stored token that your overloaded addHistoryItem routine stored.
*
* @param historyToken
* A string value that will server as a token about the applications state
* @param title the window title for this history item
*/
protected void addHistoryItem(String historyToken, String title)
{
if (historyToken != null && historyToken.length() > 0) {
if (title == null) {
JSUtil.addHistoryItem(this, historyToken, m_bookmarkable);
} else {
JSUtil.addHistoryItem(this, historyToken, m_bookmarkable, title);
}
}
}
public void setBookmarkable(boolean bookmarkable)
{
m_bookmarkable = bookmarkable;
}
public boolean getBookmarkable()
{
return m_bookmarkable;
}
/**
* Remove all the items from this navigation bar.
*
*/
@Override
public void clear()
{
super.clear();
m_items.clear();
}
/**
* Remove the specified NavItem from the navigation bar.
*
* @param item the item to remove
*/
public void remove(NavItem item)
{
super.remove(item);
m_items.remove(item);
}
}