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