| 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@420 | 34     connect(baseModel, SIGNAL(modelChanged(size_t, size_t)), | 
| Chris@413 | 35             this, SLOT(modelChanged(size_t, size_t))); | 
| Chris@413 | 36 } | 
| Chris@413 | 37 | 
| Chris@413 | 38 ModelDataTableModel::~ModelDataTableModel() | 
| Chris@413 | 39 { | 
| Chris@413 | 40 } | 
| Chris@413 | 41 | 
| Chris@413 | 42 QVariant | 
| Chris@413 | 43 ModelDataTableModel::data(const QModelIndex &index, int role) const | 
| Chris@413 | 44 { | 
| Chris@424 | 45     if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant(); | 
| Chris@413 | 46     if (!index.isValid()) return QVariant(); | 
| Chris@420 | 47     return m_model->getData(getUnsorted(index.row()), index.column(), role); | 
| Chris@413 | 48 } | 
| Chris@413 | 49 | 
| Chris@413 | 50 bool | 
| Chris@413 | 51 ModelDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role) | 
| Chris@413 | 52 { | 
| Chris@420 | 53     if (!index.isValid()) return false; | 
| Chris@421 | 54     Command *command = m_model->getSetDataCommand(getUnsorted(index.row()), | 
| Chris@421 | 55                                                   index.column(), | 
| Chris@421 | 56                                                   value, role); | 
| Chris@420 | 57     if (command) { | 
| Chris@427 | 58         emit addCommand(command); | 
| Chris@420 | 59         return true; | 
| Chris@420 | 60     } else { | 
| Chris@416 | 61         return false; | 
| Chris@416 | 62     } | 
| Chris@413 | 63 } | 
| Chris@413 | 64 | 
| Chris@427 | 65 bool | 
| Chris@427 | 66 ModelDataTableModel::insertRow(int row, const QModelIndex &parent) | 
| Chris@427 | 67 { | 
| Chris@427 | 68     if (parent.isValid()) return false; | 
| Chris@427 | 69 | 
| Chris@427 | 70     emit beginInsertRows(parent, row, row); | 
| Chris@427 | 71 | 
| Chris@427 | 72     Command *command = m_model->getInsertRowCommand(getUnsorted(row)); | 
| Chris@427 | 73 | 
| Chris@427 | 74     if (command) { | 
| Chris@427 | 75         emit addCommand(command); | 
| Chris@427 | 76     } | 
| Chris@427 | 77 | 
| Chris@427 | 78     emit endInsertRows(); | 
| Chris@427 | 79 | 
| Chris@427 | 80     return (command ? true : false); | 
| Chris@427 | 81 } | 
| Chris@427 | 82 | 
| Chris@427 | 83 bool | 
| Chris@427 | 84 ModelDataTableModel::removeRow(int row, const QModelIndex &parent) | 
| Chris@427 | 85 { | 
| Chris@427 | 86     if (parent.isValid()) return false; | 
| Chris@427 | 87 | 
| Chris@427 | 88     emit beginRemoveRows(parent, row, row); | 
| Chris@427 | 89 | 
| Chris@427 | 90     Command *command = m_model->getRemoveRowCommand(getUnsorted(row)); | 
| Chris@427 | 91 | 
| Chris@427 | 92     if (command) { | 
| Chris@427 | 93         emit addCommand(command); | 
| Chris@427 | 94     } | 
| Chris@427 | 95 | 
| Chris@427 | 96     emit endRemoveRows(); | 
| Chris@427 | 97 | 
| Chris@427 | 98     return (command ? true : false); | 
| Chris@427 | 99 } | 
| Chris@427 | 100 | 
| Chris@413 | 101 Qt::ItemFlags | 
| Chris@413 | 102 ModelDataTableModel::flags(const QModelIndex &index) const | 
| Chris@413 | 103 { | 
| Chris@416 | 104     Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable | | 
| Chris@416 | 105         Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; | 
| Chris@416 | 106     return flags; | 
| Chris@413 | 107 } | 
| Chris@413 | 108 | 
| Chris@413 | 109 QVariant | 
| Chris@413 | 110 ModelDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const | 
| Chris@413 | 111 { | 
| Chris@425 | 112     if (orientation == Qt::Vertical && role == Qt::DisplayRole) { | 
| Chris@425 | 113         return section + 1; | 
| Chris@425 | 114     } | 
| Chris@413 | 115     if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { | 
| Chris@420 | 116         return m_model->getHeading(section); | 
| Chris@425 | 117     } | 
| Chris@413 | 118     return QVariant(); | 
| Chris@413 | 119 } | 
| Chris@413 | 120 | 
| Chris@413 | 121 QModelIndex | 
| Chris@413 | 122 ModelDataTableModel::index(int row, int column, const QModelIndex &parent) const | 
| Chris@413 | 123 { | 
| Chris@413 | 124     return createIndex(row, column, 0); | 
| Chris@413 | 125 } | 
| Chris@413 | 126 | 
| Chris@413 | 127 QModelIndex | 
| Chris@413 | 128 ModelDataTableModel::parent(const QModelIndex &index) const | 
| Chris@413 | 129 { | 
| Chris@413 | 130     return QModelIndex(); | 
| Chris@413 | 131 } | 
| Chris@413 | 132 | 
| Chris@413 | 133 int | 
| Chris@413 | 134 ModelDataTableModel::rowCount(const QModelIndex &parent) const | 
| Chris@413 | 135 { | 
| Chris@413 | 136     if (parent.isValid()) return 0; | 
| Chris@420 | 137     return m_model->getRowCount(); | 
| Chris@413 | 138 } | 
| Chris@413 | 139 | 
| Chris@413 | 140 int | 
| Chris@413 | 141 ModelDataTableModel::columnCount(const QModelIndex &parent) const | 
| Chris@413 | 142 { | 
| Chris@413 | 143     if (parent.isValid()) return 0; | 
| Chris@420 | 144     return m_model->getColumnCount(); | 
| Chris@416 | 145 } | 
| Chris@416 | 146 | 
| Chris@416 | 147 QModelIndex | 
| Chris@416 | 148 ModelDataTableModel::getModelIndexForFrame(size_t frame) const | 
| Chris@416 | 149 { | 
| Chris@420 | 150     int row = m_model->getRowForFrame(frame); | 
| Chris@420 | 151     return createIndex(getSorted(row), 0, 0); | 
| Chris@413 | 152 } | 
| Chris@413 | 153 | 
| Chris@419 | 154 size_t | 
| Chris@419 | 155 ModelDataTableModel::getFrameForModelIndex(const QModelIndex &index) const | 
| Chris@419 | 156 { | 
| Chris@420 | 157     return m_model->getFrameForRow(getUnsorted(index.row())); | 
| Chris@420 | 158 } | 
| Chris@420 | 159 | 
| Chris@420 | 160 void | 
| Chris@420 | 161 ModelDataTableModel::sort(int column, Qt::SortOrder sortOrder) | 
| Chris@420 | 162 { | 
| Chris@437 | 163 //    std::cerr << "ModelDataTableModel::sort(" << column << ", " << sortOrder | 
| Chris@437 | 164 //              << ")" << std::endl; | 
| Chris@428 | 165     int prevCurrent = getCurrentRow(); | 
| Chris@422 | 166     if (m_sortColumn != column) { | 
| Chris@428 | 167         clearSort(); | 
| Chris@422 | 168     } | 
| Chris@420 | 169     m_sortColumn = column; | 
| Chris@420 | 170     m_sortOrdering = sortOrder; | 
| Chris@428 | 171     int current = getCurrentRow(); | 
| Chris@428 | 172     if (current != prevCurrent) { | 
| Chris@437 | 173 //         std::cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << std::endl; | 
| Chris@428 | 174          emit currentChanged(createIndex(current, 0, 0)); | 
| Chris@428 | 175     } | 
| Chris@420 | 176     emit layoutChanged(); | 
| Chris@419 | 177 } | 
| Chris@419 | 178 | 
| Chris@413 | 179 void | 
| Chris@413 | 180 ModelDataTableModel::modelChanged() | 
| Chris@413 | 181 { | 
| Chris@428 | 182     clearSort(); | 
| Chris@413 | 183     emit layoutChanged(); | 
| Chris@413 | 184 } | 
| Chris@413 | 185 | 
| Chris@413 | 186 void | 
| Chris@416 | 187 ModelDataTableModel::modelChanged(size_t f0, size_t f1) | 
| Chris@416 | 188 { | 
| Chris@420 | 189     //!!! inefficient | 
| Chris@428 | 190     clearSort(); | 
| Chris@416 | 191     emit layoutChanged(); | 
| Chris@416 | 192 } | 
| Chris@413 | 193 | 
| Chris@420 | 194 int | 
| Chris@426 | 195 ModelDataTableModel::getSorted(int row) const | 
| Chris@413 | 196 { | 
| Chris@420 | 197     if (m_model->isColumnTimeValue(m_sortColumn)) { | 
| Chris@420 | 198         if (m_sortOrdering == Qt::AscendingOrder) { | 
| Chris@420 | 199             return row; | 
| Chris@420 | 200         } else { | 
| Chris@420 | 201             return rowCount() - row - 1; | 
| Chris@420 | 202         } | 
| Chris@420 | 203     } | 
| Chris@413 | 204 | 
| Chris@420 | 205     if (m_sort.empty()) { | 
| Chris@420 | 206         resort(); | 
| Chris@413 | 207     } | 
| Chris@422 | 208     int result = 0; | 
| Chris@422 | 209     if (row >= 0 && row < m_sort.size()) { | 
| Chris@422 | 210         result = m_sort[row]; | 
| Chris@422 | 211     } | 
| Chris@422 | 212     if (m_sortOrdering == Qt::DescendingOrder) { | 
| Chris@422 | 213         result = rowCount() - result - 1; | 
| Chris@422 | 214     } | 
| Chris@422 | 215 | 
| Chris@422 | 216     return result; | 
| Chris@413 | 217 } | 
| Chris@413 | 218 | 
| Chris@420 | 219 int | 
| Chris@426 | 220 ModelDataTableModel::getUnsorted(int row) const | 
| Chris@413 | 221 { | 
| Chris@420 | 222     if (m_model->isColumnTimeValue(m_sortColumn)) { | 
| Chris@420 | 223         if (m_sortOrdering == Qt::AscendingOrder) { | 
| Chris@420 | 224             return row; | 
| Chris@420 | 225         } else { | 
| Chris@420 | 226             return rowCount() - row - 1; | 
| Chris@420 | 227         } | 
| Chris@413 | 228     } | 
| Chris@422 | 229 | 
| Chris@420 | 230     if (m_sort.empty()) { | 
| Chris@420 | 231         resort(); | 
| Chris@420 | 232     } | 
| Chris@422 | 233 | 
| Chris@422 | 234     int result = 0; | 
| Chris@422 | 235     if (row >= 0 && row < m_sort.size()) { | 
| Chris@422 | 236         if (m_sortOrdering == Qt::AscendingOrder) { | 
| Chris@422 | 237             result = m_rsort[row]; | 
| Chris@422 | 238         } else { | 
| Chris@422 | 239             result = m_rsort[rowCount() - row - 1]; | 
| Chris@422 | 240         } | 
| Chris@422 | 241     } | 
| Chris@422 | 242 | 
| Chris@422 | 243     return result; | 
| Chris@413 | 244 } | 
| Chris@413 | 245 | 
| Chris@420 | 246 void | 
| Chris@426 | 247 ModelDataTableModel::resort() const | 
| Chris@413 | 248 { | 
| Chris@422 | 249     bool numeric = (m_model->getSortType(m_sortColumn) == | 
| Chris@422 | 250                     TabularModel::SortNumeric); | 
| Chris@422 | 251 | 
| Chris@422 | 252     m_sort.clear(); | 
| Chris@422 | 253     m_rsort.clear(); | 
| Chris@422 | 254 | 
| Chris@422 | 255     if (numeric) resortNumeric(); | 
| Chris@422 | 256     else resortAlphabetical(); | 
| Chris@422 | 257 | 
| Chris@422 | 258     std::map<int, int> tmp; | 
| Chris@422 | 259 | 
| Chris@422 | 260     // rsort maps from sorted row number to original row number | 
| Chris@422 | 261 | 
| Chris@422 | 262     for (int i = 0; i < m_rsort.size(); ++i) { | 
| Chris@422 | 263         tmp[m_rsort[i]] = i; | 
| Chris@422 | 264     } | 
| Chris@422 | 265 | 
| Chris@422 | 266     // tmp now maps from original row number to sorted row number | 
| Chris@422 | 267 | 
| Chris@422 | 268     for (std::map<int, int>::const_iterator i = tmp.begin(); i != tmp.end(); ++i) { | 
| Chris@422 | 269         m_sort.push_back(i->second); | 
| Chris@422 | 270     } | 
| Chris@422 | 271 | 
| Chris@422 | 272     // and sort now maps from original row number to sorted row number | 
| Chris@413 | 273 } | 
| Chris@413 | 274 | 
| Chris@422 | 275 void | 
| Chris@426 | 276 ModelDataTableModel::resortNumeric() const | 
| Chris@422 | 277 { | 
| Chris@422 | 278     typedef std::multimap<double, int> MapType; | 
| Chris@422 | 279 | 
| Chris@422 | 280     MapType rowMap; | 
| Chris@422 | 281     int rows = m_model->getRowCount(); | 
| Chris@422 | 282 | 
| Chris@422 | 283     for (int i = 0; i < rows; ++i) { | 
| Chris@424 | 284         QVariant value = m_model->getData(i, m_sortColumn, TabularModel::SortRole); | 
| Chris@422 | 285         rowMap.insert(MapType::value_type(value.toDouble(), i)); | 
| Chris@422 | 286     } | 
| Chris@422 | 287 | 
| Chris@422 | 288     for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) { | 
| Chris@422 | 289         m_rsort.push_back(i->second); | 
| Chris@422 | 290     } | 
| Chris@422 | 291 | 
| Chris@422 | 292     // rsort now maps from sorted row number to original row number | 
| Chris@422 | 293 } | 
| Chris@422 | 294 | 
| Chris@422 | 295 void | 
| Chris@426 | 296 ModelDataTableModel::resortAlphabetical() const | 
| Chris@422 | 297 { | 
| Chris@422 | 298     typedef std::multimap<QString, int> MapType; | 
| Chris@422 | 299 | 
| Chris@422 | 300     MapType rowMap; | 
| Chris@422 | 301     int rows = m_model->getRowCount(); | 
| Chris@422 | 302 | 
| Chris@422 | 303     for (int i = 0; i < rows; ++i) { | 
| Chris@422 | 304         QVariant value = | 
| Chris@422 | 305             m_model->getData(i, m_sortColumn, TabularModel::SortRole); | 
| Chris@422 | 306         rowMap.insert(MapType::value_type(value.toString(), i)); | 
| Chris@422 | 307     } | 
| Chris@422 | 308 | 
| Chris@422 | 309     for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) { | 
| Chris@422 | 310         m_rsort.push_back(i->second); | 
| Chris@422 | 311     } | 
| Chris@422 | 312 | 
| Chris@422 | 313     // rsort now maps from sorted row number to original row number | 
| Chris@422 | 314 } | 
| Chris@422 | 315 | 
| Chris@428 | 316 int | 
| Chris@428 | 317 ModelDataTableModel::getCurrentRow() | 
| Chris@428 | 318 { | 
| Chris@428 | 319     return getSorted(m_currentRow); | 
| Chris@428 | 320 } | 
| Chris@428 | 321 | 
| Chris@428 | 322 void | 
| Chris@428 | 323 ModelDataTableModel::setCurrentRow(int row) | 
| Chris@428 | 324 { | 
| Chris@428 | 325     m_currentRow = getUnsorted(row); | 
| Chris@428 | 326 } | 
| Chris@428 | 327 | 
| Chris@428 | 328 void | 
| Chris@428 | 329 ModelDataTableModel::clearSort() | 
| Chris@428 | 330 { | 
| Chris@428 | 331 //    int prevCurrent = getCurrentRow(); | 
| Chris@428 | 332     m_sort.clear(); | 
| Chris@428 | 333 //    int current = getCurrentRow(); //!!! no --  not until the sort criteria have changed | 
| Chris@428 | 334 //    if (current != prevCurrent) { | 
| Chris@428 | 335 //        std::cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << std::endl; | 
| Chris@428 | 336 //        emit currentRowChanged(createIndex(current, 0, 0)); | 
| Chris@428 | 337 //    } | 
| Chris@428 | 338 } | 
| Chris@428 | 339 | 
| Chris@428 | 340 |