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.I_CmsHasInit; 031import org.opencms.gwt.client.Messages; 032import org.opencms.gwt.client.ui.I_CmsAutoHider; 033import org.opencms.gwt.client.ui.css.I_CmsInputLayoutBundle; 034import org.opencms.gwt.client.ui.input.form.CmsWidgetFactoryRegistry; 035import org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory; 036import org.opencms.gwt.client.util.CmsMessages; 037 038import org.opencms.util.CmsStringUtil; 039 040import java.util.HashMap; 041import java.util.Map; 042 043import com.google.common.base.Optional; 044import com.google.gwt.event.dom.client.BlurEvent; 045import com.google.gwt.event.dom.client.BlurHandler; 046import com.google.gwt.event.dom.client.ClickEvent; 047import com.google.gwt.event.dom.client.ClickHandler; 048import com.google.gwt.event.dom.client.FocusEvent; 049import com.google.gwt.event.dom.client.FocusHandler; 050import com.google.gwt.event.logical.shared.ValueChangeHandler; 051import com.google.gwt.event.shared.HandlerRegistration; 052import com.google.gwt.user.client.ui.FocusPanel; 053import com.google.gwt.user.client.ui.Panel; 054import com.google.gwt.user.client.ui.SimplePanel; 055import com.google.gwt.user.client.ui.TextBox; 056 057/** 058 * Widget for selecting one of multiple items from a drop-down list which opens 059 * after the user clicks on the widget.<p> 060 * 061 * @since 8.0.0 062 * 063 */ 064public class CmsComboBox extends A_CmsSelectBox<CmsLabelSelectCell> implements I_CmsHasInit, I_CmsHasGhostValue { 065 066 /** The key for the text which should be displayed in the opener if no option is available. */ 067 public static final String NO_SELECTION_OPENER_TEXT = "%NO_SELECTION_OPENER_TEXT%"; 068 069 /** The key for the text which should be displayed if no option is available. */ 070 public static final String NO_SELECTION_TEXT = "%NO_SELECTION_TEXT%"; 071 072 /** The widget type identifier. */ 073 private static final String WIDGET_TYPE = "combo"; 074 075 /** CSS style name for combo boxes. */ 076 public static final String CSS_CLASS = I_CmsInputLayoutBundle.INSTANCE.inputCss().comboBox(); 077 078 /** The ghost value. */ 079 protected String m_ghostValue; 080 081 /** The widget displayed in the opener. */ 082 protected CmsSimpleTextBox m_openerWidget; 083 084 /** The fade panel of this input box. */ 085 Panel m_fadePanel; 086 087 /** A map from select options to their label texts. */ 088 private Map<String, String> m_items; 089 090 /** The inner main panel for the input box. */ 091 private Panel m_mainPanel; 092 093 /** The text which should be displayed in the opener if there is no selection. */ 094 private String m_noSelectionOpenerText; 095 096 /** The text which should be displayed if there is no selection. */ 097 private String m_noSelectionText; 098 099 /** A map of titles for the select options which should be displayed on mouseover. */ 100 private Map<String, String> m_titles = new HashMap<String, String>(); 101 102 /** 103 * Default constructor.<p> 104 */ 105 public CmsComboBox() { 106 107 super(); 108 addStyleName(CSS_CLASS); 109 110 } 111 112 /** 113 * Constructs a new select box from a map.<p> 114 * 115 * The keys of the map are the values of the select options, and the values of the map are the labels to be displayed 116 * for each option. 117 * 118 * @param items the map of select options 119 */ 120 public CmsComboBox(Map<String, String> items) { 121 122 this(); 123 setItems(items); 124 } 125 126 /** 127 * Creates a new select box, with the option of adding a "not selected" choice.<p> 128 * 129 * @param items the map of select options 130 * @param addNullOption if true, a "not selected" option will be added to the select box 131 */ 132 public CmsComboBox(Map<String, String> items, boolean addNullOption) { 133 134 super(); 135 addStyleName(CSS_CLASS); 136 String resizable = items.remove(CmsSelectBox.OPTION_RESIZABLE); 137 if ((resizable != null) && Boolean.FALSE.toString().equals(resizable)) { 138 setPopupResize(false); 139 } 140 141 if (items.containsKey(NO_SELECTION_TEXT)) { 142 m_noSelectionText = items.get(NO_SELECTION_TEXT); 143 m_noSelectionOpenerText = items.get(NO_SELECTION_OPENER_TEXT); 144 if (m_noSelectionOpenerText == null) { 145 m_noSelectionOpenerText = m_noSelectionText; 146 } 147 items.remove(NO_SELECTION_TEXT); 148 items.remove(NO_SELECTION_OPENER_TEXT); 149 } 150 if (addNullOption) { 151 String text = Messages.get().key(Messages.GUI_SELECTBOX_EMPTY_SELECTION_0); 152 items.put("", text); 153 } 154 setItems(items); 155 if (addNullOption) { 156 selectValue(""); 157 } 158 } 159 160 /** 161 * Initializes this class.<p> 162 */ 163 public static void initClass() { 164 165 // registers a factory for creating new instances of this widget 166 CmsWidgetFactoryRegistry.instance().registerFactory(WIDGET_TYPE, new I_CmsFormWidgetFactory() { 167 168 /** 169 * @see org.opencms.gwt.client.ui.input.form.I_CmsFormWidgetFactory#createWidget(java.util.Map, com.google.common.base.Optional) 170 */ 171 public I_CmsFormWidget createWidget(Map<String, String> widgetParams, Optional<String> defaultValue) { 172 173 return new CmsComboBox(widgetParams, false); 174 } 175 }); 176 } 177 178 /** 179 * Adds a new selection cell.<p> 180 * 181 * @param value the value of the select option 182 * @param text the text to be displayed for the select option 183 */ 184 public void addOption(String value, String text) { 185 186 String title = getTitle(value, text); 187 CmsLabelSelectCell cell = new CmsLabelSelectCell(value, text, title); 188 addOption(cell); 189 } 190 191 /** 192 * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 193 */ 194 @Override 195 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { 196 197 return m_openerWidget.addValueChangeHandler(handler); 198 } 199 200 /** 201 * 202 */ 203 public void closeSelector() { 204 205 close(); 206 } 207 208 /** 209 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getApparentValue() 210 */ 211 public String getApparentValue() { 212 213 String val = getFormValueAsString(); 214 if (val == null) { 215 val = m_ghostValue; 216 } 217 return val; 218 219 } 220 221 /** 222 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValue() 223 */ 224 @Override 225 public Object getFormValue() { 226 227 if (m_openerWidget.getText() == null) { 228 return ""; 229 } 230 return m_openerWidget.getText(); 231 } 232 233 /** 234 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#getFormValueAsString() 235 */ 236 @Override 237 public String getFormValueAsString() { 238 239 return (String)getFormValue(); 240 } 241 242 /** 243 * Returns the mainpanel of this widget.<p> 244 * 245 * @return the mainpanel of this widget 246 */ 247 public Panel getMainPanel() { 248 249 return m_mainPanel; 250 } 251 252 /** 253 * Returns the opener of this widget.<p> 254 * 255 * @return the opener of this widget 256 */ 257 public FocusPanel getOpener() { 258 259 return m_opener; 260 } 261 262 /** 263 * Returns the text box of this widget.<p> 264 * 265 * @return the text box of this widget 266 */ 267 public TextBox getTextBox() { 268 269 return m_openerWidget; 270 } 271 272 /***/ 273 public void openSelector() { 274 275 open(); 276 } 277 278 /** 279 * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#selectValue(java.lang.String) 280 */ 281 @Override 282 public void selectValue(String value) { 283 284 super.selectValue(value); 285 updateStyle(); 286 } 287 288 /** 289 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setAutoHideParent(org.opencms.gwt.client.ui.I_CmsAutoHider) 290 */ 291 public void setAutoHideParent(I_CmsAutoHider autoHideParent) { 292 293 // nothing to do 294 295 } 296 297 /** 298 * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#setFormValue(java.lang.Object) 299 */ 300 @Override 301 public void setFormValue(Object value) { 302 303 if (value == null) { 304 value = ""; 305 } 306 307 if (value instanceof String) { 308 String strValue = (String)value; 309 if (m_selectCells.containsKey(value)) { 310 selectValue(strValue); 311 onValueSelect(strValue); 312 } else { 313 m_openerWidget.setText(strValue); 314 m_openerWidget.getElement().setTitle(strValue); 315 } 316 317 } 318 } 319 320 /** 321 * @see org.opencms.gwt.client.ui.input.I_CmsFormWidget#setFormValueAsString(java.lang.String) 322 */ 323 @Override 324 public void setFormValueAsString(String formValue) { 325 326 setFormValue(formValue); 327 } 328 329 /** 330 * @see org.opencms.gwt.client.ui.input.I_CmsHasGhostValue#setGhostMode(boolean) 331 */ 332 public void setGhostMode(boolean ghostMode) { 333 334 // do nothing for now 335 336 } 337 338 /** 339 * @see org.opencms.gwt.client.ui.input.I_CmsHasGhostValue#setGhostValue(java.lang.String, boolean) 340 */ 341 public void setGhostValue(String value, boolean ghostMode) { 342 343 if (value == null) { 344 value = ""; 345 } 346 String otherOptionText = m_items.get(value); 347 String message = m_noSelectionText != null 348 ? m_noSelectionText 349 : Messages.get().key(Messages.GUI_SELECTBOX_EMPTY_SELECTION_1); 350 message = CmsMessages.formatMessage(message, otherOptionText); 351 m_ghostValue = value; 352 updateCells(); 353 if (ghostMode) { 354 selectValue(""); 355 } 356 } 357 358 /** 359 * Sets the items using a map from option values to label texts.<p> 360 * 361 * @param items the map containing the select options 362 */ 363 public void setItems(Map<String, String> items) { 364 365 clearItems(); 366 m_items = items; 367 for (Map.Entry<String, String> entry : items.entrySet()) { 368 addOption(entry.getKey(), entry.getValue()); 369 } 370 } 371 372 /** 373 * Sets the title for a select option.<p> 374 * 375 * Note: This will only affect select options added *after* calling this method! 376 * 377 * @param text the new title for the option 378 */ 379 public void setText(String text) { 380 381 m_openerWidget.setText(text); 382 } 383 384 /** 385 * Sets the text that is used for the "not selected" option.<p> 386 * 387 * @param text the text which should be used for the "not selected" option 388 */ 389 public void setTextForNullSelection(String text) { 390 391 // do nothing if there's no null option 392 CmsLabelSelectCell cell = m_selectCells.get(""); 393 if (cell == null) { 394 return; 395 } 396 cell.setText(text); 397 // if the null option is selected, we still need to update the opener 398 if (CmsStringUtil.isEmptyOrWhitespaceOnly(m_selectedValue)) { 399 selectValue(""); 400 } 401 } 402 403 /** 404 * Sets the title for a select option.<p> 405 * 406 * Note: This will only affect select options added *after* calling this method! 407 * 408 * @param option the select option value 409 * @param title the new title for the option 410 */ 411 public void setTitle(String option, String title) { 412 413 m_titles.put(option, title); 414 } 415 416 /** 417 * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#truncateOpener(java.lang.String, int) 418 */ 419 @Override 420 public void truncateOpener(String prefix, int width) { 421 422 //m_openerWidget.truncate(prefix + '_' + TM_OPENER_LABEL, width); 423 } 424 425 /** 426 * Updates the select cells.<p> 427 */ 428 public void updateCells() { 429 430 for (CmsLabelSelectCell cell : m_selectCells.values()) { 431 updateCell(cell); 432 } 433 } 434 435 /** 436 * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#createUnknownOption(java.lang.String) 437 */ 438 @Override 439 protected CmsLabelSelectCell createUnknownOption(String value) { 440 441 CmsLabelSelectCell cell = new CmsLabelSelectCell(value, value); 442 return cell; 443 444 } 445 446 /** 447 * Helper method to get the title for a given select option.<p> 448 * 449 * @param option the select option value 450 * @param defaultValue the value to return when no title for the value was found 451 * 452 * @return the title for the select option 453 */ 454 protected String getTitle(String option, String defaultValue) { 455 456 if ((option != null) && m_titles.containsKey(option)) { 457 return m_titles.get(option); 458 } 459 return defaultValue; 460 } 461 462 /** 463 * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#initOpener() 464 */ 465 @Override 466 protected void initOpener() { 467 468 m_mainPanel = new SimplePanel(); 469 m_fadePanel = new SimplePanel(); 470 m_openerWidget = new CmsSimpleTextBox(); 471 m_panel.add(m_fadePanel); 472 473 m_openerWidget.addBlurHandler(new BlurHandler() { 474 475 public void onBlur(BlurEvent event) { 476 477 m_panel.add(m_fadePanel); 478 m_openerWidget.getElement().setTitle(m_openerWidget.getText()); 479 } 480 }); 481 m_openerWidget.addFocusHandler(new FocusHandler() { 482 483 public void onFocus(FocusEvent event) { 484 485 // on focus remove the fader. 486 m_panel.remove(m_fadePanel); 487 m_openerWidget.getElement().setTitle(""); 488 489 } 490 }); 491 492 m_mainPanel.setStyleName(CSS.comboBoxOpener()); 493 m_fadePanel.addDomHandler(new ClickHandler() { 494 495 public void onClick(ClickEvent event) { 496 497 m_openerWidget.setFocus(true); 498 m_openerWidget.setCursorPos(m_openerWidget.getText().length()); 499 500 if (m_popup.isShowing()) { 501 close(); 502 } else { 503 open(); 504 } 505 506 } 507 }, ClickEvent.getType()); 508 m_fadePanel.setStyleName(CSS.fader()); 509 m_mainPanel.add(m_openerWidget); 510 m_opener.add(m_mainPanel); 511 512 } 513 514 /** 515 * @see com.google.gwt.user.client.ui.Widget#onLoad() 516 */ 517 @Override 518 protected void onLoad() { 519 520 super.onLoad(); 521 updateStyle(); 522 } 523 524 /** 525 * Updates the select cell.<p> 526 * 527 * @param cell the select cell 528 */ 529 protected void updateCell(CmsLabelSelectCell cell) { 530 // do nothing 531 532 } 533 534 /** 535 * @see org.opencms.gwt.client.ui.input.A_CmsSelectBox#updateOpener(java.lang.String) 536 */ 537 @Override 538 protected void updateOpener(String newValue) { 539 540 CmsLabelSelectCell cell = m_selectCells.get(newValue); 541 String openerText = cell.getOpenerText(); 542 m_openerWidget.setTitle(openerText); 543 m_openerWidget.setValue(newValue, true); 544 } 545 546 /** 547 * This method should be used to make changes to the CSS style of the select box when the value changes.<p> 548 */ 549 protected void updateStyle() { 550 551 // do nothing 552 553 } 554 555}