1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-15 04:15:26 +03:00

Merge with latest 2.3

This commit is contained in:
Bertrand Songis 2020-05-30 11:57:03 +02:00
commit bddccb9765
No known key found for this signature in database
GPG key ID: F189F79290FEC50F
240 changed files with 16053 additions and 14407 deletions

12
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,12 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: ['PayPal.Me/opentx']

View file

@ -6,7 +6,7 @@ set(VERSION_REVISION "0")
set(VERSION_SUFFIX $ENV{OPENTX_VERSION_SUFFIX})
set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR})
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX})
set(SDCARD_REVISION "0026")
set(SDCARD_REVISION "0028")
set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION})
cmake_minimum_required(VERSION 2.8)

View file

@ -11,6 +11,7 @@ Arne Schwabe (schwabe)
[Translators]
[fr] Sebastien Charpentier (LapinFou)
[es] Daniel Gorbea (dgatf)
[Other contributors]
Rob Thomson
@ -422,7 +423,6 @@ Pablo Gomez Martinez
Alan Thoren
Jack Clodfelter
William Simon
James Houck
Peter Fithern
Thomas Svedman
TechFixPros
@ -687,7 +687,6 @@ Shaun Currier
Danilo Schiara
John Hennig
RK Custom Homes
James Houck
Piet Teekens
Tommy Widenflycht
Johan Petrus Theophilus
@ -1707,3 +1706,30 @@ Fabrice Debonnet
James Laver
Kevin Bowens
David Frech
Udo Kastenholz
Julio Margarido
Uwe Muendelein
James Jewitt
Daniel Ferreira
Michael Charlston
Leopoldo Cicero
Tom Van Driel
Karl Vetter
Johannes Janssen
François Blondé
Precision Aero
Thomas Håkansson
Gordon Evans
Benny Simonsen
George Kennedy
Stefano Boggia
Maksym Arshynkin
Tomas Sodergren
Jörg Lemnitz
TMac FPV
Josef Schaeffler
Mike Frizell
MeCodeGoodSomeday
Timothy Fry
John Beech
Wojciech Winiarski

View file

@ -1,7 +1,7 @@
## OpenTX 2.3 Branch
[![Travis build Status](https://travis-ci.org/opentx/opentx.svg?branch=2.3)](https://travis-ci.org/opentx/opentx)
[![Join the Discord chat](https://img.shields.io/badge/discord-join_chat-yellow.svg)](https://discord.gg/pAmmCp2)
[![Join the Discord chat](https://img.shields.io/badge/discord-join_chat-yellow.svg)](https://discord.gg/CZCwVx2)
[![Donate using Paypal](https://img.shields.io/badge/paypal-donate-yellow.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=DJ9MASSKVW8WN)
The ongoing development on 2.3.x is done in this branch.

View file

@ -250,6 +250,7 @@ set(companion_SRCS
wizarddialog.cpp
styleeditdialog.cpp
dialogs/filesyncdialog.cpp # TODO move to own lib with other dialogs
profilechooser.cpp
)
set(companion_MOC_HDRS
@ -284,6 +285,7 @@ set(companion_MOC_HDRS
styleeditdialog.h
helpers_html.h
dialogs/filesyncdialog.h
profilechooser.h
)
set(companion_UIS
@ -305,6 +307,7 @@ set(companion_UIS
flasheepromdialog.ui
radionotfound.ui
styleeditdialog.ui
profilechooser.ui
)
if(WIN32)

View file

@ -73,6 +73,7 @@ void AppPreferencesDialog::accept()
g.OpenTxBranch(AppData::DownloadBranchType(ui->OpenTxBranch->currentIndex()));
g.autoCheckFw(ui->autoCheckFirmware->isChecked());
g.showSplash(ui->showSplash->isChecked());
g.promptProfile(ui->chkPromptProfile->isChecked());
g.simuSW(ui->simuSW->isChecked());
g.removeModelSlots(ui->opt_removeBlankSlots->isChecked());
g.newModelAction(ui->opt_newMdl_useWizard->isChecked() ? AppData::MODEL_ACT_WIZARD : ui->opt_newMdl_useEditor->isChecked() ? AppData::MODEL_ACT_EDITOR : AppData::MODEL_ACT_NONE);
@ -185,6 +186,7 @@ void AppPreferencesDialog::initSettings()
ui->autoCheckCompanion->setChecked(g.autoCheckApp());
ui->autoCheckFirmware->setChecked(g.autoCheckFw());
ui->showSplash->setChecked(g.showSplash());
ui->chkPromptProfile->setChecked(g.promptProfile());
ui->historySize->setValue(g.historySize());
ui->backLightColor->setCurrentIndex(g.backLight());
ui->volumeGain->setValue(profile.volumeGain() / 10.0);
@ -352,6 +354,13 @@ void AppPreferencesDialog::on_ge_pathButton_clicked()
}
}
void AppPreferencesDialog::on_btnClearPos_clicked()
{
SimulatorOptions opts = g.profile[g.sessionId()].simulatorOptions();
opts.controlsState.clear();
g.profile[g.sessionId()].simulatorOptions(opts);
}
#if defined(JOYSTICKS)
void AppPreferencesDialog::on_joystickChkB_clicked() {
if (ui->joystickChkB->isChecked()) {

View file

@ -66,6 +66,7 @@ class AppPreferencesDialog : public QDialog
void on_SplashSelect_clicked();
void on_clearImageButton_clicked();
void on_btn_appLogsDir_clicked();
void on_btnClearPos_clicked();
#if defined(JOYSTICKS)
void on_joystickChkB_clicked();

View file

@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>741</width>
<width>864</width>
<height>668</height>
</rect>
</property>
@ -45,7 +45,7 @@
</sizepolicy>
</property>
<property name="currentIndex">
<number>1</number>
<number>0</number>
</property>
<widget class="QWidget" name="profileTab">
<attribute name="title">
@ -61,22 +61,40 @@
</sizepolicy>
</property>
<layout class="QGridLayout" name="optionsLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="horizontalSpacing">
<number>5</number>
</property>
<property name="verticalSpacing">
<number>4</number>
</property>
<property name="margin">
<number>0</number>
</property>
</layout>
</widget>
</item>
<item row="7" column="0" colspan="4">
<widget class="QWidget" name="widget_splashImage" native="true">
<layout class="QGridLayout" name="gridLayout_5">
<property name="margin">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
@ -704,29 +722,90 @@ Mode 4:
<property name="spacing">
<number>6</number>
</property>
<item row="7" column="1">
<layout class="QVBoxLayout" name="updatesLayout">
<item>
<widget class="QCheckBox" name="autoCheckFirmware">
<property name="text">
<string>Automatic check for OpenTX firmware updates</string>
<item row="15" column="1">
<widget class="QCheckBox" name="backupEnable">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="checked">
<bool>true</bool>
<property name="text">
<string>Enable automatic backup before writing firmware</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoCheckCompanion">
<property name="text">
<string>Automatic check for Companion updates</string>
<item row="22" column="0">
<widget class="QLabel" name="ge_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="checked">
<bool>true</bool>
<property name="text">
<string>Google Earth Executable</string>
</property>
</widget>
</item>
</layout>
<item row="9" column="1">
<widget class="QComboBox" name="OpenTxBranch">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Releases (stable)</string>
</property>
</item>
<item>
<property name="text">
<string>Release candidates (testing)</string>
</property>
</item>
<item>
<property name="text">
<string>Nightly builds (unstable)</string>
</property>
</item>
</widget>
</item>
<item row="18" column="1">
<widget class="QComboBox" name="splashincludeCB">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Only show user splash images</string>
</property>
</item>
<item>
<property name="text">
<string>Show user and companion splash images</string>
</property>
</item>
</widget>
</item>
<item row="12" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="16" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10" stretch="0,1">
@ -764,120 +843,27 @@ Mode 4:
</item>
</layout>
</item>
<item row="6" column="0" rowspan="2">
<widget class="QLabel" name="label_16">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Startup Settings</string>
</property>
</widget>
</item>
<item row="21" column="0">
<widget class="QLabel" name="ge_label">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Google Earth Executable</string>
</property>
</widget>
</item>
<item row="11" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QCheckBox" name="showSplash">
<property name="text">
<string>Show splash screen when Companion starts</string>
<string>Show splash screen</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="13" column="0">
<widget class="QLabel" name="label_17">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Automatic Backup Folder</string>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remember</string>
</property>
</widget>
</item>
<item row="13" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item row="22" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="backupPath">
<widget class="QLineEdit" name="ge_lineedit">
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="backupPathButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="18" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLineEdit" name="libraryPath">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="libraryPathButton">
<widget class="QPushButton" name="ge_pathButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
@ -885,49 +871,13 @@ Mode 4:
</sizepolicy>
</property>
<property name="text">
<string>Select Folder</string>
<string>Select Executable</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="1">
<widget class="QCheckBox" name="opt_removeBlankSlots">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. &lt;/p&gt;&lt;p&gt;When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Remove empty model slots when deleting models (only applies for radios w/out categories)</string>
</property>
</widget>
</item>
<item row="24" column="0">
<widget class="QLabel" name="lbl_appLogsDir">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Output Logs Folder</string>
</property>
</widget>
</item>
<item row="23" column="0">
<widget class="QLabel" name="label_12">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Debug Output Logging</string>
</property>
</widget>
</item>
<item row="9" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QRadioButton" name="opt_newMdl_useWizard">
@ -962,90 +912,22 @@ Mode 4:
</layout>
</item>
<item row="14" column="1">
<widget class="QCheckBox" name="backupEnable">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Enable automatic backup before writing firmware</string>
</property>
</widget>
</item>
<item row="19" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="17" column="1">
<widget class="QComboBox" name="splashincludeCB">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<layout class="QHBoxLayout" name="horizontalLayout_8">
<item>
<property name="text">
<string>Only show user splash images</string>
</property>
</item>
<item>
<property name="text">
<string>Show user and companion splash images</string>
</property>
</item>
</widget>
</item>
<item row="15" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_5">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="21" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_9">
<item>
<widget class="QLineEdit" name="ge_lineedit">
<widget class="QLineEdit" name="backupPath">
<property name="readOnly">
<bool>true</bool>
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="ge_pathButton">
<widget class="QPushButton" name="backupPathButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select Executable</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="24" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="appLogsDir">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_appLogsDir">
<property name="text">
<string>Select Folder</string>
</property>
@ -1053,14 +935,56 @@ Mode 4:
</item>
</layout>
</item>
<item row="22" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
<item row="3" column="0">
<widget class="QLabel" name="label_5">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remember</string>
</property>
</widget>
</item>
<item row="18" column="0">
<item row="25" column="0">
<widget class="QLabel" name="lbl_appLogsDir">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Output Logs Folder</string>
</property>
</widget>
</item>
<item row="6" column="0" rowspan="3">
<widget class="QLabel" name="label_16">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Startup Settings</string>
</property>
</widget>
</item>
<item row="11" column="1">
<widget class="QCheckBox" name="opt_removeBlankSlots">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. &lt;/p&gt;&lt;p&gt;When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Remove empty model slots when deleting models (only applies for radios w/out categories)</string>
</property>
</widget>
</item>
<item row="19" column="0">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -1073,33 +997,7 @@ Mode 4:
</property>
</widget>
</item>
<item row="17" column="0">
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Splash Screen Library</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Action on New Model</string>
</property>
</widget>
</item>
<item row="23" column="1">
<item row="24" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="opt_appDebugLog">
@ -1123,36 +1021,163 @@ Mode 4:
</item>
</layout>
</item>
<item row="8" column="0">
<item row="25" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLineEdit" name="appLogsDir">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="btn_appLogsDir">
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="10" column="0">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Action on New Model</string>
</property>
</widget>
</item>
<item row="19" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QLineEdit" name="libraryPath">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="libraryPathButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Select Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="18" column="0">
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Splash Screen Library</string>
</property>
</widget>
</item>
<item row="9" column="0">
<widget class="QLabel" name="label_18">
<property name="text">
<string>Release channel</string>
</property>
</widget>
</item>
<item row="8" column="1">
<widget class="QComboBox" name="OpenTxBranch">
<item row="4" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="24" column="0">
<widget class="QLabel" name="label_12">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Releases (stable)</string>
<string>Debug Output Logging</string>
</property>
</widget>
</item>
<item row="23" column="0" colspan="2">
<widget class="Line" name="line_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="14" column="0">
<widget class="QLabel" name="label_17">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Automatic Backup Folder</string>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QVBoxLayout" name="updatesLayout">
<item>
<widget class="QCheckBox" name="autoCheckFirmware">
<property name="text">
<string>Automatic check for OpenTX firmware updates</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="autoCheckCompanion">
<property name="text">
<string>Release candidates (testing)</string>
<string>Automatic check for Companion updates</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
</layout>
</item>
<item row="20" column="0" rowspan="2" colspan="2">
<widget class="Line" name="line_7">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item row="7" column="1">
<widget class="QCheckBox" name="chkPromptProfile">
<property name="text">
<string>Nightly builds (unstable)</string>
<string>Prompt for radio profile</string>
</property>
</item>
</widget>
</item>
</layout>
@ -1180,7 +1205,38 @@ Mode 4:
<property name="spacing">
<number>6</number>
</property>
<item row="5" column="1">
<item row="10" column="1">
<widget class="QDoubleSpinBox" name="volumeGain">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.500000000000000</double>
</property>
<property name="maximum">
<double>3.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
</property>
</widget>
</item>
<item row="9" column="1">
<widget class="QComboBox" name="joystickCB">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -1193,14 +1249,20 @@ Mode 4:
</property>
</widget>
</item>
<item row="5" column="3">
<widget class="QPushButton" name="joystickcalButton">
<item row="0" column="0">
<widget class="QLabel" name="label_19">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Calibrate</string>
<string>Screenshot capture folder</string>
</property>
</widget>
</item>
<item row="3" column="1">
<item row="7" column="1">
<widget class="QComboBox" name="backLightColor">
<property name="maximumSize">
<size>
@ -1235,31 +1297,22 @@ Mode 4:
</item>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_19">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="9" column="3">
<widget class="QPushButton" name="joystickcalButton">
<property name="text">
<string>Screenshot capture folder</string>
<string>Calibrate</string>
</property>
</widget>
</item>
<item row="8" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
<item row="0" column="3">
<widget class="QPushButton" name="snapshotPathButton">
<property name="text">
<string>Select Folder</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
<property name="flat">
<bool>false</bool>
</property>
</spacer>
</widget>
</item>
<item row="1" column="1" colspan="3">
<widget class="QCheckBox" name="snapshotClipboardCKB">
@ -1274,7 +1327,7 @@ Mode 4:
</property>
</widget>
</item>
<item row="5" column="2">
<item row="9" column="2">
<widget class="QCheckBox" name="joystickChkB">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -1287,17 +1340,20 @@ Mode 4:
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QPushButton" name="snapshotPathButton">
<property name="text">
<string>Select Folder</string>
<item row="12" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="flat">
<bool>false</bool>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>5</height>
</size>
</property>
</widget>
</spacer>
</item>
<item row="5" column="0">
<item row="9" column="0">
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -1310,7 +1366,20 @@ Mode 4:
</property>
</widget>
</item>
<item row="3" column="0">
<item row="10" column="0">
<widget class="QLabel" name="label_volumeGain">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Simulator Volume Gain</string>
</property>
</widget>
</item>
<item row="7" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -1348,60 +1417,30 @@ Mode 4:
</property>
</widget>
</item>
<item row="2" column="1" colspan="3">
<item row="2" column="0">
<widget class="QLabel" name="label_20">
<property name="text">
<string>Simulator controls</string>
</property>
</widget>
</item>
<item row="2" column="1" colspan="2">
<widget class="QCheckBox" name="simuSW">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remember simulator switch values</string>
<string>Save switch/pot positions on simulator exit</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QLabel" name="label_volumeGain">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item row="2" column="3">
<widget class="QPushButton" name="btnClearPos">
<property name="text">
<string>Simulator Volume Gain</string>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QDoubleSpinBox" name="volumeGain">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="maximumSize">
<size>
<width>50</width>
<height>16777215</height>
</size>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.500000000000000</double>
</property>
<property name="maximum">
<double>3.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>1.000000000000000</double>
<string>Clear saved positions</string>
</property>
</widget>
</item>

View file

@ -1,5 +1,6 @@
set(firmwares_SRCS
adjustmentreference.cpp
boards.cpp
curvereference.cpp
customfunctiondata.cpp

View file

@ -0,0 +1,81 @@
/*
* 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 "adjustmentreference.h"
#include "radiodata.h" // for ModelData
AdjustmentReference::AdjustmentReference(int value)
{
int val = value;
if (abs(val) > ADJUST_REF_GVAR_BASE && abs(val) <= ADJUST_REF_GVAR_BASE + CPN_MAX_GVARS) {
type = ADJUST_REF_GVAR;
val = val >= 0 ? val - ADJUST_REF_GVAR_BASE : val + ADJUST_REF_GVAR_BASE;
}
else
this->type = ADJUST_REF_VALUE;
this->value = val;
}
AdjustmentReference::AdjustmentReference(AdjustRefType type, int value)
{
this->type = type;
if (isValid(value))
this->value = value;
else
clear();
}
bool AdjustmentReference::isValid(const int value) const
{
switch(type) {
case ADJUST_REF_VALUE:
if (abs(value) < ADJUST_REF_GVAR_BASE)
return true;
break;
case ADJUST_REF_GVAR:
if (abs(value) > 0 && abs(value) <= CPN_MAX_GVARS);
return true;
break;
}
return false;
}
QString AdjustmentReference::toString(const ModelData * model, const bool sign) const
{
QString ret;
switch(type) {
case ADJUST_REF_GVAR:
ret = RawSource(SOURCE_TYPE_GVAR, abs(value) - 1).toString(model);
if (value < 0)
ret.prepend("-");
else if (sign)
ret.prepend("+");
break;
default:
ret = "%1%";
if (sign && value > 0)
ret.prepend("+");
ret = ret.arg(value);
break;
}
return ret;
}

View file

@ -0,0 +1,69 @@
/*
* 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.
*/
#ifndef ADJUSTMENTREFERENCE_H
#define ADJUSTMENTREFERENCE_H
#include <QtCore>
class ModelData;
constexpr int ADJUST_REF_GVAR_BASE { 10000 };
class AdjustmentReference {
Q_DECLARE_TR_FUNCTIONS(AdjustmentReference)
public:
enum AdjustRefType {
ADJUST_REF_VALUE,
ADJUST_REF_GVAR,
};
AdjustmentReference() { clear(); }
explicit AdjustmentReference(int value);
AdjustmentReference(const AdjustRefType type, const int value);
void clear() { memset(this, 0, sizeof(AdjustmentReference)); }
bool isSet() const { return type != ADJUST_REF_VALUE || value != 0; }
bool isValid(const int value) const;
QString toString(const ModelData * model = nullptr, const bool sign = false) const;
inline const int toValue() const
{
if (type == ADJUST_REF_GVAR)
return value >= 0 ? value + ADJUST_REF_GVAR_BASE : value - ADJUST_REF_GVAR_BASE;
else
return value;
}
bool operator== ( const AdjustmentReference & other) const {
return (this->type == other.type) && (this->value == other.value);
}
bool operator!= ( const AdjustmentReference & other) const {
return (this->type != other.type) || (this->value != other.value);
}
AdjustRefType type;
int value;
};
#endif // ADJUSTMENTREFERENCE_H

View file

@ -50,6 +50,7 @@ class CurveReference {
int value;
QString toString(const ModelData * model = NULL, bool verbose = true) const;
bool isSet() const { return type != CURVE_REF_DIFF || value != 0; }
};
#endif // CURVEREFERENCE_H

View file

@ -100,6 +100,8 @@ enum Capability {
GvarsName,
NoTelemetryProtocol,
TelemetryCustomScreens,
TelemetryCustomScreensBars,
TelemetryCustomScreensLines,
TelemetryCustomScreensFieldsPerLine,
TelemetryMaxMultiplier,
HasVario,
@ -149,7 +151,8 @@ enum Capability {
DangerousFunctions,
HasModelCategories,
HasSwitchableJack,
PwrButtonPress
PwrButtonPress,
Sensors
};
class EEPROMInterface

View file

@ -123,6 +123,12 @@ GeneralSettings::GeneralSettings()
strcpy(bluetoothName, "taranis");
}
for (uint8_t i = 0; i < 4; i++) {
trainer.mix[i].mode = 2; // replace (:=)
trainer.mix[i].src = i;
trainer.mix[i].weight = 100;
}
templateSetup = g.profile[g.sessionId()].channelOrder();
stickMode = g.profile[g.sessionId()].defaultMode();

View file

@ -90,3 +90,8 @@ float GVarData::getMaxPrec() const
{
return getMax() * multiplierGet();
}
bool GVarData::isEmpty() const
{
return (name[0] == '\0' && min == 0 && max == 0 && (!popup) && prec == 0 && unit == 0);
}

View file

@ -62,6 +62,7 @@ class GVarData {
int getMax() const;
float getMinPrec() const;
float getMaxPrec() const;
bool isEmpty() const;
};

View file

@ -35,6 +35,10 @@ void ExpoData::convert(RadioDataConversionState & cstate)
swtch.convert(cstate);
}
bool ExpoData::isEmpty() const
{
return (chn == 0 && mode == INPUT_MODE_NONE);
}
/*
* MixData
@ -48,6 +52,10 @@ void MixData::convert(RadioDataConversionState & cstate)
swtch.convert(cstate);
}
bool MixData::isEmpty() const
{
return (destCh == 0);
}
/*
* LimitData
@ -87,17 +95,16 @@ void LimitData::clear()
bool LimitData::isEmpty() const
{
return (min == -1000 && max == 1000 && !revert && !offset && !ppmCenter && !symetrical && name[0] == '\0');
return (min == -1000 && max == 1000 && !revert && !offset && !ppmCenter && !symetrical && name[0] == '\0' && !curve.isSet());
}
/*
* CurveData
*/
CurveData::CurveData()
{
clear(5);
clear();
}
void CurveData::clear(int count)
@ -126,22 +133,20 @@ QString CurveData::nameToString(const int idx) const
* FlightModeData
*/
void FlightModeData::clear(const int phase)
void FlightModeData::clear(const int phaseIdx)
{
memset(reinterpret_cast<void *>(this), 0, sizeof(FlightModeData));
if (phase != 0) {
for (int idx=0; idx<CPN_MAX_GVARS; idx++) {
gvars[idx] = 1025;
}
for (int idx=0; idx<CPN_MAX_ENCODERS; idx++) {
rotaryEncoders[idx] = 1025;
for (int i = 0; i < CPN_MAX_GVARS; i++) {
gvars[i] = linkedGVarFlightModeZero(phaseIdx);
}
for (int i = 0; i < CPN_MAX_ENCODERS; i++) {
rotaryEncoders[i] = linkedREncFlightModeZero(phaseIdx);
}
}
QString FlightModeData::nameToString(int index) const
QString FlightModeData::nameToString(int phaseIdx) const
{
return RadioData::getElementName(tr("FM"), index, name); // names are zero-based, FM0, FM1, etc
return RadioData::getElementName(tr("FM"), phaseIdx, name); // names are zero-based, FM0, FM1, etc
}
void FlightModeData::convert(RadioDataConversionState & cstate)
@ -150,3 +155,53 @@ void FlightModeData::convert(RadioDataConversionState & cstate)
cstate.setSubComp(nameToString(cstate.subCompIdx));
swtch.convert(cstate);
}
bool FlightModeData::isEmpty(int phaseIdx) const
{
if (name[0] != '\0' || swtch.isSet() || fadeIn != 0 || fadeOut != 0)
return false;
for (int i = 0; i < CPN_MAX_TRIMS; i++) {
if (trim[i] != 0 || trimRef[i] != 0 || trimMode[i] != 0)
return false;
}
for (int i = 0; i < CPN_MAX_GVARS; i++) {
if (!isGVarEmpty(phaseIdx, i))
return false;
}
for (int i = 0; i < CPN_MAX_ENCODERS; i++) {
if (!isREncEmpty(phaseIdx, i))
return false;
}
return true;
}
bool FlightModeData::isGVarEmpty(int phaseIdx, int gvIdx) const
{
if ((phaseIdx == 0 && gvars[gvIdx] == 0) || (phaseIdx != 0 && gvars[gvIdx] == linkedGVarFlightModeZero(phaseIdx)))
return true;
return false;
}
bool FlightModeData::isREncEmpty(int phaseIdx, int reIdx) const
{
if ((phaseIdx == 0 && rotaryEncoders[reIdx] == 0) || (phaseIdx != 0 && rotaryEncoders[reIdx] == linkedREncFlightModeZero(phaseIdx)))
return true;
return false;
}
int FlightModeData::linkedFlightModeZero(int phaseIdx, int maxOwnValue) const
{
if (phaseIdx == 0)
return 0;
return maxOwnValue + 1;
}
int FlightModeData::linkedGVarFlightModeZero(int phaseIdx) const
{
return linkedFlightModeZero(phaseIdx, GVAR_MAX_VALUE);
}
int FlightModeData::linkedREncFlightModeZero(int phaseIdx) const
{
return linkedFlightModeZero(phaseIdx, RENC_MAX_VALUE);
}

View file

@ -37,6 +37,8 @@ enum InputMode {
INPUT_MODE_BOTH
};
#define EXPODATA_NAME_LEN 10
class ExpoData {
Q_DECLARE_TR_FUNCTIONS(ExpoData)
@ -52,9 +54,10 @@ class ExpoData {
int offset;
CurveReference curve;
int carryTrim;
char name[10+1];
char name[EXPODATA_NAME_LEN+1];
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(ExpoData)); }
void convert(RadioDataConversionState & cstate);
bool isEmpty() const;
};
enum MltpxValue {
@ -90,8 +93,11 @@ class MixData {
char name[MIXDATA_NAME_LEN+1];
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(MixData)); }
bool isEmpty() const;
};
#define LIMITDATA_NAME_LEN 6
class LimitData {
Q_DECLARE_TR_FUNCTIONS(LimitData)
@ -103,7 +109,7 @@ class LimitData {
int offset;
int ppmCenter;
bool symetrical;
char name[6+1];
char name[LIMITDATA_NAME_LEN+1];
CurveReference curve;
QString minToString() const;
QString maxToString() const;
@ -120,6 +126,8 @@ class CurvePoint {
int8_t y;
};
#define CURVEDATA_NAME_LEN 6
class CurveData {
Q_DECLARE_TR_FUNCTIONS(CurveData)
@ -131,7 +139,7 @@ class CurveData {
};
CurveData();
void clear(int count);
void clear(int count = 5);
bool isEmpty() const;
QString nameToString(const int idx) const;
@ -139,9 +147,13 @@ class CurveData {
bool smooth;
int count;
CurvePoint points[CPN_MAX_POINTS];
char name[6+1];
char name[CURVEDATA_NAME_LEN+1];
};
#define FLIGHTMODE_NAME_LEN 10
#define RENC_MAX_VALUE 1024
#define RENC_MIN_VALUE -RENC_MAX_VALUE
class FlightModeData {
Q_DECLARE_TR_FUNCTIONS(FlightModeData)
@ -151,14 +163,20 @@ class FlightModeData {
int trimRef[CPN_MAX_TRIMS];
int trim[CPN_MAX_TRIMS];
RawSwitch swtch;
char name[10+1];
char name[FLIGHTMODE_NAME_LEN+1];
unsigned int fadeIn;
unsigned int fadeOut;
int rotaryEncoders[CPN_MAX_ENCODERS];
int gvars[CPN_MAX_GVARS];
void clear(const int phase);
QString nameToString(int index) const;
void clear(const int phaseIdx);
QString nameToString(int phaseIdx) const;
void convert(RadioDataConversionState & cstate);
bool isEmpty(int phaseIdx) const;
bool isGVarEmpty(int phaseIdx, int gvIdx) const;
bool isREncEmpty(int phaseIdx, int reIdx) const;
int linkedFlightModeZero(int phaseIdx, int maxOwnValue) const;
int linkedGVarFlightModeZero(int phaseIdx) const;
int linkedREncFlightModeZero(int phaseIdx) const;
};
class SwashRingData {

View file

@ -25,6 +25,7 @@
#include "macros.h"
#include "radiodataconversionstate.h"
#include "helpers.h"
#include "adjustmentreference.h"
/*
* TimerData
@ -37,6 +38,10 @@ void TimerData::convert(RadioDataConversionState & cstate)
mode.convert(cstate);
}
bool TimerData::isEmpty()
{
return (mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0) && name[0] == '\0' && minuteBeep == 0 && countdownBeep == COUNTDOWN_SILENT && val == 0 && persistent == 0 /*&& pvalue == 0*/);
}
/*
* ModelData
@ -69,7 +74,7 @@ bool ModelData::isInputValid(const unsigned int idx) const
{
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
const ExpoData * expo = &expoData[i];
if (expo->mode == 0) break;
if (expo->mode == INPUT_MODE_NONE) break;
if (expo->chn == idx)
return true;
}
@ -80,7 +85,7 @@ bool ModelData::hasExpos(uint8_t inputIdx) const
{
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
const ExpoData & expo = expoData[i];
if (expo.chn==inputIdx && expo.mode!=0) {
if (expo.chn == inputIdx && expo.mode != INPUT_MODE_NONE) {
return true;
}
}
@ -103,7 +108,7 @@ QVector<const ExpoData *> ModelData::expos(int input) const
QVector<const ExpoData *> result;
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
const ExpoData * ed = &expoData[i];
if ((int)ed->chn==input && ed->mode!=0) {
if ((int)ed->chn == input && ed->mode != INPUT_MODE_NONE) {
result << ed;
}
}
@ -182,7 +187,7 @@ void ModelData::clear()
for (int i = 0; i < CPN_MAX_SPECIAL_FUNCTIONS; i++)
customFn[i].clear();
for (int i = 0; i < CPN_MAX_CURVES; i++)
curves[i].clear(5);
curves[i].clear();
for (int i = 0; i < CPN_MAX_TIMERS; i++)
timers[i].clear();
swashRingData.clear();
@ -270,19 +275,68 @@ int ModelData::getTrimValue(int phaseIdx, int trimIdx)
bool ModelData::isGVarLinked(int phaseIdx, int gvarIdx)
{
return flightModeData[phaseIdx].gvars[gvarIdx] > 1024;
return flightModeData[phaseIdx].gvars[gvarIdx] > GVAR_MAX_VALUE;
}
int ModelData::getGVarFieldValue(int phaseIdx, int gvarIdx)
bool ModelData::isGVarLinkedCircular(int phaseIdx, int gvarIdx)
{
int idx = flightModeData[phaseIdx].gvars[gvarIdx];
for (int i=0; idx>GVAR_MAX_VALUE && i<CPN_MAX_FLIGHT_MODES; i++) {
int nextPhase = idx - GVAR_MAX_VALUE - 1;
if (nextPhase >= phaseIdx) nextPhase += 1;
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
int val = flightModeData[phaseIdx].gvars[gvarIdx];
if (phaseIdx == 0 || val <= GVAR_MAX_VALUE)
return false;
int nextPhase = val - (GVAR_MAX_VALUE + 1);
if (nextPhase >= phaseIdx)
nextPhase += 1;
phaseIdx = nextPhase;
idx = flightModeData[phaseIdx].gvars[gvarIdx];
}
return idx;
return true;
}
int ModelData::getGVarValue(int phaseIdx, int gvarIdx)
{
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
int val = flightModeData[phaseIdx].gvars[gvarIdx];
if (phaseIdx == 0 || val <= GVAR_MAX_VALUE)
return val;
int nextPhase = val - (GVAR_MAX_VALUE + 1);
if (nextPhase >= phaseIdx)
nextPhase += 1;
phaseIdx = nextPhase;
}
return flightModeData[0].gvars[gvarIdx]; // circular linked so return FM0 value
}
bool ModelData::isREncLinked(int phaseIdx, int reIdx)
{
return flightModeData[phaseIdx].rotaryEncoders[reIdx] > RENC_MAX_VALUE;
}
bool ModelData::isREncLinkedCircular(int phaseIdx, int reIdx)
{
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
int val = flightModeData[phaseIdx].rotaryEncoders[reIdx];
if (phaseIdx == 0 || val <= RENC_MAX_VALUE)
return false;
int nextPhase = val - (RENC_MAX_VALUE + 1);
if (nextPhase >= phaseIdx)
nextPhase += 1;
phaseIdx = nextPhase;
}
return true;
}
int ModelData::getREncValue(int phaseIdx, int reIdx)
{
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
int val = flightModeData[phaseIdx].rotaryEncoders[reIdx];
if (phaseIdx == 0 || val <= RENC_MAX_VALUE)
return val;
int nextPhase = val - (RENC_MAX_VALUE + 1);
if (nextPhase >= phaseIdx)
nextPhase += 1;
phaseIdx = nextPhase;
}
return flightModeData[0].rotaryEncoders[reIdx]; // circular linked so return FM0 value
}
void ModelData::setTrimValue(int phaseIdx, int trimIdx, int value)
@ -364,9 +418,9 @@ bool ModelData::isAvailable(const RawSwitch & swtch) const
}
}
float ModelData::getGVarFieldValuePrec(int phaseIdx, int gvarIdx)
float ModelData::getGVarValuePrec(int phaseIdx, int gvarIdx)
{
return getGVarFieldValue(phaseIdx, gvarIdx) * gvarData[gvarIdx].multiplierGet();
return getGVarValue(phaseIdx, gvarIdx) * gvarData[gvarIdx].multiplierGet();
}
void ModelData::convert(RadioDataConversionState & cstate)
@ -412,3 +466,837 @@ void ModelData::convert(RadioDataConversionState & cstate)
moduleData[i].convert(cstate.withComponentIndex(i));
}
}
#define MAX_REF_UPDATES 100
void ModelData::appendUpdateReferenceParams(const ReferenceUpdateType type, const ReferenceUpdateAction action, const int index1, const int index2, const int shift)
{
if (updRefList) {
//qDebug() << "Append parameters - type:" << type << " action:" << action << " index1:" << index1 << " index2:" << index2 << " shift:" << shift;
if (updRefList->size() <= MAX_REF_UPDATES)
updRefList->append(UpdateReferenceParams(type, action, index1, index2, shift));
else
qDebug() << "Warning: Update ignored as the list of updates exceeds " << MAX_REF_UPDATES;
}
}
int ModelData::updateAllReferences(const ReferenceUpdateType type, const ReferenceUpdateAction action, const int index1, const int index2, const int shift)
{
//Stopwatch s1("ModelData::updateAllReferences");
//s1.report("Start");
int loopcnt = 0;
int updcnt = 0;
updRefList = nullptr;
QVector<UpdateReferenceParams> updRefParams; // declaring this variable in ModelData class crashes program on opening model file
updRefList = &updRefParams; // so declare pointer variable in ModelData class and pass it the address of the local array
if (updRefList) {
appendUpdateReferenceParams(type, action, index1, index2, shift);
while (!updRefList->isEmpty())
{
if (++loopcnt > MAX_REF_UPDATES) {
qDebug() << "Warning: Update iterations terminated early as the list exceeded " << MAX_REF_UPDATES;
break;
}
//qDebug() << "Start of iteration:" << loopcnt;
updcnt += updateReference();
updRefList->removeFirst();
}
}
qDebug() << "Iterations:" << loopcnt << " References updated:" << updcnt;
//s1.report("Finish");
return updcnt;
}
int ModelData::updateReference()
{
UpdateReferenceParams p = updRefList->first();
if (p.action < REF_UPD_ACT_CLEAR || p.action > REF_UPD_ACT_SWAP || p.type < REF_UPD_TYPE_CHANNEL || p.type > REF_UPD_TYPE_TIMER) {
qDebug() << "Error - invalid parameters" << " > type:" << p.type << " action:" << p.action << " index1:" << p.index1 << " index2:" << p.index2 << " shift:" << p.shift;
return 0;
}
memset(&updRefInfo, 0, sizeof(updRefInfo));
updRefInfo.type = p.type;
updRefInfo.action = p.action;
updRefInfo.index1 = abs(p.index1);
updRefInfo.index2 = abs(p.index2);
updRefInfo.shift = p.shift;
if ((updRefInfo.action == REF_UPD_ACT_SWAP && updRefInfo.index1 == updRefInfo.index2) || (updRefInfo.action == REF_UPD_ACT_SHIFT && updRefInfo.shift == 0)) {
qDebug() << "Warning - nothing to do" << " > type:" << updRefInfo.type << " action:" << updRefInfo.action << " index1:" << updRefInfo.index1 << " index2:" << updRefInfo.index2 << " shift:" << updRefInfo.shift;
return 0;
}
//Stopwatch s1("ModelData::updateReference");
//s1.report("Start");
Firmware *fw = getCurrentFirmware();
switch (updRefInfo.type)
{
case REF_UPD_TYPE_CHANNEL:
updRefInfo.srcType = SOURCE_TYPE_CH;
updRefInfo.maxindex = fw->getCapability(Outputs);
break;
case REF_UPD_TYPE_CURVE:
updRefInfo.maxindex = fw->getCapability(NumCurves);
break;
case REF_UPD_TYPE_FLIGHT_MODE:
updRefInfo.swtchType = SWITCH_TYPE_FLIGHT_MODE;
updRefInfo.maxindex = fw->getCapability(FlightModes);
break;
case REF_UPD_TYPE_GLOBAL_VARIABLE:
updRefInfo.srcType = SOURCE_TYPE_GVAR;
updRefInfo.maxindex = fw->getCapability(Gvars);
break;
case REF_UPD_TYPE_INPUT:
updRefInfo.srcType = SOURCE_TYPE_VIRTUAL_INPUT;
updRefInfo.maxindex = fw->getCapability(VirtualInputs);
break;
case REF_UPD_TYPE_LOGICAL_SWITCH:
updRefInfo.srcType = SOURCE_TYPE_CUSTOM_SWITCH;
updRefInfo.swtchType = SWITCH_TYPE_VIRTUAL;
updRefInfo.maxindex = fw->getCapability(LogicalSwitches);
break;
case REF_UPD_TYPE_SCRIPT:
updRefInfo.srcType = SOURCE_TYPE_LUA_OUTPUT;
updRefInfo.maxindex = fw->getCapability(LuaScripts);
break;
case REF_UPD_TYPE_SENSOR:
updRefInfo.srcType = SOURCE_TYPE_TELEMETRY;
updRefInfo.swtchType = SWITCH_TYPE_SENSOR;
updRefInfo.maxindex = fw->getCapability(Sensors);
break;
case REF_UPD_TYPE_TIMER:
updRefInfo.srcType = SOURCE_TYPE_SPECIAL;
updRefInfo.maxindex = fw->getCapability(Timers);
// rawsource index offset TODO refactor timers so be own category
updRefInfo.index1 += 2;
updRefInfo.index2 += 2;
updRefInfo.maxindex += 2;
break;
default:
qDebug() << "Error - unhandled reference type:" << updRefInfo.type;
return 0;
}
updRefInfo.maxindex--; // getCapabilities and constants are 1 based
//qDebug() << "updRefInfo - type:" << updRefInfo.type << " action:" << updRefInfo.action << " index1:" << updRefInfo.index1 << " index2:" << updRefInfo.index2 << " shift:" << updRefInfo.shift;
//qDebug() << "maxindex:" << updRefInfo.maxindex << "updRefInfo - srcType:" << updRefInfo.srcType << " swtchType:" << updRefInfo.swtchType;
//s1.report("Initialise");
for (int i = fw->getCapability(NumFirstUsableModule); i < fw->getCapability(NumModules); i++) {
ModuleData *md = &moduleData[i];
if (md->protocol != PULSES_OFF && md->failsafeMode == FAILSAFE_CUSTOM && md->hasFailsafes(fw))
updateModuleFailsafes(md);
}
//s1.report("Modules");
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
TimerData *td = &timers[i];
if (!td->isModeOff()) {
updateTimerMode(td->mode);
if (td->isModeOff())
appendUpdateReferenceParams(REF_UPD_TYPE_TIMER, REF_UPD_ACT_CLEAR, i);
}
}
//s1.report("Timers");
for (int i = 1; i < CPN_MAX_FLIGHT_MODES; i++) { // skip FM0 as switch not used
FlightModeData *fmd = &flightModeData[i];
if (fmd->swtch.isSet()) {
updateSwitchRef(fmd->swtch);
if(!fmd->swtch.isSet())
appendUpdateReferenceParams(REF_UPD_TYPE_FLIGHT_MODE, REF_UPD_ACT_CLEAR, i);
}
}
//s1.report("Flight Modes");
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
ExpoData *ed = &expoData[i];
if (!ed->isEmpty()) {
updateSourceRef(ed->srcRaw);
if (ed->srcRaw.isSet()) {
updateSwitchRef(ed->swtch);
updateCurveRef(ed->curve);
updateAdjustRef(ed->weight);
updateAdjustRef(ed->offset);
updateFlightModeFlags(ed->flightModes);
}
else {
unsigned int chnsave = ed->chn;
removeInput(i);
i--;
if (!hasExpos(chnsave))
appendUpdateReferenceParams(REF_UPD_TYPE_INPUT, REF_UPD_ACT_CLEAR, chnsave);
}
}
}
//s1.report("Inputs");
for (int i = 0; i < CPN_MAX_MIXERS; i++) {
MixData *md = &mixData[i];
if (!md->isEmpty()) {
updateDestCh(md);
if (!md->isEmpty()) {
updateSourceRef(md->srcRaw);
if (md->srcRaw.isSet()) {
updateSwitchRef(md->swtch);
updateCurveRef(md->curve);
updateAdjustRef(md->weight);
updateAdjustRef(md->sOffset);
updateFlightModeFlags(md->flightModes);
}
else {
removeMix(i);
i--;
}
}
}
}
//s1.report("Mixes");
for (int i = 0; i < CPN_MAX_CHNOUT; i++) {
LimitData *ld = &limitData[i];
if (!ld->isEmpty()) {
updateAdjustRef(ld->min);
updateAdjustRef(ld->max);
updateAdjustRef(ld->offset);
updateLimitCurveRef(ld->curve);
}
}
//s1.report("Outputs");
for (int i = 0; i < CPN_MAX_LOGICAL_SWITCHES; i++) {
LogicalSwitchData *lsd = &logicalSw[i];
if (!lsd->isEmpty()) {
bool clearlsd = false;
CSFunctionFamily family = lsd->getFunctionFamily();
switch(family) {
case LS_FAMILY_VOFS:
updateSourceIntRef(lsd->val1);
if (lsd->val1 == 0)
clearlsd = true;
break;
case LS_FAMILY_STICKY:
case LS_FAMILY_VBOOL:
updateSwitchIntRef(lsd->val1);
updateSwitchIntRef(lsd->val2);
if (lsd->val1 == 0 && lsd->val2 == 0)
clearlsd = true;
break;
case LS_FAMILY_EDGE:
updateSwitchIntRef(lsd->val1);
if (lsd->val1 == 0)
clearlsd = true;
break;
case LS_FAMILY_VCOMP:
updateSourceIntRef(lsd->val1);
updateSourceIntRef(lsd->val2);
if (lsd->val1 == 0 && lsd->val2 == 0)
clearlsd = true;
break;
default:
break;
}
if (clearlsd) {
lsd->clear();
appendUpdateReferenceParams(REF_UPD_TYPE_LOGICAL_SWITCH, REF_UPD_ACT_CLEAR, i);
}
else {
updateSwitchIntRef(lsd->andsw);
}
}
}
//s1.report("Logical Switches");
for (int i = 0; i < CPN_MAX_SPECIAL_FUNCTIONS; i++) {
CustomFunctionData *cfd = &customFn[i];
if (!cfd->isEmpty()) {
updateAssignFunc(cfd);
if (!cfd->isEmpty()) {
updateSwitchRef(cfd->swtch);
if (cfd->func == FuncVolume || cfd->func == FuncPlayValue || (cfd->func >= FuncAdjustGV1 && cfd->func <= FuncAdjustGVLast && (cfd->adjustMode == FUNC_ADJUST_GVAR_GVAR || cfd->adjustMode == FUNC_ADJUST_GVAR_SOURCE))) {
updateSourceIntRef(cfd->param);
if (cfd->param == 0)
cfd->clear();
}
}
}
}
//s1.report("Special Functions");
if (fw->getCapability(Heli)) {
updateSourceRef(swashRingData.aileronSource);
updateSourceRef(swashRingData.collectiveSource);
updateSourceRef(swashRingData.elevatorSource);
//s1.report("Heli");
}
if (fw->getCapability(Telemetry)) {
updateTelemetryRef(frsky.voltsSource);
updateTelemetryRef(frsky.altitudeSource);
updateTelemetryRef(frsky.currentSource);
updateTelemetryRef(frsky.varioSource);
for (int i = 0; i < fw->getCapability(TelemetryCustomScreens); i++) {
switch(frsky.screens[i].type) {
case TELEMETRY_SCREEN_BARS:
for (int j = 0; j < fw->getCapability(TelemetryCustomScreensBars); j++) {
FrSkyBarData *fbd = &frsky.screens[i].body.bars[j];
updateSourceRef(fbd->source);
if (!fbd->source.isSet()) {
fbd->barMin = 0;
fbd->barMax = 0;
}
}
break;
case TELEMETRY_SCREEN_NUMBERS:
for (int j = 0; j < fw->getCapability(TelemetryCustomScreensLines); j++) {
FrSkyLineData *fld = &frsky.screens[i].body.lines[j];
for (int k = 0; k < fw->getCapability(TelemetryCustomScreensFieldsPerLine); k++) {
updateSourceRef(fld->source[k]);
}
}
break;
default:
break;
}
}
//s1.report("Telemetry");
}
// TODO maybe less risk to leave it up to the user??
/*
for (int i = 0; i < CPN_MAX_SENSORS; i++) {
SensorData *sd = &sensorData[i];
if (sd->isAvailable() && sd->type == SensorData::TELEM_TYPE_CALCULATED) {
}
}
s1.report("Telemetry Sensors");
*/
// TODO needs lua incorporated into Companion as script needs to be parsed to determine if input field is source or value
/*
for (int i=0; i < CPN_MAX_SCRIPTS; i++) {
ScriptData *sd = &scriptData[i];
if (sd->filename[0] != '\0') {
for (int j = 0; j < CPN_MAX_SCRIPT_INPUTS; j++) {
// get input parameters and for each one where type is SOURCE
if(inputtype = "SOURCE")
updateSourceIntRef(sd->inputs[j]);
}
}
}
s1.report("Custom Scripts");
*/
// TODO Horus CustomScreenData and TopBarData will need checking for updates but Companion does not current handle ie just data blobs refer modeldata.h
qDebug() << "References updated this iteration:" << updRefInfo.updcnt;
//s1.report("Finish");
return updRefInfo.updcnt;
}
template <class R, typename T>
void ModelData::updateTypeIndexRef(R & curRef, const T type, const int idxAdj, const bool defClear, const int defType, const int defIndex)
{
//qDebug() << "Raw value: " << curRef.toValue() << " String:" << curRef.toString() << " Type: " << curRef.type << " Index:" << curRef.index << " idxAdj:" << idxAdj << " defClear:" << defClear << " defType:" << defType << " defIndex:" << defIndex;
if (!curRef.isSet() || curRef.type != type)
return;
R newRef;
newRef.type = curRef.type;
newRef.index = abs(curRef.index);
switch (updRefInfo.action)
{
case REF_UPD_ACT_CLEAR:
if (newRef.index != (updRefInfo.index1 + idxAdj))
return;
if (defClear)
newRef.clear();
else {
newRef.type = (T)defType;
newRef.index = defIndex;
}
break;
case REF_UPD_ACT_SHIFT:
if (newRef.index < (updRefInfo.index1 + idxAdj))
return;
newRef.index += updRefInfo.shift;
if (newRef.index < (updRefInfo.index1 + idxAdj) || newRef.index > (updRefInfo.maxindex + idxAdj)) {
if (defClear)
newRef.clear();
else {
newRef.type = (T)defType;
newRef.index = defIndex;
}
}
break;
case REF_UPD_ACT_SWAP:
if (newRef.index == updRefInfo.index1 + idxAdj)
newRef.index = updRefInfo.index2 + idxAdj;
else if (newRef.index == updRefInfo.index2 + idxAdj)
newRef.index = updRefInfo.index1 + idxAdj;
break;
default:
qDebug() << "Error - unhandled action:" << updRefInfo.action;
return;
}
if (curRef.type != newRef.type || abs(curRef.index) != newRef.index) {
newRef.index = curRef.index < 0 ? -newRef.index : newRef.index;
//qDebug() << "Updated reference: " << curRef.toString() << " -> " << newRef.toString();
curRef = newRef;
updRefInfo.updcnt++;
}
}
template <class R, typename T>
void ModelData::updateTypeValueRef(R & curRef, const T type, const int idxAdj, const bool defClear, const int defType, const int defValue)
{
//qDebug() << " String:" << curRef.toString() << " Type: " << curRef.type << " Value:" << curRef.value;
if (!curRef.isSet() || curRef.type != type)
return;
R newRef;
newRef.type = curRef.type;
newRef.value = abs(curRef.value);
switch (updRefInfo.action)
{
case REF_UPD_ACT_CLEAR:
if (newRef.value != (updRefInfo.index1 + idxAdj))
return;
if (defClear)
newRef.clear();
else {
newRef.type = (T)defType;
newRef.value = defValue;
}
break;
case REF_UPD_ACT_SHIFT:
if (newRef.value < (updRefInfo.index1 + idxAdj))
return;
newRef.value += updRefInfo.shift;
if (newRef.value < (updRefInfo.index1 + idxAdj) || newRef.value > (updRefInfo.maxindex + idxAdj)) {
if (defClear)
newRef.clear();
else {
newRef.type = (T)defType;
newRef.value = defValue;
}
}
break;
case REF_UPD_ACT_SWAP:
if (newRef.value == updRefInfo.index1 + idxAdj)
newRef.value = updRefInfo.index2 + idxAdj;
else if (newRef.value == updRefInfo.index2 + idxAdj)
newRef.value = updRefInfo.index1 + idxAdj;
break;
default:
qDebug() << "Error - unhandled action:" << updRefInfo.action;
return;
}
if (curRef.type != newRef.type || abs(curRef.value) != newRef.value) {
newRef.value = curRef.value < 0 ? -newRef.value : newRef.value;
//qDebug() << "Updated reference: " << curRef.toString() << " -> " << newRef.toString();
curRef = newRef;
updRefInfo.updcnt++;
}
}
void ModelData::updateCurveRef(CurveReference & crv)
{
if (updRefInfo.type == REF_UPD_TYPE_GLOBAL_VARIABLE && (crv.type == CurveReference::CURVE_REF_DIFF || crv.type == CurveReference::CURVE_REF_EXPO))
updateAdjustRef(crv.value);
else if (updRefInfo.type == REF_UPD_TYPE_CURVE && crv.type == CurveReference::CURVE_REF_CUSTOM)
updateTypeValueRef<CurveReference, CurveReference::CurveRefType>(crv, CurveReference::CURVE_REF_CUSTOM, 1);
}
void ModelData::updateLimitCurveRef(CurveReference & crv)
{
CurveReference src = CurveReference(CurveReference::CURVE_REF_CUSTOM, crv.value);
updateCurveRef(src);
if (crv.value != src.value)
crv.value = src.value;
}
void ModelData::updateAdjustRef(int & value)
{
if (updRefInfo.type != REF_UPD_TYPE_GLOBAL_VARIABLE)
return;
AdjustmentReference adj = AdjustmentReference(value);
updateTypeValueRef<AdjustmentReference, AdjustmentReference::AdjustRefType>(adj, AdjustmentReference::ADJUST_REF_GVAR, 1);
if (value != adj.toValue())
value = adj.toValue();
}
void ModelData::updateAssignFunc(CustomFunctionData * cfd)
{
const int invalidateRef = -1;
int newRef = (int)cfd->func;
int idxAdj;
switch (updRefInfo.type)
{
case REF_UPD_TYPE_CHANNEL:
if(cfd->func < FuncOverrideCH1 || cfd->func > FuncOverrideCH32) // TODO refactor to FuncOverrideCHLast
return;
idxAdj = FuncOverrideCH1;
break;
case REF_UPD_TYPE_GLOBAL_VARIABLE:
if (cfd->func < FuncAdjustGV1 || cfd->func > FuncAdjustGVLast)
return;
idxAdj = FuncAdjustGV1;
break;
case REF_UPD_TYPE_TIMER:
if (cfd->func < FuncSetTimer1 || cfd->func > FuncSetTimer3) // TODO refactor to FuncSetTimerLast
return;
idxAdj = FuncSetTimer1 - 2; // reverse earlier offset requiured for rawsource
break;
default:
return;
}
switch (updRefInfo.action)
{
case REF_UPD_ACT_CLEAR:
if (newRef != (updRefInfo.index1 + idxAdj))
return;
newRef = invalidateRef;
break;
case REF_UPD_ACT_SHIFT:
if (newRef < (updRefInfo.index1 + idxAdj))
return;
newRef += updRefInfo.shift;
if (newRef < (updRefInfo.index1 + idxAdj) || newRef > (updRefInfo.maxindex + idxAdj))
newRef = invalidateRef;
break;
case REF_UPD_ACT_SWAP:
if (newRef == updRefInfo.index1 + idxAdj)
newRef = updRefInfo.index2 + idxAdj;
else if (newRef == updRefInfo.index2 + idxAdj)
newRef = updRefInfo.index1 + idxAdj;
break;
default:
qDebug() << "Error - unhandled action:" << updRefInfo.action;
return;
}
if (newRef == invalidateRef) {
cfd->clear();
//qDebug() << "Function cleared";
updRefInfo.updcnt++;
}
else if (cfd->func != (AssignFunc)newRef) {
//qDebug() << "Updated reference:" << cfd->func << " -> " << newRef;
cfd->func = (AssignFunc)newRef;
updRefInfo.updcnt++;
}
}
void ModelData::updateDestCh(MixData * md)
{
if (updRefInfo.type != REF_UPD_TYPE_CHANNEL)
return;
const int invalidateRef = -1;
const int idxAdj = 1;
int newRef = md->destCh;
switch (updRefInfo.action)
{
case REF_UPD_ACT_CLEAR:
if (newRef != (updRefInfo.index1 + idxAdj))
return;
newRef = invalidateRef;
break;
case REF_UPD_ACT_SHIFT:
if (newRef < (updRefInfo.index1 + idxAdj))
return;
newRef += updRefInfo.shift;
if (newRef < (updRefInfo.index1 + idxAdj) || newRef > (updRefInfo.maxindex + idxAdj))
newRef = invalidateRef;
break;
case REF_UPD_ACT_SWAP:
if (newRef == updRefInfo.index1 + idxAdj)
newRef = updRefInfo.index2 + idxAdj;
else if (newRef == updRefInfo.index2 + idxAdj)
newRef = updRefInfo.index1 + idxAdj;
break;
default:
qDebug() << "Error - unhandled action:" << updRefInfo.action;
return;
}
if (newRef == invalidateRef) {
md->clear();
//qDebug() << "Mix cleared";
updRefInfo.updcnt++;
}
else if (md->destCh != static_cast<unsigned int>(newRef)) {
//qDebug() << "Updated reference:" << md->destCh << " -> " << newRef;
md->destCh = newRef;
updRefInfo.updcnt++;
}
}
void ModelData::updateFlightModeFlags(unsigned int & curRef)
{
if (updRefInfo.type != REF_UPD_TYPE_FLIGHT_MODE || curRef == 0 /*all selected*/ || curRef == 511 /*all deselected*/)
return;
if (updRefInfo.index1 > CPN_MAX_FLIGHT_MODES || updRefInfo.index2 > CPN_MAX_FLIGHT_MODES)
return;
unsigned int newRef = curRef;
bool flag[CPN_MAX_FLIGHT_MODES];
bool f;
int mask = 1;
for (int i = 0; i < CPN_MAX_FLIGHT_MODES; i++) {
flag[i] = curRef & mask;
mask <<= 1;
}
switch (updRefInfo.action)
{
case REF_UPD_ACT_CLEAR:
flag[updRefInfo.index1] = false;
break;
case REF_UPD_ACT_SHIFT:
if(updRefInfo.shift < 0) {
for (int i = updRefInfo.index1; i < CPN_MAX_FLIGHT_MODES; i++) {
if (i - updRefInfo.shift <= updRefInfo.maxindex)
flag[i] = flag[i - updRefInfo.shift];
else
flag[i] = false;
}
}
else {
for (int i = CPN_MAX_FLIGHT_MODES - 1; i >= updRefInfo.index1; i--) {
if (i - updRefInfo.shift >= updRefInfo.index1)
flag[i] = flag[i - updRefInfo.shift];
else
flag[i] = false;
}
}
break;
case REF_UPD_ACT_SWAP:
f = flag[updRefInfo.index1];
flag[updRefInfo.index1] = flag[updRefInfo.index2];
flag[updRefInfo.index2] = f;
break;
default:
qDebug() << "Error - unhandled action:" << updRefInfo.action;
return;
}
newRef = 0;
for (int i = CPN_MAX_FLIGHT_MODES - 1; i >= 0 ; i--) {
if (flag[i])
newRef++;
newRef <<= 1;
}
newRef >>= 1;
if (curRef != newRef) {
//qDebug() << "Updated reference:" << curRef << " -> " << newRef;
curRef = newRef;
updRefInfo.updcnt++;
}
}
void ModelData::updateTelemetryRef(unsigned int & curRef)
{
if (updRefInfo.type != REF_UPD_TYPE_SENSOR)
return;
const int idxAdj = 1;
int newRef = curRef;
switch (updRefInfo.action)
{
case REF_UPD_ACT_CLEAR:
if (newRef != (updRefInfo.index1 + idxAdj))
return;
newRef = 0;
break;
case REF_UPD_ACT_SHIFT:
if (newRef < (updRefInfo.index1 + idxAdj))
return;
newRef += updRefInfo.shift;
if (newRef < (updRefInfo.index1 + idxAdj) || newRef > (updRefInfo.maxindex + idxAdj))
newRef = 0;
break;
case REF_UPD_ACT_SWAP:
if (newRef == updRefInfo.index1 + idxAdj)
newRef = updRefInfo.index2 + idxAdj;
else if (newRef == updRefInfo.index2 + idxAdj)
newRef = updRefInfo.index1 + idxAdj;
break;
default:
qDebug() << "Error - unhandled action:" << updRefInfo.action;
return;
}
if (curRef != static_cast<unsigned int>(newRef)) {
//qDebug() << "Updated reference:" << curRef << " -> " << newRef;
curRef = newRef;
updRefInfo.updcnt++;
}
}
void ModelData::updateModuleFailsafes(ModuleData * md)
{
if (updRefInfo.type != REF_UPD_TYPE_CHANNEL)
return;
bool updated = false;
switch (updRefInfo.action)
{
case REF_UPD_ACT_CLEAR:
break;
case REF_UPD_ACT_SHIFT:
if (updRefInfo.shift == 0 || updRefInfo.index1 < 0 || updRefInfo.index1 > CPN_MAX_CHNOUT - 1)
return;
if (updRefInfo.shift > 0) {
for (int i = (CPN_MAX_CHNOUT - 1); i > updRefInfo.index1; i--) {
md->failsafeChannels[i] = md->failsafeChannels[i - 1];
}
md->failsafeChannels[updRefInfo.index1] = 0;
}
else {
for (int i = (updRefInfo.index1 + 1); i < (CPN_MAX_CHNOUT - 1); i++) {
md->failsafeChannels[i - 1] = md->failsafeChannels[i];
}
md->failsafeChannels[CPN_MAX_CHNOUT - 1] = 0;
}
updated = true;
break;
case REF_UPD_ACT_SWAP:
int tmp;
if (updRefInfo.index1 >= 0 && updRefInfo.index1 < CPN_MAX_CHNOUT) {
updated = true;
tmp = md->failsafeChannels[updRefInfo.index1];
if (updRefInfo.index2 >= 0 && updRefInfo.index2 < CPN_MAX_CHNOUT)
md->failsafeChannels[updRefInfo.index1] = md->failsafeChannels[updRefInfo.index2];
else
md->failsafeChannels[updRefInfo.index1] = 0;
}
else
tmp = 0;
if (updRefInfo.index2 >= 0 && updRefInfo.index2 < CPN_MAX_CHNOUT)
updated = true;
md->failsafeChannels[updRefInfo.index2] = tmp;
break;
default:
qDebug() << "Error - unhandled action:" << updRefInfo.action;
return;
}
if (updated) {
//qDebug() << "Updated module failsafes";
updRefInfo.updcnt++;
}
}
int ModelData::linkedFlightModeIndexToValue(const int phaseIdx, const int useFmIdx, const int maxOwnValue)
{
int val;
if (phaseIdx == useFmIdx || phaseIdx < 0 || phaseIdx > (CPN_MAX_FLIGHT_MODES - 1) || useFmIdx < 0 || useFmIdx > (CPN_MAX_FLIGHT_MODES - 1))
val = flightModeData[phaseIdx].linkedFlightModeZero(phaseIdx, maxOwnValue);
else
val = maxOwnValue + useFmIdx + (useFmIdx >= phaseIdx ? 0 : 1);
return val;
}
int ModelData::linkedFlightModeValueToIndex(const int phaseIdx, const int val, const int maxOwnValue)
{
int idx = val - maxOwnValue - 1;
if (idx >= phaseIdx)
idx += 1;
return idx;
}
int ModelData::getGVarFlightModeIndex(const int phaseIdx, const int gvarIdx)
{
if (!isGVarLinked(phaseIdx, gvarIdx))
return -1;
return (linkedFlightModeValueToIndex(phaseIdx, flightModeData[phaseIdx].gvars[gvarIdx], GVAR_MAX_VALUE));
}
void ModelData::setGVarFlightModeIndexToValue(const int phaseIdx, const int gvarIdx, const int useFmIdx)
{
flightModeData[phaseIdx].gvars[gvarIdx] = linkedFlightModeIndexToValue(phaseIdx, useFmIdx, GVAR_MAX_VALUE);
}
int ModelData::getREncFlightModeIndex(const int phaseIdx, const int reIdx)
{
if (!isREncLinked(phaseIdx, reIdx))
return -1;
return (linkedFlightModeValueToIndex(phaseIdx, flightModeData[phaseIdx].rotaryEncoders[reIdx], RENC_MAX_VALUE));
}
void ModelData::setREncFlightModeIndexToValue(const int phaseIdx, const int reIdx, const int useFmIdx)
{
flightModeData[phaseIdx].rotaryEncoders[reIdx] = linkedFlightModeIndexToValue(phaseIdx, useFmIdx, RENC_MAX_VALUE);
}
bool ModelData::isExpoParent(const int index)
{
const ExpoData &ed = expoData[index];
const QVector<const ExpoData *> chexpos = expos(ed.chn);
return chexpos.constFirst() == &ed;
}
bool ModelData::isExpoChild(const int index)
{
const ExpoData &ed = expoData[index];
const QVector<const ExpoData *> chexpos = expos(ed.chn);
return chexpos.constFirst() != &ed;
}
bool ModelData::hasExpoChildren(const int index)
{
const ExpoData &ed = expoData[index];
const QVector<const ExpoData *> chexpos = expos(ed.chn);
return chexpos.constFirst() == &ed && chexpos.constLast() != &ed;
}
bool ModelData::hasExpoSiblings(const int index)
{
const ExpoData &ed = expoData[index];
const QVector<const ExpoData *> chexpos = expos(ed.chn);
return !isExpoParent(index) && chexpos.size() > 2;
}
void ModelData::removeMix(const int idx)
{
memmove(&mixData[idx], &mixData[idx + 1], (CPN_MAX_MIXERS - (idx + 1)) * sizeof(MixData));
mixData[CPN_MAX_MIXERS - 1].clear();
}

View file

@ -76,6 +76,8 @@ class TimerData {
int pvalue;
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); }
void convert(RadioDataConversionState & cstate);
bool isEmpty();
bool isModeOff() { return mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); }
};
#define CPN_MAX_SCRIPTS 9
@ -128,6 +130,8 @@ enum TrainerMode {
TRAINER_MODE_MULTI
};
#define INPUT_NAME_LEN 4
class ModelData {
Q_DECLARE_TR_FUNCTIONS(ModelData)
@ -170,7 +174,7 @@ class ModelData {
MixData mixData[CPN_MAX_MIXERS];
LimitData limitData[CPN_MAX_CHNOUT];
char inputNames[CPN_MAX_INPUTS][4+1];
char inputNames[CPN_MAX_INPUTS][INPUT_NAME_LEN+1];
ExpoData expoData[CPN_MAX_EXPOS];
CurveData curves[CPN_MAX_CURVES];
@ -219,11 +223,23 @@ class ModelData {
void setTrimValue(int phaseIdx, int trimIdx, int value);
bool isGVarLinked(int phaseIdx, int gvarIdx);
int getGVarFieldValue(int phaseIdx, int gvarIdx);
float getGVarFieldValuePrec(int phaseIdx, int gvarIdx);
bool isGVarLinkedCircular(int phaseIdx, int gvarIdx);
int getGVarValue(int phaseIdx, int gvarIdx);
float getGVarValuePrec(int phaseIdx, int gvarIdx);
int getGVarFlightModeIndex(const int phaseIdx, const int gvarIdx);
void setGVarFlightModeIndexToValue(const int phaseIdx, const int gvarIdx, const int useFmIdx);
bool isREncLinked(int phaseIdx, int reIdx);
bool isREncLinkedCircular(int phaseIdx, int reIdx);
int getREncValue(int phaseIdx, int reIdx);
int getREncFlightModeIndex(const int phaseIdx, const int reIdx);
void setREncFlightModeIndexToValue(const int phaseIdx, const int reIdx, const int useFmIdx);
ModelData removeGlobalVars();
int linkedFlightModeIndexToValue(const int phaseIdx, const int useFmIdx, const int maxOwnValue);
int linkedFlightModeValueToIndex(const int phaseIdx, const int val, const int maxOwnValue);
void clearMixes();
void clearInputs();
@ -231,8 +247,95 @@ class ModelData {
bool isAvailable(const RawSwitch & swtch) const;
enum ReferenceUpdateAction {
REF_UPD_ACT_CLEAR,
REF_UPD_ACT_SHIFT,
REF_UPD_ACT_SWAP,
};
enum ReferenceUpdateType {
REF_UPD_TYPE_CHANNEL,
REF_UPD_TYPE_CURVE,
REF_UPD_TYPE_FLIGHT_MODE,
REF_UPD_TYPE_GLOBAL_VARIABLE,
REF_UPD_TYPE_INPUT,
REF_UPD_TYPE_LOGICAL_SWITCH,
REF_UPD_TYPE_SCRIPT,
REF_UPD_TYPE_SENSOR,
REF_UPD_TYPE_TIMER,
};
struct UpdateReferenceParams
{
ReferenceUpdateType type;
ReferenceUpdateAction action;
int index1;
int index2;
int shift;
UpdateReferenceParams() {}
UpdateReferenceParams(ReferenceUpdateType t, ReferenceUpdateAction a, int i1, int i2 = 0, int s = 0) :
type(t), action(a), index1(i1), index2(i2), shift(s) {}
};
int updateAllReferences(const ReferenceUpdateType type, const ReferenceUpdateAction action, const int index1, const int index2 = 0, const int shift = 0);
bool isExpoParent(const int index);
bool isExpoChild(const int index);
bool hasExpoChildren(const int index);
bool hasExpoSiblings(const int index);
void removeMix(const int idx);
protected:
void removeGlobalVar(int & var);
private:
QVector<UpdateReferenceParams> *updRefList = nullptr;
struct UpdateReferenceInfo
{
ReferenceUpdateType type;
ReferenceUpdateAction action;
int index1;
int index2;
int shift;
int updcnt;
int maxindex;
RawSourceType srcType;
RawSwitchType swtchType;
};
UpdateReferenceInfo updRefInfo;
int updateReference();
void appendUpdateReferenceParams(const ReferenceUpdateType type, const ReferenceUpdateAction action, const int index1, const int index2 = 0, const int shift = 0);
template <class R, typename T>
void updateTypeIndexRef(R & curref, const T type, const int idxAdj = 0, const bool defClear = true, const int defType = 0, const int defIndex = 0);
template <class R, typename T>
void updateTypeValueRef(R & curref, const T type, const int idxAdj = 0, const bool defClear = true, const int defType = 0, const int defValue = 0);
void updateAdjustRef(int & adj);
void updateAssignFunc(CustomFunctionData * cfd);
void updateCurveRef(CurveReference & crv);
void updateDestCh(MixData * md);
void updateLimitCurveRef(CurveReference & crv);
void updateFlightModeFlags(unsigned int & flags);
void updateTelemetryRef(unsigned int & idx);
void updateModuleFailsafes(ModuleData * md);
inline void updateSourceRef(RawSource & src) { updateTypeIndexRef<RawSource, RawSourceType>(src, updRefInfo.srcType); }
inline void updateSwitchRef(RawSwitch & swtch) { updateTypeIndexRef<RawSwitch, RawSwitchType>(swtch, updRefInfo.swtchType, 1); }
inline void updateTimerMode(RawSwitch & swtch) { updateTypeIndexRef<RawSwitch, RawSwitchType>(swtch, updRefInfo.swtchType, 1, false, (int)SWITCH_TYPE_TIMER_MODE, 0); }
inline void updateSourceIntRef(int & value)
{
RawSource src = RawSource(value);
updateTypeIndexRef<RawSource, RawSourceType>(src, updRefInfo.srcType);
if (value != src.toValue())
value = src.toValue();
}
inline void updateSwitchIntRef(int & value)
{
RawSwitch swtch = RawSwitch(value);
updateTypeIndexRef<RawSwitch, RawSwitchType>(swtch, updRefInfo.swtchType, 1);
if (value != swtch.toValue())
value = swtch.toValue();
}
};
#endif // MODELDATA_H

View file

@ -181,3 +181,59 @@ QStringList ModuleData::powerValueStrings(int subType, Firmware * fw)
strIdx += 2;
return strings[strIdx];
}
bool ModuleData::hasFailsafes(Firmware * fw) const
{
return fw->getCapability(HasFailsafe) && (
protocol == PULSES_ACCESS_ISRM ||
protocol == PULSES_ACCST_ISRM_D16 ||
protocol == PULSES_PXX_XJT_X16 ||
protocol == PULSES_PXX_R9M ||
protocol == PULSES_ACCESS_R9M ||
protocol == PULSES_ACCESS_R9M_LITE ||
protocol == PULSES_ACCESS_R9M_LITE_PRO ||
protocol == PULSES_XJT_LITE_X16 ||
protocol == PULSES_MULTIMODULE
);
}
int ModuleData::getMaxChannelCount()
{
switch (protocol) {
case PULSES_ACCESS_ISRM:
return 24;
case PULSES_PXX_R9M:
case PULSES_ACCESS_R9M:
case PULSES_ACCESS_R9M_LITE:
case PULSES_ACCESS_R9M_LITE_PRO:
case PULSES_ACCST_ISRM_D16:
case PULSES_XJT_LITE_X16:
case PULSES_PXX_XJT_X16:
case PULSES_CROSSFIRE:
case PULSES_SBUS:
case PULSES_PPM:
return 16;
case PULSES_XJT_LITE_LR12:
case PULSES_PXX_XJT_LR12:
return 12;
case PULSES_PXX_DJT:
case PULSES_XJT_LITE_D8:
case PULSES_PXX_XJT_D8:
return 8;
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
return 6;
case PULSES_MULTIMODULE:
if (multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2)
return 12;
else
return 16;
break;
case PULSES_OFF:
break;
default:
break;
}
return 8;
}

View file

@ -119,7 +119,20 @@ enum MultiModuleRFProtocols {
MODULE_SUBTYPE_MULTI_AFHDS2A_RX,
MODULE_SUBTYPE_MULTI_HOTT,
MODULE_SUBTYPE_MULTI_FX816,
MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_FX816
MODULE_SUBTYPE_MULTI_BAYANG_RX,
MODULE_SUBTYPE_MULTI_PELIKAN,
MODULE_SUBTYPE_MULTI_TIGER,
MODULE_SUBTYPE_MULTI_XK,
MODULE_SUBTYPE_MULTI_XN297DUMP,
MODULE_SUBTYPE_MULTI_FRSKYX2,
MODULE_SUBTYPE_MULTI_FRSKY_R9,
MODULE_SUBTYPE_MULTI_PROPEL,
MODULE_SUBTYPE_MULTI_FRSKYL,
MODULE_SUBTYPE_MULTI_SKYARTEC,
MODULE_SUBTYPE_MULTI_ESKY150V2,
MODULE_SUBTYPE_MULTI_DSM_RX,
MODULE_SUBTYPE_MULTI_JJRC345,
MODULE_SUBTYPE_MULTI_LAST = MODULE_SUBTYPE_MULTI_JJRC345
};
enum TrainerProtocol {
@ -197,6 +210,8 @@ class ModuleData {
static QString indexToString(int index, Firmware * fw);
static QString protocolToString(unsigned protocol);
static QStringList powerValueStrings(int subType, Firmware * fw);
bool hasFailsafes(Firmware * fw) const;
int getMaxChannelCount();
};
#endif // MODULEDATA_H

View file

@ -39,7 +39,7 @@ static const QStringList STR_SUBTYPE_CUSTOM ({
});
static const QStringList STR_SUBTYPE_FLYSKY {"Standard", "V9x9", "V6x6", "V912", "CX20"};
static const QStringList STR_SUBTYPE_HUBSAN {"H107", "H301", "H501"};
static const QStringList STR_SUBTYPE_FRSKY {"D16", "D8", "D16 8ch", "V8", "D16 EU-LBT", "D16 EU-LBT 8ch"};
static const QStringList STR_SUBTYPE_FRSKY {"D16", "D8", "D16 8ch", "V8", "D16 EU-LBT", "D16 EU-LBT 8ch", "D8 Cloned", "D16 Cloned"};
static const QStringList STR_SUBTYPE_HISKY {"Standard", "HK310"};
static const QStringList STR_SUBTYPE_V2X2 {"Standard", "JXD506"};
static const QStringList STR_SUBTYPE_DSM {"DSM2 22ms", "DSM2 11ms", "DSMX 22ms", "DSMX 11ms"};
@ -51,6 +51,7 @@ static const QStringList STR_SUBTYPE_SLT {"V1 (6 Channel)", "V2 (8 Channel
static const QStringList STR_SUBTYPE_CX10 {"Green", "Blue", "DM007", "-", "JC3015a", "JC3015b", "MK33041"};
static const QStringList STR_SUBTYPE_CG023 {"Standard", "YD829"};
static const QStringList STR_SUBTYPE_BAYANG {"Standard", "H8S3D", "X16 AH", "IRDRONE", "DHD D4"};
static const QStringList STR_SUBTYPE_ESky {"Standard", "ET4"};
static const QStringList STR_SUBTYPE_MT99 {"MT99", "H7", "YZ", "LS", "FY805"};
static const QStringList STR_SUBTYPE_MJXQ {"WLH08", "X600", "X800", "H26D", "E010", "H26WH", "Phoenix"};
static const QStringList STR_SUBTYPE_FY326 {"Standard", "FY319"};
@ -60,20 +61,29 @@ static const QStringList STR_SUBTYPE_Q2X2 {"Q222", "Q242", "Q282"};
static const QStringList STR_SUBTYPE_WK2x01 {"WK2801", "WK2401", "W6_5_1", "W6_6_1", "W6_HEL", "W6_HEL_I"};
static const QStringList STR_SUBTYPE_Q303 {"Standard", "CX35", "CX10D", "CX10WD"};
static const QStringList STR_SUBTYPE_CABELL {"Cabell V3", "Cab V3 Telem", "-", "-", "-", "-", "Set FailSafe", "Unbind"};
static const QStringList STR_SUBTYPE_ESKY150 {"4 Channel", "7 Channel"};
static const QStringList STR_SUBTYPE_H83D {"H8 Mini 3D", "H20H", "H20 Mini", "H30 Mini"};
static const QStringList STR_SUBTYPE_CORONA {"Corona V1", "Corona V2", "Flydream V3"};
static const QStringList STR_SUBTYPE_HITEC {"Optima", "Optima Hub Telem", "Minima"};
static const QStringList STR_SUBTYPE_TRAXXAS {"6519 RX"};
static const QStringList STR_SUBTYPE_WFLY {"WFR0xS"};
static const QStringList STR_SUBTYPE_BUGS_MINI {"Standard", "Bugs 3H"};
static const QStringList STR_SUBTYPE_TRAXXAS {"6519 RX"};
static const QStringList STR_SUBTYPE_E01X {"E012", "E015", "E016H"};
static const QStringList STR_SUBTYPE_V911S {"Standard", "E119"};
static const QStringList STR_SUBTYPE_GD00X {"GD V1", "GD V2"};
static const QStringList STR_SUBTYPE_REDPINE {"Fast", "Slow"};
static const QStringList STR_SUBTYPE_POTENSIC {"A20 Firefly"};
static const QStringList STR_SUBTYPE_ZSX {"JJRC ZSX-280"};
static const QStringList STR_SUBTYPE_FLYZONE {"FZ-410 TX"};
static const QStringList STR_SUBTYPE_FRSKYX_RX {"D16 FCC", "D16 LBT"};
static const QStringList STR_SUBTYPE_FRSKYX_RX {"RX", "Clone TX"};
static const QStringList STR_SUBTYPE_FX816 {"P38"};
static const QStringList STR_SUBTYPE_ESKY150 {"4CH", "7CH"};
static const QStringList STR_SUBTYPE_XK {"X450", "X420"};
static const QStringList STR_SUBTYPE_XN297DUMP {"250K", "1M", "2M", "AUTO"};
static const QStringList STR_SUBTYPE_FRSKYX2 {"D16", "D16 8ch", "D16 EU-LBT", "D16 EU-LBT 8ch", "D16 Cloned"};
static const QStringList STR_SUBTYPE_FRSKYR9 {"915 MHz", "868 MHz", "915 MHz 8-Channel", "868 MHz 8-Channel"};
static const QStringList STR_SUBTYPE_PROPEL {"74-Z"};
static const QStringList STR_SUBTYPE_FRSKYL {"LR12", "LR12 6-Channel"};
static const QStringList STR_SUBTYPE_ESKY150V2 {"150 V2"};
static const QStringList NO_SUBTYPE {STR_MULTI_DEFAULT};
@ -83,7 +93,7 @@ static const QStringList NO_SUBTYPE {STR_MULTI_DEFAULT};
const Multiprotocols multiProtocols {
{MODULE_SUBTYPE_MULTI_FLYSKY, 4, false, STR_SUBTYPE_FLYSKY, nullptr},
{MODULE_SUBTYPE_MULTI_HUBSAN, 2, false, STR_SUBTYPE_HUBSAN, STR_MULTI_VIDFREQ},
{MODULE_SUBTYPE_MULTI_FRSKY, 5, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_FRSKY, 7, false, STR_SUBTYPE_FRSKY, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_HISKY, 1, false, STR_SUBTYPE_HISKY, nullptr},
{MODULE_SUBTYPE_MULTI_V2X2, 1, false, STR_SUBTYPE_V2X2, nullptr},
{MODULE_SUBTYPE_MULTI_DSM2, 3, false, STR_SUBTYPE_DSM, nullptr},
@ -91,14 +101,16 @@ const Multiprotocols multiProtocols {
{MODULE_SUBTYPE_MULTI_YD717, 4, false, STR_SUBTYPE_YD717, nullptr},
{MODULE_SUBTYPE_MULTI_KN, 1, false, STR_SUBTYPE_KN, nullptr},
{MODULE_SUBTYPE_MULTI_SYMAX, 1, false, STR_SUBTYPE_SYMAX, nullptr},
{MODULE_SUBTYPE_MULTI_SLT, 4, false, STR_SUBTYPE_SLT, nullptr},
{MODULE_SUBTYPE_MULTI_SLT, 4, false, STR_SUBTYPE_SLT, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_CX10, 6, false, STR_SUBTYPE_CX10, nullptr},
{MODULE_SUBTYPE_MULTI_CG023, 1, false, STR_SUBTYPE_CG023, nullptr},
{MODULE_SUBTYPE_MULTI_BAYANG, 4, false, STR_SUBTYPE_BAYANG, STR_MULTI_TELEMETRY},
{MODULE_SUBTYPE_MULTI_ESky, 1, false, STR_SUBTYPE_ESky, nullptr},
{MODULE_SUBTYPE_MULTI_MT99XX, 4, false, STR_SUBTYPE_MT99, nullptr},
{MODULE_SUBTYPE_MULTI_MJXQ, 6, false, STR_SUBTYPE_MJXQ, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_FY326, 1, false, STR_SUBTYPE_FY326, nullptr},
{MODULE_SUBTYPE_MULTI_SFHSS, 0, true, NO_SUBTYPE, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_J6PRO, 0, false, NO_SUBTYPE, nullptr},
{MODULE_SUBTYPE_MULTI_HONTAI, 3, false, STR_SUBTYPE_HONTAI, nullptr},
{MODULE_SUBTYPE_MULTI_OLRS, 0, false, NO_SUBTYPE, STR_MULTI_RFPOWER},
{MODULE_SUBTYPE_MULTI_FS_AFHDS2A, 3, true, STR_SUBTYPE_AFHDS2A, STR_MULTI_SERVOFREQ},
@ -106,25 +118,32 @@ const Multiprotocols multiProtocols {
{MODULE_SUBTYPE_MULTI_WK_2X01, 5, false, STR_SUBTYPE_WK2x01, nullptr},
{MODULE_SUBTYPE_MULTI_Q303, 3, false, STR_SUBTYPE_Q303, nullptr},
{MODULE_SUBTYPE_MULTI_CABELL, 7, false, STR_SUBTYPE_CABELL, STR_MULTI_OPTION},
{MODULE_SUBTYPE_MULTI_ESKY150, 1, false, STR_SUBTYPE_ESKY150, nullptr},
{MODULE_SUBTYPE_MULTI_H83D, 3, false, STR_SUBTYPE_H83D, nullptr},
{MODULE_SUBTYPE_MULTI_CORONA, 2, false, STR_SUBTYPE_CORONA, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_HITEC, 2, false, STR_SUBTYPE_HITEC, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_WFLY, 0, false, STR_SUBTYPE_WFLY, nullptr},
{MODULE_SUBTYPE_MULTI_BUGS_MINI, 1, false, STR_SUBTYPE_BUGS_MINI, nullptr},
{MODULE_SUBTYPE_MULTI_TRAXXAS, 0, false, STR_SUBTYPE_TRAXXAS, nullptr},
{MODULE_SUBTYPE_MULTI_E01X, 2, false, STR_SUBTYPE_E01X, STR_MULTI_OPTION},
{MODULE_SUBTYPE_MULTI_V911S, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_GD00X, 1, false, STR_SUBTYPE_GD00X, nullptr},
{MODULE_SUBTYPE_MULTI_V911S, 1, false, STR_SUBTYPE_V911S, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_GD00X, 1, false, STR_SUBTYPE_GD00X, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_KF606, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_REDPINE, 1, false, STR_SUBTYPE_REDPINE, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_POTENSIC, 0, false, STR_SUBTYPE_POTENSIC, nullptr},
{MODULE_SUBTYPE_MULTI_ZSX, 0, false, STR_SUBTYPE_ZSX, nullptr},
{MODULE_SUBTYPE_MULTI_FLYZONE, 0, false, STR_SUBTYPE_FLYZONE, nullptr},
{MODULE_SUBTYPE_MULTI_FRSKYX_RX, 1, false, STR_SUBTYPE_FRSKYX_RX, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_ESky, 0, false, NO_SUBTYPE, nullptr},
{MODULE_SUBTYPE_MULTI_J6PRO, 0, false, NO_SUBTYPE, nullptr},
{MODULE_SUBTYPE_MULTI_ESKY150, 1, false, STR_SUBTYPE_ESKY150, nullptr},
{MODULE_SUBTYPE_MULTI_FX816, 0, false, STR_SUBTYPE_FX816, nullptr},
{MODULE_SUBTYPE_MULTI_HOTT, 0, true, NO_SUBTYPE, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_FX816, 0, false, STR_SUBTYPE_FX816, nullptr},
{MODULE_SUBTYPE_MULTI_XK, 1, false, STR_SUBTYPE_XK, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_XN297DUMP, 3, false, STR_SUBTYPE_XN297DUMP, nullptr},
{MODULE_SUBTYPE_MULTI_FRSKYX2, 4, true, STR_SUBTYPE_FRSKYX2, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_FRSKY_R9, 3, true, STR_SUBTYPE_FRSKYR9, nullptr},
{MODULE_SUBTYPE_MULTI_PROPEL, 0, false, STR_SUBTYPE_PROPEL, nullptr},
{MODULE_SUBTYPE_MULTI_FRSKYL, 1, false, STR_SUBTYPE_FRSKYL, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_SKYARTEC, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_ESKY150V2, 0, false, STR_SUBTYPE_ESKY150V2, STR_MULTI_RFTUNE},
{MM_RF_CUSTOM_SELECTED, 7, true, STR_SUBTYPE_CUSTOM, STR_MULTI_OPTION},
// Sentinel and default for protocols not listed above (MM_RF_CUSTOM is 0xff)
@ -165,14 +184,13 @@ QString Multiprotocols::protocolToString(int protocol, bool custom)
static const QStringList strings({
"FlySky", "Hubsan", "FrSky", "Hisky", "V2x2", "DSM", "Devo", "YD717", "KN", "SymaX", "SLT", "CX10", "CG023",
"Bayang", "ESky", "MT99XX", "MJXQ", "Shenqi", "FY326", "SFHSS", "J6 PRO","FQ777","Assan","Hontai","Open LRS",
"FlySky AFHDS2A", "Q2x2", "Walkera", "Q303", "GW008", "DM002", "Cabell", "ESky 150", "H8 3D", "Corona", "CFlie",
"FlySky AFHDS2A", "Q2x2", "WK2x01", "Q303", "GW008", "DM002", "Cabell", "ESky 150", "H8 3D", "Corona", "CFlie",
"Hitec", "Wfly", "Bugs", "Bugs Mini", "Traxxas", "NCC-1701-A", "E01X", "WL Heli V911S", "GD00X", "Volantex V761",
"KFPlan KF606", "Redpine", "Potensic", "ZXS", "FlyZone", "Scanner", "FrSky RX", "FlySky AFHDS2A RX", "HoTT", "Fx816"
"KFPlan KF606", "Redpine", "Potensic", "ZSX", "FlyZone", "Scanner", "FrSky RX", "FlySky AFHDS2A RX", "HoTT", "Fx816",
"Bayang RX", "Pelikan", "Tiger", "XK", "XN297 Dump", "FrSky X 2.1", "FrSky R9", "Propel", "FrSky L", "Skyartec",
"ESky 150v2", "DSM RX", "JJRC345"
});
if (protocol == MM_RF_CUSTOM_SELECTED || custom)
return tr("Custom - proto %1)").arg(protocol);
else
return strings.value(protocol, CPN_STR_UNKNOWN_ITEM);
}

View file

@ -693,9 +693,11 @@ class FlightModeField: public TransformedField {
internalField.Append(new UnsignedField<8>(this, phase.fadeIn));
internalField.Append(new UnsignedField<8>(this, phase.fadeOut));
if (version < 219) {
for (int i = 0; i < rotencCount; i++) {
internalField.Append(new SignedField<16>(this, phase.rotaryEncoders[i]));
}
}
for (int i = 0; i < MAX_GVARS(board, version); i++) {
internalField.Append(new SignedField<16>(this, phase.gvars[i]));
@ -1914,7 +1916,7 @@ class SensorField: public TransformedField {
internalField.Append(new UnsignedField<32>(this, _param, "param"));
}
virtual void beforeExport()
void beforeExport() override
{
if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) {
_id = sensor.id;
@ -2054,17 +2056,12 @@ class ModuleUnionField: public UnionField<unsigned int> {
void beforeExport() override
{
if (module.multi.rfProtocol > MODULE_SUBTYPE_MULTI_LAST)
module.multi.rfProtocol += 3;
rfProtExtra = (module.multi.rfProtocol & 0x70) >> 4;
module.multi.rfProtocol &= 0x0f;
}
void afterImport() override
{
module.multi.rfProtocol = (rfProtExtra << 4) + (module.rfProtocol & 0xf);
if (module.multi.rfProtocol > MODULE_SUBTYPE_MULTI_LAST)
module.multi.rfProtocol -= 3;
}
private:
@ -2133,7 +2130,8 @@ class ModuleUnionField: public UnionField<unsigned int> {
memset(receiverName, 0, sizeof(receiverName));
}
bool select(const unsigned int& attr) const override {
bool select(const unsigned int& attr) const override
{
return attr >= PULSES_ACCESS_ISRM && attr <= PULSES_ACCESS_R9M_LITE_PRO;
}
@ -2141,7 +2139,6 @@ class ModuleUnionField: public UnionField<unsigned int> {
{
for (int i=0; i<PXX2_MAX_RECEIVERS_PER_MODULE; i++) {
for (int pos=0; pos<PXX2_LEN_RX_NAME+1; pos++) {
if (pos == PXX2_LEN_RX_NAME || module.access.receiverName[i][pos] == '\0') {
memset(module.access.receiverName[i]+pos,'\0',PXX2_LEN_RX_NAME-pos);
break;
@ -2222,7 +2219,7 @@ class ModuleField: public TransformedField {
module.subType = module.protocol - PULSES_PXX_XJT_X16;
}
else if (module.protocol == PULSES_MULTIMODULE) {
module.rfProtocol = module.multi.rfProtocol & 0xf;
module.rfProtocol = module.multi.rfProtocol & 0x0F;
}
}

View file

@ -564,7 +564,7 @@ int OpenTxFirmware::getCapability(::Capability capability)
case SoundPitch:
return 1;
case Haptic:
return (IS_2560(board) || IS_SKY9X(board) || IS_TARANIS_PLUS(board) || IS_TARANIS_SMALL(board) || IS_TARANIS_X9E(board) || IS_FAMILY_HORUS_OR_T16(board) || IS_JUMPER_T12(board) || id.contains("haptic"));
return board != Board::BOARD_TARANIS_X9D || id.contains("haptic");
case ModelTrainerEnable:
if (IS_HORUS_OR_TARANIS(board) && board!=Board::BOARD_TARANIS_XLITE)
return 1;
@ -616,6 +616,10 @@ int OpenTxFirmware::getCapability(::Capability capability)
return 0;
else
return IS_ARM(board) ? 4 : 2;
case TelemetryCustomScreensBars:
return (getCapability(TelemetryCustomScreens) ? 4 : 0);
case TelemetryCustomScreensLines:
return (getCapability(TelemetryCustomScreens) ? 4 : 0);
case TelemetryCustomScreensFieldsPerLine:
return HAS_LARGE_LCD(board) ? 3 : 2;
case NoTelemetryProtocol:
@ -754,6 +758,11 @@ int OpenTxFirmware::getCapability(::Capability capability)
return IS_TARANIS_XLITES(board);
case PwrButtonPress:
return IS_HORUS_OR_TARANIS(board) && (board!=Board::BOARD_TARANIS_X9D) && (board!=Board::BOARD_TARANIS_X9DP);
case Sensors:
if (IS_FAMILY_HORUS_OR_T16(board) || IS_TARANIS_X9(board))
return 60;
else
return 40;
default:
return 0;
}
@ -793,7 +802,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port)
case PULSES_ACCST_ISRM_D16:
return IS_ACCESS_RADIO(board, id);
case PULSES_MULTIMODULE:
return id.contains("internalmulti");
return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board);
default:
return false;
}
@ -1252,6 +1261,7 @@ void registerOpenTxFirmwares()
/* FrSky Taranis X9D board */
firmware = new OpenTxFirmware("opentx-x9d", Firmware::tr("FrSky Taranis X9D"), BOARD_TARANIS_X9D);
firmware->addOption("noras", Firmware::tr("Disable RAS (SWR)"));
firmware->addOption("haptic", Firmware::tr("Haptic module installed"));
addOpenTxTaranisOptions(firmware);
addPPMInternalModuleHack(firmware);
@ -1331,7 +1341,6 @@ void registerOpenTxFirmwares()
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
firmware->addOption("lua", Firmware::tr("Enable Lua custom scripts screen"));
firmware->addOption("flexr9m", Firmware::tr("Enable non certified R9M firmwares"));
firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module"));
addOpenTxFontOptions(firmware);
registerOpenTxFirmware(firmware);
@ -1342,15 +1351,17 @@ void registerOpenTxFirmwares()
addOpenTxFrskyOptions(firmware);
firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module"));
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX);
registerOpenTxFirmware(firmware);
// TX16S is unavailable until finalized
/* Radiomaster TX16S board
/* Radiomaster TX16S board */
firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16s / TX16s Hall / TX16s Masterfire"), BOARD_RADIOMASTER_TX16S);
addOpenTxFrskyOptions(firmware);
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
registerOpenTxFirmware(firmware);*/
addOpenTxRfOptions(firmware, FLEX);
static const Firmware::Option opt_bt("bluetooth", Firmware::tr("Support for bluetooth module"));
static const Firmware::Option opt_internal_gps("internalgps", Firmware::tr("Support internal GPS"));
firmware->addOptionsGroup({opt_bt, opt_internal_gps});
registerOpenTxFirmware(firmware);
/* 9XR-Pro */
firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO);

View file

@ -227,11 +227,7 @@ class RawSource {
AllSourceGroups = InputSourceGroups | GVarsGroup | TelemGroup | ScriptsGroup
};
RawSource():
type(SOURCE_TYPE_NONE),
index(0)
{
}
RawSource() { clear(); }
explicit RawSource(int value):
type(RawSourceType(abs(value)/65536)),
@ -258,6 +254,8 @@ class RawSource {
bool isSlider(int * sliderIndex = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
bool isTimeBased(Board::Type board = Board::BOARD_UNKNOWN) const;
bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
bool isSet() const { return type != SOURCE_TYPE_NONE || index != 0; }
void clear() { type = SOURCE_TYPE_NONE; index = 0; }
bool operator == ( const RawSource & other) const {
return (this->type == other.type) && (this->index == other.index);

View file

@ -65,11 +65,7 @@ class RawSwitch {
AllSwitchContexts = AllModelContexts | GlobalFunctionsContext
};
RawSwitch():
type(SWITCH_TYPE_NONE),
index(0)
{
}
RawSwitch() { clear(); }
explicit RawSwitch(int value):
type(RawSwitchType(abs(value)/256)),
@ -91,6 +87,8 @@ class RawSwitch {
RawSwitch convert(RadioDataConversionState & cstate);
QString toString(Board::Type board = Board::BOARD_UNKNOWN, const GeneralSettings * const generalSettings = NULL, const ModelData * const modelData = NULL) const;
bool isAvailable(const ModelData * const model = NULL, const GeneralSettings * const gs = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
bool isSet() const { return type != SWITCH_TYPE_NONE || index != 0; }
void clear() { type = SWITCH_TYPE_NONE; index = 0; }
bool operator== ( const RawSwitch& other) const {
return (this->type == other.type) && (this->index == other.index);
@ -100,7 +98,6 @@ class RawSwitch {
return (this->type != other.type) || (this->index != other.index);
}
RawSwitchType type;
int index;
};

View file

@ -82,6 +82,8 @@ QString SensorData::unitString() const
return tr("seconds");
case UNIT_CELLS:
return tr("V");
case UNIT_MILLILITERS_PER_MINUTE:
return tr("ml/minute");
default:
return "";
}

View file

@ -90,6 +90,7 @@ class SensorData {
UNIT_RADIANS,
UNIT_MILLILITERS,
UNIT_FLOZ,
UNIT_MILLILITERS_PER_MINUTE,
UNIT_HOURS,
UNIT_MINUTES,
UNIT_SECONDS,

View file

@ -1124,7 +1124,7 @@ p, li { white-space: pre-wrap; }
<item row="19" column="1">
<widget class="QLineEdit" name="registrationId">
<property name="inputMask">
<string>aaaaaaAA</string>
<string>nnnnnnNN</string>
</property>
<property name="maxLength">
<number>8</number>

0
companion/src/images/simulator/TX16S/bottom.png Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 250 B

After

Width:  |  Height:  |  Size: 250 B

Before After
Before After

BIN
companion/src/images/simulator/TX16S/left.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

BIN
companion/src/images/simulator/TX16S/left_page.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

BIN
companion/src/images/simulator/TX16S/left_page2.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

BIN
companion/src/images/simulator/TX16S/left_rtn.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

BIN
companion/src/images/simulator/TX16S/left_sys.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

BIN
companion/src/images/simulator/TX16S/left_tele.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Before After
Before After

0
companion/src/images/simulator/TX16S/right.png Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

0
companion/src/images/simulator/TX16S/right_ent.png Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

0
companion/src/images/simulator/TX16S/right_mdl.png Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before After
Before After

0
companion/src/images/simulator/TX16S/top.png Executable file → Normal file
View file

Before

Width:  |  Height:  |  Size: 439 B

After

Width:  |  Height:  |  Size: 439 B

Before After
Before After

View file

@ -1,500 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>preferencesDialog</class>
<widget class="QDialog" name="preferencesDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>685</width>
<height>432</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>685</width>
<height>0</height>
</size>
</property>
<property name="windowTitle">
<string>Preferences</string>
</property>
<layout class="QGridLayout" name="gridLayout_4" columnstretch="1">
<property name="sizeConstraint">
<enum>QLayout::SetMinimumSize</enum>
</property>
<property name="verticalSpacing">
<number>2</number>
</property>
<property name="margin">
<number>4</number>
</property>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_2">
<property name="spacing">
<number>8</number>
</property>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="1">
<widget class="QLineEdit" name="snapshotPath">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Simu BackLight</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_19">
<property name="text">
<string>Simulator capture folder</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_11">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Joystick</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Remember simulator switches</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QCheckBox" name="simuSW">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QComboBox" name="joystickCB">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="joystickChkB">
<property name="text">
<string>Enable</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="snapshotClipboardCKB">
<property name="text">
<string>Use clipboard only</string>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QPushButton" name="joystickcalButton">
<property name="text">
<string>Calibrate</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QComboBox" name="backLightColor">
<item>
<property name="text">
<string>Blue</string>
</property>
</item>
<item>
<property name="text">
<string>Green</string>
</property>
</item>
<item>
<property name="text">
<string>Red</string>
</property>
</item>
<item>
<property name="text">
<string>Orange</string>
</property>
</item>
<item>
<property name="text">
<string>Yellow</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="3" column="2">
<widget class="QPushButton" name="snapshotPathButton">
<property name="text">
<string>Open Folder</string>
</property>
<property name="flat">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_7">
<item row="1" column="0">
<widget class="QLabel" name="label_9">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Personal splash library</string>
</property>
</widget>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QLineEdit" name="libraryPath">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="libraryPathButton">
<property name="text">
<string>Open Folder</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_7">
<item>
<widget class="QComboBox" name="splashincludeCB">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<item>
<property name="text">
<string>Include companion splashes</string>
</property>
</item>
<item>
<property name="text">
<string>Only user defined splashes</string>
</property>
</item>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_10">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Splash library behaviour</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Custom TX splash screen</string>
</property>
</widget>
</item>
<item row="0" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
<widget class="QLineEdit" name="SplashFileName">
<property name="enabled">
<bool>true</bool>
</property>
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="SplashSelect">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Open Image</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QToolButton" name="SplashLibraryDialogButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QToolButton" name="clearImageButton">
<property name="text">
<string>...</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="InvertPixels">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Invert Pixels</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QLabel" name="imageLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>128</width>
<height>64</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>128</width>
<height>64</height>
</size>
</property>
<property name="frameShape">
<enum>QFrame::Panel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
<property name="text">
<string/>
</property>
<property name="scaledContents">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="Line" name="line_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<tabstops>
<tabstop>backLightColor</tabstop>
<tabstop>joystickCB</tabstop>
<tabstop>joystickChkB</tabstop>
<tabstop>joystickcalButton</tabstop>
<tabstop>simuSW</tabstop>
<tabstop>snapshotPath</tabstop>
<tabstop>snapshotPathButton</tabstop>
<tabstop>snapshotClipboardCKB</tabstop>
<tabstop>libraryPath</tabstop>
<tabstop>libraryPathButton</tabstop>
<tabstop>splashincludeCB</tabstop>
<tabstop>SplashFileName</tabstop>
<tabstop>SplashSelect</tabstop>
<tabstop>SplashLibraryDialogButton</tabstop>
<tabstop>clearImageButton</tabstop>
<tabstop>InvertPixels</tabstop>
<tabstop>buttonBox</tabstop>
</tabstops>
<resources>
<include location="companion.qrc"/>
</resources>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>preferencesDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>370</x>
<y>49</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>preferencesDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>390</x>
<y>55</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -49,6 +49,7 @@
#include "translations.h"
#include "dialogs/filesyncdialog.h"
#include "profilechooser.h"
#include <QtGui>
#include <QFileInfo>
@ -130,11 +131,17 @@ MainWindow::MainWindow():
else {
if (!g.previousVersion().isEmpty())
g.warningId(g.warningId() | AppMessages::MSG_UPGRADED);
if (g.promptProfile()) {
QTimer::singleShot(updateDelay, this, SLOT(chooseProfile())); // add an extra second to give mainwindow time to load
updateDelay += 5000; // give user time to select profile before warnings
}
else {
if (checkProfileRadioExists(g.sessionId()))
QTimer::singleShot(updateDelay, this, SLOT(doAutoUpdates()));
else
g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE);
}
}
QTimer::singleShot(updateDelay, this, SLOT(displayWarnings()));
QStringList strl = QApplication::arguments();
@ -916,14 +923,15 @@ void MainWindow::changelog()
void MainWindow::customizeSplash()
{
CustomizeSplashDialog * dialog = new CustomizeSplashDialog(this);
auto * dialog = new CustomizeSplashDialog(this);
dialog->exec();
dialog->deleteLater();
}
void MainWindow::writeEeprom()
{
if (activeMdiChild()) activeMdiChild()->writeEeprom();
if (activeMdiChild())
activeMdiChild()->writeEeprom();
}
void MainWindow::readEeprom()
@ -1806,3 +1814,20 @@ void MainWindow::autoClose()
{
this->close();
}
void MainWindow::chooseProfile()
{
QMap<int, QString> active;
active = g.getActiveProfiles();
if (active.size() > 1) {
ProfileChooserDialog *pcd = new ProfileChooserDialog(this);
connect(pcd, &ProfileChooserDialog::profileChanged, this, &MainWindow::loadProfileId);
pcd->exec();
delete pcd;
// doi here as need to wait until dialog dismissed and current radio type is set
if (checkProfileRadioExists(g.sessionId()))
doAutoUpdates();
else
g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE);
}
}

View file

@ -128,6 +128,7 @@ class MainWindow : public QMainWindow
void exportSettings();
void importSettings();
void autoClose();
void chooseProfile();
void openUpdatesWaitDialog();
void closeUpdatesWaitDialog();

View file

@ -1490,18 +1490,18 @@ bool MdiChild::convertStorage(Board::Type from, Board::Type to, bool newFile)
isUntitled = true;
if (cstate.hasLogEntries(RadioDataConversionState::EVT_INF)) {
QDialog * msgBox = new QDialog(Q_NULLPTR, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
auto * msgBox = new QDialog(Q_NULLPTR, Qt::Dialog | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
ExportableTableView * tv = new ExportableTableView(msgBox);
auto * tv = new ExportableTableView(msgBox);
tv->setSortingEnabled(true);
tv->verticalHeader()->hide();
tv->setModel(cstate.getLogModel(RadioDataConversionState::EVT_INF, tv));
tv->resizeColumnsToContents();
tv->resizeRowsToContents();
QDialogButtonBox * btnBox = new QDialogButtonBox(QDialogButtonBox::Ok, this);
auto * btnBox = new QDialogButtonBox(QDialogButtonBox::Ok, this);
QVBoxLayout * lo = new QVBoxLayout(msgBox);
auto * lo = new QVBoxLayout(msgBox);
lo->addWidget(new QLabel(tr("<b>The conversion generated some important messages, please review them below.</b>")));
lo->addWidget(tv);
lo->addWidget(btnBox);

View file

@ -131,7 +131,7 @@ Channels::Channels(QWidget * parent, ModelData & model, GeneralSettings & genera
label->setContextMenuPolicy(Qt::CustomContextMenu);
label->setToolTip(tr("Popup menu available"));
label->setMouseTracking(true);
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(chn_customContextMenuRequested(QPoint)));
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint)));
tableLayout->addWidget(i, col++, label);
// Channel name
@ -312,118 +312,140 @@ void Channels::updateLine(int i)
lock = false;
}
void Channels::chnPaste()
void Channels::cmPaste()
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasFormat(MIMETYPE_CHN)) {
QByteArray chnData = mimeData->data(MIMETYPE_CHN);
LimitData *chn = &model->limitData[selectedChannel];
memcpy(chn, chnData.constData(), sizeof(LimitData));
updateLine(selectedChannel);
QByteArray data;
if (hasClipboardData(&data)) {
memcpy(&model->limitData[selectedIndex], data.constData(), sizeof(LimitData));
updateLine(selectedIndex);
emit modified();
}
}
void Channels::chnDelete()
void Channels::cmDelete()
{
int maxidx = chnCapability - 1;
for (int i=selectedChannel; i<maxidx; i++) {
if (!model->limitData[i].isEmpty() || !model->limitData[i+1].isEmpty()) {
LimitData *chn1 = &model->limitData[i];
LimitData *chn2 = &model->limitData[i+1];
memcpy(chn1, chn2, sizeof(LimitData));
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Channel. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
memmove(&model->limitData[selectedIndex], &model->limitData[selectedIndex + 1], (CPN_MAX_CHNOUT - (selectedIndex + 1)) * sizeof(LimitData));
model->limitData[chnCapability - 1].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1);
for (int i = selectedIndex; i < chnCapability; i++) {
updateLine(i);
}
}
model->limitData[maxidx].clear();
updateLine(maxidx);
emit modified();
}
void Channels::chnCopy()
void Channels::cmCopy()
{
QByteArray chnData;
chnData.append((char*)&model->limitData[selectedChannel],sizeof(LimitData));
QByteArray data;
data.append((char*)&model->limitData[selectedIndex], sizeof(LimitData));
QMimeData *mimeData = new QMimeData;
mimeData->setData(MIMETYPE_CHN, chnData);
mimeData->setData(MIMETYPE_CHANNEL, data);
QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard);
}
void Channels::chnCut()
void Channels::cmCut()
{
chnCopy();
chnClear();
cmCopy();
cmClear();
}
void Channels::chn_customContextMenuRequested(QPoint pos)
void Channels::onCustomContextMenuRequested(QPoint pos)
{
QLabel *label = (QLabel *)sender();
selectedChannel = label->property("index").toInt();
selectedIndex = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos);
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
bool hasData = mimeData->hasFormat(MIMETYPE_CHN);
bool moveUpAllowed = (selectedChannel > 0);
bool moveDownAllowed = (selectedChannel < (chnCapability - 1));
bool insertAllowed = (selectedChannel < (chnCapability - 1)) && (model->limitData[chnCapability - 1].isEmpty());
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(chnCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(chnCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(chnPaste()))->setEnabled(hasData);
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(chnClear()));
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(cmCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(cmCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"),this,SLOT(chnInsert()))->setEnabled(insertAllowed);
contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"),this,SLOT(chnDelete()));
contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"),this,SLOT(chnMoveUp()))->setEnabled(moveUpAllowed);
contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"),this,SLOT(chnMoveDown()))->setEnabled(moveDownAllowed);
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(chnClearAll()));
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"),this,SLOT(cmClearAll()));
contextMenu.exec(globalPos);
}
void Channels::chnMoveUp()
bool Channels::hasClipboardData(QByteArray * data) const
{
swapChnData(selectedChannel, selectedChannel - 1);
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
if (mimeData->hasFormat(MIMETYPE_CHANNEL)) {
if (data)
data->append(mimeData->data(MIMETYPE_CHANNEL));
return true;
}
return false;
}
void Channels::chnMoveDown()
bool Channels::insertAllowed() const
{
swapChnData(selectedChannel, selectedChannel + 1);
return ((selectedIndex < chnCapability - 1) && (model->limitData[chnCapability - 1].isEmpty()));
}
void Channels::chnClear()
bool Channels::moveDownAllowed() const
{
model->limitData[selectedChannel].clear();
updateLine(selectedChannel);
return selectedIndex < chnCapability - 1;
}
bool Channels::moveUpAllowed() const
{
return selectedIndex > 0;
}
void Channels::cmMoveUp()
{
swapData(selectedIndex, selectedIndex - 1);
}
void Channels::cmMoveDown()
{
swapData(selectedIndex, selectedIndex + 1);
}
void Channels::cmClear()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Channel. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
model->limitData[selectedIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_CLEAR, selectedIndex);
updateLine(selectedIndex);
emit modified();
}
void Channels::chnClearAll()
void Channels::cmClearAll()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Channels. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
for (int i = 0; i < chnCapability; i++) {
model->limitData[i].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_CLEAR, i);
updateLine(i);
}
emit modified();
}
void Channels::chnInsert()
void Channels::cmInsert()
{
for (int i=(chnCapability - 1); i>selectedChannel; i--) {
if (!model->limitData[i].isEmpty() || !model->limitData[i-1].isEmpty()) {
memcpy(&model->limitData[i], &model->limitData[i-1], sizeof(LimitData));
updateLine(i);
}
}
chnClear();
memmove(&model->limitData[selectedIndex + 1], &model->limitData[selectedIndex], (CPN_MAX_CHNOUT - (selectedIndex + 1)) * sizeof(LimitData));
model->limitData[selectedIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1);
update();
emit modified();
}
void Channels::swapChnData(int idx1, int idx2)
void Channels::swapData(int idx1, int idx2)
{
if ((idx1 != idx2) && (!model->limitData[idx1].isEmpty() || !model->limitData[idx2].isEmpty())) {
LimitData chntmp = model->limitData[idx2];
@ -431,6 +453,7 @@ void Channels::swapChnData(int idx1, int idx2)
LimitData *chn2 = &model->limitData[idx2];
memcpy(chn2, chn1, sizeof(LimitData));
memcpy(chn1, &chntmp, sizeof(LimitData));
model->updateAllReferences(ModelData::REF_UPD_TYPE_CHANNEL, ModelData::REF_UPD_ACT_SWAP, idx1, idx2);
updateLine(idx1);
updateLine(idx2);
emit modified();

View file

@ -26,7 +26,7 @@
#include <QtCore>
constexpr char MIMETYPE_CHN[] = "application/x-companion-chn";
constexpr char MIMETYPE_CHANNEL[] = "application/x-companion-channel";
class GVarGroup;
@ -68,19 +68,23 @@ class Channels : public ModelPanel
void ppmcenterEdited();
void update();
void updateLine(int index);
void chnDelete();
void chnCopy();
void chnPaste();
void chnCut();
void chnMoveUp();
void chnMoveDown();
void chnInsert();
void chnClear();
void chnClearAll();
void chn_customContextMenuRequested(QPoint pos);
void cmDelete();
void cmCopy();
void cmPaste();
void cmCut();
void cmMoveUp();
void cmMoveDown();
void cmInsert();
void cmClear();
void cmClearAll();
void onCustomContextMenuRequested(QPoint pos);
private:
void swapChnData(int idx1, int idx2);
bool hasClipboardData(QByteArray * data = nullptr) const;
bool insertAllowed() const;
bool moveDownAllowed() const;
bool moveUpAllowed() const;
void swapData(int idx1, int idx2);
QLineEdit *name[CPN_MAX_CHNOUT];
LimitsGroup *chnOffset[CPN_MAX_CHNOUT];
LimitsGroup *chnMin[CPN_MAX_CHNOUT];
@ -89,7 +93,7 @@ class Channels : public ModelPanel
QComboBox *curveCB[CPN_MAX_CHNOUT];
QSpinBox *centerSB[CPN_MAX_CHNOUT];
QCheckBox *symlimitsChk[CPN_MAX_CHNOUT];
int selectedChannel;
int selectedIndex;
int chnCapability;
};

View file

@ -117,7 +117,12 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
lock = true;
if (!firmware->getCapability(HasCvNames)) {
maxCurves = firmware->getCapability(NumCurves);
hasNames = firmware->getCapability(HasCvNames);
hasEnhanced = firmware->getCapability(EnhancedCurves);
maxPoints = firmware->getCapability(NumCurvePoints);
if (!hasNames) {
ui->curveName->hide();
ui->curveNameLabel->hide();
}
@ -127,14 +132,14 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
connect(scene, SIGNAL(newPoint(int, int)), this, SLOT(onSceneNewPoint(int, int)));
ui->curvePreview->setScene(scene);
int numcurves=firmware->getCapability(NumCurves);
int limit;
if (numcurves>16) {
limit=numcurves/2;
if (maxCurves > 16) {
limit = maxCurves / 2;
} else {
limit=numcurves;
limit = maxCurves;
}
for (int i=0; i<numcurves; i++) {
for (int i = 0; i < maxCurves; i++) {
visibleCurves[i] = false;
// The edit curve button
@ -154,7 +159,7 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
edit->setText(tr("Curve %1").arg(i + 1));
edit->setContextMenuPolicy(Qt::CustomContextMenu);
edit->setToolTip(tr("Popup menu available"));
connect(edit, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(ShowContextMenu(const QPoint&)));
connect(edit, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(onCustomContextMenuRequested(const QPoint&)));
connect(edit, SIGNAL(clicked()), this, SLOT(editCurve()));
if (i < limit) {
ui->curvesLayout->addWidget(edit, i, 1, 1, 1);
@ -176,7 +181,7 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
QSpacerItem * item = new QSpacerItem(1,1, QSizePolicy::Fixed, QSizePolicy::Expanding);
ui->curvesLayout->addItem(item,limit + 1, 1, 1, 1, 0);
if (limit!=numcurves) {
if (limit != maxCurves) {
QSpacerItem * item2 = new QSpacerItem(1,1, QSizePolicy::Fixed, QSizePolicy::Expanding);
ui->curvesLayout2->addItem(item2,limit + 1, 1, 1, 1, 0);
}
@ -199,7 +204,7 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
ui->pointsLayout->addWidget(spny[i], i, 1, 1, 1);
bool insert;
if (firmware->getCapability(EnhancedCurves)) {
if (hasEnhanced) {
insert = (i >= 1);
}
else {
@ -250,7 +255,7 @@ void Curves::update()
{
lock = true;
if (firmware->getCapability(HasCvNames)) {
if (hasNames) {
ui->curveName->setText(model->curves[currentCurve].name);
}
@ -272,7 +277,7 @@ void Curves::updateCurveType()
int index = 0;
if (firmware->getCapability(EnhancedCurves)) {
if (hasEnhanced) {
index = model->curves[currentCurve].count - 2;
}
else {
@ -318,8 +323,7 @@ void Curves::updateCurve()
pen.setWidth(1);
pen.setStyle(Qt::SolidLine);
int numcurves = firmware->getCapability(NumCurves);
for (int k=0; k<numcurves; k++) {
for (int k = 0; k < maxCurves; k++) {
pen.setColor(colors[k]);
if (currentCurve != k && visibleCurves[k]) {
int numpoints = model->curves[k].count;
@ -360,7 +364,8 @@ void Curves::updateCurve()
connect(nodex, SIGNAL(unfocus()), this, SLOT(onNodeUnfocus()));
connect(nodex, SIGNAL(deleteMe()), this, SLOT(onNodeDelete()));
scene->addItem(nodex);
if (i>0) scene->addItem(new Edge(nodel, nodex));
if (i > 0)
scene->addItem(new Edge(nodel, nodex));
}
lock = false;
@ -444,17 +449,14 @@ void Curves::onNodeUnfocus()
bool Curves::allowCurveType(int points, CurveData::CurveType type)
{
int numcurves = firmware->getCapability(NumCurves);
int totalpoints = 0;
for (int i=0; i<numcurves; i++) {
for (int i = 0; i < maxCurves; i++) {
int cvPoints = (i == currentCurve ? points : model->curves[i].count);
CurveData::CurveType cvType = (i == currentCurve ? type : model->curves[i].type);
totalpoints += cvPoints + (cvType == CurveData::CURVE_TYPE_CUSTOM ? cvPoints - 2 : 0);
}
int fwpoints = firmware->getCapability(NumCurvePoints);
if (totalpoints > fwpoints) {
if (totalpoints > maxPoints) {
QMessageBox::warning(this, "companion", tr("Not enough free points in EEPROM to store the curve."));
return false;
}
@ -491,6 +493,7 @@ void Curves::on_curveCustom_currentIndexChanged(int index)
if (!lock) {
CurveData::CurveType type = (CurveData::CurveType)index;
int numpoints = ui->curvePoints->itemData(ui->curvePoints->currentIndex()).toInt();
if (allowCurveType(model->curves[currentCurve].count, type)) {
model->curves[currentCurve].type = type;
@ -591,68 +594,6 @@ void Curves::on_curveApply_clicked()
emit modified();
}
void Curves::ShowContextMenu(const QPoint& pos) // this is a slot
{
QPushButton *button = (QPushButton *)sender();
int index = button->property("index").toInt();
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
QPoint globalPos = button->mapToGlobal(pos);
QMenu myMenu;
QAction *action;
action = myMenu.addAction(CompanionIcon("copy.png"),tr("Copy"));
action->setProperty("index", CURVE_COPY);
action = myMenu.addAction(CompanionIcon("paste.png"),tr("Paste"));
if (!mimeData->hasFormat("application/x-companion-curve-item")) {
action->setEnabled(false);
}
action->setProperty("index", CURVE_PASTE);
action = myMenu.addAction(CompanionIcon("clear.png"),tr("Clear"));
action->setProperty("index", CURVE_RESET);
action = myMenu.addAction(CompanionIcon("clear.png"),tr("Clear all curves"));
action->setProperty("index", CURVE_RESETALL);
QAction* selectedItem = myMenu.exec(globalPos);
if (selectedItem) {
int action=selectedItem->property("index").toInt();
if (action==CURVE_COPY) {
QByteArray curveData;
QMimeData *mimeData2 = new QMimeData;
curveData.append((char*)&model->curves[index], sizeof(CurveData));
mimeData2->setData("application/x-companion-curve-item", curveData);
QApplication::clipboard()->setMimeData(mimeData2, QClipboard::Clipboard);
}
else if (action==CURVE_PASTE) {
QByteArray curveData = mimeData->data("application/x-companion-curve-item");
CurveData *curve = &model->curves[index];
memcpy(curve, curveData.constData(), sizeof(CurveData));
update();
emit modified();
}
else if (action==CURVE_RESET) {
int res = QMessageBox::question(this, "companion", tr("Are you sure you want to reset curve %1?").arg(index+1), QMessageBox::Yes | QMessageBox::No);
if (res == QMessageBox::Yes) {
model->curves[index].clear(5);
update();
emit modified();
}
}
else if (action==CURVE_RESETALL) {
int res = QMessageBox::question(this, "companion", tr("Are you sure you want to reset all curves?"), QMessageBox::Yes | QMessageBox::No);
if (res == QMessageBox::Yes) {
int numcurves = firmware->getCapability(NumCurves);
for (int i=0; i<numcurves; i++) {
model->curves[i].clear(5);
}
update();
emit modified();
}
}
}
}
void Curves::onPointSizeEdited()
{
if (!lock) {
@ -699,7 +640,7 @@ void Curves::onSceneNewPoint(int x, int y)
}
numpoints++;
model->curves[currentCurve].count = numpoints;
for (int idx=(numpoints-1); idx>(newidx); idx--) {
for (int idx = (numpoints - 1); idx > newidx; idx--) {
model->curves[currentCurve].points[idx] = model->curves[currentCurve].points[idx - 1];
}
model->curves[currentCurve].points[newidx].x = x;
@ -709,6 +650,150 @@ void Curves::onSceneNewPoint(int x, int y)
}
}
void Curves::onCustomContextMenuRequested(QPoint pos)
{
QPushButton *button = (QPushButton *)sender();
selectedIndex = button->property("index").toInt();
QPoint globalPos = button->mapToGlobal(pos);
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"), this, SLOT(cmCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"), this, SLOT(cmCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"), this, SLOT(cmPaste()))->setEnabled(hasClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"), this, SLOT(cmClear()));
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 Curves::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 Curves::insertAllowed() const
{
return ((selectedIndex < maxCurves - 1) && (model->curves[maxCurves - 1].isEmpty()));
}
bool Curves::moveDownAllowed() const
{
return selectedIndex < maxCurves - 1;
}
bool Curves::moveUpAllowed() const
{
return selectedIndex > 0;
}
void Curves::cmClear()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Curve. Are you sure?"), 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();
emit modified();
}
void Curves::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();
emit modified();
}
void Curves::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 Curves::cmCut()
{
cmCopy();
cmClear();
}
void Curves::cmDelete()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Curve. Are you sure?"), 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);
update();
emit modified();
}
void Curves::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);
update();
emit modified();
}
void Curves::cmMoveDown()
{
swapData(selectedIndex, selectedIndex + 1);
}
void Curves::cmMoveUp()
{
swapData(selectedIndex, selectedIndex - 1);
}
void Curves::cmPaste()
{
QByteArray data;
if (hasClipboardData(&data)) {
CurveData *cd = &model->curves[selectedIndex];
memcpy(cd, data.constData(), sizeof(CurveData));
update();
emit modified();
}
}
void Curves::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();
emit modified();
}
}
CustomScene::CustomScene(QGraphicsView * view) :
QGraphicsScene(view)
{

View file

@ -26,12 +26,7 @@
#include <QGraphicsScene>
#include <QGraphicsView>
enum CopyAction {
CURVE_COPY,
CURVE_PASTE,
CURVE_RESET,
CURVE_RESETALL
};
constexpr char MIMETYPE_CURVE[] = "application/x-companion-curve";
namespace Ui {
class Curves;
@ -71,7 +66,6 @@ class Curves : public ModelPanel
private slots:
void editCurve();
void ShowContextMenu(const QPoint& pos);
void plotCurve(bool checked);
void on_curveName_editingFinished();
void on_curvePoints_currentIndexChanged(int index);
@ -86,6 +80,16 @@ class Curves : public ModelPanel
void onSceneNewPoint(int x, int y);
void onPointSizeEdited();
void onNodeDelete();
void onCustomContextMenuRequested(QPoint pos);
void cmClear();
void cmClearAll();
void cmCopy();
void cmCut();
void cmDelete();
void cmInsert();
void cmPaste();
void cmMoveDown();
void cmMoveUp();
protected:
virtual void resizeEvent(QResizeEvent *event);
@ -105,6 +109,16 @@ class Curves : public ModelPanel
bool allowCurveType(int points, CurveData::CurveType type);
void setPointY(int i, int x, int y);
CustomScene * scene;
int maxCurves;
int hasNames;
int hasEnhanced;
int maxPoints;
int selectedIndex;
bool hasClipboardData(QByteArray * data = nullptr) const;
bool insertAllowed() const;
bool moveDownAllowed() const;
bool moveUpAllowed() const;
void swapData(int idx1, int idx2);
};
#endif // _CURVES_H_

View file

@ -127,7 +127,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model,
else
label->setText(tr("GF%1").arg(i+1));
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(fsw_customContextMenuRequested(QPoint)));
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint)));
tableLayout->addWidget(i, 0, label);
// s1.report("label");
@ -608,76 +608,63 @@ void CustomFunctionsPanel::update()
lock = false;
}
void CustomFunctionsPanel::fswPaste()
void CustomFunctionsPanel::cmPaste()
{
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasFormat(MIMETYPE_FSW)) {
QByteArray fswData = mimeData->data(MIMETYPE_FSW);
CustomFunctionData *fsw = &functions[selectedFunction];
memcpy(fsw, fswData.constData(), sizeof(CustomFunctionData));
resetCBsAndRefresh(selectedFunction);
QByteArray data;
if (hasClipboardData(&data)) {
memcpy(&functions[selectedIndex], data.constData(), sizeof(CustomFunctionData));
resetCBsAndRefresh(selectedIndex);
emit modified();
}
}
void CustomFunctionsPanel::fswDelete()
void CustomFunctionsPanel::cmDelete()
{
int maxidx = fswCapability - 1;
for (int i=selectedFunction; i<maxidx; i++) {
if (!functions[i].isEmpty() || !functions[i+1].isEmpty()) {
CustomFunctionData *fsw1 = &functions[i];
CustomFunctionData *fsw2 = &functions[i+1];
memcpy(fsw1, fsw2, sizeof(CustomFunctionData));
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
memmove(&functions[selectedIndex], &functions[selectedIndex + 1], (CPN_MAX_SPECIAL_FUNCTIONS - (selectedIndex + 1)) * sizeof(CustomFunctionData));
functions[fswCapability - 1].clear();
for (int i = selectedIndex; i < (fswCapability - 1); i++) {
resetCBsAndRefresh(i);
}
}
functions[maxidx].clear();
resetCBsAndRefresh(maxidx);
emit modified();
}
void CustomFunctionsPanel::fswCopy()
void CustomFunctionsPanel::cmCopy()
{
QByteArray fswData;
fswData.append((char*)&functions[selectedFunction], sizeof(CustomFunctionData));
QByteArray data;
data.append((char*)&functions[selectedIndex], sizeof(CustomFunctionData));
QMimeData *mimeData = new QMimeData;
mimeData->setData(MIMETYPE_FSW, fswData);
mimeData->setData(MIMETYPE_CUSTOM_FUNCTION, data);
QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
}
void CustomFunctionsPanel::fswCut()
void CustomFunctionsPanel::cmCut()
{
fswCopy();
fswClear();
cmCopy();
cmClear();
}
void CustomFunctionsPanel::fsw_customContextMenuRequested(QPoint pos)
void CustomFunctionsPanel::onCustomContextMenuRequested(QPoint pos)
{
QLabel *label = (QLabel *)sender();
selectedFunction = label->property("index").toInt();
selectedIndex = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos);
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
bool hasData = mimeData->hasFormat(MIMETYPE_FSW);
bool moveUpAllowed = (selectedFunction > 0);
bool moveDownAllowed = (selectedFunction < (fswCapability - 1));
bool insertAllowed = (selectedFunction < (fswCapability - 1)) && (functions[fswCapability - 1].isEmpty());
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(fswCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(fswCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(fswPaste()))->setEnabled(hasData);
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(fswClear()));
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(cmCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(cmCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"),this,SLOT(fswInsert()))->setEnabled(insertAllowed);
contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"),this,SLOT(fswDelete()));
contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"),this,SLOT(fswMoveUp()))->setEnabled(moveUpAllowed);
contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"),this,SLOT(fswMoveDown()))->setEnabled(moveDownAllowed);
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(fswClearAll()));
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"),this,SLOT(cmClearAll()));
contextMenu.exec(globalPos);
}
@ -762,25 +749,31 @@ void CustomFunctionsPanel::populateFuncParamCB(QComboBox *b, uint function, unsi
}
}
void CustomFunctionsPanel::fswMoveUp()
void CustomFunctionsPanel::cmMoveUp()
{
swapFuncData(selectedFunction, selectedFunction - 1);
swapData(selectedIndex, selectedIndex - 1);
}
void CustomFunctionsPanel::fswMoveDown()
void CustomFunctionsPanel::cmMoveDown()
{
swapFuncData(selectedFunction, selectedFunction + 1);
swapData(selectedIndex, selectedIndex + 1);
}
void CustomFunctionsPanel::fswClear()
void CustomFunctionsPanel::cmClear()
{
functions[selectedFunction].clear();
resetCBsAndRefresh(selectedFunction);
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
functions[selectedIndex].clear();
resetCBsAndRefresh(selectedIndex);
emit modified();
}
void CustomFunctionsPanel::fswClearAll()
void CustomFunctionsPanel::cmClearAll()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Special Functions. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
for (int i=0; i<fswCapability; i++) {
functions[i].clear();
resetCBsAndRefresh(i);
@ -788,18 +781,17 @@ void CustomFunctionsPanel::fswClearAll()
emit modified();
}
void CustomFunctionsPanel::fswInsert()
void CustomFunctionsPanel::cmInsert()
{
for (int i=(fswCapability - 1); i>selectedFunction; i--) {
if (!functions[i].isEmpty() || !functions[i-1].isEmpty()) {
memcpy(&functions[i], &functions[i-1], sizeof(CustomFunctionData));
memmove(&functions[selectedIndex + 1], &functions[selectedIndex], (CPN_MAX_SPECIAL_FUNCTIONS - (selectedIndex + 1)) * sizeof(CustomFunctionData));
functions[selectedIndex].clear();
for (int i = selectedIndex; i < (fswCapability - 1); i++) {
resetCBsAndRefresh(i);
}
}
fswClear();
}
void CustomFunctionsPanel::swapFuncData(int idx1, int idx2)
void CustomFunctionsPanel::swapData(int idx1, int idx2)
{
if ((idx1 != idx2) && (!functions[idx1].isEmpty() || !functions[idx2].isEmpty())) {
CustomFunctionData fswtmp = functions[idx2];
@ -823,3 +815,30 @@ void CustomFunctionsPanel::resetCBsAndRefresh(int idx)
refreshCustomFunction(idx);
lock = false;
}
bool CustomFunctionsPanel::hasClipboardData(QByteArray * data) const
{
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
if (mimeData->hasFormat(MIMETYPE_CUSTOM_FUNCTION)) {
if (data)
data->append(mimeData->data(MIMETYPE_CUSTOM_FUNCTION));
return true;
}
return false;
}
bool CustomFunctionsPanel::insertAllowed() const
{
return ((selectedIndex < fswCapability - 1) && (model->curves[fswCapability - 1].isEmpty()));
}
bool CustomFunctionsPanel::moveDownAllowed() const
{
return selectedIndex < fswCapability - 1;
}
bool CustomFunctionsPanel::moveUpAllowed() const
{
return selectedIndex > 0;
}

View file

@ -30,7 +30,7 @@ class RawSourceFilterItemModel;
class RawSwitchFilterItemModel;
class TimerEdit;
constexpr char MIMETYPE_FSW[] = "application/x-companion-fsw";
constexpr char MIMETYPE_CUSTOM_FUNCTION[] = "application/x-companion-custom-function";
class RepeatComboBox: public QComboBox
{
@ -67,7 +67,7 @@ class CustomFunctionsPanel : public GenericPanel
void updateDataModels();
void customFunctionEdited();
void functionEdited();
void fsw_customContextMenuRequested(QPoint pos);
void onCustomContextMenuRequested(QPoint pos);
void refreshCustomFunction(int index, bool modified=false);
void onChildModified();
bool playSound(int index);
@ -75,21 +75,25 @@ class CustomFunctionsPanel : public GenericPanel
void toggleSound(bool play);
void onMediaPlayerStateChanged(QMediaPlayer::State state);
void onMediaPlayerError(QMediaPlayer::Error error);
void fswDelete();
void fswCopy();
void fswPaste();
void fswCut();
void fswMoveUp();
void fswMoveDown();
void fswInsert();
void fswClear();
void fswClearAll();
void cmDelete();
void cmCopy();
void cmPaste();
void cmCut();
void cmMoveUp();
void cmMoveDown();
void cmInsert();
void cmClear();
void cmClearAll();
private:
void populateFuncCB(QComboBox *b, unsigned int value);
void populateGVmodeCB(QComboBox *b, unsigned int value);
void populateFuncParamCB(QComboBox *b, uint function, unsigned int value, unsigned int adjustmode=0);
void swapFuncData(int idx1, int idx2);
bool hasClipboardData(QByteArray * data = nullptr) const;
bool insertAllowed() const;
bool moveDownAllowed() const;
bool moveUpAllowed() const;
void swapData(int idx1, int idx2);
void resetCBsAndRefresh(int idx);
RawSwitchFilterItemModel * rawSwitchItemModel;
RawSourceFilterItemModel * rawSrcAllItemModel;
@ -113,7 +117,7 @@ class CustomFunctionsPanel : public GenericPanel
QSlider * fswtchBLcolor[CPN_MAX_SPECIAL_FUNCTIONS];
QMediaPlayer * mediaPlayer;
int selectedFunction;
int selectedIndex;
int fswCapability;
};

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,10 @@
class RawSwitchFilterItemModel;
constexpr char MIMETYPE_FLIGHTMODE[] = "application/x-companion-flightmode";
constexpr char MIMETYPE_GVAR_PARAMS[] = "application/x-companion-gvar-params";
constexpr char MIMETYPE_GVAR_VALUE[] = "application/x-companion-gvar-value";
namespace Ui {
class FlightMode;
}
@ -42,6 +46,7 @@ class FlightModePanel : public ModelPanel
signals:
void nameModified();
void datachanged();
private slots:
void phaseName_editingFinished();
@ -61,18 +66,36 @@ class FlightModePanel : public ModelPanel
void phaseGVMax_editingFinished();
void phaseREValue_editingFinished();
void phaseREUse_currentIndexChanged(int index);
void name_customContextMenuRequested(const QPoint & pos);
void fmClear();
void gvLabel_customContextMenuRequested(const QPoint & pos);
void gvClear();
void onCustomContextMenuRequested(QPoint pos);
void cmClear();
void cmClearAll();
void cmCopy();
void cmCut();
void cmDelete();
void cmInsert();
void cmPaste();
void cmMoveDown();
void cmMoveUp();
void gvOnCustomContextMenuRequested(QPoint pos);
void gvCmClear();
void gvCmClearAll();
void gvCmCopy();
void gvCmCut();
void gvCmDelete();
void gvCmInsert();
void gvCmPaste();
void gvCmMoveDown();
void gvCmMoveUp();
private:
Ui::FlightMode *ui;
int phaseIdx;
FlightModeData & phase;
int fmCount;
int reCount;
int gvCount;
int gvIdx;
int trimCount;
QVector<QLabel *> trimsLabel;
QLineEdit * gvNames[CPN_MAX_GVARS];
QDoubleSpinBox * gvValues[CPN_MAX_GVARS];
@ -88,12 +111,27 @@ class FlightModePanel : public ModelPanel
QVector<QSpinBox *> trimsValue;
QVector<QSlider *> trimsSlider;
RawSwitchFilterItemModel * rawSwitchItemModel;
Board::Type board;
void trimUpdate(unsigned int trim);
void updateGVar(int index);
void updateRotaryEncoder(int index);
void setGVSB(QDoubleSpinBox * spinBox, int min, int max, int val);
void populateGvarUnitCB(QComboBox * cb);
void populateGvarPrecCB(QComboBox * cb);
bool hasClipboardData(QByteArray * data = nullptr) const;
bool insertAllowed() const;
bool moveDownAllowed() const;
bool moveUpAllowed() const;
void swapData(int idx1, int idx2);
bool gvHasClipboardData() const;
bool gvHasDefnClipboardData(QByteArray * data = nullptr) const;
bool gvHasValueClipboardData(QByteArray * data = nullptr) const;
bool gvDeleteAllowed() const;
bool gvInsertAllowed() const;
bool gvMoveDownAllowed() const;
bool gvMoveUpAllowed() const;
void gvSwapData(int idx1, int idx2);
};
class FlightModesPanel : public ModelPanel

View file

@ -108,9 +108,8 @@ void InputsPanel::AddInputLine(int dest)
const ExpoData &md = model->expoData[dest];
qba.append((const char*)&md, sizeof(ExpoData));
destId = md.chn + 1;
const QVector<const ExpoData *> expos = model->expos(md.chn);
newChan = (expos.constFirst() == &md);
hasSibs = (expos.constLast() != &md);
newChan = model->isExpoParent(dest);
hasSibs = (model->hasExpoChildren(dest) || model->hasExpoSiblings(dest));
}
QListWidgetItem *itm = new QListWidgetItem(getInputText(dest, newChan));
itm->setData(Qt::UserRole, qba);
@ -151,7 +150,7 @@ QString InputsPanel::getInputText(int dest, bool newChan)
bool InputsPanel::gm_insertExpo(int idx)
{
if (idx < 0 || idx >= CPN_MAX_EXPOS || model->expoData[CPN_MAX_EXPOS-1].mode > 0) {
QMessageBox::information(this, CPN_STR_APP_NAME, tr("Not enough available inputs!"));
QMessageBox::information(this, CPN_STR_APP_NAME, tr("Not enough available Inputs!"));
return false;
}
@ -172,19 +171,20 @@ void InputsPanel::gm_deleteExpo(int index, bool clearName)
void InputsPanel::gm_openExpo(int index)
{
if(index<0 || index>=CPN_MAX_EXPOS) return;
if (index < 0 || index >= CPN_MAX_EXPOS)
return;
ExpoData mixd(model->expoData[index]);
ExpoData ed(model->expoData[index]);
QString inputName;
if (firmware->getCapability(VirtualInputs))
inputName = model->inputNames[mixd.chn];
inputName = model->inputNames[ed.chn];
ExpoDialog *g = new ExpoDialog(this, *model, &mixd, generalSettings, firmware, inputName);
ExpoDialog *g = new ExpoDialog(this, *model, &ed, generalSettings, firmware, inputName);
if (g->exec()) {
model->expoData[index] = mixd;
model->expoData[index] = ed;
if (firmware->getCapability(VirtualInputs))
strncpy(model->inputNames[mixd.chn], inputName.toLatin1().data(), 4);
strncpy(model->inputNames[ed.chn], inputName.toLatin1().data(), INPUT_NAME_LEN);
emit modified();
update();
}
@ -200,23 +200,24 @@ void InputsPanel::gm_openExpo(int index)
int InputsPanel::getExpoIndex(unsigned int dch)
{
unsigned int i = 0;
while (model->expoData[i].chn<=dch && model->expoData[i].mode && i<CPN_MAX_EXPOS) i++;
if(i==CPN_MAX_EXPOS) return -1;
while (model->expoData[i].chn <= dch && model->expoData[i].mode && i < CPN_MAX_EXPOS)
i++;
if (i == CPN_MAX_EXPOS)
return -1;
return i;
}
QList<int> InputsPanel::createExpoListFromSelected()
{
QList<int> list;
foreach (QListWidgetItem *item, ExposlistWidget->selectedItems()) {
int idx = item->data(Qt::UserRole).toByteArray().at(0);
if(idx>=0 && idx<CPN_MAX_EXPOS) list << idx;
if (idx >= 0 && idx < CPN_MAX_EXPOS)
list << idx;
}
return list;
}
void InputsPanel::setSelectedByExpoList(QList<int> list)
{
for (int i = 0; i < ExposlistWidget->count(); i++) {
@ -229,15 +230,13 @@ void InputsPanel::setSelectedByExpoList(QList<int> list)
void InputsPanel::exposDelete(bool ask)
{
QList<int> list = createExpoListFromSelected();
if(list.isEmpty()) return;
if (list.isEmpty())
return;
QMessageBox::StandardButton ret = QMessageBox::No;
if (ask)
ret = QMessageBox::warning(this, "companion",
tr("Delete Selected Inputs?"),
QMessageBox::Yes | QMessageBox::No);
ret = QMessageBox::warning(this, CPN_STR_APP_NAME, tr("Delete selected Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No);
if ((ret == QMessageBox::Yes) || (!ask)) {
exposDeleteList(list, ask);
@ -256,13 +255,13 @@ void InputsPanel::exposCopy()
{
QList<int> list = createExpoListFromSelected();
QByteArray mxData;
QByteArray exData;
foreach (int idx, list) {
mxData.append((char*)&model->expoData[idx], sizeof(ExpoData));
exData.append((char*)&model->expoData[idx], sizeof(ExpoData));
}
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-companion-expo", mxData);
mimeData->setData(MIMETYPE_EXPO, exData);
QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
}
@ -285,30 +284,31 @@ void InputsPanel::mimeExpoDropped(int index, const QMimeData *data, Qt::DropActi
void InputsPanel::pasteExpoMimeData(const QMimeData * mimeData, int destIdx)
{
if (mimeData->hasFormat("application/x-companion-expo")) {
int idx; // mixer index
if (mimeData->hasFormat(MIMETYPE_EXPO)) {
int idx; // expo index
int dch;
if (destIdx < 0) {
dch = -destIdx - 1;
idx = getExpoIndex(dch) - 1; //get expo index to insert
} else {
}
else {
idx = destIdx;
dch = model->expoData[idx].chn;
}
QByteArray mxData = mimeData->data("application/x-companion-expo");
QByteArray exData = mimeData->data(MIMETYPE_EXPO);
int i = 0;
while (i < mxData.size()) {
while (i < exData.size()) {
idx++;
if (!gm_insertExpo(idx))
break;
ExpoData *md = &model->expoData[idx];
memcpy(md, mxData.mid(i, sizeof(ExpoData)).constData(), sizeof(ExpoData));
const int oldChan = md->chn;
md->chn = dch;
maybeCopyInputName(oldChan, md->chn);
ExpoData *ed = &model->expoData[idx];
memcpy(ed, exData.mid(i, sizeof(ExpoData)).constData(), sizeof(ExpoData));
const int oldChan = ed->chn;
ed->chn = dch;
maybeCopyInputName(oldChan, ed->chn);
i += sizeof(ExpoData);
}
@ -334,7 +334,6 @@ void InputsPanel::exposDuplicate()
exposPaste();
}
void InputsPanel::expoOpen(QListWidgetItem *item)
{
if (!item)
@ -348,7 +347,8 @@ void InputsPanel::expoOpen(QListWidgetItem *item)
return;
model->expoData[idx].chn = ch;
expoInserted = true;
} else {
}
else {
expoInserted = false;
}
gm_openExpo(idx);
@ -360,7 +360,8 @@ void InputsPanel::expoAdd()
if (index < 0) { // if empty then return relevant index
expoOpen();
} else {
}
else {
index++;
if (!gm_insertExpo(index))
return;
@ -376,37 +377,57 @@ void InputsPanel::expolistWidget_customContextMenuRequested(QPoint pos)
const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
bool hasData = mimeData->hasFormat("application/x-companion-expo");
bool hasData = mimeData->hasFormat(MIMETYPE_EXPO);
selectedIdx = getIndexFromSelected();
inputIdx = getInputIndexFromSelected();
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("add.png"), tr("&Add"),this,SLOT(expoAdd()),tr("Ctrl+A"));
contextMenu.addAction(CompanionIcon("edit.png"), tr("&Edit"),this,SLOT(expoOpen()),tr("Enter"));
QMenu *contextMenuLines = contextMenu.addMenu(tr("Lines"));
contextMenuLines->addAction(CompanionIcon("add.png"), tr("&Add"), this, SLOT(expoAdd()), tr("Ctrl+A"));
contextMenuLines->addAction(CompanionIcon("edit.png"), tr("&Edit"), this, SLOT(expoOpen()), tr("Enter"));
contextMenuLines->addSeparator();
contextMenuLines->addAction(CompanionIcon("clear.png"), tr("&Delete"), this, SLOT(exposDelete()), tr("Delete"));
contextMenuLines->addAction(CompanionIcon("copy.png"), tr("&Copy"), this, SLOT(exposCopy()), tr("Ctrl+C"));
contextMenuLines->addAction(CompanionIcon("cut.png"), tr("&Cut"), this, SLOT(exposCut()), tr("Ctrl+X"));
contextMenuLines->addAction(CompanionIcon("paste.png"), tr("&Paste"), this, SLOT(exposPaste()), tr("Ctrl+V"))->setEnabled(hasData);
contextMenuLines->addAction(CompanionIcon("duplicate.png"), tr("Du&plicate"), this, SLOT(exposDuplicate()), tr("Ctrl+U"));
contextMenuLines->addSeparator();
contextMenuLines->addAction(CompanionIcon("moveup.png"), tr("Move Up"), this, SLOT(moveExpoUp()), tr("Ctrl+Up"));
contextMenuLines->addAction(CompanionIcon("movedown.png"), tr("Move Down"), this, SLOT(moveExpoDown()), tr("Ctrl+Down"));
QMenu *contextMenuInputs = contextMenu.addMenu(tr("Input"));
contextMenuInputs->addAction(CompanionIcon("arrow-right.png"), tr("Insert"), this, SLOT(cmInputInsert()))->setEnabled(cmInputInsertAllowed());
contextMenuInputs->addAction(CompanionIcon("arrow-left.png"), tr("Delete"), this, SLOT(cmInputDelete()));
contextMenuInputs->addAction(CompanionIcon("moveup.png"), tr("Move Up"), this, SLOT(cmInputMoveUp()))->setEnabled(cmInputMoveUpAllowed());
contextMenuInputs->addAction(CompanionIcon("movedown.png"), tr("Move Down"), this, SLOT(cmInputMoveDown()))->setEnabled(cmInputMoveDownAllowed());
contextMenuInputs->addSeparator();
contextMenuInputs->addAction(CompanionIcon("clear.png"), tr("Clear"), this, SLOT(cmInputClear()))->setEnabled(isExpoIndex(selectedIdx));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("&Delete"),this,SLOT(exposDelete()),tr("Delete"));
contextMenu.addAction(CompanionIcon("copy.png"), tr("&Copy"),this,SLOT(exposCopy()),tr("Ctrl+C"));
contextMenu.addAction(CompanionIcon("cut.png"), tr("&Cut"),this,SLOT(exposCut()),tr("Ctrl+X"));
contextMenu.addAction(CompanionIcon("paste.png"), tr("&Paste"),this,SLOT(exposPaste()),tr("Ctrl+V"))->setEnabled(hasData);
contextMenu.addAction(CompanionIcon("duplicate.png"), tr("Du&plicate"),this,SLOT(exposDuplicate()),tr("Ctrl+U"));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"),this,SLOT(moveExpoUp()),tr("Ctrl+Up"));
contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"),this,SLOT(moveExpoDown()),tr("Ctrl+Down"));
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"), this, SLOT(clearExpos()));
contextMenu.addSeparator();
contextMenu.addActions(ExposlistWidget->actions());
contextMenu.exec(globalPos);
}
void InputsPanel::expolistWidget_KeyPress(QKeyEvent *event)
{
if(event->matches(QKeySequence::SelectAll)) expoAdd(); //Ctrl A
if(event->matches(QKeySequence::Delete)) exposDelete();
if(event->matches(QKeySequence::Copy)) exposCopy();
if(event->matches(QKeySequence::Cut)) exposCut();
if(event->matches(QKeySequence::Paste)) exposPaste();
if(event->matches(QKeySequence::Underline)) exposDuplicate();
if (event->matches(QKeySequence::SelectAll))
expoAdd(); //Ctrl A
if (event->matches(QKeySequence::Delete))
exposDelete();
if (event->matches(QKeySequence::Copy))
exposCopy();
if (event->matches(QKeySequence::Cut))
exposCut();
if (event->matches(QKeySequence::Paste))
exposPaste();
if (event->matches(QKeySequence::Underline))
exposDuplicate();
if(event->key()==Qt::Key_Return || event->key()==Qt::Key_Enter) expoOpen();
if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)
expoOpen();
if (event->matches(QKeySequence::MoveToNextLine))
ExposlistWidget->setCurrentRow(ExposlistWidget->currentRow() + 1);
if (event->matches(QKeySequence::MoveToPreviousLine))
@ -494,8 +515,11 @@ void InputsPanel::exposDeleteList(QList<int> list, bool clearName)
void InputsPanel::clearExpos()
{
if (QMessageBox::question(this, tr("Clear Inputs?"), tr("Really clear all the inputs?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::Yes) {
model->clearInputs();
for (int i = 0; i < inputsCount; i++) {
model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_CLEAR, i);
}
emit modified();
update();
}
@ -512,8 +536,172 @@ void InputsPanel::maybeCopyInputName(int srcChan, int destChan)
if (srcEmpty) {
// if destination input name is empty, copy it from source expo input
if (!strlen(model->inputNames[destChan]))
strncpy(model->inputNames[destChan], model->inputNames[srcChan], 5);
strncpy(model->inputNames[destChan], model->inputNames[srcChan], INPUT_NAME_LEN);
// clear the emptry source channel name
model->inputNames[srcChan][0] = 0;
}
}
bool InputsPanel::cmInputInsertAllowed() const
{
return ((!model->hasExpos(inputsCount - 1)) && (inputIdx < inputsCount - 1));
}
bool InputsPanel::cmInputMoveDownAllowed() const
{
return (inputIdx >= 0 && (inputIdx < inputsCount - 1));
}
bool InputsPanel::cmInputMoveUpAllowed() const
{
return inputIdx > 0;
}
void InputsPanel::cmInputClear()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Input. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
for (int i = CPN_MAX_EXPOS - 1; i >= 0; i--) {
ExpoData *ed = &model->expoData[i];
if ((int)ed->chn == inputIdx)
model->removeInput(i);
}
model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_CLEAR, inputIdx);
update();
emit modified();
}
void InputsPanel::cmInputDelete()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Input. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
ExpoData *ed = &model->expoData[i];
if ((int)ed->chn == inputIdx)
model->removeInput(i);
else if ((int)ed->chn > inputIdx)
ed->chn--;
}
for (int i = inputIdx; i < inputsCount; i++) {
strncpy(model->inputNames[i], model->inputNames[i + 1], sizeof(model->inputNames[i]) - 1);
}
model->inputNames[inputsCount - 1][0] = 0;
model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_SHIFT, inputIdx, 0, -1);
update();
emit modified();
}
void InputsPanel::cmInputInsert()
{
for (int i = 0; i < CPN_MAX_EXPOS; i++) {
ExpoData *ed = &model->expoData[i];
if ((int)ed->chn >= inputIdx)
ed->chn++;
}
for (int i = inputsCount - 1; i > inputIdx; i--) {
strncpy(model->inputNames[i], model->inputNames[i - 1], sizeof(model->inputNames[i]) - 1);
}
model->inputNames[inputIdx][0] = 0;
model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_SHIFT, inputIdx, 0, 1);
update();
emit modified();
}
void InputsPanel::cmInputMoveDown()
{
cmInputSwapData(inputIdx, inputIdx + 1);
}
void InputsPanel::cmInputMoveUp()
{
cmInputSwapData(inputIdx - 1, inputIdx);
}
void InputsPanel::cmInputSwapData(int idx1, int idx2)
{
if (idx1 >= idx2 || (!model->hasExpos(idx1) && !model->hasExpos(idx2)))
return;
// save expos
int expoidx = -1;
QVector<ExpoData> edtmp;
int i;
for (i = 0; i < CPN_MAX_EXPOS; i++) {
ExpoData *ed = &model->expoData[i];
if ((int)ed->chn == idx1) {
edtmp << model->expoData[i];
if (expoidx < 0)
expoidx = i;
}
else if ((int)ed->chn > idx1)
break;
}
// move expos up
const int offset = i - expoidx;
int expocnt;
for (int j = i; j < CPN_MAX_EXPOS; j++) {
ExpoData *ed = &model->expoData[j];
if ((int)ed->chn == idx2) {
ExpoData *dest = &model->expoData[j - offset];
memcpy(dest, &model->expoData[j], sizeof(ExpoData));
dest->chn = idx1;
expocnt++;
}
else if ((int)ed->chn > idx2)
break;
}
// copy back saved expos
int cnt = 0;
foreach (ExpoData ed, edtmp) {
ExpoData *dest = &model->expoData[expoidx + expocnt + cnt];
memcpy(dest, &ed, sizeof(ExpoData));
dest->chn = idx2;
cnt++;
}
// swap names
QByteArray *tname = new QByteArray(model->inputNames[idx2]);
strncpy(model->inputNames[idx2], model->inputNames[idx1], sizeof(model->inputNames[idx2]) - 1);
strncpy(model->inputNames[idx1], tname->data(), sizeof(model->inputNames[idx1]) - 1);
model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_SWAP, idx1, idx2);
update();
emit modified();
}
bool InputsPanel::isInputIndex(const int index)
{
if (index < 0)
return true;
else
return false;
}
bool InputsPanel::isExpoIndex(const int index)
{
return !isInputIndex(index);
}
int InputsPanel::getIndexFromSelected()
{
return ExposlistWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0);
}
int InputsPanel::getInputIndexFromSelected()
{
const int selidx = getIndexFromSelected();
int idx;
if (isInputIndex(selidx))
idx = -selidx - 1;
else {
const ExpoData *ed = &model->expoData[selidx];
idx = ed->chn;
}
return idx;
}

View file

@ -25,6 +25,8 @@
#include "mixerslistwidget.h"
#include "modelprinter.h"
constexpr char MIMETYPE_EXPO[] = "application/x-companion-expo";
class InputsPanel : public ModelPanel
{
Q_OBJECT
@ -52,12 +54,19 @@ class InputsPanel : public ModelPanel
void expoOpen(QListWidgetItem *item = NULL);
void expoAdd();
void maybeCopyInputName(int srcChan, int destChan);
void cmInputClear();
void cmInputDelete();
void cmInputInsert();
void cmInputMoveDown();
void cmInputMoveUp();
private:
bool expoInserted;
MixersListWidget *ExposlistWidget;
int inputsCount;
ModelPrinter modelPrinter;
int selectedIdx;
int inputIdx;
int getExpoIndex(unsigned int dch);
bool gm_insertExpo(int idx);
@ -70,7 +79,14 @@ class InputsPanel : public ModelPanel
void pasteExpoMimeData(const QMimeData * mimeData, int destIdx);
void AddInputLine(int dest);
QString getInputText(int dest, bool newChan);
bool cmInputInsertAllowed() const;
bool cmInputMoveDownAllowed() const;
bool cmInputMoveUpAllowed() const;
void cmInputSwapData(int idx1, int idx2);
bool isInputIndex(const int index);
bool isExpoIndex(const int index);
int getIndexFromSelected();
int getInputIndexFromSelected();
};
#endif // _INPUTS_H_

View file

@ -26,7 +26,7 @@
LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
ModelPanel(parent, model, generalSettings, firmware),
selectedSwitch(0)
selectedIndex(0)
{
Stopwatch s1("LogicalSwitchesPanel");
@ -35,19 +35,22 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model,
const int srcGroups = firmware->getCapability(GvarsInCS) ? 0 : (RawSource::AllSourceGroups & ~RawSource::GVarsGroup);
rawSourceItemModel = new RawSourceFilterItemModel(&generalSettings, &model, srcGroups, this);
lsCapability = firmware->getCapability(LogicalSwitches);
lsCapabilityExt = firmware->getCapability(LogicalSwitchesExt);
QStringList headerLabels;
headerLabels << "#" << tr("Function") << tr("V1") << tr("V2") << tr("AND Switch");
if (firmware->getCapability(LogicalSwitchesExt)) {
if (lsCapabilityExt) {
headerLabels << tr("Duration") << tr("Delay");
}
TableLayout * tableLayout = new TableLayout(this, firmware->getCapability(LogicalSwitches), headerLabels);
TableLayout * tableLayout = new TableLayout(this, lsCapability, headerLabels);
s1.report("header");
const int channelsMax = model.getChannelsMax(true);
lock = true;
for (int i=0; i<firmware->getCapability(LogicalSwitches); i++) {
for (int i=0; i<lsCapability; i++) {
// The label
QLabel * label = new QLabel(this);
label->setProperty("index", i);
@ -56,96 +59,96 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model,
label->setToolTip(tr("Popup menu available"));
label->setMouseTracking(true);
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum);
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(csw_customContextMenuRequested(QPoint)));
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onCustomContextMenuRequested(QPoint)));
tableLayout->addWidget(i, 0, label);
// The function
csw[i] = new QComboBox(this);
csw[i]->setProperty("index", i);
populateCSWCB(csw[i]);
connect(csw[i], SIGNAL(currentIndexChanged(int)), this, SLOT(functionChanged()));
tableLayout->addWidget(i, 1, csw[i]);
cbFunction[i] = new QComboBox(this);
cbFunction[i]->setProperty("index", i);
populateFunctionCB(cbFunction[i]);
connect(cbFunction[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onFunctionChanged()));
tableLayout->addWidget(i, 1, cbFunction[i]);
// V1
QHBoxLayout *v1Layout = new QHBoxLayout();
cswitchSource1[i] = new QComboBox(this);
cswitchSource1[i]->setProperty("index",i);
connect(cswitchSource1[i], SIGNAL(currentIndexChanged(int)), this, SLOT(v1Edited(int)));
v1Layout->addWidget(cswitchSource1[i]);
cswitchSource1[i]->setVisible(false);
cswitchValue[i] = new QDoubleSpinBox(this);
cswitchValue[i]->setMaximum(channelsMax);
cswitchValue[i]->setMinimum(-channelsMax);
cswitchValue[i]->setAccelerated(true);
cswitchValue[i]->setDecimals(0);
cswitchValue[i]->setProperty("index", i);
connect(cswitchValue[i], SIGNAL(editingFinished()), this, SLOT(offsetEdited()));
v1Layout->addWidget(cswitchValue[i]);
cswitchValue[i]->setVisible(false);
cbSource1[i] = new QComboBox(this);
cbSource1[i]->setProperty("index",i);
connect(cbSource1[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onV1Changed(int)));
v1Layout->addWidget(cbSource1[i]);
cbSource1[i]->setVisible(false);
dsbValue[i] = new QDoubleSpinBox(this);
dsbValue[i]->setMaximum(channelsMax);
dsbValue[i]->setMinimum(-channelsMax);
dsbValue[i]->setAccelerated(true);
dsbValue[i]->setDecimals(0);
dsbValue[i]->setProperty("index", i);
connect(dsbValue[i], SIGNAL(editingFinished()), this, SLOT(onOffsetChanged()));
v1Layout->addWidget(dsbValue[i]);
dsbValue[i]->setVisible(false);
tableLayout->addLayout(i, 2, v1Layout);
// V2
QHBoxLayout *v2Layout = new QHBoxLayout();
cswitchSource2[i] = new QComboBox(this);
cswitchSource2[i]->setProperty("index", i);
connect(cswitchSource2[i], SIGNAL(currentIndexChanged(int)), this, SLOT(v2Edited(int)));
v2Layout->addWidget(cswitchSource2[i]);
cswitchSource2[i]->setVisible(false);
cswitchOffset[i] = new QDoubleSpinBox(this);
cswitchOffset[i]->setProperty("index",i);
cswitchOffset[i]->setMaximum(channelsMax);
cswitchOffset[i]->setMinimum(-channelsMax);
cswitchOffset[i]->setAccelerated(true);
cswitchOffset[i]->setDecimals(0);
connect(cswitchOffset[i], SIGNAL(editingFinished()), this, SLOT(offsetEdited()));
cswitchOffset[i]->setVisible(false);
v2Layout->addWidget(cswitchOffset[i]);
cswitchOffset2[i] = new QDoubleSpinBox(this);
cswitchOffset2[i]->setProperty("index",i);
cswitchOffset2[i]->setMaximum(channelsMax);
cswitchOffset2[i]->setMinimum(-channelsMax);
cswitchOffset2[i]->setAccelerated(true);
cswitchOffset2[i]->setDecimals(0);
cswitchOffset2[i]->setSpecialValueText(" " + tr("(instant)"));
connect(cswitchOffset2[i], SIGNAL(editingFinished()), this, SLOT(offsetEdited()));
cswitchOffset2[i]->setVisible(false);
v2Layout->addWidget(cswitchOffset2[i]);
cswitchTOffset[i] = new TimerEdit(this);
cswitchTOffset[i]->setProperty("index",i);
connect(cswitchTOffset[i],SIGNAL(editingFinished()),this,SLOT(offsetEdited()));
v2Layout->addWidget(cswitchTOffset[i]);
cswitchTOffset[i]->setVisible(false);
cbSource2[i] = new QComboBox(this);
cbSource2[i]->setProperty("index", i);
connect(cbSource2[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onV2Changed(int)));
v2Layout->addWidget(cbSource2[i]);
cbSource2[i]->setVisible(false);
dsbOffset[i] = new QDoubleSpinBox(this);
dsbOffset[i]->setProperty("index",i);
dsbOffset[i]->setMaximum(channelsMax);
dsbOffset[i]->setMinimum(-channelsMax);
dsbOffset[i]->setAccelerated(true);
dsbOffset[i]->setDecimals(0);
connect(dsbOffset[i], SIGNAL(editingFinished()), this, SLOT(onOffsetChanged()));
dsbOffset[i]->setVisible(false);
v2Layout->addWidget(dsbOffset[i]);
dsbOffset2[i] = new QDoubleSpinBox(this);
dsbOffset2[i]->setProperty("index",i);
dsbOffset2[i]->setMaximum(channelsMax);
dsbOffset2[i]->setMinimum(-channelsMax);
dsbOffset2[i]->setAccelerated(true);
dsbOffset2[i]->setDecimals(0);
dsbOffset2[i]->setSpecialValueText(" " + tr("(instant)"));
connect(dsbOffset2[i], SIGNAL(editingFinished()), this, SLOT(onOffsetChanged()));
dsbOffset2[i]->setVisible(false);
v2Layout->addWidget(dsbOffset2[i]);
teOffset[i] = new TimerEdit(this);
teOffset[i]->setProperty("index",i);
connect(teOffset[i],SIGNAL(editingFinished()),this,SLOT(onOffsetChanged()));
v2Layout->addWidget(teOffset[i]);
teOffset[i]->setVisible(false);
tableLayout->addLayout(i, 3, v2Layout);
// AND
cswitchAnd[i] = new QComboBox(this);
cswitchAnd[i]->setProperty("index", i);
populateAndSwitchCB(cswitchAnd[i]);
connect(cswitchAnd[i], SIGNAL(currentIndexChanged(int)), this, SLOT(andEdited(int)));
tableLayout->addWidget(i, 4, cswitchAnd[i]);
cbAndSwitch[i] = new QComboBox(this);
cbAndSwitch[i]->setProperty("index", i);
populateAndSwitchCB(cbAndSwitch[i]);
connect(cbAndSwitch[i], SIGNAL(currentIndexChanged(int)), this, SLOT(onAndSwitchChanged(int)));
tableLayout->addWidget(i, 4, cbAndSwitch[i]);
if (firmware->getCapability(LogicalSwitchesExt)) {
if (lsCapabilityExt) {
// Duration
cswitchDuration[i] = new QDoubleSpinBox(this);
cswitchDuration[i]->setProperty("index", i);
cswitchDuration[i]->setSingleStep(0.1);
cswitchDuration[i]->setMaximum(25);
cswitchDuration[i]->setMinimum(0);
cswitchDuration[i]->setAccelerated(true);
cswitchDuration[i]->setDecimals(1);
connect(cswitchDuration[i], SIGNAL(valueChanged(double)), this, SLOT(durationEdited(double)));
tableLayout->addWidget(i, 5, cswitchDuration[i]);
dsbDuration[i] = new QDoubleSpinBox(this);
dsbDuration[i]->setProperty("index", i);
dsbDuration[i]->setSingleStep(0.1);
dsbDuration[i]->setMaximum(25);
dsbDuration[i]->setMinimum(0);
dsbDuration[i]->setAccelerated(true);
dsbDuration[i]->setDecimals(1);
connect(dsbDuration[i], SIGNAL(valueChanged(double)), this, SLOT(onDurationChanged(double)));
tableLayout->addWidget(i, 5, dsbDuration[i]);
// Delay
cswitchDelay[i] = new QDoubleSpinBox(this);
cswitchDelay[i]->setProperty("index", i);
cswitchDelay[i]->setSingleStep(0.1);
cswitchDelay[i]->setMaximum(25);
cswitchDelay[i]->setMinimum(0);
cswitchDelay[i]->setAccelerated(true);
cswitchDelay[i]->setDecimals(1);
connect(cswitchDelay[i], SIGNAL(valueChanged(double)), this, SLOT(delayEdited(double)));
tableLayout->addWidget(i, 6, cswitchDelay[i]);
dsbDelay[i] = new QDoubleSpinBox(this);
dsbDelay[i]->setProperty("index", i);
dsbDelay[i]->setSingleStep(0.1);
dsbDelay[i]->setMaximum(25);
dsbDelay[i]->setMinimum(0);
dsbDelay[i]->setAccelerated(true);
dsbDelay[i]->setDecimals(1);
connect(dsbDelay[i], SIGNAL(valueChanged(double)), this, SLOT(onDelayChanged(double)));
tableLayout->addWidget(i, 6, dsbDelay[i]);
}
}
@ -155,7 +158,7 @@ LogicalSwitchesPanel::LogicalSwitchesPanel(QWidget * parent, ModelData & model,
lock = false;
update();
tableLayout->resizeColumnsToContents();
tableLayout->pushRowsUp(firmware->getCapability(LogicalSwitches)+1);
tableLayout->pushRowsUp(lsCapability+1);
s1.report("end");
}
@ -172,10 +175,10 @@ void LogicalSwitchesPanel::updateDataModels()
lock = oldLock;
}
void LogicalSwitchesPanel::functionChanged()
void LogicalSwitchesPanel::onFunctionChanged()
{
int i = sender()->property("index").toInt();
unsigned newFunc = csw[i]->currentData().toUInt();
unsigned newFunc = cbFunction[i]->currentData().toUInt();
if (model->logicalSw[i].func == newFunc)
return;
@ -204,13 +207,13 @@ void LogicalSwitchesPanel::functionChanged()
emit modified();
}
void LogicalSwitchesPanel::v1Edited(int value)
void LogicalSwitchesPanel::onV1Changed(int value)
{
if (!lock) {
int i = sender()->property("index").toInt();
model->logicalSw[i].val1 = cswitchSource1[i]->itemData(value).toInt();
model->logicalSw[i].val1 = cbSource1[i]->itemData(value).toInt();
if (model->logicalSw[i].getFunctionFamily() == LS_FAMILY_VOFS) {
if (!offsetEditedAt(i))
if (!offsetChangedAt(i))
updateLine(i);
}
else {
@ -219,25 +222,25 @@ void LogicalSwitchesPanel::v1Edited(int value)
}
}
void LogicalSwitchesPanel::v2Edited(int value)
void LogicalSwitchesPanel::onV2Changed(int value)
{
if (!lock) {
int i = sender()->property("index").toInt();
model->logicalSw[i].val2 = cswitchSource2[i]->itemData(value).toInt();
model->logicalSw[i].val2 = cbSource2[i]->itemData(value).toInt();
emit modified();
}
}
void LogicalSwitchesPanel::andEdited(int value)
void LogicalSwitchesPanel::onAndSwitchChanged(int value)
{
if (!lock) {
int index = sender()->property("index").toInt();
model->logicalSw[index].andsw = cswitchAnd[index]->itemData(value).toInt();
model->logicalSw[index].andsw = cbAndSwitch[index]->itemData(value).toInt();
emit modified();
}
}
void LogicalSwitchesPanel::durationEdited(double duration)
void LogicalSwitchesPanel::onDurationChanged(double duration)
{
if (!lock) {
int index = sender()->property("index").toInt();
@ -246,7 +249,7 @@ void LogicalSwitchesPanel::durationEdited(double duration)
}
}
void LogicalSwitchesPanel::delayEdited(double delay)
void LogicalSwitchesPanel::onDelayChanged(double delay)
{
if (!lock) {
int index = sender()->property("index").toInt();
@ -255,12 +258,12 @@ void LogicalSwitchesPanel::delayEdited(double delay)
}
}
void LogicalSwitchesPanel::offsetEdited()
void LogicalSwitchesPanel::onOffsetChanged()
{
offsetEditedAt(sender()->property("index").toInt());
offsetChangedAt(sender()->property("index").toInt());
}
bool LogicalSwitchesPanel::offsetEditedAt(int index)
bool LogicalSwitchesPanel::offsetChangedAt(int index)
{
if (lock)
return false;
@ -275,7 +278,7 @@ bool LogicalSwitchesPanel::offsetEditedAt(int index)
{
RawSource source = RawSource(model->logicalSw[index].val1);
RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[index].getRangeFlags());
double currVal = source.isTimeBased() ? cswitchTOffset[index]->timeInSeconds() : cswitchOffset[index]->value();
double currVal = source.isTimeBased() ? teOffset[index]->timeInSeconds() : dsbOffset[index]->value();
value = round((currVal - range.offset) / range.step);
mod = (mod || value != model->logicalSw[index].val2);
model->logicalSw[index].val2 = value;
@ -283,22 +286,22 @@ bool LogicalSwitchesPanel::offsetEditedAt(int index)
}
case LS_FAMILY_TIMER:
value = TimToVal(cswitchValue[index]->value());
value = TimToVal(dsbValue[index]->value());
mod = (mod || value != model->logicalSw[index].val1);
model->logicalSw[index].val1 = value;
value = TimToVal(cswitchOffset[index]->value());
value = TimToVal(dsbOffset[index]->value());
mod = (mod || value != model->logicalSw[index].val2);
model->logicalSw[index].val2 = value;
break;
case LS_FAMILY_EDGE:
if (sender() == cswitchOffset[index]) {
value = TimToVal(cswitchOffset[index]->value());
if (sender() == dsbOffset[index]) {
value = TimToVal(dsbOffset[index]->value());
mod = (mod || value != model->logicalSw[index].val2);
model->logicalSw[index].val2 = value;
}
else {
value = TimToVal(cswitchOffset2[index]->value()) - model->logicalSw[index].val2;
value = TimToVal(dsbOffset2[index]->value()) - model->logicalSw[index].val2;
mod = (mod || value != model->logicalSw[index].val3);
model->logicalSw[index].val3 = value;
}
@ -347,8 +350,8 @@ void LogicalSwitchesPanel::updateLine(int i)
lock = true;
unsigned int mask;
csw[i]->setCurrentIndex(csw[i]->findData(model->logicalSw[i].func));
cswitchAnd[i]->setCurrentIndex(cswitchAnd[i]->findData(RawSwitch(model->logicalSw[i].andsw).toValue()));
cbFunction[i]->setCurrentIndex(cbFunction[i]->findData(model->logicalSw[i].func));
cbAndSwitch[i]->setCurrentIndex(cbAndSwitch[i]->findData(RawSwitch(model->logicalSw[i].andsw).toValue()));
if (!model->logicalSw[i].func) {
mask = 0;
@ -364,27 +367,27 @@ void LogicalSwitchesPanel::updateLine(int i)
RawSource source = RawSource(model->logicalSw[i].val1);
RawSourceRange range = source.getRange(model, generalSettings, model->logicalSw[i].getRangeFlags());
double value = range.step * model->logicalSw[i].val2 + range.offset; /* TODO+source.getRawOffset(model)*/
cswitchSource1[i]->setModel(rawSourceItemModel);
cswitchSource1[i]->setCurrentIndex(cswitchSource1[i]->findData(source.toValue()));
cbSource1[i]->setModel(rawSourceItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(source.toValue()));
if (source.isTimeBased()) {
mask |= VALUE_TO_VISIBLE;
cswitchTOffset[i]->setTimeRange(range.min, range.max);
cswitchTOffset[i]->setSingleStep(range.step);
cswitchTOffset[i]->setPageStep(range.step * 60);
cswitchTOffset[i]->setShowSeconds(range.step != 60);
cswitchTOffset[i]->setTime((int)value);
teOffset[i]->setTimeRange(range.min, range.max);
teOffset[i]->setSingleStep(range.step);
teOffset[i]->setPageStep(range.step * 60);
teOffset[i]->setShowSeconds(range.step != 60);
teOffset[i]->setTime((int)value);
}
else {
mask |= VALUE2_VISIBLE;
if (range.unit.isEmpty())
cswitchOffset[i]->setSuffix("");
dsbOffset[i]->setSuffix("");
else
cswitchOffset[i]->setSuffix(" " + range.unit);
cswitchOffset[i]->setDecimals(range.decimals);
cswitchOffset[i]->setMinimum(range.min);
cswitchOffset[i]->setMaximum(range.max);
cswitchOffset[i]->setSingleStep(range.step);
cswitchOffset[i]->setValue(value);
dsbOffset[i]->setSuffix(" " + range.unit);
dsbOffset[i]->setDecimals(range.decimals);
dsbOffset[i]->setMinimum(range.min);
dsbOffset[i]->setMaximum(range.max);
dsbOffset[i]->setSingleStep(range.step);
dsbOffset[i]->setValue(value);
}
break;
@ -393,58 +396,58 @@ void LogicalSwitchesPanel::updateLine(int i)
case LS_FAMILY_STICKY: // no break
case LS_FAMILY_VBOOL:
mask |= SOURCE1_VISIBLE | SOURCE2_VISIBLE;
cswitchSource1[i]->setModel(rawSwitchItemModel);
cswitchSource1[i]->setCurrentIndex(cswitchSource1[i]->findData(model->logicalSw[i].val1));
cswitchSource2[i]->setModel(rawSwitchItemModel);
cswitchSource2[i]->setCurrentIndex(cswitchSource2[i]->findData(model->logicalSw[i].val2));
cbSource1[i]->setModel(rawSwitchItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(model->logicalSw[i].val1));
cbSource2[i]->setModel(rawSwitchItemModel);
cbSource2[i]->setCurrentIndex(cbSource2[i]->findData(model->logicalSw[i].val2));
break;
case LS_FAMILY_EDGE:
mask |= SOURCE1_VISIBLE | VALUE2_VISIBLE | VALUE3_VISIBLE;
mask &= ~DELAY_ENABLED;
cswitchSource1[i]->setModel(rawSwitchItemModel);
cswitchSource1[i]->setCurrentIndex(cswitchSource1[i]->findData(model->logicalSw[i].val1));
updateTimerParam(cswitchOffset[i], model->logicalSw[i].val2, 0.0);
updateTimerParam(cswitchOffset2[i], model->logicalSw[i].val2+model->logicalSw[i].val3, ValToTim(TimToVal(cswitchOffset[i]->value())-1));
cswitchOffset2[i]->setSuffix((model->logicalSw[i].val3) ? "" : tr(" (infinite)"));
cbSource1[i]->setModel(rawSwitchItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(model->logicalSw[i].val1));
updateTimerParam(dsbOffset[i], model->logicalSw[i].val2, 0.0);
updateTimerParam(dsbOffset2[i], model->logicalSw[i].val2+model->logicalSw[i].val3, ValToTim(TimToVal(dsbOffset[i]->value())-1));
dsbOffset2[i]->setSuffix((model->logicalSw[i].val3) ? "" : tr(" (infinite)"));
break;
case LS_FAMILY_VCOMP:
mask |= SOURCE1_VISIBLE | SOURCE2_VISIBLE;
cswitchSource1[i]->setModel(rawSourceItemModel);
cswitchSource1[i]->setCurrentIndex(cswitchSource1[i]->findData(model->logicalSw[i].val1));
cswitchSource2[i]->setModel(rawSourceItemModel);
cswitchSource2[i]->setCurrentIndex(cswitchSource2[i]->findData(model->logicalSw[i].val2));
cbSource1[i]->setModel(rawSourceItemModel);
cbSource1[i]->setCurrentIndex(cbSource1[i]->findData(model->logicalSw[i].val1));
cbSource2[i]->setModel(rawSourceItemModel);
cbSource2[i]->setCurrentIndex(cbSource2[i]->findData(model->logicalSw[i].val2));
break;
case LS_FAMILY_TIMER:
mask |= VALUE1_VISIBLE | VALUE2_VISIBLE;
updateTimerParam(cswitchValue[i], model->logicalSw[i].val1, 0.1);
updateTimerParam(cswitchOffset[i], model->logicalSw[i].val2, 0.1);
updateTimerParam(dsbValue[i], model->logicalSw[i].val1, 0.1);
updateTimerParam(dsbOffset[i], model->logicalSw[i].val2, 0.1);
break;
}
}
cswitchSource1[i]->setVisible(mask & SOURCE1_VISIBLE);
cswitchSource2[i]->setVisible(mask & SOURCE2_VISIBLE);
cswitchValue[i]->setVisible(mask & VALUE1_VISIBLE);
cswitchOffset[i]->setVisible(mask & VALUE2_VISIBLE);
cswitchOffset2[i]->setVisible(mask & VALUE3_VISIBLE);
cswitchTOffset[i]->setVisible(mask & VALUE_TO_VISIBLE);
cswitchAnd[i]->setVisible(mask & LINE_ENABLED);
if (firmware->getCapability(LogicalSwitchesExt)) {
cswitchDuration[i]->setVisible(mask & DURATION_ENABLED);
cswitchDelay[i]->setVisible(mask & DELAY_ENABLED);
cbSource1[i]->setVisible(mask & SOURCE1_VISIBLE);
cbSource2[i]->setVisible(mask & SOURCE2_VISIBLE);
dsbValue[i]->setVisible(mask & VALUE1_VISIBLE);
dsbOffset[i]->setVisible(mask & VALUE2_VISIBLE);
dsbOffset2[i]->setVisible(mask & VALUE3_VISIBLE);
teOffset[i]->setVisible(mask & VALUE_TO_VISIBLE);
cbAndSwitch[i]->setVisible(mask & LINE_ENABLED);
if (lsCapabilityExt) {
dsbDuration[i]->setVisible(mask & DURATION_ENABLED);
dsbDelay[i]->setVisible(mask & DELAY_ENABLED);
if (mask & DURATION_ENABLED)
cswitchDuration[i]->setValue(model->logicalSw[i].duration/10.0);
dsbDuration[i]->setValue(model->logicalSw[i].duration/10.0);
if (mask & DELAY_ENABLED)
cswitchDelay[i]->setValue(model->logicalSw[i].delay/10.0);
dsbDelay[i]->setValue(model->logicalSw[i].delay/10.0);
}
lock = false;
}
void LogicalSwitchesPanel::populateCSWCB(QComboBox *b)
void LogicalSwitchesPanel::populateFunctionCB(QComboBox *b)
{
int order[] = {
LS_FN_OFF,
@ -514,63 +517,150 @@ void LogicalSwitchesPanel::populateAndSwitchCB(QComboBox *b)
void LogicalSwitchesPanel::update()
{
updateDataModels();
for (int i=0; i<firmware->getCapability(LogicalSwitches); i++) {
for (int i=0; i<lsCapability; i++) {
updateLine(i);
}
}
void LogicalSwitchesPanel::cswPaste()
void LogicalSwitchesPanel::cmPaste()
{
QByteArray data;
if (hasClipboardData(&data)) {
memcpy(&model->logicalSw[selectedIndex], data.constData(), sizeof(LogicalSwitchData));
updateDataModels();
updateLine(selectedIndex);
emit modified();
}
}
void LogicalSwitchesPanel::cmDelete()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Logical Switch. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
memmove(&model->logicalSw[selectedIndex], &model->logicalSw[selectedIndex + 1], (CPN_MAX_LOGICAL_SWITCHES - (selectedIndex + 1)) * sizeof(LogicalSwitchData));
model->logicalSw[lsCapability - 1].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, -1);
update();
emit modified();
}
void LogicalSwitchesPanel::cmCopy()
{
QByteArray data;
data.append((char*)&model->logicalSw[selectedIndex], sizeof(LogicalSwitchData));
QMimeData *mimeData = new QMimeData;
mimeData->setData(MIMETYPE_LOGICAL_SWITCH, data);
QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard);
}
void LogicalSwitchesPanel::cmCut()
{
cmCopy();
cmClear();
}
// TODO make something generic here!
void LogicalSwitchesPanel::onCustomContextMenuRequested(QPoint pos)
{
QLabel *label = (QLabel *)sender();
selectedIndex = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos);
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(cmCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(cmCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear()));
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 LogicalSwitchesPanel::hasClipboardData(QByteArray * data) const
{
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
if (mimeData->hasFormat("application/x-companion-csw")) {
QByteArray cswData = mimeData->data("application/x-companion-csw");
LogicalSwitchData *csw = &model->logicalSw[selectedSwitch];
memcpy(csw, cswData.constData(), sizeof(LogicalSwitchData));
if (mimeData->hasFormat(MIMETYPE_LOGICAL_SWITCH)) {
if (data)
data->append(mimeData->data(MIMETYPE_LOGICAL_SWITCH));
return true;
}
return false;
}
bool LogicalSwitchesPanel::insertAllowed() const
{
return ((selectedIndex < lsCapability - 1) && (model->logicalSw[lsCapability - 1].isEmpty()));
}
bool LogicalSwitchesPanel::moveDownAllowed() const
{
return selectedIndex < lsCapability - 1;
}
bool LogicalSwitchesPanel::moveUpAllowed() const
{
return selectedIndex > 0;
}
void LogicalSwitchesPanel::cmMoveUp()
{
swapData(selectedIndex, selectedIndex - 1);
}
void LogicalSwitchesPanel::cmMoveDown()
{
swapData(selectedIndex, selectedIndex + 1);
}
void LogicalSwitchesPanel::cmClear()
{
model->logicalSw[selectedIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_CLEAR, selectedIndex);
update();
emit modified();
}
void LogicalSwitchesPanel::cmClearAll()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Logical Switches. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
for (int i=0; i<lsCapability; i++) {
model->logicalSw[i].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_CLEAR, i);
}
update();
emit modified();
}
void LogicalSwitchesPanel::cmInsert()
{
memmove(&model->logicalSw[selectedIndex + 1], &model->logicalSw[selectedIndex], (CPN_MAX_LOGICAL_SWITCHES - (selectedIndex + 1)) * sizeof(LogicalSwitchData));
model->logicalSw[selectedIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_SHIFT, selectedIndex, 0, 1);
update();
emit modified();
}
void LogicalSwitchesPanel::swapData(int idx1, int idx2)
{
if ((idx1 != idx2) && (!model->logicalSw[idx1].isEmpty() || !model->logicalSw[idx2].isEmpty())) {
LogicalSwitchData lstmp = model->logicalSw[idx2];
LogicalSwitchData *lsw1 = &model->logicalSw[idx1];
LogicalSwitchData *lsw2 = &model->logicalSw[idx2];
memcpy(lsw2, lsw1, sizeof(LogicalSwitchData));
memcpy(lsw1, &lstmp, sizeof(LogicalSwitchData));
model->updateAllReferences(ModelData::REF_UPD_TYPE_LOGICAL_SWITCH, ModelData::REF_UPD_ACT_SWAP, idx1, idx2);
update();
emit modified();
}
}
void LogicalSwitchesPanel::cswDelete()
{
model->logicalSw[selectedSwitch].clear();
emit modified();
updateLine(selectedSwitch);
}
void LogicalSwitchesPanel::cswCopy()
{
QByteArray cswData;
cswData.append((char*)&model->logicalSw[selectedSwitch],sizeof(LogicalSwitchData));
QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-companion-csw", cswData);
QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard);
}
void LogicalSwitchesPanel::cswCut()
{
cswCopy();
cswDelete();
}
// TODO make something generic here!
void LogicalSwitchesPanel::csw_customContextMenuRequested(QPoint pos)
{
QLabel *label = (QLabel *)sender();
selectedSwitch = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos);
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
bool hasData = mimeData->hasFormat("application/x-companion-csw");
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("&Copy"),this,SLOT(cswCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("&Cut"),this,SLOT(cswCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("&Paste"),this,SLOT(cswPaste()))->setEnabled(hasData);
contextMenu.addAction(CompanionIcon("clear.png"), tr("&Delete"),this,SLOT(cswDelete()));
contextMenu.exec(globalPos);
}

View file

@ -28,6 +28,8 @@ class RawSwitchFilterItemModel;
class RawSourceFilterItemModel;
class TimerEdit;
constexpr char MIMETYPE_LOGICAL_SWITCH[] = "application/x-companion-logical-switch";
class LogicalSwitchesPanel : public ModelPanel
{
Q_OBJECT
@ -40,40 +42,50 @@ class LogicalSwitchesPanel : public ModelPanel
private slots:
void updateDataModels();
void functionChanged();
void v1Edited(int value);
void v2Edited(int value);
void andEdited(int value);
void durationEdited(double duration);
void delayEdited(double delay);
void offsetEdited();
bool offsetEditedAt(int index);
void onFunctionChanged();
void onV1Changed(int value);
void onV2Changed(int value);
void onAndSwitchChanged(int value);
void onDurationChanged(double duration);
void onDelayChanged(double delay);
void onOffsetChanged();
bool offsetChangedAt(int index);
void updateLine(int index);
void csw_customContextMenuRequested(QPoint pos);
void cswDelete();
void cswCopy();
void cswPaste();
void cswCut();
void onCustomContextMenuRequested(QPoint pos);
void cmDelete();
void cmCopy();
void cmPaste();
void cmCut();
void cmMoveUp();
void cmMoveDown();
void cmInsert();
void cmClear();
void cmClearAll();
private:
QComboBox * csw[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * cswitchValue[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * cswitchOffset[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * cswitchOffset2[CPN_MAX_LOGICAL_SWITCHES];
TimerEdit * cswitchTOffset[CPN_MAX_LOGICAL_SWITCHES];
QComboBox * cswitchAnd[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * cswitchDuration[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * cswitchDelay[CPN_MAX_LOGICAL_SWITCHES];
QComboBox * cswitchSource1[CPN_MAX_LOGICAL_SWITCHES];
QComboBox * cswitchSource2[CPN_MAX_LOGICAL_SWITCHES];
QComboBox * cbFunction[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * dsbValue[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * dsbOffset[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * dsbOffset2[CPN_MAX_LOGICAL_SWITCHES];
TimerEdit * teOffset[CPN_MAX_LOGICAL_SWITCHES];
QComboBox * cbAndSwitch[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * dsbDuration[CPN_MAX_LOGICAL_SWITCHES];
QDoubleSpinBox * dsbDelay[CPN_MAX_LOGICAL_SWITCHES];
QComboBox * cbSource1[CPN_MAX_LOGICAL_SWITCHES];
QComboBox * cbSource2[CPN_MAX_LOGICAL_SWITCHES];
RawSwitchFilterItemModel * rawSwitchItemModel;
RawSourceFilterItemModel * rawSourceItemModel;
int selectedSwitch;
void populateCSWCB(QComboBox *b);
int selectedIndex;
void populateFunctionCB(QComboBox *b);
void populateAndSwitchCB(QComboBox *b);
void updateTimerParam(QDoubleSpinBox *sb, int timer, double minimum=0);
int lsCapability;
int lsCapabilityExt;
void swapData(int idx1, int idx2);
bool hasClipboardData(QByteArray * data = nullptr) const;
bool insertAllowed() const;
bool moveDownAllowed() const;
bool moveUpAllowed() const;
};
#endif // _LOGICALSWITCHES_H_

View file

@ -49,7 +49,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge
}
else {
ui->name->setMaxLength(length);
ui->name->setText(timer.name);
//ui->name->setText(timer.name);
}
// Mode
@ -85,6 +85,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge
QWidget::setTabOrder(ui->countdownBeep, ui->minuteBeep);
QWidget::setTabOrder(ui->minuteBeep, ui->persistent);
update();
lock = false;
}
@ -95,6 +96,9 @@ TimerPanel::~TimerPanel()
void TimerPanel::update()
{
lock = true;
ui->name->setText(timer.name);
int hour = timer.val / 3600;
int min = (timer.val - (hour * 3600)) / 60;
int sec = (timer.val - (hour * 3600)) % 60;
@ -116,7 +120,10 @@ void TimerPanel::update()
ui->persistentValue->setText(QString(" %1(%2:%3:%4)").arg(sign<0 ? "-" :" ").arg(hours, 2, 10, QLatin1Char('0')).arg(minutes, 2, 10, QLatin1Char('0')).arg(seconds, 2, 10, QLatin1Char('0')));
}
ui->countdownBeep->updateValue();
ui->minuteBeep->setChecked(timer.minuteBeep);
ui->persistent->updateValue();
lock = false;
}
QWidget * TimerPanel::getLastFocus()
@ -126,18 +133,18 @@ QWidget * TimerPanel::getLastFocus()
void TimerPanel::on_value_editingFinished()
{
if (!lock) {
unsigned val = ui->value->time().hour()*3600 + ui->value->time().minute()*60 + ui->value->time().second();
if (timer.val != val) {
timer.val = val;
emit modified();
}
}
}
void TimerPanel::onModeChanged(int index)
{
if (lock)
return;
if (!lock) {
bool ok;
const RawSwitch rs(ui->mode->itemData(index).toInt(&ok));
if (ok && timer.mode.toValue() != rs.toValue()) {
@ -145,21 +152,26 @@ void TimerPanel::onModeChanged(int index)
emit modified();
}
}
}
void TimerPanel::on_minuteBeep_toggled(bool checked)
{
if (!lock) {
timer.minuteBeep = checked;
emit modified();
}
}
void TimerPanel::on_name_editingFinished()
{
if (!lock) {
if (QString(timer.name) != ui->name->text()) {
int length = ui->name->maxLength();
strncpy(timer.name, ui->name->text().toLatin1(), length);
emit modified();
}
}
}
/******************************************************************************/
@ -273,27 +285,12 @@ ModulePanel::~ModulePanel()
delete ui;
}
bool ModulePanel::moduleHasFailsafes()
{
return firmware->getCapability(HasFailsafe) && (
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_ACCESS_ISRM ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_ACCST_ISRM_D16 ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_XJT_X16 ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_PXX_R9M ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_ACCESS_R9M ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_ACCESS_R9M_LITE ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_ACCESS_R9M_LITE_PRO ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_XJT_LITE_X16 ||
(PulsesProtocol)module.protocol == PulsesProtocol::PULSES_MULTIMODULE
);
}
void ModulePanel::setupFailsafes()
{
ChannelFailsafeWidgetsGroup grp;
const int start = module.channelsStart;
const int end = start + module.channelsCount;
const bool hasFailsafe = moduleHasFailsafes();
const bool hasFailsafe = module.hasFailsafes(firmware);
lock = true;
@ -382,48 +379,6 @@ void ModulePanel::setupFailsafes()
lock = false;
}
int ModulePanel::getMaxChannelCount()
{
const PulsesProtocol protocol = (PulsesProtocol)module.protocol;
switch (protocol) {
case PULSES_ACCESS_ISRM:
return 24;
case PULSES_PXX_R9M:
case PULSES_ACCESS_R9M:
case PULSES_ACCESS_R9M_LITE:
case PULSES_ACCESS_R9M_LITE_PRO:
case PULSES_ACCST_ISRM_D16:
case PULSES_XJT_LITE_X16:
case PULSES_PXX_XJT_X16:
case PULSES_CROSSFIRE:
case PULSES_SBUS:
case PULSES_PPM:
return 16;
case PULSES_XJT_LITE_LR12:
case PULSES_PXX_XJT_LR12:
return 12;
case PULSES_PXX_DJT:
case PULSES_XJT_LITE_D8:
case PULSES_PXX_XJT_D8:
return 8;
case PULSES_LP45:
case PULSES_DSM2:
case PULSES_DSMX:
return 6;
case PULSES_MULTIMODULE:
if (module.multi.rfProtocol == MODULE_SUBTYPE_MULTI_DSM2)
return 12;
else
return 16;
break;
case PULSES_OFF:
break;
default:
break;
}
return 8;
}
void ModulePanel::update()
{
const PulsesProtocol protocol = (PulsesProtocol)module.protocol;
@ -510,7 +465,7 @@ void ModulePanel::update()
mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT;
}
if (moduleHasFailsafes()) {
if (module.hasFailsafes(firmware)) {
mask |= MASK_FAILSAFES;
}
@ -527,7 +482,7 @@ void ModulePanel::update()
ui->label_channelsCount->setVisible(mask & MASK_CHANNELS_RANGE);
ui->channelsCount->setVisible(mask & MASK_CHANNELS_RANGE);
ui->channelsCount->setEnabled(mask & MASK_CHANNELS_COUNT);
ui->channelsCount->setMaximum(getMaxChannelCount());
ui->channelsCount->setMaximum(module.getMaxChannelCount());
ui->channelsCount->setValue(module.channelsCount);
ui->channelsCount->setSingleStep(firmware->getCapability(HasPPMStart) ? 1 : 2);
@ -695,7 +650,7 @@ void ModulePanel::onProtocolChanged(int index)
{
if (!lock && module.protocol != ui->protocol->itemData(index).toUInt()) {
module.protocol = ui->protocol->itemData(index).toInt();
module.channelsCount = getMaxChannelCount();
module.channelsCount = module.getMaxChannelCount();
update();
emit modified();
}
@ -790,7 +745,7 @@ void ModulePanel::onMultiProtocolChanged(int index)
if (rfProtocol > MODULE_SUBTYPE_MULTI_LAST)
maxSubTypes=8;
module.subType = std::min(module.subType, maxSubTypes -1);
module.channelsCount = getMaxChannelCount();
module.channelsCount = module.getMaxChannelCount();
update();
emit modified();
lock = false;
@ -1064,16 +1019,28 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
RawSwitchFilterItemModel * swModel = new RawSwitchFilterItemModel(&generalSettings, &model, RawSwitch::TimersContext, this);
connect(this, &SetupPanel::updated, swModel, &RawSwitchFilterItemModel::update);
timersCount = firmware->getCapability(Timers);
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
if (i<firmware->getCapability(Timers)) {
if (i < timersCount) {
timers[i] = new TimerPanel(this, model, model.timers[i], generalSettings, firmware, prevFocus, swModel);
ui->gridLayout->addWidget(timers[i], 1+i, 1);
connect(timers[i], &TimerPanel::modified, this, &SetupPanel::modified);
connect(this, &SetupPanel::updated, timers[i], &TimerPanel::update);
connect(this, &SetupPanel::timerUpdated, timers[i], &TimerPanel::update);
prevFocus = timers[i]->getLastFocus();
// TODO more reliable method required
QLabel *label = findChild<QLabel *>(QString("label_timer%1").arg(i + 1));
if (label) { // to stop crashing if not found
label->setProperty("index", i);
label->setContextMenuPolicy(Qt::CustomContextMenu);
label->setToolTip(tr("Popup menu available"));
label->setMouseTracking(true);
connect(label, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(onTimerCustomContextMenuRequested(QPoint)));
}
}
else {
foreach(QLabel *label, findChildren<QLabel *>(QRegularExpression(QString("label_timer%1").arg(i+1)))) {
foreach(QLabel *label, findChildren<QLabel *>(QRegularExpression(QString("label_timer%1").arg(i + 1 )))) { //TODO more reliable method required
label->hide();
}
}
@ -1082,7 +1049,7 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
if (firmware->getCapability(HasTopLcd)) {
ui->toplcdTimer->setField(model.toplcdTimer, this);
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
if (i<firmware->getCapability(Timers)) {
if (i<timersCount) {
ui->toplcdTimer->addItem(tr("Timer %1").arg(i + 1), i);
}
}
@ -1528,3 +1495,155 @@ void SetupPanel::on_editText_clicked()
}
}
void SetupPanel::onTimerCustomContextMenuRequested(QPoint pos)
{
QLabel *label = (QLabel *)sender();
selectedTimerIndex = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos);
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"), this, SLOT(cmTimerCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"), this, SLOT(cmTimerCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"), this, SLOT(cmTimerPaste()))->setEnabled(hasTimerClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"), this, SLOT(cmTimerClear()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"), this, SLOT(cmTimerInsert()))->setEnabled(insertTimerAllowed());
contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"), this, SLOT(cmTimerDelete()));
contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"), this, SLOT(cmTimerMoveUp()))->setEnabled(moveTimerUpAllowed());
contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"), this, SLOT(cmTimerMoveDown()))->setEnabled(moveTimerDownAllowed());
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"), this, SLOT(cmTimerClearAll()));
contextMenu.exec(globalPos);
}
bool SetupPanel::hasTimerClipboardData(QByteArray * data) const
{
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
if (mimeData->hasFormat(MIMETYPE_TIMER)) {
if (data)
data->append(mimeData->data(MIMETYPE_TIMER));
return true;
}
return false;
}
bool SetupPanel::insertTimerAllowed() const
{
return ((selectedTimerIndex < timersCount - 1) && (model->timers[timersCount - 1].isEmpty()));
}
bool SetupPanel::moveTimerDownAllowed() const
{
return selectedTimerIndex < timersCount - 1;
}
bool SetupPanel::moveTimerUpAllowed() const
{
return selectedTimerIndex > 0;
}
void SetupPanel::cmTimerClear()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Timer. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
model->timers[selectedTimerIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_TIMER, ModelData::REF_UPD_ACT_CLEAR, selectedTimerIndex);
emit timerUpdated();
emit modified();
}
void SetupPanel::cmTimerClearAll()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Timers. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
for (int i = 0; i < timersCount; i++) {
model->timers[i].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_TIMER, ModelData::REF_UPD_ACT_CLEAR, i);
}
emit timerUpdated();
emit modified();
}
void SetupPanel::cmTimerCopy()
{
QByteArray data;
data.append((char*)&model->timers[selectedTimerIndex], sizeof(TimerData));
QMimeData *mimeData = new QMimeData;
mimeData->setData(MIMETYPE_TIMER, data);
QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard);
}
void SetupPanel::cmTimerCut()
{
cmTimerCopy();
cmTimerClear();
}
void SetupPanel::cmTimerDelete()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Timer. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
int maxidx = timersCount - 1;
for (int i = selectedTimerIndex; i < maxidx; i++) {
if (!model->timers[i].isEmpty() || !model->timers[i + 1].isEmpty()) {
memcpy(&model->timers[i], &model->timers[i+1], sizeof(TimerData));
}
}
model->timers[maxidx].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_TIMER, ModelData::REF_UPD_ACT_SHIFT, selectedTimerIndex, 0, -1);
emit timerUpdated();
emit modified();
}
void SetupPanel::cmTimerInsert()
{
for (int i = (timersCount - 1); i > selectedTimerIndex; i--) {
if (!model->timers[i].isEmpty() || !model->timers[i-1].isEmpty()) {
memcpy(&model->timers[i], &model->timers[i-1], sizeof(TimerData));
}
}
model->timers[selectedTimerIndex].clear();
model->updateAllReferences(ModelData::REF_UPD_TYPE_TIMER, ModelData::REF_UPD_ACT_SHIFT, selectedTimerIndex, 0, 1);
emit timerUpdated();
emit modified();
}
void SetupPanel::cmTimerMoveDown()
{
swapTimerData(selectedTimerIndex, selectedTimerIndex + 1);
}
void SetupPanel::cmTimerMoveUp()
{
swapTimerData(selectedTimerIndex, selectedTimerIndex - 1);
}
void SetupPanel::cmTimerPaste()
{
QByteArray data;
if (hasTimerClipboardData(&data)) {
TimerData *td = &model->timers[selectedTimerIndex];
memcpy(td, data.constData(), sizeof(TimerData));
emit timerUpdated();
emit modified();
}
}
void SetupPanel::swapTimerData(int idx1, int idx2)
{
if ((idx1 != idx2) && (!model->timers[idx1].isEmpty() || !model->timers[idx2].isEmpty())) {
TimerData tdtmp = model->timers[idx2];
TimerData *td1 = &model->timers[idx1];
TimerData *td2 = &model->timers[idx2];
memcpy(td2, td1, sizeof(TimerData));
memcpy(td1, &tdtmp, sizeof(TimerData));
model->updateAllReferences(ModelData::REF_UPD_TYPE_TIMER, ModelData::REF_UPD_ACT_SWAP, idx1, idx2);
emit timerUpdated();
emit modified();
}
}

View file

@ -24,6 +24,8 @@
#include "modeledit.h"
#include "eeprominterface.h"
constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer";
class RawSwitchFilterItemModel;
namespace Ui {
@ -62,7 +64,6 @@ class ModulePanel : public ModelPanel
ModulePanel(QWidget *parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx);
virtual ~ModulePanel();
virtual void update();
bool moduleHasFailsafes();
public slots:
void onExtendedLimitsToggled();
@ -71,7 +72,6 @@ class ModulePanel : public ModelPanel
void channelsRangeChanged();
private slots:
int getMaxChannelCount();
void setupFailsafes();
void on_trainerMode_currentIndexChanged(int index);
void onProtocolChanged(int index);
@ -129,6 +129,7 @@ class SetupPanel : public ModelPanel
signals:
void extendedLimitsToggled();
void updated();
void timerUpdated();
private slots:
void on_name_editingFinished();
@ -148,6 +149,16 @@ class SetupPanel : public ModelPanel
void potWarningToggled(bool checked);
void on_potWarningMode_currentIndexChanged(int index);
void on_editText_clicked();
void onTimerCustomContextMenuRequested(QPoint pos);
void cmTimerClear();
void cmTimerClearAll();
void cmTimerCopy();
void cmTimerCut();
void cmTimerDelete();
void cmTimerInsert();
void cmTimerPaste();
void cmTimerMoveDown();
void cmTimerMoveUp();
private:
Ui::Setup *ui;
@ -161,6 +172,13 @@ class SetupPanel : public ModelPanel
void updatePotWarnings();
void updateBeepCenter();
void populateThrottleSourceCB();
int timersCount;
int selectedTimerIndex;
bool hasTimerClipboardData(QByteArray * data = nullptr) const;
bool insertTimerAllowed() const;
bool moveTimerDownAllowed() const;
bool moveTimerUpAllowed() const;
void swapTimerData(int idx1, int idx2);
};
#endif // _SETUP_H_

View file

@ -590,7 +590,7 @@ If this is checked the throttle will be reversed. Idle will be forward, trim wi
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_timer3_2">
<widget class="QLabel" name="label_timer3">
<property name="font">
<font>
<weight>50</weight>

View file

@ -592,13 +592,28 @@ void TelemetryCustomScreen::barTimeChanged()
}
}
TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
/******************************************************/
TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
ModelPanel(parent, model, generalSettings, firmware),
ui(new Ui::TelemetrySensor),
sensor(sensor),
lock(false)
lock(false),
sensorIndex(sensorIndex),
selectedIndex(0)
{
ui->setupUi(this);
ui->numLabel->setText(tr("TELE%1").arg(sensorIndex + 1));
ui->numLabel->setProperty("index", sensorIndex);
ui->numLabel->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
QFontMetrics *f = new QFontMetrics(QFont());
QSize sz;
sz = f->size(Qt::TextSingleLine, "TELE00");
ui->numLabel->setMinimumWidth(sz.width());
ui->numLabel->setContextMenuPolicy(Qt::CustomContextMenu);
ui->numLabel->setToolTip(tr("Popup menu available"));
ui->numLabel->setMouseTracking(true);
connect(ui->numLabel, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(on_customContextMenuRequested(QPoint)));
ui->id->setField(sensor.id, this);
ui->instance->setField(sensor.instance, this);
ui->ratio->setField(sensor.ratio, this);
@ -622,6 +637,7 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor,
ui->source2->setField(sensor.sources[1], this);
ui->source3->setField(sensor.sources[2], this);
ui->source4->setField(sensor.sources[3], this);
ui->prec->setField(sensor.prec, 0, 2, false, "", this);
update();
}
@ -645,7 +661,21 @@ void TelemetrySensorPanel::update()
ui->name->setText(sensor.label);
ui->type->setCurrentIndex(sensor.type);
ui->unit->setCurrentIndex(sensor.unit);
ui->prec->setValue(sensor.prec);
ui->id->updateValue();
ui->instance->updateValue();
ui->ratio->updateValue();
ui->offset->updateValue();
ui->autoOffset->updateValue();
ui->filter->updateValue();
ui->logs->updateValue();
ui->persistent->updateValue();
ui->onlyPositive->updateValue();
ui->gpsSensor->updateValue();
ui->altSensor->updateValue();
ui->ampsSensor->updateValue();
ui->cellsSensor->updateValue();
ui->cellsIndex->updateValue();
ui->prec->updateValue();
if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
sensor.updateUnit();
@ -825,15 +855,85 @@ void TelemetrySensorPanel::on_unit_currentIndexChanged(int index)
}
}
void TelemetrySensorPanel::on_prec_valueChanged(double value)
void TelemetrySensorPanel::on_prec_valueChanged()
{
if (!lock) {
sensor.prec = value;
update();
}
}
void TelemetrySensorPanel::on_customContextMenuRequested(QPoint pos)
{
QLabel *label = (QLabel *)sender();
selectedIndex = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos);
QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(cmCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(cmCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear()));
contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"),this,SLOT(cmClearAll()));
contextMenu.exec(globalPos);
}
bool TelemetrySensorPanel::hasClipboardData(QByteArray * data) const
{
const QClipboard * clipboard = QApplication::clipboard();
const QMimeData * mimeData = clipboard->mimeData();
if (mimeData->hasFormat(MIMETYPE_TELE_SENSOR)) {
if (data)
data->append(mimeData->data(MIMETYPE_TELE_SENSOR));
return true;
}
return false;
}
void TelemetrySensorPanel::cmCopy()
{
QByteArray data;
data.append((char*)&sensor, sizeof(SensorData));
QMimeData *mimeData = new QMimeData;
mimeData->setData(MIMETYPE_TELE_SENSOR, data);
QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
}
void TelemetrySensorPanel::cmCut()
{
cmCopy();
cmClear();
}
void TelemetrySensorPanel::cmPaste()
{
QByteArray data;
if (hasClipboardData(&data)) {
memcpy(&sensor, data.constData(), sizeof(SensorData));
emit dataModified();
emit modified();
}
}
void TelemetrySensorPanel::cmClear()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Telemetry Sensor. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
sensor.clear();
emit dataModified();
emit modified();
}
void TelemetrySensorPanel::cmClearAll()
{
if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear all Telemetry Sensors. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
return;
emit clearAllSensors();
}
/******************************************************/
TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
@ -852,11 +952,12 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin
ui->A1GB->hide();
ui->A2GB->hide();
for (unsigned i= 0; i < CPN_MAX_SENSORS; ++i) {
TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], model, generalSettings, firmware);
TelemetrySensorPanel * panel = new TelemetrySensorPanel(this, model.sensorData[i], i, model, generalSettings, firmware);
ui->sensorsLayout->addWidget(panel);
sensorPanels[i] = panel;
connect(panel, SIGNAL(dataModified()), this, SLOT(update()));
connect(panel, SIGNAL(modified()), this, SLOT(onModified()));
connect(panel, SIGNAL(clearAllSensors()), this, SLOT(on_clearAllSensors()));
}
}
else {
@ -1198,3 +1299,13 @@ void TelemetryPanel::on_mahCount_ChkB_toggled(bool checked)
ui->mahCount_SB->setDisabled(!checked);
emit modified();
}
void TelemetryPanel::on_clearAllSensors()
{
for (int i = 0; i < CPN_MAX_SENSORS; i++) {
model->sensorData[i].clear();
}
update();
emit modified();
}

View file

@ -24,6 +24,8 @@
#include "modeledit.h"
#include "eeprominterface.h"
constexpr char MIMETYPE_TELE_SENSOR[] = "application/x-companion-tele-sensor";
class AutoComboBox;
class RawSourceFilterItemModel;
class TimerEdit;
@ -103,19 +105,27 @@ class TelemetrySensorPanel: public ModelPanel
Q_OBJECT
public:
TelemetrySensorPanel(QWidget *parent, SensorData & sensor, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware);
TelemetrySensorPanel(QWidget *parent, SensorData & sensor, int sensorIndex, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware);
~TelemetrySensorPanel();
void update();
signals:
void dataModified();
void clearAllSensors();
protected slots:
void on_name_editingFinished();
void on_type_currentIndexChanged(int index);
void on_formula_currentIndexChanged(int index);
void on_unit_currentIndexChanged(int index);
void on_prec_valueChanged(double value);
void on_prec_valueChanged();
void on_customContextMenuRequested(QPoint pos);
bool hasClipboardData(QByteArray * data = nullptr) const;
void cmCopy();
void cmCut();
void cmPaste();
void cmClear();
void cmClearAll();
protected:
void updateSourcesComboBox(AutoComboBox * cb, bool negative);
@ -123,7 +133,9 @@ class TelemetrySensorPanel: public ModelPanel
private:
Ui::TelemetrySensor * ui;
SensorData & sensor;
bool lock;
bool lock = false;
int sensorIndex = 0;
int selectedIndex = 0;
};
class TelemetryPanel : public ModelPanel
@ -154,6 +166,7 @@ class TelemetryPanel : public ModelPanel
void on_fasOffset_DSB_editingFinished();
void on_mahCount_SB_editingFinished();
void on_mahCount_ChkB_toggled(bool checked);
void on_clearAllSensors();
private:
Ui::Telemetry *ui;

View file

@ -13,13 +13,32 @@
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,1,0,0,0,1,0,0,1,1,1,0,0,1,0,1,0,1,1,1,1,1,1,0,1,0,0,1,0,0,1,0,0,0,0,0">
<layout class="QHBoxLayout" name="horizontalLayout" stretch="0,0,1,0,0,0,1,0,0,1,1,1,0,0,1,0,1,0,1,1,1,1,1,1,0,0,0,0,1,0,0,1,0,0,0,0,0">
<property name="leftMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QLabel" name="numLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="text">
<string>TELE##</string>
</property>
</widget>
</item>
<item>
<widget class="QLineEdit" name="name">
<property name="sizePolicy">
@ -520,6 +539,11 @@
<string>US fl.Oz.</string>
</property>
</item>
<item>
<property name="text">
<string>ml/min</string>
</property>
</item>
</widget>
</item>
<item>
@ -536,23 +560,7 @@
</widget>
</item>
<item>
<widget class="QDoubleSpinBox" name="prec">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="decimals">
<number>0</number>
</property>
<property name="maximum">
<double>2.000000000000000</double>
</property>
</widget>
<widget class="AutoPrecisionComboBox" name="prec"/>
</item>
<item>
<widget class="QLabel" name="ratioLabel">
@ -711,6 +719,11 @@
<extends>QSpinBox</extends>
<header>autohexspinbox.h</header>
</customwidget>
<customwidget>
<class>AutoPrecisionComboBox</class>
<extends>QComboBox</extends>
<header>autoprecisioncombobox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>

View file

@ -0,0 +1,62 @@
/*
* 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 "profilechooser.h"
#include "ui_profilechooser.h"
#include "appdata.h"
ProfileChooserDialog::ProfileChooserDialog(QWidget * parent):
QDialog(parent),
ui(new Ui::ProfileChooserDialog)
{
ui->setupUi(this);
setWindowIcon(QIcon(":/icon.png"));
QComboBox *prof = ui->cboProfiles;
prof->clear();
QMap<int, QString> active;
active = g.getActiveProfiles();
QMapIterator<int, QString> i(active);
while (i.hasNext()) {
i.next();
prof->addItem(i.value(), i.key());
}
prof->setSizeAdjustPolicy(QComboBox::AdjustToContents);
prof->setCurrentIndex(prof->findData(g.id()));
connect(prof, SIGNAL(currentIndexChanged(int)), this, SLOT(on_cboProfilesCurrentIndexChanged(int)));
}
ProfileChooserDialog::~ProfileChooserDialog()
{
delete ui;
}
void ProfileChooserDialog::on_cboProfilesCurrentIndexChanged(int index)
{
QComboBox *prof = ui->cboProfiles;
if ((prof->currentIndex() > -1)) {
int oldid = g.id();
int newid = prof->currentData().toInt();
if (oldid != newid)
emit profileChanged(newid);
}
}

View file

@ -0,0 +1,47 @@
/*
* 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.
*/
#ifndef _PROFILECHOOSER_H_
#define _PROFILECHOOSER_H_
#include <QtWidgets>
namespace Ui {
class ProfileChooserDialog;
}
class ProfileChooserDialog : public QDialog
{
Q_OBJECT
public:
ProfileChooserDialog(QWidget * parent = 0);
~ProfileChooserDialog();
signals:
void profileChanged(int newid);
private slots:
void on_cboProfilesCurrentIndexChanged(int index);
private:
Ui::ProfileChooserDialog *ui;
};
#endif // _PROFILECHOOSER_H_

View file

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ProfileChooserDialog</class>
<widget class="QDialog" name="ProfileChooserDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>352</width>
<height>102</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="windowTitle">
<string>Select Profile</string>
</property>
<widget class="QWidget" name="layoutWidget">
<property name="geometry">
<rect>
<x>20</x>
<y>20</y>
<width>311</width>
<height>66</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Profile</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="cboProfiles">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Ok</set>
</property>
<property name="centerButtons">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>ProfileChooserDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ProfileChooserDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View file

@ -15,6 +15,7 @@ set(shared_HDRS
autolineedit.h
genericpanel.h
hexspinbox.h
autoprecisioncombobox.h
)
qt5_wrap_cpp(shared_SRCS ${shared_HDRS})

View file

@ -0,0 +1,257 @@
/*
* 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.
*/
#ifndef _AUTOPRECISIONCOMBOBOX_H_
#define _AUTOPRECISIONCOMBOBOX_H_
#include "genericpanel.h"
#include <QComboBox>
#include <QString>
class AutoPrecisionComboBox: public QComboBox
{
Q_OBJECT
//! \property minDecimals Determines the first list item
Q_PROPERTY(unsigned int minDecimals READ minDecimals WRITE setMinDecimals)
//! \property maxDecimals Determines the last list item
Q_PROPERTY(unsigned int maxDecimals READ maxDecimals WRITE setMaxDecimals)
//! \property padding If true, add trailing underscore placeholders to each list item
Q_PROPERTY(bool padding READ padding WRITE setPadding)
//! \property suffix Optional text suffix to each list value
Q_PROPERTY(QString suffix READ suffix WRITE setSuffix)
public:
explicit AutoPrecisionComboBox(QWidget * parent = nullptr):
QComboBox(parent)
{
init();
}
explicit AutoPrecisionComboBox(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr):
QComboBox(parent)
{
setField(field, minDecimals, maxDecimals, padding, suffix, panel);
init();
}
explicit AutoPrecisionComboBox(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", QWidget * parent = nullptr, GenericPanel * panel = nullptr):
QComboBox(parent)
{
setField(field, minDecimals, maxDecimals, padding, suffix, panel);
init();
}
inline int minDecimals() const { return m_minDecimals; }
inline int maxDecimals() const { return m_maxDecimals; }
inline bool padding() const { return m_padding; }
inline QString suffix() const { return m_suffix; }
public slots:
void setField(int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr)
{
m_field = reinterpret_cast<unsigned int *>(&field);
initField(minDecimals, maxDecimals, padding, suffix);
setPanel(panel);
}
void setField(unsigned int & field, unsigned int minDecimals = 0, unsigned int maxDecimals = 1, bool padding = false, QString suffix = "", GenericPanel * panel = nullptr)
{
m_field = &field;
initField(minDecimals, maxDecimals, padding, suffix);
setPanel(panel);
}
void setMinDecimals(unsigned int minDecimals)
{
m_minDecimals = rangecheckDecimals(minDecimals);
if (m_minDecimals > m_maxDecimals)
m_minDecimals = m_maxDecimals;
if (!m_lock)
updateList();
}
void setMaxDecimals(unsigned int maxDecimals)
{
m_maxDecimals = rangecheckDecimals(maxDecimals);
if (m_maxDecimals < m_minDecimals)
m_maxDecimals = m_minDecimals;
if (!m_lock)
updateList();
}
void setPadding(bool padding)
{
m_padding = padding;
if (!m_lock)
updateList();
}
void setSuffix(QString suffix)
{
m_suffix = suffix.trimmed();
if (!m_lock)
updateList();
}
void setPanel(GenericPanel * panel)
{
m_panel = panel;
}
void setValue(int value)
{
if (!m_field)
return;
const unsigned int val = (unsigned int)value;
if (*m_field != val) {
*m_field = rangecheckDecimals(val);
updateValue();
emit valueChanged();
}
}
void setValue(unsigned int value)
{
if (!m_field)
return;
if (*m_field != value) {
*m_field = rangecheckDecimals(value);
updateValue();
emit valueChanged();
}
}
void updateValue()
{
if (!m_field)
return;
if (!isValidDecimals(*m_field))
return;
if (*m_field > m_maxDecimals) {
setMaxDecimals(*m_field);
}
const bool savedlock = setLocked(true);
setCurrentIndex(findData(*m_field));
setLocked(savedlock);
}
void setAutoIndexes()
{
for (int i = 0; i < count(); ++i)
setItemData(i, i);
updateValue();
}
signals:
void valueChanged();
protected slots:
void init()
{
connect(this, QOverload<int>::of(&AutoPrecisionComboBox::currentIndexChanged), this, &AutoPrecisionComboBox::onCurrentIndexChanged);
}
void initField(unsigned int minDecimals, unsigned int maxDecimals, bool padding, QString suffix)
{
const bool savedlock = setLocked(true);
setMinDecimals(minDecimals);
setMaxDecimals(maxDecimals);
setPadding(padding);
setSuffix(suffix);
setLocked(savedlock);
updateList();
}
void updateList()
{
const bool savedlock = setLocked(true);
unsigned int i;
int j;
clear();
for (i = APCB_DECIMALS_MIN, j = (int)m_maxDecimals; j >= 0; i++, j--) {
if (i >= m_minDecimals) {
QString str = QString("0.%1").arg(QString(i, '0'));
if (m_padding)
str.append(QString(j, '_'));
if (m_suffix != "")
str.append(QString(" %1").arg(m_suffix));
addItem(str, i);
}
}
setSizeAdjustPolicy(QComboBox::AdjustToContents);
setLocked(savedlock);
}
void onCurrentIndexChanged(int index)
{
if (index < 0)
return;
if (m_field && !m_lock) {
*m_field = itemData(index).toUInt();
if (m_panel)
emit m_panel->modified();
emit valueChanged();
}
}
unsigned int rangecheckDecimals(unsigned int decimals)
{
unsigned int ret;
if (decimals < APCB_DECIMALS_MIN)
ret = APCB_DECIMALS_MIN;
else if (decimals > APCB_DECIMALS_MAX)
ret = APCB_DECIMALS_MAX;
else
ret = decimals;
return ret;
}
bool isValidDecimals(unsigned int value)
{
if (value >= APCB_DECIMALS_MIN && value <= APCB_DECIMALS_MAX)
return true;
else
return false;
}
bool setLocked(bool lock)
{
const bool ret = m_lock;
m_lock = lock;
return ret;
}
protected:
constexpr static unsigned int APCB_DECIMALS_MIN = {0};
constexpr static unsigned int APCB_DECIMALS_MAX = {6};
unsigned int * m_field = nullptr;
GenericPanel * m_panel = nullptr;
unsigned int m_minDecimals = 0;
unsigned int m_maxDecimals = 0;
bool m_padding = false;
QString m_suffix = "";
bool m_lock = false;
};
#endif // _AUTOPRECISIONCOMBOBOX_H_

View file

@ -39,6 +39,7 @@ class GenericPanel : public QWidget
friend class AutoHexSpinBox;
friend class AutoLineEdit;
friend class GVarGroup;
friend class AutoPrecisionComboBox;
public:
GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware);

View file

@ -99,9 +99,10 @@ void Joystick::processEvents()
axes[i] = moved;
}
}
else
else {
axisRepeatTimers[i].restart();
}
}
else {
emit axisValueChanged(i, 0);
}

View file

@ -34,7 +34,7 @@ SimulatedUIWidgetTX16S::SimulatedUIWidgetTX16S(SimulatorInterface *simulator, QW
// add actions in order of appearance on the help menu
act = new RadioUiAction(3, QList<int>() << Qt::Key_Up, SIMU_STR_HLP_KEY_UP, SIMU_STR_HLP_ACT_MDL);
addRadioWidget(ui->rightbuttons->addArea(QRect(10, 0, 80, 35), "TX16S/right_mdl.png", act));
addRadioWidget(ui->rightbuttons->addArea(QRect(40, 0, 110, 35), "TX16S/right_mdl.png", act));
m_mouseMidClickAction = new RadioUiAction(2, QList<int>() << Qt::Key_Enter << Qt::Key_Return, SIMU_STR_HLP_KEYS_ACTIVATE, SIMU_STR_HLP_ACT_ROT_DN);
addRadioWidget(ui->rightbuttons->addArea(QRect(45, 70, 100, 160), "TX16S/right_ent.png", m_mouseMidClickAction));
@ -59,7 +59,7 @@ SimulatedUIWidgetTX16S::SimulatedUIWidgetTX16S(SimulatorInterface *simulator, QW
m_scrollDnAction = new RadioUiAction(-1, QList<int>() << Qt::Key_Plus << Qt::Key_Equal, SIMU_STR_HLP_KEY_PLS % "|" % SIMU_STR_HLP_MOUSE_DN, SIMU_STR_HLP_ACT_ROT_RGT);
connectScrollActions();
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 252, 30, 30), "TX16S/left_scrnsht.png", m_screenshotAction));
addRadioWidget(ui->leftbuttons->addArea(QRect(10, 245, 30, 30), "TX16S/left_scrnsht.png", m_screenshotAction));
m_backlightColors << QColor(47, 123, 227);

View file

@ -699,11 +699,13 @@ void SimulatorWidget::restoreRadioWidgetsState()
void SimulatorWidget::saveRadioWidgetsState(QList<QByteArray> & state)
{
if (m_radioWidgets.size()) {
if (g.simuSW()) {
state.clear();
foreach (RadioWidget * rw, m_radioWidgets)
state.append(rw->getStateData());
}
}
}
/*

View file

@ -575,6 +575,7 @@ class AppData: public CompStoreObj
PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false)
PROPERTY4(bool, autoCheckApp, "startup_check_companion", true)
PROPERTY4(bool, autoCheckFw, "startup_check_fw", true)
PROPERTY4(bool, promptProfile, "startup_prompt_profile", false)
PROPERTY(bool, enableBackup, false)
PROPERTY(bool, backupOnFlash, true)

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -10184,7 +10184,7 @@ We recommend you view the release notes using the button below to learn about an
<message>
<location filename="../mainwindow.cpp" line="1491"/>
<source>Set Menu Language</source>
<translation>Inposta Menu Lingua</translation>
<translation>Menù Impostazione Lingua</translation>
</message>
<message>
<location filename="../mainwindow.cpp" line="1230"/>

View file

@ -6,6 +6,7 @@
;Include Modern UI
!include "MUI2.nsh"
!include "nsDialogs.nsh"
!include "@CMAKE_CURRENT_SOURCE_DIR@\..\targets\windows\FileAssociation.nsh"
;--------------------------------
@ -32,6 +33,10 @@
;Variables
Var StartMenuFolder
Var StartMenuLocationDialog
Var StartMenuLocationRadioCurrent
Var StartMenuLocationRadioAll
Var StartMenuLocationValue ; "current_user" or "all_users"
;--------------------------------
;Interface Settings
@ -68,9 +73,11 @@
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\OpenTX\Companion @VERSION_FAMILY@"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder
;Start Menu Folder for current user or all users?
Page custom StartMenuLocationCreator StartMenuLocationLeave
!insertmacro MUI_PAGE_INSTFILES
# These indented statements modify settings for MUI_PAGE_FINISH
@ -117,6 +124,7 @@ Section "OpenTX Companion @VERSION_FAMILY@" SecDummy
;Registry information for add/remove programs
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenTX Companion @VERSION_FAMILY@" "DisplayName" "OpenTX Companion @VERSION_FAMILY@"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenTX Companion @VERSION_FAMILY@" "DisplayVersion" "@VERSION@"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenTX Companion @VERSION_FAMILY@" "UninstallString" "$\"$INSTDIR\Uninstall.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenTX Companion @VERSION_FAMILY@" "DisplayIcon" "$\"$INSTDIR\companion.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenTX Companion @VERSION_FAMILY@" "Publisher" "OpenTX"
@ -126,10 +134,14 @@ Section "OpenTX Companion @VERSION_FAMILY@" SecDummy
!insertmacro MUI_STARTMENU_WRITE_BEGIN Application
;Create shortcuts
${If} $StartMenuLocationValue == "all_users"
SetShellVarContext all
${Endif}
CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Companion @VERSION_FAMILY@.lnk" "$INSTDIR\companion.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Firmware Simulator @VERSION_FAMILY@.lnk" "$INSTDIR\simulator.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall Companion @VERSION_FAMILY@.lnk" "$INSTDIR\Uninstall.exe"
SetShellVarContext current
!insertmacro MUI_STARTMENU_WRITE_END
@ -141,6 +153,14 @@ SectionEnd
;Language strings
LangString DESC_SecDummy ${LANG_ENGLISH} "Models and settings editor for OpenTX"
LangString DESC_SecDummy ${LANG_FRENCH} "Editeur de r<>glages et mod<6F>les pour OpenTX"
LangString SML_SubTitle ${LANG_ENGLISH} "Choose a location for the Start Menu shortcuts"
LangString SML_SubTitle ${LANG_FRENCH} "Choisissez un emplacement pour les raccourcis de l'application"
LangString SML_MainLabel ${LANG_ENGLISH} "Create start menu shortcuts for:"
LangString SML_MainLabel ${LANG_FRENCH} "Emplacement pour les raccourcis de l'application:"
LangString SML_RadioCurrent ${LANG_ENGLISH} "Current user only"
LangString SML_RadioCurrent ${LANG_FRENCH} "Utilisateur actuel"
LangString SML_RadioAll ${LANG_ENGLISH} "All users"
LangString SML_RadioAll ${LANG_FRENCH} "Tous les utilisateurs"
;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
@ -203,6 +223,13 @@ Section "un.OpenTX Companion @VERSION_FAMILY@"
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder
; Always remove start menu folder for both locations: current user and all users
SetShellVarContext all
Delete "$SMPROGRAMS\$StartMenuFolder\Companion @VERSION_FAMILY@.lnk"
Delete "$SMPROGRAMS\$StartMenuFolder\Firmware Simulator @VERSION_FAMILY@.lnk"
Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Companion @VERSION_FAMILY@.lnk"
RMDir "$SMPROGRAMS\$StartMenuFolder"
SetShellVarContext current
Delete "$SMPROGRAMS\$StartMenuFolder\Companion @VERSION_FAMILY@.lnk"
Delete "$SMPROGRAMS\$StartMenuFolder\Firmware Simulator @VERSION_FAMILY@.lnk"
Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Companion @VERSION_FAMILY@.lnk"
@ -218,3 +245,58 @@ SectionEnd
Function LaunchLink
ExecShell "" "$INSTDIR\companion.exe"
FunctionEnd
; Custom page with radio buttons, if the start menu entries shall be created
; for the current user or for all users.
Function StartMenuLocationCreator
; Set default value if not already set. Do this before we might "abort" this page.
${If} $StartMenuLocationValue == ""
StrCpy $StartMenuLocationValue "current_user"
${EndIf}
; If the folder starts with >, the user has chosen not to create a shortcut (see NSIS/StartMenu.nsh)
StrCpy $R0 $StartMenuFolder 1
${If} $R0 == ">"
Abort
${EndIf}
!insertmacro MUI_HEADER_TEXT_PAGE $(MUI_TEXT_STARTMENU_TITLE) $(SML_SubTitle)
nsDialogs::Create 1018
Pop $StartMenuLocationDialog
${If} $StartMenuLocationDialog == error
Abort
${EndIf}
${NSD_CreateLabel} 0u 0u 100% 12u $(SML_MainLabel)
Pop $R0
${NSD_CreateRadioButton} 8u 20u 100% 12u $(SML_RadioCurrent)
Pop $StartMenuLocationRadioCurrent
${NSD_AddStyle} $StartMenuLocationRadioCurrent ${WS_GROUP}
${NSD_CreateRadioButton} 8u 40u 100% 12u $(SML_RadioAll)
Pop $StartMenuLocationRadioAll
${If} $StartMenuLocationValue == "all_users"
${NSD_SetState} $StartMenuLocationRadioAll ${BST_CHECKED}
${Else}
${NSD_SetState} $StartMenuLocationRadioCurrent ${BST_CHECKED}
${EndIf}
${NSD_OnBack} StartMenuLocationLeave
nsDialogs::Show
FunctionEnd
; Converts the radio button state back into a value for "StartMenuLocationValue"
Function StartMenuLocationLeave
${NSD_GetState} $StartMenuLocationRadioAll $R0
${If} $R0 == ${BST_CHECKED}
StrCpy $StartMenuLocationValue "all_users"
${Else}
StrCpy $StartMenuLocationValue "current_user"
${Endif}
FunctionEnd

View file

@ -489,7 +489,7 @@ end
local function runPopupPage(event)
local result
if fieldPopup.status == 3 then
result = popupConfirmation(fieldPopup.info, event)
result = popupConfirmation("Confirmation", fieldPopup.info, event)
else
result = popupWarning(fieldPopup.info, event)
end

View file

@ -0,0 +1,157 @@
1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8
1,1,Flysky,V9x9,1,Flip,Light,Pict,Video
1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL
1,3,Flysky,V912,1,BtmBtn,TopBtn
1,4,Flysky,CX20,0,CH5,CH6,CH7
28,0,Flysky_AFHDS2A,PWM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
53,0,Flyzone,FZ-410,0
2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess
2,1,Hubsan,H301,0,RTH,Light,Stab,Video
2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes
41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED
60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8
37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8
37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8
37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8
25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8
3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8
3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8
67,0,FrSkyL,LR12,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
67,1,FrSkyL,LR12_6CH,0,CH5,CH6
15,0,FrSkyX,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
15,1,FrSkyX,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8
15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8
64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
55,1,FrSkyRX,CloneTX,0
39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9
39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9
39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9
57,0,HoTT,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8
68,0,Skyartec,Std,0,CH5,CH6,CH7
7,0,Devo,8CH,0,CH5,CH6,CH7,CH8
7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10
7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
7,3,Devo,6CH,0,CH5,CH6
7,4,Devo,7CH,0,CH5,CH6,CH7
30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8
30,1,WK2x01,WK2401,0
30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro
30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro
30,4,WK2x01,W6HEL,0,Gear,Col,Gyro
30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro
6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9
24,0,Assan,Std,0,CH5,CH6,CH7,CH8
14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2
14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates
14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf
14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video
42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED
42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol
34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess
13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess
12,0,CX10,Green,1,Flip,Rate
12,1,CX10,Blue,1,Flip,Rate,Pict,Video
12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess
12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video
12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip
12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH
33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow
45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH
45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH
45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH
16,0,ESKY,Std,0,Gyro,Pitch
16,1,ESKY,ET4,0,Gyro,Pitch
35,0,ESKY150,4CH,0
35,1,ESKY150,7CH,0,FMode,Aux6,Aux7
20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib
20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib
23,0,FY326,FY326,1,Flip,RTH,HLess,Expert
32,0,GW008,FY326,1,Flip
36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1
36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8
9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib
26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,3,MJXQ,H26D,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,4,MJXQ,E010,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,5,MJXQ,H26WH,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,6,MJXQ,Phoenix,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
17,0,MT99XX,Std,1,Flip,LED,Pict,Video,HLess
17,1,MT99XX,H7,1,Flip,LED,Pict,Video,HLess
17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess
17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess
17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess
44,0,NCC1701,Std,1,Warp
51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess
66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train
29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal
29,1,Q2x2,Q242,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
29,2,Q2x2,Q282,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
31,0,Q303,Q303,1,AltHol,Flip,Pict,Video,HLess,RTH,Gimbal
31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal
31,2,Q303,CX10D,1,Arm,Flip
31,3,Q303,CX10WD,1,Arm,Flip
50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
11,0,SLT,V1,0,Gear,Pitch
11,1,SLT,V2,0,CH5,CH6,CH7,CH8
11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib
11,3,SLT,Q200,0,Rates,n-a,CH7,CH8,Mode,VidOn,VidOff,Calib
11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict
10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess
10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess
5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY
5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD
61,0,Tiger,Std,1,Flip,Light
46,0,V911s,Std,1,Calib
46,1,E119,Std,1,Calib
62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess
8,1,YD717,SkyWlkr,1,Flip,Light,Pict,Video,HLess
8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess
8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess
8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess
65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8
65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8
56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
19,0,Shenqi,Cycle,1
4,1,Hisky,HK310,0,Aux
43,0,Traxxas,6519,0
52,0,ZSX,280,1,Light
48,0,V761,Std,1,Gyro
49,0,KF606,Std,1,Trim
47,0,GD00x,V1,1,Trim,LED,Rate
47,1,GD00x,V2,1,Trim,LED,Rate
58,0,FX816,P38,1

View file

@ -0,0 +1,302 @@
local toolName = "TNS|Multi chan namer|TNE"
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # 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. #
---- # #
---- #########################################################################
local protocol_name = ""
local sub_protocol_name = ""
local bind_ch = 0
local module_conf = {}
local module_pos = "Internal"
local file_ok = 0
local done = 0
local protocol = 0
local sub_protocol = 0
local f_seek = 0
local channel_names={}
local num_search = "Searching"
local function drawScreenTitle(title)
if LCD_W == 480 then
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
lcd.drawText(1, 5, title, MENU_TITLE_COLOR)
else
lcd.drawScreenTitle(title, 0, 0)
end
end
function bitand(a, b)
local result = 0
local bitval = 1
while a > 0 and b > 0 do
if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits
result = result + bitval -- set the current bit
end
bitval = bitval * 2 -- shift left
a = math.floor(a/2) -- shift right
b = math.floor(b/2)
end
return result
end
local function Multi_Draw_LCD(event)
local line = 0
lcd.clear()
drawScreenTitle("Multi channels namer")
--Display settings
local lcd_opt = 0
if LCD_W == 480 then
x_pos = 10
y_pos = 32
y_inc = 20
else
x_pos = 0
y_pos = 9
y_inc = 8
lcd_opt = SMLSIZE
end
--Multi Module detection
if module_conf["Type"] ~= 6 then
if LCD_W == 480 then
lcd.drawText(10,50,"No Multi module configured...", BLINK)
else
--Draw on LCD_W=128
lcd.drawText(2,17,"No Multi module configured...",SMLSIZE)
end
return
else
lcd.drawText(x_pos, y_pos+y_inc*line,module_pos .. " Multi detected.", lcd_opt)
line = line + 1
end
--Channel order
if (ch_order == -1) then
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels order can't be read from Multi...", lcd_opt)
line = line + 1
end
--Can't open file MultiChan.txt
if file_ok == 0 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Can't read MultiChan.txt file...", lcd_opt)
return
end
if ( protocol_name == "" or sub_protocol_name == "" ) and f_seek ~=-1 then
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
if f == nil then return end
lcd.drawText(x_pos, y_pos+y_inc*line,num_search, lcd_opt)
num_search = num_search .. "."
if #num_search > 15 then
num_search = string.sub(num_search,1,9)
end
local proto = 0
local sub_proto = 0
local proto_name = ""
local sub_proto_name = ""
local channels = ""
local nbr_try = 0
local nbr_car = 0
repeat
io.seek(f, f_seek)
local data = io.read(f, 100) -- read 100 characters
if #data ==0 then
f_seek = -1 -- end of file
break
end
proto, sub_proto, proto_name, sub_proto_name, bind_ch, channels = string.match(data,'(%d+),(%d),([%w-_ ]+),([%w-_ ]+),(%d)(.+)')
if proto ~= nil and sub_proto ~= nil and protocol_name ~= nil and sub_protocol_name ~= nil and bind_ch ~= nil then
if tonumber(proto) == protocol and tonumber(sub_proto) == sub_protocol then
protocol_name = proto_name
sub_protocol_name = sub_proto_name
bind_ch = tonumber(bind_ch)
if channels ~= nil then
--extract channel names
nbr_car = string.find(channels, "\r")
if nbr_car == nil then nbr_car = string.find(channels, "\n") end
if nbr_car ~= nil then
channels = string.sub(channels,1,nbr_car-1)
end
local i = 5
for k in string.gmatch(channels, ",([%w-_ ]+)") do
channel_names[i] = k
i = i + 1
end
end
f_seek = -1 -- protocol found
break
end
end
if f_seek ~= -1 then
nbr_car = string.find(data, "\n")
if nbr_car == nil then nbr_car = string.find(data, "\r") end
if nbr_car == nil then
f_seek = -1 -- end of file
break
end
f_seek = f_seek + nbr_car -- seek to next line
nbr_try = nbr_try + 1
end
until nbr_try > 20 or f_seek == -1
io.close(f)
end
if f_seek ~= -1 then
return -- continue searching...
end
--Protocol & Sub_protocol
if protocol_name == "" or sub_protocol_name == "" then
lcd.drawText(x_pos, y_pos+y_inc*line,"Unknown protocol "..tostring(protocol).."/"..tostring(sub_protocol).." ...", lcd_opt)
return
elseif LCD_W > 128 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name .. " / SubProtocol: " .. sub_protocol_name, lcd_opt)
line = line + 1
else
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name, lcd_opt)
line = line + 1
lcd.drawText(x_pos, y_pos+y_inc*line,"SubProtocol: " .. sub_protocol_name, lcd_opt)
line = line + 1
end
text1=""
text2=""
for i,v in ipairs(channel_names) do
if i<=8 then
if i==1 then
text1 = v
else
text1=text1 .. "," .. v
end
else
if i==9 then
text2 = v
else
text2=text2 .. "," .. v
end
end
end
if LCD_W > 128 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels: " .. text1, lcd_opt)
line = line + 1
if text2 ~= "" then
lcd.drawText(x_pos*9, y_pos+y_inc*line,text2, lcd_opt)
line = line + 1
end
end
if event ~= EVT_VIRTUAL_ENTER and done == 0 then
lcd.drawText(x_pos, y_pos+y_inc*line,"<ENT> Save", lcd_opt + INVERS + BLINK)
return
end
lcd.drawText(x_pos, y_pos+y_inc*line,"Setting channel names.", lcd_opt)
line = line + 1
local output, nbr
if done == 0 then
for i,v in ipairs(channel_names) do
output = model.getOutput(i-1)
output["name"] = v
model.setOutput(i-1,output)
nbr = i
end
for i = nbr, 15 do
output = model.getOutput(i)
output["name"] = "n-a"
model.setOutput(i,output)
end
if bind_ch == 1 then
output = model.getOutput(15)
output["name"] = "BindCH"
model.setOutput(15,output)
end
done = 1
end
lcd.drawText(x_pos, y_pos+y_inc*line,"Done!", lcd_opt)
line = line + 1
end
-- Init
local function Multi_Init()
module_conf = model.getModule(0)
if module_conf["Type"] ~= 6 then
module_pos = "External"
module_conf = model.getModule(1)
if module_conf["Type"] ~= 6 then
return
end
end
protocol = module_conf["protocol"]
sub_protocol = module_conf["subProtocol"]
--Exceptions on first 4 channels...
local stick_names = { "Rud", "Ele", "Thr", "Ail" }
if ( protocol == 4 and sub_protocol ==1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX
stick_names[2] = "n-a"
stick_names[4] = "n-a"
elseif protocol == 43 then -- Traxxas
stick_names[2] = "Aux4"
stick_names[4] = "Aux3"
elseif protocol == 48 then -- V761
stick_names[4] = "n-a"
elseif protocol == 47 or protocol == 49 or protocol == 58 then -- GD00x, KF606, FX816
stick_names[1] = "n-a"
stick_names[2] = "n-a"
end
--Determine fist 4 channels order
local ch_order=module_conf["channelsOrder"]
if (ch_order == -1) then
channel_names[1] = stick_names[defaultChannel(0)+1]
channel_names[2] = stick_names[defaultChannel(1)+1]
channel_names[3] = stick_names[defaultChannel(2)+1]
channel_names[4] = stick_names[defaultChannel(3)+1]
else
channel_names[bitand(ch_order,3)+1] = stick_names[4]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[2]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[3]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[1]
end
--Check MultiChan.txt
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
if f == nil then return end
file_ok = 1
io.close(f)
end
-- Main
local function Multi_Run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
else
Multi_Draw_LCD(event)
if event == EVT_VIRTUAL_EXIT then
return 2
end
end
return 0
end
return { init=Multi_Init, run=Multi_Run }

BIN
radio/sdcard/horus/THEMES/Darkblue/tx16s.bmp Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Before After
Before After

View file

@ -19,7 +19,8 @@
-- Horus Widget to display the levels of lipo battery with per cell indication
-- 3djc & Offer Shmuely
-- Date: 2020
-- ver: 0.4
-- ver: 0.5
local version = "v0.5"
local _options = {
{ "Sensor", SOURCE, 0 }, -- default to 'Cels'
@ -28,6 +29,40 @@ local _options = {
{ "LowestCell", BOOL, 1 } -- 0=main voltage display shows all-cell-voltage, 1=main voltage display shows lowest-cell
}
-- Data gathered from commercial lipo sensors
local _lipoPercentListSplit = {
{ { 3, 0 }, { 3.093, 1 }, { 3.196, 2 }, { 3.301, 3 }, { 3.401, 4 }, { 3.477, 5 }, { 3.544, 6 }, { 3.601, 7 }, { 3.637, 8 }, { 3.664, 9 }, { 3.679, 10 }, { 3.683, 11 }, { 3.689, 12 }, { 3.692, 13 } },
{ { 3.705, 14 }, { 3.71, 15 }, { 3.713, 16 }, { 3.715, 17 }, { 3.72, 18 }, { 3.731, 19 }, { 3.735, 20 }, { 3.744, 21 }, { 3.753, 22 }, { 3.756, 23 }, { 3.758, 24 }, { 3.762, 25 }, { 3.767, 26 } },
{ { 3.774, 27 }, { 3.78, 28 }, { 3.783, 29 }, { 3.786, 30 }, { 3.789, 31 }, { 3.794, 32 }, { 3.797, 33 }, { 3.8, 34 }, { 3.802, 35 }, { 3.805, 36 }, { 3.808, 37 }, { 3.811, 38 }, { 3.815, 39 } },
{ { 3.818, 40 }, { 3.822, 41 }, { 3.825, 42 }, { 3.829, 43 }, { 3.833, 44 }, { 3.836, 45 }, { 3.84, 46 }, { 3.843, 47 }, { 3.847, 48 }, { 3.85, 49 }, { 3.854, 50 }, { 3.857, 51 }, { 3.86, 52 } },
{ { 3.863, 53 }, { 3.866, 54 }, { 3.87, 55 }, { 3.874, 56 }, { 3.879, 57 }, { 3.888, 58 }, { 3.893, 59 }, { 3.897, 60 }, { 3.902, 61 }, { 3.906, 62 }, { 3.911, 63 }, { 3.918, 64 } },
{ { 3.923, 65 }, { 3.928, 66 }, { 3.939, 67 }, { 3.943, 68 }, { 3.949, 69 }, { 3.955, 70 }, { 3.961, 71 }, { 3.968, 72 }, { 3.974, 73 }, { 3.981, 74 }, { 3.987, 75 }, { 3.994, 76 } },
{ { 4.001, 77 }, { 4.007, 78 }, { 4.014, 79 }, { 4.021, 80 }, { 4.029, 81 }, { 4.036, 82 }, { 4.044, 83 }, { 4.052, 84 }, { 4.062, 85 }, { 4.074, 86 }, { 4.085, 87 }, { 4.095, 88 } },
{ { 4.105, 89 }, { 4.111, 90 }, { 4.116, 91 }, { 4.12, 92 }, { 4.125, 93 }, { 4.129, 94 }, { 4.135, 95 }, { 4.145, 96 }, { 4.176, 97 }, { 4.179, 98 }, { 4.193, 99 }, { 4.2, 100 } },
}
local function periodicInit(t, durationMili)
t.startTime = getTime();
t.durationMili = durationMili;
end
local function periodicReset(t)
t.startTime = getTime();
end
local function periodicHasPassed(t)
local elapsed = getTime() - t.startTime;
local elapsedMili = elapsed * 10;
if (elapsedMili < t.durationMili) then
return false;
end
return true;
end
local function periodicGetElapsedTime(t)
local elapsed = getTime() - t.startTime;
local elapsedMili = elapsed * 10;
return elapsedMili;
end
-- This function is run once at the creation of the widget
local function create(zone, options)
local wgt = {
@ -50,7 +85,10 @@ local function create(zone, options)
cellCount = 0,
cellSum = 0,
mainValue = 0,
secondaryValue = 0
secondaryValue = 0,
periodic1 = { startTime = getTime(), durationMili = 1000 },
periodicProfiler = { startTime = getTime(), durationMili = 5000 },
profTimes = {},
}
-- use default if user did not set, So widget is operational on "select widget"
@ -65,7 +103,9 @@ end
-- This function allow updates when you change widgets settings
local function update(wgt, options)
if (wgt == nil) then return end
if (wgt == nil) then
return
end
wgt.options = options
@ -99,9 +139,12 @@ end
local function detectResetEvent(wgt)
local currMinRSSI = getValue('RSSI-')
if (currMinRSSI == nil) then return
if (currMinRSSI == nil) then
return
end
if (currMinRSSI == wgt.telemResetLowestMinRSSI) then
return
end
if (currMinRSSI == wgt.telemResetLowestMinRSSI) then return end
if (currMinRSSI < wgt.telemResetLowestMinRSSI) then
-- rssi just got lower, record it
@ -118,29 +161,29 @@ local function detectResetEvent(wgt)
end
--- This function return the percentage remaining in a single Lipo cel
local function getCellPercent(cellValue)
--- since running on long array found to be very intensive to hrous cpu, we are splitting the list to small lists
local function getCellPercent(wgt, cellValue)
if cellValue == nil then
return 0
end
local result = 0;
-- in case somehow voltage is higher, don't return nil
if (cellValue > 4.2) then
return 100
end
-- Data gathered from commercial lipo sensors
local myArrayPercentList = { {3,0},{3.093,1},{3.196,2},{3.301,3},{3.401,4},{3.477,5},{3.544,6},{3.601,7},{3.637,8},{3.664,9},{3.679,10},{3.683,11},{3.689,12},{3.692,13},{3.705,14},{3.71,15},{3.713,16},{3.715,17},{3.72,18},{3.731,19},{3.735,20},{3.744,21},{3.753,22},{3.756,23},{3.758,24},{3.762,25},{3.767,26},{3.774,27},{3.78,28},{3.783,29},{3.786,30},{3.789,31},{3.794,32},{3.797,33},{3.8,34},{3.802,35},{3.805,36},{3.808,37},{3.811,38},{3.815,39},{3.818,40},{3.822,41},{3.825,42},{3.829,43},{3.833,44},{3.836,45},{3.84,46},{3.843,47},{3.847,48},{3.85,49},{3.854,50},{3.857,51},{3.86,52},{3.863,53},{3.866,54},{3.87,55},{3.874,56},{3.879,57},{3.888,58},{3.893,59},{3.897,60},{3.902,61},{3.906,62},{3.911,63},{3.918,64},{3.923,65},{3.928,66},{3.939,67},{3.943,68},{3.949,69},{3.955,70},{3.961,71},{3.968,72},{3.974,73},{3.981,74},{3.987,75},{3.994,76},{4.001,77},{4.007,78},{4.014,79},{4.021,80},{4.029,81},{4.036,82},{4.044,83},{4.052,84},{4.062,85},{4.074,86},{4.085,87},{4.095,88},{4.105,89},{4.111,90},{4.116,91},{4.12,92},{4.125,93},{4.129,94},{4.135,95},{4.145,96},{4.176,97},{4.179,98},{4.193,99},{4.2,100} }
for i, v in ipairs(myArrayPercentList) do
if v[1] >= cellValue then
result = v[2]
break
end
end
for i1, v1 in ipairs(_lipoPercentListSplit) do
--is the cellVal < last-value-on-sub-list? (first-val:v1[1], last-val:v1[#v1])
if (cellValue <= v1[#v1][1]) then
-- cellVal is in this sub-list, find the exact value
for i2, v2 in ipairs(v1) do
if v2[1] >= cellValue then
result = v2[2]
return result
end
end
end
end
-- in case somehow voltage is too high (>4.2), don't return nil
return 100
end
--- This function returns a table with cels values
local function calculateBatteryData(wgt)
@ -152,20 +195,13 @@ local function calculateBatteryData(wgt)
return
end
-- this is necessary for simu where cell-count can change
if #wgt.cellDataHistoryLowest ~= #newCellData then
wgt.cellDataHistoryLowest = {}
for k, v in pairs(newCellData) do
wgt.cellDataHistoryLowest[k] = 5 -- invalid reading, set high value so the min() will update it soon
end
end
local cellMax = 0
local cellMin = 5
local cellSum = 0
for k, v in pairs(newCellData) do
-- stores the lowest cell values in historical table
if v > 1 and v < wgt.cellDataHistoryLowest[k] then -- min 1v to consider a valid reading
if v > 1 and v < wgt.cellDataHistoryLowest[k] then
-- min 1v to consider a valid reading
wgt.cellDataHistoryLowest[k] = v
--- calc history lowest of all cells
@ -181,7 +217,8 @@ local function calculateBatteryData(wgt)
end
--- calc lowest of all cells
if v < cellMin and v > 1 then -- min 1v to consider a valid reading
if v < cellMin and v > 1 then
-- min 1v to consider a valid reading
cellMin = v
end
--- sum of all cells
@ -197,7 +234,6 @@ local function calculateBatteryData(wgt)
--- average of all cells
wgt.cellAvg = wgt.cellSum / wgt.cellCount
wgt.cellPercent = getCellPercent(wgt.cellMin)
wgt.cellDataLive = newCellData
@ -219,12 +255,18 @@ local function calculateBatteryData(wgt)
wgt.secondaryValue = "-2"
end
wgt.isDataAvailable = true
-- calculate intensive CPU data
if (periodicHasPassed(wgt.periodic1) or wgt.cellPercent == 0) then
--wgt.cellPercent = getCellPercent(wgt, wgt.cellMin) -- use batt percentage by lowest cell voltage
wgt.cellPercent = getCellPercent(wgt, wgt.cellAvg) -- use batt percentage by average cell voltage
periodicReset(wgt.periodic1)
end
end
-- color for battery
-- This function returns green at 100%, red bellow 30% and graduate in between
@ -242,24 +284,36 @@ end
-- This function returns green at gvalue, red at rvalue and graduate in between
local function getRangeColor(value, green_value, red_value)
local range = math.abs(green_value - red_value)
if range==0 then return lcd.RGB(0, 0xdf, 0) end
if range == 0 then
return lcd.RGB(0, 0xdf, 0)
end
if value == nil then
return lcd.RGB(0, 0xdf, 0)
end
if green_value > red_value then
if value > green_value then return lcd.RGB(0, 0xdf, 0) end
if value < red_value then return lcd.RGB(0xdf, 0, 0) end
if value > green_value then
return lcd.RGB(0, 0xdf, 0)
end
if value < red_value then
return lcd.RGB(0xdf, 0, 0)
end
g = math.floor(0xdf * (value - red_value) / range)
r = 0xdf - g
return lcd.RGB(r, g, 0)
else
if value > green_value then return lcd.RGB(0, 0xdf, 0) end
if value < red_value then return lcd.RGB(0xdf, 0, 0) end
if value > green_value then
return lcd.RGB(0, 0xdf, 0)
end
if value < red_value then
return lcd.RGB(0xdf, 0, 0)
end
r = math.floor(0xdf * (value - green_value) / range)
g = 0xdf - r
return lcd.RGB(r, g, 0)
end
end
--- Zone size: 70x39 1/8th top bar
local function refreshZoneTiny(wgt)
local myString = string.format("%2.1fV", wgt.mainValue)
@ -307,12 +361,12 @@ local function refreshZoneMedium(wgt)
end
-- draw values
lcd.drawText(wgt.zone.x, wgt.zone.y + 35, string.format("%2.1fV", wgt.mainValue), DBLSIZE + CUSTOM_COLOR + wgt.shadowed + wgt.no_telem_blink)
lcd.drawText(wgt.zone.x + wgt.zone.w, wgt.zone.y, string.format("%2.1fV", wgt.mainValue), DBLSIZE + CUSTOM_COLOR + RIGHT + wgt.shadowed + wgt.no_telem_blink)
-- more info if 1/4 is high enough (without trim & slider)
if wgt.zone.h > 80 then
lcd.drawText(wgt.zone.x , wgt.zone.y + 70, string.format("%2.2fV" , wgt.secondaryValue), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
lcd.drawText(wgt.zone.x + 50, wgt.zone.y + 70, string.format("dV %2.2fV", wgt.cellMax - wgt.cellMin), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
--lcd.drawText(wgt.zone.x + 50 , wgt.zone.y + 70, string.format("%2.2fV" , wgt.secondaryValue), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
lcd.drawText(wgt.zone.x, wgt.zone.y + 70, string.format("dV %2.2fV", wgt.cellMax - wgt.cellMin), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
lcd.drawText(wgt.zone.x, wgt.zone.y + 84, string.format("Min %2.2fV", wgt.cellDataHistoryCellLowest), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
end
@ -321,28 +375,21 @@ local function refreshZoneMedium(wgt)
lcd.drawGauge(wgt.zone.x + myBatt.x, wgt.zone.y + myBatt.y, myBatt.w, myBatt.h, wgt.cellPercent, 100, CUSTOM_COLOR)
-- draw cells
local cellH = wgt.zone.h / wgt.cellCount
if cellH > 20 then cellH =20 end
local cellH = 19
local cellX = 118
local cellW = 58
local cellW = wgt.zone.w / 3
local pos = { { x = 0, y = 38 }, { x = cellW, y = 38 }, { x = 2 * cellW, y = 38 }, { x = 0, y = 57 }, { x = cellW, y = 57 }, { x = 2 * cellW, y = 57 } }
for i = 1, wgt.cellCount, 1 do
local cellY = wgt.zone.y + (i - 1) * (cellH - 1)
-- fill current cell
lcd.setColor(CUSTOM_COLOR, getRangeColor(wgt.cellDataLive[i], wgt.cellMax, wgt.cellMax - 0.2))
--lcd.drawFilledRectangle(wgt.zone.x + cellX , cellY, 58, cellH, CUSTOM_COLOR)
local percentCurrent = getCellPercent(wgt.cellDataLive[i])
local percentMin = getCellPercent(wgt.cellDataHistoryLowest[i])
lcd.drawFilledRectangle(wgt.zone.x + cellX , cellY, cellW * percentCurrent / 100, cellH, CUSTOM_COLOR)
-- fill min
lcd.setColor(CUSTOM_COLOR, getRangeColor(wgt.cellDataHistoryLowest[i], wgt.cellMax, wgt.cellMax - 0.2))
lcd.drawFilledRectangle(wgt.zone.x + cellX + ((percentCurrent - percentMin) / 100) , cellY, cellW - (cellW * (percentCurrent - percentMin) / 100), cellH, CUSTOM_COLOR)
lcd.setColor(CUSTOM_COLOR, getRangeColor(wgt.cellDataLive[i], wgt.cellMax, wgt.cellMax - 0.2))
lcd.drawFilledRectangle(wgt.zone.x + pos[i].x, wgt.zone.y + pos[i].y, cellW - 1, 20, CUSTOM_COLOR)
lcd.setColor(CUSTOM_COLOR, WHITE)
lcd.drawText (wgt.zone.x + cellX + 10, cellY, string.format("%.2f", wgt.cellDataLive[i]), SMLSIZE + CUSTOM_COLOR + wgt.shadowed + wgt.no_telem_blink)
lcd.drawRectangle (wgt.zone.x + cellX , cellY, 59, cellH, CUSTOM_COLOR , 1)
lcd.drawText(wgt.zone.x + pos[i].x + 10, wgt.zone.y + pos[i].y, string.format("%.2f", wgt.cellDataLive[i]), CUSTOM_COLOR + wgt.shadowed)
lcd.drawRectangle(wgt.zone.x + pos[i].x, wgt.zone.y + pos[i].y, cellW, 20, CUSTOM_COLOR, 1)
end
-- draws bat
@ -447,7 +494,9 @@ end
-- This function allow recording of lowest cells when widget is in background
local function background(wgt)
if (wgt == nil) then return end
if (wgt == nil) then
return
end
detectResetEvent(wgt)
@ -456,11 +505,21 @@ local function background(wgt)
end
local function refresh(wgt)
if (wgt == nil) then return end
if (wgt.options == nil) then return end
if (wgt.zone == nil) then return end
if (wgt.options.LowestCell == nil) then return end
if (wgt == nil) then
return
end
if type(wgt) ~= "table" then
return
end
if (wgt.options == nil) then
return
end
if (wgt.zone == nil) then
return
end
if (wgt.options.LowestCell == nil) then
return
end
if wgt.options.Shadow == 1 then
wgt.shadowed = SHADOWED
@ -478,14 +537,17 @@ local function refresh(wgt)
wgt.no_telem_blink = INVERS + BLINK
end
if wgt.zone.w > 380 and wgt.zone.h > 165 then refreshZoneXLarge(wgt)
elseif wgt.zone.w > 180 and wgt.zone.h > 145 then refreshZoneLarge(wgt)
elseif wgt.zone.w > 170 and wgt.zone.h > 65 then refreshZoneMedium(wgt)
elseif wgt.zone.w > 150 and wgt.zone.h > 28 then refreshZoneSmall(wgt)
elseif wgt.zone.w > 65 and wgt.zone.h > 35 then refreshZoneTiny(wgt)
if wgt.zone.w > 380 and wgt.zone.h > 165 then
refreshZoneXLarge(wgt)
elseif wgt.zone.w > 180 and wgt.zone.h > 145 then
refreshZoneLarge(wgt)
elseif wgt.zone.w > 170 and wgt.zone.h > 65 then
refreshZoneMedium(wgt)
elseif wgt.zone.w > 150 and wgt.zone.h > 28 then
refreshZoneSmall(wgt)
elseif wgt.zone.w > 65 and wgt.zone.h > 35 then
refreshZoneTiny(wgt)
end
end
return { name = "BattCheck", options = _options, create = create, update = update, background = background, refresh = refresh }

View file

@ -0,0 +1,157 @@
1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8
1,1,Flysky,V9x9,1,Flip,Light,Pict,Video
1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL
1,3,Flysky,V912,1,BtmBtn,TopBtn
1,4,Flysky,CX20,0,CH5,CH6,CH7
28,0,Flysky_AFHDS2A,PWM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
53,0,Flyzone,FZ-410,0
2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess
2,1,Hubsan,H301,0,RTH,Light,Stab,Video
2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes
41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED
60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8
37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8
37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8
37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8
25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8
3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8
3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8
67,0,FrSkyL,LR12,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
67,1,FrSkyL,LR12_6CH,0,CH5,CH6
15,0,FrSkyX,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
15,1,FrSkyX,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8
15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8
64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
55,1,FrSkyRX,CloneTX,0
39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9
39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9
39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9
57,0,HoTT,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8
68,0,Skyartec,Std,0,CH5,CH6,CH7
7,0,Devo,8CH,0,CH5,CH6,CH7,CH8
7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10
7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
7,3,Devo,6CH,0,CH5,CH6
7,4,Devo,7CH,0,CH5,CH6,CH7
30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8
30,1,WK2x01,WK2401,0
30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro
30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro
30,4,WK2x01,W6HEL,0,Gear,Col,Gyro
30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro
6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9
24,0,Assan,Std,0,CH5,CH6,CH7,CH8
14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2
14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates
14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf
14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video
42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED
42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol
34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess
13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess
12,0,CX10,Green,1,Flip,Rate
12,1,CX10,Blue,1,Flip,Rate,Pict,Video
12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess
12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video
12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip
12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH
33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow
45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH
45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH
45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH
16,0,ESKY,Std,0,Gyro,Pitch
16,1,ESKY,ET4,0,Gyro,Pitch
35,0,ESKY150,4CH,0
35,1,ESKY150,7CH,0,FMode,Aux6,Aux7
20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib
20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib
23,0,FY326,FY326,1,Flip,RTH,HLess,Expert
32,0,GW008,FY326,1,Flip
36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1
36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8
9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib
26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,3,MJXQ,H26D,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,4,MJXQ,E010,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,5,MJXQ,H26WH,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,6,MJXQ,Phoenix,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
17,0,MT99XX,Std,1,Flip,LED,Pict,Video,HLess
17,1,MT99XX,H7,1,Flip,LED,Pict,Video,HLess
17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess
17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess
17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess
44,0,NCC1701,Std,1,Warp
51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess
66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train
29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal
29,1,Q2x2,Q242,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
29,2,Q2x2,Q282,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
31,0,Q303,Q303,1,AltHol,Flip,Pict,Video,HLess,RTH,Gimbal
31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal
31,2,Q303,CX10D,1,Arm,Flip
31,3,Q303,CX10WD,1,Arm,Flip
50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
11,0,SLT,V1,0,Gear,Pitch
11,1,SLT,V2,0,CH5,CH6,CH7,CH8
11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib
11,3,SLT,Q200,0,Rates,n-a,CH7,CH8,Mode,VidOn,VidOff,Calib
11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict
10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess
10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess
5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY
5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD
61,0,Tiger,Std,1,Flip,Light
46,0,V911s,Std,1,Calib
46,1,E119,Std,1,Calib
62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess
8,1,YD717,SkyWlkr,1,Flip,Light,Pict,Video,HLess
8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess
8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess
8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess
65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8
65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8
56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
19,0,Shenqi,Cycle,1
4,1,Hisky,HK310,0,Aux
43,0,Traxxas,6519,0
52,0,ZSX,280,1,Light
48,0,V761,Std,1,Gyro
49,0,KF606,Std,1,Trim
47,0,GD00x,V1,1,Trim,LED,Rate
47,1,GD00x,V2,1,Trim,LED,Rate
58,0,FX816,P38,1

View file

@ -0,0 +1,302 @@
local toolName = "TNS|Multi chan namer|TNE"
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # 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. #
---- # #
---- #########################################################################
local protocol_name = ""
local sub_protocol_name = ""
local bind_ch = 0
local module_conf = {}
local module_pos = "Internal"
local file_ok = 0
local done = 0
local protocol = 0
local sub_protocol = 0
local f_seek = 0
local channel_names={}
local num_search = "Searching"
local function drawScreenTitle(title)
if LCD_W == 480 then
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
lcd.drawText(1, 5, title, MENU_TITLE_COLOR)
else
lcd.drawScreenTitle(title, 0, 0)
end
end
function bitand(a, b)
local result = 0
local bitval = 1
while a > 0 and b > 0 do
if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits
result = result + bitval -- set the current bit
end
bitval = bitval * 2 -- shift left
a = math.floor(a/2) -- shift right
b = math.floor(b/2)
end
return result
end
local function Multi_Draw_LCD(event)
local line = 0
lcd.clear()
drawScreenTitle("Multi channels namer")
--Display settings
local lcd_opt = 0
if LCD_W == 480 then
x_pos = 10
y_pos = 32
y_inc = 20
else
x_pos = 0
y_pos = 9
y_inc = 8
lcd_opt = SMLSIZE
end
--Multi Module detection
if module_conf["Type"] ~= 6 then
if LCD_W == 480 then
lcd.drawText(10,50,"No Multi module configured...", BLINK)
else
--Draw on LCD_W=128
lcd.drawText(2,17,"No Multi module configured...",SMLSIZE)
end
return
else
lcd.drawText(x_pos, y_pos+y_inc*line,module_pos .. " Multi detected.", lcd_opt)
line = line + 1
end
--Channel order
if (ch_order == -1) then
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels order can't be read from Multi...", lcd_opt)
line = line + 1
end
--Can't open file MultiChan.txt
if file_ok == 0 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Can't read MultiChan.txt file...", lcd_opt)
return
end
if ( protocol_name == "" or sub_protocol_name == "" ) and f_seek ~=-1 then
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
if f == nil then return end
lcd.drawText(x_pos, y_pos+y_inc*line,num_search, lcd_opt)
num_search = num_search .. "."
if #num_search > 15 then
num_search = string.sub(num_search,1,9)
end
local proto = 0
local sub_proto = 0
local proto_name = ""
local sub_proto_name = ""
local channels = ""
local nbr_try = 0
local nbr_car = 0
repeat
io.seek(f, f_seek)
local data = io.read(f, 100) -- read 100 characters
if #data ==0 then
f_seek = -1 -- end of file
break
end
proto, sub_proto, proto_name, sub_proto_name, bind_ch, channels = string.match(data,'(%d+),(%d),([%w-_ ]+),([%w-_ ]+),(%d)(.+)')
if proto ~= nil and sub_proto ~= nil and protocol_name ~= nil and sub_protocol_name ~= nil and bind_ch ~= nil then
if tonumber(proto) == protocol and tonumber(sub_proto) == sub_protocol then
protocol_name = proto_name
sub_protocol_name = sub_proto_name
bind_ch = tonumber(bind_ch)
if channels ~= nil then
--extract channel names
nbr_car = string.find(channels, "\r")
if nbr_car == nil then nbr_car = string.find(channels, "\n") end
if nbr_car ~= nil then
channels = string.sub(channels,1,nbr_car-1)
end
local i = 5
for k in string.gmatch(channels, ",([%w-_ ]+)") do
channel_names[i] = k
i = i + 1
end
end
f_seek = -1 -- protocol found
break
end
end
if f_seek ~= -1 then
nbr_car = string.find(data, "\n")
if nbr_car == nil then nbr_car = string.find(data, "\r") end
if nbr_car == nil then
f_seek = -1 -- end of file
break
end
f_seek = f_seek + nbr_car -- seek to next line
nbr_try = nbr_try + 1
end
until nbr_try > 20 or f_seek == -1
io.close(f)
end
if f_seek ~= -1 then
return -- continue searching...
end
--Protocol & Sub_protocol
if protocol_name == "" or sub_protocol_name == "" then
lcd.drawText(x_pos, y_pos+y_inc*line,"Unknown protocol "..tostring(protocol).."/"..tostring(sub_protocol).." ...", lcd_opt)
return
elseif LCD_W > 128 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name .. " / SubProtocol: " .. sub_protocol_name, lcd_opt)
line = line + 1
else
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name, lcd_opt)
line = line + 1
lcd.drawText(x_pos, y_pos+y_inc*line,"SubProtocol: " .. sub_protocol_name, lcd_opt)
line = line + 1
end
text1=""
text2=""
for i,v in ipairs(channel_names) do
if i<=8 then
if i==1 then
text1 = v
else
text1=text1 .. "," .. v
end
else
if i==9 then
text2 = v
else
text2=text2 .. "," .. v
end
end
end
if LCD_W > 128 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels: " .. text1, lcd_opt)
line = line + 1
if text2 ~= "" then
lcd.drawText(x_pos*9, y_pos+y_inc*line,text2, lcd_opt)
line = line + 1
end
end
if event ~= EVT_VIRTUAL_ENTER and done == 0 then
lcd.drawText(x_pos, y_pos+y_inc*line,"<ENT> Save", lcd_opt + INVERS + BLINK)
return
end
lcd.drawText(x_pos, y_pos+y_inc*line,"Setting channel names.", lcd_opt)
line = line + 1
local output, nbr
if done == 0 then
for i,v in ipairs(channel_names) do
output = model.getOutput(i-1)
output["name"] = v
model.setOutput(i-1,output)
nbr = i
end
for i = nbr, 15 do
output = model.getOutput(i)
output["name"] = "n-a"
model.setOutput(i,output)
end
if bind_ch == 1 then
output = model.getOutput(15)
output["name"] = "BindCH"
model.setOutput(15,output)
end
done = 1
end
lcd.drawText(x_pos, y_pos+y_inc*line,"Done!", lcd_opt)
line = line + 1
end
-- Init
local function Multi_Init()
module_conf = model.getModule(0)
if module_conf["Type"] ~= 6 then
module_pos = "External"
module_conf = model.getModule(1)
if module_conf["Type"] ~= 6 then
return
end
end
protocol = module_conf["protocol"]
sub_protocol = module_conf["subProtocol"]
--Exceptions on first 4 channels...
local stick_names = { "Rud", "Ele", "Thr", "Ail" }
if ( protocol == 4 and sub_protocol ==1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX
stick_names[2] = "n-a"
stick_names[4] = "n-a"
elseif protocol == 43 then -- Traxxas
stick_names[2] = "Aux4"
stick_names[4] = "Aux3"
elseif protocol == 48 then -- V761
stick_names[4] = "n-a"
elseif protocol == 47 or protocol == 49 or protocol == 58 then -- GD00x, KF606, FX816
stick_names[1] = "n-a"
stick_names[2] = "n-a"
end
--Determine fist 4 channels order
local ch_order=module_conf["channelsOrder"]
if (ch_order == -1) then
channel_names[1] = stick_names[defaultChannel(0)+1]
channel_names[2] = stick_names[defaultChannel(1)+1]
channel_names[3] = stick_names[defaultChannel(2)+1]
channel_names[4] = stick_names[defaultChannel(3)+1]
else
channel_names[bitand(ch_order,3)+1] = stick_names[4]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[2]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[3]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[1]
end
--Check MultiChan.txt
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
if f == nil then return end
file_ok = 1
io.close(f)
end
-- Main
local function Multi_Run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
else
Multi_Draw_LCD(event)
if event == EVT_VIRTUAL_EXIT then
return 2
end
end
return 0
end
return { init=Multi_Init, run=Multi_Run }

View file

@ -0,0 +1,157 @@
1,0,Flysky,Flysky,0,CH5,CH6,CH7,CH8
1,1,Flysky,V9x9,1,Flip,Light,Pict,Video
1,2,Flysky,V6x6,1,Flip,Light,Pict,Video,HLess,RTH,XCAL,YCAL
1,3,Flysky,V912,1,BtmBtn,TopBtn
1,4,Flysky,CX20,0,CH5,CH6,CH7
28,0,Flysky_AFHDS2A,PWM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,1,Flysky_AFHDS2A,PPM_IBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,2,Flysky_AFHDS2A,PWM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
28,3,Flysky_AFHDS2A,PPM_SBUS,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
53,0,Flyzone,FZ-410,0
2,0,Hubsan,H107,1,Flip,Light,Pict,Video,HLess
2,1,Hubsan,H301,0,RTH,Light,Stab,Video
2,2,Hubsan,H501,0,RTH,Light,Pict,Video,HLess,GPS_H,ALT_H,Flip,FModes
41,0,Bugs,3-6-8,0,Arm,Angle,Flip,Pict,Video,LED
60,0,Pelikan,PRO_V4,0,CH5,CH6,CH7,CH8
37,0,Corona,COR_V1,0,CH5,CH6,CH7,CH8
37,1,Corona,COR_V2,0,CH5,CH6,CH7,CH8
37,2,Corona,FD_V3,0,CH5,CH6,CH7,CH8
25,0,FrSkyV,V8,0,CH5,CH6,CH7,CH8
3,0,FrSkyD,D8,0,CH5,CH6,CH7,CH8
3,0,FrSkyD,D8Cloned,0,CH5,CH6,CH7,CH8
67,0,FrSkyL,LR12,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
67,1,FrSkyL,LR12_6CH,0,CH5,CH6
15,0,FrSkyX,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
15,1,FrSkyX,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
15,2,FrSkyX,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
15,3,FrSkyX,D16_8CH_LBT,0,CH5,CH6,CH7,CH8
15,4,FrSkyX,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,0,FrSkyX2,D16_FCC,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,1,FrSkyX2,D16_8CH_FCC,0,CH5,CH6,CH7,CH8
64,2,FrSkyX2,D16_LBT,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
64,3,FrSkyX2,D16_8CH_LBT,1,CH5,CH6,CH7,CH8
64,4,FrSkyX2,D16Cloned,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
55,0,FrSkyRX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
55,1,FrSkyRX,CloneTX,0
39,0,Hitec,Opt_Fw,0,CH5,CH6,CH7,CH8,CH9
39,1,Hitec,Opt_Hub,0,CH5,CH6,CH7,CH8,CH9
39,2,Hitec,Minima,0,CH5,CH6,CH7,CH8,CH9
57,0,HoTT,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
21,0,SFHSS,Std,0,CH5,CH6,CH7,CH8
68,0,Skyartec,Std,0,CH5,CH6,CH7
7,0,Devo,8CH,0,CH5,CH6,CH7,CH8
7,1,Devo,10CH,0,CH5,CH6,CH7,CH8,CH9,CH10
7,2,Devo,12CH,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
7,3,Devo,6CH,0,CH5,CH6
7,4,Devo,7CH,0,CH5,CH6,CH7
30,0,WK2x01,WK2801,0,CH5,CH6,CH7,CH8
30,1,WK2x01,WK2401,0
30,2,WK2x01,W6_5_1,0,Gear,Dis,Gyro
30,3,WK2x01,W6_6_1,0,Gear,Col,Gyro
30,4,WK2x01,W6HEL,0,Gear,Col,Gyro
30,5,WK2x01,W6HEL_I,0,Gear,Col,Gyro
6,0,DSM,2_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,1,DSM,2_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,2,DSM,X_22,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
6,3,DSM,X_11,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,n-a,ThKill
22,0,J6Pro,Std,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12
22,0,WFLY,WFR0xS,0,CH5,CH6,CH7,CH8,CH9
24,0,Assan,Std,0,CH5,CH6,CH7,CH8
14,0,Bayang,Std,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,n-a,n-a,AnAux1,AnAux2
14,1,Bayang,H8S3D,1,Flip,RTH,Pict,Video,HLess,Invert,Rates
14,2,Bayang,X16_AH,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf
14,3,Bayang,IRDRONE,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
14,4,Bayang,DHD_D4,1,Flip,RTH,Pict,Video,HLess,Invert,Rates,TakeOf,EmStop
59,0,BayangRX,RX,1,AnAux1,AnAux2,Flip,RTH,Pict,Video
42,0,BugsMini,Mini,0,Arm,Angle,Flip,Pict,Video,LED
42,1,BugsMini,3H,0,Arm,Angle,Flip,Pict,Video,LED,AltHol
34,0,Cabell,V3,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
34,1,Cabell,V3Telem,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
13,0,CG023,Std,1,Flip,Light,Pict,Video,HLess
13,1,CG023,YD829,1,Flip,n-a,Pict,Video,HLess
12,0,CX10,Green,1,Flip,Rate
12,1,CX10,Blue,1,Flip,Rate,Pict,Video
12,2,CX10,DM007,1,Flip,Mode,Pict,Video,HLess
12,4,CX10,JC3015_1,1,Flip,Mode,Pict,Video
12,5,CX10,JC3015_2,1,Flip,Mode,LED,DFlip
12,6,CX10,MK33041,1,Flip,Mode,Pict,Video,HLess,RTH
33,0,DM022,Std,1,Flip,LED,Cam1,Cam2,HLess,RTH,RLow
45,0,E01X,E012,1,n-a,Flip,n-a,HLess,RTH
45,1,E01X,E015,1,Arm,Flip,LED,HLess,RTH
45,2,E01X,E016H,1,Stop,Flip,n-a,HLess,RTH
16,0,ESKY,Std,0,Gyro,Pitch
16,1,ESKY,ET4,0,Gyro,Pitch
35,0,ESKY150,4CH,0
35,1,ESKY150,7CH,0,FMode,Aux6,Aux7
20,0,FY326,FY326,1,Flip,RTH,HLess,Expert,Calib
20,1,FY326,FY319,1,Flip,RTH,HLess,Expert,Calib
23,0,FY326,FY326,1,Flip,RTH,HLess,Expert
32,0,GW008,FY326,1,Flip
36,0,H8_3D,Std,1,Flip,Light,Pict,Video,RTH,FlMode,Cal1
36,1,H8_3D,H20H,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
36,2,H8_3D,H20,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
36,3,H8_3D,H30,1,Flip,Light,Pict,Video,Opt1,Opt2,Cal1,Cal2,Gimbal
4,0,Hisky,Std,0,Gear,Pitch,Gyro,CH8
9,0,KN,WLToys,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
9,1,KN,Feilun,0,DRate,THold,IdleUp,Gyro,Ttrim,Atrim,Etrim
26,0,Hontai,Std,1,Flip,LED,Pict,Video,HLess,RTH,Calib
26,1,Hontai,JJRCX1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,2,Hontai,X5C1,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
26,3,Hontai,FQ777_951,1,Flip,Arm,Pict,Video,HLess,RTH,Calib
18,0,MJXQ,WHL08,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,1,MJXQ,X600,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,2,MJXQ,X800,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,3,MJXQ,H26D,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,4,MJXQ,E010,1,Flip,LED,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,5,MJXQ,H26WH,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
18,6,MJXQ,Phoenix,1,Flip,Arm,Pict,Video,HLess,RTH,AuFlip,Pan,Tilt,Rate
17,0,MT99XX,Std,1,Flip,LED,Pict,Video,HLess
17,1,MT99XX,H7,1,Flip,LED,Pict,Video,HLess
17,2,MT99XX,YZ,1,Flip,LED,Pict,Video,HLess
17,3,MT99XX,LS,1,Flip,Invert,Pict,Video,HLess
17,4,MT99XX,FY805,1,Flip,n-a,n-a,n-a,HLess
44,0,NCC1701,Std,1,Warp
51,0,Potensic,A20,1,TakLan,Emerg,Mode,HLess
66,0,Propel,74-Z,1,LEDs,RollCW,RolCCW,Fire,Weapon,Calib,AltHol,TakeOf,Land,Train
29,0,Q2x2,Q222,1,Flip,LED,Mod2,Mod1,HLess,RTH,XCal,YCal
29,1,Q2x2,Q242,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
29,2,Q2x2,Q282,1,Flip,LED,Pict,Video,HLess,RTH,XCal,YCal
31,0,Q303,Q303,1,AltHol,Flip,Pict,Video,HLess,RTH,Gimbal
31,1,Q303,C35,1,Arm,VTX,Pict,Video,n-a,RTH,Gimbal
31,2,Q303,CX10D,1,Arm,Flip
31,3,Q303,CX10WD,1,Arm,Flip
50,0,Redpine,Fast,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
50,1,Redpine,Slow,0,sCH5,sCH6,sCH7,sCH8,sCH9,sCH10,sCH11,sCH12,sCH13,sCH14,sCH15,sCH16
11,0,SLT,V1,0,Gear,Pitch
11,1,SLT,V2,0,CH5,CH6,CH7,CH8
11,2,SLT,Q100,0,Rates,n-a,CH7,CH8,Mode,Flip,n-a,n-a,Calib
11,3,SLT,Q200,0,Rates,n-a,CH7,CH8,Mode,VidOn,VidOff,Calib
11,4,SLT,MR100,0,Rates,n-a,CH7,CH8,Mode,Flip,Video,Pict
10,0,Symax,Std,1,Flip,Rates,Pict,Video,HLess
10,1,Symax,X5C,1,Flip,Rates,Pict,Video,HLess
5,0,V2x2,Std,1,Flip,Light,Pict,Video,HLess,CalX,CalY
5,1,V2x2,JXD506,1,Flip,Light,Pict,Video,HLess,StaSto,Emerg,Cam_UD
61,0,Tiger,Std,1,Flip,Light
46,0,V911s,Std,1,Calib
46,1,E119,Std,1,Calib
62,0,XK,X450,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
62,1,XK,X420,1,FMode,TakeOf,Emerg,3D_6G,Pict,Video
8,0,YD717,Std,1,Flip,Light,Pict,Video,HLess
8,1,YD717,SkyWlkr,1,Flip,Light,Pict,Video,HLess
8,2,YD717,Simax4,1,Flip,Light,Pict,Video,HLess
8,3,YD717,XinXun,1,Flip,Light,Pict,Video,HLess
8,4,YD717,NiHui,1,Flip,Light,Pict,Video,HLess
65,0,FrSkyR9,R9_915,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
65,1,FrSkyR9,R9_868,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14,CH15,CH16
65,2,FrSkyR9,R9_915_8CH,0,CH5,CH6,CH7,CH8
65,3,FrSkyR9,R9_968_8CH,0,CH5,CH6,CH7,CH8
56,0,Flysky2A_RX,RX,0,CH5,CH6,CH7,CH8,CH9,CH10,CH11,CH12,CH13,CH14
19,0,Shenqi,Cycle,1
4,1,Hisky,HK310,0,Aux
43,0,Traxxas,6519,0
52,0,ZSX,280,1,Light
48,0,V761,Std,1,Gyro
49,0,KF606,Std,1,Trim
47,0,GD00x,V1,1,Trim,LED,Rate
47,1,GD00x,V2,1,Trim,LED,Rate
58,0,FX816,P38,1

View file

@ -0,0 +1,302 @@
local toolName = "TNS|Multi chan namer|TNE"
---- #########################################################################
---- # #
---- # Copyright (C) OpenTX #
-----# #
---- # 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. #
---- # #
---- #########################################################################
local protocol_name = ""
local sub_protocol_name = ""
local bind_ch = 0
local module_conf = {}
local module_pos = "Internal"
local file_ok = 0
local done = 0
local protocol = 0
local sub_protocol = 0
local f_seek = 0
local channel_names={}
local num_search = "Searching"
local function drawScreenTitle(title)
if LCD_W == 480 then
lcd.drawFilledRectangle(0, 0, LCD_W, 30, TITLE_BGCOLOR)
lcd.drawText(1, 5, title, MENU_TITLE_COLOR)
else
lcd.drawScreenTitle(title, 0, 0)
end
end
function bitand(a, b)
local result = 0
local bitval = 1
while a > 0 and b > 0 do
if a % 2 == 1 and b % 2 == 1 then -- test the rightmost bits
result = result + bitval -- set the current bit
end
bitval = bitval * 2 -- shift left
a = math.floor(a/2) -- shift right
b = math.floor(b/2)
end
return result
end
local function Multi_Draw_LCD(event)
local line = 0
lcd.clear()
drawScreenTitle("Multi channels namer")
--Display settings
local lcd_opt = 0
if LCD_W == 480 then
x_pos = 10
y_pos = 32
y_inc = 20
else
x_pos = 0
y_pos = 9
y_inc = 8
lcd_opt = SMLSIZE
end
--Multi Module detection
if module_conf["Type"] ~= 6 then
if LCD_W == 480 then
lcd.drawText(10,50,"No Multi module configured...", BLINK)
else
--Draw on LCD_W=128
lcd.drawText(2,17,"No Multi module configured...",SMLSIZE)
end
return
else
lcd.drawText(x_pos, y_pos+y_inc*line,module_pos .. " Multi detected.", lcd_opt)
line = line + 1
end
--Channel order
if (ch_order == -1) then
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels order can't be read from Multi...", lcd_opt)
line = line + 1
end
--Can't open file MultiChan.txt
if file_ok == 0 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Can't read MultiChan.txt file...", lcd_opt)
return
end
if ( protocol_name == "" or sub_protocol_name == "" ) and f_seek ~=-1 then
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
if f == nil then return end
lcd.drawText(x_pos, y_pos+y_inc*line,num_search, lcd_opt)
num_search = num_search .. "."
if #num_search > 15 then
num_search = string.sub(num_search,1,9)
end
local proto = 0
local sub_proto = 0
local proto_name = ""
local sub_proto_name = ""
local channels = ""
local nbr_try = 0
local nbr_car = 0
repeat
io.seek(f, f_seek)
local data = io.read(f, 100) -- read 100 characters
if #data ==0 then
f_seek = -1 -- end of file
break
end
proto, sub_proto, proto_name, sub_proto_name, bind_ch, channels = string.match(data,'(%d+),(%d),([%w-_ ]+),([%w-_ ]+),(%d)(.+)')
if proto ~= nil and sub_proto ~= nil and protocol_name ~= nil and sub_protocol_name ~= nil and bind_ch ~= nil then
if tonumber(proto) == protocol and tonumber(sub_proto) == sub_protocol then
protocol_name = proto_name
sub_protocol_name = sub_proto_name
bind_ch = tonumber(bind_ch)
if channels ~= nil then
--extract channel names
nbr_car = string.find(channels, "\r")
if nbr_car == nil then nbr_car = string.find(channels, "\n") end
if nbr_car ~= nil then
channels = string.sub(channels,1,nbr_car-1)
end
local i = 5
for k in string.gmatch(channels, ",([%w-_ ]+)") do
channel_names[i] = k
i = i + 1
end
end
f_seek = -1 -- protocol found
break
end
end
if f_seek ~= -1 then
nbr_car = string.find(data, "\n")
if nbr_car == nil then nbr_car = string.find(data, "\r") end
if nbr_car == nil then
f_seek = -1 -- end of file
break
end
f_seek = f_seek + nbr_car -- seek to next line
nbr_try = nbr_try + 1
end
until nbr_try > 20 or f_seek == -1
io.close(f)
end
if f_seek ~= -1 then
return -- continue searching...
end
--Protocol & Sub_protocol
if protocol_name == "" or sub_protocol_name == "" then
lcd.drawText(x_pos, y_pos+y_inc*line,"Unknown protocol "..tostring(protocol).."/"..tostring(sub_protocol).." ...", lcd_opt)
return
elseif LCD_W > 128 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name .. " / SubProtocol: " .. sub_protocol_name, lcd_opt)
line = line + 1
else
lcd.drawText(x_pos, y_pos+y_inc*line,"Protocol: " .. protocol_name, lcd_opt)
line = line + 1
lcd.drawText(x_pos, y_pos+y_inc*line,"SubProtocol: " .. sub_protocol_name, lcd_opt)
line = line + 1
end
text1=""
text2=""
for i,v in ipairs(channel_names) do
if i<=8 then
if i==1 then
text1 = v
else
text1=text1 .. "," .. v
end
else
if i==9 then
text2 = v
else
text2=text2 .. "," .. v
end
end
end
if LCD_W > 128 then
lcd.drawText(x_pos, y_pos+y_inc*line,"Channels: " .. text1, lcd_opt)
line = line + 1
if text2 ~= "" then
lcd.drawText(x_pos*9, y_pos+y_inc*line,text2, lcd_opt)
line = line + 1
end
end
if event ~= EVT_VIRTUAL_ENTER and done == 0 then
lcd.drawText(x_pos, y_pos+y_inc*line,"<ENT> Save", lcd_opt + INVERS + BLINK)
return
end
lcd.drawText(x_pos, y_pos+y_inc*line,"Setting channel names.", lcd_opt)
line = line + 1
local output, nbr
if done == 0 then
for i,v in ipairs(channel_names) do
output = model.getOutput(i-1)
output["name"] = v
model.setOutput(i-1,output)
nbr = i
end
for i = nbr, 15 do
output = model.getOutput(i)
output["name"] = "n-a"
model.setOutput(i,output)
end
if bind_ch == 1 then
output = model.getOutput(15)
output["name"] = "BindCH"
model.setOutput(15,output)
end
done = 1
end
lcd.drawText(x_pos, y_pos+y_inc*line,"Done!", lcd_opt)
line = line + 1
end
-- Init
local function Multi_Init()
module_conf = model.getModule(0)
if module_conf["Type"] ~= 6 then
module_pos = "External"
module_conf = model.getModule(1)
if module_conf["Type"] ~= 6 then
return
end
end
protocol = module_conf["protocol"]
sub_protocol = module_conf["subProtocol"]
--Exceptions on first 4 channels...
local stick_names = { "Rud", "Ele", "Thr", "Ail" }
if ( protocol == 4 and sub_protocol ==1 ) or protocol == 19 or protocol == 52 then -- Hisky/HK310, Shenqi, ZSX
stick_names[2] = "n-a"
stick_names[4] = "n-a"
elseif protocol == 43 then -- Traxxas
stick_names[2] = "Aux4"
stick_names[4] = "Aux3"
elseif protocol == 48 then -- V761
stick_names[4] = "n-a"
elseif protocol == 47 or protocol == 49 or protocol == 58 then -- GD00x, KF606, FX816
stick_names[1] = "n-a"
stick_names[2] = "n-a"
end
--Determine fist 4 channels order
local ch_order=module_conf["channelsOrder"]
if (ch_order == -1) then
channel_names[1] = stick_names[defaultChannel(0)+1]
channel_names[2] = stick_names[defaultChannel(1)+1]
channel_names[3] = stick_names[defaultChannel(2)+1]
channel_names[4] = stick_names[defaultChannel(3)+1]
else
channel_names[bitand(ch_order,3)+1] = stick_names[4]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[2]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[3]
ch_order = math.floor(ch_order/4)
channel_names[bitand(ch_order,3)+1] = stick_names[1]
end
--Check MultiChan.txt
local f = io.open("/SCRIPTS/TOOLS/MultiChan.txt", "r")
if f == nil then return end
file_ok = 1
io.close(f)
end
-- Main
local function Multi_Run(event)
if event == nil then
error("Cannot be run as a model script!")
return 2
else
Multi_Draw_LCD(event)
if event == EVT_VIRTUAL_EXIT then
return 2
end
end
return 0
end
return { init=Multi_Init, run=Multi_Run }

View file

@ -381,9 +381,12 @@ endif()
add_definitions(-DPOPUP_LEVEL=${POPUP_LEVEL})
if(BOOTLOADER)
add_definitions(-DBOOTLOADER)
endif(BOOTLOADER)
if(INTERNAL_MODULE_MULTI)
set(DEFAULT_TEMPLATE_SETUP 21 CACHE STRING "")
else()
set(DEFAULT_TEMPLATE_SETUP 0 CACHE STRING "")
endif()
add_definitions(-DDEFAULT_TEMPLATE_SETUP=${DEFAULT_TEMPLATE_SETUP})
set(SRC
${SRC}
@ -444,7 +447,6 @@ endif()
set(SRC ${SRC} ${FIRMWARE_SRC})
##### firmware target #####
option(FIRMWARE_TARGET "Configure Firmware target (can be turned of for compiling Companion only)" ON)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 601 B

After

Width:  |  Height:  |  Size: 310 B

Before After
Before After

BIN
radio/src/bitmaps/480x272/mask_txbat_charging.png Executable file → Normal file

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 553 B

Before After
Before After

View file

@ -315,7 +315,7 @@ void Bluetooth::wakeup(void)
uint8_t len = ZLEN(g_eeGeneral.bluetoothName);
if (len > 0) {
for (int i = 0; i < len; i++) {
*cur++ = char2lower(zchar2char(g_eeGeneral.bluetoothName[i]));
*cur++ = char2lower(g_eeGeneral.bluetoothName[i]);
}
*cur = '\0';
}
@ -418,7 +418,7 @@ void Bluetooth::wakeup()
uint8_t len = ZLEN(g_eeGeneral.bluetoothName);
if (len > 0) {
for (int i = 0; i < len; i++) {
*cur++ = char2lower(zchar2char(g_eeGeneral.bluetoothName[i]));
*cur++ = char2lower(g_eeGeneral.bluetoothName[i]);
}
*cur = '\0';
}

View file

@ -22,8 +22,6 @@
#define _BUZZER_H_
#if defined(BUZZER)
extern uint8_t g_beepCnt;
extern uint8_t beepAgain;
extern uint8_t beepAgainOrig;
extern uint8_t beepOn;
extern bool warble;

View file

@ -20,7 +20,6 @@
#include "opentx.h"
uint8_t s_curveChan;
int8_t * curveEnd[MAX_CURVES];
void loadCurves()
@ -120,7 +119,7 @@ bool isCurveUsed(uint8_t index)
}
#define CUSTOM_POINT_X(points, count, idx) ((idx)==0 ? -100 : (((idx)==(count)-1) ? 100 : points[(count)+(idx)-1]))
int32_t compute_tangent(CurveHeader * crv, int8_t * points, int i)
int32_t compute_tangent(CurveHeader * crv, const int8_t * points, int i)
{
int32_t m=0;
uint8_t num_points = crv->points + 5;
@ -240,7 +239,7 @@ int intpol(int x, uint8_t idx) // -100, -75, -50, -25, 0 ,25 ,50, 75, 100
int8_t * points = curveAddress(idx);
uint8_t count = crv.points+5;
bool custom = (crv.type == CURVE_TYPE_CUSTOM);
int16_t erg = 0;
int16_t erg;
x += RESXu;
@ -339,11 +338,6 @@ int applyCustomCurve(int x, uint8_t idx)
return intpol(x, idx);
}
point_t getPoint(uint8_t index)
{
return getPoint(s_curveChan, index);
}
point_t getPoint(uint8_t curveIndex, uint8_t index)
{
point_t result = {0, 0};
@ -363,7 +357,14 @@ point_t getPoint(uint8_t curveIndex, uint8_t index)
return result;
}
#if !defined(COLORLCD)
point_t getPoint(uint8_t index)
{
return getPoint(s_currIdxSubMenu, index);
}
int applyCurrentCurve(int x)
{
return applyCustomCurve(x, s_curveChan);
return applyCustomCurve(x, s_currIdxSubMenu);
}
#endif

View file

@ -208,6 +208,16 @@ enum TrainerMode {
};
#endif
#if defined(RADIO_FAMILY_T16) || (defined(RADIO_T12) && defined(INTERNAL_MODULE_MULTI)) || defined(ALLOW_TRAINER_MULTI)
#define TRAINER_MODE_MAX() TRAINER_MODE_MULTI
#elif defined(BLUETOOTH)
#define TRAINER_MODE_MAX() TRAINER_MODE_SLAVE_BLUETOOTH
#elif defined(PCBX7) || defined(PCBXLITE)
#define TRAINER_MODE_MAX() TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE
#else
#define TRAINER_MODE_MAX() HAS_WIRELESS_TRAINER_HARDWARE() ? TRAINER_MODE_MASTER_BATTERY_COMPARTMENT : TRAINER_MODE_MASTER_CPPM_EXTERNAL_MODULE
#endif
#if defined(HARDWARE_INTERNAL_MODULE)
#define IS_INTERNAL_MODULE_ENABLED() (g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE)
#else

View file

@ -26,6 +26,7 @@
#include "dataconstants.h"
#include "definitions.h"
#include "opentx_types.h"
#include "globals.h"
#if defined(PCBTARANIS)
#define N_TARANIS_FIELD(x)
@ -331,10 +332,10 @@ PACK(struct TelemetrySensor {
NOBACKUP(uint16_t persistentValue);
} NAME(id1) FUNC(select_id1);
union {
PACK(struct {
NOBACKUP(PACK(struct {
uint8_t physID:5;
uint8_t rxIndex:3; // 1 bit for module index, 2 bits for receiver index
}) frskyInstance;
}) frskyInstance);
uint8_t instance;
NOBACKUP(uint8_t formula);
} NAME(id2) FUNC(select_id2);
@ -513,8 +514,6 @@ PACK(struct ModuleData {
* Model structure
*/
typedef uint16_t BeepANACenter;
#if LEN_BITMAP_NAME > 0
#define MODEL_HEADER_BITMAP_FIELD NOBACKUP(char bitmap[LEN_BITMAP_NAME]);
#else
@ -697,7 +696,8 @@ PACK(struct TrainerData {
#if defined(PCBHORUS) || defined(PCBNV14)
#define EXTRA_GENERAL_FIELDS \
NOBACKUP(uint8_t auxSerialMode); \
NOBACKUP(uint8_t auxSerialMode:4); \
NOBACKUP(uint8_t aux2SerialMode:4); \
swconfig_t switchConfig ARRAY(2,struct_switchConfig,nullptr); \
uint16_t potsConfig ARRAY(2,struct_potConfig,nullptr); /* two bits per pot */ \
uint8_t slidersConfig ARRAY(1,struct_sliderConfig,nullptr); /* 1 bit per slider */ \

View file

@ -37,6 +37,7 @@
extern volatile uint32_t g_tmr10ms;
uint8_t auxSerialTracesEnabled();
uint8_t aux2SerialTracesEnabled();
#if defined(SIMU)
typedef void (*traceCallbackFunc)(const char * text);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 B

After

Width:  |  Height:  |  Size: 143 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 155 B

After

Width:  |  Height:  |  Size: 150 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 161 B

After

Width:  |  Height:  |  Size: 190 B

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 174 B

After

Width:  |  Height:  |  Size: 250 B

Before After
Before After

Some files were not shown because too many files have changed in this diff Show more