/* Copyright © 2018 Hasan Yavuz Özderya This file is part of serialplot. serialplot 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 3 of the License, or (at your option) any later version. serialplot 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 serialplot. If not, see . */ #include "channelinfomodel.h" #include "setting_defines.h" #define NUMOF_COLORS (32) const QColor colors[NUMOF_COLORS] = { QColor("#ff0056"), QColor("#7e2dd2"), QColor("#00ae7e"), QColor("#fe8900"), QColor("#ff937e"), QColor("#6a826c"), QColor("#ff029d"), QColor("#00b917"), QColor("#7a4782"), QColor("#85a900"), QColor("#a42400"), QColor("#683d3b"), QColor("#bdc6ff"), QColor("#263400"), QColor("#bdd393"), QColor("#d5ff00"), QColor("#9e008e"), QColor("#001544"), QColor("#c28c9f"), QColor("#ff74a3"), QColor("#01d0ff"), QColor("#004754"), QColor("#e56ffe"), QColor("#788231"), QColor("#0e4ca1"), QColor("#91d0cb"), QColor("#be9970"), QColor("#968ae8"), QColor("#bb8800"), QColor("#43002c"), QColor("#deff74"), QColor("#00ffc6") }; ChannelInfoModel::ChannelInfoModel(unsigned numberOfChannels, QObject* parent) : QAbstractTableModel(parent) { _numOfChannels = 0; setNumOfChannels(numberOfChannels); } ChannelInfoModel::ChannelInfoModel(const ChannelInfoModel& other) : ChannelInfoModel(other.rowCount(), other.parent()) { // TODO: why not set (copy) info list directly instead? for (int i = 0; i < other.rowCount(); i++) { setData(index(i, COLUMN_NAME), other.data(other.index(i, COLUMN_NAME), Qt::EditRole), Qt::EditRole); setData(index(i, COLUMN_NAME), other.data(other.index(i, COLUMN_NAME), Qt::ForegroundRole), Qt::ForegroundRole); setData(index(i, COLUMN_VISIBILITY), other.data(other.index(i, COLUMN_VISIBILITY), Qt::CheckStateRole), Qt::CheckStateRole); setData(index(i, COLUMN_GAIN), other.data(other.index(i, COLUMN_GAIN), Qt::CheckStateRole), Qt::CheckStateRole); setData(index(i, COLUMN_GAIN), other.data(other.index(i, COLUMN_GAIN), Qt::EditRole), Qt::EditRole); setData(index(i, COLUMN_OFFSET), other.data(other.index(i, COLUMN_OFFSET), Qt::CheckStateRole), Qt::CheckStateRole); setData(index(i, COLUMN_OFFSET), other.data(other.index(i, COLUMN_OFFSET), Qt::EditRole), Qt::EditRole); } } ChannelInfoModel::ChannelInfoModel(const QStringList& channelNames) : ChannelInfoModel(channelNames.length(), NULL) { for (int i = 0; i < channelNames.length(); i++) { setData(index(i, COLUMN_NAME), channelNames[i], Qt::EditRole); } } ChannelInfoModel::ChannelInfo::ChannelInfo(unsigned index) { name = tr("Channel %1").arg(index + 1); visibility = true; color = colors[index % NUMOF_COLORS]; gain = 1.0; offset = 0.0; gainEn = false; offsetEn = false; } QString ChannelInfoModel::name(unsigned i) const { return infos[i].name; } QColor ChannelInfoModel::color(unsigned i) const { return infos[i].color; } bool ChannelInfoModel::isVisible(unsigned i) const { return infos[i].visibility; } bool ChannelInfoModel::gainEn (unsigned i) const { return infos[i].gainEn; } double ChannelInfoModel::gain (unsigned i) const { return infos[i].gain; } bool ChannelInfoModel::offsetEn (unsigned i) const { return infos[i].offsetEn; } double ChannelInfoModel::offset (unsigned i) const { return infos[i].offset; } QStringList ChannelInfoModel::channelNames() const { QStringList r; for (unsigned ci = 0; ci < _numOfChannels; ci++) { r << name(ci); } return r; } int ChannelInfoModel::rowCount(const QModelIndex &parent) const { return _numOfChannels; } int ChannelInfoModel::columnCount(const QModelIndex & parent) const { return COLUMN_COUNT; } Qt::ItemFlags ChannelInfoModel::flags(const QModelIndex &index) const { if (index.column() == COLUMN_NAME) { return Qt::ItemIsEditable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; } else if (index.column() == COLUMN_VISIBILITY) { return Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; } else if (index.column() == COLUMN_GAIN || index.column() == COLUMN_OFFSET) { return Qt::ItemIsEditable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren | Qt::ItemIsSelectable; } return Qt::NoItemFlags; } QVariant ChannelInfoModel::data(const QModelIndex &index, int role) const { // check index if (index.row() >= (int) _numOfChannels || index.row() < 0) { return QVariant(); } auto &info = infos[index.row()]; // get color if (role == Qt::ForegroundRole) { return info.color; } // name if (index.column() == COLUMN_NAME) { if (role == Qt::DisplayRole || role == Qt::EditRole) { return QVariant(info.name); } } // visibility else if (index.column() == COLUMN_VISIBILITY) { if (role == Qt::CheckStateRole) { bool visible = info.visibility; return visible ? Qt::Checked : Qt::Unchecked; } } // gain else if (index.column() == COLUMN_GAIN) { if (role == Qt::CheckStateRole) { return info.gainEn ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::DisplayRole || role == Qt::EditRole) { return QVariant(info.gain); } } // offset else if (index.column() == COLUMN_OFFSET) { if (role == Qt::CheckStateRole) { return info.offsetEn ? Qt::Checked : Qt::Unchecked; } else if (role == Qt::DisplayRole || role == Qt::EditRole) { return QVariant(info.offset); } } return QVariant(); } QVariant ChannelInfoModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation == Qt::Horizontal) { if (role == Qt::DisplayRole) { if (section == COLUMN_NAME) { return tr("Channel"); } else if (section == COLUMN_VISIBILITY) { return tr("Visible"); } else if (section == COLUMN_GAIN) { return tr("Gain"); } else if (section == COLUMN_OFFSET) { return tr("Offset"); } } } else // vertical { if (section < (int) _numOfChannels && role == Qt::DisplayRole) { return QString::number(section + 1); } } return QVariant(); } bool ChannelInfoModel::setData(const QModelIndex &index, const QVariant &value, int role) { // check index if (index.row() >= (int) _numOfChannels || index.row() < 0) { return false; } auto &info = infos[index.row()]; // set color if (role == Qt::ForegroundRole) { info.color = value.value(); emit dataChanged(index, index, QVector({Qt::ForegroundRole})); return true; } // set name bool r = false; if (index.column() == COLUMN_NAME) { if (role == Qt::DisplayRole || role == Qt::EditRole) { info.name = value.toString(); r = true; } } // set visibility else if (index.column() == COLUMN_VISIBILITY) { if (role == Qt::CheckStateRole) { bool checked = value.toInt() == Qt::Checked; info.visibility = checked; r = true; } } else if (index.column() == COLUMN_GAIN) { if (role == Qt::DisplayRole || role == Qt::EditRole) { info.gain = value.toDouble(); r = true; } else if (role == Qt::CheckStateRole) { bool checked = value.toInt() == Qt::Checked; info.gainEn = checked; if (_gainOrOffsetEn != checked) updateGainOrOffsetEn(); r = true; } } else if (index.column() == COLUMN_OFFSET) { if (role == Qt::DisplayRole || role == Qt::EditRole) { info.offset = value.toDouble(); r = true; } else if (role == Qt::CheckStateRole) { bool checked = value.toInt() == Qt::Checked; info.offsetEn = checked; if (_gainOrOffsetEn != checked) updateGainOrOffsetEn(); r = true; } } if (r) { emit dataChanged(index, index, QVector({role})); } return r; } void ChannelInfoModel::setNumOfChannels(unsigned number) { if (number == _numOfChannels) return; bool isInserting = number > _numOfChannels; if (isInserting) { beginInsertRows(QModelIndex(), _numOfChannels, number-1); } else { beginRemoveRows(QModelIndex(), number, _numOfChannels-1); } // we create channel info but never remove channel info to // remember user entered info if ((int) number > infos.length()) { for (unsigned ci = infos.length(); ci < number; ci++) { infos.append(ChannelInfo(ci)); } } // make sure newly available channels are visible, we don't // remember visibility option intentionally so that user doesn't // get confused if (number > _numOfChannels) { for (unsigned ci = _numOfChannels; ci < number; ci++) { infos[ci].visibility = true; } } _numOfChannels = number; updateGainOrOffsetEn(); if (isInserting) { endInsertRows(); } else { endRemoveRows(); } } void ChannelInfoModel::resetInfos() { beginResetModel(); for (unsigned ci = 0; (int) ci < infos.length(); ci++) { infos[ci] = ChannelInfo(ci); } endResetModel(); } // TODO: fix repetitive code, ChannelInfoModel::reset* functions void ChannelInfoModel::resetNames() { beginResetModel(); for (unsigned ci = 0; (int) ci < infos.length(); ci++) { // TODO: do not create a full object every time (applies to other reset methods as well) infos[ci].name = ChannelInfo(ci).name; } endResetModel(); } void ChannelInfoModel::resetColors() { beginResetModel(); for (unsigned ci = 0; (int) ci < infos.length(); ci++) { infos[ci].color = ChannelInfo(ci).color; } endResetModel(); } void ChannelInfoModel::resetVisibility(bool visible) { beginResetModel(); for (unsigned ci = 0; (int) ci < infos.length(); ci++) { infos[ci].visibility = visible; } endResetModel(); } void ChannelInfoModel::resetGains() { beginResetModel(); for (unsigned ci = 0; (int) ci < infos.length(); ci++) { infos[ci].gain = ChannelInfo(ci).gain; infos[ci].gainEn = ChannelInfo(ci).gainEn; } updateGainOrOffsetEn(); endResetModel(); } void ChannelInfoModel::resetOffsets() { beginResetModel(); for (unsigned ci = 0; (int) ci < infos.length(); ci++) { infos[ci].offset = ChannelInfo(ci).offset; infos[ci].offsetEn = ChannelInfo(ci).offsetEn; } updateGainOrOffsetEn(); endResetModel(); } bool ChannelInfoModel::gainOrOffsetEn() const { return _gainOrOffsetEn; } void ChannelInfoModel::updateGainOrOffsetEn() { _gainOrOffsetEn = false; for (unsigned ci = 0; ci < _numOfChannels; ci++) { auto& info = infos[ci]; _gainOrOffsetEn |= (info.gainEn || info.offsetEn); } } void ChannelInfoModel::saveSettings(QSettings* settings) const { settings->beginGroup(SettingGroup_Channels); settings->beginWriteArray(SG_Channels_Channel); // save all channel information regardless of current number of channels for (unsigned ci = 0; (int) ci < infos.length(); ci++) { settings->setArrayIndex(ci); auto& info = infos[ci]; settings->setValue(SG_Channels_Name, info.name); settings->setValue(SG_Channels_Color, info.color); settings->setValue(SG_Channels_Visible, info.visibility); settings->setValue(SG_Channels_Gain, info.gain); settings->setValue(SG_Channels_GainEn, info.gainEn); settings->setValue(SG_Channels_Offset, info.offset); settings->setValue(SG_Channels_OffsetEn, info.offsetEn); } settings->endArray(); settings->endGroup(); } void ChannelInfoModel::loadSettings(QSettings* settings) { settings->beginGroup(SettingGroup_Channels); unsigned size = settings->beginReadArray(SG_Channels_Channel); for (unsigned ci = 0; ci < size; ci++) { settings->setArrayIndex(ci); ChannelInfo chanInfo(ci); chanInfo.name = settings->value(SG_Channels_Name , chanInfo.name).toString(); chanInfo.color = settings->value(SG_Channels_Color , chanInfo.color).value(); chanInfo.visibility = settings->value(SG_Channels_Visible , chanInfo.visibility).toBool(); chanInfo.gain = settings->value(SG_Channels_Gain , chanInfo.gain).toDouble(); chanInfo.gainEn = settings->value(SG_Channels_GainEn , chanInfo.gainEn).toBool(); chanInfo.offset = settings->value(SG_Channels_Offset , chanInfo.offset).toDouble(); chanInfo.offsetEn = settings->value(SG_Channels_OffsetEn , chanInfo.offsetEn).toBool(); if ((int) ci < infos.size()) { infos[ci] = chanInfo; if (ci < _numOfChannels) { auto roles = QVector({ Qt::DisplayRole, Qt::EditRole, Qt::ForegroundRole, Qt::CheckStateRole}); emit dataChanged(index(ci, 0), index(ci, COLUMN_COUNT-1), roles); } } else { infos.append(chanInfo); } } updateGainOrOffsetEn(); settings->endArray(); settings->endGroup(); }