1 /*
2 * Copyright (C) 2010 Emweb bv, Herent, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6 #include <boost/test/unit_test.hpp>
7 #include <iostream>
8
9 #include <Wt/WStandardItemModel.h>
10 #include <Wt/WStandardItem.h>
11 #include <Wt/WBatchEditProxyModel.h>
12
13 using namespace Wt;
14
15 enum ModelType { SourceModel, ProxyModel };
16
17 namespace {
18 std::string d(WAbstractItemModel *model, int row, int column,
19 const WModelIndex& parent = WModelIndex())
20 {
21 return cpp17::any_cast<std::string>
22 (model->data(row, column, ItemDataRole::Display, parent));
23 }
24 }
25
26 struct BatchEditFixture {
BatchEditFixtureBatchEditFixture27 BatchEditFixture() {
28 sourceModel_ = std::make_shared<WStandardItemModel>(0, 4);
29 proxyModel_ = std::make_shared<WBatchEditProxyModel>();
30 proxyModel_->setSourceModel(sourceModel_);
31
32 proxyModel_->setNewRowData(0, std::string("New column 0"));
33 proxyModel_->setNewRowData(1, std::string("New column 1"));
34 proxyModel_->setNewRowData(2, std::string("New column 2"));
35 proxyModel_->setNewRowData(3, std::string("New column 3"));
36
37 connectEvents(sourceModel_, SourceModel);
38 connectEvents(proxyModel_, ProxyModel);
39 }
40
41 std::shared_ptr<Wt::WStandardItemModel> sourceModel_;
42 std::shared_ptr<Wt::WBatchEditProxyModel> proxyModel_;
43
44 enum EventType {
45 RowsInserted,
46 ColumnsInserted,
47 RowsRemoved,
48 ColumnsRemoved,
49 DataChanged
50 };
51
52 struct ModelEvent {
53 Wt::WModelIndex index;
54 int start, end;
55 bool ended;
56 EventType type;
57 };
58
59 std::vector<ModelEvent> modelEvents_[2];
60
connectEventsBatchEditFixture61 void connectEvents(std::shared_ptr<WAbstractItemModel> model,
62 ModelType modelType)
63 {
64 typedef BatchEditFixture This;
65 using namespace std::placeholders;
66
67 model->rowsAboutToBeInserted().connect
68 (std::bind(&This::geometryChanged, this, _1, _2, _3,
69 modelType, RowsInserted, false));
70 model->rowsInserted().connect
71 (std::bind(&This::geometryChanged, this, _1, _2, _3,
72 modelType, RowsInserted, true));
73
74 model->rowsAboutToBeRemoved().connect
75 (std::bind(&This::geometryChanged, this, _1, _2, _3,
76 modelType, RowsRemoved, false));
77 model->rowsRemoved().connect
78 (std::bind(&This::geometryChanged, this, _1, _2, _3,
79 modelType, RowsRemoved, true));
80
81 model->columnsAboutToBeInserted().connect
82 (std::bind(&This::geometryChanged, this, _1, _2, _3,
83 modelType, ColumnsInserted, false));
84 model->columnsInserted().connect
85 (std::bind(&This::geometryChanged, this, _1, _2, _3,
86 modelType, ColumnsInserted, true));
87
88 model->columnsAboutToBeRemoved().connect
89 (std::bind(&This::geometryChanged, this, _1, _2, _3,
90 modelType, ColumnsRemoved, false));
91 model->columnsRemoved().connect
92 (std::bind(&This::geometryChanged, this, _1, _2, _3,
93 modelType, ColumnsRemoved, true));
94 }
95
96
clearEventsBatchEditFixture97 void clearEvents()
98 {
99 modelEvents_[0].clear();
100 modelEvents_[1].clear();
101 }
102
geometryChangedBatchEditFixture103 void geometryChanged(const WModelIndex& parent,
104 int start, int end,
105 ModelType model, EventType type,
106 bool ended)
107 {
108 if (!ended) {
109 ModelEvent event;
110 event.index = parent;
111 event.start = start;
112 event.end = end;
113 event.type = type;
114 event.ended = false;
115
116 modelEvents_[model].push_back(event);
117 } else {
118 for (int i = (int)(modelEvents_[model].size()) - 1; i >= 0; --i) {
119 ModelEvent& e = modelEvents_[model][i];
120 if (e.type == type
121 && e.index == parent
122 && e.start == start
123 && e.end == end
124 && !e.ended) {
125 e.ended = true;
126 return;
127 }
128 }
129
130 BOOST_FAIL("Non-matched geometry ending event");
131 }
132 }
133
134 };
135
BOOST_AUTO_TEST_CASE(batchedit_test1)136 BOOST_AUTO_TEST_CASE( batchedit_test1 )
137 {
138 BatchEditFixture f;
139
140 auto sm = f.sourceModel_.get();
141 auto pm = f.proxyModel_.get();
142
143 BOOST_REQUIRE(sm->columnCount() == 4);
144 BOOST_REQUIRE(pm->columnCount() == 4);
145
146 BOOST_REQUIRE(sm->rowCount() == 0);
147 BOOST_REQUIRE(pm->rowCount() == 0);
148
149 pm->insertRows(0, 2);
150
151 BOOST_REQUIRE(f.modelEvents_[SourceModel].size() == 0);
152 BOOST_REQUIRE(f.modelEvents_[ProxyModel].size() == 1);
153
154 BOOST_REQUIRE(sm->rowCount() == 0);
155 BOOST_REQUIRE(pm->rowCount() == 2);
156
157 f.clearEvents();
158
159 BOOST_REQUIRE(d(pm, 0, 0) == "New column 0");
160 BOOST_REQUIRE(d(pm, 0, 1) == "New column 1");
161 BOOST_REQUIRE(d(pm, 0, 2) == "New column 2");
162 BOOST_REQUIRE(d(pm, 0, 3) == "New column 3");
163
164 pm->setData(1, 0, std::string("Column 0"), ItemDataRole::Display);
165 pm->setData(1, 1, std::string("Column 1"), ItemDataRole::Display);
166 pm->setData(1, 2, std::string("Column 2"), ItemDataRole::Display);
167 pm->setData(1, 3, std::string("Column 3"), ItemDataRole::Display);
168
169 BOOST_REQUIRE(d(pm, 1, 0) == "Column 0");
170 BOOST_REQUIRE(d(pm, 1, 1) == "Column 1");
171 BOOST_REQUIRE(d(pm, 1, 2) == "Column 2");
172 BOOST_REQUIRE(d(pm, 1, 3) == "Column 3");
173
174 pm->removeRow(1);
175
176 BOOST_REQUIRE(f.modelEvents_[SourceModel].size() == 0);
177 BOOST_REQUIRE(f.modelEvents_[ProxyModel].size() == 1);
178
179 BOOST_REQUIRE(sm->rowCount() == 0);
180 BOOST_REQUIRE(pm->rowCount() == 1);
181
182 f.clearEvents();
183
184 f.proxyModel_->commitAll();
185
186 BOOST_REQUIRE(f.modelEvents_[SourceModel].size() == 1);
187 BOOST_REQUIRE(f.modelEvents_[ProxyModel].size() == 0);
188
189 BOOST_REQUIRE(sm->rowCount() == 1);
190 BOOST_REQUIRE(pm->rowCount() == 1);
191
192 f.clearEvents();
193
194 sm->insertRows(1, 3);
195
196 BOOST_REQUIRE(f.modelEvents_[SourceModel].size() == 1);
197 BOOST_REQUIRE(f.modelEvents_[ProxyModel].size() == 3);
198
199 BOOST_REQUIRE(sm->rowCount() == 4);
200 BOOST_REQUIRE(pm->rowCount() == 4);
201
202 f.clearEvents();
203
204 pm->removeRow(3);
205 pm->removeRow(1);
206
207 BOOST_REQUIRE(pm->rowCount() == 2);
208
209 BOOST_REQUIRE(f.modelEvents_[SourceModel].size() == 0);
210 BOOST_REQUIRE(f.modelEvents_[ProxyModel].size() == 2);
211
212 f.clearEvents();
213
214 pm->setData(1, 0, std::string("sm(1, 0)"), ItemDataRole::Edit);
215 BOOST_REQUIRE(d(pm, 1, 0) == "sm(1, 0)");
216
217 f.proxyModel_->commitAll();
218
219 BOOST_REQUIRE(sm->rowCount() == 2);
220
221 BOOST_REQUIRE(d(pm, 1, 0) == "sm(1, 0)");
222 BOOST_REQUIRE(d(sm, 1, 0) == "sm(1, 0)");
223
224 WModelIndex p = pm->index(1, 0);
225 pm->insertColumns(0, 4, p);
226 BOOST_REQUIRE(pm->columnCount(p) == 4);
227 pm->insertRow(0, p);
228 BOOST_REQUIRE(pm->rowCount(p) == 1);
229
230 f.proxyModel_->commitAll();
231
232 BOOST_REQUIRE(sm->rowCount(sm->index(1, 0)) == 1);
233 BOOST_REQUIRE(sm->columnCount(sm->index(1, 0)) == 4);
234 }
235
236
BOOST_AUTO_TEST_CASE(batchedit_test2)237 BOOST_AUTO_TEST_CASE( batchedit_test2 )
238 {
239 BatchEditFixture f;
240
241 auto sm = f.sourceModel_.get();
242 auto pm = f.proxyModel_.get();
243
244 BOOST_REQUIRE(sm->columnCount() == 4);
245 BOOST_REQUIRE(pm->columnCount() == 4);
246
247 BOOST_REQUIRE(sm->rowCount() == 0);
248 BOOST_REQUIRE(pm->rowCount() == 0);
249
250 sm->insertRows(0, 2);
251
252 BOOST_REQUIRE(sm->rowCount() == 2);
253 BOOST_REQUIRE(pm->rowCount() == 2);
254
255 f.clearEvents();
256
257 pm->setData(0, 0, std::string("sm(1, 0)"), ItemDataRole::Edit);
258 pm->setData(1, 0, std::string("sm(2, 0)"), ItemDataRole::Edit);
259 BOOST_REQUIRE(d(pm, 0, 0) == "sm(1, 0)");
260 BOOST_REQUIRE(d(pm, 1, 0) == "sm(2, 0)");
261
262 pm->insertRows(2, 1);
263 pm->setData(2, 0, std::string("sm(3, 0)"), ItemDataRole::Edit);
264
265 BOOST_REQUIRE(sm->rowCount() == 2);
266 BOOST_REQUIRE(pm->rowCount() == 3);
267
268 pm->removeRow(0);
269
270 BOOST_REQUIRE(sm->rowCount() == 2);
271 BOOST_REQUIRE(pm->rowCount() == 2);
272
273 BOOST_REQUIRE(d(pm, 0, 0) == "sm(2, 0)");
274 BOOST_REQUIRE(d(pm, 1, 0) == "sm(3, 0)");
275
276 f.proxyModel_->commitAll();
277
278 BOOST_REQUIRE(sm->rowCount() == 2);
279
280 BOOST_REQUIRE(d(sm, 0, 0) == "sm(2, 0)");
281 BOOST_REQUIRE(d(sm, 1, 0) == "sm(3, 0)");
282
283 BOOST_REQUIRE(d(pm, 0, 0) == "sm(2, 0)");
284 BOOST_REQUIRE(d(pm, 1, 0) == "sm(3, 0)");
285 }
286
BOOST_AUTO_TEST_CASE(batchedit_test3)287 BOOST_AUTO_TEST_CASE( batchedit_test3 )
288 {
289 // Test flags
290 BatchEditFixture f;
291
292 WStandardItemModel *sm = f.sourceModel_.get();
293 WBatchEditProxyModel *pm = f.proxyModel_.get();
294
295 pm->insertRows(0, 3);
296 pm->commitAll();
297
298 sm->item(0)->setFlags(ItemFlag::Selectable);
299 sm->item(1)->setFlags(ItemFlag::Editable);
300 sm->item(2)->setFlags(ItemFlag::UserCheckable);
301
302 pm->setNewRowFlags(0, ItemFlag::DragEnabled);
303 pm->insertRows(1, 1);
304
305 BOOST_REQUIRE(pm->flags(pm->index(0, 0)) == ItemFlag::Selectable);
306 BOOST_REQUIRE(pm->flags(pm->index(1, 0)) == ItemFlag::DragEnabled);
307 BOOST_REQUIRE(pm->flags(pm->index(2, 0)) == ItemFlag::Editable);
308 BOOST_REQUIRE(pm->flags(pm->index(3, 0)) == ItemFlag::UserCheckable);
309 }
310