1 /*
2 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include <boost/algorithm/string.hpp>
7
8 #include "Wt/WApplication.h"
9 #include "Wt/WLogger.h"
10 #include "Wt/WText.h"
11 #include "DomElement.h"
12 #include "WebSession.h"
13 #include "RefEncoder.h"
14
15 namespace Wt {
16
17 LOGGER("WText");
18
RichText()19 WText::RichText::RichText()
20 : format(TextFormat::XHTML)
21 { }
22
setText(const WString & newText)23 bool WText::RichText::setText(const WString& newText)
24 {
25 text = newText;
26
27 bool ok = checkWellFormed();
28 if (!ok)
29 format = TextFormat::Plain;
30
31 return ok;
32 }
33
setFormat(TextFormat newFormat)34 bool WText::RichText::setFormat(TextFormat newFormat)
35 {
36 if (format != newFormat) {
37 TextFormat oldFormat = format;
38 format = newFormat;
39 bool ok = checkWellFormed();
40
41 if (!ok)
42 format = oldFormat;
43
44 return ok;
45 } else
46 return true;
47 }
48
checkWellFormed()49 bool WText::RichText::checkWellFormed()
50 {
51 if (format == TextFormat::XHTML &&
52 (text.literal() || !text.args().empty())) {
53 return removeScript(text);
54 } else
55 return true;
56 }
57
formattedText()58 std::string WText::RichText::formattedText() const
59 {
60 if (format == TextFormat::Plain)
61 return escapeText(text, true).toUTF8();
62 else
63 return text.toXhtmlUTF8();
64 }
65
WText()66 WText::WText()
67 : padding_(nullptr)
68 {
69 flags_.set(BIT_WORD_WRAP);
70 }
71
WText(const WString & text)72 WText::WText(const WString& text)
73 : padding_(nullptr)
74 {
75 flags_.set(BIT_WORD_WRAP);
76 setText(text);
77 }
78
WText(const WString & text,TextFormat format)79 WText::WText(const WString& text, TextFormat format)
80 : padding_(nullptr)
81 {
82 text_.format = format;
83 flags_.set(BIT_WORD_WRAP);
84 setText(text);
85 }
86
~WText()87 WText::~WText()
88 {
89 delete[] padding_;
90 }
91
setText(const WString & text)92 bool WText::setText(const WString& text)
93 {
94 bool unChanged = canOptimizeUpdates() && (text == text_.text);
95
96 // Even if the current text is the same, it may be a tr() one which
97 // may get other content in a different locale
98 bool ok = text_.setText(text);
99
100 if (canOptimizeUpdates() && unChanged)
101 return true;
102
103 flags_.set(BIT_TEXT_CHANGED);
104 repaint(RepaintFlag::SizeAffected);
105
106 return ok;
107 }
108
autoAdjustInline()109 void WText::autoAdjustInline()
110 {
111 if (text_.format != TextFormat::Plain && isInline()) {
112 std::string t = text_.text.toUTF8();
113 boost::trim_left(t);
114 if ( boost::istarts_with(t, "<div")
115 || boost::istarts_with(t, "<p")
116 || boost::istarts_with(t, "<h"))
117 setInline(false);
118 }
119 }
120
setWordWrap(bool wordWrap)121 void WText::setWordWrap(bool wordWrap)
122 {
123 if (flags_.test(BIT_WORD_WRAP) != wordWrap) {
124 flags_.set(BIT_WORD_WRAP, wordWrap);
125 flags_.set(BIT_WORD_WRAP_CHANGED);
126 repaint(RepaintFlag::SizeAffected);
127 }
128 }
129
setTextAlignment(AlignmentFlag textAlignment)130 void WText::setTextAlignment(AlignmentFlag textAlignment)
131 {
132 flags_.reset(BIT_TEXT_ALIGN_LEFT);
133 flags_.reset(BIT_TEXT_ALIGN_CENTER);
134 flags_.reset(BIT_TEXT_ALIGN_RIGHT);
135
136 switch (textAlignment) {
137 case AlignmentFlag::Left: flags_.set(BIT_TEXT_ALIGN_LEFT); break;
138 case AlignmentFlag::Center: flags_.set(BIT_TEXT_ALIGN_CENTER); break;
139 case AlignmentFlag::Right: flags_.set(BIT_TEXT_ALIGN_RIGHT); break;
140 default:
141 LOG_ERROR("setTextAlignment(): illegal value for textAlignment");
142 return;
143 }
144
145 flags_.set(BIT_TEXT_ALIGN_CHANGED);
146 repaint();
147 }
148
textAlignment()149 AlignmentFlag WText::textAlignment() const
150 {
151 if (flags_.test(BIT_TEXT_ALIGN_CENTER))
152 return AlignmentFlag::Center;
153 else if (flags_.test(BIT_TEXT_ALIGN_RIGHT))
154 return AlignmentFlag::Right;
155 else
156 return AlignmentFlag::Left; // perhaps take into account RLT setting?
157 }
158
updateDom(DomElement & element,bool all)159 void WText::updateDom(DomElement& element, bool all)
160 {
161 if (flags_.test(BIT_TEXT_CHANGED) || all) {
162 std::string text = formattedText();
163 if (flags_.test(BIT_TEXT_CHANGED) || !text.empty())
164 element.setProperty(Wt::Property::InnerHTML, text);
165 flags_.reset(BIT_TEXT_CHANGED);
166 }
167
168 if (flags_.test(BIT_WORD_WRAP_CHANGED) || all) {
169 if (!all || !flags_.test(BIT_WORD_WRAP))
170 element.setProperty(Wt::Property::StyleWhiteSpace,
171 flags_.test(BIT_WORD_WRAP) ? "normal" : "nowrap");
172 flags_.reset(BIT_WORD_WRAP_CHANGED);
173 }
174
175 if (flags_.test(BIT_PADDINGS_CHANGED)
176 || (all && padding_ &&
177 !( padding_[0].isAuto() && padding_[1].isAuto()
178 && padding_[2].isAuto() && padding_[3].isAuto()))) {
179
180 if ((padding_[0] == padding_[1]) && (padding_[0] == padding_[2])
181 && (padding_[0] == padding_[3]))
182 element.setProperty(Property::StylePadding, padding_[0].cssText());
183 else {
184 WStringStream s;
185 for (unsigned i = 0; i < 4; ++i) {
186 if (i != 0)
187 s << ' ';
188 s << (padding_[i].isAuto() ? "0" : padding_[i].cssText());
189 }
190 element.setProperty(Property::StylePadding, s.str());
191 }
192
193 flags_.reset(BIT_PADDINGS_CHANGED);
194 }
195
196 if (flags_.test(BIT_TEXT_ALIGN_CHANGED) || all) {
197 if (flags_.test(BIT_TEXT_ALIGN_CENTER))
198 element.setProperty(Property::StyleTextAlign, "center");
199 else if (flags_.test(BIT_TEXT_ALIGN_RIGHT))
200 element.setProperty(Property::StyleTextAlign, "right");
201 else if (flags_.test(BIT_TEXT_ALIGN_LEFT))
202 element.setProperty(Property::StyleTextAlign, "left");
203 else if (!all)
204 element.setProperty(Property::StyleTextAlign, "");
205
206 flags_.reset(BIT_TEXT_ALIGN_CHANGED);
207 }
208
209 WInteractWidget::updateDom(element, all);
210 }
211
propagateRenderOk(bool deep)212 void WText::propagateRenderOk(bool deep)
213 {
214 flags_.reset(BIT_TEXT_CHANGED);
215 flags_.reset(BIT_WORD_WRAP_CHANGED);
216 flags_.reset(BIT_PADDINGS_CHANGED);
217 flags_.reset(BIT_TEXT_ALIGN_CHANGED);
218
219 WInteractWidget::propagateRenderOk(deep);
220 }
221
setTextFormat(TextFormat textFormat)222 bool WText::setTextFormat(TextFormat textFormat)
223 {
224 return text_.setFormat(textFormat);
225 }
226
setInternalPathEncoding(bool enabled)227 void WText::setInternalPathEncoding(bool enabled)
228 {
229 if (flags_.test(BIT_ENCODE_INTERNAL_PATHS) != enabled) {
230 flags_.set(BIT_ENCODE_INTERNAL_PATHS, enabled);
231 flags_.set(BIT_TEXT_CHANGED);
232 }
233 }
234
setPadding(const WLength & length,WFlags<Side> sides)235 void WText::setPadding(const WLength& length, WFlags<Side> sides)
236 {
237 if (!padding_) {
238 padding_ = new WLength[4];
239 #ifdef WT_TARGET_JAVA
240 padding_[0] = padding_[1] = padding_[2] = padding_[3] = WLength::Auto;
241 #endif // WT_TARGET_JAVA
242 }
243
244 if (sides.test(Side::Top)) {
245 if (isInline()) {
246 LOG_WARN("setPadding(..., Side::Top) is not supported for inline WText. "
247 "If your WText is not inline, you can call setInline(true) before setPadding(...) "
248 "to disable this warning.");
249 }
250 padding_[0] = length;
251 }
252 if (sides.test(Side::Right))
253 padding_[1] = length;
254 if (sides.test(Side::Bottom)) {
255 if (isInline()) {
256 LOG_WARN("setPadding(..., Side::Bottom) is not supported for inline WText. "
257 "If your WText is not inline, you can call setInline(true) before setPadding(...) "
258 "to disable this warning.");
259 }
260 padding_[2] = length;
261 }
262 if (sides.test(Side::Left))
263 padding_[3] = length;
264
265 flags_.set(BIT_PADDINGS_CHANGED);
266 repaint(RepaintFlag::SizeAffected);
267 }
268
padding(Side side)269 WLength WText::padding(Side side) const
270 {
271 if (!padding_)
272 return WLength::Auto;
273
274 switch (side) {
275 case Side::Top:
276 return padding_[0];
277 case Side::Right:
278 return padding_[1];
279 case Side::Bottom:
280 return padding_[2];
281 case Side::Left:
282 return padding_[3];
283 default:
284 LOG_ERROR("padding(): improper side.");
285 return WLength();
286 }
287 }
288
formattedText()289 std::string WText::formattedText() const
290 {
291 if (text_.format == TextFormat::Plain)
292 return escapeText(text_.text, true).toUTF8();
293 else {
294 WApplication *app = WApplication::instance();
295 if (flags_.test(BIT_ENCODE_INTERNAL_PATHS)
296 || app->session()->hasSessionIdInUrl()) {
297 WFlags<RefEncoderOption> options;
298 if (flags_.test(BIT_ENCODE_INTERNAL_PATHS))
299 options |= EncodeInternalPaths;
300 if (app->session()->hasSessionIdInUrl())
301 options |= EncodeRedirectTrampoline;
302 return EncodeRefs(text_.text, options).toXhtmlUTF8();
303 } else
304 return text_.text.toXhtmlUTF8();
305 }
306 }
307
domElementType()308 DomElementType WText::domElementType() const
309 {
310 return isInline() ? DomElementType::SPAN : DomElementType::DIV;
311 }
312
render(WFlags<RenderFlag> flags)313 void WText::render(WFlags<RenderFlag> flags)
314 {
315 if (flags_.test(BIT_TEXT_CHANGED))
316 autoAdjustInline();
317
318 WInteractWidget::render(flags);
319 }
320
refresh()321 void WText::refresh()
322 {
323 if (text_.text.refresh()) {
324 flags_.set(BIT_TEXT_CHANGED);
325 repaint(RepaintFlag::SizeAffected);
326 }
327
328 WInteractWidget::refresh();
329 }
330
331 }
332