Coverage Report

Created: 2026-06-10 06:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wt/src/Wt/WCssTheme.C
Line
Count
Source
1
/*
2
 * Copyright (C) 2012 Emweb bv, Herent, Belgium.
3
 *
4
 * See the LICENSE file for terms of use.
5
 */
6
#include <vector>
7
#include <boost/algorithm/string.hpp>
8
9
#include "Wt/WAbstractItemView.h"
10
#include "Wt/WAbstractSpinBox.h"
11
#include "Wt/WApplication.h"
12
#include "Wt/WCalendar.h"
13
#include "Wt/WCssTheme.h"
14
#include "Wt/WDateEdit.h"
15
#include "Wt/WDialog.h"
16
#include "Wt/WEnvironment.h"
17
#include "Wt/WLinkedCssStyleSheet.h"
18
#include "Wt/WMessageResourceBundle.h"
19
#include "Wt/WPanel.h"
20
#include "Wt/WPopupMenu.h"
21
#include "Wt/WProgressBar.h"
22
#include "Wt/WPushButton.h"
23
#include "Wt/WSuggestionPopup.h"
24
#include "Wt/WTabWidget.h"
25
#include "Wt/WTimeEdit.h"
26
#include "Wt/WIconPair.h"
27
28
#include "DomElement.h"
29
30
#ifndef WT_DEBUG_JS
31
#include "js/CssThemeValidate.min.js"
32
#endif
33
34
namespace skeletons {
35
  extern const char* AuthCssTheme_xml;
36
}
37
38
namespace Wt {
39
40
WCssTheme::WCssTheme(const std::string& name)
41
0
  : WTheme(),
42
0
    name_(name)
43
0
{ }
44
45
WCssTheme::~WCssTheme()
46
0
{ }
47
48
std::vector<WLinkedCssStyleSheet> WCssTheme::styleSheets() const
49
0
{
50
0
  std::vector<WLinkedCssStyleSheet> result;
51
52
0
  if (!name_.empty()) {
53
0
    std::string themeDir = resourcesUrl();
54
55
0
    WApplication *app = WApplication::instance();
56
57
0
    result.push_back(WLinkedCssStyleSheet(WLink(themeDir + "wt.css")));
58
59
0
    if (app->environment().agentIsIElt(9))
60
0
      result.push_back(WLinkedCssStyleSheet(WLink(themeDir + "wt_ie.css")));
61
62
0
    if (app->environment().agent() == UserAgent::IE6)
63
0
      result.push_back(WLinkedCssStyleSheet(WLink(themeDir + "wt_ie6.css")));
64
0
  }
65
66
0
  return result;
67
0
}
68
69
void WCssTheme::apply(WWidget *widget, DomElement& element, int elementRole)
70
  const
71
0
{
72
0
  bool creating = element.mode() == DomElement::Mode::Create;
73
74
0
  if (!widget->isThemeStyleEnabled())
75
0
    return;
76
77
0
  {
78
0
    WPopupWidget *popup = dynamic_cast<WPopupWidget *>(widget);
79
0
    if (popup)
80
0
      element.addPropertyWord(Property::Class, "Wt-outset");
81
0
  }
82
83
0
  switch (element.type()) {
84
0
  case DomElementType::BUTTON:
85
0
    if (creating) {
86
0
      element.addPropertyWord(Property::Class, "Wt-btn");
87
0
      WPushButton *b = dynamic_cast<WPushButton *>(widget);
88
0
      if (b) {
89
0
        if (b->isDefault())
90
0
          element.addPropertyWord(Property::Class, "Wt-btn-default");
91
92
0
        if (!b->text().empty())
93
0
          element.addPropertyWord(Property::Class, "with-label");
94
0
      }
95
0
    }
96
0
    break;
97
98
0
  case DomElementType::UL:
99
0
    if (dynamic_cast<WPopupMenu *>(widget)) {
100
0
      element.addPropertyWord(Property::Class, "Wt-popupmenu");
101
0
      element.addPropertyWord(Property::Class, "Wt-outset");
102
0
    } else {
103
0
      WTabWidget *tabs
104
0
        = dynamic_cast<WTabWidget *>(widget->parent()->parent());
105
106
0
      if (tabs)
107
0
        element.addPropertyWord(Property::Class, "Wt-tabs");
108
0
      else {
109
0
        WSuggestionPopup *suggestions
110
0
          = dynamic_cast<WSuggestionPopup *>(widget);
111
112
0
        if (suggestions)
113
0
          element.addPropertyWord(Property::Class, "Wt-suggest");
114
0
      }
115
0
    }
116
0
    break;
117
118
0
  case DomElementType::LI:
119
0
    {
120
0
      WMenuItem *item = dynamic_cast<WMenuItem *>(widget);
121
0
      if (item) {
122
0
        if (item->isSeparator())
123
0
          element.addPropertyWord(Property::Class, "Wt-separator");
124
0
           if (item->isSectionHeader())
125
0
          element.addPropertyWord(Property::Class, "Wt-sectheader");
126
0
        if (item->menu())
127
0
          element.addPropertyWord(Property::Class, "submenu");
128
0
      }
129
0
    }
130
0
    break;
131
132
0
  case DomElementType::DIV:
133
0
    {
134
0
      WDialog *dialog = dynamic_cast<WDialog *>(widget);
135
0
      if (dialog) {
136
0
        element.addPropertyWord(Property::Class, "Wt-dialog");
137
0
        return;
138
0
      }
139
140
0
      WPanel *panel = dynamic_cast<WPanel *>(widget);
141
0
      if (panel) {
142
0
        element.addPropertyWord(Property::Class, "Wt-panel");
143
0
        element.addPropertyWord(Property::Class, "Wt-outset");
144
0
        return;
145
0
      }
146
147
0
      WProgressBar *bar = dynamic_cast<WProgressBar *>(widget);
148
0
      if (bar) {
149
0
        switch (elementRole) {
150
0
        case MainElement:
151
0
          element.addPropertyWord(Property::Class, "Wt-progressbar");
152
0
          break;
153
0
        case ProgressBarBar:
154
0
          element.addPropertyWord(Property::Class, "Wt-pgb-bar");
155
0
          break;
156
0
        case ProgressBarLabel:
157
0
          element.addPropertyWord(Property::Class, "Wt-pgb-label");
158
0
        }
159
0
        return;
160
0
      }
161
0
    }
162
163
0
    break;
164
165
0
  case DomElementType::INPUT:
166
0
    {
167
0
      WAbstractSpinBox *spinBox = dynamic_cast<WAbstractSpinBox *>(widget);
168
0
      if (spinBox) {
169
0
        element.addPropertyWord(Property::Class, "Wt-spinbox");
170
0
        return;
171
0
      }
172
173
0
      WDateEdit *dateEdit = dynamic_cast<WDateEdit *>(widget);
174
0
      if (dateEdit && !dateEdit->nativeControl()) {
175
0
        element.addPropertyWord(Property::Class, "Wt-dateedit");
176
0
        return;
177
0
      }
178
179
0
      WTimeEdit *timeEdit = dynamic_cast<WTimeEdit *>(widget);
180
0
      if (timeEdit && !timeEdit->nativeControl()) {
181
0
        element.addPropertyWord(Property::Class, "Wt-timeedit");
182
0
        return;
183
0
      }
184
0
    }
185
0
    break;
186
187
0
  case DomElementType::SPAN:
188
0
    {
189
0
      WBadge *badge = dynamic_cast<WBadge *>(widget);
190
0
      if (badge) {
191
0
        element.addPropertyWord(Property::Class, "Wt-badge");
192
0
      }
193
0
    }
194
195
0
  default:
196
0
    break;
197
0
  }
198
0
}
199
200
std::string WCssTheme::disabledClass() const
201
0
{
202
0
  return "Wt-disabled";
203
0
}
204
205
std::string WCssTheme::activeClass() const
206
0
{
207
0
  return "Wt-selected";
208
0
}
209
210
std::string WCssTheme::utilityCssClass(int utilityCssClassRole) const
211
0
{
212
0
  switch (utilityCssClassRole) {
213
0
  case ToolTipOuter:
214
0
    return "Wt-tooltip";
215
0
  default:
216
0
    return "";
217
0
  }
218
0
}
219
220
std::string WCssTheme::name() const
221
0
{
222
0
  return name_;
223
0
}
224
225
bool WCssTheme::canStyleAnchorAsButton() const
226
0
{
227
0
  return false;
228
0
}
229
230
void WCssTheme::loadValidationStyling(WApplication* app) const
231
0
{
232
0
  LOAD_JAVASCRIPT(app, "js/CssThemeValidate.js", "validate", wtjs1);
233
0
  LOAD_JAVASCRIPT(app, "js/CssThemeValidate.js", "setValidationState", wtjs2);
234
0
}
235
236
void WCssTheme::applyValidationStyle(WWidget *widget,
237
                                     const Wt::WValidator::Result& validation,
238
                                     WFlags<ValidationStyleFlag> styles) const
239
0
{
240
0
  WApplication *app = WApplication::instance();
241
0
  loadValidationStyling(app);
242
243
0
  if (app->environment().ajax()) {
244
0
    WStringStream js;
245
0
    js << WT_CLASS ".setValidationState(" << widget->jsRef() << ","
246
0
       << (validation.state() == ValidationState::Valid) << ","
247
0
       << validation.message().jsStringLiteral() << ","
248
0
       << styles.value() << ");";
249
250
0
    widget->doJavaScript(js.str());
251
0
  } else {
252
0
    bool validStyle
253
0
      = (validation.state() == ValidationState::Valid) &&
254
0
      styles.test(ValidationStyleFlag::ValidStyle);
255
0
    bool invalidStyle
256
0
      = (validation.state() != ValidationState::Valid) &&
257
0
      styles.test(ValidationStyleFlag::InvalidStyle);
258
259
0
    widget->toggleStyleClass("Wt-valid", validStyle);
260
0
    widget->toggleStyleClass("Wt-invalid", invalidStyle);
261
0
  }
262
0
}
263
264
bool WCssTheme::canBorderBoxElement(WT_MAYBE_UNUSED const DomElement& element) const
265
0
{
266
0
  return true;
267
0
}
268
269
void WCssTheme::applyFunctionalStyling(WWidget *widget,
270
                                       WWidget *child,
271
                                       int widgetRole) const
272
0
{
273
0
  switch (widgetRole) {
274
0
  case DialogCloseIcon:
275
0
    child->addStyleClass("closeicon");
276
0
    break;
277
278
0
  case MenuItemIcon:
279
0
    child->addStyleClass("Wt-icon");
280
0
    break;
281
0
  case MenuItemCheckBox:
282
0
    child->addStyleClass("Wt-chkbox");
283
0
    break;
284
0
  case MenuItemClose:
285
0
    widget->addStyleClass("Wt-closable");
286
0
    child->addStyleClass("closeicon");
287
0
    break;
288
289
0
  case TableViewRowContainer:
290
0
    {
291
0
      WAbstractItemView *view = dynamic_cast<WAbstractItemView *>(widget);
292
293
0
      std::string backgroundImage;
294
295
0
      if (view->alternatingRowColors())
296
0
        backgroundImage = "stripes/stripe-";
297
0
      else
298
0
        backgroundImage = "no-stripes/no-stripe-";
299
300
0
      backgroundImage = resourcesUrl() + backgroundImage
301
0
        + std::to_string(static_cast<int>(view->rowHeight().toPixels()))
302
0
        + "px.gif";
303
304
0
      child->decorationStyle().setBackgroundImage(WLink(backgroundImage));
305
306
0
      break;
307
0
    }
308
0
  }
309
0
}
310
311
void WCssTheme::applyOptionalStyling(WT_MAYBE_UNUSED WWidget *widget,
312
                                     WWidget *child,
313
                                     int widgetRole) const
314
0
{
315
0
  switch (widgetRole) {
316
0
  case DialogCoverWidget:
317
0
    child->setStyleClass("Wt-dialogcover in");
318
0
    break;
319
0
  case DialogTitleBar:
320
0
    child->addStyleClass("titlebar");
321
0
    break;
322
0
  case DialogBody:
323
0
    child->addStyleClass("body");
324
0
    break;
325
0
  case DialogFooter:
326
0
    child->addStyleClass("footer");
327
0
    break;
328
0
  case DatePickerPopup:
329
0
    child->addStyleClass("Wt-datepicker");
330
0
    break;
331
0
  case DatePickerIcon:
332
0
    {
333
0
      auto icon = dynamic_cast<WImage*>(child);
334
0
      icon->setImageLink(WApplication::relativeResourcesUrl() + "date.gif");
335
0
      icon->setVerticalAlignment(AlignmentFlag::Middle);
336
0
      icon->resize(16, 16);
337
0
      break;
338
0
    }
339
340
0
  case PanelTitleBar:
341
0
    child->addStyleClass("titlebar");
342
0
    break;
343
0
  case PanelBody:
344
0
    child->addStyleClass("body");
345
0
    break;
346
0
  case PanelCollapseButton:
347
0
    child->addStyleClass("Wt-collapse-button");
348
0
    break;
349
350
0
  case AuthWidgets:
351
0
    WApplication *app = WApplication::instance();
352
0
    app->useStyleSheet(WApplication::relativeResourcesUrl() + "form.css");
353
0
    app->builtinLocalizedStrings().useBuiltin(skeletons::AuthCssTheme_xml);
354
0
    break;
355
0
  }
356
0
}
357
358
}