/* * Copyright (C) OpenTX * * Based on code named * th9x - http://code.google.com/p/th9x * er9x - http://code.google.com/p/er9x * gruvin9x - http://code.google.com/p/gruvin9x * * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * This program 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. */ #include "curves.h" #include "curveimagewidget.h" #include "compounditemmodels.h" #include "datahelpers.h" #include "helpers.h" CurvesPanel::CurvesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware, CompoundItemModelFactory * sharedItemModels): ModelPanel(parent, model, generalSettings, firmware), sharedItemModels(sharedItemModels) { lock = true; maxCurves = firmware->getCapability(NumCurves); QStringList headerLabels; headerLabels << "#"; headerLabels << tr("Plot") << tr("Details"); TableLayout *tableLayout = new TableLayout(this, maxCurves, headerLabels); for (int i = 0; i < maxCurves; i++) { int col = 0; QLabel *label = new QLabel(this); label->setText(tr("CV%1").arg(i+1)); label->setProperty("index", i); label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); label->setContextMenuPolicy(Qt::CustomContextMenu); label->setToolTip(tr("Popup menu available")); label->setMouseTracking(true); connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint))); tableLayout->addWidget(i, col++, label); image[i] = new CurveImageWidget(this); image[i]->set(&model, firmware, sharedItemModels, i, colors[i], 3); image[i]->setGrid(Qt::gray, 2); image[i]->setProperty("index", i); image[i]->setFixedSize(QSize(100, 100)); connect(image[i], &CurveImageWidget::doubleClicked, this, &CurvesPanel::on_curveImageDoubleClicked); tableLayout->addWidget(i, col++, image[i]); grp[i] = new QWidget(); // layouts are not hideable so use widget as parent for grid grid[i] = new QGridLayout(grp[i]); int row = 0; grid[i]->addWidget(new QLabel(tr("Name:")), row, 0, Qt::AlignLeft); name[i] = new QLabel(); grid[i]->addWidget(name[i], row++, 1, Qt::AlignLeft); grid[i]->addWidget(new QLabel(tr("Type:")), row, 0, Qt::AlignLeft); type[i] = new QLabel(); grid[i]->addWidget(type[i], row++, 1, Qt::AlignLeft); grid[i]->addWidget(new QLabel(tr("Points:")), row, 0, Qt::AlignLeft); numpoints[i] = new QLabel(); grid[i]->addWidget(numpoints[i], row, 1); points[i] = new QLabel(); grid[i]->addWidget(points[i], row++, 2, Qt::AlignLeft); grid[i]->addWidget(new QLabel(tr("Smooth:")), row, 0, Qt::AlignLeft); smooth[i] = new QLabel(); grid[i]->addWidget(smooth[i], row++, 1, Qt::AlignLeft); tableLayout->addWidget(i, col++, grp[i], Qt::AlignLeft); } tableLayout->resizeColumnsToContents(); tableLayout->pushColumnsLeft(headerLabels.count()); tableLayout->pushRowsUp(maxCurves + 1); for (int i = 0; i < CPN_MAX_CURVES; i++) { update(i); } lock = false; } CurvesPanel::~CurvesPanel() { // compiler warning if delete[] for (int i = 0; i < CPN_MAX_CURVES; i++) { delete image[i]; delete name[i]; delete type[i]; delete numpoints[i]; delete points[i]; delete smooth[i]; delete grid[i]; delete grp[i]; } } void CurvesPanel::update() { for (int i = 0; i < maxCurves; i++) { update(i); } } void CurvesPanel::update(int index) { if (model->curves[index].isEmpty()) { image[index]->setVisible(false); grp[index]->setVisible(false); } else { image[index]->setVisible(true); grp[index]->setVisible(true); image[index]->draw(); name[index]->setText(QString(model->curves[index].name)); type[index]->setText(model->curves[index].typeToString()); numpoints[index]->setText(QString::number(model->curves[index].count)); points[index]->setText(model->curves[index].pointsToString()); smooth[index]->setText(DataHelpers::boolToString(model->curves[index].smooth, DataHelpers::BOOL_FMT_YESNO)); } } void CurvesPanel::editCurve(int index) { if (image[index]->edit()) { update(index); emit modified(); } } void CurvesPanel::on_curveImageDoubleClicked() { bool ok = false; int index = sender()->property("index").toInt(&ok); if (ok) editCurve(index); } void CurvesPanel::onCustomContextMenuRequested(QPoint pos) { QPushButton *button = (QPushButton *)sender(); selectedIndex = button->property("index").toInt(); QPoint globalPos = button->mapToGlobal(pos); QMenu contextMenu; contextMenu.addAction(CompanionIcon("new.png"), tr("Add"), this, SLOT(cmEdit()))->setEnabled(!curveExists()); contextMenu.addAction(CompanionIcon("edit.png"), tr("Edit"), this, SLOT(cmEdit()))->setEnabled(curveExists()); contextMenu.addSeparator(); contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"), this, SLOT(cmCopy()))->setEnabled(curveExists()); contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"), this, SLOT(cmCut()))->setEnabled(curveExists()); contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"), this, SLOT(cmPaste()))->setEnabled(hasClipboardData()); contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"), this, SLOT(cmClear()))->setEnabled(curveExists()); contextMenu.addSeparator(); contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"), this, SLOT(cmInsert()))->setEnabled(insertAllowed()); contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"), this, SLOT(cmDelete())); contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"), this, SLOT(cmMoveUp()))->setEnabled(moveUpAllowed()); contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"), this, SLOT(cmMoveDown()))->setEnabled(moveDownAllowed()); contextMenu.addSeparator(); contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"), this, SLOT(cmClearAll())); contextMenu.exec(globalPos); } bool CurvesPanel::hasClipboardData(QByteArray * data) const { const QClipboard * clipboard = QApplication::clipboard(); const QMimeData * mimeData = clipboard->mimeData(); if (mimeData->hasFormat(MIMETYPE_CURVE)) { if (data) data->append(mimeData->data(MIMETYPE_CURVE)); return true; } return false; } bool CurvesPanel::curveExists() const { return !model->curves[selectedIndex].isEmpty(); } bool CurvesPanel::insertAllowed() const { return ((selectedIndex < maxCurves - 1) && (model->curves[maxCurves - 1].isEmpty())); } bool CurvesPanel::moveDownAllowed() const { return selectedIndex < maxCurves - 1 && curveExists(); } bool CurvesPanel::moveUpAllowed() const { return selectedIndex > 0 && curveExists(); } void CurvesPanel::cmClear(bool prompt) { if (prompt) { if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear curve %1:%2. Are you sure?").arg(QString::number(selectedIndex + 1)).arg(model->curves[selectedIndex].name), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; } model->curves[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_CLEAR, selectedIndex); update(selectedIndex); updateItemModels(); emit modified(); } void CurvesPanel::cmClearAll() { if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all curves. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; for (int i = 0; i < maxCurves; i++) { model->curves[i].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_CLEAR, i); } update(); updateItemModels(); emit modified(); } void CurvesPanel::cmCopy() { QByteArray data; data.append((char*)&model->curves[selectedIndex], sizeof(CurveData)); QMimeData *mimeData = new QMimeData; mimeData->setData(MIMETYPE_CURVE, data); QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard); } void CurvesPanel::cmCut() { if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Cut curve %1:%2. Are you sure?").arg(QString::number(selectedIndex + 1)).arg(model->curves[selectedIndex].name), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; cmCopy(); cmClear(false); } void CurvesPanel::cmDelete() { if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete curve %1:%2. Are you sure?").arg(QString::number(selectedIndex + 1)).arg(model->curves[selectedIndex].name), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No) return; memmove(&model->curves[selectedIndex], &model->curves[selectedIndex + 1], (CPN_MAX_CURVES - (selectedIndex + 1)) * sizeof(CurveData)); model->curves[maxCurves - 1].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1); for (int i = selectedIndex; i < CPN_MAX_CURVES; i++) { update(i); } updateItemModels(); emit modified(); } void CurvesPanel::cmEdit() { editCurve(selectedIndex); } void CurvesPanel::cmInsert() { memmove(&model->curves[selectedIndex + 1], &model->curves[selectedIndex], (CPN_MAX_CURVES - (selectedIndex + 1)) * sizeof(CurveData)); model->curves[selectedIndex].clear(); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1); for (int i = selectedIndex; i < CPN_MAX_CURVES; i++) { update(i); } updateItemModels(); emit modified(); } void CurvesPanel::cmMoveDown() { swapData(selectedIndex, selectedIndex + 1); } void CurvesPanel::cmMoveUp() { swapData(selectedIndex, selectedIndex - 1); } void CurvesPanel::cmPaste() { QByteArray data; if (hasClipboardData(&data)) { CurveData *cd = &model->curves[selectedIndex]; memcpy(cd, data.constData(), sizeof(CurveData)); update(selectedIndex); updateItemModels(); emit modified(); } } void CurvesPanel::swapData(int idx1, int idx2) { if ((idx1 != idx2) && (!model->curves[idx1].isEmpty() || !model->curves[idx2].isEmpty())) { CurveData cdtmp = model->curves[idx2]; CurveData *cd1 = &model->curves[idx1]; CurveData *cd2 = &model->curves[idx2]; memcpy(cd2, cd1, sizeof(CurveData)); memcpy(cd1, &cdtmp, sizeof(CurveData)); model->updateAllReferences(ModelData::REF_UPD_TYPE_CURVE, ModelData::REF_UPD_ACT_SWAP, idx1, idx2); update(idx1); update(idx2); updateItemModels(); emit modified(); } } void CurvesPanel::updateItemModels() { sharedItemModels->update(AbstractItemModel::IMUE_Curves); }