package votorola.a.web; // Copyright 2008, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Votorola Software"), to deal in the Votorola Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Votorola Software, and to permit persons to whom the Votorola Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Votorola Software. THE VOTOROLA SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE VOTOROLA SOFTWARE OR THE USE OR OTHER DEALINGS IN THE VOTOROLA SOFTWARE.
import java.util.*;
import org.apache.wicket.*;
import org.apache.wicket.markup.html.*;
import org.apache.wicket.markup.html.panel.*;
import org.apache.wicket.markup.repeater.*;
import votorola.g.lang.*;
import votorola.g.servlet.*;
/** Navigation bars for the page top. At the very top is a super-bar of navigation links
* and session controls. Beneath it is a pile of tabbed bars. The depth of the pile
* corresponds to the depth of descent in the page hierarchy. Each tabbed bar contains a
* horizontal array of tabs, functioning as navigation links. Links followed by the user
* are highlighted in the pile to reveal the path of descent (like a trail of
* breadcrumbs).
*
* @see
* WC_NavPile.html
*/
public @ThreadRestricted("wicket") class WC_NavPile extends Panel
{
// Tabbed navigation, in general:
//
// - http://www.useit.com/alertbox/tabs.html
// - http://www.jakeo.com/words/tabs.php
// - http://www.lukew.com/ff/entry.asp?178
// - http://nontroppo.org/test/tab1.html
//
// CSS and image tricks:
//
// - http://icant.co.uk/articles/flexible-css-menu/
// - http://tutorials.mezane.org/tabbed-navigation-using-css/
//
protected WC_NavPile( String id )
{
super( id );
setRenderBodyOnly( true );
}
/** Constructs a WC_NavPile.
*
* @param pageTab the navigation tab of the page
*/
public WC_NavPile( String id, final NavTab pageTab, final VRequestCycle cycle )
{
this( id );
init( pageTab, cycle );
}
protected final void init( final NavTab pageTab, final VRequestCycle cycle )
{
final ArrayList pathTabList = // in document order (top down)
new ArrayList( /*initial capacity*/8 );
for( NavTab pathTab = pageTab;; ) // populate list in reverse (upward)
{
pathTabList.add( 0, pathTab );
pathTab = pathTab.navBar().superTab();
if( pathTab == null ) break;
}
final RepeatingView barRepeating = new RepeatingView( "navRow" );
final NavBar.SessionScope scopeNavBar = cycle.vSession().scopeNavBar();
boolean isIndented = true; // for previous bar, first bar will negate this, to be unindented
for( int b = 0, bN = pathTabList.size();; ) // top down, bar to bar
{
final WebMarkupContainer navRow = new WebMarkupContainer( barRepeating.newChildId() );
final NavTab pathTab = pathTabList.get( b );
final NavBar bar = pathTab.navBar(); // bar of sister tabs
navRow.add( newVBarLeader( "bar-leader", bar ));
final WebMarkupContainer ul = newVBar( "bar", bar );
final RepeatingView tabRepeating = new RepeatingView( "tab" );
for( int t = 0, tN = bar.tabList().size();; ) // horizontal, tab to tab
{
final NavTab barTab = bar.tabList().get( t );
final boolean barTabOnPath = barTab == pathTab;
if( barTabOnPath ) // persist the path, where it crosses this bar, as default
{
if( t == NavBar.DEFAULT_TAB_INDEX )
{
scopeNavBar.removeLastIndexOnPath( bar.hashKey() );
}
else scopeNavBar.putLastIndexOnPath( bar.hashKey(), t );
}
final boolean isBarTabEnabled = barTab.isEnabled( cycle );
final TabLink li = // a JavaScript link
new TabLink( tabRepeating.newChildId(), barTab, barTabOnPath, cycle );
VPage.appendStyleClass( li, barTabOnPath? "on": "off" ); // style on/off path
VPage.appendStyleClass( li, isBarTabEnabled? "enabled": "disabled" );
li.setEnabled( isBarTabEnabled && !barTabOnPath );
li.add( t == 0? VPage.newLabelNBSP( "tab-leader" ): // only for first tab
VPage.newNullComponent( "tab-leader" ));
final TabLink link = new TabLink( "tab-link", barTab, barTabOnPath, cycle );
link.setBodyModel( barTab.shortTitle( cycle ));
link.setEnabled( isBarTabEnabled && !barTabOnPath );
li.add( link );
final Component trailer = VPage.newLabelNBSP( "tab-trailer" );
li.add( trailer );
tabRepeating.add( li );
++t;
if( t >= tN )
{
VPage.appendStyleClass( trailer, "bar-trailer" ); // only for last tab
break;
}
}
ul.add( tabRepeating );
isIndented = isIndented( bar, isIndented );
if( isIndented ) VPage.appendStyleClass( ul, "indented" );
navRow.add( ul );
barRepeating.add( navRow );
++b;
if( b >= bN )
{
VPage.appendStyleClass( ul, "pile-trailer" ); // only for last bar
break;
}
}
add( barRepeating );
}
//// P r i v a t e ///////////////////////////////////////////////////////////////////////
// - W C - N a v - P i l e -----------------------------------------------------------
/** Answers whether the view of the specified bar should be indented. The default
* implementation of this method (in WC_NavPile) returns !wasIndented, for a
* staggered effect.
*
* @param wasIndented whether the previous bar was indented
*/
protected boolean isIndented( NavBar bar, boolean wasIndented ) { return !wasIndented; }
/** Constructs a view of the specified navigation bar. The default implementation of
* this method (in WC_NavPile) returns a plain WebMarkupContainer.
*/
protected WebMarkupContainer newVBar( String id, NavBar bar )
{
return new WebMarkupContainer( id );
}
/** Constructs a component to precede the specified navigation bar. The default
* implementation of this method (in WC_NavPile) returns a no-op component.
*/
protected Component newVBarLeader( String id, NavBar bar )
{
return VPage.newNullComponent( id );
}
// ====================================================================================
private static final class TabLink extends LinkX
{
private TabLink( String id, NavTab tab, final boolean tabOnPath, final VRequestCycle cycle )
{
super( id );
if( tabOnPath ) runner = RequestCycleRunner0.i(); // save cost of super-tab bookmark/runner lookup, if link is going to end up disabled
else runner = tab.runner( cycle );
}
private final RequestCycleRunner runner;
// - L i n k ----------------------------------------------------------------------
public @Override void onClick() { runner.run( VRequestCycle.get() ); }
}
}