Files
edgetx-gx12/companion/src/firmwares/radiodata.cpp
T

376 lines
9.3 KiB
C++

/*
* Copyright (C) EdgeTX
*
* Based on code named
* opentx - https://github.com/opentx/opentx
* 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 "radiodata.h"
#include "radiodataconversionstate.h"
#include "eeprominterface.h"
#include "compounditemmodels.h"
RadioData::RadioData() :
sortOrder(0)
{
models.resize(getCurrentFirmware()->getCapability(Models));
}
void RadioData::setCurrentModel(unsigned int index)
{
generalSettings.currModelIndex = index;
if (index < models.size()) {
strcpy(generalSettings.currModelFilename, models[index].filename.c_str());
}
}
void RadioData::fixModelFilename(unsigned int index)
{
ModelData & model = models[index];
QString filename = model.filename.toQString();
const bool hasSDCard = Boards::getCapability(getCurrentFirmware()->getBoard(), Board::HasSDCard);
bool ok = hasSDCard ? filename.endsWith(".yml") : filename.endsWith(".bin");
if (ok) {
if (filename.startsWith("model") && filename.mid(5, filename.length()-9).toInt() > 0) {
ok = false;
}
}
if (ok) {
for (unsigned i=0; i<index; i++) {
if (models[i].filename == model.filename) {
ok = false;
break;
}
}
}
if (!ok) {
model.filename = QString::asprintf("model%d.%s", index + 1, hasSDCard ? "yml" : "bin");
}
}
void RadioData::fixModelFilenames()
{
for (unsigned int i=0; i<models.size(); i++) {
fixModelFilename(i);
}
setCurrentModel(generalSettings.currModelIndex);
}
QString RadioData::getNextModelFilename()
{
int8_t index = 0;
bool found = true;
QString filename;
while (found) {
filename = QString::asprintf("model%d.yml", ++index);
found = false;
for (unsigned int i = 0; i < models.size(); i++) {
if (models[i].filename == filename.toStdString()) {
found = true;
break;
}
}
}
return filename;
}
void RadioData::convert(RadioDataConversionState & cstate)
{
generalSettings.convert(cstate.withModelIndex(-1));
for (unsigned i=0; i<models.size(); i++) {
models[i].convert(cstate.withModelIndex(i));
}
if (IS_FAMILY_HORUS_OR_T16(cstate.toType)) {
fixModelFilenames();
}
// ensure proper number of model slots
if (getCurrentFirmware()->getCapability(Models) && getCurrentFirmware()->getCapability(Models) != (int)models.size()) {
models.resize(getCurrentFirmware()->getCapability(Models));
}
}
void RadioData::addLabel(QString label)
{
label = unEscapeCSV(label);
// Truncate possible UTF-8 to 16 char maximum
QByteArray output = label.toLatin1();
if (output.size() > LABEL_LENGTH) {
int truncateAt = 0;
for (int i = LABEL_LENGTH; i > 0; i--) {
if ((output[i] & 0xC0) != 0x80) {
truncateAt = i;
break;
}
}
output.truncate(truncateAt);
}
label = QString(output);
if (indexOfLabel(label) < 0) {
LabelData ld = { label, false };
labels.append(ld);
}
}
bool RadioData::deleteLabel(QString label)
{
bool deleted = false;
// Remove labels in the models
for(auto& model : models) {
QStringList modelLabels = fromCSV(QString::fromLatin1(model.labels.c_str()));
if (modelLabels.indexOf(label) >= 0) {
deleted = true;
modelLabels.removeAll(label);
}
model.labels = toCSV(modelLabels).toLatin1().constData();
}
// Remove the label from the global list
int index = indexOfLabel(label);
if (index > -1)
labels.remove(index);
// If no labels remain, add a Favorites one
if (!labels.size()) {
addLabel(tr("Favorites"));
}
return deleted;
}
bool RadioData::deleteLabel(int index)
{
if (index >= labels.size()) return false;
QString modelLabel = labels.at(index).name;
return deleteLabel(modelLabel);
}
bool RadioData::renameLabel(QString from, QString to)
{
bool success = true;
QString csvFrom = escapeCSV(from);
QString csvTo = escapeCSV(to);
int lengthdiff = csvTo.size() - csvFrom.size();
// Check that rename is possible (Rename won't cause too long of a string)
for(auto& model : models) {
if (RadioData::fromCSV(model.labels.toQString()).indexOf(from) != -1) {
if ((int)model.labels.size() + lengthdiff > (int)model.labels.capacity()) {
success = false;
throw std::length_error(model.name);
break;
}
}
}
if (success) {
for(auto& model : models) {
QStringList modelLabels = model.labels.toQString().split(',', Qt::SkipEmptyParts);
int ind = modelLabels.indexOf(csvFrom);
if (ind != -1) {
modelLabels.replace(ind, csvTo);
QString outputcsv = QString(modelLabels.join(','));
if (outputcsv.toLatin1().size() <= (int)model.labels.capacity()) {
model.labels = outputcsv.toLatin1().constData();
} else { // Shouldn't ever get here, from check above
success = false;
throw std::length_error(model.name);
break;
}
}
}
int ind = indexOfLabel(from);
if (ind != -1) {
labels[ind].name = to;
}
}
return success;
}
bool RadioData::renameLabel(int index, QString to)
{
if (index >= labels.size()) return false;
QString from = labels.at(index).name;
return renameLabel(from, to);
}
void RadioData::swapLabel(int indFrom, int indTo)
{
if(abs(indFrom - indTo) > 1 ||
indFrom >= labels.size() ||
indTo >= labels.size() ||
indFrom < 0 ||
indTo < 0)
return;
LabelData tmplbl = labels.at(indFrom);
labels.replace(indFrom, labels.at(indTo));
labels.replace(indTo, tmplbl);
}
bool RadioData::addLabelToModel(int index, QString label)
{
if ((unsigned int)index >= models.size()) return false;
label = escapeCSV(label);
auto & modelLabelCsv = models[index].labels;
// Make sure it will fit
if (modelLabelCsv.size() + (size_t)label.size() + 1 < modelLabelCsv.capacity()) {
QStringList modelLabels = QString::fromLatin1(modelLabelCsv.c_str()).split(',', Qt::SkipEmptyParts);
if (modelLabels.indexOf(label) == -1) {
modelLabels.append(label);
modelLabelCsv = QString(modelLabels.join(',')).toLatin1().constData();
return true;
}
}
throw std::length_error(models[index].name);
return false;
}
bool RadioData::removeLabelFromModel(int index, QString label)
{
if ((unsigned int)index >= models.size()) return false;
QStringList lbls = fromCSV(QString::fromLatin1(models[index].labels.c_str()));
if (lbls.indexOf(label) >= 0) {
lbls.removeAll(label);
models[index].labels = toCSV(lbls).toLatin1().constData();
return true;
}
return false;
}
void RadioData::addLabelsFromModels()
{
for(const auto &model: models) {
QStringList labels = model.labels.toQString().split(',', Qt::SkipEmptyParts);
foreach(QString label, labels) {
addLabel(label);
}
}
}
int RadioData::indexOfLabel(QString & label) const
{
int index = -1;
for (int i = 0; i < labels.size(); i++) {
if (labels.at(i).name == label) {
index = i;
break;
}
}
return index;
}
QStringList RadioData::fromCSV(const QString &csv)
{
QStringList lbls = QString(csv).split(',', Qt::SkipEmptyParts);
for(QString &label: lbls) {
label = unEscapeCSV(label);
}
return lbls;
}
QString RadioData::toCSV(QStringList lbls)
{
for(QString &label: lbls) {
label = escapeCSV(label);
}
return lbls.join(',');
}
QString RadioData::escapeCSV(QString str)
{
str.replace("/","//");
str.replace(",","/c");
return str;
}
QString RadioData::unEscapeCSV(QString str)
{
str.replace("/c",",");
str.replace("//","/");
return str;
}
// static
QString RadioData::modelSortOrderToString(int value)
{
switch(value) {
case MSO_NO_SORT:
return tr("None");
case MSO_NAME_ASC:
return tr("Name %1").arg(CPN_STR_SW_INDICATOR_UP);
case MSO_NAME_DES:
return tr("Name %1").arg(CPN_STR_SW_INDICATOR_DN);
case MSO_DATE_ASC:
return tr("Last Opened %1").arg(CPN_STR_SW_INDICATOR_UP);
case MSO_DATE_DES:
return tr("Last Opened %1").arg(CPN_STR_SW_INDICATOR_DN);
default:
return CPN_STR_UNKNOWN_ITEM;
}
}
// static
AbstractStaticItemModel * RadioData::modelSortOrderItemModel()
{
AbstractStaticItemModel * mdl = new AbstractStaticItemModel();
mdl->setName("radio.modelsortorder");
for (int i = 0; i < MSO_SORT_COUNT; i++) {
mdl->appendToItemList(modelSortOrderToString(i), i);
}
mdl->loadItemList();
return mdl;
}
void RadioData::validateModels()
{
for(auto &model: models)
model.validate();
}
int RadioData::invalidModels()
{
int cnt = 0;
for(auto &model: models)
cnt += model.isValid() ? 0 : 1;
return cnt;
}
QStringList RadioData::modelErrorsList()
{
QStringList ret;
for(auto &model: models) {
if (!model.isValid()) {
ret.append("");
ret.append(model.name.toQString());
ret.append(model.errorsList());
}
}
return ret;
}