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.form; 029 030import org.opencms.gwt.client.util.CmsJsUtil; 031 032import com.google.gwt.core.client.GWT; 033import com.google.gwt.dom.client.Element; 034import com.google.gwt.dom.client.EventTarget; 035import com.google.gwt.dom.client.NativeEvent; 036import com.google.gwt.dom.client.Style; 037import com.google.gwt.dom.client.Style.Unit; 038import com.google.gwt.dom.client.StyleInjector; 039import com.google.gwt.uibinder.client.UiBinder; 040import com.google.gwt.uibinder.client.UiField; 041import com.google.gwt.user.client.Event; 042import com.google.gwt.user.client.Event.NativePreviewEvent; 043import com.google.gwt.user.client.Event.NativePreviewHandler; 044import com.google.gwt.user.client.ui.Composite; 045import com.google.gwt.user.client.ui.HTML; 046import com.google.gwt.user.client.ui.Panel; 047import com.google.gwt.user.client.ui.RootPanel; 048import com.google.gwt.user.client.ui.Widget; 049 050/** 051 * Tooltip widget for element setting help texts.<p> 052 */ 053public class CmsFieldTooltip extends Composite { 054 055 /** 056 * Data needed to create a tooltip.<p> 057 */ 058 public static class Data { 059 060 /** The tooltip content. */ 061 private String m_info; 062 063 /** True if the content is HTML, false if it is plain text. */ 064 private boolean m_isHtml; 065 066 /** The icon for which the tooltip should be shown. */ 067 private Panel m_reference; 068 069 /** 070 * Creates a new instance.<p> 071 * @param reference the icon for which the tooltip should be shown 072 * @param info the tooltip content 073 * @param isHtml true if the content is HTML 074 */ 075 public Data(Panel reference, String info, boolean isHtml) { 076 077 m_info = info; 078 m_isHtml = isHtml; 079 m_reference = reference; 080 } 081 082 /** 083 * Gets the tooltip content.<p> 084 * 085 * @return the tooltip content 086 */ 087 public String getInfo() { 088 089 return m_info; 090 } 091 092 /** 093 * Gets the icon for which the tooltip is intended.<p> 094 * 095 * @return the icon for which to display a tooltip 096 */ 097 public Panel getReference() { 098 099 return m_reference; 100 } 101 102 /** 103 * Returns true if the tooltip content is HTML.<p> 104 * 105 * @return true if the tooltip content is HTML 106 */ 107 public boolean isHtml() { 108 109 return m_isHtml; 110 } 111 112 /** 113 * @see java.lang.Object#toString() 114 */ 115 @Override 116 public String toString() { 117 118 return m_info; 119 } 120 } 121 122 /** 123 * Event handler for managing tooltip visibility.<p> 124 */ 125 public static class Handler implements NativePreviewHandler { 126 127 /** The currently active tooltip. */ 128 private CmsFieldTooltip m_tooltip; 129 130 /** 131 * Creats a new instance.<p> 132 */ 133 public Handler() { 134 135 Event.addNativePreviewHandler(this); 136 } 137 138 /** 139 * Called when the user clicks on the icon.<p> 140 * 141 * @param data the tooltip data 142 */ 143 public void buttonClick(Data data) { 144 145 if (m_tooltip != null) { 146 m_tooltip.makePersistent(); 147 } 148 } 149 150 /** 151 * Called if the mouse moves over the button with a tooltip.<p> 152 * 153 * @param data the tooltip data for a button 154 */ 155 public void buttonHover(Data data) { 156 157 if (data != getData()) { 158 closeTooltip(); 159 CmsFieldTooltip tooltip = new CmsFieldTooltip(data); 160 if (data.isHtml()) { 161 tooltip.getLabel().setHTML(data.getInfo()); 162 } else { 163 tooltip.getLabel().setText(data.getInfo()); 164 } 165 m_tooltip = tooltip; 166 RootPanel.get().add(tooltip); 167 position(tooltip.getElement(), data.getReference().getElement()); 168 } 169 } 170 171 /** 172 * Called if the mouse leaves a button with a tooltip.<p> 173 * 174 * @param data the tooltip data for the button 175 */ 176 public void buttonOut(Data data) { 177 178 closeTooltip(false); 179 } 180 181 /** 182 * Closes the tooltip.<p> 183 */ 184 public void closeTooltip() { 185 186 closeTooltip(true); 187 } 188 189 /** 190 * Closes the active tooltip.<p> 191 * 192 * @param closePersistent true if a persistent tooltip should also be closed 193 */ 194 public void closeTooltip(boolean closePersistent) { 195 196 if (m_tooltip != null) { 197 198 if (m_tooltip.isPersistent() && !closePersistent) { 199 return; 200 } 201 if (CmsJsUtil.getAttributeString(CmsJsUtil.getWindow(), "cmsDisableCloseTooltip") == null) { 202 m_tooltip.removeFromParent(); 203 } 204 m_tooltip = null; 205 } 206 } 207 208 /** 209 * Gets the target element for a native event, or null if there is no target element.<p> 210 * 211 * @param nativeEvent the native event 212 * @return the target element, or null if there is no target element 213 */ 214 public Element getTargetElement(NativeEvent nativeEvent) { 215 216 EventTarget target = nativeEvent.getEventTarget(); 217 Element targetElement = null; 218 if (Element.is(target)) { 219 targetElement = Element.as(target); 220 } 221 return targetElement; 222 } 223 224 /** 225 * @see com.google.gwt.user.client.Event.NativePreviewHandler#onPreviewNativeEvent(com.google.gwt.user.client.Event.NativePreviewEvent) 226 */ 227 public void onPreviewNativeEvent(NativePreviewEvent event) { 228 229 int eventType = event.getTypeInt(); 230 NativeEvent nativeEvent = event.getNativeEvent(); 231 switch (eventType) { 232 case Event.ONMOUSEWHEEL: 233 closeTooltip(); 234 break; 235 case Event.ONMOUSEDOWN: 236 if (tooltipContains(getTargetElement(nativeEvent))) { 237 event.consume(); 238 return; 239 } else { 240 closeTooltip(); 241 } 242 break; 243 case Event.ONCLICK: 244 if (tooltipContains(getTargetElement(nativeEvent))) { 245 event.consume(); 246 return; 247 } 248 break; 249 default: // do nothing 250 break; 251 252 } 253 } 254 255 /** 256 * Checks if the tooltip contains a given element.<p> 257 * 258 * @param targetElement the element to check 259 * @return true if the tooltip contains an element 260 */ 261 public boolean tooltipContains(Element targetElement) { 262 263 if ((targetElement != null) && (m_tooltip != null)) { 264 return m_tooltip.getElement().isOrHasChild(targetElement) 265 || m_tooltip.getData().getReference().getElement().isOrHasChild(targetElement); 266 } 267 return false; 268 } 269 270 /** 271 * Gets the tooltip data for the active tooltip, or null if no tooltip is active.<p> 272 * 273 * @return the tooltip data 274 */ 275 Data getData() { 276 277 if (m_tooltip == null) { 278 return null; 279 } 280 return m_tooltip.getData(); 281 } 282 } 283 284 /** The ui binder interface for this widget. */ 285 protected interface I_UiBinder extends UiBinder<Widget, CmsFieldTooltip> { 286 //UIBinder interface 287 288 } 289 290 /** The handler instance. */ 291 private static Handler m_handler = new Handler(); 292 293 /** The label with the help text. */ 294 @UiField 295 protected HTML m_label; 296 297 /** The tooltip data. */ 298 private Data m_data; 299 300 /** Flag indicating whether tooltip is persistent (i.e. not closed when the mouse cursor leaves the icon). */ 301 private boolean m_persistent; 302 303 /** 304 * Creates a new instance.<p> 305 * 306 * @param data the tooltip data 307 */ 308 public CmsFieldTooltip(Data data) { 309 310 I_UiBinder uiBinder = GWT.create(I_UiBinder.class); 311 initWidget(uiBinder.createAndBindUi(this)); 312 // force synchronous injection of styles 313 StyleInjector.flush(); 314 m_data = data; 315 } 316 317 /** 318 * Gets the handler instance.<p> 319 * 320 * @return the handler instance 321 */ 322 public static Handler getHandler() { 323 324 return m_handler; 325 } 326 327 /** 328 * Positions the tooltip.<p> 329 * 330 * 331 * @param elem the tooltip element 332 * @param referenceElement the tooltip icon element 333 */ 334 public static void position(Element elem, Element referenceElement) { 335 336 int dy = 25; 337 Style style = elem.getStyle(); 338 style.setLeft(0, Unit.PX); 339 style.setTop(0, Unit.PX); 340 int myX = elem.getAbsoluteLeft(); 341 int myY = elem.getAbsoluteTop(); 342 int refX = referenceElement.getAbsoluteLeft(); 343 int refY = referenceElement.getAbsoluteTop(); 344 int refWidth = referenceElement.getOffsetWidth(); 345 int newX = (refX - myX - ((2 * elem.getOffsetWidth()) / 3)) + (refWidth / 2); 346 int newY = (refY - myY) + dy; 347 style.setLeft(newX, Unit.PX); 348 style.setTop(newY, Unit.PX); 349 } 350 351 /** 352 * Gets the tooltip data.<p> 353 * 354 * @return the tooltip data 355 */ 356 public Data getData() { 357 358 return m_data; 359 } 360 361 /** 362 * Gets the label for the help text.<p> 363 * 364 * @return the label for the help text 365 */ 366 public HTML getLabel() { 367 368 return m_label; 369 } 370 371 /** 372 * Checks if the tooltip is persistent, i.e. it can no longer be closed by leaving the button with the mouse cursor, 373 * but needs to be closed by clicking somewhere else.<p> 374 * 375 * @return true if the tooltip is persistent 376 */ 377 public boolean isPersistent() { 378 379 return m_persistent; 380 } 381 382 /** 383 * Makes the tooltip persistent, i.e. it can no longer be closed by leaving the button with the mouse cursor, 384 * but needs to be closed by clicking somewhere else.<p> 385 */ 386 public void makePersistent() { 387 388 m_persistent = true; 389 } 390 391}