1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include <algorithm>
7 #include <cmath>
8 #include <cstdlib>
9 #include <boost/algorithm/string.hpp>
10
11 #include "Wt/WApplication.h"
12 #include "Wt/WCompositeWidget.h"
13 #include "Wt/WContainerWidget.h"
14 #include "Wt/WLogger.h"
15 #include "Wt/WJavaScript.h"
16 #include "Wt/WStringStream.h"
17 #include "Wt/WTheme.h"
18 #include "Wt/WWebWidget.h"
19
20 #include "DomElement.h"
21 #include "EscapeOStream.h"
22 #include "WebRenderer.h"
23 #include "WebSession.h"
24 #include "WebUtils.h"
25 #include "StringUtils.h"
26 #include "XSSFilter.h"
27
28 #include "3rdparty/rapidxml/rapidxml.hpp"
29 #include "3rdparty/rapidxml/rapidxml_xhtml.hpp"
30
31 #ifndef WT_DEBUG_JS
32 #include "js/WWebWidget.min.js"
33 #include "js/ToolTip.min.js"
34 #include "js/ScrollVisibility.min.js"
35 #endif
36
37 #ifdef max
38 #undef max
39 #endif
40
41 namespace Wt {
42
43 LOGGER("WWebWidget");
44
45 namespace {
46
nonNegative(const WLength & w)47 WLength nonNegative(const WLength& w) {
48 if (w.isAuto())
49 return w;
50 else
51 return WLength(std::fabs(w.value()), w.unit());
52 }
53
54 }
55
56 const char *WWebWidget::FOCUS_SIGNAL = "focus";
57 const char *WWebWidget::BLUR_SIGNAL = "blur";
58 const int WWebWidget::DEFAULT_BASE_Z_INDEX = 1100;
59 const int WWebWidget::Z_INDEX_INCREMENT = 1100;
60
61 #ifndef WT_TARGET_JAVA
62 const std::bitset<38> WWebWidget::AllChangeFlags = std::bitset<38>()
63 .set(BIT_FLEX_BOX_CHANGED)
64 .set(BIT_HIDDEN_CHANGED)
65 .set(BIT_GEOMETRY_CHANGED)
66 .set(BIT_FLOAT_SIDE_CHANGED)
67 .set(BIT_TOOLTIP_CHANGED)
68 .set(BIT_MARGINS_CHANGED)
69 .set(BIT_STYLECLASS_CHANGED)
70 .set(BIT_SELECTABLE_CHANGED)
71 .set(BIT_WIDTH_CHANGED)
72 .set(BIT_HEIGHT_CHANGED)
73 .set(BIT_DISABLED_CHANGED)
74 .set(BIT_ZINDEX_CHANGED)
75 .set(BIT_TABINDEX_CHANGED)
76 .set(BIT_SCROLL_VISIBILITY_CHANGED)
77 .set(BIT_OBJECT_NAME_CHANGED);
78 #endif // WT_TARGET_JAVA
79
TransientImpl()80 WWebWidget::TransientImpl::TransientImpl()
81 : addedChildren_(0),
82 specialChildRemove_(false)
83 { }
84
~TransientImpl()85 WWebWidget::TransientImpl::~TransientImpl()
86 { }
87
LayoutImpl()88 WWebWidget::LayoutImpl::LayoutImpl()
89 : positionScheme_(PositionScheme::Static),
90 floatSide_(static_cast<Side>(0)),
91 clearSides_(None),
92 minimumWidth_(0),
93 minimumHeight_(0),
94 baseZIndex_(DEFAULT_BASE_Z_INDEX),
95 zIndex_(0),
96 verticalAlignment_(AlignmentFlag::Baseline)
97 {
98 for (unsigned i = 0; i < 4; ++i) {
99 #ifdef WT_TARGET_JAVA
100 offsets_[i] = WLength::Auto;
101 #endif // WT_TARGET_JAVA
102 margin_[i] = WLength(0);
103 }
104 }
105
LookImpl(WWebWidget * w)106 WWebWidget::LookImpl::LookImpl(WWebWidget *w)
107 : toolTipTextFormat_(TextFormat::Plain),
108 loadToolTip_(w, "Wt-loadToolTip")
109 { }
110
~LookImpl()111 WWebWidget::LookImpl::~LookImpl()
112 { }
113
JavaScriptStatement(JavaScriptStatementType aType,const std::string & aData)114 WWebWidget::OtherImpl::JavaScriptStatement::JavaScriptStatement
115 (JavaScriptStatementType aType, const std::string& aData)
116 : type(aType),
117 data(aData)
118 { }
119
OtherImpl(WWebWidget * const self)120 WWebWidget::OtherImpl::OtherImpl(WWebWidget *const self)
121 : elementTagName_(nullptr),
122 tabIndex_(std::numeric_limits<int>::min()),
123 scrollVisibilityMargin_(0)
124 { }
125
~OtherImpl()126 WWebWidget::OtherImpl::~OtherImpl()
127 { }
128
WWebWidget()129 WWebWidget::WWebWidget()
130 : id_(nullptr) // CNOR needs an extra hint so it sets id_ to null instead of empty string
131 {
132 flags_.set(BIT_INLINE);
133 flags_.set(BIT_ENABLED);
134 }
135
136
137 #ifndef WT_TARGET_JAVA
getStateless(Method method)138 WStatelessSlot *WWebWidget::getStateless(Method method)
139 {
140 typedef void (WWebWidget::*Type)();
141
142 Type focusMethod = &WWebWidget::setFocus;
143
144 if (method == static_cast<WObject::Method>(focusMethod))
145 return implementStateless(focusMethod,
146 &WWebWidget::undoSetFocus);
147 else
148 return WWidget::getStateless(method);
149 }
150 #endif
151
setFormObject(bool how)152 void WWebWidget::setFormObject(bool how)
153 {
154 flags_.set(BIT_FORM_OBJECT, how);
155
156 WApplication::instance()
157 ->session()->renderer().updateFormObjects(this, false);
158 }
159
setId(const std::string & id)160 void WWebWidget::setId(const std::string& id)
161 {
162 if (!otherImpl_)
163 otherImpl_.reset(new OtherImpl(this));
164
165 WApplication* app = WApplication::instance();
166 for (std::size_t i = 0; i < jsignals_.size(); ++i) {
167 EventSignalBase* signal = jsignals_[i];
168 if (signal->isExposedSignal())
169 app->removeExposedSignal(signal);
170 }
171
172 if (!id_)
173 id_.reset(new std::string());
174
175 *id_ = id;
176
177 for (std::size_t i = 0; i < jsignals_.size(); ++i) {
178 EventSignalBase* signal = jsignals_[i];
179 if (signal->isExposedSignal())
180 app->addExposedSignal(signal);
181 }
182 }
183
setSelectable(bool selectable)184 void WWebWidget::setSelectable(bool selectable)
185 {
186 flags_.set(BIT_SET_SELECTABLE, selectable);
187 flags_.set(BIT_SET_UNSELECTABLE, !selectable);
188 flags_.set(BIT_SELECTABLE_CHANGED);
189
190 repaint();
191 }
192
id()193 const std::string WWebWidget::id() const
194 {
195 if (id_)
196 return *id_;
197 else
198 return WWidget::id();
199 }
200
repaint(WFlags<RepaintFlag> flags)201 void WWebWidget::repaint(WFlags<RepaintFlag> flags)
202 {
203 /*
204 * If the widget is currently stubbed, we need to redo the slot
205 * learning while unstubbing.
206 */
207 if (isStubbed()) {
208 WebRenderer& renderer = WApplication::instance()->session()->renderer();
209 if (renderer.preLearning())
210 renderer.learningIncomplete();
211 }
212
213 /*
214 * We ignore repaints to an unrendered widget.
215 */
216 if (!flags_.test(BIT_RENDERED))
217 return;
218
219 WWidget::scheduleRerender(false, flags);
220
221 if (flags.test(RepaintFlag::ToAjax))
222 flags_.set(BIT_REPAINT_TO_AJAX);
223 }
224
renderOk()225 void WWebWidget::renderOk()
226 {
227 WWidget::renderOk();
228
229 flags_.reset(BIT_REPAINT_TO_AJAX);
230 }
231
signalConnectionsChanged()232 void WWebWidget::signalConnectionsChanged()
233 {
234 repaint();
235 }
236
beingDeleted()237 void WWebWidget::beingDeleted()
238 {
239 // flag that we are being deleted, this allows some optimalizations
240 flags_.set(BIT_BEING_DELETED);
241 }
242
~WWebWidget()243 WWebWidget::~WWebWidget()
244 {
245 beingDeleted();
246 std::unique_ptr<WWidget> unique_this = removeFromParent();
247 #ifndef WT_TARGET_JAVA
248 // removeFromParent should always return a nullptr if it is called in the destructor!
249 assert(unique_this == nullptr);
250 #endif // WT_TARGET_JAVA
251
252 // Run destructors in this order (same as Wt 3), some destructors
253 // rely on otherImpl_ (loadToolTip_ JSignal destructor unexposes signal,
254 // which calls id(), which looks at otherImpl_)
255 transientImpl_.reset();
256 layoutImpl_.reset();
257 lookImpl_.reset();
258 otherImpl_.reset();
259 }
260
decorationStyle()261 WCssDecorationStyle& WWebWidget::decorationStyle()
262 {
263 if (!lookImpl_)
264 lookImpl_.reset(new LookImpl(this));
265
266 if (!lookImpl_->decorationStyle_) {
267 lookImpl_->decorationStyle_.reset(new WCssDecorationStyle());
268 lookImpl_->decorationStyle_->setWebWidget(this);
269 }
270
271 return *lookImpl_->decorationStyle_;
272 }
273
decorationStyle()274 const WCssDecorationStyle& WWebWidget::decorationStyle() const
275 {
276 return const_cast<WWebWidget *>(this)->decorationStyle();
277 }
278
setDecorationStyle(const WCssDecorationStyle & style)279 void WWebWidget::setDecorationStyle(const WCssDecorationStyle& style)
280 {
281 #ifndef WT_TARGET_JAVA
282 decorationStyle() = style;
283 #else
284 if (!lookImpl_)
285 lookImpl_.reset(new LookImpl(this));
286
287 lookImpl_->decorationStyle_.reset(&style);
288 #endif // WT_TARGET_JAVA
289 }
290
iterateChildren(const HandleWidgetMethod & method)291 void WWebWidget::iterateChildren(const HandleWidgetMethod& method) const
292 { }
293
renderRemoveJs(bool recursive)294 std::string WWebWidget::renderRemoveJs(bool recursive)
295 {
296 Wt::WStringStream result;
297
298 if (isRendered() && scrollVisibilityEnabled()) {
299 result << WT_CLASS ".scrollVisibility.remove("
300 << jsStringLiteral(id()) << ");";
301 flags_.set(BIT_SCROLL_VISIBILITY_CHANGED);
302 flags_.reset(BIT_SCROLL_VISIBILITY_LOADED);
303 }
304
305 iterateChildren
306 ([&](WWidget *c) {
307 result << c->renderRemoveJs(true);
308 });
309
310 if (!recursive) {
311 if (result.empty())
312 result << "_" << id();
313 else
314 result << WT_CLASS ".remove('" << id() << "');";
315 }
316
317 return result.str();
318 }
319
widgetRemoved(WWidget * child,bool renderRemove)320 void WWebWidget::widgetRemoved(WWidget *child, bool renderRemove)
321 {
322 if (!flags_.test(BIT_BEING_DELETED) && renderRemove) {
323 std::string js = child->renderRemoveJs(false);
324
325 if (!transientImpl_)
326 transientImpl_.reset(new TransientImpl());
327
328 transientImpl_->childRemoveChanges_.push_back(js);
329 if (js[0] != '_')
330 transientImpl_->specialChildRemove_ = true;
331
332 repaint(RepaintFlag::SizeAffected);
333 }
334
335 child->setParentWidget(nullptr);
336
337 if (transientImpl_ &&
338 !child->webWidget()->isRendered() &&
339 !child->webWidget()->isStubbed())
340 --transientImpl_->addedChildren_;
341
342 /*
343 * When the child is about to be deleted, all of its descendants
344 * properly removes itself from the renderer "dirty" list. If not,
345 * we here force this propagation.
346 */
347 if (!child->webWidget()->flags_.test(BIT_BEING_DELETED))
348 child->webWidget()->setRendered(false);
349
350 WApplication::instance()
351 ->session()->renderer().updateFormObjects(child->webWidget(), true);
352
353 emitChildrenChanged();
354 }
355
childrenChanged()356 Signal<>& WWebWidget::childrenChanged()
357 {
358 if (!otherImpl_)
359 otherImpl_.reset(new OtherImpl(this));
360
361 return otherImpl_->childrenChanged_;
362 }
363
setPositionScheme(PositionScheme scheme)364 void WWebWidget::setPositionScheme(PositionScheme scheme)
365 {
366 if (!layoutImpl_)
367 layoutImpl_.reset(new LayoutImpl());
368
369 layoutImpl_->positionScheme_ = scheme;
370
371 if ((scheme == PositionScheme::Absolute) || (scheme == PositionScheme::Fixed))
372 flags_.reset(BIT_INLINE);
373
374 flags_.set(BIT_GEOMETRY_CHANGED);
375 repaint(RepaintFlag::SizeAffected);
376 }
377
positionScheme()378 PositionScheme WWebWidget::positionScheme() const
379 {
380 return layoutImpl_ ? layoutImpl_->positionScheme_ : PositionScheme::Static;
381 }
382
resize(const WLength & width,const WLength & height)383 void WWebWidget::resize(const WLength& width, const WLength& height)
384 {
385 bool changed = false;
386
387 if (!width_ && !width.isAuto())
388 width_.reset(new WLength());
389
390 if (width_ && *width_ != width) {
391 changed = true;
392 *width_ = nonNegative(width);
393 flags_.set(BIT_WIDTH_CHANGED);
394 }
395
396 if (!height_ && !height.isAuto())
397 height_.reset(new WLength());
398
399 if (height_ && *height_ != height) {
400 changed = true;
401 *height_ = nonNegative(height);
402 flags_.set(BIT_HEIGHT_CHANGED);
403 }
404
405 if (changed) {
406 repaint(RepaintFlag::SizeAffected);
407 WWidget::resize(width, height);
408 }
409 }
410
width()411 WLength WWebWidget::width() const
412 {
413 return width_ ? *width_ : WLength::Auto;
414 }
415
height()416 WLength WWebWidget::height() const
417 {
418 return height_ ? *height_ : WLength::Auto;
419 }
420
setMinimumSize(const WLength & width,const WLength & height)421 void WWebWidget::setMinimumSize(const WLength& width, const WLength& height)
422 {
423 if (!layoutImpl_)
424 layoutImpl_.reset(new LayoutImpl());
425
426 layoutImpl_->minimumWidth_ = nonNegative(width);
427 layoutImpl_->minimumHeight_ = nonNegative(height);
428
429 flags_.set(BIT_GEOMETRY_CHANGED);
430
431 repaint(RepaintFlag::SizeAffected);
432 }
433
minimumWidth()434 WLength WWebWidget::minimumWidth() const
435 {
436 return layoutImpl_ ? layoutImpl_->minimumWidth_ : WLength(0);
437 }
438
minimumHeight()439 WLength WWebWidget::minimumHeight() const
440 {
441 return layoutImpl_ ? layoutImpl_->minimumHeight_ : WLength(0);
442 }
443
setMaximumSize(const WLength & width,const WLength & height)444 void WWebWidget::setMaximumSize(const WLength& width, const WLength& height)
445 {
446 if (!layoutImpl_)
447 layoutImpl_.reset(new LayoutImpl());
448
449 layoutImpl_->maximumWidth_ = nonNegative(width);
450 layoutImpl_->maximumHeight_ = nonNegative(height);
451
452 flags_.set(BIT_GEOMETRY_CHANGED);
453
454 repaint(RepaintFlag::SizeAffected);
455 }
456
maximumWidth()457 WLength WWebWidget::maximumWidth() const
458 {
459 return layoutImpl_ ? layoutImpl_->maximumWidth_ : WLength::Auto;
460 }
461
maximumHeight()462 WLength WWebWidget::maximumHeight() const
463 {
464 return layoutImpl_ ? layoutImpl_->maximumHeight_ : WLength::Auto;
465 }
466
setLineHeight(const WLength & height)467 void WWebWidget::setLineHeight(const WLength& height)
468 {
469 if (!layoutImpl_)
470 layoutImpl_.reset(new LayoutImpl());
471
472 layoutImpl_->lineHeight_ = height;
473
474 flags_.set(BIT_GEOMETRY_CHANGED);
475
476 repaint(RepaintFlag::SizeAffected);
477 }
478
lineHeight()479 WLength WWebWidget::lineHeight() const
480 {
481 return layoutImpl_ ? layoutImpl_->lineHeight_ : WLength::Auto;
482 }
483
setFloatSide(Side s)484 void WWebWidget::setFloatSide(Side s)
485 {
486 if (!layoutImpl_)
487 layoutImpl_.reset(new LayoutImpl());
488
489 layoutImpl_->floatSide_ = s;
490
491 flags_.set(BIT_FLOAT_SIDE_CHANGED);
492
493 repaint();
494 }
495
floatSide()496 Side WWebWidget::floatSide() const
497 {
498 if (layoutImpl_)
499 return layoutImpl_->floatSide_;
500 else
501 return static_cast<Side>(0);
502 }
503
setClearSides(WFlags<Side> sides)504 void WWebWidget::setClearSides(WFlags<Side> sides)
505 {
506 if (!layoutImpl_)
507 layoutImpl_.reset(new LayoutImpl());
508
509 layoutImpl_->clearSides_ = sides;
510
511 flags_.set(BIT_GEOMETRY_CHANGED);
512
513 repaint();
514 }
515
clearSides()516 WFlags<Side> WWebWidget::clearSides() const
517 {
518 if (layoutImpl_)
519 return layoutImpl_->clearSides_;
520 else
521 return None;
522 }
523
setVerticalAlignment(AlignmentFlag alignment,const WLength & length)524 void WWebWidget::setVerticalAlignment(AlignmentFlag alignment,
525 const WLength& length)
526 {
527 if (AlignHorizontalMask.test(alignment))
528 LOG_ERROR("setVerticalAlignment(): alignment "
529 << static_cast<int>(alignment)
530 << " is not vertical");
531
532 if (!layoutImpl_)
533 layoutImpl_.reset(new LayoutImpl());
534
535 layoutImpl_->verticalAlignment_ = alignment;
536 layoutImpl_->verticalAlignmentLength_ = length;
537
538 flags_.set(BIT_GEOMETRY_CHANGED);
539
540 repaint();
541 }
542
verticalAlignment()543 AlignmentFlag WWebWidget::verticalAlignment() const
544 {
545 return layoutImpl_
546 ? layoutImpl_->verticalAlignment_ : AlignmentFlag::Baseline;
547 }
548
verticalAlignmentLength()549 WLength WWebWidget::verticalAlignmentLength() const
550 {
551 return layoutImpl_ ? layoutImpl_->verticalAlignmentLength_ : WLength::Auto;
552 }
553
setOffsets(const WLength & offset,WFlags<Side> sides)554 void WWebWidget::setOffsets(const WLength& offset, WFlags<Side> sides)
555 {
556 if (!layoutImpl_)
557 layoutImpl_.reset(new LayoutImpl());
558
559 if (sides.test(Side::Top))
560 layoutImpl_->offsets_[0] = offset;
561 if (sides.test(Side::Right))
562 layoutImpl_->offsets_[1] = offset;
563 if (sides.test(Side::Bottom))
564 layoutImpl_->offsets_[2] = offset;
565 if (sides.test(Side::Left))
566 layoutImpl_->offsets_[3] = offset;
567
568 flags_.set(BIT_GEOMETRY_CHANGED);
569
570 repaint();
571 }
572
offset(Side s)573 WLength WWebWidget::offset(Side s) const
574 {
575 if (!layoutImpl_)
576 return WLength::Auto;
577
578 switch (s) {
579 case Side::Top:
580 return layoutImpl_->offsets_[0];
581 case Side::Right:
582 return layoutImpl_->offsets_[1];
583 case Side::Bottom:
584 return layoutImpl_->offsets_[2];
585 case Side::Left:
586 return layoutImpl_->offsets_[3];
587 default:
588 LOG_ERROR("offset(Side) with invalid side: " << (int)s);
589 return WLength();
590 }
591 }
592
zIndex()593 int WWebWidget::zIndex() const
594 {
595 if (layoutImpl_)
596 return layoutImpl_->zIndex_;
597 else
598 return 0;
599 }
600
setInline(bool inl)601 void WWebWidget::setInline(bool inl)
602 {
603 flags_.set(BIT_INLINE, inl);
604
605 resetLearnedSlot(&WWidget::show);
606
607 flags_.set(BIT_GEOMETRY_CHANGED);
608
609 repaint();
610 }
611
isInline()612 bool WWebWidget::isInline() const
613 {
614 return flags_.test(BIT_INLINE);
615 }
616
setFlexBox(bool enabled)617 void WWebWidget::setFlexBox(bool enabled)
618 {
619 flags_.set(BIT_FLEX_BOX, enabled);
620 flags_.set(BIT_FLEX_BOX_CHANGED);
621 }
622
setPopup(bool popup)623 void WWebWidget::setPopup(bool popup)
624 {
625 if (!layoutImpl_)
626 layoutImpl_.reset(new LayoutImpl());
627
628 layoutImpl_->zIndex_ = popup ? -1 : 0;
629
630 if (popup && parent())
631 calcZIndex();
632
633 flags_.set(BIT_ZINDEX_CHANGED);
634
635 repaint();
636 }
637
setZIndex(int zIndex)638 void WWebWidget::setZIndex(int zIndex)
639 {
640 if (!layoutImpl_)
641 layoutImpl_.reset(new LayoutImpl());
642
643 layoutImpl_->zIndex_ = zIndex;
644
645 flags_.set(BIT_ZINDEX_CHANGED);
646
647 repaint();
648 }
649
setParentWidget(WWidget * parent)650 void WWebWidget::setParentWidget(WWidget *parent)
651 {
652 WWidget::setParentWidget(parent);
653
654 if (parent)
655 if (isPopup())
656 calcZIndex();
657 }
658
parentWebWidget()659 WWebWidget *WWebWidget::parentWebWidget() const
660 {
661 /*
662 * Returns the parent webwidget, i.e. skipping composite widgets
663 */
664 WWidget *p = this->parent();
665 while (p != nullptr && dynamic_cast<WCompositeWidget *>(p) != nullptr)
666 p = p->parent();
667
668 return p ? p->webWidget() : nullptr;
669 }
670
selfWidget()671 WWidget *WWebWidget::selfWidget()
672 {
673 /*
674 * Returns the composite widget implemented by this web widget
675 */
676 WWidget *p = nullptr, *p_parent = this;
677 do {
678 p = p_parent;
679 p_parent = p->parent();
680 } while (p_parent != nullptr &&
681 dynamic_cast<WCompositeWidget *>(p_parent) != nullptr);
682
683 return p;
684 }
685
calcZIndex()686 void WWebWidget::calcZIndex()
687 {
688 layoutImpl_->zIndex_ = -1;
689
690 WWebWidget *ww = parentWebWidget();
691 if (ww) {
692 const std::vector<WWidget *>& children = ww->children();
693
694 int maxZ = 0;
695 for (unsigned i = 0; i < children.size(); ++i) {
696 WWebWidget *wi = children[i]->webWidget();
697 if (wi->baseZIndex() <= baseZIndex())
698 maxZ = std::max(maxZ, wi->zIndex());
699 }
700
701 layoutImpl_->zIndex_ = std::max(baseZIndex(), maxZ + Z_INDEX_INCREMENT);
702 }
703 }
704
isPopup()705 bool WWebWidget::isPopup() const
706 {
707 return layoutImpl_ ? layoutImpl_->zIndex_ != 0 : false;
708 }
709
setMargin(const WLength & margin,WFlags<Side> sides)710 void WWebWidget::setMargin(const WLength& margin, WFlags<Side> sides)
711 {
712 if (!layoutImpl_)
713 layoutImpl_.reset(new LayoutImpl());
714
715 if (sides.test(Side::Top))
716 layoutImpl_->margin_[0] = margin;
717 if (sides.test(Side::Right))
718 layoutImpl_->margin_[1] = margin;
719 if (sides.test(Side::Bottom))
720 layoutImpl_->margin_[2] = margin;
721 if (sides.test(Side::Left))
722 layoutImpl_->margin_[3] = margin;
723
724 flags_.set(BIT_MARGINS_CHANGED);
725
726 repaint(RepaintFlag::SizeAffected);
727 }
728
margin(Side side)729 WLength WWebWidget::margin(Side side) const
730 {
731 if (!layoutImpl_)
732 return WLength(0);
733
734 switch (side) {
735 case Side::Top:
736 return layoutImpl_->margin_[0];
737 case Side::Right:
738 return layoutImpl_->margin_[1];
739 case Side::Bottom:
740 return layoutImpl_->margin_[2];
741 case Side::Left:
742 return layoutImpl_->margin_[3];
743 default:
744 LOG_ERROR("margin(Side) with invalid side: " << (int)side);
745 return WLength();
746 }
747 }
748
addStyleClass(const WT_USTRING & styleClass,bool force)749 void WWebWidget::addStyleClass(const WT_USTRING& styleClass, bool force)
750 {
751 if (!lookImpl_)
752 lookImpl_.reset(new LookImpl(this));
753
754 std::string currentClass = lookImpl_->styleClass_.toUTF8();
755 Utils::SplitSet classes;
756 Utils::split(classes, currentClass, " ", true);
757
758 if (classes.find(styleClass.toUTF8()) == classes.end()) {
759 lookImpl_->styleClass_
760 = WT_USTRING::fromUTF8(Utils::addWord(lookImpl_->styleClass_.toUTF8(),
761 styleClass.toUTF8()));
762
763 if (!force) {
764 flags_.set(BIT_STYLECLASS_CHANGED);
765 repaint(RepaintFlag::SizeAffected);
766 }
767 }
768
769 if (force && isRendered()) {
770 if (!transientImpl_)
771 transientImpl_.reset(new TransientImpl());
772
773 Utils::add(transientImpl_->addedStyleClasses_, styleClass);
774 Utils::erase(transientImpl_->removedStyleClasses_, styleClass);
775
776 repaint(RepaintFlag::SizeAffected);
777 }
778 }
779
removeStyleClass(const WT_USTRING & styleClass,bool force)780 void WWebWidget::removeStyleClass(const WT_USTRING& styleClass, bool force)
781 {
782 if (!lookImpl_)
783 lookImpl_.reset(new LookImpl(this));
784
785 if (hasStyleClass(styleClass)) {
786 // perhaps it is quicker to join the classes back, but then we need to
787 // make sure we keep the original order ?
788 lookImpl_->styleClass_
789 = WT_USTRING::fromUTF8(Utils::eraseWord(lookImpl_->styleClass_.toUTF8(),
790 styleClass.toUTF8()));
791 if (!force) {
792 flags_.set(BIT_STYLECLASS_CHANGED);
793 repaint(RepaintFlag::SizeAffected);
794 }
795 }
796
797 if (force && isRendered()) {
798 if (!transientImpl_)
799 transientImpl_.reset(new TransientImpl());
800
801 Utils::add(transientImpl_->removedStyleClasses_, styleClass);
802 Utils::erase(transientImpl_->addedStyleClasses_, styleClass);
803
804 repaint(RepaintFlag::SizeAffected);
805 }
806 }
807
addStyleClass(const char * styleClass,bool force)808 void WWebWidget::addStyleClass(const char *styleClass, bool force)
809 {
810 addStyleClass(WString::fromUTF8(styleClass), force);
811 }
812
removeStyleClass(const char * styleClass,bool force)813 void WWebWidget::removeStyleClass(const char *styleClass, bool force)
814 {
815 removeStyleClass(WString::fromUTF8(styleClass), force);
816 }
817
hasStyleClass(const WT_USTRING & styleClass)818 bool WWebWidget::hasStyleClass(const WT_USTRING& styleClass) const
819 {
820 if (!lookImpl_)
821 return false;
822
823 std::string currentClass = lookImpl_->styleClass_.toUTF8();
824 Utils::SplitSet classes;
825 Utils::split(classes, currentClass, " ", true);
826
827 return classes.find(styleClass.toUTF8()) != classes.end();
828 }
829
setStyleClass(const WT_USTRING & styleClass)830 void WWebWidget::setStyleClass(const WT_USTRING& styleClass)
831 {
832 if (canOptimizeUpdates() && (styleClass == this->styleClass()))
833 return;
834
835 if (!lookImpl_)
836 lookImpl_.reset(new LookImpl(this));
837
838 lookImpl_->styleClass_ = styleClass;
839
840 flags_.set(BIT_STYLECLASS_CHANGED);
841
842 repaint(RepaintFlag::SizeAffected);
843 }
844
setStyleClass(const char * styleClass)845 void WWebWidget::setStyleClass(const char *styleClass)
846 {
847 setStyleClass(WString::fromUTF8(styleClass));
848 }
849
styleClass()850 WT_USTRING WWebWidget::styleClass() const
851 {
852 return lookImpl_ ? lookImpl_->styleClass_ : WT_USTRING();
853 }
854
setAttributeValue(const std::string & name,const WT_USTRING & value)855 void WWebWidget::setAttributeValue(const std::string& name,
856 const WT_USTRING& value)
857 {
858 if (!otherImpl_)
859 otherImpl_.reset(new OtherImpl(this));
860
861 if (!otherImpl_->attributes_)
862 otherImpl_->attributes_.reset(new std::map<std::string, WT_USTRING>);
863
864 std::map<std::string, WT_USTRING>::const_iterator i
865 = otherImpl_->attributes_->find(name);
866
867 if (i != otherImpl_->attributes_->end() && i->second == value)
868 return;
869
870 (*otherImpl_->attributes_)[name] = value;
871
872 if (!transientImpl_)
873 transientImpl_.reset(new TransientImpl());
874
875 transientImpl_->attributesSet_.push_back(name);
876
877 repaint();
878 }
879
attributeValue(const std::string & name)880 WT_USTRING WWebWidget::attributeValue(const std::string& name) const
881 {
882 if (otherImpl_ && otherImpl_->attributes_) {
883 std::map<std::string, WT_USTRING>::const_iterator i
884 = otherImpl_->attributes_->find(name);
885
886 if (i != otherImpl_->attributes_->end())
887 return i->second;
888 }
889
890 return WT_USTRING();
891 }
892
setJavaScriptMember(const std::string & name,const std::string & value)893 void WWebWidget::setJavaScriptMember(const std::string& name,
894 const std::string& value)
895 {
896 if (!otherImpl_)
897 otherImpl_.reset(new OtherImpl(this));
898
899 if (!otherImpl_->jsMembers_)
900 otherImpl_->jsMembers_.reset(new std::vector<OtherImpl::Member>);
901
902 std::vector<OtherImpl::Member>& members = *otherImpl_->jsMembers_;
903 int index = indexOfJavaScriptMember(name);
904
905 if (index != -1 && (members[index].value == value))
906 return;
907
908 if (value.empty()) {
909 if (index != -1)
910 members.erase(members.begin() + index);
911 else
912 return;
913 } else {
914 if (index == -1) {
915 OtherImpl::Member m;
916 m.name = name;
917 m.value = value;
918 members.push_back(m);
919 } else {
920 members[index].value = value;
921 }
922 }
923
924 addJavaScriptStatement(JavaScriptStatementType::SetMember, name);
925
926 repaint();
927 }
928
javaScriptMember(const std::string & name)929 std::string WWebWidget::javaScriptMember(const std::string& name) const
930 {
931 int index = indexOfJavaScriptMember(name);
932 if (index != -1)
933 return (*otherImpl_->jsMembers_)[index].value;
934 else
935 return std::string();
936 }
937
indexOfJavaScriptMember(const std::string & name)938 int WWebWidget::indexOfJavaScriptMember(const std::string& name) const
939 {
940 if (otherImpl_ && otherImpl_->jsMembers_)
941 for (unsigned i = 0; i < otherImpl_->jsMembers_->size(); i++)
942 if ((*otherImpl_->jsMembers_)[i].name == name)
943 return i;
944
945 return -1;
946 }
947
callJavaScriptMember(const std::string & name,const std::string & args)948 void WWebWidget::callJavaScriptMember(const std::string& name,
949 const std::string& args)
950 {
951 addJavaScriptStatement(JavaScriptStatementType::CallMethod,
952 name + "(" + args + ");");
953
954 repaint();
955 }
956
addJavaScriptStatement(JavaScriptStatementType type,const std::string & data)957 void WWebWidget::addJavaScriptStatement(JavaScriptStatementType type,
958 const std::string& data)
959 {
960 if (!otherImpl_)
961 otherImpl_.reset(new OtherImpl(this));
962
963 if (!otherImpl_->jsStatements_)
964 otherImpl_->jsStatements_.reset
965 (new std::vector<OtherImpl::JavaScriptStatement>());
966
967 std::vector<OtherImpl::JavaScriptStatement>& v = *otherImpl_->jsStatements_;
968
969 /*
970 * a SetMember is idempotent, if one is already scheduled we do not need
971 * to add another statement.
972 */
973 if (type == JavaScriptStatementType::SetMember) {
974 for (unsigned i = 0; i < v.size(); ++i) {
975 if (v[i].type == JavaScriptStatementType::SetMember && v[i].data == data)
976 return;
977 }
978 }
979
980 /*
981 * If the last statement is exactly the same, then it's a dupe, discard it
982 * too.
983 */
984 if (v.empty() ||
985 v.back().type != type ||
986 v.back().data != data)
987 v.push_back(OtherImpl::JavaScriptStatement(type, data));
988 }
989
990
loadToolTip()991 void WWebWidget::loadToolTip()
992 {
993 if (!lookImpl_->toolTip_)
994 lookImpl_->toolTip_.reset(new WString());
995 *lookImpl_->toolTip_ = toolTip();
996
997 flags_.set(BIT_TOOLTIP_CHANGED);
998 repaint();
999 }
1000
setToolTip(const WString & text,TextFormat textFormat)1001 void WWebWidget::setToolTip(const WString& text, TextFormat textFormat)
1002 {
1003 flags_.reset(BIT_TOOLTIP_DEFERRED);
1004
1005 if (canOptimizeUpdates() && text == storedToolTip())
1006 return;
1007
1008 if (!lookImpl_)
1009 lookImpl_.reset(new LookImpl(this));
1010
1011 if (!lookImpl_->toolTip_)
1012 lookImpl_->toolTip_.reset(new WString());
1013
1014 *lookImpl_->toolTip_ = text;
1015 lookImpl_->toolTipTextFormat_ = textFormat;
1016
1017 flags_.set(BIT_TOOLTIP_CHANGED);
1018
1019 repaint();
1020 }
1021
toolTip()1022 WString WWebWidget::toolTip() const
1023 {
1024 return storedToolTip();
1025 }
1026
storedToolTip()1027 WString WWebWidget::storedToolTip() const
1028 {
1029 return lookImpl_ && lookImpl_->toolTip_
1030 ? *lookImpl_->toolTip_
1031 : WString::Empty;
1032 }
1033
setDeferredToolTip(bool enable,TextFormat textFormat)1034 void WWebWidget::setDeferredToolTip(bool enable, TextFormat textFormat)
1035 {
1036 flags_.set(BIT_TOOLTIP_DEFERRED, enable);
1037
1038 if (!enable)
1039 setToolTip("", textFormat);
1040 else{
1041 if (!lookImpl_)
1042 lookImpl_.reset(new LookImpl(this));
1043
1044 if (!lookImpl_->toolTip_)
1045 lookImpl_->toolTip_.reset(new WString());
1046 else
1047 *lookImpl_->toolTip_ = WString();
1048
1049 lookImpl_->toolTipTextFormat_ = textFormat;
1050
1051 flags_.set(BIT_TOOLTIP_CHANGED);
1052
1053 repaint();
1054 }
1055 }
1056
setHiddenKeepsGeometry(bool enabled)1057 void WWebWidget::setHiddenKeepsGeometry(bool enabled)
1058 {
1059 flags_.set(BIT_DONOT_STUB);
1060 flags_.set(BIT_HIDE_WITH_VISIBILITY, enabled);
1061 flags_.set(BIT_HIDDEN_CHANGED);
1062 }
1063
hiddenKeepsGeometry()1064 bool WWebWidget::hiddenKeepsGeometry() const
1065 {
1066 return flags_.test(BIT_HIDE_WITH_VISIBILITY)
1067 && !flags_.test(BIT_HIDE_WITH_OFFSETS);
1068 }
1069
setHidden(bool hidden,const WAnimation & animation)1070 void WWebWidget::setHidden(bool hidden, const WAnimation& animation)
1071 {
1072 if (canOptimizeUpdates() && (animation.empty() && hidden == isHidden()))
1073 return;
1074
1075 bool wasVisible = isVisible();
1076
1077 flags_.set(BIT_HIDDEN, hidden);
1078 flags_.set(BIT_HIDDEN_CHANGED);
1079
1080 if (!animation.empty()
1081 && WApplication::instance()->environment().supportsCss3Animations()
1082 && WApplication::instance()->environment().ajax()) {
1083 if (!transientImpl_)
1084 transientImpl_.reset(new TransientImpl());
1085 transientImpl_->animation_ = animation;
1086 }
1087
1088 bool shouldBeVisible = !hidden;
1089 if (shouldBeVisible && parent())
1090 shouldBeVisible = parent()->isVisible();
1091
1092 if (!canOptimizeUpdates() || shouldBeVisible != wasVisible)
1093 propagateSetVisible(shouldBeVisible);
1094
1095 WApplication::instance()
1096 ->session()->renderer().updateFormObjects(this, true);
1097
1098 repaint(RepaintFlag::SizeAffected);
1099 }
1100
parentResized(WWidget * parent,WFlags<Orientation> directions)1101 void WWebWidget::parentResized(WWidget *parent, WFlags<Orientation> directions)
1102 {
1103 if (flags_.test(BIT_CONTAINS_LAYOUT)) {
1104 iterateChildren
1105 ([=](WWidget *c) {
1106 if (!c->isHidden())
1107 c->webWidget()->parentResized(parent, directions);
1108 });
1109 }
1110 }
1111
containsLayout()1112 void WWebWidget::containsLayout()
1113 {
1114 if (!flags_.test(BIT_CONTAINS_LAYOUT)) {
1115 flags_.set(BIT_CONTAINS_LAYOUT);
1116 WWebWidget *p = parentWebWidget();
1117 if (p)
1118 p->containsLayout();
1119 }
1120 }
1121
isHidden()1122 bool WWebWidget::isHidden() const
1123 {
1124 return flags_.test(BIT_HIDDEN);
1125 }
1126
isVisible()1127 bool WWebWidget::isVisible() const
1128 {
1129 if (flags_.test(BIT_STUBBED) || flags_.test(BIT_HIDDEN))
1130 return false;
1131 else
1132 if (parent())
1133 return parent()->isVisible();
1134 else
1135 return this == WApplication::instance()->domRoot() ||
1136 this == WApplication::instance()->domRoot2();
1137 }
1138
setDisabled(bool disabled)1139 void WWebWidget::setDisabled(bool disabled)
1140 {
1141 if (canOptimizeUpdates() && (disabled == flags_.test(BIT_DISABLED)))
1142 return;
1143
1144 bool wasEnabled = isEnabled();
1145
1146 flags_.set(BIT_DISABLED, disabled);
1147 flags_.set(BIT_DISABLED_CHANGED);
1148
1149 bool shouldBeEnabled = !disabled;
1150 if (shouldBeEnabled && parent())
1151 shouldBeEnabled = parent()->isEnabled();
1152
1153 if (shouldBeEnabled != wasEnabled)
1154 propagateSetEnabled(shouldBeEnabled);
1155
1156 WApplication::instance()
1157 ->session()->renderer().updateFormObjects(this, true);
1158
1159 repaint();
1160 }
1161
propagateSetEnabled(bool enabled)1162 void WWebWidget::propagateSetEnabled(bool enabled)
1163 {
1164 iterateChildren
1165 ([=](WWidget *c) {
1166 if (!c->isDisabled())
1167 c->webWidget()->propagateSetEnabled(enabled);
1168 });
1169 }
1170
propagateSetVisible(bool visible)1171 void WWebWidget::propagateSetVisible(bool visible)
1172 {
1173 iterateChildren
1174 ([=](WWidget *c) {
1175 if (!c->isHidden())
1176 c->webWidget()->propagateSetVisible(visible);
1177 });
1178 }
1179
isDisabled()1180 bool WWebWidget::isDisabled() const
1181 {
1182 return flags_.test(BIT_DISABLED);
1183 }
1184
isEnabled()1185 bool WWebWidget::isEnabled() const
1186 {
1187 if (isDisabled())
1188 return false;
1189 else
1190 if (parent())
1191 return parent()->isEnabled();
1192 else
1193 return true;
1194 }
1195
widgetAdded(WWidget * child)1196 void WWebWidget::widgetAdded(WWidget *child)
1197 {
1198 child->setParentWidget(this);
1199
1200 if (flags_.test(BIT_LOADED)) {
1201 doLoad(child);
1202 }
1203
1204 WApplication::instance()
1205 ->session()->renderer().updateFormObjects(this, false);
1206
1207 if (!transientImpl_)
1208 transientImpl_.reset(new TransientImpl());
1209 ++transientImpl_->addedChildren_;
1210
1211 emitChildrenChanged();
1212 }
1213
children()1214 std::vector<WWidget *> WWebWidget::children() const
1215 {
1216 std::vector<WWidget *> result;
1217
1218 iterateChildren
1219 ([&](WWidget *c) {
1220 result.push_back(c);
1221 });
1222
1223 return result;
1224 }
1225
setHideWithOffsets(bool how)1226 void WWebWidget::setHideWithOffsets(bool how)
1227 {
1228 if (how) {
1229 if (!flags_.test(BIT_HIDE_WITH_OFFSETS)) {
1230 flags_.set(BIT_HIDE_WITH_VISIBILITY);
1231 flags_.set(BIT_HIDE_WITH_OFFSETS);
1232
1233 resetLearnedSlot(&WWidget::show);
1234 resetLearnedSlot(&WWidget::hide);
1235
1236 if (parent())
1237 parent()->setHideWithOffsets(true);
1238 }
1239 }
1240 }
1241
setImplementLayoutSizeAware(bool aware)1242 void WWebWidget::setImplementLayoutSizeAware(bool aware)
1243 {
1244 if (!aware) {
1245 if (otherImpl_) {
1246 if (otherImpl_->resized_) {
1247 otherImpl_->resized_.reset();
1248
1249 std::string v = javaScriptMember(WT_RESIZE_JS);
1250 if (v.length() == 1)
1251 setJavaScriptMember(WT_RESIZE_JS, std::string());
1252 else
1253 addJavaScriptStatement(JavaScriptStatementType::SetMember, WT_RESIZE_JS);
1254 }
1255 }
1256 }
1257 }
1258
resized()1259 JSignal<int, int>& WWebWidget::resized()
1260 {
1261 if (!otherImpl_)
1262 otherImpl_.reset(new OtherImpl(this));
1263
1264 if (!otherImpl_->resized_) {
1265 otherImpl_->resized_.reset(new JSignal<int, int>(this, "resized"));
1266 otherImpl_->resized_->connect(this, &WWidget::layoutSizeChanged);
1267
1268 std::string v = javaScriptMember(WT_RESIZE_JS);
1269 if (v.empty())
1270 setJavaScriptMember(WT_RESIZE_JS, "0");
1271 else
1272 addJavaScriptStatement(JavaScriptStatementType::SetMember, WT_RESIZE_JS);
1273 }
1274
1275 return *otherImpl_->resized_;
1276 }
1277
updateDom(DomElement & element,bool all)1278 void WWebWidget::updateDom(DomElement& element, bool all)
1279 {
1280 WApplication *app = nullptr;
1281
1282 /*
1283 * determine display
1284 */
1285
1286 if (flags_.test(BIT_GEOMETRY_CHANGED) ||
1287 flags_.test(BIT_FLEX_BOX_CHANGED) ||
1288 (!flags_.test(BIT_HIDE_WITH_VISIBILITY) &&
1289 flags_.test(BIT_HIDDEN_CHANGED)) ||
1290 all) {
1291 if (flags_.test(BIT_HIDE_WITH_VISIBILITY) || !flags_.test(BIT_HIDDEN)) {
1292 const char *Inline = "inline";
1293 const char *InlineTable = "inline-table";
1294 const char *InlineBlock = "inline-block";
1295 const char *Block = "block";
1296 const char *Flex = "flex";
1297 const char *InlineFlex = "inline-flex";
1298 const char *Empty = "";
1299 const char *display = nullptr;
1300
1301 const bool defaultInline = element.type() == DomElementType::OTHER ? DomElement::isDefaultInline(domElementType()) : element.isDefaultInline();
1302 if (defaultInline != flags_.test(BIT_INLINE)) {
1303 if (flags_.test(BIT_INLINE)) {
1304 if (element.type() == DomElementType::TABLE)
1305 display = InlineTable;
1306 if (element.type() == DomElementType::LI)
1307 display = Inline;
1308 else if (element.type() != DomElementType::TD) {
1309 if (!app) app = WApplication::instance();
1310 if (app->environment().agentIsIElt(9)) {
1311 display = Inline;
1312 element.setProperty(Property::StyleZoom, "1");
1313 } else
1314 display = InlineBlock;
1315 }
1316 } else {
1317 display = Block;
1318 }
1319 } else if (!all && flags_.test(BIT_HIDDEN_CHANGED)) {
1320 if (defaultInline == flags_.test(BIT_INLINE))
1321 display = Empty;
1322 else
1323 display = flags_.test(BIT_INLINE) ? Inline : Block;
1324 }
1325
1326 if (flags_.test(BIT_FLEX_BOX))
1327 display = flags_.test(BIT_INLINE) ? InlineFlex : Flex;
1328 else if (flags_.test(BIT_FLEX_BOX_CHANGED) && !display)
1329 display = Empty;
1330
1331 if (display)
1332 element.setProperty(Property::StyleDisplay, display);
1333 } else
1334 element.setProperty(Property::StyleDisplay, "none");
1335 }
1336
1337 if (flags_.test(BIT_ZINDEX_CHANGED) || all) {
1338 if (layoutImpl_) {
1339 /*
1340 * set z-index
1341 */
1342 if (layoutImpl_->zIndex_ > 0) {
1343 element.setProperty(Property::StyleZIndex,
1344 std::to_string(layoutImpl_->zIndex_));
1345 element.addPropertyWord(Property::Class, "Wt-popup");
1346 if (!all &&
1347 !flags_.test(BIT_STYLECLASS_CHANGED) &&
1348 lookImpl_ && !lookImpl_->styleClass_.empty())
1349 element.addPropertyWord(Property::Class,
1350 lookImpl_->styleClass_.toUTF8());
1351
1352 if (!app) app = WApplication::instance();
1353 if (all && app->environment().agent() == UserAgent::IE6
1354 && element.type() == DomElementType::DIV) {
1355 DomElement *i = DomElement::createNew(DomElementType::IFRAME);
1356 i->setId("sh" + id());
1357 i->setProperty(Property::Class, "Wt-shim");
1358 i->setProperty(Property::Src, "javascript:false;");
1359 i->setAttribute("title", "Popup Shim");
1360 i->setAttribute("tabindex", "-1");
1361 i->setAttribute("frameborder", "0");
1362
1363 app->addAutoJavaScript
1364 ("{var w = " + jsRef() + ";"
1365 "if (w && !" WT_CLASS ".isHidden(w)) {"
1366 "var i = " WT_CLASS ".getElement('" + i->id() + "');"
1367 "i.style.width=w.clientWidth + 'px';"
1368 "i.style.height=w.clientHeight + 'px';"
1369 "}}");
1370
1371 element.addChild(i);
1372 }
1373 }
1374 }
1375
1376 flags_.reset(BIT_ZINDEX_CHANGED);
1377 }
1378
1379 if (flags_.test(BIT_GEOMETRY_CHANGED) || all) {
1380 if (layoutImpl_) {
1381 /*
1382 * set position
1383 */
1384 if (!(flags_.test(BIT_HIDE_WITH_VISIBILITY) && flags_.test(BIT_HIDDEN)))
1385 switch (layoutImpl_->positionScheme_) {
1386 case PositionScheme::Static:
1387 break;
1388 case PositionScheme::Relative:
1389 element.setProperty(Property::StylePosition, "relative"); break;
1390 case PositionScheme::Absolute:
1391 element.setProperty(Property::StylePosition, "absolute"); break;
1392 case PositionScheme::Fixed:
1393 element.setProperty(Property::StylePosition, "fixed"); break;
1394 }
1395
1396 /*
1397 * set clear: FIXME: multiple values
1398 */
1399 if (layoutImpl_->clearSides_ == Side::Left) {
1400 element.setProperty(Property::StyleClear, "left");
1401 } else if (layoutImpl_->clearSides_ == Side::Right) {
1402 element.setProperty(Property::StyleClear, "right");
1403 } else if (layoutImpl_->clearSides_ == (Side::Left | Side::Right)) {
1404 element.setProperty(Property::StyleClear, "both");
1405 }
1406
1407 if (layoutImpl_->minimumWidth_.value() != 0) {
1408 std::string text
1409 = layoutImpl_->minimumWidth_.isAuto() ? "0px"
1410 : layoutImpl_->minimumWidth_.cssText();
1411 element.setProperty(Property::StyleMinWidth, text);
1412 }
1413 if (layoutImpl_->minimumHeight_.value() != 0) {
1414 std::string text
1415 = layoutImpl_->minimumHeight_.isAuto() ? "0px"
1416 : layoutImpl_->minimumHeight_.cssText();
1417 element.setProperty(Property::StyleMinHeight, text);
1418 }
1419 if (!layoutImpl_->maximumWidth_.isAuto())
1420 element.setProperty(Property::StyleMaxWidth,
1421 layoutImpl_->maximumWidth_.cssText());
1422 if (!layoutImpl_->maximumHeight_.isAuto())
1423 element.setProperty(Property::StyleMaxHeight,
1424 layoutImpl_->maximumHeight_.cssText());
1425
1426 /*
1427 * set offsets
1428 */
1429 if (layoutImpl_->positionScheme_ != PositionScheme::Static) {
1430 static const Property properties[] = { Property::StyleTop,
1431 Property::StyleRight,
1432 Property::StyleBottom,
1433 Property::StyleLeft };
1434
1435 if (!layoutImpl_->offsets_[0].isAuto()
1436 || !layoutImpl_->offsets_[1].isAuto()
1437 || !layoutImpl_->offsets_[2].isAuto()
1438 || !layoutImpl_->offsets_[3].isAuto()) {
1439 for (unsigned i = 0; i < 4; ++i) {
1440 Property property = properties[i];
1441
1442 if (!app) app = WApplication::instance();
1443
1444 if (app->layoutDirection() == LayoutDirection::RightToLeft) {
1445 if (i == 1) property = properties[3];
1446 else if (i == 3) property = properties[1];
1447 }
1448
1449 if ((app->environment().ajax()
1450 && !app->environment().agentIsIElt(9))
1451 || !layoutImpl_->offsets_[i].isAuto())
1452 element.setProperty(property, layoutImpl_->offsets_[i].cssText());
1453 }
1454 }
1455 }
1456
1457 /*
1458 * set vertical alignment
1459 */
1460 switch (layoutImpl_->verticalAlignment_) {
1461 case AlignmentFlag::Baseline:
1462 break;
1463 case AlignmentFlag::Sub:
1464 element.setProperty(Property::StyleVerticalAlign, "sub"); break;
1465 case AlignmentFlag::Super:
1466 element.setProperty(Property::StyleVerticalAlign, "super"); break;
1467 case AlignmentFlag::Top:
1468 element.setProperty(Property::StyleVerticalAlign, "top"); break;
1469 case AlignmentFlag::TextTop:
1470 element.setProperty(Property::StyleVerticalAlign, "text-top"); break;
1471 case AlignmentFlag::Middle:
1472 element.setProperty(Property::StyleVerticalAlign, "middle"); break;
1473 case AlignmentFlag::Bottom:
1474 element.setProperty(Property::StyleVerticalAlign, "bottom"); break;
1475 case AlignmentFlag::TextBottom:
1476 element.setProperty(Property::StyleVerticalAlign, "text-bottom"); break;
1477 default:
1478 break;
1479 }
1480
1481 if (!layoutImpl_->lineHeight_.isAuto()) // == none
1482 element.setProperty(Property::StyleLineHeight,
1483 layoutImpl_->lineHeight_.cssText());
1484
1485 }
1486
1487 flags_.reset(BIT_GEOMETRY_CHANGED);
1488 }
1489
1490
1491 /*
1492 * set width & height
1493 */
1494 if (width_ && (flags_.test(BIT_WIDTH_CHANGED) || all)) {
1495 if (!all || !width_->isAuto())
1496 element.setProperty(Property::StyleWidth, width_->cssText());
1497 flags_.reset(BIT_WIDTH_CHANGED);
1498 }
1499
1500 if (height_ && (flags_.test(BIT_HEIGHT_CHANGED) || all)) {
1501 if (!all || !height_->isAuto())
1502 element.setProperty(Property::StyleHeight, height_->cssText());
1503 flags_.reset(BIT_HEIGHT_CHANGED);
1504 }
1505
1506 if (flags_.test(BIT_FLOAT_SIDE_CHANGED) || all) {
1507 if (layoutImpl_) {
1508 if (layoutImpl_->floatSide_ == static_cast<Side>(0)) {
1509 if (flags_.test(BIT_FLOAT_SIDE_CHANGED))
1510 element.setProperty(Property::StyleFloat, "none");
1511 }
1512 else {
1513 /*
1514 * set float
1515 */
1516 if (!app) app = WApplication::instance();
1517
1518 bool ltr = app->layoutDirection() == LayoutDirection::LeftToRight;
1519 switch (layoutImpl_->floatSide_) {
1520 case Side::Left:
1521 element.setProperty(Property::StyleFloat, ltr ? "left" : "right");
1522 break;
1523 case Side::Right:
1524 element.setProperty(Property::StyleFloat, ltr ? "right" : "left");
1525 break;
1526 default:
1527 /* illegal values */
1528 ;
1529 }
1530 }
1531 }
1532
1533 flags_.reset(BIT_FLOAT_SIDE_CHANGED);
1534 }
1535
1536 if (layoutImpl_) {
1537 bool changed = flags_.test(BIT_MARGINS_CHANGED);
1538 if (changed || all) {
1539 if (changed || (layoutImpl_->margin_[0].value() != 0))
1540 element.setProperty(Property::StyleMarginTop,
1541 layoutImpl_->margin_[0].cssText());
1542 if (changed || (layoutImpl_->margin_[1].value() != 0))
1543 element.setProperty(Property::StyleMarginRight,
1544 layoutImpl_->margin_[1].cssText());
1545 if (changed || (layoutImpl_->margin_[2].value() != 0))
1546 element.setProperty(Property::StyleMarginBottom,
1547 layoutImpl_->margin_[2].cssText());
1548 if (changed || (layoutImpl_->margin_[3].value() != 0))
1549 element.setProperty(Property::StyleMarginLeft,
1550 layoutImpl_->margin_[3].cssText());
1551
1552 flags_.reset(BIT_MARGINS_CHANGED);
1553 }
1554 }
1555
1556 if (lookImpl_) {
1557 if ((lookImpl_->toolTip_ || flags_.test(BIT_TOOLTIP_DEFERRED))
1558 && (flags_.test(BIT_TOOLTIP_CHANGED) || all)) {
1559 if (!all || (!lookImpl_->toolTip_->empty() ||
1560 flags_.test(BIT_TOOLTIP_DEFERRED))) {
1561 if (!app) app = WApplication::instance();
1562 if ( (lookImpl_->toolTipTextFormat_ != TextFormat::Plain
1563 || flags_.test(BIT_TOOLTIP_DEFERRED))
1564 && app->environment().ajax()) {
1565 LOAD_JAVASCRIPT(app, "js/ToolTip.js", "toolTip", wtjs10);
1566
1567 WString tooltipText(lookImpl_->toolTip_->toUTF8()); // UTF8 Guarantees copy for JWt
1568 if (lookImpl_->toolTipTextFormat_ == TextFormat::Plain) {
1569 tooltipText = escapeText(*lookImpl_->toolTip_);
1570 } else if (lookImpl_->toolTipTextFormat_ == TextFormat::XHTML) {
1571 bool res = removeScript(tooltipText);
1572 if (!res) {
1573 tooltipText = escapeText(*lookImpl_->toolTip_);
1574 }
1575 }
1576
1577 std::string deferred = flags_.test(BIT_TOOLTIP_DEFERRED) ?
1578 "true" : "false";
1579 element.callJavaScript(WT_CLASS ".toolTip(" +
1580 app->javaScriptClass() + ","
1581 + jsStringLiteral(id()) + ","
1582 + tooltipText.jsStringLiteral()
1583 + ", " + deferred
1584 + ", " +
1585 jsStringLiteral(app->theme()->
1586 utilityCssClass(ToolTipInner))
1587 + ", " +
1588 jsStringLiteral(app->theme()->
1589 utilityCssClass(ToolTipOuter))
1590 + ");");
1591
1592 if (flags_.test(BIT_TOOLTIP_DEFERRED) &&
1593 !lookImpl_->loadToolTip_.isConnected())
1594 lookImpl_->loadToolTip_.connect(this, &WWebWidget::loadToolTip);
1595
1596 element.removeAttribute("title");
1597 } else
1598 element.setAttribute("title", lookImpl_->toolTip_->toUTF8());
1599 }
1600
1601 flags_.reset(BIT_TOOLTIP_CHANGED);
1602 }
1603
1604 if (lookImpl_->decorationStyle_)
1605 lookImpl_->decorationStyle_->updateDomElement(element, all);
1606
1607 if (all || flags_.test(BIT_STYLECLASS_CHANGED))
1608 if (!all || !lookImpl_->styleClass_.empty())
1609 element.addPropertyWord(Property::Class, lookImpl_->styleClass_.toUTF8());
1610
1611 flags_.reset(BIT_STYLECLASS_CHANGED);
1612 }
1613
1614 if (!all && transientImpl_) {
1615 for (unsigned i = 0; i < transientImpl_->addedStyleClasses_.size(); ++i)
1616 element.callJavaScript("$('#" + id() + "').addClass('"
1617 + transientImpl_->addedStyleClasses_[i].toUTF8()
1618 +"');");
1619
1620 for (unsigned i = 0; i < transientImpl_->removedStyleClasses_.size(); ++i)
1621 element.callJavaScript("$('#" + id() + "').removeClass('"
1622 + transientImpl_->removedStyleClasses_[i].toUTF8()
1623 +"');");
1624
1625 if (!transientImpl_->childRemoveChanges_.empty()) {
1626 if ((int)children().size() != transientImpl_->addedChildren_
1627 || transientImpl_->specialChildRemove_) {
1628 for (unsigned i = 0; i < transientImpl_->childRemoveChanges_.size();
1629 ++i) {
1630 const std::string& js = transientImpl_->childRemoveChanges_[i];
1631 if (js[0] == '_')
1632 element.callJavaScript(WT_CLASS ".remove('" + js.substr(1) + "');",
1633 true);
1634 else
1635 element.callJavaScript(js, true);
1636 }
1637 } else
1638 element.removeAllChildren();
1639
1640 transientImpl_->addedChildren_ = 0;
1641 transientImpl_->childRemoveChanges_.clear();
1642 transientImpl_->specialChildRemove_ = false;
1643 }
1644 }
1645
1646 if (all || flags_.test(BIT_SELECTABLE_CHANGED)) {
1647 if (flags_.test(BIT_SET_UNSELECTABLE)) {
1648 element.addPropertyWord(Property::Class, "unselectable");
1649 element.setAttribute("unselectable", "on");
1650 element.setAttribute("onselectstart", "return false;");
1651 } else if (flags_.test(BIT_SET_SELECTABLE)) {
1652 element.addPropertyWord(Property::Class, "selectable");
1653 element.setAttribute("unselectable", "off");
1654 element.setAttribute("onselectstart",
1655 "event.cancelBubble=true; return true;");
1656 }
1657
1658 flags_.reset(BIT_SELECTABLE_CHANGED);
1659 }
1660
1661 if (otherImpl_) {
1662 if (otherImpl_->attributes_) {
1663 if (all) {
1664 for (std::map<std::string, WT_USTRING>::const_iterator i
1665 = otherImpl_->attributes_->begin();
1666 i != otherImpl_->attributes_->end(); ++i)
1667 if (i->first == "style")
1668 element.setProperty(Property::Style, i->second.toUTF8());
1669 else
1670 element.setAttribute(i->first, i->second.toUTF8());
1671 } else if (transientImpl_) {
1672 for (unsigned i = 0; i < transientImpl_->attributesSet_.size(); ++i) {
1673 std::string attr = transientImpl_->attributesSet_[i];
1674 if (attr == "style")
1675 element.setProperty(Property::Style,
1676 (*otherImpl_->attributes_)[attr].toUTF8());
1677 else
1678 element.setAttribute(attr,
1679 (*otherImpl_->attributes_)[attr].toUTF8());
1680 }
1681 }
1682 }
1683
1684 if (all && otherImpl_->jsMembers_) {
1685 for (unsigned i = 0; i < otherImpl_->jsMembers_->size(); i++) {
1686 OtherImpl::Member member = (*otherImpl_->jsMembers_)[i];
1687
1688 bool notHere = false;
1689 if (otherImpl_->jsStatements_) {
1690 for (unsigned j = 0; j < otherImpl_->jsStatements_->size(); ++j) {
1691 const OtherImpl::JavaScriptStatement& jss
1692 = (*otherImpl_->jsStatements_)[j];
1693
1694 if (jss.type == JavaScriptStatementType::SetMember &&
1695 jss.data == member.name) {
1696 notHere = true;
1697 break;
1698 }
1699 }
1700 }
1701
1702 if (notHere)
1703 continue;
1704
1705 declareJavaScriptMember(element, member.name, member.value);
1706 }
1707 }
1708
1709 if (otherImpl_->jsStatements_) {
1710 for (unsigned i = 0; i < otherImpl_->jsStatements_->size(); ++i) {
1711 const OtherImpl::JavaScriptStatement& jss
1712 = (*otherImpl_->jsStatements_)[i];
1713
1714 switch (jss.type) {
1715 case JavaScriptStatementType::SetMember:
1716 declareJavaScriptMember(element, jss.data,
1717 javaScriptMember(jss.data));
1718 break;
1719 case JavaScriptStatementType::CallMethod:
1720 element.callMethod(jss.data);
1721 break;
1722 case JavaScriptStatementType::Statement:
1723 element.callJavaScript(jss.data);
1724 break;
1725 }
1726 }
1727
1728 otherImpl_->jsStatements_.reset();
1729 }
1730 }
1731
1732 if (flags_.test(BIT_HIDE_WITH_VISIBILITY)) {
1733 if (flags_.test(BIT_HIDDEN_CHANGED)
1734 || (all && flags_.test(BIT_HIDDEN))) {
1735 if (flags_.test(BIT_HIDDEN)) {
1736 element.callJavaScript("$('#" + id() + "').addClass('Wt-hidden');");
1737 element.setProperty(Property::StyleVisibility, "hidden");
1738 if (flags_.test(BIT_HIDE_WITH_OFFSETS)) {
1739 element.setProperty(Property::StylePosition, "absolute");
1740 element.setProperty(Property::StyleTop, "-10000px");
1741 element.setProperty(Property::StyleLeft, "-10000px");
1742 }
1743 } else {
1744 if (flags_.test(BIT_HIDE_WITH_OFFSETS)) {
1745 if (layoutImpl_) {
1746 switch (layoutImpl_->positionScheme_) {
1747 case PositionScheme::Static:
1748 element.setProperty(Property::StylePosition, "static"); break;
1749 case PositionScheme::Relative:
1750 element.setProperty(Property::StylePosition, "relative"); break;
1751 case PositionScheme::Absolute:
1752 element.setProperty(Property::StylePosition, "absolute"); break;
1753 case PositionScheme::Fixed:
1754 element.setProperty(Property::StylePosition, "fixed"); break;
1755 }
1756
1757 if (!layoutImpl_->offsets_[0].isAuto())
1758 element.setProperty(Property::StyleTop,
1759 layoutImpl_->offsets_[0].cssText());
1760 else
1761 element.setProperty(Property::StyleTop, "");
1762
1763 if (!layoutImpl_->offsets_[3].isAuto())
1764 element.setProperty(Property::StyleLeft,
1765 layoutImpl_->offsets_[3].cssText());
1766 else
1767 element.setProperty(Property::StyleTop, "");
1768 } else {
1769 element.setProperty(Property::StylePosition, "static");
1770 element.setProperty(Property::StyleTop, "");
1771 element.setProperty(Property::StyleLeft, "");
1772 }
1773 }
1774 element.callJavaScript("$('#" + id() + "').removeClass('Wt-hidden');");
1775 element.setProperty(Property::StyleVisibility, "visible");
1776 }
1777 }
1778 }
1779
1780 if ((!all && flags_.test(BIT_HIDDEN_CHANGED))
1781 || (all && !flags_.test(BIT_HIDDEN))) {
1782 if (transientImpl_ && !transientImpl_->animation_.empty()) {
1783 const char *THIS_JS = "js/WWebWidget.js";
1784
1785 if (!app) app = WApplication::instance();
1786
1787 LOAD_JAVASCRIPT(app, THIS_JS, "animateDisplay", wtjs1);
1788 LOAD_JAVASCRIPT(app, THIS_JS, "animateVisible", wtjs2);
1789
1790 if (!flags_.test(BIT_HIDE_WITH_VISIBILITY)) {
1791 /*
1792 * Not using visibility: delay changing the display property
1793 */
1794 WStringStream ss;
1795 ss << WT_CLASS << ".animateDisplay("
1796 << app->javaScriptClass()
1797 << ",'" << id()
1798 << "'," << transientImpl_->animation_.effects().value()
1799 << "," << (int)transientImpl_->animation_.timingFunction()
1800 << "," << transientImpl_->animation_.duration()
1801 << ",'" << element.getProperty(Property::StyleDisplay)
1802 << "');";
1803 element.callJavaScript(ss.str());
1804
1805 if (all)
1806 element.setProperty(Property::StyleDisplay, "none");
1807 else
1808 element.removeProperty(Property::StyleDisplay);
1809 } else {
1810 /*
1811 * Using visibility: remember how to show/hide the widget
1812 */
1813 WStringStream ss;
1814 ss << WT_CLASS << ".animateVisible('" << id()
1815 << "'," << transientImpl_->animation_.effects().value()
1816 << "," << (int)transientImpl_->animation_.timingFunction()
1817 << "," << transientImpl_->animation_.duration()
1818 << ",'" << element.getProperty(Property::StyleVisibility)
1819 << "','" << element.getProperty(Property::StylePosition)
1820 << "','" << element.getProperty(Property::StyleTop)
1821 << "','" << element.getProperty(Property::StyleLeft)
1822 << "');";
1823 element.callJavaScript(ss.str());
1824
1825 if (all) {
1826 element.setProperty(Property::StyleVisibility, "hidden");
1827 element.setProperty(Property::StylePosition, "absolute");
1828 element.setProperty(Property::StyleTop, "-10000px");
1829 element.setProperty(Property::StyleLeft, "-10000px");
1830 } else {
1831 element.removeProperty(Property::StyleVisibility);
1832 element.removeProperty(Property::StylePosition);
1833 element.removeProperty(Property::StyleTop);
1834 element.removeProperty(Property::StyleLeft);
1835 }
1836 }
1837 }
1838 }
1839
1840 flags_.reset(BIT_HIDDEN_CHANGED);
1841
1842 if (flags_.test(BIT_GOT_FOCUS)) {
1843 if (!app) app = WApplication::instance();
1844 const WEnvironment& env = app->environment();
1845
1846 element.callJavaScript("setTimeout(function() {"
1847 """var o = " + jsRef() + ";"
1848 """if (o) {"
1849 "" "if (!$(o).hasClass('" +
1850 app->theme()->disabledClass() + "')) {"
1851 "" "try { "
1852 "" "o.focus();"
1853 "" "} catch (e) {}"
1854 "" "}"
1855 """}"
1856 "}, " + (env.agentIsIElt(9) ? "500" : "10") + ");");
1857
1858 flags_.reset(BIT_GOT_FOCUS);
1859 }
1860
1861 if (flags_.test(BIT_TABINDEX_CHANGED) || all) {
1862 if (otherImpl_ && otherImpl_->tabIndex_ != std::numeric_limits<int>::min())
1863 element.setProperty(Property::TabIndex,
1864 std::to_string(otherImpl_->tabIndex_));
1865 else if (!all)
1866 element.removeAttribute("tabindex");
1867
1868 flags_.reset(BIT_TABINDEX_CHANGED);
1869 }
1870
1871 if (all || flags_.test(BIT_SCROLL_VISIBILITY_CHANGED)) {
1872 const char *SCROLL_JS = "js/ScrollVisibility.js";
1873
1874 if (!app) app = WApplication::instance();
1875
1876 if (!app->javaScriptLoaded(SCROLL_JS) && scrollVisibilityEnabled()) {
1877 LOAD_JAVASCRIPT(app, SCROLL_JS, "ScrollVisibility", wtjs3);
1878 WStringStream ss;
1879 ss << "if (!" WT_CLASS ".scrollVisibility) {"
1880 WT_CLASS ".scrollVisibility = new ";
1881 ss << WT_CLASS ".ScrollVisibility(" << app->javaScriptClass() + "); }";
1882 element.callJavaScript(ss.str());
1883 }
1884
1885 if (scrollVisibilityEnabled()) {
1886 WStringStream ss;
1887 ss << WT_CLASS ".scrollVisibility.add({";
1888 ss << "el:" << jsRef() << ',';
1889 ss << "margin:" << scrollVisibilityMargin() << ',';
1890 ss << "visible:" << isScrollVisible();
1891 ss << "});";
1892 element.callJavaScript(ss.str());
1893 flags_.set(BIT_SCROLL_VISIBILITY_LOADED);
1894 } else if (flags_.test(BIT_SCROLL_VISIBILITY_LOADED)) {
1895 element.callJavaScript(WT_CLASS ".scrollVisibility.remove("
1896 + jsStringLiteral(id()) + ");");
1897 flags_.reset(BIT_SCROLL_VISIBILITY_LOADED);
1898 }
1899 flags_.reset(BIT_SCROLL_VISIBILITY_CHANGED);
1900 }
1901
1902 if (all || flags_.test(BIT_OBJECT_NAME_CHANGED)) {
1903 if (!objectName().empty()) {
1904 element.setAttribute("data-object-name", objectName());
1905 } else if (!all) {
1906 element.removeAttribute("data-object-name");
1907 }
1908 flags_.reset(BIT_OBJECT_NAME_CHANGED);
1909 }
1910
1911 renderOk();
1912
1913 transientImpl_.reset();
1914 }
1915
declareJavaScriptMember(DomElement & element,const std::string & name,const std::string & value)1916 void WWebWidget::declareJavaScriptMember(DomElement& element,
1917 const std::string& name,
1918 const std::string& value)
1919 {
1920 if (name[0] != ' ') {
1921 if (name == WT_RESIZE_JS && otherImpl_->resized_) {
1922 WStringStream combined;
1923 if (value.length() > 1) {
1924 combined << name << "=function(s,w,h) {"
1925 << WApplication::instance()->javaScriptClass()
1926 << "._p_.propagateSize(s,w,h);"
1927 << "(" << value << ")(s,w,h);"
1928 << "}";
1929 } else
1930 combined << name << "="
1931 << WApplication::instance()->javaScriptClass()
1932 << "._p_.propagateSize";
1933
1934 element.callMethod(combined.str());
1935 } else {
1936 if (value.length() > 0)
1937 element.callMethod(name + "=" + value);
1938 else
1939 element.callMethod(name + "=null");
1940 }
1941 } else
1942 element.callJavaScript(value);
1943 }
1944
isStubbed()1945 bool WWebWidget::isStubbed() const
1946 {
1947 if (flags_.test(BIT_STUBBED))
1948 return true;
1949 else {
1950 WWidget *p = parent();
1951 return p ? p->isStubbed() : false;
1952 }
1953 }
1954
needsToBeRendered()1955 bool WWebWidget::needsToBeRendered() const
1956 {
1957 /*
1958 * Returns whether this widget should be rendered. The only alternative
1959 * is to be stubbed as an invisible widget.
1960 */
1961 return flags_.test(BIT_DONOT_STUB)
1962 || !flags_.test(BIT_HIDDEN)
1963 || !WApplication::instance()->session()->renderer().visibleOnly();
1964 }
1965
setCanReceiveFocus(bool enabled)1966 void WWebWidget::setCanReceiveFocus(bool enabled)
1967 {
1968 setTabIndex(enabled ? 0 : std::numeric_limits<int>::min());
1969 }
1970
canReceiveFocus()1971 bool WWebWidget::canReceiveFocus() const
1972 {
1973 if (otherImpl_)
1974 return otherImpl_->tabIndex_ != std::numeric_limits<int>::min();
1975 else
1976 return false;
1977 }
1978
setTabIndex(int index)1979 void WWebWidget::setTabIndex(int index)
1980 {
1981 if (!otherImpl_)
1982 otherImpl_.reset(new OtherImpl(this));
1983
1984 otherImpl_->tabIndex_ = index;
1985
1986 flags_.set(BIT_TABINDEX_CHANGED);
1987 repaint();
1988 }
1989
tabIndex()1990 int WWebWidget::tabIndex() const
1991 {
1992 if (!otherImpl_)
1993 return canReceiveFocus() ? 0 : std::numeric_limits<int>::min();
1994 else
1995 return otherImpl_->tabIndex_;
1996 }
1997
setFirstFocus()1998 bool WWebWidget::setFirstFocus()
1999 {
2000 if (isVisible() && isEnabled()) {
2001 if (canReceiveFocus()) {
2002 setFocus(true);
2003 return true;
2004 }
2005
2006 bool result[1];
2007 result[0] = false;
2008 iterateChildren([&](Wt::WWidget *w){
2009 if (!result[0])
2010 result[0] = w->setFirstFocus();
2011 });
2012 if (result[0])
2013 return true;
2014
2015 return false;
2016 } else
2017 return false;
2018 }
2019
blurred()2020 EventSignal<>& WWebWidget::blurred()
2021 {
2022 return *voidEventSignal(BLUR_SIGNAL, true);
2023 }
2024
focussed()2025 EventSignal<>& WWebWidget::focussed()
2026 {
2027 return *voidEventSignal(FOCUS_SIGNAL, true);
2028 }
2029
setFocus(bool focus)2030 void WWebWidget::setFocus(bool focus)
2031 {
2032 flags_.set(BIT_GOT_FOCUS, focus);
2033 repaint();
2034
2035 WApplication *app = WApplication::instance();
2036 if (focus)
2037 app->setFocus(id(), -1, -1);
2038 else if (app->focus() == id())
2039 app->setFocus(std::string(), -1, -1);
2040 }
2041
undoSetFocus()2042 void WWebWidget::undoSetFocus()
2043 { }
2044
hasFocus()2045 bool WWebWidget::hasFocus() const
2046 {
2047 return WApplication::instance()->focus() == id();
2048 }
2049
getSFormObjects(FormObjectsMap & result)2050 void WWebWidget::getSFormObjects(FormObjectsMap& result)
2051 {
2052 if (!flags_.test(BIT_STUBBED) && !flags_.test(BIT_HIDDEN)
2053 && flags_.test(BIT_RENDERED))
2054 getFormObjects(result);
2055 }
2056
getFormObjects(FormObjectsMap & formObjects)2057 void WWebWidget::getFormObjects(FormObjectsMap& formObjects)
2058 {
2059 if (flags_.test(BIT_FORM_OBJECT))
2060 formObjects[id()] = this;
2061
2062 iterateChildren
2063 ([&](WWidget *c) {
2064 c->webWidget()->getSFormObjects(formObjects);
2065 });
2066 }
2067
getDomChanges(std::vector<DomElement * > & result,WApplication * app)2068 void WWebWidget::getDomChanges(std::vector<DomElement *>& result,
2069 WApplication *app)
2070 {
2071 DomElement *e = DomElement::getForUpdate(this, domElementType());
2072 updateDom(*e, false);
2073 result.push_back(e);
2074 }
2075
getSDomChanges(std::vector<DomElement * > & result,WApplication * app)2076 void WWebWidget::getSDomChanges(std::vector<DomElement *>& result,
2077 WApplication *app)
2078 {
2079 if (flags_.test(BIT_STUBBED)) {
2080 /*
2081 * If we are prelearning, we still want to catch changes to *this*
2082 * widget, since it could be related to the prelearning. Note that
2083 * this assumes that getDomChanges() does not attempt to do things
2084 * like recreating the widget...
2085 *
2086 * ... which is what happens in WContainerWidget, we make the exception
2087 * there...
2088 */
2089 if (app->session()->renderer().preLearning()) {
2090 getDomChanges(result, app);
2091 scheduleRerender(true);
2092 } else {
2093 if (!app->session()->renderer().visibleOnly()) {
2094 flags_.reset(BIT_STUBBED);
2095
2096 DomElement *stub = DomElement::getForUpdate(this, DomElementType::SPAN);
2097 WWidget *self = selfWidget();
2098 setRendered(true);
2099 self->render(RenderFlag::Full);
2100 DomElement *realElement = createDomElement(app);
2101 app->theme()->apply(self, *realElement, MainElement);
2102 stub->unstubWith(realElement, !flags_.test(BIT_HIDE_WITH_OFFSETS));
2103 result.push_back(stub);
2104 }
2105 }
2106 } else {
2107 render(RenderFlag::Update);
2108
2109 getDomChanges(result, app);
2110 }
2111 }
2112
doneRerender()2113 void WWebWidget::doneRerender()
2114 {
2115 iterateChildren
2116 ([](WWidget *c) {
2117 c->webWidget()->doneRerender();
2118 });
2119 }
2120
propagateRenderOk(bool deep)2121 void WWebWidget::propagateRenderOk(bool deep)
2122 {
2123 #ifndef WT_TARGET_JAVA
2124 flags_ &= ~AllChangeFlags;
2125 #else
2126 flags_.reset(BIT_FLEX_BOX_CHANGED);
2127 flags_.reset(BIT_HIDDEN_CHANGED);
2128 flags_.reset(BIT_GEOMETRY_CHANGED);
2129 flags_.reset(BIT_FLOAT_SIDE_CHANGED);
2130 flags_.reset(BIT_TOOLTIP_CHANGED);
2131 flags_.reset(BIT_MARGINS_CHANGED);
2132 flags_.reset(BIT_STYLECLASS_CHANGED);
2133 flags_.reset(BIT_SELECTABLE_CHANGED);
2134 flags_.reset(BIT_WIDTH_CHANGED);
2135 flags_.reset(BIT_HEIGHT_CHANGED);
2136 flags_.reset(BIT_DISABLED_CHANGED);
2137 flags_.reset(BIT_ZINDEX_CHANGED);
2138 flags_.reset(BIT_TABINDEX_CHANGED);
2139 flags_.reset(BIT_SCROLL_VISIBILITY_CHANGED);
2140 flags_.reset(BIT_OBJECT_NAME_CHANGED);
2141 #endif
2142
2143 renderOk();
2144
2145 if (deep)
2146 iterateChildren
2147 ([](WWidget *c) {
2148 c->webWidget()->propagateRenderOk();
2149 });
2150
2151 transientImpl_.reset();
2152 }
2153
setRendered(bool rendered)2154 void WWebWidget::setRendered(bool rendered)
2155 {
2156 if (rendered)
2157 flags_.set(BIT_RENDERED);
2158 else {
2159 flags_.reset(BIT_RENDERED);
2160
2161 renderOk();
2162
2163 iterateChildren
2164 ([](WWidget *c) {
2165 c->webWidget()->setRendered(false);
2166 });
2167 }
2168 }
2169
setLoadLaterWhenInvisible(bool how)2170 void WWebWidget::setLoadLaterWhenInvisible(bool how)
2171 {
2172 flags_.set(BIT_DONOT_STUB, !how);
2173 }
2174
setHtmlTagName(const std::string & tag)2175 void WWebWidget::setHtmlTagName(const std::string& tag)
2176 {
2177 if (!otherImpl_)
2178 otherImpl_.reset(new OtherImpl(this));
2179
2180 if (!otherImpl_->elementTagName_)
2181 otherImpl_->elementTagName_.reset(new std::string());
2182
2183 *otherImpl_->elementTagName_ = tag;
2184 }
2185
htmlTagName()2186 std::string WWebWidget::htmlTagName() const
2187 {
2188 if (otherImpl_ && otherImpl_->elementTagName_)
2189 return *otherImpl_->elementTagName_;
2190 else {
2191 DomElementType type = domElementType();
2192 return DomElement::tagName(type);
2193 }
2194 }
2195
setId(DomElement * element,WApplication * app)2196 void WWebWidget::setId(DomElement *element, WApplication *app)
2197 {
2198 if (!app->environment().agentIsSpiderBot()
2199 || id_) {
2200 if (!flags_.test(BIT_FORM_OBJECT))
2201 element->setId(id());
2202 else
2203 element->setName(id());
2204 }
2205 }
2206
find(const std::string & name)2207 WWidget *WWebWidget::find(const std::string& name)
2208 {
2209 if (objectName() == name)
2210 return this;
2211 else {
2212 WWidget *result[1];
2213 result[0] = nullptr;
2214 iterateChildren
2215 ([&](WWidget *c) {
2216 if (!result[0])
2217 result[0] = c->find(name);
2218 });
2219
2220 return result[0];
2221 }
2222 }
2223
findById(const std::string & id)2224 WWidget *WWebWidget::findById(const std::string& id)
2225 {
2226 if (this->id() == id)
2227 return this;
2228 else {
2229 WWidget *result[1];
2230 result[0] = nullptr;
2231 iterateChildren
2232 ([&](WWidget *c) {
2233 if (!result[0])
2234 result[0] = c->findById(id);
2235 });
2236 if (result[0])
2237 return result[0];
2238 }
2239
2240 return nullptr;
2241 }
2242
createDomElement(WApplication * app)2243 DomElement *WWebWidget::createDomElement(WApplication *app)
2244 {
2245 setRendered(true);
2246 DomElement *result;
2247
2248 if (otherImpl_ && otherImpl_->elementTagName_) {
2249 result = DomElement::createNew(DomElementType::OTHER);
2250 result->setDomElementTagName(*otherImpl_->elementTagName_);
2251 } else
2252 result = DomElement::createNew(domElementType());
2253
2254 setId(result, app);
2255 updateDom(*result, true);
2256
2257 return result;
2258 }
2259
domCanBeSaved()2260 bool WWebWidget::domCanBeSaved() const
2261 {
2262 bool canBeSaved[1];
2263 canBeSaved[0] = true;
2264 iterateChildren([&](WWidget *child){
2265 canBeSaved[0] = canBeSaved[0] && child->webWidget()->domCanBeSaved();
2266 });
2267 return canBeSaved[0];
2268 }
2269
isRendered()2270 bool WWebWidget::isRendered() const
2271 {
2272 return flags_.test(WWebWidget::BIT_RENDERED);
2273 }
2274
createStubElement(WApplication * app)2275 DomElement *WWebWidget::createStubElement(WApplication *app)
2276 {
2277 /*
2278 * Make sure the object itself is clean, so that stateless slot
2279 * learning is not confused.
2280 */
2281 propagateRenderOk();
2282
2283 flags_.set(BIT_STUBBED);
2284
2285 DomElement *stub = DomElement::createNew(DomElementType::SPAN);
2286 if (!flags_.test(BIT_HIDE_WITH_OFFSETS)) {
2287 stub->setProperty(Property::StyleDisplay, "none");
2288 } else {
2289 stub->setProperty(Property::StylePosition, "absolute");
2290 stub->setProperty(Property::StyleLeft, "-10000px");
2291 stub->setProperty(Property::StyleTop, "-10000px");
2292 stub->setProperty(Property::StyleVisibility, "hidden");
2293 }
2294 if (app->environment().javaScript())
2295 stub->setProperty(Property::InnerHTML, "...");
2296
2297 if (!app->environment().agentIsSpiderBot()
2298 || id_)
2299 stub->setId(id());
2300
2301 return stub;
2302 }
2303
createActualElement(WWidget * self,WApplication * app)2304 DomElement *WWebWidget::createActualElement(WWidget *self, WApplication *app)
2305 {
2306 flags_.reset(BIT_STUBBED);
2307
2308 DomElement *result = createDomElement(app);
2309
2310 app->theme()->apply(self, *result, MainElement);
2311
2312 /* Make sure addStyleClass() does not mess up later */
2313 std::string styleClass = result->getProperty(Property::Class);
2314 if (!styleClass.empty()) {
2315 if (!lookImpl_)
2316 lookImpl_.reset(new LookImpl(this));
2317
2318 lookImpl_->styleClass_ = styleClass;
2319 }
2320
2321 return result;
2322 }
2323
refresh()2324 void WWebWidget::refresh()
2325 {
2326 if (lookImpl_ && lookImpl_->toolTip_)
2327 if (lookImpl_->toolTip_->refresh()) {
2328 flags_.set(BIT_TOOLTIP_CHANGED);
2329 repaint();
2330 }
2331
2332 iterateChildren
2333 ([](WWidget *c) {
2334 c->refresh();
2335 });
2336
2337 WWidget::refresh();
2338 }
2339
enableAjax()2340 void WWebWidget::enableAjax()
2341 {
2342 /*
2343 * What needs to be done ? We want to get to the same state as the normal
2344 * AJAX bootstrap: thus still leaving stubs as is.
2345 */
2346 if (!isStubbed()) {
2347 for (EventSignalList::iterator i = eventSignals().begin();
2348 i != eventSignals().end(); ++i) {
2349 EventSignalBase& s = **i;
2350 if (s.name() == WInteractWidget::M_CLICK_SIGNAL)
2351 repaint(RepaintFlag::ToAjax);
2352
2353 s.ownerRepaint();
2354 }
2355 }
2356
2357 if (flags_.test(BIT_TOOLTIP_DEFERRED) ||
2358 (lookImpl_ && lookImpl_->toolTipTextFormat_ != TextFormat::Plain)) {
2359 flags_.set(BIT_TOOLTIP_CHANGED);
2360 repaint();
2361 }
2362
2363 iterateChildren
2364 ([](WWidget *c) {
2365 c->enableAjax();
2366 });
2367 }
2368
escapeText(const WString & text,bool newlinestoo)2369 WString WWebWidget::escapeText(const WString& text, bool newlinestoo)
2370 {
2371 std::string result = text.toUTF8();
2372 result = escapeText(result, newlinestoo);
2373 return WString::fromUTF8(result);
2374 }
2375
escapeText(std::string & text,bool newlinestoo)2376 std::string& WWebWidget::escapeText(std::string& text, bool newlinestoo)
2377 {
2378 EscapeOStream sout;
2379 if (newlinestoo)
2380 sout.pushEscape(EscapeOStream::PlainTextNewLines);
2381 else
2382 sout.pushEscape(EscapeOStream::Plain);
2383
2384 Wt::Utils::sanitizeUnicode(sout, text);
2385
2386 #ifndef WT_TARGET_JAVA
2387 text = sout.str();
2388 return text;
2389 #else
2390 return sout.str();
2391 #endif // WT_TARGET_JAVA
2392 }
2393
unescapeText(std::string & text)2394 std::string& WWebWidget::unescapeText(std::string &text)
2395 {
2396 #ifndef WT_TARGET_JAVA
2397 char *inP = &text[0];
2398 char *const inEndP = &text[text.size()];
2399 char *outP = &text[0];
2400 char *ampP = nullptr;
2401 do {
2402 assert(inP >= outP);
2403 ampP = std::find(inP, inEndP, '&');
2404 if (inP == outP) {
2405 inP = ampP;
2406 outP = ampP;
2407 } else {
2408 while (inP != ampP) {
2409 *(outP++) = *(inP++);
2410 }
2411 }
2412 assert(inP >= outP);
2413 if (inP != inEndP) {
2414 char *semiP = std::find(inP, inEndP, ';');
2415 if (semiP == inEndP)
2416 *(outP++) = *(inP++);
2417 else {
2418 if (*(ampP + 1) == '#') {
2419 unsigned long codept = 0;
2420 bool valid = false;
2421 if (*(ampP + 2) == 'x') {
2422 char *end = nullptr;
2423 codept = std::strtoul(ampP + 3, &end, 16);
2424 valid = end == semiP;
2425 } else {
2426 char *end = nullptr;
2427 codept = std::strtoul(ampP + 2, &end, 10);
2428 valid = end == semiP;
2429 }
2430 if (valid) {
2431 Wt::rapidxml::xml_document<>::insert_coded_character<0>(outP, codept);
2432 inP = semiP + 1;
2433 } else {
2434 *(outP++) = *(inP++);
2435 }
2436 } else {
2437 if (!rapidxml::translate_xhtml_entity(inP, outP)) {
2438 *(outP++) = *(inP++);
2439 }
2440 }
2441 }
2442 }
2443 } while (inP < inEndP);
2444 assert(inP >= outP);
2445 *outP = '\0';
2446 std::size_t s = (std::size_t)(outP - (&text[0]));
2447 assert(s <= text.size());
2448 text.resize(s);
2449 return text;
2450 #else // WT_TARGET_JAVA
2451 return doUnescapeText(text);
2452 #endif // WT_TARGET_JAVA
2453 }
2454
jsStringLiteral(const std::string & value,char delimiter)2455 std::string WWebWidget::jsStringLiteral(const std::string& value,
2456 char delimiter)
2457 {
2458 WStringStream result;
2459 DomElement::jsStringLiteral(result, value, delimiter);
2460 return result.str();
2461 }
2462
jsStringLiteral(const WString & value,char delimiter)2463 std::string WWebWidget::jsStringLiteral(const WString& value,
2464 char delimiter)
2465 {
2466 return value.jsStringLiteral(delimiter);
2467 }
2468
load()2469 void WWebWidget::load()
2470 {
2471 flags_.set(BIT_LOADED);
2472
2473 iterateChildren
2474 ([&](WWidget *c) {
2475 doLoad(c);
2476 });
2477
2478 if (flags_.test(BIT_HIDE_WITH_OFFSETS))
2479 parent()->setHideWithOffsets(true);
2480 }
2481
doLoad(WWidget * w)2482 void WWebWidget::doLoad(WWidget *w)
2483 {
2484 w->load();
2485 if (!w->loaded())
2486 LOG_ERROR("improper load() implementation: base implementation not called");
2487 }
2488
render(WFlags<RenderFlag> flags)2489 void WWebWidget::render(WFlags<RenderFlag> flags)
2490 {
2491 WWidget::render(flags);
2492 }
2493
doJavaScript(const std::string & javascript)2494 void WWebWidget::doJavaScript(const std::string& javascript)
2495 {
2496 addJavaScriptStatement(JavaScriptStatementType::Statement, javascript);
2497 repaint();
2498 }
2499
loaded()2500 bool WWebWidget::loaded() const
2501 {
2502 return flags_.test(BIT_LOADED);
2503 }
2504
DropMimeType(const WT_USTRING & aHoverStyleClass)2505 WWebWidget::DropMimeType::DropMimeType(const WT_USTRING& aHoverStyleClass)
2506 : hoverStyleClass(aHoverStyleClass)
2507 { }
2508
DropMimeType()2509 WWebWidget::DropMimeType::DropMimeType()
2510 { }
2511
setAcceptDropsImpl(const std::string & mimeType,bool accept,const WT_USTRING & hoverStyleClass)2512 bool WWebWidget::setAcceptDropsImpl(const std::string& mimeType, bool accept,
2513 const WT_USTRING& hoverStyleClass)
2514 {
2515 bool result = false; // whether the signal needs to be connected.
2516 bool changed = false;
2517
2518 if (!otherImpl_)
2519 otherImpl_.reset(new OtherImpl(this));
2520 if (!otherImpl_->acceptedDropMimeTypes_)
2521 otherImpl_->acceptedDropMimeTypes_.reset(new OtherImpl::MimeTypesMap);
2522
2523 OtherImpl::MimeTypesMap::iterator i
2524 = otherImpl_->acceptedDropMimeTypes_->find(mimeType);
2525
2526 if (i == otherImpl_->acceptedDropMimeTypes_->end()) {
2527 if (accept) {
2528 result = otherImpl_->acceptedDropMimeTypes_->empty();
2529 (*otherImpl_->acceptedDropMimeTypes_)[mimeType]
2530 = DropMimeType(hoverStyleClass);
2531 changed = true;
2532 }
2533 } else {
2534 if (!accept) {
2535 #ifndef WT_TARGET_JAVA
2536 otherImpl_->acceptedDropMimeTypes_->erase(i);
2537 #else
2538 otherImpl_->acceptedDropMimeTypes_->erase(mimeType);
2539 #endif // WT_TARGET_JAVA
2540 changed = true;
2541 }
2542 }
2543
2544 if (changed) {
2545 std::string mimeTypes = "";
2546
2547 for (OtherImpl::MimeTypesMap::const_iterator j
2548 = otherImpl_->acceptedDropMimeTypes_->begin();
2549 j != otherImpl_->acceptedDropMimeTypes_->end(); ++j) {
2550 mimeTypes
2551 += "{" + j->first + ":" + j->second.hoverStyleClass.toUTF8() + "}";
2552 }
2553 setAttributeValue("amts", mimeTypes);
2554 }
2555
2556 if (result && !otherImpl_->dropSignal_)
2557 otherImpl_->dropSignal_
2558 .reset(new JSignal<std::string,std::string, WMouseEvent>(this, "_drop"));
2559
2560 if (result && !otherImpl_->dropSignal2_)
2561 otherImpl_->dropSignal2_
2562 .reset(new JSignal<std::string,std::string, WTouchEvent>(this, "_drop2"));
2563
2564 return result;
2565 }
2566
keyEventSignal(const char * name,bool create)2567 EventSignal<WKeyEvent> *WWebWidget::keyEventSignal(const char *name,
2568 bool create)
2569 {
2570 EventSignalBase *b = getEventSignal(name);
2571 if (b)
2572 return static_cast<EventSignal<WKeyEvent> *>(b);
2573 else if (!create)
2574 return nullptr;
2575 else {
2576 #ifndef WT_TARGET_JAVA
2577 EventSignal<WKeyEvent> *result = new EventSignal<WKeyEvent>(name, this);
2578 #else
2579 EventSignal<WKeyEvent> *result
2580 = new EventSignal<WKeyEvent>(name, this, WKeyEvent::templateEvent);
2581 #endif // WT_TARGET_JAVA
2582
2583 addEventSignal(*result);
2584 return result;
2585 }
2586 }
2587
voidEventSignal(const char * name,bool create)2588 EventSignal<> *WWebWidget::voidEventSignal(const char *name, bool create)
2589 {
2590 EventSignalBase *b = getEventSignal(name);
2591 if (b)
2592 return static_cast<EventSignal<> *>(b);
2593 else if (!create)
2594 return nullptr;
2595 else {
2596 EventSignal<> *result = new EventSignal<>(name, this);
2597 addEventSignal(*result);
2598 return result;
2599 }
2600 }
2601
mouseEventSignal(const char * name,bool create)2602 EventSignal<WMouseEvent> *WWebWidget::mouseEventSignal(const char *name,
2603 bool create)
2604 {
2605 EventSignalBase *b = getEventSignal(name);
2606 if (b)
2607 return static_cast<EventSignal<WMouseEvent> *>(b);
2608 else if (!create)
2609 return nullptr;
2610 else {
2611 #ifndef WT_TARGET_JAVA
2612 EventSignal<WMouseEvent> *result = new EventSignal<WMouseEvent>(name, this);
2613 #else
2614 EventSignal<WMouseEvent> *result
2615 = new EventSignal<WMouseEvent>(name, this, WMouseEvent::templateEvent);
2616 #endif // WT_TARGET_JAVA
2617 addEventSignal(*result);
2618 return result;
2619 }
2620 }
2621
touchEventSignal(const char * name,bool create)2622 EventSignal<WTouchEvent> *WWebWidget::touchEventSignal(const char *name,
2623 bool create)
2624 {
2625 EventSignalBase *b = getEventSignal(name);
2626 if (b)
2627 return static_cast<EventSignal<WTouchEvent> *>(b);
2628 else if (!create)
2629 return nullptr;
2630 else {
2631 #ifndef WT_TARGET_JAVA
2632 EventSignal<WTouchEvent> *result = new EventSignal<WTouchEvent>(name, this);
2633 #else
2634 EventSignal<WTouchEvent> *result
2635 = new EventSignal<WTouchEvent>(name, this, WTouchEvent::templateEvent);
2636 #endif // WT_TARGET_JAVA
2637 addEventSignal(*result);
2638 return result;
2639 }
2640 }
2641
gestureEventSignal(const char * name,bool create)2642 EventSignal<WGestureEvent> *WWebWidget::gestureEventSignal(const char *name,
2643 bool create)
2644 {
2645 EventSignalBase *b = getEventSignal(name);
2646 if (b)
2647 return static_cast<EventSignal<WGestureEvent> *>(b);
2648 else if (!create)
2649 return nullptr;
2650 else {
2651 #ifndef WT_TARGET_JAVA
2652 EventSignal<WGestureEvent> *result
2653 = new EventSignal<WGestureEvent>(name, this);
2654 #else
2655 EventSignal<WGestureEvent> *result
2656 = new EventSignal<WGestureEvent>(name, this,
2657 WGestureEvent::templateEvent);
2658 #endif // WT_TARGET_JAVA
2659 addEventSignal(*result);
2660 return result;
2661 }
2662 }
2663
scrollEventSignal(const char * name,bool create)2664 EventSignal<WScrollEvent> *WWebWidget::scrollEventSignal(const char *name,
2665 bool create)
2666 {
2667 EventSignalBase *b = getEventSignal(name);
2668 if (b)
2669 return static_cast<EventSignal<WScrollEvent> *>(b);
2670 else if (!create)
2671 return nullptr;
2672 else {
2673 #ifndef WT_TARGET_JAVA
2674 EventSignal<WScrollEvent> *result
2675 = new EventSignal<WScrollEvent>(name, this);
2676 #else
2677 EventSignal<WScrollEvent> *result
2678 = new EventSignal<WScrollEvent>(name, this, WScrollEvent::templateEvent);
2679 #endif // WT_TARGET_JAVA
2680 addEventSignal(*result);
2681 return result;
2682 }
2683 }
2684
updateSignalConnection(DomElement & element,EventSignalBase & signal,const char * name,bool all)2685 void WWebWidget::updateSignalConnection(DomElement& element,
2686 EventSignalBase &signal,
2687 const char *name,
2688 bool all)
2689 {
2690 if (name[0] != 'M' && signal.needsUpdate(all)) {
2691 element.setEventSignal(name, signal);
2692 signal.updateOk();
2693 }
2694 }
2695
canOptimizeUpdates()2696 bool WWebWidget::canOptimizeUpdates()
2697 {
2698 return !WApplication::instance()->session()->renderer().preLearning();
2699 }
2700
resolveRelativeUrl(const std::string & url)2701 std::string WWebWidget::resolveRelativeUrl(const std::string& url)
2702 {
2703 return WApplication::instance()->resolveRelativeUrl(url);
2704 }
2705
removeScript(WString & text)2706 bool WWebWidget::removeScript(WString& text)
2707 {
2708 #ifndef WT_NO_XSS_FILTER
2709 return XSSFilterRemoveScript(text);
2710 #else
2711 return true;
2712 #endif
2713 }
2714
scrollVisibilityEnabled()2715 bool WWebWidget::scrollVisibilityEnabled() const
2716 {
2717 return flags_.test(BIT_SCROLL_VISIBILITY_ENABLED);
2718 }
2719
setScrollVisibilityEnabled(bool enabled)2720 void WWebWidget::setScrollVisibilityEnabled(bool enabled)
2721 {
2722 if (enabled) {
2723 if (!otherImpl_)
2724 otherImpl_.reset(new OtherImpl(this));
2725 if (!otherImpl_->jsScrollVisibilityChanged_) {
2726 otherImpl_->jsScrollVisibilityChanged_.reset(new JSignal<bool>(this, "scrollVisibilityChanged"));
2727 otherImpl_->jsScrollVisibilityChanged_->connect(this, &WWebWidget::jsScrollVisibilityChanged);
2728 }
2729 }
2730
2731 if (scrollVisibilityEnabled() != enabled) {
2732 flags_.set(BIT_SCROLL_VISIBILITY_ENABLED, enabled);
2733 flags_.set(BIT_SCROLL_VISIBILITY_CHANGED);
2734 repaint();
2735 }
2736 }
2737
scrollVisibilityMargin()2738 int WWebWidget::scrollVisibilityMargin() const
2739 {
2740 if (!otherImpl_)
2741 return 0;
2742 else
2743 return otherImpl_->scrollVisibilityMargin_;
2744 }
2745
setScrollVisibilityMargin(int margin)2746 void WWebWidget::setScrollVisibilityMargin(int margin)
2747 {
2748 if (scrollVisibilityMargin() != margin) {
2749 if (!otherImpl_)
2750 otherImpl_.reset(new OtherImpl(this));
2751 otherImpl_->scrollVisibilityMargin_ = margin;
2752 if (scrollVisibilityEnabled()) {
2753 flags_.set(BIT_SCROLL_VISIBILITY_CHANGED);
2754 repaint();
2755 }
2756 }
2757 }
2758
scrollVisibilityChanged()2759 Signal<bool> &WWebWidget::scrollVisibilityChanged()
2760 {
2761 if (!otherImpl_)
2762 otherImpl_.reset(new OtherImpl(this));
2763
2764 return otherImpl_->scrollVisibilityChanged_;
2765 }
2766
isScrollVisible()2767 bool WWebWidget::isScrollVisible() const
2768 {
2769 return flags_.test(BIT_IS_SCROLL_VISIBLE);
2770 }
2771
jsScrollVisibilityChanged(bool visible)2772 void WWebWidget::jsScrollVisibilityChanged(bool visible)
2773 {
2774 flags_.set(BIT_IS_SCROLL_VISIBLE, visible);
2775 if (otherImpl_)
2776 otherImpl_->scrollVisibilityChanged_.emit(visible);
2777 }
2778
setThemeStyleEnabled(bool enabled)2779 void WWebWidget::setThemeStyleEnabled(bool enabled)
2780 {
2781 flags_.set(BIT_THEME_STYLE_DISABLED, !enabled);
2782 }
2783
isThemeStyleEnabled()2784 bool WWebWidget::isThemeStyleEnabled() const
2785 {
2786 return !flags_.test(BIT_THEME_STYLE_DISABLED);
2787 }
2788
setObjectName(const std::string & name)2789 void WWebWidget::setObjectName(const std::string& name)
2790 {
2791 if (objectName() != name) {
2792 WWidget::setObjectName(name);
2793 flags_.set(BIT_OBJECT_NAME_CHANGED);
2794 repaint();
2795 }
2796 }
2797
baseZIndex()2798 int WWebWidget::baseZIndex() const
2799 {
2800 if (!layoutImpl_)
2801 return DEFAULT_BASE_Z_INDEX;
2802 else
2803 return layoutImpl_->baseZIndex_;
2804 }
2805
setBaseZIndex(int zIndex)2806 void WWebWidget::setBaseZIndex(int zIndex)
2807 {
2808 if (!layoutImpl_)
2809 layoutImpl_.reset(new LayoutImpl());
2810
2811 layoutImpl_->baseZIndex_ = zIndex;
2812 }
2813
emitChildrenChanged()2814 void WWebWidget::emitChildrenChanged()
2815 {
2816 if (!flags_.test(BIT_BEING_DELETED) && otherImpl_) {
2817 otherImpl_->childrenChanged_.emit();
2818 }
2819 }
2820
2821 }
2822