Coverage Report

Created: 2026-09-23 07:12

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wt/src/Wt/WAbstractSpinBox.C
Line
Count
Source
1
/*
2
 * Copyright (C) 2011 Emweb bv, Herent, Belgium.
3
 *
4
 * See the LICENSE file for terms of use.
5
 */
6
7
#include "Wt/WAbstractSpinBox.h"
8
#include "Wt/WApplication.h"
9
#include "Wt/WEnvironment.h"
10
#include "Wt/WValidator.h"
11
12
#include "DomElement.h"
13
14
#include <boost/algorithm/string.hpp>
15
16
#ifndef WT_DEBUG_JS
17
#include "js/WSpinBox.min.js"
18
#endif
19
20
namespace Wt {
21
22
class SpinBoxValidator : public WValidator
23
{
24
public:
25
  SpinBoxValidator(WAbstractSpinBox *spinBox)
26
0
    : spinBox_(spinBox)
27
0
  { }
28
29
0
  virtual Result validate(const WT_USTRING& input) const override {
30
0
    bool valid = spinBox_->parseValue(input);
31
0
    if (valid)
32
0
      return spinBox_->validateRange();
33
0
    else
34
0
      return Result(ValidationState::Invalid);
35
0
  }
36
37
0
  virtual std::string javaScriptValidate() const override {
38
0
    return
39
0
      "new function() { "
40
0
      """this.validate = function(t) {"
41
0
      ""  "return " + spinBox_->jsRef() + ".wtObj.validate(t);"
42
0
      """};"
43
0
      "}";
44
0
  }
45
46
private:
47
  WAbstractSpinBox *spinBox_;
48
};
49
50
WAbstractSpinBox::WAbstractSpinBox()
51
0
  : changed_(false),
52
0
    valueChangedConnection_(false),
53
0
    preferNative_(false),
54
0
    setup_(false),
55
0
    jsValueChanged_(this, "spinboxValueChanged", true)
56
0
{
57
0
  setNativeControl(WWebWidget::defaultNativeControl());
58
0
}
59
60
void WAbstractSpinBox::setNativeControl(bool nativeControl)
61
0
{
62
0
  preferNative_ = nativeControl;
63
0
}
64
65
bool WAbstractSpinBox::nativeControl() const
66
0
{
67
0
  if (preferNative_) {
68
0
    if (!WLineEdit::inputMask().empty()) {
69
0
      return false;
70
0
    }
71
72
0
    const WEnvironment& env = WApplication::instance()->environment();
73
0
    if ((env.agentIsChrome() &&
74
0
         static_cast<unsigned int>(env.agent()) >=
75
0
         static_cast<unsigned int>(UserAgent::Chrome5))
76
0
        || (env.agentIsSafari() &&
77
0
            static_cast<unsigned int>(env.agent()) >=
78
0
            static_cast<unsigned int>(UserAgent::Safari4))
79
0
        || (env.agentIsOpera() &&
80
0
            static_cast<unsigned int>(env.agent()) >=
81
0
            static_cast<unsigned int>(UserAgent::Opera10)))
82
0
      return true;
83
0
  }
84
85
0
  return false;
86
0
}
87
88
void WAbstractSpinBox::setPrefix(const WString& prefix)
89
0
{
90
0
  if (prefix_ != prefix) {
91
0
    prefix_ = prefix;
92
0
    setText(textFromValue());
93
0
    changed_ = true;
94
0
    repaint();
95
0
  }
96
0
}
97
98
void WAbstractSpinBox::setSuffix(const WString& suffix)
99
0
{
100
0
  if (suffix_ != suffix) {
101
0
    suffix_ = suffix;
102
0
    setText(textFromValue());
103
0
    changed_ = true;
104
0
    repaint();
105
0
  }
106
0
}
107
108
void WAbstractSpinBox::render(WFlags<RenderFlag> flags)
109
0
{
110
  /*
111
   * In theory we are a bit late here to decide what we want to become:
112
   * somebody could already have asked the domElementType()
113
   */
114
0
  if (!setup_ && flags.test(RenderFlag::Full)) {
115
0
    setup();
116
0
  }
117
118
0
  if (jsValueChanged().needsUpdate(true)) {
119
0
    WStringStream function;
120
0
    function << jsRef() << ".wtObj.jsValueChanged=";
121
0
    if (jsValueChanged().isConnected()) {
122
0
      function << "function(oldv, v){"
123
0
#ifndef WT_TARGET_JAVA
124
0
               << "var o=null;var e=null;" << jsValueChanged().createCall({"oldv", "v"}) << "};";
125
#else // WT_TARGET_JAVA
126
               << "var o=null;var e=null;" << jsValueChanged().createCall("oldv", "v") << "};";
127
#endif // WT_TARGET_JAVA
128
0
    } else {
129
0
      function << "function() {};";
130
0
    }
131
0
    doJavaScript(function.str());
132
0
  }
133
134
0
  WLineEdit::render(flags);
135
0
}
136
137
void WAbstractSpinBox::connectJavaScript(Wt::EventSignalBase& s,
138
                                         const std::string& methodName)
139
0
{
140
0
  std::string jsFunction =
141
0
    "function(obj, event) {"
142
0
    """var o = " + jsRef() + ";"
143
0
    """if (o && o.wtObj) o.wtObj." + methodName + "(obj, event);"
144
0
    "}";
145
146
0
  s.connect(jsFunction);
147
0
}
148
149
void WAbstractSpinBox::defineJavaScript()
150
0
{
151
0
  WApplication *app = WApplication::instance();
152
153
0
  LOAD_JAVASCRIPT(app, "js/WSpinBox.js", "WSpinBox", wtjs1);
154
155
0
  WStringStream ss;
156
157
0
  ss << "new " WT_CLASS ".WSpinBox("
158
0
    << app->javaScriptClass() << "," << jsRef() << "," << decimals() << ","
159
0
    << prefix().jsStringLiteral() << ","
160
0
    << suffix().jsStringLiteral() << ","
161
0
    << jsMinMaxStep() << ","
162
0
    << jsStringLiteral(WLocale::currentLocale().decimalPoint()) << ","
163
0
    << jsStringLiteral(WLocale::currentLocale().groupSeparator())
164
0
    << ");";
165
166
0
  setJavaScriptMember(" WSpinBox", ss.str());
167
0
}
168
169
void WAbstractSpinBox::setText(const WT_USTRING& text)
170
0
{
171
0
  parseValue(text);
172
173
0
  WLineEdit::setText(textFromValue());
174
0
}
175
176
void WAbstractSpinBox::setFormData(const FormData& formData)
177
0
{
178
0
  WLineEdit::setFormData(formData);
179
180
0
  parseValue(text());
181
0
}
182
183
void WAbstractSpinBox::updateDom(DomElement& element, bool all)
184
0
{
185
0
  if (all || changed_) {
186
0
    if (!all) {
187
0
      if (!nativeControl())
188
0
        doJavaScript(jsRef() + ".wtObj"
189
0
                     ".configure("
190
0
                     + std::to_string(decimals()) + ","
191
0
                     + prefix().jsStringLiteral() + ","
192
0
                     + suffix().jsStringLiteral() + ","
193
0
                     + jsMinMaxStep() + ");");
194
0
      else
195
0
        setValidator(std::shared_ptr<WValidator>(createValidator().release()));
196
0
    }
197
0
  }
198
199
0
  changed_ = false;
200
201
0
  WLineEdit::updateDom(element, all);
202
203
0
  if (all && nativeControl())
204
0
    element.setAttribute("type", "number");
205
0
}
206
207
void WAbstractSpinBox::propagateRenderOk(bool deep)
208
0
{
209
0
  changed_ = false;
210
211
0
  WLineEdit::propagateRenderOk(deep);
212
0
}
213
214
void WAbstractSpinBox::setup()
215
0
{
216
0
  setup_ = true;
217
0
  bool useNative = nativeControl();
218
219
0
  if (!useNative) {
220
0
    defineJavaScript();
221
222
#ifdef WT_CNOR
223
    EventSignalBase& b = mouseMoved();
224
    EventSignalBase& c = keyWentDown();
225
#endif
226
227
0
    connectJavaScript(mouseMoved(), "mouseMove");
228
0
    connectJavaScript(mouseWentUp(), "mouseUp");
229
0
    connectJavaScript(mouseWentDown(), "mouseDown");
230
0
    connectJavaScript(mouseWentOut(), "mouseOut");
231
0
    connectJavaScript(keyWentDown(), "keyDown");
232
0
    connectJavaScript(keyWentUp(), "keyUp");
233
234
0
    if (!prefix_.empty() || !suffix_.empty())
235
0
      setValidator(std::shared_ptr<SpinBoxValidator>(new SpinBoxValidator(this)));
236
0
  }
237
0
}
238
239
ValidationState WAbstractSpinBox::validate()
240
0
{
241
0
  return WLineEdit::validate();
242
0
}
243
244
void WAbstractSpinBox::refresh()
245
0
{
246
0
  if (isRendered()) {
247
0
    doJavaScript
248
0
      (jsRef() + ".wtObj"
249
0
       ".setLocale("
250
0
       + jsStringLiteral(WLocale::currentLocale().decimalPoint()) + ","
251
0
       + jsStringLiteral(WLocale::currentLocale().groupSeparator()) + ");");
252
0
  }
253
254
0
  WLineEdit::refresh();
255
0
}
256
257
int WAbstractSpinBox::boxPadding(Orientation orientation) const
258
0
{
259
0
  if (!nativeControl() && orientation == Orientation::Horizontal)
260
0
    return WLineEdit::boxPadding(orientation) + 8; // Half since for one side
261
0
  else
262
0
    return WLineEdit::boxPadding(orientation);
263
0
}
264
265
bool WAbstractSpinBox::parseValue(const WT_USTRING& text)
266
0
{
267
0
  std::string textUtf8 = text.toUTF8();
268
269
0
  bool valid = true;
270
271
0
  if (!nativeControl()) {
272
0
    valid = false;
273
274
0
    std::string prefixUtf8 = prefix_.toUTF8();
275
0
    std::string suffixUtf8 = suffix_.toUTF8();
276
277
0
    if (boost::starts_with(textUtf8, prefixUtf8)) {
278
0
      textUtf8 = textUtf8.substr(prefixUtf8.length());
279
0
      if (boost::ends_with(textUtf8, suffixUtf8)) {
280
0
        textUtf8 = textUtf8.substr(0, textUtf8.length() - suffixUtf8.length());
281
0
        valid = true;
282
0
      }
283
0
    }
284
0
  }
285
286
0
  if (valid)
287
0
    valid = textUtf8.length() > 0;
288
289
0
  if (valid)
290
0
    valid = parseNumberValue(textUtf8);
291
292
0
  return valid;
293
0
}
294
295
}