1 /*
2  * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3  *
4  * See the LICENSE file for terms of use.
5  */
6 #include "Wt/WInteractWidget.h"
7 #include "Wt/WApplication.h"
8 #include "Wt/WEnvironment.h"
9 #include "Wt/WFormWidget.h"
10 #include "Wt/WPopupWidget.h"
11 #include "Wt/WServer.h"
12 #include "Wt/WTheme.h"
13 
14 #include "Configuration.h"
15 #include "DomElement.h"
16 
17 /*
18  * FIXME: provide a cross-browser mechanism to "cancel" the default
19  * action for a keyboard event (and other events!).
20  * Note that for key-up/key-down events, you will need to do something with
21  * the key-press event, as per http://unixpapa.com/js/key.html
22  */
23 
24 namespace Wt {
25 
26 const char *WInteractWidget::KEYDOWN_SIGNAL = "M_keydown";
27 const char *WInteractWidget::KEYPRESS_SIGNAL = "keypress";
28 const char *WInteractWidget::KEYUP_SIGNAL = "keyup";
29 const char *WInteractWidget::ENTER_PRESS_SIGNAL = "M_enterpress";
30 const char *WInteractWidget::ESCAPE_PRESS_SIGNAL = "M_escapepress";
31 const char *WInteractWidget::CLICK_SIGNAL = "click";
32 const char *WInteractWidget::M_CLICK_SIGNAL = "M_click";
33 const char *WInteractWidget::DBL_CLICK_SIGNAL = "M_dblclick";
34 const char *WInteractWidget::MOUSE_DOWN_SIGNAL = "M_mousedown";
35 const char *WInteractWidget::MOUSE_UP_SIGNAL = "M_mouseup";
36 const char *WInteractWidget::MOUSE_OUT_SIGNAL = "M_mouseout";
37 const char *WInteractWidget::MOUSE_OVER_SIGNAL = "M_mouseover";
38 const char *WInteractWidget::MOUSE_MOVE_SIGNAL = "M_mousemove";
39 const char *WInteractWidget::MOUSE_DRAG_SIGNAL = "M_mousedrag";
40 const char *WInteractWidget::MOUSE_WHEEL_SIGNAL = "mousewheel";
41 const char *WInteractWidget::WHEEL_SIGNAL = "wheel";
42 const char *WInteractWidget::TOUCH_START_SIGNAL = "touchstart";
43 const char *WInteractWidget::TOUCH_MOVE_SIGNAL = "touchmove";
44 const char *WInteractWidget::TOUCH_END_SIGNAL = "touchend";
45 const char *WInteractWidget::GESTURE_START_SIGNAL = "gesturestart";
46 const char *WInteractWidget::GESTURE_CHANGE_SIGNAL = "gesturechange";
47 const char *WInteractWidget::GESTURE_END_SIGNAL = "gestureend";
48 const char *WInteractWidget::DRAGSTART_SIGNAL = "dragstart";
49 
WInteractWidget()50 WInteractWidget::WInteractWidget()
51   : mouseOverDelay_(0)
52 { }
53 
~WInteractWidget()54 WInteractWidget::~WInteractWidget()
55 { }
56 
setPopup(bool popup)57 void WInteractWidget::setPopup(bool popup)
58 {
59   if (popup && wApp->environment().ajax()) {
60     clicked().connect
61       ("function(o,e) { "
62        " if (" WT_CLASS ".WPopupWidget && o.wtPopup) {"
63            WT_CLASS ".WPopupWidget.popupClicked = o;"
64            "$(document).trigger('click', e);"
65            WT_CLASS ".WPopupWidget.popupClicked = null;"
66        " }"
67        "}");
68     clicked().preventPropagation();
69   }
70 
71   WWebWidget::setPopup(popup);
72 }
73 
keyWentDown()74 EventSignal<WKeyEvent>& WInteractWidget::keyWentDown()
75 {
76   return *keyEventSignal(KEYDOWN_SIGNAL, true);
77 }
78 
keyPressed()79 EventSignal<WKeyEvent>& WInteractWidget::keyPressed()
80 {
81   return *keyEventSignal(KEYPRESS_SIGNAL, true);
82 }
83 
keyWentUp()84 EventSignal<WKeyEvent>& WInteractWidget::keyWentUp()
85 {
86   return *keyEventSignal(KEYUP_SIGNAL, true);
87 }
88 
enterPressed()89 EventSignal<>& WInteractWidget::enterPressed()
90 {
91   return *voidEventSignal(ENTER_PRESS_SIGNAL, true);
92 }
93 
escapePressed()94 EventSignal<>& WInteractWidget::escapePressed()
95 {
96   return *voidEventSignal(ESCAPE_PRESS_SIGNAL, true);
97 }
98 
clicked()99 EventSignal<WMouseEvent>& WInteractWidget::clicked()
100 {
101   return *mouseEventSignal(M_CLICK_SIGNAL, true);
102 }
103 
doubleClicked()104 EventSignal<WMouseEvent>& WInteractWidget::doubleClicked()
105 {
106   return *mouseEventSignal(DBL_CLICK_SIGNAL, true);
107 }
108 
mouseWentDown()109 EventSignal<WMouseEvent>& WInteractWidget::mouseWentDown()
110 {
111   return *mouseEventSignal(MOUSE_DOWN_SIGNAL, true);
112 }
113 
mouseWentUp()114 EventSignal<WMouseEvent>& WInteractWidget::mouseWentUp()
115 {
116   return *mouseEventSignal(MOUSE_UP_SIGNAL, true);
117 }
118 
mouseWentOut()119 EventSignal<WMouseEvent>& WInteractWidget::mouseWentOut()
120 {
121   return *mouseEventSignal(MOUSE_OUT_SIGNAL, true);
122 }
123 
mouseWentOver()124 EventSignal<WMouseEvent>& WInteractWidget::mouseWentOver()
125 {
126   return *mouseEventSignal(MOUSE_OVER_SIGNAL, true);
127 }
128 
mouseMoved()129 EventSignal<WMouseEvent>& WInteractWidget::mouseMoved()
130 {
131   return *mouseEventSignal(MOUSE_MOVE_SIGNAL, true);
132 }
133 
mouseDragged()134 EventSignal<WMouseEvent>& WInteractWidget::mouseDragged()
135 {
136   return *mouseEventSignal(MOUSE_DRAG_SIGNAL, true);
137 }
138 
mouseWheel()139 EventSignal<WMouseEvent>& WInteractWidget::mouseWheel()
140 {
141   if (WApplication::instance()->environment().agentIsIElt(9) ||
142       WApplication::instance()->environment().agent()
143       == UserAgent::Edge) {
144     return *mouseEventSignal(MOUSE_WHEEL_SIGNAL, true);
145   } else {
146     return *mouseEventSignal(WHEEL_SIGNAL, true);
147   }
148 }
149 
touchStarted()150 EventSignal<WTouchEvent>& WInteractWidget::touchStarted()
151 {
152   return *touchEventSignal(TOUCH_START_SIGNAL, true);
153 }
154 
touchMoved()155 EventSignal<WTouchEvent>& WInteractWidget::touchMoved()
156 {
157   return *touchEventSignal(TOUCH_MOVE_SIGNAL, true);
158 }
159 
touchEnded()160 EventSignal<WTouchEvent>& WInteractWidget::touchEnded()
161 {
162   return *touchEventSignal(TOUCH_END_SIGNAL, true);
163 }
164 
gestureStarted()165 EventSignal<WGestureEvent>& WInteractWidget::gestureStarted()
166 {
167   return *gestureEventSignal(GESTURE_START_SIGNAL, true);
168 }
169 
gestureChanged()170 EventSignal<WGestureEvent>& WInteractWidget::gestureChanged()
171 {
172   return *gestureEventSignal(GESTURE_CHANGE_SIGNAL, true);
173 }
174 
gestureEnded()175 EventSignal<WGestureEvent>& WInteractWidget::gestureEnded()
176 {
177   return *gestureEventSignal(GESTURE_END_SIGNAL, true);
178 }
179 
updateDom(DomElement & element,bool all)180 void WInteractWidget::updateDom(DomElement& element, bool all)
181 {
182   bool updateKeyDown = false;
183 
184   WApplication *app = WApplication::instance();
185 
186   /*
187    * -- combine enterPress, escapePress and keyDown signals
188    */
189   EventSignal<> *enterPress = voidEventSignal(ENTER_PRESS_SIGNAL, false);
190   EventSignal<> *escapePress = voidEventSignal(ESCAPE_PRESS_SIGNAL, false);
191   EventSignal<WKeyEvent> *keyDown = keyEventSignal(KEYDOWN_SIGNAL, false);
192 
193   updateKeyDown = (enterPress && enterPress->needsUpdate(all))
194     || (escapePress && escapePress->needsUpdate(all))
195     || (keyDown && keyDown->needsUpdate(all));
196 
197   if (updateKeyDown) {
198     std::vector<DomElement::EventAction> actions;
199 
200     if (enterPress) {
201       if (enterPress->needsUpdate(true)) {
202 	/*
203 	 * prevent enterPressed from triggering a changed event on all
204 	 * browsers except for Opera and IE
205 	 */
206 	std::string extraJS;
207 
208 	const WEnvironment& env = app->environment();
209 
210 	if (dynamic_cast<WFormWidget *>(this)
211 	    && !env.agentIsOpera() && !env.agentIsIE())
212 	  extraJS = "var g=this.onchange;"
213 	    ""      "this.onchange=function(){this.onchange=g;};";
214 
215 	actions.push_back
216 	  (DomElement::EventAction("e.keyCode && (e.keyCode == 13)",
217 				   enterPress->javaScript() + extraJS,
218 				   enterPress->encodeCmd(),
219 				   enterPress->isExposedSignal()));
220       }
221       enterPress->updateOk();
222     }
223 
224     if (escapePress) {
225       if (escapePress->needsUpdate(true)) {
226 	actions.push_back
227 	  (DomElement::EventAction("e.keyCode && (e.keyCode == 27)",
228 				   escapePress->javaScript(),
229 				   escapePress->encodeCmd(),
230 				   escapePress->isExposedSignal()));
231       }
232       escapePress->updateOk();
233     }
234 
235     if (keyDown) {
236       if (keyDown->needsUpdate(true)) {
237 	actions.push_back
238 	  (DomElement::EventAction(std::string(),
239 				   keyDown->javaScript(),
240 				   keyDown->encodeCmd(),
241 				   keyDown->isExposedSignal()));
242       }
243       keyDown->updateOk();
244     }
245 
246     if (!actions.empty())
247       element.setEvent("keydown", actions);
248     else if (!all)
249       element.setEvent("keydown", std::string(), std::string());
250   }
251 
252   /*
253    * -- allow computation of dragged mouse distance
254    */
255   EventSignal<WMouseEvent> *mouseDown
256     = mouseEventSignal(MOUSE_DOWN_SIGNAL, false);
257   EventSignal<WMouseEvent> *mouseUp
258     = mouseEventSignal(MOUSE_UP_SIGNAL, false);
259   EventSignal<WMouseEvent> *mouseMove
260     = mouseEventSignal(MOUSE_MOVE_SIGNAL, false);
261   EventSignal<WMouseEvent> *mouseDrag
262     = mouseEventSignal(MOUSE_DRAG_SIGNAL, false);
263 
264   bool updateMouseMove
265     = (mouseMove && mouseMove->needsUpdate(all))
266     || (mouseDrag && mouseDrag->needsUpdate(all));
267 
268   bool updateMouseDown
269     = (mouseDown && mouseDown->needsUpdate(all))
270     || updateMouseMove;
271 
272   bool updateMouseUp
273     = (mouseUp && mouseUp->needsUpdate(all))
274     || updateMouseMove;
275 
276   std::string CheckDisabled = "if($(o).hasClass('" +
277     app->theme()->disabledClass() +
278     "')){" WT_CLASS ".cancelEvent(e);return;}";
279 
280   if (updateMouseDown) {
281     /*
282      * when we have a mouseUp event, we also need a mouseDown event
283      * to be able to compute dragDX/Y.
284      *
285      * When we have:
286      *  - a mouseDrag
287      *  - or a mouseDown + (mouseMove or mouseUp),
288      * we need to capture everything after on mouse down, and keep track of the
289      * down button if we have a mouseMove or mouseDrag
290      */
291     WStringStream js;
292 
293     js << CheckDisabled;
294 
295     if (mouseUp && mouseUp->isConnected())
296       js << app->javaScriptClass() << "._p_.saveDownPos(event);";
297 
298     if ((mouseDrag && mouseDrag->isConnected())
299 	|| (mouseDown && mouseDown->isConnected()
300 	    && ((mouseUp && mouseUp->isConnected())
301 		|| (mouseMove && mouseMove->isConnected()))))
302       js << WT_CLASS ".capture(this);";
303 
304     if ((mouseMove && mouseMove->isConnected())
305 	|| (mouseDrag && mouseDrag->isConnected()))
306       js << WT_CLASS ".mouseDown(e);";
307 
308     if (mouseDown) {
309       js << mouseDown->javaScript();
310       element.setEvent("mousedown", js.str(),
311 		       mouseDown->encodeCmd(), mouseDown->isExposedSignal());
312       mouseDown->updateOk();
313     } else
314       element.setEvent("mousedown", js.str(), std::string(), false);
315   }
316 
317   if (updateMouseUp) {
318     WStringStream js;
319 
320     /*
321      * when we have a mouseMove or mouseDrag event, we need to keep track
322      * of unpressing the button.
323      */
324     js << CheckDisabled;
325 
326     if ((mouseMove && mouseMove->isConnected())
327 	|| (mouseDrag && mouseDrag->isConnected()))
328       js << WT_CLASS ".mouseUp(e);";
329 
330     if (mouseUp) {
331       js << mouseUp->javaScript();
332       element.setEvent("mouseup", js.str(),
333 		       mouseUp->encodeCmd(), mouseUp->isExposedSignal());
334       mouseUp->updateOk();
335     } else
336       element.setEvent("mouseup", js.str(), std::string(), false);
337   }
338 
339   if (updateMouseMove) {
340     /*
341      * We need to mix mouseDrag and mouseMove events.
342      */
343     std::vector<DomElement::EventAction> actions;
344 
345     if (mouseMove) {
346       actions.push_back
347 	(DomElement::EventAction(std::string(),
348 				 mouseMove->javaScript(),
349 				 mouseMove->encodeCmd(),
350 				 mouseMove->isExposedSignal()));
351       mouseMove->updateOk();
352     }
353 
354     if (mouseDrag) {
355       actions.push_back
356 	(DomElement::EventAction(WT_CLASS ".buttons",
357 				 mouseDrag->javaScript()
358 				 + WT_CLASS ".drag(e);",
359 				 mouseDrag->encodeCmd(),
360 				 mouseDrag->isExposedSignal()));
361       mouseDrag->updateOk();
362     }
363 
364     element.setEvent("mousemove", actions);
365   }
366   /*
367    * -- allow computation of dragged touch distance
368    */
369   EventSignal<WTouchEvent> *touchStart
370     = touchEventSignal(TOUCH_START_SIGNAL, false);
371   EventSignal<WTouchEvent> *touchEnd
372     = touchEventSignal(TOUCH_END_SIGNAL, false);
373   EventSignal<WTouchEvent> *touchMove
374     = touchEventSignal(TOUCH_MOVE_SIGNAL, false);
375 
376   bool updateTouchMove
377     = (touchMove && touchMove->needsUpdate(all));
378 
379   bool updateTouchStart
380     = (touchStart && touchStart->needsUpdate(all))
381     || updateTouchMove;
382 
383   bool updateTouchEnd
384     = (touchEnd && touchEnd->needsUpdate(all))
385     || updateTouchMove;
386 
387   if (updateTouchStart) {
388     /*
389      * when we have a touchStart event, we also need a touchEnd event
390      * to be able to compute dragDX/Y.
391      *
392      * When we have:
393      *  - a touchStart + (touchMove or touchEnd),
394      * we need to capture everything after on touch start, and keep track of the
395      * down button if we have a touchMove
396      */
397     WStringStream js;
398 
399     js << CheckDisabled;
400 
401     if (touchEnd && touchEnd->isConnected())
402       js << app->javaScriptClass() << "._p_.saveDownPos(event);";
403 
404     if ((touchStart && touchStart->isConnected()
405 	    && ((touchEnd && touchEnd->isConnected())
406 		|| (touchMove && touchMove->isConnected()))))
407       js << WT_CLASS ".capture(this);";
408 
409     if (touchStart) {
410       js << touchStart->javaScript();
411       element.setEvent("touchstart", js.str(),
412 		       touchStart->encodeCmd(), touchStart->isExposedSignal());
413       touchStart->updateOk();
414     } else
415       element.setEvent("touchstart", js.str(), std::string(), false);
416   }
417 
418   if (updateTouchEnd) {
419     WStringStream js;
420 
421     /*
422      * when we have a touchMove, we need to keep track
423      * of removing touch.
424      */
425     js << CheckDisabled;
426 
427     if (touchEnd) {
428       js << touchEnd->javaScript();
429       element.setEvent("touchend", js.str(),
430 		         touchEnd->encodeCmd(), touchEnd->isExposedSignal());
431       touchEnd->updateOk();
432     } else
433       element.setEvent("touchend", js.str(), std::string(), false);
434   }
435 
436   if (updateTouchMove) {
437 
438     if (touchMove) {
439       element.setEvent("touchmove", touchMove->javaScript(),
440 			touchMove->encodeCmd(), touchMove->isExposedSignal());
441       touchMove->updateOk();
442     }
443   }
444 
445   /*
446    * -- mix mouseClick and mouseDblClick events in mouseclick since we
447    *    only want to fire one of both
448    */
449   EventSignal<WMouseEvent> *mouseClick
450     = mouseEventSignal(M_CLICK_SIGNAL, false);
451   EventSignal<WMouseEvent> *mouseDblClick
452     = mouseEventSignal(DBL_CLICK_SIGNAL, false);
453 
454   bool updateMouseClick
455     = (mouseClick && mouseClick->needsUpdate(all))
456     || (mouseDblClick && mouseDblClick->needsUpdate(all));
457 
458   if (updateMouseClick) {
459     WStringStream js;
460 
461     js << CheckDisabled;
462 
463     if (mouseDrag)
464       js << "if (" WT_CLASS ".dragged()) return;";
465 
466     if (mouseDblClick && mouseDblClick->needsUpdate(all)) {
467       /*
468        * Click: if timer is running:
469        *  - clear timer, dblClick()
470        *  - start timer, clear timer and click()
471        */
472 
473       /* We have to prevent this immediately ! */
474       if (mouseClick) {
475 	if (mouseClick->defaultActionPrevented() ||
476 	    mouseClick->propagationPrevented()) {
477 	  js << WT_CLASS ".cancelEvent(e";
478 	  if (mouseClick->defaultActionPrevented() &&
479 	      mouseClick->propagationPrevented())
480 	    js << ");";
481 	  else if (mouseClick->defaultActionPrevented())
482 	    js << ",0x2);";
483 	  else
484 	    js << ",0x1);";
485 	}
486       }
487 
488       js << "if(" WT_CLASS ".isDblClick(o, e)) {"
489 	 << mouseDblClick->javaScript();
490 
491       if (mouseDblClick->isExposedSignal())
492 	js << app->javaScriptClass()
493 		 << "._p_.update(o,'" << mouseDblClick->encodeCmd()
494 		 << "',e,true);";
495 
496       mouseDblClick->updateOk();
497 
498       js <<
499 	"}else{"
500 	"""if (" WT_CLASS ".isIElt9 && document.createEventObject) "
501 	""  "e = document.createEventObject(e);"
502 	"""o.wtE1 = e;"
503 	"""o.wtClickTimeout = setTimeout(function() {"
504 	""   "o.wtClickTimeout = null; o.wtE1 = null;";
505 
506       if (mouseClick) {
507 	js << mouseClick->javaScript();
508 
509 	if (mouseClick->isExposedSignal()) {
510 	  js << app->javaScriptClass()
511 		   << "._p_.update(o,'" << mouseClick->encodeCmd()
512 		   << "',e,true);";
513 	}
514 
515 	mouseClick->updateOk();
516       }
517 
518       const Configuration& conf = app->environment().server()->configuration();
519       js << "}," << conf.doubleClickTimeout() << ");}";
520     } else {
521       if (mouseClick && mouseClick->needsUpdate(all)) {
522 	js << mouseClick->javaScript();
523 
524 	if (mouseClick->isExposedSignal()) {
525 	  js << app->javaScriptClass()
526 	     << "._p_.update(o,'" << mouseClick->encodeCmd()
527 	     << "',e,true);";
528 	}
529 
530 	mouseClick->updateOk();
531       }
532     }
533 
534     element.setEvent(CLICK_SIGNAL, js.str(),
535 		     mouseClick ? mouseClick->encodeCmd() : "");
536 
537     if (mouseDblClick) {
538       if (app->environment().agentIsIElt(9))
539 	element.setEvent("dblclick", "this.onclick()");
540     }
541   }
542 
543   /*
544    * -- mouseOver with delay
545    */
546   EventSignal<WMouseEvent> *mouseOver
547     = mouseEventSignal(MOUSE_OVER_SIGNAL, false);
548   EventSignal<WMouseEvent> *mouseOut
549     = mouseEventSignal(MOUSE_OUT_SIGNAL, false);
550 
551   bool updateMouseOver = mouseOver && mouseOver->needsUpdate(all);
552 
553   if (mouseOverDelay_) {
554     if (updateMouseOver) {
555       WStringStream js;
556       js << "o.over=setTimeout(function() {"
557 	 << "o.over = null;"
558 	 << mouseOver->javaScript();
559 
560       if (mouseOver->isExposedSignal()) {
561 	js << app->javaScriptClass()
562 	   << "._p_.update(o,'" << mouseOver->encodeCmd() << "',e,true);";
563       }
564 
565       js << "}," << mouseOverDelay_ << ");";
566 
567       element.setEvent("mouseover", js.str(), "");
568 
569       mouseOver->updateOk();
570 
571       if (!mouseOut)
572 	mouseOut = mouseEventSignal(MOUSE_OUT_SIGNAL, true);
573 
574       element.setEvent("mouseout",
575 		       "clearTimeout(o.over); o.over=null;"
576 		       + mouseOut->javaScript(),
577 		       mouseOut->encodeCmd(), mouseOut->isExposedSignal());
578       mouseOut->updateOk();
579     }
580   } else {
581     if (updateMouseOver) {
582       element.setEventSignal("mouseover", *mouseOver);
583       mouseOver->updateOk();
584     }
585 
586     bool updateMouseOut = mouseOut && mouseOut->needsUpdate(all);
587 
588     if (updateMouseOut) {
589       element.setEventSignal("mouseout", *mouseOut);
590       mouseOut->updateOk();
591     }
592   }
593 
594   EventSignal<> *dragStart = voidEventSignal(DRAGSTART_SIGNAL, false);
595   if (dragStart && dragStart->needsUpdate(all)) {
596     element.setEventSignal("dragstart", *dragStart);
597     dragStart->updateOk();
598   }
599 
600   updateEventSignals(element, all);
601 
602   WWebWidget::updateDom(element, all);
603 }
604 
setMouseOverDelay(int delay)605 void WInteractWidget::setMouseOverDelay(int delay)
606 {
607   mouseOverDelay_ = delay;
608 
609   EventSignal<WMouseEvent> *mouseOver
610     = mouseEventSignal(MOUSE_OVER_SIGNAL, false);
611   if (mouseOver)
612     mouseOver->ownerRepaint();
613 }
614 
mouseOverDelay()615 int WInteractWidget::mouseOverDelay() const
616 {
617   return mouseOverDelay_;
618 }
619 
updateEventSignals(DomElement & element,bool all)620 void WInteractWidget::updateEventSignals(DomElement& element, bool all)
621 {
622   EventSignalList& other = eventSignals();
623 
624   for (EventSignalList::iterator i = other.begin(); i != other.end(); ++i) {
625     EventSignalBase& s = **i;
626 
627     if (s.name() == WInteractWidget::M_CLICK_SIGNAL
628 	&& flags_.test(BIT_REPAINT_TO_AJAX))
629       element.unwrap();
630 
631     updateSignalConnection(element, s, s.name(), all);
632   }
633 }
634 
propagateRenderOk(bool deep)635 void WInteractWidget::propagateRenderOk(bool deep)
636 {
637   EventSignalList& other = eventSignals();
638 
639   for (EventSignalList::iterator i = other.begin(); i != other.end(); ++i) {
640     EventSignalBase& s = **i;
641     s.updateOk();
642   }
643 
644   WWebWidget::propagateRenderOk(deep);
645 }
646 
load()647 void WInteractWidget::load()
648 {
649   if (!isDisabled()) {
650     if (parent())
651       flags_.set(BIT_ENABLED, parent()->isEnabled());
652     else
653       flags_.set(BIT_ENABLED, true);
654   } else
655     flags_.set(BIT_ENABLED, false);
656 
657   WWebWidget::load();
658 }
659 
isEnabled()660 bool WInteractWidget::isEnabled() const
661 {
662   return !isDisabled() && flags_.test(BIT_ENABLED);
663 }
664 
propagateSetEnabled(bool enabled)665 void WInteractWidget::propagateSetEnabled(bool enabled)
666 {
667   flags_.set(BIT_ENABLED, enabled);
668 
669   WApplication *app = WApplication::instance();
670   std::string disabledClass = app->theme()->disabledClass();
671   toggleStyleClass(disabledClass, !enabled, true);
672 
673   WWebWidget::propagateSetEnabled(enabled);
674 }
675 
setDraggable(const std::string & mimeType,WWidget * dragWidget,bool isDragWidgetOnly,WObject * sourceObject)676 void WInteractWidget::setDraggable(const std::string& mimeType,
677 				   WWidget *dragWidget, bool isDragWidgetOnly,
678 				   WObject *sourceObject)
679 {
680   if (dragWidget == nullptr)
681     dragWidget = this;
682 
683   if (sourceObject == nullptr)
684     sourceObject = this;
685 
686   if (isDragWidgetOnly) {
687     dragWidget->hide();
688   }
689 
690   WApplication *app = WApplication::instance();
691 
692   setAttributeValue("dmt", mimeType);
693   setAttributeValue("dwid", dragWidget->id());
694   setAttributeValue("dsid", app->encodeObject(sourceObject));
695 
696   if (!dragSlot_) {
697     dragSlot_.reset(new JSlot());
698     dragSlot_->setJavaScript("function(o,e){" + app->javaScriptClass()
699 			     + "._p_.dragStart(o,e);" + "}");
700   }
701 
702   if (!dragTouchSlot_) {
703     dragTouchSlot_.reset(new JSlot());
704     dragTouchSlot_->setJavaScript("function(o,e){" + app->javaScriptClass()
705 				+ "._p_.touchStart(o,e);" + "}");
706   }
707 
708   if (!dragTouchEndSlot_) {
709     dragTouchEndSlot_.reset(new JSlot());
710     dragTouchEndSlot_->setJavaScript("function(){" + app->javaScriptClass()
711 				+ "._p_.touchEnded();" + "}");
712   }
713 
714   voidEventSignal(DRAGSTART_SIGNAL, true)->preventDefaultAction(true);
715 
716   mouseWentDown().connect(*dragSlot_);
717   touchStarted().connect(*dragTouchSlot_);
718   touchStarted().preventDefaultAction(true);
719   touchEnded().connect(*dragTouchEndSlot_);
720 }
721 
unsetDraggable()722 void WInteractWidget::unsetDraggable()
723 {
724   if (dragSlot_) {
725     mouseWentDown().disconnect(*dragSlot_);
726     dragSlot_.reset();
727   }
728 
729   if (dragTouchSlot_) {
730     touchStarted().disconnect(*dragTouchSlot_);
731     dragTouchSlot_.reset();
732   }
733 
734   if (dragTouchEndSlot_) {
735     touchEnded().disconnect(*dragTouchEndSlot_);
736     dragTouchEndSlot_.reset();
737   }
738 
739   EventSignal<> *dragStart = voidEventSignal(DRAGSTART_SIGNAL, false);
740   if (dragStart) {
741     dragStart->preventDefaultAction(false);
742   }
743 }
744 
745 }
746