annotate data/model/ModelDataTableModel.cpp @ 1804:343ef2a866a4

Implement missing TabularModel editing methods. Also made these pure in TabularModel, since almost all subclasses want them and (clearly) forgetting to implement them is a problem!
author Chris Cannam
date Mon, 14 Oct 2019 14:17:37 +0100
parents dffc70996f54
children
rev   line source
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