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 java.util.Iterator;
019:
020: /**
021: * A panel that represents a tabbed set of pages, each of which contains another
022: * widget. Its child widgets are shown as the user selects the various tabs
023: * associated with them. The tabs can contain arbitrary HTML.
024: *
025: * <p>
026: * <img class='gallery' src='TabPanel.png'/>
027: * </p>
028: *
029: * <p>
030: * Note that this widget is not a panel per se, but rather a
031: * {@link com.google.gwt.user.client.ui.Composite} that aggregates a
032: * {@link com.google.gwt.user.client.ui.TabBar} and a
033: * {@link com.google.gwt.user.client.ui.DeckPanel}. It does, however, implement
034: * {@link com.google.gwt.user.client.ui.HasWidgets}.
035: * </p>
036: *
037: * <h3>CSS Style Rules</h3>
038: * <ul class='css'>
039: * <li>.gwt-TabPanel { the tab panel itself }</li>
040: * <li>.gwt-TabPanelBottom { the bottom section of the tab panel (the deck
041: * containing the widget) }</li>
042: * </ul>
043: *
044: * <p>
045: * <h3>Example</h3>
046: * {@example com.google.gwt.examples.TabPanelExample}
047: * </p>
048: */
049: public class TabPanel extends Composite implements TabListener,
050: SourcesTabEvents, HasWidgets, IndexedPanel {
051:
052: /**
053: * This extension of DeckPanel overrides the public mutator methods to prevent
054: * external callers from adding to the state of the DeckPanel.
055: * <p>
056: * Removal of Widgets is supported so that WidgetCollection.WidgetIterator
057: * operates as expected.
058: * </p>
059: * <p>
060: * We ensure that the DeckPanel cannot become of of sync with its associated
061: * TabBar by delegating all mutations to the TabBar to this implementation of
062: * DeckPanel.
063: * </p>
064: */
065: private static class TabbedDeckPanel extends DeckPanel {
066: private UnmodifiableTabBar tabBar;
067:
068: public TabbedDeckPanel(UnmodifiableTabBar tabBar) {
069: this .tabBar = tabBar;
070: }
071:
072: @Override
073: public void add(Widget w) {
074: throw new UnsupportedOperationException(
075: "Use TabPanel.add() to alter the DeckPanel");
076: }
077:
078: @Override
079: public void clear() {
080: throw new UnsupportedOperationException(
081: "Use TabPanel.clear() to alter the DeckPanel");
082: }
083:
084: @Override
085: public void insert(Widget w, int beforeIndex) {
086: throw new UnsupportedOperationException(
087: "Use TabPanel.insert() to alter the DeckPanel");
088: }
089:
090: @Override
091: public boolean remove(Widget w) {
092: // Removal of items from the TabBar is delegated to the DeckPanel
093: // to ensure consistency
094: int idx = getWidgetIndex(w);
095: if (idx != -1) {
096: tabBar.removeTabProtected(idx);
097: return super .remove(w);
098: }
099:
100: return false;
101: }
102:
103: protected void insertProtected(Widget w, String tabText,
104: boolean asHTML, int beforeIndex) {
105:
106: // Check to see if the TabPanel already contains the Widget. If so,
107: // remove it and see if we need to shift the position to the left.
108: int idx = getWidgetIndex(w);
109: if (idx != -1) {
110: remove(w);
111: if (idx < beforeIndex) {
112: beforeIndex--;
113: }
114: }
115:
116: tabBar.insertTabProtected(tabText, asHTML, beforeIndex);
117: super .insert(w, beforeIndex);
118: }
119:
120: protected void insertProtected(Widget w, Widget tabWidget,
121: int beforeIndex) {
122:
123: // Check to see if the TabPanel already contains the Widget. If so,
124: // remove it and see if we need to shift the position to the left.
125: int idx = getWidgetIndex(w);
126: if (idx != -1) {
127: remove(w);
128: if (idx < beforeIndex) {
129: beforeIndex--;
130: }
131: }
132:
133: tabBar.insertTabProtected(tabWidget, beforeIndex);
134: super .insert(w, beforeIndex);
135: }
136: };
137:
138: /**
139: * This extension of TabPanel overrides the public mutator methods to prevent
140: * external callers from modifying the state of the TabBar.
141: */
142: private static class UnmodifiableTabBar extends TabBar {
143: @Override
144: public void insertTab(String text, boolean asHTML,
145: int beforeIndex) {
146: throw new UnsupportedOperationException(
147: "Use TabPanel.insert() to alter the TabBar");
148: }
149:
150: @Override
151: public void insertTab(Widget widget, int beforeIndex) {
152: throw new UnsupportedOperationException(
153: "Use TabPanel.insert() to alter the TabBar");
154: }
155:
156: @Override
157: public void removeTab(int index) {
158: // It's possible for removeTab() to function correctly, but it's
159: // preferable to have only TabbedDeckPanel.remove() be operable,
160: // especially since TabBar does not export an Iterator over its values.
161: throw new UnsupportedOperationException(
162: "Use TabPanel.remove() to alter the TabBar");
163: }
164:
165: protected void insertTabProtected(String text, boolean asHTML,
166: int beforeIndex) {
167: super .insertTab(text, asHTML, beforeIndex);
168: }
169:
170: protected void insertTabProtected(Widget widget, int beforeIndex) {
171: super .insertTab(widget, beforeIndex);
172: }
173:
174: protected void removeTabProtected(int index) {
175: super .removeTab(index);
176: }
177: }
178:
179: private UnmodifiableTabBar tabBar = new UnmodifiableTabBar();
180: private TabbedDeckPanel deck = new TabbedDeckPanel(tabBar);
181: private TabListenerCollection tabListeners;
182:
183: /**
184: * Creates an empty tab panel.
185: */
186: public TabPanel() {
187: VerticalPanel panel = new VerticalPanel();
188: panel.add(tabBar);
189: panel.add(deck);
190:
191: panel.setCellHeight(deck, "100%");
192: tabBar.setWidth("100%");
193:
194: tabBar.addTabListener(this );
195: initWidget(panel);
196: setStyleName("gwt-TabPanel");
197: deck.setStyleName("gwt-TabPanelBottom");
198: }
199:
200: public void add(Widget w) {
201: throw new UnsupportedOperationException(
202: "A tabText parameter must be specified with add().");
203: }
204:
205: /**
206: * Adds a widget to the tab panel. If the Widget is already attached to the
207: * TabPanel, it will be moved to the right-most index.
208: *
209: * @param w the widget to be added
210: * @param tabText the text to be shown on its tab
211: */
212: public void add(Widget w, String tabText) {
213: insert(w, tabText, getWidgetCount());
214: }
215:
216: /**
217: * Adds a widget to the tab panel. If the Widget is already attached to the
218: * TabPanel, it will be moved to the right-most index.
219: *
220: * @param w the widget to be added
221: * @param tabText the text to be shown on its tab
222: * @param asHTML <code>true</code> to treat the specified text as HTML
223: */
224: public void add(Widget w, String tabText, boolean asHTML) {
225: insert(w, tabText, asHTML, getWidgetCount());
226: }
227:
228: /**
229: * Adds a widget to the tab panel. If the Widget is already attached to the
230: * TabPanel, it will be moved to the right-most index.
231: *
232: * @param w the widget to be added
233: * @param tabWidget the widget to be shown in the tab
234: */
235: public void add(Widget w, Widget tabWidget) {
236: insert(w, tabWidget, getWidgetCount());
237: }
238:
239: public void addTabListener(TabListener listener) {
240: if (tabListeners == null) {
241: tabListeners = new TabListenerCollection();
242: }
243: tabListeners.add(listener);
244: }
245:
246: public void clear() {
247: while (getWidgetCount() > 0) {
248: remove(getWidget(0));
249: }
250: }
251:
252: /**
253: * Gets the deck panel within this tab panel. Adding or removing Widgets from
254: * the DeckPanel is not supported and will throw
255: * UnsupportedOperationExceptions.
256: *
257: * @return the deck panel
258: */
259: public DeckPanel getDeckPanel() {
260: return deck;
261: }
262:
263: /**
264: * Gets the tab bar within this tab panel. Adding or removing tabs from from
265: * the TabBar is not supported and will throw UnsupportedOperationExceptions.
266: *
267: * @return the tab bar
268: */
269: public TabBar getTabBar() {
270: return tabBar;
271: }
272:
273: public Widget getWidget(int index) {
274: return deck.getWidget(index);
275: }
276:
277: public int getWidgetCount() {
278: return deck.getWidgetCount();
279: }
280:
281: public int getWidgetIndex(Widget widget) {
282: return deck.getWidgetIndex(widget);
283: }
284:
285: /**
286: * Inserts a widget into the tab panel. If the Widget is already attached to
287: * the TabPanel, it will be moved to the requested index.
288: *
289: * @param widget the widget to be inserted
290: * @param tabText the text to be shown on its tab
291: * @param asHTML <code>true</code> to treat the specified text as HTML
292: * @param beforeIndex the index before which it will be inserted
293: */
294: public void insert(Widget widget, String tabText, boolean asHTML,
295: int beforeIndex) {
296: // Delegate updates to the TabBar to our DeckPanel implementation
297: deck.insertProtected(widget, tabText, asHTML, beforeIndex);
298: }
299:
300: /**
301: * Inserts a widget into the tab panel. If the Widget is already attached to
302: * the TabPanel, it will be moved to the requested index.
303: *
304: * @param widget the widget to be inserted.
305: * @param tabWidget the widget to be shown on its tab.
306: * @param beforeIndex the index before which it will be inserted.
307: */
308: public void insert(Widget widget, Widget tabWidget, int beforeIndex) {
309: // Delegate updates to the TabBar to our DeckPanel implementation
310: deck.insertProtected(widget, tabWidget, beforeIndex);
311: }
312:
313: /**
314: * Inserts a widget into the tab panel. If the Widget is already attached to
315: * the TabPanel, it will be moved to the requested index.
316: *
317: * @param widget the widget to be inserted
318: * @param tabText the text to be shown on its tab
319: * @param beforeIndex the index before which it will be inserted
320: */
321: public void insert(Widget widget, String tabText, int beforeIndex) {
322: insert(widget, tabText, false, beforeIndex);
323: }
324:
325: public Iterator<Widget> iterator() {
326: // The Iterator returned by DeckPanel supports removal and will invoke
327: // TabbedDeckPanel.remove(), which is an active function.
328: return deck.iterator();
329: }
330:
331: public boolean onBeforeTabSelected(SourcesTabEvents sender,
332: int tabIndex) {
333: if (tabListeners != null) {
334: return tabListeners.fireBeforeTabSelected(this , tabIndex);
335: }
336: return true;
337: }
338:
339: public void onTabSelected(SourcesTabEvents sender, int tabIndex) {
340: deck.showWidget(tabIndex);
341: if (tabListeners != null) {
342: tabListeners.fireTabSelected(this , tabIndex);
343: }
344: }
345:
346: public boolean remove(int index) {
347: // Delegate updates to the TabBar to our DeckPanel implementation
348: return deck.remove(index);
349: }
350:
351: /**
352: * Removes the given widget, and its associated tab.
353: *
354: * @param widget the widget to be removed
355: */
356: public boolean remove(Widget widget) {
357: // Delegate updates to the TabBar to our DeckPanel implementation
358: return deck.remove(widget);
359: }
360:
361: public void removeTabListener(TabListener listener) {
362: if (tabListeners != null) {
363: tabListeners.remove(listener);
364: }
365: }
366:
367: /**
368: * Programmatically selects the specified tab.
369: *
370: * @param index the index of the tab to be selected
371: */
372: public void selectTab(int index) {
373: tabBar.selectTab(index);
374: }
375: }
|