001 /* 002 * Copyright 2012 GWT-Bootstrap 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of 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, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package com.github.gwtbootstrap.client.ui; 017 018 import java.util.HashSet; 019 import java.util.Set; 020 021 import com.github.gwtbootstrap.client.ui.base.DivWidget; 022 import com.github.gwtbootstrap.client.ui.base.HasVisibility; 023 import com.github.gwtbootstrap.client.ui.base.HasVisibleHandlers; 024 import com.github.gwtbootstrap.client.ui.base.IsAnimated; 025 import com.github.gwtbootstrap.client.ui.constants.BackdropType; 026 import com.github.gwtbootstrap.client.ui.constants.Constants; 027 import com.github.gwtbootstrap.client.ui.constants.DismissType; 028 import com.github.gwtbootstrap.client.ui.event.HiddenEvent; 029 import com.github.gwtbootstrap.client.ui.event.HiddenHandler; 030 import com.github.gwtbootstrap.client.ui.event.HideEvent; 031 import com.github.gwtbootstrap.client.ui.event.HideHandler; 032 import com.github.gwtbootstrap.client.ui.event.ShowEvent; 033 import com.github.gwtbootstrap.client.ui.event.ShowHandler; 034 import com.github.gwtbootstrap.client.ui.event.ShownEvent; 035 import com.github.gwtbootstrap.client.ui.event.ShownHandler; 036 import com.google.gwt.dom.client.Element; 037 import com.google.gwt.dom.client.Style; 038 import com.google.gwt.event.shared.HandlerRegistration; 039 import com.google.gwt.user.client.ui.PopupPanel; 040 import com.google.gwt.user.client.ui.RootPanel; 041 import com.google.gwt.user.client.ui.Widget; 042 043 //@formatter:off 044 /** 045 * Popup dialog with optional header and {@link ModalFooter footer.} 046 * <p> 047 * By default, all other Modals are closed once a new one is opened. This 048 * setting can be {@link #setHideOthers(boolean) overridden.} 049 * 050 * <p> 051 * <h3>UiBinder Usage:</h3> 052 * 053 * <pre> 054 * {@code 055 * <b:Modal title="My Modal" backdrop="STATIC"> 056 * <g:Label>Modal Content!</g:Label> 057 * <b:ModalFooter> 058 * <b:Button icon="FILE">Save</b:Button> 059 * </b:ModalFooter> 060 * </b:Modal> 061 * } 062 * </pre> 063 * 064 * All arguments are optional. 065 * </p> 066 * 067 * @since 2.0.4.0 068 * 069 * @author Carlos Alexandro Becker 070 * 071 * @author Dominik Mayer 072 * 073 * @see <a 074 * href="http://twitter.github.com/bootstrap/javascript.html#modals">Bootstrap 075 * documentation</a> 076 * @see PopupPanel 077 */ 078 // @formatter:on 079 public class Modal extends DivWidget implements HasVisibility, HasVisibleHandlers, IsAnimated { 080 081 private static Set<Modal> currentlyShown = new HashSet<Modal>(); 082 083 private final DivWidget header = new DivWidget(); 084 085 private final DivWidget body = new DivWidget("modal-body"); 086 087 private boolean keyboard = true; 088 089 private BackdropType backdropType = BackdropType.NORMAL; 090 091 private boolean show = false; 092 093 private boolean hideOthers = true; 094 095 private boolean configured = false; 096 097 private Close close = new Close(DismissType.MODAL); 098 099 private String title; 100 101 /** 102 * Creates an empty, hidden widget. 103 */ 104 public Modal() { 105 super("modal"); 106 super.add(header); 107 super.add(body); 108 setVisible(false); 109 } 110 111 /** 112 * Creates an empty, hidden widget with specified show behavior. 113 * 114 * @param animated 115 * <code>true</code> if the widget should be animated. 116 * 117 */ 118 public Modal(boolean animated) { 119 this(animated, false); 120 } 121 122 /** 123 * Creates an empty, hidden widget with specified show behavior. 124 * 125 * @param animated 126 * <code>true</code> if the widget should be animated. 127 * 128 * @param dynamicSafe 129 * <code>true</code> removes from RootPanel when hidden 130 */ 131 public Modal(boolean animated, 132 boolean dynamicSafe) { 133 this(); 134 setAnimation(animated); 135 setDynamicSafe(dynamicSafe); 136 } 137 138 /** 139 * Setup the modal to prevent memory leaks. When modal is hidden, will 140 * remove all event handlers, and them remove the modal DOM from document 141 * DOM. 142 * 143 * Default is false. 144 * 145 * @param dynamicSafe 146 */ 147 public void setDynamicSafe(boolean dynamicSafe) { 148 if (dynamicSafe) { 149 addHiddenHandler(new HiddenHandler() { 150 151 @Override 152 public void onHidden(HiddenEvent hiddenEvent) { 153 unsetHandlerFunctions(getElement()); 154 Modal.this.removeFromParent(); 155 } 156 }); 157 } 158 } 159 160 /** 161 * Sets the title of the Modal. 162 * 163 * @param title 164 * the title of the Modal 165 */ 166 @Override 167 public void setTitle(String title) { 168 this.title = title; 169 170 header.clear(); 171 if (title == null || title.isEmpty()) { 172 showHeader(false); 173 } else { 174 175 header.add(close); 176 header.add(new Heading(3, title)); 177 showHeader(true); 178 } 179 } 180 181 private void showHeader(boolean show) { 182 if (show) 183 header.setStyleName(Constants.MODAL_HEADER); 184 else 185 header.removeStyleName(Constants.MODAL_HEADER); 186 } 187 188 /** 189 * {@inheritDoc} 190 */ 191 public void setAnimation(boolean animated) { 192 if (animated) 193 addStyleName(Constants.FADE); 194 else 195 removeStyleName(Constants.FADE); 196 } 197 198 /** 199 * {@inheritDoc} 200 */ 201 public boolean getAnimation() { 202 return getStyleName().contains(Constants.FADE); 203 } 204 205 /** 206 * Sets whether this Modal appears on top of others or is the only one 207 * visible on screen. 208 * 209 * @param hideOthers 210 * <code>true</code> to make sure that this modal is the only one 211 * shown. All others will be hidden. Default: <code>true</code> 212 */ 213 public void setHideOthers(boolean hideOthers) { 214 this.hideOthers = hideOthers; 215 } 216 217 /** 218 * Sets whether the Modal is closed when the <code>ESC</code> is pressed. 219 * 220 * @param keyboard 221 * <code>true</code> if the Modal is closed by <code>ESC</code> 222 * key. Default: <code>true</code> 223 */ 224 public void setKeyboard(boolean keyboard) { 225 this.keyboard = keyboard; 226 reconfigure(); 227 } 228 229 /** 230 * Get Keyboard enable state 231 * 232 * @return true:enable false:disable 233 */ 234 public boolean isKeyboardEnable() { 235 return this.keyboard; 236 } 237 238 /** 239 * Sets the type of the backdrop. 240 * 241 * @param type 242 * the backdrop type 243 */ 244 public void setBackdrop(BackdropType type) { 245 backdropType = type; 246 reconfigure(); 247 248 } 249 250 /** 251 * Get backdrop type. 252 * 253 * @return backdrop type. 254 */ 255 public BackdropType getBackdropType() { 256 return this.backdropType; 257 } 258 259 /** 260 * Reconfigures the modal with changed settings. 261 */ 262 protected void reconfigure() { 263 if (configured) { 264 reconfigure(keyboard, backdropType, show); 265 } 266 } 267 268 /** 269 * {@inheritDoc} 270 */ 271 @Override 272 public void add(Widget w) { 273 if (w instanceof ModalFooter) { 274 super.add(w); 275 } else 276 body.add(w); 277 } 278 279 /** 280 * {@inheritDoc} 281 */ 282 @Override 283 public void insert(Widget w, int beforeIndex) { 284 body.insert(w, beforeIndex); 285 } 286 287 /** 288 * {@inheritDoc} 289 */ 290 public void show() { 291 292 if (!this.isAttached()) { 293 294 RootPanel.get().add(this); 295 } 296 297 changeVisibility("show"); 298 } 299 300 @Override 301 protected void onAttach() { 302 super.onAttach(); 303 configure(keyboard, backdropType, show); 304 setHandlerFunctions(getElement()); 305 configured = true; 306 } 307 308 /** 309 * {@inheritDoc} 310 */ 311 public void hide() { 312 changeVisibility("hide"); 313 } 314 315 /** 316 * {@inheritDoc} 317 */ 318 public void toggle() { 319 changeVisibility("toggle"); 320 } 321 322 private void changeVisibility(String visibility) { 323 changeVisibility(getElement(), visibility); 324 } 325 326 /** 327 * This method is called immediately when the widget's {@link #hide()} 328 * method is executed. 329 */ 330 protected void onHide() { 331 fireEvent(new HideEvent()); 332 } 333 334 /** 335 * This method is called once the widget is completely hidden. 336 */ 337 protected void onHidden() { 338 fireEvent(new HiddenEvent()); 339 currentlyShown.remove(this); 340 } 341 342 /** 343 * This method is called immediately when the widget's {@link #show()} 344 * method is executed. 345 */ 346 protected void onShow() { 347 if (hideOthers) 348 hideShownModals(); 349 fireEvent(new ShowEvent()); 350 } 351 352 private void hideShownModals() { 353 for (Modal m : currentlyShown) 354 m.hide(); 355 } 356 357 /** 358 * This method is called once the widget is completely shown. 359 */ 360 protected void onShown() { 361 fireEvent(new ShownEvent()); 362 currentlyShown.add(this); 363 } 364 365 private void reconfigure(boolean keyboard, BackdropType backdropType, boolean show) { 366 367 if (backdropType == BackdropType.NORMAL) { 368 reconfigure(getElement(), keyboard, true, show); 369 } else if (backdropType == BackdropType.NONE) { 370 reconfigure(getElement(), keyboard, false, show); 371 } else if (backdropType == BackdropType.STATIC) { 372 reconfigure(getElement(), keyboard, BackdropType.STATIC.get(), show); 373 } 374 } 375 376 private void configure(boolean keyboard, BackdropType backdropType, boolean show) { 377 378 if (backdropType == BackdropType.NORMAL) { 379 configure(getElement(), keyboard, true, show); 380 } else if (backdropType == BackdropType.NONE) { 381 configure(getElement(), keyboard, false, show); 382 } else if (backdropType == BackdropType.STATIC) { 383 configure(getElement(), keyboard, BackdropType.STATIC.get(), show); 384 } 385 } 386 387 //@formatter:off 388 389 private native void reconfigure(Element e, boolean k, boolean b, boolean s) /*-{ 390 var modal = null; 391 if($wnd.jQuery(e).data('modal')) { 392 modal = $wnd.jQuery(e).data('modal'); 393 $wnd.jQuery(e).removeData('modal'); 394 } 395 $wnd.jQuery(e).modal({ 396 keyboard : k, 397 backdrop : b, 398 show : s 399 }); 400 401 if(modal) { 402 $wnd.jQuery(e).data('modal').isShown = modal.isShown; 403 } 404 405 }-*/; 406 private native void reconfigure(Element e, boolean k, String b, boolean s) /*-{ 407 var modal = null; 408 if($wnd.jQuery(e).data('modal')) { 409 modal = $wnd.jQuery(e).data('modal'); 410 $wnd.jQuery(e).removeData('modal'); 411 } 412 $wnd.jQuery(e).modal({ 413 keyboard : k, 414 backdrop : b, 415 show : s 416 }); 417 418 if(modal) { 419 $wnd.jQuery(e).data('modal').isShown = modal.isShown; 420 } 421 }-*/; 422 423 424 private native void configure(Element e, boolean k, boolean b, boolean s) /*-{ 425 $wnd.jQuery(e).modal({ 426 keyboard : k, 427 backdrop : b, 428 show : s 429 }); 430 431 }-*/; 432 private native void configure(Element e, boolean k, String b, boolean s) /*-{ 433 $wnd.jQuery(e).modal({ 434 keyboard : k, 435 backdrop : b, 436 show : s 437 }); 438 }-*/; 439 440 private native void changeVisibility(Element e, String visibility) /*-{ 441 $wnd.jQuery(e).modal(visibility); 442 }-*/; 443 444 /** 445 * Links the Java functions that fire the events. 446 */ 447 private native void setHandlerFunctions(Element e) /*-{ 448 var that = this; 449 $wnd.jQuery(e).on('hide', function() { 450 [email protected]::onHide()(); 451 }); 452 $wnd.jQuery(e).on('hidden', function() { 453 [email protected]::onHidden()(); 454 }); 455 $wnd.jQuery(e).on('show', function() { 456 [email protected]::onShow()(); 457 }); 458 $wnd.jQuery(e).on('shown', function() { 459 [email protected]::onShown()(); 460 }); 461 }-*/; 462 463 /** 464 * Unlinks all the Java functions that fire the events. 465 */ 466 private native void unsetHandlerFunctions(Element e) /*-{ 467 $wnd.jQuery(e).off('hide'); 468 $wnd.jQuery(e).off('hidden'); 469 $wnd.jQuery(e).off('show'); 470 $wnd.jQuery(e).off('shown'); 471 }-*/; 472 //@formatter:on 473 474 /** 475 * {@inheritDoc} 476 */ 477 public HandlerRegistration addHideHandler(HideHandler handler) { 478 return addHandler(handler, HideEvent.getType()); 479 } 480 481 /** 482 * {@inheritDoc} 483 */ 484 public HandlerRegistration addHiddenHandler(HiddenHandler handler) { 485 return addHandler(handler, HiddenEvent.getType()); 486 } 487 488 /** 489 * {@inheritDoc} 490 */ 491 public HandlerRegistration addShowHandler(ShowHandler handler) { 492 return addHandler(handler, ShowEvent.getType()); 493 } 494 495 /** 496 * {@inheritDoc} 497 */ 498 public HandlerRegistration addShownHandler(ShownHandler handler) { 499 return addHandler(handler, ShownEvent.getType()); 500 } 501 502 /** 503 * Show/Hide close button. The Modal must have a title. 504 * 505 * @param visible 506 * <b>true</b> for show and <b>false</b> to hide. Defaults is 507 * <b>true</b>. 508 */ 509 public void setCloseVisible(boolean visible) { 510 close.getElement().getStyle().setVisibility(visible 511 ? Style.Visibility.VISIBLE 512 : Style.Visibility.HIDDEN); 513 } 514 515 /** 516 * @deprecated modal do not support setSize method 517 */ 518 @Override 519 public void setSize(String width, String height) { 520 throw new UnsupportedOperationException("modal do not support setSize method"); 521 } 522 523 }