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