001package votorola.a.web.wic; // Copyright 2008, 2010, 2012, 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. 002 003import java.util.*; 004import org.apache.wicket.*; 005import org.apache.wicket.markup.html.*; 006import org.apache.wicket.markup.html.panel.*; 007import org.apache.wicket.markup.repeater.*; 008import votorola.g.lang.*; 009import votorola.g.web.wic.*; 010 011 012/** A pile of navigation bars. Each bar represents a single level in the page hierarchy 013 * and contains a horizontal array of tabs. Highlighted tabs function as a vertical 014 * breadcrumb trail through the page hierarchy. 015 * 016 * <p>The complexity of tabs proved confusing for some and the top bar is now hidden. 017 * Given our shallow page hierarchy, this limits the pile to a single bar with no 018 * vertical breadcrumbs. We plan to offload navigation onto the {@linkplain 019 * votorola.s.gwt.stage.link.LinkTrackV LinkTrackV} in 2012, in any case.</p> 020 * 021 * @see <a href='../../../../../../a/web/wic/WC_NavPile.html' 022 * target='_top'>WC_NavPile.html</a> 023 */ 024public @ThreadRestricted("wicket") class WC_NavPile extends Panel 025{ 026 027 // Tabbed navigation, in general: 028 // 029 // - http://www.useit.com/alertbox/tabs.html 030 // - http://www.jakeo.com/words/tabs.php 031 // - http://www.lukew.com/ff/entry.asp?178 032 // - http://nontroppo.org/test/tab1.html 033 // 034 // CSS and image tricks: 035 // 036 // - http://icant.co.uk/articles/flexible-css-menu/ 037 // - http://tutorials.mezane.org/tabbed-navigation-using-css/ 038 // 039 040 041 protected WC_NavPile( String id ) 042 { 043 super( id ); 044 setRenderBodyOnly( true ); 045 } 046 047 048 049 /** Constructs a WC_NavPile. 050 * 051 * @param pageTab the navigation tab of the page 052 */ 053 public WC_NavPile( String id, final NavTab pageTab, final VRequestCycle cycle ) 054 { 055 this( id ); 056 init( pageTab, cycle ); 057 } 058 059 060 061 protected final void init( final NavTab pageTab, final VRequestCycle cycle ) 062 { 063 final ArrayList<NavTab> pathTabList = new ArrayList<NavTab>( /*initial capacity*/8 ); // in document order (top down) 064 for( NavTab pathTab = pageTab;; ) // populate list in reverse (upward) 065 { 066 pathTabList.add( 0, pathTab ); 067 pathTab = pathTab.navBar().superTab(); 068 if( pathTab == null ) break; 069 } 070 071 final RepeatingView barRepeating = new RepeatingView( "navRow" ); 072 final NavBar.SessionScope scopeNavBar = VSession.get().scopeNavBar(); 073 boolean isIndented = true; // for previous bar, first bar will negate this, to be unindented 074 for( int b = 1, bN = pathTabList.size();; ) // top down, bar to bar (but skipping topmost) 075 { 076 final WebMarkupContainer navRow = new WebMarkupContainer( barRepeating.newChildId() ); 077 final NavTab pathTab = pathTabList.get( b ); 078 final NavBar bar = pathTab.navBar(); // bar of sister tabs 079 navRow.add( newVBarLeader( "bar-leader", bar )); 080 081 final WebMarkupContainer ul = newVBar( "bar", bar ); 082 final RepeatingView tabRepeating = new RepeatingView( "tab" ); 083 for( int t = 0, tN = bar.tabList().size();; ) // horizontal, tab to tab 084 { 085 final NavTab barTab = bar.tabList().get( t ); 086 final boolean barTabOnPath = barTab == pathTab; 087 if( barTabOnPath ) // persist the path, where it crosses this bar, as default 088 { 089 if( t == bar.defaultTabIndex() ) 090 { 091 scopeNavBar.removeLastIndexOnPath( bar.hashKey() ); 092 } 093 else scopeNavBar.putLastIndexOnPath( bar.hashKey(), t ); 094 } 095 096 final boolean isBarTabEnabled = barTab.isEnabled( cycle ); 097 final BookmarkablePageLinkX li = newTabLink( tabRepeating.newChildId(), barTab ); // a JavaScript link 098 VPageHTML.appendStyleClass( li, barTabOnPath? "on": "off" ); // style on/off path 099 VPageHTML.appendStyleClass( li, isBarTabEnabled? "enabled": "disabled" ); 100 li.setEnabled( isBarTabEnabled && !barTabOnPath ); 101 102 li.add( t == 0? VPageHTML.newLabelNBSP( "tab-leader" ): // only for first tab 103 VPage.newNullComponent( "tab-leader" )); 104 105 final BookmarkablePageLinkX link = newTabLink( "tab-link", barTab ); 106 link.setBody( barTab.shortTitle( cycle )); 107 link.setEnabled( isBarTabEnabled && !barTabOnPath ); 108 li.add( link ); 109 110 final Component trailer = VPageHTML.newLabelNBSP( "tab-trailer" ); 111 li.add( trailer ); 112 113 tabRepeating.add( li ); 114 ++t; 115 if( t >= tN ) 116 { 117 VPageHTML.appendStyleClass( trailer, "bar-trailer" ); // only for last tab 118 break; 119 } 120 } 121 ul.add( tabRepeating ); 122 123 isIndented = isIndented( bar, isIndented ); 124 if( isIndented ) VPageHTML.appendStyleClass( ul, "indented" ); 125 126 navRow.add( ul ); 127 barRepeating.add( navRow ); 128 129 ++b; 130 if( b >= bN ) 131 { 132 VPageHTML.appendStyleClass( ul, "pile-trailer" ); // only for last bar, except subclasses may bend this rule when the following bar has a leader, per newVBarLeader() 133 break; 134 } 135 } 136 add( barRepeating ); 137 } 138 139 140 141//// P r i v a t e /////////////////////////////////////////////////////////////////////// 142 143 144 // - W C - N a v - P i l e ----------------------------------------------------------- 145 146 147 /** Answers whether the view of the specified bar should be indented. The default 148 * implementation of this method (in WC_NavPile) returns !wasIndented, for a 149 * staggered effect. 150 * 151 * @param wasIndented whether the previous bar was indented 152 */ 153 protected boolean isIndented( NavBar bar, boolean wasIndented ) { return !wasIndented; } 154 155 156 157 private static BookmarkablePageLinkX newTabLink( final String id, final NavTab tab ) 158 { 159 return new BookmarkablePageLinkX( id, tab.bookmark() ); 160 } 161 162 163 164 /** Constructs a view of the specified navigation bar. The default implementation of 165 * this method (in WC_NavPile) returns a plain WebMarkupContainer. 166 */ 167 protected WebMarkupContainer newVBar( String id, NavBar bar ) 168 { 169 return new WebMarkupContainer( id ); 170 } 171 172 173 174 /** Constructs a component to precede the specified navigation bar. The default 175 * implementation of this method (in WC_NavPile) returns a no-op component. 176 */ 177 protected Component newVBarLeader( String id, NavBar bar ) 178 { 179 return VPage.newNullComponent( id ); 180 } 181 182 183 184}