1 /*
2 * Copyright (C) 2012 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6
7 #include "Wt/WApplication.h"
8 #include "Wt/WContainerWidget.h"
9 #include "Wt/WEnvironment.h"
10 #include "Wt/WGridLayout.h"
11 #include "Wt/WLogger.h"
12
13 #include "StdGridLayoutImpl2.h"
14 #include "SizeHandle.h"
15 #include "DomElement.h"
16 #include "WebUtils.h"
17
18 #ifndef WT_DEBUG_JS
19 #include "js/StdGridLayoutImpl2.min.js"
20 #include "js/WtResize.min.js"
21 #endif
22
23 #ifdef WT_WIN32
24 #define snprintf _snprintf
25 #endif
26
27 namespace Wt {
28
29 LOGGER("WGridLayout2");
30
StdGridLayoutImpl2(WLayout * layout,Impl::Grid & grid)31 StdGridLayoutImpl2::StdGridLayoutImpl2(WLayout *layout, Impl::Grid& grid)
32 : StdLayoutImpl(layout),
33 grid_(grid),
34 needAdjust_(false),
35 needRemeasure_(false),
36 needConfigUpdate_(false)
37 {
38 const char *THIS_JS = "js/StdGridLayoutImpl2.js";
39
40 WApplication *app = WApplication::instance();
41
42 if (!app->javaScriptLoaded(THIS_JS)) {
43 app->styleSheet().addRule("table.Wt-hcenter", "margin: 0px auto;"
44 "position: relative");
45
46 LOAD_JAVASCRIPT(app, THIS_JS, "StdLayout2", wtjs1);
47 LOAD_JAVASCRIPT(app, THIS_JS, "layouts2", appjs1);
48
49 app->doJavaScript(app->javaScriptClass() + ".layouts2.scheduleAdjust();");
50 app->doJavaScript("(function(){"
51 "var f=function(){"
52 + app->javaScriptClass() + ".layouts2.scheduleAdjust();"
53 "};"
54 "if($().jquery.indexOf('1.') === 0)"
55 "$(window).load(f);"
56 "else "
57 "$(window).on('load',f);"
58 "})();");
59
60 WApplication::instance()->addAutoJavaScript
61 ("if(" + app->javaScriptClass() + ".layouts2) "
62 + app->javaScriptClass() + ".layouts2.adjustNow();");
63 }
64 }
65
itemResized(WLayoutItem * item)66 bool StdGridLayoutImpl2::itemResized(WLayoutItem *item)
67 {
68 const unsigned colCount = grid_.columns_.size();
69 const unsigned rowCount = grid_.rows_.size();
70
71 for (unsigned row = 0; row < rowCount; ++row)
72 for (unsigned col = 0; col < colCount; ++col)
73 if (grid_.items_[row][col].item_.get() == item &&
74 !grid_.items_[row][col].update_) {
75 grid_.items_[row][col].update_ = true;
76 needAdjust_ = true;
77 return true;
78 }
79
80 return false;
81 }
82
parentResized()83 bool StdGridLayoutImpl2::parentResized()
84 {
85 if (!needRemeasure_) {
86 needRemeasure_ = true;
87 return true;
88 } else
89 return false;
90 }
91
nextRowWithItem(int row,int c)92 int StdGridLayoutImpl2::nextRowWithItem(int row, int c) const
93 {
94 for (row += grid_.items_[row][c].rowSpan_; row < (int)grid_.rows_.size();
95 ++row) {
96 for (unsigned col = 0; col < grid_.columns_.size();
97 col += grid_.items_[row][col].colSpan_) {
98 if (hasItem(row, col))
99 return row;
100 }
101 }
102
103 return grid_.rows_.size();
104 }
105
nextColumnWithItem(int row,int col)106 int StdGridLayoutImpl2::nextColumnWithItem(int row, int col) const
107 {
108 for (;;) {
109 col = col + grid_.items_[row][col].colSpan_;
110
111 if (col < (int)grid_.columns_.size()) {
112 for (unsigned i = 0; i < grid_.rows_.size(); ++i)
113 if (hasItem(i, col))
114 return col;
115 } else
116 return grid_.columns_.size();
117 }
118 }
119
hasItem(int row,int col)120 bool StdGridLayoutImpl2::hasItem(int row, int col) const
121 {
122 WLayoutItem *item = grid_.items_[row][col].item_.get();
123
124 if (item) {
125 WWidget *w = item->widget();
126 return !w || !w->isHidden();
127 } else
128 return false;
129 }
130
createElement(WLayoutItem * item,WApplication * app)131 DomElement *StdGridLayoutImpl2::createElement(WLayoutItem *item,
132 WApplication *app)
133 {
134 DomElement *c = getImpl(item)->createDomElement(nullptr, true, true, app);
135
136 c->setProperty(Property::StyleVisibility, "hidden");
137
138 return c;
139 }
140
updateDom(DomElement & parent)141 void StdGridLayoutImpl2::updateDom(DomElement& parent)
142 {
143 WApplication *app = WApplication::instance();
144
145 if (needConfigUpdate_) {
146 needConfigUpdate_ = false;
147
148 DomElement *div = DomElement::getForUpdate(this, DomElementType::DIV);
149
150 for (unsigned i = 0; i < addedItems_.size(); ++i) {
151 WLayoutItem *item = addedItems_[i];
152 DomElement *c = createElement(item, app);
153 div->addChild(c);
154 }
155
156 addedItems_.clear();
157
158 for (unsigned i = 0; i < removedItems_.size(); ++i)
159 parent.callJavaScript(WT_CLASS ".remove('" + removedItems_[i] + "');",
160 true);
161
162 removedItems_.clear();
163
164 parent.addChild(div);
165
166 WStringStream js;
167 js << app->javaScriptClass() << ".layouts2.updateConfig('"
168 << id() << "',";
169 streamConfig(js, app);
170 js << ");";
171
172 app->doJavaScript(js.str());
173
174 needRemeasure_ = false;
175 needAdjust_ = false;
176 }
177
178 if (needRemeasure_) {
179 needRemeasure_ = false;
180 WStringStream js;
181 js << app->javaScriptClass() << ".layouts2.setDirty('" << id() << "');";
182 app->doJavaScript(js.str());
183 }
184
185 if (needAdjust_) {
186 needAdjust_ = false;
187
188 WStringStream js;
189 js << app->javaScriptClass() << ".layouts2.adjust('" << id() << "', [";
190
191 bool first = true;
192
193 const unsigned colCount = grid_.columns_.size();
194 const unsigned rowCount = grid_.rows_.size();
195
196 for (unsigned row = 0; row < rowCount; ++row)
197 for (unsigned col = 0; col < colCount; ++col)
198 if (grid_.items_[row][col].update_) {
199 grid_.items_[row][col].update_ = false;
200 if (!first)
201 js << ",";
202 first = false;
203 js << "[" << (int)row << "," << (int)col << "]";
204 }
205
206 js << "]);";
207
208 app->doJavaScript(js.str());
209 }
210
211 const unsigned colCount = grid_.columns_.size();
212 const unsigned rowCount = grid_.rows_.size();
213
214 for (unsigned i = 0; i < rowCount; ++i) {
215 for (unsigned j = 0; j < colCount; ++j) {
216 WLayoutItem *item = grid_.items_[i][j].item_.get();
217 if (item) {
218 WLayout *nested = item->layout();
219 if (nested)
220 (dynamic_cast<StdLayoutImpl *>(nested->impl()))->updateDom(parent);
221 }
222 }
223 }
224 }
225
~StdGridLayoutImpl2()226 StdGridLayoutImpl2::~StdGridLayoutImpl2()
227 {
228 WApplication *app = WApplication::instance();
229
230 /*
231 * If it is a top-level layout (as opposed to a nested layout),
232 * configure overflow of the container.
233 */
234 if (parentLayoutImpl() == nullptr) {
235 if (container() == app->root()) {
236 app->setBodyClass("");
237 app->setHtmlClass("");
238 }
239
240 if (app->environment().agentIsIElt(9) && container())
241 container()->setOverflow(Overflow::Visible);
242 }
243 }
244
minimumHeightForRow(int row)245 int StdGridLayoutImpl2::minimumHeightForRow(int row) const
246 {
247 int minHeight = 0;
248
249 const unsigned colCount = grid_.columns_.size();
250 for (unsigned j = 0; j < colCount; ++j) {
251 WLayoutItem *item = grid_.items_[row][j].item_.get();
252 if (item)
253 minHeight = std::max(minHeight, getImpl(item)->minimumHeight());
254 }
255
256 return minHeight;
257 }
258
minimumWidthForColumn(int col)259 int StdGridLayoutImpl2::minimumWidthForColumn(int col) const
260 {
261 int minWidth = 0;
262
263 const unsigned rowCount = grid_.rows_.size();
264 for (unsigned i = 0; i < rowCount; ++i) {
265 WLayoutItem *item = grid_.items_[i][col].item_.get();
266 if (item)
267 minWidth = std::max(minWidth, getImpl(item)->minimumWidth());
268 }
269
270 return minWidth;
271 }
272
minimumWidth()273 int StdGridLayoutImpl2::minimumWidth() const
274 {
275 const unsigned colCount = grid_.columns_.size();
276
277 int total = 0;
278
279 for (unsigned i = 0; i < colCount; ++i)
280 total += minimumWidthForColumn(i);
281
282 return total + (colCount-1) * grid_.horizontalSpacing_;
283 }
284
minimumHeight()285 int StdGridLayoutImpl2::minimumHeight() const
286 {
287 const unsigned rowCount = grid_.rows_.size();
288
289 int total = 0;
290
291 for (unsigned i = 0; i < rowCount; ++i)
292 total += minimumHeightForRow(i);
293
294 return total + (rowCount-1) * grid_.verticalSpacing_;
295 }
296
itemAdded(WLayoutItem * item)297 void StdGridLayoutImpl2::itemAdded(WLayoutItem *item)
298 {
299 addedItems_.push_back(item);
300 update();
301 }
302
itemRemoved(WLayoutItem * item)303 void StdGridLayoutImpl2::itemRemoved(WLayoutItem *item)
304 {
305 Utils::erase(addedItems_, item);
306 removedItems_.push_back(getImpl(item)->id());
307 update();
308 }
309
update()310 void StdGridLayoutImpl2::update()
311 {
312 WContainerWidget *c = container();
313
314 if (c)
315 c->layoutChanged(false);
316
317 needConfigUpdate_ = true;
318 }
319
320 void StdGridLayoutImpl2
streamConfig(WStringStream & js,const std::vector<Impl::Grid::Section> & sections,bool rows,WApplication * app)321 ::streamConfig(WStringStream& js,
322 const std::vector<Impl::Grid::Section>& sections,
323 bool rows, WApplication *app)
324 {
325 js << "[";
326
327 for (unsigned i = 0; i < sections.size(); ++i) {
328 if (i != 0)
329 js << ",";
330
331 js << "[" << sections[i].stretch_ << ",";
332
333 if (sections[i].resizable_) {
334 SizeHandle::loadJavaScript(app);
335
336 js << "[";
337
338 const WLength& size = sections[i].initialSize_;
339
340 if (size.isAuto())
341 js << "-1";
342 else if (size.unit() == LengthUnit::Percentage)
343 js << size.value() << ",1";
344 else
345 js << size.toPixels();
346
347 js << "],";
348 } else
349 js << "0,";
350
351 if (rows)
352 js << minimumHeightForRow(i);
353 else
354 js << minimumWidthForColumn(i);
355
356 js << "]";
357
358 }
359
360 js << "]";
361 }
362
streamConfig(WStringStream & js,WApplication * app)363 void StdGridLayoutImpl2::streamConfig(WStringStream& js, WApplication *app)
364 {
365 js << "{ rows:";
366
367 streamConfig(js, grid_.rows_, true, app);
368
369 js << ", cols:";
370
371 streamConfig(js, grid_.columns_, false, app);
372
373 js << ", items: [";
374
375 const unsigned colCount = grid_.columns_.size();
376 const unsigned rowCount = grid_.rows_.size();
377
378 for (unsigned row = 0; row < rowCount; ++row) {
379 for (unsigned col = 0; col < colCount; ++col) {
380 Impl::Grid::Item& item = grid_.items_[row][col];
381
382 AlignmentFlag hAlign = item.alignment_ & AlignHorizontalMask;
383 AlignmentFlag vAlign = item.alignment_ & AlignVerticalMask;
384
385 if (row + col != 0)
386 js << ",";
387
388 if (item.item_) {
389 std::string id = getImpl(item.item_.get())->id();
390
391 js << "{";
392
393 if (item.colSpan_ != 1 || item.rowSpan_ != 1)
394 js << "span: [" << item.colSpan_ << "," << item.rowSpan_ << "],";
395
396 if (item.alignment_.value()) {
397 unsigned align = 0;
398
399 if (hAlign != static_cast<AlignmentFlag>(0))
400 switch (hAlign) {
401 case AlignmentFlag::Left: align |= 0x1; break;
402 case AlignmentFlag::Right: align |= 0x2; break;
403 case AlignmentFlag::Center: align |= 0x4; break;
404 default: break;
405 }
406
407 if (vAlign != static_cast<AlignmentFlag>(0))
408 switch (vAlign) {
409 case AlignmentFlag::Top: align |= 0x10; break;
410 case AlignmentFlag::Bottom: align |= 0x20; break;
411 case AlignmentFlag::Middle: align |= 0x40; break;
412 default: break;
413 }
414
415 js << "align:" << (int)align << ",";
416 }
417
418 js << "dirty:" << (grid_.items_[row][col].update_ ? 2 : 0)
419 << ",id:'" << id << "'"
420 << "}";
421
422 grid_.items_[row][col].update_ = 0;
423 } else
424 js << "null";
425 }
426 }
427
428 js << "]}";
429 }
430
pixelSize(const WLength & size)431 int StdGridLayoutImpl2::pixelSize(const WLength& size)
432 {
433 if (size.unit() == LengthUnit::Percentage)
434 return 0;
435 else
436 return (int)size.toPixels();
437 }
438
439 /*
440 * fitWidth, fitHeight:
441 * - from setLayout(AlignmentFlag::Left | AlignmentFlag::Top)
442 * is being deprecated but still needs to be implemented
443 * - nested layouts: handles as other layout items
444 */
createDomElement(DomElement * parent,bool fitWidth,bool fitHeight,WApplication * app)445 DomElement *StdGridLayoutImpl2::createDomElement(DomElement *parent,
446 bool fitWidth, bool fitHeight,
447 WApplication *app)
448 {
449 needAdjust_ = needConfigUpdate_ = needRemeasure_ = false;
450 addedItems_.clear();
451 removedItems_.clear();
452
453 const unsigned colCount = grid_.columns_.size();
454 const unsigned rowCount = grid_.rows_.size();
455
456 int margin[] = { 0, 0, 0, 0};
457
458 int maxWidth = 0, maxHeight = 0;
459
460 if (layout()->parentLayout() == nullptr) {
461 /*
462 * If it is a top-level layout (as opposed to a nested layout),
463 * configure overflow of the container.
464 */
465 if (container() == app->root()) {
466 /*
467 * Reset body,html default paddings and so on if we are doing layout
468 * in the entire document.
469 */
470 app->setBodyClass(app->bodyClass() + " Wt-layout");
471 app->setHtmlClass(app->htmlClass() + " Wt-layout");
472 }
473
474 #ifndef WT_TARGET_JAVA
475 layout()->getContentsMargins(margin + 3, margin, margin + 1, margin + 2);
476 #else // WT_TARGET_JAVA
477 margin[3] = layout()->getContentsMargin(Side::Left);
478 margin[0] = layout()->getContentsMargin(Side::Top);
479 margin[1] = layout()->getContentsMargin(Side::Right);
480 margin[2] = layout()->getContentsMargin(Side::Bottom);
481 #endif // WT_TARGET_JAVA
482
483 maxWidth = pixelSize(container()->maximumWidth());
484 maxHeight = pixelSize(container()->maximumHeight());
485 }
486
487 WStringStream js;
488
489 js << app->javaScriptClass()
490 << ".layouts2.add(new " WT_CLASS ".StdLayout2("
491 << app->javaScriptClass() << ",'"
492 << id() << "',";
493
494 if (layout()->parentLayout() &&
495 dynamic_cast<StdGridLayoutImpl2*>(getImpl(layout()->parentLayout())))
496 js << "'" << getImpl(layout()->parentLayout())->id() << "',";
497 else
498 js << "null,";
499
500 bool progressive = !app->environment().ajax();
501 js << (fitWidth ? '1' : '0') << "," << (fitHeight ? '1' : '0') << ","
502 << (progressive ? '1' : '0') << ",";
503
504 js << maxWidth << "," << maxHeight
505 << ",["
506 << grid_.horizontalSpacing_ << "," << margin[3] << "," << margin[1]
507 << "],["
508 << grid_.verticalSpacing_ << "," << margin[0] << "," << margin[2] << "],";
509
510 streamConfig(js, app);
511
512 DomElement *div = DomElement::createNew(DomElementType::DIV);
513 div->setId(id());
514 div->setProperty(Property::StylePosition, "relative");
515
516 DomElement *table = nullptr, *tbody = nullptr, *tr = nullptr;
517 if (progressive) {
518 table = DomElement::createNew(DomElementType::TABLE);
519
520 WStringStream style;
521 if (maxWidth)
522 style << "max-width: " << maxWidth << "px;";
523 if (maxHeight)
524 style << "max-height: " << maxHeight << "px;";
525 style << "width: 100%;";
526
527 table->setProperty(Property::Style, style.str());
528
529 int totalColStretch = 0;
530 for (unsigned col = 0; col < colCount; ++col)
531 totalColStretch += std::max(0, grid_.columns_[col].stretch_);
532
533 for (unsigned col = 0; col < colCount; ++col) {
534 DomElement *c = DomElement::createNew(DomElementType::COL);
535 int stretch = std::max(0, grid_.columns_[col].stretch_);
536
537 if (stretch || totalColStretch == 0) {
538 char buf[30];
539
540 double pct = totalColStretch == 0 ? 100.0 / colCount
541 : (100.0 * stretch / totalColStretch);
542
543 WStringStream ss;
544 ss << "width:" << Utils::round_css_str(pct, 2, buf) << "%;";
545 c->setProperty(Property::Style, ss.str());
546 }
547
548 table->addChild(c);
549 }
550
551 tbody = DomElement::createNew(DomElementType::TBODY);
552 }
553
554 #ifndef WT_TARGET_JAVA
555 std::vector<bool> overSpanned(colCount * rowCount, false);
556 #else
557 std::vector<bool> overSpanned;
558 overSpanned.insert(0, colCount * rowCount, false);
559 #endif // WT_TARGET_JAVA
560
561 int prevRowWithItem = -1;
562
563 for (unsigned row = 0; row < rowCount; ++row) {
564 if (table)
565 tr = DomElement::createNew(DomElementType::TR);
566
567 bool rowVisible = false;
568 int prevColumnWithItem = -1;
569
570 for (unsigned col = 0; col < colCount; ++col) {
571 Impl::Grid::Item& item = grid_.items_[row][col];
572
573 if (!overSpanned[row * colCount + col]) {
574 for (int i = 0; i < item.rowSpan_; ++i)
575 for (int j = 0; j < item.colSpan_; ++j)
576 if (i + j > 0)
577 overSpanned[(row + i) * colCount + col + j] = true;
578
579 AlignmentFlag hAlign = item.alignment_ & AlignHorizontalMask;
580 AlignmentFlag vAlign = item.alignment_ & AlignVerticalMask;
581
582 DomElement *td = nullptr;
583
584 if (table) {
585 bool itemVisible = hasItem(row, col);
586 rowVisible = rowVisible || itemVisible;
587
588 td = DomElement::createNew(DomElementType::TD);
589
590 if (itemVisible) {
591 int padding[] = { 0, 0, 0, 0 };
592
593 int nextRow = nextRowWithItem(row, col);
594 int prevRow = prevRowWithItem;
595
596 int nextCol = nextColumnWithItem(row, col);
597 int prevCol = prevColumnWithItem;
598
599 if (prevRow == -1)
600 padding[0] = margin[0];
601 else
602 padding[0] = (grid_.verticalSpacing_+1) / 2;
603
604 if (nextRow == (int)rowCount)
605 padding[2] = margin[2];
606 else
607 padding[2] = grid_.verticalSpacing_ / 2;
608
609 if (prevCol == -1)
610 padding[3] = margin[3];
611 else
612 padding[3] = (grid_.horizontalSpacing_ + 1)/2;
613
614 if (nextCol == (int)colCount)
615 padding[1] = margin[1];
616 else
617 padding[1] = (grid_.horizontalSpacing_)/2;
618
619 WStringStream style;
620
621 if (app->layoutDirection() == LayoutDirection::RightToLeft)
622 std::swap(padding[1], padding[3]);
623
624 if (padding[0] == padding[1] && padding[0] == padding[2]
625 && padding[0] == padding[3]) {
626 if (padding[0] != 0)
627 style << "padding:" << padding[0] << "px;";
628 } else
629 style << "padding:"
630 << padding[0] << "px " << padding[1] << "px "
631 << padding[2] << "px " << padding[3] << "px;";
632
633 if (static_cast<unsigned int>(vAlign) != 0)
634 switch (vAlign) {
635 case AlignmentFlag::Top:
636 style << "vertical-align:top;";
637 break;
638 case AlignmentFlag::Middle:
639 style << "vertical-align:middle;";
640 break;
641 case AlignmentFlag::Bottom:
642 style << "vertical-align:bottom;";
643 default:
644 break;
645 }
646
647 td->setProperty(Property::Style, style.str());
648
649 if (item.rowSpan_ != 1)
650 td->setProperty(Property::RowSpan,
651 std::to_string(item.rowSpan_));
652 if (item.colSpan_ != 1)
653 td->setProperty(Property::ColSpan,
654 std::to_string(item.colSpan_));
655
656 prevColumnWithItem = col;
657 }
658 }
659
660 DomElement *c = nullptr;
661
662 if (!table) {
663 if (item.item_) {
664 c = createElement(item.item_.get(), app);
665 div->addChild(c);
666 }
667 } else
668 if (item.item_)
669 c = getImpl(item.item_.get())->createDomElement(nullptr, true,
670 true, app);
671
672 if (table) {
673 if (c) {
674 if (!app->environment().agentIsIElt(9))
675 c->setProperty(Property::StyleBoxSizing, "border-box");
676
677 if (static_cast<unsigned int>(hAlign) == 0)
678 hAlign = AlignmentFlag::Justify;
679
680 switch (hAlign) {
681 case AlignmentFlag::Center: {
682 DomElement *itable = DomElement::createNew(DomElementType::TABLE);
683 itable->setProperty(Property::Class, "Wt-hcenter");
684 if (static_cast<unsigned int>(vAlign) == 0)
685 itable->setProperty(Property::Style, "height:100%;");
686 DomElement *irow = DomElement::createNew(DomElementType::TR);
687 DomElement *itd = DomElement::createNew(DomElementType::TD);
688 if (static_cast<unsigned int>(vAlign) == 0)
689 itd->setProperty(Property::Style, "height:100%;");
690
691 bool haveMinWidth
692 = !c->getProperty(Property::StyleMinWidth).empty();
693
694 itd->addChild(c);
695
696 if (app->environment().agentIsIElt(9)) {
697 // IE7 and IE8 do support min-width but do not enforce it
698 // properly when in a table.
699 // see http://stackoverflow.com/questions/2356525
700 // /css-min-width-in-ie6-7-and-8
701 if (haveMinWidth) {
702 DomElement *spacer = DomElement::createNew(DomElementType::DIV);
703 spacer->setProperty(Property::StyleWidth,
704 c->getProperty(Property::StyleMinWidth));
705 spacer->setProperty(Property::StyleHeight, "1px");
706 itd->addChild(spacer);
707 }
708 }
709
710 irow->addChild(itd);
711 itable->addChild(irow);
712 c = itable;
713 break;
714 }
715 case AlignmentFlag::Right:
716 if (!c->isDefaultInline())
717 c->setProperty(Property::StyleFloat, "right");
718 else
719 td->setProperty(Property::StyleTextAlign, "right");
720 break;
721 case AlignmentFlag::Left:
722 if (!c->isDefaultInline())
723 c->setProperty(Property::StyleFloat, "left");
724 else
725 td->setProperty(Property::StyleTextAlign, "left");
726 break;
727 default:
728 break;
729 }
730
731 bool haveMinWidth
732 = !c->getProperty(Property::StyleMinWidth).empty();
733
734 td->addChild(c);
735
736 if (app->environment().agentIsIElt(9)) {
737 // IE7 and IE8 do support min-width but do not enforce it properly
738 // when in a table.
739 // see http://stackoverflow.com/questions/2356525
740 // /css-min-width-in-ie6-7-and-8
741 if (haveMinWidth) {
742 DomElement *spacer = DomElement::createNew(DomElementType::DIV);
743 spacer->setProperty(Property::StyleWidth,
744 c->getProperty(Property::StyleMinWidth));
745 spacer->setProperty(Property::StyleHeight, "1px");
746 td->addChild(spacer);
747 }
748 }
749 }
750
751 tr->addChild(td);
752 }
753 }
754 }
755
756 if (tr) {
757 if (!rowVisible)
758 tr->setProperty(Property::StyleDisplay, "hidden");
759 else
760 prevRowWithItem = row;
761 tbody->addChild(tr);
762 }
763 }
764
765 js << "));";
766
767 if (table) {
768 table->addChild(tbody);
769 div->addChild(table);
770 }
771
772 div->callJavaScript(js.str());
773
774 if (layout()->parentLayout() == nullptr) {
775 WContainerWidget *c = container();
776
777 /*
778 * Take the hint: if the container is relative, then we can use an absolute
779 * layout for its contents, under the assumption that a .wtResize or
780 * auto-javascript sets the width too (like in WTreeView, WTableView)
781 */
782 if (c->positionScheme() == PositionScheme::Relative ||
783 c->positionScheme() == PositionScheme::Absolute) {
784 div->setProperty(Property::StylePosition, "absolute");
785 div->setProperty(Property::StyleLeft, "0");
786 div->setProperty(Property::StyleRight, "0");
787 } else if (app->environment().agentIsIE()) {
788 /*
789 * position: relative element needs to be in a position: relative
790 * parent otherwise scrolling is broken
791 */
792 if (app->environment().agentIsIE()
793 && c->parent()->positionScheme() != PositionScheme::Static)
794 parent->setProperty(Property::StylePosition, "relative");
795 }
796
797 AlignmentFlag hAlign = c->contentAlignment() & AlignHorizontalMask;
798 switch (hAlign) {
799 case AlignmentFlag::Center: {
800 DomElement *itable = DomElement::createNew(DomElementType::TABLE);
801 itable->setProperty(Property::Class, "Wt-hcenter");
802 if (fitHeight)
803 itable->setProperty(Property::Style, "height:100%;");
804 DomElement *irow = DomElement::createNew(DomElementType::TR);
805 DomElement *itd = DomElement::createNew(DomElementType::TD);
806 if (fitHeight)
807 itd->setProperty(Property::Style, "height:100%;");
808 itd->addChild(div);
809 irow->addChild(itd);
810 itable->addChild(irow);
811 itable->setId(id() + "l");
812 div = itable;
813
814 break;
815 }
816 case AlignmentFlag::Left:
817 break;
818 case AlignmentFlag::Right:
819 div->setProperty(Property::StyleFloat, "right");
820 break;
821 default:
822 break;
823 }
824 }
825
826 return div;
827 }
828
829 }
830