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