1
0
Fork 0
mirror of https://github.com/opentx/opentx.git synced 2025-07-15 12:25:12 +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_SUFFIX $ENV{OPENTX_VERSION_SUFFIX})
set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR}) set(VERSION_FAMILY ${VERSION_MAJOR}.${VERSION_MINOR})
set(VERSION ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_REVISION}${VERSION_SUFFIX}) 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}) set(SDCARD_VERSION ${VERSION_MAJOR}.${VERSION_MINOR}V${SDCARD_REVISION})
cmake_minimum_required(VERSION 2.8) cmake_minimum_required(VERSION 2.8)

View file

@ -11,6 +11,7 @@ Arne Schwabe (schwabe)
[Translators] [Translators]
[fr] Sebastien Charpentier (LapinFou) [fr] Sebastien Charpentier (LapinFou)
[es] Daniel Gorbea (dgatf)
[Other contributors] [Other contributors]
Rob Thomson Rob Thomson
@ -422,7 +423,6 @@ Pablo Gomez Martinez
Alan Thoren Alan Thoren
Jack Clodfelter Jack Clodfelter
William Simon William Simon
James Houck
Peter Fithern Peter Fithern
Thomas Svedman Thomas Svedman
TechFixPros TechFixPros
@ -687,7 +687,6 @@ Shaun Currier
Danilo Schiara Danilo Schiara
John Hennig John Hennig
RK Custom Homes RK Custom Homes
James Houck
Piet Teekens Piet Teekens
Tommy Widenflycht Tommy Widenflycht
Johan Petrus Theophilus Johan Petrus Theophilus
@ -1707,3 +1706,30 @@ Fabrice Debonnet
James Laver James Laver
Kevin Bowens Kevin Bowens
David Frech 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 ## OpenTX 2.3 Branch
[![Travis build Status](https://travis-ci.org/opentx/opentx.svg?branch=2.3)](https://travis-ci.org/opentx/opentx) [![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) [![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. The ongoing development on 2.3.x is done in this branch.

View file

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

View file

@ -73,6 +73,7 @@ void AppPreferencesDialog::accept()
g.OpenTxBranch(AppData::DownloadBranchType(ui->OpenTxBranch->currentIndex())); g.OpenTxBranch(AppData::DownloadBranchType(ui->OpenTxBranch->currentIndex()));
g.autoCheckFw(ui->autoCheckFirmware->isChecked()); g.autoCheckFw(ui->autoCheckFirmware->isChecked());
g.showSplash(ui->showSplash->isChecked()); g.showSplash(ui->showSplash->isChecked());
g.promptProfile(ui->chkPromptProfile->isChecked());
g.simuSW(ui->simuSW->isChecked()); g.simuSW(ui->simuSW->isChecked());
g.removeModelSlots(ui->opt_removeBlankSlots->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); 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->autoCheckCompanion->setChecked(g.autoCheckApp());
ui->autoCheckFirmware->setChecked(g.autoCheckFw()); ui->autoCheckFirmware->setChecked(g.autoCheckFw());
ui->showSplash->setChecked(g.showSplash()); ui->showSplash->setChecked(g.showSplash());
ui->chkPromptProfile->setChecked(g.promptProfile());
ui->historySize->setValue(g.historySize()); ui->historySize->setValue(g.historySize());
ui->backLightColor->setCurrentIndex(g.backLight()); ui->backLightColor->setCurrentIndex(g.backLight());
ui->volumeGain->setValue(profile.volumeGain() / 10.0); 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) #if defined(JOYSTICKS)
void AppPreferencesDialog::on_joystickChkB_clicked() { void AppPreferencesDialog::on_joystickChkB_clicked() {
if (ui->joystickChkB->isChecked()) { if (ui->joystickChkB->isChecked()) {

View file

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

View file

@ -6,7 +6,7 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>741</width> <width>864</width>
<height>668</height> <height>668</height>
</rect> </rect>
</property> </property>
@ -45,7 +45,7 @@
</sizepolicy> </sizepolicy>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>1</number> <number>0</number>
</property> </property>
<widget class="QWidget" name="profileTab"> <widget class="QWidget" name="profileTab">
<attribute name="title"> <attribute name="title">
@ -61,22 +61,40 @@
</sizepolicy> </sizepolicy>
</property> </property>
<layout class="QGridLayout" name="optionsLayout"> <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"> <property name="horizontalSpacing">
<number>5</number> <number>5</number>
</property> </property>
<property name="verticalSpacing"> <property name="verticalSpacing">
<number>4</number> <number>4</number>
</property> </property>
<property name="margin">
<number>0</number>
</property>
</layout> </layout>
</widget> </widget>
</item> </item>
<item row="7" column="0" colspan="4"> <item row="7" column="0" colspan="4">
<widget class="QWidget" name="widget_splashImage" native="true"> <widget class="QWidget" name="widget_splashImage" native="true">
<layout class="QGridLayout" name="gridLayout_5"> <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> <number>0</number>
</property> </property>
<property name="spacing"> <property name="spacing">
@ -704,29 +722,90 @@ Mode 4:
<property name="spacing"> <property name="spacing">
<number>6</number> <number>6</number>
</property> </property>
<item row="7" column="1"> <item row="15" column="1">
<layout class="QVBoxLayout" name="updatesLayout"> <widget class="QCheckBox" name="backupEnable">
<item> <property name="sizePolicy">
<widget class="QCheckBox" name="autoCheckFirmware"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<property name="text"> <horstretch>0</horstretch>
<string>Automatic check for OpenTX firmware updates</string> <verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="checked"> <property name="text">
<bool>true</bool> <string>Enable automatic backup before writing firmware</string>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item row="22" column="0">
<widget class="QCheckBox" name="autoCheckCompanion"> <widget class="QLabel" name="ge_label">
<property name="text"> <property name="sizePolicy">
<string>Automatic check for Companion updates</string> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property> </property>
<property name="checked"> <property name="text">
<bool>true</bool> <string>Google Earth Executable</string>
</property> </property>
</widget> </widget>
</item> </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>
<item row="3" column="1"> <item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_10" stretch="0,1"> <layout class="QHBoxLayout" name="horizontalLayout_10" stretch="0,1">
@ -764,120 +843,27 @@ Mode 4:
</item> </item>
</layout> </layout>
</item> </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"> <item row="6" column="1">
<widget class="QCheckBox" name="showSplash"> <widget class="QCheckBox" name="showSplash">
<property name="text"> <property name="text">
<string>Show splash screen when Companion starts</string> <string>Show splash screen</string>
</property> </property>
<property name="checked"> <property name="checked">
<bool>true</bool> <bool>true</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item row="13" column="0"> <item row="22" column="1">
<widget class="QLabel" name="label_17"> <layout class="QHBoxLayout" name="horizontalLayout_9">
<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> <item>
<widget class="QLineEdit" name="backupPath"> <widget class="QLineEdit" name="ge_lineedit">
<property name="readOnly"> <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> <bool>true</bool>
</property> </property>
<property name="readOnly">
<bool>false</bool>
</property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="libraryPathButton"> <widget class="QPushButton" name="ge_pathButton">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
@ -885,49 +871,13 @@ Mode 4:
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>Select Folder</string> <string>Select Executable</string>
</property> </property>
</widget> </widget>
</item> </item>
</layout> </layout>
</item> </item>
<item row="10" column="1"> <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"> <layout class="QHBoxLayout" name="horizontalLayout_2">
<item> <item>
<widget class="QRadioButton" name="opt_newMdl_useWizard"> <widget class="QRadioButton" name="opt_newMdl_useWizard">
@ -962,90 +912,22 @@ Mode 4:
</layout> </layout>
</item> </item>
<item row="14" column="1"> <item row="14" column="1">
<widget class="QCheckBox" name="backupEnable"> <layout class="QHBoxLayout" name="horizontalLayout_8">
<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>
<item> <item>
<property name="text"> <widget class="QLineEdit" name="backupPath">
<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">
<property name="readOnly"> <property name="readOnly">
<bool>true</bool> <bool>false</bool>
</property> </property>
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QPushButton" name="ge_pathButton"> <widget class="QPushButton" name="backupPathButton">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed"> <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </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"> <property name="text">
<string>Select Folder</string> <string>Select Folder</string>
</property> </property>
@ -1053,14 +935,56 @@ Mode 4:
</item> </item>
</layout> </layout>
</item> </item>
<item row="22" column="0" colspan="2"> <item row="3" column="0">
<widget class="Line" name="line_4"> <widget class="QLabel" name="label_5">
<property name="orientation"> <property name="sizePolicy">
<enum>Qt::Horizontal</enum> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remember</string>
</property> </property>
</widget> </widget>
</item> </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"> <widget class="QLabel" name="label_9">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -1073,33 +997,7 @@ Mode 4:
</property> </property>
</widget> </widget>
</item> </item>
<item row="17" column="0"> <item row="24" column="1">
<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">
<layout class="QHBoxLayout" name="horizontalLayout_3"> <layout class="QHBoxLayout" name="horizontalLayout_3">
<item> <item>
<widget class="QCheckBox" name="opt_appDebugLog"> <widget class="QCheckBox" name="opt_appDebugLog">
@ -1123,36 +1021,163 @@ Mode 4:
</item> </item>
</layout> </layout>
</item> </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"> <widget class="QLabel" name="label_18">
<property name="text"> <property name="text">
<string>Release channel</string> <string>Release channel</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="4" column="0" rowspan="2" colspan="2">
<widget class="QComboBox" name="OpenTxBranch"> <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"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<item>
<property name="text"> <property name="text">
<string>Releases (stable)</string> <string>Debug Output Logging</string>
</property> </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>
<item> <item>
<widget class="QCheckBox" name="autoCheckCompanion">
<property name="text"> <property name="text">
<string>Release candidates (testing)</string> <string>Automatic check for Companion updates</string>
</property> </property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item> </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"> <property name="text">
<string>Nightly builds (unstable)</string> <string>Prompt for radio profile</string>
</property> </property>
</item>
</widget> </widget>
</item> </item>
</layout> </layout>
@ -1180,7 +1205,38 @@ Mode 4:
<property name="spacing"> <property name="spacing">
<number>6</number> <number>6</number>
</property> </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"> <widget class="QComboBox" name="joystickCB">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
@ -1193,14 +1249,20 @@ Mode 4:
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="3"> <item row="0" column="0">
<widget class="QPushButton" name="joystickcalButton"> <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"> <property name="text">
<string>Calibrate</string> <string>Screenshot capture folder</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="1"> <item row="7" column="1">
<widget class="QComboBox" name="backLightColor"> <widget class="QComboBox" name="backLightColor">
<property name="maximumSize"> <property name="maximumSize">
<size> <size>
@ -1235,31 +1297,22 @@ Mode 4:
</item> </item>
</widget> </widget>
</item> </item>
<item row="0" column="0"> <item row="9" column="3">
<widget class="QLabel" name="label_19"> <widget class="QPushButton" name="joystickcalButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Screenshot capture folder</string> <string>Calibrate</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="0" column="3">
<spacer name="verticalSpacer"> <widget class="QPushButton" name="snapshotPathButton">
<property name="orientation"> <property name="text">
<enum>Qt::Vertical</enum> <string>Select Folder</string>
</property> </property>
<property name="sizeHint" stdset="0"> <property name="flat">
<size> <bool>false</bool>
<width>20</width>
<height>5</height>
</size>
</property> </property>
</spacer> </widget>
</item> </item>
<item row="1" column="1" colspan="3"> <item row="1" column="1" colspan="3">
<widget class="QCheckBox" name="snapshotClipboardCKB"> <widget class="QCheckBox" name="snapshotClipboardCKB">
@ -1274,7 +1327,7 @@ Mode 4:
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="2"> <item row="9" column="2">
<widget class="QCheckBox" name="joystickChkB"> <widget class="QCheckBox" name="joystickChkB">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
@ -1287,17 +1340,20 @@ Mode 4:
</property> </property>
</widget> </widget>
</item> </item>
<item row="0" column="3"> <item row="12" column="1">
<widget class="QPushButton" name="snapshotPathButton"> <spacer name="verticalSpacer">
<property name="text"> <property name="orientation">
<string>Select Folder</string> <enum>Qt::Vertical</enum>
</property> </property>
<property name="flat"> <property name="sizeHint" stdset="0">
<bool>false</bool> <size>
<width>20</width>
<height>5</height>
</size>
</property> </property>
</widget> </spacer>
</item> </item>
<item row="5" column="0"> <item row="9" column="0">
<widget class="QLabel" name="label_11"> <widget class="QLabel" name="label_11">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -1310,7 +1366,20 @@ Mode 4:
</property> </property>
</widget> </widget>
</item> </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"> <widget class="QLabel" name="label_3">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed"> <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
@ -1348,60 +1417,30 @@ Mode 4:
</property> </property>
</widget> </widget>
</item> </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"> <widget class="QCheckBox" name="simuSW">
<property name="sizePolicy"> <property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed"> <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch> <horstretch>0</horstretch>
<verstretch>0</verstretch> <verstretch>0</verstretch>
</sizepolicy> </sizepolicy>
</property> </property>
<property name="text"> <property name="text">
<string>Remember simulator switch values</string> <string>Save switch/pot positions on simulator exit</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="0"> <item row="2" column="3">
<widget class="QLabel" name="label_volumeGain"> <widget class="QPushButton" name="btnClearPos">
<property name="sizePolicy">
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text"> <property name="text">
<string>Simulator Volume Gain</string> <string>Clear saved positions</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>
</property> </property>
</widget> </widget>
</item> </item>

View file

@ -1,5 +1,6 @@
set(firmwares_SRCS set(firmwares_SRCS
adjustmentreference.cpp
boards.cpp boards.cpp
curvereference.cpp curvereference.cpp
customfunctiondata.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; int value;
QString toString(const ModelData * model = NULL, bool verbose = true) const; QString toString(const ModelData * model = NULL, bool verbose = true) const;
bool isSet() const { return type != CURVE_REF_DIFF || value != 0; }
}; };
#endif // CURVEREFERENCE_H #endif // CURVEREFERENCE_H

View file

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

View file

@ -123,6 +123,12 @@ GeneralSettings::GeneralSettings()
strcpy(bluetoothName, "taranis"); 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(); templateSetup = g.profile[g.sessionId()].channelOrder();
stickMode = g.profile[g.sessionId()].defaultMode(); stickMode = g.profile[g.sessionId()].defaultMode();

View file

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

View file

@ -43,7 +43,7 @@ class GVarData {
GVAR_PREC_MUL1 GVAR_PREC_MUL1
}; };
char name[GVAR_NAME_LEN+1]; char name[GVAR_NAME_LEN + 1];
int min; int min;
int max; int max;
bool popup; bool popup;
@ -62,6 +62,7 @@ class GVarData {
int getMax() const; int getMax() const;
float getMinPrec() const; float getMinPrec() const;
float getMaxPrec() const; float getMaxPrec() const;
bool isEmpty() const;
}; };

View file

@ -35,6 +35,10 @@ void ExpoData::convert(RadioDataConversionState & cstate)
swtch.convert(cstate); swtch.convert(cstate);
} }
bool ExpoData::isEmpty() const
{
return (chn == 0 && mode == INPUT_MODE_NONE);
}
/* /*
* MixData * MixData
@ -48,6 +52,10 @@ void MixData::convert(RadioDataConversionState & cstate)
swtch.convert(cstate); swtch.convert(cstate);
} }
bool MixData::isEmpty() const
{
return (destCh == 0);
}
/* /*
* LimitData * LimitData
@ -87,17 +95,16 @@ void LimitData::clear()
bool LimitData::isEmpty() const 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::CurveData() CurveData::CurveData()
{ {
clear(5); clear();
} }
void CurveData::clear(int count) void CurveData::clear(int count)
@ -126,22 +133,20 @@ QString CurveData::nameToString(const int idx) const
* FlightModeData * FlightModeData
*/ */
void FlightModeData::clear(const int phase) void FlightModeData::clear(const int phaseIdx)
{ {
memset(reinterpret_cast<void *>(this), 0, sizeof(FlightModeData)); memset(reinterpret_cast<void *>(this), 0, sizeof(FlightModeData));
if (phase != 0) { for (int i = 0; i < CPN_MAX_GVARS; i++) {
for (int idx=0; idx<CPN_MAX_GVARS; idx++) { gvars[i] = linkedGVarFlightModeZero(phaseIdx);
gvars[idx] = 1025;
}
for (int idx=0; idx<CPN_MAX_ENCODERS; idx++) {
rotaryEncoders[idx] = 1025;
} }
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) void FlightModeData::convert(RadioDataConversionState & cstate)
@ -150,3 +155,53 @@ void FlightModeData::convert(RadioDataConversionState & cstate)
cstate.setSubComp(nameToString(cstate.subCompIdx)); cstate.setSubComp(nameToString(cstate.subCompIdx));
swtch.convert(cstate); 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 INPUT_MODE_BOTH
}; };
#define EXPODATA_NAME_LEN 10
class ExpoData { class ExpoData {
Q_DECLARE_TR_FUNCTIONS(ExpoData) Q_DECLARE_TR_FUNCTIONS(ExpoData)
@ -52,9 +54,10 @@ class ExpoData {
int offset; int offset;
CurveReference curve; CurveReference curve;
int carryTrim; int carryTrim;
char name[10+1]; char name[EXPODATA_NAME_LEN+1];
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(ExpoData)); } void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(ExpoData)); }
void convert(RadioDataConversionState & cstate); void convert(RadioDataConversionState & cstate);
bool isEmpty() const;
}; };
enum MltpxValue { enum MltpxValue {
@ -90,8 +93,11 @@ class MixData {
char name[MIXDATA_NAME_LEN+1]; char name[MIXDATA_NAME_LEN+1];
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(MixData)); } void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(MixData)); }
bool isEmpty() const;
}; };
#define LIMITDATA_NAME_LEN 6
class LimitData { class LimitData {
Q_DECLARE_TR_FUNCTIONS(LimitData) Q_DECLARE_TR_FUNCTIONS(LimitData)
@ -103,7 +109,7 @@ class LimitData {
int offset; int offset;
int ppmCenter; int ppmCenter;
bool symetrical; bool symetrical;
char name[6+1]; char name[LIMITDATA_NAME_LEN+1];
CurveReference curve; CurveReference curve;
QString minToString() const; QString minToString() const;
QString maxToString() const; QString maxToString() const;
@ -120,6 +126,8 @@ class CurvePoint {
int8_t y; int8_t y;
}; };
#define CURVEDATA_NAME_LEN 6
class CurveData { class CurveData {
Q_DECLARE_TR_FUNCTIONS(CurveData) Q_DECLARE_TR_FUNCTIONS(CurveData)
@ -131,7 +139,7 @@ class CurveData {
}; };
CurveData(); CurveData();
void clear(int count); void clear(int count = 5);
bool isEmpty() const; bool isEmpty() const;
QString nameToString(const int idx) const; QString nameToString(const int idx) const;
@ -139,9 +147,13 @@ class CurveData {
bool smooth; bool smooth;
int count; int count;
CurvePoint points[CPN_MAX_POINTS]; 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 { class FlightModeData {
Q_DECLARE_TR_FUNCTIONS(FlightModeData) Q_DECLARE_TR_FUNCTIONS(FlightModeData)
@ -151,14 +163,20 @@ class FlightModeData {
int trimRef[CPN_MAX_TRIMS]; int trimRef[CPN_MAX_TRIMS];
int trim[CPN_MAX_TRIMS]; int trim[CPN_MAX_TRIMS];
RawSwitch swtch; RawSwitch swtch;
char name[10+1]; char name[FLIGHTMODE_NAME_LEN+1];
unsigned int fadeIn; unsigned int fadeIn;
unsigned int fadeOut; unsigned int fadeOut;
int rotaryEncoders[CPN_MAX_ENCODERS]; int rotaryEncoders[CPN_MAX_ENCODERS];
int gvars[CPN_MAX_GVARS]; int gvars[CPN_MAX_GVARS];
void clear(const int phase); void clear(const int phaseIdx);
QString nameToString(int index) const; QString nameToString(int phaseIdx) const;
void convert(RadioDataConversionState & cstate); 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 { class SwashRingData {

File diff suppressed because it is too large Load diff

View file

@ -76,6 +76,8 @@ class TimerData {
int pvalue; int pvalue;
void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); } void clear() { memset(reinterpret_cast<void *>(this), 0, sizeof(TimerData)); mode = RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); }
void convert(RadioDataConversionState & cstate); void convert(RadioDataConversionState & cstate);
bool isEmpty();
bool isModeOff() { return mode == RawSwitch(SWITCH_TYPE_TIMER_MODE, 0); }
}; };
#define CPN_MAX_SCRIPTS 9 #define CPN_MAX_SCRIPTS 9
@ -128,6 +130,8 @@ enum TrainerMode {
TRAINER_MODE_MULTI TRAINER_MODE_MULTI
}; };
#define INPUT_NAME_LEN 4
class ModelData { class ModelData {
Q_DECLARE_TR_FUNCTIONS(ModelData) Q_DECLARE_TR_FUNCTIONS(ModelData)
@ -170,7 +174,7 @@ class ModelData {
MixData mixData[CPN_MAX_MIXERS]; MixData mixData[CPN_MAX_MIXERS];
LimitData limitData[CPN_MAX_CHNOUT]; 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]; ExpoData expoData[CPN_MAX_EXPOS];
CurveData curves[CPN_MAX_CURVES]; CurveData curves[CPN_MAX_CURVES];
@ -219,11 +223,23 @@ class ModelData {
void setTrimValue(int phaseIdx, int trimIdx, int value); void setTrimValue(int phaseIdx, int trimIdx, int value);
bool isGVarLinked(int phaseIdx, int gvarIdx); bool isGVarLinked(int phaseIdx, int gvarIdx);
int getGVarFieldValue(int phaseIdx, int gvarIdx); bool isGVarLinkedCircular(int phaseIdx, int gvarIdx);
float getGVarFieldValuePrec(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(); 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 clearMixes();
void clearInputs(); void clearInputs();
@ -231,8 +247,95 @@ class ModelData {
bool isAvailable(const RawSwitch & swtch) const; 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: protected:
void removeGlobalVar(int & var); 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 #endif // MODELDATA_H

View file

@ -181,3 +181,59 @@ QStringList ModuleData::powerValueStrings(int subType, Firmware * fw)
strIdx += 2; strIdx += 2;
return strings[strIdx]; 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_AFHDS2A_RX,
MODULE_SUBTYPE_MULTI_HOTT, MODULE_SUBTYPE_MULTI_HOTT,
MODULE_SUBTYPE_MULTI_FX816, 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 { enum TrainerProtocol {
@ -197,6 +210,8 @@ class ModuleData {
static QString indexToString(int index, Firmware * fw); static QString indexToString(int index, Firmware * fw);
static QString protocolToString(unsigned protocol); static QString protocolToString(unsigned protocol);
static QStringList powerValueStrings(int subType, Firmware * fw); static QStringList powerValueStrings(int subType, Firmware * fw);
bool hasFailsafes(Firmware * fw) const;
int getMaxChannelCount();
}; };
#endif // MODULEDATA_H #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_FLYSKY {"Standard", "V9x9", "V6x6", "V912", "CX20"};
static const QStringList STR_SUBTYPE_HUBSAN {"H107", "H301", "H501"}; 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_HISKY {"Standard", "HK310"};
static const QStringList STR_SUBTYPE_V2X2 {"Standard", "JXD506"}; static const QStringList STR_SUBTYPE_V2X2 {"Standard", "JXD506"};
static const QStringList STR_SUBTYPE_DSM {"DSM2 22ms", "DSM2 11ms", "DSMX 22ms", "DSMX 11ms"}; 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_CX10 {"Green", "Blue", "DM007", "-", "JC3015a", "JC3015b", "MK33041"};
static const QStringList STR_SUBTYPE_CG023 {"Standard", "YD829"}; 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_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_MT99 {"MT99", "H7", "YZ", "LS", "FY805"};
static const QStringList STR_SUBTYPE_MJXQ {"WLH08", "X600", "X800", "H26D", "E010", "H26WH", "Phoenix"}; static const QStringList STR_SUBTYPE_MJXQ {"WLH08", "X600", "X800", "H26D", "E010", "H26WH", "Phoenix"};
static const QStringList STR_SUBTYPE_FY326 {"Standard", "FY319"}; 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_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_Q303 {"Standard", "CX35", "CX10D", "CX10WD"};
static const QStringList STR_SUBTYPE_CABELL {"Cabell V3", "Cab V3 Telem", "-", "-", "-", "-", "Set FailSafe", "Unbind"}; 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_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_CORONA {"Corona V1", "Corona V2", "Flydream V3"};
static const QStringList STR_SUBTYPE_HITEC {"Optima", "Optima Hub Telem", "Minima"}; 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_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_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_GD00X {"GD V1", "GD V2"};
static const QStringList STR_SUBTYPE_REDPINE {"Fast", "Slow"}; static const QStringList STR_SUBTYPE_REDPINE {"Fast", "Slow"};
static const QStringList STR_SUBTYPE_POTENSIC {"A20 Firefly"}; static const QStringList STR_SUBTYPE_POTENSIC {"A20 Firefly"};
static const QStringList STR_SUBTYPE_ZSX {"JJRC ZSX-280"}; static const QStringList STR_SUBTYPE_ZSX {"JJRC ZSX-280"};
static const QStringList STR_SUBTYPE_FLYZONE {"FZ-410 TX"}; 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_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}; static const QStringList NO_SUBTYPE {STR_MULTI_DEFAULT};
@ -83,7 +93,7 @@ static const QStringList NO_SUBTYPE {STR_MULTI_DEFAULT};
const Multiprotocols multiProtocols { const Multiprotocols multiProtocols {
{MODULE_SUBTYPE_MULTI_FLYSKY, 4, false, STR_SUBTYPE_FLYSKY, nullptr}, {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_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_HISKY, 1, false, STR_SUBTYPE_HISKY, nullptr},
{MODULE_SUBTYPE_MULTI_V2X2, 1, false, STR_SUBTYPE_V2X2, nullptr}, {MODULE_SUBTYPE_MULTI_V2X2, 1, false, STR_SUBTYPE_V2X2, nullptr},
{MODULE_SUBTYPE_MULTI_DSM2, 3, false, STR_SUBTYPE_DSM, 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_YD717, 4, false, STR_SUBTYPE_YD717, nullptr},
{MODULE_SUBTYPE_MULTI_KN, 1, false, STR_SUBTYPE_KN, nullptr}, {MODULE_SUBTYPE_MULTI_KN, 1, false, STR_SUBTYPE_KN, nullptr},
{MODULE_SUBTYPE_MULTI_SYMAX, 1, false, STR_SUBTYPE_SYMAX, 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_CX10, 6, false, STR_SUBTYPE_CX10, nullptr},
{MODULE_SUBTYPE_MULTI_CG023, 1, false, STR_SUBTYPE_CG023, 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_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_MT99XX, 4, false, STR_SUBTYPE_MT99, nullptr},
{MODULE_SUBTYPE_MULTI_MJXQ, 6, false, STR_SUBTYPE_MJXQ, STR_MULTI_RFTUNE}, {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_FY326, 1, false, STR_SUBTYPE_FY326, nullptr},
{MODULE_SUBTYPE_MULTI_SFHSS, 0, true, NO_SUBTYPE, STR_MULTI_RFTUNE}, {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_HONTAI, 3, false, STR_SUBTYPE_HONTAI, nullptr},
{MODULE_SUBTYPE_MULTI_OLRS, 0, false, NO_SUBTYPE, STR_MULTI_RFPOWER}, {MODULE_SUBTYPE_MULTI_OLRS, 0, false, NO_SUBTYPE, STR_MULTI_RFPOWER},
{MODULE_SUBTYPE_MULTI_FS_AFHDS2A, 3, true, STR_SUBTYPE_AFHDS2A, STR_MULTI_SERVOFREQ}, {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_WK_2X01, 5, false, STR_SUBTYPE_WK2x01, nullptr},
{MODULE_SUBTYPE_MULTI_Q303, 3, false, STR_SUBTYPE_Q303, 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_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_H83D, 3, false, STR_SUBTYPE_H83D, nullptr},
{MODULE_SUBTYPE_MULTI_CORONA, 2, false, STR_SUBTYPE_CORONA, STR_MULTI_RFTUNE}, {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_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_BUGS_MINI, 1, false, STR_SUBTYPE_BUGS_MINI, nullptr},
{MODULE_SUBTYPE_MULTI_TRAXXAS, 0, false, STR_SUBTYPE_TRAXXAS, 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_E01X, 2, false, STR_SUBTYPE_E01X, STR_MULTI_OPTION},
{MODULE_SUBTYPE_MULTI_V911S, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE}, {MODULE_SUBTYPE_MULTI_V911S, 1, false, STR_SUBTYPE_V911S, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_GD00X, 1, false, STR_SUBTYPE_GD00X, nullptr}, {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_KF606, 0, false, NO_SUBTYPE, STR_MULTI_RFTUNE},
{MODULE_SUBTYPE_MULTI_REDPINE, 1, false, STR_SUBTYPE_REDPINE, 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_POTENSIC, 0, false, STR_SUBTYPE_POTENSIC, nullptr},
{MODULE_SUBTYPE_MULTI_ZSX, 0, false, STR_SUBTYPE_ZSX, nullptr}, {MODULE_SUBTYPE_MULTI_ZSX, 0, false, STR_SUBTYPE_ZSX, nullptr},
{MODULE_SUBTYPE_MULTI_FLYZONE, 0, false, STR_SUBTYPE_FLYZONE, 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_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_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}, {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) // 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({ static const QStringList strings({
"FlySky", "Hubsan", "FrSky", "Hisky", "V2x2", "DSM", "Devo", "YD717", "KN", "SymaX", "SLT", "CX10", "CG023", "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", "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", "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); return strings.value(protocol, CPN_STR_UNKNOWN_ITEM);
} }

View file

@ -693,11 +693,13 @@ class FlightModeField: public TransformedField {
internalField.Append(new UnsignedField<8>(this, phase.fadeIn)); internalField.Append(new UnsignedField<8>(this, phase.fadeIn));
internalField.Append(new UnsignedField<8>(this, phase.fadeOut)); internalField.Append(new UnsignedField<8>(this, phase.fadeOut));
for (int i=0; i<rotencCount; i++) { if (version < 219) {
for (int i = 0; i < rotencCount; i++) {
internalField.Append(new SignedField<16>(this, phase.rotaryEncoders[i])); internalField.Append(new SignedField<16>(this, phase.rotaryEncoders[i]));
} }
}
for (int i=0; i<MAX_GVARS(board, version); i++) { for (int i = 0; i < MAX_GVARS(board, version); i++) {
internalField.Append(new SignedField<16>(this, phase.gvars[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")); internalField.Append(new UnsignedField<32>(this, _param, "param"));
} }
virtual void beforeExport() void beforeExport() override
{ {
if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) { if (sensor.type == SensorData::TELEM_TYPE_CUSTOM) {
_id = sensor.id; _id = sensor.id;
@ -2037,7 +2039,7 @@ class ModuleUnionField: public UnionField<unsigned int> {
module(module), module(module),
rfProtExtra(0) rfProtExtra(0)
{ {
ModuleData::Multi& multi = module.multi; ModuleData::Multi & multi = module.multi;
internalField.Append(new UnsignedField<3>(this, rfProtExtra)); internalField.Append(new UnsignedField<3>(this, rfProtExtra));
internalField.Append(new BoolField<1>(this, multi.disableTelemetry)); internalField.Append(new BoolField<1>(this, multi.disableTelemetry));
internalField.Append(new BoolField<1>(this, multi.disableMapping)); internalField.Append(new BoolField<1>(this, multi.disableMapping));
@ -2054,22 +2056,17 @@ class ModuleUnionField: public UnionField<unsigned int> {
void beforeExport() override void beforeExport() override
{ {
if (module.multi.rfProtocol > MODULE_SUBTYPE_MULTI_LAST)
module.multi.rfProtocol += 3;
rfProtExtra = (module.multi.rfProtocol & 0x70) >> 4; rfProtExtra = (module.multi.rfProtocol & 0x70) >> 4;
module.multi.rfProtocol &= 0x0f;
} }
void afterImport() override void afterImport() override
{ {
module.multi.rfProtocol = (rfProtExtra << 4) + (module.rfProtocol & 0xf); module.multi.rfProtocol = (rfProtExtra << 4) + (module.rfProtocol & 0xf);
if (module.multi.rfProtocol > MODULE_SUBTYPE_MULTI_LAST)
module.multi.rfProtocol -= 3;
} }
private: private:
StructField internalField; StructField internalField;
ModuleData& module; ModuleData & module;
unsigned int rfProtExtra; unsigned int rfProtExtra;
}; };
@ -2133,7 +2130,8 @@ class ModuleUnionField: public UnionField<unsigned int> {
memset(receiverName, 0, sizeof(receiverName)); 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; 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 i=0; i<PXX2_MAX_RECEIVERS_PER_MODULE; i++) {
for (int pos=0; pos<PXX2_LEN_RX_NAME+1; pos++) { for (int pos=0; pos<PXX2_LEN_RX_NAME+1; pos++) {
if (pos == PXX2_LEN_RX_NAME || module.access.receiverName[i][pos] == '\0') { if (pos == PXX2_LEN_RX_NAME || module.access.receiverName[i][pos] == '\0') {
memset(module.access.receiverName[i]+pos,'\0',PXX2_LEN_RX_NAME-pos); memset(module.access.receiverName[i]+pos,'\0',PXX2_LEN_RX_NAME-pos);
break; break;
@ -2222,7 +2219,7 @@ class ModuleField: public TransformedField {
module.subType = module.protocol - PULSES_PXX_XJT_X16; module.subType = module.protocol - PULSES_PXX_XJT_X16;
} }
else if (module.protocol == PULSES_MULTIMODULE) { 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: case SoundPitch:
return 1; return 1;
case Haptic: 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: case ModelTrainerEnable:
if (IS_HORUS_OR_TARANIS(board) && board!=Board::BOARD_TARANIS_XLITE) if (IS_HORUS_OR_TARANIS(board) && board!=Board::BOARD_TARANIS_XLITE)
return 1; return 1;
@ -616,6 +616,10 @@ int OpenTxFirmware::getCapability(::Capability capability)
return 0; return 0;
else else
return IS_ARM(board) ? 4 : 2; return IS_ARM(board) ? 4 : 2;
case TelemetryCustomScreensBars:
return (getCapability(TelemetryCustomScreens) ? 4 : 0);
case TelemetryCustomScreensLines:
return (getCapability(TelemetryCustomScreens) ? 4 : 0);
case TelemetryCustomScreensFieldsPerLine: case TelemetryCustomScreensFieldsPerLine:
return HAS_LARGE_LCD(board) ? 3 : 2; return HAS_LARGE_LCD(board) ? 3 : 2;
case NoTelemetryProtocol: case NoTelemetryProtocol:
@ -754,6 +758,11 @@ int OpenTxFirmware::getCapability(::Capability capability)
return IS_TARANIS_XLITES(board); return IS_TARANIS_XLITES(board);
case PwrButtonPress: case PwrButtonPress:
return IS_HORUS_OR_TARANIS(board) && (board!=Board::BOARD_TARANIS_X9D) && (board!=Board::BOARD_TARANIS_X9DP); 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: default:
return 0; return 0;
} }
@ -793,7 +802,7 @@ bool OpenTxFirmware::isAvailable(PulsesProtocol proto, int port)
case PULSES_ACCST_ISRM_D16: case PULSES_ACCST_ISRM_D16:
return IS_ACCESS_RADIO(board, id); return IS_ACCESS_RADIO(board, id);
case PULSES_MULTIMODULE: case PULSES_MULTIMODULE:
return id.contains("internalmulti"); return id.contains("internalmulti") || IS_RADIOMASTER_TX16S(board);
default: default:
return false; return false;
} }
@ -1252,6 +1261,7 @@ void registerOpenTxFirmwares()
/* FrSky Taranis X9D board */ /* FrSky Taranis X9D board */
firmware = new OpenTxFirmware("opentx-x9d", Firmware::tr("FrSky Taranis X9D"), BOARD_TARANIS_X9D); 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")); firmware->addOption("haptic", Firmware::tr("Haptic module installed"));
addOpenTxTaranisOptions(firmware); addOpenTxTaranisOptions(firmware);
addPPMInternalModuleHack(firmware); addPPMInternalModuleHack(firmware);
@ -1331,7 +1341,6 @@ void registerOpenTxFirmwares()
firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support")); firmware->addOption("noheli", Firmware::tr("Disable HELI menu and cyclic mix support"));
firmware->addOption("nogvars", Firmware::tr("Disable Global variables")); firmware->addOption("nogvars", Firmware::tr("Disable Global variables"));
firmware->addOption("lua", Firmware::tr("Enable Lua custom scripts screen")); 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")); firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module"));
addOpenTxFontOptions(firmware); addOpenTxFontOptions(firmware);
registerOpenTxFirmware(firmware); registerOpenTxFirmware(firmware);
@ -1342,15 +1351,17 @@ void registerOpenTxFirmwares()
addOpenTxFrskyOptions(firmware); addOpenTxFrskyOptions(firmware);
firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module")); firmware->addOption("internalmulti", Firmware::tr("Support for MULTI internal module"));
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module"));
registerOpenTxFirmware(firmware);
addOpenTxRfOptions(firmware, FLEX); 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); firmware = new OpenTxFirmware("opentx-tx16s", Firmware::tr("Radiomaster TX16s / TX16s Hall / TX16s Masterfire"), BOARD_RADIOMASTER_TX16S);
addOpenTxFrskyOptions(firmware); addOpenTxFrskyOptions(firmware);
firmware->addOption("bluetooth", Firmware::tr("Support for bluetooth module")); addOpenTxRfOptions(firmware, FLEX);
registerOpenTxFirmware(firmware);*/ 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 */ /* 9XR-Pro */
firmware = new OpenTxFirmware("opentx-9xrpro", Firmware::tr("Turnigy 9XR-PRO"), BOARD_9XRPRO); 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 AllSourceGroups = InputSourceGroups | GVarsGroup | TelemGroup | ScriptsGroup
}; };
RawSource(): RawSource() { clear(); }
type(SOURCE_TYPE_NONE),
index(0)
{
}
explicit RawSource(int value): explicit RawSource(int value):
type(RawSourceType(abs(value)/65536)), type(RawSourceType(abs(value)/65536)),
@ -258,6 +254,8 @@ class RawSource {
bool isSlider(int * sliderIndex = NULL, Board::Type board = Board::BOARD_UNKNOWN) const; bool isSlider(int * sliderIndex = NULL, Board::Type board = Board::BOARD_UNKNOWN) const;
bool isTimeBased(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 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 { bool operator == ( const RawSource & other) const {
return (this->type == other.type) && (this->index == other.index); return (this->type == other.type) && (this->index == other.index);

View file

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

View file

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

View file

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

View file

@ -1124,7 +1124,7 @@ p, li { white-space: pre-wrap; }
<item row="19" column="1"> <item row="19" column="1">
<widget class="QLineEdit" name="registrationId"> <widget class="QLineEdit" name="registrationId">
<property name="inputMask"> <property name="inputMask">
<string>aaaaaaAA</string> <string>nnnnnnNN</string>
</property> </property>
<property name="maxLength"> <property name="maxLength">
<number>8</number> <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 "translations.h"
#include "dialogs/filesyncdialog.h" #include "dialogs/filesyncdialog.h"
#include "profilechooser.h"
#include <QtGui> #include <QtGui>
#include <QFileInfo> #include <QFileInfo>
@ -130,11 +131,17 @@ MainWindow::MainWindow():
else { else {
if (!g.previousVersion().isEmpty()) if (!g.previousVersion().isEmpty())
g.warningId(g.warningId() | AppMessages::MSG_UPGRADED); 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())) if (checkProfileRadioExists(g.sessionId()))
QTimer::singleShot(updateDelay, this, SLOT(doAutoUpdates())); QTimer::singleShot(updateDelay, this, SLOT(doAutoUpdates()));
else else
g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE); g.warningId(g.warningId() | AppMessages::MSG_NO_RADIO_TYPE);
} }
}
QTimer::singleShot(updateDelay, this, SLOT(displayWarnings())); QTimer::singleShot(updateDelay, this, SLOT(displayWarnings()));
QStringList strl = QApplication::arguments(); QStringList strl = QApplication::arguments();
@ -916,14 +923,15 @@ void MainWindow::changelog()
void MainWindow::customizeSplash() void MainWindow::customizeSplash()
{ {
CustomizeSplashDialog * dialog = new CustomizeSplashDialog(this); auto * dialog = new CustomizeSplashDialog(this);
dialog->exec(); dialog->exec();
dialog->deleteLater(); dialog->deleteLater();
} }
void MainWindow::writeEeprom() void MainWindow::writeEeprom()
{ {
if (activeMdiChild()) activeMdiChild()->writeEeprom(); if (activeMdiChild())
activeMdiChild()->writeEeprom();
} }
void MainWindow::readEeprom() void MainWindow::readEeprom()
@ -1806,3 +1814,20 @@ void MainWindow::autoClose()
{ {
this->close(); 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 exportSettings();
void importSettings(); void importSettings();
void autoClose(); void autoClose();
void chooseProfile();
void openUpdatesWaitDialog(); void openUpdatesWaitDialog();
void closeUpdatesWaitDialog(); void closeUpdatesWaitDialog();

View file

@ -1490,18 +1490,18 @@ bool MdiChild::convertStorage(Board::Type from, Board::Type to, bool newFile)
isUntitled = true; isUntitled = true;
if (cstate.hasLogEntries(RadioDataConversionState::EVT_INF)) { 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->setSortingEnabled(true);
tv->verticalHeader()->hide(); tv->verticalHeader()->hide();
tv->setModel(cstate.getLogModel(RadioDataConversionState::EVT_INF, tv)); tv->setModel(cstate.getLogModel(RadioDataConversionState::EVT_INF, tv));
tv->resizeColumnsToContents(); tv->resizeColumnsToContents();
tv->resizeRowsToContents(); 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(new QLabel(tr("<b>The conversion generated some important messages, please review them below.</b>")));
lo->addWidget(tv); lo->addWidget(tv);
lo->addWidget(btnBox); lo->addWidget(btnBox);

View file

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

View file

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

View file

@ -117,7 +117,12 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
lock = true; 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->curveName->hide();
ui->curveNameLabel->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))); connect(scene, SIGNAL(newPoint(int, int)), this, SLOT(onSceneNewPoint(int, int)));
ui->curvePreview->setScene(scene); ui->curvePreview->setScene(scene);
int numcurves=firmware->getCapability(NumCurves);
int limit; int limit;
if (numcurves>16) { if (maxCurves > 16) {
limit=numcurves/2; limit = maxCurves / 2;
} else { } else {
limit=numcurves; limit = maxCurves;
} }
for (int i=0; i<numcurves; i++) { for (int i = 0; i < maxCurves; i++) {
visibleCurves[i] = false; visibleCurves[i] = false;
// The edit curve button // The edit curve button
@ -151,15 +156,15 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
edit->setStyleSheet(QString("background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;").arg(colors[i].name())); edit->setStyleSheet(QString("background-color: %1; color: white; padding: 2px 3px; border-style: outset; border-width: 1px; border-radius: 2px; border-color: inherit;").arg(colors[i].name()));
#endif #endif
edit->setPalette(palette); edit->setPalette(palette);
edit->setText(tr("Curve %1").arg(i+1)); edit->setText(tr("Curve %1").arg(i + 1));
edit->setContextMenuPolicy(Qt::CustomContextMenu); edit->setContextMenuPolicy(Qt::CustomContextMenu);
edit->setToolTip(tr("Popup menu available")); 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())); connect(edit, SIGNAL(clicked()), this, SLOT(editCurve()));
if (i<limit) { if (i < limit) {
ui->curvesLayout->addWidget(edit, i, 1, 1, 1); ui->curvesLayout->addWidget(edit, i, 1, 1, 1);
} else { } else {
ui->curvesLayout2->addWidget(edit, i-limit, 2, 1, 1); ui->curvesLayout2->addWidget(edit, i - limit, 2, 1, 1);
} }
// The curve plot checkbox // The curve plot checkbox
@ -167,7 +172,7 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
plot->setProperty("index", i); plot->setProperty("index", i);
plot->setPalette(palette); plot->setPalette(palette);
connect(plot, SIGNAL(toggled(bool)), this, SLOT(plotCurve(bool))); connect(plot, SIGNAL(toggled(bool)), this, SLOT(plotCurve(bool)));
if (i<limit) { if (i < limit) {
ui->curvesLayout->addWidget(plot, i, 2, 1, 1); ui->curvesLayout->addWidget(plot, i, 2, 1, 1);
} else { } else {
ui->curvesLayout2->addWidget(plot, i-limit, 1, 1, 1); ui->curvesLayout2->addWidget(plot, i-limit, 1, 1, 1);
@ -175,13 +180,13 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
} }
QSpacerItem * item = new QSpacerItem(1,1, QSizePolicy::Fixed, QSizePolicy::Expanding); QSpacerItem * item = new QSpacerItem(1,1, QSizePolicy::Fixed, QSizePolicy::Expanding);
ui->curvesLayout->addItem(item,limit+1,1,1,1,0); 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); QSpacerItem * item2 = new QSpacerItem(1,1, QSizePolicy::Fixed, QSizePolicy::Expanding);
ui->curvesLayout2->addItem(item2,limit+1,1,1,1,0); ui->curvesLayout2->addItem(item2,limit + 1, 1, 1, 1, 0);
} }
for (int i=0; i<CPN_MAX_POINTS; i++) { for (int i = 0; i < CPN_MAX_POINTS; i++) {
spnx[i] = new QSpinBox(this); spnx[i] = new QSpinBox(this);
spnx[i]->setProperty("index", i); spnx[i]->setProperty("index", i);
spnx[i]->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); spnx[i]->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter);
@ -199,14 +204,14 @@ Curves::Curves(QWidget * parent, ModelData & model, GeneralSettings & generalSet
ui->pointsLayout->addWidget(spny[i], i, 1, 1, 1); ui->pointsLayout->addWidget(spny[i], i, 1, 1, 1);
bool insert; bool insert;
if (firmware->getCapability(EnhancedCurves)) { if (hasEnhanced) {
insert = (i >= 1); insert = (i >= 1);
} }
else { else {
insert = (i==2 || i==4 || i==8 || i==16); insert = (i == 2 || i == 4 || i ==8 || i == 16);
} }
if (insert) { if (insert) {
ui->curvePoints->addItem(tr("%1 points").arg(i+1), i+1); ui->curvePoints->addItem(tr("%1 points").arg(i + 1), i + 1);
} }
} }
@ -250,7 +255,7 @@ void Curves::update()
{ {
lock = true; lock = true;
if (firmware->getCapability(HasCvNames)) { if (hasNames) {
ui->curveName->setText(model->curves[currentCurve].name); ui->curveName->setText(model->curves[currentCurve].name);
} }
@ -272,7 +277,7 @@ void Curves::updateCurveType()
int index = 0; int index = 0;
if (firmware->getCapability(EnhancedCurves)) { if (hasEnhanced) {
index = model->curves[currentCurve].count - 2; index = model->curves[currentCurve].count - 2;
} }
else { else {
@ -305,35 +310,34 @@ void Curves::updateCurve()
qreal width = scene->sceneRect().width(); qreal width = scene->sceneRect().width();
qreal height = scene->sceneRect().height(); qreal height = scene->sceneRect().height();
qreal centerX = scene->sceneRect().left() + width/2; qreal centerX = scene->sceneRect().left() + width / 2;
qreal centerY = scene->sceneRect().top() + height/2; qreal centerY = scene->sceneRect().top() + height / 2;
QGraphicsSimpleTextItem *ti = scene->addSimpleText(tr("Editing curve %1").arg(currentCurve+1)); QGraphicsSimpleTextItem *ti = scene->addSimpleText(tr("Editing curve %1").arg(currentCurve + 1));
ti->setPos(3, 3); ti->setPos(3, 3);
scene->addLine(centerX, GFX_MARGIN, centerX, height+GFX_MARGIN); scene->addLine(centerX, GFX_MARGIN, centerX, height + GFX_MARGIN);
scene->addLine(GFX_MARGIN, centerY, width+GFX_MARGIN, centerY); scene->addLine(GFX_MARGIN, centerY, width + GFX_MARGIN, centerY);
QPen pen; QPen pen;
pen.setWidth(1); pen.setWidth(1);
pen.setStyle(Qt::SolidLine); pen.setStyle(Qt::SolidLine);
int numcurves = firmware->getCapability(NumCurves); for (int k = 0; k < maxCurves; k++) {
for (int k=0; k<numcurves; k++) {
pen.setColor(colors[k]); pen.setColor(colors[k]);
if (currentCurve!=k && visibleCurves[k]) { if (currentCurve != k && visibleCurves[k]) {
int numpoints = model->curves[k].count; int numpoints = model->curves[k].count;
for (int i=0; i<numpoints-1; i++) { for (int i = 0; i < numpoints - 1; i++) {
if (model->curves[k].type == CurveData::CURVE_TYPE_CUSTOM) if (model->curves[k].type == CurveData::CURVE_TYPE_CUSTOM)
scene->addLine(centerX + (qreal)model->curves[k].points[i].x*width/200,centerY - (qreal)model->curves[k].points[i].y*height/200,centerX + (qreal)model->curves[k].points[i+1].x*width/200,centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen); scene->addLine(centerX + (qreal)model->curves[k].points[i].x*width/200, centerY - (qreal)model->curves[k].points[i].y*height/200,centerX + (qreal)model->curves[k].points[i+1].x*width/200,centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen);
else else
scene->addLine(GFX_MARGIN + i*width/(numpoints-1),centerY - (qreal)model->curves[k].points[i].y*height/200,GFX_MARGIN + (i+1)*width/(numpoints-1),centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen); scene->addLine(GFX_MARGIN + i*width/(numpoints-1), centerY - (qreal)model->curves[k].points[i].y*height/200,GFX_MARGIN + (i+1)*width/(numpoints-1),centerY - (qreal)model->curves[k].points[i+1].y*height/200, pen);
} }
} }
} }
int numpoints = model->curves[currentCurve].count; int numpoints = model->curves[currentCurve].count;
for (int i=0; i<numpoints; i++) { for (int i = 0; i < numpoints; i++) {
nodel = nodex; nodel = nodex;
nodex = new Node(); nodex = new Node();
nodex->setProperty("index", i); nodex->setProperty("index", i);
@ -341,26 +345,27 @@ void Curves::updateCurve()
nodex->setBallSize(ui->pointSize->value()); nodex->setBallSize(ui->pointSize->value());
nodex->setBallHeight(0); nodex->setBallHeight(0);
if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) { if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) {
if (i>0 && i<numpoints-1) { if (i > 0 && i < numpoints - 1) {
nodex->setFixedX(false); nodex->setFixedX(false);
nodex->setMinX(model->curves[currentCurve].points[i-1].x); nodex->setMinX(model->curves[currentCurve].points[i - 1].x);
nodex->setMaxX(model->curves[currentCurve].points[i+1].x); nodex->setMaxX(model->curves[currentCurve].points[i + 1].x);
} }
else { else {
nodex->setFixedX(true); nodex->setFixedX(true);
} }
nodex->setPos(centerX + (qreal)model->curves[currentCurve].points[i].x*width/200,centerY - (qreal)model->curves[currentCurve].points[i].y*height/200); nodex->setPos(centerX + (qreal)model->curves[currentCurve].points[i].x * width / 200, centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200);
} }
else { else {
nodex->setFixedX(true); nodex->setFixedX(true);
nodex->setPos(GFX_MARGIN + i*width/(numpoints-1), centerY - (qreal)model->curves[currentCurve].points[i].y*height/200); nodex->setPos(GFX_MARGIN + i * width / (numpoints - 1), centerY - (qreal)model->curves[currentCurve].points[i].y * height / 200);
} }
connect(nodex, SIGNAL(moved(int, int)), this, SLOT(onNodeMoved(int, int))); connect(nodex, SIGNAL(moved(int, int)), this, SLOT(onNodeMoved(int, int)));
connect(nodex, SIGNAL(focus()), this, SLOT(onNodeFocus())); connect(nodex, SIGNAL(focus()), this, SLOT(onNodeFocus()));
connect(nodex, SIGNAL(unfocus()), this, SLOT(onNodeUnfocus())); connect(nodex, SIGNAL(unfocus()), this, SLOT(onNodeUnfocus()));
connect(nodex, SIGNAL(deleteMe()), this, SLOT(onNodeDelete())); connect(nodex, SIGNAL(deleteMe()), this, SLOT(onNodeDelete()));
scene->addItem(nodex); scene->addItem(nodex);
if (i>0) scene->addItem(new Edge(nodel, nodex)); if (i > 0)
scene->addItem(new Edge(nodel, nodex));
} }
lock = false; lock = false;
@ -371,20 +376,20 @@ void Curves::updateCurvePoints()
lock = true; lock = true;
int count = model->curves[currentCurve].count; int count = model->curves[currentCurve].count;
for (int i=0; i<count; i++) { for (int i = 0; i < count; i++) {
spny[i]->show(); spny[i]->show();
spny[i]->setValue(model->curves[currentCurve].points[i].y); spny[i]->setValue(model->curves[currentCurve].points[i].y);
if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) { if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) {
spnx[i]->show(); spnx[i]->show();
if (i==0 || i==model->curves[currentCurve].count-1) { if (i == 0 || i == model->curves[currentCurve].count - 1) {
spnx[i]->setDisabled(true); spnx[i]->setDisabled(true);
spnx[i]->setMaximum(+100); spnx[i]->setMaximum(+100);
spnx[i]->setMinimum(-100); spnx[i]->setMinimum(-100);
} }
else { else {
spnx[i]->setDisabled(false); spnx[i]->setDisabled(false);
spnx[i]->setMaximum(model->curves[currentCurve].points[i+1].x); spnx[i]->setMaximum(model->curves[currentCurve].points[i + 1].x);
spnx[i]->setMinimum(model->curves[currentCurve].points[i-1].x); spnx[i]->setMinimum(model->curves[currentCurve].points[i - 1].x);
} }
spnx[i]->setValue(model->curves[currentCurve].points[i].x); spnx[i]->setValue(model->curves[currentCurve].points[i].x);
} }
@ -392,7 +397,7 @@ void Curves::updateCurvePoints()
spnx[i]->hide(); spnx[i]->hide();
} }
} }
for (int i=count; i<CPN_MAX_POINTS; i++) { for (int i = count; i < CPN_MAX_POINTS; i++) {
spny[i]->hide(); spny[i]->hide();
spnx[i]->hide(); spnx[i]->hide();
} }
@ -421,9 +426,9 @@ void Curves::onNodeMoved(int x, int y)
spnx[index]->setValue(x); spnx[index]->setValue(x);
spny[index]->setValue(y); spny[index]->setValue(y);
if (index > 0) if (index > 0)
spnx[index-1]->setMaximum(x); spnx[index - 1]->setMaximum(x);
if (index < model->curves[currentCurve].count-1) if (index < model->curves[currentCurve].count - 1)
spnx[index+1]->setMinimum(x); spnx[index + 1]->setMinimum(x);
emit modified(); emit modified();
lock = false; lock = false;
} }
@ -444,17 +449,14 @@ void Curves::onNodeUnfocus()
bool Curves::allowCurveType(int points, CurveData::CurveType type) bool Curves::allowCurveType(int points, CurveData::CurveType type)
{ {
int numcurves = firmware->getCapability(NumCurves);
int totalpoints = 0; 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); int cvPoints = (i == currentCurve ? points : model->curves[i].count);
CurveData::CurveType cvType = (i==currentCurve ? type : model->curves[i].type); CurveData::CurveType cvType = (i == currentCurve ? type : model->curves[i].type);
totalpoints += cvPoints + (cvType==CurveData::CURVE_TYPE_CUSTOM ? cvPoints-2 : 0); totalpoints += cvPoints + (cvType == CurveData::CURVE_TYPE_CUSTOM ? cvPoints - 2 : 0);
} }
int fwpoints = firmware->getCapability(NumCurvePoints); if (totalpoints > maxPoints) {
if (totalpoints > fwpoints) {
QMessageBox::warning(this, "companion", tr("Not enough free points in EEPROM to store the curve.")); QMessageBox::warning(this, "companion", tr("Not enough free points in EEPROM to store the curve."));
return false; return false;
} }
@ -472,8 +474,8 @@ void Curves::on_curvePoints_currentIndexChanged(int index)
model->curves[currentCurve].count = numpoints; model->curves[currentCurve].count = numpoints;
// TODO something better + reuse! // TODO something better + reuse!
for (int i=0; i<CPN_MAX_POINTS; i++) { for (int i = 0; i < CPN_MAX_POINTS; i++) {
model->curves[currentCurve].points[i].x = (i >= numpoints-1 ? +100 : -100 + (200*i)/(numpoints-1)); model->curves[currentCurve].points[i].x = (i >= numpoints - 1 ? +100 : -100 + (200 * i) / (numpoints - 1));
model->curves[currentCurve].points[i].y = 0; model->curves[currentCurve].points[i].y = 0;
} }
@ -491,12 +493,13 @@ void Curves::on_curveCustom_currentIndexChanged(int index)
if (!lock) { if (!lock) {
CurveData::CurveType type = (CurveData::CurveType)index; CurveData::CurveType type = (CurveData::CurveType)index;
int numpoints = ui->curvePoints->itemData(ui->curvePoints->currentIndex()).toInt(); int numpoints = ui->curvePoints->itemData(ui->curvePoints->currentIndex()).toInt();
if (allowCurveType(model->curves[currentCurve].count, type)) { if (allowCurveType(model->curves[currentCurve].count, type)) {
model->curves[currentCurve].type = type; model->curves[currentCurve].type = type;
// TODO something better + reuse! // TODO something better + reuse!
for (int i=0; i<CPN_MAX_POINTS; i++) { for (int i = 0; i < CPN_MAX_POINTS; i++) {
model->curves[currentCurve].points[i].x = (i >= numpoints-1 ? +100 : -100 + (200*i)/(numpoints-1)); model->curves[currentCurve].points[i].x = (i >= numpoints - 1 ? +100 : -100 + (200 * i) / (numpoints - 1));
model->curves[currentCurve].points[i].y = 0; model->curves[currentCurve].points[i].y = 0;
} }
@ -525,7 +528,7 @@ void Curves::on_curveName_editingFinished()
void Curves::resizeEvent(QResizeEvent *event) void Curves::resizeEvent(QResizeEvent *event)
{ {
QRect qr = ui->curvePreview->contentsRect(); QRect qr = ui->curvePreview->contentsRect();
ui->curvePreview->scene()->setSceneRect(GFX_MARGIN, GFX_MARGIN, qr.width()-GFX_MARGIN*2, qr.height()-GFX_MARGIN*2); ui->curvePreview->scene()->setSceneRect(GFX_MARGIN, GFX_MARGIN, qr.width() - GFX_MARGIN * 2, qr.height() - GFX_MARGIN * 2);
updateCurve(); updateCurve();
ModelPanel::resizeEvent(event); ModelPanel::resizeEvent(event);
} }
@ -559,12 +562,12 @@ void Curves::on_curveApply_clicked()
int index = ui->curveType->currentIndex(); int index = ui->curveType->currentIndex();
int numpoints = model->curves[currentCurve].count; int numpoints = model->curves[currentCurve].count;
for (int i=0; i<numpoints; i++) { for (int i = 0; i < numpoints; i++) {
float x; float x;
if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM) if (model->curves[currentCurve].type == CurveData::CURVE_TYPE_CUSTOM)
x = model->curves[currentCurve].points[i].x; x = model->curves[currentCurve].points[i].x;
else else
x = -100.0 + (200.0/(numpoints-1))*i; x = -100.0 + (200.0 / (numpoints - 1)) * i;
bool apply = false; bool apply = false;
switch (ui->curveSide->currentIndex()) { switch (ui->curveSide->currentIndex()) {
@ -572,11 +575,11 @@ void Curves::on_curveApply_clicked()
apply = true; apply = true;
break; break;
case 1: case 1:
if (x>=0) if (x >= 0)
apply = true; apply = true;
break; break;
case 2: case 2:
if (x<0) if (x < 0)
apply = true; apply = true;
break; break;
} }
@ -591,68 +594,6 @@ void Curves::on_curveApply_clicked()
emit modified(); 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() void Curves::onPointSizeEdited()
{ {
if (!lock) { if (!lock) {
@ -690,7 +631,7 @@ void Curves::onSceneNewPoint(int x, int y)
newidx = numpoints; newidx = numpoints;
} }
else { else {
for (int i=0; i<numpoints; i++) { for (int i = 0; i < numpoints; i++) {
if (x < model->curves[currentCurve].points[i].x) { if (x < model->curves[currentCurve].points[i].x) {
newidx = i; newidx = i;
break; break;
@ -699,8 +640,8 @@ void Curves::onSceneNewPoint(int x, int y)
} }
numpoints++; numpoints++;
model->curves[currentCurve].count = 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[idx] = model->curves[currentCurve].points[idx - 1];
} }
model->curves[currentCurve].points[newidx].x = x; model->curves[currentCurve].points[newidx].x = x;
model->curves[currentCurve].points[newidx].y = y; model->curves[currentCurve].points[newidx].y = y;
@ -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) : CustomScene::CustomScene(QGraphicsView * view) :
QGraphicsScene(view) QGraphicsScene(view)
{ {

View file

@ -26,12 +26,7 @@
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QGraphicsView> #include <QGraphicsView>
enum CopyAction { constexpr char MIMETYPE_CURVE[] = "application/x-companion-curve";
CURVE_COPY,
CURVE_PASTE,
CURVE_RESET,
CURVE_RESETALL
};
namespace Ui { namespace Ui {
class Curves; class Curves;
@ -71,7 +66,6 @@ class Curves : public ModelPanel
private slots: private slots:
void editCurve(); void editCurve();
void ShowContextMenu(const QPoint& pos);
void plotCurve(bool checked); void plotCurve(bool checked);
void on_curveName_editingFinished(); void on_curveName_editingFinished();
void on_curvePoints_currentIndexChanged(int index); void on_curvePoints_currentIndexChanged(int index);
@ -86,6 +80,16 @@ class Curves : public ModelPanel
void onSceneNewPoint(int x, int y); void onSceneNewPoint(int x, int y);
void onPointSizeEdited(); void onPointSizeEdited();
void onNodeDelete(); 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: protected:
virtual void resizeEvent(QResizeEvent *event); virtual void resizeEvent(QResizeEvent *event);
@ -105,6 +109,16 @@ class Curves : public ModelPanel
bool allowCurveType(int points, CurveData::CurveType type); bool allowCurveType(int points, CurveData::CurveType type);
void setPointY(int i, int x, int y); void setPointY(int i, int x, int y);
CustomScene * scene; 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_ #endif // _CURVES_H_

View file

@ -127,7 +127,7 @@ CustomFunctionsPanel::CustomFunctionsPanel(QWidget * parent, ModelData * model,
else else
label->setText(tr("GF%1").arg(i+1)); label->setText(tr("GF%1").arg(i+1));
label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Minimum); 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); tableLayout->addWidget(i, 0, label);
// s1.report("label"); // s1.report("label");
@ -608,76 +608,63 @@ void CustomFunctionsPanel::update()
lock = false; lock = false;
} }
void CustomFunctionsPanel::fswPaste() void CustomFunctionsPanel::cmPaste()
{ {
const QClipboard *clipboard = QApplication::clipboard(); QByteArray data;
const QMimeData *mimeData = clipboard->mimeData(); if (hasClipboardData(&data)) {
if (mimeData->hasFormat(MIMETYPE_FSW)) { memcpy(&functions[selectedIndex], data.constData(), sizeof(CustomFunctionData));
QByteArray fswData = mimeData->data(MIMETYPE_FSW); resetCBsAndRefresh(selectedIndex);
CustomFunctionData *fsw = &functions[selectedFunction];
memcpy(fsw, fswData.constData(), sizeof(CustomFunctionData));
resetCBsAndRefresh(selectedFunction);
emit modified(); emit modified();
} }
} }
void CustomFunctionsPanel::fswDelete() void CustomFunctionsPanel::cmDelete()
{ {
int maxidx = fswCapability - 1; if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Delete Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
for (int i=selectedFunction; i<maxidx; i++) { return;
if (!functions[i].isEmpty() || !functions[i+1].isEmpty()) {
CustomFunctionData *fsw1 = &functions[i]; memmove(&functions[selectedIndex], &functions[selectedIndex + 1], (CPN_MAX_SPECIAL_FUNCTIONS - (selectedIndex + 1)) * sizeof(CustomFunctionData));
CustomFunctionData *fsw2 = &functions[i+1]; functions[fswCapability - 1].clear();
memcpy(fsw1, fsw2, sizeof(CustomFunctionData));
for (int i = selectedIndex; i < (fswCapability - 1); i++) {
resetCBsAndRefresh(i); resetCBsAndRefresh(i);
} }
}
functions[maxidx].clear();
resetCBsAndRefresh(maxidx);
emit modified(); emit modified();
} }
void CustomFunctionsPanel::fswCopy() void CustomFunctionsPanel::cmCopy()
{ {
QByteArray fswData; QByteArray data;
fswData.append((char*)&functions[selectedFunction], sizeof(CustomFunctionData)); data.append((char*)&functions[selectedIndex], sizeof(CustomFunctionData));
QMimeData *mimeData = new QMimeData; QMimeData *mimeData = new QMimeData;
mimeData->setData(MIMETYPE_FSW, fswData); mimeData->setData(MIMETYPE_CUSTOM_FUNCTION, data);
QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
} }
void CustomFunctionsPanel::fswCut() void CustomFunctionsPanel::cmCut()
{ {
fswCopy(); cmCopy();
fswClear(); cmClear();
} }
void CustomFunctionsPanel::fsw_customContextMenuRequested(QPoint pos) void CustomFunctionsPanel::onCustomContextMenuRequested(QPoint pos)
{ {
QLabel *label = (QLabel *)sender(); QLabel *label = (QLabel *)sender();
selectedFunction = label->property("index").toInt(); selectedIndex = label->property("index").toInt();
QPoint globalPos = label->mapToGlobal(pos); 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; QMenu contextMenu;
contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(fswCopy())); contextMenu.addAction(CompanionIcon("copy.png"), tr("Copy"),this,SLOT(cmCopy()));
contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(fswCut())); contextMenu.addAction(CompanionIcon("cut.png"), tr("Cut"),this,SLOT(cmCut()));
contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(fswPaste()))->setEnabled(hasData); contextMenu.addAction(CompanionIcon("paste.png"), tr("Paste"),this,SLOT(cmPaste()))->setEnabled(hasClipboardData());
contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(fswClear())); contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear"),this,SLOT(cmClear()));
contextMenu.addSeparator(); contextMenu.addSeparator();
contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"),this,SLOT(fswInsert()))->setEnabled(insertAllowed); contextMenu.addAction(CompanionIcon("arrow-right.png"), tr("Insert"),this,SLOT(cmInsert()))->setEnabled(insertAllowed());
contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"),this,SLOT(fswDelete())); contextMenu.addAction(CompanionIcon("arrow-left.png"), tr("Delete"),this,SLOT(cmDelete()));
contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"),this,SLOT(fswMoveUp()))->setEnabled(moveUpAllowed); contextMenu.addAction(CompanionIcon("moveup.png"), tr("Move Up"),this,SLOT(cmMoveUp()))->setEnabled(moveUpAllowed());
contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"),this,SLOT(fswMoveDown()))->setEnabled(moveDownAllowed); contextMenu.addAction(CompanionIcon("movedown.png"), tr("Move Down"),this,SLOT(cmMoveDown()))->setEnabled(moveDownAllowed());
contextMenu.addSeparator(); 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); 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(); if (QMessageBox::question(this, CPN_STR_APP_NAME, tr("Clear Special Function. Are you sure?"), QMessageBox::Yes | QMessageBox::No) == QMessageBox::No)
resetCBsAndRefresh(selectedFunction); return;
functions[selectedIndex].clear();
resetCBsAndRefresh(selectedIndex);
emit modified(); 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++) { for (int i=0; i<fswCapability; i++) {
functions[i].clear(); functions[i].clear();
resetCBsAndRefresh(i); resetCBsAndRefresh(i);
@ -788,18 +781,17 @@ void CustomFunctionsPanel::fswClearAll()
emit modified(); emit modified();
} }
void CustomFunctionsPanel::fswInsert() void CustomFunctionsPanel::cmInsert()
{ {
for (int i=(fswCapability - 1); i>selectedFunction; i--) { memmove(&functions[selectedIndex + 1], &functions[selectedIndex], (CPN_MAX_SPECIAL_FUNCTIONS - (selectedIndex + 1)) * sizeof(CustomFunctionData));
if (!functions[i].isEmpty() || !functions[i-1].isEmpty()) { functions[selectedIndex].clear();
memcpy(&functions[i], &functions[i-1], sizeof(CustomFunctionData));
for (int i = selectedIndex; i < (fswCapability - 1); i++) {
resetCBsAndRefresh(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())) { if ((idx1 != idx2) && (!functions[idx1].isEmpty() || !functions[idx2].isEmpty())) {
CustomFunctionData fswtmp = functions[idx2]; CustomFunctionData fswtmp = functions[idx2];
@ -823,3 +815,30 @@ void CustomFunctionsPanel::resetCBsAndRefresh(int idx)
refreshCustomFunction(idx); refreshCustomFunction(idx);
lock = false; 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 RawSwitchFilterItemModel;
class TimerEdit; class TimerEdit;
constexpr char MIMETYPE_FSW[] = "application/x-companion-fsw"; constexpr char MIMETYPE_CUSTOM_FUNCTION[] = "application/x-companion-custom-function";
class RepeatComboBox: public QComboBox class RepeatComboBox: public QComboBox
{ {
@ -67,7 +67,7 @@ class CustomFunctionsPanel : public GenericPanel
void updateDataModels(); void updateDataModels();
void customFunctionEdited(); void customFunctionEdited();
void functionEdited(); void functionEdited();
void fsw_customContextMenuRequested(QPoint pos); void onCustomContextMenuRequested(QPoint pos);
void refreshCustomFunction(int index, bool modified=false); void refreshCustomFunction(int index, bool modified=false);
void onChildModified(); void onChildModified();
bool playSound(int index); bool playSound(int index);
@ -75,21 +75,25 @@ class CustomFunctionsPanel : public GenericPanel
void toggleSound(bool play); void toggleSound(bool play);
void onMediaPlayerStateChanged(QMediaPlayer::State state); void onMediaPlayerStateChanged(QMediaPlayer::State state);
void onMediaPlayerError(QMediaPlayer::Error error); void onMediaPlayerError(QMediaPlayer::Error error);
void fswDelete(); void cmDelete();
void fswCopy(); void cmCopy();
void fswPaste(); void cmPaste();
void fswCut(); void cmCut();
void fswMoveUp(); void cmMoveUp();
void fswMoveDown(); void cmMoveDown();
void fswInsert(); void cmInsert();
void fswClear(); void cmClear();
void fswClearAll(); void cmClearAll();
private: private:
void populateFuncCB(QComboBox *b, unsigned int value); void populateFuncCB(QComboBox *b, unsigned int value);
void populateGVmodeCB(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 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); void resetCBsAndRefresh(int idx);
RawSwitchFilterItemModel * rawSwitchItemModel; RawSwitchFilterItemModel * rawSwitchItemModel;
RawSourceFilterItemModel * rawSrcAllItemModel; RawSourceFilterItemModel * rawSrcAllItemModel;
@ -113,7 +117,7 @@ class CustomFunctionsPanel : public GenericPanel
QSlider * fswtchBLcolor[CPN_MAX_SPECIAL_FUNCTIONS]; QSlider * fswtchBLcolor[CPN_MAX_SPECIAL_FUNCTIONS];
QMediaPlayer * mediaPlayer; QMediaPlayer * mediaPlayer;
int selectedFunction; int selectedIndex;
int fswCapability; int fswCapability;
}; };

File diff suppressed because it is too large Load diff

View file

@ -26,6 +26,10 @@
class RawSwitchFilterItemModel; 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 { namespace Ui {
class FlightMode; class FlightMode;
} }
@ -42,6 +46,7 @@ class FlightModePanel : public ModelPanel
signals: signals:
void nameModified(); void nameModified();
void datachanged();
private slots: private slots:
void phaseName_editingFinished(); void phaseName_editingFinished();
@ -61,18 +66,36 @@ class FlightModePanel : public ModelPanel
void phaseGVMax_editingFinished(); void phaseGVMax_editingFinished();
void phaseREValue_editingFinished(); void phaseREValue_editingFinished();
void phaseREUse_currentIndexChanged(int index); void phaseREUse_currentIndexChanged(int index);
void name_customContextMenuRequested(const QPoint & pos); void onCustomContextMenuRequested(QPoint pos);
void fmClear(); void cmClear();
void gvLabel_customContextMenuRequested(const QPoint & pos); void cmClearAll();
void gvClear(); 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: private:
Ui::FlightMode *ui; Ui::FlightMode *ui;
int phaseIdx; int phaseIdx;
FlightModeData & phase; FlightModeData & phase;
int fmCount;
int reCount; int reCount;
int gvCount; int gvCount;
int gvIdx; int gvIdx;
int trimCount;
QVector<QLabel *> trimsLabel; QVector<QLabel *> trimsLabel;
QLineEdit * gvNames[CPN_MAX_GVARS]; QLineEdit * gvNames[CPN_MAX_GVARS];
QDoubleSpinBox * gvValues[CPN_MAX_GVARS]; QDoubleSpinBox * gvValues[CPN_MAX_GVARS];
@ -88,12 +111,27 @@ class FlightModePanel : public ModelPanel
QVector<QSpinBox *> trimsValue; QVector<QSpinBox *> trimsValue;
QVector<QSlider *> trimsSlider; QVector<QSlider *> trimsSlider;
RawSwitchFilterItemModel * rawSwitchItemModel; RawSwitchFilterItemModel * rawSwitchItemModel;
Board::Type board;
void trimUpdate(unsigned int trim); void trimUpdate(unsigned int trim);
void updateGVar(int index); void updateGVar(int index);
void updateRotaryEncoder(int index);
void setGVSB(QDoubleSpinBox * spinBox, int min, int max, int val); void setGVSB(QDoubleSpinBox * spinBox, int min, int max, int val);
void populateGvarUnitCB(QComboBox * cb); void populateGvarUnitCB(QComboBox * cb);
void populateGvarPrecCB(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 class FlightModesPanel : public ModelPanel

View file

@ -47,18 +47,18 @@ InputsPanel::InputsPanel(QWidget *parent, ModelData & model, GeneralSettings & g
qbClear->setText(tr("Clear All Inputs")); qbClear->setText(tr("Clear All Inputs"));
qbClear->setIcon(CompanionIcon("clear.png")); qbClear->setIcon(CompanionIcon("clear.png"));
exposLayout->addWidget(ExposlistWidget,1,1,1,3); exposLayout->addWidget(ExposlistWidget, 1, 1, 1, 3);
exposLayout->addWidget(qbUp,2,1); exposLayout->addWidget(qbUp, 2, 1);
exposLayout->addWidget(qbClear,2,2); exposLayout->addWidget(qbClear, 2, 2);
exposLayout->addWidget(qbDown,2,3); exposLayout->addWidget(qbDown, 2, 3);
connect(ExposlistWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(expolistWidget_customContextMenuRequested(QPoint))); connect(ExposlistWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(expolistWidget_customContextMenuRequested(QPoint)));
connect(ExposlistWidget, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(expolistWidget_doubleClicked(QModelIndex))); connect(ExposlistWidget, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(expolistWidget_doubleClicked(QModelIndex)));
connect(ExposlistWidget, SIGNAL(mimeDropped(int,const QMimeData*,Qt::DropAction)), this, SLOT(mimeExpoDropped(int, const QMimeData*, Qt::DropAction))); connect(ExposlistWidget, SIGNAL(mimeDropped(int, const QMimeData*,Qt::DropAction)), this, SLOT(mimeExpoDropped(int, const QMimeData*, Qt::DropAction)));
connect(qbUp, SIGNAL(pressed()),SLOT(moveExpoUp())); connect(qbUp, SIGNAL(pressed()), SLOT(moveExpoUp()));
connect(qbDown, SIGNAL(pressed()),SLOT(moveExpoDown())); connect(qbDown, SIGNAL(pressed()), SLOT(moveExpoDown()));
connect(qbClear, SIGNAL(pressed()),SLOT(clearExpos())); connect(qbClear, SIGNAL(pressed()), SLOT(clearExpos()));
connect(ExposlistWidget, SIGNAL(keyWasPressed(QKeyEvent*)), this, SLOT(expolistWidget_KeyPress(QKeyEvent*))); connect(ExposlistWidget, SIGNAL(keyWasPressed(QKeyEvent*)), this, SLOT(expolistWidget_KeyPress(QKeyEvent*)));
} }
@ -71,9 +71,9 @@ void InputsPanel::update()
{ {
lock = true; lock = true;
ExposlistWidget->clear(); ExposlistWidget->clear();
for (int i=0; i < inputsCount; ++i) { for (int i = 0; i < inputsCount; ++i) {
bool filled = false; bool filled = false;
for (uint j=0; j < CPN_MAX_EXPOS; ++j) { for (uint j = 0; j < CPN_MAX_EXPOS; ++j) {
const ExpoData & ed = model->expoData[j]; const ExpoData & ed = model->expoData[j];
if ((int)ed.chn == i && ed.mode) { if ((int)ed.chn == i && ed.mode) {
AddInputLine(j); AddInputLine(j);
@ -81,7 +81,7 @@ void InputsPanel::update()
} }
} }
if (!filled) { if (!filled) {
AddInputLine(-i-1); AddInputLine(-i - 1);
} }
} }
lock = false; lock = false;
@ -108,9 +108,8 @@ void InputsPanel::AddInputLine(int dest)
const ExpoData &md = model->expoData[dest]; const ExpoData &md = model->expoData[dest];
qba.append((const char*)&md, sizeof(ExpoData)); qba.append((const char*)&md, sizeof(ExpoData));
destId = md.chn + 1; destId = md.chn + 1;
const QVector<const ExpoData *> expos = model->expos(md.chn); newChan = model->isExpoParent(dest);
newChan = (expos.constFirst() == &md); hasSibs = (model->hasExpoChildren(dest) || model->hasExpoSiblings(dest));
hasSibs = (expos.constLast() != &md);
} }
QListWidgetItem *itm = new QListWidgetItem(getInputText(dest, newChan)); QListWidgetItem *itm = new QListWidgetItem(getInputText(dest, newChan));
itm->setData(Qt::UserRole, qba); itm->setData(Qt::UserRole, qba);
@ -133,10 +132,10 @@ QString InputsPanel::getInputText(int dest, bool newChan)
{ {
QString str; QString str;
if (dest < 0) { if (dest < 0) {
str = modelPrinter.printInputName(-dest-1); str = modelPrinter.printInputName(-dest - 1);
} }
else { else {
const ExpoData & input = model->expoData[dest]; const ExpoData &input = model->expoData[dest];
const int nameChars = (firmware->getCapability(VirtualInputs) ? 10 : 4); const int nameChars = (firmware->getCapability(VirtualInputs) ? 10 : 4);
if (newChan) if (newChan)
str = QString("%1").arg(modelPrinter.printInputName(input.chn), -nameChars, QChar(' ')); str = QString("%1").arg(modelPrinter.printInputName(input.chn), -nameChars, QChar(' '));
@ -150,14 +149,14 @@ QString InputsPanel::getInputText(int dest, bool newChan)
bool InputsPanel::gm_insertExpo(int idx) bool InputsPanel::gm_insertExpo(int idx)
{ {
if (idx<0 || idx>=CPN_MAX_EXPOS || model->expoData[CPN_MAX_EXPOS-1].mode > 0) { 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; return false;
} }
int chn = model->expoData[idx].chn; int chn = model->expoData[idx].chn;
ExpoData * newExpo = model->insertInput(idx); ExpoData *newExpo = model->insertInput(idx);
newExpo->chn = chn; newExpo->chn = chn;
newExpo->weight = 100; newExpo->weight = 100;
newExpo->mode = INPUT_MODE_BOTH; newExpo->mode = INPUT_MODE_BOTH;
@ -172,19 +171,20 @@ void InputsPanel::gm_deleteExpo(int index, bool clearName)
void InputsPanel::gm_openExpo(int index) 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; QString inputName;
if (firmware->getCapability(VirtualInputs)) 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()) { if (g->exec()) {
model->expoData[index] = mixd; model->expoData[index] = ed;
if (firmware->getCapability(VirtualInputs)) 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(); emit modified();
update(); update();
} }
@ -200,28 +200,29 @@ void InputsPanel::gm_openExpo(int index)
int InputsPanel::getExpoIndex(unsigned int dch) int InputsPanel::getExpoIndex(unsigned int dch)
{ {
unsigned int i = 0; unsigned int i = 0;
while (model->expoData[i].chn<=dch && model->expoData[i].mode && i<CPN_MAX_EXPOS) i++; while (model->expoData[i].chn <= dch && model->expoData[i].mode && i < CPN_MAX_EXPOS)
if(i==CPN_MAX_EXPOS) return -1; i++;
if (i == CPN_MAX_EXPOS)
return -1;
return i; return i;
} }
QList<int> InputsPanel::createExpoListFromSelected() QList<int> InputsPanel::createExpoListFromSelected()
{ {
QList<int> list; QList<int> list;
foreach(QListWidgetItem *item, ExposlistWidget->selectedItems()) { foreach (QListWidgetItem *item, ExposlistWidget->selectedItems()) {
int idx= item->data(Qt::UserRole).toByteArray().at(0); 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; return list;
} }
void InputsPanel::setSelectedByExpoList(QList<int> list) void InputsPanel::setSelectedByExpoList(QList<int> list)
{ {
for(int i=0; i<ExposlistWidget->count(); i++) { for (int i = 0; i < ExposlistWidget->count(); i++) {
int t = ExposlistWidget->item(i)->data(Qt::UserRole).toByteArray().at(0); int t = ExposlistWidget->item(i)->data(Qt::UserRole).toByteArray().at(0);
if(list.contains(t)) if (list.contains(t))
ExposlistWidget->item(i)->setSelected(true); ExposlistWidget->item(i)->setSelected(true);
} }
} }
@ -229,15 +230,13 @@ void InputsPanel::setSelectedByExpoList(QList<int> list)
void InputsPanel::exposDelete(bool ask) void InputsPanel::exposDelete(bool ask)
{ {
QList<int> list = createExpoListFromSelected(); QList<int> list = createExpoListFromSelected();
if(list.isEmpty()) return; if (list.isEmpty())
return;
QMessageBox::StandardButton ret = QMessageBox::No; QMessageBox::StandardButton ret = QMessageBox::No;
if(ask) if (ask)
ret = QMessageBox::warning(this, "companion", ret = QMessageBox::warning(this, CPN_STR_APP_NAME, tr("Delete selected Inputs. Are you sure?"), QMessageBox::Yes | QMessageBox::No);
tr("Delete Selected Inputs?"),
QMessageBox::Yes | QMessageBox::No);
if ((ret == QMessageBox::Yes) || (!ask)) { if ((ret == QMessageBox::Yes) || (!ask)) {
exposDeleteList(list, ask); exposDeleteList(list, ask);
@ -256,23 +255,23 @@ void InputsPanel::exposCopy()
{ {
QList<int> list = createExpoListFromSelected(); QList<int> list = createExpoListFromSelected();
QByteArray mxData; QByteArray exData;
foreach(int idx, list) { foreach (int idx, list) {
mxData.append((char*)&model->expoData[idx], sizeof(ExpoData)); exData.append((char*)&model->expoData[idx], sizeof(ExpoData));
} }
QMimeData *mimeData = new QMimeData; QMimeData *mimeData = new QMimeData;
mimeData->setData("application/x-companion-expo", mxData); mimeData->setData(MIMETYPE_EXPO, exData);
QApplication::clipboard()->setMimeData(mimeData,QClipboard::Clipboard); QApplication::clipboard()->setMimeData(mimeData, QClipboard::Clipboard);
} }
void InputsPanel::mimeExpoDropped(int index, const QMimeData *data, Qt::DropAction action) void InputsPanel::mimeExpoDropped(int index, const QMimeData *data, Qt::DropAction action)
{ {
int idx = ExposlistWidget->item(index)->data(Qt::UserRole).toByteArray().at(0); int idx = ExposlistWidget->item(index)->data(Qt::UserRole).toByteArray().at(0);
if (action==Qt::CopyAction) { if (action == Qt::CopyAction) {
pasteExpoMimeData(data, idx); pasteExpoMimeData(data, idx);
} }
else if (action==Qt::MoveAction) { else if (action == Qt::MoveAction) {
QList<int> list = createExpoListFromSelected(); QList<int> list = createExpoListFromSelected();
exposDeleteList(list, false); exposDeleteList(list, false);
foreach (const int del, list) { foreach (const int del, list) {
@ -285,30 +284,31 @@ void InputsPanel::mimeExpoDropped(int index, const QMimeData *data, Qt::DropActi
void InputsPanel::pasteExpoMimeData(const QMimeData * mimeData, int destIdx) void InputsPanel::pasteExpoMimeData(const QMimeData * mimeData, int destIdx)
{ {
if (mimeData->hasFormat("application/x-companion-expo")) { if (mimeData->hasFormat(MIMETYPE_EXPO)) {
int idx; // mixer index int idx; // expo index
int dch; int dch;
if (destIdx < 0) { if (destIdx < 0) {
dch = -destIdx - 1; dch = -destIdx - 1;
idx = getExpoIndex(dch) - 1; //get expo index to insert idx = getExpoIndex(dch) - 1; //get expo index to insert
} else { }
else {
idx = destIdx; idx = destIdx;
dch = model->expoData[idx].chn; dch = model->expoData[idx].chn;
} }
QByteArray mxData = mimeData->data("application/x-companion-expo"); QByteArray exData = mimeData->data(MIMETYPE_EXPO);
int i = 0; int i = 0;
while (i < mxData.size()) { while (i < exData.size()) {
idx++; idx++;
if (!gm_insertExpo(idx)) if (!gm_insertExpo(idx))
break; break;
ExpoData *md = &model->expoData[idx]; ExpoData *ed = &model->expoData[idx];
memcpy(md, mxData.mid(i, sizeof(ExpoData)).constData(), sizeof(ExpoData)); memcpy(ed, exData.mid(i, sizeof(ExpoData)).constData(), sizeof(ExpoData));
const int oldChan = md->chn; const int oldChan = ed->chn;
md->chn = dch; ed->chn = dch;
maybeCopyInputName(oldChan, md->chn); maybeCopyInputName(oldChan, ed->chn);
i += sizeof(ExpoData); i += sizeof(ExpoData);
} }
@ -334,22 +334,22 @@ void InputsPanel::exposDuplicate()
exposPaste(); exposPaste();
} }
void InputsPanel::expoOpen(QListWidgetItem *item) void InputsPanel::expoOpen(QListWidgetItem *item)
{ {
if (!item) if (!item)
item = ExposlistWidget->currentItem(); item = ExposlistWidget->currentItem();
int idx = item->data(Qt::UserRole).toByteArray().at(0); int idx = item->data(Qt::UserRole).toByteArray().at(0);
if (idx<0) { if (idx < 0) {
int ch = -idx-1; int ch = -idx - 1;
idx = getExpoIndex(ch); // get expo index to insert idx = getExpoIndex(ch); // get expo index to insert
if (!gm_insertExpo(idx)) if (!gm_insertExpo(idx))
return; return;
model->expoData[idx].chn = ch; model->expoData[idx].chn = ch;
expoInserted=true; expoInserted = true;
} else { }
expoInserted=false; else {
expoInserted = false;
} }
gm_openExpo(idx); gm_openExpo(idx);
} }
@ -358,14 +358,15 @@ void InputsPanel::expoAdd()
{ {
int index = ExposlistWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0); int index = ExposlistWidget->currentItem()->data(Qt::UserRole).toByteArray().at(0);
if(index<0) { // if empty then return relevant index if (index < 0) { // if empty then return relevant index
expoOpen(); expoOpen();
} else { }
else {
index++; index++;
if (!gm_insertExpo(index)) if (!gm_insertExpo(index))
return; return;
model->expoData[index].chn = model->expoData[index-1].chn; model->expoData[index].chn = model->expoData[index-1].chn;
expoInserted=true; expoInserted = true;
} }
gm_openExpo(index); gm_openExpo(index);
} }
@ -376,41 +377,61 @@ void InputsPanel::expolistWidget_customContextMenuRequested(QPoint pos)
const QClipboard *clipboard = QApplication::clipboard(); const QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData(); 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; QMenu contextMenu;
contextMenu.addAction(CompanionIcon("add.png"), tr("&Add"),this,SLOT(expoAdd()),tr("Ctrl+A")); QMenu *contextMenuLines = contextMenu.addMenu(tr("Lines"));
contextMenu.addAction(CompanionIcon("edit.png"), tr("&Edit"),this,SLOT(expoOpen()),tr("Enter")); 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.addSeparator();
contextMenu.addAction(CompanionIcon("clear.png"), tr("&Delete"),this,SLOT(exposDelete()),tr("Delete")); contextMenu.addAction(CompanionIcon("clear.png"), tr("Clear All"), this, SLOT(clearExpos()));
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.addSeparator(); contextMenu.addSeparator();
contextMenu.addActions(ExposlistWidget->actions()); contextMenu.addActions(ExposlistWidget->actions());
contextMenu.exec(globalPos); contextMenu.exec(globalPos);
} }
void InputsPanel::expolistWidget_KeyPress(QKeyEvent *event) void InputsPanel::expolistWidget_KeyPress(QKeyEvent *event)
{ {
if(event->matches(QKeySequence::SelectAll)) expoAdd(); //Ctrl A if (event->matches(QKeySequence::SelectAll))
if(event->matches(QKeySequence::Delete)) exposDelete(); expoAdd(); //Ctrl A
if(event->matches(QKeySequence::Copy)) exposCopy(); if (event->matches(QKeySequence::Delete))
if(event->matches(QKeySequence::Cut)) exposCut(); exposDelete();
if(event->matches(QKeySequence::Paste)) exposPaste(); if (event->matches(QKeySequence::Copy))
if(event->matches(QKeySequence::Underline)) exposDuplicate(); 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)
if(event->matches(QKeySequence::MoveToNextLine)) expoOpen();
ExposlistWidget->setCurrentRow(ExposlistWidget->currentRow()+1); if (event->matches(QKeySequence::MoveToNextLine))
if(event->matches(QKeySequence::MoveToPreviousLine)) ExposlistWidget->setCurrentRow(ExposlistWidget->currentRow() + 1);
ExposlistWidget->setCurrentRow(ExposlistWidget->currentRow()-1); if (event->matches(QKeySequence::MoveToPreviousLine))
ExposlistWidget->setCurrentRow(ExposlistWidget->currentRow() - 1);
} }
int InputsPanel::gm_moveExpo(int idx, bool dir) //true=inc=down false=dec=up int InputsPanel::gm_moveExpo(int idx, bool dir) //true=inc=down false=dec=up
@ -418,7 +439,7 @@ int InputsPanel::gm_moveExpo(int idx, bool dir) //true=inc=down false=dec=up
if (idx >= CPN_MAX_EXPOS || idx < 0 || (dir && idx >= CPN_MAX_EXPOS - 1) || (!dir && !idx)) if (idx >= CPN_MAX_EXPOS || idx < 0 || (dir && idx >= CPN_MAX_EXPOS - 1) || (!dir && !idx))
return -1; return -1;
const int tdx = dir ? idx+1 : idx-1; const int tdx = dir ? idx + 1 : idx-1;
ExpoData temp; ExpoData temp;
ExpoData &src = model->expoData[idx]; ExpoData &src = model->expoData[idx];
ExpoData &tgt = model->expoData[tdx]; ExpoData &tgt = model->expoData[tdx];
@ -432,7 +453,7 @@ int InputsPanel::gm_moveExpo(int idx, bool dir) //true=inc=down false=dec=up
if (tgt.chn != src.chn || tgtempty) { if (tgt.chn != src.chn || tgtempty) {
const int oldChan = src.chn; const int oldChan = src.chn;
if (dir && src.chn < unsigned(inputsCount-1)) if (dir && src.chn < unsigned(inputsCount - 1))
src.chn++; src.chn++;
else if (!dir && src.chn > 0) else if (!dir && src.chn > 0)
src.chn--; src.chn--;
@ -452,7 +473,7 @@ void InputsPanel::moveExpoList(bool down)
QList<int> list = createExpoListFromSelected(); QList<int> list = createExpoListFromSelected();
QList<int> highlightList; QList<int> highlightList;
bool mod = false; bool mod = false;
foreach(int idx, list) { foreach (int idx, list) {
const int newIdx = gm_moveExpo(idx, down); const int newIdx = gm_moveExpo(idx, down);
if (newIdx > -1) { if (newIdx > -1) {
highlightList << newIdx; highlightList << newIdx;
@ -486,7 +507,7 @@ void InputsPanel::exposDeleteList(QList<int> list, bool clearName)
std::sort(list.begin(), list.end()); std::sort(list.begin(), list.end());
int iDec = 0; int iDec = 0;
foreach(int idx, list) { foreach (int idx, list) {
gm_deleteExpo(idx-iDec, clearName); gm_deleteExpo(idx-iDec, clearName);
iDec++; iDec++;
} }
@ -494,8 +515,11 @@ void InputsPanel::exposDeleteList(QList<int> list, bool clearName)
void InputsPanel::clearExpos() 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(); model->clearInputs();
for (int i = 0; i < inputsCount; i++) {
model->updateAllReferences(ModelData::REF_UPD_TYPE_INPUT, ModelData::REF_UPD_ACT_CLEAR, i);
}
emit modified(); emit modified();
update(); update();
} }
@ -512,8 +536,172 @@ void InputsPanel::maybeCopyInputName(int srcChan, int destChan)
if (srcEmpty) { if (srcEmpty) {
// if destination input name is empty, copy it from source expo input // if destination input name is empty, copy it from source expo input
if (!strlen(model->inputNames[destChan])) 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 // clear the emptry source channel name
model->inputNames[srcChan][0] = 0; 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 "mixerslistwidget.h"
#include "modelprinter.h" #include "modelprinter.h"
constexpr char MIMETYPE_EXPO[] = "application/x-companion-expo";
class InputsPanel : public ModelPanel class InputsPanel : public ModelPanel
{ {
Q_OBJECT Q_OBJECT
@ -52,12 +54,19 @@ class InputsPanel : public ModelPanel
void expoOpen(QListWidgetItem *item = NULL); void expoOpen(QListWidgetItem *item = NULL);
void expoAdd(); void expoAdd();
void maybeCopyInputName(int srcChan, int destChan); void maybeCopyInputName(int srcChan, int destChan);
void cmInputClear();
void cmInputDelete();
void cmInputInsert();
void cmInputMoveDown();
void cmInputMoveUp();
private: private:
bool expoInserted; bool expoInserted;
MixersListWidget *ExposlistWidget; MixersListWidget *ExposlistWidget;
int inputsCount; int inputsCount;
ModelPrinter modelPrinter; ModelPrinter modelPrinter;
int selectedIdx;
int inputIdx;
int getExpoIndex(unsigned int dch); int getExpoIndex(unsigned int dch);
bool gm_insertExpo(int idx); bool gm_insertExpo(int idx);
@ -70,7 +79,14 @@ class InputsPanel : public ModelPanel
void pasteExpoMimeData(const QMimeData * mimeData, int destIdx); void pasteExpoMimeData(const QMimeData * mimeData, int destIdx);
void AddInputLine(int dest); void AddInputLine(int dest);
QString getInputText(int dest, bool newChan); 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_ #endif // _INPUTS_H_

View file

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

View file

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

View file

@ -49,7 +49,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge
} }
else { else {
ui->name->setMaxLength(length); ui->name->setMaxLength(length);
ui->name->setText(timer.name); //ui->name->setText(timer.name);
} }
// Mode // Mode
@ -85,6 +85,7 @@ TimerPanel::TimerPanel(QWidget *parent, ModelData & model, TimerData & timer, Ge
QWidget::setTabOrder(ui->countdownBeep, ui->minuteBeep); QWidget::setTabOrder(ui->countdownBeep, ui->minuteBeep);
QWidget::setTabOrder(ui->minuteBeep, ui->persistent); QWidget::setTabOrder(ui->minuteBeep, ui->persistent);
update();
lock = false; lock = false;
} }
@ -95,6 +96,9 @@ TimerPanel::~TimerPanel()
void TimerPanel::update() void TimerPanel::update()
{ {
lock = true;
ui->name->setText(timer.name);
int hour = timer.val / 3600; int hour = timer.val / 3600;
int min = (timer.val - (hour * 3600)) / 60; int min = (timer.val - (hour * 3600)) / 60;
int sec = (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->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->minuteBeep->setChecked(timer.minuteBeep);
ui->persistent->updateValue();
lock = false;
} }
QWidget * TimerPanel::getLastFocus() QWidget * TimerPanel::getLastFocus()
@ -126,39 +133,44 @@ QWidget * TimerPanel::getLastFocus()
void TimerPanel::on_value_editingFinished() void TimerPanel::on_value_editingFinished()
{ {
if (!lock) {
unsigned val = ui->value->time().hour()*3600 + ui->value->time().minute()*60 + ui->value->time().second(); unsigned val = ui->value->time().hour()*3600 + ui->value->time().minute()*60 + ui->value->time().second();
if (timer.val != val) { if (timer.val != val) {
timer.val = val; timer.val = val;
emit modified(); emit modified();
} }
}
} }
void TimerPanel::onModeChanged(int index) void TimerPanel::onModeChanged(int index)
{ {
if (lock) if (!lock) {
return;
bool ok; bool ok;
const RawSwitch rs(ui->mode->itemData(index).toInt(&ok)); const RawSwitch rs(ui->mode->itemData(index).toInt(&ok));
if (ok && timer.mode.toValue() != rs.toValue()) { if (ok && timer.mode.toValue() != rs.toValue()) {
timer.mode = rs; timer.mode = rs;
emit modified(); emit modified();
} }
}
} }
void TimerPanel::on_minuteBeep_toggled(bool checked) void TimerPanel::on_minuteBeep_toggled(bool checked)
{ {
if (!lock) {
timer.minuteBeep = checked; timer.minuteBeep = checked;
emit modified(); emit modified();
}
} }
void TimerPanel::on_name_editingFinished() void TimerPanel::on_name_editingFinished()
{ {
if (!lock) {
if (QString(timer.name) != ui->name->text()) { if (QString(timer.name) != ui->name->text()) {
int length = ui->name->maxLength(); int length = ui->name->maxLength();
strncpy(timer.name, ui->name->text().toLatin1(), length); strncpy(timer.name, ui->name->text().toLatin1(), length);
emit modified(); emit modified();
} }
}
} }
/******************************************************************************/ /******************************************************************************/
@ -273,27 +285,12 @@ ModulePanel::~ModulePanel()
delete ui; 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() void ModulePanel::setupFailsafes()
{ {
ChannelFailsafeWidgetsGroup grp; ChannelFailsafeWidgetsGroup grp;
const int start = module.channelsStart; const int start = module.channelsStart;
const int end = start + module.channelsCount; const int end = start + module.channelsCount;
const bool hasFailsafe = moduleHasFailsafes(); const bool hasFailsafe = module.hasFailsafes(firmware);
lock = true; lock = true;
@ -382,48 +379,6 @@ void ModulePanel::setupFailsafes()
lock = false; 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() void ModulePanel::update()
{ {
const PulsesProtocol protocol = (PulsesProtocol)module.protocol; const PulsesProtocol protocol = (PulsesProtocol)module.protocol;
@ -510,7 +465,7 @@ void ModulePanel::update()
mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT; mask |= MASK_PPM_FIELDS | MASK_CHANNELS_RANGE | MASK_CHANNELS_COUNT;
} }
if (moduleHasFailsafes()) { if (module.hasFailsafes(firmware)) {
mask |= MASK_FAILSAFES; mask |= MASK_FAILSAFES;
} }
@ -527,7 +482,7 @@ void ModulePanel::update()
ui->label_channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); ui->label_channelsCount->setVisible(mask & MASK_CHANNELS_RANGE);
ui->channelsCount->setVisible(mask & MASK_CHANNELS_RANGE); ui->channelsCount->setVisible(mask & MASK_CHANNELS_RANGE);
ui->channelsCount->setEnabled(mask & MASK_CHANNELS_COUNT); ui->channelsCount->setEnabled(mask & MASK_CHANNELS_COUNT);
ui->channelsCount->setMaximum(getMaxChannelCount()); ui->channelsCount->setMaximum(module.getMaxChannelCount());
ui->channelsCount->setValue(module.channelsCount); ui->channelsCount->setValue(module.channelsCount);
ui->channelsCount->setSingleStep(firmware->getCapability(HasPPMStart) ? 1 : 2); 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()) { if (!lock && module.protocol != ui->protocol->itemData(index).toUInt()) {
module.protocol = ui->protocol->itemData(index).toInt(); module.protocol = ui->protocol->itemData(index).toInt();
module.channelsCount = getMaxChannelCount(); module.channelsCount = module.getMaxChannelCount();
update(); update();
emit modified(); emit modified();
} }
@ -790,7 +745,7 @@ void ModulePanel::onMultiProtocolChanged(int index)
if (rfProtocol > MODULE_SUBTYPE_MULTI_LAST) if (rfProtocol > MODULE_SUBTYPE_MULTI_LAST)
maxSubTypes=8; maxSubTypes=8;
module.subType = std::min(module.subType, maxSubTypes -1); module.subType = std::min(module.subType, maxSubTypes -1);
module.channelsCount = getMaxChannelCount(); module.channelsCount = module.getMaxChannelCount();
update(); update();
emit modified(); emit modified();
lock = false; lock = false;
@ -1064,16 +1019,28 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
RawSwitchFilterItemModel * swModel = new RawSwitchFilterItemModel(&generalSettings, &model, RawSwitch::TimersContext, this); RawSwitchFilterItemModel * swModel = new RawSwitchFilterItemModel(&generalSettings, &model, RawSwitch::TimersContext, this);
connect(this, &SetupPanel::updated, swModel, &RawSwitchFilterItemModel::update); connect(this, &SetupPanel::updated, swModel, &RawSwitchFilterItemModel::update);
for (int i=0; i<CPN_MAX_TIMERS; i++) { timersCount = firmware->getCapability(Timers);
if (i<firmware->getCapability(Timers)) {
for (int i = 0; i < CPN_MAX_TIMERS; i++) {
if (i < timersCount) {
timers[i] = new TimerPanel(this, model, model.timers[i], generalSettings, firmware, prevFocus, swModel); timers[i] = new TimerPanel(this, model, model.timers[i], generalSettings, firmware, prevFocus, swModel);
ui->gridLayout->addWidget(timers[i], 1+i, 1); ui->gridLayout->addWidget(timers[i], 1+i, 1);
connect(timers[i], &TimerPanel::modified, this, &SetupPanel::modified); connect(timers[i], &TimerPanel::modified, this, &SetupPanel::modified);
connect(this, &SetupPanel::updated, timers[i], &TimerPanel::update); connect(this, &SetupPanel::updated, timers[i], &TimerPanel::update);
connect(this, &SetupPanel::timerUpdated, timers[i], &TimerPanel::update);
prevFocus = timers[i]->getLastFocus(); 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 { 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(); label->hide();
} }
} }
@ -1081,9 +1048,9 @@ SetupPanel::SetupPanel(QWidget * parent, ModelData & model, GeneralSettings & ge
if (firmware->getCapability(HasTopLcd)) { if (firmware->getCapability(HasTopLcd)) {
ui->toplcdTimer->setField(model.toplcdTimer, this); ui->toplcdTimer->setField(model.toplcdTimer, this);
for (int i=0; i<CPN_MAX_TIMERS; i++) { 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); 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 "modeledit.h"
#include "eeprominterface.h" #include "eeprominterface.h"
constexpr char MIMETYPE_TIMER[] = "application/x-companion-timer";
class RawSwitchFilterItemModel; class RawSwitchFilterItemModel;
namespace Ui { namespace Ui {
@ -62,7 +64,6 @@ class ModulePanel : public ModelPanel
ModulePanel(QWidget *parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx); ModulePanel(QWidget *parent, ModelData & model, ModuleData & module, GeneralSettings & generalSettings, Firmware * firmware, int moduleIdx);
virtual ~ModulePanel(); virtual ~ModulePanel();
virtual void update(); virtual void update();
bool moduleHasFailsafes();
public slots: public slots:
void onExtendedLimitsToggled(); void onExtendedLimitsToggled();
@ -71,7 +72,6 @@ class ModulePanel : public ModelPanel
void channelsRangeChanged(); void channelsRangeChanged();
private slots: private slots:
int getMaxChannelCount();
void setupFailsafes(); void setupFailsafes();
void on_trainerMode_currentIndexChanged(int index); void on_trainerMode_currentIndexChanged(int index);
void onProtocolChanged(int index); void onProtocolChanged(int index);
@ -129,6 +129,7 @@ class SetupPanel : public ModelPanel
signals: signals:
void extendedLimitsToggled(); void extendedLimitsToggled();
void updated(); void updated();
void timerUpdated();
private slots: private slots:
void on_name_editingFinished(); void on_name_editingFinished();
@ -148,6 +149,16 @@ class SetupPanel : public ModelPanel
void potWarningToggled(bool checked); void potWarningToggled(bool checked);
void on_potWarningMode_currentIndexChanged(int index); void on_potWarningMode_currentIndexChanged(int index);
void on_editText_clicked(); 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: private:
Ui::Setup *ui; Ui::Setup *ui;
@ -161,6 +172,13 @@ class SetupPanel : public ModelPanel
void updatePotWarnings(); void updatePotWarnings();
void updateBeepCenter(); void updateBeepCenter();
void populateThrottleSourceCB(); 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_ #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> </widget>
</item> </item>
<item row="3" column="0"> <item row="3" column="0">
<widget class="QLabel" name="label_timer3_2"> <widget class="QLabel" name="label_timer3">
<property name="font"> <property name="font">
<font> <font>
<weight>50</weight> <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), ModelPanel(parent, model, generalSettings, firmware),
ui(new Ui::TelemetrySensor), ui(new Ui::TelemetrySensor),
sensor(sensor), sensor(sensor),
lock(false) lock(false),
sensorIndex(sensorIndex),
selectedIndex(0)
{ {
ui->setupUi(this); 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->id->setField(sensor.id, this);
ui->instance->setField(sensor.instance, this); ui->instance->setField(sensor.instance, this);
ui->ratio->setField(sensor.ratio, this); ui->ratio->setField(sensor.ratio, this);
@ -613,7 +628,7 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor,
ui->ampsSensor->setField(sensor.amps, this); ui->ampsSensor->setField(sensor.amps, this);
ui->cellsSensor->setField(sensor.source, this); ui->cellsSensor->setField(sensor.source, this);
ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST); ui->cellsIndex->addItem(tr("Lowest"), SensorData::TELEM_CELL_INDEX_LOWEST);
for (int i=1; i<=6; i++) for (int i = 1; i <= 6; i++)
ui->cellsIndex->addItem(tr("Cell %1").arg(i), i); ui->cellsIndex->addItem(tr("Cell %1").arg(i), i);
ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST); ui->cellsIndex->addItem(tr("Highest"), SensorData::TELEM_CELL_INDEX_HIGHEST);
ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA); ui->cellsIndex->addItem(tr("Delta"), SensorData::TELEM_CELL_INDEX_DELTA);
@ -622,6 +637,7 @@ TelemetrySensorPanel::TelemetrySensorPanel(QWidget *parent, SensorData & sensor,
ui->source2->setField(sensor.sources[1], this); ui->source2->setField(sensor.sources[1], this);
ui->source3->setField(sensor.sources[2], this); ui->source3->setField(sensor.sources[2], this);
ui->source4->setField(sensor.sources[3], this); ui->source4->setField(sensor.sources[3], this);
ui->prec->setField(sensor.prec, 0, 2, false, "", this);
update(); update();
} }
@ -645,7 +661,21 @@ void TelemetrySensorPanel::update()
ui->name->setText(sensor.label); ui->name->setText(sensor.label);
ui->type->setCurrentIndex(sensor.type); ui->type->setCurrentIndex(sensor.type);
ui->unit->setCurrentIndex(sensor.unit); 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) { if (sensor.type == SensorData::TELEM_TYPE_CALCULATED) {
sensor.updateUnit(); 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) { 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 dataModified();
emit modified(); 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): TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettings & generalSettings, Firmware * firmware):
@ -851,12 +951,13 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin
ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this); ui->varioCenterSilent->setField(model.frsky.varioCenterSilent, this);
ui->A1GB->hide(); ui->A1GB->hide();
ui->A2GB->hide(); ui->A2GB->hide();
for (unsigned i=0; i<CPN_MAX_SENSORS; ++i) { 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); ui->sensorsLayout->addWidget(panel);
sensorPanels[i] = panel; sensorPanels[i] = panel;
connect(panel, SIGNAL(dataModified()), this, SLOT(update())); connect(panel, SIGNAL(dataModified()), this, SLOT(update()));
connect(panel, SIGNAL(modified()), this, SLOT(onModified())); connect(panel, SIGNAL(modified()), this, SLOT(onModified()));
connect(panel, SIGNAL(clearAllSensors()), this, SLOT(on_clearAllSensors()));
} }
} }
else { else {
@ -881,7 +982,7 @@ TelemetryPanel::TelemetryPanel(QWidget *parent, ModelData & model, GeneralSettin
RawSourceFilterItemModel * srcModel = (new RawSourceFilterItemModel(&generalSettings, &model, this)); RawSourceFilterItemModel * srcModel = (new RawSourceFilterItemModel(&generalSettings, &model, this));
connect(this, &TelemetryPanel::updated, srcModel, &RawSourceFilterItemModel::update); connect(this, &TelemetryPanel::updated, srcModel, &RawSourceFilterItemModel::update);
for (int i=0; i<firmware->getCapability(TelemetryCustomScreens); i++) { for (int i = 0; i < firmware->getCapability(TelemetryCustomScreens); i++) {
TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, srcModel); TelemetryCustomScreen * tab = new TelemetryCustomScreen(this, model, model.frsky.screens[i], generalSettings, firmware, srcModel);
ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i+1)); ui->customScreens->addTab(tab, tr("Telemetry screen %1").arg(i+1));
telemetryCustomScreens[i] = tab; telemetryCustomScreens[i] = tab;
@ -1198,3 +1299,13 @@ void TelemetryPanel::on_mahCount_ChkB_toggled(bool checked)
ui->mahCount_SB->setDisabled(!checked); ui->mahCount_SB->setDisabled(!checked);
emit modified(); 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 "modeledit.h"
#include "eeprominterface.h" #include "eeprominterface.h"
constexpr char MIMETYPE_TELE_SENSOR[] = "application/x-companion-tele-sensor";
class AutoComboBox; class AutoComboBox;
class RawSourceFilterItemModel; class RawSourceFilterItemModel;
class TimerEdit; class TimerEdit;
@ -103,19 +105,27 @@ class TelemetrySensorPanel: public ModelPanel
Q_OBJECT Q_OBJECT
public: 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(); ~TelemetrySensorPanel();
void update(); void update();
signals: signals:
void dataModified(); void dataModified();
void clearAllSensors();
protected slots: protected slots:
void on_name_editingFinished(); void on_name_editingFinished();
void on_type_currentIndexChanged(int index); void on_type_currentIndexChanged(int index);
void on_formula_currentIndexChanged(int index); void on_formula_currentIndexChanged(int index);
void on_unit_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: protected:
void updateSourcesComboBox(AutoComboBox * cb, bool negative); void updateSourcesComboBox(AutoComboBox * cb, bool negative);
@ -123,7 +133,9 @@ class TelemetrySensorPanel: public ModelPanel
private: private:
Ui::TelemetrySensor * ui; Ui::TelemetrySensor * ui;
SensorData & sensor; SensorData & sensor;
bool lock; bool lock = false;
int sensorIndex = 0;
int selectedIndex = 0;
}; };
class TelemetryPanel : public ModelPanel class TelemetryPanel : public ModelPanel
@ -154,6 +166,7 @@ class TelemetryPanel : public ModelPanel
void on_fasOffset_DSB_editingFinished(); void on_fasOffset_DSB_editingFinished();
void on_mahCount_SB_editingFinished(); void on_mahCount_SB_editingFinished();
void on_mahCount_ChkB_toggled(bool checked); void on_mahCount_ChkB_toggled(bool checked);
void on_clearAllSensors();
private: private:
Ui::Telemetry *ui; Ui::Telemetry *ui;

View file

@ -13,13 +13,32 @@
<property name="windowTitle"> <property name="windowTitle">
<string>Form</string> <string>Form</string>
</property> </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"> <property name="leftMargin">
<number>0</number> <number>0</number>
</property> </property>
<property name="bottomMargin"> <property name="bottomMargin">
<number>0</number> <number>0</number>
</property> </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> <item>
<widget class="QLineEdit" name="name"> <widget class="QLineEdit" name="name">
<property name="sizePolicy"> <property name="sizePolicy">
@ -520,6 +539,11 @@
<string>US fl.Oz.</string> <string>US fl.Oz.</string>
</property> </property>
</item> </item>
<item>
<property name="text">
<string>ml/min</string>
</property>
</item>
</widget> </widget>
</item> </item>
<item> <item>
@ -536,23 +560,7 @@
</widget> </widget>
</item> </item>
<item> <item>
<widget class="QDoubleSpinBox" name="prec"> <widget class="AutoPrecisionComboBox" 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>
</item> </item>
<item> <item>
<widget class="QLabel" name="ratioLabel"> <widget class="QLabel" name="ratioLabel">
@ -711,6 +719,11 @@
<extends>QSpinBox</extends> <extends>QSpinBox</extends>
<header>autohexspinbox.h</header> <header>autohexspinbox.h</header>
</customwidget> </customwidget>
<customwidget>
<class>AutoPrecisionComboBox</class>
<extends>QComboBox</extends>
<header>autoprecisioncombobox.h</header>
</customwidget>
</customwidgets> </customwidgets>
<resources/> <resources/>
<connections/> <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 autolineedit.h
genericpanel.h genericpanel.h
hexspinbox.h hexspinbox.h
autoprecisioncombobox.h
) )
qt5_wrap_cpp(shared_SRCS ${shared_HDRS}) 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 AutoHexSpinBox;
friend class AutoLineEdit; friend class AutoLineEdit;
friend class GVarGroup; friend class GVarGroup;
friend class AutoPrecisionComboBox;
public: public:
GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware); GenericPanel(QWidget *parent, ModelData * model, GeneralSettings & generalSettings, Firmware * firmware);

View file

@ -99,9 +99,10 @@ void Joystick::processEvents()
axes[i] = moved; axes[i] = moved;
} }
} }
else else {
axisRepeatTimers[i].restart(); axisRepeatTimers[i].restart();
} }
}
else { else {
emit axisValueChanged(i, 0); 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 // 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); 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); 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)); 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); 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(); 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); m_backlightColors << QColor(47, 123, 227);

View file

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

View file

@ -575,6 +575,7 @@ class AppData: public CompStoreObj
PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false) PROPERTY4(bool, snapToClpbrd, "snapshot_to_clipboard", false)
PROPERTY4(bool, autoCheckApp, "startup_check_companion", true) PROPERTY4(bool, autoCheckApp, "startup_check_companion", true)
PROPERTY4(bool, autoCheckFw, "startup_check_fw", true) PROPERTY4(bool, autoCheckFw, "startup_check_fw", true)
PROPERTY4(bool, promptProfile, "startup_prompt_profile", false)
PROPERTY(bool, enableBackup, false) PROPERTY(bool, enableBackup, false)
PROPERTY(bool, backupOnFlash, true) 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> <message>
<location filename="../mainwindow.cpp" line="1491"/> <location filename="../mainwindow.cpp" line="1491"/>
<source>Set Menu Language</source> <source>Set Menu Language</source>
<translation>Inposta Menu Lingua</translation> <translation>Menù Impostazione Lingua</translation>
</message> </message>
<message> <message>
<location filename="../mainwindow.cpp" line="1230"/> <location filename="../mainwindow.cpp" line="1230"/>

View file

@ -6,6 +6,7 @@
;Include Modern UI ;Include Modern UI
!include "MUI2.nsh" !include "MUI2.nsh"
!include "nsDialogs.nsh"
!include "@CMAKE_CURRENT_SOURCE_DIR@\..\targets\windows\FileAssociation.nsh" !include "@CMAKE_CURRENT_SOURCE_DIR@\..\targets\windows\FileAssociation.nsh"
;-------------------------------- ;--------------------------------
@ -32,6 +33,10 @@
;Variables ;Variables
Var StartMenuFolder Var StartMenuFolder
Var StartMenuLocationDialog
Var StartMenuLocationRadioCurrent
Var StartMenuLocationRadioAll
Var StartMenuLocationValue ; "current_user" or "all_users"
;-------------------------------- ;--------------------------------
;Interface Settings ;Interface Settings
@ -68,9 +73,11 @@
!define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU" !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU"
!define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\OpenTX\Companion @VERSION_FAMILY@" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\OpenTX\Companion @VERSION_FAMILY@"
!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder !insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder
;Start Menu Folder for current user or all users?
Page custom StartMenuLocationCreator StartMenuLocationLeave
!insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_INSTFILES
# These indented statements modify settings for MUI_PAGE_FINISH # 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 ;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@" "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@" "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@" "DisplayIcon" "$\"$INSTDIR\companion.exe$\""
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\OpenTX Companion @VERSION_FAMILY@" "Publisher" "OpenTX" 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 !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
;Create shortcuts ;Create shortcuts
${If} $StartMenuLocationValue == "all_users"
SetShellVarContext all
${Endif}
CreateDirectory "$SMPROGRAMS\$StartMenuFolder" CreateDirectory "$SMPROGRAMS\$StartMenuFolder"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Companion @VERSION_FAMILY@.lnk" "$INSTDIR\companion.exe" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Companion @VERSION_FAMILY@.lnk" "$INSTDIR\companion.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Firmware Simulator @VERSION_FAMILY@.lnk" "$INSTDIR\simulator.exe" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Firmware Simulator @VERSION_FAMILY@.lnk" "$INSTDIR\simulator.exe"
CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall Companion @VERSION_FAMILY@.lnk" "$INSTDIR\Uninstall.exe" CreateShortCut "$SMPROGRAMS\$StartMenuFolder\Uninstall Companion @VERSION_FAMILY@.lnk" "$INSTDIR\Uninstall.exe"
SetShellVarContext current
!insertmacro MUI_STARTMENU_WRITE_END !insertmacro MUI_STARTMENU_WRITE_END
@ -141,6 +153,14 @@ SectionEnd
;Language strings ;Language strings
LangString DESC_SecDummy ${LANG_ENGLISH} "Models and settings editor for OpenTX" 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 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 ;Assign language strings to sections
!insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
@ -203,6 +223,13 @@ Section "un.OpenTX Companion @VERSION_FAMILY@"
!insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder !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\Companion @VERSION_FAMILY@.lnk"
Delete "$SMPROGRAMS\$StartMenuFolder\Firmware Simulator @VERSION_FAMILY@.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\Firmware Simulator @VERSION_FAMILY@.lnk"
Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Companion @VERSION_FAMILY@.lnk" Delete "$SMPROGRAMS\$StartMenuFolder\Uninstall Companion @VERSION_FAMILY@.lnk"
@ -218,3 +245,58 @@ SectionEnd
Function LaunchLink Function LaunchLink
ExecShell "" "$INSTDIR\companion.exe" ExecShell "" "$INSTDIR\companion.exe"
FunctionEnd 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 function runPopupPage(event)
local result local result
if fieldPopup.status == 3 then if fieldPopup.status == 3 then
result = popupConfirmation(fieldPopup.info, event) result = popupConfirmation("Confirmation", fieldPopup.info, event)
else else
result = popupWarning(fieldPopup.info, event) result = popupWarning(fieldPopup.info, event)
end 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 -- Horus Widget to display the levels of lipo battery with per cell indication
-- 3djc & Offer Shmuely -- 3djc & Offer Shmuely
-- Date: 2020 -- Date: 2020
-- ver: 0.4 -- ver: 0.5
local version = "v0.5"
local _options = { local _options = {
{ "Sensor", SOURCE, 0 }, -- default to 'Cels' { "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 { "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 -- This function is run once at the creation of the widget
local function create(zone, options) local function create(zone, options)
local wgt = { local wgt = {
@ -40,8 +75,8 @@ local function create(zone, options)
telemResetLowestMinRSSI = 101, telemResetLowestMinRSSI = 101,
no_telem_blink = 0, no_telem_blink = 0,
isDataAvailable = 0, isDataAvailable = 0,
cellDataLive = {0,0,0,0,0,0}, cellDataLive = { 0, 0, 0, 0, 0, 0 },
cellDataHistoryLowest = {5,5,5,5,5,5}, cellDataHistoryLowest = { 5, 5, 5, 5, 5, 5 },
cellDataHistoryCellLowest = 5, cellDataHistoryCellLowest = 5,
cellMax = 0, cellMax = 0,
cellMin = 0, cellMin = 0,
@ -50,7 +85,10 @@ local function create(zone, options)
cellCount = 0, cellCount = 0,
cellSum = 0, cellSum = 0,
mainValue = 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" -- 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 -- This function allow updates when you change widgets settings
local function update(wgt, options) local function update(wgt, options)
if (wgt == nil) then return end if (wgt == nil) then
return
end
wgt.options = options wgt.options = options
@ -83,8 +123,8 @@ end
local function onTelemetryResetEvent(wgt) local function onTelemetryResetEvent(wgt)
wgt.telemResetCount = wgt.telemResetCount + 1 wgt.telemResetCount = wgt.telemResetCount + 1
wgt.cellDataLive = {0,0,0,0,0,0} wgt.cellDataLive = { 0, 0, 0, 0, 0, 0 }
wgt.cellDataHistoryLowest = {5,5,5,5,5,5} wgt.cellDataHistoryLowest = { 5, 5, 5, 5, 5, 5 }
wgt.cellDataHistoryCellLowest = 5 wgt.cellDataHistoryCellLowest = 5
end end
@ -99,9 +139,12 @@ end
local function detectResetEvent(wgt) local function detectResetEvent(wgt)
local currMinRSSI = getValue('RSSI-') local currMinRSSI = getValue('RSSI-')
if (currMinRSSI == nil) then return if (currMinRSSI == nil) then
return
end
if (currMinRSSI == wgt.telemResetLowestMinRSSI) then
return
end end
if (currMinRSSI == wgt.telemResetLowestMinRSSI) then return end
if (currMinRSSI < wgt.telemResetLowestMinRSSI) then if (currMinRSSI < wgt.telemResetLowestMinRSSI) then
-- rssi just got lower, record it -- rssi just got lower, record it
@ -118,28 +161,28 @@ local function detectResetEvent(wgt)
end end
--- This function return the percentage remaining in a single Lipo cel --- 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 if cellValue == nil then
return 0 return 0
end end
local result = 0;
-- in case somehow voltage is higher, don't return nil for i1, v1 in ipairs(_lipoPercentListSplit) do
if (cellValue > 4.2) then --is the cellVal < last-value-on-sub-list? (first-val:v1[1], last-val:v1[#v1])
return 100 if (cellValue <= v1[#v1][1]) then
end -- cellVal is in this sub-list, find the exact value
for i2, v2 in ipairs(v1) do
-- Data gathered from commercial lipo sensors if v2[1] >= cellValue then
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} } result = v2[2]
for i, v in ipairs(myArrayPercentList) do
if v[1] >= cellValue then
result = v[2]
break
end
end
return result return result
end
end
end
end
-- in case somehow voltage is too high (>4.2), don't return nil
return 100
end end
--- This function returns a table with cels values --- This function returns a table with cels values
@ -152,20 +195,13 @@ local function calculateBatteryData(wgt)
return return
end 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 cellMax = 0
local cellMin = 5 local cellMin = 5
local cellSum = 0 local cellSum = 0
for k, v in pairs(newCellData) do for k, v in pairs(newCellData) do
-- stores the lowest cell values in historical table -- 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 wgt.cellDataHistoryLowest[k] = v
--- calc history lowest of all cells --- calc history lowest of all cells
@ -181,7 +217,8 @@ local function calculateBatteryData(wgt)
end end
--- calc lowest of all cells --- 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 cellMin = v
end end
--- sum of all cells --- sum of all cells
@ -197,7 +234,6 @@ local function calculateBatteryData(wgt)
--- average of all cells --- average of all cells
wgt.cellAvg = wgt.cellSum / wgt.cellCount wgt.cellAvg = wgt.cellSum / wgt.cellCount
wgt.cellPercent = getCellPercent(wgt.cellMin)
wgt.cellDataLive = newCellData wgt.cellDataLive = newCellData
@ -219,13 +255,19 @@ local function calculateBatteryData(wgt)
wgt.secondaryValue = "-2" wgt.secondaryValue = "-2"
end end
wgt.isDataAvailable = true 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 end
-- color for battery -- color for battery
-- This function returns green at 100%, red bellow 30% and graduate in between -- This function returns green at 100%, red bellow 30% and graduate in between
local function getPercentColor(percent) local function getPercentColor(percent)
@ -242,34 +284,46 @@ end
-- This function returns green at gvalue, red at rvalue and graduate in between -- This function returns green at gvalue, red at rvalue and graduate in between
local function getRangeColor(value, green_value, red_value) local function getRangeColor(value, green_value, red_value)
local range = math.abs(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 green_value > red_value then
if value > green_value then return lcd.RGB(0, 0xdf, 0) end if value > green_value then
if value < red_value then return lcd.RGB(0xdf, 0, 0) end 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) g = math.floor(0xdf * (value - red_value) / range)
r = 0xdf - g r = 0xdf - g
return lcd.RGB(r, g, 0) return lcd.RGB(r, g, 0)
else else
if value > green_value then return lcd.RGB(0, 0xdf, 0) end if value > green_value then
if value < red_value then return lcd.RGB(0xdf, 0, 0) end 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) r = math.floor(0xdf * (value - green_value) / range)
g = 0xdf - r g = 0xdf - r
return lcd.RGB(r, g, 0) return lcd.RGB(r, g, 0)
end end
end end
--- Zone size: 70x39 1/8th top bar --- Zone size: 70x39 1/8th top bar
local function refreshZoneTiny(wgt) local function refreshZoneTiny(wgt)
local myString = string.format("%2.1fV", wgt.mainValue) local myString = string.format("%2.1fV", wgt.mainValue)
lcd.drawText(wgt.zone.x + wgt.zone.w -25, wgt.zone.y + 5, wgt.cellPercent .. "%", RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) lcd.drawText(wgt.zone.x + wgt.zone.w - 25, wgt.zone.y + 5, wgt.cellPercent .. "%", RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
lcd.drawText(wgt.zone.x + wgt.zone.w -25, wgt.zone.y + 20, myString, RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink) lcd.drawText(wgt.zone.x + wgt.zone.w - 25, wgt.zone.y + 20, myString, RIGHT + SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
-- draw batt -- draw batt
lcd.drawRectangle(wgt.zone.x + 50, wgt.zone.y + 9, 16, 25, CUSTOM_COLOR, 2) lcd.drawRectangle(wgt.zone.x + 50, wgt.zone.y + 9, 16, 25, CUSTOM_COLOR, 2)
lcd.drawFilledRectangle(wgt.zone.x +50 + 4, wgt.zone.y + 7, 6, 3, CUSTOM_COLOR) lcd.drawFilledRectangle(wgt.zone.x + 50 + 4, wgt.zone.y + 7, 6, 3, CUSTOM_COLOR)
local rect_h = math.floor(25 * wgt.cellPercent / 100) local rect_h = math.floor(25 * wgt.cellPercent / 100)
lcd.drawFilledRectangle(wgt.zone.x +50, wgt.zone.y + 9 + 25 - rect_h, 16, rect_h, CUSTOM_COLOR + wgt.no_telem_blink) lcd.drawFilledRectangle(wgt.zone.x + 50, wgt.zone.y + 9 + 25 - rect_h, 16, rect_h, CUSTOM_COLOR + wgt.no_telem_blink)
end end
--- Zone size: 160x32 1/8th --- Zone size: 160x32 1/8th
@ -307,13 +361,13 @@ local function refreshZoneMedium(wgt)
end end
-- draw values -- 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) -- more info if 1/4 is high enough (without trim & slider)
if wgt.zone.h > 80 then 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("%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, 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) lcd.drawText(wgt.zone.x, wgt.zone.y + 84, string.format("Min %2.2fV", wgt.cellDataHistoryCellLowest), SMLSIZE + CUSTOM_COLOR + wgt.no_telem_blink)
end end
-- fill batt -- fill batt
@ -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) lcd.drawGauge(wgt.zone.x + myBatt.x, wgt.zone.y + myBatt.y, myBatt.w, myBatt.h, wgt.cellPercent, 100, CUSTOM_COLOR)
-- draw cells -- draw cells
local cellH = wgt.zone.h / wgt.cellCount local cellH = 19
if cellH > 20 then cellH =20 end
local cellX = 118 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 for i = 1, wgt.cellCount, 1 do
local cellY = wgt.zone.y + (i-1)* (cellH -1) local cellY = wgt.zone.y + (i - 1) * (cellH - 1)
-- fill current cell -- 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) --lcd.drawFilledRectangle(wgt.zone.x + cellX , cellY, 58, cellH, CUSTOM_COLOR)
local percentCurrent = getCellPercent(wgt.cellDataLive[i]) lcd.setColor(CUSTOM_COLOR, getRangeColor(wgt.cellDataLive[i], wgt.cellMax, wgt.cellMax - 0.2))
local percentMin = getCellPercent(wgt.cellDataHistoryLowest[i]) lcd.drawFilledRectangle(wgt.zone.x + pos[i].x, wgt.zone.y + pos[i].y, cellW - 1, 20, CUSTOM_COLOR)
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, WHITE) 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.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 + cellX , cellY, 59, cellH, CUSTOM_COLOR , 1) lcd.drawRectangle(wgt.zone.x + pos[i].x, wgt.zone.y + pos[i].y, cellW, 20, CUSTOM_COLOR, 1)
end end
-- draws bat -- draws bat
@ -447,7 +494,9 @@ end
-- This function allow recording of lowest cells when widget is in background -- This function allow recording of lowest cells when widget is in background
local function background(wgt) local function background(wgt)
if (wgt == nil) then return end if (wgt == nil) then
return
end
detectResetEvent(wgt) detectResetEvent(wgt)
@ -456,11 +505,21 @@ local function background(wgt)
end end
local function refresh(wgt) local function refresh(wgt)
if (wgt == nil) then
if (wgt == nil) then return end return
if (wgt.options == nil) then return end end
if (wgt.zone == nil) then return end if type(wgt) ~= "table" then
if (wgt.options.LowestCell == nil) then return end 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 if wgt.options.Shadow == 1 then
wgt.shadowed = SHADOWED wgt.shadowed = SHADOWED
@ -478,14 +537,17 @@ local function refresh(wgt)
wgt.no_telem_blink = INVERS + BLINK wgt.no_telem_blink = INVERS + BLINK
end end
if wgt.zone.w > 380 and wgt.zone.h > 165 then
if wgt.zone.w > 380 and wgt.zone.h > 165 then refreshZoneXLarge(wgt) refreshZoneXLarge(wgt)
elseif wgt.zone.w > 180 and wgt.zone.h > 145 then refreshZoneLarge(wgt) elseif wgt.zone.w > 180 and wgt.zone.h > 145 then
elseif wgt.zone.w > 170 and wgt.zone.h > 65 then refreshZoneMedium(wgt) refreshZoneLarge(wgt)
elseif wgt.zone.w > 150 and wgt.zone.h > 28 then refreshZoneSmall(wgt) elseif wgt.zone.w > 170 and wgt.zone.h > 65 then
elseif wgt.zone.w > 65 and wgt.zone.h > 35 then refreshZoneTiny(wgt) 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
end end
return { name="BattCheck", options=_options, create=create, update=update, background=background, refresh=refresh } 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}) add_definitions(-DPOPUP_LEVEL=${POPUP_LEVEL})
if(BOOTLOADER) if(INTERNAL_MODULE_MULTI)
add_definitions(-DBOOTLOADER) set(DEFAULT_TEMPLATE_SETUP 21 CACHE STRING "")
endif(BOOTLOADER) else()
set(DEFAULT_TEMPLATE_SETUP 0 CACHE STRING "")
endif()
add_definitions(-DDEFAULT_TEMPLATE_SETUP=${DEFAULT_TEMPLATE_SETUP})
set(SRC set(SRC
${SRC} ${SRC}
@ -444,7 +447,6 @@ endif()
set(SRC ${SRC} ${FIRMWARE_SRC}) set(SRC ${SRC} ${FIRMWARE_SRC})
##### firmware target ##### ##### firmware target #####
option(FIRMWARE_TARGET "Configure Firmware target (can be turned of for compiling Companion only)" ON) 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); uint8_t len = ZLEN(g_eeGeneral.bluetoothName);
if (len > 0) { if (len > 0) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
*cur++ = char2lower(zchar2char(g_eeGeneral.bluetoothName[i])); *cur++ = char2lower(g_eeGeneral.bluetoothName[i]);
} }
*cur = '\0'; *cur = '\0';
} }
@ -418,7 +418,7 @@ void Bluetooth::wakeup()
uint8_t len = ZLEN(g_eeGeneral.bluetoothName); uint8_t len = ZLEN(g_eeGeneral.bluetoothName);
if (len > 0) { if (len > 0) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
*cur++ = char2lower(zchar2char(g_eeGeneral.bluetoothName[i])); *cur++ = char2lower(g_eeGeneral.bluetoothName[i]);
} }
*cur = '\0'; *cur = '\0';
} }

View file

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

View file

@ -20,7 +20,6 @@
#include "opentx.h" #include "opentx.h"
uint8_t s_curveChan;
int8_t * curveEnd[MAX_CURVES]; int8_t * curveEnd[MAX_CURVES];
void loadCurves() 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])) #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; int32_t m=0;
uint8_t num_points = crv->points + 5; 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); int8_t * points = curveAddress(idx);
uint8_t count = crv.points+5; uint8_t count = crv.points+5;
bool custom = (crv.type == CURVE_TYPE_CUSTOM); bool custom = (crv.type == CURVE_TYPE_CUSTOM);
int16_t erg = 0; int16_t erg;
x += RESXu; x += RESXu;
@ -339,11 +338,6 @@ int applyCustomCurve(int x, uint8_t idx)
return intpol(x, 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 getPoint(uint8_t curveIndex, uint8_t index)
{ {
point_t result = {0, 0}; point_t result = {0, 0};
@ -363,7 +357,14 @@ point_t getPoint(uint8_t curveIndex, uint8_t index)
return result; return result;
} }
#if !defined(COLORLCD)
point_t getPoint(uint8_t index)
{
return getPoint(s_currIdxSubMenu, index);
}
int applyCurrentCurve(int x) int applyCurrentCurve(int x)
{ {
return applyCustomCurve(x, s_curveChan); return applyCustomCurve(x, s_currIdxSubMenu);
} }
#endif

View file

@ -208,6 +208,16 @@ enum TrainerMode {
}; };
#endif #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) #if defined(HARDWARE_INTERNAL_MODULE)
#define IS_INTERNAL_MODULE_ENABLED() (g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE) #define IS_INTERNAL_MODULE_ENABLED() (g_model.moduleData[INTERNAL_MODULE].type != MODULE_TYPE_NONE)
#else #else

View file

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

View file

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