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.input; 029 030import org.opencms.gwt.client.ui.CmsPushButton; 031import org.opencms.gwt.client.ui.CmsScrollPanel; 032import org.opencms.gwt.client.ui.I_CmsButton; 033import org.opencms.gwt.client.ui.I_CmsButton.ButtonStyle; 034import org.opencms.gwt.client.ui.I_CmsButton.Size; 035import org.opencms.gwt.client.ui.I_CmsTruncable; 036import org.opencms.gwt.client.ui.css.I_CmsInputCss; 037import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle; 038import org.opencms.gwt.client.ui.css.I_CmsLayoutBundle; 039import org.opencms.gwt.client.util.CmsDomUtil; 040import org.opencms.gwt.client.util.CmsStyleVariable; 041 042import java.util.HashMap; 043import java.util.Map; 044 045import com.google.gwt.core.client.GWT; 046import com.google.gwt.core.client.Scheduler; 047import com.google.gwt.core.client.Scheduler.ScheduledCommand; 048import com.google.gwt.dom.client.Element; 049import com.google.gwt.event.dom.client.ClickEvent; 050import com.google.gwt.event.dom.client.ClickHandler; 051import com.google.gwt.event.dom.client.FocusEvent; 052import com.google.gwt.event.dom.client.FocusHandler; 053import com.google.gwt.event.dom.client.HasFocusHandlers; 054import com.google.gwt.event.dom.client.MouseOutEvent; 055import com.google.gwt.event.dom.client.MouseOutHandler; 056import com.google.gwt.event.dom.client.MouseOverEvent; 057import com.google.gwt.event.dom.client.MouseOverHandler; 058import com.google.gwt.event.dom.client.MouseWheelEvent; 059import com.google.gwt.event.dom.client.MouseWheelHandler; 060import com.google.gwt.event.logical.shared.CloseEvent; 061import com.google.gwt.event.logical.shared.CloseHandler; 062import com.google.gwt.event.logical.shared.HasValueChangeHandlers; 063import com.google.gwt.event.logical.shared.ResizeEvent; 064import com.google.gwt.event.logical.shared.ResizeHandler; 065import com.google.gwt.event.logical.shared.ValueChangeEvent; 066import com.google.gwt.event.logical.shared.ValueChangeHandler; 067import com.google.gwt.event.shared.HandlerRegistration; 068import com.google.gwt.event.shared.SimpleEventBus; 069import com.google.gwt.uibinder.client.UiBinder; 070import com.google.gwt.uibinder.client.UiField; 071import com.google.gwt.uibinder.client.UiHandler; 072import com.google.gwt.user.client.Event; 073import com.google.gwt.user.client.Window; 074import com.google.gwt.user.client.ui.Composite; 075import com.google.gwt.user.client.ui.FlowPanel; 076import com.google.gwt.user.client.ui.FocusPanel; 077import com.google.gwt.user.client.ui.Panel; 078import com.google.gwt.user.client.ui.PopupPanel; 079import com.google.gwt.user.client.ui.RootPanel; 080import com.google.gwt.user.client.ui.Widget; 081 082import elemental2.dom.CSSProperties.MaxHeightUnionType; 083import elemental2.dom.HTMLElement; 084import jsinterop.base.Js; 085 086/** 087 * Abstract superclass for select box widgets.<p> 088 * 089 * @param <OPTION> the widget type of the select options 090 * 091 * @since 8.0.0 092 * 093 */ 094public abstract class A_CmsSelectBox<OPTION extends A_CmsSelectCell> extends Composite 095implements I_CmsFormWidget, HasValueChangeHandlers<String>, HasFocusHandlers, I_CmsTruncable { 096 097 /** 098 * The UI Binder interface for this widget.<p> 099 */ 100 protected interface I_CmsSelectBoxUiBinder extends UiBinder<Panel, A_CmsSelectBox<?>> { 101 // binder interface 102 } 103 104 /** The layout bundle. */ 105 protected static final I_CmsInputCss CSS = I_CmsInputLayoutBundle.INSTANCE.inputCss(); 106 107 /** The UiBinder instance used for this widget. */ 108 private static I_CmsSelectBoxUiBinder uiBinder = GWT.create(I_CmsSelectBoxUiBinder.class); 109 110 /** Error widget. */ 111 @UiField 112 protected CmsErrorWidget m_error; 113 114 /** The event bus. */ 115 protected SimpleEventBus m_eventBus; 116 117 /** Handler registration for mouse wheel handlers. */ 118 protected HandlerRegistration m_mousewheelRegistration; 119 120 /** The open-close button. */ 121 protected CmsPushButton m_openClose; 122 123 /** The opener widget. */ 124 @UiField 125 protected FocusPanel m_opener; 126 127 /** Container for the opener and error widget. */ 128 @UiField 129 protected Panel m_panel; 130 131 /** The popup panel inside which the selector will be shown.<p> */ 132 protected PopupPanel m_popup = new PopupPanel(true); 133 134 /** Style of the select box widget. */ 135 protected final CmsStyleVariable m_selectBoxState; 136 137 /** The map of select options. */ 138 protected Map<String, OPTION> m_selectCells = new HashMap<String, OPTION>(); 139 140 /** The value of the currently selected option. */ 141 protected String m_selectedValue; 142 143 /** The selector which contains the select options. */ 144 protected Panel m_selector = new FlowPanel(); 145 146 /** Style of the select box widget. */ 147 protected final CmsStyleVariable m_selectorState; 148 149 /** Flag indicating whether this widget is enabled. */ 150 private boolean m_enabled = true; 151 152 /** The value of the first select option. */ 153 private String m_firstValue; 154 155 /** The maximum cell width. */ 156 private int m_preferredPopupWidth; 157 158 /** The value to test the popup resize behaviour.*/ 159 private boolean m_resizePopup = true; 160 161 /** The text metrics prefix. */ 162 private String m_textMetricsPrefix; 163 164 /** The widget width for truncation. */ 165 private int m_widgetWidth; 166 167 /** Handler registration for the window resize handler. */ 168 private HandlerRegistration m_windowResizeHandlerReg; 169 170 /** 171 * Creates a new select box.<p> 172 */ 173 public A_CmsSelectBox() { 174 175 m_eventBus = new SimpleEventBus(); 176 m_panel = uiBinder.createAndBindUi(this); 177 initWidget(m_panel); 178 m_selectBoxState = new CmsStyleVariable(m_opener); 179 m_selectBoxState.setValue(I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll()); 180 181 m_selectorState = new CmsStyleVariable(m_popup); 182 m_selectorState.setValue(I_CmsLayoutBundle.INSTANCE.generalCss().cornerBottom()); 183 184 m_opener.addStyleName(CSS.selectBoxSelected()); 185 addHoverHandlers(m_opener); 186 addMainPanelHoverHandlers(m_panel); 187 m_openClose = new CmsPushButton(I_CmsButton.TRIANGLE_RIGHT, I_CmsButton.TRIANGLE_DOWN); 188 m_openClose.setButtonStyle(ButtonStyle.FONT_ICON, null); 189 m_openClose.setSize(Size.small); 190 m_openClose.addStyleName(CSS.selectIcon()); 191 m_panel.add(m_openClose); 192 m_openClose.addClickHandler(new ClickHandler() { 193 194 /** 195 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 196 */ 197 public void onClick(ClickEvent event) { 198 199 if (m_popup.isShowing()) { 200 close(); 201 } else { 202 open(); 203 } 204 } 205 }); 206 m_popup.setWidget(m_selector); 207 m_popup.addStyleName(CSS.selectorPopup()); 208 m_popup.addAutoHidePartner(m_panel.getElement()); 209 210 m_popup.addStyleName(CSS.selectBoxSelector()); 211 m_popup.addStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().opencms()); 212 m_popup.addStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().cornerBottom()); 213 m_popup.addStyleName(I_CmsLayoutBundle.INSTANCE.generalCss().textMedium()); 214 m_popup.addCloseHandler(new CloseHandler<PopupPanel>() { 215 216 /** 217 * @see CloseHandler#onClose(CloseEvent) 218 */ 219 public void onClose(CloseEvent<PopupPanel> e) { 220 221 close(); 222 } 223 }); 224 initOpener(); 225 } 226 227 /** 228 * @see com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com.google.gwt.event.dom.client.FocusHandler) 229 */ 230 public HandlerRegistration addFocusHandler(FocusHandler handler) { 231 232 return addDomHandler(handler, FocusEvent.getType()); 233 } 234 235 /** 236 * Adds a new select option to the select box.<p> 237 * 238 * @param cell the widget representing the select option 239 */ 240 public void addOption(OPTION cell) { 241 242 String value = cell.getValue(); 243 boolean first = m_selectCells.isEmpty(); 244 m_selectCells.put(value, cell); 245 246 m_selector.add(cell); 247 if (first) { 248 selectValue(value); 249 m_firstValue = value; 250 } 251 initSelectCell(cell); 252 } 253 254 /** 255 * @see com.google.gwt.event.logical.shared.HasValueChangeHandlers#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 256 */ 257 public HandlerRegistration addValueChangeHandler(final ValueChangeHandler<String> handler) { 258 259 return super.addHandler(handler, ValueChangeEvent.getType()); 260 } 261 262 /** 263 * Adds a widget.<p> 264 * 265 * @param widget the widget to add 266 */ 267 public void addWidget(Widget widget) { 268 269 m_panel.add(widget); 270 } 271 272 /** 273 * Returns whether the select options are being displayed below or above the widget.<p> 274 * 275 * @return <code>true</code> in case the select options are displayed above the widget 276 */ 277 public boolean displayingAbove() { 278 279 int popupHeight = getPopupHeight(); 280 // Calculate top position for the popup 281 int top = m_opener.getAbsoluteTop(); 282 283 // Make sure scrolling is taken into account, since 284 // box.getAbsoluteTop() takes scrolling into account. 285 int windowTop = Window.getScrollTop(); 286 int windowBottom = Window.getScrollTop() + Window.getClientHeight(); 287 288 // Distance from the top edge of the window to the top edge of the 289 // text box 290 int distanceFromWindowTop = top - windowTop; 291 292 // Distance from the bottom edge of the window to the bottom edge of 293 // the text box 294 int distanceToWindowBottom = windowBottom - (top + m_opener.getOffsetHeight()); 295 296 // If there is not enough space for the popup's height below the text 297 // box and there IS enough space for the popup's height above the text 298 // box, then then position the popup above the text box. However, if there 299 // is not enough space on either side, then stick with displaying the 300 // popup below the text box. 301 boolean displayAbove = (distanceFromWindowTop > distanceToWindowBottom) 302 && (distanceToWindowBottom < popupHeight); 303 return displayAbove; 304 } 305 306 /** 307 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFieldType() 308 */ 309 public FieldType getFieldType() { 310 311 return FieldType.STRING; 312 } 313 314 /** 315 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValue() 316 */ 317 public Object getFormValue() { 318 319 if (m_selectedValue == null) { 320 return ""; 321 } 322 return m_selectedValue; 323 } 324 325 /** 326 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValueAsString() 327 */ 328 public String getFormValueAsString() { 329 330 return (String)getFormValue(); 331 } 332 333 /** 334 * Returns the selector of this widget.<p> 335 * 336 * @return the selector of this widget 337 */ 338 public Panel getSelectorPopup() { 339 340 return m_popup; 341 } 342 343 /** 344 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#isEnabled() 345 */ 346 public boolean isEnabled() { 347 348 return m_enabled; 349 } 350 351 /** 352 * @see com.google.gwt.user.client.ui.Composite#onBrowserEvent(com.google.gwt.user.client.Event) 353 */ 354 @Override 355 public void onBrowserEvent(Event event) { 356 357 // Should not act on button if disabled. 358 if (!isEnabled()) { 359 return; 360 } 361 super.onBrowserEvent(event); 362 } 363 364 /** 365 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#reset() 366 */ 367 public void reset() { 368 369 close(); 370 onValueSelect(m_firstValue); 371 } 372 373 /** 374 * Helper method to set the current selected option.<p> 375 * 376 * This method does not trigger the "value changed" event.<p> 377 * 378 * @param value the new value 379 */ 380 public void selectValue(String value) { 381 382 if (m_selectCells.get(value) == null) { 383 return; 384 } 385 386 updateOpener(value); 387 if (m_textMetricsPrefix != null) { 388 truncate(m_textMetricsPrefix, m_widgetWidth); 389 } 390 m_selectedValue = value; 391 close(); 392 } 393 394 /** 395 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setEnabled(boolean) 396 */ 397 public void setEnabled(boolean enabled) { 398 399 close(); 400 m_enabled = enabled; 401 getElement().setPropertyBoolean("disabled", !enabled); 402 m_openClose.setEnabled(enabled); 403 if (enabled) { 404 removeStyleName(CSS.selectBoxDisabled()); 405 } else { 406 addStyleName(CSS.selectBoxDisabled()); 407 } 408 } 409 410 /** 411 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setErrorMessage(java.lang.String) 412 */ 413 public void setErrorMessage(String errorMessage) { 414 415 m_error.setText(errorMessage); 416 } 417 418 /** 419 * Sets the form value of this select box.<p> 420 * 421 * @param value the new value 422 */ 423 public void setFormValue(Object value) { 424 425 setFormValue(value, false); 426 } 427 428 /** 429 * Sets the form value of this select box.<p> 430 * 431 * @param value the new value 432 * @param fireEvents true if change events should be fired 433 */ 434 public void setFormValue(Object value, boolean fireEvents) { 435 436 if (value == null) { 437 value = ""; 438 } 439 if (!"".equals(value) && !m_selectCells.containsKey(value)) { 440 OPTION option = createUnknownOption((String)value); 441 if (option != null) { 442 addOption(option); 443 } 444 } 445 if (value instanceof String) { 446 String strValue = (String)value; 447 onValueSelect(strValue, fireEvents); 448 } 449 } 450 451 /** 452 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setFormValueAsString(java.lang.String) 453 */ 454 public void setFormValueAsString(String formValue) { 455 456 setFormValue(formValue); 457 } 458 459 /** 460 * Sets the behavior of the popup if the input is bigger than the selectbox itself. 461 * 462 * @param resize <code>true</code> to resize 463 */ 464 public void setPopupResize(boolean resize) { 465 466 m_resizePopup = resize; 467 } 468 469 /** 470 * @see org.opencms.gwt.client.ui.I_CmsTruncable#truncate(java.lang.String, int) 471 */ 472 public void truncate(String textMetricsPrefix, int widgetWidth) { 473 474 m_textMetricsPrefix = textMetricsPrefix; 475 m_widgetWidth = widgetWidth; 476 truncateOpener(textMetricsPrefix, widgetWidth); 477 } 478 479 /** 480 * Internal helper method for clearing the select options.<p> 481 */ 482 protected void clearItems() { 483 484 m_selectCells.clear(); 485 m_selector.clear(); 486 m_selectedValue = null; 487 } 488 489 /** 490 * Internal method which is called when the selector is closed.<p> 491 */ 492 protected void close() { 493 494 if (!m_enabled) { 495 return; 496 } 497 m_openClose.setDown(false); 498 m_popup.hide(); 499 m_selectBoxState.setValue(I_CmsLayoutBundle.INSTANCE.generalCss().cornerAll()); 500 } 501 502 /** 503 * Internal method to create a select option for an unknown value.<p> 504 * 505 * @param value the value for which to create the option 506 * 507 * @return the new option 508 */ 509 protected abstract OPTION createUnknownOption(String value); 510 511 /** 512 * Handle clicks on the opener.<p> 513 * 514 * @param e the click event 515 */ 516 @UiHandler("m_opener") 517 protected void doClickOpener(ClickEvent e) { 518 519 toggleOpen(); 520 } 521 522 /** 523 * Returns the offset height of the popup panel, should also work when the popup is currently not showing.<p> 524 * 525 * @return the offset height 526 */ 527 protected int getPopupHeight() { 528 529 if (m_popup.isShowing()) { 530 return m_popup.getOffsetHeight(); 531 } else { 532 Element el = CmsDomUtil.clone(m_popup.getElement()); 533 RootPanel.getBodyElement().appendChild(el); 534 el.getStyle().setProperty("position", "absolute"); 535 int height = el.getOffsetHeight(); 536 el.removeFromParent(); 537 return height; 538 } 539 } 540 541 /** 542 * The implementation of this method should initialize the opener of the select box.<p> 543 */ 544 protected abstract void initOpener(); 545 546 /** 547 * Initializes the selector width.<p> 548 * 549 * @return the preferred popup width 550 */ 551 protected int initPreferredPopupWidth() { 552 553 int width = m_opener.getOffsetWidth() - 2 /*border*/; 554 for (Widget widget : m_selector) { 555 if (widget instanceof A_CmsSelectCell) { 556 int cellWidth = ((A_CmsSelectCell)widget).getRequiredWidth(); 557 if (cellWidth > width) { 558 width = cellWidth; 559 } 560 } 561 } 562 return width; 563 } 564 565 /** 566 * @see com.google.gwt.user.client.ui.Composite#onDetach() 567 */ 568 @Override 569 protected void onDetach() { 570 571 super.onDetach(); 572 removeWindowResizeHandler(); 573 } 574 575 /** 576 * Handles the focus event on the opener.<p> 577 * 578 * @param event the focus event 579 */ 580 @UiHandler("m_opener") 581 protected void onFocus(FocusEvent event) { 582 583 CmsDomUtil.fireFocusEvent(this); 584 } 585 586 /** 587 * @see com.google.gwt.user.client.ui.Widget#onLoad() 588 */ 589 @Override 590 protected void onLoad() { 591 592 removeWindowResizeHandler(); 593 m_windowResizeHandlerReg = Window.addResizeHandler(new ResizeHandler() { 594 595 public void onResize(ResizeEvent event) { 596 597 close(); 598 } 599 }); 600 601 m_mousewheelRegistration = RootPanel.get().addDomHandler(new MouseWheelHandler() { 602 603 public void onMouseWheel(MouseWheelEvent event) { 604 605 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 606 607 public void execute() { 608 609 positionPopup(); 610 } 611 }); 612 613 } 614 }, MouseWheelEvent.getType()); 615 616 } 617 618 /** 619 * @see com.google.gwt.user.client.ui.Widget#onUnload() 620 */ 621 @Override 622 protected void onUnload() { 623 624 super.onUnload(); 625 if (m_mousewheelRegistration != null) { 626 m_mousewheelRegistration.removeHandler(); 627 } 628 } 629 630 /** 631 * This method is called when a value is selected.<p> 632 * 633 * @param value the selected value 634 */ 635 protected void onValueSelect(String value) { 636 637 onValueSelect(value, true); 638 } 639 640 /** 641 * Internal handler method which is called when a new value is selected.<p> 642 * 643 * @param value the new value 644 * @param fireEvents true if change events should be fired 645 */ 646 protected void onValueSelect(String value, boolean fireEvents) { 647 648 String oldValue = m_selectedValue; 649 selectValue(value); 650 if (fireEvents) { 651 if ((oldValue == null) || !oldValue.equals(value)) { 652 // fire value change only if the the value really changed 653 ValueChangeEvent.<String> fire(this, value); 654 } 655 } 656 } 657 658 /** 659 * Internal method which is called when the selector is opened.<p> 660 */ 661 protected void open() { 662 663 if (!m_enabled) { 664 return; 665 } 666 m_openClose.setDown(true); 667 int selectorWidth; 668 // if the resize option is deactivated the popup should not be wider than the selectbox. 669 // Default its true. 670 if (!m_resizePopup) { 671 selectorWidth = m_opener.getOffsetWidth() - 2; 672 } else { 673 if (m_preferredPopupWidth == 0) { 674 m_preferredPopupWidth = initPreferredPopupWidth(); 675 } 676 selectorWidth = m_preferredPopupWidth; 677 int windowWidth = Window.getClientWidth(); 678 if (m_preferredPopupWidth > windowWidth) { 679 selectorWidth = windowWidth - 10; 680 } 681 } 682 m_popup.setWidth(selectorWidth + "px"); 683 m_popup.setWidget(m_selector); 684 m_popup.show(); 685 686 positionPopup(); 687 } 688 689 /** 690 * Deinstalls the window resize handler.<p> 691 */ 692 protected void removeWindowResizeHandler() { 693 694 if (m_windowResizeHandlerReg != null) { 695 m_windowResizeHandlerReg.removeHandler(); 696 m_windowResizeHandlerReg = null; 697 } 698 } 699 700 /** 701 * Abstract method whose implementation should truncate the opener widget(s).<p> 702 * 703 * @param prefix the text metrics prefix 704 * @param width the widget width 705 */ 706 protected abstract void truncateOpener(String prefix, int width); 707 708 /** 709 * The implementation of this method should update the opener when a new value is selected by the user.<p> 710 * 711 * @param newValue the value selected by the user 712 */ 713 protected abstract void updateOpener(String newValue); 714 715 /** 716 * Positions the select popup.<p> 717 */ 718 void positionPopup() { 719 720 if (m_popup.isShowing()) { 721 int width = m_popup.getOffsetWidth(); 722 723 int openerHeight = CmsDomUtil.getCurrentStyleInt(m_opener.getElement(), CmsDomUtil.Style.height); 724 int popupHeight = getPopupHeight(); 725 int dx = 0; 726 if (width > (m_opener.getOffsetWidth())) { 727 int spaceOnTheRight = (Window.getClientWidth() + Window.getScrollLeft()) 728 - m_opener.getAbsoluteLeft() 729 - width; 730 dx = spaceOnTheRight < 0 ? spaceOnTheRight : 0; 731 } 732 // Calculate top position for the popup 733 int top = m_opener.getAbsoluteTop(); 734 735 // Make sure scrolling is taken into account, since 736 // box.getAbsoluteTop() takes scrolling into account. 737 int windowTop = Window.getScrollTop(); 738 int windowBottom = Window.getScrollTop() + Window.getClientHeight(); 739 740 // Distance from the top edge of the window to the top edge of the 741 // text box 742 int distanceFromWindowTop = top - windowTop; 743 744 // Distance from the bottom edge of the window to the bottom edge of 745 // the text box 746 int distanceToWindowBottom = windowBottom - (top + m_opener.getOffsetHeight()); 747 748 boolean displayAbove = displayingAbove(); 749 750 // in case there is not enough space, add a scroll panel to the selector popup 751 if ((displayAbove && (distanceFromWindowTop < popupHeight)) 752 || (!displayAbove && (distanceToWindowBottom < popupHeight))) { 753 setScrollingSelector((displayAbove ? distanceFromWindowTop : distanceToWindowBottom) - 10); 754 popupHeight = m_popup.getOffsetHeight(); 755 } 756 757 if (displayAbove) { 758 // Position above the text box 759 CmsDomUtil.positionElement(m_popup.getElement(), m_panel.getElement(), dx, -(popupHeight - 2)); 760 m_selectBoxState.setValue(I_CmsLayoutBundle.INSTANCE.generalCss().cornerBottom()); 761 m_selectorState.setValue(I_CmsLayoutBundle.INSTANCE.generalCss().cornerTop()); 762 } else { 763 CmsDomUtil.positionElement(m_popup.getElement(), m_panel.getElement(), dx, openerHeight - 1); 764 m_selectBoxState.setValue(I_CmsLayoutBundle.INSTANCE.generalCss().cornerTop()); 765 m_selectorState.setValue(I_CmsLayoutBundle.INSTANCE.generalCss().cornerBottom()); 766 } 767 } 768 } 769 770 /** 771 * Helper method for adding event handlers for a 'hover' effect to the opener.<p> 772 * 773 * @param panel the opener 774 */ 775 private void addHoverHandlers(FocusPanel panel) { 776 777 final CmsStyleVariable hoverVar = new CmsStyleVariable(panel); 778 hoverVar.setValue(I_CmsLayoutBundle.INSTANCE.globalWidgetCss().openerNoHover()); 779 panel.addMouseOverHandler(new MouseOverHandler() { 780 781 /** 782 * @see com.google.gwt.event.dom.client.MouseOverHandler#onMouseOver(com.google.gwt.event.dom.client.MouseOverEvent) 783 */ 784 public void onMouseOver(MouseOverEvent event) { 785 786 hoverVar.setValue(I_CmsLayoutBundle.INSTANCE.globalWidgetCss().openerHover()); 787 } 788 }); 789 790 panel.addMouseOutHandler(new MouseOutHandler() { 791 792 /** 793 * @see com.google.gwt.event.dom.client.MouseOutHandler#onMouseOut(com.google.gwt.event.dom.client.MouseOutEvent) 794 */ 795 public void onMouseOut(MouseOutEvent event) { 796 797 hoverVar.setValue(I_CmsLayoutBundle.INSTANCE.globalWidgetCss().openerNoHover()); 798 } 799 }); 800 801 } 802 803 /** 804 * Helper method for adding event handlers for a 'hover' effect to the main panel.<p> 805 * 806 * @param panel the main panel 807 */ 808 private void addMainPanelHoverHandlers(Panel panel) { 809 810 final CmsStyleVariable hoverPanel = new CmsStyleVariable(panel); 811 hoverPanel.setValue(I_CmsLayoutBundle.INSTANCE.globalWidgetCss().openerNoHover()); 812 panel.addDomHandler(new MouseOverHandler() { 813 814 public void onMouseOver(MouseOverEvent event) { 815 816 hoverPanel.setValue(I_CmsLayoutBundle.INSTANCE.globalWidgetCss().openerHover()); 817 818 } 819 }, MouseOverEvent.getType()); 820 panel.addDomHandler(new MouseOutHandler() { 821 822 public void onMouseOut(MouseOutEvent event) { 823 824 hoverPanel.setValue(I_CmsLayoutBundle.INSTANCE.globalWidgetCss().openerNoHover()); 825 826 } 827 }, MouseOutEvent.getType()); 828 } 829 830 /** 831 * Initializes the event handlers of a select cell.<p> 832 * 833 * @param cell the select cell whose event handlers should be initialized 834 */ 835 private void initSelectCell(final A_CmsSelectCell cell) { 836 837 cell.registerDomHandler(new ClickHandler() { 838 839 /** 840 * @see com.google.gwt.event.dom.client.ClickHandler#onClick(com.google.gwt.event.dom.client.ClickEvent) 841 */ 842 public void onClick(ClickEvent e) { 843 844 onValueSelect(cell.getValue()); 845 cell.removeStyleName(CSS.selectHover()); 846 } 847 }, ClickEvent.getType()); 848 849 cell.registerDomHandler(new MouseOverHandler() { 850 851 /** 852 * @see com.google.gwt.event.dom.client.MouseOverHandler#onMouseOver(com.google.gwt.event.dom.client.MouseOverEvent) 853 */ 854 public void onMouseOver(MouseOverEvent e) { 855 856 cell.addStyleName(CSS.selectHover()); 857 858 } 859 }, MouseOverEvent.getType()); 860 861 cell.registerDomHandler(new MouseOutHandler() { 862 863 /** 864 * @see com.google.gwt.event.dom.client.MouseOutHandler#onMouseOut(com.google.gwt.event.dom.client.MouseOutEvent) 865 */ 866 public void onMouseOut(MouseOutEvent e) { 867 868 cell.removeStyleName(CSS.selectHover()); 869 } 870 }, MouseOutEvent.getType()); 871 } 872 873 /** 874 * Adds a scroll panel to the selector popup.<p> 875 * 876 * @param availableHeight the available popup height 877 */ 878 private void setScrollingSelector(int availableHeight) { 879 880 CmsScrollPanel panel = GWT.create(CmsScrollPanel.class); 881 HTMLElement elem = Js.cast(panel.getElement()); 882 elem.style.maxHeight = MaxHeightUnionType.of("" + availableHeight + "px"); 883 panel.setWidget(m_selector); 884 m_popup.setWidget(panel); 885 } 886 887 /** 888 * Toggles the state of the selector popup between 'open' and 'closed'.<p> 889 */ 890 private void toggleOpen() { 891 892 if (!m_enabled) { 893 return; 894 } 895 if (m_popup.isShowing()) { 896 close(); 897 } else { 898 open(); 899 } 900 } 901}