1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include "StdWidgetItemImpl.h"
7 #include "StdGridLayoutImpl2.h"
8
9 #include "Wt/WApplication.h"
10 #include "Wt/WBorderLayout.h"
11 #include "Wt/WBoxLayout.h"
12 #include "Wt/WContainerWidget.h"
13 #include "Wt/WFitLayout.h"
14 #include "Wt/WGridLayout.h"
15 #include "Wt/WWidgetItem.h"
16 #include "WebUtils.h"
17
18 #include "DomElement.h"
19 #include "WebSession.h"
20
21 #include <boost/algorithm/string.hpp>
22
23 namespace Wt {
24
25 LOGGER("WContainerWidget");
26
27 const char *WContainerWidget::SCROLL_SIGNAL = "scroll";
28
WContainerWidget()29 WContainerWidget::WContainerWidget()
30 : contentAlignment_(AlignmentFlag::Left),
31 overflow_(nullptr),
32 padding_(nullptr),
33 globalUnfocused_(false),
34 scrollTop_(0),
35 scrollLeft_(0)
36 {
37 setInline(false);
38 setLoadLaterWhenInvisible(false);
39 }
40
scrolled()41 EventSignal<WScrollEvent>& WContainerWidget::scrolled()
42 {
43 return *scrollEventSignal(SCROLL_SIGNAL, true);
44 }
45
~WContainerWidget()46 WContainerWidget::~WContainerWidget()
47 {
48 beingDeleted();
49 clear();
50
51 delete[] padding_;
52 delete[] overflow_;
53 }
54
55 #ifndef WT_NO_LAYOUT
layoutImpl()56 StdLayoutImpl *WContainerWidget::layoutImpl() const
57 {
58 return dynamic_cast<StdLayoutImpl *>(layout_->impl());
59 }
60 #endif // WT_NO_LAYOUT
61
setLayout(std::unique_ptr<WLayout> layout)62 void WContainerWidget::setLayout(std::unique_ptr<WLayout> layout)
63 {
64 #ifndef WT_NO_LAYOUT
65 // make sure old layout is deleted first, std::unique_ptr assignment
66 // changes the value of the pointer and then deletes the old value, but
67 // we need to delete the old value first, otherwise we run into problems
68 // FIXME: maybe the code should be fixed so this is not necessary? Having
69 // to call reset() first feels dirty
70 clear();
71
72 layout_ = std::move(layout);
73 if (layout_)
74 layout_->setParentWidget(this);
75 contentAlignment_ = AlignmentFlag::Justify;
76 flags_.set(BIT_LAYOUT_NEEDS_RERENDER);
77 repaint();
78 #else
79 assert(false);
80 #endif
81 }
82
childResized(WWidget * child,WFlags<Orientation> directions)83 void WContainerWidget::childResized(WWidget *child,
84 WFlags<Orientation> directions)
85 {
86 #ifndef WT_NO_LAYOUT
87 if (layout_) {
88 WWidgetItem *item = layout_->findWidgetItem(child);
89 if (item) {
90 if (dynamic_cast<StdLayoutImpl *>(item->parentLayout()->impl())
91 ->itemResized(item)) {
92 flags_.set(BIT_LAYOUT_NEEDS_UPDATE);
93 repaint();
94 }
95 }
96 } else
97 WInteractWidget::childResized(child, directions);
98 #endif
99 }
100
parentResized(WWidget * parent,WFlags<Orientation> directions)101 void WContainerWidget::parentResized(WWidget *parent,
102 WFlags<Orientation> directions)
103 {
104 #ifndef WT_NO_LAYOUT
105 if (layout_) {
106 if (dynamic_cast<StdLayoutImpl *>(layout_->impl())->parentResized()) {
107 flags_.set(BIT_LAYOUT_NEEDS_UPDATE);
108 repaint();
109 }
110 } else
111 WInteractWidget::parentResized(parent, directions);
112 #endif
113 }
114
addWidget(std::unique_ptr<WWidget> widget)115 void WContainerWidget::addWidget(std::unique_ptr<WWidget> widget)
116 {
117 insertWidget(children_.size(), std::move(widget));
118 }
119
insertBefore(std::unique_ptr<WWidget> widget,WWidget * before)120 void WContainerWidget::insertBefore(std::unique_ptr<WWidget> widget,
121 WWidget *before)
122 {
123 int index = indexOf(before);
124
125 if (index == -1) {
126 LOG_ERROR("insertBefore(): before is not in container, appending at back");
127 index = children_.size();
128 }
129
130 insertWidget(index, std::move(widget));
131 }
132
insertWidget(int index,std::unique_ptr<WWidget> widget)133 void WContainerWidget::insertWidget(int index, std::unique_ptr<WWidget> widget)
134 {
135 WWidget *w = widget.get();
136
137 if (!addedChildren_) {
138 addedChildren_.reset(new std::vector<WWidget *>());
139
140 // A TD/TH node cannot be stubbed
141 if (domElementType() != DomElementType::TD &&
142 domElementType() != DomElementType::TH)
143 setLoadLaterWhenInvisible(true);
144 }
145
146 addedChildren_->push_back(widget.get());
147 children_.insert(children_.begin() + index, widget.get());
148 #ifndef WT_TARGET_JAVA
149 addChild(std::move(widget));
150 #endif // WT_TARGET_JAVA
151 flags_.set(BIT_ADJUST_CHILDREN_ALIGN); // children margins hacks
152 repaint(RepaintFlag::SizeAffected);
153
154 widgetAdded(w);
155 }
156
removeWidget(WWidget * widget)157 std::unique_ptr<WWidget> WContainerWidget::removeWidget(WWidget *widget)
158 {
159 #ifndef WT_NO_LAYOUT
160 if (layout_) {
161 auto result = layout_->removeWidget(widget);
162 if (result)
163 widgetRemoved(result.get(), false);
164 return result;
165 }
166 #endif // WT_NO_LAYOUT
167
168 int index = indexOf(widget);
169
170 if (index != -1) {
171 bool renderRemove = true;
172
173 if (addedChildren_ && Utils::erase(*addedChildren_, widget))
174 renderRemove = false;
175
176 children_.erase(children_.begin() + index);
177
178 // NOTE: result may be null if the widget is not owned by this WContainerWidget!
179 #ifndef WT_TARGET_JAVA
180 std::unique_ptr<WWidget> result = removeChild(widget);
181 #else // WT_TARGET_JAVA
182 std::unique_ptr<WWidget> result = std::unique_ptr<WWidget>(widget);
183 #endif // WT_TARGET_JAVA
184
185 repaint(RepaintFlag::SizeAffected);
186
187 widgetRemoved(widget, renderRemove);
188
189 return result;
190 } else {
191 LOG_ERROR("removeWidget(): widget not in container");
192 return std::unique_ptr<WWidget>();
193 }
194 }
195
clear()196 void WContainerWidget::clear()
197 {
198 #ifndef WT_NO_LAYOUT
199 layout_.reset();
200 #endif
201
202 while (!children_.empty())
203 removeWidget(children_.back());
204 }
205
iterateChildren(const HandleWidgetMethod & method)206 void WContainerWidget::iterateChildren(const HandleWidgetMethod& method) const
207 {
208 // It's possible that a child is added during iteration,
209 // e.g. when load() is called on a widget that adds a
210 // new global widget to the domroot. This would invalidate
211 // the iterator. That's why we're iterating over children_
212 // the old school way, with an index. Then there's no iterator
213 // that ends up invalidated.
214 for (std::size_t i = 0;
215 i < children_.size(); ++i)
216 #ifndef WT_TARGET_JAVA
217 method(children_[i]);
218 #else
219 method.handle(children_[i]);
220 #endif
221
222 if (layout_)
223 layout_->iterateWidgets(method);
224 }
225
indexOf(WWidget * widget)226 int WContainerWidget::indexOf(WWidget *widget) const
227 {
228 for (unsigned i = 0; i < children_.size(); ++i)
229 if (children_[i] == widget)
230 return i;
231
232 return -1;
233 }
234
widget(int index)235 WWidget *WContainerWidget::widget(int index) const
236 {
237 return children_[index];
238 }
239
count()240 int WContainerWidget::count() const
241 {
242 return children_.size();
243 }
244
setContentAlignment(WFlags<AlignmentFlag> alignment)245 void WContainerWidget::setContentAlignment(WFlags<AlignmentFlag> alignment)
246 {
247 contentAlignment_ = alignment;
248
249 /* Make sure vertical alignment is always specified */
250 AlignmentFlag vAlign = contentAlignment_ & AlignVerticalMask;
251 if (vAlign == static_cast<AlignmentFlag>(0))
252 contentAlignment_ |= AlignmentFlag::Top;
253
254 flags_.set(BIT_CONTENT_ALIGNMENT_CHANGED);
255
256 repaint();
257 }
258
setList(bool list,bool ordered)259 void WContainerWidget::setList(bool list, bool ordered)
260 {
261 flags_.set(BIT_LIST, list);
262 flags_.set(BIT_ORDERED_LIST, ordered);
263 }
264
isList()265 bool WContainerWidget::isList() const
266 {
267 return flags_.test(BIT_LIST);
268 }
269
isOrderedList()270 bool WContainerWidget::isOrderedList() const
271 {
272 return flags_.test(BIT_LIST) && flags_.test(BIT_ORDERED_LIST);
273 }
274
isUnorderedList()275 bool WContainerWidget::isUnorderedList() const
276 {
277 return flags_.test(BIT_LIST) && !flags_.test(BIT_ORDERED_LIST);
278 }
279
setPadding(const WLength & length,WFlags<Side> sides)280 void WContainerWidget::setPadding(const WLength& length, WFlags<Side> sides)
281 {
282 if (!padding_) {
283 padding_ = new WLength[4];
284 #ifdef WT_TARGET_JAVA
285 padding_[0] = padding_[1] = padding_[2] = padding_[3] = WLength::Auto;
286 #endif // WT_TARGET_JAVA
287 }
288
289 if (sides.test(Side::Top))
290 padding_[0] = length;
291 if (sides.test(Side::Right))
292 padding_[1] = length;
293 if (sides.test(Side::Bottom))
294 padding_[2] = length;
295 if (sides.test(Side::Left))
296 padding_[3] = length;
297
298 flags_.set(BIT_PADDINGS_CHANGED);
299 repaint(RepaintFlag::SizeAffected);
300 }
301
setOverflow(Overflow value,WFlags<Orientation> orientation)302 void WContainerWidget::setOverflow(Overflow value,
303 WFlags<Orientation> orientation)
304 {
305 if (!overflow_) {
306 overflow_ = new Overflow[2];
307 overflow_[0] = overflow_[1] = Overflow::Visible;
308 }
309
310 if (orientation.test(Orientation::Horizontal))
311 overflow_[0] = value;
312 if (orientation.test(Orientation::Vertical))
313 overflow_[1] = value;
314
315 // Could be a workaround for IE, but sometimes causes other problems:
316 // if (value == OverflowScroll || value == OverflowAuto)
317 // setPositionScheme(PositionScheme::Relative);
318
319 flags_.set(BIT_OVERFLOW_CHANGED);
320 repaint();
321 }
322
padding(Side side)323 WLength WContainerWidget::padding(Side side) const
324 {
325 if (!padding_)
326 return WLength::Auto;
327
328 switch (side) {
329 case Side::Top:
330 return padding_[0];
331 case Side::Right:
332 return padding_[1];
333 case Side::Bottom:
334 return padding_[2];
335 case Side::Left:
336 return padding_[3];
337 default:
338 LOG_ERROR("padding(): improper side.");
339 return WLength();
340 }
341 }
342
updateDom(DomElement & element,bool all)343 void WContainerWidget::updateDom(DomElement& element, bool all)
344 {
345 element.setGlobalUnfocused(globalUnfocused_);
346 if (all && element.type() == DomElementType::LI && isInline())
347 element.setProperty(Property::StyleDisplay, "inline");
348
349 if (flags_.test(BIT_CONTENT_ALIGNMENT_CHANGED) || all) {
350 AlignmentFlag hAlign = contentAlignment_ & AlignHorizontalMask;
351
352 bool ltr = WApplication::instance()->layoutDirection()
353 == LayoutDirection::LeftToRight;
354
355 switch (hAlign) {
356 case AlignmentFlag::Left:
357 if (flags_.test(BIT_CONTENT_ALIGNMENT_CHANGED))
358 element.setProperty(Property::StyleTextAlign, ltr ? "left" : "right");
359 break;
360 case AlignmentFlag::Right:
361 element.setProperty(Property::StyleTextAlign, ltr ? "right" : "left");
362 break;
363 case AlignmentFlag::Center:
364 element.setProperty(Property::StyleTextAlign, "center");
365 break;
366 case AlignmentFlag::Justify:
367 #ifndef WT_NO_LAYOUT
368 if (!layout_)
369 #endif // WT_NO_LAYOUT
370 element.setProperty(Property::StyleTextAlign, "justify");
371 break;
372 default:
373 break;
374 }
375
376 if (domElementType() == DomElementType::TD) {
377 AlignmentFlag vAlign = contentAlignment_ & AlignVerticalMask;
378 switch (vAlign) {
379 case AlignmentFlag::Top:
380 if (flags_.test(BIT_CONTENT_ALIGNMENT_CHANGED))
381 element.setProperty(Property::StyleVerticalAlign, "top");
382 break;
383 case AlignmentFlag::Middle:
384 element.setProperty(Property::StyleVerticalAlign, "middle");
385 break;
386 case AlignmentFlag::Bottom:
387 element.setProperty(Property::StyleVerticalAlign, "bottom");
388 default:
389 break;
390 }
391 }
392 }
393
394 if (flags_.test(BIT_ADJUST_CHILDREN_ALIGN) ||
395 flags_.test(BIT_CONTENT_ALIGNMENT_CHANGED) || all) {
396 /*
397 * Welcome to CSS hell.
398 *
399 * Apparently, the text-align property only applies to inline elements.
400 * To center non-inline children, the standard says to set its left and
401 * right margin to 'auto'.
402 *
403 * I assume the same applies for aligning to the right ?
404 */
405 for (unsigned i = 0; i < children_.size(); ++i) {
406 WWidget *child = children_[i];
407
408 if (!child->isInline()) {
409 AlignmentFlag ha = contentAlignment_ & AlignHorizontalMask;
410 if (ha == AlignmentFlag::Center) {
411 if (!child->margin(Side::Left).isAuto())
412 child->setMargin(WLength::Auto, Side::Left);
413 if (!child->margin(Side::Right).isAuto())
414 child->setMargin(WLength::Auto, Side::Right);
415 } else if (ha == AlignmentFlag::Right) {
416 if (!child->margin(Side::Left).isAuto())
417 child->setMargin(WLength::Auto, Side::Left);
418 }
419 }
420 }
421
422 flags_.reset(BIT_CONTENT_ALIGNMENT_CHANGED);
423 flags_.reset(BIT_ADJUST_CHILDREN_ALIGN);
424 }
425
426 if (flags_.test(BIT_PADDINGS_CHANGED)
427 || (all && padding_ &&
428 !( padding_[0].isAuto() && padding_[1].isAuto()
429 && padding_[2].isAuto() && padding_[3].isAuto()))) {
430
431 if ((padding_[0] == padding_[1]) && (padding_[0] == padding_[2])
432 && (padding_[0] == padding_[3]))
433 element.setProperty(Property::StylePadding, padding_[0].cssText());
434 else {
435 WStringStream s;
436 for (unsigned i = 0; i < 4; ++i) {
437 if (i != 0)
438 s << ' ';
439 s << (padding_[i].isAuto() ? "0" : padding_[i].cssText());
440 }
441 element.setProperty(Property::StylePadding, s.str());
442 }
443
444 flags_.reset(BIT_PADDINGS_CHANGED);
445 }
446
447 WInteractWidget::updateDom(element, all);
448
449 if (flags_.test(BIT_OVERFLOW_CHANGED) ||
450 (all && overflow_ &&
451 !(overflow_[0] == Overflow::Visible &&
452 overflow_[1] == Overflow::Visible))) {
453 static const char *cssText[] = { "visible", "auto", "hidden", "scroll" };
454
455 element.setProperty(Property::StyleOverflowX,
456 cssText[static_cast<unsigned int>(overflow_[0])]);
457 element.setProperty(Property::StyleOverflowY,
458 cssText[static_cast<unsigned int>(overflow_[1])]);
459 // enable form object to retrieve scroll state
460 setFormObject(true);
461
462 //declare javascript function Wt.encodeValue()
463 this->doJavaScript(this->jsRef()
464 + ".wtEncodeValue = function() {"
465 + "return " + this->jsRef() + ".scrollTop"
466 + " + ';' + " + this->jsRef() + ".scrollLeft;"
467 + "};");
468
469 flags_.reset(BIT_OVERFLOW_CHANGED);
470
471 /* If a container widget has overflow, then, if ever something
472 * inside it has position scheme relative/absolute, it will not
473 * scroll properly unless every element up to the container and including
474 * the container itself has overflow: relative.
475 *
476 * The following fixes the common case:
477 * container (overflow) - container - layout
478 */
479 WApplication *app = WApplication::instance();
480 if (app->environment().agentIsIE()
481 && (overflow_[0] == Overflow::Auto ||
482 overflow_[0] == Overflow::Scroll))
483 if (positionScheme() == PositionScheme::Static)
484 element.setProperty(Property::StylePosition, "relative");
485 }
486 }
487
firstChildIndex()488 int WContainerWidget::firstChildIndex() const
489 {
490 return 0;
491 }
492
propagateLayoutItemsOk(WLayoutItem * item)493 void WContainerWidget::propagateLayoutItemsOk(WLayoutItem *item)
494 {
495 if (!item)
496 return;
497
498 if (item->layout()) {
499 WLayout *layout = item->layout();
500 const int c = layout->count();
501 for (int i = 0; i < c; ++i)
502 propagateLayoutItemsOk(layout->itemAt(i));
503 } else if (item->widget()) {
504 WWidget *w = item->widget();
505 w->webWidget()->propagateRenderOk(true);
506 }
507 }
508
propagateRenderOk(bool deep)509 void WContainerWidget::propagateRenderOk(bool deep)
510 {
511 flags_.reset(BIT_CONTENT_ALIGNMENT_CHANGED);
512 flags_.reset(BIT_PADDINGS_CHANGED);
513 flags_.reset(BIT_OVERFLOW_CHANGED);
514 flags_.reset(BIT_LAYOUT_NEEDS_RERENDER);
515 flags_.reset(BIT_LAYOUT_NEEDS_UPDATE);
516
517 #ifndef WT_NO_LAYOUT
518 if (layout_ && deep)
519 propagateLayoutItemsOk(layout());
520 else
521 #endif
522 addedChildren_.reset();
523
524 WInteractWidget::propagateRenderOk(deep);
525 }
526
wasEmpty()527 bool WContainerWidget::wasEmpty() const
528 {
529 /*
530 * First case: on IE6, a popup widget has a shim child.
531 * Second case: WGroupBox always has a legend
532 */
533 if (isPopup() || firstChildIndex() > 0)
534 return false;
535 else
536 return (addedChildren_ ? addedChildren_->size() : 0) == children_.size();
537 }
538
domElementType()539 DomElementType WContainerWidget::domElementType() const
540 {
541 DomElementType type = isInline() ? DomElementType::SPAN : DomElementType::DIV;
542
543 WContainerWidget *p = dynamic_cast<WContainerWidget *>(parentWebWidget());
544 if (p && p->isList())
545 type = DomElementType::LI;
546
547 if (isList())
548 type = isOrderedList() ? DomElementType::OL : DomElementType::UL;
549
550 return type;
551 }
552
getDomChanges(std::vector<DomElement * > & result,WApplication * app)553 void WContainerWidget::getDomChanges(std::vector<DomElement *>& result,
554 WApplication *app)
555 {
556 DomElement *e = DomElement::getForUpdate(this, domElementType());
557
558 #ifndef WT_NO_LAYOUT
559 if (!app->session()->renderer().preLearning()) {
560 if (flags_.test(BIT_LAYOUT_NEEDS_RERENDER)) {
561 e->removeAllChildren(firstChildIndex());
562 createDomChildren(*e, app);
563
564 flags_.reset(BIT_LAYOUT_NEEDS_RERENDER);
565 flags_.reset(BIT_LAYOUT_NEEDS_UPDATE);
566 }
567 }
568 #endif // WT_NO_LAYOUT
569
570 updateDomChildren(*e, app);
571
572 updateDom(*e, false);
573
574 result.push_back(e);
575 }
576
createDomElement(WApplication * app)577 DomElement *WContainerWidget::createDomElement(WApplication *app)
578 {
579 return createDomElement(app, true);
580 }
581
createDomElement(WApplication * app,bool addChildren)582 DomElement *WContainerWidget::createDomElement(WApplication *app,
583 bool addChildren)
584 {
585 addedChildren_.reset();
586
587 DomElement *result = WWebWidget::createDomElement(app);
588
589 if (addChildren)
590 createDomChildren(*result, app);
591
592 return result;
593 }
594
createDomChildren(DomElement & parent,WApplication * app)595 void WContainerWidget::createDomChildren(DomElement& parent, WApplication *app)
596 {
597 if (layout_) {
598 #ifndef WT_NO_LAYOUT
599 containsLayout();
600 bool fitWidth = true;
601 bool fitHeight = true;
602
603 DomElement *c = layoutImpl()->createDomElement(&parent,
604 fitWidth, fitHeight, app);
605
606 if (c != &parent)
607 parent.addChild(c);
608
609 flags_.reset(BIT_LAYOUT_NEEDS_RERENDER);
610 flags_.reset(BIT_LAYOUT_NEEDS_UPDATE);
611 #endif // WT_NO_LAYOUT
612 } else {
613 for (unsigned i = 0; i < children_.size(); ++i)
614 parent.addChild(children_[i]->createSDomElement(app));
615 }
616
617 addedChildren_.reset();
618 }
619
updateDomChildren(DomElement & parent,WApplication * app)620 void WContainerWidget::updateDomChildren(DomElement& parent, WApplication *app)
621 {
622 if (!app->session()->renderer().preLearning() && !layout_) {
623 if (parent.mode() == DomElement::Mode::Update)
624 parent.setWasEmpty(wasEmpty());
625
626 if (addedChildren_) {
627 for (;;) {
628 std::vector<int> orderedInserts;
629 std::vector<WWidget *>& ac = *addedChildren_;
630
631 for (unsigned i = 0; i < ac.size(); ++i)
632 orderedInserts.push_back(indexOf(ac[i]));
633
634 Utils::sort(orderedInserts);
635
636 int addedCount = addedChildren_->size();
637 int totalCount = children_.size();
638 int insertCount = 0;
639
640 addedChildren_.reset();
641
642 for (unsigned i = 0; i < orderedInserts.size(); ++i) {
643 int pos = orderedInserts[i];
644
645 DomElement *c = (children_)[pos]->createSDomElement(app);
646
647 if (pos + (addedCount - insertCount) == totalCount)
648 parent.addChild(c);
649 else
650 parent.insertChildAt(c, pos + firstChildIndex());
651
652 ++insertCount;
653 }
654
655 if (!addedChildren_ || addedChildren_->empty())
656 break;
657 }
658
659 addedChildren_.reset();
660 }
661 }
662
663 #ifndef WT_NO_LAYOUT
664 if (flags_.test(BIT_LAYOUT_NEEDS_UPDATE)) {
665 if (layout_)
666 layoutImpl()->updateDom(parent);
667
668 flags_.reset(BIT_LAYOUT_NEEDS_UPDATE);
669 }
670 #endif // WT_NO_LAYOUT
671 }
672
layoutChanged(bool rerender)673 void WContainerWidget::layoutChanged(bool rerender)
674 {
675 #ifndef WT_NO_LAYOUT
676 if (rerender)
677 flags_.set(BIT_LAYOUT_NEEDS_RERENDER);
678 else
679 flags_.set(BIT_LAYOUT_NEEDS_UPDATE);
680
681 repaint(RepaintFlag::SizeAffected);
682 #endif // WT_NO_LAYOUT
683 }
684
rootAsJavaScript(WApplication * app,WStringStream & out,bool all)685 void WContainerWidget::rootAsJavaScript(WApplication *app, WStringStream& out,
686 bool all)
687 {
688 std::vector<WWidget *> *toAdd = all ? &children_ : addedChildren_.get();
689
690 if (toAdd)
691 for (unsigned i = 0; i < toAdd->size(); ++i) {
692 DomElement *c = (*toAdd)[i]->createSDomElement(app);
693
694 app->streamBeforeLoadJavaScript(out, false);
695
696 c->callMethod("omousemove=function(e) {"
697 "if (!e) e = window.event;"
698 "return " + app->javaScriptClass()
699 + "._p_.dragDrag(event); }");
700 c->callMethod("mouseup=function(e) {"
701 "if (!e) e = window.event;"
702 "return " + app->javaScriptClass()
703 + "._p_.dragEnd(event);}");
704 c->callMethod("dragstart=function(){return false;}");
705 c->asJavaScript(out);
706 delete c;
707 }
708
709 addedChildren_.reset();
710
711 if (!all) {
712 /* Note: we ignore rendering of deletion of a bound widget... */
713 }
714
715 // FIXME
716 propagateRenderOk(false);
717 }
718
setGlobalUnfocused(bool b)719 void WContainerWidget::setGlobalUnfocused(bool b)
720 {
721 globalUnfocused_ = b;
722 }
723
isGlobalUnfocussed()724 bool WContainerWidget::isGlobalUnfocussed() const
725 {
726 return globalUnfocused_;
727 }
728
setFormData(const FormData & formData)729 void WContainerWidget::setFormData(const FormData& formData)
730 {
731 if (!Utils::isEmpty(formData.values)) {
732 std::vector<std::string> attributes;
733 boost::split(attributes, formData.values[0], boost::is_any_of(";"));
734
735 if (attributes.size() == 2) {
736 try {
737 scrollTop_ = (int)Utils::stod(attributes[0]);
738 scrollLeft_ = (int)Utils::stod(attributes[1]);
739
740 }catch (const std::exception& e) {
741 throw WException("WContainerWidget: error parsing: " + formData.values[0] + ": " + e.what());
742 }
743 } else
744 throw WException("WContainerWidget: error parsing: " + formData.values[0]);
745 }
746
747 }
748
749 }
750