001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.user.client.ui;
017:
018: import com.google.gwt.user.client.DOM;
019: import com.google.gwt.user.client.Event;
020:
021: /**
022: * A horizontal bar of folder-style tabs, most commonly used as part of a
023: * {@link com.google.gwt.user.client.ui.TabPanel}.
024: * <p>
025: * <img class='gallery' src='TabBar.png'/>
026: * </p>
027: * <h3>CSS Style Rules</h3>
028: * <ul class='css'>
029: * <li>.gwt-TabBar { the tab bar itself }</li>
030: * <li>.gwt-TabBar .gwt-TabBarFirst { the left edge of the bar }</li>
031: * <li>.gwt-TabBar .gwt-TabBarRest { the right edge of the bar }</li>
032: * <li>.gwt-TabBar .gwt-TabBarItem { unselected tabs }</li>
033: * <li>.gwt-TabBar .gwt-TabBarItem-wrapper { table cell around tab }</li>
034: * <li>.gwt-TabBar .gwt-TabBarItem-selected { additional style for selected
035: * tabs } </li>
036: * <li>.gwt-TabBar .gwt-TabBarItem-wrapper-selected { table cell around
037: * selected tab }</li>
038: * </ul>
039: * <p>
040: * <h3>Example</h3>
041: * {@example com.google.gwt.examples.TabBarExample}
042: * </p>
043: */
044: public class TabBar extends Composite implements SourcesTabEvents,
045: ClickListener {
046:
047: /**
048: * <code>ClickDecoratorPanel</code> decorates any widget with the minimal
049: * amount of machinery to receive clicks for delegation to the parent.
050: * {@link SourcesClickEvents} is not implemented due to the fact that only a
051: * single observer is needed.
052: */
053: private static final class ClickDecoratorPanel extends SimplePanel {
054: ClickListener delegate;
055:
056: ClickDecoratorPanel(Widget child, ClickListener delegate) {
057: this .delegate = delegate;
058: setWidget(child);
059: sinkEvents(Event.ONCLICK);
060: }
061:
062: @Override
063: public void onBrowserEvent(Event event) {
064: // No need for call to super.
065: switch (DOM.eventGetType(event)) {
066: case Event.ONCLICK:
067: delegate.onClick(this );
068: }
069: }
070: }
071:
072: private static final String STYLENAME_DEFAULT = "gwt-TabBarItem";
073: private HorizontalPanel panel = new HorizontalPanel();
074: private Widget selectedTab;
075: private TabListenerCollection tabListeners;
076:
077: /**
078: * Creates an empty tab bar.
079: */
080: public TabBar() {
081: initWidget(panel);
082: sinkEvents(Event.ONCLICK);
083: setStyleName("gwt-TabBar");
084:
085: panel.setVerticalAlignment(HasVerticalAlignment.ALIGN_BOTTOM);
086:
087: HTML first = new HTML(" ", true), rest = new HTML(
088: " ", true);
089: first.setStyleName("gwt-TabBarFirst");
090: rest.setStyleName("gwt-TabBarRest");
091: first.setHeight("100%");
092: rest.setHeight("100%");
093:
094: panel.add(first);
095: panel.add(rest);
096: first.setHeight("100%");
097: panel.setCellHeight(first, "100%");
098: panel.setCellWidth(rest, "100%");
099: }
100:
101: /**
102: * Adds a new tab with the specified text.
103: *
104: * @param text the new tab's text
105: */
106: public void addTab(String text) {
107: insertTab(text, getTabCount());
108: }
109:
110: /**
111: * Adds a new tab with the specified text.
112: *
113: * @param text the new tab's text
114: * @param asHTML <code>true</code> to treat the specified text as html
115: */
116: public void addTab(String text, boolean asHTML) {
117: insertTab(text, asHTML, getTabCount());
118: }
119:
120: /**
121: * Adds a new tab with the specified widget.
122: *
123: * @param widget the new tab's widget.
124: */
125: public void addTab(Widget widget) {
126: insertTab(widget, getTabCount());
127: }
128:
129: public void addTabListener(TabListener listener) {
130: if (tabListeners == null) {
131: tabListeners = new TabListenerCollection();
132: }
133: tabListeners.add(listener);
134: }
135:
136: /**
137: * Gets the tab that is currently selected.
138: *
139: * @return the selected tab
140: */
141: public int getSelectedTab() {
142: if (selectedTab == null) {
143: return -1;
144: }
145: return panel.getWidgetIndex(selectedTab) - 1;
146: }
147:
148: /**
149: * Gets the number of tabs present.
150: *
151: * @return the tab count
152: */
153: public int getTabCount() {
154: return panel.getWidgetCount() - 2;
155: }
156:
157: /**
158: * Gets the specified tab's HTML.
159: *
160: * @param index the index of the tab whose HTML is to be retrieved
161: * @return the tab's HTML
162: */
163: public String getTabHTML(int index) {
164: if (index >= getTabCount()) {
165: return null;
166: }
167: Widget widget = panel.getWidget(index + 1);
168: if (widget instanceof HTML) {
169: return ((HTML) widget).getHTML();
170: } else if (widget instanceof Label) {
171: return ((Label) widget).getText();
172: } else {
173: // This will be a ClickDecorator holding a user-supplied widget.
174: return DOM.getInnerHTML(widget.getElement());
175: }
176: }
177:
178: /**
179: * Inserts a new tab at the specified index.
180: *
181: * @param text the new tab's text
182: * @param asHTML <code>true</code> to treat the specified text as HTML
183: * @param beforeIndex the index before which this tab will be inserted
184: */
185: public void insertTab(String text, boolean asHTML, int beforeIndex) {
186: checkInsertBeforeTabIndex(beforeIndex);
187:
188: Label item;
189: if (asHTML) {
190: item = new HTML(text);
191: } else {
192: item = new Label(text);
193: }
194:
195: item.setWordWrap(false);
196: item.addClickListener(this );
197: item.setStyleName(STYLENAME_DEFAULT);
198: panel.insert(item, beforeIndex + 1);
199: setStyleName(DOM.getParent(item.getElement()),
200: STYLENAME_DEFAULT + "-wrapper", true);
201: }
202:
203: /**
204: * Inserts a new tab at the specified index.
205: *
206: * @param text the new tab's text
207: * @param beforeIndex the index before which this tab will be inserted
208: */
209: public void insertTab(String text, int beforeIndex) {
210: insertTab(text, false, beforeIndex);
211: }
212:
213: /**
214: * Inserts a new tab at the specified index.
215: *
216: * @param widget widget to be used in the new tab.
217: * @param beforeIndex the index before which this tab will be inserted.
218: */
219: public void insertTab(Widget widget, int beforeIndex) {
220: checkInsertBeforeTabIndex(beforeIndex);
221:
222: ClickDecoratorPanel decWidget = new ClickDecoratorPanel(widget,
223: this );
224: decWidget.addStyleName(STYLENAME_DEFAULT);
225: panel.insert(decWidget, beforeIndex + 1);
226: setStyleName(DOM.getParent(decWidget.getElement()),
227: STYLENAME_DEFAULT + "-wrapper", true);
228: }
229:
230: public void onClick(Widget sender) {
231: for (int i = 1; i < panel.getWidgetCount() - 1; ++i) {
232: if (panel.getWidget(i) == sender) {
233: selectTab(i - 1);
234: return;
235: }
236: }
237: }
238:
239: /**
240: * Removes the tab at the specified index.
241: *
242: * @param index the index of the tab to be removed
243: */
244: public void removeTab(int index) {
245: checkTabIndex(index);
246:
247: // (index + 1) to account for 'first' placeholder widget.
248: Widget toRemove = panel.getWidget(index + 1);
249: if (toRemove == selectedTab) {
250: selectedTab = null;
251: }
252: panel.remove(toRemove);
253: }
254:
255: public void removeTabListener(TabListener listener) {
256: if (tabListeners != null) {
257: tabListeners.remove(listener);
258: }
259: }
260:
261: /**
262: * Programmatically selects the specified tab. Use index -1 to specify that no
263: * tab should be selected.
264: *
265: * @param index the index of the tab to be selected.
266: * @return <code>true</code> if successful, <code>false</code> if the
267: * change is denied by the {@link TabListener}.
268: */
269: public boolean selectTab(int index) {
270: checkTabIndex(index);
271:
272: if (tabListeners != null) {
273: if (!tabListeners.fireBeforeTabSelected(this , index)) {
274: return false;
275: }
276: }
277:
278: // Check for -1.
279: setSelectionStyle(selectedTab, false);
280: if (index == -1) {
281: selectedTab = null;
282: return true;
283: }
284:
285: selectedTab = panel.getWidget(index + 1);
286: setSelectionStyle(selectedTab, true);
287:
288: if (tabListeners != null) {
289: tabListeners.fireTabSelected(this , index);
290: }
291: return true;
292: }
293:
294: private void checkInsertBeforeTabIndex(int beforeIndex) {
295: if ((beforeIndex < 0) || (beforeIndex > getTabCount())) {
296: throw new IndexOutOfBoundsException();
297: }
298: }
299:
300: private void checkTabIndex(int index) {
301: if ((index < -1) || (index >= getTabCount())) {
302: throw new IndexOutOfBoundsException();
303: }
304: }
305:
306: private void setSelectionStyle(Widget item, boolean selected) {
307: if (item != null) {
308: if (selected) {
309: item.addStyleName("gwt-TabBarItem-selected");
310: setStyleName(DOM.getParent(item.getElement()),
311: "gwt-TabBarItem-wrapper-selected", true);
312: } else {
313: item.removeStyleName("gwt-TabBarItem-selected");
314: setStyleName(DOM.getParent(item.getElement()),
315: "gwt-TabBarItem-wrapper-selected", false);
316: }
317: }
318: }
319: }
|