mirror of
https://gitlab.com/pholy/OSCAR-code.git
synced 2025-04-04 02:00:43 +00:00
1394 lines
55 KiB
C++
1394 lines
55 KiB
C++
/* OSCAR Preferences Dialog Implementation
|
|
*
|
|
* Copyright (c) 2019-2024 The OSCAR Team
|
|
* Copyright (c) 2011-2018 Mark Watkins
|
|
*
|
|
* This file is subject to the terms and conditions of the GNU General Public
|
|
* License. See the file COPYING in the main directory of the source code
|
|
* for more details. */
|
|
|
|
#define TEST_MACROS_ENABLEDoff
|
|
#include <test_macros.h>
|
|
|
|
#include <QLabel>
|
|
#include <QColorDialog>
|
|
#include <QMessageBox>
|
|
#include <QStatusBar>
|
|
#include <QProcess>
|
|
#include <QDesktopServices>
|
|
#include <QFileDialog>
|
|
#include <QTextStream>
|
|
#include <QCalendarWidget>
|
|
//#include <QMenuBar>
|
|
#include <QDebug>
|
|
|
|
#include <cmath>
|
|
|
|
#include "preferencesdialog.h"
|
|
#include "version.h"
|
|
|
|
#include <Graphs/gGraphView.h>
|
|
#include <mainwindow.h>
|
|
#include "ui_preferencesdialog.h"
|
|
#include "SleepLib/machine_common.h"
|
|
#include "highresolution.h"
|
|
#include "daily.h"
|
|
|
|
extern QFont *defaultfont;
|
|
extern QFont *mediumfont;
|
|
extern QFont *bigfont;
|
|
extern MainWindow *mainwin;
|
|
|
|
typedef QMessageBox::StandardButton StandardButton;
|
|
typedef QMessageBox::StandardButtons StandardButtons;
|
|
|
|
QHash<schema::ChanType, QString> channeltype;
|
|
|
|
|
|
QString PreferencesDialog::clinicalHelp() {
|
|
QStringList str; str
|
|
<<tr("Clinical Mode:")
|
|
<<tr("Reports what is on the data card, all of it including any and all data deselected in the Permissive mode.")
|
|
<<tr("Basically replicates the reports and data stored on the devices data card.")
|
|
<<tr("This includes pap devices, oximeters, etc. Compliance reports fall under this mode.")
|
|
<<tr("Compliance reports always include all data within the chosen Compliance period, even if otherwise deselected.")
|
|
<<""
|
|
<<tr("Permissive Mode:")
|
|
<<tr("Allows user to select which data sets/ sessions to be used for calculations and display.")
|
|
<<tr("Additional charts and calculations may be available that are not available from the vendor data.")
|
|
<<tr("Enables Custom UserFlags displayed in the statistics Therapy Efficacy section")
|
|
<<"";
|
|
return str.join("\n");
|
|
}
|
|
|
|
PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) :
|
|
QDialog(parent, Qt::WindowTitleHint | Qt::WindowCloseButtonHint),
|
|
ui(new Ui::PreferencesDialog),
|
|
profile(_profile)
|
|
{
|
|
ui->setupUi(this);
|
|
|
|
channeltype.clear();
|
|
channeltype[schema::FLAG] = tr("Flag");
|
|
channeltype[schema::MINOR_FLAG] = tr("Minor Flag");
|
|
channeltype[schema::SPAN] = tr("Span");
|
|
channeltype[schema::UNKNOWN] = tr("Always Minor");
|
|
bool haveResMed = false;
|
|
QList<Machine *> machines = profile->GetMachines(MT_CPAP);
|
|
// qDebug() << "Machile list size is" << machines.size();
|
|
if ( machines.size() > 0 ) {
|
|
for (QList<Machine *>::iterator it = machines.begin(); it != machines.end(); ++it) {
|
|
const QString & mclass=(*it)->loaderName();
|
|
if (mclass == STR_MACH_ResMed) {
|
|
haveResMed = true;
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
if (QMessageBox::question(this, tr("No CPAP devices detected"),
|
|
tr("Will you be using a ResMed brand device?"),
|
|
QMessageBox::Yes, QMessageBox::No) == QMessageBox::Yes )
|
|
haveResMed = true;
|
|
}
|
|
|
|
#ifdef LOCK_RESMED_SESSIONS
|
|
// Remove access to session splitting options and show ResMed users a notice instead
|
|
ui->ResMedWarning->setText(tr("<p><b>Please Note:</b> OSCAR's advanced session splitting capabilities are not possible with <b>ResMed</b> devices due to a limitation in the way their settings and summary data is stored, and therefore they have been disabled for this profile.</p><p>On ResMed devices, days will <b>split at noon</b> like in ResMed's commercial software.</p>"));
|
|
ui->ResMedWarning->setVisible(haveResMed);
|
|
|
|
if (haveResMed) {
|
|
profile->forceResmedPrefs();
|
|
ui->sessionSplitWidget->setVisible(!haveResMed);
|
|
ui->prefCalcMax->setEnabled(false);
|
|
ui->prefCalcMiddle->setEnabled(false);
|
|
ui->prefCalcPercentile->setEnabled(false);
|
|
ui->showUnknownFlags->setEnabled(false);
|
|
ui->calculateUnintentionalLeaks->setEnabled(false);
|
|
ui->createSDBackups->setChecked(true);
|
|
ui->createSDBackups->setEnabled(false);
|
|
|
|
}
|
|
ui->resmedPrefCalcsNotice->setVisible(haveResMed);
|
|
#endif
|
|
|
|
int gfxEngine=(int)currentGFXEngine();
|
|
|
|
int selIdx = 0;
|
|
for (int j=0, i=0; i<=(int)MaxGFXEngine; ++i) {
|
|
if (gfxEgnineIsSupported((GFXEngine) i)) {
|
|
ui->gfxEngineCombo->addItem(GFXEngineNames[i], i);
|
|
if (i==gfxEngine) {
|
|
selIdx = j;
|
|
}
|
|
++j;
|
|
}
|
|
}
|
|
ui->gfxEngineCombo->setCurrentIndex(selIdx);
|
|
|
|
|
|
ui->culminativeIndices->setEnabled(false);
|
|
|
|
QLocale locale = QLocale::system();
|
|
QString shortformat = locale.dateFormat(QLocale::ShortFormat);
|
|
|
|
if (!shortformat.toLower().contains("yyyy")) {
|
|
shortformat.replace("yy", "yyyy");
|
|
}
|
|
|
|
// Qt::DayOfWeek dow = firstDayOfWeekFromLocale();
|
|
|
|
// QTextCharFormat format = ui->startedUsingMask->calendarWidget()->weekdayTextFormat(Qt::Saturday);
|
|
// format.setForeground(QBrush(Qt::black, Qt::SolidPattern));
|
|
|
|
if (profile == nullptr) {
|
|
qCritical() << "Preferences dialog created without legit profile object";
|
|
return;
|
|
}
|
|
ui->tabWidget->setCurrentIndex(0);
|
|
|
|
//i=ui->timeZoneCombo->findText((*profile)["TimeZone"].toString());
|
|
//ui->timeZoneCombo->setCurrentIndex(i);
|
|
|
|
ui->showLeakRedline->setChecked(profile->cpap->showLeakRedline());
|
|
ui->leakRedlineSpinbox->setValue(profile->cpap->leakRedline());
|
|
|
|
//change initialization from hard coded in schema to profile data.
|
|
ui->oxiDesaturationThreshold->setValue(profile->oxi->oxiDesaturationThreshold());
|
|
ui->flagPulseAbove->setValue(profile->oxi->flagPulseAbove());
|
|
ui->flagPulseBelow->setValue(profile->oxi->flagPulseBelow());
|
|
|
|
ui->spo2Drop->setValue(profile->oxi->spO2DropPercentage());
|
|
ui->spo2DropDuration->setValue(profile->oxi->spO2DropDuration());
|
|
ui->pulseChange->setValue(profile->oxi->pulseChangeBPM());
|
|
ui->pulseChangeDuration->setValue(profile->oxi->pulseChangeDuration());
|
|
ui->oxiDiscardThreshold->setValue(profile->oxi->oxiDiscardThreshold());
|
|
|
|
ui->eventIndexCombo->setCurrentIndex(profile->general->calculateRDI() ? 1 : 0);
|
|
|
|
ui->automaticImport->setChecked(profile->cpap->autoImport()); // Skip extra dialogs, this needs to rename.
|
|
|
|
ui->autoLoadLastUsed->setChecked(AppSetting->autoOpenLastUsed());
|
|
|
|
ui->timeEdit->setTime(profile->session->daySplitTime());
|
|
int val = profile->session->combineCloseSessions();
|
|
ui->combineSlider->setValue(val);
|
|
|
|
ui->openingTabCombo->setCurrentIndex(AppSetting->openTabAtStart());
|
|
ui->importTabCombo->setCurrentIndex(AppSetting->openTabAfterImport());
|
|
|
|
|
|
if (val > 0) {
|
|
ui->combineLCD->display(val);
|
|
} else { ui->combineLCD->display(STR_TR_Off); }
|
|
|
|
val = profile->session->ignoreShortSessions();
|
|
ui->IgnoreSlider->setValue(val);
|
|
|
|
if (val > 0) {
|
|
ui->IgnoreLCD->display(val);
|
|
} else { ui->IgnoreLCD->display(STR_TR_Off); }
|
|
|
|
ui->LockSummarySessionSplitting->setChecked(profile->session->lockSummarySessions());
|
|
ui->warnOnUntestedMachine->setChecked(profile->session->warnOnUntestedMachine());
|
|
ui->warnOnUnexpectedData->setChecked(profile->session->warnOnUnexpectedData());
|
|
|
|
// macOS default system fonts are not in QFontCombobox because they are "private":
|
|
// See https://github.com/musescore/MuseScore/commit/0eecb165664a0196c2eee12e42fb273dcfc9c637
|
|
// The below makes sure any default system font is present in QFontComboBox.
|
|
QString systemFontFamily = QFontDatabase::systemFont(QFontDatabase::GeneralFont).family();
|
|
if (-1 == ui->applicationFont->findText(systemFontFamily)) {
|
|
ui->applicationFont->addItem(systemFontFamily);
|
|
}
|
|
// If the current font is the system font, setCurrentFont() won't work as intended,
|
|
// so select the font by searching for its name, which will always work.
|
|
ui->applicationFont->setCurrentIndex(ui->applicationFont->findText(QApplication::font().family()));
|
|
//ui->applicationFont->setFont(QApplication::font());
|
|
ui->applicationFontSize->setValue(QApplication::font().pointSize());
|
|
ui->applicationFontBold->setChecked(QApplication::font().weight() == QFont::Bold);
|
|
ui->applicationFontItalic->setChecked(QApplication::font().italic());
|
|
// ui->applicationFont->setEditable(false);
|
|
|
|
ui->graphFont->setCurrentFont(*defaultfont);
|
|
//ui->graphFont->setFont(*defaultfont);
|
|
ui->graphFontSize->setValue(defaultfont->pointSize());
|
|
ui->graphFontBold->setChecked(defaultfont->weight() == QFont::Bold);
|
|
ui->graphFontItalic->setChecked(defaultfont->italic());
|
|
// ui->graphFont->setEditable(false);
|
|
|
|
ui->titleFont->setCurrentFont(*mediumfont);
|
|
//ui->titleFont->setFont(*mediumfont);
|
|
ui->titleFontSize->setValue(mediumfont->pointSize());
|
|
ui->titleFontBold->setChecked(mediumfont->weight() == QFont::Bold);
|
|
ui->titleFontItalic->setChecked(mediumfont->italic());
|
|
// ui->titleFont->setEditable(false);
|
|
|
|
ui->bigFont->setCurrentFont(*bigfont);
|
|
//ui->bigFont->setFont(*bigfont);
|
|
ui->bigFontSize->setValue(bigfont->pointSize());
|
|
ui->bigFontBold->setChecked(bigfont->weight() == QFont::Bold);
|
|
ui->bigFontItalic->setChecked(bigfont->italic());
|
|
// ui->bigFont->setEditable(false);
|
|
|
|
ui->lineThicknessSlider->setValue(AppSetting->lineThickness()*2.0);
|
|
|
|
ui->resyncMachineDetectedEvents->setChecked(profile->cpap->resyncFromUserFlagging());
|
|
|
|
ui->useAntiAliasing->setChecked(AppSetting->antiAliasing());
|
|
ui->usePixmapCaching->setChecked(AppSetting->usePixmapCaching());
|
|
ui->useSquareWavePlots->setChecked(AppSetting->squareWavePlots());
|
|
// ui->enableGraphSnapshots->setChecked(AppSetting->graphSnapshots());
|
|
ui->graphTooltips->setChecked(AppSetting->graphTooltips());
|
|
ui->allowYAxisScaling->setChecked(AppSetting->allowYAxisScaling());
|
|
ui->includeSerial->setChecked(AppSetting->includeSerial());
|
|
ui->monochromePrinting->setChecked(AppSetting->monochromePrinting());
|
|
ui->eventFlagSessionBar->setChecked(profile->appearance->eventFlagSessionBar());
|
|
ui->disableDailyGraphTitles->setChecked(AppSetting->disableDailyGraphTitles());
|
|
ui->complianceHours->setValue(profile->cpap->complianceHours());
|
|
ui->clinicalMode->setChecked(profile->cpap->clinicalMode());
|
|
ui->clinicalTextEdit->setPlainText(clinicalHelp());
|
|
// clinicalMode and permissiveMode are radio buttons and must be set to opposite values. Once clinicalMode is used.
|
|
// Radio Buttons illustrate the operating mode.
|
|
ui->permissiveMode->setChecked(!profile->cpap->clinicalMode());
|
|
HighResolution::checkBox(false,ui->highResolution);
|
|
ui->alternatingColorsCombo->setCurrentIndex(AppSetting->alternatingColorsCombo());
|
|
|
|
ui->autoLaunchImporter->setChecked(AppSetting->autoLaunchImport());
|
|
#ifndef NO_CHECKUPDATES
|
|
ui->updateCheckEvery->setValue(AppSetting->updateCheckFrequency());
|
|
ui->allowEarlyUpdates->setChecked(AppSetting->allowEarlyUpdates());
|
|
ui->test_invite->setVisible(false);
|
|
if (!getVersion().IsReleaseVersion()) {
|
|
// Test version
|
|
ui->automaticallyCheckUpdates->setVisible(false);
|
|
ui->allowEarlyUpdates->setVisible(false);
|
|
ui->updateCheckEvery->setMaximum(7);
|
|
}
|
|
else {
|
|
// Release version
|
|
ui->updateCheckEvery->setMaximum(90);
|
|
ui->always_look_for_updates->setVisible(false);
|
|
if (!AppSetting->allowEarlyUpdates()) {
|
|
ui->test_invite->setVisible(true);
|
|
ui->allowEarlyUpdates->setVisible(false);
|
|
}
|
|
}
|
|
#else
|
|
ui->automaticallyCheckUpdates_GroupBox->setVisible(false);
|
|
#endif
|
|
|
|
int s = profile->cpap->clockDrift();
|
|
int m = (s / 60) % 60;
|
|
int h = (s / 3600);
|
|
s %= 60;
|
|
ui->clockDriftHours->setValue(h);
|
|
ui->clockDriftMinutes->setValue(m);
|
|
ui->clockDriftSeconds->setValue(s);
|
|
|
|
ui->skipEmptyDays->setChecked(profile->general->skipEmptyDays());
|
|
ui->showUnknownFlags->setChecked(profile->general->showUnknownFlags());
|
|
// ui->enableMultithreading->setChecked(AppSetting->multithreading());
|
|
ui->enableMultithreading->setVisible(false);
|
|
ui->removeCardNotificationCheckbox->setChecked(AppSetting->removeCardReminder());
|
|
ui->notifyMessageBoxCheckbox->setChecked(AppSetting->notifyMessagBoxOption());
|
|
ui->dontAskWhenSavingScreenshotsCheckbox->setChecked(AppSetting->dontAskWhenSavingScreenshots());
|
|
ui->cacheSessionData->setChecked(AppSetting->cacheSessions());
|
|
ui->preloadSummaries->setChecked(profile->session->preloadSummaries());
|
|
ui->animationsAndTransitionsCheckbox->setChecked(AppSetting->animations());
|
|
|
|
ui->prefCalcMiddle->setCurrentIndex(profile->general->prefCalcMiddle());
|
|
ui->prefCalcMax->setCurrentIndex(profile->general->prefCalcMax());
|
|
float f = profile->general->prefCalcPercentile();
|
|
ui->prefCalcPercentile->setValue(f);
|
|
|
|
ui->tooltipTimeoutSlider->setValue(AppSetting->tooltipTimeout());
|
|
on_tooltipTimeoutSlider_valueChanged(ui->tooltipTimeoutSlider->value());
|
|
//ui->tooltipMS->display(profile->general->tooltipTimeout());
|
|
|
|
ui->scrollDampeningSlider->setValue(AppSetting->scrollDampening() / 10);
|
|
on_scrollDampeningSlider_valueChanged(ui->scrollDampeningSlider->value());
|
|
|
|
bool bcd = profile->session->backupCardData();
|
|
ui->createSDBackups->setChecked(bcd);
|
|
ui->compressSDBackups->setEnabled(bcd);
|
|
ui->compressSDBackups->setChecked(profile->session->compressBackupData());
|
|
ui->compressSessionData->setChecked(profile->session->compressSessionData());
|
|
ui->ignoreOlderSessionsCheck->setChecked(profile->session->ignoreOlderSessions());
|
|
ui->ignoreOlderSessionsDate->setDate(profile->session->ignoreOlderSessionsDate().date());
|
|
|
|
ui->graphHeight->setValue(AppSetting->graphHeight());
|
|
|
|
#ifndef NO_CHECKUPDATES
|
|
ui->automaticallyCheckUpdates->setChecked(AppSetting->updatesAutoCheck());
|
|
ui->updateCheckEvery->setValue(AppSetting->updateCheckFrequency());
|
|
if (AppSetting->updatesLastChecked().isValid()) {
|
|
RefreshLastChecked();
|
|
} else {
|
|
ui->updateLastChecked->setText(tr("Never"));
|
|
}
|
|
#endif
|
|
|
|
ui->overlayFlagsCombo->setCurrentIndex(AppSetting->overlayType());
|
|
#ifndef REMOVE_FITNESS
|
|
ui->overviewLinecharts->setCurrentIndex(AppSetting->overviewLinechartMode());
|
|
#else
|
|
ui->overviewLinecharts->hide();
|
|
ui->overviewLinechartsLabel->hide();
|
|
#endif
|
|
|
|
ui->ahiGraphWindowSize->setEnabled(false);
|
|
ui->ahiGraphWindowSize->setValue(profile->cpap->AHIWindow());
|
|
ui->ahiGraphZeroReset->setChecked(profile->cpap->AHIReset());
|
|
|
|
ui->customEventGroupbox->setChecked(profile->cpap->userEventFlagging());
|
|
ui->apneaDuration->setValue(profile->cpap->userEventDuration());
|
|
ui->apneaFlowRestriction->setValue(profile->cpap->userEventRestriction());
|
|
ui->apneaDuration2->setValue(profile->cpap->userEventDuration2());
|
|
ui->apneaFlowRestriction2->setValue(profile->cpap->userEventRestriction2());
|
|
ui->userEventDuplicates->setChecked(profile->cpap->userEventDuplicates());
|
|
ui->userEventDuplicates->setVisible(false);
|
|
|
|
ui->showUserFlagsInPie->setChecked(AppSetting->userEventPieChart());
|
|
|
|
bool b;
|
|
ui->calculateUnintentionalLeaks->setChecked(b=profile->cpap->calculateUnintentionalLeaks());
|
|
|
|
ui->maskLeaks4Slider->setValue(profile->cpap->custom4cmH2OLeaks()*10.0);
|
|
ui->maskLeaks20Slider->setValue(profile->cpap->custom20cmH2OLeaks()*10.0);
|
|
|
|
ui->maskLeaks4Label->setText(QString("%1 %2").arg(profile->cpap->custom4cmH2OLeaks(), 5, 'f', 1).arg(STR_UNIT_LPM));
|
|
ui->maskLeaks20Label->setText(QString("%1 %2").arg(profile->cpap->custom20cmH2OLeaks(), 5, 'f', 1).arg(STR_UNIT_LPM));
|
|
|
|
/* QLocale locale=QLocale::system();
|
|
QString shortformat=locale.dateFormat(QLocale::ShortFormat);
|
|
if (!shortformat.toLower().contains("yyyy")) {
|
|
shortformat.replace("yy","yyyy");
|
|
}*/
|
|
|
|
chanFilterModel = new MySortFilterProxyModel(this);
|
|
chanModel = new QStandardItemModel(this);
|
|
chanFilterModel->setSourceModel(chanModel);
|
|
chanFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
chanFilterModel->setFilterKeyColumn(0);
|
|
ui->chanView->setModel(chanFilterModel);
|
|
|
|
InitChanInfo();
|
|
|
|
waveFilterModel = new MySortFilterProxyModel(this);
|
|
waveModel = new QStandardItemModel(this);
|
|
waveFilterModel->setSourceModel(waveModel);
|
|
waveFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
waveFilterModel->setFilterKeyColumn(0);
|
|
ui->waveView->setModel(waveFilterModel);
|
|
InitWaveInfo();
|
|
|
|
ui->waveView->setSortingEnabled(true);
|
|
ui->chanView->setSortingEnabled(true);
|
|
|
|
ui->waveView->sortByColumn(0, Qt::AscendingOrder);
|
|
ui->chanView->sortByColumn(0, Qt::AscendingOrder);
|
|
|
|
}
|
|
|
|
#include <QItemDelegate>
|
|
class SpinBoxDelegate : public QItemDelegate
|
|
{
|
|
|
|
public:
|
|
SpinBoxDelegate(QObject *parent = 0):QItemDelegate(parent) {}
|
|
|
|
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
|
|
|
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
|
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
|
|
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
|
};
|
|
|
|
QWidget *SpinBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const
|
|
{
|
|
QDoubleSpinBox *editor = new QDoubleSpinBox(parent);
|
|
//editor->setMinimum(0);
|
|
//editor->setMaximum(100.0);
|
|
|
|
return editor;
|
|
}
|
|
|
|
void SpinBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
|
{
|
|
double value = index.model()->data(index, Qt::EditRole).toDouble();
|
|
|
|
QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
|
|
spinBox->setMinimum(-9999999.0);
|
|
spinBox->setMaximum(9999999.0);
|
|
|
|
spinBox->setValue(value);
|
|
}
|
|
|
|
void SpinBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
|
{
|
|
QDoubleSpinBox *spinBox = static_cast<QDoubleSpinBox*>(editor);
|
|
spinBox->interpretText();
|
|
double value = spinBox->value();
|
|
|
|
model->setData(index, value, Qt::EditRole);
|
|
}
|
|
void SpinBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
|
|
{
|
|
editor->setGeometry(option.rect);
|
|
}
|
|
|
|
|
|
#include <QItemDelegate>
|
|
class ComboBoxDelegate : public QItemDelegate
|
|
{
|
|
|
|
public:
|
|
ComboBoxDelegate(QObject *parent = 0):QItemDelegate(parent) {}
|
|
|
|
virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
|
|
|
virtual void setEditorData(QWidget *editor, const QModelIndex &index) const;
|
|
virtual void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const;
|
|
virtual void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
|
};
|
|
|
|
QWidget *ComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const
|
|
{
|
|
QComboBox *combo = new QComboBox(parent);
|
|
|
|
QHash<schema::ChanType, QString>::iterator it;
|
|
for (it = channeltype.begin(); it != channeltype.end(); ++it) {
|
|
if (it.key() == schema::UNKNOWN) continue;
|
|
combo->addItem(it.value());
|
|
}
|
|
//editor->setMinimum(0);
|
|
//editor->setMaximum(100.0);
|
|
|
|
return combo;
|
|
}
|
|
|
|
void ComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
|
{
|
|
QString value = index.model()->data(index, Qt::EditRole).toString();
|
|
|
|
QComboBox *combo = static_cast<QComboBox*>(editor);
|
|
|
|
combo->setCurrentText(value);
|
|
}
|
|
|
|
void ComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
|
|
{
|
|
QComboBox *combo = static_cast<QComboBox*>(editor);
|
|
|
|
model->setData(index, combo->currentText(), Qt::EditRole);
|
|
}
|
|
void ComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const
|
|
{
|
|
editor->setGeometry(option.rect);
|
|
}
|
|
|
|
|
|
void PreferencesDialog::InitChanInfo()
|
|
{
|
|
QHash<MachineType, int> toprows;
|
|
|
|
chanModel->clear();
|
|
toplevel.clear();
|
|
toprows.clear();
|
|
QStringList headers;
|
|
headers.append(tr("Name"));
|
|
headers.append(tr("Color"));
|
|
headers.append(tr("Overview"));
|
|
headers.append(tr("Flag Type"));
|
|
headers.append(tr("Label"));
|
|
headers.append(tr("Details"));
|
|
chanModel->setHorizontalHeaderLabels(headers);
|
|
ui->chanView->setColumnWidth(0, 200);
|
|
ui->chanView->setColumnWidth(1, 40);
|
|
ui->chanView->setColumnWidth(2, 60);
|
|
ui->chanView->setColumnWidth(3, 100);
|
|
ui->chanView->setColumnWidth(4, 100);
|
|
ui->chanView->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
ui->chanView->setSelectionBehavior(QAbstractItemView::SelectItems);
|
|
|
|
chanModel->setColumnCount(6);
|
|
|
|
QStandardItem *hdr = nullptr;
|
|
|
|
QMap<MachineType, QString> Section;
|
|
|
|
Section[MT_CPAP] = tr("CPAP Events");
|
|
Section[MT_OXIMETER] = tr("Oximeter Events");
|
|
Section[MT_POSITION] = tr("Positional Events");
|
|
Section[MT_SLEEPSTAGE] = tr("Sleep Stage Events");
|
|
Section[MT_UNCATEGORIZED] = tr("Unknown Events");
|
|
|
|
QMap<MachineType, QString>::iterator it;
|
|
|
|
QHash<QString, schema::Channel *>::iterator ci;
|
|
|
|
for (it = Section.begin(); it != Section.end(); ++it) {
|
|
toplevel[it.key()] = hdr = new QStandardItem(it.value());
|
|
hdr->setEditable(false);
|
|
QList<QStandardItem *> list;
|
|
list.append(hdr);
|
|
for (int i=0; i<5; i++) {
|
|
QStandardItem *it = new QStandardItem();
|
|
it->setEnabled(false);
|
|
list.append(it);
|
|
}
|
|
chanModel->appendRow(list);
|
|
}
|
|
|
|
ui->chanView->setAlternatingRowColors(true);
|
|
|
|
// ui->graphView->setFirstColumnSpanned(0,daily->index(),true); // Crashes on windows.. Why do I need this again?
|
|
|
|
|
|
ComboBoxDelegate * combobox = new ComboBoxDelegate(ui->waveView);
|
|
|
|
ui->chanView->setItemDelegateForColumn(3,combobox);
|
|
|
|
int row = 0;
|
|
for (ci = schema::channel.names.begin(); ci != schema::channel.names.end(); ci++) {
|
|
schema::Channel * chan = ci.value();
|
|
if ((chan->type() == schema::DATA) || (chan->type() == schema::SETTING) || chan->type() == schema::WAVEFORM) continue;
|
|
|
|
QList<QStandardItem *> items;
|
|
QStandardItem *it = new QStandardItem(chan->fullname());
|
|
it->setCheckable(true);
|
|
it->setCheckState(chan->enabled() ? Qt::Checked : Qt::Unchecked);
|
|
it->setEditable(true);
|
|
it->setData(chan->id(), Qt::UserRole);
|
|
|
|
// Dear translators: %1 is a unique ascii english string used to indentify channels in the code, I'd like feedback on how this goes..
|
|
// It's here in case users mess up which field is which.. it will always show the Channel Code underneath in the tooltip.
|
|
it->setToolTip(tr("Double click to change the descriptive name the '%1' channel.").arg(chan->code()));
|
|
items.push_back(it);
|
|
|
|
|
|
it = new QStandardItem();
|
|
it->setBackground(QBrush(chan->defaultColor()));
|
|
it->setEditable(false);
|
|
it->setData(chan->defaultColor().rgba(), Qt::UserRole);
|
|
it->setToolTip(tr("Double click to change the default color for this channel plot/flag/data."));
|
|
it->setSelectable(false);
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem(QString());
|
|
it->setToolTip(tr("Whether this flag has a dedicated overview chart."));
|
|
it->setCheckable(true);
|
|
it->setCheckState(chan->showInOverview() ? Qt::Checked : Qt::Unchecked);
|
|
it->setTextAlignment(Qt::AlignCenter);
|
|
it->setData(chan->id(), Qt::UserRole);
|
|
items.push_back(it);
|
|
|
|
schema::ChanType type = chan->type();
|
|
|
|
it = new QStandardItem(channeltype[type]);
|
|
it->setToolTip(tr("Here you can change the type of flag shown for this event"));
|
|
it->setEditable(type != schema::UNKNOWN);
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem(chan->label());
|
|
it->setToolTip(tr("This is the short-form label to indicate this channel on screen."));
|
|
|
|
it->setEditable(true);
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem(chan->description());
|
|
it->setToolTip(tr("This is a description of what this channel does."));
|
|
|
|
it->setEditable(true);
|
|
items.push_back(it);
|
|
|
|
MachineType mt = chan->machtype();
|
|
if (chan->type() == schema::UNKNOWN) mt = MT_UNCATEGORIZED;
|
|
row = toprows[mt]++;
|
|
toplevel[mt]->insertRow(row, items);
|
|
}
|
|
|
|
|
|
for(QHash<MachineType, QStandardItem *>::iterator i = toplevel.begin(); i != toplevel.end(); ++i) {
|
|
if (i.value()->rowCount() == 0) {
|
|
chanModel->removeRow(i.value()->row());
|
|
}
|
|
}
|
|
|
|
ui->chanView->expandAll();
|
|
ui->chanView->setSortingEnabled(true);
|
|
}
|
|
|
|
void PreferencesDialog::InitWaveInfo()
|
|
{
|
|
QHash<MachineType, int> toprows;
|
|
|
|
waveModel->clear();
|
|
machlevel.clear();
|
|
toprows.clear();
|
|
QStringList headers;
|
|
headers.append(tr("Name"));
|
|
headers.append(tr("Color"));
|
|
headers.append(tr("Overview"));
|
|
headers.append(tr("Lower"));
|
|
headers.append(tr("Upper"));
|
|
headers.append(tr("Label"));
|
|
headers.append(tr("Details"));
|
|
waveModel->setHorizontalHeaderLabels(headers);
|
|
ui->waveView->setColumnWidth(0, 200);
|
|
ui->waveView->setColumnWidth(1, 40);
|
|
ui->waveView->setColumnWidth(2, 60);
|
|
ui->waveView->setColumnWidth(3, 50);
|
|
ui->waveView->setColumnWidth(4, 50);
|
|
ui->waveView->setColumnWidth(5, 100);
|
|
ui->waveView->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
ui->waveView->setSelectionBehavior(QAbstractItemView::SelectItems);
|
|
|
|
waveModel->setColumnCount(7);
|
|
|
|
QStandardItem *hdr = nullptr;
|
|
|
|
QMap<MachineType, QString> Section;
|
|
|
|
Section[MT_CPAP] = tr("CPAP Waveforms");
|
|
Section[MT_OXIMETER] = tr("Oximeter Waveforms");
|
|
Section[MT_POSITION] = tr("Positional Waveforms");
|
|
Section[MT_SLEEPSTAGE] = tr("Sleep Stage Waveforms");
|
|
|
|
QMap<MachineType, QString>::iterator it;
|
|
|
|
for (it = Section.begin(); it != Section.end(); ++it) {
|
|
machlevel[it.key()] = hdr = new QStandardItem(it.value());
|
|
hdr->setEditable(false);
|
|
QList<QStandardItem *> list;
|
|
list.append(hdr);
|
|
for (int i=0; i<6; i++) {
|
|
QStandardItem *it = new QStandardItem();
|
|
it->setEnabled(false);
|
|
list.append(it);
|
|
}
|
|
waveModel->appendRow(list);
|
|
}
|
|
|
|
ui->waveView->setAlternatingRowColors(true);
|
|
|
|
// ui->graphView->setFirstColumnSpanned(0,daily->index(),true); // Crashes on windows.. Why do I need this again?
|
|
|
|
QHash<QString, schema::Channel *>::iterator ci;
|
|
|
|
SpinBoxDelegate * spinbox = new SpinBoxDelegate(ui->waveView);
|
|
|
|
ui->waveView->setItemDelegateForColumn(3,spinbox);
|
|
ui->waveView->setItemDelegateForColumn(4,spinbox);
|
|
|
|
int row = 0;
|
|
for (ci = schema::channel.names.begin(); ci != schema::channel.names.end(); ci++) {
|
|
schema::Channel * chan = ci.value();
|
|
if (chan->type() != schema::WAVEFORM) continue;
|
|
|
|
QList<QStandardItem *> items;
|
|
QStandardItem *it = new QStandardItem(chan->fullname());
|
|
|
|
it->setCheckable(true);
|
|
it->setCheckState(chan->enabled() ? Qt::Checked : Qt::Unchecked);
|
|
it->setEditable(true);
|
|
it->setData(chan->id(), Qt::UserRole);
|
|
it->setToolTip(tr("Double click to change the descriptive name this channel."));
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem();
|
|
it->setBackground(QBrush(chan->defaultColor()));
|
|
it->setEditable(false);
|
|
it->setData(chan->defaultColor().rgba(), Qt::UserRole);
|
|
it->setToolTip(tr("Double click to change the default color for this channel plot/flag/data."));
|
|
it->setSelectable(false);
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem();
|
|
it->setCheckable(true);
|
|
it->setCheckState(chan->showInOverview() ? Qt::Checked : Qt::Unchecked);
|
|
it->setEditable(true);
|
|
it->setData(chan->id(), Qt::UserRole);
|
|
it->setToolTip(tr("Whether a breakdown of this waveform displays in overview."));
|
|
items.push_back(it);
|
|
|
|
|
|
it = new QStandardItem(QString::number(chan->lowerThreshold(),'f',1));
|
|
it->setToolTip(tr("Here you can set the <b>lower</b> threshold used for certain calculations on the %1 waveform").arg(chan->fullname()));
|
|
it->setEditable(true);
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem(QString::number(chan->upperThreshold(),'f',1));
|
|
it->setToolTip(tr("Here you can set the <b>upper</b> threshold used for certain calculations on the %1 waveform").arg(chan->fullname()));
|
|
it->setEditable(true);
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem(chan->label());
|
|
it->setToolTip(tr("This is the short-form label to indicate this channel on screen."));
|
|
|
|
it->setEditable(true);
|
|
items.push_back(it);
|
|
|
|
it = new QStandardItem(chan->description());
|
|
it->setToolTip(tr("This is a description of what this channel does."));
|
|
|
|
it->setEditable(true);
|
|
items.push_back(it);
|
|
|
|
row = toprows[chan->machtype()]++;
|
|
machlevel[chan->machtype()]->insertRow(row, items);
|
|
}
|
|
|
|
for(QHash<MachineType, QStandardItem *>::iterator i = machlevel.begin(); i != machlevel.end(); ++i) {
|
|
if (i.value()->rowCount() == 0) {
|
|
waveModel->removeRow(i.value()->row());
|
|
}
|
|
}
|
|
|
|
ui->waveView->expandAll();
|
|
ui->waveView->setSortingEnabled(true);
|
|
}
|
|
|
|
|
|
PreferencesDialog::~PreferencesDialog()
|
|
{
|
|
delete ui;
|
|
}
|
|
|
|
bool PreferencesDialog::Save()
|
|
{
|
|
bool recompress_events = false;
|
|
bool recalc_events = false;
|
|
bool needs_restart = false;
|
|
bool needs_reload = false;
|
|
|
|
if (ui->ahiGraphZeroReset->isChecked() != profile->cpap->AHIReset()) { recalc_events = true; }
|
|
|
|
if (ui->useSquareWavePlots->isChecked() != AppSetting->squareWavePlots()) {
|
|
needs_reload = true;
|
|
}
|
|
|
|
if ((profile->session->daySplitTime() != ui->timeEdit->time()) ||
|
|
(profile->session->combineCloseSessions() != ui->combineSlider->value()) ||
|
|
(profile->session->ignoreShortSessions() != ui->IgnoreSlider->value())) {
|
|
needs_reload = true;
|
|
}
|
|
|
|
if (profile->session->lockSummarySessions() != ui->LockSummarySessionSplitting->isChecked()) {
|
|
needs_reload = true;
|
|
}
|
|
|
|
if (AppSetting->userEventPieChart() != ui->showUserFlagsInPie->isChecked()) {
|
|
// lazy.. fix me
|
|
needs_reload = true;
|
|
}
|
|
|
|
if ((GFXEngine)ui->gfxEngineCombo->currentData().toUInt() != currentGFXEngine()) {
|
|
setCurrentGFXEngine((GFXEngine)ui->gfxEngineCombo->currentData().toUInt());
|
|
needs_restart = true;
|
|
}
|
|
|
|
int rdi_set = profile->general->calculateRDI() ? 1 : 0;
|
|
if (rdi_set != ui->eventIndexCombo->currentIndex()) {
|
|
//recalc_events=true;
|
|
needs_reload = true;
|
|
}
|
|
|
|
if ((profile->general->prefCalcMiddle() != ui->prefCalcMiddle->currentIndex())
|
|
|| (profile->general->prefCalcMax() != ui->prefCalcMax->currentIndex())
|
|
|| (profile->general->prefCalcPercentile() != ui->prefCalcPercentile->value())) {
|
|
needs_reload = true;
|
|
}
|
|
|
|
if (profile->cpap->leakRedline() != ui->leakRedlineSpinbox->value()) {
|
|
needs_reload = true;
|
|
}
|
|
|
|
|
|
if (profile->cpap->userEventFlagging() &&
|
|
(profile->cpap->userEventDuration() != ui->apneaDuration->value() ||
|
|
profile->cpap->userEventDuration2() != ui->apneaDuration2->value() ||
|
|
profile->cpap->userEventDuplicates() != ui->userEventDuplicates->isChecked() ||
|
|
profile->cpap->userEventRestriction2() != ui->apneaFlowRestriction2->value() ||
|
|
profile->cpap->userEventRestriction() != ui->apneaFlowRestriction->value())) {
|
|
recalc_events = true;
|
|
}
|
|
|
|
// Restart if turning user event flagging on/off
|
|
if (profile->cpap->userEventFlagging() != ui->customEventGroupbox->isChecked()) {
|
|
// if (profile->cpap->userEventFlagging()) {
|
|
// Don't bother recalculating, just switch off
|
|
needs_reload = true;
|
|
//} else
|
|
recalc_events = true;
|
|
}
|
|
|
|
if (profile->session->compressSessionData() != ui->compressSessionData->isChecked()) {
|
|
recompress_events = true;
|
|
}
|
|
|
|
if (recompress_events) {
|
|
if (profile->countDays(MT_CPAP, profile->FirstDay(), profile->LastDay()) > 0) {
|
|
if (QMessageBox::question(this, tr("Data Processing Required"),
|
|
tr("A data re/decompression proceedure is required to apply these changes. This operation may take a couple of minutes to complete.\n\nAre you sure you want to make these changes?"),
|
|
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
|
|
return false;
|
|
}
|
|
} else { recompress_events = false; }
|
|
}
|
|
if (recalc_events) {
|
|
if (profile->countDays(MT_CPAP, profile->FirstDay(), profile->LastDay()) > 0) {
|
|
if (QMessageBox::question(this, tr("Data Reindex Required"),
|
|
tr("A data reindexing proceedure is required to apply these changes. This operation may take a couple of minutes to complete.\n\nAre you sure you want to make these changes?"),
|
|
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
|
|
return false;
|
|
}
|
|
} else { recalc_events = false; }
|
|
} else if (needs_restart) {
|
|
if (QMessageBox::question(this, tr("Restart Required"),
|
|
tr("One or more of the changes you have made will require this application to be restarted, in order for these changes to come into effect.\n\nWould you like do this now?"),
|
|
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
AppSetting->setUserEventPieChart(ui->showUserFlagsInPie->isChecked());
|
|
profile->session->setLockSummarySessions(ui->LockSummarySessionSplitting->isChecked());
|
|
profile->session->setWarnOnUntestedMachine(ui->warnOnUntestedMachine->isChecked());
|
|
profile->session->setWarnOnUnexpectedData(ui->warnOnUnexpectedData->isChecked());
|
|
|
|
AppSetting->setOpenTabAtStart(ui->openingTabCombo->currentIndex());
|
|
AppSetting->setOpenTabAfterImport(ui->importTabCombo->currentIndex());
|
|
|
|
AppSetting->setAllowYAxisScaling(ui->allowYAxisScaling->isChecked());
|
|
AppSetting->setIncludeSerial(ui->includeSerial->isChecked());
|
|
AppSetting->setMonochromePrinting(ui->monochromePrinting->isChecked());
|
|
p_profile->appearance->setEventFlagSessionBar(ui->eventFlagSessionBar->isChecked());
|
|
AppSetting->setDisableDailyGraphTitles(ui->disableDailyGraphTitles->isChecked());
|
|
|
|
bool clicicalModeChanged = profile->cpap->clinicalMode() != ui->clinicalMode->isChecked() ;
|
|
p_profile->cpap->setClinicalMode(ui->clinicalMode->isChecked());
|
|
|
|
if (HighResolution::checkBox(true,ui->highResolution) ) {
|
|
//needs_restart = true;
|
|
QTimer::singleShot(0, mainwin, SLOT(RestartApplication(true, "-p")));
|
|
return true; // save profile
|
|
}
|
|
|
|
if (ui->alternatingColorsCombo->currentIndex() != AppSetting->alternatingColorsCombo()) {
|
|
AppSetting->setAlternatingColorsCombo(ui->alternatingColorsCombo->currentIndex());
|
|
mainwin->GenerateStatistics();
|
|
}
|
|
|
|
AppSetting->setGraphTooltips(ui->graphTooltips->isChecked());
|
|
|
|
AppSetting->setAntiAliasing(ui->useAntiAliasing->isChecked());
|
|
AppSetting->setUsePixmapCaching(ui->usePixmapCaching->isChecked());
|
|
AppSetting->setSquareWavePlots(ui->useSquareWavePlots->isChecked());
|
|
// AppSetting->setGraphSnapshots(ui->enableGraphSnapshots->isChecked());
|
|
AppSetting->setLineThickness(float(ui->lineThicknessSlider->value()) / 2.0);
|
|
|
|
profile->general->setSkipEmptyDays(ui->skipEmptyDays->isChecked());
|
|
|
|
AppSetting->setTooltipTimeout(ui->tooltipTimeoutSlider->value());
|
|
AppSetting->setScrollDampening(ui->scrollDampeningSlider->value() * 10);
|
|
|
|
profile->general->setShowUnknownFlags(ui->showUnknownFlags->isChecked());
|
|
AppSetting->setMultithreading(ui->enableMultithreading->isChecked());
|
|
AppSetting->setRemoveCardReminder(ui->removeCardNotificationCheckbox->isChecked());
|
|
AppSetting->setNotifyMessagBoxOption(ui->notifyMessageBoxCheckbox->isChecked());
|
|
AppSetting->setDontAskWhenSavingScreenshots(ui->dontAskWhenSavingScreenshotsCheckbox->isChecked());
|
|
|
|
AppSetting->setCacheSessions(ui->cacheSessionData->isChecked());
|
|
profile->session->setPreloadSummaries(ui->preloadSummaries->isChecked());
|
|
AppSetting->setAnimations(ui->animationsAndTransitionsCheckbox->isChecked());
|
|
|
|
profile->cpap->setShowLeakRedline(ui->showLeakRedline->isChecked());
|
|
profile->cpap->setLeakRedline(ui->leakRedlineSpinbox->value());
|
|
|
|
profile->cpap->setComplianceHours(ui->complianceHours->value());
|
|
|
|
if (ui->graphHeight->value() != AppSetting->graphHeight()) {
|
|
AppSetting->setGraphHeight(ui->graphHeight->value());
|
|
mainwin->getDaily()->ResetGraphLayout();
|
|
mainwin->getOverview()->ResetGraphLayout();
|
|
}
|
|
|
|
profile->general->setPrefCalcMiddle(ui->prefCalcMiddle->currentIndex());
|
|
profile->general->setPrefCalcMax(ui->prefCalcMax->currentIndex());
|
|
profile->general->setPrefCalcPercentile(ui->prefCalcPercentile->value());
|
|
|
|
profile->general->setCalculateRDI((ui->eventIndexCombo->currentIndex() == 1));
|
|
profile->session->setBackupCardData(ui->createSDBackups->isChecked());
|
|
profile->session->setCompressBackupData(ui->compressSDBackups->isChecked());
|
|
profile->session->setCompressSessionData(ui->compressSessionData->isChecked());
|
|
|
|
profile->session->setCombineCloseSessions(ui->combineSlider->value());
|
|
profile->session->setIgnoreShortSessions(ui->IgnoreSlider->value());
|
|
profile->session->setDaySplitTime(ui->timeEdit->time());
|
|
profile->session->setIgnoreOlderSessions(ui->ignoreOlderSessionsCheck->isChecked());
|
|
profile->session->setIgnoreOlderSessionsDate(ui->ignoreOlderSessionsDate->date());
|
|
|
|
int s = ui->clockDriftHours->value() * 3600 + ui->clockDriftMinutes->value() * 60 + ui->clockDriftSeconds->value();
|
|
profile->cpap->setClockDrift(s);
|
|
|
|
AppSetting->setOverlayType((OverlayDisplayType)ui->overlayFlagsCombo->currentIndex());
|
|
#ifndef REMOVE_FITNESS
|
|
AppSetting->setOverviewLinechartMode((OverviewLinechartModes)ui->overviewLinecharts->currentIndex());
|
|
#endif
|
|
|
|
profile->oxi->setSpO2DropPercentage(ui->spo2Drop->value());
|
|
profile->oxi->setSpO2DropDuration(ui->spo2DropDuration->value());
|
|
profile->oxi->setPulseChangeBPM(ui->pulseChange->value());
|
|
profile->oxi->setPulseChangeDuration(ui->pulseChangeDuration->value());
|
|
profile->oxi->setOxiDiscardThreshold(ui->oxiDiscardThreshold->value());
|
|
|
|
profile->oxi->setOxiDesaturationThreshold(ui->oxiDesaturationThreshold->value());
|
|
profile->oxi->setFlagPulseAbove(ui->flagPulseAbove->value());
|
|
profile->oxi->setFlagPulseBelow(ui->flagPulseBelow->value());
|
|
|
|
profile->cpap->setAHIWindow(ui->ahiGraphWindowSize->value());
|
|
profile->cpap->setAHIReset(ui->ahiGraphZeroReset->isChecked());
|
|
|
|
profile->cpap->setAutoImport(ui->automaticImport->isChecked()); // delete me???
|
|
AppSetting->setAutoOpenLastUsed(ui->autoLoadLastUsed->isChecked());
|
|
|
|
profile->cpap->setUserEventFlagging(ui->customEventGroupbox->isChecked());
|
|
|
|
profile->cpap->setResyncFromUserFlagging(ui->resyncMachineDetectedEvents->isChecked());
|
|
profile->cpap->setUserEventDuration(ui->apneaDuration->value());
|
|
profile->cpap->setUserEventRestriction(ui->apneaFlowRestriction->value());
|
|
profile->cpap->setUserEventDuration2(ui->apneaDuration2->value());
|
|
profile->cpap->setUserEventRestriction2(ui->apneaFlowRestriction2->value());
|
|
|
|
profile->cpap->setUserEventDuplicates(ui->userEventDuplicates->isChecked());
|
|
|
|
|
|
|
|
if ((ui->calculateUnintentionalLeaks->isChecked() != profile->cpap->calculateUnintentionalLeaks())
|
|
|| (fabs((ui->maskLeaks4Slider->value()/10.0)-profile->cpap->custom4cmH2OLeaks())>.1)
|
|
|| (fabs((ui->maskLeaks20Slider->value()/10.0)-profile->cpap->custom20cmH2OLeaks())>.1)) {
|
|
recalc_events = true;
|
|
}
|
|
|
|
profile->cpap->setCalculateUnintentionalLeaks(ui->calculateUnintentionalLeaks->isChecked());
|
|
profile->cpap->setCustom4cmH2OLeaks(double(ui->maskLeaks4Slider->value()) / 10.0f);
|
|
profile->cpap->setCustom20cmH2OLeaks(double(ui->maskLeaks20Slider->value()) / 10.0f);
|
|
|
|
AppSetting->setAutoLaunchImport(ui->autoLaunchImporter->isChecked());
|
|
|
|
#ifndef NO_CHECKUPDATES
|
|
AppSetting->setUpdatesAutoCheck(ui->automaticallyCheckUpdates->isChecked());
|
|
AppSetting->setUpdateCheckFrequency(ui->updateCheckEvery->value());
|
|
AppSetting->setAllowEarlyUpdates(ui->allowEarlyUpdates->isChecked());
|
|
#endif
|
|
|
|
|
|
(*p_pref)["Fonts_Application_Name"] = ui->applicationFont->currentText();
|
|
(*p_pref)["Fonts_Application_Size"] = ui->applicationFontSize->value();
|
|
(*p_pref)["Fonts_Application_Bold"] = ui->applicationFontBold->isChecked();
|
|
(*p_pref)["Fonts_Application_Italic"] = ui->applicationFontItalic->isChecked();
|
|
|
|
(*p_pref)["Fonts_Graph_Name"] = ui->graphFont->currentText();
|
|
(*p_pref)["Fonts_Graph_Size"] = ui->graphFontSize->value();
|
|
(*p_pref)["Fonts_Graph_Bold"] = ui->graphFontBold->isChecked();
|
|
(*p_pref)["Fonts_Graph_Italic"] = ui->graphFontItalic->isChecked();
|
|
|
|
(*p_pref)["Fonts_Title_Name"] = ui->titleFont->currentText();
|
|
(*p_pref)["Fonts_Title_Size"] = ui->titleFontSize->value();
|
|
(*p_pref)["Fonts_Title_Bold"] = ui->titleFontBold->isChecked();
|
|
(*p_pref)["Fonts_Title_Italic"] = ui->titleFontItalic->isChecked();
|
|
|
|
(*p_pref)["Fonts_Big_Name"] = ui->bigFont->currentText();
|
|
(*p_pref)["Fonts_Big_Size"] = ui->bigFontSize->value();
|
|
(*p_pref)["Fonts_Big_Bold"] = ui->bigFontBold->isChecked();
|
|
(*p_pref)["Fonts_Big_Italic"] = ui->bigFontItalic->isChecked();
|
|
|
|
validateAllFonts(); // Make sure fonts are valid and reset to default values if not
|
|
|
|
// Changes to the application font will be set by the caller (mainwindow.cpp)
|
|
|
|
// Set other fonts for use by graph etc. pages
|
|
defaultfont = new QFont(((*p_pref)["Fonts_Graph_Name"]).toString());
|
|
defaultfont->setPointSize(((*p_pref)["Fonts_Graph_Size"]).toInt());
|
|
defaultfont->setWeight(((*p_pref)["Fonts_Graph_Bold"]).toBool() ? QFont::Bold : QFont::Normal);
|
|
defaultfont->setItalic(((*p_pref)["Fonts_Graph_Italic"]).toBool());
|
|
|
|
mediumfont = new QFont(((*p_pref)["Fonts_Title_Name"]).toString());
|
|
mediumfont->setPointSize(((*p_pref)["Fonts_Title_Size"]).toInt());
|
|
mediumfont->setWeight(((*p_pref)["Fonts_Title_Bold"]).toBool() ? QFont::Bold : QFont::Normal);
|
|
mediumfont->setItalic(((*p_pref)["Fonts_Title_Italic"]).toBool());
|
|
|
|
bigfont = new QFont(((*p_pref)["Fonts_Big_Name"]).toString());
|
|
bigfont->setPointSize(((*p_pref)["Fonts_Big_Size"]).toInt());
|
|
bigfont->setWeight(((*p_pref)["Fonts_Big_Bold"]).toBool() ? QFont::Bold : QFont::Normal);
|
|
bigfont->setItalic(((*p_pref)["Fonts_Big_Italic"]).toBool());
|
|
|
|
saveChanInfo();
|
|
saveWaveInfo();
|
|
//qDebug() << "TODO: Save channels.xml to update channel data";
|
|
|
|
p_pref->Save();
|
|
profile->Save();
|
|
profile->resetOxiChannelPref();
|
|
|
|
if (clicicalModeChanged) {
|
|
// this fails - causing duplicate reload.
|
|
//QTimer::singleShot(0, mainwin, SLOT(reloadProfile()));
|
|
// while this one works.
|
|
mainwin->reloadProfile();
|
|
} else
|
|
if (recompress_events) {
|
|
mainwin->recompressEvents();
|
|
} else if (recalc_events) {
|
|
// send a signal instead?
|
|
mainwin->reprocessEvents(needs_restart);
|
|
} else if (needs_reload) {
|
|
QTimer::singleShot(0, mainwin, SLOT(reloadProfile()));
|
|
} else if (needs_restart) {
|
|
mainwin->RestartApplication(true,"-l");
|
|
} else {
|
|
mainwin->getDaily()->LoadDate(mainwin->getDaily()->getDate());
|
|
// Save early.. just in case..
|
|
mainwin->getDaily()->graphView()->SaveSettings("Daily");
|
|
mainwin->getOverview()->graphView()->SaveSettings("Overview");
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void PreferencesDialog::saveChanInfo()
|
|
{
|
|
// Change focus to force save of any open editors..
|
|
ui->channelSearch->setFocus();
|
|
|
|
int toprows = chanModel->rowCount();
|
|
bool ok;
|
|
|
|
for (int i=0; i < toprows; i++) {
|
|
QStandardItem * topitem = chanModel->item(i,0);
|
|
|
|
if (!topitem) continue;
|
|
int rows = topitem->rowCount();
|
|
for (int j=0; j< rows; ++j) {
|
|
QStandardItem * item = topitem->child(j, 0);
|
|
if (!item) continue;
|
|
|
|
ChannelID id = item->data(Qt::UserRole).toUInt(&ok);
|
|
schema::Channel & chan = schema::channel[id];
|
|
if (chan.isNull()) continue;
|
|
chan.setEnabled(item->checkState() == Qt::Checked ? true : false);
|
|
chan.setFullname(item->text());
|
|
chan.setDefaultColor(QColor(topitem->child(j,1)->data(Qt::UserRole).toUInt()));
|
|
chan.setShowInOverview(topitem->child(j,2)->checkState() == Qt::Checked);
|
|
QString ts = topitem->child(j,3)->text();
|
|
schema::ChanType type = schema::MINOR_FLAG;
|
|
for (QHash<schema::ChanType, QString>::iterator it = channeltype.begin(); it!= channeltype.end(); ++it) {
|
|
if (it.value() == ts) {
|
|
type = it.key();
|
|
break;
|
|
}
|
|
}
|
|
chan.setType(type);
|
|
chan.setLabel(topitem->child(j,4)->text());
|
|
chan.setDescription(topitem->child(j,5)->text());
|
|
}
|
|
}
|
|
}
|
|
void PreferencesDialog::saveWaveInfo()
|
|
{
|
|
// Change focus to force save of any open editors..
|
|
ui->waveSearch->setFocus();
|
|
|
|
int toprows = waveModel->rowCount();
|
|
|
|
bool ok;
|
|
for (int i=0; i < toprows; i++) {
|
|
QStandardItem * topitem = waveModel->item(i,0);
|
|
|
|
if (!topitem) continue;
|
|
int rows = topitem->rowCount();
|
|
for (int j=0; j< rows; ++j) {
|
|
QStandardItem * item = topitem->child(j, 0);
|
|
if (!item) continue;
|
|
|
|
ChannelID id = item->data(Qt::UserRole).toUInt(&ok);
|
|
schema::Channel & chan = schema::channel[id];
|
|
if (chan.isNull()) continue;
|
|
chan.setEnabled(item->checkState() == Qt::Checked ? true : false);
|
|
chan.setFullname(item->text());
|
|
chan.setDefaultColor(QColor(topitem->child(j,1)->data(Qt::UserRole).toUInt()));
|
|
chan.setShowInOverview(topitem->child(j,2)->checkState() == Qt::Checked);
|
|
chan.setLowerThreshold(topitem->child(j,3)->text().toDouble());
|
|
chan.setUpperThreshold(topitem->child(j,4)->text().toDouble());
|
|
chan.setLabel(topitem->child(j,5)->text());
|
|
chan.setDescription(topitem->child(j,6)->text());
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::on_combineSlider_valueChanged(int position)
|
|
{
|
|
if (position > 0) {
|
|
ui->combineLCD->display(position);
|
|
} else { ui->combineLCD->display(STR_TR_Off); }
|
|
}
|
|
|
|
void PreferencesDialog::on_IgnoreSlider_valueChanged(int position)
|
|
{
|
|
if (position > 0) {
|
|
ui->IgnoreLCD->display(position);
|
|
} else { ui->IgnoreLCD->display(STR_TR_Off); }
|
|
}
|
|
|
|
#ifndef NO_CHECKUPDATES
|
|
#include "mainwindow.h"
|
|
extern MainWindow *mainwin;
|
|
void PreferencesDialog::RefreshLastChecked()
|
|
{
|
|
ui->updateLastChecked->setText(AppSetting->updatesLastChecked().toString(Qt::SystemLocaleLongDate));
|
|
}
|
|
#endif
|
|
|
|
MySortFilterProxyModel::MySortFilterProxyModel(QObject *parent)
|
|
: QSortFilterProxyModel(parent)
|
|
{
|
|
|
|
}
|
|
|
|
bool MySortFilterProxyModel::filterAcceptsRow(int source_row,
|
|
const QModelIndex &source_parent) const
|
|
{
|
|
if (source_parent == qobject_cast<QStandardItemModel *>
|
|
(sourceModel())->invisibleRootItem()->index()) {
|
|
// always accept children of rootitem, since we want to filter their children
|
|
return true;
|
|
}
|
|
|
|
return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
|
|
}
|
|
|
|
// Might still be useful..
|
|
//void PreferencesDialog::on_resetGraphButton_clicked()
|
|
//{
|
|
// QString title = tr("Confirmation");
|
|
// QString text = tr("Are you sure you want to reset your graph preferences to the defaults?");
|
|
// StandardButtons buttons = QMessageBox::Yes | QMessageBox::No;
|
|
// StandardButton defaultButton = QMessageBox::No;
|
|
|
|
// // Display confirmation dialog.
|
|
// StandardButton choice = QMessageBox::question(this, title, text, buttons, defaultButton);
|
|
|
|
// if (choice == QMessageBox::No) {
|
|
// return;
|
|
// }
|
|
|
|
// gGraphView *views[3] = {0};
|
|
// views[0] = mainwin->getDaily()->graphView();
|
|
// views[1] = mainwin->getOverview()->graphView();
|
|
|
|
// // Iterate over all graph containers.
|
|
// for (unsigned j = 0; j < 3; j++) {
|
|
// gGraphView *view = views[j];
|
|
|
|
// if (!view) {
|
|
// continue;
|
|
// }
|
|
|
|
// // Iterate over all contained graphs.
|
|
// for (int i = 0; i < view->size(); i++) {
|
|
// gGraph *g = (*view)[i];
|
|
// g->setRecMaxY(0); // FIXME: should be g->reset(), but need other patches to land.
|
|
// g->setRecMinY(0);
|
|
// g->setVisible(true);
|
|
// }
|
|
|
|
// view->updateScale();
|
|
// }
|
|
|
|
// resetGraphModel();
|
|
// ui->graphView->update();
|
|
//}
|
|
|
|
void PreferencesDialog::on_createSDBackups_toggled(bool checked)
|
|
{
|
|
if (profile->session->backupCardData() && !checked) {
|
|
QList<Machine *> mach = p_profile->GetMachines(MT_CPAP);
|
|
bool haveS9 = false;
|
|
|
|
for (int i = 0; i < mach.size(); i++) {
|
|
if (mach[i]->loaderName() == STR_MACH_ResMed) {
|
|
haveS9 = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (haveS9 && QMessageBox::question(this,
|
|
tr("This may not be a good idea"),
|
|
tr("ResMed S9 devices routinely delete certain data from your SD card older than 7 and 30 days (depending on resolution).") +
|
|
tr(" If you ever need to reimport this data again (whether in OSCAR or ResScan) this data won't come back.") +
|
|
tr(" If you need to conserve disk space, please remember to carry out manual backups.") +
|
|
tr(" Are you sure you want to disable these backups?"),
|
|
QMessageBox::Yes, QMessageBox::No) == QMessageBox::No) {
|
|
ui->createSDBackups->setChecked(true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!checked) { ui->compressSDBackups->setChecked(false); }
|
|
|
|
ui->compressSDBackups->setEnabled(checked);
|
|
}
|
|
|
|
void PreferencesDialog::on_okButton_clicked()
|
|
{
|
|
if (Save()) {
|
|
accept();
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::on_scrollDampeningSlider_valueChanged(int value)
|
|
{
|
|
if (value > 0) {
|
|
ui->scrollDampDisplay->setText(QString("%1%2").arg(value * 10).arg(STR_UNIT_ms));
|
|
} else { ui->scrollDampDisplay->setText(STR_TR_Off); }
|
|
}
|
|
|
|
void PreferencesDialog::on_tooltipTimeoutSlider_valueChanged(int value)
|
|
{
|
|
ui->tooltipTimeoutDisplay->setText(QString("%1%2").arg(double(value)/1000.0,0,'f',1).arg(STR_UNIT_s));
|
|
}
|
|
|
|
void PreferencesDialog::on_resetChannelDefaults_clicked()
|
|
{
|
|
if (QMessageBox::question(this, STR_MessageBox_Warning, QObject::tr("Are you sure you want to reset all your channel colors and settings to defaults?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
|
schema::resetChannels();
|
|
saveWaveInfo();
|
|
InitChanInfo();
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::on_resetOxiMetryDefaults_clicked()
|
|
{
|
|
|
|
if (QMessageBox::question(this, STR_MessageBox_Warning, QObject::tr("Are you sure you want to reset all your oximetry settings to defaults?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
|
|
|
// reset ui with defaul values
|
|
ui->spo2Drop->setValue( profile->oxi->defaultValue_OS_SPO2DropPercentage);
|
|
ui->spo2DropDuration->setValue( profile->oxi->defaultValue_OS_SPO2DropDuration);
|
|
ui->pulseChange->setValue( profile->oxi->defaultValue_OS_PulseChangeBPM);
|
|
ui->pulseChangeDuration->setValue(profile->oxi->defaultValue_OS_PulseChangeDuration);
|
|
|
|
ui->oxiDiscardThreshold->setValue(profile->oxi->defaultValue_OS_OxiDiscardThreshold);
|
|
ui->oxiDesaturationThreshold->setValue( profile->oxi->defaultValue_OS_oxiDesaturationThreshold);
|
|
ui->flagPulseAbove->setValue( profile->oxi->defaultValue_OS_flagPulseAbove );
|
|
ui->flagPulseBelow->setValue( profile->oxi->defaultValue_OS_flagPulseBelow );
|
|
|
|
if (Save() ) {
|
|
// comment accept out to return to the preference tab
|
|
// other wise the preference tab will close and return
|
|
accept();
|
|
}
|
|
} else {
|
|
// restore values changed
|
|
ui->spo2Drop->setValue(profile->oxi->spO2DropPercentage());
|
|
ui->spo2DropDuration->setValue(profile->oxi->spO2DropDuration());
|
|
ui->pulseChange->setValue(profile->oxi->pulseChangeBPM());
|
|
ui->pulseChangeDuration->setValue(profile->oxi->pulseChangeDuration());
|
|
|
|
ui->oxiDiscardThreshold->setValue(profile->oxi->oxiDiscardThreshold());
|
|
ui->oxiDesaturationThreshold->setValue(profile->oxi->defaultValue_OS_oxiDesaturationThreshold);
|
|
ui->flagPulseAbove->setValue( profile->oxi->defaultValue_OS_flagPulseAbove );
|
|
ui->flagPulseBelow->setValue( profile->oxi->defaultValue_OS_flagPulseBelow );
|
|
}
|
|
|
|
}
|
|
|
|
void PreferencesDialog::on_createSDBackups_clicked(bool checked)
|
|
{
|
|
if (!checked && p_profile->session->backupCardData()) {
|
|
if (QMessageBox::question(this,
|
|
STR_MessageBox_Warning, tr("Switching off backups is not a good idea, because OSCAR needs these to rebuild the database if errors are found.\n\n") +
|
|
tr("Are you really sure you want to do this?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
|
// do nothing
|
|
} else {
|
|
ui->createSDBackups->setChecked(true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::on_channelSearch_textChanged(const QString &arg1)
|
|
{
|
|
chanFilterModel->setFilterFixedString(arg1);
|
|
}
|
|
|
|
void PreferencesDialog::on_chanView_doubleClicked(const QModelIndex &index)
|
|
{
|
|
if (index.column() == 1) {
|
|
QColorDialog a;
|
|
|
|
if (!(index.flags() & Qt::ItemIsEnabled)) return;
|
|
quint32 color = index.data(Qt::UserRole).toUInt();
|
|
|
|
a.setCurrentColor(QColor((QRgb)color));
|
|
|
|
if (a.exec() == QColorDialog::Accepted) {
|
|
quint32 cv = a.currentColor().rgba();
|
|
|
|
chanFilterModel->setData(index, cv, Qt::UserRole);
|
|
chanFilterModel->setData(index, a.currentColor(), Qt::BackgroundRole);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::on_waveSearch_textChanged(const QString &arg1)
|
|
{
|
|
waveFilterModel->setFilterFixedString(arg1);
|
|
}
|
|
|
|
void PreferencesDialog::on_resetWaveformChannels_clicked()
|
|
{
|
|
if (QMessageBox::question(this, STR_MessageBox_Warning, QObject::tr("Are you sure you want to reset all your waveform channel colors and settings to defaults?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::Yes) {
|
|
schema::resetChannels();
|
|
saveChanInfo(); // reset clears EVERYTHING, so have to put these back in case they cancel.
|
|
InitWaveInfo();
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::on_waveView_doubleClicked(const QModelIndex &index)
|
|
{
|
|
if (index.column() == 1) {
|
|
QColorDialog a;
|
|
|
|
if (!(index.flags() & Qt::ItemIsEnabled)) return;
|
|
quint32 color = index.data(Qt::UserRole).toUInt();
|
|
|
|
a.setCurrentColor(QColor((QRgb)color));
|
|
|
|
if (a.exec() == QColorDialog::Accepted) {
|
|
quint32 cv = a.currentColor().rgba();
|
|
|
|
waveFilterModel->setData(index, cv, Qt::UserRole);
|
|
waveFilterModel->setData(index, a.currentColor(), Qt::BackgroundRole);
|
|
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
void PreferencesDialog::on_maskLeaks4Slider_valueChanged(int value)
|
|
{
|
|
ui->maskLeaks4Label->setText(QString("%1 %2").arg(value/10.0f, 5,'f',1).arg(STR_UNIT_LPM));
|
|
}
|
|
|
|
void PreferencesDialog::on_maskLeaks20Slider_valueChanged(int value)
|
|
{
|
|
ui->maskLeaks20Label->setText(QString("%1 %2").arg(value/10.0f, 5,'f',1).arg(STR_UNIT_LPM));
|
|
}
|
|
|
|
void PreferencesDialog::on_calculateUnintentionalLeaks_toggled(bool)
|
|
{
|
|
|
|
}
|