annotate data/model/ModelDataTableModel.cpp @ 1833:21c792334c2e sensible-delimited-data-strings

Rewrite all the DelimitedDataString stuff so as to return vectors of individual cell strings rather than having the classes add the delimiters themselves. Rename accordingly to names based on StringExport. Take advantage of this in the CSV writer code so as to properly quote cells that contain delimiter characters.
author Chris Cannam
date Fri, 03 Apr 2020 17:11:05 +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