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

ossia / score / 30213925900

26 Jul 2026 06:03PM UTC coverage: 15.298% (-0.01%) from 15.311%
30213925900

push

github

jcelerier
3rdparty: bump libossia for the network-context poll fix

Brings in ossia/libossia#913: SDL joystick init no longer requires haptic
support (which the Emscripten SDL2 port does not have), and
network_context::poll() restarts the io_context, without which the
WebAssembly polling path stops after its first tick.

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

30400 of 198713 relevant lines covered (15.3%)

997.67 hits per line

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

38.59
/src/plugins/score-plugin-deviceexplorer/Explorer/Explorer/DeviceExplorerView.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 "DeviceExplorerView.hpp"
4

5
#include "DeviceExplorerFilterProxyModel.hpp"
6
#include "DeviceExplorerModel.hpp"
7

8
#include <score/model/Skin.hpp>
9
#include <score/widgets/Pixmap.hpp>
10
#if defined(__EMSCRIPTEN__)
11
#include <score/widgets/ItemViewDrag.hpp>
12

13
#include <QStyleOptionViewItem>
14
#endif
15

16
#include <ossia/detail/ssize.hpp>
17

18
#include <QAbstractItemView>
19
#include <QAbstractProxyModel>
20
#include <QAction>
21
#include <QDebug>
22
#include <QDrag>
23
#include <QFile>
24
#include <QHeaderView>
25
#include <QKeyEvent>
26
#include <QMenu>
27
#include <QPainter>
28
#include <QSettings>
29
#include <QString>
30

31
W_REGISTER_ARGTYPE(QItemSelection)
32

33
#include <wobjectimpl.h>
34
W_OBJECT_IMPL(Explorer::DeviceExplorerView)
674✔
35
namespace
36
{
37
const QString GeometrySetting("DeviceExplorerView/Geometry");
37✔
38
const QString HeaderViewSetting("DeviceExplorerView/HeaderView");
37✔
39
}
40

41
namespace Explorer
42
{
43
DeviceExplorerView::DeviceExplorerView(QWidget* parent)
16✔
44
    : QTreeView(parent)
8✔
45
    , m_hasProxy(false)
8✔
46
{
8✔
47
  setAllColumnsShowFocus(true);
8✔
48

49
  setSelectionBehavior(QAbstractItemView::SelectRows);
8✔
50
  setSelectionMode(QAbstractItemView::ExtendedSelection);
8✔
51
  setAlternatingRowColors(true);
8✔
52

53
  header()->setContextMenuPolicy(Qt::CustomContextMenu);
8✔
54
  connect(
8✔
55
      header(), &QWidget::customContextMenuRequested, this,
8✔
56
      &DeviceExplorerView::headerMenuRequested);
57

58
  header()->setMaximumHeight(20);
8✔
59
  //- Drag'n Drop.
60

61
  setDragEnabled(true);
8✔
62
  setAcceptDrops(true);
8✔
63
  setUniformRowHeights(true);
8✔
64

65
  setDragDropMode(QAbstractItemView::DragDrop);
8✔
66
  setDropIndicatorShown(true);
8✔
67

68
  setDefaultDropAction(Qt::MoveAction);
8✔
69

70
  setDragDropOverwriteMode(false);
8✔
71
}
8✔
72

73
DeviceExplorerView::~DeviceExplorerView()
×
74
{
×
75
  saveSettings();
×
76
}
×
77
QModelIndexList DeviceExplorerView::selectedDraggableIndexes() const
×
78
{
79
  QModelIndexList indexes = selectedIndexes();
×
80
  auto m = QTreeView::model();
×
81
  auto isNotDragEnabled = [m](const QModelIndex& index) {
×
82
    return !(m->flags(index) & Qt::ItemIsDragEnabled);
×
83
  };
84
  indexes.erase(
×
85
      std::remove_if(indexes.begin(), indexes.end(), isNotDragEnabled), indexes.end());
×
86
  return indexes;
×
87
}
×
88

89
void DeviceExplorerView::startDrag(Qt::DropActions)
×
90
{
91
  QModelIndexList indexes = selectedDraggableIndexes();
×
92
  if(indexes.count() > 0)
×
93
  {
94
    QMimeData* data = QTreeView::model()->mimeData(indexes);
×
95
    if(!data)
×
96
      return;
×
97

98
    QDrag* drag = new QDrag(this);
×
99
    drag->setMimeData(data);
×
100
    /*
101
        auto p =
102
       score::get_pixmap(QStringLiteral(":/icons/cursor_drag_device.png"));
103
        drag->setPixmap(QPixmap());
104
        drag->setDragCursor(p, Qt::CopyAction);
105
        drag->setDragCursor(p, Qt::MoveAction);
106
        drag->setDragCursor(p, Qt::LinkAction);
107
        drag->setDragCursor(p, Qt::ActionMask);
108
        drag->setDragCursor(p, Qt::IgnoreAction);
109
        drag->setDragCursor(p, Qt::TargetMoveAction);
110
        */
111
#if defined(__EMSCRIPTEN__)
112
    QStyleOptionViewItem opt;
113
    initViewItemOption(&opt);
114
    score::setItemViewDragPixmap(*drag, *this, opt, indexes);
115
#endif
116
    drag->exec();
×
117
  }
×
118
}
×
119

120
void DeviceExplorerView::setInitialColumnsSizes()
6✔
121
{
122
  SCORE_ASSERT(model());
6✔
123

124
  header()->resizeSection((int)Explorer::Column::Name, 180);
6✔
125
  header()->resizeSection((int)Explorer::Column::Value, 50);
6✔
126
}
6✔
127

128
void DeviceExplorerView::saveSettings()
×
129
{
130
  QSettings settings;
×
131
  settings.setValue(GeometrySetting, saveGeometry());
×
132
  settings.setValue(HeaderViewSetting, header()->saveState());
×
133
}
×
134

135
void DeviceExplorerView::restoreSettings()
×
136
{
137
  QSettings settings;
×
138
  if(settings.contains(GeometrySetting))
×
139
  {
140
    restoreGeometry(settings.value(GeometrySetting).toByteArray());
×
141
    header()->restoreState(settings.value(HeaderViewSetting).toByteArray());
×
142
    this->updateGeometry();
×
143
  }
×
144
}
×
145

146
void DeviceExplorerView::setModel(QAbstractItemModel* model)
13✔
147
{
148
  m_hasProxy = false;
13✔
149
  QTreeView::setModel(model);
13✔
150
  // TODO review the save/restore system
151
  if(model)
13✔
152
  {
153
    setInitialColumnsSizes();
×
154
    //        restoreSettings();
155
    initActions(); // after restoreSettings() to have actions correctly
×
156
    // initialized
157
  }
×
158
}
13✔
159

160
void DeviceExplorerView::setModel(DeviceExplorerFilterProxyModel* model)
6✔
161
{
162
  QTreeView::setModel(static_cast<QAbstractItemModel*>(model));
6✔
163
  m_hasProxy = true;
6✔
164

165
  if(model)
6✔
166
  {
167
    setInitialColumnsSizes();
6✔
168
    //        restoreSettings();
169
    initActions(); // after restoreSettings() to have actions correctly
6✔
170
    // initialized
171
  }
6✔
172
}
6✔
173

174
DeviceExplorerModel* DeviceExplorerView::model()
67✔
175
{
176
  if(!m_hasProxy)
67✔
177
  {
178
    return static_cast<DeviceExplorerModel*>(QTreeView::model());
13✔
179
  }
180
  else
181
  {
182
    return static_cast<DeviceExplorerModel*>(
54✔
183
        static_cast<QAbstractProxyModel*>(QTreeView::model())->sourceModel());
54✔
184
  }
185
}
67✔
186

187
const DeviceExplorerModel* DeviceExplorerView::model() const
×
188
{
189
  auto m = QTreeView::model();
×
190
  if(!m)
×
191
    return nullptr;
×
192

193
  if(!m_hasProxy)
×
194
  {
195
    return static_cast<const DeviceExplorerModel*>(m);
×
196
  }
197
  else
198
  {
199
    auto model = static_cast<const QAbstractProxyModel*>(m);
×
200
    return qobject_cast<const DeviceExplorerModel*>(model->sourceModel());
×
201
  }
202
}
×
203

204
void DeviceExplorerView::initActions()
6✔
205
{
206
  SCORE_ASSERT(model());
6✔
207
  const int n = model()->columnCount();
6✔
208
  m_actions.reserve(n);
6✔
209

210
  for(int i = 0; i < n; ++i)
18✔
211
  {
212
    QAction* a = new QAction(
24✔
213
        model()->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), this);
12✔
214
    a->setCheckable(true);
12✔
215

216
    a->setChecked(!isColumnHidden(i));
12✔
217

218
    connect(a, &QAction::toggled, this, &DeviceExplorerView::columnVisibilityChanged);
12✔
219
    m_actions.append(a);
12✔
220
  }
12✔
221
}
6✔
222
bool DeviceExplorerView::hasProxy() const
×
223
{
224
  return m_hasProxy;
×
225
}
226

227
void DeviceExplorerView::columnVisibilityChanged(bool shown)
×
228
{
229
  QAction* a = qobject_cast<QAction*>(sender());
×
230
  SCORE_ASSERT(a);
×
231
  const int ind = m_actions.indexOf(a);
×
232
  SCORE_ASSERT(ind != -1);
×
233
  setColumnHidden(ind, !shown);
×
234
}
×
235

236
void DeviceExplorerView::keyPressEvent(QKeyEvent* k)
×
237
{
238
  if(k->key() == Qt::Key_Escape)
×
239
  {
240
    clearSelection();
×
241
    k->accept();
×
242
  }
×
243
  else
244
  {
245
    QTreeView::keyPressEvent(k);
×
246
  }
247
}
×
248

249
void DeviceExplorerView::rowsInserted(const QModelIndex& parent, int start, int end)
×
250
{
251
  QTreeView::rowsInserted(parent, start, end);
×
252
  created(parent, start, end);
×
253
}
×
254

255
void DeviceExplorerView::headerMenuRequested(const QPoint& pos)
×
256
{
257
  QMenu contextMenu(this);
×
258
  const int n = std::ssize(m_actions);
×
259

260
  for(int i = 0; i < n; ++i)
×
261
  {
262
    contextMenu.addAction(m_actions.at(i));
×
263
  }
×
264

265
  contextMenu.exec(mapToGlobal(pos));
×
266
}
×
267

268
void DeviceExplorerView::selectionChanged(
19✔
269
    const QItemSelection& selected, const QItemSelection& deselected)
270
{
271
  QTreeView::selectionChanged(selected, deselected);
19✔
272
  selectionChanged();
19✔
273
}
19✔
274

275
QModelIndexList DeviceExplorerView::selectedIndexes() const
19✔
276
{
277
  // As we have done setSelectionBehavior(QAbstractItemView::SelectRows)
278
  // we get the indexes for each column corresponding to a selected row.
279
  // We want only the indexes of the NAME column.
280

281
  const int col = (int)Explorer::Column::Name;
19✔
282
  QModelIndexList l = QTreeView::selectedIndexes();
19✔
283

284
  QModelIndexList l0;
19✔
285
  Q_FOREACH(const QModelIndex& index, l)
19✔
286
  {
287
    if(index.column() == col)
×
288
    {
289

290
      if(!index.isValid())
×
291
      {
292
        qDebug() << " !!! invalid index in selection !!!";
×
293
      }
×
294

295
      l0.append(index);
×
296
      // REM: we append index in ProxyModel if m_hasProxy
297
      // it is necessary because drag'n drop will automatically call
298
      // selectedIndexes() & mapToSource().
299
    }
×
300
  }
301

302
  return l0;
19✔
303
}
19✔
304

305
// REM: use selectedIndex() & setSelectedIndex()
306
// instead of currentIndex() & setCurrentIndex()
307
// to take into account proxy presence.
308

309
QModelIndex DeviceExplorerView::selectedIndex() const
×
310
{
311
  if(!m_hasProxy)
×
312
  {
313
    return currentIndex();
×
314
  }
315
  else
316
  {
317
    return static_cast<const QAbstractProxyModel*>(QTreeView::model())
×
318
        ->mapToSource(currentIndex());
×
319
  }
320
}
×
321

322
void DeviceExplorerView::setSelectedIndex(const QModelIndex& index)
×
323
{
324
  if(!m_hasProxy)
×
325
  {
326
    return setCurrentIndex(index);
×
327
  }
328
  else
329
  {
330
    return setCurrentIndex(static_cast<const QAbstractProxyModel*>(QTreeView::model())
×
331
                               ->mapFromSource(index));
×
332
  }
333
}
×
334
void DeviceExplorerView::paintEvent(QPaintEvent* event)
×
335
{
336
  QTreeView::paintEvent(event);
×
337
  if(!this->isEnabled())
×
338
    return;
×
339
  if(model() && model()->rowCount(rootIndex()) > 0)
×
340
    return;
×
341

342
  QPainter p{this->viewport()};
×
343
  const auto& skin = score::Skin::instance();
×
344
  auto font = skin.Bold12Pt;
×
345
  font.setPointSize(24);
×
346
  p.setFont(font);
×
347
  auto pen = p.pen();
×
348
  auto col = pen.color();
×
349
  col.setAlphaF(0.5);
×
350
  pen.setColor(col);
×
351
  p.setPen(pen);
×
352
  p.drawText(rect(), Qt::AlignCenter, "Right-click\nto add a device");
×
353
}
×
354
}
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