001 package org.gwtbootstrap3.client.ui;
002
003 /*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2013 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 * http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023 import java.util.Iterator;
024 import java.util.NoSuchElementException;
025
026 import org.gwtbootstrap3.client.shared.event.HiddenEvent;
027 import org.gwtbootstrap3.client.shared.event.HiddenHandler;
028 import org.gwtbootstrap3.client.shared.event.HideEvent;
029 import org.gwtbootstrap3.client.shared.event.HideHandler;
030 import org.gwtbootstrap3.client.shared.event.ShowEvent;
031 import org.gwtbootstrap3.client.shared.event.ShowHandler;
032 import org.gwtbootstrap3.client.shared.event.ShownEvent;
033 import org.gwtbootstrap3.client.shared.event.ShownHandler;
034 import org.gwtbootstrap3.client.ui.base.HasHover;
035 import org.gwtbootstrap3.client.ui.base.HasId;
036 import org.gwtbootstrap3.client.ui.constants.Placement;
037 import org.gwtbootstrap3.client.ui.constants.Trigger;
038
039 import com.google.gwt.dom.client.Element;
040 import com.google.gwt.event.logical.shared.AttachEvent;
041 import com.google.gwt.user.client.Event;
042 import com.google.gwt.user.client.ui.HasOneWidget;
043 import com.google.gwt.user.client.ui.HasWidgets;
044 import com.google.gwt.user.client.ui.IsWidget;
045 import com.google.gwt.user.client.ui.Widget;
046 import com.google.web.bindery.event.shared.HandlerRegistration;
047
048 /**
049 * @author Joshua Godi
050 */
051 public class Popover implements IsWidget, HasWidgets, HasOneWidget, HasId, HasHover {
052 private static final String TOGGLE = "toggle";
053 private static final String SHOW = "show";
054 private static final String HIDE = "hide";
055 private static final String DESTROY = "destroy";
056
057 // Defaults from http://getbootstrap.com/javascript/#popovers
058 private boolean isAnimated = true;
059 private boolean isHTML = false;
060 private Placement placement = Placement.TOP;
061 private Trigger trigger = Trigger.HOVER;
062 private String title = "";
063 private String content = "";
064 private int hideDelayMs = 0;
065 private int showDelayMs = 0;
066 private String container = null;
067 private final String selector = null;
068
069 private Widget widget;
070 private String id;
071
072 public Popover() {
073 }
074
075 public Popover(final Widget w) {
076 setWidget(w);
077 }
078
079 @Override
080 public void setWidget(final Widget w) {
081 // Validate
082 if (w == widget) {
083 return;
084 }
085
086 // Detach new child
087 if (w != null) {
088 w.removeFromParent();
089 }
090
091 // Remove old child
092 if (widget != null) {
093 remove(widget);
094 }
095
096 // Logical attach, but don't physical attach; done by jquery.
097 widget = w;
098 if (widget == null) {
099 return;
100 }
101
102 // Bind jquery events
103 bindJavaScriptEvents(widget.getElement());
104
105 // When we attach it, configure the tooltip
106 widget.addAttachHandler(new AttachEvent.Handler() {
107 @Override
108 public void onAttachOrDetach(final AttachEvent event) {
109 reconfigure();
110 }
111 });
112 }
113
114 @Override
115 public void add(final Widget child) {
116 if (getWidget() != null) {
117 throw new IllegalStateException("Can only contain one child widget");
118 }
119 setWidget(child);
120 }
121
122 @Override
123 public void setWidget(final IsWidget w) {
124 widget = (w == null) ? null : w.asWidget();
125 }
126
127 @Override
128 public Widget getWidget() {
129 return widget;
130 }
131
132 @Override
133 public void setId(final String id) {
134 this.id = id;
135 if (widget != null) {
136 widget.getElement().setId(id);
137 }
138 }
139
140 @Override
141 public String getId() {
142 return (widget == null) ? id : widget.getElement().getId();
143 }
144
145 @Override
146 public void setIsAnimated(final boolean isAnimated) {
147 this.isAnimated = isAnimated;
148 }
149
150 @Override
151 public boolean isAnimated() {
152 return isAnimated;
153 }
154
155 @Override
156 public void setIsHtml(final boolean isHTML) {
157 this.isHTML = isHTML;
158 }
159
160 @Override
161 public boolean isHtml() {
162 return isHTML;
163 }
164
165 @Override
166 public void setPlacement(final Placement placement) {
167 this.placement = placement;
168 }
169
170 @Override
171 public Placement getPlacement() {
172 return placement;
173 }
174
175 @Override
176 public void setTrigger(final Trigger trigger) {
177 this.trigger = trigger;
178 }
179
180 @Override
181 public Trigger getTrigger() {
182 return trigger;
183 }
184
185 @Override
186 public void setShowDelayMs(final int showDelayMs) {
187 this.showDelayMs = showDelayMs;
188 }
189
190 @Override
191 public int getShowDelayMs() {
192 return showDelayMs;
193 }
194
195 @Override
196 public void setHideDelayMs(final int hideDelayMs) {
197 this.hideDelayMs = hideDelayMs;
198 }
199
200 @Override
201 public int getHideDelayMs() {
202 return hideDelayMs;
203 }
204
205 @Override
206 public void setContainer(final String container) {
207 this.container = container;
208 }
209
210 @Override
211 public String getContainer() {
212 return container;
213 }
214
215 public String getTitle() {
216 return title;
217 }
218
219 public void setContent(final String content) {
220 this.content = content;
221 }
222
223 public void setTitle(final String title) {
224 this.title = title;
225 }
226
227 public void reconfigure() {
228 // First destroy the old tooltip
229 destroy();
230
231 // Setup the new tooltip
232 if (container != null && selector != null) {
233 popover(widget.getElement(), isAnimated, isHTML, placement.getCssName(), selector, title, content,
234 trigger.getCssName(), showDelayMs, hideDelayMs, container);
235 } else if (container != null) {
236 popover(widget.getElement(), isAnimated, isHTML, placement.getCssName(), title, content,
237 trigger.getCssName(), showDelayMs, hideDelayMs, container);
238 } else if (selector != null) {
239 popover(widget.getElement(), isAnimated, isHTML, placement.getCssName(), selector, title, content,
240 trigger.getCssName(), showDelayMs, hideDelayMs);
241 } else {
242 popover(widget.getElement(), isAnimated, isHTML, placement.getCssName(), title, content,
243 trigger.getCssName(), showDelayMs, hideDelayMs);
244 }
245 }
246
247 public void toggle() {
248 call(widget.getElement(), TOGGLE);
249 }
250
251 public void show() {
252 call(widget.getElement(), SHOW);
253 }
254
255 public void hide() {
256 call(widget.getElement(), HIDE);
257 }
258
259 public void destroy() {
260 call(widget.getElement(), DESTROY);
261 }
262
263 /**
264 * Can be override by subclasses to handle Tooltip's "show" event however
265 * it's recommended to add an event handler to the tooltip.
266 *
267 * @param evt Event
268 * @see org.gwtbootstrap3.client.shared.event.ShowEvent
269 */
270 protected void onShow(final Event evt) {
271 widget.fireEvent(new ShowEvent(evt));
272 }
273
274 /**
275 * Can be override by subclasses to handle Tooltip's "shown" event however
276 * it's recommended to add an event handler to the tooltip.
277 *
278 * @param evt Event
279 * @see org.gwtbootstrap3.client.shared.event.ShownEvent
280 */
281 protected void onShown(final Event evt) {
282 widget.fireEvent(new ShownEvent(evt));
283 }
284
285 /**
286 * Can be override by subclasses to handle Tooltip's "hide" event however
287 * it's recommended to add an event handler to the tooltip.
288 *
289 * @param evt Event
290 * @see org.gwtbootstrap3.client.shared.event.HideEvent
291 */
292 protected void onHide(final Event evt) {
293 widget.fireEvent(new HideEvent(evt));
294 }
295
296 /**
297 * Can be override by subclasses to handle Tooltip's "hidden" event however
298 * it's recommended to add an event handler to the tooltip.
299 *
300 * @param evt Event
301 * @see org.gwtbootstrap3.client.shared.event.HiddenEvent
302 */
303 protected void onHidden(final Event evt) {
304 widget.fireEvent(new HiddenEvent(evt));
305 }
306
307 public HandlerRegistration addShowHandler(final ShowHandler showHandler) {
308 return widget.addHandler(showHandler, ShowEvent.getType());
309 }
310
311 public HandlerRegistration addShownHandler(final ShownHandler shownHandler) {
312 return widget.addHandler(shownHandler, ShownEvent.getType());
313 }
314
315 public HandlerRegistration addHideHandler(final HideHandler hideHandler) {
316 return widget.addHandler(hideHandler, HideEvent.getType());
317 }
318
319 public HandlerRegistration addHiddenHandler(final HiddenHandler hiddenHandler) {
320 return widget.addHandler(hiddenHandler, HiddenEvent.getType());
321 }
322
323 @Override
324 public void clear() {
325 widget = null;
326 }
327
328 @Override
329 public Iterator<Widget> iterator() {
330 // Simple iterator for the widget
331 return new Iterator<Widget>() {
332 boolean hasElement = widget != null;
333 Widget returned = null;
334
335 @Override
336 public boolean hasNext() {
337 return hasElement;
338 }
339
340 @Override
341 public Widget next() {
342 if (!hasElement || (widget == null)) {
343 throw new NoSuchElementException();
344 }
345 hasElement = false;
346 return (returned = widget);
347 }
348
349 @Override
350 public void remove() {
351 if (returned != null) {
352 Popover.this.remove(returned);
353 }
354 }
355 };
356 }
357
358 @Override
359 public boolean remove(final Widget w) {
360 // Validate.
361 if (widget != w) {
362 return false;
363 }
364
365 // Logical detach.
366 clear();
367 return true;
368 }
369
370 @Override
371 public Widget asWidget() {
372 return widget;
373 }
374
375 // @formatter:off
376 private native void bindJavaScriptEvents(final Element e) /*-{
377 var target = this;
378 var $popover = $wnd.jQuery(e);
379
380 $popover.on('show.bs.popover', function (evt) {
381 target.@org.gwtbootstrap3.client.ui.Popover::onShow(Lcom/google/gwt/user/client/Event;)(evt);
382 });
383
384 $popover.on('shown.bs.popover', function (evt) {
385 target.@org.gwtbootstrap3.client.ui.Popover::onShown(Lcom/google/gwt/user/client/Event;)(evt);
386 });
387
388 $popover.on('hide.bs.popover', function (evt) {
389 target.@org.gwtbootstrap3.client.ui.Popover::onHide(Lcom/google/gwt/user/client/Event;)(evt);
390 });
391
392 $popover.on('hidden.bs.popover', function (evt) {
393 target.@org.gwtbootstrap3.client.ui.Popover::onHidden(Lcom/google/gwt/user/client/Event;)(evt);
394 });
395 }-*/;
396
397 private native void call(final Element e, final String arg) /*-{
398 $wnd.jQuery(e).popover(arg);
399 }-*/;
400
401 private native void popover(Element e, boolean animation, boolean html, String placement, String selector,
402 String title, String content, String trigger, int showDelay, int hideDelay, String container) /*-{
403 $wnd.jQuery(e).popover({
404 animation: animation,
405 html: html,
406 placement: placement,
407 selector: selector,
408 title: title,
409 content: content,
410 trigger: trigger,
411 delay: {
412 show: showDelay,
413 hide: hideDelay
414 },
415 container: container
416 });
417 }-*/;
418
419 private native void popover(Element e, boolean animation, boolean html, String placement,
420 String title, String content, String trigger, int showDelay, int hideDelay, String container) /*-{
421 $wnd.jQuery(e).popover({
422 animation: animation,
423 html: html,
424 placement: placement,
425 title: title,
426 content: content,
427 trigger: trigger,
428 delay: {
429 show: showDelay,
430 hide: hideDelay
431 },
432 container: container
433 });
434 }-*/;
435
436 private native void popover(Element e, boolean animation, boolean html, String placement, String selector,
437 String title, String content, String trigger, int showDelay, int hideDelay) /*-{
438 $wnd.jQuery(e).popover({
439 animation: animation,
440 html: html,
441 placement: placement,
442 selector: selector,
443 title: title,
444 content: content,
445 trigger: trigger,
446 delay: {
447 show: showDelay,
448 hide: hideDelay
449 }
450 });
451 }-*/;
452
453 private native void popover(Element e, boolean animation, boolean html, String placement,
454 String title, String content, String trigger, int showDelay, int hideDelay) /*-{
455 $wnd.jQuery(e).popover({
456 animation: animation,
457 html: html,
458 placement: placement,
459 title: title,
460 content: content,
461 trigger: trigger,
462 delay: {
463 show: showDelay,
464 hide: hideDelay
465 }
466 });
467 }-*/;
468 }