annotate data/model/ModelDataTableModel.cpp @ 1761:ee7fd2c01d87 by-id

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