1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include "Wt/WApplication.h"
7 #include "Wt/WException.h"
8 #include "Wt/WWidget.h"
9 #include "Wt/WWebWidget.h"
10 #include "Wt/WCompositeWidget.h"
11 #include "Wt/WContainerWidget.h"
12 #include "Wt/WLayout.h"
13 #include "Wt/WJavaScript.h"
14
15 #include "DomElement.h"
16 #include "EscapeOStream.h"
17 #include "WebRenderer.h"
18 #include "WebSession.h"
19
20 namespace Wt {
21
22 const char *WWidget::WT_RESIZE_JS = "wtResize";
23 const char *WWidget::WT_GETPS_JS = "wtGetPS";
24
25 LOGGER("WWidget");
26
WWidget()27 WWidget::WWidget()
28 : parent_(nullptr)
29 {
30 flags_.set(BIT_NEED_RERENDER);
31 }
32
~WWidget()33 WWidget::~WWidget()
34 {
35 while (!eventSignals_.empty()) {
36 EventSignalBase *s = eventSignals_.front();
37 eventSignals_.pop_front();
38 #ifndef WT_TARGET_JAVA
39 delete s;
40 #else
41 s->~EventSignalBase();
42 #endif
43 }
44
45 renderOk();
46 }
47
removeWidget(WWidget * widget)48 std::unique_ptr<WWidget> WWidget::removeWidget(WWidget *widget)
49 {
50 throw std::logic_error("WWidget::removeWidget() ought not to be called");
51 }
52
setParentWidget(WWidget * p)53 void WWidget::setParentWidget(WWidget *p)
54 {
55 parent_ = p;
56 }
57
removeFromParent()58 std::unique_ptr<WWidget> WWidget::removeFromParent()
59 {
60 WWidget *p = parent();
61 if (p)
62 return p->removeWidget(this);
63 else
64 return std::unique_ptr<WWidget>();
65 }
66
refresh()67 void WWidget::refresh()
68 { }
69
resize(const WLength & width,const WLength & height)70 void WWidget::resize(const WLength& width, const WLength& height)
71 {
72 setJsSize();
73 }
74
setHeight(const WLength & height)75 void WWidget::setHeight(const WLength& height)
76 {
77 resize(width(), height);
78 }
79
setWidth(const WLength & width)80 void WWidget::setWidth(const WLength& width)
81 {
82 resize(width, height());
83 }
84
setJsSize()85 void WWidget::setJsSize()
86 {
87 if (!height().isAuto() && height().unit() != LengthUnit::Percentage
88 && !javaScriptMember(WT_RESIZE_JS).empty())
89 callJavaScriptMember
90 (WT_RESIZE_JS, jsRef() + ","
91 + std::to_string(width().toPixels()) + ","
92 + std::to_string(height().toPixels()) + ","
93 + "false");
94 }
95
render(WFlags<RenderFlag> flags)96 void WWidget::render(WFlags<RenderFlag> flags)
97 { }
98
isRendered()99 bool WWidget::isRendered() const
100 {
101 WWidget *self = const_cast<WWidget *>(this);
102 return self->webWidget()->isRendered();
103 }
104
renderOk()105 void WWidget::renderOk()
106 {
107 if (flags_.test(BIT_NEED_RERENDER)) {
108 flags_.reset(BIT_NEED_RERENDER);
109 flags_.reset(BIT_NEED_RERENDER_SIZE_CHANGE);
110 WApplication *app = WApplication::instance();
111 if (app)
112 app->session()->renderer().doneUpdate(this);
113 }
114 }
115
scheduleRender(WFlags<RepaintFlag> flags)116 void WWidget::scheduleRender(WFlags<RepaintFlag> flags)
117 {
118 scheduleRerender(false, flags);
119 }
120
scheduleRerender(bool laterOnly,WFlags<RepaintFlag> flags)121 void WWidget::scheduleRerender(bool laterOnly, WFlags<RepaintFlag> flags)
122 {
123 if (!flags_.test(BIT_NEED_RERENDER)) {
124 flags_.set(BIT_NEED_RERENDER);
125 WApplication::instance()->session()->renderer().needUpdate(this, laterOnly);
126 }
127
128 if (flags.test(RepaintFlag::SizeAffected) &&
129 !flags_.test(BIT_NEED_RERENDER_SIZE_CHANGE)) {
130 flags_.set(BIT_NEED_RERENDER_SIZE_CHANGE);
131
132 webWidget()->parentResized(this, Orientation::Vertical);
133
134 /*
135 * A size change to an absolutely positioned widget will not affect
136 * a layout computation, except if it's itself in a layout!
137 */
138 if (positionScheme() == PositionScheme::Absolute && !isInLayout())
139 return;
140
141 /*
142 * Propagate event up, this will be caught by a container widget
143 * with a layout manager.
144 */
145 WWidget *p = parent();
146
147 if (p)
148 p->childResized(this, Orientation::Vertical);
149 }
150 }
151
childResized(WWidget * child,WFlags<Orientation> directions)152 void WWidget::childResized(WWidget *child, WFlags<Orientation> directions)
153 {
154 /*
155 * Stop propagation at an absolutely positioned widget
156 */
157 if (positionScheme() == PositionScheme::Absolute && !isInLayout())
158 return;
159
160 WWidget *p = parent();
161
162 if (p)
163 p->childResized(this, directions);
164 }
165
setStyleClass(const char * styleClass)166 void WWidget::setStyleClass(const char *styleClass)
167 {
168 setStyleClass(WString::fromUTF8(styleClass));
169 }
170
addStyleClass(const char * styleClass,bool force)171 void WWidget::addStyleClass(const char *styleClass, bool force)
172 {
173 addStyleClass(WString::fromUTF8(styleClass), force);
174 }
175
removeStyleClass(const char * styleClass,bool force)176 void WWidget::removeStyleClass(const char *styleClass, bool force)
177 {
178 removeStyleClass(WString::fromUTF8(styleClass), force);
179 }
180
toggleStyleClass(const WT_USTRING & styleClass,bool add,bool force)181 void WWidget::toggleStyleClass(const WT_USTRING& styleClass, bool add,
182 bool force)
183 {
184 if (add)
185 addStyleClass(styleClass, force);
186 else
187 removeStyleClass(styleClass, force);
188 }
189
toggleStyleClass(const char * styleClass,bool add,bool force)190 void WWidget::toggleStyleClass(const char *styleClass, bool add, bool force)
191 {
192 toggleStyleClass(WString::fromUTF8(styleClass), add, force);
193 }
194
hide()195 void WWidget::hide()
196 {
197 flags_.set(BIT_WAS_HIDDEN, isHidden());
198 setHidden(true);
199 }
200
show()201 void WWidget::show()
202 {
203 flags_.set(BIT_WAS_HIDDEN, isHidden());
204 setHidden(false);
205 }
206
animateShow(const WAnimation & animation)207 void WWidget::animateShow(const WAnimation& animation)
208 {
209 setHidden(false, animation);
210 }
211
animateHide(const WAnimation & animation)212 void WWidget::animateHide(const WAnimation& animation)
213 {
214 setHidden(true, animation);
215 }
216
disable()217 void WWidget::disable()
218 {
219 flags_.set(BIT_WAS_DISABLED, isDisabled());
220 setDisabled(true);
221 }
222
enable()223 void WWidget::enable()
224 {
225 flags_.set(BIT_WAS_DISABLED, isDisabled());
226 setDisabled(false);
227 }
228
229 #ifndef WT_TARGET_JAVA
getStateless(Method method)230 WStatelessSlot *WWidget::getStateless(Method method)
231 {
232 if (method == static_cast<WObject::Method>(&WWidget::hide))
233 return implementStateless(&WWidget::hide, &WWidget::undoHideShow);
234 else if (method == static_cast<WObject::Method>(&WWidget::show))
235 return implementStateless(&WWidget::show, &WWidget::undoHideShow);
236 else if (method == static_cast<WObject::Method>(&WWidget::enable))
237 return implementStateless(&WWidget::enable, &WWidget::undoDisableEnable);
238 else if (method == static_cast<WObject::Method>(&WWidget::disable))
239 return implementStateless(&WWidget::disable, &WWidget::undoDisableEnable);
240 else
241 return WObject::getStateless(method);
242 }
243 #endif // WT_TARGET_JAVA
244
undoHideShow()245 void WWidget::undoHideShow()
246 {
247 setHidden(flags_.test(BIT_WAS_HIDDEN));
248 }
249
undoDisableEnable()250 void WWidget::undoDisableEnable()
251 {
252 setDisabled(flags_.test(BIT_WAS_DISABLED));
253 }
254
255 #ifdef WT_TARGET_JAVA
resize(int widthPixels,int heightPixels)256 void WWidget::resize(int widthPixels, int heightPixels)
257 {
258 resize(WLength(widthPixels), WLength(heightPixels));
259 }
260
setMargin(int pixels,WFlags<Side> sides)261 void WWidget::setMargin(int pixels, WFlags<Side> sides)
262 {
263 setMargin(WLength(pixels), sides);
264 }
265
setOffsets(int pixels,WFlags<Side> sides)266 void WWidget::setOffsets(int pixels, WFlags<Side> sides)
267 {
268 setOffsets(WLength(pixels), sides);
269 }
270 #endif // WT_TARGET_JAVA
271
jsRef()272 std::string WWidget::jsRef() const
273 {
274 return WT_CLASS ".$('" + id() + "')";
275 }
276
htmlText(std::ostream & out)277 void WWidget::htmlText(std::ostream& out)
278 {
279 DomElement *element = createSDomElement(WApplication::instance());
280
281 DomElement::TimeoutList timeouts;
282 EscapeOStream sout(out);
283 EscapeOStream js;
284 element->asHTML(sout, js, timeouts);
285
286 WApplication::instance()->doJavaScript(js.str());
287
288 delete element;
289 }
290
inlineCssStyle()291 std::string WWidget::inlineCssStyle()
292 {
293 WWebWidget *ww = webWidget();
294 DomElement *e = DomElement::getForUpdate(ww, ww->domElementType());
295 ww->updateDom(*e, true);
296 std::string result = e->cssStyle();
297 delete e;
298 return result;
299 }
300
adam()301 WWidget *WWidget::adam()
302 {
303 WWidget *p = parent();
304 return p ? p->adam() : this;
305 }
306
tr(const char * key)307 WString WWidget::tr(const char *key)
308 {
309 return WString::tr(key);
310 }
311
tr(const std::string & key)312 WString WWidget::tr(const std::string& key)
313 {
314 return WString::tr(key);
315 }
316
setFocus()317 void WWidget::setFocus()
318 {
319 setFocus(true);
320 }
321
acceptDrops(const std::string & mimeType,const WT_USTRING & hoverStyleClass)322 void WWidget::acceptDrops(const std::string& mimeType,
323 const WT_USTRING& hoverStyleClass)
324 {
325 WWebWidget *thisWebWidget = webWidget();
326
327 if (thisWebWidget->setAcceptDropsImpl(mimeType, true, hoverStyleClass)) {
328 thisWebWidget->otherImpl_->dropSignal_->connect(this, &WWidget::getDrop);
329 thisWebWidget->otherImpl_->dropSignal2_->connect(this, &WWidget::getDropTouch);
330 }
331 }
332
stopAcceptDrops(const std::string & mimeType)333 void WWidget::stopAcceptDrops(const std::string& mimeType)
334 {
335 WWebWidget *thisWebWidget = webWidget();
336
337 thisWebWidget->setAcceptDropsImpl(mimeType, false, "");
338 }
339
getDrop(const std::string sourceId,const std::string mimeType,WMouseEvent event)340 void WWidget::getDrop(const std::string sourceId, const std::string mimeType,
341 WMouseEvent event)
342 {
343 WDropEvent e(WApplication::instance()->decodeObject(sourceId), mimeType,
344 event);
345
346 dropEvent(e);
347 }
348
getDropTouch(const std::string sourceId,const std::string mimeType,WTouchEvent event)349 void WWidget::getDropTouch(const std::string sourceId, const std::string mimeType,
350 WTouchEvent event)
351 {
352 WDropEvent e(WApplication::instance()->decodeObject(sourceId), mimeType,
353 event);
354
355 dropEvent(e);
356 }
357
dropEvent(WDropEvent event)358 void WWidget::dropEvent(WDropEvent event)
359 { }
360
createSDomElement(WApplication * app)361 DomElement *WWidget::createSDomElement(WApplication *app)
362 {
363 if (!needsToBeRendered()) {
364 DomElement *result = webWidget()->createStubElement(app);
365 renderOk();
366 scheduleRerender(true);
367 return result;
368 } else {
369 webWidget()->setRendered(true);
370 render(RenderFlag::Full);
371 return webWidget()->createActualElement(this, app);
372 }
373 }
374
createJavaScript(WStringStream & js,std::string insertJS)375 std::string WWidget::createJavaScript(WStringStream& js,
376 std::string insertJS)
377 {
378 WApplication *app = WApplication::instance();
379 DomElement *de = createSDomElement(app);
380
381 std::string var = de->createVar();
382 if (!insertJS.empty())
383 insertJS += var + ");";
384 de->createElement(js, app, insertJS);
385
386 delete de;
387
388 return var;
389 }
390
addEventSignal(EventSignalBase & s)391 void WWidget::addEventSignal(EventSignalBase& s)
392 {
393 eventSignals_.push_back(&s);
394 }
395
getEventSignal(const char * name)396 EventSignalBase *WWidget::getEventSignal(const char *name)
397 {
398 for (EventSignalList::iterator i = eventSignals_.begin();
399 i != eventSignals_.end(); ++i) {
400 EventSignalBase& s = **i;
401 if (s.name() == name)
402 return &s;
403 }
404
405 return nullptr;
406 }
407
boxPadding(Orientation orientation)408 int WWidget::boxPadding(Orientation orientation) const
409 {
410 return 0;
411 }
412
boxBorder(Orientation orientation)413 int WWidget::boxBorder(Orientation orientation) const
414 {
415 return 0;
416 }
417
positionAt(const WWidget * widget,Orientation orientation)418 void WWidget::positionAt(const WWidget *widget, Orientation orientation)
419 {
420 if (isHidden())
421 show();
422
423 std::string side = (orientation == Orientation::Horizontal
424 ? ".Horizontal" : ".Vertical");
425
426 doJavaScript(WT_CLASS ".positionAtWidget('"
427 + id() + "','"
428 + widget->id() + "',"
429 WT_CLASS + side + ");");
430 }
431
setLayoutSizeAware(bool aware)432 void WWidget::setLayoutSizeAware(bool aware)
433 {
434 if (aware == flags_.test(BIT_RESIZE_AWARE))
435 return;
436
437 flags_.set(BIT_RESIZE_AWARE, aware);
438
439 if (aware) {
440 /*
441 * Signals are created in a memory pool maintained by the application.
442 * WPaintedWidget can be used offline.
443 */
444 if (!WApplication::instance())
445 return;
446
447 WWebWidget *w = webWidget();
448 if (w == this)
449 webWidget()->resized();
450 else
451 webWidget()->resized().connect(this, &WWidget::layoutSizeChanged);
452 } else
453 webWidget()->setImplementLayoutSizeAware(false);
454 }
455
layoutSizeAware()456 bool WWidget::layoutSizeAware() const
457 {
458 return flags_.test(BIT_RESIZE_AWARE);
459 }
460
layoutSizeChanged(int width,int height)461 void WWidget::layoutSizeChanged(int width, int height)
462 { }
463
isInLayout()464 bool WWidget::isInLayout() const
465 {
466 WWidget *p = parent();
467 if (p != nullptr &&
468 (dynamic_cast<WCompositeWidget *>(p) != nullptr ||
469 !p->javaScriptMember(WT_RESIZE_JS).empty()))
470 return p->isInLayout();
471
472 WContainerWidget *c = dynamic_cast<WContainerWidget *>(p);
473
474 return c != nullptr && c->layout() != nullptr;
475 }
476
setTabOrder(WWidget * first,WWidget * second)477 void WWidget::setTabOrder(WWidget *first, WWidget *second)
478 {
479 second->setTabIndex(first->tabIndex() + 1);
480 }
481
hasParent()482 bool WWidget::hasParent() const
483 {
484 return parent_;
485 }
486
isExposed(WWidget * w)487 bool WWidget::isExposed(WWidget *w)
488 {
489 if (w == this)
490 return true;
491
492 for (WWidget *p = w; p; p = p->parent())
493 if (p == this)
494 return true;
495
496 return false;
497 }
498
addCssRule(const std::string & selector,const std::string & declarations,const std::string & ruleName)499 WCssTextRule *WWidget::addCssRule(const std::string& selector,
500 const std::string& declarations,
501 const std::string& ruleName)
502 {
503 WApplication *app = WApplication::instance();
504 std::unique_ptr<WCssTextRule> rule(new WCssTextRule(selector, declarations));
505 WCssTextRule *result = rule.get();
506 app->styleSheet().addRule(std::move(rule), ruleName);
507 return result;
508 }
509
addJSignal(EventSignalBase * signal)510 void WWidget::addJSignal(EventSignalBase* signal)
511 {
512 jsignals_.push_back(signal);
513 }
514
isGlobalWidget()515 bool WWidget::isGlobalWidget() const
516 {
517 return flags_.test(BIT_GLOBAL_WIDGET);
518 }
519
setGlobalWidget(bool globalWidget)520 void WWidget::setGlobalWidget(bool globalWidget)
521 {
522 flags_.set(BIT_GLOBAL_WIDGET, globalWidget);
523 }
524
525 }
526