KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > google > gwt > user > client > ui > DisclosurePanel


1 /*
2  * Copyright 2007 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5  * use this file except in compliance with the License. You may obtain a copy of
6  * the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13  * License for the specific language governing permissions and limitations under
14  * the License.
15  */

16 package com.google.gwt.user.client.ui;
17
18 import com.google.gwt.core.client.GWT;
19 import com.google.gwt.user.client.DOM;
20 import com.google.gwt.user.client.Element;
21 import com.google.gwt.user.client.Event;
22
23 import java.util.ArrayList JavaDoc;
24 import java.util.Iterator JavaDoc;
25
26 /**
27  * A widget that consists of a header and a content panel that discloses the
28  * content when a user clicks on the header.
29  *
30  * <h3>CSS Style Rules</h3>
31  * <ul class="css">
32  * <li>.gwt-DisclosurePanel { the panel's primary style }</li>
33  * <li>.gwt-DisclosurePanel-open { dependent style set when panel is open }</li>
34  * <li>.gwt-DisclosurePanel-closed { dependent style set when panel is closed }</li>
35  * <li>.header { the header section }</li>
36  * <li>.content { the content section }</li>
37  * </ul>
38  *
39  * <p>
40  * <img class='gallery' SRC='DisclosurePanel.png'/>
41  * </p>
42  *
43  * <p>
44  * The header and content sections can be easily selected using css with a child
45  * selector:<br/> .gwt-DisclosurePanel-open .header { ... }
46  * </p>
47  */

48 public final class DisclosurePanel extends Composite implements
49     FiresDisclosureEvents, HasWidgets {
50
51   /**
52    * Used to wrap widgets in the header to provide click support. Effectively
53    * wraps the widget in an <code>anchor</code> to get automatic keyboard
54    * access.
55    */

56   private final class ClickableHeader extends SimplePanel {
57
58     private ClickableHeader() {
59       // Anchor is used to allow keyboard access.
60
super(DOM.createAnchor());
61       Element elem = getElement();
62       DOM.setElementProperty(elem, "href", "javascript:void(0);");
63       // Avoids layout problems from having blocks in inlines.
64
DOM.setStyleAttribute(elem, "display", "block");
65       sinkEvents(Event.ONCLICK);
66       setStyleName(STYLENAME_HEADER);
67     }
68
69     public final void onBrowserEvent(Event event) {
70       // no need to call super.
71
switch (DOM.eventGetType(event)) {
72         case Event.ONCLICK:
73           // Prevent link default action.
74
DOM.eventPreventDefault(event);
75           setOpen(!isOpen);
76       }
77     }
78   }
79
80   /**
81    * The default header widget used within a {@link DisclosurePanel}.
82    */

83   private class DefaultHeader extends Widget implements HasText,
84       DisclosureHandler {
85
86     /**
87      * imageTD holds the image for the icon, not null. labelTD holds the text
88      * for the label.
89      */

90     private final Element labelTD;
91     
92     private final Image iconImage;
93     private final DisclosurePanelImages images;
94     
95     private DefaultHeader(DisclosurePanelImages images, String JavaDoc text) {
96       this.images = images;
97
98       iconImage = isOpen ? images.disclosurePanelOpen().createImage()
99           : images.disclosurePanelClosed().createImage();
100       
101       // I do not need any Widgets here, just a DOM structure.
102
Element root = DOM.createTable();
103       Element tbody = DOM.createTBody();
104       Element tr = DOM.createTR();
105       final Element imageTD = DOM.createTD();
106       labelTD = DOM.createTD();
107
108       setElement(root);
109
110       DOM.appendChild(root, tbody);
111       DOM.appendChild(tbody, tr);
112       DOM.appendChild(tr, imageTD);
113       DOM.appendChild(tr, labelTD);
114
115       // set image TD to be same width as image.
116
DOM.setElementProperty(imageTD, "align", "center");
117       DOM.setElementProperty(imageTD, "valign", "middle");
118       DOM.setStyleAttribute(imageTD, "width", iconImage.getWidth() + "px");
119
120       DOM.appendChild(imageTD, iconImage.getElement());
121
122       setText(text);
123
124       addEventHandler(this);
125       setStyle();
126     }
127
128     public final String JavaDoc getText() {
129       return DOM.getInnerText(labelTD);
130     }
131
132     public final void onClose(DisclosureEvent event) {
133       setStyle();
134     }
135
136     public final void onOpen(DisclosureEvent event) {
137       setStyle();
138     }
139
140     public final void setText(String JavaDoc text) {
141       DOM.setInnerText(labelTD, text);
142     }
143
144     private void setStyle() {
145       if (isOpen) {
146         images.disclosurePanelOpen().applyTo(iconImage);
147       } else {
148         images.disclosurePanelClosed().applyTo(iconImage);
149       }
150     }
151   }
152
153   // Stylename constants.
154
private static final String JavaDoc STYLENAME_DEFAULT = "gwt-DisclosurePanel";
155
156   private static final String JavaDoc STYLENAME_SUFFIX_OPEN = "-open";
157
158   private static final String JavaDoc STYLENAME_SUFFIX_CLOSED = "-closed";
159
160   private static final String JavaDoc STYLENAME_HEADER = "header";
161
162   private static final String JavaDoc STYLENAME_CONTENT = "content";
163
164   private static DisclosurePanelImages createDefaultImages() {
165     return (DisclosurePanelImages) GWT.create(DisclosurePanelImages.class);
166   }
167
168   /**
169    * top level widget. The first child will be a reference to {@link #header}.
170    * The second child will either not exist or be a non-null to reference to
171    * {@link #content}.
172    */

173   private final VerticalPanel mainPanel = new VerticalPanel();
174
175   /**
176    * holds the header widget.
177    */

178   private final ClickableHeader header = new ClickableHeader();
179
180   /**
181    * the content widget, this can be null.
182    */

183   private Widget content;
184
185   private boolean isOpen = false;
186
187   /**
188    * null until #{@link #addEventHandler(DisclosureHandler)} is called (lazily
189    * initialized).
190    */

191   private ArrayList JavaDoc /* <DisclosureHandler> */handlers;
192   
193   /**
194    * Creates an empty DisclosurePanel that is initially closed.
195    */

196   public DisclosurePanel() {
197     init(false);
198   }
199
200   /**
201    * Creates a DisclosurePanel with the specified header text, an initial
202    * open/close state and a bundle of images to be used in the default header
203    * widget.
204    *
205    * @param images a bundle that provides disclosure panel specific images
206    * @param headerText the text to be displayed in the header
207    * @param isOpen the initial open/close state of the content panel
208    */

209   public DisclosurePanel(DisclosurePanelImages images, String JavaDoc headerText,
210       boolean isOpen) {
211     init(isOpen);
212     setHeader(new DefaultHeader(images, headerText));
213   }
214
215   /**
216    * Creates a DisclosurePanel that will be initially closed using the specified
217    * text in the header.
218    *
219    * @param headerText the text to be displayed in the header.
220    */

221   public DisclosurePanel(String JavaDoc headerText) {
222     this(createDefaultImages(), headerText, false);
223   }
224
225   /**
226    * Creates a DisclosurePanel with the specified header text and an initial
227    * open/close state.
228    *
229    * @param headerText the text to be displayed in the header
230    * @param isOpen the initial open/close state of the content panel
231    */

232   public DisclosurePanel(String JavaDoc headerText, boolean isOpen) {
233     this(createDefaultImages(), headerText, isOpen);
234   }
235
236   /**
237    * Creates a DisclosurePanel that will be initially closed using a widget as
238    * the header.
239    *
240    * @param header the widget to be used as a header
241    */

242   public DisclosurePanel(Widget header) {
243     this(header, false);
244   }
245   
246   /**
247    * Creates a DisclosurePanel using a widget as the header and an initial
248    * open/close state.
249    *
250    * @param header the widget to be used as a header
251    * @param isOpen the initial open/close state of the content panel
252    */

253   public DisclosurePanel(Widget header, boolean isOpen) {
254     init(isOpen);
255     setHeader(header);
256   }
257
258   public void add(Widget w) {
259     if (this.getContent() == null) {
260       setContent(w);
261     } else {
262       throw new IllegalStateException JavaDoc(
263           "A DisclosurePanel can only contain two Widgets.");
264     }
265   }
266
267   /**
268    * Attaches an event handler to the panel to receive {@link DisclosureEvent}
269    * notification.
270    *
271    * @param handler the handler to be added (should not be null)
272    */

273   public final void addEventHandler(DisclosureHandler handler) {
274     if (handlers == null) {
275       handlers = new ArrayList JavaDoc();
276     }
277     handlers.add(handler);
278   }
279
280   public void clear() {
281     setContent(null);
282   }
283
284   /**
285    * Gets the widget that was previously set in {@link #setContent(Widget)}.
286    *
287    * @return the panel's current content widget
288    */

289   public final Widget getContent() {
290     return content;
291   }
292
293   /**
294    * Gets the widget that is currently being used as a header.
295    *
296    * @return the widget currently being used as a header
297    */

298   public final Widget getHeader() {
299     return header.getWidget();
300   }
301
302   /**
303    * Gets a {@link HasText} instance to provide access to the headers's text, if
304    * the header widget does provide such access.
305    *
306    * @return a reference to the header widget if it implements {@link HasText},
307    * <code>null</code> otherwise
308    */

309   public final HasText getHeaderTextAccessor() {
310     Widget widget = header.getWidget();
311     return (widget instanceof HasText) ? (HasText) widget : null;
312   }
313
314   /**
315    * Determines whether the panel is open.
316    *
317    * @return <code>true</code> if panel is in open state
318    */

319   public final boolean isOpen() {
320     return isOpen;
321   }
322
323   public Iterator JavaDoc iterator() {
324     return WidgetIterators.createWidgetIterator(this,
325         new Widget[] {getContent()});
326   }
327
328   public boolean remove(Widget w) {
329     if (w == getContent()) {
330       setContent(null);
331       return true;
332     }
333     return false;
334   }
335
336   /**
337    * Removes an event handler from the panel.
338    *
339    * @param handler the handler to be removed
340    */

341   public final void removeEventHandler(DisclosureHandler handler) {
342     if (handlers == null) {
343       return;
344     }
345     handlers.remove(handler);
346   }
347
348   /**
349    * Sets the content widget which can be opened and closed by this panel. If
350    * there is a preexisting content widget, it will be detached.
351    *
352    * @param content the widget to be used as the content panel
353    */

354   public final void setContent(Widget content) {
355     final Widget currentContent = this.content;
356
357     // Remove existing content widget.
358
if (currentContent != null) {
359       mainPanel.remove(currentContent);
360       currentContent.removeStyleName(STYLENAME_CONTENT);
361     }
362
363     // Add new content widget if != null.
364
this.content = content;
365     if (content != null) {
366       mainPanel.add(content);
367       content.addStyleName(STYLENAME_CONTENT);
368       setContentDisplay();
369     }
370   }
371
372   /**
373    * Sets the widget used as the header for the panel.
374    *
375    * @param headerWidget the widget to be used as the header
376    */

377   public final void setHeader(Widget headerWidget) {
378     header.setWidget(headerWidget);
379   }
380
381   /**
382    * Changes the visible state of this <code>DisclosurePanel</code>.
383    *
384    * @param isOpen <code>true</code> to open the panel, <code>false</code>
385    * to close
386    */

387   public final void setOpen(boolean isOpen) {
388     if (this.isOpen != isOpen) {
389       this.isOpen = isOpen;
390       setContentDisplay();
391       fireEvent();
392     }
393   }
394
395   private void fireEvent() {
396     if (handlers == null) {
397       return;
398     }
399
400     DisclosureEvent event = new DisclosureEvent(this);
401     for (Iterator JavaDoc it = handlers.iterator(); it.hasNext();) {
402       DisclosureHandler handler = (DisclosureHandler) it.next();
403       if (isOpen) {
404         handler.onOpen(event);
405       } else {
406         handler.onClose(event);
407       }
408     }
409   }
410
411   private void init(boolean isOpen) {
412     initWidget(mainPanel);
413     mainPanel.add(header);
414     this.isOpen = isOpen;
415     setStyleName(STYLENAME_DEFAULT);
416     setContentDisplay();
417   }
418
419   private void setContentDisplay() {
420     // UIObject#replaceStylename has been suggested and would replace this.
421
String JavaDoc primaryStyleName = getStyleName();
422     if (isOpen) {
423       removeStyleName(primaryStyleName + STYLENAME_SUFFIX_CLOSED);
424       addStyleName(primaryStyleName + STYLENAME_SUFFIX_OPEN);
425     } else {
426       removeStyleName(primaryStyleName + STYLENAME_SUFFIX_OPEN);
427       addStyleName(primaryStyleName + STYLENAME_SUFFIX_CLOSED);
428     }
429
430     if (content != null) {
431       content.setVisible(isOpen);
432     }
433   }
434 }
435
Popular Tags