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.base; 017 018 import java.util.Iterator; 019 import java.util.NoSuchElementException; 020 021 import com.github.gwtbootstrap.client.ui.constants.Placement; 022 import com.github.gwtbootstrap.client.ui.constants.Trigger; 023 import com.github.gwtbootstrap.client.ui.constants.VisibilityChange; 024 import com.google.gwt.dom.client.Element; 025 import com.google.gwt.user.client.DOM; 026 import com.google.gwt.user.client.ui.HasOneWidget; 027 import com.google.gwt.user.client.ui.HasText; 028 import com.google.gwt.user.client.ui.HasWidgets; 029 import com.google.gwt.user.client.ui.IsWidget; 030 import com.google.gwt.user.client.ui.Widget; 031 032 //@formatter:off 033 /** 034 * Base class for widgets that hover above other widgets. 035 * 036 * @since 2.0.4.0 037 * 038 * @author Dominik Mayer 039 * 040 * @see <a href="http://twitter.github.com/bootstrap/javascript.html#popovers">Bootstrap documentation</a> 041 */ 042 //@formatter:on 043 public abstract class HoverBase extends ComplexWidget implements HasWidgets, HasOneWidget, IsAnimated, HasTrigger, HasPlacement, HasText, HasShowDelay, HasVisibility { 044 045 Widget widget; 046 047 /** 048 * Whether the widget is animated or not. 049 */ 050 protected boolean animated = true; 051 052 /** 053 * The placement of the widget relative to its trigger element. 054 */ 055 protected Placement placement = Placement.TOP; 056 057 /** 058 * The action that triggers the widget. 059 */ 060 protected Trigger trigger = Trigger.HOVER; 061 062 /** 063 * The delay until the widget is shown. 064 */ 065 protected int showDelayInMilliseconds = 0; 066 067 /** 068 * The delay until the widget is hidden. 069 */ 070 protected int hideDelayInMilliseconds = 0; 071 072 /** 073 * Creates a new widget based on the provided HTML tag. 074 */ 075 public HoverBase() { 076 super("span"); 077 } 078 079 /** 080 * {@inheritDoc} 081 */ 082 @Override 083 protected void onLoad() { 084 super.onLoad(); 085 086 removeDataIfExists(); 087 088 reconfigure(); 089 } 090 091 protected void removeDataIfExists() { 092 removeDataIfExists(getWidget().getElement(), getDataName()); 093 } 094 095 protected native void removeDataIfExists(Element e, String dataName) /*-{ 096 if($wnd.jQuery(e).data(dataName)) { 097 var data = $wnd.jQuery(e).data(dataName); 098 var eventIn, eventOut; 099 if (data.options.trigger != 'manual') { 100 eventIn = data.options.trigger == 'hover' ? 'mouseenter' : 'focus' 101 eventOut = data.options.trigger == 'hover' ? 'mouseleave' : 'blur' 102 data.$element.off(eventIn); 103 data.$element.off(eventOut); 104 } 105 $wnd.jQuery(e).removeData(dataName); 106 } 107 }-*/; 108 109 /** 110 * Adds an HTML data attribute to the widget's tag. 111 * 112 * @param e target element 113 * 114 * @param attribute 115 * the name of the attribute without leading <code>"data-"</code> 116 * @param value 117 * the value to be stored 118 */ 119 protected void setDataAttribute(Element e , String attribute, String value) { 120 e.setAttribute("data-" + attribute, value); 121 } 122 123 /** 124 * Returns the data stored in one of the widget's HTML data attributes. 125 * 126 * @param attribute 127 * the name of the attribute without leading <code>"data-"</code> 128 * @return the value stored in the tag 129 */ 130 protected String getDataAttribute(String attribute) { 131 return getElement().getAttribute("data-" + attribute); 132 } 133 134 /** 135 * {@inheritDoc} 136 */ 137 public void setAnimation(boolean animated) { 138 this.animated = animated; 139 } 140 141 /** 142 * Redraws the widget with the currently set options. This must <b>not</b> 143 * be called when a parameter is updated because it would deactivate all 144 * other parameters. No idea why... 145 */ 146 public abstract void reconfigure(); 147 148 /** 149 * {@inheritDoc} 150 */ 151 public boolean getAnimation() { 152 return animated; 153 } 154 155 /** 156 * {@inheritDoc} Relative to its trigger element. 157 */ 158 public void setPlacement(Placement placement) { 159 this.placement = placement; 160 } 161 162 /** 163 * {@inheritDoc} 164 */ 165 public Placement getPlacement() { 166 return placement; 167 } 168 169 /** 170 * {@inheritDoc} 171 */ 172 public void setTrigger(Trigger trigger) { 173 this.trigger = trigger; 174 } 175 176 /** 177 * {@inheritDoc} 178 */ 179 public Trigger getTrigger() { 180 return trigger; 181 } 182 183 /** 184 * {@inheritDoc} 185 */ 186 public void setShowDelay(int delayInMilliseconds) { 187 showDelayInMilliseconds = delayInMilliseconds; 188 } 189 190 /** 191 * {@inheritDoc} 192 */ 193 public int getShowDelay() { 194 return showDelayInMilliseconds; 195 } 196 197 /** 198 * {@inheritDoc} 199 */ 200 public void setHideDelay(int delayInMilliseconds) { 201 hideDelayInMilliseconds = delayInMilliseconds; 202 } 203 204 /** 205 * {@inheritDoc} 206 */ 207 public int getHideDelay() { 208 return hideDelayInMilliseconds; 209 } 210 211 /** 212 * {@inheritDoc} 213 */ 214 public void show() { 215 changeVisibility(VisibilityChange.SHOW); 216 } 217 218 /** 219 * {@inheritDoc} 220 */ 221 public void hide() { 222 changeVisibility(VisibilityChange.HIDE); 223 } 224 225 /** 226 * {@inheritDoc} 227 */ 228 public void toggle() { 229 changeVisibility(VisibilityChange.TOGGLE); 230 } 231 232 /** 233 * Changes the visibility of the widget. 234 * 235 * @param visibilityChange 236 * the action to be performed 237 */ 238 protected abstract void changeVisibility(VisibilityChange visibilityChange); 239 240 /** 241 * Adds a widget to this panel. 242 * 243 * @param w 244 * the child widget to be added 245 */ 246 @Override 247 public void add(Widget w) { 248 // Can't add() more than one widget to a SimplePanel. 249 if (getWidget() != null) { 250 throw new IllegalStateException("SimplePanel can only contain one child widget"); 251 } 252 setWidget(w); 253 } 254 255 /** 256 * Gets the panel's child widget. 257 * 258 * @return the child widget, or <code>null</code> if none is present 259 */ 260 public Widget getWidget() { 261 return widget; 262 } 263 264 public Iterator<Widget> iterator() { 265 // Return a simple iterator that enumerates the 0 or 1 elements in this 266 // panel. 267 return new Iterator<Widget>() { 268 269 boolean hasElement = widget != null; 270 271 Widget returned = null; 272 273 public boolean hasNext() { 274 return hasElement; 275 } 276 277 public Widget next() { 278 if (!hasElement || (widget == null)) { 279 throw new NoSuchElementException(); 280 } 281 hasElement = false; 282 return (returned = widget); 283 } 284 285 public void remove() { 286 if (returned != null) { 287 HoverBase.this.remove(returned); 288 } 289 } 290 }; 291 } 292 293 @Override 294 public boolean remove(Widget w) { 295 // Validate. 296 if (widget != w) { 297 return false; 298 } 299 300 // Orphan. 301 try { 302 orphan(w); 303 } finally { 304 // Physical detach. 305 getContainerElement().removeChild(w.getElement()); 306 307 // Logical detach. 308 widget = null; 309 } 310 return true; 311 } 312 313 public void setWidget(IsWidget w) { 314 setWidget(asWidgetOrNull(w)); 315 } 316 317 /** 318 * Sets this panel's widget. Any existing child widget will be removed. 319 * 320 * @param w 321 * the panel's new widget, or <code>null</code> to clear the 322 * panel 323 */ 324 public void setWidget(Widget w) { 325 // Validate 326 if (w == widget) { 327 return; 328 } 329 330 if(w.getParent() != null) { 331 if(widget != null) { 332 remove(widget); 333 } 334 widget = w; 335 return; 336 } 337 338 // Detach new child. 339 if (w != null) { 340 w.removeFromParent(); 341 } 342 343 // Remove old child. 344 if (widget != null) { 345 remove(widget); 346 } 347 348 // Logical attach. 349 widget = w; 350 351 if (w != null) { 352 // Physical attach. 353 DOM.appendChild(getContainerElement(), widget.getElement()); 354 355 adopt(widget); 356 } 357 } 358 359 /** 360 * Override this method to specify that an element other than the root 361 * element be the container for the panel's child widget. This can be useful 362 * when you want to create a simple panel that decorates its contents. 363 * 364 * Note that this method continues to return the 365 * {@link com.google.gwt.user.client.Element} class defined in the 366 * <code>User</code> module to maintain backwards compatibility. 367 * 368 * @return the element to be used as the panel's container 369 */ 370 protected com.google.gwt.user.client.Element getContainerElement() { 371 return getElement(); 372 } 373 374 /** 375 * Get data name of JS Data API. 376 * @return data name 377 */ 378 protected abstract String getDataName(); 379 }