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@413
|
18 #include "SparseTimeValueModel.h"
|
Chris@413
|
19 #include "SparseOneDimensionalModel.h"
|
Chris@413
|
20 #include "SparseModel.h"
|
Chris@413
|
21
|
Chris@417
|
22 #include <algorithm>
|
Chris@417
|
23
|
Chris@413
|
24 ModelDataTableModel::ModelDataTableModel(Model *m) :
|
Chris@413
|
25 m_model(m)
|
Chris@413
|
26 {
|
Chris@413
|
27 connect(m, SIGNAL(modelChanged()), this, SLOT(modelChanged()));
|
Chris@413
|
28 connect(m, SIGNAL(modelChanged(size_t, size_t)),
|
Chris@413
|
29 this, SLOT(modelChanged(size_t, size_t)));
|
Chris@413
|
30 rebuildRowVector();
|
Chris@413
|
31 }
|
Chris@413
|
32
|
Chris@413
|
33 ModelDataTableModel::~ModelDataTableModel()
|
Chris@413
|
34 {
|
Chris@413
|
35 }
|
Chris@413
|
36
|
Chris@413
|
37 QVariant
|
Chris@413
|
38 ModelDataTableModel::data(const QModelIndex &index, int role) const
|
Chris@413
|
39 {
|
Chris@416
|
40 if (role != Qt::DisplayRole && role != Qt::EditRole) {
|
Chris@413
|
41 return QVariant();
|
Chris@413
|
42 }
|
Chris@413
|
43
|
Chris@416
|
44 bool withUnit = (role == Qt::DisplayRole);
|
Chris@416
|
45
|
Chris@413
|
46 if (!index.isValid()) return QVariant();
|
Chris@413
|
47
|
Chris@413
|
48 int row = index.row(), col = index.column();
|
Chris@413
|
49
|
Chris@413
|
50 if (row < 0 || row >= m_rows.size()) return QVariant();
|
Chris@413
|
51
|
Chris@413
|
52 if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
|
Chris@416
|
53 return dataSparse<SparseOneDimensionalModel::Point>(row, col, withUnit);
|
Chris@413
|
54 } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
|
Chris@416
|
55 return dataSparse<SparseTimeValueModel::Point>(row, col, withUnit);
|
Chris@413
|
56 }
|
Chris@413
|
57
|
Chris@413
|
58 return QVariant();
|
Chris@413
|
59 }
|
Chris@413
|
60
|
Chris@413
|
61 template <typename PointType>
|
Chris@413
|
62 QVariant
|
Chris@416
|
63 ModelDataTableModel::dataSparse(int row, int col, bool withUnit) const
|
Chris@413
|
64 {
|
Chris@413
|
65 size_t frame = m_rows[row];
|
Chris@413
|
66
|
Chris@413
|
67 // This is just garbage. This would be a reasonable enough way to
|
Chris@413
|
68 // handle this in a dynamically typed language but it's hopeless
|
Chris@413
|
69 // in C++. The design is simply wrong. We need virtual helper
|
Chris@413
|
70 // methods in the model itself.
|
Chris@413
|
71
|
Chris@413
|
72 typedef SparseModel<PointType> ModelType;
|
Chris@413
|
73 typedef std::multiset<PointType, typename PointType::OrderComparator>
|
Chris@413
|
74 PointListType;
|
Chris@413
|
75
|
Chris@413
|
76 const ModelType *sm = dynamic_cast<const ModelType *>(m_model);
|
Chris@413
|
77 const PointListType &points = sm->getPoints(frame);
|
Chris@413
|
78
|
Chris@413
|
79 // it is possible to have more than one point at the same frame
|
Chris@413
|
80
|
Chris@413
|
81 int indexAtFrame = 0;
|
Chris@413
|
82 int ri = row;
|
Chris@413
|
83 while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
|
Chris@413
|
84
|
Chris@413
|
85 for (typename PointListType::const_iterator i = points.begin();
|
Chris@413
|
86 i != points.end(); ++i) {
|
Chris@413
|
87
|
Chris@413
|
88 const PointType *point = &(*i);
|
Chris@413
|
89 if (point->frame < frame) continue;
|
Chris@413
|
90 if (point->frame > frame) return QVariant();
|
Chris@413
|
91 if (indexAtFrame > 0) { --indexAtFrame; continue; }
|
Chris@413
|
92
|
Chris@413
|
93 switch (col) {
|
Chris@413
|
94
|
Chris@413
|
95 case 0:
|
Chris@416
|
96 {
|
Chris@416
|
97 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
|
Chris@416
|
98 std::cerr << "Returning time " << rt << std::endl;
|
Chris@416
|
99 return QVariant(rt.toText().c_str());
|
Chris@416
|
100 }
|
Chris@416
|
101
|
Chris@416
|
102 case 1:
|
Chris@413
|
103 std::cerr << "Returning frame " << frame << std::endl;
|
Chris@417
|
104 return QVariant(int(frame));
|
Chris@413
|
105
|
Chris@416
|
106 case 2:
|
Chris@413
|
107 if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
|
Chris@413
|
108 const SparseOneDimensionalModel::Point *cp =
|
Chris@413
|
109 reinterpret_cast<const SparseOneDimensionalModel::Point *>(point);
|
Chris@413
|
110 std::cerr << "Returning label \"" << cp->label.toStdString() << "\"" << std::endl;
|
Chris@413
|
111 return QVariant(cp->label);
|
Chris@413
|
112 } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
|
Chris@413
|
113 const SparseTimeValueModel::Point *cp =
|
Chris@413
|
114 reinterpret_cast<const SparseTimeValueModel::Point *>(point);
|
Chris@413
|
115 std::cerr << "Returning value " << cp->value << std::endl;
|
Chris@416
|
116 if (withUnit) {
|
Chris@416
|
117 return QVariant(QString("%1 %2").arg(cp->value)
|
Chris@416
|
118 .arg(dynamic_cast<const SparseTimeValueModel *>(m_model)->getScaleUnits()));
|
Chris@416
|
119 } else {
|
Chris@416
|
120 return cp->value;
|
Chris@416
|
121 }
|
Chris@413
|
122 } else return QVariant();
|
Chris@413
|
123
|
Chris@416
|
124 case 3:
|
Chris@413
|
125 if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
|
Chris@413
|
126 return QVariant();
|
Chris@413
|
127 } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
|
Chris@413
|
128 return reinterpret_cast<const SparseTimeValueModel::Point *>(point)->label;
|
Chris@413
|
129 } else return QVariant();
|
Chris@413
|
130 }
|
Chris@413
|
131 }
|
Chris@413
|
132
|
Chris@413
|
133 return QVariant();
|
Chris@413
|
134 }
|
Chris@413
|
135
|
Chris@413
|
136 bool
|
Chris@413
|
137 ModelDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
Chris@413
|
138 {
|
Chris@416
|
139 if (role != Qt::EditRole) {
|
Chris@416
|
140 std::cerr << "setData: ignoring role " << role << std::endl;
|
Chris@416
|
141 return false;
|
Chris@416
|
142 }
|
Chris@416
|
143
|
Chris@416
|
144 //!!! see comment about disgustuality of this whole process, in
|
Chris@416
|
145 //dataSparse above
|
Chris@416
|
146
|
Chris@416
|
147 if (!index.isValid()) return false;
|
Chris@416
|
148
|
Chris@416
|
149 int row = index.row(), col = index.column();
|
Chris@416
|
150
|
Chris@416
|
151 if (row < 0 || row >= m_rows.size()) return false;
|
Chris@416
|
152
|
Chris@416
|
153 if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
|
Chris@416
|
154 return setDataSparse<SparseOneDimensionalModel::Point>(row, col, value);
|
Chris@416
|
155 } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
|
Chris@416
|
156 return setDataSparse<SparseTimeValueModel::Point>(row, col, value);
|
Chris@416
|
157 }
|
Chris@416
|
158
|
Chris@416
|
159 return false;
|
Chris@416
|
160 }
|
Chris@416
|
161
|
Chris@416
|
162 template <typename PointType>
|
Chris@416
|
163 bool
|
Chris@416
|
164 ModelDataTableModel::setDataSparse(int row, int col, QVariant value)
|
Chris@416
|
165 {
|
Chris@416
|
166 size_t frame = m_rows[row];
|
Chris@416
|
167
|
Chris@416
|
168 typedef SparseModel<PointType> ModelType;
|
Chris@416
|
169 typedef std::multiset<PointType, typename PointType::OrderComparator>
|
Chris@416
|
170 PointListType;
|
Chris@416
|
171 typedef typename ModelType::EditCommand EditCommandType;
|
Chris@416
|
172
|
Chris@417
|
173 ModelType *sm = dynamic_cast<ModelType *>(m_model);
|
Chris@416
|
174 const PointListType &points = sm->getPoints(frame);
|
Chris@416
|
175
|
Chris@416
|
176 // it is possible to have more than one point at the same frame
|
Chris@416
|
177
|
Chris@416
|
178 int indexAtFrame = 0;
|
Chris@416
|
179 int ri = row;
|
Chris@416
|
180 while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; }
|
Chris@416
|
181
|
Chris@416
|
182 for (typename PointListType::const_iterator i = points.begin();
|
Chris@416
|
183 i != points.end(); ++i) {
|
Chris@416
|
184
|
Chris@416
|
185 const PointType *point = &(*i);
|
Chris@416
|
186 if (point->frame < frame) continue;
|
Chris@416
|
187 if (point->frame > frame) return false;
|
Chris@416
|
188 if (indexAtFrame > 0) { --indexAtFrame; continue; }
|
Chris@416
|
189
|
Chris@416
|
190 switch (col) {
|
Chris@416
|
191
|
Chris@416
|
192 case 0:
|
Chris@416
|
193 {
|
Chris@416
|
194 /*
|
Chris@416
|
195 RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate());
|
Chris@416
|
196 std::cerr << "Returning time " << rt << std::endl;
|
Chris@416
|
197 return QVariant(rt.toText().c_str());
|
Chris@416
|
198 */
|
Chris@416
|
199 }
|
Chris@416
|
200
|
Chris@416
|
201 case 1:
|
Chris@416
|
202 {
|
Chris@416
|
203 EditCommandType *command =
|
Chris@416
|
204 new EditCommandType(sm, tr("Edit point time"));
|
Chris@416
|
205 PointType newPoint(*point);
|
Chris@416
|
206 newPoint.frame = value.toInt(); //!!! check validity
|
Chris@416
|
207 command->deletePoint(*point);
|
Chris@416
|
208 command->addPoint(newPoint);
|
Chris@416
|
209 command = command->finish();
|
Chris@416
|
210 if (command) emit executeCommand(command);
|
Chris@416
|
211 return true;
|
Chris@416
|
212 }
|
Chris@416
|
213 // std::cerr << "Returning frame " << frame << std::endl;
|
Chris@416
|
214 // return QVariant(frame); //!!! RealTime
|
Chris@416
|
215
|
Chris@416
|
216 case 2:
|
Chris@416
|
217 break;
|
Chris@416
|
218
|
Chris@416
|
219 case 3:
|
Chris@416
|
220 break;
|
Chris@416
|
221 }
|
Chris@416
|
222 }
|
Chris@416
|
223
|
Chris@413
|
224 return false;
|
Chris@413
|
225 }
|
Chris@413
|
226
|
Chris@413
|
227 Qt::ItemFlags
|
Chris@413
|
228 ModelDataTableModel::flags(const QModelIndex &index) const
|
Chris@413
|
229 {
|
Chris@416
|
230 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable |
|
Chris@416
|
231 Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable;
|
Chris@416
|
232 return flags;
|
Chris@413
|
233 }
|
Chris@413
|
234
|
Chris@413
|
235 QVariant
|
Chris@413
|
236 ModelDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const
|
Chris@413
|
237 {
|
Chris@413
|
238 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
|
Chris@416
|
239 if (section == 0) return QVariant(tr("Time"));
|
Chris@416
|
240 if (section == 1) return QVariant(tr("Frame"));
|
Chris@416
|
241 else if (section == 2) {
|
Chris@413
|
242 if (dynamic_cast<const SparseOneDimensionalModel *>(m_model)) {
|
Chris@413
|
243 return QVariant(tr("Label"));
|
Chris@413
|
244 } else if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
|
Chris@413
|
245 return QVariant(tr("Value"));
|
Chris@413
|
246 }
|
Chris@416
|
247 } else if (section == 3) {
|
Chris@413
|
248 if (dynamic_cast<const SparseTimeValueModel *>(m_model)) {
|
Chris@413
|
249 return QVariant(tr("Label"));
|
Chris@413
|
250 }
|
Chris@413
|
251 }
|
Chris@413
|
252 }
|
Chris@413
|
253 return QVariant();
|
Chris@413
|
254 }
|
Chris@413
|
255
|
Chris@413
|
256 QModelIndex
|
Chris@413
|
257 ModelDataTableModel::index(int row, int column, const QModelIndex &parent) const
|
Chris@413
|
258 {
|
Chris@413
|
259 return createIndex(row, column, 0);
|
Chris@413
|
260 }
|
Chris@413
|
261
|
Chris@413
|
262 QModelIndex
|
Chris@413
|
263 ModelDataTableModel::parent(const QModelIndex &index) const
|
Chris@413
|
264 {
|
Chris@413
|
265 return QModelIndex();
|
Chris@413
|
266 }
|
Chris@413
|
267
|
Chris@413
|
268 int
|
Chris@413
|
269 ModelDataTableModel::rowCount(const QModelIndex &parent) const
|
Chris@413
|
270 {
|
Chris@413
|
271 if (parent.isValid()) return 0;
|
Chris@413
|
272 return m_rows.size();
|
Chris@413
|
273 }
|
Chris@413
|
274
|
Chris@413
|
275 int
|
Chris@413
|
276 ModelDataTableModel::columnCount(const QModelIndex &parent) const
|
Chris@413
|
277 {
|
Chris@413
|
278 if (parent.isValid()) return 0;
|
Chris@413
|
279 if (!canHandleModelType(m_model)) return 0;
|
Chris@413
|
280
|
Chris@413
|
281 if (dynamic_cast<SparseOneDimensionalModel *>(m_model)) {
|
Chris@416
|
282 return 3;
|
Chris@413
|
283 } else if (dynamic_cast<SparseTimeValueModel *>(m_model)) {
|
Chris@416
|
284 return 4;
|
Chris@413
|
285 }
|
Chris@413
|
286
|
Chris@416
|
287 return 2;
|
Chris@416
|
288 }
|
Chris@416
|
289
|
Chris@416
|
290 QModelIndex
|
Chris@416
|
291 ModelDataTableModel::getModelIndexForFrame(size_t frame) const
|
Chris@416
|
292 {
|
Chris@416
|
293 std::vector<size_t>::const_iterator i =
|
Chris@416
|
294 std::lower_bound(m_rows.begin(), m_rows.end(), frame);
|
Chris@416
|
295 size_t dist = std::distance(m_rows.begin(), i);
|
Chris@416
|
296 return createIndex(dist, 0, 0);
|
Chris@413
|
297 }
|
Chris@413
|
298
|
Chris@419
|
299 size_t
|
Chris@419
|
300 ModelDataTableModel::getFrameForModelIndex(const QModelIndex &index) const
|
Chris@419
|
301 {
|
Chris@419
|
302 int row = index.row();
|
Chris@419
|
303 if (m_rows.empty()) return 0;
|
Chris@419
|
304 if (row < 0) row == 0;
|
Chris@419
|
305 if (row > m_rows.size()-1) row = m_rows.size()-1;
|
Chris@419
|
306 return m_rows[row];
|
Chris@419
|
307 }
|
Chris@419
|
308
|
Chris@413
|
309 void
|
Chris@413
|
310 ModelDataTableModel::modelChanged()
|
Chris@413
|
311 {
|
Chris@413
|
312 rebuildRowVector();
|
Chris@413
|
313 emit layoutChanged();
|
Chris@413
|
314 }
|
Chris@413
|
315
|
Chris@413
|
316 void
|
Chris@416
|
317 ModelDataTableModel::modelChanged(size_t f0, size_t f1)
|
Chris@416
|
318 {
|
Chris@416
|
319 std::cerr << "ModelDataTableModel::modelChanged(" << f0 << "," << f1 << ")" << std::endl;
|
Chris@416
|
320 //!!! highly inefficient
|
Chris@416
|
321 rebuildRowVector();
|
Chris@416
|
322 emit layoutChanged();
|
Chris@416
|
323 }
|
Chris@413
|
324
|
Chris@413
|
325 void
|
Chris@413
|
326 ModelDataTableModel::rebuildRowVector()
|
Chris@413
|
327 {
|
Chris@413
|
328 if (!canHandleModelType(m_model)) return;
|
Chris@413
|
329
|
Chris@413
|
330 m_rows.clear();
|
Chris@413
|
331
|
Chris@413
|
332 if (dynamic_cast<SparseOneDimensionalModel *>(m_model)) {
|
Chris@413
|
333 rebuildRowVectorSparse<SparseOneDimensionalModel::Point>();
|
Chris@413
|
334 } else if (dynamic_cast<SparseTimeValueModel *>(m_model)) {
|
Chris@413
|
335 rebuildRowVectorSparse<SparseTimeValueModel::Point>();
|
Chris@413
|
336 }
|
Chris@413
|
337 }
|
Chris@413
|
338
|
Chris@413
|
339 template <typename PointType>
|
Chris@413
|
340 void
|
Chris@413
|
341 ModelDataTableModel::rebuildRowVectorSparse()
|
Chris@413
|
342 {
|
Chris@413
|
343 // gah
|
Chris@413
|
344
|
Chris@413
|
345 typedef SparseModel<PointType> ModelType;
|
Chris@413
|
346 typedef std::multiset<PointType, typename PointType::OrderComparator>
|
Chris@413
|
347 PointListType;
|
Chris@413
|
348
|
Chris@413
|
349 ModelType *sm = dynamic_cast<ModelType *>(m_model);
|
Chris@413
|
350 const PointListType &points = sm->getPoints();
|
Chris@413
|
351
|
Chris@413
|
352 for (typename PointListType::const_iterator i = points.begin();
|
Chris@413
|
353 i != points.end(); ++i) {
|
Chris@413
|
354 m_rows.push_back(i->frame);
|
Chris@413
|
355 }
|
Chris@413
|
356 }
|
Chris@413
|
357
|
Chris@413
|
358 bool
|
Chris@413
|
359 ModelDataTableModel::canHandleModelType(Model *m)
|
Chris@413
|
360 {
|
Chris@413
|
361 if (dynamic_cast<SparseOneDimensionalModel *>(m)) return true;
|
Chris@413
|
362 if (dynamic_cast<SparseTimeValueModel *>(m)) return true;
|
Chris@413
|
363 return false;
|
Chris@413
|
364 }
|
Chris@413
|
365
|
Chris@413
|
366
|