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