From 555639e78b7ac03da8e95a6c8be86c73071761a8 Mon Sep 17 00:00:00 2001
From: Guy Scharf
Date: Tue, 8 Sep 2020 19:33:09 -0700
Subject: [PATCH 1/8] Language improvements for password prompt tooltip and
debugging channels
---
oscar/SleepLib/schema.cpp | 4 ++--
oscar/newprofile.ui | 4 ++--
2 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/oscar/SleepLib/schema.cpp b/oscar/SleepLib/schema.cpp
index fda3bb1e..2c78a7bf 100644
--- a/oscar/SleepLib/schema.cpp
+++ b/oscar/SleepLib/schema.cpp
@@ -340,8 +340,8 @@ void init()
//
//
- schema::channel.add(GRP_CPAP, ch=new Channel(CPAP_Test1 = 0x111e, DATA, MT_CPAP, SESSION, STR_GRAPH_TestChan1, QObject::tr("Debugging channel #1"), QObject::tr("Top secret internal stuff you're not supposed to see ;)"), QObject::tr("Test #1"), QString(), INTEGER, QColor("pink")));
- schema::channel.add(GRP_CPAP, ch=new Channel(CPAP_Test2 = 0x111f, DATA, MT_CPAP, SESSION, STR_GRAPH_TestChan2, QObject::tr("Debugging channel #2"), QObject::tr("Top secret internal stuff you're not supposed to see ;)"), QObject::tr("Test #2"), QString(), INTEGER, Qt::blue));
+ schema::channel.add(GRP_CPAP, ch=new Channel(CPAP_Test1 = 0x111e, DATA, MT_CPAP, SESSION, STR_GRAPH_TestChan1, QObject::tr("Debugging channel #1"), QObject::tr("For internal use only"), QObject::tr("Test #1"), QString(), INTEGER, QColor("pink")));
+ schema::channel.add(GRP_CPAP, ch=new Channel(CPAP_Test2 = 0x111f, DATA, MT_CPAP, SESSION, STR_GRAPH_TestChan2, QObject::tr("Debugging channel #2"), QObject::tr("For internal use only"), QObject::tr("Test #2"), QString(), INTEGER, Qt::blue));
RMS9_E01 = schema::channel["RMS9_E01"].id();
RMS9_E02 = schema::channel["RMS9_E02"].id();
diff --git a/oscar/newprofile.ui b/oscar/newprofile.ui
index 75e76bea..91320c3a 100644
--- a/oscar/newprofile.ui
+++ b/oscar/newprofile.ui
@@ -40,7 +40,7 @@
-
- 2
+ 1
@@ -149,7 +149,7 @@
-
- Keep the kids out.. Nothing more.. This isn't meant to be uber security.
+ Very weak password protection and not recommended if security is required.
Password Protect Profile
From 6406545c33c8708364bec22581eaa4f24f970687 Mon Sep 17 00:00:00 2001
From: Guy Scharf
Date: Tue, 8 Sep 2020 19:54:56 -0700
Subject: [PATCH 2/8] Correct percentile calculation for multiple days -
Previously, would stop and return 0 if any day in range was suumary only -
Now, just ignores summary-only days if there are days with data -
Re-indentation makes change look bigger than it is
---
oscar/SleepLib/profiles.cpp | 79 +++++++++++++++++++------------------
1 file changed, 40 insertions(+), 39 deletions(-)
diff --git a/oscar/SleepLib/profiles.cpp b/oscar/SleepLib/profiles.cpp
index f1004985..e25927b5 100644
--- a/oscar/SleepLib/profiles.cpp
+++ b/oscar/SleepLib/profiles.cpp
@@ -1724,69 +1724,70 @@ EventDataType Profile::calcPercentile(ChannelID code, EventDataType percent, Mac
qint64 SN = 0;
bool timeweight;
- bool summaryOnly = false;
-
+ bool summaryOnly = true;
do {
Day *day = GetGoodDay(date, mt);
if (day) {
if (day->summaryOnly()) {
- summaryOnly = true;
- break;
+ date = date.addDays(1);
+ continue;
}
+ summaryOnly = false;
+
// why was this nested like this???
//for (int i = 0; i < day->size(); i++) {
- for (auto & sess : day->sessions) {
- if (!sess->enabled()) {
- continue;
- }
+ for (auto & sess : day->sessions) {
+ if (!sess->enabled()) {
+ continue;
+ }
- gain = sess->m_gain[code];
+ gain = sess->m_gain[code];
- if (!gain) { gain = 1; }
+ if (!gain) { gain = 1; }
- vsi = sess->m_valuesummary.find(code);
+ vsi = sess->m_valuesummary.find(code);
- if (vsi == sess->m_valuesummary.end()) { continue; }
+ if (vsi == sess->m_valuesummary.end()) { continue; }
- tsi = sess->m_timesummary.find(code);
- timeweight = (tsi != sess->m_timesummary.end());
+ tsi = sess->m_timesummary.find(code);
+ timeweight = (tsi != sess->m_timesummary.end());
- QHash &vsum = vsi.value();
- QHash &tsum = tsi.value();
+ QHash &vsum = vsi.value();
+ QHash &tsum = tsi.value();
- if (timeweight) {
- for (auto k=tsum.begin(), tsumend=tsum.end(); k != tsumend; k++) {
- weight = k.value();
- value = EventDataType(k.key()) * gain;
+ if (timeweight) {
+ for (auto k=tsum.begin(), tsumend=tsum.end(); k != tsumend; k++) {
+ weight = k.value();
+ value = EventDataType(k.key()) * gain;
- SN += weight;
- wmi = wmap.find(value);
+ SN += weight;
+ wmi = wmap.find(value);
- if (wmi == wmap.end()) {
- wmap[value] = weight;
- } else {
- wmi.value() += weight;
- }
+ if (wmi == wmap.end()) {
+ wmap[value] = weight;
+ } else {
+ wmi.value() += weight;
}
- } else {
- for (auto k=vsum.begin(), vsumend=vsum.end(); k!=vsumend; k++) {
- weight = k.value();
- value = EventDataType(k.key()) * gain;
+ }
+ } else {
+ for (auto k=vsum.begin(), vsumend=vsum.end(); k!=vsumend; k++) {
+ weight = k.value();
+ value = EventDataType(k.key()) * gain;
- SN += weight;
- wmi = wmap.find(value);
+ SN += weight;
+ wmi = wmap.find(value);
- if (wmi == wmap.end()) {
- wmap[value] = weight;
- } else {
- wmi.value() += weight;
- }
+ if (wmi == wmap.end()) {
+ wmap[value] = weight;
+ } else {
+ wmi.value() += weight;
}
}
}
- // }
+ }
+ // }
}
date = date.addDays(1);
From fea497aca6fd4035724b3e359bfde55de90d7e52 Mon Sep 17 00:00:00 2001
From: Phil Olynyk
Date: Wed, 9 Sep 2020 20:55:42 -0400
Subject: [PATCH 3/8] Test for Crostini 9p filesystem before looking for
/mnt/chromeos/removable
---
oscar/mainwindow.cpp | 32 +++++++++++++++++++++++---------
1 file changed, 23 insertions(+), 9 deletions(-)
diff --git a/oscar/mainwindow.cpp b/oscar/mainwindow.cpp
index ad0d271f..794df17c 100644
--- a/oscar/mainwindow.cpp
+++ b/oscar/mainwindow.cpp
@@ -811,6 +811,7 @@ void MainWindow::importCPAPBackups()
QStringList getDriveList()
{
QStringList drivelist;
+ bool crostini_detected = false;
#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
#if defined(Q_OS_LINUX)
@@ -833,20 +834,31 @@ QStringList getDriveList()
if (storage.fileSystemType() == VFAT) {
qDebug() << "Adding" << storage.name() << "on" << storage.rootPath() << "to drivelist";
drivelist.append(storage.rootPath());
+ } else if (storage.fileSystemType() == "9p") {
+ qDebug() << "Crostini filesystem 9p found";
+ crostini_detected = true;
}
}
}
#endif
#if defined(Q_OS_LINUX)
- QString mntName("/mnt/chromeos/removable");
- QDir mnt(mntName);
- qDebug() << "Checking for" << mntName;
- if ( mnt.exists() ) {
- qDebug() << "Checking Crostini removable folders";
- QFileInfoList mntPts = mnt.entryInfoList();
- foreach ( const auto dir, mntPts ) {
- qDebug() << "Adding" << dir.filePath() << "to drivelist";
- drivelist.append(dir.filePath() );
+ if (crostini_detected) {
+ QString mntName("/mnt/chromeos/removable");
+ QDir mnt(mntName);
+ qDebug() << "Checking for" << mntName;
+ if ( mnt.exists() ) {
+ qDebug() << "Checking Crostini removable folders";
+ QFileInfoList mntPts = mnt.entryInfoList();
+ foreach ( const auto dir, mntPts ) {
+ qDebug() << "Adding" << dir.filePath() << "to drivelist";
+ drivelist.append(dir.filePath() );
+ }
+ } else {
+ QMessageBox::warning(nullptr, STR_MessageBox_Warning,
+ QObject::tr("Chromebook file system detected, but no removable device found\n") +
+ QObject::tr("You must share your SD card with Linux using the ChromeOS Files program"));
+ drivelist.clear();
+ drivelist.append("CROSTINI");
}
}
#endif
@@ -903,6 +915,8 @@ QList MainWindow::detectCPAPCards()
do {
// Rescan in case card inserted
QStringList AutoScannerPaths = getDriveList();
+ if (AutoScannerPaths.contains("CROSTINI")) // no Crostini removable drives found!
+ break; // break out of the 20 second wait loop
// AutoScannerPaths.push_back(lastpath);
qDebug() << "Drive list size:" << AutoScannerPaths.size();
From de0069171a2ba4544c517314abe6656aaa651da0 Mon Sep 17 00:00:00 2001
From: Guy Scharf
Date: Wed, 9 Sep 2020 18:10:36 -0700
Subject: [PATCH 4/8] Support migration of OSCAR as well as SleepyHead data.
Update Release Notes.
---
Htmldocs/release_notes.html | 11 ++++++++++-
oscar/main.cpp | 18 +++++++++++-------
2 files changed, 21 insertions(+), 8 deletions(-)
diff --git a/Htmldocs/release_notes.html b/Htmldocs/release_notes.html
index ab38af77..c8dab34e 100644
--- a/Htmldocs/release_notes.html
+++ b/Htmldocs/release_notes.html
@@ -8,7 +8,16 @@
- Changes and fixes in OSCAR v1.2.0
+ Changes and fixes in OSCAR after v1.2.0-beta-1
+
Portions of OSCAR are © 2019-2020 by
+ The OSCAR Team
+
+ - [fix] Support migration from both SleepyHead and OSCAR.
+ - [fix] Correct 95th percentile computations on Statistics page when some days were summary-only.
+ - [fix] Improve some prompts and tooltip messages.
+ - [fix] Improve support of rare Philips Respironics 950P events and update warnings.
+
+ Changes and fixes in OSCAR v1.2.0-beta-1
Portions of OSCAR are © 2019-2020 by
The OSCAR Team
diff --git a/oscar/main.cpp b/oscar/main.cpp
index 5c0941fd..8419a68d 100644
--- a/oscar/main.cpp
+++ b/oscar/main.cpp
@@ -120,9 +120,12 @@ bool processPreferenceFile( QString path ) {
tmp.open(QIODevice::WriteOnly);
QTextStream instr(&fl);
QTextStream outstr(&tmp);
+ bool isSleepyHead = false;
while (instr.readLineInto(&line)) {
+ if (line.contains("")) // Is this SleepyHead or OSCAR preferences file?
+ isSleepyHead = true;
line.replace("SleepyHead","OSCAR");
- if (line.contains("VersionString")) {
+ if (isSleepyHead && line.contains("VersionString")) {
int rtAngle = line.indexOf(">", 0);
int lfAngle = line.indexOf("<", rtAngle);
line.replace(rtAngle+1, lfAngle-rtAngle-1, "1.0.0-beta");
@@ -185,7 +188,7 @@ bool migrateFromSH(QString destDir) {
while (selectingFolder) {
datadir = QFileDialog::getExistingDirectory(nullptr,
- QObject::tr("Choose the SleepyHead data folder to migrate")+" "+
+ QObject::tr("Choose the SleepyHead or OSCAR data folder to migrate")+" "+
QObject::tr("or CANCEL to skip migration."),
homeDocs, QFileDialog::ShowDirsOnly);
qDebug() << "Migration folder selected: " + datadir;
@@ -200,7 +203,7 @@ bool migrateFromSH(QString destDir) {
if (!file.exists() || !dirP.exists()) { // It doesn't have a Preferences.xml file or a Profiles directory in it
// Not a new directory.. nag the user.
QMessageBox::warning(nullptr, STR_MessageBox_Error,
- QObject::tr("The folder you chose does not contain valid SleepyHead data.") +
+ QObject::tr("The folder you chose does not contain valid SleepyHead or OSCAR data.") +
"\n\n"+QObject::tr("You cannot use this folder:")+" " + datadir,
QMessageBox::Ok);
continue; // Nope, don't use it, go around the loop again
@@ -486,7 +489,8 @@ int main(int argc, char *argv[]) {
if ( ! force_data_dir ) { // unless they explicitly selected it by --datadir param
if (QMessageBox::question(nullptr, STR_MessageBox_Question,
QObject::tr("OSCAR will set up a folder for your data.")+"\n"+
- QObject::tr("If you have been using SleepyHead, OSCAR can copy your old data to this folder later.")+"\n"+
+ QObject::tr("If you have been using SleepyHead or an older version of OSCAR,") + "\n" +
+ QObject::tr("OSCAR can copy your old data to this folder later.")+"\n"+
QObject::tr("We suggest you use this folder: ")+QDir::toNativeSeparators(GetAppData())+"\n"+
QObject::tr("Click Ok to accept this, or No if you want to use a different folder."),
QMessageBox::Ok | QMessageBox::No, QMessageBox::Ok) == QMessageBox::No) {
@@ -539,9 +543,9 @@ int main(int argc, char *argv[]) {
#else
if ( ! newDir.exists() || newDir.isEmpty() ) { // directory doesn't exist yet or is empty, try to migrate old data
#endif
- if (QMessageBox::question(nullptr, QObject::tr("Migrate SleepyHead Data?"),
- QObject::tr("On the next screen OSCAR will ask you to select a folder with SleepyHead data") +"\n" +
- QObject::tr("Click [OK] to go to the next screen or [No] if you do not wish to use any SleepyHead data."),
+ if (QMessageBox::question(nullptr, QObject::tr("Migrate SleepyHead or OSCAR Data?"),
+ QObject::tr("On the next screen OSCAR will ask you to select a folder with SleepyHead or OSCAR data") +"\n" +
+ QObject::tr("Click [OK] to go to the next screen or [No] if you do not wish to use any SleepyHead or OSCAR data."),
QMessageBox::Ok|QMessageBox::No, QMessageBox::Ok) == QMessageBox::Ok) {
migrateFromSH( GetAppData() ); // doesn't matter if no migration
}
From f423ef496714423a8b87b69f33aa7f6b81217e27 Mon Sep 17 00:00:00 2001
From: Guy Scharf
Date: Wed, 9 Sep 2020 18:27:58 -0700
Subject: [PATCH 5/8] Fix reversion in fea497ac causing fatal 'variable not
used' message
---
oscar/mainwindow.cpp | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/oscar/mainwindow.cpp b/oscar/mainwindow.cpp
index 794df17c..d8fba199 100644
--- a/oscar/mainwindow.cpp
+++ b/oscar/mainwindow.cpp
@@ -818,9 +818,11 @@ QStringList getDriveList()
#define VFAT "vfat"
#elif defined(Q_OS_WIN)
#define VFAT "FAT32"
+ Q_UNUSED(crostini_detected)
#elif defined(Q_OS_MAC)
#define VFAT "msdos"
-#endif
+ Q_UNUSED(crostini_detected)
+#endif
foreach (const QStorageInfo &storage, QStorageInfo::mountedVolumes()) {
if (storage.isValid() && storage.isReady()) {
#ifdef DEBUG_SDCARD
From c475f89d613882c0fe6e278184dc4af07f2e32b7 Mon Sep 17 00:00:00 2001
From: Phil Olynyk
Date: Wed, 9 Sep 2020 21:31:49 -0400
Subject: [PATCH 6/8] Update Linux packaging files to fix lintian errors
---
Building/Linux/copyright | 28 ++++++++++++++++++++++++++++
Building/Linux/mkDistDeb.sh | 26 +++++++++++++++++---------
Building/Linux/rm_usrbin-test.sh | 2 --
Building/Linux/rm_usrbin.sh | 1 -
Building/Linux/tst_user.sh | 10 +++++-----
5 files changed, 50 insertions(+), 17 deletions(-)
create mode 100644 Building/Linux/copyright
diff --git a/Building/Linux/copyright b/Building/Linux/copyright
new file mode 100644
index 00000000..e9356d11
--- /dev/null
+++ b/Building/Linux/copyright
@@ -0,0 +1,28 @@
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Upstream-Name: OSCAR
+
+Files: *
+Copyright: 2011-2018 Mark Watkins
+ 2019-2020 The OSCAR Team
+License: GPL-3+
+ This program is free software; you can redistribute it
+ and/or modify it under the terms of the GNU General Public
+ License as published by the Free Software Foundation; either
+ version 3 of the License, or (at your option) any later
+ version.
+ .
+ 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.
+ .
+ You should have received a copy of the GNU General Public
+ License along with this package; if not, write to the Free
+ Software Foundation, Inc., 51 Franklin St, Fifth Floor,
+ Boston, MA 02110-1301 USA
+ .
+ On Debian systems, the full text of the GNU General Public
+ License version 3 can be found in the file
+ `/usr/share/common-licenses/GPL-3'.
+
diff --git a/Building/Linux/mkDistDeb.sh b/Building/Linux/mkDistDeb.sh
index c0417773..c5efed59 100755
--- a/Building/Linux/mkDistDeb.sh
+++ b/Building/Linux/mkDistDeb.sh
@@ -36,11 +36,13 @@ echo Version: ${VERSION}
# application name
appli_name="OSCAR"
+package_name="oscar"
pre_inst="tst_user.sh"
# build folder (absolute path is better)
build_folder="/home/$USER/OSCAR/build"
if [[ -n ${PRERELEASE} && -z ${RC} ]] ; then
appli_name=${appli_name}-test
+ package_name=${package_name}-test
post_inst="ln_usrbin-test.sh"
pre_rem="rm_usrbin-test.sh"
post_rem="clean_rm-test.sh"
@@ -98,7 +100,7 @@ mkdir ${temp_folder}/bin
mkdir ${temp_folder}/share
mkdir ${temp_folder}/share/${appli_name}
mkdir ${temp_folder}/share/doc
-share_doc_folder="${temp_folder}/share/doc/${appli_name}"
+share_doc_folder="${temp_folder}/share/doc/${package_name}"
mkdir ${share_doc_folder}
mkdir ${temp_folder}/share/icons
mkdir ${temp_folder}/share/icons/hicolor
@@ -121,22 +123,29 @@ cp ./${appli_name}.png ${temp_folder}/share/icons/hicolor/48x48/apps/${appli_nam
cp ./${appli_name}.svg ${temp_folder}/share/icons/hicolor/scalable/apps/${appli_name}.svg
cp ./${appli_name}.desktop ${temp_folder}/share/applications/${appli_name}.desktop
-echo "Copyright 2019-2020 oscar-team.org " > $share_doc_folder/copyright
+#echo "Copyright 2019-2020 oscar-team.org " > $share_doc_folder/copyright
+#echo "Licensed under /usr/share/common-licenses/GPL-3" >> $share_doc_folder/copyright
+cp ./copyright $share_doc_folder/copyright
-changelog_file="$share_doc_folder/changelog"
+changelog_file="./changelog"
#automatic changelog as a bad name
# need to generate one and say fpm to use it instead of create one
# it seems that it needs both of them...
-# creation of the changelog.Debian.gz
+# creation of the Debian changelog
echo "$appli_name (${VERSION}-${ITERATION}) whatever; urgency=medium" > $changelog_file
echo "" >> $changelog_file
echo " * Package created with FPM." >> $changelog_file
echo "" >> $changelog_file
-echo " -- oscar-team.org " >> $changelog_file
-gzip --best $changelog_file
-description='Open Source CPAP Analysis Reporter\n'
+echo " * See the Release Notes under Help/About menu" >> $changelog_file
+echo "" >> $changelog_file
+echo -n " -- oscar-team.org " >> $changelog_file
+date -Iminutes >> $changelog_file
+cp $changelog_file $share_doc_folder/changelog
+gzip --best $share_doc_folder/changelog
+
+description='Open Source CPAP Analysis Reporter\nProvides graphical and statistical display of the CPAP stored data'
# trick for dummies : need to use echo -e to take care of \n (cariage return to slip description and extra one
description=$(echo -e $description)
@@ -154,7 +163,6 @@ fpm --input-type dir --output-type deb \
--category misc \
--deb-priority optional \
--maintainer " -- oscar-team.org " \
- --license GPL-v3 \
--vendor oscar-team.org \
--description "$description" \
--url https://sleepfiles.com/OSCAR \
@@ -162,7 +170,7 @@ fpm --input-type dir --output-type deb \
--depends $dblPkg \
--depends libpcre16-3 \
--depends qttranslations5-l10n \
- --depends "libqt5core5a > $qtver" \
+ --depends "libqt5core5a > 5.9" \
--depends libqt5serialport5 \
--depends libqt5xml5 \
--depends libqt5network5 \
diff --git a/Building/Linux/rm_usrbin-test.sh b/Building/Linux/rm_usrbin-test.sh
index 4bc6f24a..dd1b3549 100755
--- a/Building/Linux/rm_usrbin-test.sh
+++ b/Building/Linux/rm_usrbin-test.sh
@@ -37,6 +37,4 @@ if [ -f "$file" ]; then
rm $file
fi
-if [ -x /usr/bin/update-menus ]; then update-menus; fi
-
diff --git a/Building/Linux/rm_usrbin.sh b/Building/Linux/rm_usrbin.sh
index cbaaae73..2f84eb87 100755
--- a/Building/Linux/rm_usrbin.sh
+++ b/Building/Linux/rm_usrbin.sh
@@ -90,6 +90,5 @@ if [ -f "$file" ]; then
rm $file
fi
-if [ -x /usr/bin/update-menus ]; then update-menus; fi
diff --git a/Building/Linux/tst_user.sh b/Building/Linux/tst_user.sh
index 85a47110..0f32210b 100755
--- a/Building/Linux/tst_user.sh
+++ b/Building/Linux/tst_user.sh
@@ -1,10 +1,10 @@
#! /bin/bash
-#set -e
+set -e
#
#test USER (must be root) vs SUDO_USER (must be other than root but not empty)
-#if [ "$USER" != "root" ] || [ "$SUDO_USER" = "root" ] || [ -z "$SUDO_USER" ]; then
-# echo "dpkg -i must be launched as normal user with sudo command. fatal error"
-# exit
-#fi
+if [ "$USER" != "root" ] || [ "$SUDO_USER" = "root" ] || [ -z "$SUDO_USER" ]; then
+ echo "Installation must be done as normal user with sudo command. fatal error"
+ exit
+fi
# do nothing
From b49e483fa4411249b0866ca5300c1887da129af9 Mon Sep 17 00:00:00 2001
From: Guy Scharf
Date: Wed, 9 Sep 2020 20:55:23 -0700
Subject: [PATCH 7/8] Remove SleepyHead modules ProfileSelect.h, .cpp, .ui.
OSCAR uses ProfileSelector.* modules
---
oscar/oscar.pro | 3 -
oscar/profileselect.cpp | 368 -----------------------------------
oscar/profileselect.h | 62 ------
oscar/profileselect.ui | 422 ----------------------------------------
4 files changed, 855 deletions(-)
delete mode 100644 oscar/profileselect.cpp
delete mode 100644 oscar/profileselect.h
delete mode 100644 oscar/profileselect.ui
diff --git a/oscar/oscar.pro b/oscar/oscar.pro
index 71c5d30b..eb704b56 100644
--- a/oscar/oscar.pro
+++ b/oscar/oscar.pro
@@ -258,7 +258,6 @@ SOURCES += \
newprofile.cpp \
overview.cpp \
preferencesdialog.cpp \
- profileselect.cpp \
reports.cpp \
sessionbar.cpp \
# updateparser.cpp \
@@ -337,7 +336,6 @@ HEADERS += \
newprofile.h \
overview.h \
preferencesdialog.h \
- profileselect.h \
reports.h \
sessionbar.h \
# updateparser.h \
@@ -415,7 +413,6 @@ FORMS += \
mainwindow.ui \
oximetry.ui \
preferencesdialog.ui \
- profileselect.ui \
newprofile.ui \
exportcsv.ui \
# UpdaterWindow.ui \
diff --git a/oscar/profileselect.cpp b/oscar/profileselect.cpp
deleted file mode 100644
index fa62f99c..00000000
--- a/oscar/profileselect.cpp
+++ /dev/null
@@ -1,368 +0,0 @@
-/* Profile Select Implementation (Login Screen)
- *
- * 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. */
-
-#include "profileselect.h"
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include
-#include "ui_profileselect.h"
-#include "SleepLib/profiles.h"
-#include "newprofile.h"
-#include "mainwindow.h"
-
-extern MainWindow * mainwin;
-
-ProfileSelect::ProfileSelect(QWidget *parent) :
- QDialog(parent),
- ui(new Ui::ProfileSelect)
-{
- ui->setupUi(this);
- QStringList str;
- model = new QStandardItemModel(0, 0);
-
- int i = 0;
- int sel = -1;
- QString name;
-
- QIcon icon(":/icons/moon.png");
-
- int w=0;
- QFont font("Sans Serif", 18, QFont::Bold, false);
- ui->listView->setFont(font);
-
- QFontMetrics fm(font);
- QMap::iterator p;
- for (p = Profiles::profiles.begin(); p != Profiles::profiles.end(); p++) {
- name = p.key();
-
- if (AppSetting->profileName() == name) {
- sel = i;
- }
-
- QStandardItem *item = new QStandardItem(name);
-
- item->setData(p.key(), Qt::UserRole+2);
- item->setEditable(false);
-
- if (!(*p)->checkLock().isEmpty()) {
- item->setForeground(QBrush(Qt::red));
- item->setText(name+" (open)");
- }
-
- QRect rect = fm.boundingRect(name);
- if (rect.width() > w) w = rect.width();
-
- // Profile fonts arern't loaded yet.. Using generic font.
- item->setFont(font);
- model->appendRow(item);
- i++;
- }
- w+=20;
- ui->listView->setMinimumWidth(w);
-
- proxy = new QSortFilterProxyModel(this);
- proxy->setSourceModel(model);
- proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
-
- ui->listView->setModel(proxy);
- ui->listView->setSelectionBehavior(QAbstractItemView::SelectRows);
- ui->listView->setSelectionMode(QAbstractItemView::SingleSelection);
-
- if (sel >= 0) { ui->listView->setCurrentIndex(proxy->index(sel,0)); } //model->item(sel)->index()); }
-
- proxy->sort(0, Qt::AscendingOrder);
-
- m_tries = 0;
-
- popupMenu = new QMenu(this);
- popupMenu->addAction(tr("Open Profile"), this, SLOT(openProfile()));
- popupMenu->addAction(tr("Edit Profile"), this, SLOT(editProfile()));
- popupMenu->addSeparator();
- popupMenu->addAction(tr("Delete Profile"), this, SLOT(deleteProfile()));
-
- ui->labelAppName->setText(STR_TR_OSCAR);
- ui->labelVersion->setText("");
- // if (GIT_BRANCH!="master")
- // ui->labelBuild->setText(GIT_BRANCH);
- // else ui->labelBuild->setText(QString());
- ui->labelFolder->setText(GetAppData());
- ui->labelFolder->setToolTip("Current OSCAR data folder\n" + GetAppData());
-
- ui->listView->verticalScrollBar()->setStyleSheet("QScrollBar:vertical {border: 0px solid grey; background: transparent; }"
- "QScrollBar::handle:vertical {"
- " background: qlineargradient(x1:0, y1:0, x2:1, y2:0,"
- " stop: 0 rgb(230, 230, 230), stop: 0.5 rgb(255, 255, 255), stop:1 rgb(230, 230, 230));"
- " min-height: 0px;"
- " border: 1px solid gray;"
- " border-radius: 5px;"
- "}");
-}
-
-ProfileSelect::~ProfileSelect()
-{
- delete model; // why is this not being cleaned up by Qt?
- delete popupMenu;
- delete ui;
-}
-void ProfileSelect::earlyExit()
-{
- accept();
-}
-void ProfileSelect::editProfile()
-{
- QString name = ui->listView->currentIndex().data(Qt::UserRole+2).toString();
- Profile *profile = Profiles::Get(name);
-
- if (!profile) { return; }
-
- bool reallyEdit = false;
-
- if (profile->user->hasPassword()) {
- QDialog dialog(this, Qt::Dialog);
- QLineEdit *e = new QLineEdit(&dialog);
- e->setEchoMode(QLineEdit::Password);
- dialog.connect(e, SIGNAL(returnPressed()), &dialog, SLOT(accept()));
- dialog.setWindowTitle(tr("Enter Password for %1").arg(name));
- dialog.setMinimumWidth(300);
- QVBoxLayout *lay = new QVBoxLayout();
- dialog.setLayout(lay);
- lay->addWidget(e);
- int tries = 0;
-
- do {
- e->setText("");
-
- if (dialog.exec() != QDialog::Accepted) { break; }
-
- tries++;
-
- if (profile->user->checkPassword(e->text())) {
- reallyEdit = true;
- break;
- } else {
- if (tries < 3) {
- QMessageBox::warning(this, STR_MessageBox_Error, tr("Incorrect Password"), QMessageBox::Ok);
- } else {
- QMessageBox::warning(this, STR_MessageBox_Error, tr("You entered the password wrong too many times."),
- QMessageBox::Ok);
- reject();
- }
- }
- } while (tries < 3);
- } else { reallyEdit = true; }
-
- if (reallyEdit) {
- NewProfile newprof(this);
- newprof.edit(name);
- newprof.exec();
- }
-
- //qDebug() << "edit" << name;
-}
-void ProfileSelect::deleteProfile()
-{
- QString name = ui->listView->currentIndex().data(Qt::UserRole+2).toString();
-
- QDialog confirmdlg;
- QVBoxLayout layout(&confirmdlg);
- QLabel message(QString(""+STR_MessageBox_Warning+": "+tr("You are about to destroy profile '%1'.")+"
"+tr("Enter the word DELETE below to confirm.")).arg(name), &confirmdlg);
- layout.insertWidget(0,&message,1);
- QLineEdit lineedit(&confirmdlg);
- layout.insertWidget(1, &lineedit, 1);
- QHBoxLayout layout2;
- layout.insertLayout(2,&layout2,1);
- QPushButton cancel(QString("&Cancel"), &confirmdlg);
- QPushButton accept(QString("&Delete Profile"), &confirmdlg);
- layout2.addWidget(&cancel);
- layout2.addStretch(1);
- layout2.addWidget(&accept);
- confirmdlg.connect(&cancel, SIGNAL(clicked()), &confirmdlg, SLOT(reject()));
- confirmdlg.connect(&accept, SIGNAL(clicked()), &confirmdlg, SLOT(accept()));
- confirmdlg.connect(&lineedit, SIGNAL(returnPressed()), &confirmdlg, SLOT(accept()));
-
- if (confirmdlg.exec() != QDialog::Accepted)
- return;
-
- if (lineedit.text().compare("DELETE")!=0) {
- QMessageBox::information(NULL, tr("Sorry"), tr("You need to enter DELETE in capital letters."), QMessageBox::Ok);
- return;
- }
-
- Profile * profile = Profiles::profiles[name];
- p_profile = profile;
- // Hmmmmm.....
-// if (!profile->Load()) {
-// QMessageBox::warning(this, STR_MessageBox_Error,
-// QString(tr("Could not open profile.. You will need to delete this profile directory manually")+
-// "\n\n"+tr("You will find it under the following location:")+"\n\n%1").arg(QDir::toNativeSeparators(GetAppData() + "/Profiles/" + profile->user->userName())), QMessageBox::Ok);
-// return;
-// }
- bool reallydelete = false;
- if (profile->user->hasPassword()) {
- QDialog dialog(this, Qt::Dialog);
- QLineEdit *e = new QLineEdit(&dialog);
- e->setEchoMode(QLineEdit::Password);
- dialog.connect(e, SIGNAL(returnPressed()), &dialog, SLOT(accept()));
- dialog.setWindowTitle(tr("Enter Password for %1").arg(name));
- dialog.setMinimumWidth(300);
- QVBoxLayout *lay = new QVBoxLayout();
- dialog.setLayout(lay);
- lay->addWidget(e);
- int tries = 0;
-
- do {
- e->setText("");
-
- if (dialog.exec() != QDialog::Accepted) { break; }
-
- tries++;
-
- if (profile->user->checkPassword(e->text())) {
- reallydelete = true;
- break;
- } else {
- if (tries < 3) {
- QMessageBox::warning(this, STR_MessageBox_Error, tr("You entered an incorrect password"), QMessageBox::Ok);
- } else {
- QMessageBox::warning(this, STR_MessageBox_Error,
- tr("If you're trying to delete because you forgot the password, you need to delete it manually."),
- QMessageBox::Ok);
- }
- }
- } while (tries < 3);
- } else { reallydelete = true; }
-
- if (reallydelete) {
- QString path = profile->Get(PrefMacro(STR_GEN_DataFolder));
-
- if (!path.isEmpty()) {
- if (!removeDir(path)) {
- QMessageBox::information(this, STR_MessageBox_Error,
- tr("There was an error deleting the profile directory, you need to manually remove it.")+QString("\n\n%1").arg(path),
- QMessageBox::Ok);
- }
- qDebug() << "Delete" << path;
- QMessageBox::information(this, STR_MessageBox_Information, tr("Profile '%1' was succesfully deleted").arg(name),QMessageBox::Ok);
- }
-
- int row = ui->listView->currentIndex().row();
- proxy->removeRow(row);
- delete p_profile;
- p_profile = nullptr;
- }
-}
-
-//! \fn ProfileSelect::QuickLogin()
-//! \brief For programmatically bypassing the login window
-void ProfileSelect::QuickLogin()
-{
- on_listView_activated(ui->listView->currentIndex());
-}
-
-void ProfileSelect::on_selectButton_clicked()
-{
- on_listView_activated(ui->listView->currentIndex());
-}
-void ProfileSelect::openProfile()
-{
- on_listView_activated(ui->listView->currentIndex());
-}
-
-void ProfileSelect::on_newProfileButton_clicked()
-{
- NewProfile newprof(this);
- newprof.skipWelcomeScreen();
- newprof.setWindowTitle(tr("Create new profile"));
-
- if (newprof.exec() == NewProfile::Rejected) {
- // reject();
- } else { accept(); }
-}
-
-
-//! \fn ProfileSelect::on_listView_activated(const QModelIndex &index)
-//! \brief Process the selected login, requesting passwords if required.
-void ProfileSelect::on_listView_activated(const QModelIndex &index)
-{
- QString name = index.data(Qt::UserRole+2).toString();
- Profile *profile = Profiles::profiles[name];
-
- if (!profile) { return; }
- if (!profile->isOpen()) {
- p_profile = profile;
- // Do this in case user renames the directory (otherwise it won't load)
- // Essentially makes the folder name the user name, but whatever..
- // TODO: Change the profile editor one day to make it rename the actual folder
- profile->p_preferences[STR_UI_UserName] = name;
- }
-
- if (!profile->user->hasPassword()) {
- m_selectedProfile = name;
- AppSetting->setProfileName(name);
- accept();
- return;
- } else {
- int tries = 0;
-
- do {
- QDialog dialog(this, Qt::Dialog);
- QLineEdit *e = new QLineEdit(&dialog);
- e->setEchoMode(QLineEdit::Password);
- dialog.connect(e, SIGNAL(returnPressed()), &dialog, SLOT(accept()));
- dialog.setWindowTitle(tr("Enter Password"));
- QVBoxLayout *lay = new QVBoxLayout();
- dialog.setLayout(lay);
- lay->addWidget(e);
- dialog.exec();
-
- if (profile->user->checkPassword(e->text())) {
- m_selectedProfile = name;
- AppSetting->setProfileName(name);
- accept();
- return;
- }
-
- tries++;
-
- if (tries < 3) {
- QMessageBox::warning(this, STR_MessageBox_Error, tr("Incorrect Password"), QMessageBox::Ok);
- } else {
- QMessageBox::warning(this, STR_MessageBox_Error,
- tr("You entered an Incorrect Password too many times. Exiting!"), QMessageBox::Ok);
- }
- } while (tries < 3);
- }
-
- reject();
- return;
-}
-
-void ProfileSelect::on_listView_customContextMenuRequested(const QPoint &pos)
-{
- popupMenu->popup(QWidget::mapToGlobal(pos));
-}
-
-void ProfileSelect::on_pushButton_clicked()
-{
- mainwin->RestartApplication(false, "-d");
-}
-
-void ProfileSelect::on_filter_textChanged(const QString &arg1)
-{
- QRegExp regExp("*"+arg1+"*", Qt::CaseInsensitive, QRegExp::Wildcard);
- proxy->setFilterRegExp(regExp);
-}
diff --git a/oscar/profileselect.h b/oscar/profileselect.h
deleted file mode 100644
index ce37fd0b..00000000
--- a/oscar/profileselect.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/* Profile Select Header (Login Screen)
- *
- * 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. */
-
-#ifndef PROFILESELECT_H
-#define PROFILESELECT_H
-
-#include
-#include
-#include
-#include
-#include
-
-namespace Ui {
-class ProfileSelect;
-}
-
-/*! \class ProfileSelect
- \brief Simple Login Window providing a list of all profiles to select from
- */
-class ProfileSelect : public QDialog
-{
- Q_OBJECT
-
- public:
- explicit ProfileSelect(QWidget *parent = 0);
- ~ProfileSelect();
-
- QString selectedProfile();
- void QuickLogin();
- private slots:
- void on_selectButton_clicked();
-
- void on_newProfileButton_clicked();
-
- void on_listView_activated(const QModelIndex &index);
- void earlyExit();
-
- void openProfile();
- void editProfile();
- void deleteProfile();
-
- void on_listView_customContextMenuRequested(const QPoint &pos);
-
- void on_pushButton_clicked();
-
- void on_filter_textChanged(const QString &arg1);
-
-private:
- Ui::ProfileSelect *ui;
- QString m_selectedProfile;
- int m_tries;
- QMenu *popupMenu;
- QStandardItemModel *model;
- QSortFilterProxyModel *proxy;
-};
-
-#endif // PROFILESELECT_H
diff --git a/oscar/profileselect.ui b/oscar/profileselect.ui
deleted file mode 100644
index caa66b77..00000000
--- a/oscar/profileselect.ui
+++ /dev/null
@@ -1,422 +0,0 @@
-
-
- ProfileSelect
-
-
-
- 0
- 0
- 418
- 272
-
-
-
- Select Profile
-
-
-
- :/icons/logo-sm.png:/icons/logo-sm.png
-
-
- QDialog {
- background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(240, 240, 240, 255), stop:1 rgba(220, 220, 220, 255));
-}
-
-QGroupBox {
- background-color: white;
- border: 1px solid gray;
- border-radius: 5px;
- margin-top: 3ex; /* leave space at the top for the title */
- }
-
- QGroupBox::title {
- subcontrol-origin: margin;
- subcontrol-position: top center; /* position at the top center */
- padding: 2px;
- background-color: white;
-}
-
-QFrame {
-background: white;
-border: 1px solid gray;
-border-radius: 10px;
-}
-
-QLabel {
-background: transparent;
-border: 0px;
-}
-
-QPushButton {
-background: qlineargradient(spread:pad, x1:0, y1:0, x2:0, y2:1, stop:0 rgba(240, 240, 240, 255), stop:1 rgba(255, 255, 255, 255));
-border: 1px solid gray;
-border-radius: 10px;
-padding: 3px;
-padding-left: 20px;
-padding-right: 20px;
-}
-
-QPushButton:hover {
-background: white;
-border: 2px solid gray;
-}
-
-QPushButton:pressed {
-background: gray;
-}
-
-QLineEdit {
-border-radius: 5px;
-border: 1px solid gray;
-background: white;
-}
-
-
-
- 6
-
-
- 6
-
-
- 6
-
-
- 6
-
-
- 6
-
- -
-
-
- 6
-
-
-
-
-
-
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
-
-
-
-
-
-
- border: 0px;
-
-
- Search:
-
-
-
- -
-
-
-
-
-
-
-
-
- -
-
-
- Qt::CustomContextMenu
-
-
- border: 0px;
-
-
- QFrame::NoFrame
-
-
- QAbstractScrollArea::AdjustToContents
-
-
- true
-
-
- QAbstractItemView::ScrollPerPixel
-
-
-
-
-
-
- -
-
-
- QFrame {
-background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, 255, 255, 255), stop:1 rgba(220, 220, 220, 255));
-};
-
-
- QFrame::StyledPanel
-
-
- QFrame::Raised
-
-
-
- 6
-
-
- 16
-
-
- 16
-
-
- 16
-
-
- 16
-
-
-
-
-
-
- 0
- 0
-
-
-
- Start with the selected user profile.
-
-
- &Select User
-
-
-
- :/icons/forward.png:/icons/forward.png
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Create a new user profile.
-
-
- New Profile
-
-
- false
-
-
-
- -
-
-
- Qt::Horizontal
-
-
-
- -
-
-
- Choose a different OSCAR data folder.
-
-
-
-
-
- &Different Folder
-
-
- false
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
-
- 15
- 75
- true
-
-
-
- border: 0px;
-
-
- OSCAR
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
- border: 0px;
-
-
- [version]
-
-
- Qt::AlignCenter
-
-
-
- -
-
-
- Qt::Vertical
-
-
-
- 20
- 40
-
-
-
-
- -
-
-
- Click here if you didn't want to start OSCAR.
-
-
- &Quit
-
-
- false
-
-
-
-
-
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
-
-
-
- QFrame::StyledPanel
-
-
- QFrame::Plain
-
-
-
- 4
-
-
- 4
-
-
- 4
-
-
- 4
-
-
- 4
-
-
-
-
-
-
- 75
- true
-
-
-
- border: 0px;
-
-
- Folder:
-
-
-
- -
-
-
-
- 0
- 0
-
-
-
- The current location of OSCAR data store.
-
-
- border: 0px;
-
-
- [data directory]
-
-
- true
-
-
-
-
-
-
-
-
-
-
-
-
-
- quitButton
- clicked()
- ProfileSelect
- reject()
-
-
- 52
- 276
-
-
- 199
- 149
-
-
-
-
-
From e39adbd41ec60e2787e34a840afdfa1114baba19 Mon Sep 17 00:00:00 2001
From: Guy Scharf
Date: Wed, 9 Sep 2020 22:03:32 -0700
Subject: [PATCH 8/8] Avoid data loss when destroying a profile that's just an
empty directory; Release notes for v1.2.0-beta-2 - If trying to delete a
profile folder with no contents, now displays an error message and doesn't
delete it. - Folders with no contents are no longer listed on the Profiles
page. - Release Notes updated with the fixes for beta-2.
---
Htmldocs/release_notes.html | 8 ++++++--
oscar/SleepLib/profiles.cpp | 3 +++
oscar/profileselector.cpp | 4 ++++
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/Htmldocs/release_notes.html b/Htmldocs/release_notes.html
index c8dab34e..f3eb620e 100644
--- a/Htmldocs/release_notes.html
+++ b/Htmldocs/release_notes.html
@@ -8,7 +8,7 @@
- Changes and fixes in OSCAR after v1.2.0-beta-1
+ Changes and fixes in OSCAR v1.2.0-beta-2
Portions of OSCAR are © 2019-2020 by
The OSCAR Team
@@ -16,7 +16,11 @@
- [fix] Correct 95th percentile computations on Statistics page when some days were summary-only.
- [fix] Improve some prompts and tooltip messages.
- [fix] Improve support of rare Philips Respironics 950P events and update warnings.
-
+ - [fix] Empty directories in Profiles directory are not shown in Profiles list.
+ - [fix] Show correct copyright when installing Debian version.
+ - [fix] Improvements to Chromebook identification.
+
+
Changes and fixes in OSCAR v1.2.0-beta-1
Portions of OSCAR are © 2019-2020 by
The OSCAR Team
diff --git a/oscar/SleepLib/profiles.cpp b/oscar/SleepLib/profiles.cpp
index e25927b5..11d80312 100644
--- a/oscar/SleepLib/profiles.cpp
+++ b/oscar/SleepLib/profiles.cpp
@@ -1147,6 +1147,9 @@ void Scan()
// Iterate through subdirectories and load profiles..
for (auto & fi : list) {
QString npath = fi.canonicalFilePath();
+ QDir profilePath(npath);
+ if (profilePath.isEmpty()) // skip any empty folders
+ continue;
Profile *prof = new Profile(npath);
//prof->Open();
diff --git a/oscar/profileselector.cpp b/oscar/profileselector.cpp
index 8db6f013..21e74648 100644
--- a/oscar/profileselector.cpp
+++ b/oscar/profileselector.cpp
@@ -353,6 +353,10 @@ void ProfileSelector::on_buttonDestroyProfile_clicked()
QString name = proxy->data(proxy->index(ui->profileView->currentIndex().row(), 0, QModelIndex()), Qt::UserRole+2).toString();
Profile * profile = Profiles::profiles[name];
QString path = profile->Get(PrefMacro(STR_GEN_DataFolder));
+ if (path == (GetAppData() + "/Profiles/")) {
+ QMessageBox::warning(this, STR_MessageBox_Error, tr("The selected profile does not appear to contain any data and cannot be removed by OSCAR"), QMessageBox::Ok);
+ return;
+ }
bool verified = true;
if (profile->user->hasPassword()) {