View Javadoc

1   /**
2    * Copyright 2010 The Kuali Foundation Licensed under the
3    * Educational Community License, Version 2.0 (the "License"); you may
4    * not use this file except in compliance with the License. You may
5    * obtain a copy of the License at
6    *
7    * http://www.osedu.org/licenses/ECL-2.0
8    *
9    * Unless required by applicable law or agreed to in writing,
10   * software distributed under the License is distributed on an "AS IS"
11   * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
12   * or implied. See the License for the specific language governing
13   * permissions and limitations under the License.
14   */
15  
16  package org.kuali.student.common.ui.client.widgets;
17  
18  import java.util.Arrays;
19  import java.util.List;
20  
21  import com.google.gwt.dom.client.Element;
22  import com.google.gwt.dom.client.NativeEvent;
23  import com.google.gwt.dom.client.NodeList;
24  import com.google.gwt.dom.client.Style.Overflow;
25  import com.google.gwt.dom.client.Style.Unit;
26  import com.google.gwt.event.dom.client.ClickEvent;
27  import com.google.gwt.event.dom.client.ClickHandler;
28  import com.google.gwt.event.dom.client.KeyCodes;
29  import com.google.gwt.event.logical.shared.ResizeEvent;
30  import com.google.gwt.event.logical.shared.ResizeHandler;
31  import com.google.gwt.event.shared.HandlerRegistration;
32  import com.google.gwt.user.client.Command;
33  import com.google.gwt.user.client.DeferredCommand;
34  import com.google.gwt.user.client.Event.NativePreviewEvent;
35  import com.google.gwt.user.client.Window;
36  import com.google.gwt.user.client.ui.Anchor;
37  import com.google.gwt.user.client.ui.DialogBox;
38  import com.google.gwt.user.client.ui.DockLayoutPanel;
39  import com.google.gwt.user.client.ui.FlowPanel;
40  import com.google.gwt.user.client.ui.ScrollPanel;
41  import com.google.gwt.user.client.ui.SimplePanel;
42  import com.google.gwt.user.client.ui.Widget;
43  
44  /**
45   * A KSLightBox is a dialog box that lays out its contents as follows using a
46   * {@link DockLayoutPanel}:<br/>
47   * <br />
48   * ------------------<br/>
49   * HEADER (either static caption / header widget that is not part of the scrollable area)<br/>
50   * ------------------<br/>
51   * CONTENT (part of the scrollable area that will fill available space)<br/>
52   * ------------------<br/>
53   * BUTTONS (static button area that is not part of the scrollable area)<br/>
54   * ------------------<br/>
55   * <br/>
56   * 
57   * The size of the dock panel will determine the size of the lightbox. Thus only the dock
58   * panel size will need to be set.<br/>
59   * The size of the dock panel is usually determined dynamically when the lightbox is
60   * displayed, but you can also statically set it with one of the 'setSize' methods.<br/>
61   * If you however set the size statically, the lightbox won't resize.<br/>
62   * If you're making use of the <b>static sizes, the non-caption header and displaying the
63   * buttons</b>, don't set the height of the dialog smaller than about 155px. Otherwise
64   * there's too little space left for the content and the content (even if its just one line 
65   * of text) will be displayed in a scroll panel.
66   * 
67   */
68  public class KSLightBox extends DialogBox {
69  
70      private static final List<String> FOCUSABLE_TAGS = Arrays.asList("INPUT", "SELECT", "BUTTON", "TEXTAREA");
71  
72      /**
73       * An enum that specifies predefined width and height values (in pixels) for the
74       * lightbox.
75       */
76      public enum Size {
77          SMALL(400, 155), MEDIUM(550, 340), LARGE(600, 400);
78  
79          private int width;
80  
81          private int height;
82  
83          Size(int width, int height) {
84              this.width = width;
85              this.height = height;
86          }
87  
88          public int getWidth() {
89              return width;
90          }
91  
92          public int getHeight() {
93              return height;
94          }
95      }
96  
97      private enum LightBoxStyle {
98          LIGHT_BOX {
99              public String toString() {
100                 return "ks-lightbox";
101             }
102         },
103         MAIN_PANEL {
104             public String toString() {
105                 return "ks-lightbox-mainPanel";
106             }
107         },
108         TITLE_PANEL {
109             public String toString() {
110                 return "ks-lightbox-titlePanel";
111             }
112         },
113         SCROLL_PANEL {
114             public String toString() {
115                 return "ks-lightbox-scrollPanel";
116             }
117         },
118         BUTTON_PANEL {
119             public String toString() {
120                 return "ks-lightbox-buttonPanel";
121             }
122         },
123         CLOSE_LINK {
124             public String toString() {
125                 return "ks-lightbox-closeLink";
126             }
127         },
128         CLOSE_LINK_WITH_CAPTION {
129             public String toString() {
130                 return "ks-lightbox-closeLink-with-Caption";
131             }
132         }
133     }
134 
135     //Resizing variables
136     private int maxWidth = 800;
137     private int maxHeight = 0;
138     private int minWidth = 400;
139     private int minHeight = 200;
140     private int permWidth = -1;
141     private int permHeight = -1;
142 
143     //DockLayoutPanel sizes
144     private static final int BUTTON_HEIGHT = 40;//'px' units (specified in mainPanel's constructor)
145     private static final int NON_CAPTION_HEIGHT = 40;//'px' units (specified in mainPanel's constructor)
146 
147     private DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX);
148     private FlowPanel closeLinkPanel = new FlowPanel();
149     private SimplePanel titlePanel = new SimplePanel();
150     private ScrollPanel scrollPanel = new ScrollPanel();
151     private SimplePanel contentPanel = new SimplePanel();
152     private FlowPanel buttonPanel = new FlowPanel();
153     private Anchor closeLink = new Anchor();
154 
155     private KSDialogResizeHandler resizeHandler = new KSDialogResizeHandler();
156     private HandlerRegistration resizeHandlerRegistrater;
157 
158     public KSLightBox() {
159         getCaption().asWidget().setVisible(false);
160         init();
161     }
162 
163     public KSLightBox(boolean addCloseLink) {
164         getCaption().asWidget().setVisible(false);
165         init();
166         closeLink.setVisible(addCloseLink);
167     }
168 
169     public KSLightBox(String title) {
170         init();
171         setText(title);
172     }
173 
174     public KSLightBox(String title, Size size) {
175         init();
176         setText(title);
177         setSize(size);
178     }
179 
180     private void init() {
181         mainPanel.setStyleName(LightBoxStyle.MAIN_PANEL.toString());
182         titlePanel.setStyleName(LightBoxStyle.TITLE_PANEL.toString());
183         closeLink.setStyleName(LightBoxStyle.CLOSE_LINK.toString());
184         scrollPanel.setStyleName(LightBoxStyle.SCROLL_PANEL.toString());
185         buttonPanel.setStyleName(LightBoxStyle.BUTTON_PANEL.toString());
186 
187         setGlassEnabled(true);
188         super.setWidget(mainPanel);
189         mainPanel.addNorth(closeLinkPanel, 1);
190         mainPanel.addNorth(titlePanel, 0);
191         mainPanel.addSouth(buttonPanel, BUTTON_HEIGHT);
192         mainPanel.add(scrollPanel);
193         closeLinkPanel.add(closeLink);
194         //parent element sets overflow to hidden to allow background and other css styling on the titlePanel
195         Element titlePanelContainer = mainPanel.getWidgetContainerElement(titlePanel);
196         titlePanelContainer.getStyle().setOverflow(Overflow.VISIBLE);
197         //parent element sets overflow to hidden, must reset overflow to visible to show the 'closeLink'
198         Element closeLinkPanelContainer = mainPanel.getWidgetContainerElement(closeLinkPanel);
199         closeLinkPanelContainer.getStyle().setOverflow(Overflow.VISIBLE);
200         scrollPanel.add(contentPanel);
201 
202         installResizeHandler();
203         //super.
204         closeLink.addClickHandler(new ClickHandler() {
205 
206             @Override
207             public void onClick(ClickEvent event) {
208                 hide();
209             }
210         });
211         super.setStyleName(LightBoxStyle.LIGHT_BOX.toString());
212 
213     }
214 
215     public void setCloseLinkVisible(boolean visible) {
216         closeLink.setVisible(visible);
217     }
218 
219     public void removeCloseLink() {
220         closeLink.setVisible(false);
221     }
222 
223     public HandlerRegistration addCloseLinkClickHandler(ClickHandler clickHandler) {
224         return closeLink.addClickHandler(clickHandler);
225     }
226 
227     /**
228      * Sets the header that will be displayed at the top of the lightbox.<br/>
229      * Please note: This header will not be displayed in the caption, but in the actual
230      * lightbox content area.
231      * @param widget The header widget.
232      */
233     public void setNonCaptionHeader(Widget widget) {
234         titlePanel.setWidget(widget);
235         mainPanel.setWidgetSize(titlePanel, NON_CAPTION_HEIGHT);
236         mainPanel.forceLayout();
237     }
238 
239     @Override
240     public void setText(String text) {
241         super.setText(text);
242         getCaption().asWidget().setVisible(true);
243         //Style needed to reposition the 'closeLink'
244         closeLink.addStyleName(LightBoxStyle.CLOSE_LINK_WITH_CAPTION.toString());
245     }
246     
247     /**
248      * Removes all the buttons at the bottom of the lightbox.
249      */
250     public void clearButtons() {
251         buttonPanel.clear();
252     }
253 
254     /**
255      * Adds a button to the bottom of the lightbox.
256      */
257     public void addButton(Widget button) {
258         button.addStyleName("ks-button-spacing");
259         buttonPanel.add(button);
260     }
261 
262     /**
263      * Adds a {@link org.kuali.student.common.ui.client.widgets.field.layout.button.ButtonGroup} to the button panel at the bottom of the lightbox. 
264      * @param group
265      */
266     @SuppressWarnings("rawtypes")
267     public void addButtonGroup(org.kuali.student.common.ui.client.widgets.field.layout.button.ButtonGroup group) {
268         buttonPanel.add(group);
269     }
270     
271     /**
272      * Adds a {@link org.kuali.student.common.ui.client.widgets.buttongroups.ButtonGroup} to the button panel at the bottom of the lightbox.
273      * @param group
274      */
275     @SuppressWarnings("rawtypes")
276     public void addButtonGroup(org.kuali.student.common.ui.client.widgets.buttongroups.ButtonGroup group) {
277         buttonPanel.add(group);
278         
279     }
280 
281     public void showButtons(boolean show) {
282         buttonPanel.setVisible(show);
283         if (show) {
284             mainPanel.setWidgetSize(buttonPanel, BUTTON_HEIGHT);
285         } else {
286             mainPanel.setWidgetSize(buttonPanel, 0);
287         }
288         mainPanel.forceLayout();
289     }
290 
291     /**
292      * Set the maximum width in pixels that this dialog will grow to.<br />
293      * Please note: If the lightbox's size was set explicitly, this call will have no
294      * effect.
295      * 
296      * @param width The dialog's maximum width in pixels.
297      */
298     public void setMaxWidth(int width) {
299         this.maxWidth = width;
300     }
301 
302     /**
303      * Set the maximum height in pixels that this dialog will grow to.<br />
304      * Please note: If the lightbox's size was set explicitly, this call will have no
305      * effect.
306      * 
307      * @param height The dialog's maximum height in pixels.
308      */
309     public void setMaxHeight(int height) {
310         this.maxHeight = height;
311     }
312 
313     /**
314      * Set the width and height of the lightbox in pixels.<br/>
315      * Please note: These values will not be affected by resizing. Thus the lightbox will
316      * remain the specified size, irrespective of resizing.
317      * 
318      * @param width The specified width in pixels.
319      * @param height The specified height in pixels.
320      */
321     public void setSize(int width, int height) {
322         mainPanel.setSize(width + "px", width + "px");
323         this.permHeight = height;
324         this.permWidth = width;
325     }
326 
327     /**
328      * Set the width and height of the lightbox in pixels using the {@link Size} enum.
329      * 
330      * @param size A predefined dialog size.
331      */
332     public void setSize(Size size) {
333         setSize(size.getWidth(), size.getHeight());
334     }
335 
336     @Override
337     public void setWidget(Widget content) {
338         contentPanel.setWidget(content);
339     }
340 
341     @Override
342     public Widget getWidget() {
343         return contentPanel.getWidget();
344     }
345 
346     @Override
347     public void hide() {
348         super.hide();
349         uninstallResizeHandler();
350     }
351 
352     @Override
353     public void show() {
354         resizeDialog();
355         installResizeHandler();
356         super.show();
357         super.center();
358         grabFocus();
359     }
360 
361     private void resizeDialog() {
362 
363         int width = maxWidth;
364         int height = maxHeight;
365 
366         //Width calculation
367         if (permWidth != -1) {
368             width = permWidth;
369         } else {
370             if (Window.getClientWidth() < 850) {
371                 width = Window.getClientWidth() - 160;
372             }
373             if (width > maxWidth) {
374                 width = maxWidth;
375             }
376             if (width < minWidth) {
377                 width = minWidth;
378             }
379         }
380 
381         //Height calculation
382         if (permHeight != -1) {
383             height = permHeight;
384         } else {
385             height = Window.getClientHeight() - 160;
386 
387             if (height > maxHeight && maxHeight != 0) {
388                 height = maxHeight;
389             }
390             if (height < minHeight) {
391                 height = minHeight;
392             }
393         }
394 
395         if (width > 0 && height > 0) {
396             mainPanel.setSize(width + "px", height + "px");
397         }
398 
399     }
400 
401     private void grabFocus() {
402         Widget mainContent = contentPanel.getWidget();
403         NodeList<Element> nodeList = mainContent.getElement().getElementsByTagName("*");
404         for (int i = 0; i < nodeList.getLength(); i++) {
405             Element e = nodeList.getItem(i);
406             if (FOCUSABLE_TAGS.contains(e.getTagName().toUpperCase())) {
407                 e.focus();
408                 return;
409             }
410         }
411 
412     }
413 
414     @Override
415     protected void onPreviewNativeEvent(NativePreviewEvent preview) {
416         super.onPreviewNativeEvent(preview);
417         NativeEvent evt = preview.getNativeEvent();
418         if (evt.getType().equals("keydown")) {
419             switch (evt.getKeyCode()) {
420                 case KeyCodes.KEY_ESCAPE:
421                     hide();
422                     break;
423             }
424         }
425     }
426 
427     public void uninstallResizeHandler() {
428         if (resizeHandlerRegistrater != null) {
429             resizeHandlerRegistrater.removeHandler();
430             resizeHandlerRegistrater = null;
431 
432         }
433     }
434 
435     public void installResizeHandler() {
436         if (resizeHandlerRegistrater == null) {
437             resizeHandlerRegistrater = Window.addResizeHandler(resizeHandler);
438         }
439     }
440 
441     class KSDialogResizeHandler implements ResizeHandler {
442         @SuppressWarnings("deprecation")
443         @Override
444         public void onResize(ResizeEvent event) {
445             DeferredCommand.addCommand(new Command() {
446 
447                 @Override
448                 public void execute() {
449                     resizeDialog();
450                     int left = (Window.getClientWidth() - getOffsetWidth()) >> 1;
451                     int top = (Window.getClientHeight() - getOffsetHeight()) >> 1;
452                     setPopupPosition(Math.max(Window.getScrollLeft() + left, 0), Math.max(
453                             Window.getScrollTop() + top, 0));
454                 }
455             });
456         }
457     }
458 
459     
460 
461 }