5 * th9x - http://code.google.com/p/th9x
6 * er9x - http://code.google.com/p/er9x
7 * gruvin9x - http://code.google.com/p/gruvin9x
9 * License GPLv2: http://www.gnu.org/licenses/gpl-2.0.html
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
25 #elif defined __GNUC__
32 #include "simulatormainwindow.h"
33 #include "storage/sdcard.h"
36 #include <QMessageBox>
38 using namespace Helpers
;
40 Stopwatch
gStopwatch("global");
42 const QColor colors
[CPN_MAX_CURVES
] = {
81 GVarGroup::GVarGroup(QCheckBox
* weightGV
, QAbstractSpinBox
* weightSB
, QComboBox
* weightCB
, int & weight
, const ModelData
& model
, const int deflt
, const int mini
, const int maxi
, const double step
, bool allowGvars
):
85 sb(dynamic_cast<QSpinBox
*>(weightSB
)),
86 dsb(dynamic_cast<QDoubleSpinBox
*>(weightSB
)),
92 if (allowGvars
&& getCurrentFirmware()->getCapability(Gvars
)) {
93 Helpers::populateGVCB(*weightCB
, weight
, model
);
94 connect(weightGV
, SIGNAL(stateChanged(int)), this, SLOT(gvarCBChanged(int)));
95 connect(weightCB
, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged()));
99 if (weight
> maxi
|| weight
< mini
) {
106 if (weight
>maxi
|| weight
<mini
) {
108 weightGV
->setChecked(true);
114 weightGV
->setChecked(false);
120 sb
->setMinimum(mini
);
121 sb
->setMaximum(maxi
);
125 dsb
->setMinimum(mini
*step
);
126 dsb
->setMaximum(maxi
*step
);
127 dsb
->setValue(val
*step
);
130 connect(weightSB
, SIGNAL(editingFinished()), this, SLOT(valuesChanged()));
135 void GVarGroup::gvarCBChanged(int state
)
137 weightCB
->setVisible(state
);
139 weightSB
->setVisible(!state
);
141 weightSB
->setVisible(!state
);
145 void GVarGroup::valuesChanged()
148 if (weightGV
->isChecked())
149 weight
= weightCB
->itemData(weightCB
->currentIndex()).toInt();
151 weight
= sb
->value();
153 weight
= round(dsb
->value()/step
);
163 CurveGroup::CurveGroup(QComboBox
* curveTypeCB
, QCheckBox
* curveGVarCB
, QComboBox
* curveValueCB
, QSpinBox
* curveValueSB
, CurveReference
& curve
, const ModelData
& model
, unsigned int flags
):
165 curveTypeCB(curveTypeCB
),
166 curveGVarCB(curveGVarCB
),
167 curveValueCB(curveValueCB
),
168 curveValueSB(curveValueSB
),
175 if (!(flags
& HIDE_DIFF
)) curveTypeCB
->addItem(tr("Diff"), 0);
176 if (!(flags
& HIDE_EXPO
)) curveTypeCB
->addItem(tr("Expo"), 1);
177 curveTypeCB
->addItem(tr("Func"), 2);
178 curveTypeCB
->addItem(tr("Curve"), 3);
180 curveValueCB
->setMaxVisibleItems(10);
182 connect(curveTypeCB
, SIGNAL(currentIndexChanged(int)), this, SLOT(typeChanged(int)));
183 connect(curveGVarCB
, SIGNAL(stateChanged(int)), this, SLOT(gvarCBChanged(int)));
184 connect(curveValueCB
, SIGNAL(currentIndexChanged(int)), this, SLOT(valuesChanged()));
185 connect(curveValueSB
, SIGNAL(editingFinished()), this, SLOT(valuesChanged()));
190 void CurveGroup::update()
194 int found
= curveTypeCB
->findData(curve
.type
);
195 if (found
< 0) found
= 0;
196 curveTypeCB
->setCurrentIndex(found
);
198 if (curve
.type
== CurveReference::CURVE_REF_DIFF
|| curve
.type
== CurveReference::CURVE_REF_EXPO
) {
199 curveGVarCB
->setVisible(getCurrentFirmware()->getCapability(Gvars
));
200 if (curve
.value
> 100 || curve
.value
< -100) {
201 curveGVarCB
->setChecked(true);
202 if (lastType
!= CurveReference::CURVE_REF_DIFF
&& lastType
!= CurveReference::CURVE_REF_EXPO
) {
203 lastType
= curve
.type
;
204 Helpers::populateGVCB(*curveValueCB
, curve
.value
, model
);
206 curveValueCB
->show();
207 curveValueSB
->hide();
210 curveGVarCB
->setChecked(false);
211 curveValueSB
->setMinimum(-100);
212 curveValueSB
->setMaximum(100);
213 curveValueSB
->setValue(curve
.value
);
214 curveValueSB
->show();
215 curveValueCB
->hide();
220 curveValueSB
->hide();
221 curveValueCB
->show();
222 switch (curve
.type
) {
223 case CurveReference::CURVE_REF_FUNC
:
224 if (lastType
!= curve
.type
) {
225 lastType
= curve
.type
;
226 curveValueCB
->clear();
227 for (int i
=0; i
<=6/*TODO constant*/; i
++) {
228 curveValueCB
->addItem(CurveReference(CurveReference::CURVE_REF_FUNC
, i
).toString(&model
, false));
231 curveValueCB
->setCurrentIndex(curve
.value
);
233 case CurveReference::CURVE_REF_CUSTOM
:
235 int numcurves
= getCurrentFirmware()->getCapability(NumCurves
);
236 if (lastType
!= curve
.type
) {
237 lastType
= curve
.type
;
238 curveValueCB
->clear();
239 for (int i
= ((flags
& HIDE_NEGATIVE_CURVES
) ? 0 : -numcurves
); i
<=numcurves
; i
++) {
240 curveValueCB
->addItem(CurveReference(CurveReference::CURVE_REF_CUSTOM
, i
).toString(&model
, false), i
);
241 if (i
== curve
.value
) {
242 curveValueCB
->setCurrentIndex(curveValueCB
->count() - 1);
256 void CurveGroup::gvarCBChanged(int state
)
260 curve
.value
= 10000+1; // TODO constant in EEpromInterface ...
261 lastType
= -1; // quickfix for issue #3518: force refresh of curveValueCB at next update() to set current index to GV1
264 curve
.value
= 0; // TODO could be better
271 void CurveGroup::typeChanged(int value
)
274 int type
= curveTypeCB
->itemData(curveTypeCB
->currentIndex()).toInt();
277 curve
= CurveReference(CurveReference::CURVE_REF_DIFF
, 0);
280 curve
= CurveReference(CurveReference::CURVE_REF_EXPO
, 0);
283 curve
= CurveReference(CurveReference::CURVE_REF_FUNC
, 0);
286 curve
= CurveReference(CurveReference::CURVE_REF_CUSTOM
, 0);
294 void CurveGroup::valuesChanged()
297 switch (curveTypeCB
->itemData(curveTypeCB
->currentIndex()).toInt()) {
302 if (curveGVarCB
->isChecked())
303 value
= curveValueCB
->itemData(curveValueCB
->currentIndex()).toInt();
305 value
= curveValueSB
->value();
306 curve
= CurveReference(curveTypeCB
->itemData(curveTypeCB
->currentIndex()).toInt() == 0 ? CurveReference::CURVE_REF_DIFF
: CurveReference::CURVE_REF_EXPO
, value
);
310 curve
= CurveReference(CurveReference::CURVE_REF_FUNC
, curveValueCB
->currentIndex());
313 curve
= CurveReference(CurveReference::CURVE_REF_CUSTOM
, curveValueCB
->itemData(curveValueCB
->currentIndex()).toInt());
322 * Helpers namespace functions
325 void Helpers::populateGVCB(QComboBox
& b
, int value
, const ModelData
& model
)
327 int count
= getCurrentFirmware()->getCapability(Gvars
);
331 for (int i
=-count
; i
<=-1; i
++) {
332 int16_t gval
= (int16_t)(-10000+i
);
333 b
.addItem("-" + RawSource(SOURCE_TYPE_GVAR
, abs(i
)-1).toString(&model
), gval
);
336 for (int i
=1; i
<=count
; i
++) {
337 int16_t gval
= (int16_t)(10000+i
);
338 b
.addItem(RawSource(SOURCE_TYPE_GVAR
, i
-1).toString(&model
), gval
);
341 b
.setCurrentIndex(b
.findData(value
));
342 if (b
.currentIndex() == -1)
343 b
.setCurrentIndex(count
);
346 // Returns Diff/Expo/Weight/Offset adjustment value as either a percentage or a global variable name.
347 QString
Helpers::getAdjustmentString(int16_t val
, const ModelData
* model
, bool sign
)
350 if (val
>= -10000 && val
<= 10000) {
357 ret
= RawSource(SOURCE_TYPE_GVAR
, abs(val
) - 10001).toString(model
);
366 void Helpers::populateGvarUseCB(QComboBox
* b
, unsigned int phase
)
368 b
->addItem(QObject::tr("Own value"));
369 for (int i
=0; i
<getCurrentFirmware()->getCapability(FlightModes
); i
++) {
370 if (i
!= (int)phase
) {
371 b
->addItem(QObject::tr("Flight mode %1 value").arg(i
));
376 void Helpers::populateFileComboBox(QComboBox
* b
, const QSet
<QString
> & set
, const QString
& current
)
382 // Convert set into list and sort it alphabetically case insensitive
383 QStringList list
= QStringList::fromSet(set
);
384 qSort(list
.begin(), list
.end(), caseInsensitiveLessThan
);
385 foreach (QString entry
, list
) {
387 if (entry
== current
) {
388 b
->setCurrentIndex(b
->count()-1);
393 if (!added
&& !current
.isEmpty()) {
395 b
->setCurrentIndex(b
->count()-1);
399 void Helpers::getFileComboBoxValue(QComboBox
* b
, char * dest
, int length
)
401 memset(dest
, 0, length
+1);
402 if (b
->currentText() != "----") {
403 strncpy(dest
, b
->currentText().toLatin1(), length
);
407 void Helpers::addRawSourceItems(QStandardItemModel
* itemModel
, const RawSourceType
& type
, int count
, const GeneralSettings
* const generalSettings
,
408 const ModelData
* const model
, const int start
, const QList
<int> exclude
)
410 for (int i
= start
; i
< start
+ count
; i
++) {
411 if (exclude
.contains(i
))
414 RawSource src
= RawSource(type
, i
);
416 if (type
== SOURCE_TYPE_VIRTUAL_INPUT
&& !model
->isInputValid(i
))
419 if (generalSettings
) {
421 if (type
== SOURCE_TYPE_STICK
&& ((src
.isPot(&genAryIdx
) && !generalSettings
->isPotAvailable(genAryIdx
)) || (src
.isSlider(&genAryIdx
) && !generalSettings
->isSliderAvailable(genAryIdx
))))
425 QStandardItem
* modelItem
= new QStandardItem(src
.toString(model
, generalSettings
));
426 modelItem
->setData(src
.toValue(), Qt::UserRole
);
427 itemModel
->appendRow(modelItem
);
429 if (type
== SOURCE_TYPE_SWITCH
&& generalSettings
&& IS_HORUS_OR_TARANIS(getCurrentBoard()) && !generalSettings
->switchSourceAllowedTaranis(i
)) {
430 modelItem
->setData(0, Qt::UserRole
- 1); // trick to disable an item
435 QStandardItemModel
* Helpers::getRawSourceItemModel(const GeneralSettings
* const generalSettings
, const ModelData
* const model
, unsigned int flags
)
437 QStandardItemModel
* itemModel
= new QStandardItemModel();
438 Boards board
= Boards(getCurrentBoard());
439 Firmware
* fw
= getCurrentFirmware();
441 if (flags
& POPULATE_NONE
) {
442 addRawSourceItems(itemModel
, SOURCE_TYPE_NONE
, 1, generalSettings
, model
);
445 if (flags
& POPULATE_SCRIPT_OUTPUTS
) {
446 for (int i
=0; i
< getCurrentFirmware()->getCapability(LuaScripts
); i
++) {
447 addRawSourceItems(itemModel
, SOURCE_TYPE_LUA_OUTPUT
, fw
->getCapability(LuaOutputsPerScript
), generalSettings
, model
, i
* 16);
451 if (model
&& (flags
& POPULATE_VIRTUAL_INPUTS
)) {
452 addRawSourceItems(itemModel
, SOURCE_TYPE_VIRTUAL_INPUT
, fw
->getCapability(VirtualInputs
), generalSettings
, model
);
455 if (flags
& POPULATE_SOURCES
) {
456 int totalSources
= CPN_MAX_STICKS
+ board
.getCapability(Board::Pots
) + board
.getCapability(Board::Sliders
) + board
.getCapability(Board::MouseAnalogs
);
457 addRawSourceItems(itemModel
, SOURCE_TYPE_STICK
, totalSources
, generalSettings
, model
);
458 addRawSourceItems(itemModel
, SOURCE_TYPE_ROTARY_ENCODER
, fw
->getCapability(RotaryEncoders
), generalSettings
, model
);
461 if (flags
& POPULATE_TRIMS
) {
462 addRawSourceItems(itemModel
, SOURCE_TYPE_TRIM
, board
.getCapability(Board::NumTrims
), generalSettings
, model
);
465 if (flags
& POPULATE_SOURCES
) {
466 addRawSourceItems(itemModel
, SOURCE_TYPE_MAX
, 1, generalSettings
, model
);
469 if (flags
& POPULATE_SWITCHES
) {
470 addRawSourceItems(itemModel
, SOURCE_TYPE_SWITCH
, board
.getCapability(Board::Switches
), generalSettings
, model
);
471 addRawSourceItems(itemModel
, SOURCE_TYPE_CUSTOM_SWITCH
, fw
->getCapability(LogicalSwitches
), generalSettings
, model
);
474 if (flags
& POPULATE_SOURCES
) {
475 addRawSourceItems(itemModel
, SOURCE_TYPE_CYC
, CPN_MAX_CYC
, generalSettings
, model
);
476 addRawSourceItems(itemModel
, SOURCE_TYPE_PPM
, fw
->getCapability(TrainerInputs
), generalSettings
, model
);
477 addRawSourceItems(itemModel
, SOURCE_TYPE_CH
, fw
->getCapability(Outputs
), generalSettings
, model
);
480 if (flags
& POPULATE_TELEMETRY
) {
481 if (IS_ARM(board
.getBoardType())) {
482 addRawSourceItems(itemModel
, SOURCE_TYPE_SPECIAL
, 5, generalSettings
, model
);
486 for (int i
=0; i
< CPN_MAX_SENSORS
* 3; ++i
) {
487 //this conditon must be false if we populate Global Functions where model = 0
488 if (!model
->sensorData
[div(i
, 3).quot
].isAvailable())
491 if (exclude
.size() < CPN_MAX_SENSORS
* 3)
492 addRawSourceItems(itemModel
, SOURCE_TYPE_TELEMETRY
, CPN_MAX_SENSORS
* 3, generalSettings
, model
, 0, exclude
);
497 if (!fw
->getCapability(RtcTime
))
498 exclude
<< TELEMETRY_SOURCE_TX_TIME
;
499 if (!fw
->getCapability(SportTelemetry
))
500 exclude
<< TELEMETRY_SOURCE_SWR
;
501 if (!IS_ARM(board
.getBoardType()))
502 exclude
<< TELEMETRY_SOURCE_TIMER3
;
503 int count
= ((flags
& POPULATE_TELEMETRYEXT
) ? TELEMETRY_SOURCES_STATUS_COUNT
: TELEMETRY_SOURCES_COUNT
);
504 addRawSourceItems(itemModel
, SOURCE_TYPE_TELEMETRY
, count
, generalSettings
, model
, 0, exclude
);
508 if (flags
& POPULATE_GVARS
) {
509 addRawSourceItems(itemModel
, SOURCE_TYPE_GVAR
, fw
->getCapability(Gvars
), generalSettings
, model
);
515 QString
image2qstring(QImage image
)
520 image
.save(&buffer
, "PNG");
523 int size
=buffer
.data().size();
524 for (int j
= 0; j
< size
; j
++) {
525 b
=buffer
.data().at(j
);
526 ImageStr
+= QString("%1").arg(b
&0xff, 2, 16, QChar('0'));
531 int findmult(float value
, float base
)
533 int vvalue
= value
*10;
538 for (int i
=8; i
>=0; i
--) {
539 if (vvalue
/vbase
>= (1<<i
)) {
548 QString
getFrSkyAlarmType(int alarm
)
552 return QObject::tr("Yellow");
554 return QObject::tr("Orange");
556 return QObject::tr("Red");
562 QString
getFrSkyUnits(int units
)
566 return QObject::tr("---");
572 QString
getFrSkyProtocol(int protocol
)
576 if ((getCurrentFirmware()->getCapability(Telemetry
) & TM_HASWSHH
))
577 return QObject::tr("Winged Shadow How High");
579 return QObject::tr("Winged Shadow How High (not supported)");
581 return QObject::tr("FrSky Sensor Hub");
583 return QObject::tr("None");
587 QString
getFrSkyMeasure(int units
)
591 return QObject::tr("Imperial");
593 return QObject::tr("Metric");
597 QString
getFrSkySrc(int index
)
599 return RawSource(SOURCE_TYPE_TELEMETRY
, index
-1).toString();
604 int theme_set
= g
.theme();
626 CompanionIcon::CompanionIcon(const QString
&baseimage
)
628 static QString theme
= getTheme();
629 addFile(":/themes/"+theme
+"/16/"+baseimage
, QSize(16,16));
630 addFile(":/themes/"+theme
+"/24/"+baseimage
, QSize(24,24));
631 addFile(":/themes/"+theme
+"/32/"+baseimage
, QSize(32,32));
632 addFile(":/themes/"+theme
+"/48/"+baseimage
, QSize(48,48));
635 void startSimulation(QWidget
* parent
, RadioData
& radioData
, int modelIdx
)
637 QString fwId
= SimulatorLoader::findSimulatorByFirmwareName(getCurrentFirmware()->getId());
638 if (fwId
.isEmpty()) {
639 QMessageBox::warning(NULL
,
640 QObject::tr("Warning"),
641 QObject::tr("Simulator for this firmware is not yet available"));
645 RadioData
* simuData
= new RadioData(radioData
);
646 unsigned int flags
= 0;
649 flags
|= SIMULATOR_FLAGS_NOTX
;
650 simuData
->setCurrentModel(modelIdx
);
653 SimulatorMainWindow
* dialog
= new SimulatorMainWindow(parent
, fwId
, flags
);
654 dialog
->setWindowModality(Qt::ApplicationModal
);
655 dialog
->setAttribute(Qt::WA_DeleteOnClose
);
657 QObject::connect(dialog
, &SimulatorMainWindow::destroyed
, [simuData
] (void) {
658 // TODO simuData and Horus tmp directory is deleted on simulator close OR we could use it to get back data from the simulation
663 if (dialog
->getExitStatus(&resultMsg
)) {
664 if (resultMsg
.isEmpty())
665 resultMsg
= QObject::tr("Uknown error during Simulator startup.");
666 QMessageBox::critical(NULL
, QObject::tr("Simulator Error"), resultMsg
);
667 dialog
->deleteLater();
669 else if (dialog
->setRadioData(simuData
)) {
673 QMessageBox::critical(NULL
, QObject::tr("Data Load Error"), QObject::tr("Error occurred while starting simulator."));
674 dialog
->deleteLater();
678 QPixmap
makePixMap(const QImage
& image
)
680 Firmware
* firmware
= getCurrentFirmware();
681 QImage result
= image
.scaled(firmware
->getCapability(LcdWidth
), firmware
->getCapability(LcdHeight
));
682 if (firmware
->getCapability(LcdDepth
) == 4) {
683 result
= result
.convertToFormat(QImage::Format_RGB32
);
684 for (int i
= 0; i
< result
.width(); ++i
) {
685 for (int j
= 0; j
< result
.height(); ++j
) {
686 QRgb col
= result
.pixel(i
, j
);
687 int gray
= qGray(col
);
688 result
.setPixel(i
, j
, qRgb(gray
, gray
, gray
));
693 result
= result
.convertToFormat(QImage::Format_Mono
);
696 return QPixmap::fromImage(result
);
700 int version2index(const QString
& version
)
703 QStringList parts
= version
.split("N");
704 if (parts
.size() > 1)
705 result
= parts
[1].toInt(); // nightly build
706 parts
= parts
[0].split('.');
707 if (parts
.size() > 2)
708 result
+= 1000 * parts
[2].toInt();
709 if (parts
.size() > 1)
710 result
+= 100000 * parts
[1].toInt();
711 if (parts
.size() > 0)
712 result
+= 10000000 * parts
[0].toInt();
716 const QString
index2version(int index
)
719 QString
templt("%1.%2.%3");
720 if (index
>= 19900000) {
721 int nightly
= index
% 1000;
723 int revision
= index
% 100;
725 int minor
= index
% 100;
726 int major
= index
/ 100;
727 result
= templt
.arg(major
).arg(minor
).arg(revision
);
728 if (nightly
> 0 && nightly
< 999) {
729 result
+= "N" + QString::number(nightly
);
732 else if (index
>= 19900) {
733 int revision
= index
% 100;
735 int minor
= index
% 100;
736 int major
= index
/ 100;
737 result
= templt
.arg(major
).arg(minor
).arg(revision
);
742 bool qunlink(const QString
& fileName
)
744 return QFile::remove(fileName
);
747 QString
generateProcessUniqueTempFileName(const QString
& fileName
)
749 QString sanitizedFileName
= fileName
;
750 sanitizedFileName
.remove('/');
751 return QDir::tempPath() + QString("/%1-").arg(QCoreApplication::applicationPid()) + sanitizedFileName
;
754 bool isTempFileName(const QString
& fileName
)
756 return fileName
.startsWith(QDir::tempPath());
759 QString
getSoundsPath(const GeneralSettings
&generalSettings
)
761 QString path
= g
.profile
[g
.id()].sdPath() + "/SOUNDS/";
762 QString lang
= generalSettings
.ttsLanguage
;
769 QSet
<QString
> getFilesSet(const QString
&path
, const QStringList
&filter
, int maxLen
)
771 QSet
<QString
> result
;
774 foreach (QString filename
, dir
.entryList(filter
, QDir::Files
)) {
775 QFileInfo
file(filename
);
776 QString name
= file
.completeBaseName();
777 if (name
.length() <= maxLen
) {
785 bool caseInsensitiveLessThan(const QString
&s1
, const QString
&s2
)
787 return s1
.toLower() < s2
.toLower();
790 bool GpsGlitchFilter::isGlitch(GpsCoord coord
)
792 if ((fabs(coord
.latitude
) < 0.1) && (fabs(coord
.longitude
) < 0.1)) {
797 if (fabs(coord
.latitude
- lastLat
) > 0.01) {
798 // qDebug() << "GpsGlitchFilter(): latitude glitch " << coord.latitude << lastLat;
799 if ( ++glitchCount
< 10) {
803 if (fabs(coord
.longitude
- lastLon
) > 0.01) {
804 // qDebug() << "GpsGlitchFilter(): longitude glitch " << coord.longitude << lastLon;
805 if ( ++glitchCount
< 10) {
810 lastLat
= coord
.latitude
;
811 lastLon
= coord
.longitude
;
817 bool GpsLatLonFilter::isValid(GpsCoord coord
)
819 if (lastLat
== coord
.latitude
) {
822 if (lastLon
== coord
.longitude
) {
825 lastLat
= coord
.latitude
;
826 lastLon
= coord
.longitude
;
830 double toDecimalCoordinate(const QString
& value
)
832 if (value
.isEmpty()) return 0.0;
833 double temp
= int(value
.left(value
.length()-1).toDouble() / 100);
834 double result
= temp
+ (value
.left(value
.length() - 1).toDouble() - temp
* 100) / 60.0;
835 QChar direction
= value
.at(value
.size()-1);
836 if ((direction
== 'S') || (direction
== 'W')) {
842 GpsCoord
extractGpsCoordinates(const QString
& position
)
845 QStringList parts
= position
.split(' ');
846 if (parts
.size() == 2) {
847 QString value
= parts
.at(0).trimmed();
848 QChar direction
= value
.at(value
.size()-1);
849 if (direction
== 'E' || direction
== 'W') {
850 // OpenTX 2.1 format: "NNN.MMM[E|W] NNN.MMM[N|S]" <longitude> <latitude>
851 result
.latitude
= toDecimalCoordinate(parts
.at(1).trimmed());
852 result
.longitude
= toDecimalCoordinate(parts
.at(0).trimmed());
855 // OpenTX 2.2 format: "DD.DDDDDD DD.DDDDDD" <latitude> <longitude> both in Signed degrees format (DDD.dddd)
856 // Precede South latitudes and West longitudes with a minus sign.
857 // Latitudes range from -90 to 90.
858 // Longitudes range from -180 to 180.
859 result
.latitude
= parts
.at(0).trimmed().toDouble();
860 result
.longitude
= parts
.at(1).trimmed().toDouble();
866 TableLayout::TableLayout(QWidget
* parent
, int rowCount
, const QStringList
& headerLabels
)
868 #if defined(TABLE_LAYOUT)
869 tableWidget
= new QTableWidget(parent
);
870 QVBoxLayout
* layout
= new QVBoxLayout();
871 layout
->addWidget(tableWidget
);
872 layout
->setContentsMargins(0, 0, 0, 0);
873 parent
->setLayout(layout
);
875 tableWidget
->setRowCount(rowCount
);
876 tableWidget
->setColumnCount(headerLabels
.size());
877 tableWidget
->setShowGrid(false);
878 tableWidget
->verticalHeader()->setVisible(false);
879 tableWidget
->setSelectionBehavior(QAbstractItemView::SelectRows
);
880 tableWidget
->setSelectionMode(QAbstractItemView::NoSelection
);
881 tableWidget
->setFrameStyle(QFrame::NoFrame
| QFrame::Plain
);
882 tableWidget
->setStyleSheet("QTableWidget {background-color: transparent;}");
883 tableWidget
->setHorizontalHeaderLabels(headerLabels
);
885 gridWidget
= new QGridLayout(parent
);
888 foreach(QString text
, headerLabels
) {
889 QLabel
*label
= new QLabel();
890 label
->setFrameShape(QFrame::Panel
);
891 label
->setFrameShadow(QFrame::Raised
);
892 label
->setMidLineWidth(0);
893 label
->setAlignment(Qt::AlignCenter
);
895 label
->setText(text
);
897 // label->setMinimumWidth(100);
898 gridWidget
->addWidget(label
, 0, col
++);
903 void TableLayout::addWidget(int row
, int column
, QWidget
* widget
)
905 #if defined(TABLE_LAYOUT)
906 QHBoxLayout
* layout
= new QHBoxLayout(tableWidget
);
907 layout
->addWidget(widget
);
908 addLayout(row
, column
, layout
);
910 gridWidget
->addWidget(widget
, row
+ 1, column
);
914 void TableLayout::addLayout(int row
, int column
, QLayout
* layout
)
916 #if defined(TABLE_LAYOUT)
917 layout
->setContentsMargins(1, 3, 1, 3);
918 QWidget
* containerWidget
= new QWidget(tableWidget
);
919 containerWidget
->setLayout(layout
);
920 tableWidget
->setCellWidget(row
, column
, containerWidget
);
922 gridWidget
->addLayout(layout
, row
+ 1, column
);
926 void TableLayout::resizeColumnsToContents()
928 #if defined(TABLE_LAYOUT)
929 tableWidget
->resizeColumnsToContents();
934 void TableLayout::setColumnWidth(int col
, int width
)
936 #if defined(TABLE_LAYOUT)
937 tableWidget
->setColumnWidth(col
, width
);
942 void TableLayout::pushRowsUp(int row
)
944 #if defined(TABLE_LAYOUT)
947 QSpacerItem
* spacer
= new QSpacerItem(0, 0, QSizePolicy::Minimum
, QSizePolicy::Expanding
);
948 gridWidget
->addItem(spacer
, row
, 0);
951 // addDoubleSpring(gridLayout, 5, num_fsw+1);