Chris@413
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@413
|
2
|
Chris@413
|
3 /*
|
Chris@413
|
4 Sonic Visualiser
|
Chris@413
|
5 An audio file viewer and annotation editor.
|
Chris@413
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@413
|
7 This file copyright 2008 QMUL.
|
Chris@413
|
8
|
Chris@413
|
9 This program is free software; you can redistribute it and/or
|
Chris@413
|
10 modify it under the terms of the GNU General Public License as
|
Chris@413
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@413
|
12 License, or (at your option) any later version. See the file
|
Chris@413
|
13 COPYING included with this distribution for more information.
|
Chris@413
|
14 */
|
Chris@413
|
15
|
Chris@413
|
16 #include "ModelDataTableModel.h"
|
Chris@413
|
17
|
Chris@420
|
18 #include "TabularModel.h"
|
Chris@420
|
19 #include "Model.h"
|
Chris@413
|
20
|
Chris@422
|
21 #include <map>
|
Chris@417
|
22 #include <algorithm>
|
Chris@420
|
23 #include <iostream>
|
Chris@417
|
24
|
Chris@1748
|
25 ModelDataTableModel::ModelDataTableModel(ModelId m) :
|
Chris@421
|
26 m_model(m),
|
Chris@421
|
27 m_sortColumn(0),
|
Chris@428
|
28 m_sortOrdering(Qt::AscendingOrder),
|
Chris@428
|
29 m_currentRow(0)
|
Chris@413
|
30 {
|
Chris@1748
|
31 auto model = ModelById::get(m);
|
Chris@1748
|
32 if (model) {
|
Chris@1770
|
33 connect(model.get(), SIGNAL(modelChanged(ModelId)),
|
Chris@1770
|
34 this, SLOT(modelChanged(ModelId)));
|
Chris@1770
|
35 connect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)),
|
Chris@1770
|
36 this, SLOT(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)));
|
Chris@1748
|
37 }
|
Chris@413
|
38 }
|
Chris@413
|
39
|
Chris@413
|
40 ModelDataTableModel::~ModelDataTableModel()
|
Chris@413
|
41 {
|
Chris@413
|
42 }
|
Chris@413
|
43
|
Chris@413
|
44 QVariant
|
Chris@413
|
45 ModelDataTableModel::data(const QModelIndex &index, int role) const
|
Chris@413
|
46 {
|
Chris@1748
|
47 auto model = getTabularModel();
|
Chris@1748
|
48 if (!model) return QVariant();
|
Chris@424
|
49 if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();
|
Chris@413
|
50 if (!index.isValid()) return QVariant();
|
Chris@1748
|
51 QVariant d = model->getData(getUnsorted(index.row()), index.column(), role);
|
Chris@1254
|
52 return d;
|
Chris@413
|
53 }
|
Chris@413
|
54
|
Chris@413
|
55 bool
|
Chris@413
|
56 ModelDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
Chris@413
|
57 {
|
Chris@1748
|
58 auto model = getTabularModel();
|
Chris@1748
|
59 if (!model) return false;
|
Chris@420
|
60 if (!index.isValid()) return false;
|
Chris@1748
|
61 Command *command = model->getSetDataCommand(getUnsorted(index.row()),
|
Chris@421
|
62 index.column(),
|
Chris@421
|
63 value, role);
|
Chris@420
|
64 if (command) {
|
Chris@427
|
65 emit addCommand(command);
|
Chris@420
|
66 return true;
|
Chris@420
|
67 } else {
|
Chris@416
|
68 return false;
|
Chris@416
|
69 }
|
Chris@413
|
70 }
|
Chris@413
|
71
|
Chris@427
|
72 bool
|
Chris@427
|
73 ModelDataTableModel::insertRow(int row, const QModelIndex &parent)
|
Chris@427
|
74 {
|
Chris@1748
|
75 auto model = getTabularModel();
|
Chris@1748
|
76 if (!model) return false;
|
Chris@427
|
77 if (parent.isValid()) return false;
|
Chris@427
|
78
|
Chris@1748
|
79 Command *command = model->getInsertRowCommand(getUnsorted(row));
|
Chris@427
|
80
|
Chris@427
|
81 if (command) {
|
Chris@427
|
82 emit addCommand(command);
|
Chris@427
|
83 }
|
Chris@427
|
84
|
Chris@427
|
85 return (command ? true : false);
|
Chris@427
|
86 }
|
Chris@427
|
87
|
Chris@427
|
88 bool
|
Chris@427
|
89 ModelDataTableModel::removeRow(int row, const QModelIndex &parent)
|
Chris@427
|
90 {
|
Chris@1748
|
91 auto model = getTabularModel();
|
Chris@1748
|
92 if (!model) return false;
|
Chris@427
|
93 if (parent.isValid()) return false;
|
Chris@427
|
94
|
Chris@1748
|
95 Command *command = model->getRemoveRowCommand(getUnsorted(row));
|
Chris@427
|
96
|
Chris@427
|
97 if (command) {
|
Chris@427
|
98 emit addCommand(command);
|
Chris@427
|
99 }
|
Chris@427
|
100
|
Chris@427
|
101 return (command ? true : false);
|
Chris@427
|
102 }
|
Chris@427
|
103
|
Chris@413
|
104 Qt::ItemFlags
|
Chris@929
|
105 ModelDataTableModel::flags(const QModelIndex &) const
|
Chris@413
|
106 {
|
Chris@416
|
107 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable |
|
Chris@416
|
108 Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable;
|
Chris@416
|
109 return flags;
|
Chris@413
|
110 }
|
Chris@413
|
111
|
Chris@413
|
112 QVariant
|
Chris@413
|
113 ModelDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
Chris@413
|
114 {
|
Chris@1748
|
115 auto model = getTabularModel();
|
Chris@1748
|
116 if (!model) return QVariant();
|
Chris@454
|
117
|
Chris@425
|
118 if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
|
Chris@425
|
119 return section + 1;
|
Chris@425
|
120 }
|
Chris@413
|
121 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
Chris@1748
|
122 return model->getHeading(section);
|
Chris@425
|
123 }
|
Chris@413
|
124 return QVariant();
|
Chris@413
|
125 }
|
Chris@413
|
126
|
Chris@413
|
127 QModelIndex
|
Chris@929
|
128 ModelDataTableModel::index(int row, int column, const QModelIndex &) const
|
Chris@413
|
129 {
|
Chris@1582
|
130 return createIndex(row, column, (void *)nullptr);
|
Chris@413
|
131 }
|
Chris@413
|
132
|
Chris@413
|
133 QModelIndex
|
Chris@929
|
134 ModelDataTableModel::parent(const QModelIndex &) const
|
Chris@413
|
135 {
|
Chris@413
|
136 return QModelIndex();
|
Chris@413
|
137 }
|
Chris@413
|
138
|
Chris@413
|
139 int
|
Chris@413
|
140 ModelDataTableModel::rowCount(const QModelIndex &parent) const
|
Chris@413
|
141 {
|
Chris@1748
|
142 auto model = getTabularModel();
|
Chris@1748
|
143 if (!model) return 0;
|
Chris@413
|
144 if (parent.isValid()) return 0;
|
Chris@1748
|
145 int count = model->getRowCount();
|
Chris@1455
|
146 return count;
|
Chris@413
|
147 }
|
Chris@413
|
148
|
Chris@413
|
149 int
|
Chris@413
|
150 ModelDataTableModel::columnCount(const QModelIndex &parent) const
|
Chris@413
|
151 {
|
Chris@1748
|
152 auto model = getTabularModel();
|
Chris@1748
|
153 if (!model) return 0;
|
Chris@413
|
154 if (parent.isValid()) return 0;
|
Chris@1748
|
155 return model->getColumnCount();
|
Chris@416
|
156 }
|
Chris@416
|
157
|
Chris@416
|
158 QModelIndex
|
Chris@1038
|
159 ModelDataTableModel::getModelIndexForFrame(sv_frame_t frame) const
|
Chris@416
|
160 {
|
Chris@1748
|
161 auto model = getTabularModel();
|
Chris@1748
|
162 if (!model) return createIndex(0, 0);
|
Chris@1748
|
163 int row = model->getRowForFrame(frame);
|
Chris@1582
|
164 return createIndex(getSorted(row), 0, (void *)nullptr);
|
Chris@413
|
165 }
|
Chris@413
|
166
|
Chris@1038
|
167 sv_frame_t
|
Chris@419
|
168 ModelDataTableModel::getFrameForModelIndex(const QModelIndex &index) const
|
Chris@419
|
169 {
|
Chris@1748
|
170 auto model = getTabularModel();
|
Chris@1748
|
171 if (!model) return 0;
|
Chris@1748
|
172 return model->getFrameForRow(getUnsorted(index.row()));
|
Chris@420
|
173 }
|
Chris@420
|
174
|
Chris@618
|
175 QModelIndex
|
Chris@618
|
176 ModelDataTableModel::findText(QString text) const
|
Chris@618
|
177 {
|
Chris@1748
|
178 auto model = getTabularModel();
|
Chris@1748
|
179 if (!model) return QModelIndex();
|
Chris@618
|
180 if (text == "") return QModelIndex();
|
Chris@618
|
181 int rows = rowCount();
|
Chris@618
|
182 int cols = columnCount();
|
Chris@618
|
183 int current = getCurrentRow();
|
Chris@618
|
184 for (int row = 1; row <= rows; ++row) {
|
Chris@618
|
185 int wrapped = (row + current) % rows;
|
Chris@618
|
186 for (int col = 0; col < cols; ++col) {
|
Chris@1748
|
187 if (model->getSortType(col) != TabularModel::SortAlphabetical) {
|
Chris@618
|
188 continue;
|
Chris@618
|
189 }
|
Chris@1748
|
190 QString cell = model->getData(getUnsorted(wrapped), col,
|
Chris@618
|
191 Qt::DisplayRole).toString();
|
Chris@618
|
192 if (cell.contains(text, Qt::CaseInsensitive)) {
|
Chris@618
|
193 return createIndex(wrapped, col);
|
Chris@618
|
194 }
|
Chris@618
|
195 }
|
Chris@618
|
196 }
|
Chris@618
|
197 return QModelIndex();
|
Chris@618
|
198 }
|
Chris@618
|
199
|
Chris@420
|
200 void
|
Chris@420
|
201 ModelDataTableModel::sort(int column, Qt::SortOrder sortOrder)
|
Chris@420
|
202 {
|
Chris@690
|
203 // SVDEBUG << "ModelDataTableModel::sort(" << column << ", " << sortOrder
|
Chris@687
|
204 // << ")" << endl;
|
Chris@428
|
205 int prevCurrent = getCurrentRow();
|
Chris@422
|
206 if (m_sortColumn != column) {
|
Chris@428
|
207 clearSort();
|
Chris@422
|
208 }
|
Chris@420
|
209 m_sortColumn = column;
|
Chris@420
|
210 m_sortOrdering = sortOrder;
|
Chris@428
|
211 int current = getCurrentRow();
|
Chris@428
|
212 if (current != prevCurrent) {
|
Chris@843
|
213 // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl;
|
Chris@1582
|
214 emit currentChanged(createIndex(current, 0, (void *)nullptr));
|
Chris@428
|
215 }
|
Chris@420
|
216 emit layoutChanged();
|
Chris@419
|
217 }
|
Chris@419
|
218
|
Chris@413
|
219 void
|
Chris@1770
|
220 ModelDataTableModel::modelChanged(ModelId)
|
Chris@413
|
221 {
|
Chris@1455
|
222 SVDEBUG << "ModelDataTableModel::modelChanged" << endl;
|
Chris@1455
|
223 QModelIndex ix0;
|
Chris@1455
|
224 QModelIndex ix1;
|
Chris@1455
|
225 if (rowCount() > 0) {
|
Chris@1455
|
226 ix0 = createIndex(0, 0);
|
Chris@1455
|
227 int lastCol = columnCount() - 1;
|
Chris@1455
|
228 if (lastCol < 0) lastCol = 0;
|
Chris@1455
|
229 ix1 = createIndex(rowCount(), lastCol);
|
Chris@1455
|
230 }
|
Chris@1455
|
231 SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl;
|
Chris@1455
|
232 emit dataChanged(ix0, ix1);
|
Chris@428
|
233 clearSort();
|
Chris@413
|
234 emit layoutChanged();
|
Chris@413
|
235 }
|
Chris@413
|
236
|
Chris@413
|
237 void
|
Chris@1770
|
238 ModelDataTableModel::modelChangedWithin(ModelId, sv_frame_t f0, sv_frame_t f1)
|
Chris@416
|
239 {
|
Chris@1455
|
240 SVDEBUG << "ModelDataTableModel::modelChangedWithin(" << f0 << "," << f1 << ")" << endl;
|
Chris@1455
|
241 QModelIndex ix0 = getModelIndexForFrame(f0);
|
Chris@1455
|
242 QModelIndex ix1 = getModelIndexForFrame(f1);
|
Chris@1455
|
243 int row0 = ix0.row();
|
Chris@1455
|
244 int row1 = ix1.row();
|
Chris@1455
|
245 if (row0 > 0) {
|
Chris@1582
|
246 ix0 = createIndex(row0 - 1, ix0.column(), (void *)nullptr);
|
Chris@1455
|
247 }
|
Chris@1455
|
248 if (row1 + 1 < rowCount()) {
|
Chris@1582
|
249 ix1 = createIndex(row1 + 1, ix1.column(), (void *)nullptr);
|
Chris@1455
|
250 }
|
Chris@1455
|
251 SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl;
|
Chris@1455
|
252 emit dataChanged(ix0, ix1);
|
Chris@428
|
253 clearSort();
|
Chris@416
|
254 emit layoutChanged();
|
Chris@416
|
255 }
|
Chris@413
|
256
|
Chris@420
|
257 int
|
Chris@426
|
258 ModelDataTableModel::getSorted(int row) const
|
Chris@413
|
259 {
|
Chris@1748
|
260 auto model = getTabularModel();
|
Chris@1748
|
261 if (!model) return row;
|
Chris@454
|
262
|
Chris@1748
|
263 if (model->isColumnTimeValue(m_sortColumn)) {
|
Chris@420
|
264 if (m_sortOrdering == Qt::AscendingOrder) {
|
Chris@420
|
265 return row;
|
Chris@420
|
266 } else {
|
Chris@420
|
267 return rowCount() - row - 1;
|
Chris@420
|
268 }
|
Chris@420
|
269 }
|
Chris@413
|
270
|
Chris@420
|
271 if (m_sort.empty()) {
|
Chris@420
|
272 resort();
|
Chris@413
|
273 }
|
Chris@422
|
274 int result = 0;
|
Chris@929
|
275 if (row >= 0 && row < (int)m_sort.size()) {
|
Chris@422
|
276 result = m_sort[row];
|
Chris@422
|
277 }
|
Chris@422
|
278 if (m_sortOrdering == Qt::DescendingOrder) {
|
Chris@422
|
279 result = rowCount() - result - 1;
|
Chris@422
|
280 }
|
Chris@422
|
281
|
Chris@422
|
282 return result;
|
Chris@413
|
283 }
|
Chris@413
|
284
|
Chris@420
|
285 int
|
Chris@426
|
286 ModelDataTableModel::getUnsorted(int row) const
|
Chris@413
|
287 {
|
Chris@1748
|
288 auto model = getTabularModel();
|
Chris@1748
|
289 if (!model) return row;
|
Chris@454
|
290
|
Chris@1748
|
291 if (model->isColumnTimeValue(m_sortColumn)) {
|
Chris@420
|
292 if (m_sortOrdering == Qt::AscendingOrder) {
|
Chris@420
|
293 return row;
|
Chris@420
|
294 } else {
|
Chris@420
|
295 return rowCount() - row - 1;
|
Chris@420
|
296 }
|
Chris@413
|
297 }
|
Chris@422
|
298
|
Chris@420
|
299 if (m_sort.empty()) {
|
Chris@420
|
300 resort();
|
Chris@420
|
301 }
|
Chris@422
|
302
|
Chris@422
|
303 int result = 0;
|
Chris@929
|
304 if (row >= 0 && row < (int)m_sort.size()) {
|
Chris@422
|
305 if (m_sortOrdering == Qt::AscendingOrder) {
|
Chris@422
|
306 result = m_rsort[row];
|
Chris@422
|
307 } else {
|
Chris@422
|
308 result = m_rsort[rowCount() - row - 1];
|
Chris@422
|
309 }
|
Chris@422
|
310 }
|
Chris@422
|
311
|
Chris@422
|
312 return result;
|
Chris@413
|
313 }
|
Chris@413
|
314
|
Chris@420
|
315 void
|
Chris@426
|
316 ModelDataTableModel::resort() const
|
Chris@413
|
317 {
|
Chris@1748
|
318 auto model = getTabularModel();
|
Chris@1748
|
319 if (!model) return;
|
Chris@454
|
320
|
Chris@1748
|
321 bool numeric = (model->getSortType(m_sortColumn) ==
|
Chris@422
|
322 TabularModel::SortNumeric);
|
Chris@422
|
323
|
Chris@843
|
324 // cerr << "resort: numeric == " << numeric << endl;
|
Chris@618
|
325
|
Chris@422
|
326 m_sort.clear();
|
Chris@422
|
327 m_rsort.clear();
|
Chris@422
|
328
|
Chris@422
|
329 if (numeric) resortNumeric();
|
Chris@422
|
330 else resortAlphabetical();
|
Chris@422
|
331
|
Chris@422
|
332 std::map<int, int> tmp;
|
Chris@422
|
333
|
Chris@422
|
334 // rsort maps from sorted row number to original row number
|
Chris@422
|
335
|
Chris@929
|
336 for (int i = 0; i < (int)m_rsort.size(); ++i) {
|
Chris@422
|
337 tmp[m_rsort[i]] = i;
|
Chris@422
|
338 }
|
Chris@422
|
339
|
Chris@422
|
340 // tmp now maps from original row number to sorted row number
|
Chris@422
|
341
|
Chris@422
|
342 for (std::map<int, int>::const_iterator i = tmp.begin(); i != tmp.end(); ++i) {
|
Chris@422
|
343 m_sort.push_back(i->second);
|
Chris@422
|
344 }
|
Chris@422
|
345
|
Chris@422
|
346 // and sort now maps from original row number to sorted row number
|
Chris@413
|
347 }
|
Chris@413
|
348
|
Chris@422
|
349 void
|
Chris@426
|
350 ModelDataTableModel::resortNumeric() const
|
Chris@422
|
351 {
|
Chris@1748
|
352 auto model = getTabularModel();
|
Chris@1748
|
353 if (!model) return;
|
Chris@454
|
354
|
Chris@422
|
355 typedef std::multimap<double, int> MapType;
|
Chris@422
|
356
|
Chris@422
|
357 MapType rowMap;
|
Chris@1748
|
358 int rows = model->getRowCount();
|
Chris@422
|
359
|
Chris@422
|
360 for (int i = 0; i < rows; ++i) {
|
Chris@1748
|
361 QVariant value = model->getData(i, m_sortColumn, TabularModel::SortRole);
|
Chris@422
|
362 rowMap.insert(MapType::value_type(value.toDouble(), i));
|
Chris@422
|
363 }
|
Chris@422
|
364
|
Chris@422
|
365 for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) {
|
Chris@843
|
366 // cerr << "resortNumeric: " << i->second << ": " << i->first << endl;
|
Chris@422
|
367 m_rsort.push_back(i->second);
|
Chris@422
|
368 }
|
Chris@422
|
369
|
Chris@422
|
370 // rsort now maps from sorted row number to original row number
|
Chris@422
|
371 }
|
Chris@422
|
372
|
Chris@422
|
373 void
|
Chris@426
|
374 ModelDataTableModel::resortAlphabetical() const
|
Chris@422
|
375 {
|
Chris@1748
|
376 auto model = getTabularModel();
|
Chris@1748
|
377 if (!model) return;
|
Chris@454
|
378
|
Chris@422
|
379 typedef std::multimap<QString, int> MapType;
|
Chris@422
|
380
|
Chris@422
|
381 MapType rowMap;
|
Chris@1748
|
382 int rows = model->getRowCount();
|
Chris@422
|
383
|
Chris@422
|
384 for (int i = 0; i < rows; ++i) {
|
Chris@422
|
385 QVariant value =
|
Chris@1748
|
386 model->getData(i, m_sortColumn, TabularModel::SortRole);
|
Chris@422
|
387 rowMap.insert(MapType::value_type(value.toString(), i));
|
Chris@422
|
388 }
|
Chris@422
|
389
|
Chris@422
|
390 for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) {
|
Chris@843
|
391 // cerr << "resortAlphabetical: " << i->second << ": " << i->first << endl;
|
Chris@422
|
392 m_rsort.push_back(i->second);
|
Chris@422
|
393 }
|
Chris@422
|
394
|
Chris@422
|
395 // rsort now maps from sorted row number to original row number
|
Chris@422
|
396 }
|
Chris@422
|
397
|
Chris@428
|
398 int
|
Chris@618
|
399 ModelDataTableModel::getCurrentRow() const
|
Chris@428
|
400 {
|
Chris@428
|
401 return getSorted(m_currentRow);
|
Chris@428
|
402 }
|
Chris@428
|
403
|
Chris@428
|
404 void
|
Chris@428
|
405 ModelDataTableModel::setCurrentRow(int row)
|
Chris@428
|
406 {
|
Chris@428
|
407 m_currentRow = getUnsorted(row);
|
Chris@428
|
408 }
|
Chris@428
|
409
|
Chris@428
|
410 void
|
Chris@428
|
411 ModelDataTableModel::clearSort()
|
Chris@428
|
412 {
|
Chris@428
|
413 // int prevCurrent = getCurrentRow();
|
Chris@428
|
414 m_sort.clear();
|
Chris@428
|
415 // int current = getCurrentRow(); //!!! no -- not until the sort criteria have changed
|
Chris@428
|
416 // if (current != prevCurrent) {
|
Chris@843
|
417 // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl;
|
Chris@428
|
418 // emit currentRowChanged(createIndex(current, 0, 0));
|
Chris@428
|
419 // }
|
Chris@428
|
420 }
|
Chris@428
|
421
|
Chris@428
|
422
|