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