Coverage Report

Created: 2026-07-15 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wt/src/Wt/WAbstractItemModel.C
Line
Count
Source
1
/*
2
 * Copyright (C) 2008 Emweb bv, Herent, Belgium.
3
 *
4
 * See the LICENSE file for terms of use.
5
 */
6
#include "Wt/WAbstractItemModel.h"
7
#include "Wt/WEvent.h"
8
#include "Wt/WException.h"
9
#include "Wt/WItemSelectionModel.h"
10
#include "Wt/WLogger.h"
11
#include "Wt/WModelIndex.h"
12
13
#include "WebUtils.h"
14
15
#ifdef WT_WIN32
16
#define snprintf _snprintf
17
#endif
18
19
namespace {
20
  const char *DRAG_DROP_MIME_TYPE = "application/x-wabstractitemmodelselection";
21
}
22
23
namespace Wt {
24
25
LOGGER("WAbstractItemModel");
26
27
WAbstractItemModel::WAbstractItemModel()
28
0
{ }
29
30
WAbstractItemModel::~WAbstractItemModel()
31
0
{ }
32
33
bool WAbstractItemModel::canFetchMore(WT_MAYBE_UNUSED const WModelIndex& parent) const
34
0
{
35
0
  return false;
36
0
}
37
38
void WAbstractItemModel::fetchMore(WT_MAYBE_UNUSED const WModelIndex& parent)
39
0
{ }
40
41
WFlags<ItemFlag> WAbstractItemModel::flags(WT_MAYBE_UNUSED const WModelIndex& index) const
42
0
{
43
0
  return ItemFlag::Selectable;
44
0
}
45
46
WFlags<HeaderFlag> WAbstractItemModel::headerFlags(WT_MAYBE_UNUSED int section, WT_MAYBE_UNUSED Orientation orientation)
47
  const
48
0
{
49
0
  return None;
50
0
}
51
52
bool WAbstractItemModel::hasChildren(const WModelIndex& index) const
53
0
{
54
0
  return rowCount(index) > 0 && columnCount(index) > 0;
55
0
}
56
57
bool WAbstractItemModel::hasIndex(int row, int column,
58
                                  const WModelIndex& parent) const
59
0
{
60
0
  return (row >= 0
61
0
          && column >= 0
62
0
          && row < rowCount(parent)
63
0
          && column < columnCount(parent));
64
0
}
65
66
WAbstractItemModel::DataMap
67
WAbstractItemModel::itemData(const WModelIndex& index) const
68
0
{
69
0
  DataMap result;
70
71
0
  if (index.isValid()) {
72
0
#ifndef WT_TARGET_JAVA
73
0
    for (int i = 0; i <= ItemDataRole::BarBrushColor; ++i)
74
#else
75
    for (int i = 0; i <= ItemDataRole::BarBrushColor.value(); ++i)
76
#endif
77
0
      result[ItemDataRole(i)] = data(index, ItemDataRole(i));
78
0
    result[ItemDataRole::User] = data(index, ItemDataRole::User);
79
0
  }
80
81
0
  return result;
82
0
}
83
84
cpp17::any WAbstractItemModel::data(int row, int column, ItemDataRole role,
85
                                 const WModelIndex& parent) const
86
0
{
87
0
  return data(index(row, column, parent), role);
88
0
}
89
90
cpp17::any WAbstractItemModel::headerData(WT_MAYBE_UNUSED int section, WT_MAYBE_UNUSED Orientation orientation, ItemDataRole role) const
91
0
{
92
0
  if (role == ItemDataRole::Level)
93
0
    return cpp17::any((int)0);
94
0
  else
95
0
    return cpp17::any();
96
0
}
97
98
void WAbstractItemModel::sort(WT_MAYBE_UNUSED int column, WT_MAYBE_UNUSED SortOrder order)
99
0
{ }
100
101
void WAbstractItemModel::expandColumn(WT_MAYBE_UNUSED int column)
102
0
{ }
103
104
void WAbstractItemModel::collapseColumn(WT_MAYBE_UNUSED int column)
105
0
{ }
106
107
bool WAbstractItemModel::insertColumns(WT_MAYBE_UNUSED int column, WT_MAYBE_UNUSED int count, WT_MAYBE_UNUSED const WModelIndex& parent)
108
0
{
109
0
  return false;
110
0
}
111
112
bool WAbstractItemModel::insertRows(WT_MAYBE_UNUSED int column, WT_MAYBE_UNUSED int count, WT_MAYBE_UNUSED const WModelIndex& parent)
113
0
{
114
0
  return false;
115
0
}
116
117
bool WAbstractItemModel::removeColumns(WT_MAYBE_UNUSED int column, WT_MAYBE_UNUSED int count, WT_MAYBE_UNUSED const WModelIndex& parent)
118
0
{
119
0
  return false;
120
0
}
121
122
bool WAbstractItemModel::removeRows(WT_MAYBE_UNUSED int column, WT_MAYBE_UNUSED int count, WT_MAYBE_UNUSED const WModelIndex& parent)
123
0
{
124
0
  return false;
125
0
}
126
127
bool WAbstractItemModel::setData(WT_MAYBE_UNUSED const WModelIndex& index, WT_MAYBE_UNUSED const cpp17::any& value, WT_MAYBE_UNUSED ItemDataRole role)
128
0
{
129
0
  return false;
130
0
}
131
132
bool WAbstractItemModel::setHeaderData(WT_MAYBE_UNUSED int section, WT_MAYBE_UNUSED Orientation orientation, WT_MAYBE_UNUSED const cpp17::any& value, WT_MAYBE_UNUSED ItemDataRole role)
133
0
{
134
0
  return false;
135
0
}
136
137
bool WAbstractItemModel::setHeaderData(int section, const cpp17::any& value)
138
0
{
139
0
  return setHeaderData(section, Orientation::Horizontal, value);
140
0
}
141
142
bool WAbstractItemModel::setItemData(const WModelIndex& index,
143
                                     const DataMap& values)
144
0
{
145
0
  bool result = true;
146
147
0
  for (DataMap::const_iterator i = values.begin(); i != values.end(); ++i)
148
    // if (i->first != ItemDataRole::Edit)
149
0
      if (!setData(index, i->second, i->first))
150
0
        result = false;
151
152
0
  dataChanged().emit(index, index);
153
154
0
  return result;
155
0
}
156
157
bool WAbstractItemModel::insertColumn(int column, const WModelIndex& parent)
158
0
{
159
0
  return insertColumns(column, 1, parent);
160
0
}
161
162
bool WAbstractItemModel::insertRow(int row, const WModelIndex& parent)
163
0
{
164
0
  return insertRows(row, 1, parent);
165
0
}
166
167
bool WAbstractItemModel::removeColumn(int column, const WModelIndex& parent)
168
0
{
169
0
  return removeColumns(column, 1, parent);
170
0
}
171
172
bool WAbstractItemModel::removeRow(int row, const WModelIndex& parent)
173
0
{
174
0
  return removeRows(row, 1, parent);
175
0
}
176
177
bool WAbstractItemModel::setData(int row, int column, const cpp17::any& value,
178
                                 ItemDataRole role, const WModelIndex& parent)
179
0
{
180
0
  WModelIndex i = index(row, column, parent);
181
182
0
  if (i.isValid())
183
0
    return setData(i, value, role);
184
0
  else
185
0
    return false;
186
0
}
187
188
void WAbstractItemModel::reset()
189
0
{
190
0
  modelReset_.emit();
191
0
}
192
193
WModelIndex WAbstractItemModel::createIndex(int row, int column, void *ptr)
194
  const
195
0
{
196
0
  return WModelIndex(row, column, this, ptr);
197
0
}
198
199
WModelIndex WAbstractItemModel::createIndex(int row, int column, ::uint64_t id)
200
  const
201
0
{
202
0
  return WModelIndex(row, column, this, id);
203
0
}
204
205
void *WAbstractItemModel::toRawIndex(WT_MAYBE_UNUSED const WModelIndex& index) const
206
0
{
207
0
  return nullptr;
208
0
}
209
210
WModelIndex WAbstractItemModel::fromRawIndex(WT_MAYBE_UNUSED void* rawIndex) const
211
0
{
212
0
  return WModelIndex();
213
0
}
214
215
std::string WAbstractItemModel::mimeType() const
216
0
{
217
0
  return DRAG_DROP_MIME_TYPE;
218
0
}
219
220
std::vector<std::string> WAbstractItemModel::acceptDropMimeTypes() const
221
0
{
222
0
  std::vector<std::string> result;
223
224
0
  result.push_back(DRAG_DROP_MIME_TYPE);
225
226
0
  return result;
227
0
}
228
229
void WAbstractItemModel::copyData(const WModelIndex& sIndex,
230
                                  const WModelIndex& dIndex)
231
0
{
232
0
  if (dIndex.model() != this)
233
0
    throw WException("WAbstractItemModel::copyData(): dIndex must be an index of this model");
234
235
0
  DataMap values = itemData(dIndex);
236
0
  for (DataMap::const_iterator i = values.begin(); i != values.end(); ++i)
237
0
    setData(dIndex, cpp17::any(), i->first);
238
239
0
  auto source = sIndex.model();
240
0
  setItemData(dIndex, source->itemData(sIndex));
241
0
}
242
243
void WAbstractItemModel::dropEvent(const WDropEvent& e, DropAction action,
244
                                   int row, WT_MAYBE_UNUSED int column,
245
                                   const WModelIndex& parent)
246
0
{
247
  // TODO: For now, we assumes selectionBehavior() == RowSelection !
248
249
0
  WItemSelectionModel *selectionModel
250
0
    = dynamic_cast<WItemSelectionModel *>(e.source());
251
0
  if (selectionModel) {
252
0
    auto sourceModel = selectionModel->model();
253
254
    /*
255
     * (1) Insert new rows (or later: cells ?)
256
     */
257
0
    if (action == DropAction::Move || row == -1) {
258
0
      if (row == -1)
259
0
        row = rowCount(parent);
260
261
0
      if (!insertRows(row, selectionModel->selectedIndexes().size(), parent)) {
262
0
        LOG_ERROR("dropEvent(): could not insertRows()");
263
0
        return;
264
0
      }
265
0
    }
266
267
    /*
268
     * (2) Copy data
269
     */
270
0
    WModelIndexSet selection = selectionModel->selectedIndexes();
271
272
0
    int r = row;
273
0
    for (WModelIndexSet::const_iterator i = selection.begin();
274
0
         i != selection.end(); ++i) {
275
0
      WModelIndex sourceIndex = *i;
276
0
      if (selectionModel->selectionBehavior() ==
277
0
          SelectionBehavior::Rows) {
278
0
        WModelIndex sourceParent = sourceIndex.parent();
279
280
0
        for (int col = 0; col < sourceModel->columnCount(sourceParent); ++col) {
281
0
          WModelIndex s = sourceModel->index(sourceIndex.row(), col,
282
0
                                             sourceParent);
283
0
          WModelIndex d = index(r, col, parent);
284
0
          copyData(s, d);
285
0
        }
286
287
0
        ++r;
288
0
      }
289
0
    }
290
291
    /*
292
     * (3) Remove original data
293
     */
294
0
    if (action == DropAction::Move) {
295
0
      while (!selectionModel->selectedIndexes().empty()) {
296
0
        WModelIndex i = Utils::last(selectionModel->selectedIndexes());
297
298
0
        if (!sourceModel->removeRow(i.row(), i.parent())) {
299
0
          LOG_ERROR("dropEvent(): could not removeRows()");
300
0
          return;
301
0
        }
302
0
      }
303
0
    }
304
0
  }
305
0
}
306
307
void WAbstractItemModel::dropEvent(const WDropEvent& e, DropAction action,
308
                                   const WModelIndex& pindex, Wt::Side side)
309
0
{
310
0
  WItemSelectionModel *selectionModel
311
0
    = dynamic_cast<WItemSelectionModel *>(e.source());
312
0
  if (selectionModel) {
313
0
    auto sourceModel = selectionModel->model();
314
315
0
    const WModelIndex& parent = pindex.parent();
316
0
    int row = !pindex.isValid() ? rowCount() :
317
0
      side == Side::Bottom ? pindex.row()+1 : pindex.row();
318
319
    /*
320
     * (1) Insert new rows (or later: cells ?)
321
     */
322
0
    if (!insertRows(row, selectionModel->selectedIndexes().size(), parent)) {
323
0
      LOG_ERROR("dropEvent(): could not insertRows()");
324
0
      return;
325
0
    }
326
327
    /*
328
     * (2) Copy data
329
     */
330
0
    WModelIndexSet selection = selectionModel->selectedIndexes();
331
332
0
    int r = row;
333
0
    for (WModelIndexSet::const_iterator i = selection.begin();
334
0
         i != selection.end(); ++i) {
335
0
      WModelIndex sourceIndex = *i;
336
0
      if (selectionModel->selectionBehavior() ==
337
0
          SelectionBehavior::Rows) {
338
0
        WModelIndex sourceParent = sourceIndex.parent();
339
340
0
        for (int col = 0; col < sourceModel->columnCount(sourceParent); ++col) {
341
0
          WModelIndex s = sourceModel->index(sourceIndex.row(), col,
342
0
                                             sourceParent);
343
0
          WModelIndex d = index(r, col, parent);
344
0
          copyData(s, d);
345
0
        }
346
347
0
        ++r;
348
0
      }
349
0
    }
350
351
    /*
352
     * (3) Remove original data
353
     */
354
0
    if (action == DropAction::Move) {
355
0
      while (!selectionModel->selectedIndexes().empty()) {
356
0
        WModelIndex i = Utils::last(selectionModel->selectedIndexes());
357
358
0
        if (!sourceModel->removeRow(i.row(), i.parent())) {
359
0
          LOG_ERROR("dropEvent(): could not removeRows()");
360
0
          return;
361
0
        }
362
0
      }
363
0
    }
364
0
  }
365
0
}
366
367
void WAbstractItemModel::beginInsertColumns(const WModelIndex& parent,
368
                                            int first, int last)
369
0
{
370
0
  first_ = first;
371
0
  last_ = last;
372
0
  parent_ = parent;
373
374
0
  columnsAboutToBeInserted().emit(parent_, first, last);
375
0
}
376
377
void WAbstractItemModel::endInsertColumns()
378
0
{
379
0
  columnsInserted().emit(parent_, first_, last_);
380
0
}
381
382
void WAbstractItemModel::beginInsertRows(const WModelIndex& parent,
383
                                         int first, int last)
384
0
{
385
0
  first_ = first;
386
0
  last_ = last;
387
0
  parent_ = parent;
388
389
0
  rowsAboutToBeInserted().emit(parent, first, last);
390
0
}
391
392
void WAbstractItemModel::endInsertRows()
393
0
{
394
0
  rowsInserted().emit(parent_, first_, last_);
395
0
}
396
397
void WAbstractItemModel::beginRemoveColumns(const WModelIndex& parent,
398
                                            int first, int last)
399
0
{
400
0
  first_ = first;
401
0
  last_ = last;
402
0
  parent_ = parent;
403
404
0
  columnsAboutToBeRemoved().emit(parent, first, last);
405
0
}
406
407
void WAbstractItemModel::endRemoveColumns()
408
0
{
409
0
  columnsRemoved().emit(parent_, first_, last_);
410
0
}
411
412
void WAbstractItemModel::beginRemoveRows(const WModelIndex& parent,
413
                                         int first, int last)
414
0
{
415
0
  first_ = first;
416
0
  last_ = last;
417
0
  parent_ = parent;
418
419
0
  rowsAboutToBeRemoved().emit(parent, first, last);
420
0
}
421
422
void WAbstractItemModel::endRemoveRows()
423
0
{
424
0
  rowsRemoved().emit(parent_, first_, last_);
425
0
}
426
427
WModelIndexList WAbstractItemModel::match(const WModelIndex& start,
428
                                          ItemDataRole role,
429
                                          const cpp17::any& value,
430
                                          int hits,
431
                                          WFlags<MatchFlag> flags)
432
  const
433
0
{
434
0
  WModelIndexList result;
435
436
0
  const int rc = rowCount(start.parent());
437
438
0
  for (int i = 0; i < rc; ++i) {
439
0
    int row = start.row() + i;
440
441
0
    if (row >= rc) {
442
0
      if (!(flags & MatchFlag::Wrap))
443
0
        break;
444
0
      else
445
0
        row -= rc;
446
0
    }
447
448
0
    WModelIndex idx = index(row, start.column(), start.parent());
449
0
    cpp17::any v = data(idx, role);
450
451
0
    if (Impl::matchValue(v, value, flags)) {
452
0
      result.push_back(idx);
453
0
      if (hits != -1 && (int)result.size() == hits)
454
0
        break;
455
0
    }
456
0
  }
457
458
0
  return result;
459
0
}
460
461
}