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@420
|
25 ModelDataTableModel::ModelDataTableModel(TabularModel *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@420
|
31 Model *baseModel = dynamic_cast<Model *>(m);
|
Chris@420
|
32
|
Chris@420
|
33 connect(baseModel, SIGNAL(modelChanged()), this, SLOT(modelChanged()));
|
Chris@1046
|
34 connect(baseModel, SIGNAL(modelChangedWithin(sv_frame_t, sv_frame_t)),
|
Chris@1046
|
35 this, SLOT(modelChangedWithin(sv_frame_t, sv_frame_t)));
|
Chris@454
|
36 connect(baseModel, SIGNAL(aboutToBeDeleted()),
|
Chris@454
|
37 this, SLOT(modelAboutToBeDeleted()));
|
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@454
|
47 if (!m_model) return QVariant();
|
Chris@424
|
48 if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant();
|
Chris@413
|
49 if (!index.isValid()) return QVariant();
|
Chris@1254
|
50 QVariant d = m_model->getData(getUnsorted(index.row()), index.column(), role);
|
Chris@1254
|
51 return d;
|
Chris@413
|
52 }
|
Chris@413
|
53
|
Chris@413
|
54 bool
|
Chris@413
|
55 ModelDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
Chris@413
|
56 {
|
Chris@454
|
57 if (!m_model) return false;
|
Chris@420
|
58 if (!index.isValid()) return false;
|
Chris@421
|
59 Command *command = m_model->getSetDataCommand(getUnsorted(index.row()),
|
Chris@421
|
60 index.column(),
|
Chris@421
|
61 value, role);
|
Chris@420
|
62 if (command) {
|
Chris@427
|
63 emit addCommand(command);
|
Chris@420
|
64 return true;
|
Chris@420
|
65 } else {
|
Chris@416
|
66 return false;
|
Chris@416
|
67 }
|
Chris@413
|
68 }
|
Chris@413
|
69
|
Chris@427
|
70 bool
|
Chris@427
|
71 ModelDataTableModel::insertRow(int row, const QModelIndex &parent)
|
Chris@427
|
72 {
|
Chris@454
|
73 if (!m_model) return false;
|
Chris@427
|
74 if (parent.isValid()) return false;
|
Chris@427
|
75
|
Chris@427
|
76 emit beginInsertRows(parent, row, row);
|
Chris@427
|
77
|
Chris@427
|
78 Command *command = m_model->getInsertRowCommand(getUnsorted(row));
|
Chris@427
|
79
|
Chris@427
|
80 if (command) {
|
Chris@427
|
81 emit addCommand(command);
|
Chris@427
|
82 }
|
Chris@427
|
83
|
Chris@427
|
84 emit endInsertRows();
|
Chris@427
|
85
|
Chris@427
|
86 return (command ? true : false);
|
Chris@427
|
87 }
|
Chris@427
|
88
|
Chris@427
|
89 bool
|
Chris@427
|
90 ModelDataTableModel::removeRow(int row, const QModelIndex &parent)
|
Chris@427
|
91 {
|
Chris@454
|
92 if (!m_model) return false;
|
Chris@427
|
93 if (parent.isValid()) return false;
|
Chris@427
|
94
|
Chris@427
|
95 emit beginRemoveRows(parent, row, row);
|
Chris@427
|
96
|
Chris@427
|
97 Command *command = m_model->getRemoveRowCommand(getUnsorted(row));
|
Chris@427
|
98
|
Chris@427
|
99 if (command) {
|
Chris@427
|
100 emit addCommand(command);
|
Chris@427
|
101 }
|
Chris@427
|
102
|
Chris@427
|
103 emit endRemoveRows();
|
Chris@427
|
104
|
Chris@427
|
105 return (command ? true : false);
|
Chris@427
|
106 }
|
Chris@427
|
107
|
Chris@413
|
108 Qt::ItemFlags
|
Chris@929
|
109 ModelDataTableModel::flags(const QModelIndex &) const
|
Chris@413
|
110 {
|
Chris@416
|
111 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable |
|
Chris@416
|
112 Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable;
|
Chris@416
|
113 return flags;
|
Chris@413
|
114 }
|
Chris@413
|
115
|
Chris@413
|
116 QVariant
|
Chris@413
|
117 ModelDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
Chris@413
|
118 {
|
Chris@454
|
119 if (!m_model) return QVariant();
|
Chris@454
|
120
|
Chris@425
|
121 if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
|
Chris@425
|
122 return section + 1;
|
Chris@425
|
123 }
|
Chris@413
|
124 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
Chris@420
|
125 return m_model->getHeading(section);
|
Chris@425
|
126 }
|
Chris@413
|
127 return QVariant();
|
Chris@413
|
128 }
|
Chris@413
|
129
|
Chris@413
|
130 QModelIndex
|
Chris@929
|
131 ModelDataTableModel::index(int row, int column, const QModelIndex &) const
|
Chris@413
|
132 {
|
Chris@763
|
133 return createIndex(row, column, (void *)0);
|
Chris@413
|
134 }
|
Chris@413
|
135
|
Chris@413
|
136 QModelIndex
|
Chris@929
|
137 ModelDataTableModel::parent(const QModelIndex &) const
|
Chris@413
|
138 {
|
Chris@413
|
139 return QModelIndex();
|
Chris@413
|
140 }
|
Chris@413
|
141
|
Chris@413
|
142 int
|
Chris@413
|
143 ModelDataTableModel::rowCount(const QModelIndex &parent) const
|
Chris@413
|
144 {
|
Chris@454
|
145 if (!m_model) return 0;
|
Chris@413
|
146 if (parent.isValid()) return 0;
|
Chris@420
|
147 return m_model->getRowCount();
|
Chris@413
|
148 }
|
Chris@413
|
149
|
Chris@413
|
150 int
|
Chris@413
|
151 ModelDataTableModel::columnCount(const QModelIndex &parent) const
|
Chris@413
|
152 {
|
Chris@454
|
153 if (!m_model) return 0;
|
Chris@413
|
154 if (parent.isValid()) return 0;
|
Chris@420
|
155 return m_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@454
|
161 if (!m_model) return createIndex(0, 0);
|
Chris@420
|
162 int row = m_model->getRowForFrame(frame);
|
Chris@763
|
163 return createIndex(getSorted(row), 0, (void *)0);
|
Chris@413
|
164 }
|
Chris@413
|
165
|
Chris@1038
|
166 sv_frame_t
|
Chris@419
|
167 ModelDataTableModel::getFrameForModelIndex(const QModelIndex &index) const
|
Chris@419
|
168 {
|
Chris@454
|
169 if (!m_model) return 0;
|
Chris@420
|
170 return m_model->getFrameForRow(getUnsorted(index.row()));
|
Chris@420
|
171 }
|
Chris@420
|
172
|
Chris@618
|
173 QModelIndex
|
Chris@618
|
174 ModelDataTableModel::findText(QString text) const
|
Chris@618
|
175 {
|
Chris@618
|
176 if (text == "") return QModelIndex();
|
Chris@618
|
177 int rows = rowCount();
|
Chris@618
|
178 int cols = columnCount();
|
Chris@618
|
179 int current = getCurrentRow();
|
Chris@618
|
180 for (int row = 1; row <= rows; ++row) {
|
Chris@618
|
181 int wrapped = (row + current) % rows;
|
Chris@618
|
182 for (int col = 0; col < cols; ++col) {
|
Chris@618
|
183 if (m_model->getSortType(col) != TabularModel::SortAlphabetical) {
|
Chris@618
|
184 continue;
|
Chris@618
|
185 }
|
Chris@618
|
186 QString cell = m_model->getData(getUnsorted(wrapped), col,
|
Chris@618
|
187 Qt::DisplayRole).toString();
|
Chris@618
|
188 if (cell.contains(text, Qt::CaseInsensitive)) {
|
Chris@618
|
189 return createIndex(wrapped, col);
|
Chris@618
|
190 }
|
Chris@618
|
191 }
|
Chris@618
|
192 }
|
Chris@618
|
193 return QModelIndex();
|
Chris@618
|
194 }
|
Chris@618
|
195
|
Chris@420
|
196 void
|
Chris@420
|
197 ModelDataTableModel::sort(int column, Qt::SortOrder sortOrder)
|
Chris@420
|
198 {
|
Chris@690
|
199 // SVDEBUG << "ModelDataTableModel::sort(" << column << ", " << sortOrder
|
Chris@687
|
200 // << ")" << endl;
|
Chris@428
|
201 int prevCurrent = getCurrentRow();
|
Chris@422
|
202 if (m_sortColumn != column) {
|
Chris@428
|
203 clearSort();
|
Chris@422
|
204 }
|
Chris@420
|
205 m_sortColumn = column;
|
Chris@420
|
206 m_sortOrdering = sortOrder;
|
Chris@428
|
207 int current = getCurrentRow();
|
Chris@428
|
208 if (current != prevCurrent) {
|
Chris@843
|
209 // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl;
|
Chris@763
|
210 emit currentChanged(createIndex(current, 0, (void *)0));
|
Chris@428
|
211 }
|
Chris@420
|
212 emit layoutChanged();
|
Chris@419
|
213 }
|
Chris@419
|
214
|
Chris@413
|
215 void
|
Chris@413
|
216 ModelDataTableModel::modelChanged()
|
Chris@413
|
217 {
|
Chris@428
|
218 clearSort();
|
Chris@413
|
219 emit layoutChanged();
|
Chris@413
|
220 }
|
Chris@413
|
221
|
Chris@413
|
222 void
|
Chris@1046
|
223 ModelDataTableModel::modelChangedWithin(sv_frame_t, sv_frame_t)
|
Chris@416
|
224 {
|
Chris@420
|
225 //!!! inefficient
|
Chris@428
|
226 clearSort();
|
Chris@416
|
227 emit layoutChanged();
|
Chris@416
|
228 }
|
Chris@413
|
229
|
Chris@454
|
230 void
|
Chris@454
|
231 ModelDataTableModel::modelAboutToBeDeleted()
|
Chris@454
|
232 {
|
Chris@454
|
233 m_model = 0;
|
Chris@454
|
234 emit modelRemoved();
|
Chris@454
|
235 }
|
Chris@454
|
236
|
Chris@420
|
237 int
|
Chris@426
|
238 ModelDataTableModel::getSorted(int row) const
|
Chris@413
|
239 {
|
Chris@454
|
240 if (!m_model) return row;
|
Chris@454
|
241
|
Chris@420
|
242 if (m_model->isColumnTimeValue(m_sortColumn)) {
|
Chris@420
|
243 if (m_sortOrdering == Qt::AscendingOrder) {
|
Chris@420
|
244 return row;
|
Chris@420
|
245 } else {
|
Chris@420
|
246 return rowCount() - row - 1;
|
Chris@420
|
247 }
|
Chris@420
|
248 }
|
Chris@413
|
249
|
Chris@420
|
250 if (m_sort.empty()) {
|
Chris@420
|
251 resort();
|
Chris@413
|
252 }
|
Chris@422
|
253 int result = 0;
|
Chris@929
|
254 if (row >= 0 && row < (int)m_sort.size()) {
|
Chris@422
|
255 result = m_sort[row];
|
Chris@422
|
256 }
|
Chris@422
|
257 if (m_sortOrdering == Qt::DescendingOrder) {
|
Chris@422
|
258 result = rowCount() - result - 1;
|
Chris@422
|
259 }
|
Chris@422
|
260
|
Chris@422
|
261 return result;
|
Chris@413
|
262 }
|
Chris@413
|
263
|
Chris@420
|
264 int
|
Chris@426
|
265 ModelDataTableModel::getUnsorted(int row) const
|
Chris@413
|
266 {
|
Chris@454
|
267 if (!m_model) return row;
|
Chris@454
|
268
|
Chris@420
|
269 if (m_model->isColumnTimeValue(m_sortColumn)) {
|
Chris@420
|
270 if (m_sortOrdering == Qt::AscendingOrder) {
|
Chris@420
|
271 return row;
|
Chris@420
|
272 } else {
|
Chris@420
|
273 return rowCount() - row - 1;
|
Chris@420
|
274 }
|
Chris@413
|
275 }
|
Chris@422
|
276
|
Chris@420
|
277 if (m_sort.empty()) {
|
Chris@420
|
278 resort();
|
Chris@420
|
279 }
|
Chris@422
|
280
|
Chris@422
|
281 int result = 0;
|
Chris@929
|
282 if (row >= 0 && row < (int)m_sort.size()) {
|
Chris@422
|
283 if (m_sortOrdering == Qt::AscendingOrder) {
|
Chris@422
|
284 result = m_rsort[row];
|
Chris@422
|
285 } else {
|
Chris@422
|
286 result = m_rsort[rowCount() - row - 1];
|
Chris@422
|
287 }
|
Chris@422
|
288 }
|
Chris@422
|
289
|
Chris@422
|
290 return result;
|
Chris@413
|
291 }
|
Chris@413
|
292
|
Chris@420
|
293 void
|
Chris@426
|
294 ModelDataTableModel::resort() const
|
Chris@413
|
295 {
|
Chris@454
|
296 if (!m_model) return;
|
Chris@454
|
297
|
Chris@422
|
298 bool numeric = (m_model->getSortType(m_sortColumn) ==
|
Chris@422
|
299 TabularModel::SortNumeric);
|
Chris@422
|
300
|
Chris@843
|
301 // cerr << "resort: numeric == " << numeric << endl;
|
Chris@618
|
302
|
Chris@422
|
303 m_sort.clear();
|
Chris@422
|
304 m_rsort.clear();
|
Chris@422
|
305
|
Chris@422
|
306 if (numeric) resortNumeric();
|
Chris@422
|
307 else resortAlphabetical();
|
Chris@422
|
308
|
Chris@422
|
309 std::map<int, int> tmp;
|
Chris@422
|
310
|
Chris@422
|
311 // rsort maps from sorted row number to original row number
|
Chris@422
|
312
|
Chris@929
|
313 for (int i = 0; i < (int)m_rsort.size(); ++i) {
|
Chris@422
|
314 tmp[m_rsort[i]] = i;
|
Chris@422
|
315 }
|
Chris@422
|
316
|
Chris@422
|
317 // tmp now maps from original row number to sorted row number
|
Chris@422
|
318
|
Chris@422
|
319 for (std::map<int, int>::const_iterator i = tmp.begin(); i != tmp.end(); ++i) {
|
Chris@422
|
320 m_sort.push_back(i->second);
|
Chris@422
|
321 }
|
Chris@422
|
322
|
Chris@422
|
323 // and sort now maps from original row number to sorted row number
|
Chris@413
|
324 }
|
Chris@413
|
325
|
Chris@422
|
326 void
|
Chris@426
|
327 ModelDataTableModel::resortNumeric() const
|
Chris@422
|
328 {
|
Chris@454
|
329 if (!m_model) return;
|
Chris@454
|
330
|
Chris@422
|
331 typedef std::multimap<double, int> MapType;
|
Chris@422
|
332
|
Chris@422
|
333 MapType rowMap;
|
Chris@422
|
334 int rows = m_model->getRowCount();
|
Chris@422
|
335
|
Chris@422
|
336 for (int i = 0; i < rows; ++i) {
|
Chris@424
|
337 QVariant value = m_model->getData(i, m_sortColumn, TabularModel::SortRole);
|
Chris@422
|
338 rowMap.insert(MapType::value_type(value.toDouble(), i));
|
Chris@422
|
339 }
|
Chris@422
|
340
|
Chris@422
|
341 for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) {
|
Chris@843
|
342 // cerr << "resortNumeric: " << i->second << ": " << i->first << endl;
|
Chris@422
|
343 m_rsort.push_back(i->second);
|
Chris@422
|
344 }
|
Chris@422
|
345
|
Chris@422
|
346 // rsort now maps from sorted row number to original row number
|
Chris@422
|
347 }
|
Chris@422
|
348
|
Chris@422
|
349 void
|
Chris@426
|
350 ModelDataTableModel::resortAlphabetical() const
|
Chris@422
|
351 {
|
Chris@454
|
352 if (!m_model) return;
|
Chris@454
|
353
|
Chris@422
|
354 typedef std::multimap<QString, int> MapType;
|
Chris@422
|
355
|
Chris@422
|
356 MapType rowMap;
|
Chris@422
|
357 int rows = m_model->getRowCount();
|
Chris@422
|
358
|
Chris@422
|
359 for (int i = 0; i < rows; ++i) {
|
Chris@422
|
360 QVariant value =
|
Chris@422
|
361 m_model->getData(i, m_sortColumn, TabularModel::SortRole);
|
Chris@422
|
362 rowMap.insert(MapType::value_type(value.toString(), i));
|
Chris@422
|
363 }
|
Chris@422
|
364
|
Chris@422
|
365 for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) {
|
Chris@843
|
366 // cerr << "resortAlphabetical: " << 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@428
|
373 int
|
Chris@618
|
374 ModelDataTableModel::getCurrentRow() const
|
Chris@428
|
375 {
|
Chris@428
|
376 return getSorted(m_currentRow);
|
Chris@428
|
377 }
|
Chris@428
|
378
|
Chris@428
|
379 void
|
Chris@428
|
380 ModelDataTableModel::setCurrentRow(int row)
|
Chris@428
|
381 {
|
Chris@428
|
382 m_currentRow = getUnsorted(row);
|
Chris@428
|
383 }
|
Chris@428
|
384
|
Chris@428
|
385 void
|
Chris@428
|
386 ModelDataTableModel::clearSort()
|
Chris@428
|
387 {
|
Chris@428
|
388 // int prevCurrent = getCurrentRow();
|
Chris@428
|
389 m_sort.clear();
|
Chris@428
|
390 // int current = getCurrentRow(); //!!! no -- not until the sort criteria have changed
|
Chris@428
|
391 // if (current != prevCurrent) {
|
Chris@843
|
392 // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl;
|
Chris@428
|
393 // emit currentRowChanged(createIndex(current, 0, 0));
|
Chris@428
|
394 // }
|
Chris@428
|
395 }
|
Chris@428
|
396
|
Chris@428
|
397
|