• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ossia / score / 35310244323

18 Sep 2026 05:17AM UTC coverage: 39.902% (+2.2%) from 37.724%
35310244323

push

github

jcelerier
tests: include CommandDispatcher where JsLibraryScriptSavingTest uses it

ee4b67af6f added the test with a CommandDispatcher<> but without its header,
relying on it arriving transitively. The Coverage build compiles with clang-19
at gnu++23 and does not get it that way:

  JsLibraryScriptSavingTest.cpp:70:3: error: no template named 'CommandDispatcher'

This has been broken since 2026-09-13 and was hidden behind the boost download
failure, which stopped that job before it ever reached the compile.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K1e2ZdUQh7Srmv6BGVakrQ

114906 of 287968 relevant lines covered (39.9%)

77906.02 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/plugins/score-plugin-protocols/Protocols/Bitfocus/BitfocusProtocolSettingsWidget.cpp
1
// This is an open source non-commercial project. Dear PVS-Studio, please check
2
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
3
#include "BitfocusProtocolSettingsWidget.hpp"
4

5
#include "BitfocusContext.hpp"
6
#include "BitfocusProtocolFactory.hpp"
7
#include "BitfocusSpecificSettings.hpp"
8

9
#include <State/Widgets/AddressFragmentLineEdit.hpp>
10

11
#include <Device/Protocol/ProtocolSettingsWidget.hpp>
12

13
#include <score/widgets/ValidationPalette.hpp>
14
#include <score/widgets/MarginLess.hpp>
15

16
#include <ossia/network/value/value_conversion.hpp>
17

18
#include <QCheckBox>
19
#include <QComboBox>
20
#include <QDir>
21
#include <QFormLayout>
22
#include <QLabel>
23
#include <QLineEdit>
24
#include <QResizeEvent>
25
#include <QScrollArea>
26
#include <QSpinBox>
27
#include <QVariant>
28

29
#include <wobjectimpl.h>
30

31
namespace Protocols
32
{
33

34
BitfocusProtocolSettingsWidget::BitfocusProtocolSettingsWidget(QWidget* parent)
×
35
    : ProtocolSettingsWidget(parent)
×
36
{
×
37
  m_deviceNameEdit = new State::AddressFragmentLineEdit{this};
×
38
  m_deviceNameEdit->setText("OSCdevice");
×
39
  checkForChanges(m_deviceNameEdit);
×
40

41
  m_rootLayout = new score::MarginLess<QFormLayout>{this};
×
42
  m_rootLayout->addRow(tr("Name"), m_deviceNameEdit);
×
43
  m_rootLayout->addRow(new QLabel{
×
44
      tr("To add support for Bitfocus Companion modules:\n - Go to Settings > "
×
45
         "Package Manager\n - Install the \"Bitfocus Companion Modules\" package.")});
46
  m_hasInitLabel = true;
×
47
  m_rootLayout->setFieldGrowthPolicy(QFormLayout::ExpandingFieldsGrow);
×
48

49
  m_scroll = new QScrollArea{this};
×
50
  m_scroll->setFrameShape(QFrame::NoFrame);
×
51
  QSizePolicy sz;
×
52
  sz.setHorizontalPolicy(QSizePolicy::Ignored);
×
53
  sz.setVerticalPolicy(QSizePolicy::MinimumExpanding);
×
54
  m_scroll->setSizePolicy(sz);
×
55
  m_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
×
56
  m_scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
×
57
  m_scroll->setWidgetResizable(true);
×
58
  m_rootLayout->addRow(m_scroll);
×
59
}
×
60

61
Device::DeviceSettings BitfocusProtocolSettingsWidget::getSettings() const
×
62
{
63
  Device::DeviceSettings s;
×
64
  s.name = m_deviceNameEdit->text();
×
65
  s.protocol = BitfocusProtocolFactory::static_concreteKey();
×
66

67
  BitfocusSpecificSettings osc = m_settings;
×
68
  for(auto& widg : m_widgets)
×
69
  {
70
    if(widg.second.getValue)
×
71
    {
72
      osc.configuration.emplace_back(widg.first, widg.second.getValue());
×
73
    }
×
74
  }
75
  s.deviceSpecificSettings = QVariant::fromValue(osc);
×
76

77
  return s;
×
78
}
×
79

80
static void makeValidator(QLineEdit* widg, QString rx)
×
81
{
82
  auto val = new QRegularExpressionValidator;
×
83
  if(rx.startsWith("/^"))
×
84
  {
85
    rx.remove(0, 2);
×
86
  }
×
87
  if(rx.endsWith("$/"))
×
88
  {
89
    rx.resize(rx.size() - 2);
×
90
  }
×
91
  val->setRegularExpression(QRegularExpression(rx));
×
92
  val->setParent(widg);
×
93
  widg->setValidator(val);
×
94

95
  QObject::connect(widg, &QLineEdit::textChanged, [widg](const QString& str) {
×
96
    if(!widg->validator())
×
97
      return;
×
98

99
    QString s = str;
×
100
    int i = 0;
×
101
    score::setInputValidity(
×
102
        *widg, widg->validator()->validate(s, i) == QValidator::State::Acceptable
×
103
                   ? score::InputValidity::Valid
104
                   : score::InputValidity::Invalid);
105
  });
×
106
}
×
107

108
void BitfocusProtocolSettingsWidget::updateFields()
×
109
{
110
  if(!m_settings.handler)
×
111
    return;
×
112

113
  auto& m = m_settings.handler->model();
×
114
  for(auto& field : m.config_fields)
×
115
  {
116
    // 1. Add the field label
117
    QLabel* lab{};
×
118
    if(!field.label.isEmpty())
×
119
    {
120
      lab = new QLabel{};
×
121
      lab->setWordWrap(true);
×
122
      lab->setTextFormat(Qt::RichText);
×
123
      lab->setText(QString("<b>%1</b>").arg(field.label));
×
124
      m_subForm->addWidget(lab);
×
125
    }
×
126

127
    // 2. Create the widget proper
128
    if(field.type == "static-text")
×
129
    {
130
      if(auto str = field.value.toString(); !str.isEmpty())
×
131
      {
132
        auto static_text = new QLabel{};
×
133
        static_text->setWordWrap(true);
×
134
        static_text->setTextFormat(Qt::RichText);
×
135
        static_text->setText(str);
×
136

137
        m_subForm->addWidget(static_text);
×
138
        m_widgets[field.id]
×
139
            = widget{.label = lab, .widg = static_text, .getValue = {}, .setValue = {}};
×
140
      }
×
141
    }
×
142
    else if(field.type == "textinput" || field.type == "bonjourdevice")
×
143
    {
144
      auto widg = new QLineEdit;
×
145
      if(!field.regex.isEmpty())
×
146
        makeValidator(widg, field.regex);
×
147
      widg->setText(field.default_value.toString());
×
148
      m_subForm->addWidget(widg);
×
149
      m_widgets[field.id]
×
150
          = {.label = lab, .widg = widg, .getValue = [widg]() -> ossia::value {
×
151
        return widg->text().toStdString();
×
152
      }, .setValue = [widg](ossia::value v) {
×
153
        widg->setText(QString::fromStdString(ossia::convert<std::string>(v)));
×
154
      }};
×
155
    }
×
156
    else if(field.type == "number")
×
157
    {
158
      auto tmin = field.min.typeId();
×
159
      auto tmax = field.max.typeId();
×
160
      if((tmin == QMetaType::LongLong && tmax == QMetaType::LongLong))
×
161
      {
162
        auto widg = new QSpinBox;
×
163
        if(tmin == QMetaType::LongLong && tmax == QMetaType::LongLong)
×
164
          widg->setRange(field.min.toInt(), field.max.toInt());
×
165
        else
166
          widg->setRange(0, 100000);
×
167
        widg->setValue(field.default_value.toInt());
×
168
        m_subForm->addWidget(widg);
×
169
        m_widgets[field.id]
×
170
            = {.label = lab, .widg = widg, .getValue = [widg]() -> ossia::value {
×
171
          return widg->value();
×
172
        }, .setValue = [widg](ossia::value v) {
×
173
          widg->setValue(ossia::convert<float>(v));
×
174
        }};
×
175
      }
×
176
      else if(tmin == QMetaType::Double && tmax == QMetaType::Double)
×
177
      {
178
        auto widg = new QDoubleSpinBox;
×
179
        widg->setRange(field.min.toDouble(), field.max.toDouble());
×
180
        widg->setValue(field.default_value.toDouble());
×
181
        m_subForm->addWidget(widg);
×
182
        m_widgets[field.id]
×
183
            = {.label = lab, .widg = widg, .getValue = [widg]() -> ossia::value {
×
184
          return widg->value();
×
185
        }, .setValue = [widg](ossia::value v) {
×
186
          widg->setValue(ossia::convert<float>(v));
×
187
        }};
×
188
      }
×
189
      else if(!field.regex.isEmpty())
×
190
      {
191
        auto widg = new QLineEdit;
×
192
        makeValidator(widg, field.regex);
×
193
        widg->setText(field.default_value.toString());
×
194
        m_subForm->addWidget(widg);
×
195
        m_widgets[field.id]
×
196
            = {.label = lab, .widg = widg, .getValue = [widg]() -> ossia::value {
×
197
          return widg->text().toInt();
×
198
        }, .setValue = [widg](ossia::value v) {
×
199
          widg->setText(QString::number(ossia::convert<int>(v)));
×
200
        }};
×
201
      }
×
202
      else
203
      {
204
        auto widg = new QSpinBox;
×
205
        widg->setRange(0, 65535);
×
206
        widg->setValue(field.default_value.toInt());
×
207
        m_subForm->addWidget(widg);
×
208
        m_widgets[field.id]
×
209
            = {.label = lab, .widg = widg, .getValue = [widg]() -> ossia::value {
×
210
          return widg->value();
×
211
        }, .setValue = [widg](ossia::value v) {
×
212
          widg->setValue(ossia::convert<float>(v));
×
213
        }};
×
214
      }
215
    }
×
216
    else if(field.type == "checkbox")
×
217
    {
218
      auto widg = new QCheckBox;
×
219
      widg->setChecked(field.default_value.toBool() == true);
×
220
      m_subForm->addWidget(widg);
×
221
      m_widgets[field.id]
×
222
          = {.label = lab, .widg = widg, .getValue = [widg]() -> ossia::value {
×
223
        return widg->isChecked();
×
224
      }, .setValue = [widg](ossia::value v) {
×
225
        widg->setChecked(ossia::convert<bool>(v));
×
226
      }};
×
227
    }
×
228
    else if(field.type == "choices" || field.type == "dropdown")
×
229
    {
230
      auto widg = new QComboBox;
×
231
      int i = 0;
×
232
      int default_i = -1;
×
233
      auto default_v = field.default_value.toString();
×
234
      for(const auto& choice : field.choices)
×
235
      {
236
        widg->addItem(choice.label, choice.id);
×
237
        if(choice.id == default_v)
×
238
          default_i = i;
×
239
        i++;
×
240
      }
241
      if(default_i != -1)
×
242
        widg->setCurrentIndex(default_i);
×
243

244
      m_subForm->addWidget(widg);
×
245
      m_widgets[field.id]
×
246
          = {.label = lab, .widg = widg, .getValue = [widg]() -> ossia::value {
×
247
        return widg->currentData().toString().toStdString();
×
248
      }, .setValue = [widg](ossia::value v) {
×
249
        auto id = QString::fromStdString(ossia::convert<std::string>(v));
×
250
        int idx = -1;
×
251
        for(int i = 0; i < widg->count(); i++)
×
252
        {
253
          if(widg->itemData(i) == id)
×
254
          {
255
            idx = i;
×
256
            break;
×
257
          }
258
        }
×
259
        if(idx != -1)
×
260
          widg->setCurrentIndex(idx);
×
261
      }};
×
262
    }
×
263
  }
264
  m_subForm->addStretch(1);
×
265
}
×
266

267
void BitfocusProtocolSettingsWidget::resizeEvent(QResizeEvent* res)
×
268
{
269
  m_scroll->setMinimumWidth(0.8 * res->size().width());
×
270
  m_scroll->setMinimumHeight(0.8 * res->size().height());
×
271
}
×
272

273
void BitfocusProtocolSettingsWidget::setSettings(const Device::DeviceSettings& settings)
×
274
{
275
  if(m_hasInitLabel)
×
276
  {
277
    if(settings.deviceSpecificSettings.canConvert<BitfocusSpecificSettings>())
×
278
    {
279
      auto stgs = settings.deviceSpecificSettings.value<BitfocusSpecificSettings>();
×
280
      if(!stgs.path.isEmpty())
×
281
      {
282
        m_rootLayout->removeRow(1);
×
283
        m_hasInitLabel = false;
×
284
      }
×
285
    }
×
286
  }
×
287

288
  m_widgets.clear();
×
289
  delete m_subWidget;
×
290
  m_subWidget = new QWidget{this};
×
291
  m_subForm = new score::MarginLess<QVBoxLayout>{m_subWidget};
×
292
  m_scroll->setWidget(m_subWidget);
×
293

294
  m_deviceNameEdit->setText(settings.name);
×
295

296
  bool mustLoad = false;
×
297
  if(settings.deviceSpecificSettings.canConvert<BitfocusSpecificSettings>())
×
298
  {
299
    auto stgs = settings.deviceSpecificSettings.value<BitfocusSpecificSettings>();
×
300
    if(!stgs.path.isEmpty() && QDir{stgs.path}.exists())
×
301
    {
302
      m_deviceNameEdit->setText(stgs.name);
×
303
      if(!stgs.handler)
×
304
      {
305
        // First load
306
        auto conf = bitfocus::module_configuration{};
×
307
        {
308
          if(!stgs.product.isEmpty())
×
309
          {
310
            conf["product"] = stgs.product;
×
311
          }
×
312
        }
313
        stgs.handler = std::make_shared<bitfocus::module_handler>(
×
314
            stgs.path, stgs.entrypoint, stgs.nodeVersion, stgs.apiVersion,
×
315
            std::move(conf));
316
        connect(
×
317
            stgs.handler.get(), &bitfocus::module_handler::configurationParsed, this,
×
318
            [this] { updateFields(); });
×
319
      }
×
320
      else
321
      {
322
        // Device already loaded, we're editing
323
        mustLoad = true;
×
324
      }
325
    }
×
326

327
    m_settings = stgs;
×
328

329
    if(mustLoad)
×
330
    {
331
      // 1. Load the fields
332
      updateFields();
×
333

334
      // 2. Load our saved values
335
      for(auto& [k, v] : stgs.configuration)
×
336
      {
337
        if(auto member = this->m_widgets.find(k); member != m_widgets.end())
×
338
        {
339
          if(member->second.setValue)
×
340
            member->second.setValue(v);
×
341
        }
×
342
      }
343
    }
×
344
  }
×
345
}
×
346
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc