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@413: #include "SparseTimeValueModel.h" Chris@413: #include "SparseOneDimensionalModel.h" Chris@413: #include "SparseModel.h" Chris@413: Chris@417: #include Chris@417: Chris@413: ModelDataTableModel::ModelDataTableModel(Model *m) : Chris@413: m_model(m) Chris@413: { Chris@413: connect(m, SIGNAL(modelChanged()), this, SLOT(modelChanged())); Chris@413: connect(m, SIGNAL(modelChanged(size_t, size_t)), Chris@413: this, SLOT(modelChanged(size_t, size_t))); Chris@413: rebuildRowVector(); 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@416: if (role != Qt::DisplayRole && role != Qt::EditRole) { Chris@413: return QVariant(); Chris@413: } Chris@413: Chris@416: bool withUnit = (role == Qt::DisplayRole); Chris@416: Chris@413: if (!index.isValid()) return QVariant(); Chris@413: Chris@413: int row = index.row(), col = index.column(); Chris@413: Chris@413: if (row < 0 || row >= m_rows.size()) return QVariant(); Chris@413: Chris@413: if (dynamic_cast(m_model)) { Chris@416: return dataSparse(row, col, withUnit); Chris@413: } else if (dynamic_cast(m_model)) { Chris@416: return dataSparse(row, col, withUnit); Chris@413: } Chris@413: Chris@413: return QVariant(); Chris@413: } Chris@413: Chris@413: template Chris@413: QVariant Chris@416: ModelDataTableModel::dataSparse(int row, int col, bool withUnit) const Chris@413: { Chris@413: size_t frame = m_rows[row]; Chris@413: Chris@413: // This is just garbage. This would be a reasonable enough way to Chris@413: // handle this in a dynamically typed language but it's hopeless Chris@413: // in C++. The design is simply wrong. We need virtual helper Chris@413: // methods in the model itself. Chris@413: Chris@413: typedef SparseModel ModelType; Chris@413: typedef std::multiset Chris@413: PointListType; Chris@413: Chris@413: const ModelType *sm = dynamic_cast(m_model); Chris@413: const PointListType &points = sm->getPoints(frame); Chris@413: Chris@413: // it is possible to have more than one point at the same frame Chris@413: Chris@413: int indexAtFrame = 0; Chris@413: int ri = row; Chris@413: while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; } Chris@413: Chris@413: for (typename PointListType::const_iterator i = points.begin(); Chris@413: i != points.end(); ++i) { Chris@413: Chris@413: const PointType *point = &(*i); Chris@413: if (point->frame < frame) continue; Chris@413: if (point->frame > frame) return QVariant(); Chris@413: if (indexAtFrame > 0) { --indexAtFrame; continue; } Chris@413: Chris@413: switch (col) { Chris@413: Chris@413: case 0: Chris@416: { Chris@416: RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); Chris@416: std::cerr << "Returning time " << rt << std::endl; Chris@416: return QVariant(rt.toText().c_str()); Chris@416: } Chris@416: Chris@416: case 1: Chris@413: std::cerr << "Returning frame " << frame << std::endl; Chris@417: return QVariant(int(frame)); Chris@413: Chris@416: case 2: Chris@413: if (dynamic_cast(m_model)) { Chris@413: const SparseOneDimensionalModel::Point *cp = Chris@413: reinterpret_cast(point); Chris@413: std::cerr << "Returning label \"" << cp->label.toStdString() << "\"" << std::endl; Chris@413: return QVariant(cp->label); Chris@413: } else if (dynamic_cast(m_model)) { Chris@413: const SparseTimeValueModel::Point *cp = Chris@413: reinterpret_cast(point); Chris@413: std::cerr << "Returning value " << cp->value << std::endl; Chris@416: if (withUnit) { Chris@416: return QVariant(QString("%1 %2").arg(cp->value) Chris@416: .arg(dynamic_cast(m_model)->getScaleUnits())); Chris@416: } else { Chris@416: return cp->value; Chris@416: } Chris@413: } else return QVariant(); Chris@413: Chris@416: case 3: Chris@413: if (dynamic_cast(m_model)) { Chris@413: return QVariant(); Chris@413: } else if (dynamic_cast(m_model)) { Chris@413: return reinterpret_cast(point)->label; Chris@413: } else return QVariant(); Chris@413: } Chris@413: } Chris@413: Chris@413: return QVariant(); Chris@413: } Chris@413: Chris@413: bool Chris@413: ModelDataTableModel::setData(const QModelIndex &index, const QVariant &value, int role) Chris@413: { Chris@416: if (role != Qt::EditRole) { Chris@416: std::cerr << "setData: ignoring role " << role << std::endl; Chris@416: return false; Chris@416: } Chris@416: Chris@416: //!!! see comment about disgustuality of this whole process, in Chris@416: //dataSparse above Chris@416: Chris@416: if (!index.isValid()) return false; Chris@416: Chris@416: int row = index.row(), col = index.column(); Chris@416: Chris@416: if (row < 0 || row >= m_rows.size()) return false; Chris@416: Chris@416: if (dynamic_cast(m_model)) { Chris@416: return setDataSparse(row, col, value); Chris@416: } else if (dynamic_cast(m_model)) { Chris@416: return setDataSparse(row, col, value); Chris@416: } Chris@416: Chris@416: return false; Chris@416: } Chris@416: Chris@416: template Chris@416: bool Chris@416: ModelDataTableModel::setDataSparse(int row, int col, QVariant value) Chris@416: { Chris@416: size_t frame = m_rows[row]; Chris@416: Chris@416: typedef SparseModel ModelType; Chris@416: typedef std::multiset Chris@416: PointListType; Chris@416: typedef typename ModelType::EditCommand EditCommandType; Chris@416: Chris@417: ModelType *sm = dynamic_cast(m_model); Chris@416: const PointListType &points = sm->getPoints(frame); Chris@416: Chris@416: // it is possible to have more than one point at the same frame Chris@416: Chris@416: int indexAtFrame = 0; Chris@416: int ri = row; Chris@416: while (ri > 0 && m_rows[ri-1] == m_rows[row]) { --ri; ++indexAtFrame; } Chris@416: Chris@416: for (typename PointListType::const_iterator i = points.begin(); Chris@416: i != points.end(); ++i) { Chris@416: Chris@416: const PointType *point = &(*i); Chris@416: if (point->frame < frame) continue; Chris@416: if (point->frame > frame) return false; Chris@416: if (indexAtFrame > 0) { --indexAtFrame; continue; } Chris@416: Chris@416: switch (col) { Chris@416: Chris@416: case 0: Chris@416: { Chris@416: /* Chris@416: RealTime rt = RealTime::frame2RealTime(frame, m_model->getSampleRate()); Chris@416: std::cerr << "Returning time " << rt << std::endl; Chris@416: return QVariant(rt.toText().c_str()); Chris@416: */ Chris@416: } Chris@416: Chris@416: case 1: Chris@416: { Chris@416: EditCommandType *command = Chris@416: new EditCommandType(sm, tr("Edit point time")); Chris@416: PointType newPoint(*point); Chris@416: newPoint.frame = value.toInt(); //!!! check validity Chris@416: command->deletePoint(*point); Chris@416: command->addPoint(newPoint); Chris@416: command = command->finish(); Chris@416: if (command) emit executeCommand(command); Chris@416: return true; Chris@416: } Chris@416: // std::cerr << "Returning frame " << frame << std::endl; Chris@416: // return QVariant(frame); //!!! RealTime Chris@416: Chris@416: case 2: Chris@416: break; Chris@416: Chris@416: case 3: Chris@416: break; Chris@416: } Chris@416: } Chris@416: Chris@413: return false; Chris@413: } Chris@413: Chris@413: Qt::ItemFlags Chris@413: ModelDataTableModel::flags(const QModelIndex &index) 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@413: if (orientation == Qt::Horizontal && role == Qt::DisplayRole) { Chris@416: if (section == 0) return QVariant(tr("Time")); Chris@416: if (section == 1) return QVariant(tr("Frame")); Chris@416: else if (section == 2) { Chris@413: if (dynamic_cast(m_model)) { Chris@413: return QVariant(tr("Label")); Chris@413: } else if (dynamic_cast(m_model)) { Chris@413: return QVariant(tr("Value")); Chris@413: } Chris@416: } else if (section == 3) { Chris@413: if (dynamic_cast(m_model)) { Chris@413: return QVariant(tr("Label")); Chris@413: } Chris@413: } Chris@413: } Chris@413: return QVariant(); Chris@413: } Chris@413: Chris@413: QModelIndex Chris@413: ModelDataTableModel::index(int row, int column, const QModelIndex &parent) const Chris@413: { Chris@413: return createIndex(row, column, 0); Chris@413: } Chris@413: Chris@413: QModelIndex Chris@413: ModelDataTableModel::parent(const QModelIndex &index) 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@413: if (parent.isValid()) return 0; Chris@413: return m_rows.size(); Chris@413: } Chris@413: Chris@413: int Chris@413: ModelDataTableModel::columnCount(const QModelIndex &parent) const Chris@413: { Chris@413: if (parent.isValid()) return 0; Chris@413: if (!canHandleModelType(m_model)) return 0; Chris@413: Chris@413: if (dynamic_cast(m_model)) { Chris@416: return 3; Chris@413: } else if (dynamic_cast(m_model)) { Chris@416: return 4; Chris@413: } Chris@413: Chris@416: return 2; Chris@416: } Chris@416: Chris@416: QModelIndex Chris@416: ModelDataTableModel::getModelIndexForFrame(size_t frame) const Chris@416: { Chris@416: std::vector::const_iterator i = Chris@416: std::lower_bound(m_rows.begin(), m_rows.end(), frame); Chris@416: size_t dist = std::distance(m_rows.begin(), i); Chris@416: return createIndex(dist, 0, 0); Chris@413: } Chris@413: Chris@413: void Chris@413: ModelDataTableModel::modelChanged() Chris@413: { Chris@413: rebuildRowVector(); Chris@413: emit layoutChanged(); Chris@413: } Chris@413: Chris@413: void Chris@416: ModelDataTableModel::modelChanged(size_t f0, size_t f1) Chris@416: { Chris@416: std::cerr << "ModelDataTableModel::modelChanged(" << f0 << "," << f1 << ")" << std::endl; Chris@416: //!!! highly inefficient Chris@416: rebuildRowVector(); Chris@416: emit layoutChanged(); Chris@416: } Chris@413: Chris@413: void Chris@413: ModelDataTableModel::rebuildRowVector() Chris@413: { Chris@413: if (!canHandleModelType(m_model)) return; Chris@413: Chris@413: m_rows.clear(); Chris@413: Chris@413: if (dynamic_cast(m_model)) { Chris@413: rebuildRowVectorSparse(); Chris@413: } else if (dynamic_cast(m_model)) { Chris@413: rebuildRowVectorSparse(); Chris@413: } Chris@413: } Chris@413: Chris@413: template Chris@413: void Chris@413: ModelDataTableModel::rebuildRowVectorSparse() Chris@413: { Chris@413: // gah Chris@413: Chris@413: typedef SparseModel ModelType; Chris@413: typedef std::multiset Chris@413: PointListType; Chris@413: Chris@413: ModelType *sm = dynamic_cast(m_model); Chris@413: const PointListType &points = sm->getPoints(); Chris@413: Chris@413: for (typename PointListType::const_iterator i = points.begin(); Chris@413: i != points.end(); ++i) { Chris@413: m_rows.push_back(i->frame); Chris@413: } Chris@413: } Chris@413: Chris@413: bool Chris@413: ModelDataTableModel::canHandleModelType(Model *m) Chris@413: { Chris@413: if (dynamic_cast(m)) return true; Chris@413: if (dynamic_cast(m)) return true; Chris@413: return false; Chris@413: } Chris@413: Chris@413: