001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.gwt.client.ui; 029 030import org.opencms.gwt.client.Messages; 031import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 032import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 033import org.opencms.gwt.client.util.CmsFadeAnimation; 034import org.opencms.util.CmsStringUtil; 035 036import java.util.Iterator; 037import java.util.List; 038 039import com.google.common.collect.Lists; 040import com.google.gwt.core.client.GWT; 041import com.google.gwt.dom.client.Document; 042import com.google.gwt.dom.client.Element; 043import com.google.gwt.dom.client.EventTarget; 044import com.google.gwt.dom.client.NativeEvent; 045import com.google.gwt.dom.client.Style.Position; 046import com.google.gwt.dom.client.Style.Unit; 047import com.google.gwt.event.dom.client.ClickEvent; 048import com.google.gwt.event.dom.client.ClickHandler; 049import com.google.gwt.event.dom.client.MouseDownEvent; 050import com.google.gwt.event.dom.client.MouseDownHandler; 051import com.google.gwt.event.dom.client.MouseMoveEvent; 052import com.google.gwt.event.dom.client.MouseMoveHandler; 053import com.google.gwt.event.dom.client.MouseUpEvent; 054import com.google.gwt.event.dom.client.MouseUpHandler; 055import com.google.gwt.event.logical.shared.CloseEvent; 056import com.google.gwt.event.logical.shared.CloseHandler; 057import com.google.gwt.event.logical.shared.ResizeEvent; 058import com.google.gwt.event.logical.shared.ResizeHandler; 059import com.google.gwt.event.logical.shared.ValueChangeEvent; 060import com.google.gwt.event.logical.shared.ValueChangeHandler; 061import com.google.gwt.event.shared.HandlerRegistration; 062import com.google.gwt.user.client.Command; 063import com.google.gwt.user.client.DOM; 064import com.google.gwt.user.client.Event; 065import com.google.gwt.user.client.Event.NativePreviewEvent; 066import com.google.gwt.user.client.History; 067import com.google.gwt.user.client.Window; 068import com.google.gwt.user.client.ui.FlowPanel; 069import com.google.gwt.user.client.ui.HTML; 070import com.google.gwt.user.client.ui.IsWidget; 071import com.google.gwt.user.client.ui.PopupPanel; 072import com.google.gwt.user.client.ui.SimplePanel; 073import com.google.gwt.user.client.ui.Widget; 074import com.google.gwt.user.client.ui.WidgetCollection; 075 076/** 077 * Provides a pop up dialog base. 078 * 079 * @since 8.0.0 080 */ 081public class CmsPopup extends PopupPanel implements I_CmsAutoHider { 082 083 /** 084 * Handles fragment changes by closing the active popups.<p> 085 * 086 * Only used for GWT dialogs opened from Vaadin. 087 */ 088 public static class HistoryHandler implements ValueChangeHandler<String> { 089 090 /** The list of active popups. */ 091 private List<CmsPopup> m_popups = Lists.newArrayList(); 092 093 /** 094 * Adds a popup to the list of active popups.<p> 095 * 096 * @param popup the popup 097 */ 098 public void addPopup(CmsPopup popup) { 099 100 m_popups.add(popup); 101 } 102 103 /** 104 * @see com.google.gwt.event.logical.shared.ValueChangeHandler#onValueChange(com.google.gwt.event.logical.shared.ValueChangeEvent) 105 */ 106 public void onValueChange(ValueChangeEvent<String> event) { 107 108 if (GWT.getModuleName().equals("org.opencms.ui.WidgetSet")) { 109 // only do this when popup is opened from Vaadin 110 // (copying the list to avoid ConcurrentModificationExceptions) 111 for (CmsPopup popup : Lists.newArrayList(m_popups)) { 112 try { 113 if (popup.isAttached()) { 114 popup.hide(); 115 } 116 } catch (Exception e) { 117 // ignore 118 } 119 } 120 } 121 m_popups.clear(); 122 } 123 124 /** 125 * Removes a popup from the list of active popups.<p> 126 * 127 * @param popup the popup to remove 128 */ 129 public void removePopup(CmsPopup popup) { 130 131 m_popups.remove(popup); 132 } 133 134 } 135 136 /** 137 * The dialog button panel.<p> 138 */ 139 private class ButtonPanel extends FlowPanel { 140 141 /** 142 * Default constructor.<p> 143 */ 144 protected ButtonPanel() { 145 146 // nothing to do 147 } 148 149 /** 150 * Making function visible.<p> 151 * 152 * @see com.google.gwt.user.client.ui.Widget#onAttach() 153 */ 154 @Override 155 protected void onAttach() { 156 157 super.onAttach(); 158 } 159 160 /** 161 * Making function visible.<p> 162 * 163 * @see com.google.gwt.user.client.ui.Widget#onDetach() 164 */ 165 @Override 166 protected void onDetach() { 167 168 super.onDetach(); 169 } 170 } 171 172 /** 173 * The dialog caption.<p> 174 */ 175 private class Caption extends HTML { 176 177 /** 178 * Default constructor.<p> 179 */ 180 protected Caption() { 181 182 // nothing to do 183 } 184 185 /** 186 * Making function visible.<p> 187 * 188 * @see com.google.gwt.user.client.ui.Widget#onAttach() 189 */ 190 @Override 191 protected void onAttach() { 192 193 super.onAttach(); 194 } 195 196 /** 197 * Making function visible.<p> 198 * 199 * @see com.google.gwt.user.client.ui.Widget#onDetach() 200 */ 201 @Override 202 protected void onDetach() { 203 204 super.onDetach(); 205 } 206 } 207 208 /** 209 * The dialog close button.<p> 210 */ 211 private class CloseButton extends CmsPushButton { 212 213 /** 214 * Default constructor.<p> 215 */ 216 protected CloseButton() { 217 218 // nothing to do 219 } 220 221 /** 222 * Making function visible.<p> 223 * 224 * @see com.google.gwt.user.client.ui.Widget#onAttach() 225 */ 226 @Override 227 protected void onAttach() { 228 229 super.onAttach(); 230 } 231 232 /** 233 * Making function visible.<p> 234 * 235 * @see com.google.gwt.user.client.ui.Widget#onDetach() 236 */ 237 @Override 238 protected void onDetach() { 239 240 super.onDetach(); 241 } 242 } 243 244 /** 245 * The dialog mouse handler.<p> 246 */ 247 private class MouseHandler implements MouseDownHandler, MouseUpHandler, MouseMoveHandler { 248 249 /** 250 * Default constructor.<p> 251 */ 252 protected MouseHandler() { 253 254 // nothing to do 255 } 256 257 /** 258 * @see com.google.gwt.event.dom.client.MouseDownHandler#onMouseDown(com.google.gwt.event.dom.client.MouseDownEvent) 259 */ 260 public void onMouseDown(MouseDownEvent event) { 261 262 beginDragging(event); 263 } 264 265 /** 266 * @see com.google.gwt.event.dom.client.MouseMoveHandler#onMouseMove(com.google.gwt.event.dom.client.MouseMoveEvent) 267 */ 268 public void onMouseMove(MouseMoveEvent event) { 269 270 continueDragging(event); 271 } 272 273 /** 274 * @see com.google.gwt.event.dom.client.MouseUpHandler#onMouseUp(com.google.gwt.event.dom.client.MouseUpEvent) 275 */ 276 public void onMouseUp(MouseUpEvent event) { 277 278 endDragging(event); 279 } 280 } 281 282 /** The default width of this dialog. */ 283 public static final int DEFAULT_WIDTH = 600; 284 285 /** The wide dialog width. */ 286 public static final int WIDE_WIDTH = 800; 287 288 /** The history handler used to remove popups when the fragment is changed. */ 289 private static HistoryHandler m_historyHandler; 290 291 /** The close command. */ 292 protected Command m_closeCommand; 293 294 /** Flag which indicates whether the notification widget has already been installed. */ 295 protected boolean m_notificationWidgetInstalled; 296 297 /** The window width. */ 298 protected int m_windowWidth; 299 300 /** The panel holding the dialog's buttons. */ 301 private ButtonPanel m_buttonPanel; 302 303 /** The dialog caption. */ 304 private Caption m_caption; 305 306 /** Flag indicating if the dialog should catch all notifications while visible. */ 307 private boolean m_catchNotifications; 308 309 /** The child widgets. */ 310 private WidgetCollection m_children; 311 312 /** Body offset left. */ 313 private int m_clientLeft; 314 315 /** Body offset top. */ 316 private int m_clientTop; 317 318 /** The panel for the close button. */ 319 private CloseButton m_close; 320 321 /** The dialog closing handler registration used for organizing notifications. */ 322 private HandlerRegistration m_closingHandlerRegistration; 323 324 /** The popup container element. */ 325 private Element m_containerElement; 326 327 /** The content height correction, used when explicitly setting the dialog height. */ 328 private int m_contentHeightCorrection = 6; 329 330 /** Flag if dragging. */ 331 private boolean m_dragging; 332 333 /** Drag starting x position. */ 334 private int m_dragStartX; 335 336 /** Drag starting y position. */ 337 private int m_dragStartY; 338 339 /** The main widget of this dialog containing all others. */ 340 private Element m_main; 341 342 /** The own notification widget. */ 343 private CmsNotificationWidget m_ownNotificationWidget; 344 345 /** The parent notification widget. */ 346 private I_CmsNotificationWidget m_parentNotificationWidget; 347 348 /** The resize handler registration .*/ 349 private HandlerRegistration m_resizeHandlerRegistration; 350 351 /** Signals whether a animation should be used to show the popup or not. */ 352 private boolean m_useAnimation = true; 353 354 /** The content width, -1 indicating the value was not set. */ 355 private int m_width = -1; 356 357 /** 358 * Constructor.<p> 359 */ 360 public CmsPopup() { 361 362 this(DEFAULT_WIDTH); 363 } 364 365 /** 366 * Constructor setting the width of the dialog.<p> 367 * 368 * @param width the width to set 369 */ 370 public CmsPopup(int width) { 371 372 super(false, true); 373 // super(autoHide, modal); 374 375 m_containerElement = super.getContainerElement(); 376 setStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().popup()); 377 addStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().opencms()); 378 m_containerElement.setClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().popupContent()); 379 setGlassStyleName( 380 I_CmsLayoutBundle.INSTANCE.dialogCss().popupOverlay() 381 + " " 382 + I_CmsLayoutBundle.INSTANCE.generalCss().opencms()); 383 Element dragOverlay = DOM.createDiv(); 384 dragOverlay.setClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().dragOverlay()); 385 getElement().insertFirst(dragOverlay); 386 387 m_caption = new Caption(); 388 m_caption.setStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().caption()); 389 // Add the caption to the top of the popup-panel. We need to 390 // logically adopt the caption so we can catch mouse events. 391 DOM.appendChild(m_containerElement, m_caption.getElement()); 392 adopt(m_caption); 393 m_children = new WidgetCollection(this); 394 m_main = DOM.createDiv(); 395 m_main.addClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().popupMainContent()); 396 m_main.addClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().contentPadding()); 397 DOM.appendChild(m_containerElement, m_main); 398 m_buttonPanel = new ButtonPanel(); 399 m_buttonPanel.setStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().hideButtonPanel()); 400 // Add the caption to the top of the popup-panel. We need to 401 // logically adopt the caption so we can catch mouse events. 402 DOM.appendChild(m_containerElement, m_buttonPanel.getElement()); 403 adopt(m_buttonPanel); 404 405 MouseHandler mouseHandler = new MouseHandler(); 406 addDomHandler(mouseHandler, MouseDownEvent.getType()); 407 addDomHandler(mouseHandler, MouseUpEvent.getType()); 408 addDomHandler(mouseHandler, MouseMoveEvent.getType()); 409 410 setWidth(width); 411 getElement().addClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().hideCaption()); 412 413 if (m_historyHandler == null) { 414 m_historyHandler = new HistoryHandler(); 415 History.addValueChangeHandler(m_historyHandler); 416 } 417 m_historyHandler.addPopup(this); 418 } 419 420 /** 421 * Constructor setting the dialog caption.<p> 422 * 423 * @param caption the caption to set 424 */ 425 public CmsPopup(String caption) { 426 427 this(); 428 setCaption(caption); 429 } 430 431 /** 432 * Constructor setting caption and width.<p> 433 * 434 * @param caption the caption to set 435 * @param width the width to set 436 */ 437 public CmsPopup(String caption, int width) { 438 439 this(width); 440 setCaption(caption); 441 } 442 443 /** 444 * The constructor.<p> 445 * 446 * @param title the title and heading of the dialog 447 * @param content the content widget 448 */ 449 public CmsPopup(String title, Widget content) { 450 451 this(title); 452 setMainContent(content); 453 } 454 455 /** 456 * Wraps the given Widget with a cornered border, padding and margin.<p> 457 * 458 * @param w the widget to wrap 459 * 460 * @return a new widget that wraps the given one 461 */ 462 public static Widget wrapWithBorderPadding(Widget w) { 463 464 w.addStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().borderPadding()); 465 w.addStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll()); 466 SimplePanel panel = new SimplePanel(); 467 panel.add(w); 468 return panel; 469 } 470 471 /** 472 * Adds the given child widget.<p> 473 * 474 * @param w the widget 475 */ 476 @Override 477 public void add(Widget w) { 478 479 add(w, m_main); 480 } 481 482 /** 483 * Adds a button widget to the button panel.<p> 484 * 485 * @param button the button widget 486 */ 487 public void addButton(Widget button) { 488 489 addButton(button, 0); 490 } 491 492 /** 493 * Adds a button widget to the button panel before the given position.<p> 494 * 495 * @param button the button widget 496 * @param position the position to insert the button 497 */ 498 public void addButton(Widget button, int position) { 499 500 m_buttonPanel.insert(button, position); 501 m_buttonPanel.setStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().popupButtonPanel()); 502 } 503 504 /** 505 * Adds a close "button" to the top of the popup.<p> 506 * 507 * @param cmd the command that should be executed when the close button is clicked 508 */ 509 public void addDialogClose(final Command cmd) { 510 511 m_closeCommand = cmd; 512 if (m_close == null) { 513 m_close = new CloseButton(); 514 m_close.setTitle(Messages.get().key(Messages.GUI_CLOSE_0)); 515 m_close.addStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().closePopup()); 516 m_close.setImageClass(I_CmsLayoutBundle.INSTANCE.dialogCss().closePopupImage()); 517 m_close.setButtonStyle(ButtonStyle.TRANSPARENT, null); 518 m_close.addClickHandler(new ClickHandler() { 519 520 /** 521 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 522 */ 523 public void onClick(ClickEvent event) { 524 525 boolean cancelled = false; 526 try { 527 if (m_closeCommand != null) { 528 m_closeCommand.execute(); 529 } 530 } catch (CmsCancelCloseException e) { 531 cancelled = true; 532 } finally { 533 if (!cancelled) { 534 hide(); 535 } 536 } 537 } 538 }); 539 DOM.appendChild(m_containerElement, m_close.getElement()); 540 adopt(m_close); 541 } 542 543 } 544 545 /** 546 * Replaces current notification widget by an overlay.<p> 547 */ 548 public void catchNotifications() { 549 550 m_catchNotifications = true; 551 if (isShowing()) { 552 installNotificationWidget(); 553 } 554 if (m_closingHandlerRegistration == null) { 555 // when closing the dialog 556 m_closingHandlerRegistration = addCloseHandler(new CloseHandler<PopupPanel>() { 557 558 /** 559 * @see CloseHandler#onClose(CloseEvent) 560 */ 561 public void onClose(CloseEvent<PopupPanel> event) { 562 563 clearNotifications(); 564 } 565 }); 566 } 567 } 568 569 /** 570 * @see com.google.gwt.user.client.ui.PopupPanel#center() 571 */ 572 @Override 573 public void center() { 574 575 show(); 576 if (Position.FIXED.getCssName().equals(getElement().getStyle().getPosition())) { 577 // keep position fixed, as may have been set to absolute 578 setPositionFixed(); 579 int left = (Window.getClientWidth() - getOffsetWidth()) >> 1; 580 int top = (Window.getClientHeight() - getOffsetHeight()) >> 1; 581 setPopupPosition(Math.max(left, 0), Math.max(top, 0)); 582 } else { 583 super.center(); 584 } 585 } 586 587 /** 588 * Shows the dialog and centers it horizontally, but positions it at a fixed vertical position.<p> 589 * 590 * @param top the top position 591 */ 592 public void centerHorizontally(int top) { 593 594 if (Position.FIXED.getCssName().equals(getElement().getStyle().getPosition())) { 595 show(); 596 // keep position fixed, as may have been set to absolute 597 setPositionFixed(); 598 int left = (Window.getClientWidth() - getOffsetWidth()) >> 1; 599 setPopupPosition(Math.max(left, 0), Math.max(top, 0)); 600 } else { 601 show(); 602 int left = (Window.getClientWidth() - getOffsetWidth()) >> 1; 603 setPopupPosition(Math.max(Window.getScrollLeft() + left, 0), Math.max(Window.getScrollTop() + top, 0)); 604 } 605 } 606 607 /** 608 * @see com.google.gwt.user.client.ui.Panel#clear() 609 */ 610 @Override 611 public void clear() { 612 613 for (Widget w : this) { 614 // Orphan. 615 try { 616 orphan(w); 617 } finally { 618 // Physical detach. 619 Element elem = w.getElement(); 620 elem.removeFromParent(); 621 } 622 } 623 m_children = new WidgetCollection(this); 624 } 625 626 /** 627 * Returns the maximum available height inside the popup.<p> 628 * 629 * @param fixedContentHeight fixed content height to deduct from the available height 630 * 631 * @return the maximum available height 632 */ 633 public int getAvailableHeight(int fixedContentHeight) { 634 635 if (m_buttonPanel.isVisible()) { 636 fixedContentHeight += m_buttonPanel.getOffsetHeight(); 637 } 638 return Window.getClientHeight() - 150 - fixedContentHeight; 639 } 640 641 /** 642 * Returns the dialog caption text.<p> 643 * 644 * @return the dialog caption 645 */ 646 public String getCaption() { 647 648 return m_caption.getText(); 649 } 650 651 /** 652 * Returns the child widget with the given index.<p> 653 * 654 * @param index the index 655 * 656 * @return the child widget 657 */ 658 public Widget getWidget(int index) { 659 660 return getChildren().get(index); 661 } 662 663 /** 664 * Returns the number of child widgets.<p> 665 * 666 * @return the number of child widgets 667 */ 668 public int getWidgetCount() { 669 670 return getChildren().size(); 671 } 672 673 /** 674 * Returns the index of the given widget.<p> 675 * 676 * @param child the child widget 677 * 678 * @return the index of the child widget 679 */ 680 public int getWidgetIndex(IsWidget child) { 681 682 return getWidgetIndex(asWidgetOrNull(child)); 683 } 684 685 /** 686 * Returns the index of the given child widget.<p> 687 * 688 * @param child the child widget 689 * 690 * @return the index 691 */ 692 public int getWidgetIndex(Widget child) { 693 694 return getChildren().indexOf(child); 695 } 696 697 /** 698 * Returns the dialog content width, -1 if not set.<p> 699 * 700 * @return the dialog content width 701 */ 702 public int getWidth() { 703 704 return m_width; 705 } 706 707 /** 708 * Returns <code>true</code> if a caption is set for this popup <code>false</code> otherwise.<p> 709 * 710 * @return <code>true</code> if a caption is set for this popup <code>false</code> otherwise 711 */ 712 public boolean hasCaption() { 713 714 return CmsStringUtil.isNotEmptyOrWhitespaceOnly(m_caption.getText()); 715 } 716 717 /** 718 * @see com.google.gwt.user.client.ui.PopupPanel#hide() 719 */ 720 @Override 721 public void hide() { 722 723 if (m_resizeHandlerRegistration != null) { 724 m_resizeHandlerRegistration.removeHandler(); 725 m_resizeHandlerRegistration = null; 726 } 727 super.hide(); 728 } 729 730 /** 731 * @see com.google.gwt.user.client.ui.PopupPanel#hide(boolean) 732 */ 733 @Override 734 public void hide(boolean autoClosed) { 735 736 super.hide(autoClosed); 737 m_historyHandler.removePopup(this); 738 } 739 740 /** 741 * Inserts a child widget before the given index.<p> 742 * 743 * @param w the child widget 744 * @param beforeIndex the index 745 * 746 * @throws IndexOutOfBoundsException if the index is out of bounds 747 */ 748 public void insert(Widget w, int beforeIndex) throws IndexOutOfBoundsException { 749 750 insert(w, m_main, beforeIndex, true); 751 } 752 753 /** 754 * Inserts a widget as the first widget in the popup.<p> 755 * 756 * @param widget the widget to insert 757 */ 758 public void insertFront(Widget widget) { 759 760 insert(widget, 0); 761 } 762 763 /** 764 * @see com.google.gwt.user.client.ui.SimplePanel#iterator() 765 */ 766 @Override 767 public Iterator<Widget> iterator() { 768 769 return getChildren().iterator(); 770 } 771 772 /** 773 * @see com.google.gwt.user.client.ui.Widget#onBrowserEvent(com.google.gwt.user.client.Event) 774 */ 775 @Override 776 public void onBrowserEvent(Event event) { 777 778 // If we're not yet dragging, only trigger mouse events if the event occurs 779 // in the caption wrapper 780 switch (event.getTypeInt()) { 781 case Event.ONMOUSEDOWN: 782 case Event.ONMOUSEUP: 783 case Event.ONMOUSEMOVE: 784 case Event.ONMOUSEOVER: 785 case Event.ONMOUSEOUT: 786 if (!m_dragging && !isCaptionEvent(event)) { 787 return; 788 } 789 break; 790 default: 791 } 792 793 super.onBrowserEvent(event); 794 } 795 796 /** 797 * Removes a child widget.<p> 798 * 799 * @param index the index of the widget to remove 800 * 801 * @return <code>true</code> if the there was a widget at the given index to remove 802 */ 803 public boolean remove(int index) { 804 805 Widget w = getWidget(index); 806 if (w != null) { 807 return remove(getWidget(index)); 808 } 809 return false; 810 } 811 812 /** 813 * @see com.google.gwt.user.client.ui.SimplePanel#remove(com.google.gwt.user.client.ui.Widget) 814 */ 815 @Override 816 public boolean remove(Widget w) { 817 818 // Validate. 819 if (w.getParent() != this) { 820 return false; 821 } 822 // Orphan. 823 try { 824 orphan(w); 825 } finally { 826 // Physical detach. 827 Element elem = w.getElement(); 828 elem.removeFromParent(); 829 830 // Logical detach. 831 getChildren().remove(w); 832 } 833 return true; 834 } 835 836 /** 837 * Removes all buttons.<p> 838 */ 839 public void removeAllButtons() { 840 841 m_buttonPanel.clear(); 842 } 843 844 /** 845 * Removes the given button widget from the button panel.<p> 846 * 847 * @param button the button widget to remove 848 */ 849 public void removeButton(Widget button) { 850 851 m_buttonPanel.remove(button); 852 if (m_buttonPanel.getWidgetCount() == 0) { 853 m_buttonPanel.setStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().hideButtonPanel()); 854 } 855 } 856 857 /** 858 * Removes the padding from the popup's content.<p> 859 */ 860 public void removePadding() { 861 862 m_main.removeClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().contentPadding()); 863 m_contentHeightCorrection = -6; 864 } 865 866 /** 867 * Sets the popup's content background.<p> 868 * 869 * @param color the color to set 870 */ 871 public void setBackgroundColor(String color) { 872 873 m_main.getStyle().setBackgroundColor(color); 874 } 875 876 /** 877 * Sets the captions text.<p> 878 * 879 * @param caption the text to set 880 */ 881 public void setCaption(String caption) { 882 883 if (CmsStringUtil.isNotEmptyOrWhitespaceOnly(caption)) { 884 getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().hideCaption()); 885 m_caption.setText(caption); 886 } else { 887 getElement().addClassName(I_CmsLayoutBundle.INSTANCE.dialogCss().hideCaption()); 888 } 889 } 890 891 /** 892 * Sets the height for the popup content.<p> 893 * 894 * @param height the height in pixels 895 */ 896 public void setHeight(int height) { 897 898 if (height <= 0) { 899 m_containerElement.getStyle().clearWidth(); 900 m_main.getStyle().clearHeight(); 901 } else { 902 int contentHeight = height - 6; 903 if (hasCaption()) { 904 contentHeight = contentHeight - 36; 905 } 906 if (hasButtons()) { 907 contentHeight = contentHeight - 34; 908 } 909 contentHeight = contentHeight - m_contentHeightCorrection; 910 m_main.getStyle().setHeight(contentHeight, Unit.PX); 911 } 912 } 913 914 /** 915 * @see com.google.gwt.user.client.ui.PopupPanel#setHeight(java.lang.String) 916 */ 917 @Override 918 @Deprecated 919 public void setHeight(String height) { 920 921 throw new UnsupportedOperationException(); 922 } 923 924 /** 925 * Replaces the content from the main widget.<p> 926 * 927 * @param w the widget that should replace the main content 928 */ 929 public void setMainContent(Widget w) { 930 931 clear(); 932 add(w); 933 } 934 935 /** 936 * @see com.google.gwt.user.client.ui.UIObject#setSize(java.lang.String, java.lang.String) 937 */ 938 @Override 939 @Deprecated 940 public void setPixelSize(int width, int height) { 941 942 throw new UnsupportedOperationException(); 943 } 944 945 /** 946 * Sets the popup's dialog position to 'fixed'.<p> 947 */ 948 public void setPositionFixed() { 949 950 getElement().getStyle().setPosition(Position.FIXED); 951 } 952 953 /** 954 * @see com.google.gwt.user.client.ui.UIObject#setSize(java.lang.String, java.lang.String) 955 */ 956 @Override 957 @Deprecated 958 public void setSize(String width, String height) { 959 960 throw new UnsupportedOperationException(); 961 } 962 963 /** 964 * Sets an additional CSS class to the main content element.<p> 965 * 966 * @param cssClassName the CSS class to set 967 */ 968 public void setSpecialBackgroundClass(String cssClassName) { 969 970 m_main.addClassName(cssClassName); 971 } 972 973 /** 974 * Sets the use animation flag.<p> 975 * 976 * @param use <code>true</code> if the animation should be used, default is <code>true</code> 977 */ 978 public void setUseAnimation(boolean use) { 979 980 m_useAnimation = use; 981 } 982 983 /** 984 * Unsupported operation.<p> 985 * 986 * @see com.google.gwt.user.client.ui.PopupPanel#setWidget(com.google.gwt.user.client.ui.Widget) 987 */ 988 @Override 989 @Deprecated 990 public void setWidget(Widget w) { 991 992 throw new UnsupportedOperationException(); 993 } 994 995 /** 996 * Sets the width for the popup content.<p> 997 * 998 * @param width the width in pixels 999 */ 1000 public void setWidth(int width) { 1001 1002 if (width <= 0) { 1003 m_containerElement.getStyle().clearWidth(); 1004 m_width = -1; 1005 } else { 1006 m_containerElement.getStyle().setWidth(width, Unit.PX); 1007 m_width = width; 1008 } 1009 } 1010 1011 /** 1012 * @see com.google.gwt.user.client.ui.PopupPanel#setWidth(java.lang.String) 1013 */ 1014 @Override 1015 @Deprecated 1016 public void setWidth(String width) { 1017 1018 throw new UnsupportedOperationException(); 1019 } 1020 1021 /** 1022 * @see com.google.gwt.user.client.ui.PopupPanel#show() 1023 */ 1024 @Override 1025 public void show() { 1026 1027 boolean fixed = Position.FIXED.getCssName().equals(getElement().getStyle().getPosition()); 1028 boolean wasAlreadyShowing = isShowing(); 1029 super.show(); 1030 if (fixed) { 1031 // keep position fixed as it may have been set to absolute 1032 setPositionFixed(); 1033 } 1034 if (m_useAnimation && !wasAlreadyShowing) { 1035 CmsFadeAnimation.fadeIn(getElement(), null, 250); 1036 } 1037 if (m_resizeHandlerRegistration == null) { 1038 m_resizeHandlerRegistration = Window.addResizeHandler(new ResizeHandler() { 1039 1040 public void onResize(ResizeEvent event) { 1041 1042 m_windowWidth = event.getWidth(); 1043 } 1044 }); 1045 } 1046 if (m_catchNotifications) { 1047 catchNotifications(); 1048 } 1049 } 1050 1051 /** 1052 * Adds a new child widget to the panel, attaching its Element to the 1053 * specified container Element. 1054 * 1055 * @param child the child widget to be added 1056 * @param container the element within which the child will be contained 1057 */ 1058 protected void add(Widget child, Element container) { 1059 1060 // Detach new child. 1061 child.removeFromParent(); 1062 1063 // Logical attach. 1064 getChildren().add(child); 1065 1066 // Physical attach. 1067 DOM.appendChild(container, child.getElement()); 1068 1069 // Adopt. 1070 adopt(child); 1071 } 1072 1073 /** 1074 * Adjusts beforeIndex to account for the possibility that the given widget is 1075 * already a child of this panel. 1076 * 1077 * @param child the widget that might be an existing child 1078 * @param beforeIndex the index at which it will be added to this panel 1079 * @return the modified index 1080 */ 1081 protected int adjustIndex(Widget child, int beforeIndex) { 1082 1083 checkIndexBoundsForInsertion(beforeIndex); 1084 1085 // Check to see if this widget is already a direct child. 1086 if (child.getParent() == this) { 1087 // If the Widget's previous position was left of the desired new position 1088 // shift the desired position left to reflect the removal 1089 int idx = getWidgetIndex(child); 1090 if (idx < beforeIndex) { 1091 beforeIndex--; 1092 } 1093 } 1094 1095 return beforeIndex; 1096 } 1097 1098 /** 1099 * Called on mouse down in the caption area, begins the dragging loop by 1100 * turning on event capture. 1101 * 1102 * @see DOM#setCapture 1103 * @see #continueDragging 1104 * @param event the mouse down event that triggered dragging 1105 */ 1106 protected void beginDragging(MouseDownEvent event) { 1107 1108 m_dragging = true; 1109 m_windowWidth = Window.getClientWidth(); 1110 m_clientLeft = Document.get().getBodyOffsetLeft(); 1111 m_clientTop = Document.get().getBodyOffsetTop(); 1112 DOM.setCapture(getElement()); 1113 m_dragStartX = event.getX(); 1114 m_dragStartY = event.getY(); 1115 addStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().dragging()); 1116 } 1117 1118 /** 1119 * Checks that <code>index</code> is in the range [0, getWidgetCount()), which 1120 * is the valid range on accessible indexes. 1121 * 1122 * @param index the index being accessed 1123 */ 1124 protected void checkIndexBoundsForAccess(int index) { 1125 1126 if ((index < 0) || (index >= getWidgetCount())) { 1127 throw new IndexOutOfBoundsException(); 1128 } 1129 } 1130 1131 /** 1132 * Checks that <code>index</code> is in the range [0, getWidgetCount()], which 1133 * is the valid range for indexes on an insertion. 1134 * 1135 * @param index the index where insertion will occur 1136 */ 1137 protected void checkIndexBoundsForInsertion(int index) { 1138 1139 if ((index < 0) || (index > getWidgetCount())) { 1140 throw new IndexOutOfBoundsException(); 1141 } 1142 } 1143 1144 /** 1145 * Called on mouse move in the caption area, continues dragging if it was 1146 * started by {@link #beginDragging}. 1147 * 1148 * @see #beginDragging 1149 * @see #endDragging 1150 * @param event the mouse move event that continues dragging 1151 */ 1152 protected void continueDragging(MouseMoveEvent event) { 1153 1154 if (m_dragging) { 1155 int absX = event.getX() + getAbsoluteLeft(); 1156 int absY = event.getY() + getAbsoluteTop(); 1157 1158 // if the mouse is off the screen to the left, right, or top, don't 1159 // move the dialog box. This would let users lose dialog boxes, which 1160 // would be bad for modal popups. 1161 if ((absX < m_clientLeft) || (absX >= m_windowWidth) || (absY < m_clientTop)) { 1162 return; 1163 } 1164 1165 setPopupPosition(absX - m_dragStartX, absY - m_dragStartY); 1166 } 1167 } 1168 1169 /** 1170 * Creates a new notification widget for this dialog.<p> 1171 * 1172 * @return the notification widget for this dialog 1173 */ 1174 protected CmsNotificationWidget createDialogNotificationWidget() { 1175 1176 return new CmsNotificationWidget(); 1177 } 1178 1179 /** 1180 * @see com.google.gwt.user.client.ui.Panel#doAttachChildren() 1181 */ 1182 @Override 1183 protected void doAttachChildren() { 1184 1185 try { 1186 super.doAttachChildren(); 1187 } finally { 1188 // See comment in doDetachChildren for an explanation of this call 1189 m_caption.onAttach(); 1190 m_buttonPanel.onAttach(); 1191 if (m_close != null) { 1192 m_close.onAttach(); 1193 } 1194 } 1195 } 1196 1197 /** 1198 * @see com.google.gwt.user.client.ui.Panel#doDetachChildren() 1199 */ 1200 @Override 1201 protected void doDetachChildren() { 1202 1203 try { 1204 super.doDetachChildren(); 1205 } finally { 1206 // We need to detach the caption specifically because it is not part of the 1207 // iterator of Widgets that the {@link SimplePanel} super class returns. 1208 // This is similar to a {@link ComplexPanel}, but we do not want to expose 1209 // the caption widget, as its just an internal implementation. 1210 m_caption.onDetach(); 1211 m_buttonPanel.onDetach(); 1212 if (m_close != null) { 1213 m_close.onDetach(); 1214 } 1215 } 1216 } 1217 1218 /** 1219 * Called on mouse up in the caption area, ends dragging by ending event 1220 * capture. 1221 * 1222 * @param event the mouse up event that ended dragging 1223 * 1224 * @see DOM#releaseCapture 1225 * @see #beginDragging 1226 * @see #endDragging 1227 */ 1228 protected void endDragging(MouseUpEvent event) { 1229 1230 m_dragging = false; 1231 DOM.releaseCapture(getElement()); 1232 removeStyleName(I_CmsLayoutBundle.INSTANCE.dialogCss().dragging()); 1233 } 1234 1235 /** 1236 * Gets the list of children contained in this panel. 1237 * 1238 * @return a collection of child widgets 1239 */ 1240 protected WidgetCollection getChildren() { 1241 1242 return m_children; 1243 } 1244 1245 /** 1246 * @see com.google.gwt.user.client.ui.PopupPanel#getContainerElement() 1247 */ 1248 @SuppressWarnings("deprecation") 1249 @Override 1250 protected com.google.gwt.user.client.Element getContainerElement() { 1251 1252 if (m_containerElement == null) { 1253 m_containerElement = super.getContainerElement(); 1254 } 1255 return (com.google.gwt.user.client.Element)m_containerElement; 1256 } 1257 1258 /** 1259 * Insert a new child Widget into this Panel at a specified index, attaching 1260 * its Element to the specified container Element. The child Element will 1261 * either be attached to the container at the same index, or simply appended 1262 * to the container, depending on the value of <code>domInsert</code>. 1263 * 1264 * @param child the child Widget to be added 1265 * @param container the Element within which <code>child</code> will be 1266 * contained 1267 * @param beforeIndex the index before which <code>child</code> will be 1268 * inserted 1269 * @param domInsert if <code>true</code>, insert <code>child</code> into 1270 * <code>container</code> at <code>beforeIndex</code>; otherwise 1271 * append <code>child</code> to the end of <code>container</code>. 1272 */ 1273 protected void insert(Widget child, Element container, int beforeIndex, boolean domInsert) { 1274 1275 // Validate index; adjust if the widget is already a child of this panel. 1276 beforeIndex = adjustIndex(child, beforeIndex); 1277 1278 // Detach new child. 1279 child.removeFromParent(); 1280 1281 // Logical attach. 1282 getChildren().insert(child, beforeIndex); 1283 1284 // Physical attach. 1285 if (domInsert) { 1286 DOM.insertChild(container, child.getElement(), beforeIndex); 1287 } else { 1288 DOM.appendChild(container, child.getElement()); 1289 } 1290 1291 // Adopt. 1292 adopt(child); 1293 } 1294 1295 /** 1296 * Sets the notification widget.<p> 1297 */ 1298 protected void installNotificationWidget() { 1299 1300 if (m_notificationWidgetInstalled) { 1301 return; 1302 } 1303 // remember current notification widget 1304 m_parentNotificationWidget = CmsNotification.get().getWidget(); 1305 // create our own notification overlay 1306 if (m_ownNotificationWidget == null) { 1307 m_ownNotificationWidget = createDialogNotificationWidget(); 1308 } 1309 add(m_ownNotificationWidget); 1310 CmsNotification.get().setWidget(m_ownNotificationWidget); 1311 m_notificationWidgetInstalled = true; 1312 } 1313 1314 /** 1315 * Override to work around the glass overlay still showing after dialog hide.<p> 1316 * 1317 * @see com.google.gwt.user.client.ui.Widget#onDetach() 1318 */ 1319 @Override 1320 protected void onDetach() { 1321 1322 super.onDetach(); 1323 if (getGlassElement() != null) { 1324 getGlassElement().removeFromParent(); 1325 } 1326 } 1327 1328 /** 1329 * @see com.google.gwt.user.client.ui.PopupPanel#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent) 1330 */ 1331 @Override 1332 protected void onPreviewNativeEvent(NativePreviewEvent event) { 1333 1334 NativeEvent nativeEvent = event.getNativeEvent(); 1335 int eventType = event.getTypeInt(); 1336 1337 // We need to preventDefault() on mouseDown events (outside of the 1338 // DialogBox content) to keep text from being selected when it 1339 // is dragged. 1340 1341 if (!event.isCanceled() && (eventType == Event.ONMOUSEDOWN) && isCaptionEvent(nativeEvent)) { 1342 nativeEvent.preventDefault(); 1343 } 1344 1345 super.onPreviewNativeEvent(event); 1346 } 1347 1348 /** 1349 * Appends the arrow element to the popup's dialog.<p> 1350 * 1351 * @param arrow the arrow element to add 1352 */ 1353 protected void showArrow(Element arrow) { 1354 1355 getElement().appendChild(arrow); 1356 } 1357 1358 /** 1359 * Resets the notification to the parent notification widget and detaches the own notification widget.<p> 1360 */ 1361 void clearNotifications() { 1362 1363 if (m_parentNotificationWidget != null) { 1364 // restore the previous notification widget 1365 CmsNotification.get().setWidget(m_parentNotificationWidget); 1366 } 1367 if (m_ownNotificationWidget != null) { 1368 // remove the overlay notification widget 1369 remove(m_ownNotificationWidget); 1370 } 1371 } 1372 1373 /** 1374 * Returns <code>true</code> if this popup has buttons <code>false</code> otherwise.<p> 1375 * 1376 * @return <code>true</code> if this popup has buttons <code>false</code> otherwise 1377 */ 1378 private boolean hasButtons() { 1379 1380 return m_buttonPanel.getWidgetCount() != 0; 1381 } 1382 1383 /** 1384 * Checks if the target of the given event is the caption or a child of the caption.<p> 1385 * 1386 * @param event the event to check 1387 * 1388 * @return <code>true</code> if the target of the given event is the caption <code>false</code> otherwise 1389 */ 1390 private boolean isCaptionEvent(NativeEvent event) { 1391 1392 EventTarget target = event.getEventTarget(); 1393 if (com.google.gwt.dom.client.Element.is(target)) { 1394 return m_caption.getElement().isOrHasChild(com.google.gwt.dom.client.Element.as(target)); 1395 } 1396 return false; 1397 } 1398}