void WAbstractSpinBox::render(WFlags<RenderFlag> flags) { /* * In theory we are a bit late here to decide what we want to become: * somebody could already have asked the domElementType() */ if (!setup_ && flags.test(RenderFlag::Full)) { setup(); } if (jsValueChanged().needsUpdate(true)) { WStringStream function; function << "jQuery.data(" + jsRef() + ",'obj').jsValueChanged="; if (jsValueChanged().isConnected()) { function << "function(oldv, v){" << "var o=null;var e=null;" << jsValueChanged().createCall({"oldv", "v"}) << "};"; } else { function << "function() {};"; } doJavaScript(function.str()); } WLineEdit::render(flags); }
void WCalendar::create() { selectionMode_ = SingleSelection; singleClickSelect_ = false; horizontalHeaderFormat_ = ShortDayNames; firstDayOfWeek_ = 1; cellClickMapper_ = 0; cellDblClickMapper_ = 0; WDate currentDay = WDate::currentDate(); currentYear_ = currentDay.year(); currentMonth_ = currentDay.month(); WStringStream text; text << "<table class=\"days ${table-class}\" cellspacing=\"0\" cellpadding=\"0\">" """<tr>" "" "<th class=\"caption\">${nav-prev}</th>" "" "<th class=\"caption\"colspan=\"5\">${month} ${year}</th>" "" "<th class=\"caption\">${nav-next}</th>" """</tr>" """<tr>"; for (int j = 0; j < 7; ++j) text << "<th title=\"${t" << j << "}\" scope=\"col\">${d" << j << "}</th>"; text << "</tr>"; for (int i = 0; i < 6; ++i) { text << "<tr>"; for (int j = 0; j < 7; ++j) text << "<td>${c" << (i * 7 + j) << "}</td>"; text << "</tr>"; } text << "</table>"; setImplementation(impl_ = new WTemplate()); impl_->setTemplateText(WString::fromUTF8(text.str()), XHTMLUnsafeText); impl_->setStyleClass("Wt-cal"); setSelectable(false); WText *prevMonth = new WText(tr("Wt.WCalendar.PrevMonth")); prevMonth->setStyleClass("Wt-cal-navbutton"); prevMonth->clicked().connect(this, &WCalendar::browseToPreviousMonth); WText *nextMonth = new WText(tr("Wt.WCalendar.NextMonth")); nextMonth->setStyleClass("Wt-cal-navbutton"); nextMonth->clicked().connect(this, &WCalendar::browseToNextMonth); monthEdit_ = new WComboBox(); monthEdit_->setInline(true); for (unsigned i = 0; i < 12; ++i) monthEdit_->addItem(WDate::longMonthName(i+1)); monthEdit_->activated().connect(this, &WCalendar::monthChanged); monthEdit_->setDisabled(!WApplication::instance()->environment().ajax()); yearEdit_ = new WInPlaceEdit(""); yearEdit_->setButtonsEnabled(false); yearEdit_->lineEdit()->setTextSize(4); yearEdit_->setStyleClass("Wt-cal-year"); yearEdit_->valueChanged().connect(this, &WCalendar::yearChanged); impl_->bindWidget("nav-prev", prevMonth); impl_->bindWidget("nav-next", nextMonth); impl_->bindWidget("month", monthEdit_); impl_->bindWidget("year", yearEdit_); setHorizontalHeaderFormat(horizontalHeaderFormat_); setFirstDayOfWeek(firstDayOfWeek_); }
std::size_t WTemplate::parseArgs(const std::string& text, std::size_t pos, std::vector<WString>& result) { std::size_t Error = std::string::npos; if (pos == std::string::npos) return Error; enum { Next, Name, Value, SValue, DValue } state = Next; WStringStream v; for (; pos < text.length(); ++pos) { char c = text[pos]; switch (state) { case Next: if (!std::isspace(c)) { if (c == '}') return pos; else if (std::isalpha(c) || c == '_') { state = Name; v.clear(); v << c; } else if (c == '\'') { state = SValue; v.clear(); } else if (c == '"') { state = DValue; v.clear(); } else return Error; } break; case Name: if (c == '=') { state = Value; v << '='; } else if (std::isspace(c)) { result.push_back(WString::fromUTF8(v.str())); state = Next; } else if (c == '}') { result.push_back(WString::fromUTF8(v.str())); return pos; } else if (std::isalnum(c) || c == '_' || c == '-') v << c; else return Error; break; case Value: if (c == '\'') state = SValue; else if (c == '"') state = DValue; else return Error; break; case SValue: case DValue: char quote = state == SValue ? '\'' : '"'; std::size_t end = text.find(quote, pos); if (end == std::string::npos) return Error; if (text[end - 1] == '\\') v << text.substr(pos, end - pos - 1) << quote; else { v << text.substr(pos, end - pos); result.push_back(WString::fromUTF8(v.str())); state = Next; } pos = end; } } return pos == text.length() ? std::string::npos : pos; }
void WPainter::drawText(const WRectF& rectangle, WFlags<AlignmentFlag> alignmentFlags, TextFlag textFlag, const WString& text) { if (textFlag == TextSingleLine) drawText(rectangle, alignmentFlags, text); else { if (!(alignmentFlags & AlignVerticalMask)) alignmentFlags |= AlignTop; if (!(alignmentFlags & AlignHorizontalMask)) alignmentFlags |= AlignLeft; if (device_->features() & WPaintDevice::CanWordWrap) device_->drawText(rectangle.normalized(), alignmentFlags, textFlag, text); else if (device_->features() & WPaintDevice::HasFontMetrics) { #ifndef WT_TARGET_JAVA MultiLineTextRenderer renderer(*this, rectangle); AlignmentFlag horizontalAlign = alignmentFlags & AlignHorizontalMask; AlignmentFlag verticalAlign = alignmentFlags & AlignVerticalMask; /* * Oh irony: after all these years of hating CSS, we now * implemented an XHTML renderer for which we need to use the * same silly workarounds to render the text with all possible * alignment options */ WStringStream s; s << "<table style=\"width:" << (int)rectangle.width() << "px;\"" "cellspacing=\"0\"><tr>" "<td style=\"padding:0px;height:" << (int)rectangle.height() << "px;color:" << pen().color().cssText() << ";text-align:"; switch (horizontalAlign) { case AlignLeft: s << "left"; break; case AlignRight: s << "right"; break; case AlignCenter: s << "center"; break; default: break; } s << ";vertical-align:"; switch (verticalAlign) { case AlignTop: s << "top"; break; case AlignBottom: s << "bottom"; break; case AlignMiddle: s << "middle"; break; default: break; } s << ";" << font().cssText(false); s << "\">" << WWebWidget::escapeText(text, true).toUTF8() << "</td></tr></table>"; save(); /* * FIXME: what if there was already a clip path? We need to combine * them ... */ WPainterPath p; p.addRect(rectangle.x() + 1, rectangle.y() + 1, rectangle.width() - 2, rectangle.height() - 2); setClipPath(p); setClipping(true); renderer.render(WString::fromUTF8(s.str())); restore(); #endif // WT_TARGET_JAVA } else throw WException("WPainter::drawText(): device does not support " "TextWordWrap or FontMetrics"); } }
/* * fitWidth, fitHeight: * - from setLayout(AlignLeft | AlignTop) * is being deprecated but still needs to be implemented * - nested layouts: handles as other layout items */ DomElement *StdGridLayoutImpl2::createDomElement(bool fitWidth, bool fitHeight, WApplication *app) { needAdjust_ = needConfigUpdate_ = needRemeasure_ = false; addedItems_.clear(); removedItems_.clear(); const unsigned colCount = grid_.columns_.size(); const unsigned rowCount = grid_.rows_.size(); int margin[] = { 0, 0, 0, 0}; int maxWidth = 0, maxHeight = 0; if (layout()->parentLayout() == 0) { #ifndef WT_TARGET_JAVA layout()->getContentsMargins(margin + 3, margin, margin + 1, margin + 2); #else // WT_TARGET_JAVA margin[3] = layout()->getContentsMargin(Left); margin[0] = layout()->getContentsMargin(Top); margin[1] = layout()->getContentsMargin(Right); margin[2] = layout()->getContentsMargin(Bottom); #endif // WT_TARGET_JAVA maxWidth = pixelSize(container()->maximumWidth()); maxHeight = pixelSize(container()->maximumHeight()); } WStringStream js; js << app->javaScriptClass() << ".layouts2.add(new " WT_CLASS ".StdLayout2(" << app->javaScriptClass() << ",'" << id() << "',"; if (layout()->parentLayout()) js << "'" << getImpl(layout()->parentLayout())->id() << "',"; else js << "null,"; bool progressive = !app->environment().ajax(); js << (fitWidth ? '1' : '0') << "," << (fitHeight ? '1' : '0') << "," << (progressive ? '1' : '0') << ","; js << maxWidth << "," << maxHeight << ",[" << grid_.horizontalSpacing_ << "," << margin[3] << "," << margin[1] << "],[" << grid_.verticalSpacing_ << "," << margin[0] << "," << margin[2] << "],"; streamConfig(js, app); DomElement *div = DomElement::createNew(DomElement_DIV); div->setId(id()); div->setProperty(PropertyStylePosition, "relative"); DomElement *table = 0, *tbody = 0, *tr = 0; if (progressive) { table = DomElement::createNew(DomElement_TABLE); WStringStream style; if (maxWidth) style << "max-width: " << maxWidth << "px;"; if (maxHeight) style << "max-height: " << maxHeight << "px;"; style << "width: 100%;"; table->setProperty(PropertyStyle, style.str()); int totalColStretch = 0; for (unsigned col = 0; col < colCount; ++col) totalColStretch += std::max(0, grid_.columns_[col].stretch_); for (unsigned col = 0; col < colCount; ++col) { DomElement *c = DomElement::createNew(DomElement_COL); int stretch = std::max(0, grid_.columns_[col].stretch_); if (stretch || totalColStretch == 0) { char buf[30]; double pct = totalColStretch == 0 ? 100.0 / colCount : (100.0 * stretch / totalColStretch); WStringStream ss; ss << "width:" << Utils::round_css_str(pct, 2, buf) << "%;"; c->setProperty(PropertyStyle, ss.str()); } table->addChild(c); } tbody = DomElement::createNew(DomElement_TBODY); } #ifndef WT_TARGET_JAVA std::vector<bool> overSpanned(colCount * rowCount, false); #else std::vector<bool> overSpanned; overSpanned.insert(0, colCount * rowCount, false); #endif // WT_TARGET_JAVA int prevRowWithItem = -1; for (unsigned row = 0; row < rowCount; ++row) { if (table) tr = DomElement::createNew(DomElement_TR); bool rowVisible = false; int prevColumnWithItem = -1; for (unsigned col = 0; col < colCount; ++col) { Impl::Grid::Item& item = grid_.items_[row][col]; if (!overSpanned[row * colCount + col]) { for (int i = 0; i < item.rowSpan_; ++i) for (int j = 0; j < item.colSpan_; ++j) if (i + j > 0) overSpanned[(row + i) * colCount + col + j] = true; AlignmentFlag hAlign = item.alignment_ & AlignHorizontalMask; AlignmentFlag vAlign = item.alignment_ & AlignVerticalMask; DomElement *td = 0; if (table) { bool itemVisible = hasItem(row, col); rowVisible = rowVisible || itemVisible; td = DomElement::createNew(DomElement_TD); if (itemVisible) { int padding[] = { 0, 0, 0, 0 }; int nextRow = nextRowWithItem(row, col); int prevRow = prevRowWithItem; int nextCol = nextColumnWithItem(row, col); int prevCol = prevColumnWithItem; if (prevRow == -1) padding[0] = margin[0]; else padding[0] = (grid_.verticalSpacing_+1) / 2; if (nextRow == (int)rowCount) padding[2] = margin[2]; else padding[2] = grid_.verticalSpacing_ / 2; if (prevCol == -1) padding[3] = margin[3]; else padding[3] = (grid_.horizontalSpacing_ + 1)/2; if (nextCol == (int)colCount) padding[1] = margin[1]; else padding[1] = (grid_.horizontalSpacing_)/2; WStringStream style; if (app->layoutDirection() == RightToLeft) std::swap(padding[1], padding[3]); if (padding[0] == padding[1] && padding[0] == padding[2] && padding[0] == padding[3]) { if (padding[0] != 0) style << "padding:" << padding[0] << "px;"; } else style << "padding:" << padding[0] << "px " << padding[1] << "px " << padding[2] << "px " << padding[3] << "px;"; if (vAlign != 0) switch (vAlign) { case AlignTop: style << "vertical-align:top;"; break; case AlignMiddle: style << "vertical-align:middle;"; break; case AlignBottom: style << "vertical-align:bottom;"; default: break; } td->setProperty(PropertyStyle, style.str()); if (item.rowSpan_ != 1) td->setProperty(PropertyRowSpan, boost::lexical_cast<std::string>(item.rowSpan_)); if (item.colSpan_ != 1) td->setProperty(PropertyColSpan, boost::lexical_cast<std::string>(item.colSpan_)); prevColumnWithItem = col; } } DomElement *c = 0; if (!table) { if (item.item_) { c = createElement(item.item_, app); div->addChild(c); } } else if (item.item_) c = getImpl(item.item_)->createDomElement(true, true, app); if (table) { if (c) { if (!app->environment().agentIsIElt(9)) c->setProperty(PropertyStyleBoxSizing, "border-box"); if (hAlign == 0) hAlign = AlignJustify; switch (hAlign) { case AlignCenter: { DomElement *itable = DomElement::createNew(DomElement_TABLE); itable->setProperty(PropertyClass, "Wt-hcenter"); if (vAlign == 0) itable->setProperty(PropertyStyle, "height:100%;"); DomElement *irow = DomElement::createNew(DomElement_TR); DomElement *itd = DomElement::createNew(DomElement_TD); if (vAlign == 0) itd->setProperty(PropertyStyle, "height:100%;"); bool haveMinWidth = !c->getProperty(PropertyStyleMinWidth).empty(); itd->addChild(c); if (app->environment().agentIsIElt(9)) { // IE7 and IE8 do support min-width but do not enforce it // properly when in a table. // see http://stackoverflow.com/questions/2356525 // /css-min-width-in-ie6-7-and-8 if (haveMinWidth) { DomElement *spacer = DomElement::createNew(DomElement_DIV); spacer->setProperty(PropertyStyleWidth, c->getProperty(PropertyStyleMinWidth)); spacer->setProperty(PropertyStyleHeight, "1px"); itd->addChild(spacer); } } irow->addChild(itd); itable->addChild(irow); c = itable; break; } case AlignRight: if (!c->isDefaultInline()) c->setProperty(PropertyStyleFloat, "right"); else td->setProperty(PropertyStyleTextAlign, "right"); break; case AlignLeft: if (!c->isDefaultInline()) c->setProperty(PropertyStyleFloat, "left"); else td->setProperty(PropertyStyleTextAlign, "left"); break; default: break; } td->addChild(c); if (app->environment().agentIsIElt(9)) { // IE7 and IE8 do support min-width but do not enforce it properly // when in a table. // see http://stackoverflow.com/questions/2356525 // /css-min-width-in-ie6-7-and-8 if (!c->getProperty(PropertyStyleMinWidth).empty()) { DomElement *spacer = DomElement::createNew(DomElement_DIV); spacer->setProperty(PropertyStyleWidth, c->getProperty(PropertyStyleMinWidth)); spacer->setProperty(PropertyStyleHeight, "1px"); td->addChild(spacer); } } } tr->addChild(td); } } } if (tr) { if (!rowVisible) tr->setProperty(PropertyStyleDisplay, "hidden"); else prevRowWithItem = row; tbody->addChild(tr); } } js << "));"; if (table) { table->addChild(tbody); div->addChild(table); } div->callJavaScript(js.str()); return div; }
void StdGridLayoutImpl2::updateDom(DomElement& parent) { WApplication *app = WApplication::instance(); if (needConfigUpdate_) { needConfigUpdate_ = false; DomElement *div = DomElement::getForUpdate(this, DomElement_DIV); for (unsigned i = 0; i < addedItems_.size(); ++i) { WLayoutItem *item = addedItems_[i]; DomElement *c = createElement(item, app); div->addChild(c); } addedItems_.clear(); for (unsigned i = 0; i < removedItems_.size(); ++i) parent.callJavaScript(WT_CLASS ".remove('" + removedItems_[i] + "');", true); removedItems_.clear(); parent.addChild(div); WStringStream js; js << app->javaScriptClass() << ".layouts2.updateConfig('" << id() << "',"; streamConfig(js, app); js << ");"; app->doJavaScript(js.str()); } if (needRemeasure_) { needRemeasure_ = false; WStringStream js; js << app->javaScriptClass() << ".layouts2.setDirty('" << id() << "');"; app->doJavaScript(js.str()); } if (needAdjust_) { needAdjust_ = false; WStringStream js; js << app->javaScriptClass() << ".layouts2.adjust('" << id() << "', ["; bool first = true; const unsigned colCount = grid_.columns_.size(); const unsigned rowCount = grid_.rows_.size(); for (unsigned row = 0; row < rowCount; ++row) for (unsigned col = 0; col < colCount; ++col) if (grid_.items_[row][col].update_) { grid_.items_[row][col].update_ = false; if (!first) js << ","; first = false; js << "[" << (int)row << "," << (int)col << "]"; } js << "]);"; app->doJavaScript(js.str()); } const unsigned colCount = grid_.columns_.size(); const unsigned rowCount = grid_.rows_.size(); for (unsigned i = 0; i < rowCount; ++i) { for (unsigned j = 0; j < colCount; ++j) { WLayoutItem *item = grid_.items_[i][j].item_; if (item) { WLayout *nested = item->layout(); if (nested) (dynamic_cast<StdLayoutImpl *>(nested->impl()))->updateDom(parent); } } } }
DomElement *FlexLayoutImpl::createElement(Orientation orientation, unsigned index, int totalStretch, WApplication *app) { Impl::Grid::Item& it = item(orientation, index); Impl::Grid::Section& s = section(orientation, index); DomElement *el = getImpl(it.item_.get())->createDomElement(nullptr, true, true, app); if (dynamic_cast<StdGridLayoutImpl2*>(getImpl(it.item_.get()))) { DomElement *wrapEl = DomElement::createNew(DomElementType::DIV); wrapEl->addChild(el); el = wrapEl; } int m[] = { 0, 0, 0, 0 }; if (FlexLayoutImpl *flexImpl = dynamic_cast<FlexLayoutImpl*>(getImpl(it.item_.get()))) { Orientation elOrientation = flexImpl->getOrientation(); if (elOrientation == Orientation::Horizontal) { m[3] -= (flexImpl->grid_.horizontalSpacing_) / 2; m[1] -= (flexImpl->grid_.horizontalSpacing_ + 1) / 2; } else { m[0] -= (flexImpl->grid_.verticalSpacing_) / 2; m[2] -= (flexImpl->grid_.horizontalSpacing_ + 1) / 2; } } AlignmentFlag hAlign = it.alignment_ & AlignHorizontalMask; AlignmentFlag vAlign = it.alignment_ & AlignVerticalMask; /* * If not justifying along main axis, then we need to wrap inside * an additional (flex) element */ if (orientation == Orientation::Horizontal) { if (hAlign != static_cast<AlignmentFlag>(0)) { el->setProperty(Property::StyleFlex, "0 0 auto"); DomElement *wrap = DomElement::createNew(DomElementType::DIV); wrap->setId("w" + el->id()); wrap->setProperty(Property::StyleDisplay, styleDisplay()); wrap->setProperty(Property::StyleFlexFlow, styleFlex()); wrap->addChild(el); el = wrap; switch (hAlign) { case AlignmentFlag::Left: el->setProperty(Property::StyleJustifyContent, "flex-start"); break; case AlignmentFlag::Center: el->setProperty(Property::StyleJustifyContent, "center"); break; case AlignmentFlag::Right: el->setProperty(Property::StyleJustifyContent, "flex-end"); default: break; } } if (vAlign != static_cast<AlignmentFlag>(0)) switch (vAlign) { case AlignmentFlag::Top: el->setProperty(Property::StyleAlignSelf, "flex-start"); break; case AlignmentFlag::Middle: el->setProperty(Property::StyleAlignSelf, "center"); break; case AlignmentFlag::Bottom: el->setProperty(Property::StyleAlignSelf, "flex-end"); break; case AlignmentFlag::Baseline: el->setProperty(Property::StyleAlignSelf, "baseline"); default: break; } } else { if (vAlign != static_cast<AlignmentFlag>(0)) { el->setProperty(Property::StyleFlex, "0 0 auto"); DomElement *wrap = DomElement::createNew(DomElementType::DIV); wrap->setId("w" + el->id()); wrap->setProperty(Property::StyleDisplay, styleDisplay()); wrap->setProperty(Property::StyleFlexFlow, styleFlex()); wrap->addChild(el); el = wrap; switch (vAlign) { case AlignmentFlag::Top: el->setProperty(Property::StyleJustifyContent, "flex-start"); break; case AlignmentFlag::Middle: el->setProperty(Property::StyleJustifyContent, "center"); break; case AlignmentFlag::Bottom: el->setProperty(Property::StyleJustifyContent, "flex-end"); default: break; } } if (hAlign != static_cast<AlignmentFlag>(0)) switch (hAlign) { case AlignmentFlag::Left: el->setProperty(Property::StyleAlignSelf, "flex-start"); break; case AlignmentFlag::Center: el->setProperty(Property::StyleAlignSelf, "center"); break; case AlignmentFlag::Right: el->setProperty(Property::StyleAlignSelf, "flex-end"); break; default: break; } } { WStringStream flexProperty; int flexGrow = totalStretch == 0 ? 1 : s.stretch_; int flexShrink = totalStretch == 0 ? 1 : (s.stretch_ == 0 ? 0 : 1); flexProperty << flexGrow << ' ' << flexShrink << ' ' << s.initialSize_.cssText(); if (s.stretch_ == 0) el->setAttribute("flg", "0"); el->setProperty(Property::StyleFlex, flexProperty.str()); } switch (getDirection()) { case LayoutDirection::LeftToRight: m[3] += (grid_.horizontalSpacing_ + 1) / 2; m[1] += (grid_.horizontalSpacing_) / 2; break; case LayoutDirection::RightToLeft: m[1] += (grid_.horizontalSpacing_ + 1) / 2; m[3] += (grid_.horizontalSpacing_) / 2; break; case LayoutDirection::TopToBottom: m[0] += (grid_.horizontalSpacing_ + 1) / 2; m[2] += (grid_.horizontalSpacing_) / 2; break; case LayoutDirection::BottomToTop: m[2] += (grid_.horizontalSpacing_ + 1) / 2; m[0] += (grid_.horizontalSpacing_) / 2; break; } if (m[0] != 0 || m[1] != 0 || m[2] != 0 || m[3] != 0) { WStringStream marginProperty; marginProperty << m[0] << "px " << m[1] << "px " << m[2] << "px " << m[3] << "px"; el->setProperty(Property::StyleMargin, marginProperty.str()); } return el; }
DomElement *FlexLayoutImpl::createDomElement(DomElement *parent, bool fitWidth, bool fitHeight, WApplication *app) { addedItems_.clear(); removedItems_.clear(); int margin[] = { 0, 0, 0, 0 }; DomElement *result; if (layout()->parentLayout() == nullptr) { /* * If it is a top-level layout (as opposed to a nested layout), * configure overflow of the container. */ if (container() == app->root()) { /* * Reset body,html default paddings and so on if we are doing layout * in the entire document. */ app->setBodyClass(app->bodyClass() + " Wt-layout"); app->setHtmlClass(app->htmlClass() + " Wt-layout"); parent->setProperty(Property::StyleBoxSizing, "border-box"); } #ifndef WT_TARGET_JAVA layout()->getContentsMargins(margin + 3, margin, margin + 1, margin + 2); #else // WT_TARGET_JAVA margin[3] = layout()->getContentsMargin(Side::Left); margin[0] = layout()->getContentsMargin(Side::Top); margin[1] = layout()->getContentsMargin(Side::Right); margin[2] = layout()->getContentsMargin(Side::Bottom); #endif // WT_TARGET_JAVA Orientation orientation = getOrientation(); if (orientation == Orientation::Horizontal) { margin[3] = std::max(0, margin[3] - (grid_.horizontalSpacing_) / 2); margin[1] = std::max(0, margin[1] - (grid_.horizontalSpacing_ + 1) / 2); } else { margin[0] = std::max(0, margin[0] - (grid_.verticalSpacing_) / 2); margin[2] = std::max(0, margin[2] - (grid_.horizontalSpacing_ + 1) / 2); } ResizeSensor::applyIfNeeded(container()); container()->setFlexBox(true); result = parent; elId_ = container()->id(); } else { result = DomElement::createNew(DomElementType::DIV); elId_ = id(); result->setId(elId_); } if (margin[0] != 0 || margin[1] != 0 || margin[2] != 0 || margin[3] != 0) { WStringStream paddingProperty; paddingProperty << margin[0] << "px " << margin[1] << "px " << margin[2] << "px " << margin[3] << "px"; result->setProperty(Property::StylePadding, paddingProperty.str()); } // FIXME minsize/maxsize result->setProperty(Property::StyleDisplay, styleDisplay()); result->setProperty(Property::StyleFlexFlow, styleFlex()); Orientation orientation = getOrientation(); int c = count(orientation); int totalStretch = getTotalStretch(orientation); for (int i = 0; i < c; ++i) { DomElement *el = createElement(orientation, i, totalStretch, app); result->addChild(el); } WStringStream js; js << "layout=new " WT_CLASS ".FlexLayout(" << app->javaScriptClass() << ",'" << elId_ << "');"; result->callMethod(js.str()); return result; }
void WInteractWidget::updateDom(DomElement& element, bool all) { bool updateKeyDown = false; WApplication *app = WApplication::instance(); /* * -- combine enterPress, escapePress and keyDown signals */ EventSignal<> *enterPress = voidEventSignal(ENTER_PRESS_SIGNAL, false); EventSignal<> *escapePress = voidEventSignal(ESCAPE_PRESS_SIGNAL, false); EventSignal<WKeyEvent> *keyDown = keyEventSignal(KEYDOWN_SIGNAL, false); updateKeyDown = (enterPress && enterPress->needsUpdate(all)) || (escapePress && escapePress->needsUpdate(all)) || (keyDown && keyDown->needsUpdate(all)); if (updateKeyDown) { std::vector<DomElement::EventAction> actions; if (enterPress) { if (enterPress->needsUpdate(true)) { /* * prevent enterPressed from triggering a changed event on all * browsers except for Opera and IE */ std::string extraJS; const WEnvironment& env = app->environment(); if (dynamic_cast<WFormWidget *>(this) && !env.agentIsOpera() && !env.agentIsIE()) extraJS = "var g=this.onchange;" "" "this.onchange=function(){this.onchange=g;};"; actions.push_back (DomElement::EventAction("e.keyCode && (e.keyCode == 13)", enterPress->javaScript() + extraJS, enterPress->encodeCmd(), enterPress->isExposedSignal())); } enterPress->updateOk(); } if (escapePress) { if (escapePress->needsUpdate(true)) { actions.push_back (DomElement::EventAction("e.keyCode && (e.keyCode == 27)", escapePress->javaScript(), escapePress->encodeCmd(), escapePress->isExposedSignal())); } escapePress->updateOk(); } if (keyDown) { if (keyDown->needsUpdate(true)) { actions.push_back (DomElement::EventAction(std::string(), keyDown->javaScript(), keyDown->encodeCmd(), keyDown->isExposedSignal())); } keyDown->updateOk(); } if (!actions.empty()) element.setEvent("keydown", actions); else if (!all) element.setEvent("keydown", std::string(), std::string()); } /* * -- allow computation of dragged mouse distance */ EventSignal<WMouseEvent> *mouseDown = mouseEventSignal(MOUSE_DOWN_SIGNAL, false); EventSignal<WMouseEvent> *mouseUp = mouseEventSignal(MOUSE_UP_SIGNAL, false); EventSignal<WMouseEvent> *mouseMove = mouseEventSignal(MOUSE_MOVE_SIGNAL, false); EventSignal<WMouseEvent> *mouseDrag = mouseEventSignal(MOUSE_DRAG_SIGNAL, false); bool updateMouseMove = (mouseMove && mouseMove->needsUpdate(all)) || (mouseDrag && mouseDrag->needsUpdate(all)); bool updateMouseDown = (mouseDown && mouseDown->needsUpdate(all)) || updateMouseMove; bool updateMouseUp = (mouseUp && mouseUp->needsUpdate(all)) || updateMouseMove; std::string CheckDisabled = "if($(o).hasClass('" + app->theme()->disabledClass() + "')){" WT_CLASS ".cancelEvent(e);return;}"; if (updateMouseDown) { /* * when we have a mouseUp event, we also need a mouseDown event * to be able to compute dragDX/Y. * * When we have: * - a mouseDrag * - or a mouseDown + (mouseMove or mouseUp), * we need to capture everything after on mouse down, and keep track of the * down button if we have a mouseMove or mouseDrag */ WStringStream js; js << CheckDisabled; if (mouseUp && mouseUp->isConnected()) js << app->javaScriptClass() << "._p_.saveDownPos(event);"; if ((mouseDrag && mouseDrag->isConnected()) || (mouseDown && mouseDown->isConnected() && ((mouseUp && mouseUp->isConnected()) || (mouseMove && mouseMove->isConnected())))) js << WT_CLASS ".capture(this);"; if ((mouseMove && mouseMove->isConnected()) || (mouseDrag && mouseDrag->isConnected())) js << WT_CLASS ".mouseDown(e);"; if (mouseDown) { js << mouseDown->javaScript(); element.setEvent("mousedown", js.str(), mouseDown->encodeCmd(), mouseDown->isExposedSignal()); mouseDown->updateOk(); } else element.setEvent("mousedown", js.str(), std::string(), false); } if (updateMouseUp) { WStringStream js; /* * when we have a mouseMove or mouseDrag event, we need to keep track * of unpressing the button. */ js << CheckDisabled; if ((mouseMove && mouseMove->isConnected()) || (mouseDrag && mouseDrag->isConnected())) js << WT_CLASS ".mouseUp(e);"; if (mouseUp) { js << mouseUp->javaScript(); element.setEvent("mouseup", js.str(), mouseUp->encodeCmd(), mouseUp->isExposedSignal()); mouseUp->updateOk(); } else element.setEvent("mouseup", js.str(), std::string(), false); } if (updateMouseMove) { /* * We need to mix mouseDrag and mouseMove events. */ std::vector<DomElement::EventAction> actions; if (mouseMove) { actions.push_back (DomElement::EventAction(std::string(), mouseMove->javaScript(), mouseMove->encodeCmd(), mouseMove->isExposedSignal())); mouseMove->updateOk(); } if (mouseDrag) { actions.push_back (DomElement::EventAction(WT_CLASS ".buttons", mouseDrag->javaScript() + WT_CLASS ".drag(e);", mouseDrag->encodeCmd(), mouseDrag->isExposedSignal())); mouseDrag->updateOk(); } element.setEvent("mousemove", actions); } /* * -- mix mouseClick and mouseDblClick events in mouseclick since we * only want to fire one of both */ EventSignal<WMouseEvent> *mouseClick = mouseEventSignal(M_CLICK_SIGNAL, false); EventSignal<WMouseEvent> *mouseDblClick = mouseEventSignal(DBL_CLICK_SIGNAL, false); bool updateMouseClick = (mouseClick && mouseClick->needsUpdate(all)) || (mouseDblClick && mouseDblClick->needsUpdate(all)); if (updateMouseClick) { WStringStream js; js << CheckDisabled; if (mouseDrag) js << "if (" WT_CLASS ".dragged()) return;"; if (mouseDblClick && mouseDblClick->needsUpdate(all)) { /* * Click: if timer is running: * - clear timer, dblClick() * - start timer, clear timer and click() */ /* We have to prevent this immediately ! */ if (mouseClick) { if (mouseClick->defaultActionPrevented() || mouseClick->propagationPrevented()) { js << WT_CLASS ".cancelEvent(e"; if (mouseClick->defaultActionPrevented() && mouseClick->propagationPrevented()) js << ");"; else if (mouseClick->defaultActionPrevented()) js << ",0x2);"; else js << ",0x1);"; } } js << "if(" WT_CLASS ".isDblClick(o, e)) {" << mouseDblClick->javaScript(); if (mouseDblClick->isExposedSignal()) js << app->javaScriptClass() << "._p_.update(o,'" << mouseDblClick->encodeCmd() << "',e,true);"; mouseDblClick->updateOk(); js << "}else{" """if (" WT_CLASS ".isIElt9 && document.createEventObject) " "" "e = document.createEventObject(e);" """o.wtE1 = e;" """o.wtClickTimeout = setTimeout(function() {" "" "o.wtClickTimeout = null; o.wtE1 = null;"; if (mouseClick) { js << mouseClick->javaScript(); if (mouseClick->isExposedSignal()) { js << app->javaScriptClass() << "._p_.update(o,'" << mouseClick->encodeCmd() << "',e,true);"; } mouseClick->updateOk(); } const Configuration& conf = app->environment().server()->configuration(); js << "}," << conf.doubleClickTimeout() << ");}"; } else { if (mouseClick && mouseClick->needsUpdate(all)) { js << mouseClick->javaScript(); if (mouseClick->isExposedSignal()) { js << app->javaScriptClass() << "._p_.update(o,'" << mouseClick->encodeCmd() << "',e,true);"; } mouseClick->updateOk(); } } element.setEvent(CLICK_SIGNAL, js.str(), mouseClick ? mouseClick->encodeCmd() : ""); if (mouseDblClick) { if (app->environment().agentIsIElt(9)) element.setEvent("dblclick", "this.onclick()"); } } /* * -- mouseOver with delay */ EventSignal<WMouseEvent> *mouseOver = mouseEventSignal(MOUSE_OVER_SIGNAL, false); EventSignal<WMouseEvent> *mouseOut = mouseEventSignal(MOUSE_OUT_SIGNAL, false); bool updateMouseOver = mouseOver && mouseOver->needsUpdate(all); if (mouseOverDelay_) { if (updateMouseOver) { WStringStream js; js << "o.over=setTimeout(function() {" << "o.over = null;" << mouseOver->javaScript(); if (mouseOver->isExposedSignal()) { js << app->javaScriptClass() << "._p_.update(o,'" << mouseOver->encodeCmd() << "',e,true);"; } js << "}," << mouseOverDelay_ << ");"; element.setEvent("mouseover", js.str(), ""); mouseOver->updateOk(); if (!mouseOut) mouseOut = mouseEventSignal(MOUSE_OUT_SIGNAL, true); element.setEvent("mouseout", "clearTimeout(o.over); o.over=null;" + mouseOut->javaScript(), mouseOut->encodeCmd(), mouseOut->isExposedSignal()); mouseOut->updateOk(); } } else { if (updateMouseOver) { element.setEventSignal("mouseover", *mouseOver); mouseOver->updateOk(); } bool updateMouseOut = mouseOut && mouseOut->needsUpdate(all); if (updateMouseOut) { element.setEventSignal("mouseout", *mouseOut); mouseOut->updateOk(); } } updateEventSignals(element, all); WWebWidget::updateDom(element, all); }
void WMediaPlayer::render(WFlags<RenderFlag> flags) { // XXX subtitles, chapters, stream ? static const char *mediaNames[] = { "poster", "mp3", "m4a", "oga", "wav", "webma", "fla", "m4v", "ogv", "webmv", "flv" }; WApplication *app = WApplication::instance(); if (mediaUpdated_) { WStringStream ss; ss << '{'; bool first = true; for (unsigned i = 0; i < media_.size(); ++i) { if (media_[i].link.isNull()) continue; if (!first) ss << ','; std::string url = app->resolveRelativeUrl(media_[i].link.url()); ss << const_cast<char *>(mediaNames[media_[i].encoding]) << ": " << WWebWidget::jsStringLiteral(url); first = false; } ss << '}'; if (!(flags & RenderFull)) playerDo("setMedia", ss.str()); else { initialJs_ = ".jPlayer('setMedia', " + ss.str() + ')' + initialJs_; } mediaUpdated_ = false; } if (flags & RenderFull) { if (gui_ == this) createDefaultGui(); WStringStream ss; ss << jsPlayerRef() << ".jPlayer({" << "ready: function () {"; if (!initialJs_.empty()) ss << "$(this)" << initialJs_ << ';'; initialJs_.clear(); ss << "}," << "swfPath: \"" << WApplication::resourcesUrl() << "jPlayer\"," << "supplied: \""; bool first = true; for (unsigned i = 0; i < media_.size(); ++i) { if (media_[i].encoding != PosterImage) { if (!first) ss << ','; ss << const_cast<char *>(mediaNames[media_[i].encoding]); first = false; } } ss << "\","; if (mediaType_ == Video) { ss << "size: {" << "width: \"" << videoWidth_ << "px\"," << "height: \"" << videoHeight_ << "px\"," << "cssClass: \"jp-video-" << videoHeight_ << "p\"" << "},"; } ss << "cssSelectorAncestor: " << (gui_ ? "'#" + id() + '\'' : "''") << ", cssSelector: {"; const char *controlSelectors[] = { "videoPlay", "play", "pause", "stop", "volumeMute", "volumeUnmute", "volumeMax", "fullScreen", "restoreScreen", "repeat", "repeatOff" }; first = true; for (unsigned i = VideoPlay; i < RepeatOff; ++i) { if (control_[i]) { if (!first) ss << ", "; ss << const_cast<char *>(controlSelectors[i]) << ":\"#" << control_[i]->id() << "\""; first = false; } } const char *displaySelectors[] = { "currentTime", "duration" }; for (unsigned i = CurrentTime; i < Duration; ++i) { if (control_[i]) { if (!first) ss << ", "; ss << const_cast<char *>(displaySelectors[i]) << ":\"#" << display_[i]->id() << "\""; first = false; } } if (progressBar_[Time]) { if (!first) ss << ", "; ss << "seekBar:\"#" << progressBar_[Time]->id() << "\", " << "playBar:\"#bar" << progressBar_[Time]->id() << "\""; first = false; } if (progressBar_[Volume]) { if (!first) ss << ", "; ss << "volumeBar:\"#" << progressBar_[Volume]->id() << "\", " << "volumeBarValue:\"#bar" << progressBar_[Volume]->id() << "\""; first = false; } ss << '}' << "});"; ss << "new " WT_CLASS ".WMediaPlayer(" << app->javaScriptClass() << ',' << jsRef() << ");"; doJavaScript(ss.str()); boundSignals_ = 0; } if (boundSignals_ < signals_.size()) { WStringStream ss; ss << jsPlayerRef(); for (unsigned i = boundSignals_; i < signals_.size(); ++i) ss << ".bind('" << signals_[i]->name() << "', function(o, e) { " << signals_[i]->createCall() << "})"; ss << ';'; doJavaScript(ss.str()); boundSignals_ = signals_.size(); } WCompositeWidget::render(flags); }
void PaintedSlider::updateState() { bool rtl = WApplication::instance()->layoutDirection() == LayoutDirection::RightToLeft; Orientation o = slider_->orientation(); if (o == Orientation::Horizontal) { handle_->resize(slider_->handleWidth(), h()); handle_->setOffsets(0, Side::Top); } else { handle_->resize(w(), slider_->handleWidth()); handle_->setOffsets(0, Side::Left); } double l = o == Orientation::Horizontal ? w() : h(); double pixelsPerUnit = (l - slider_->handleWidth()) / range(); std::string dir; std::string size; if (o == Orientation::Horizontal) { dir = rtl ? "right" : "left"; size = "width"; } else { dir = "top"; size = "height"; } char u = (o == Orientation::Horizontal ? 'x' : 'y'); double max = l - slider_->handleWidth(); bool horizontal = o == Orientation::Horizontal; char buf[30]; // Buffer for round_js_str /* * Note: cancelling the mouseDown event prevents the selection behaviour */ WStringStream mouseDownJS; mouseDownJS << "obj.setAttribute('down', " WT_CLASS << ".widgetCoordinates(obj, event)." << u << ");" << WT_CLASS ".cancelEvent(event);"; WStringStream computeD; // = 'u' position relative to background, corrected for slider computeD << "var objh = " << handle_->jsRef() << "," << "objf = " << fill_->jsRef() << "," << "objb = " << slider_->jsRef() << "," << "page_u = WT.pageCoordinates(event)." << u << "," << "widget_page_u = WT.widgetPageCoordinates(objb)." << u << "," << "pos = page_u - widget_page_u," << "rtl = " << rtl << "," << "horizontal = " << horizontal << ";" << "if (rtl && horizontal)"; computeD << "pos = " << Utils::round_js_str(l, 3, buf) << " - pos;"; computeD << "var d = pos - down;"; WStringStream mouseMovedJS; mouseMovedJS << "var down = obj.getAttribute('down');" << "var WT = " WT_CLASS ";" << "if (down != null && down != '') {" << computeD.str(); mouseMovedJS << "d = Math.max(0, Math.min(d, " << Utils::round_js_str(max, 3, buf) << "));"; mouseMovedJS << "var v = Math.round(d/" << Utils::round_js_str(pixelsPerUnit, 3, buf) << ");"; mouseMovedJS << "var intd = v*" << Utils::round_js_str(pixelsPerUnit, 3, buf) << ";"; mouseMovedJS << "if (Math.abs(WT.pxself(objh, '" << dir << "') - intd) > 1) {" << "objf.style." << size << " = "; if (o == Orientation::Vertical) { mouseMovedJS << '(' << Utils::round_js_str(max, 3, buf); mouseMovedJS << " - intd + " << (slider_->handleWidth() / 2) << ")"; } else mouseMovedJS << "intd + " << (slider_->handleWidth() / 2); mouseMovedJS << " + 'px';" << "objh.style." << dir << " = intd + 'px';" << "var vs = "; if (o == Orientation::Horizontal) mouseMovedJS << "v + " << slider_->minimum(); else mouseMovedJS << slider_->maximum() << " - v"; mouseMovedJS << ";" << "var f = objb.onValueChange;" << "if (f) f(vs);"; if (slider_->sliderMoved().needsUpdate(true)) { #ifndef WT_TARGET_JAVA mouseMovedJS << slider_->sliderMoved().createCall({"vs"}); #else mouseMovedJS << slider_->sliderMoved().createCall("vs"); #endif } mouseMovedJS << "}" << "}"; WStringStream mouseUpJS; mouseUpJS << "var down = obj.getAttribute('down');" << "var WT = " WT_CLASS ";" << "if (down != null && down != '') {" << computeD.str() << "d += " << (slider_->handleWidth() / 2) << ";" #ifndef WT_TARGET_JAVA << sliderReleased_.createCall({"Math.round(d)"}) #else << sliderReleased_.createCall("Math.round(d)") #endif << "obj.removeAttribute('down');" << "}"; bool enabled = !slider_->isDisabled(); mouseDownJS_.setJavaScript(std::string("function(obj, event) {") + (enabled ? mouseDownJS.str() : "") + "}"); mouseMovedJS_.setJavaScript(std::string("function(obj, event) {") + (enabled ? mouseMovedJS.str() : "") + "}"); mouseUpJS_.setJavaScript(std::string("function(obj, event) {") + (enabled ? mouseUpJS.str() : "") + "}"); handleClickedJS_.setJavaScript(std::string("function(obj, event) {") + WT_CLASS + ".cancelEvent(event," + WT_CLASS + ".CancelPropagate); }"); update(); updateSliderPosition(); }
WT_BOSTREAM& Response::out() { if (!headersCommitted_) { if (response_ && !continuation_ && (resource_->dispositionType() != ContentDisposition::None || !resource_->suggestedFileName().empty())) { WStringStream cdp; switch (resource_->dispositionType()) { default: case ContentDisposition::Inline: cdp << "inline"; break; case ContentDisposition::Attachment: cdp << "attachment"; break; } const WString& fileName = resource_->suggestedFileName(); if (!fileName.empty()) { if (resource_->dispositionType() == ContentDisposition::None) { // backward compatibility-ish with older Wt versions cdp.clear(); cdp << "attachment"; } // Browser incompatibility hell: internatianalized filename suggestions // First filename is for browsers that don't support RFC 5987 // Second filename is for browsers that do support RFC 5987 cdp << ';'; // We cannot query wApp here, because wApp doesn't exist for // static resources. const char *ua = response_->userAgent(); bool isIE = ua && strstr(ua, "MSIE") != nullptr; bool isChrome = ua && strstr(ua, "Chrome") != nullptr; if (isIE || isChrome) { // filename="foo-%c3%a4-%e2%82%ac.html" // Note: IE never converts %20 back to space, so avoid escaping // IE wil also not url decode the filename if the file has no ASCII // extension (e.g. .txt) cdp << "filename=\"" << Utils::urlEncode(fileName.toUTF8(), " ") << "\";"; } else { // Binary UTF-8 sequence: for FF3, Safari, Chrome, Chrome9 cdp << "filename=\"" << fileName.toUTF8() << "\";"; } // Next will be picked by RFC 5987 in favour of the // one without specified encoding (Chrome9, cdp << Utils::EncodeHttpHeaderField("filename", fileName); addHeader("Content-Disposition", cdp.str()); } else { addHeader("Content-Disposition", cdp.str()); } } headersCommitted_ = true; } if (out_) return *out_; else return response_->out(); }
WString toWString(const Color& val) { WStringStream stream; stream << val.r << L" " << val.g << L" " << val.b << L" " << val.a; return stream.str(); }
WString toWString(const Quaternion& val) { WStringStream stream; stream << val.w << L" " << val.x << L" " << val.y << L" " << val.z; return stream.str(); }
WString toWString(const Vector4& val) { WStringStream stream; stream << val.x << L" " << val.y << L" " << val.z << L" " << val.w; return stream.str(); }
WString toWString(const Vector2I& val) { WStringStream stream; stream << val.x << L" " << val.y; return stream.str(); }