annotate data/model/ModelDataTableModel.cpp @ 1651:7a56bb85030f single-point

Introduce deferred notifier, + start converting sparse time-value model (perhaps we should rename it too)
author Chris Cannam
date Mon, 18 Mar 2019 14:17:20 +0000
parents 70e172e6cc59
children 91a194e2d80b
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@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 Command *command = m_model->getInsertRowCommand(getUnsorted(row));
Chris@427 77
Chris@427 78 if (command) {
Chris@427 79 emit addCommand(command);
Chris@427 80 }
Chris@427 81
Chris@427 82 return (command ? true : false);
Chris@427 83 }
Chris@427 84
Chris@427 85 bool
Chris@427 86 ModelDataTableModel::removeRow(int row, const QModelIndex &parent)
Chris@427 87 {
Chris@454 88 if (!m_model) return false;
Chris@427 89 if (parent.isValid()) return false;
Chris@427 90
Chris@427 91 Command *command = m_model->getRemoveRowCommand(getUnsorted(row));
Chris@427 92
Chris@427 93 if (command) {
Chris@427 94 emit addCommand(command);
Chris@427 95 }
Chris@427 96
Chris@427 97 return (command ? true : false);
Chris@427 98 }
Chris@427 99
Chris@413 100 Qt::ItemFlags
Chris@929 101 ModelDataTableModel::flags(const QModelIndex &) const
Chris@413 102 {
Chris@416 103 Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable |
Chris@416 104 Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable;
Chris@416 105 return flags;
Chris@413 106 }
Chris@413 107
Chris@413 108 QVariant
Chris@413 109 ModelDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const
Chris@413 110 {
Chris@454 111 if (!m_model) return QVariant();
Chris@454 112
Chris@425 113 if (orientation == Qt::Vertical && role == Qt::DisplayRole) {
Chris@425 114 return section + 1;
Chris@425 115 }
Chris@413 116 if (orientation == Qt::Horizontal && role == Qt::DisplayRole) {
Chris@420 117 return m_model->getHeading(section);
Chris@425 118 }
Chris@413 119 return QVariant();
Chris@413 120 }
Chris@413 121
Chris@413 122 QModelIndex
Chris@929 123 ModelDataTableModel::index(int row, int column, const QModelIndex &) const
Chris@413 124 {
Chris@1582 125 return createIndex(row, column, (void *)nullptr);
Chris@413 126 }
Chris@413 127
Chris@413 128 QModelIndex
Chris@929 129 ModelDataTableModel::parent(const QModelIndex &) const
Chris@413 130 {
Chris@413 131 return QModelIndex();
Chris@413 132 }
Chris@413 133
Chris@413 134 int
Chris@413 135 ModelDataTableModel::rowCount(const QModelIndex &parent) const
Chris@413 136 {
Chris@454 137 if (!m_model) return 0;
Chris@413 138 if (parent.isValid()) return 0;
Chris@1455 139 int count = m_model->getRowCount();
Chris@1455 140 return count;
Chris@413 141 }
Chris@413 142
Chris@413 143 int
Chris@413 144 ModelDataTableModel::columnCount(const QModelIndex &parent) const
Chris@413 145 {
Chris@454 146 if (!m_model) return 0;
Chris@413 147 if (parent.isValid()) return 0;
Chris@420 148 return m_model->getColumnCount();
Chris@416 149 }
Chris@416 150
Chris@416 151 QModelIndex
Chris@1038 152 ModelDataTableModel::getModelIndexForFrame(sv_frame_t frame) const
Chris@416 153 {
Chris@454 154 if (!m_model) return createIndex(0, 0);
Chris@420 155 int row = m_model->getRowForFrame(frame);
Chris@1582 156 return createIndex(getSorted(row), 0, (void *)nullptr);
Chris@413 157 }
Chris@413 158
Chris@1038 159 sv_frame_t
Chris@419 160 ModelDataTableModel::getFrameForModelIndex(const QModelIndex &index) const
Chris@419 161 {
Chris@454 162 if (!m_model) return 0;
Chris@420 163 return m_model->getFrameForRow(getUnsorted(index.row()));
Chris@420 164 }
Chris@420 165
Chris@618 166 QModelIndex
Chris@618 167 ModelDataTableModel::findText(QString text) const
Chris@618 168 {
Chris@618 169 if (text == "") return QModelIndex();
Chris@618 170 int rows = rowCount();
Chris@618 171 int cols = columnCount();
Chris@618 172 int current = getCurrentRow();
Chris@618 173 for (int row = 1; row <= rows; ++row) {
Chris@618 174 int wrapped = (row + current) % rows;
Chris@618 175 for (int col = 0; col < cols; ++col) {
Chris@618 176 if (m_model->getSortType(col) != TabularModel::SortAlphabetical) {
Chris@618 177 continue;
Chris@618 178 }
Chris@618 179 QString cell = m_model->getData(getUnsorted(wrapped), col,
Chris@618 180 Qt::DisplayRole).toString();
Chris@618 181 if (cell.contains(text, Qt::CaseInsensitive)) {
Chris@618 182 return createIndex(wrapped, col);
Chris@618 183 }
Chris@618 184 }
Chris@618 185 }
Chris@618 186 return QModelIndex();
Chris@618 187 }
Chris@618 188
Chris@420 189 void
Chris@420 190 ModelDataTableModel::sort(int column, Qt::SortOrder sortOrder)
Chris@420 191 {
Chris@690 192 // SVDEBUG << "ModelDataTableModel::sort(" << column << ", " << sortOrder
Chris@687 193 // << ")" << endl;
Chris@428 194 int prevCurrent = getCurrentRow();
Chris@422 195 if (m_sortColumn != column) {
Chris@428 196 clearSort();
Chris@422 197 }
Chris@420 198 m_sortColumn = column;
Chris@420 199 m_sortOrdering = sortOrder;
Chris@428 200 int current = getCurrentRow();
Chris@428 201 if (current != prevCurrent) {
Chris@843 202 // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl;
Chris@1582 203 emit currentChanged(createIndex(current, 0, (void *)nullptr));
Chris@428 204 }
Chris@420 205 emit layoutChanged();
Chris@419 206 }
Chris@419 207
Chris@413 208 void
Chris@413 209 ModelDataTableModel::modelChanged()
Chris@413 210 {
Chris@1455 211 SVDEBUG << "ModelDataTableModel::modelChanged" << endl;
Chris@1455 212 QModelIndex ix0;
Chris@1455 213 QModelIndex ix1;
Chris@1455 214 if (rowCount() > 0) {
Chris@1455 215 ix0 = createIndex(0, 0);
Chris@1455 216 int lastCol = columnCount() - 1;
Chris@1455 217 if (lastCol < 0) lastCol = 0;
Chris@1455 218 ix1 = createIndex(rowCount(), lastCol);
Chris@1455 219 }
Chris@1455 220 SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl;
Chris@1455 221 emit dataChanged(ix0, ix1);
Chris@428 222 clearSort();
Chris@413 223 emit layoutChanged();
Chris@413 224 }
Chris@413 225
Chris@413 226 void
Chris@1455 227 ModelDataTableModel::modelChangedWithin(sv_frame_t f0, sv_frame_t f1)
Chris@416 228 {
Chris@1455 229 SVDEBUG << "ModelDataTableModel::modelChangedWithin(" << f0 << "," << f1 << ")" << endl;
Chris@1455 230 QModelIndex ix0 = getModelIndexForFrame(f0);
Chris@1455 231 QModelIndex ix1 = getModelIndexForFrame(f1);
Chris@1455 232 int row0 = ix0.row();
Chris@1455 233 int row1 = ix1.row();
Chris@1455 234 if (row0 > 0) {
Chris@1582 235 ix0 = createIndex(row0 - 1, ix0.column(), (void *)nullptr);
Chris@1455 236 }
Chris@1455 237 if (row1 + 1 < rowCount()) {
Chris@1582 238 ix1 = createIndex(row1 + 1, ix1.column(), (void *)nullptr);
Chris@1455 239 }
Chris@1455 240 SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl;
Chris@1455 241 emit dataChanged(ix0, ix1);
Chris@428 242 clearSort();
Chris@416 243 emit layoutChanged();
Chris@416 244 }
Chris@413 245
Chris@454 246 void
Chris@454 247 ModelDataTableModel::modelAboutToBeDeleted()
Chris@454 248 {
Chris@1582 249 m_model = nullptr;
Chris@454 250 emit modelRemoved();
Chris@454 251 }
Chris@454 252
Chris@420 253 int
Chris@426 254 ModelDataTableModel::getSorted(int row) const
Chris@413 255 {
Chris@454 256 if (!m_model) return row;
Chris@454 257
Chris@420 258 if (m_model->isColumnTimeValue(m_sortColumn)) {
Chris@420 259 if (m_sortOrdering == Qt::AscendingOrder) {
Chris@420 260 return row;
Chris@420 261 } else {
Chris@420 262 return rowCount() - row - 1;
Chris@420 263 }
Chris@420 264 }
Chris@413 265
Chris@420 266 if (m_sort.empty()) {
Chris@420 267 resort();
Chris@413 268 }
Chris@422 269 int result = 0;
Chris@929 270 if (row >= 0 && row < (int)m_sort.size()) {
Chris@422 271 result = m_sort[row];
Chris@422 272 }
Chris@422 273 if (m_sortOrdering == Qt::DescendingOrder) {
Chris@422 274 result = rowCount() - result - 1;
Chris@422 275 }
Chris@422 276
Chris@422 277 return result;
Chris@413 278 }
Chris@413 279
Chris@420 280 int
Chris@426 281 ModelDataTableModel::getUnsorted(int row) const
Chris@413 282 {
Chris@454 283 if (!m_model) return row;
Chris@454 284
Chris@420 285 if (m_model->isColumnTimeValue(m_sortColumn)) {
Chris@420 286 if (m_sortOrdering == Qt::AscendingOrder) {
Chris@420 287 return row;
Chris@420 288 } else {
Chris@420 289 return rowCount() - row - 1;
Chris@420 290 }
Chris@413 291 }
Chris@422 292
Chris@420 293 if (m_sort.empty()) {
Chris@420 294 resort();
Chris@420 295 }
Chris@422 296
Chris@422 297 int result = 0;
Chris@929 298 if (row >= 0 && row < (int)m_sort.size()) {
Chris@422 299 if (m_sortOrdering == Qt::AscendingOrder) {
Chris@422 300 result = m_rsort[row];
Chris@422 301 } else {
Chris@422 302 result = m_rsort[rowCount() - row - 1];
Chris@422 303 }
Chris@422 304 }
Chris@422 305
Chris@422 306 return result;
Chris@413 307 }
Chris@413 308
Chris@420 309 void
Chris@426 310 ModelDataTableModel::resort() const
Chris@413 311 {
Chris@454 312 if (!m_model) return;
Chris@454 313
Chris@422 314 bool numeric = (m_model->getSortType(m_sortColumn) ==
Chris@422 315 TabularModel::SortNumeric);
Chris@422 316
Chris@843 317 // cerr << "resort: numeric == " << numeric << endl;
Chris@618 318
Chris@422 319 m_sort.clear();
Chris@422 320 m_rsort.clear();
Chris@422 321
Chris@422 322 if (numeric) resortNumeric();
Chris@422 323 else resortAlphabetical();
Chris@422 324
Chris@422 325 std::map<int, int> tmp;
Chris@422 326
Chris@422 327 // rsort maps from sorted row number to original row number
Chris@422 328
Chris@929 329 for (int i = 0; i < (int)m_rsort.size(); ++i) {
Chris@422 330 tmp[m_rsort[i]] = i;
Chris@422 331 }
Chris@422 332
Chris@422 333 // tmp now maps from original row number to sorted row number
Chris@422 334
Chris@422 335 for (std::map<int, int>::const_iterator i = tmp.begin(); i != tmp.end(); ++i) {
Chris@422 336 m_sort.push_back(i->second);
Chris@422 337 }
Chris@422 338
Chris@422 339 // and sort now maps from original row number to sorted row number
Chris@413 340 }
Chris@413 341
Chris@422 342 void
Chris@426 343 ModelDataTableModel::resortNumeric() const
Chris@422 344 {
Chris@454 345 if (!m_model) return;
Chris@454 346
Chris@422 347 typedef std::multimap<double, int> MapType;
Chris@422 348
Chris@422 349 MapType rowMap;
Chris@422 350 int rows = m_model->getRowCount();
Chris@422 351
Chris@422 352 for (int i = 0; i < rows; ++i) {
Chris@424 353 QVariant value = m_model->getData(i, m_sortColumn, TabularModel::SortRole);
Chris@422 354 rowMap.insert(MapType::value_type(value.toDouble(), i));
Chris@422 355 }
Chris@422 356
Chris@422 357 for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) {
Chris@843 358 // cerr << "resortNumeric: " << i->second << ": " << i->first << endl;
Chris@422 359 m_rsort.push_back(i->second);
Chris@422 360 }
Chris@422 361
Chris@422 362 // rsort now maps from sorted row number to original row number
Chris@422 363 }
Chris@422 364
Chris@422 365 void
Chris@426 366 ModelDataTableModel::resortAlphabetical() const
Chris@422 367 {
Chris@454 368 if (!m_model) return;
Chris@454 369
Chris@422 370 typedef std::multimap<QString, int> MapType;
Chris@422 371
Chris@422 372 MapType rowMap;
Chris@422 373 int rows = m_model->getRowCount();
Chris@422 374
Chris@422 375 for (int i = 0; i < rows; ++i) {
Chris@422 376 QVariant value =
Chris@422 377 m_model->getData(i, m_sortColumn, TabularModel::SortRole);
Chris@422 378 rowMap.insert(MapType::value_type(value.toString(), i));
Chris@422 379 }
Chris@422 380
Chris@422 381 for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) {
Chris@843 382 // cerr << "resortAlphabetical: " << i->second << ": " << i->first << endl;
Chris@422 383 m_rsort.push_back(i->second);
Chris@422 384 }
Chris@422 385
Chris@422 386 // rsort now maps from sorted row number to original row number
Chris@422 387 }
Chris@422 388
Chris@428 389 int
Chris@618 390 ModelDataTableModel::getCurrentRow() const
Chris@428 391 {
Chris@428 392 return getSorted(m_currentRow);
Chris@428 393 }
Chris@428 394
Chris@428 395 void
Chris@428 396 ModelDataTableModel::setCurrentRow(int row)
Chris@428 397 {
Chris@428 398 m_currentRow = getUnsorted(row);
Chris@428 399 }
Chris@428 400
Chris@428 401 void
Chris@428 402 ModelDataTableModel::clearSort()
Chris@428 403 {
Chris@428 404 // int prevCurrent = getCurrentRow();
Chris@428 405 m_sort.clear();
Chris@428 406 // int current = getCurrentRow(); //!!! no -- not until the sort criteria have changed
Chris@428 407 // if (current != prevCurrent) {
Chris@843 408 // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl;
Chris@428 409 // emit currentRowChanged(createIndex(current, 0, 0));
Chris@428 410 // }
Chris@428 411 }
Chris@428 412
Chris@428 413