/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /*************************************************************************** * filelist.cc * * Mon Nov 30 15:35:52 CET 2009 * Copyright 2009 Bent Bisballe Nyeng * deva@aasimon.org ****************************************************************************/ /* * This file is part of DrumGizmo. * * DrumGizmo is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * DrumGizmo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with DrumGizmo; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include "filelist.h" #include #include #include #include #include "itemeditor.h" #include "project.h" #include #include #include #include #include #include #include #include #include #include #include #include class ChannelMapDeligate : public QStyledItemDelegate { public: ChannelMapDeligate(Instrument& instrument) : instrument(instrument) { } QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { // Main channel: if(index.column() == 2) { auto w = new QCheckBox(parent); auto audiofile_ids = instrument.getAudioFileList(); auto audiofile_id = audiofile_ids.begin() + index.row(); auto& audiofile = instrument.getAudioFile(*audiofile_id); connect(w, &QCheckBox::stateChanged, [&audiofile](int state) { audiofile.setMainChannel(state == Qt::Checked); }); return w; } // Name: if(index.column() == 3) { auto w = new QLineEdit(parent); auto audiofile_ids = instrument.getAudioFileList(); auto audiofile_id = audiofile_ids.begin() + index.row(); auto& audiofile = instrument.getAudioFile(*audiofile_id); connect(w, &QLineEdit::textEdited, [&audiofile](const QString& name) { audiofile.setName(name); }); return w; } // Channel Map ID: if(index.column() == 4) { auto w = new QComboBox(parent); w->addItem(tr(""), -1); auto channel_ids = instrument.getProject().getChannelList(); for(auto channel_id : channel_ids) { const auto& channel = instrument.getProject().getChannel(channel_id); w->addItem(channel.getChannelName(), channel.getId()); } auto audiofile_ids = instrument.getAudioFileList(); auto audiofile_id = audiofile_ids.begin() + index.row(); auto& audiofile = instrument.getAudioFile(*audiofile_id); connect(w, // This wierd line points the compiler to the correct overloaded // version of QComboBox::currentIndexChanged // ie. chooses the (int) version over the (const QString&) version QOverload::of(&QComboBox::currentIndexChanged), [w, &audiofile](int idx) { audiofile.setChannelMapId(w->itemData(idx).toInt()); }); return w; } return QStyledItemDelegate::createEditor(parent, option, index); } void setEditorData(QWidget *editor, const QModelIndex &index) const override { // Main channel: if(index.column() == 2) { auto w = static_cast(editor); auto b = index.data(Qt::EditRole).toBool(); w->setCheckState(b ? Qt::Checked : Qt::Unchecked); } // Name: if(index.column() == 3) { auto w = static_cast(editor); auto s = index.data(Qt::EditRole).toString(); w->setText(s); } // Channel Map ID: if(index.column() == 4) { auto w = static_cast(editor); auto i = w->findData(index.data(Qt::EditRole).toInt()); w->setCurrentIndex(i); } } void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override { // Main channel: if(index.column() == 2) { model->setData(index, static_cast(editor)->checkState() == Qt::Checked, Qt::EditRole); } // Name: if(index.column() == 3) { model->setData(index, static_cast(editor)->text(), Qt::EditRole); } // Channel Map ID: if(index.column() == 4) { model->setData(index, static_cast(editor)->currentData(), Qt::EditRole); } } void paint(QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index) const override { if(index.column() == 2) // Main Channel { auto audiofile_ids = instrument.getAudioFileList(); auto audiofile_id = audiofile_ids.begin() + index.row(); const auto& audiofile = instrument.getAudioFile(*audiofile_id); bool checked = audiofile.getMainChannel(); QStyleOptionButton butOpt; butOpt.state = QStyle::State_Enabled; butOpt.state |= checked ? QStyle::State_On : QStyle::State_Off; butOpt.rect = option.rect; QApplication::style()->drawControl( QStyle::CE_CheckBox, &butOpt, painter ); } else { QStyledItemDelegate::paint(painter, option, index); } } private: Instrument& instrument; }; class FileDataModel : public QAbstractItemModel { public: FileDataModel(Instrument& instrument) : instrument(instrument) { } QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override { if(!hasIndex(row, column, parent)) { return QModelIndex(); } auto audiofile_ids = instrument.getAudioFileList(); if(!parent.isValid()) { if(row < audiofile_ids.size()) { return createIndex(row, column, &instrument); } else { return QModelIndex(); // row is out of bounds. } } return QModelIndex(); } QModelIndex parent(const QModelIndex &index) const override { return QModelIndex(); // no parent } int rowCount(const QModelIndex &parent = QModelIndex()) const override { if(parent.column() > 0) // only return row count on first column { return 0; } if(!parent.isValid()) { auto audiofile_ids = instrument.getAudioFileList(); return audiofile_ids.size(); // root level } return 0; // no children } int columnCount(const QModelIndex &parent = QModelIndex()) const override { return 5; } QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override { if(!index.isValid()) { return QVariant(); } auto audiofile_ids = instrument.getAudioFileList(); auto audiofile_id = audiofile_ids.begin() + index.row(); const auto& audiofile = instrument.getAudioFile(*audiofile_id); if(role == Qt::DecorationRole ) { if(index.column() == 0) { if(audiofile.getAbsoluteFile() == instrument.getMasterFile()) { return QPixmap(":/icons/master.png"); } else { return QPixmap(":/icons/file.png"); } } } if(role == Qt::DisplayRole) { switch(index.column()) { case 0: return QVariant(); // Master case 1: return audiofile.getFile(); case 2: return audiofile.getMainChannel() ? "x" : " "; case 3: return audiofile.getName(); case 4: { auto channelMapId = audiofile.getChannelMapId(); if(channelMapId == -1) { return tr(""); } return instrument.getProject().getChannel(channelMapId).getChannelName(); } default: return QVariant(); } } else if(role == Qt::EditRole) { switch(index.column()) { case 0: return QVariant(); // Master case 1: return audiofile.getFile(); case 2: return audiofile.getMainChannel(); case 3: return audiofile.getName(); case 4: return audiofile.getChannelMapId(); default: return QVariant(); } } else { return QVariant(); } } QVariant headerData(int section, Qt::Orientation orientation, int role) const override { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch(section) { case 0: return tr("M"); case 1: return tr("Filename"); case 2: return tr("m"); case 3: return tr("Name"); case 4: return tr("Kit Channel"); default: return QVariant(); } } return QVariant(); } Qt::ItemFlags flags(const QModelIndex &index) const override { if(!index.isValid()) { return 0; } switch(index.column()) { case 0: // Master return QAbstractItemModel::flags(index); case 1: // File return QAbstractItemModel::flags(index); case 2: // Main channel return Qt::ItemIsEditable | QAbstractItemModel::flags(index); case 3: // Name return Qt::ItemIsEditable | QAbstractItemModel::flags(index); case 4: // Channel map id return Qt::ItemIsEditable | QAbstractItemModel::flags(index); } } bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override { auto audiofile_ids = instrument.getAudioFileList(); if(index.row() > audiofile_ids.size() || index.column() > (columnCount() - 1)) { return false; } auto audiofile_id = audiofile_ids.begin() + index.row(); auto& audiofile = instrument.getAudioFile(*audiofile_id); switch(index.column()) { case 0: // Master break; case 1: // File break; case 2: // Main Channel audiofile.setMainChannel(value.toBool()); break; case 3: // Name audiofile.setName(value.toString()); break; case 4: // Channel map id audiofile.setChannelMapId(value.toInt()); break; default: break; } return true; } void refresh() { beginResetModel(); endResetModel(); } private: Instrument& instrument; }; FileList::FileList(Instrument& instrument) : instrument(instrument) { model = new FileDataModel(instrument); setModel(model); setItemDelegate(new ChannelMapDeligate(instrument)); setEditTriggers(QAbstractItemView::AllEditTriggers); // show list on click setContextMenuPolicy(Qt::CustomContextMenu); // enable context menu connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(onCustomContextMenu(const QPoint &))); setRootIsDecorated(false); header()->resizeSection(0, 24); header()->resizeSection(2, 24); connect(this, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(selectionChanged(const QModelIndex&))); createMenus(); } static double getSamplerate(const QString& file) { SF_INFO sf_info; SNDFILE *fh = sf_open(file.toStdString().c_str(), SFM_READ, &sf_info); if(!fh) { return -1.0; } return sf_info.samplerate; } void FileList::addFiles() { auto root = instrument.getProject().getRawFileRoot(); if(path.isEmpty()) { path = root; } QStringList files = QFileDialog::getOpenFileNames(this, tr("Open file"), path, tr("Audio Files (*.wav)")); QStringList::Iterator i = files.begin(); while(i != files.end()) { QString file = *i; QFileInfo fi(file); QString name = fi.baseName(); path = fi.absolutePath(); double samplerate = getSamplerate(file); if(samplerate == -1.0) { // Error reading file. Skip it. std::cout << "Error reading file. Skip it.\n"; ++i; continue; } if(instrument.getProject().getProjectSamplerate() == -1.0) { // Samplerate not yet set instrument.getProject().setProjectSamplerate(samplerate); } if(instrument.getProject().getProjectSamplerate() != samplerate) { // Samplerate of file differs from the projeft samplerate std::cout << "Samplerate of file differs from the project samplerate:\n"; std::cout << " project: " << instrument.getProject().getProjectSamplerate() << std::endl; std::cout << " file: " << samplerate << std::endl; } if(root == file.left(root.length())) { file = file.mid(root.length() + 1); } auto id = instrument.createAudioFile(); auto& audiofile = instrument.getAudioFile(id); audiofile.setName(name); audiofile.setFile(file); audiofile.setChannelMapId(-1); audiofile.setMainChannel(false); i++; } model->refresh(); } void FileList::selectionChanged(const QModelIndex &index) { auto audiofile_ids = instrument.getAudioFileList(); auto audiofile_id = audiofile_ids.begin() + index.row(); const auto& audiofile = instrument.getAudioFile(*audiofile_id); instrument.setMasterFile(audiofile.getFile()); emit masterFileChanged(audiofile.getAbsoluteFile()); //setMasterFile(i); model->refresh(); } void FileList::createMenus() { menu = new QMenu(); setMasterAction = new QAction(tr("Set as Master (dbl-click)"), this); connect(setMasterAction, SIGNAL(triggered()), this, SLOT(setMaster())); removeAction = new QAction(tr("Remove"), this); connect(removeAction, SIGNAL(triggered()), this, SLOT(removeFile())); removeAllAction = new QAction(tr("Remove all"), this); connect(removeAllAction, SIGNAL(triggered()), this, SLOT(removeAllFiles())); menu->addAction(setMasterAction); menu->addAction(removeAction); menu->addSeparator(); menu->addAction(removeAllAction); } void FileList::onCustomContextMenu(const QPoint &point) { QModelIndex index = indexAt(point); if(index.isValid()) { menu->popup(viewport()->mapToGlobal(point)); } } void FileList::setMaster() { // setMasterFile(activeItem); } void FileList::removeFile() { auto audiofile_ids = instrument.getAudioFileList(); auto audiofile_id = audiofile_ids.begin() + currentIndex().row(); auto& audiofile = instrument.getAudioFile(*audiofile_id); if(instrument.getMasterFile() == audiofile.getAbsoluteFile()) { emit masterFileChanged(""); // Clear canvas } instrument.deleteAudioFile(*audiofile_id); //emit fileRemoved(file, name); } void FileList::removeAllFiles() { instrument.setMasterFile(""); auto audiofile_ids = instrument.getAudioFileList(); for(auto audiofile_id : audiofile_ids) { instrument.deleteAudioFile(audiofile_id); } reset(); emit masterFileChanged(""); // Clear canvas emit allFilesRemoved(); }