Fix remaining model editor sync issues with RawSource changes. (#5936)
[opentx.git] / companion / src / modeledit / customfunctions.cpp
blob79d8699f0152fab90b54241fc39d19478c6a3aac
1 /*
2 * Copyright (C) OpenTX
4 * Based on code named
5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
21 #include "customfunctions.h"
22 #include "switchitemmodel.h"
23 #include "helpers.h"
24 #include "appdata.h"
26 #include <TimerEdit>
28 RepeatComboBox::RepeatComboBox(QWidget *parent, int & repeatParam):
29 QComboBox(parent),
30 repeatParam(repeatParam)
32 unsigned int step = IS_ARM(getCurrentBoard()) ? 1 : 10;
33 int value = repeatParam/step;
35 setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
37 if (step == 1) {
38 addItem(tr("Played once, not during startup"), -1);
39 value++;
42 addItem(tr("No repeat"), 0);
44 for (unsigned int i=step; i<=60; i+=step) {
45 addItem(tr("%1s").arg(i), i);
48 setCurrentIndex(value);
50 connect(this, SIGNAL(currentIndexChanged(int)), this, SLOT(onIndexChanged(int)));
53 void RepeatComboBox::onIndexChanged(int index)
55 repeatParam = itemData(index).toInt();
56 emit modified();
59 void RepeatComboBox::update()
61 unsigned int step = IS_ARM(getCurrentBoard()) ? 1 : 10;
62 int value = repeatParam/step;
63 if (step == 1) {
64 value++;
66 setCurrentIndex(value);
69 CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware):
70 GenericPanel(parent, model, generalSettings, firmware),
71 functions(model ? model->customFn : generalSettings.customFn),
72 rawSwitchItemModel(NULL),
73 rawSrcInputsItemModel(NULL),
74 rawSrcAllItemModel(NULL),
75 mediaPlayerCurrent(-1),
76 mediaPlayer(NULL)
78 Stopwatch s1("CustomFunctionsPanel - populate");
79 lock = true;
80 int num_fsw = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions);
82 setDataModels();
84 if (!firmware->getCapability(VoicesAsNumbers)) {
85 tracksSet = getFilesSet(getSoundsPath(generalSettings), QStringList() << "*.wav" << "*.WAV", firmware->getCapability(VoicesMaxLength));
86 for (int i=0; i<num_fsw; i++) {
87 if (functions[i].func==FuncPlayPrompt || functions[i].func==FuncBackgroundMusic) {
88 QString temp = functions[i].paramarm;
89 if (!temp.isEmpty()) {
90 tracksSet.insert(temp);
96 s1.report("get tracks");
98 if (IS_STM32(firmware->getBoard())) {
99 scriptsSet = getFilesSet(g.profile[g.id()].sdPath() + "/SCRIPTS/FUNCTIONS", QStringList() << "*.lua", firmware->getCapability(VoicesMaxLength));
100 for (int i=0; i<num_fsw; i++) {
101 if (functions[i].func==FuncPlayScript) {
102 QString temp = functions[i].paramarm;
103 if (!temp.isEmpty()) {
104 scriptsSet.insert(temp);
109 s1.report("get scripts");
111 CompanionIcon playIcon("play.png");
113 QStringList headerLabels;
114 headerLabels << "#" << tr("Switch") << tr("Action") << tr("Parameters") << tr("Enable");
115 TableLayout * tableLayout = new TableLayout(this, num_fsw, headerLabels);
117 for (int i=0; i<num_fsw; i++) {
118 // The label
119 QLabel * label = new QLabel(this);
120 label->setContextMenuPolicy(Qt::CustomContextMenu);
121 label->setMouseTracking(true);
122 label->setProperty("index", i);
123 if (model)
124 label->setText(tr("SF%1").arg(i+1));
125 else
126 label->setText(tr("GF%1").arg(i+1));
127 label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
128 connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(fsw_customContextMenuRequested(QPoint)));
129 tableLayout->addWidget(i, 0, label);
130 // s1.report("label");
132 // The switch
133 fswtchSwtch[i] = new QComboBox(this);
134 fswtchSwtch[i]->setModel(rawSwitchItemModel);
135 fswtchSwtch[i]->setCurrentIndex(fswtchSwtch[i]->findData(functions[i].swtch.toValue()));
136 fswtchSwtch[i]->setProperty("index", i);
137 fswtchSwtch[i]->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
138 fswtchSwtch[i]->setMaxVisibleItems(10);
139 connect(fswtchSwtch[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited()));
140 tableLayout->addWidget(i, 1, fswtchSwtch[i]);
141 // s1.report("switch");
143 // The function
144 fswtchFunc[i] = new QComboBox(this);
145 if (!i) {
146 populateFuncCB(fswtchFunc[i], functions[i].func);
148 else {
149 fswtchFunc[i]->setModel(fswtchFunc[0]->model());
150 fswtchFunc[i]->setCurrentIndex(fswtchFunc[i]->findData(functions[i].func));
152 fswtchFunc[i]->setProperty("index", i);
153 connect(fswtchFunc[i], SIGNAL(currentIndexChanged(int)), this, SLOT(functionEdited()));
154 tableLayout->addWidget(i, 2, fswtchFunc[i]);
155 // s1.report("func");
157 // The parameters
158 QHBoxLayout * paramLayout = new QHBoxLayout();
159 tableLayout->addLayout(i, 3, paramLayout);
161 fswtchGVmode[i] = new QComboBox(this);
162 fswtchGVmode[i]->setProperty("index", i);
163 populateGVmodeCB(fswtchGVmode[i], functions[i].adjustMode);
164 fswtchGVmode[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents);
165 connect(fswtchGVmode[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited()));
166 paramLayout->addWidget(fswtchGVmode[i]);
168 fswtchParamGV[i] = new QCheckBox(this);
169 fswtchParamGV[i]->setProperty("index", i);
170 fswtchParamGV[i]->setText("GV");
171 fswtchParamGV[i]->setSizePolicy(QSizePolicy::Minimum,QSizePolicy::Minimum);
172 connect(fswtchParamGV[i], SIGNAL(stateChanged(int)), this, SLOT(customFunctionEdited()));
173 paramLayout->addWidget(fswtchParamGV[i]);
175 fswtchParam[i] = new QDoubleSpinBox(this);
176 fswtchParam[i]->setProperty("index", i);
177 fswtchParam[i]->setAccelerated(true);
178 fswtchParam[i]->setDecimals(0);
179 connect(fswtchParam[i], SIGNAL(editingFinished()), this, SLOT(customFunctionEdited()));
180 paramLayout->addWidget(fswtchParam[i]);
182 fswtchParamTime[i] = new TimerEdit(this);
183 fswtchParamTime[i]->setProperty("index", i);
184 connect(fswtchParamTime[i], SIGNAL(editingFinished()), this, SLOT(customFunctionEdited()));
185 paramLayout->addWidget(fswtchParamTime[i]);
187 fswtchParamT[i] = new QComboBox(this);
188 fswtchParamT[i]->setProperty("index", i);
189 populateFuncParamCB(fswtchParamT[i], functions[i].func, functions[i].param, functions[i].adjustMode);
190 paramLayout->addWidget(fswtchParamT[i]);
191 fswtchParamT[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents);
192 connect(fswtchParamT[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited()));
194 fswtchParamArmT[i] = new QComboBox(this);
195 fswtchParamArmT[i]->setProperty("index", i);
196 fswtchParamArmT[i]->setEditable(true);
197 fswtchParamArmT[i]->setSizeAdjustPolicy(QComboBox::AdjustToContents);
198 paramLayout->addWidget(fswtchParamArmT[i]);
200 connect(fswtchParamArmT[i], SIGNAL(currentIndexChanged(int)), this, SLOT(customFunctionEdited()));
201 connect(fswtchParamArmT[i], SIGNAL(editTextChanged ( const QString)), this, SLOT(customFunctionEdited()));
203 fswtchBLcolor[i] = new QSlider(this);
204 fswtchBLcolor[i]->setProperty("index", i);
205 fswtchBLcolor[i]->setMinimum(0);
206 fswtchBLcolor[i]->setMaximum(100);
207 fswtchBLcolor[i]->setSingleStep(1);
208 fswtchBLcolor[i]->setOrientation(Qt::Horizontal);
209 paramLayout->addWidget(fswtchBLcolor[i]);
210 connect(fswtchBLcolor[i], SIGNAL(sliderReleased()), this, SLOT(customFunctionEdited()));
212 playBT[i] = new QPushButton(this);
213 playBT[i]->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
214 playBT[i]->setProperty("index", i);
215 playBT[i]->setIcon(playIcon);
216 paramLayout->addWidget(playBT[i]);
217 connect(playBT[i], SIGNAL(pressed()), this, SLOT(playMusic()));
219 QHBoxLayout * repeatLayout = new QHBoxLayout();
220 tableLayout->addLayout(i, 4, repeatLayout);
221 fswtchRepeat[i] = new RepeatComboBox(this, functions[i].repeatParam);
222 repeatLayout->addWidget(fswtchRepeat[i], i+1);
223 connect(fswtchRepeat[i], SIGNAL(modified()), this, SLOT(onChildModified()));
225 fswtchEnable[i] = new QCheckBox(this);
226 fswtchEnable[i]->setProperty("index", i);
227 fswtchEnable[i]->setText(tr("ON"));
228 fswtchEnable[i]->setFixedWidth(200);
229 repeatLayout->addWidget(fswtchEnable[i], i+1);
230 connect(fswtchEnable[i], SIGNAL(stateChanged(int)), this, SLOT(customFunctionEdited()));
233 s1.report("add items");
235 disableMouseScrolling();
236 s1.report("disableMouseScrolling");
238 lock = false;
240 update();
241 s1.report("update");
242 tableLayout->resizeColumnsToContents();
243 s1.report("resizeColumnsToContents");
244 tableLayout->setColumnWidth(3, 300);
245 tableLayout->pushRowsUp(num_fsw+1);
246 s1.report("end");
249 CustomFunctionsPanel::~CustomFunctionsPanel()
253 void CustomFunctionsPanel::setDataModels()
255 if (rawSwitchItemModel)
256 rawSwitchItemModel->update();
257 else
258 rawSwitchItemModel = new RawSwitchFilterItemModel(&generalSettings, model, model ? SpecialFunctionsContext : GlobalFunctionsContext);
260 // The RawSource item models have to be reloaded on every update(). TODO: convert to filtered model like for RawSwitches
262 if (rawSrcInputsItemModel)
263 rawSrcInputsItemModel->deleteLater();
264 if (rawSrcAllItemModel)
265 rawSrcAllItemModel->deleteLater();
267 rawSrcInputsItemModel = Helpers::getRawSourceItemModel(&generalSettings, model, POPULATE_NONE|POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_TRIMS|POPULATE_SWITCHES);
268 rawSrcInputsItemModel->setParent(this);
269 rawSrcAllItemModel = Helpers::getRawSourceItemModel(&generalSettings, model, POPULATE_NONE|POPULATE_SOURCES|POPULATE_VIRTUAL_INPUTS|POPULATE_SWITCHES|POPULATE_GVARS|POPULATE_TRIMS|POPULATE_TELEMETRY|POPULATE_TELEMETRYEXT|POPULATE_SCRIPT_OUTPUTS);
270 rawSrcAllItemModel->setParent(this);
273 void CustomFunctionsPanel::onMediaPlayerStateChanged(QMediaPlayer::State state)
275 if (!lock) {
276 lock = true;
277 if (state==QMediaPlayer::StoppedState || state==QMediaPlayer::PausedState) {
278 mediaPlayer->stop();
279 if (mediaPlayerCurrent >= 0) {
280 playBT[mediaPlayerCurrent]->setIcon(CompanionIcon("play.png"));
281 mediaPlayerCurrent = -1;
284 lock = false;
288 void CustomFunctionsPanel::onMediaPlayerError(QMediaPlayer::Error error)
290 if (!lock) {
291 lock = true;
292 if (mediaPlayerCurrent >= 0) {
293 playBT[mediaPlayerCurrent]->setIcon(CompanionIcon("play.png"));
294 mediaPlayerCurrent = -1;
296 lock = false;
300 void CustomFunctionsPanel::playMusic()
302 if (!mediaPlayer) {
303 mediaPlayer = new QMediaPlayer(this);
304 connect(mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(onMediaPlayerStateChanged(QMediaPlayer::State)));
305 connect(mediaPlayer, SIGNAL(error(QMediaPlayer::Error)), this, SLOT(onMediaPlayerError(QMediaPlayer::Error)));
308 int index = sender()->property("index").toInt();
309 QString path = g.profile[g.id()].sdPath();
310 QDir qd(path);
311 QString track;
312 if (qd.exists()) {
313 if (firmware->getCapability(VoicesAsNumbers)) {
314 track = path + QString("/%1.wav").arg(int(fswtchParam[index]->value()), 4, 10, (const QChar)'0');
316 else {
317 path.append("/SOUNDS/");
318 QString lang = generalSettings.ttsLanguage;
319 if (lang.isEmpty())
320 lang = "en";
321 path.append(lang);
322 if (fswtchParamArmT[index]->currentText() != "----") {
323 track = path + "/" + fswtchParamArmT[index]->currentText() + ".wav";
326 QFile file(track);
327 if (!file.exists()) {
328 QMessageBox::critical(this, CPN_STR_TTL_ERROR, tr("Unable to find sound file %1!").arg(track));
329 return;
332 if (mediaPlayerCurrent == index) {
333 mediaPlayer->stop();
334 playBT[index]->setIcon(CompanionIcon("play.png"));
335 mediaPlayerCurrent = -1;
337 else {
338 if (mediaPlayerCurrent >= 0) {
339 playBT[mediaPlayerCurrent]->setIcon(CompanionIcon("play.png"));
341 mediaPlayerCurrent = index;
342 mediaPlayer->setMedia(QUrl::fromLocalFile(track));
343 mediaPlayer->play();
344 playBT[index]->setIcon(CompanionIcon("stop.png"));
349 #define CUSTOM_FUNCTION_NUMERIC_PARAM (1<<0)
350 #define CUSTOM_FUNCTION_SOURCE_PARAM (1<<1)
351 #define CUSTOM_FUNCTION_FILE_PARAM (1<<2)
352 #define CUSTOM_FUNCTION_TIME_PARAM (1<<3)
353 #define CUSTOM_FUNCTION_GV_MODE (1<<4)
354 #define CUSTOM_FUNCTION_GV_TOOGLE (1<<5)
355 #define CUSTOM_FUNCTION_ENABLE (1<<6)
356 #define CUSTOM_FUNCTION_REPEAT (1<<7)
357 #define CUSTOM_FUNCTION_PLAY (1<<8)
358 #define CUSTOM_FUNCTION_BL_COLOR (1<<9)
359 #define CUSTOM_FUNCTION_SHOW_FUNC (1<<10)
361 void CustomFunctionsPanel::customFunctionEdited()
363 if (!lock) {
364 lock = true;
365 int index = sender()->property("index").toInt();
366 refreshCustomFunction(index, true);
367 emit modified();
368 lock = false;
372 void CustomFunctionsPanel::functionEdited()
374 if (!lock) {
375 lock = true;
376 int index = sender()->property("index").toInt();
377 RawSwitch swtch = functions[index].swtch;
378 functions[index].clear();
379 functions[index].swtch = swtch;
380 functions[index].func = (AssignFunc)fswtchFunc[index]->currentData().toInt();
381 refreshCustomFunction(index);
382 emit modified();
383 lock = false;
387 void CustomFunctionsPanel::onChildModified()
389 emit modified();
392 void CustomFunctionsPanel::refreshCustomFunction(int i, bool modified)
394 CustomFunctionData & cfn = functions[i];
395 AssignFunc func = (AssignFunc)fswtchFunc[i]->currentData().toInt();
397 unsigned int widgetsMask = 0;
398 if (modified) {
399 cfn.swtch = RawSwitch(fswtchSwtch[i]->currentData().toInt());
400 cfn.func = func;
401 cfn.enabled = fswtchEnable[i]->isChecked();
402 cfn.adjustMode = (AssignFunc)fswtchGVmode[i]->currentIndex();
405 if (!cfn.isEmpty()) {
406 widgetsMask |= CUSTOM_FUNCTION_SHOW_FUNC;
408 if (func>=FuncOverrideCH1 && func<=FuncOverrideCH32) {
409 if (model) {
410 int channelsMax = model->getChannelsMax(true);
411 fswtchParam[i]->setDecimals(0);
412 fswtchParam[i]->setSingleStep(1);
413 fswtchParam[i]->setMinimum(-channelsMax);
414 fswtchParam[i]->setMaximum(channelsMax);
415 if (modified) {
416 cfn.param = fswtchParam[i]->value();
418 fswtchParam[i]->setValue(cfn.param);
419 widgetsMask |= CUSTOM_FUNCTION_NUMERIC_PARAM | CUSTOM_FUNCTION_ENABLE;
422 else if (func==FuncLogs) {
423 fswtchParam[i]->setDecimals(1);
424 fswtchParam[i]->setMinimum(0);
425 fswtchParam[i]->setMaximum(25.5);
426 fswtchParam[i]->setSingleStep(0.1);
427 if (modified)
428 cfn.param = fswtchParam[i]->value()*10.0;
429 fswtchParam[i]->setValue(cfn.param/10.0);
430 widgetsMask |= CUSTOM_FUNCTION_NUMERIC_PARAM;
432 else if (func>=FuncAdjustGV1 && func<=FuncAdjustGVLast) {
433 int gvidx = func - FuncAdjustGV1;
434 if (modified)
435 cfn.adjustMode = fswtchGVmode[i]->currentIndex();
436 widgetsMask |= CUSTOM_FUNCTION_GV_MODE | CUSTOM_FUNCTION_ENABLE;
437 if (cfn.adjustMode==FUNC_ADJUST_GVAR_CONSTANT || cfn.adjustMode==FUNC_ADJUST_GVAR_INCDEC) {
438 if (modified)
439 cfn.param = fswtchParam[i]->value() * model->gvarData[gvidx].multiplierSet();
440 if (IS_ARM(getCurrentBoard())) {
441 fswtchParam[i]->setDecimals(model->gvarData[gvidx].prec);
442 fswtchParam[i]->setSingleStep(model->gvarData[gvidx].multiplierGet());
443 fswtchParam[i]->setSuffix(model->gvarData[gvidx].unitToString());
444 fswtchParam[i]->setMinimum(model->gvarData[gvidx].getMinPrec());
445 fswtchParam[i]->setMaximum(model->gvarData[gvidx].getMaxPrec());
446 fswtchParam[i]->setValue(cfn.param * model->gvarData[gvidx].multiplierGet());
448 else {
449 fswtchParam[i]->setDecimals(0);
450 fswtchParam[i]->setSingleStep(1);
451 fswtchParam[i]->setMinimum(-125);
452 fswtchParam[i]->setMaximum(125);
453 fswtchParam[i]->setValue(cfn.param);
455 widgetsMask |= CUSTOM_FUNCTION_NUMERIC_PARAM;
457 else {
458 if (modified)
459 cfn.param = fswtchParamT[i]->currentData().toInt();
460 populateFuncParamCB(fswtchParamT[i], func, cfn.param, cfn.adjustMode);
461 widgetsMask |= CUSTOM_FUNCTION_SOURCE_PARAM;
464 else if (func==FuncReset) {
465 if (modified)
466 cfn.param = fswtchParamT[i]->currentData().toInt();
467 populateFuncParamCB(fswtchParamT[i], func, cfn.param);
468 widgetsMask |= CUSTOM_FUNCTION_SOURCE_PARAM | CUSTOM_FUNCTION_ENABLE;
470 else if (func>=FuncSetTimer1 && func<=FuncSetTimer3) {
471 if (modified)
472 cfn.param = fswtchParamTime[i]->timeInSeconds();
473 RawSourceRange range = RawSource(SOURCE_TYPE_SPECIAL, func - FuncSetTimer1 + 2).getRange(model, generalSettings);
474 fswtchParamTime[i]->setTimeRange((int)range.min, (int)range.max);
475 fswtchParamTime[i]->setTime(cfn.param);
476 widgetsMask |= CUSTOM_FUNCTION_TIME_PARAM | CUSTOM_FUNCTION_ENABLE;
478 else if (func>=FuncSetFailsafeInternalModule && func<=FuncBindExternalModule) {
479 widgetsMask |= CUSTOM_FUNCTION_ENABLE;
481 else if (func==FuncVolume) {
482 if (modified)
483 cfn.param = fswtchParamT[i]->currentData().toInt();
484 populateFuncParamCB(fswtchParamT[i], func, cfn.param);
485 widgetsMask |= CUSTOM_FUNCTION_SOURCE_PARAM | CUSTOM_FUNCTION_ENABLE;
487 else if (func==FuncPlaySound || func==FuncPlayHaptic || func==FuncPlayValue || func==FuncPlayPrompt || func==FuncPlayBoth || func==FuncBackgroundMusic) {
488 if (func != FuncBackgroundMusic) {
489 widgetsMask |= CUSTOM_FUNCTION_REPEAT;
490 fswtchRepeat[i]->update();
492 if (func==FuncPlayValue) {
493 if (modified)
494 cfn.param = fswtchParamT[i]->currentData().toInt();
495 populateFuncParamCB(fswtchParamT[i], func, cfn.param);
496 widgetsMask |= CUSTOM_FUNCTION_SOURCE_PARAM | CUSTOM_FUNCTION_REPEAT;
498 else if (func==FuncPlayPrompt || func==FuncPlayBoth) {
499 if (firmware->getCapability(VoicesAsNumbers)) {
500 fswtchParam[i]->setDecimals(0);
501 fswtchParam[i]->setSingleStep(1);
502 fswtchParam[i]->setMinimum(0);
503 if (func==FuncPlayPrompt) {
504 widgetsMask |= CUSTOM_FUNCTION_NUMERIC_PARAM | CUSTOM_FUNCTION_REPEAT | CUSTOM_FUNCTION_GV_TOOGLE;
506 else {
507 widgetsMask |= CUSTOM_FUNCTION_NUMERIC_PARAM | CUSTOM_FUNCTION_REPEAT;
508 fswtchParamGV[i]->setChecked(false);
510 fswtchParam[i]->setMaximum(func==FuncPlayBoth ? 254 : 255);
511 if (modified) {
512 if (fswtchParamGV[i]->isChecked()) {
513 fswtchParam[i]->setMinimum(1);
514 cfn.param = std::min(fswtchParam[i]->value(),5.0)+(fswtchParamGV[i]->isChecked() ? 250 : 0);
516 else {
517 cfn.param = fswtchParam[i]->value();
520 if (cfn.param>250 && (func!=FuncPlayBoth)) {
521 fswtchParamGV[i]->setChecked(true);
522 fswtchParam[i]->setValue(cfn.param-250);
523 fswtchParam[i]->setMaximum(5);
525 else {
526 fswtchParamGV[i]->setChecked(false);
527 fswtchParam[i]->setValue(cfn.param);
529 if (cfn.param < 251)
530 widgetsMask |= CUSTOM_FUNCTION_PLAY;
532 else {
533 widgetsMask |= CUSTOM_FUNCTION_FILE_PARAM;
534 if (modified) {
535 Helpers::getFileComboBoxValue(fswtchParamArmT[i], cfn.paramarm, firmware->getCapability(VoicesMaxLength));
537 Helpers::populateFileComboBox(fswtchParamArmT[i], tracksSet, cfn.paramarm);
538 if (fswtchParamArmT[i]->currentText() != "----") {
539 widgetsMask |= CUSTOM_FUNCTION_PLAY;
543 else if (func==FuncBackgroundMusic) {
544 widgetsMask |= CUSTOM_FUNCTION_FILE_PARAM;
545 if (modified) {
546 Helpers::getFileComboBoxValue(fswtchParamArmT[i], cfn.paramarm, firmware->getCapability(VoicesMaxLength));
548 Helpers::populateFileComboBox(fswtchParamArmT[i], tracksSet, cfn.paramarm);
549 if (fswtchParamArmT[i]->currentText() != "----") {
550 widgetsMask |= CUSTOM_FUNCTION_PLAY;
553 else if (func==FuncPlaySound) {
554 if (modified)
555 cfn.param = (uint8_t)fswtchParamT[i]->currentIndex();
556 populateFuncParamCB(fswtchParamT[i], func, cfn.param);
557 widgetsMask |= CUSTOM_FUNCTION_SOURCE_PARAM;
559 else if (func==FuncPlayHaptic) {
560 if (modified)
561 cfn.param = (uint8_t)fswtchParamT[i]->currentIndex();
562 populateFuncParamCB(fswtchParamT[i], func, cfn.param);
563 widgetsMask |= CUSTOM_FUNCTION_SOURCE_PARAM;
566 else if (func==FuncPlayScript) {
567 widgetsMask |= CUSTOM_FUNCTION_FILE_PARAM;
568 if (modified) {
569 Helpers::getFileComboBoxValue(fswtchParamArmT[i], cfn.paramarm, 8);
571 Helpers::populateFileComboBox(fswtchParamArmT[i], scriptsSet, cfn.paramarm);
573 else if (func==FuncBacklight && IS_TARANIS_PLUS(getCurrentBoard())) {
574 if (modified)
575 cfn.param = (uint8_t)fswtchBLcolor[i]->value();
576 fswtchBLcolor[i]->setValue(cfn.param);
577 widgetsMask |= CUSTOM_FUNCTION_BL_COLOR;
579 else {
580 if (modified)
581 cfn.param = fswtchParam[i]->value();
582 fswtchParam[i]->setDecimals(0);
583 fswtchParam[i]->setSingleStep(1);
584 fswtchParam[i]->setValue(cfn.param);
585 if (func <= FuncInstantTrim) {
586 widgetsMask |= CUSTOM_FUNCTION_ENABLE;
592 fswtchFunc[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_SHOW_FUNC);
593 fswtchParam[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_NUMERIC_PARAM);
594 fswtchParamTime[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_TIME_PARAM);
595 fswtchParamGV[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_GV_TOOGLE);
596 fswtchParamT[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_SOURCE_PARAM);
597 fswtchParamArmT[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_FILE_PARAM);
598 fswtchEnable[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_ENABLE);
599 if (widgetsMask & CUSTOM_FUNCTION_ENABLE)
600 fswtchEnable[i]->setChecked(cfn.enabled);
601 else
602 fswtchEnable[i]->setChecked(false);
603 fswtchRepeat[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_REPEAT);
604 fswtchGVmode[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_GV_MODE);
605 fswtchBLcolor[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_BL_COLOR);
606 playBT[i]->setVisible(widgetsMask & CUSTOM_FUNCTION_PLAY);
609 void CustomFunctionsPanel::update()
611 setDataModels();
613 lock = true;
614 int num_fsw = model ? firmware->getCapability(CustomFunctions) : firmware->getCapability(GlobalFunctions);
615 for (int i=0; i<num_fsw; i++) {
616 refreshCustomFunction(i);
618 lock = false;
621 void CustomFunctionsPanel::fswPaste()
623 const QClipboard *clipboard = QApplication::clipboard();
624 const QMimeData *mimeData = clipboard->mimeData();
625 if (mimeData->hasFormat("application/x-companion-fsw")) {
626 QByteArray fswData = mimeData->data("application/x-companion-fsw");
627 CustomFunctionData *fsw = &functions[selectedFunction];
628 memcpy(fsw, fswData.constData(), sizeof(CustomFunctionData));
629 lock = true;
630 fswtchSwtch[selectedFunction]->setCurrentIndex(fswtchSwtch[selectedFunction]->findData(functions[selectedFunction].swtch.toValue()));
631 fswtchFunc[selectedFunction]->setCurrentIndex(fswtchFunc[selectedFunction]->findData(functions[selectedFunction].func));
632 populateGVmodeCB(fswtchGVmode[selectedFunction], functions[selectedFunction].adjustMode);
633 populateFuncParamCB(fswtchParamT[selectedFunction], functions[selectedFunction].func, functions[selectedFunction].param, functions[selectedFunction].adjustMode);
634 refreshCustomFunction(selectedFunction);
635 lock = false;
636 emit modified();
640 void CustomFunctionsPanel::fswDelete()
642 functions[selectedFunction].clear();
643 // TODO update switch and func
644 lock = true;
645 fswtchSwtch[selectedFunction]->setCurrentIndex(fswtchSwtch[selectedFunction]->findData(functions[selectedFunction].swtch.toValue()));
646 fswtchFunc[selectedFunction]->setCurrentIndex(fswtchFunc[selectedFunction]->findData(functions[selectedFunction].func));
647 refreshCustomFunction(selectedFunction);
648 lock = false;
649 emit modified();
652 void CustomFunctionsPanel::fswCopy()
654 QByteArray fswData;
655 fswData.append((char*)&functions[selectedFunction], sizeof(CustomFunctionData));
656 QMimeData *mimeData = new QMimeData;
657 mimeData->setData("application/x-companion-fsw", fswData);
658 QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
661 void CustomFunctionsPanel::fswCut()
663 fswCopy();
664 fswDelete();
667 void CustomFunctionsPanel::fsw_customContextMenuRequested(QPoint pos)
669 QLabel *label = (QLabel *)sender();
670 selectedFunction = label->property("index").toInt();
672 QPoint globalPos = label->mapToGlobal(pos);
674 const QClipboard *clipboard = QApplication::clipboard();
675 const QMimeData *mimeData = clipboard->mimeData();
676 bool hasData = mimeData->hasFormat("application/x-companion-fsw");
678 QMenu contextMenu;
679 contextMenu.addAction(CompanionIcon("copy.png"), tr("&Copy"),this,SLOT(fswCopy()));
680 contextMenu.addAction(CompanionIcon("cut.png"), tr("&Cut"),this,SLOT(fswCut()));
681 contextMenu.addAction(CompanionIcon("paste.png"), tr("&Paste"),this,SLOT(fswPaste()))->setEnabled(hasData);
682 contextMenu.addAction(CompanionIcon("clear.png"), tr("&Delete"),this,SLOT(fswDelete()));
684 contextMenu.exec(globalPos);
687 void CustomFunctionsPanel::populateFuncCB(QComboBox *b, unsigned int value)
689 b->clear();
690 for (unsigned int i=0; i<FuncCount; i++) {
691 if (((i>=FuncOverrideCH1 && i<=FuncOverrideCH32) && (!model || !firmware->getCapability(SafetyChannelCustomFunction))) ||
692 ((i==FuncVolume || i==FuncBackgroundMusic || i==FuncBackgroundMusicPause) && !firmware->getCapability(HasVolume)) ||
693 ((i==FuncPlayScript && !IS_HORUS_OR_TARANIS(firmware->getBoard()))) ||
694 ((i==FuncPlayHaptic) && !firmware->getCapability(Haptic)) ||
695 ((i==FuncPlayBoth) && !firmware->getCapability(HasBeeper)) ||
696 ((i==FuncLogs) && !firmware->getCapability(HasSDLogs)) ||
697 ((i==FuncSetTimer1 || i==FuncSetTimer2) && !IS_ARM(firmware->getBoard())) ||
698 ((i==FuncSetTimer3) && firmware->getCapability(Timers) < 3) ||
699 ((i==FuncScreenshot) && !IS_TARANIS(firmware->getBoard())) ||
700 ((i>=FuncRangeCheckInternalModule && i<=FuncBindExternalModule) && (!model || !firmware->getCapability(DangerousFunctions))) ||
701 ((i>=FuncAdjustGV1 && i<=FuncAdjustGVLast) && (!model || !firmware->getCapability(Gvars)))
703 // skipped
704 continue;
706 else {
707 b->addItem(CustomFunctionData(AssignFunc(i)).funcToString(model), i);
708 if (i == value) {
709 b->setCurrentIndex(b->count()-1);
715 void CustomFunctionsPanel::populateGVmodeCB(QComboBox *b, unsigned int value)
717 b->clear();
718 b->addItem(tr("Value"));
719 b->addItem(tr("Source"));
720 b->addItem(tr("GVAR"));
721 b->addItem(tr("Increment"));
722 b->setCurrentIndex(value);
725 void CustomFunctionsPanel::populateFuncParamCB(QComboBox *b, uint function, unsigned int value, unsigned int adjustmode)
727 QStringList qs;
728 b->setModel(new QStandardItemModel(b)); // clear combo box but not any shared item model
729 if (function==FuncPlaySound) {
730 CustomFunctionData::populatePlaySoundParams(qs);
731 b->addItems(qs);
732 b->setCurrentIndex(value);
734 else if (function==FuncPlayHaptic) {
735 CustomFunctionData::populateHapticParams(qs);
736 b->addItems(qs);
737 b->setCurrentIndex(value);
739 else if (function==FuncReset) {
740 CustomFunctionData::populateResetParams(model, b, value);
742 else if (function==FuncVolume) {
743 b->setModel(rawSrcInputsItemModel);
744 b->setCurrentIndex(b->findData(value));
746 else if (function==FuncPlayValue) {
747 b->setModel(rawSrcAllItemModel);
748 b->setCurrentIndex(b->findData(value));
750 else if (function>=FuncAdjustGV1 && function<=FuncAdjustGVLast) {
751 switch (adjustmode) {
752 case 1:
753 b->setModel(rawSrcInputsItemModel);
754 b->setCurrentIndex(b->findData(value));
755 break;
756 case 2:
757 b->setModel(Helpers::getRawSourceItemModel(&generalSettings, model, POPULATE_GVARS));
758 b->setCurrentIndex(b->findData(value));
759 break;
762 else {
763 b->hide();