Chris@413: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@413: Chris@413: /* Chris@413: Sonic Visualiser Chris@413: An audio file viewer and annotation editor. Chris@413: Centre for Digital Music, Queen Mary, University of London. Chris@413: This file copyright 2008 QMUL. Chris@413: Chris@413: This program is free software; you can redistribute it and/or Chris@413: modify it under the terms of the GNU General Public License as Chris@413: published by the Free Software Foundation; either version 2 of the Chris@413: License, or (at your option) any later version. See the file Chris@413: COPYING included with this distribution for more information. Chris@413: */ Chris@413: Chris@413: #include "ModelDataTableModel.h" Chris@413: Chris@420: #include "TabularModel.h" Chris@420: #include "Model.h" Chris@413: Chris@422: #include Chris@417: #include Chris@420: #include Chris@417: Chris@1748: ModelDataTableModel::ModelDataTableModel(ModelId m) : Chris@421: m_model(m), Chris@421: m_sortColumn(0), Chris@428: m_sortOrdering(Qt::AscendingOrder), Chris@428: m_currentRow(0) Chris@413: { Chris@1748: auto model = ModelById::get(m); Chris@1748: if (model) { Chris@1770: connect(model.get(), SIGNAL(modelChanged(ModelId)), Chris@1770: this, SLOT(modelChanged(ModelId))); Chris@1770: connect(model.get(), SIGNAL(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t)), Chris@1770: this, SLOT(modelChangedWithin(ModelId, sv_frame_t, sv_frame_t))); Chris@1748: } Chris@413: } Chris@413: Chris@413: ModelDataTableModel::~ModelDataTableModel() Chris@413: { Chris@413: } Chris@413: Chris@413: QVariant Chris@413: ModelDataTableModel::data(const QModelIndex &index, int role) const Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return QVariant(); Chris@424: if (role != Qt::EditRole && role != Qt::DisplayRole) return QVariant(); Chris@413: if (!index.isValid()) return QVariant(); Chris@1748: QVariant d = model->getData(getUnsorted(index.row()), index.column(), role); Chris@1254: return d; Chris@413: } Chris@413: Chris@413: bool Chris@413: ModelDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role) Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return false; Chris@420: if (!index.isValid()) return false; Chris@1748: Command *command = model->getSetDataCommand(getUnsorted(index.row()), Chris@421: index.column(), Chris@421: value, role); Chris@420: if (command) { Chris@427: emit addCommand(command); Chris@420: return true; Chris@420: } else { Chris@416: return false; Chris@416: } Chris@413: } Chris@413: Chris@427: bool Chris@427: ModelDataTableModel::insertRow(int row, const QModelIndex &parent) Chris@427: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return false; Chris@427: if (parent.isValid()) return false; Chris@427: Chris@1748: Command *command = model->getInsertRowCommand(getUnsorted(row)); Chris@427: Chris@427: if (command) { Chris@427: emit addCommand(command); Chris@427: } Chris@427: Chris@427: return (command ? true : false); Chris@427: } Chris@427: Chris@427: bool Chris@427: ModelDataTableModel::removeRow(int row, const QModelIndex &parent) Chris@427: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return false; Chris@427: if (parent.isValid()) return false; Chris@427: Chris@1748: Command *command = model->getRemoveRowCommand(getUnsorted(row)); Chris@427: Chris@427: if (command) { Chris@427: emit addCommand(command); Chris@427: } Chris@427: Chris@427: return (command ? true : false); Chris@427: } Chris@427: Chris@413: Qt::ItemFlags Chris@929: ModelDataTableModel::flags(const QModelIndex &) const Chris@413: { Chris@416: Qt::ItemFlags flags = Qt::ItemIsEnabled | Qt::ItemIsEditable | Chris@416: Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsSelectable; Chris@416: return flags; Chris@413: } Chris@413: Chris@413: QVariant Chris@413: ModelDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return QVariant(); Chris@454: Chris@425: if (orientation == Qt::Vertical && role == Qt::DisplayRole) { Chris@425: return section + 1; Chris@425: } Chris@413: if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { Chris@1748: return model->getHeading(section); Chris@425: } Chris@413: return QVariant(); Chris@413: } Chris@413: Chris@413: QModelIndex Chris@929: ModelDataTableModel::index(int row, int column, const QModelIndex &) const Chris@413: { Chris@1582: return createIndex(row, column, (void *)nullptr); Chris@413: } Chris@413: Chris@413: QModelIndex Chris@929: ModelDataTableModel::parent(const QModelIndex &) const Chris@413: { Chris@413: return QModelIndex(); Chris@413: } Chris@413: Chris@413: int Chris@413: ModelDataTableModel::rowCount(const QModelIndex &parent) const Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return 0; Chris@413: if (parent.isValid()) return 0; Chris@1748: int count = model->getRowCount(); Chris@1455: return count; Chris@413: } Chris@413: Chris@413: int Chris@413: ModelDataTableModel::columnCount(const QModelIndex &parent) const Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return 0; Chris@413: if (parent.isValid()) return 0; Chris@1748: return model->getColumnCount(); Chris@416: } Chris@416: Chris@416: QModelIndex Chris@1038: ModelDataTableModel::getModelIndexForFrame(sv_frame_t frame) const Chris@416: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return createIndex(0, 0); Chris@1748: int row = model->getRowForFrame(frame); Chris@1582: return createIndex(getSorted(row), 0, (void *)nullptr); Chris@413: } Chris@413: Chris@1038: sv_frame_t Chris@419: ModelDataTableModel::getFrameForModelIndex(const QModelIndex &index) const Chris@419: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return 0; Chris@1748: return model->getFrameForRow(getUnsorted(index.row())); Chris@420: } Chris@420: Chris@618: QModelIndex Chris@618: ModelDataTableModel::findText(QString text) const Chris@618: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return QModelIndex(); Chris@618: if (text == "") return QModelIndex(); Chris@618: int rows = rowCount(); Chris@618: int cols = columnCount(); Chris@618: int current = getCurrentRow(); Chris@618: for (int row = 1; row <= rows; ++row) { Chris@618: int wrapped = (row + current) % rows; Chris@618: for (int col = 0; col < cols; ++col) { Chris@1748: if (model->getSortType(col) != TabularModel::SortAlphabetical) { Chris@618: continue; Chris@618: } Chris@1748: QString cell = model->getData(getUnsorted(wrapped), col, Chris@618: Qt::DisplayRole).toString(); Chris@618: if (cell.contains(text, Qt::CaseInsensitive)) { Chris@618: return createIndex(wrapped, col); Chris@618: } Chris@618: } Chris@618: } Chris@618: return QModelIndex(); Chris@618: } Chris@618: Chris@420: void Chris@420: ModelDataTableModel::sort(int column, Qt::SortOrder sortOrder) Chris@420: { Chris@690: // SVDEBUG << "ModelDataTableModel::sort(" << column << ", " << sortOrder Chris@687: // << ")" << endl; Chris@428: int prevCurrent = getCurrentRow(); Chris@422: if (m_sortColumn != column) { Chris@428: clearSort(); Chris@422: } Chris@420: m_sortColumn = column; Chris@420: m_sortOrdering = sortOrder; Chris@428: int current = getCurrentRow(); Chris@428: if (current != prevCurrent) { Chris@843: // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl; Chris@1582: emit currentChanged(createIndex(current, 0, (void *)nullptr)); Chris@428: } Chris@420: emit layoutChanged(); Chris@419: } Chris@419: Chris@413: void Chris@1770: ModelDataTableModel::modelChanged(ModelId) Chris@413: { Chris@1455: SVDEBUG << "ModelDataTableModel::modelChanged" << endl; Chris@1455: QModelIndex ix0; Chris@1455: QModelIndex ix1; Chris@1455: if (rowCount() > 0) { Chris@1455: ix0 = createIndex(0, 0); Chris@1455: int lastCol = columnCount() - 1; Chris@1455: if (lastCol < 0) lastCol = 0; Chris@1455: ix1 = createIndex(rowCount(), lastCol); Chris@1455: } Chris@1455: SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl; Chris@1455: emit dataChanged(ix0, ix1); Chris@428: clearSort(); Chris@413: emit layoutChanged(); Chris@413: } Chris@413: Chris@413: void Chris@1770: ModelDataTableModel::modelChangedWithin(ModelId, sv_frame_t f0, sv_frame_t f1) Chris@416: { Chris@1455: SVDEBUG << "ModelDataTableModel::modelChangedWithin(" << f0 << "," << f1 << ")" << endl; Chris@1455: QModelIndex ix0 = getModelIndexForFrame(f0); Chris@1455: QModelIndex ix1 = getModelIndexForFrame(f1); Chris@1455: int row0 = ix0.row(); Chris@1455: int row1 = ix1.row(); Chris@1455: if (row0 > 0) { Chris@1582: ix0 = createIndex(row0 - 1, ix0.column(), (void *)nullptr); Chris@1455: } Chris@1455: if (row1 + 1 < rowCount()) { Chris@1582: ix1 = createIndex(row1 + 1, ix1.column(), (void *)nullptr); Chris@1455: } Chris@1455: SVDEBUG << "emitting dataChanged from row " << ix0.row() << " to " << ix1.row() << endl; Chris@1455: emit dataChanged(ix0, ix1); Chris@428: clearSort(); Chris@416: emit layoutChanged(); Chris@416: } Chris@413: Chris@420: int Chris@426: ModelDataTableModel::getSorted(int row) const Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return row; Chris@454: Chris@1748: if (model->isColumnTimeValue(m_sortColumn)) { Chris@420: if (m_sortOrdering == Qt::AscendingOrder) { Chris@420: return row; Chris@420: } else { Chris@420: return rowCount() - row - 1; Chris@420: } Chris@420: } Chris@413: Chris@420: if (m_sort.empty()) { Chris@420: resort(); Chris@413: } Chris@422: int result = 0; Chris@929: if (row >= 0 && row < (int)m_sort.size()) { Chris@422: result = m_sort[row]; Chris@422: } Chris@422: if (m_sortOrdering == Qt::DescendingOrder) { Chris@422: result = rowCount() - result - 1; Chris@422: } Chris@422: Chris@422: return result; Chris@413: } Chris@413: Chris@420: int Chris@426: ModelDataTableModel::getUnsorted(int row) const Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return row; Chris@454: Chris@1748: if (model->isColumnTimeValue(m_sortColumn)) { Chris@420: if (m_sortOrdering == Qt::AscendingOrder) { Chris@420: return row; Chris@420: } else { Chris@420: return rowCount() - row - 1; Chris@420: } Chris@413: } Chris@422: Chris@420: if (m_sort.empty()) { Chris@420: resort(); Chris@420: } Chris@422: Chris@422: int result = 0; Chris@929: if (row >= 0 && row < (int)m_sort.size()) { Chris@422: if (m_sortOrdering == Qt::AscendingOrder) { Chris@422: result = m_rsort[row]; Chris@422: } else { Chris@422: result = m_rsort[rowCount() - row - 1]; Chris@422: } Chris@422: } Chris@422: Chris@422: return result; Chris@413: } Chris@413: Chris@420: void Chris@426: ModelDataTableModel::resort() const Chris@413: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return; Chris@454: Chris@1748: bool numeric = (model->getSortType(m_sortColumn) == Chris@422: TabularModel::SortNumeric); Chris@422: Chris@843: // cerr << "resort: numeric == " << numeric << endl; Chris@618: Chris@422: m_sort.clear(); Chris@422: m_rsort.clear(); Chris@422: Chris@422: if (numeric) resortNumeric(); Chris@422: else resortAlphabetical(); Chris@422: Chris@422: std::map tmp; Chris@422: Chris@422: // rsort maps from sorted row number to original row number Chris@422: Chris@929: for (int i = 0; i < (int)m_rsort.size(); ++i) { Chris@422: tmp[m_rsort[i]] = i; Chris@422: } Chris@422: Chris@422: // tmp now maps from original row number to sorted row number Chris@422: Chris@422: for (std::map::const_iterator i = tmp.begin(); i != tmp.end(); ++i) { Chris@422: m_sort.push_back(i->second); Chris@422: } Chris@422: Chris@422: // and sort now maps from original row number to sorted row number Chris@413: } Chris@413: Chris@422: void Chris@426: ModelDataTableModel::resortNumeric() const Chris@422: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return; Chris@454: Chris@422: typedef std::multimap MapType; Chris@422: Chris@422: MapType rowMap; Chris@1748: int rows = model->getRowCount(); Chris@422: Chris@422: for (int i = 0; i < rows; ++i) { Chris@1748: QVariant value = model->getData(i, m_sortColumn, TabularModel::SortRole); Chris@422: rowMap.insert(MapType::value_type(value.toDouble(), i)); Chris@422: } Chris@422: Chris@422: for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) { Chris@843: // cerr << "resortNumeric: " << i->second << ": " << i->first << endl; Chris@422: m_rsort.push_back(i->second); Chris@422: } Chris@422: Chris@422: // rsort now maps from sorted row number to original row number Chris@422: } Chris@422: Chris@422: void Chris@426: ModelDataTableModel::resortAlphabetical() const Chris@422: { Chris@1748: auto model = getTabularModel(); Chris@1748: if (!model) return; Chris@454: Chris@422: typedef std::multimap MapType; Chris@422: Chris@422: MapType rowMap; Chris@1748: int rows = model->getRowCount(); Chris@422: Chris@422: for (int i = 0; i < rows; ++i) { Chris@422: QVariant value = Chris@1748: model->getData(i, m_sortColumn, TabularModel::SortRole); Chris@422: rowMap.insert(MapType::value_type(value.toString(), i)); Chris@422: } Chris@422: Chris@422: for (MapType::iterator i = rowMap.begin(); i != rowMap.end(); ++i) { Chris@843: // cerr << "resortAlphabetical: " << i->second << ": " << i->first << endl; Chris@422: m_rsort.push_back(i->second); Chris@422: } Chris@422: Chris@422: // rsort now maps from sorted row number to original row number Chris@422: } Chris@422: Chris@428: int Chris@618: ModelDataTableModel::getCurrentRow() const Chris@428: { Chris@428: return getSorted(m_currentRow); Chris@428: } Chris@428: Chris@428: void Chris@428: ModelDataTableModel::setCurrentRow(int row) Chris@428: { Chris@428: m_currentRow = getUnsorted(row); Chris@428: } Chris@428: Chris@428: void Chris@428: ModelDataTableModel::clearSort() Chris@428: { Chris@428: // int prevCurrent = getCurrentRow(); Chris@428: m_sort.clear(); Chris@428: // int current = getCurrentRow(); //!!! no -- not until the sort criteria have changed Chris@428: // if (current != prevCurrent) { Chris@843: // cerr << "Current row changed from " << prevCurrent << " to " << current << " for underlying row " << m_currentRow << endl; Chris@428: // emit currentRowChanged(createIndex(current, 0, 0)); Chris@428: // } Chris@428: } Chris@428: Chris@428: