diff --git a/README b/README index 3e0b9e7c..8b735bc4 100644 --- a/README +++ b/README @@ -1,4 +1,6 @@ -SleepyHead v1.0 branch +SleepyHead v1.1 branch + +*Warning*, backup your SleepyHeadData directory before using this branch as there will be no going backwards SleepyHead is cross platform, opensource sleep tracking program for reviewing CPAP and Oximetry data, which are devices used in the treatment of Sleep Disorders like Obstructive Sleep Apnea. diff --git a/sleepyhead/Graphs/MinutesAtPressure.cpp b/sleepyhead/Graphs/MinutesAtPressure.cpp index 55c76cf6..1c4ad110 100644 --- a/sleepyhead/Graphs/MinutesAtPressure.cpp +++ b/sleepyhead/Graphs/MinutesAtPressure.cpp @@ -224,6 +224,8 @@ void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion &r double s2; int widest_YAxis = 0; + float lineThickness = AppSetting->lineThickness(); + int mouseOverKey = 0; if (ipap.min_pressure > 0) { double xp,yp; @@ -308,7 +310,7 @@ void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion &r graph.renderText(label, left, top+5 ); xstep /= 5.0; - painter.setPen(QPen(ichan.defaultColor(), p_profile->appearance->lineThickness())); + painter.setPen(QPen(ichan.defaultColor(), lineThickness)); //////////////////////////////////////////////////////////////////// @@ -330,7 +332,7 @@ void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion &r if (i == mouseOverKey) { painter.setPen(QPen(Qt::black)); painter.drawRect(xp, yp-4, 8, 8); - painter.setPen(QPen(ichan.defaultColor(), p_profile->appearance->lineThickness())); + painter.setPen(QPen(ichan.defaultColor(), lineThickness)); } painter.drawLine(xp, lastyp, xp+xstep, yp); @@ -413,7 +415,7 @@ void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion &r QColor col = chan.defaultColor(); col.setAlpha(40); painter.setPen(col); - painter.setPen(QPen(col, p_profile->appearance->lineThickness())); + painter.setPen(QPen(col, lineThickness)); xp = left; @@ -468,7 +470,7 @@ void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion &r QColor col = chan.defaultColor(); col.setAlpha(50); painter.setPen(col); - painter.setPen(QPen(col, p_profile->appearance->lineThickness())); + painter.setPen(QPen(col, lineThickness)); xp = left; @@ -514,7 +516,7 @@ void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion &r */ if (epap.min_pressure) { - painter.setPen(QPen(echan.defaultColor(), p_profile->appearance->lineThickness())); + painter.setPen(QPen(echan.defaultColor(), lineThickness)); s2 = double(epap.times[qMax(min,0)]/60.0); xp=left, lastyp = bottom - (s2 * ystep); @@ -527,7 +529,7 @@ void MinutesAtPressure::paint(QPainter &painter, gGraph &graph, const QRegion &r if (i == mouseOverKey) { painter.setPen(QPen(Qt::black)); painter.drawRect(xp, yp-4, 8, 8); - painter.setPen(QPen(echan.defaultColor(), p_profile->appearance->lineThickness())); + painter.setPen(QPen(echan.defaultColor(), lineThickness)); } yp = bottom - qMax((double(p1) * ystep), 0.0); diff --git a/sleepyhead/Graphs/gFlagsLine.cpp b/sleepyhead/Graphs/gFlagsLine.cpp index dcec157b..f008c27c 100644 --- a/sleepyhead/Graphs/gFlagsLine.cpp +++ b/sleepyhead/Graphs/gFlagsLine.cpp @@ -217,7 +217,7 @@ bool gFlagsGroup::mouseMoveEvent(QMouseEvent *event, gGraph *graph) graph->timedRedraw(0); // } - if (!p_profile->appearance->graphTooltips()) { + if (!AppSetting->graphTooltips()) { return false; } @@ -306,6 +306,8 @@ void gFlagsLine::paint(QPainter &painter, gGraph &w, const QRegion ®ion) QColor color=schema::channel[m_code].defaultColor(); QBrush brush(color); + int tooltipTimeout = AppSetting->tooltipTimeout(); + bool hover = false; for (QList::iterator s = m_day->begin(); s != m_day->end(); s++) { if (!(*s)->enabled()) { @@ -384,7 +386,7 @@ void gFlagsLine::paint(QPainter &painter, gGraph &w, const QRegion ®ion) lab += QObject::tr(" (%3 sec)").arg(m).arg(s); } GetTextExtent(lab, x, y); - w.ToolTip(lab, x2 - 10, bartop + (3 * w.printScaleY()), TT_AlignRight, p_profile->general->tooltipTimeout()); + w.ToolTip(lab, x2 - 10, bartop + (3 * w.printScaleY()), TT_AlignRight, tooltipTimeout); } } @@ -412,7 +414,7 @@ void gFlagsLine::paint(QPainter &painter, gGraph &w, const QRegion ®ion) QString lab = QString("%1 (%2)").arg(schema::channel[m_code].fullname()).arg(*dptr); GetTextExtent(lab, x, y); - w.ToolTip(lab, x1 - 10, bartop + (3 * w.printScaleY()), TT_AlignRight, p_profile->general->tooltipTimeout()); + w.ToolTip(lab, x1 - 10, bartop + (3 * w.printScaleY()), TT_AlignRight, tooltipTimeout); } vlines.append(QLine(x1, bartop, x1, bottom)); diff --git a/sleepyhead/Graphs/gGraph.cpp b/sleepyhead/Graphs/gGraph.cpp index 84289a89..e6d32cdf 100644 --- a/sleepyhead/Graphs/gGraph.cpp +++ b/sleepyhead/Graphs/gGraph.cpp @@ -60,7 +60,7 @@ bool InitGraphGlobals() if (!PREF.contains("Fonts_Title_Name")) { PREF["Fonts_Title_Name"] = "Sans Serif"; - PREF["Fonts_Title_Size"] = 14; + PREF["Fonts_Title_Size"] = 12; PREF["Fonts_Title_Bold"] = true; PREF["Fonts_Title_Italic"] = false; } @@ -131,7 +131,7 @@ gGraph::gGraph(QString name, gGraphView *graphview, QString title, QString units m_visible(true) { if (height == 0) { - height = p_profile->appearance->graphHeight(); + height = AppSetting->graphHeight(); Q_UNUSED(height) } @@ -514,9 +514,9 @@ QPixmap gGraph::renderPixmap(int w, int h, bool printing) QPixmap pm(w,h); - bool pixcaching = p_profile->appearance->usePixmapCaching(); + bool pixcaching = AppSetting->usePixmapCaching(); graphView()->setUsePixmapCache(false); - p_profile->appearance->setUsePixmapCaching(false); + AppSetting->setUsePixmapCaching(false); QPainter painter(&pm); painter.fillRect(0,0,w,h,QBrush(QColor(Qt::white))); QRegion region(0,0,w,h); @@ -525,7 +525,7 @@ QPixmap gGraph::renderPixmap(int w, int h, bool printing) painter.end(); graphView()->setUsePixmapCache(pixcaching); - p_profile->appearance->setUsePixmapCaching(pixcaching); + AppSetting->setUsePixmapCaching(pixcaching); graphView()->setPrintScaleX(1); graphView()->setPrintScaleY(1); @@ -567,7 +567,7 @@ void gGraph::ResetBounds() void gGraph::ToolTip(QString text, int x, int y, ToolTipAlignment align, int timeout) { if (timeout <= 0) { - timeout = p_profile->general->tooltipTimeout(); + timeout = AppSetting->tooltipTimeout(); } m_graphview->m_tooltip->display(text, x, y, align, timeout); diff --git a/sleepyhead/Graphs/gGraphView.cpp b/sleepyhead/Graphs/gGraphView.cpp index e431573b..039b4728 100644 --- a/sleepyhead/Graphs/gGraphView.cpp +++ b/sleepyhead/Graphs/gGraphView.cpp @@ -104,7 +104,7 @@ h+=m_spacer*2; */ void gToolTip::display(QString text, int x, int y, ToolTipAlignment align, int timeout) { if (timeout <= 0) { - timeout = p_profile->general->tooltipTimeout(); + timeout = AppSetting->tooltipTimeout(); } m_alignment = align; @@ -355,7 +355,7 @@ gGraphView::gGraphView(QWidget *parent, gGraphView *shared) m_limbo = false; m_fadedir = false; m_blockUpdates = false; - use_pixmap_cache = p_profile->appearance->usePixmapCaching(); + use_pixmap_cache = AppSetting->usePixmapCaching(); pin_graph = nullptr; // pixmapcache.setCacheLimit(10240*2); @@ -633,7 +633,7 @@ void gGraphView::dumpInfo() bool gGraphView::usePixmapCache() { //use_pixmap_cache is an overide setting - return p_profile->appearance->usePixmapCaching(); + return AppSetting->usePixmapCaching(); } #define CACHE_DRAWTEXT @@ -644,7 +644,7 @@ void gGraphView::DrawTextQue(QPainter &painter) int w, h; // not sure if global antialiasing would be better.. - //painter.setRenderHint(QPainter::TextAntialiasing, p_profile->appearance->antiAliasing()); + //painter.setRenderHint(QPainter::TextAntialiasing, AppSetting->antiAliasing()); int items = m_textque.size(); for (int i = 0; i < items; ++i) { TextQue &q = m_textque[i]; @@ -1427,7 +1427,7 @@ void gGraphView::paintGL() painter.drawText(rec, Qt::AlignHCenter | Qt::AlignBottom, txt); } - if (p_profile->appearance->lineCursorMode()) { + if (AppSetting->lineCursorMode()) { emit updateCurrentTime(graphs_drawn ? m_currenttime : 0.0F); } else { emit updateRange(graphs_drawn ? m_minx : 0.0F, m_maxx); @@ -1442,7 +1442,7 @@ void gGraphView::paintGL() static int rp = 0; // Show FPS and draw time - if (m_showsplitter && p_profile->general->showPerformance()) { + if (m_showsplitter && AppSetting->showPerformance()) { QString ss; qint64 ela = time.nsecsElapsed(); double ms = double(ela) / 1000000.0; @@ -1790,7 +1790,7 @@ void gGraphView::mouseMoveEvent(QMouseEvent *event) if (ivisibleLayers()[i]->code(); QString ttip=schema::channel[code].description(); - m_tooltip->display(ttip,x,y-20,p_profile->general->tooltipTimeout()); + m_tooltip->display(ttip,x,y-20,AppSetting->tooltipTimeout()); redraw(); //qDebug() << code << ttip; } @@ -1801,7 +1801,7 @@ void gGraphView::mouseMoveEvent(QMouseEvent *event) } } else { if (!m_graphs[i]->units().isEmpty()) { - m_tooltip->display(m_graphs[i]->units(),x,y-20,p_profile->general->tooltipTimeout()); + m_tooltip->display(m_graphs[i]->units(),x,y-20,AppSetting->tooltipTimeout()); redraw(); } } @@ -2897,7 +2897,7 @@ void gGraphView::wheelEvent(QWheelEvent *event) return; if (event->modifiers() == Qt::NoModifier) { - int scrollDampening = p_profile->general->scrollDampening(); + int scrollDampening = AppSetting->scrollDampening(); if (event->orientation() == Qt::Vertical) { // Vertical Scrolling if (horizScrollTime.elapsed() < scrollDampening) { @@ -3116,7 +3116,7 @@ void gGraphView::keyPressEvent(QKeyEvent *event) } if (event->key() == Qt::Key_F3) { - p_profile->appearance->setLineCursorMode(!p_profile->appearance->lineCursorMode()); + AppSetting->setLineCursorMode(!AppSetting->lineCursorMode()); timedRedraw(0); } if ((event->key() == Qt::Key_F1)) { @@ -3130,7 +3130,7 @@ void gGraphView::keyPressEvent(QKeyEvent *event) if (event->key() == Qt::Key_PageUp) { if (m_scrollbar) { - m_offsetY -= p_profile->appearance->graphHeight() * 3 * m_scaleY; + m_offsetY -= AppSetting->graphHeight() * 3 * m_scaleY; m_scrollbar->setValue(m_offsetY); m_offsetY = m_scrollbar->value(); redraw(); @@ -3138,7 +3138,7 @@ void gGraphView::keyPressEvent(QKeyEvent *event) return; } else if (event->key() == Qt::Key_PageDown) { if (m_scrollbar) { - m_offsetY += p_profile->appearance->graphHeight() * 3 * m_scaleY; //p_profile->appearance->graphHeight(); + m_offsetY += AppSetting->graphHeight() * 3 * m_scaleY; if (m_offsetY < 0) { m_offsetY = 0; } @@ -3277,7 +3277,7 @@ void gGraphView::timedRedraw(int ms) } void gGraphView::resetLayout() { - int default_height = p_profile->appearance->graphHeight(); + int default_height = AppSetting->graphHeight(); for (int i = 0; i < m_graphs.size(); i++) { if (m_graphs[i]) m_graphs[i]->setHeight(default_height); diff --git a/sleepyhead/Graphs/gLineChart.cpp b/sleepyhead/Graphs/gLineChart.cpp index 27945bc9..d9466337 100644 --- a/sleepyhead/Graphs/gLineChart.cpp +++ b/sleepyhead/Graphs/gLineChart.cpp @@ -227,7 +227,7 @@ skipcheck: lob = new gLineOverlayBar(code, chan->defaultColor(), chan->label(), FT_Span); } if (lob != nullptr) { - lob->setOverlayDisplayType(((m_codes[0] == CPAP_FlowRate))? (OverlayDisplayType)p_profile->appearance->overlayType() : ODT_TopAndBottom); + lob->setOverlayDisplayType(((m_codes[0] == CPAP_FlowRate))? (OverlayDisplayType)AppSetting->overlayType() : ODT_TopAndBottom); lob->SetDay(m_day); flags[code] = lob; } @@ -476,7 +476,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) } - bool linecursormode = p_profile->appearance->lineCursorMode(); + bool linecursormode = AppSetting->lineCursorMode(); //////////////////////////////////////////////////////////////////////// // Display Line Cursor //////////////////////////////////////////////////////////////////////// @@ -538,7 +538,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) painter.setClipRect(left, top, width, height+1); painter.setClipping(true); - painter.setRenderHint(QPainter::Antialiasing, p_profile->appearance->antiAliasing()); + painter.setRenderHint(QPainter::Antialiasing, AppSetting->antiAliasing()); painter.setFont(*defaultfont); bool showDottedLines = true; @@ -554,6 +554,8 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) Session * sess = nullptr; ChannelID code; + float lineThickness = AppSetting->lineThickness()+0.001F; + for (int gi = 0; gi < m_codes.size(); gi++) { code = m_codes[gi]; schema::Channel &chan = schema::channel[code]; @@ -572,7 +574,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) dot.visible = true; QColor color = chan.calc[dot.type].color; color.setAlpha(200); - painter.setPen(QPen(QBrush(color), p_profile->appearance->lineThickness(), Qt::DotLine)); + painter.setPen(QPen(QBrush(color), lineThickness, Qt::DotLine)); EventDataType y=top + height + 1 - ((dot.value - miny) * ymult); painter.drawLine(left + 1, y, left + 1 + width, y); @@ -875,7 +877,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) } } - painter.setPen(QPen(chan.defaultColor(), p_profile->appearance->lineThickness())); + painter.setPen(QPen(chan.defaultColor(), lineThickness)); painter.drawLines(lines); w.graphView()->lines_drawn_this_frame += lines.count(); lines.clear(); @@ -999,7 +1001,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) } } } - painter.setPen(QPen(chan.defaultColor(),p_profile->appearance->lineThickness())); + painter.setPen(QPen(chan.defaultColor(), lineThickness)); painter.drawLines(lines); w.graphView()->lines_drawn_this_frame+=lines.count(); lines.clear(); @@ -1011,7 +1013,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) } -// painter.setPen(QPen(m_colors[gi],p_profile->appearance->lineThickness())); +// painter.setPen(QPen(m_colors[gi],lineThickness)); // painter.drawLines(lines); // w.graphView()->lines_drawn_this_frame+=lines.count(); // lines.clear(); @@ -1131,7 +1133,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) int cnt = 0; // Draw the linechart overlays - if (m_day && (p_profile->appearance->lineCursorMode() || (m_codes[0]==CPAP_FlowRate))) { + if (m_day && (AppSetting->lineCursorMode() || (m_codes[0]==CPAP_FlowRate))) { QHash::iterator fit; bool blockhover = false; diff --git a/sleepyhead/Graphs/gLineOverlay.cpp b/sleepyhead/Graphs/gLineOverlay.cpp index 43daac94..00c5e419 100644 --- a/sleepyhead/Graphs/gLineOverlay.cpp +++ b/sleepyhead/Graphs/gLineOverlay.cpp @@ -83,6 +83,8 @@ void gLineOverlayBar::paint(QPainter &painter, gGraph &w, const QRegion ®ion) qint64 drift = 0; //bool hover = false; + int tooltipTimeout = AppSetting->tooltipTimeout(); + // For each session, process it's eventlist for (QList::iterator s = m_day->begin(); s != m_day->end(); s++) { @@ -237,7 +239,7 @@ void gLineOverlayBar::paint(QPainter &painter, gGraph &w, const QRegion ®ion) QString lab = QString("%1 (%2)").arg(schema::channel[m_code].fullname()).arg(raw); GetTextExtent(lab, x, y); - w.ToolTip(lab, x1 - 10, start_py + 24 + (3 * w.printScaleY()), TT_AlignRight, p_profile->general->tooltipTimeout()); + w.ToolTip(lab, x1 - 10, start_py + 24 + (3 * w.printScaleY()), TT_AlignRight, AppSetting->tooltipTimeout()); //painter.fillRect(x1 - (x / 2) - x, start_py + 14 + (3 * w.printScaleY()), x+4,y+4, QBrush(QColor(255,255,255,245))); // painter.setPen(QPen(Qt::gray,1)); @@ -276,7 +278,7 @@ void gLineOverlayBar::paint(QPainter &painter, gGraph &w, const QRegion ®ion) QString lab = QString("%1 (%2)").arg(schema::channel[m_code].fullname()).arg(raw); GetTextExtent(lab, x, y, defaultfont); - w.ToolTip(lab, x1 - 10, start_py + 24 + (3 * w.printScaleY()), TT_AlignRight, p_profile->general->tooltipTimeout()); + w.ToolTip(lab, x1 - 10, start_py + 24 + (3 * w.printScaleY()), TT_AlignRight, tooltipTimeout); // painter.fillRect(x1 - (x / 2) - x, start_py + 14 + (3 * w.printScaleY()), x+4,y+4, QBrush(QColor(255,255,255,245))); // painter.setPen(QPen(Qt::gray,1)); diff --git a/sleepyhead/Graphs/gSessionTimesChart.cpp b/sleepyhead/Graphs/gSessionTimesChart.cpp index fad2f00b..a3c30d1d 100644 --- a/sleepyhead/Graphs/gSessionTimesChart.cpp +++ b/sleepyhead/Graphs/gSessionTimesChart.cpp @@ -1188,14 +1188,12 @@ void gAHIChart::customCalc(Day *day, QVector &list) ahi_avg += ahi_cnt; total_hours += hours; total_days++; - qDebug() << "Leaving gAHIChart::customCalc - ahi_avg: " << ahi_avg << " total_days: " << total_days ; } void gAHIChart::afterDraw(QPainter & /*painter */, gGraph &graph, QRect rect) { if (totaldays == nousedays) return; //int size = idx_end - idx_start; - qDebug() << "Entering gAHIChart::afterDraw - ahi_avg: " << ahi_avg << " total_days: " << total_days ; bool skip = true; float med = 0; diff --git a/sleepyhead/Graphs/gSummaryChart.cpp b/sleepyhead/Graphs/gSummaryChart.cpp index ca894fd2..d6b8c4e6 100644 --- a/sleepyhead/Graphs/gSummaryChart.cpp +++ b/sleepyhead/Graphs/gSummaryChart.cpp @@ -395,7 +395,7 @@ void SummaryChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) GraphType graphtype = m_graphtype; if (graphtype == GT_LINE || graphtype == GT_POINTS) { - bool pts = p_profile->appearance->overviewLinechartMode() == OLC_Lines; + bool pts = AppSetting->overviewLinechartMode() == OLC_Lines; graphtype = pts ? GT_POINTS : GT_LINE; } @@ -494,7 +494,7 @@ void SummaryChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) lastdaygood = true; // Display Line Cursor - if (p_profile->appearance->lineCursorMode()) { + if (AppSetting->lineCursorMode()) { qint64 time = lcursor; double xmult = double(width) / xx; @@ -569,6 +569,8 @@ void SummaryChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) QColor summaryColor = QColor("dark gray"); + float lineThickness = AppSetting->lineThickness(); + for (qint64 Q = minx; Q <= maxx + ms_per_day; Q += ms_per_day) { zd = Q / ms_per_day; d = m_values.find(zd); @@ -778,14 +780,14 @@ void SummaryChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) if (lastdaygood) { if (lastY[j] != py2) { // vertical line - painter.setPen(QPen(col2,p_profile->appearance->lineThickness())); + painter.setPen(QPen(col2, lineThickness)); painter.drawLine(lastX[j], lastY[j], px, py2); } - painter.setPen(QPen(col1,p_profile->appearance->lineThickness())); + painter.setPen(QPen(col1, lineThickness)); painter.drawLine(px, py2, px2, py2); } else { - painter.setPen(QPen(col1,p_profile->appearance->lineThickness())); + painter.setPen(QPen(col1, lineThickness)); painter.drawLine(x1, py2, x2, py2); } @@ -808,10 +810,10 @@ void SummaryChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion) } if (lastdaygood) { - painter.setPen(QPen(col2,p_profile->appearance->lineThickness())); + painter.setPen(QPen(col2, lineThickness)); painter.drawLine(lastX[j] - barw / 2, lastY[j], px2 - barw / 2, py2); } else { - painter.setPen(QPen(col1,p_profile->appearance->lineThickness())); + painter.setPen(QPen(col1, lineThickness)); painter.drawLine(px + barw / 2 - 1, py2, px + barw / 2 + 1, py2); } diff --git a/sleepyhead/Graphs/gYAxis.cpp b/sleepyhead/Graphs/gYAxis.cpp index b45005c4..10a23556 100644 --- a/sleepyhead/Graphs/gYAxis.cpp +++ b/sleepyhead/Graphs/gYAxis.cpp @@ -295,7 +295,7 @@ const QString gYAxis::Format(EventDataType v, int dp) bool gYAxis::mouseMoveEvent(QMouseEvent *event, gGraph *graph) { - if (!p_profile->appearance->graphTooltips()) { + if (!AppSetting->graphTooltips()) { return false; } diff --git a/sleepyhead/Resources.qrc b/sleepyhead/Resources.qrc index 9ed9ea3e..8f05c1c4 100644 --- a/sleepyhead/Resources.qrc +++ b/sleepyhead/Resources.qrc @@ -52,5 +52,6 @@ icons/aircurve.png icons/prs1_960.png icons/daily.png + icons/dv64.png diff --git a/sleepyhead/SleepLib/appsettings.h b/sleepyhead/SleepLib/appsettings.h new file mode 100644 index 00000000..386a4137 --- /dev/null +++ b/sleepyhead/SleepLib/appsettings.h @@ -0,0 +1,197 @@ +/* SleepLib AppSettings Header + * + * This file for all settings related stuff to clean up Preferences & Profiles. + * + * 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 Linux + * distribution for more details. */ + +#ifndef APPSETTINGS_H +#define APPSETTINGS_H + +#include "preferences.h" +#include "common.h" + + +class Preferences; + +enum OverviewLinechartModes { OLC_Bartop, OLC_Lines }; + + +// ApplicationWideSettings Strings +const QString STR_CS_UserEventPieChart = "UserEventPieChart"; +const QString STR_IS_Multithreading = "EnableMultithreading"; +const QString STR_AS_GraphHeight = "GraphHeight"; +const QString STR_AS_DailyPanelWidth = "DailyPanelWidth"; +const QString STR_AS_RightPanelWidth = "RightPanelWidth"; +const QString STR_AS_AntiAliasing = "UseAntiAliasing"; +const QString STR_AS_GraphSnapshots = "EnableGraphSnapshots"; +const QString STR_AS_Animations = "AnimationsAndTransitions"; +const QString STR_AS_SquareWave = "SquareWavePlots"; +const QString STR_AS_OverlayType = "OverlayType"; +const QString STR_AS_OverviewLinechartMode = "OverviewLinechartMode"; +const QString STR_AS_UsePixmapCaching = "UsePixmapCaching"; +const QString STR_AS_AllowYAxisScaling = "AllowYAxisScaling"; +const QString STR_AS_GraphTooltips = "GraphTooltips"; +const QString STR_AS_LineThickness = "LineThickness"; +const QString STR_AS_LineCursorMode = "LineCursorMode"; +const QString STR_AS_CalendarVisible = "CalendarVisible"; +const QString STR_AS_RightSidebarVisible = "RightSidebarVisible"; +const QString STR_US_TooltipTimeout = "TooltipTimeout"; +const QString STR_US_ScrollDampening = "ScrollDampening"; +const QString STR_US_ShowDebug = "ShowDebug"; +const QString STR_US_ShowPerformance = "ShowPerformance"; +const QString STR_US_ShowSerialNumbers = "ShowSerialNumbers"; +const QString STR_US_OpenTabAtStart = "OpenTabAtStart"; +const QString STR_US_OpenTabAfterImport = "OpenTabAfterImport"; +const QString STR_US_AutoLaunchImport = "AutoLaunchImport"; +const QString STR_US_RemoveCardReminder = "RemoveCardReminder"; +const QString STR_IS_CacheSessions = "MemoryHog"; + + +class AppWideSetting: public PrefSettings +{ +public: + AppWideSetting(Preferences *pref) + : PrefSettings(pref) + { + initPref(STR_IS_Multithreading, idealThreads() > 1); + initPref(STR_US_ShowPerformance, false); + initPref(STR_US_ShowDebug, false); + initPref(STR_AS_CalendarVisible, true); + initPref(STR_US_ScrollDampening, (int)50); + initPref(STR_US_TooltipTimeout, (int)2500); + initPref(STR_AS_GraphHeight, 180.0); + initPref(STR_AS_DailyPanelWidth, 350.0); + initPref(STR_AS_RightPanelWidth, 230.0); + initPref(STR_AS_AntiAliasing, true); + initPref(STR_AS_GraphSnapshots, true); + initPref(STR_AS_Animations, true); + initPref(STR_AS_SquareWave, false); + initPref(STR_AS_AllowYAxisScaling, true); + initPref(STR_AS_GraphTooltips, true); + initPref(STR_AS_UsePixmapCaching, false); + initPref(STR_AS_OverlayType, ODT_Bars); + initPref(STR_AS_OverviewLinechartMode, OLC_Bartop); + initPref(STR_AS_LineThickness, 1.0); + initPref(STR_AS_LineCursorMode, true); + initPref(STR_AS_RightSidebarVisible, true); + initPref(STR_CS_UserEventPieChart, false); + initPref(STR_US_ShowSerialNumbers, false); + initPref(STR_US_OpenTabAtStart, 1); + initPref(STR_US_OpenTabAfterImport, 0); + initPref(STR_US_AutoLaunchImport, false); + initPref(STR_IS_CacheSessions, false); + initPref(STR_US_RemoveCardReminder, true); + initPref(STR_GEN_Profile, ""); + } + + QString profileName() const { return getPref(STR_GEN_Profile).toString(); } + bool autoLaunchImport() const { return getPref(STR_US_AutoLaunchImport).toBool(); } + bool cacheSessions() const { return getPref(STR_IS_CacheSessions).toBool(); } + bool multithreading() const { return getPref(STR_IS_Multithreading).toBool(); } + bool showDebug() const { return getPref(STR_US_ShowDebug).toBool(); } + bool showPerformance() const { return getPref(STR_US_ShowPerformance).toBool(); } + //! \brief Whether to show the calendar + bool calendarVisible() const { return getPref(STR_AS_CalendarVisible).toBool(); } + int scrollDampening() const { return getPref(STR_US_ScrollDampening).toInt(); } + int tooltipTimeout() const { return getPref(STR_US_TooltipTimeout).toInt(); } + //! \brief Returns the normal (unscaled) height of a graph + int graphHeight() const { return getPref(STR_AS_GraphHeight).toInt(); } + //! \brief Returns the normal (unscaled) height of a graph + int dailyPanelWidth() const { return getPref(STR_AS_DailyPanelWidth).toInt(); } + //! \brief Returns the normal (unscaled) height of a graph + int rightPanelWidth() const { return getPref(STR_AS_RightPanelWidth).toInt(); } + //! \brief Returns true if AntiAliasing (the graphical smoothing method) is enabled + bool antiAliasing() const { return getPref(STR_AS_AntiAliasing).toBool(); } + //! \brief Returns true if renderPixmap function is in use, which takes snapshots of graphs + bool graphSnapshots() const { return getPref(STR_AS_GraphSnapshots).toBool(); } + //! \brief Returns true if Graphical animations & Transitions will be drawn + bool animations() const { return getPref(STR_AS_Animations).toBool(); } + //! \brief Returns true if PixmapCaching acceleration will be used + bool usePixmapCaching() const { return getPref(STR_AS_UsePixmapCaching).toBool(); } + //! \brief Returns true if Square Wave plots are preferred (where possible) + bool squareWavePlots() const { return getPref(STR_AS_SquareWave).toBool(); } + //! \brief Whether to allow double clicking on Y-Axis labels to change vertical scaling mode + bool allowYAxisScaling() const { return getPref(STR_AS_AllowYAxisScaling).toBool(); } + //! \brief Whether to show graph tooltips + bool graphTooltips() const { return getPref(STR_AS_GraphTooltips).toBool(); } + //! \brief Pen width of line plots + float lineThickness() const { return getPref(STR_AS_LineThickness).toFloat(); } + //! \brief Whether to show line cursor + bool lineCursorMode() const { return getPref(STR_AS_LineCursorMode).toBool(); } + //! \brief Whether to show the right sidebar + bool rightSidebarVisible() const { return getPref(STR_AS_RightSidebarVisible).toBool(); } + //! \brief Returns the type of overlay flags (which are displayed over the Flow Waveform) + OverlayDisplayType overlayType() const { + return (OverlayDisplayType)getPref(STR_AS_OverlayType).toInt(); + } + //! \brief Returns the display type of Overview pages linechart + OverviewLinechartModes overviewLinechartMode() const { + return (OverviewLinechartModes)getPref(STR_AS_OverviewLinechartMode).toInt(); + } + bool userEventPieChart() const { return getPref(STR_CS_UserEventPieChart).toBool(); } + bool showSerialNumbers() const { return getPref(STR_US_ShowSerialNumbers).toBool(); } + int openTabAtStart() const { return getPref(STR_US_OpenTabAtStart).toInt(); } + int openTabAfterImport() const { return getPref(STR_US_OpenTabAfterImport).toInt(); } + bool removeCardReminder() const { return getPref(STR_US_RemoveCardReminder).toBool(); } + + + void setProfileName(QString name) { setPref(STR_GEN_Profile, name); } + + void setAutoLaunchImport(bool b) { setPref(STR_US_AutoLaunchImport, b); } + void setCacheSessions(bool c) { setPref(STR_IS_CacheSessions, c); } + void setMultithreading(bool enabled) { setPref(STR_IS_Multithreading, enabled); } + void setShowDebug(bool b) { setPref(STR_US_ShowDebug, b); } + void setShowPerformance(bool b) { setPref(STR_US_ShowPerformance, b); } + //! \brief Sets whether to display the (Daily View) Calendar + void setCalendarVisible(bool b) { setPref(STR_AS_CalendarVisible, b); } + void setScrollDampening(int i) { setPref(STR_US_ScrollDampening, i); } + void setTooltipTimeout(int i) { setPref(STR_US_TooltipTimeout, i); } + //! \brief Set the normal (unscaled) height of a graph. + void setGraphHeight(int height) { setPref(STR_AS_GraphHeight, height); } + //! \brief Set the normal (unscaled) height of a graph. + void setDailyPanelWidth(int width) { setPref(STR_AS_DailyPanelWidth, width); } + //! \brief Set the normal (unscaled) height of a graph. + void setRightPanelWidth(int width) { setPref(STR_AS_RightPanelWidth, width); } + //! \brief Set to true to turn on AntiAliasing (the graphical smoothing method) + void setAntiAliasing(bool aa) { setPref(STR_AS_AntiAliasing, aa); } + //! \brief Set to true if renderPixmap functions are in use, which takes snapshots of graphs. + void setGraphSnapshots(bool gs) { setPref(STR_AS_GraphSnapshots, gs); } + //! \brief Set to true if Graphical animations & Transitions will be drawn + void setAnimations(bool anim) { setPref(STR_AS_Animations, anim); } + //! \brief Set to true to use Pixmap Caching of Text and other graphics caching speedup techniques + void setUsePixmapCaching(bool b) { setPref(STR_AS_UsePixmapCaching, b); } + //! \brief Set whether or not to useSquare Wave plots (where possible) + void setSquareWavePlots(bool sw) { setPref(STR_AS_SquareWave, sw); } + //! \brief Sets the type of overlay flags (which are displayed over the Flow Waveform) + void setOverlayType(OverlayDisplayType od) { setPref(STR_AS_OverlayType, (int)od); } + //! \brief Sets whether to allow double clicking on Y-Axis labels to change vertical scaling mode + void setAllowYAxisScaling(bool b) { setPref(STR_AS_AllowYAxisScaling, b); } + //! \brief Sets whether to allow double clicking on Y-Axis labels to change vertical scaling mode + void setGraphTooltips(bool b) { setPref(STR_AS_GraphTooltips, b); } + //! \brief Sets the type of overlay flags (which are displayed over the Flow Waveform) + void setOverviewLinechartMode(OverviewLinechartModes od) { + setPref(STR_AS_OverviewLinechartMode, (int)od); + } + //! \brief Set the pen width of line plots. + void setLineThickness(float size) { setPref(STR_AS_LineThickness, size); } + //! \brief Sets whether to display Line Cursor + void setLineCursorMode(bool b) { setPref(STR_AS_LineCursorMode, b); } + //! \brief Sets whether to display the right sidebar + void setRightSidebarVisible(bool b) { setPref(STR_AS_RightSidebarVisible, b); } + void setUserEventPieChart(bool b) { setPref(STR_CS_UserEventPieChart, b); } + void setShowSerialNumbers(bool enabled) { setPref(STR_US_ShowSerialNumbers, enabled); } + void setOpenTabAtStart(int idx) { setPref(STR_US_OpenTabAtStart, idx); } + void setOpenTabAfterImport(int idx) { setPref(STR_US_OpenTabAfterImport, idx); } + void setRemoveCardReminder(bool b) { setPref(STR_US_RemoveCardReminder, b); } +}; + + +extern AppWideSetting *AppSetting; + + + +#endif // APPSETTINGS_H diff --git a/sleepyhead/SleepLib/common.cpp b/sleepyhead/SleepLib/common.cpp index 0ffbe737..4b39b6f8 100644 --- a/sleepyhead/SleepLib/common.cpp +++ b/sleepyhead/SleepLib/common.cpp @@ -8,13 +8,13 @@ #include #include +#include #include #include "profiles.h" // Used by internal settings - const QString getDeveloperName() { return STR_DeveloperName; @@ -32,6 +32,8 @@ const QString getDefaultAppRoot() return approot; } +int idealThreads() { return QThread::idealThreadCount(); } + qint64 timezoneOffset() { static bool ok = false; @@ -174,6 +176,7 @@ QString STR_TR_Plethy; // Plethysomogram QString STR_TR_Pressure; QString STR_TR_Daily; +QString STR_TR_Profile; QString STR_TR_Overview; QString STR_TR_Oximetry; @@ -376,6 +379,7 @@ void initializeStrings() STR_TR_Pressure = QObject::tr("Pressure"); STR_TR_Daily = QObject::tr("Daily"); + STR_TR_Profile = QObject::tr("Profile"); STR_TR_Overview = QObject::tr("Overview"); STR_TR_Oximetry = QObject::tr("Oximetry"); diff --git a/sleepyhead/SleepLib/common.h b/sleepyhead/SleepLib/common.h index 004b15f3..54c91f2c 100644 --- a/sleepyhead/SleepLib/common.h +++ b/sleepyhead/SleepLib/common.h @@ -1,4 +1,4 @@ -/* Common code and junk +/* Common code and junk * * Copyright (C) 2011-2018 Mark Watkins * @@ -12,6 +12,7 @@ #include #include #include +#include #if QT_VERSION >= QT_VERSION_CHECK(4,8,0) @@ -45,6 +46,8 @@ struct ValueCount { double p; }; +extern int idealThreads(); + void copyPath(QString src, QString dst); @@ -206,6 +209,7 @@ extern QString STR_TR_Plethy; // Plethysomogram extern QString STR_TR_Pressure; extern QString STR_TR_Daily; +extern QString STR_TR_Profile; extern QString STR_TR_Overview; extern QString STR_TR_Oximetry; diff --git a/sleepyhead/SleepLib/journal.cpp b/sleepyhead/SleepLib/journal.cpp index ab42779f..a0ec2bc8 100644 --- a/sleepyhead/SleepLib/journal.cpp +++ b/sleepyhead/SleepLib/journal.cpp @@ -57,7 +57,7 @@ JournalEntry::JournalEntry(QDate date) // more then one.. report this. } } - jmach = MachineLoader::CreateMachine(info, machid); + jmach = p_profile->CreateMachine(info, machid); } m_date = date; diff --git a/sleepyhead/SleepLib/loader_plugins/cms50_loader.cpp b/sleepyhead/SleepLib/loader_plugins/cms50_loader.cpp index ec02b444..d65e6a6b 100644 --- a/sleepyhead/SleepLib/loader_plugins/cms50_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/cms50_loader.cpp @@ -265,7 +265,7 @@ int CMS50Loader::doImportMode() MachineInfo info = newInfo(); info.model = cms50dplus ? QObject::tr("CMS50D+") : QObject::tr("CMS50E/F"); info.serial = QString(); - Machine * mach = CreateMachine(info); + Machine * mach = p_profile->CreateMachine(info); Q_UNUSED(mach); diff --git a/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp b/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp index d52c5e61..69785513 100644 --- a/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/icon_loader.cpp @@ -126,7 +126,7 @@ int FPIconLoader::Open(QString path) for (int i = 0; i < SerialNumbers.size(); i++) { MachineInfo info = newInfo(); info.serial = SerialNumbers[i]; - m = CreateMachine(info); + m = p_profile->CreateMachine(info); npath = newpath + "/" + info.serial; diff --git a/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp b/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp index 9025fc1d..7a3a1f20 100644 --- a/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/intellipap_loader.cpp @@ -1,4 +1,4 @@ -/* SleepLib (DeVilbiss) Intellipap Loader Implementation +/* SleepLib (DeVilbiss) Intellipap Loader Implementation * * Notes: Intellipap DV54 requires the SmartLink attachment to access this data. * @@ -29,9 +29,13 @@ Intellipap::~Intellipap() IntellipapLoader::IntellipapLoader() { const QString INTELLIPAP_ICON = ":/icons/intellipap.png"; + const QString DV6_ICON = ":/icons/dv64.png"; + QString s = newInfo().series; m_pixmap_paths[s] = INTELLIPAP_ICON; m_pixmaps[s] = QPixmap(INTELLIPAP_ICON); + m_pixmap_paths["DV6"] = DV6_ICON; + m_pixmaps["DV6"] = QPixmap(DV6_ICON); m_buffer = nullptr; m_type = MT_CPAP; @@ -236,7 +240,7 @@ int IntellipapLoader::OpenDV5(QString path) } if (!info.serial.isEmpty()) { - mach = CreateMachine(info); + mach = p_profile->CreateMachine(info); } if (!mach) { @@ -781,7 +785,7 @@ int IntellipapLoader::OpenDV6(QString path) //////////////////////////////////////////////////////////////////////////////////////// // Creates Machine database record if it doesn't exist already //////////////////////////////////////////////////////////////////////////////////////// - Machine *mach = CreateMachine(info); + Machine *mach = p_profile->CreateMachine(info); if (mach == nullptr) { return -1; } diff --git a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp index 8f09ccba..6e13be16 100644 --- a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp @@ -592,7 +592,7 @@ int PRS1Loader::OpenMachine(QString path) // Which is needed to get the right machine record.. - Machine *m = CreateMachine(info); + Machine *m = p_profile->CreateMachine(info); // This time supply the machine object so it can populate machine properties.. PeekProperties(m->info, propertyfile, m); @@ -726,7 +726,7 @@ int PRS1Loader::OpenMachine(QString path) int tasks = countTasks(); - runTasks(p_profile->session->multithreading()); + runTasks(AppSetting->multithreading()); finishAddingSessions(); return m->unsupported() ? -1 : tasks; diff --git a/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp b/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp index 74aa26ab..0cf8ddeb 100644 --- a/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp @@ -1948,7 +1948,7 @@ int ResmedLoader::scanFiles(Machine * mach, QString datalog_path) // Run the tasks... int c = countTasks(); - runTasks(p_profile->session->multithreading()); + runTasks(AppSetting->multithreading()); newSkipFiles.append(skipfiles.keys()); impfile.remove(); @@ -2083,7 +2083,7 @@ int ResmedLoader::Open(QString path) /////////////////////////////////////////////////////////////////////////////////// // Create machine object (unless it's already registered) /////////////////////////////////////////////////////////////////////////////////// - Machine *m = CreateMachine(info); + Machine *m = p_profile->CreateMachine(info); bool create_backups = p_profile->session->backupCardData(); bool compress_backups = p_profile->session->compressBackupData(); diff --git a/sleepyhead/SleepLib/loader_plugins/somnopose_loader.cpp b/sleepyhead/SleepLib/loader_plugins/somnopose_loader.cpp index 26bf2267..9baf8155 100644 --- a/sleepyhead/SleepLib/loader_plugins/somnopose_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/somnopose_loader.cpp @@ -105,7 +105,7 @@ int SomnoposeLoader::OpenFile(QString filename) bool first = true; MachineInfo info = newInfo(); - Machine *mach = CreateMachine(info); + Machine *mach = p_profile->CreateMachine(info); Session *sess = nullptr; SessionID sid; diff --git a/sleepyhead/SleepLib/loader_plugins/weinmann_loader.cpp b/sleepyhead/SleepLib/loader_plugins/weinmann_loader.cpp index d0364b42..b6711a96 100644 --- a/sleepyhead/SleepLib/loader_plugins/weinmann_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/weinmann_loader.cpp @@ -130,7 +130,7 @@ int WeinmannLoader::Open(QString path) MachineInfo info = newInfo(); info.serial = "141819"; - Machine * mach = CreateMachine(info); + Machine * mach = p_profile->CreateMachine(info); int WeekComplianceOffset = index["WeekComplianceOffset"]; diff --git a/sleepyhead/SleepLib/loader_plugins/zeo_loader.cpp b/sleepyhead/SleepLib/loader_plugins/zeo_loader.cpp index 0a1598b3..5bab2cdb 100644 --- a/sleepyhead/SleepLib/loader_plugins/zeo_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/zeo_loader.cpp @@ -118,7 +118,7 @@ int ZEOLoader::OpenFile(QString filename) QStringList SG, DSG; MachineInfo info = newInfo(); - Machine *mach = CreateMachine(info); + Machine *mach = p_profile->CreateMachine(info); int idxZQ = header.indexOf("ZQ"); diff --git a/sleepyhead/SleepLib/machine.cpp b/sleepyhead/SleepLib/machine.cpp index d3170ba4..3777c32f 100644 --- a/sleepyhead/SleepLib/machine.cpp +++ b/sleepyhead/SleepLib/machine.cpp @@ -1,4 +1,4 @@ -/* SleepLib Machine Class Implementation +/* SleepLib Machine Class Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -49,9 +49,16 @@ Machine::Machine(MachineID id) srand(time(nullptr)); MachineID temp; + bool found; + // Keep trying until we get a unique machineID for this profile do { temp = rand(); - } while (p_profile->machlist.find(temp) != p_profile->machlist.end()); + + found = false; + for (int i=0;im_machlist.size(); ++i) { + if (p_profile->m_machlist.at(i)->id() == temp) found = true; + } + } while (found); m_id = temp; @@ -115,12 +122,19 @@ bool Machine::saveSessionInfo() bool Machine::loadSessionInfo() { + // SessionInfo basically just contains a list of all sessions and their enabled status, + // so the enabling/reenabling doesn't require a summary rewrite every time. + if (info.type == MT_JOURNAL) return true; QHash::iterator s; QFile file(getDataPath() + "Sessions.info"); + if (!file.open(QFile::ReadOnly)) { + // No session.info file present, so let's create one... + + // But first check for legacy SESSION_ENABLED field in session settings for (s = sessionlist.begin(); s!= sessionlist.end(); ++s) { Session * sess = s.value(); QHash::iterator it = sess->settings.find(SESSION_ENABLED); @@ -131,6 +145,8 @@ bool Machine::loadSessionInfo() } sess->setEnabled(b); // Extract from session settings and save.. } + + // Now write the file saveSessionInfo(); return true; } @@ -146,12 +162,15 @@ bool Machine::loadSessionInfo() in >> ft16; in >> version; + // Legacy crud if (version == 1) { // was available channels QHash crap; in >> crap; } + + // Read in size record, followed by size * [SessionID, bool] pairs containing the enable status. int size; in >> size; @@ -588,6 +607,7 @@ bool Machine::Load() QFile::copy(path+filename, summarypath+filename); QFile::remove(path+filename); } + // Copy old Event files to folder filters.clear(); filters << "*.001"; @@ -596,7 +616,7 @@ bool Machine::Load() size = filelist.size(); if (size > 0) { if (!dir.exists(eventpath)) dir.mkpath(eventpath); - for (int i=0; i< filelist.size(); i++) { + for (int i=0; i< size; i++) { if ((i % 50) == 0) { // This is slow.. :-/ if (progress) { progress->setValue((float(i) / float(size) * 100.0)); } @@ -689,7 +709,7 @@ void Machine::queSaveList(Session * sess) sess->UpdateSummaries(); sess->Store(getDataPath()); - if (!p_profile->session->cacheSessions()) { + if (!AppSetting->cacheSessions()) { sess->TrashEvents(); } @@ -719,7 +739,7 @@ Session *Machine::popSaveList() void Machine::StartSaveThreads() { m_savelist.clear(); - if (!p_profile->session->multithreading()) return; + if (!AppSetting->multithreading()) return; QString path = getDataPath(); @@ -822,7 +842,7 @@ void SaveTask::run() void Machine::queTask(ImportTask * task) { // Okay... what was this turned off??? - if (p_profile->session->multithreading()) { + if (AppSetting->multithreading()) { m_tasklist.push_back(task); return; } @@ -979,7 +999,7 @@ bool Machine::LoadSummary(QProgressBar * progress) for (it = sess_order.begin(); it != it_end; ++it, ++cnt) { if ((cnt % 100) == 0) { progress->setValue(cnt); - QApplication::processEvents(); + //QApplication::processEvents(); } Session * sess = it.value(); if (!AddSession(sess)) { diff --git a/sleepyhead/SleepLib/machine.h b/sleepyhead/SleepLib/machine.h index 835b1326..e1408df5 100644 --- a/sleepyhead/SleepLib/machine.h +++ b/sleepyhead/SleepLib/machine.h @@ -88,6 +88,7 @@ class Machine //! \brief Load all Machine summary data bool Load(); + bool LoadSummary(QProgressBar * progress); //! \brief Save all Sessions where changed bit is set. diff --git a/sleepyhead/SleepLib/machine_loader.cpp b/sleepyhead/SleepLib/machine_loader.cpp index a8fc838f..ad6b3d9c 100644 --- a/sleepyhead/SleepLib/machine_loader.cpp +++ b/sleepyhead/SleepLib/machine_loader.cpp @@ -58,96 +58,8 @@ MachineLoader * lookupLoader(QString loaderName) return nullptr; } -QHash > MachineList; -void MachineLoader::removeMachine(Machine * m) -{ - m_machlist.removeAll(m); - QHash >::iterator mlit = MachineList.find(m->loaderName()); - - if (mlit != MachineList.end()) { - QHash::iterator mit = mlit.value().find(m->serial()); - if (mit != mlit.value().end()) { - mlit.value().erase(mit); - } - } - -} - -Machine * MachineLoader::lookupMachine(QString serial) -{ - QHash >::iterator mlit = MachineList.find(loaderName()); - if (mlit != MachineList.end()) { - QHash::iterator mit = mlit.value().find(serial); - if (mit != mlit.value().end()) { - return mit.value(); - } - } - return nullptr; -} - -Machine * MachineLoader::CreateMachine(MachineInfo info, MachineID id) -{ - Q_ASSERT(p_profile != nullptr); - - Machine *m = nullptr; - - QHash >::iterator mlit = MachineList.find(info.loadername); - - if (mlit != MachineList.end()) { - QHash::iterator mit = mlit.value().find(info.serial); - if (mit != mlit.value().end()) { - mit.value()->setInfo(info); // update info - return mit.value(); - } - } - - // Before we create, find any lost folder to get the old ID - if ((id == 0) && ((info.type == MT_OXIMETER) || (info.type == MT_JOURNAL) || (info.type == MT_POSITION)|| (info.type == MT_SLEEPSTAGE))) { - QString dataPath = p_profile->Get("{" + STR_GEN_DataFolder + "}/"); - QDir dir(dataPath); - QStringList namefilter(QString(info.loadername+"_*")); - QStringList files = dir.entryList(namefilter, QDir::Dirs); - if (files.size() > 0) { - QString idstr = files[0].section("_",-1); - bool ok; - id = idstr.toInt(&ok, 16); - } - } - - switch (info.type) { - case MT_CPAP: - m = new CPAP(id); - break; - case MT_SLEEPSTAGE: - m = new SleepStage(id); - break; - case MT_OXIMETER: - m = new Oximeter(id); - break; - case MT_POSITION: - m = new PositionSensor(id); - break; - case MT_JOURNAL: - m = new Machine(id); - m->setType(MT_JOURNAL); - break; - default: - m = new Machine(id); - - break; - } - - m->setInfo(info); - - qDebug() << "Create" << info.loadername << "Machine" << (info.serial.isEmpty() ? m->hexid() : info.serial); - - MachineList[info.loadername][info.serial] = m; - p_profile->AddMachine(m); - - return m; -} void RegisterLoader(MachineLoader *loader) @@ -177,9 +89,9 @@ MachineLoader::MachineLoader() :QObject(nullptr) MachineLoader::~MachineLoader() { - for (QList::iterator m = m_machlist.begin(); m != m_machlist.end(); m++) { - delete *m; - } +// for (QList::iterator m = m_machlist.begin(); m != m_machlist.end(); m++) { +// delete *m; +// } } void MachineLoader::finishAddingSessions() @@ -196,13 +108,13 @@ void MachineLoader::finishAddingSessions() new_sessions.clear(); - QHash >::iterator mlit = MachineList.find(loaderName()); +/* QHash >::iterator mlit = MachineList.find(loaderName()); if (mlit != MachineList.end()) { for(QHash::iterator mit = mlit.value().begin(); mit!=mlit.value().end(); ++mit) { mit.value()->SaveSummary(); } - } + } */ } diff --git a/sleepyhead/SleepLib/machine_loader.h b/sleepyhead/SleepLib/machine_loader.h index 0cd9923f..72779afd 100644 --- a/sleepyhead/SleepLib/machine_loader.h +++ b/sleepyhead/SleepLib/machine_loader.h @@ -49,9 +49,6 @@ class MachineLoader: public QObject //! \brief Override to returns the Version number of this MachineLoader virtual int Version() = 0; - static Machine * CreateMachine(MachineInfo info, MachineID id = 0); - Machine * lookupMachine(QString serial); - // !\\brief Used internally by loaders, override to return base MachineInfo record virtual MachineInfo newInfo() { return MachineInfo(); } @@ -90,8 +87,6 @@ class MachineLoader: public QObject QMutex sessionMutex; QMutex saveMutex; - void removeMachine(Machine * m); - virtual void initChannels() {} QPixmap & getPixmap(QString series) { QHash::iterator it = m_pixmaps.find(series); @@ -113,14 +108,11 @@ signals: void machineUnsupported(Machine *); protected: - //! \brief Contains a list of Machine records known by this loader - QList m_machlist; static QPixmap * genericCPAPPixmap; MachineType m_type; QString m_class; - Profile *m_profile; int m_currenttask; int m_totaltasks; diff --git a/sleepyhead/SleepLib/preferences.cpp b/sleepyhead/SleepLib/preferences.cpp index d026696c..574cbaed 100644 --- a/sleepyhead/SleepLib/preferences.cpp +++ b/sleepyhead/SleepLib/preferences.cpp @@ -1,4 +1,4 @@ -/* SleepLib Preferences Implementation +/* SleepLib Preferences Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -301,7 +301,29 @@ bool Preferences::Open(QString filename) } root = root.nextSiblingElement(); - ExtraLoad(root); + + ////////////////////////////////////////////////////////////////////////////////////// + // This is a dirty hack to clean up a legacy issue + // The old Profile system used to have machines in Profile.xml + // We need to clean up this mistake up here, because C++ polymorphism won't otherwise + // let us open properly in constructor + ////////////////////////////////////////////////////////////////////////////////////// + if ((p_name == "Profile") && (root.tagName().toLower() == "machines")) { + + // Save this sucker + QDomDocument doc("Machines"); + + doc.appendChild(root); + + QFile file(p_path+"/machines.xml"); + + // Don't do anything if machines.xml already exists.. the user ran the old version! + if (!file.exists()) { + file.open(QFile::WriteOnly); + file.write(doc.toByteArray()); + file.close(); + } + } return true; } @@ -338,8 +360,6 @@ bool Preferences::Save(QString filename) root.appendChild(cn); } - droot.appendChild(ExtraSave(doc)); - QFile file(p_filename); if (!file.open(QIODevice::WriteOnly)) { @@ -354,3 +374,5 @@ bool Preferences::Save(QString filename) } +AppWideSetting *AppSetting = nullptr; + diff --git a/sleepyhead/SleepLib/preferences.h b/sleepyhead/SleepLib/preferences.h index cd9e5c3c..423578ae 100644 --- a/sleepyhead/SleepLib/preferences.h +++ b/sleepyhead/SleepLib/preferences.h @@ -1,4 +1,4 @@ -/* SleepLib Preferences Header +/* SleepLib Preferences Header * * Copyright (c) 2011-2018 Mark Watkins * @@ -84,22 +84,15 @@ class Preferences } } - //! \brief Derive from this to handle Loading of any custom XML sections - virtual void ExtraLoad(QDomElement &root) { root = root; } - - //! \brief Derive from this to handle Saving of any custom XML sections - //! \return Must return a QDomElement to be inserted into the generated XML - virtual QDomElement ExtraSave(QDomDocument &doc) { doc = doc; QDomElement e; return e; } - //! \brief Opens, processes the XML for this Preferences group, loading all preferences stored therein. //! \note If filename is empty, it will use the one specified in the constructor //! \returns true if succesful - virtual bool Open(QString filename = ""); + bool Open(QString filename = ""); //! \brief Saves all preferences to XML file. //! \note If filename is empty, it will use the one specified in the constructor //! \returns true if succesful - virtual bool Save(QString filename = ""); + bool Save(QString filename = ""); //! \note Sets a comment string whici will be stored in the XML void SetComment(const QString &str) { @@ -137,8 +130,34 @@ class Preferences //! \brief Main Preferences Object used throughout the application extern Preferences PREF; -//! \brief Layout Preferences Object used throughout the application -extern Preferences LAYOUT; +// Parent class for subclasses that manipulate the profile. +class PrefSettings +{ + public: + PrefSettings(Preferences *pref) + : m_pref(pref) + { } + + inline void setPref(QString name, QVariant value) { + (*m_pref)[name] = value; + } + + inline void initPref(QString name, QVariant value) { + m_pref->init(name, value); + } + + inline QVariant getPref(QString name) const { + return (*m_pref)[name]; + } + + void setPrefObject(Preferences *pref) { + m_pref = pref; + } + + public: + Preferences *m_pref; +}; +#include "appsettings.h" #endif // PREFERENCES_H diff --git a/sleepyhead/SleepLib/profiles.cpp b/sleepyhead/SleepLib/profiles.cpp index 3c0121d9..f4be606a 100644 --- a/sleepyhead/SleepLib/profiles.cpp +++ b/sleepyhead/SleepLib/profiles.cpp @@ -1,4 +1,4 @@ -/* SleepLib Profiles Implementation +/* SleepLib Profiles Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -14,6 +14,8 @@ #include #include #include +#include +#include #include #include @@ -24,8 +26,8 @@ #include "machine_loader.h" -#include #include "mainwindow.h" +#include "translation.h" extern MainWindow *mainwin; Preferences *p_pref; @@ -53,37 +55,39 @@ Profile::Profile(QString path) } p_filename = p_path + p_name + STR_ext_XML; - machlist.clear(); + m_machlist.clear(); - doctor = nullptr; - user = nullptr; - cpap = nullptr; - oxi = nullptr; - appearance = nullptr; - session = nullptr; - general = nullptr; + Open(p_filename); + + Set(STR_GEN_DataFolder, QString("{home}/Profiles/{UserName}")); + + doctor = new DoctorInfo(this); + user = new UserInfo(this); + cpap = new CPAPSettings(this); + oxi = new OxiSettings(this); + appearance = new AppearanceSettings(this); + session = new SessionSettings(this); + general = new UserSettings(this); + + OpenMachines(); + m_opened=true; } Profile::~Profile() { - QString lockfile=p_path+"/lockfile"; - QFile file(lockfile); - file.remove(); + removeLock(); - if (m_opened) { - delete user; - delete doctor; - delete cpap; - delete oxi; - delete appearance; - delete session; - delete general; + delete user; + delete doctor; + delete cpap; + delete oxi; + delete appearance; + delete session; + delete general; - - for (QHash::iterator it = machlist.begin(); it != machlist.end(); it++) { - delete it.value(); - } - m_opened=false; + // delete machine objects... + for (int i=0; i::iterator d = daylist.begin(); d != daylist.end(); d++) { @@ -95,7 +99,7 @@ Profile::~Profile() bool Profile::Save(QString filename) { if (m_opened) { - return Preferences::Save(filename) && p_profile->StoreMachines(); + return Preferences::Save(filename) && StoreMachines(); } else return false; } @@ -120,34 +124,6 @@ QString Profile::checkLock() return lockhost; } -bool Profile::Load(QString filename) -{ - p_profile = this; - - if (filename.isEmpty()) { - filename=p_filename; - } - if (m_opened) { - qDebug() << "Profile" << filename << "all ready open"; - return true; - } - - bool b = Open(filename); - - this->Set(STR_GEN_DataFolder, QString("{home}/Profiles/{UserName}")); - - doctor = new DoctorInfo(this); - user = new UserInfo(this); - cpap = new CPAPSettings(this); - oxi = new OxiSettings(this); - appearance = new AppearanceSettings(this); - session = new SessionSettings(this); - general = new UserSettings(this); - - m_opened=true; - return b; -} - const QString STR_PROP_Brand = "brand"; const QString STR_PROP_Model = "model"; const QString STR_PROP_Series = "series"; @@ -157,20 +133,20 @@ const QString STR_PROP_Serial = "serial"; const QString STR_PROP_DataVersion = "dataversion"; const QString STR_PROP_LastImported = "lastimported"; -bool Profile::OpenMachines() +void Profile::addLock() { - if (m_machopened) - return true; - - if (!m_opened) { - Open(); - } QFile lockfile(p_path+"lockfile"); lockfile.open(QFile::WriteOnly); QByteArray ba; ba.append(QHostInfo::localHostName()); lockfile.write(ba); lockfile.close(); +} + +bool Profile::OpenMachines() +{ + if (m_machopened) + return true; QString filename = p_path+"machines.xml"; QFile file(filename); @@ -198,7 +174,7 @@ bool Profile::OpenMachines() QString pKey = elem.tagName(); if (pKey.toLower() != "machine") { - qWarning() << "Profile::ExtraLoad() pKey!=\"machine\""; + qWarning() << "Profile::OpenMachines() pKey!=\"machine\""; elem = elem.nextSiblingElement(); continue; } @@ -251,33 +227,31 @@ bool Profile::OpenMachines() } } - Machine *m = nullptr; - m = MachineLoader::CreateMachine(info, m_id); - //m->setId(m_id); + // Create Machine needs a profile passed to it.. + + m = CreateMachine(info, m_id); + if (m) m->properties = prop; elem = elem.nextSiblingElement(); } m_machopened = true; - return true; - } bool Profile::StoreMachines() { QDomDocument doc("Machines"); - QDomElement elem = ExtraSave(doc); - doc.appendChild(elem); QDomElement mach = doc.createElement("machines"); - for (QHash::iterator i = machlist.begin(); i != machlist.end(); i++) { + for (int i=0; iid()); me.setAttribute("type", (int)m->type()); me.setAttribute("class", m->loaderName()); @@ -285,7 +259,7 @@ bool Profile::StoreMachines() QDomElement pe = doc.createElement("properties"); me.appendChild(pe); - for (QHash::iterator j = i.value()->properties.begin(); j != i.value()->properties.end(); j++) { + for (QHash::iterator j = m->properties.begin(); j != m->properties.end(); j++) { QDomElement pp = doc.createElement(j.key()); pp.appendChild(doc.createTextNode(j.value())); pe.appendChild(pp); @@ -569,13 +543,28 @@ void Profile::DataFormatError(Machine *m) return; } +void Profile::UnloadMachineData() +{ + Q_ASSERT(m_machopened); + QMap::iterator it; + for (it = daylist.begin(); it != daylist.end(); ++it) { + delete it.value(); + } + daylist.clear(); + + for (int i=0; isessionlist.clear(); + m->day.clear(); + } + removeLock(); +} void Profile::LoadMachineData() { - if (!m_machopened) OpenMachines(); - QHash > > cache; + addLock(); - for (QHash::iterator i = machlist.begin(); i != machlist.end(); i++) { - Machine *m = i.value(); + for (int i=0; iLoad(); } } + loadChannels(); } - -/** - * @brief Upgrade Machine XML section from old "profile.xml" - * @param root - */ -void Profile::ExtraLoad(QDomElement &root) +void Profile::removeMachine(Machine * m) { - if (root.tagName().toLower() != "machines") { - // Good! - return; + m_machlist.removeAll(m); + QHash >::iterator mlit = MachineList.find(m->loaderName()); + + if (mlit != MachineList.end()) { + QHash::iterator mit = mlit.value().find(m->serial()); + if (mit != mlit.value().end()) { + mlit.value().erase(mit); + } } - // Save this sucker - QDomDocument doc("Machines"); - - doc.appendChild(root); - - QFile file(p_path+"/machines.xml"); - - // Don't do anything if machines.xml already exists.. the user ran the old version! - if (file.exists()) return; - - file.open(QFile::WriteOnly); - - file.write(doc.toByteArray()); - - file.close(); } + +Machine * Profile::lookupMachine(QString serial, QString loadername) +{ + QHash >::iterator mlit = MachineList.find(loadername); + if (mlit != MachineList.end()) { + QHash::iterator mit = mlit.value().find(serial); + if (mit != mlit.value().end()) { + return mit.value(); + } + } + return nullptr; +} + + +Machine * Profile::CreateMachine(MachineInfo info, MachineID id) +{ + + Machine *m = nullptr; + + QHash >::iterator mlit = MachineList.find(info.loadername); + + if (mlit != MachineList.end()) { + QHash::iterator mit = mlit.value().find(info.serial); + if (mit != mlit.value().end()) { + mit.value()->setInfo(info); // update info + return mit.value(); + } + } + + // Before we create, find any lost folder to get the old ID + if ((id == 0) && ((info.type == MT_OXIMETER) || (info.type == MT_JOURNAL) || (info.type == MT_POSITION)|| (info.type == MT_SLEEPSTAGE))) { + QString dataPath = Get("{" + STR_GEN_DataFolder + "}/"); + QDir dir(dataPath); + QStringList namefilter(QString(info.loadername+"_*")); + QStringList files = dir.entryList(namefilter, QDir::Dirs); + if (files.size() > 0) { + QString idstr = files[0].section("_",-1); + bool ok; + id = idstr.toInt(&ok, 16); + } + } + + switch (info.type) { + case MT_CPAP: + m = new CPAP(id); + break; + case MT_SLEEPSTAGE: + m = new SleepStage(id); + break; + case MT_OXIMETER: + m = new Oximeter(id); + break; + case MT_POSITION: + m = new PositionSensor(id); + break; + case MT_JOURNAL: + m = new Machine(id); + m->setType(MT_JOURNAL); + break; + default: + m = new Machine(id); + + break; + } + + m->setInfo(info); + + qDebug() << "Added" << info.loadername << "Machine Record" << (info.serial.isEmpty() ? m->hexid() : info.serial); + + MachineList[info.loadername][info.serial] = m; + AddMachine(m); + + return m; +} + + + void Profile::AddMachine(Machine *m) { if (!m) { qWarning() << "Empty Machine in Profile::AddMachine()"; return; } - - machlist[m->id()] = m; + m_machlist.append(m); } void Profile::DelMachine(Machine *m) @@ -641,8 +692,7 @@ void Profile::DelMachine(Machine *m) return; } - m->loader()->removeMachine(m); - machlist.erase(machlist.find(m->id())); + removeMachine(m); } Day *Profile::addDay(QDate date) @@ -788,19 +838,18 @@ MachineLoader *GetLoader(QString name) QList Profile::GetMachines(MachineType t) { QList vec; - QHash::iterator i; - QHash::iterator machlist_end=machlist.end(); - for (i = machlist.begin(); i != machlist_end; i++) { - if (!i.value()) { - qWarning() << "Profile::GetMachines() i->second == nullptr"; + for (int i=0; itype(); + MachineType mt = m->type(); if ((t == MT_UNKNOWN) || (mt == t)) { - vec.push_back(i.value()); + vec.push_back(m); } } @@ -879,14 +928,10 @@ QMap profiles; void Done() { PREF.Save(); - LAYOUT.Save(); - - p_profile->Save(); - delete p_profile; profiles.clear(); delete p_pref; - delete p_layout; + delete AppSetting; DestroyLoaders(); } @@ -910,7 +955,6 @@ Profile *Create(QString name) //path+="/"+name; p_profile = new Profile(path); - p_profile->Load(); profiles[name] = p_profile; p_profile->user->setUserName(name); //p_profile->Set("Realname",realname); @@ -945,6 +989,8 @@ void saveProfileList() QDomElement root = doc.createElement("profiles"); doc.appendChild(root); + root.appendChild(doc.createComment("This file is created during Profile Scan for cloud access convenience, it's not used by Desktop version of SleepyHead.")); + QMap::iterator it; for (it = profiles.begin(); it != profiles.end(); ++it) { @@ -963,6 +1009,35 @@ void saveProfileList() file.close(); } +int CleanupProfile(Profile *prof) +{ + // Migrate old per Profile settings that should have been put in program main preferences. + QStringList migrateList; + migrateList << STR_IS_Multithreading << STR_US_ShowPerformance << STR_US_ShowDebug + << STR_US_ScrollDampening << STR_AS_CalendarVisible << STR_IS_CacheSessions + << STR_AS_LineCursorMode << STR_AS_RightSidebarVisible << STR_AS_DailyPanelWidth + << STR_US_ShowPerformance << STR_AS_GraphHeight << STR_AS_GraphSnapshots + << STR_AS_AntiAliasing << STR_AS_LineThickness << STR_AS_UsePixmapCaching + << STR_AS_SquareWave << STR_AS_RightPanelWidth << STR_US_TooltipTimeout + << STR_AS_Animations << STR_AS_AllowYAxisScaling << STR_AS_GraphTooltips + << STR_CS_UserEventPieChart << STR_AS_OverlayType << STR_AS_OverviewLinechartMode; + + int cnt = 0; + for (int i=0; icontains(prf)) { + qDebug() << "Migrating profile preference" << prf; + PREF[prf] = (*prof)[prf]; + prof->Erase(prf); + cnt++; + } + } + if (cnt > 0) { + qDebug() << "Migrated" << cnt << "preferences for profile" << (*prof)[STR_UI_UserName]; + prof->Save(); + } + return cnt; +} /** * @brief Scan Profile directory loading user profiles @@ -986,6 +1061,8 @@ void Scan() QFileInfoList list = dir.entryInfoList(); + int cleanup = 0; + // Iterate through subdirectories and load profiles.. for (int i = 0; i < list.size(); i++) { QFileInfo fi = list.at(i); @@ -994,8 +1071,15 @@ void Scan() //prof->Open(); profiles[fi.fileName()] = prof; + + // Migrate any old settings + cleanup += CleanupProfile(prof); } + if (cleanup > 0) { + qDebug() << "Saving preferences after migration"; + PREF.Save(); + } // Update profiles.xml for mobile version saveProfileList(); } @@ -1791,11 +1875,8 @@ QDate Profile::LastGoodDay(MachineType mt) bool Profile::channelAvailable(ChannelID code) { - QHash::iterator it; - QHash::iterator machlist_end=machlist.end(); - - for (it = machlist.begin(); it != machlist_end; it++) { - Machine * mach = it.value(); + for (int i=0; ihasChannel(code)) return true; } @@ -1833,3 +1914,145 @@ bool Profile::hasChannel(ChannelID code) return found; } + +const quint16 chandata_version = 1; +void Profile::saveChannels() +{ + QString filename = Get("{DataFolder}/") + "channels.dat"; + QFile f(filename); + qDebug() << "Saving Channel States"; + f.open(QFile::WriteOnly); + QDataStream out(&f); + out.setVersion(QDataStream::Qt_4_6); + out.setByteOrder(QDataStream::LittleEndian); + + out << (quint32)magic; + out << (quint16)chandata_version; + + QSettings settings(getDeveloperName(), getAppName()); + (*p_profile)[STR_PREF_Language] = settings.value(LangSetting, "").toString(); + + quint16 size = schema::channel.channels.size(); + out << size; + + QHash::iterator it; + QHash::iterator it_end = schema::channel.channels.end(); + + for (it = schema::channel.channels.begin(); it != it_end; ++it) { + schema::Channel * chan = it.value(); + out << it.key(); + out << chan->code(); + out << chan->enabled(); + out << chan->defaultColor(); + out << chan->fullname(); + out << chan->label(); + out << chan->description(); + out << chan->lowerThreshold(); + out << chan->lowerThresholdColor(); + out << chan->upperThreshold(); + out << chan->upperThresholdColor(); + out << chan->showInOverview(); + } + + f.close(); + +} + +void Profile::loadChannels() +{ + bool changing_language = false; + + QString filename = Get("{DataFolder}/") + "channels.dat"; + QFile f(filename); + if (!f.open(QFile::ReadOnly)) { + return; + } + qDebug() << "Loading channel.dat States"; + + QDataStream in(&f); + in.setVersion(QDataStream::Qt_4_6); + in.setByteOrder(QDataStream::LittleEndian); + + quint32 mag; + in >> mag; + + if (magic != mag) { + qDebug() << "LoadChannels: Faulty data"; + return; + } + quint16 version; + in >> version; + + QSettings settings(getDeveloperName(), getAppName()); + QString language = Get(STR_PREF_Language); + if (settings.value(LangSetting, "").toString() != language) { + qDebug() << "Language change detected, resetting default channel names"; + changing_language = true; + } + + quint16 size; + in >> size; + + QString name; + ChannelID code; + bool enabled; + QColor color; + EventDataType lowerThreshold; + QColor lowerThresholdColor; + EventDataType upperThreshold; + QColor upperThresholdColor; + + QString fullname; + QString label; + QString description; + bool showOverview = false; + + for (int i=0; i < size; i++) { + in >> code; + schema::Channel * chan = &schema::channel[code]; + in >> name; + if (chan->code() != name) { + qDebug() << "Looking up channel" << name << "by name, as it's ChannedID must have changed"; + chan = &schema::channel[name]; + } + in >> enabled; + in >> color; + in >> fullname; + in >> label; + in >> description; + in >> lowerThreshold; + in >> lowerThresholdColor; + in >> upperThreshold; + in >> upperThresholdColor; + if (version >= 1) { + in >> showOverview; + } + + if (chan->isNull()) { + qDebug() << "loadChannels has no idea about channel" << name; + if (in.atEnd()) return; + continue; + } + chan->setEnabled(enabled); + chan->setDefaultColor(color); + + // Don't import channel descriptions if event renaming is turned off. (helps pick up new translations) + if (changing_language) { + // Nothing + } else { + chan->setFullname(fullname); + chan->setLabel(label); + chan->setDescription(description); + } + + chan->setLowerThreshold(lowerThreshold); + chan->setLowerThresholdColor(lowerThresholdColor); + chan->setUpperThreshold(upperThreshold); + chan->setUpperThresholdColor(upperThresholdColor); + + chan->setShowInOverview(showOverview); + if (in.atEnd()) return; + } + + f.close(); +} diff --git a/sleepyhead/SleepLib/profiles.h b/sleepyhead/SleepLib/profiles.h index 26a382d8..dbb460be 100644 --- a/sleepyhead/SleepLib/profiles.h +++ b/sleepyhead/SleepLib/profiles.h @@ -1,4 +1,4 @@ -/* SleepLib Profiles Header +/* SleepLib Profiles Header * * Copyright (c) 2011-2018 Mark Watkins * @@ -23,7 +23,6 @@ class Machine; enum Gender { GenderNotSpecified, Male, Female }; enum MaskType { Mask_Unknown, Mask_NasalPillows, Mask_Hybrid, Mask_StandardNasal, Mask_FullFace }; -enum OverviewLinechartModes { OLC_Bartop, OLC_Lines }; class DoctorInfo; class UserInfo; @@ -33,6 +32,7 @@ class CPAPSettings; class AppearanceSettings; class SessionSettings; + /*! \class Profile \author Mark Watkins @@ -47,9 +47,6 @@ class Profile : public Preferences virtual ~Profile(); - //! \brief Open profile, parse profile.xml file, and initialize helper classes - virtual bool Load(QString filename = ""); - //! \brief Parse machines.xml bool OpenMachines(); bool StoreMachines(); @@ -60,6 +57,8 @@ class Profile : public Preferences //! \brief Removes a lockfile bool removeLock(); + void addLock(); + //! \brief Save Profile object (This is an extension to Preference::Save(..)) virtual bool Save(QString filename = ""); @@ -72,6 +71,9 @@ class Profile : public Preferences //! \brief Loads all machine (summary) data belonging to this profile void LoadMachineData(); + //! \brief Unloads all machine (summary) data for this profile to free up memory; + void UnloadMachineData(); + //! \brief Barf because data format has changed. This does a purge of CPAP data for machine *m void DataFormatError(Machine *m); @@ -182,8 +184,6 @@ class Profile : public Preferences Day * findSessionDay(Session * session); - // XML load components - virtual void ExtraLoad(QDomElement &root); //! \brief Looks for the first date containing a day record matching machinetype QDate FirstDay(MachineType mt = MT_UNKNOWN); @@ -206,8 +206,13 @@ class Profile : public Preferences //! \brief QMap of day records (iterates in order). QMap daylist; - //! \brief List of machines, indexed by MachineID. - QHash machlist; + void removeMachine(Machine *); + Machine * lookupMachine(QString serial, QString loadername); + Machine * CreateMachine(MachineInfo info, MachineID id = 0); + + void loadChannels(); + void saveChannels(); + bool is_first_day; @@ -218,6 +223,7 @@ class Profile : public Preferences AppearanceSettings *appearance; UserSettings *general; SessionSettings *session; + QList m_machlist; protected: QDate m_first; @@ -225,18 +231,37 @@ class Profile : public Preferences bool m_opened; bool m_machopened; + + QHash > MachineList; + }; class MachineLoader; extern MachineLoader *GetLoader(QString name); extern Preferences *p_pref; -extern Preferences *p_layout; extern Profile *p_profile; // these are bad and must change #define PREF (*p_pref) -#define LAYOUT (*p_layout) + + +//! \brief Returns a count of all files & directories in a supplied folder +int dirCount(QString path); + + +namespace Profiles { + +extern QMap profiles; +void Scan(); // Initialize and load Profile +void Done(); // Save all Profile objects and clear list +int CleanupProfile(Profile *prof); + +Profile *Create(QString name); +Profile *Get(QString name); +Profile *Get(); + +} // DoctorInfo Strings const QString STR_DI_Name = "DoctorName"; @@ -288,7 +313,6 @@ const QString STR_CS_UntreatedAHI = "UntreatedAHI"; const QString STR_CS_Notes = "CPAPNotes"; const QString STR_CS_DateDiagnosed = "DateDiagnosed"; const QString STR_CS_UserEventFlagging = "UserEventFlagging"; -const QString STR_CS_UserEventPieChart = "UserEventPieChart"; const QString STR_CS_AutoImport = "AutoImport"; const QString STR_CS_BrickWarning = "BrickWarning"; @@ -312,10 +336,8 @@ const QString STR_CS_20cmH2OLeaks = "Custom20cmH2OLeaks"; // ImportSettings Strings const QString STR_IS_DaySplitTime = "DaySplitTime"; const QString STR_IS_PreloadSummaries = "PreloadSummaries"; -const QString STR_IS_CacheSessions = "MemoryHog"; const QString STR_IS_CombineCloseSessions = "CombineCloserSessions"; const QString STR_IS_IgnoreShorterSessions = "IgnoreShorterSessions"; -const QString STR_IS_Multithreading = "EnableMultithreading"; const QString STR_IS_BackupCardData = "BackupCardData"; const QString STR_IS_CompressBackupData = "CompressBackupData"; const QString STR_IS_CompressSessionData = "CompressSessionData"; @@ -323,76 +345,26 @@ const QString STR_IS_IgnoreOlderSessions = "IgnoreOlderSessions"; const QString STR_IS_IgnoreOlderSessionsDate = "IgnoreOlderSessionsDate"; const QString STR_IS_LockSummarySessions = "LockSummarySessions"; -// AppearanceSettings Strings -const QString STR_AS_GraphHeight = "GraphHeight"; -const QString STR_AS_DailyPanelWidth = "DailyPanelWidth"; -const QString STR_AS_RightPanelWidth = "RightPanelWidth"; -const QString STR_AS_AntiAliasing = "UseAntiAliasing"; -const QString STR_AS_GraphSnapshots = "EnableGraphSnapshots"; -const QString STR_AS_Animations = "AnimationsAndTransitions"; -const QString STR_AS_SquareWave = "SquareWavePlots"; -const QString STR_AS_OverlayType = "OverlayType"; -const QString STR_AS_OverviewLinechartMode = "OverviewLinechartMode"; -const QString STR_AS_UsePixmapCaching = "UsePixmapCaching"; -const QString STR_AS_AllowYAxisScaling = "AllowYAxisScaling"; -const QString STR_AS_GraphTooltips = "GraphTooltips"; -const QString STR_AS_LineThickness = "LineThickness"; -const QString STR_AS_LineCursorMode = "LineCursorMode"; -const QString STR_AS_CalendarVisible = "CalendarVisible"; -const QString STR_AS_RightSidebarVisible = "RightSidebarVisible"; // UserSettings Strings const QString STR_US_UnitSystem = "UnitSystem"; const QString STR_US_EventWindowSize = "EventWindowSize"; const QString STR_US_SkipEmptyDays = "SkipEmptyDays"; const QString STR_US_RebuildCache = "RebuildCache"; -const QString STR_US_ShowDebug = "ShowDebug"; -const QString STR_US_ShowPerformance = "ShowPerformance"; const QString STR_US_LinkGroups = "LinkGroups"; const QString STR_US_CalculateRDI = "CalculateRDI"; -const QString STR_US_ShowSerialNumbers = "ShowSerialNumbers"; const QString STR_US_PrefCalcMiddle = "PrefCalcMiddle"; const QString STR_US_PrefCalcPercentile = "PrefCalcPercentile"; const QString STR_US_PrefCalcMax = "PrefCalcMax"; -const QString STR_US_TooltipTimeout = "TooltipTimeout"; -const QString STR_US_ScrollDampening = "ScrollDampening"; const QString STR_US_ShowUnknownFlags = "ShowUnknownFlags"; const QString STR_US_StatReportMode = "StatReportMode"; const QString STR_US_LastOverviewRange = "LastOverviewRange"; -// Parent class for subclasses that manipulate the profile. -class ProfileSettings -{ - public: - ProfileSettings(Profile *profile) - : m_profile(profile) - { } - - inline void setPref(QString name, QVariant value) { - (*m_profile)[name] = value; - } - - inline void initPref(QString name, QVariant value) { - m_profile->init(name, value); - } - - inline QVariant getPref(QString name) const { - return (*m_profile)[name]; - } - - void setProfile(Profile *profile) { - m_profile = profile; - } - - public: - Profile *m_profile; -}; - -class DoctorInfo : public ProfileSettings +class DoctorInfo : public PrefSettings { public: DoctorInfo(Profile *profile) - : ProfileSettings(profile) + : PrefSettings(profile) { initPref(STR_DI_Name, QString()); initPref(STR_DI_Phone, QString()); @@ -421,11 +393,11 @@ class DoctorInfo : public ProfileSettings /*! \class UserInfo \brief Profile Options relating to the User Information */ -class UserInfo : public ProfileSettings +class UserInfo : public PrefSettings { public: UserInfo(Profile *profile) - : ProfileSettings(profile) + : PrefSettings(profile) { initPref(STR_UI_DOB, QDate(1970, 1, 1)); initPref(STR_UI_FirstName, QString()); @@ -486,12 +458,12 @@ class UserInfo : public ProfileSettings /*! \class OxiSettings \brief Profile Options relating to the Oximetry settings */ -class OxiSettings : public ProfileSettings +class OxiSettings : public PrefSettings { public: //! \brief Create OxiSettings object given Profile *p, and initialize the defaults OxiSettings(Profile *profile) - : ProfileSettings(profile) + : PrefSettings(profile) { initPref(STR_OS_EnableOximetry, false); initPref(STR_OS_DefaultDevice, QString()); @@ -536,11 +508,11 @@ class OxiSettings : public ProfileSettings /*! \class CPAPSettings \brief Profile Options relating to the CPAP settings */ -class CPAPSettings : public ProfileSettings +class CPAPSettings : public PrefSettings { public: CPAPSettings(Profile *profile) - : ProfileSettings(profile) + : PrefSettings(profile) { initPref(STR_CS_ComplianceHours, 4); initPref(STR_CS_ShowCompliance, true); @@ -565,7 +537,6 @@ class CPAPSettings : public ProfileSettings initPref(STR_CS_AHIReset, false); initPref(STR_CS_LeakRedline, 24.0); initPref(STR_CS_ShowLeakRedline, true); - initPref(STR_CS_UserEventPieChart, false); initPref(STR_CS_ResyncFromUserFlagging, false); initPref(STR_CS_AutoImport, false); initPref(STR_CS_BrickWarning, true); @@ -602,7 +573,6 @@ class CPAPSettings : public ProfileSettings int clockDrift() const { return m_clock_drift; } EventDataType leakRedline() const { return getPref(STR_CS_LeakRedline).toFloat(); } bool showLeakRedline() const { return getPref(STR_CS_ShowLeakRedline).toBool(); } - bool userEventPieChart() const { return getPref(STR_CS_UserEventPieChart).toBool(); } bool resyncFromUserFlagging() const { return getPref(STR_CS_ResyncFromUserFlagging).toBool(); } bool autoImport() const { return getPref(STR_CS_AutoImport).toBool(); } bool brickWarning() const { return getPref(STR_CS_BrickWarning).toBool(); } @@ -639,7 +609,6 @@ class CPAPSettings : public ProfileSettings } void setLeakRedline(EventDataType value) { setPref(STR_CS_LeakRedline, value); } void setShowLeakRedline(bool reset) { setPref(STR_CS_ShowLeakRedline, reset); } - void setUserEventPieChart(bool b) { setPref(STR_CS_UserEventPieChart, b); } void setResyncFromUserFlagging(bool b) { setPref(STR_CS_ResyncFromUserFlagging, b); } void setAutoImport(bool b) { setPref(STR_CS_AutoImport, b); } void setBrickWarning(bool b) { setPref(STR_CS_BrickWarning, b); } @@ -655,18 +624,16 @@ class CPAPSettings : public ProfileSettings /*! \class ImportSettings \brief Profile Options relating to the Import process */ -class SessionSettings : public ProfileSettings +class SessionSettings : public PrefSettings { public: SessionSettings(Profile *profile) - : ProfileSettings(profile) + : PrefSettings(profile) { initPref(STR_IS_DaySplitTime, QTime(12, 0, 0)); - initPref(STR_IS_CacheSessions, false); initPref(STR_IS_PreloadSummaries, false); initPref(STR_IS_CombineCloseSessions, 240); initPref(STR_IS_IgnoreShorterSessions, 5); - initPref(STR_IS_Multithreading, QThread::idealThreadCount() > 1); initPref(STR_IS_BackupCardData, true); initPref(STR_IS_CompressBackupData, false); initPref(STR_IS_CompressSessionData, false); @@ -677,11 +644,9 @@ class SessionSettings : public ProfileSettings } QTime daySplitTime() const { return getPref(STR_IS_DaySplitTime).toTime(); } - bool cacheSessions() const { return getPref(STR_IS_CacheSessions).toBool(); } bool preloadSummaries() const { return getPref(STR_IS_PreloadSummaries).toBool(); } double combineCloseSessions() const { return getPref(STR_IS_CombineCloseSessions).toDouble(); } double ignoreShortSessions() const { return getPref(STR_IS_IgnoreShorterSessions).toDouble(); } - bool multithreading() const { return getPref(STR_IS_Multithreading).toBool(); } bool compressSessionData() const { return getPref(STR_IS_CompressSessionData).toBool(); } bool compressBackupData() const { return getPref(STR_IS_CompressBackupData).toBool(); } bool backupCardData() const { return getPref(STR_IS_BackupCardData).toBool(); } @@ -690,11 +655,9 @@ class SessionSettings : public ProfileSettings bool lockSummarySessions() const { return getPref(STR_IS_LockSummarySessions).toBool(); } void setDaySplitTime(QTime time) { setPref(STR_IS_DaySplitTime, time); } - void setCacheSessions(bool c) { setPref(STR_IS_CacheSessions, c); } void setPreloadSummaries(bool b) { setPref(STR_IS_PreloadSummaries, b); } void setCombineCloseSessions(double val) { setPref(STR_IS_CombineCloseSessions, val); } void setIgnoreShortSessions(double val) { setPref(STR_IS_IgnoreShorterSessions, val); } - void setMultithreading(bool enabled) { setPref(STR_IS_Multithreading, enabled); } void setBackupCardData(bool enabled) { setPref(STR_IS_BackupCardData, enabled); } void setCompressBackupData(bool enabled) { setPref(STR_IS_CompressBackupData, enabled); } void setCompressSessionData(bool enabled) { setPref(STR_IS_CompressSessionData, enabled); } @@ -707,128 +670,38 @@ class SessionSettings : public ProfileSettings /*! \class AppearanceSettings \brief Profile Options relating to Visual Appearance */ -class AppearanceSettings : public ProfileSettings +class AppearanceSettings : public PrefSettings { public: //! \brief Create AppearanceSettings object given Profile *p, and initialize the defaults AppearanceSettings(Profile *profile) - : ProfileSettings(profile) + : PrefSettings(profile) { - initPref(STR_AS_GraphHeight, 180.0); - initPref(STR_AS_DailyPanelWidth, 350.0); - initPref(STR_AS_RightPanelWidth, 230.0); - initPref(STR_AS_AntiAliasing, true); - initPref(STR_AS_GraphSnapshots, true); - initPref(STR_AS_Animations, true); - initPref(STR_AS_SquareWave, false); - initPref(STR_AS_AllowYAxisScaling, true); - initPref(STR_AS_GraphTooltips, true); - initPref(STR_AS_UsePixmapCaching, false); - initPref(STR_AS_OverlayType, ODT_Bars); - initPref(STR_AS_OverviewLinechartMode, OLC_Bartop); - initPref(STR_AS_LineThickness, 1.0); - initPref(STR_AS_LineCursorMode, true); - initPref(STR_AS_CalendarVisible, true); - initPref(STR_AS_RightSidebarVisible, true); + } - //! \brief Returns the normal (unscaled) height of a graph - int graphHeight() const { return getPref(STR_AS_GraphHeight).toInt(); } - //! \brief Returns the normal (unscaled) height of a graph - int dailyPanelWidth() const { return getPref(STR_AS_DailyPanelWidth).toInt(); } - //! \brief Returns the normal (unscaled) height of a graph - int rightPanelWidth() const { return getPref(STR_AS_RightPanelWidth).toInt(); } - //! \brief Returns true if AntiAliasing (the graphical smoothing method) is enabled - bool antiAliasing() const { return getPref(STR_AS_AntiAliasing).toBool(); } - //! \brief Returns true if renderPixmap function is in use, which takes snapshots of graphs - bool graphSnapshots() const { return getPref(STR_AS_GraphSnapshots).toBool(); } - //! \brief Returns true if Graphical animations & Transitions will be drawn - bool animations() const { return getPref(STR_AS_Animations).toBool(); } - //! \brief Returns true if PixmapCaching acceleration will be used - bool usePixmapCaching() const { return getPref(STR_AS_UsePixmapCaching).toBool(); } - //! \brief Returns true if Square Wave plots are preferred (where possible) - bool squareWavePlots() const { return getPref(STR_AS_SquareWave).toBool(); } - //! \brief Whether to allow double clicking on Y-Axis labels to change vertical scaling mode - bool allowYAxisScaling() const { return getPref(STR_AS_AllowYAxisScaling).toBool(); } - //! \brief Whether to show graph tooltips - bool graphTooltips() const { return getPref(STR_AS_GraphTooltips).toBool(); } - //! \brief Pen width of line plots - float lineThickness() const { return getPref(STR_AS_LineThickness).toFloat(); } - //! \brief Whether to show line cursor - bool lineCursorMode() const { return getPref(STR_AS_LineCursorMode).toBool(); } - //! \brief Whether to show the calendar - bool calendarVisible() const { return getPref(STR_AS_CalendarVisible).toBool(); } - //! \brief Whether to show the right sidebar - bool rightSidebarVisible() const { return getPref(STR_AS_RightSidebarVisible).toBool(); } - //! \brief Returns the type of overlay flags (which are displayed over the Flow Waveform) - OverlayDisplayType overlayType() const { - return (OverlayDisplayType)getPref(STR_AS_OverlayType).toInt(); - } - //! \brief Returns the display type of Overview pages linechart - OverviewLinechartModes overviewLinechartMode() const { - return (OverviewLinechartModes)getPref(STR_AS_OverviewLinechartMode).toInt(); - } - //! \brief Set the normal (unscaled) height of a graph. - void setGraphHeight(int height) { setPref(STR_AS_GraphHeight, height); } - //! \brief Set the normal (unscaled) height of a graph. - void setDailyPanelWidth(int width) { setPref(STR_AS_DailyPanelWidth, width); } - //! \brief Set the normal (unscaled) height of a graph. - void setRightPanelWidth(int width) { setPref(STR_AS_RightPanelWidth, width); } - //! \brief Set to true to turn on AntiAliasing (the graphical smoothing method) - void setAntiAliasing(bool aa) { setPref(STR_AS_AntiAliasing, aa); } - //! \brief Set to true if renderPixmap functions are in use, which takes snapshots of graphs. - void setGraphSnapshots(bool gs) { setPref(STR_AS_GraphSnapshots, gs); } - //! \brief Set to true if Graphical animations & Transitions will be drawn - void setAnimations(bool anim) { setPref(STR_AS_Animations, anim); } - //! \brief Set to true to use Pixmap Caching of Text and other graphics caching speedup techniques - void setUsePixmapCaching(bool b) { setPref(STR_AS_UsePixmapCaching, b); } - //! \brief Set whether or not to useSquare Wave plots (where possible) - void setSquareWavePlots(bool sw) { setPref(STR_AS_SquareWave, sw); } - //! \brief Sets the type of overlay flags (which are displayed over the Flow Waveform) - void setOverlayType(OverlayDisplayType od) { setPref(STR_AS_OverlayType, (int)od); } - //! \brief Sets whether to allow double clicking on Y-Axis labels to change vertical scaling mode - void setAllowYAxisScaling(bool b) { setPref(STR_AS_AllowYAxisScaling, b); } - //! \brief Sets whether to allow double clicking on Y-Axis labels to change vertical scaling mode - void setGraphTooltips(bool b) { setPref(STR_AS_GraphTooltips, b); } - //! \brief Sets the type of overlay flags (which are displayed over the Flow Waveform) - void setOverviewLinechartMode(OverviewLinechartModes od) { - setPref(STR_AS_OverviewLinechartMode, (int)od); - } - //! \brief Set the pen width of line plots. - void setLineThickness(float size) { setPref(STR_AS_LineThickness, size); } - //! \brief Sets whether to display Line Cursor - void setLineCursorMode(bool b) { setPref(STR_AS_LineCursorMode, b); } - //! \brief Sets whether to display the (Daily View) Calendar - void setCalendarVisible(bool b) { setPref(STR_AS_CalendarVisible, b); } - //! \brief Sets whether to display the right sidebar - void setRightSidebarVisible(bool b) { setPref(STR_AS_RightSidebarVisible, b); } }; /*! \class UserSettings \brief Profile Options relating to General User Settings */ -class UserSettings : public ProfileSettings +class UserSettings : public PrefSettings { public: UserSettings(Profile *profile) - : ProfileSettings(profile) + : PrefSettings(profile) { initPref(STR_US_UnitSystem, US_Metric); initPref(STR_US_EventWindowSize, 4.0); initPref(STR_US_SkipEmptyDays, true); initPref(STR_US_RebuildCache, false); // FIXME: jedimark: can't remember... - initPref(STR_US_ShowDebug, false); - initPref(STR_US_ShowPerformance, false); initPref(STR_US_CalculateRDI, false); - initPref(STR_US_ShowSerialNumbers, false); initPref(STR_US_PrefCalcMiddle, (int)0); initPref(STR_US_PrefCalcPercentile, (double)95.0); initPref(STR_US_PrefCalcMax, (int)0); - initPref(STR_US_TooltipTimeout, (int)2500); - initPref(STR_US_ScrollDampening, (int)50); initPref(STR_US_StatReportMode, 0); initPref(STR_US_ShowUnknownFlags, false); initPref(STR_US_LastOverviewRange, 4); @@ -838,15 +711,10 @@ class UserSettings : public ProfileSettings double eventWindowSize() const { return getPref(STR_US_EventWindowSize).toDouble(); } bool skipEmptyDays() const { return getPref(STR_US_SkipEmptyDays).toBool(); } bool rebuildCache() const { return getPref(STR_US_RebuildCache).toBool(); } - bool showDebug() const { return getPref(STR_US_ShowDebug).toBool(); } - bool showPerformance() const { return getPref(STR_US_ShowPerformance).toBool(); } bool calculateRDI() const { return getPref(STR_US_CalculateRDI).toBool(); } - bool showSerialNumbers() const { return getPref(STR_US_ShowSerialNumbers).toBool(); } int prefCalcMiddle() const { return getPref(STR_US_PrefCalcMiddle).toInt(); } double prefCalcPercentile() const { return getPref(STR_US_PrefCalcPercentile).toDouble(); } int prefCalcMax() const { return getPref(STR_US_PrefCalcMax).toInt(); } - int tooltipTimeout() const { return getPref(STR_US_TooltipTimeout).toInt(); } - int scrollDampening() const { return getPref(STR_US_ScrollDampening).toInt(); } int statReportMode() const { return getPref(STR_US_StatReportMode).toInt(); } bool showUnknownFlags() const { return getPref(STR_US_ShowUnknownFlags).toBool(); } int lastOverviewRange() const { return getPref(STR_US_LastOverviewRange).toInt(); } @@ -855,35 +723,15 @@ class UserSettings : public ProfileSettings void setEventWindowSize(double size) { setPref(STR_US_EventWindowSize, size); } void setSkipEmptyDays(bool skip) { setPref(STR_US_SkipEmptyDays, skip); } void setRebuildCache(bool rebuild) { setPref(STR_US_RebuildCache, rebuild); } - void setShowDebug(bool b) { setPref(STR_US_ShowDebug, b); } - void setShowPerformance(bool b) { setPref(STR_US_ShowPerformance, b); } void setCalculateRDI(bool rdi) { setPref(STR_US_CalculateRDI, rdi); } - void setShowSerialNumbers(bool enabled) { setPref(STR_US_ShowSerialNumbers, enabled); } void setPrefCalcMiddle(int i) { setPref(STR_US_PrefCalcMiddle, i); } void setPrefCalcPercentile(double p) { setPref(STR_US_PrefCalcPercentile, p); } void setPrefCalcMax(int i) { setPref(STR_US_PrefCalcMax, i); } - void setTooltipTimeout(int i) { setPref(STR_US_TooltipTimeout, i); } - void setScrollDampening(int i) { setPref(STR_US_ScrollDampening, i); } void setStatReportMode(int i) { setPref(STR_US_StatReportMode, i); } void setShowUnknownFlags(bool b) { setPref(STR_US_ShowUnknownFlags, b); } void setLastOverviewRange(int i) { setPref(STR_US_LastOverviewRange, i); } }; -//! \brief Returns a count of all files & directories in a supplied folder -int dirCount(QString path); - - -namespace Profiles { - -extern QMap profiles; -void Scan(); // Initialize and load Profile -void Done(); // Save all Profile objects and clear list - -Profile *Create(QString name); -Profile *Get(QString name); -Profile *Get(); - -} #endif // PROFILES_H diff --git a/sleepyhead/SleepLib/schema.cpp b/sleepyhead/SleepLib/schema.cpp index 9ca8d6d0..e59a81eb 100644 --- a/sleepyhead/SleepLib/schema.cpp +++ b/sleepyhead/SleepLib/schema.cpp @@ -1018,6 +1018,9 @@ void ChannelList::add(QString group, Channel *chan) bool ChannelList::Save(QString filename) { + if (!p_profile) + return false; + qDebug() << "Saving Channels.xml"; if (filename.isEmpty()) { filename = p_profile->Get("{DataFolder}/") + "channels.xml"; diff --git a/sleepyhead/UpdaterWindow.cpp b/sleepyhead/UpdaterWindow.cpp index e2f4ae89..c480af10 100644 --- a/sleepyhead/UpdaterWindow.cpp +++ b/sleepyhead/UpdaterWindow.cpp @@ -279,11 +279,12 @@ int checkVersionStatus(QString statusstr) return v; } - if (statusstr.compare("testing", Qt::CaseInsensitive) == 0) return 0; - else if (statusstr.compare("beta", Qt::CaseInsensitive) == 0) return 1; - else if (statusstr.compare("rc", Qt::CaseInsensitive) == 0) return 2; - else if (statusstr.compare("r", Qt::CaseInsensitive) == 0) return 3; + if ((statusstr.compare("testing", Qt::CaseInsensitive) == 0) || (statusstr.compare("unstable", Qt::CaseInsensitive) == 0)) return 0; + else if ((statusstr.compare("beta", Qt::CaseInsensitive) == 0) || (statusstr.compare("untamed", Qt::CaseInsensitive) == 0)) return 1; + else if ((statusstr.compare("rc", Qt::CaseInsensitive) == 0) || (statusstr.compare("almost", Qt::CaseInsensitive) == 0)) return 2; + else if ((statusstr.compare("r", Qt::CaseInsensitive) == 0) || (statusstr.compare("stable", Qt::CaseInsensitive) == 0)) return 3; + // anything else is considered a test build return 0; } struct VersionStruct { diff --git a/sleepyhead/build_number.h b/sleepyhead/build_number.h index 6025edac..aaaf5891 100644 --- a/sleepyhead/build_number.h +++ b/sleepyhead/build_number.h @@ -1 +1 @@ -const int build_number = 2; +const int build_number = 0; diff --git a/sleepyhead/common_gui.cpp b/sleepyhead/common_gui.cpp index 633b2d0d..de2b698d 100644 --- a/sleepyhead/common_gui.cpp +++ b/sleepyhead/common_gui.cpp @@ -1,4 +1,4 @@ -/* Common GUI Functions Implementation +/* Common GUI Functions Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -6,12 +6,87 @@ * License. See the file COPYING in the main directory of the Linux * distribution for more details. */ +#include +#include +#include + +#include "SleepLib/common.h" #include "common_gui.h" #ifndef BUILD_WITH_MSVC # include #endif +QString getOpenGLVersionString() +{ + static QString glversion; + + if (glversion.isEmpty()) { + QGLWidget w; + w.makeCurrent(); + +#if QT_VERSION < QT_VERSION_CHECK(5,4,0) + glversion = QString(QLatin1String(reinterpret_cast(glGetString(GL_VERSION)))); +#else + QOpenGLFunctions f; + f.initializeOpenGLFunctions(); + glversion = QString(QLatin1String(reinterpret_cast(f.glGetString(GL_VERSION)))); +#endif + qDebug() << "OpenGL Version:" << glversion; + } + return glversion; +} + +float getOpenGLVersion() +{ + QString glversion = getOpenGLVersionString(); + glversion = glversion.section(" ",0,0); + bool ok; + float v = glversion.toFloat(&ok); + + if (!ok) { + QString tmp = glversion.section(".",0,1); + v = tmp.toFloat(&ok); + if (!ok) { + // just look at major, we are only interested in whether we have OpenGL 2.0 anyway + tmp = glversion.section(".",0,0); + v = tmp.toFloat(&ok); + } + } + return v; +} + +QString getGraphicsEngine() +{ + QString gfxEngine = QString(); +#ifdef BROKEN_OPENGL_BUILD + gfxEngine = CSTR_GFX_BrokenGL; +#else + QString glversion = getOpenGLVersionString(); + if (glversion.contains(CSTR_GFX_ANGLE)) { + gfxEngine = CSTR_GFX_ANGLE; + } else { + gfxEngine = CSTR_GFX_OpenGL; + } +#endif + return gfxEngine; +} +QString getBranchVersion() +{ + QString version = STR_TR_AppVersion; + version += " ["; +#ifdef GIT_REVISION + if (QString(GIT_BRANCH) != "master") { + version += QString(GIT_BRANCH)+"-"; + } + version += QString(GIT_REVISION) +" "; +#endif + version += getGraphicsEngine()+"]"; + + return version; +} + + #if (QT_VERSION >= QT_VERSION_CHECK(4,8,0)) // Qt 4.8 makes this a whole lot easier Qt::DayOfWeek firstDayOfWeekFromLocale() diff --git a/sleepyhead/common_gui.h b/sleepyhead/common_gui.h index f1a7dcb4..ea04b6e0 100644 --- a/sleepyhead/common_gui.h +++ b/sleepyhead/common_gui.h @@ -1,4 +1,4 @@ -/* Common GUI Functions Header +/* Common GUI Functions Header * * Copyright (C) 2011-2018 Mark Watkins * @@ -15,6 +15,10 @@ //! \brief Gets the first day of week from the system locale, to show in the calendars. Qt::DayOfWeek firstDayOfWeekFromLocale(); + +QString getBranchVersion(); + + const QString CSTR_GFX_ANGLE = "ANGLE"; const QString CSTR_GFX_OpenGL = "OpenGL"; const QString CSTR_GFX_BrokenGL = "BrokenGL"; diff --git a/sleepyhead/daily.cpp b/sleepyhead/daily.cpp index 8ce2aa20..5c8e5425 100644 --- a/sleepyhead/daily.cpp +++ b/sleepyhead/daily.cpp @@ -59,7 +59,8 @@ void Daily::setSidebarVisible(bool visible) { QList a; - int panel_width = visible ? p_profile->appearance->dailyPanelWidth() : 0; + + int panel_width = visible ? AppSetting->dailyPanelWidth() : 0; a.push_back(panel_width); a.push_back(this->width() - panel_width); ui->splitter_2->setStretchFactor(1,1); @@ -147,7 +148,7 @@ Daily::Daily(QWidget *parent,gGraphView * shared) layout->addWidget(GraphView,1); layout->addWidget(scrollbar,0); - int default_height = p_profile->appearance->graphHeight(); + int default_height = AppSetting->graphHeight(); gGraph *GAHI = nullptr, // *TAP = nullptr, @@ -208,7 +209,7 @@ Daily::Daily(QWidget *parent,gGraphView * shared) evseg->AddSlice(CPAP_NRI,QColor(0x00,0x80,0x40,0xff),STR_TR_NR); evseg->AddSlice(CPAP_FlowLimit,QColor(0x40,0x40,0x40,0xff),STR_TR_FL); evseg->AddSlice(CPAP_SensAwake,QColor(0x40,0xC0,0x40,0xff),STR_TR_SA); - if (p_profile->cpap->userEventPieChart()) { + if (AppSetting->userEventPieChart()) { evseg->AddSlice(CPAP_UserFlag1,QColor(0xe0,0xe0,0xe0,0xff),tr("UF1")); evseg->AddSlice(CPAP_UserFlag2,QColor(0xc0,0xc0,0xe0,0xff),tr("UF2")); } @@ -257,7 +258,7 @@ Daily::Daily(QWidget *parent,gGraphView * shared) // FRW->AddLayer(AddOXI(new gLineOverlayBar(OXI_SPO2Drop, COLOR_SPO2Drop, STR_TR_O2))); - bool square=p_profile->appearance->squareWavePlots(); + bool square=AppSetting->squareWavePlots(); gLineChart *pc=new gLineChart(CPAP_Pressure, square); graphlist[schema::channel[CPAP_Pressure].code()]->AddLayer(pc); @@ -404,8 +405,8 @@ Daily::Daily(QWidget *parent,gGraphView * shared) GraphView->setEmptyText(STR_Empty_NoData); previous_date=QDate(); - ui->calButton->setChecked(p_profile->appearance->calendarVisible() ? Qt::Checked : Qt::Unchecked); - on_calButton_toggled(p_profile->appearance->calendarVisible()); + ui->calButton->setChecked(AppSetting->calendarVisible() ? Qt::Checked : Qt::Unchecked); + on_calButton_toggled(AppSetting->calendarVisible()); GraphView->resetLayout(); GraphView->LoadSettings("Daily"); @@ -586,13 +587,13 @@ void Daily::ReloadGraphs() void Daily::hideSpaceHogs() { - if (p_profile->appearance->calendarVisible()) { + if (AppSetting->calendarVisible()) { ui->calendarFrame->setVisible(false); } } void Daily::showSpaceHogs() { - if (p_profile->appearance->calendarVisible()) { + if (AppSetting->calendarVisible()) { ui->calendarFrame->setVisible(true); } } @@ -1365,7 +1366,7 @@ void Daily::Load(QDate date) posit = day->machine(MT_POSITION); } - if (!p_profile->session->cacheSessions()) { + if (!AppSetting->cacheSessions()) { // Getting trashed on purge last day... // lastcpapday can get purged and be invalid @@ -1573,7 +1574,7 @@ void Daily::Load(QDate date) html+="\n"; // Show Event Breakdown pie chart - if ((hours > 0) && p_profile->appearance->graphSnapshots()) { // AHI Pie Chart + if ((hours > 0) && AppSetting->graphSnapshots()) { // AHI Pie Chart if ((values[CPAP_Obstructive] + values[CPAP_Hypopnea] + values[CPAP_ClearAirway] + values[CPAP_Apnea] + values[CPAP_RERA] + values[CPAP_FlowLimit] + values[CPAP_SensAwake])>0) { html+=""; html+=QString("").arg(tr("Event Breakdown")); @@ -2126,7 +2127,7 @@ void Daily::on_calButton_toggled(bool checked) { bool b=checked; ui->calendarFrame->setVisible(b); - p_profile->appearance->setCalendarVisible(b); + AppSetting->setCalendarVisible(b); if (!b) { ui->calButton->setArrowType(Qt::DownArrow); @@ -2571,5 +2572,5 @@ void Daily::on_splitter_2_splitterMoved(int, int) { int size = ui->splitter_2->sizes()[0]; if (size == 0) return; - p_profile->appearance->setDailyPanelWidth(size); + AppSetting->setDailyPanelWidth(size); } diff --git a/sleepyhead/docs/release_notes.html b/sleepyhead/docs/release_notes.html index 6d5f88b4..fcb8be77 100644 --- a/sleepyhead/docs/release_notes.html +++ b/sleepyhead/docs/release_notes.html @@ -1,4 +1,19 @@ - + +Changes and fixes in v1.1.0-unstable-0 + + +
  • Added brand new live Profile switcher
  • +
  • [DeVilbiss] Added DV6 Import capability (Thanks Heynes and Brian)
  • +
  • [PR Dreamstation] Added spuport for 960T F5V3 machines
  • +
  • Fix language change glitch affecting Channel names
  • +
  • Fix a few miscellanious crash conditions
  • +
  • Cleaned up Welcome page removing what could be constituted as dodgy advice
  • +
  • Added Afrikaans language support
  • +
  • Applied a bunch of bugfixes that were provided by the community (Thanks Pholy, h-uchiy, François Revol & Kevin Lewis)
  • +
    +
    +
    + Changes and fixes in v1.0.0-beta-2 diff --git a/sleepyhead/icons/dv64.png b/sleepyhead/icons/dv64.png new file mode 100644 index 00000000..702c379f Binary files /dev/null and b/sleepyhead/icons/dv64.png differ diff --git a/sleepyhead/main.cpp b/sleepyhead/main.cpp index 8256dbc8..91ed5a59 100644 --- a/sleepyhead/main.cpp +++ b/sleepyhead/main.cpp @@ -1,4 +1,4 @@ -/* SleepyHead Main +/* SleepyHead Main * * Copyright (c) 2011-2018 Mark Watkins * @@ -101,7 +101,7 @@ void release_notes() QString html = "" - "" + //"" ""; //"

    "+QObject::tr("Greetings!")+"

    "; @@ -119,8 +119,6 @@ void release_notes() html += changeLog; html += ""; - - //QUrl("qrc:/docs/release_notes.html") // Should read these from online!!! with language code @@ -159,7 +157,6 @@ void MigrateSettings() QSettings oldcopy(getDeveloperName(), getAppName()+"-Testing"); if (oldcopy.contains("Migrated")) { return; } - //QString oldfile = oldcopy.fileName(); QStringList keys = oldcopy.allKeys(); @@ -186,7 +183,7 @@ int main(int argc, char *argv[]) QGL::setPreferredPaintEngine(QPaintEngine::OpenGL); #endif - bool force_login_screen = false; + bool dont_load_profile = false; bool force_data_dir = false; bool changing_language = false; QString load_profile = ""; @@ -204,7 +201,7 @@ int main(int argc, char *argv[]) changing_language = true; for (int i = 1; i < args.size(); i++) { - if (args[i] == "-l") { force_login_screen = true; } + if (args[i] == "-l") { dont_load_profile = true; } else if (args[i] == "-d") { force_data_dir = true; } else if (args[i] == "-language") { changing_language = true; @@ -235,12 +232,6 @@ int main(int argc, char *argv[]) a.setApplicationName(STR_TR_SleepyHead); - // Do reset strings if they selected the same langauge again.. -// if (settings.value(LangSetting, "").toString() == lastlanguage) { -// // don't reset if they selected the same language again -// changing_language = false; -// } - //////////////////////////////////////////////////////////////////////////////////////////// // OpenGL Detection //////////////////////////////////////////////////////////////////////////////////////////// @@ -377,6 +368,7 @@ retry_directory: //////////////////////////////////////////////////////////////////////////////////////////// p_pref = new Preferences("Preferences"); PREF.Open(); + AppSetting = new AppWideSetting(p_pref); initialize(); PRS1Loader::Register(); @@ -391,21 +383,14 @@ retry_directory: schema::setOrders(); - p_layout = new Preferences("Layout"); - - LAYOUT.Open(); - - // Scan for user profiles - Profiles::Scan(); - - - //qRegisterMetaType("Preference"); - PREF["AppName"] = STR_TR_SleepyHead; - - // Skip login screen, unless asked not to on the command line - bool skip_login = PREF.ExistsAndTrue(STR_GEN_SkipLogin); - - if (force_login_screen) { skip_login = false; } + // Clean up some legacy crap + QString layout = PREF.Get("{home}/Layout.xml"); + QFile lf(layout); + if (lf.exists()) { + lf.remove(); + } + PREF.Erase(STR_AppName); + PREF.Erase(STR_GEN_SkipLogin); // Todo: Make a wrapper for Preference settings, like Profile settings have.. QDateTime lastchecked, today = QDateTime::currentDateTime(); @@ -434,69 +419,28 @@ retry_directory: } } - if (!Profiles::profiles.size()) { - // Show New User wizard.. - NewProfile newprof(0); + if (PREF.contains(STR_PREF_VersionString)) { - if (newprof.exec() == NewProfile::Rejected) { - return 0; - } + int vc = compareVersion(PREF[STR_PREF_VersionString].toString()); + if (vc < 0) { + release_notes(); - release_notes(); - } else { - if (PREF.contains(STR_PREF_VersionString)) { + check_updates = false; + } else if (vc > 0) { + if (QMessageBox::warning(nullptr, STR_MessageBox_Error, + QObject::tr("The version of SleepyHead you just ran is OLDER than the one used to create this data (%1)."). + arg(PREF[STR_PREF_VersionString].toString()) +"\n\n"+ + QObject::tr("It is likely that doing this will cause data corruption, are you sure you want to do this?"), + QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) { - int vc = compareVersion(PREF[STR_PREF_VersionString].toString()); - if (vc < 0) { - release_notes(); - - check_updates = false; - } else if (vc > 0) { - if (QMessageBox::warning(nullptr, STR_MessageBox_Error, QObject::tr("The version of SleepyHead you just ran is OLDER than the one used to create this data (%1).").arg(PREF[STR_PREF_VersionString].toString()) +"\n\n"+ - QObject::tr("It is likely that doing this will cause data corruption, are you sure you want to do this?"), QMessageBox::Yes | QMessageBox::No, QMessageBox::No) == QMessageBox::No) { - - return 0; - } - - } - } - - ProfileSelect profsel(0); - - if (load_profile.size()) { - profsel.QuickLogin(); - - p_profile = Profiles::Get(load_profile); - if (!p_profile) { - NewProfile newprof(0, &load_profile); - - if (newprof.exec() == NewProfile::Rejected) { - return 0; - } - } - } else if (skip_login) { - profsel.QuickLogin(); - - if (profsel.result() == ProfileSelect::Rejected) { - exit(1); + return 0; } - p_profile = Profiles::Get(PREF[STR_GEN_Profile].toString()); - } else { p_profile = nullptr; } - - if (!p_profile) { - if (profsel.exec() == ProfileSelect::Rejected) { - exit(1); - } } } PREF[STR_PREF_VersionString] = VersionString; - p_profile = Profiles::Get(PREF[STR_GEN_Profile].toString()); - - qDebug() << "Opened Profile" << p_profile->user->userName(); - // int id=QFontDatabase::addApplicationFont(":/fonts/FreeSans.ttf"); // QFontDatabase fdb; // QStringList ffam=fdb.families(); @@ -504,8 +448,15 @@ retry_directory: // qDebug() << "Loaded Font: " << (*i); // } + if (!PREF.contains("Fonts_Application_Name")) { - PREF["Fonts_Application_Name"] = "Sans Serif"; +#ifdef Q_OS_WIN + // Windows default Sans Serif interpretation sucks + // Segoe UI is better, but that requires OS/font detection + PREF["Fonts_Application_Name"] = "Arial"; +#else + PREF["Fonts_Application_Name"] = QFontDatabase::systemFont(QFontDatabase::GeneralFont).family(); +#endif PREF["Fonts_Application_Size"] = 10; PREF["Fonts_Application_Bold"] = false; PREF["Fonts_Application_Italic"] = false; @@ -519,12 +470,18 @@ retry_directory: qDebug() << "Selected Font" << QApplication::font().family(); - // Must be initialized AFTER profile creation + // Scan for user profiles + Profiles::Scan(); + + if (!dont_load_profile) { + // TODO: set the don't automatically load profile AppSetting + } + + Q_UNUSED(changing_language) + MainWindow w; mainwin = &w; - loadChannels(changing_language); - if (check_updates) { mainwin->CheckForUpdates(); } w.show(); diff --git a/sleepyhead/mainwindow.cpp b/sleepyhead/mainwindow.cpp index 93157d0e..2195b831 100644 --- a/sleepyhead/mainwindow.cpp +++ b/sleepyhead/mainwindow.cpp @@ -1,4 +1,4 @@ -/* SleepyHead MainWindow Implementation +/* SleepyHead MainWindow Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -6,8 +6,7 @@ * License. See the file COPYING in the main directory of the Linux * distribution for more details. */ -#include - +#include #include #include #include @@ -31,13 +30,12 @@ #include #include #include +#include #include "common_gui.h" #include "version.h" -#include - // Custom loaders that don't autoscan.. #include #include @@ -69,98 +67,19 @@ QProgressBar *qprogress; QLabel *qstatus; QStatusBar *qstatusbar; -extern Profile *profile; - - -QString getOpenGLVersionString() -{ - static QString glversion; - - if (glversion.isEmpty()) { - QGLWidget w; - w.makeCurrent(); - -#if QT_VERSION < QT_VERSION_CHECK(5,4,0) - glversion = QString(QLatin1String(reinterpret_cast(glGetString(GL_VERSION)))); -#else - QOpenGLFunctions f; - f.initializeOpenGLFunctions(); - glversion = QString(QLatin1String(reinterpret_cast(f.glGetString(GL_VERSION)))); -#endif - qDebug() << "OpenGL Version:" << glversion; - } - return glversion; -} - -float getOpenGLVersion() -{ - QString glversion = getOpenGLVersionString(); - glversion = glversion.section(" ",0,0); - bool ok; - float v = glversion.toFloat(&ok); - - if (!ok) { - QString tmp = glversion.section(".",0,1); - v = tmp.toFloat(&ok); - if (!ok) { - // just look at major, we are only interested in whether we have OpenGL 2.0 anyway - tmp = glversion.section(".",0,0); - v = tmp.toFloat(&ok); - } - } - return v; -} - -QString getGraphicsEngine() -{ - QString gfxEngine = QString(); -#ifdef BROKEN_OPENGL_BUILD - gfxEngine = CSTR_GFX_BrokenGL; -#else - QString glversion = getOpenGLVersionString(); - if (glversion.contains(CSTR_GFX_ANGLE)) { - gfxEngine = CSTR_GFX_ANGLE; - } else { - gfxEngine = CSTR_GFX_OpenGL; - } -#endif - return gfxEngine; -} - -void MainWindow::logMessage(QString msg) -{ - ui->logText->appendPlainText(msg); -} - MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - Q_ASSERT(p_profile != nullptr); - ui->setupUi(this); if (logger) { connect(logger, SIGNAL(outputLog(QString)), this, SLOT(logMessage(QString))); } - QString version = STR_TR_AppVersion; + QString version = getBranchVersion(); -#ifndef TEST_BUILD - ui->warningLabel->hide(); -#endif - - version += " ["; -#ifdef GIT_REVISION - if (QString(GIT_BRANCH) != "master") { - version += QString(GIT_BRANCH)+"-"; - } - - version += QString(GIT_REVISION) +" "; -#endif - version += getGraphicsEngine()+"]"; - - this->setWindowTitle(STR_TR_SleepyHead + QString(" %1 (" + tr("Profile") + ": %2)").arg(version).arg(PREF[STR_GEN_Profile].toString())); + setWindowTitle(STR_TR_SleepyHead + QString(" %1").arg(version)); qDebug() << STR_TR_SleepyHead << VersionString << "built with Qt" << QT_VERSION_STR << "on" << __DATE__ << __TIME__; @@ -168,8 +87,6 @@ MainWindow::MainWindow(QWidget *parent) : qDebug() << "This build has been created especially for computers with older graphics hardware.\n"; #endif - //ui->tabWidget->setCurrentIndex(1); - #ifdef Q_OS_MAC ui->action_About->setMenuRole(QAction::ApplicationSpecificRole); @@ -181,27 +98,20 @@ MainWindow::MainWindow(QWidget *parent) : #endif #endif -//#ifdef LOCK_RESMED_SESSIONS -// QList machines = p_profile->GetMachines(MT_CPAP); -// for (QList::iterator it = machines.begin(); it != machines.end(); ++it) { -// QString mclass=(*it)->loaderName(); -// if (mclass == STR_MACH_ResMed) { -// qDebug() << "ResMed machine found.. locking Session splitting capabilities"; - -// // Have to sacrifice these features to get access to summary data. -// p_profile->session->setCombineCloseSessions(0); -// p_profile->session->setDaySplitTime(QTime(12,0,0)); -// p_profile->session->setIgnoreShortSessions(false); -// break; -// } -// } -//#endif - - ui->actionToggle_Line_Cursor->setChecked(p_profile->appearance->lineCursorMode()); + ui->actionToggle_Line_Cursor->setChecked(AppSetting->lineCursorMode()); + ui->actionDebug->setChecked(AppSetting->showDebug()); + ui->actionShow_Performance_Counters->setChecked(AppSetting->showPerformance()); overview = nullptr; daily = nullptr; prefdialog = nullptr; + profileSelector = nullptr; + + ui->oximetryButton->setDisabled(true); + ui->dailyButton->setDisabled(true); + ui->overviewButton->setDisabled(true); + ui->statisticsButton->setDisabled(true); + ui->importButton->setDisabled(true); m_inRecalculation = false; m_restartRequired = false; @@ -215,7 +125,6 @@ MainWindow::MainWindow(QWidget *parent) : ui->statusbar->addPermanentWidget(qstatus, 0); ui->statusbar->addPermanentWidget(qprogress, 1); - ui->actionDebug->setChecked(p_profile->general->showDebug()); QTextCharFormat format = ui->statStartDate->calendarWidget()->weekdayTextFormat(Qt::Saturday); format.setForeground(QBrush(Qt::black, Qt::SolidPattern)); @@ -233,7 +142,12 @@ MainWindow::MainWindow(QWidget *parent) : ui->statStartDate->setVisible(false); ui->reportModeRange->setVisible(false); - switch(p_profile->general->statReportMode()) { + int srm = 0; + if (p_profile) { + srm = p_profile->general->statReportMode(); + } + + switch(srm) { case 0: ui->reportModeStandard->setChecked(true); break; @@ -246,33 +160,36 @@ MainWindow::MainWindow(QWidget *parent) : ui->statStartDate->setVisible(true); break; default: - p_profile->general->setStatReportMode(0); + if (p_profile) { + p_profile->general->setStatReportMode(0); + } + break; } - if (!p_profile->general->showDebug()) { + if (!AppSetting->showDebug()) { ui->logText->hide(); } - ui->actionShow_Performance_Counters->setChecked(p_profile->general->showPerformance()); - #ifdef Q_OS_MAC - p_profile->appearance->setAntiAliasing(false); + //p_profile->appearance->setAntiAliasing(false); #endif //ui->action_Link_Graph_Groups->setChecked(p_profile->general->linkGroups()); first_load = true; - // Using the dirty registry here. :( + // Initialise sleepyHead app registry stuff QSettings settings(getDeveloperName(), getAppName()); // Load previous Window geometry (this is currently broken on Mac as of Qt5.2.1) restoreGeometry(settings.value("MainWindow/geometry").toByteArray()); - daily = new Daily(ui->tabWidget, nullptr); - ui->tabWidget->insertTab(2, daily, STR_TR_Daily); + profileSelector = new ProfileSelector(ui->tabWidget); + ui->tabWidget->insertTab(0, profileSelector, STR_TR_Profile); + // Profiles haven't been loaded here... + profileSelector->updateProfileList(); - // Start with the Summary Tab - ui->tabWidget->setCurrentWidget(ui->statisticsTab); // setting this to daily shows the cube during loading.. + // Start with the new Profile Tab + ui->tabWidget->setCurrentWidget(profileSelector); // setting this to daily shows the cube during loading.. // Nifty Notification popups in System Tray (uses Growl on Mac) if (QSystemTrayIcon::isSystemTrayAvailable() && QSystemTrayIcon::supportsMessages()) { @@ -293,16 +210,10 @@ MainWindow::MainWindow(QWidget *parent) : } ui->toolBox->setCurrentIndex(0); - bool b = p_profile->appearance->rightSidebarVisible(); + bool b = AppSetting->rightSidebarVisible(); ui->action_Sidebar_Toggle->setChecked(b); ui->toolBox->setVisible(b); - daily->graphView()->redraw(); - - if (p_profile->cpap->AHIWindow() < 30.0) { - p_profile->cpap->setAHIWindow(60.0); - } - ui->recordsBox->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); ui->statisticsView->page()->setLinkDelegationPolicy(QWebPage::DelegateAllLinks); ui->webView->page()->setLinkDelegationPolicy(QWebPage::DelegateExternalLinks); @@ -325,26 +236,12 @@ MainWindow::MainWindow(QWidget *parent) : qsrand(QDateTime::currentDateTime().toTime_t()); - // Translators, these are only temporary messages, don't bother unless you really want to.. - - warnmsg.push_back(tr("Warning: This is a pre-release build, and may at times show unstable behaviour. It is intended for testing purposes.")); - warnmsg.push_back(tr("If you experience CPAP chart/data errors after upgrading to a new version, try rebuilding your CPAP database from the Data menu.")); - warnmsg.push_back(tr("Make sure you keep your SleepyHead data folder backed up when trying testing versions.")); - warnmsg.push_back(tr("Please ensure you are running the latest version before reporting any bugs.")); - warnmsg.push_back(tr("When reporting bugs, please make sure to supply the SleepyHead version number, operating system details and CPAP machine model.")); - warnmsg.push_back(tr("Make sure you're willing and able to supply a .zip of your CPAP data or a crash report before you think about filing a bug report.")); - warnmsg.push_back(tr("Think twice before filing a bug report that already exists, PLEASE search first, as your likely not the first one to notice it!")); - warnmsg.push_back(tr("This red message line is intentional, and will not be a feature in the final version...")); - warnmsg.push_back(tr("")); - - wtimer.setParent(this); - warnidx = 0; - wtimer.singleShot(0, this, SLOT(on_changeWarningMessage())); - + // Setup to run a few last minute things before shutdown. connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(on_aboutToQuit())); QList a; - int panel_width = p_profile->appearance->rightPanelWidth(); + + int panel_width = AppSetting->rightPanelWidth(); a.push_back(this->width() - panel_width); a.push_back(panel_width); ui->mainsplitter->setSizes(a); @@ -352,173 +249,27 @@ MainWindow::MainWindow(QWidget *parent) : ui->mainsplitter->setStretchFactor(1,0); } -void MainWindow::on_changeWarningMessage() +void MainWindow::logMessage(QString msg) { - int i=warnidx++ % warnmsg.size(); - QString warning = "

    "+warnmsg[i]+"

    "; - ui->warningLabel->setText(warning); - wtimer.singleShot(10000, this, SLOT(on_changeWarningMessage())); -} - - -quint16 chandata_version = 1; - -void saveChannels() -{ - QString filename = p_profile->Get("{DataFolder}/") + "channels.dat"; - QFile f(filename); - qDebug() << "Saving Channel States"; - f.open(QFile::WriteOnly); - QDataStream out(&f); - out.setVersion(QDataStream::Qt_4_6); - out.setByteOrder(QDataStream::LittleEndian); - - out << (quint32)magic; - out << (quint16)chandata_version; - - quint16 size = schema::channel.channels.size(); - out << size; - - QHash::iterator it; - QHash::iterator it_end = schema::channel.channels.end(); - - for (it = schema::channel.channels.begin(); it != it_end; ++it) { - schema::Channel * chan = it.value(); - out << it.key(); - out << chan->code(); - out << chan->enabled(); - out << chan->defaultColor(); - out << chan->fullname(); - out << chan->label(); - out << chan->description(); - out << chan->lowerThreshold(); - out << chan->lowerThresholdColor(); - out << chan->upperThreshold(); - out << chan->upperThresholdColor(); - out << chan->showInOverview(); - } - - f.close(); - -} - - -void loadChannels(bool changing_language) -{ - QString filename = p_profile->Get("{DataFolder}/") + "channels.dat"; - QFile f(filename); - if (!f.open(QFile::ReadOnly)) { - return; - } - qDebug() << "Loading Channel States"; - - QDataStream in(&f); - in.setVersion(QDataStream::Qt_4_6); - in.setByteOrder(QDataStream::LittleEndian); - - quint32 mag; - in >> mag; - - if (magic != mag) { - qDebug() << "LoadChannels: Faulty data"; - return; - } - quint16 version; - in >> version; - - if (version < chandata_version) { - return; - //upgrade here.. - } - - quint16 size; - in >> size; - - QString name; - ChannelID code; - bool enabled; - QColor color; - EventDataType lowerThreshold; - QColor lowerThresholdColor; - EventDataType upperThreshold; - QColor upperThresholdColor; - - QString fullname; - QString label; - QString description; - bool showOverview = false; - - for (int i=0; i < size; i++) { - in >> code; - schema::Channel * chan = &schema::channel[code]; - in >> name; - if (chan->code() != name) { - qDebug() << "Looking up channel" << name << "by name, as it's ChannedID must have changed"; - chan = &schema::channel[name]; - } - in >> enabled; - in >> color; - in >> fullname; - in >> label; - in >> description; - in >> lowerThreshold; - in >> lowerThresholdColor; - in >> upperThreshold; - in >> upperThresholdColor; - if (version >= 1) { - in >> showOverview; - } - - if (chan->isNull()) { - qDebug() << "loadChannels has no idea about channel" << name; - if (in.atEnd()) return; - continue; - } - chan->setEnabled(enabled); - chan->setDefaultColor(color); - - // Don't import channel descriptions if event renaming is turned off. (helps pick up new translations) - if (changing_language) { - // Nothing - } else { - chan->setFullname(fullname); - chan->setLabel(label); - chan->setDescription(description); - } - - chan->setLowerThreshold(lowerThreshold); - chan->setLowerThresholdColor(lowerThresholdColor); - chan->setUpperThreshold(upperThreshold); - chan->setUpperThresholdColor(upperThresholdColor); - - chan->setShowInOverview(showOverview); - if (in.atEnd()) return; - } - - f.close(); + ui->logText->appendPlainText(msg); } void MainWindow::on_aboutToQuit() { - Notify(QObject::tr("Don't forget to place your datacard back in your CPAP machine"), QObject::tr("SleepyHead Reminder")); - QApplication::processEvents(); - QThread::msleep(1000); + if (systraymenu) delete systraymenu; } void MainWindow::closeEvent(QCloseEvent * event) { - - saveChannels(); + if (p_profile) { + CloseProfile(); + } schema::channel.Save(); - if (daily) { - daily->close(); - daily->deleteLater(); - } - - if (overview) { - overview->close(); - overview->deleteLater(); + if (AppSetting->removeCardReminder()) { + Notify(QObject::tr("Don't forget to place your datacard back in your CPAP machine"), QObject::tr("SleepyHead Reminder")); + QApplication::processEvents(); + QThread::msleep(1000); } // Shutdown and Save the current User profile @@ -560,12 +311,12 @@ void MainWindow::Notify(QString s, QString title, int ms) title = tr("%1 %2").arg(STR_TR_SleepyHead).arg(STR_TR_AppVersion); } if (systray) { - // GNOME3's systray hides the last line of the displayed Qt message. - // As a workaround, add an extra line to bump the message back - // into the visible area. QString msg = s; #ifdef Q_OS_UNIX + // GNOME3's systray hides the last line of the displayed Qt message. + // As a workaround, add an extra line to bump the message back + // into the visible area. char *desktop = getenv("DESKTOP_SESSION"); if (desktop && !strncmp(desktop, "gnome", 5)) { @@ -652,13 +403,82 @@ void MainWindow::PopulatePurgeMenu() QString GenerateWelcomeHTML(); -void MainWindow::Startup() +void MainWindow::OpenProfile(QString profileName) { + Profile * prof = Profiles::profiles[profileName]; + if (p_profile) { + if ((prof != p_profile)) { + CloseProfile(); + } else { + // Already open + return; + } + } + if (!prof) return; + + // TODO: Check profile password + + // Check Lockfile + QString lockhost = prof->checkLock(); + + if (!lockhost.isEmpty()) { + if (lockhost.compare(QHostInfo::localHostName()) == 0) { + // Localhost has it locked.. + if (QMessageBox::warning(nullptr, STR_MessageBox_Warning, + QObject::tr("There is a lockfile already present for profile '%1'.").arg(prof->user->userName())+"\n\n"+ + QObject::tr("You can only work with one instance of an individual SleepyHead profile at a time.")+"\n\n"+ + QObject::tr("Please close any other instances of SleepyHead running with this profile before proceeding.")+"\n\n"+ + QObject::tr("If no other instances of SleepyHead are running, (eg, it crashed last time!), it is safe to ignore this message."), + QMessageBox::Cancel |QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Cancel) { + return; + } + + } else { + if (QMessageBox::warning(nullptr, STR_MessageBox_Warning, + QObject::tr("There is a lockfile already present for this profile '%1', claimed on '%2'.").arg(prof->user->userName()).arg(lockhost)+"\n\n"+ + QObject::tr("You can only work with one instance of an individual SleepyHead profile at a time.")+"\n\n"+ + QObject::tr("If you are using cloud storage, make sure SleepyHead is closed and syncing has completed first on the other computer before proceeding."), + QMessageBox::Cancel |QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Cancel) { + return; + } + } + + prof->removeLock(); + } qstatus->setText(tr("Loading Data")); qprogress->show(); - //qstatusbar->showMessage(tr("Loading Data"),0); - // profile is a global variable set in main after login + p_profile = prof; + +#ifdef LOCK_RESMED_SESSIONS + QList machines = p_profile->GetMachines(MT_CPAP); + for (QList::iterator it = machines.begin(); it != machines.end(); ++it) { + QString mclass=(*it)->loaderName(); + if (mclass == STR_MACH_ResMed) { + qDebug() << "ResMed machine found.. locking Session splitting capabilities"; + + // Have to sacrifice these features to get access to summary data. + p_profile->session->setCombineCloseSessions(0); + p_profile->session->setDaySplitTime(QTime(12,0,0)); + p_profile->session->setIgnoreShortSessions(false); + p_profile->session->setLockSummarySessions(true); + + break; + } + } +#endif + + // Todo: move this to AHIWIndow check to profile Load function... + if (p_profile->cpap->AHIWindow() < 30.0) { + p_profile->cpap->setAHIWindow(60.0); + } + + + if (p_profile->p_preferences[STR_PREF_ReimportBackup].toBool()) { + importCPAPBackups(); + p_profile->p_preferences[STR_PREF_ReimportBackup]=false; + } + p_profile->LoadMachineData(); QList loaders = GetLoaders(); @@ -666,44 +486,78 @@ void MainWindow::Startup() connect(loaders.at(i), SIGNAL(machineUnsupported(Machine*)), this, SLOT(MachineUnsupported(Machine*))); } - PopulatePurgeMenu(); - -// SnapshotGraph = new gGraphView(this, daily->graphView()); - -// // Snapshot graphs mess up with pixmap cache -// SnapshotGraph->setUsePixmapCache(false); - -// // SnapshotGraph->setFormat(daily->graphView()->format()); -// //SnapshotGraph->setMaximumSize(1024,512); -// //SnapshotGraph->setMinimumSize(1024,512); -// SnapshotGraph->hide(); - - overview = new Overview(ui->tabWidget, daily->graphView()); - ui->tabWidget->insertTab(3, overview, STR_TR_Overview); - - -// GenerateStatistics(); ui->statStartDate->setDate(p_profile->FirstDay()); ui->statEndDate->setDate(p_profile->LastDay()); - if (overview) { overview->ReloadGraphs(); } + // Reload everything profile related + Q_ASSERT(!daily); + daily = new Daily(ui->tabWidget, nullptr); + ui->tabWidget->insertTab(2, daily, STR_TR_Daily); + daily->ReloadGraphs(); + + Q_ASSERT(!overview); + overview = new Overview(ui->tabWidget, daily->graphView()); + ui->tabWidget->insertTab(3, overview, STR_TR_Overview); + overview->ReloadGraphs(); + + // Should really create welcome and statistics here, but they need redoing later anyway to kill off webkit + ui->tabWidget->setCurrentWidget(ui->welcomeTab); + GenerateStatistics(); + PopulatePurgeMenu(); + + AppSetting->setProfileName(p_profile->user->userName()); + mainwin->setWindowTitle(STR_TR_SleepyHead + QString(" %1 (" + tr("Profile") + ": %2)").arg(getBranchVersion()).arg(AppSetting->profileName())); + + profileSelector->updateProfileHighlight(profileName); + + ui->oximetryButton->setDisabled(false); + ui->dailyButton->setDisabled(false); + ui->overviewButton->setDisabled(false); + ui->statisticsButton->setDisabled(false); + ui->importButton->setDisabled(false); qprogress->hide(); qstatus->setText(""); - if (p_profile->p_preferences[STR_PREF_ReimportBackup].toBool()) { - importCPAPBackups(); - p_profile->p_preferences[STR_PREF_ReimportBackup]=false; - } - - ui->tabWidget->setCurrentWidget(ui->welcomeTab); +} +void MainWindow::CloseProfile() +{ if (daily) { - daily->ReloadGraphs(); -// daily->populateSessionWidget(); + daily->Unload(); + daily->clearLastDay(); // otherwise Daily will crash + delete daily; + daily = nullptr; + } + if (overview) { + delete overview; + overview = nullptr; } + p_profile->StoreMachines(); + p_profile->UnloadMachineData(); + p_profile->saveChannels(); + p_profile->Save(); + p_profile->removeLock(); + p_profile = nullptr; +} + + +void MainWindow::Startup() +{ + QString lastProfile = AppSetting->profileName(); + + if (Profiles::profiles.contains(lastProfile)) { + OpenProfile(lastProfile); + ui->tabWidget->setCurrentIndex(AppSetting->openTabAtStart()); + + if (AppSetting->autoLaunchImport()) { + on_importButton_clicked(); + } + } else { + ui->tabWidget->setCurrentWidget(profileSelector); + } } int MainWindow::importCPAP(ImportPath import, const QString &message) @@ -749,9 +603,14 @@ int MainWindow::importCPAP(ImportPath import, const QString &message) qprogress->setVisible(false); delete popup; + if (AppSetting->openTabAfterImport()>0) { + ui->tabWidget->setCurrentIndex(AppSetting->openTabAfterImport()); + } + // Now what was this for??? disconnect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(on_aboutToQuit())); connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(on_aboutToQuit())); + return c; } @@ -759,12 +618,17 @@ void MainWindow::finishCPAPImport() { p_profile->StoreMachines(); GenerateStatistics(); + profileSelector->updateProfileList(); if (overview) { overview->ReloadGraphs(); } if (daily) { // daily->populateSessionWidget(); daily->ReloadGraphs(); } + if (AppSetting->openTabAfterImport()>0) { + ui->tabWidget->setCurrentIndex(AppSetting->openTabAfterImport()); + } + } void MainWindow::importCPAPBackups() @@ -1353,8 +1217,10 @@ void MainWindow::on_urlBar_activated(const QString &arg1) void MainWindow::on_dailyButton_clicked() { - ui->tabWidget->setCurrentWidget(daily); - daily->RedrawGraphs(); + if (daily) { + ui->tabWidget->setCurrentWidget(daily); + daily->RedrawGraphs(); + } } void MainWindow::JumpDaily() { @@ -1363,7 +1229,9 @@ void MainWindow::JumpDaily() void MainWindow::on_overviewButton_clicked() { - ui->tabWidget->setCurrentWidget(overview); + if (overview) { + ui->tabWidget->setCurrentWidget(overview); + } } void MainWindow::on_webView_loadFinished(bool arg1) @@ -1524,13 +1392,12 @@ void MainWindow::on_action_About_triggered() //spawn browser with paypal site. } - disconnect(&webview, SIGNAL(linkClicked(const QUrl &)), this, - SLOT(aboutBoxLinkClicked(const QUrl &))); + disconnect(&webview, SIGNAL(linkClicked(const QUrl &)), this, SLOT(aboutBoxLinkClicked(const QUrl &))); } void MainWindow::on_actionDebug_toggled(bool checked) { - p_profile->general->setShowDebug(checked); + AppSetting->setShowDebug(checked); logger->strlock.lock(); if (checked) { @@ -1552,6 +1419,10 @@ void MainWindow::on_action_Reset_Graph_Layout_triggered() void MainWindow::on_action_Preferences_triggered() { //MW: TODO: This will crash if attempted to enter while still loading.. + if (!p_profile) { + mainwin->Notify(tr("Please open a profile first.")); + return; + } if (m_inRecalculation) { mainwin->Notify(tr("Access to Preferences has been blocked until recalculation completes.")); @@ -1584,8 +1455,10 @@ QDateTime datetimeDialog(QDateTime datetime, QString message); void MainWindow::on_oximetryButton_clicked() { - OximeterImport oxiimp(this); - oxiimp.exec(); + if (p_profile) { + OximeterImport oxiimp(this); + oxiimp.exec(); + } } void MainWindow::CheckForUpdates() @@ -1723,7 +1596,7 @@ void MainWindow::on_actionPrint_Report_triggered() void MainWindow::on_action_Edit_Profile_triggered() { NewProfile *newprof = new NewProfile(this); - QString name =PREF[STR_GEN_Profile].toString(); + QString name = AppSetting->profileName(); newprof->edit(name); newprof->setWindowModality(Qt::ApplicationModal); newprof->setModal(true); @@ -2023,14 +1896,6 @@ void MainWindow::RestartApplication(bool force_login, QString cmdline) #endif } -void MainWindow::on_actionChange_User_triggered() -{ - p_profile->Save(); - PREF.Save(); - - RestartApplication(true); -} - void MainWindow::on_actionPurge_Current_Day_triggered() { QDate date = getDaily()->getDate(); @@ -2259,7 +2124,7 @@ void MainWindow::keyPressEvent(QKeyEvent *event) void MainWindow::on_action_Sidebar_Toggle_toggled(bool visible) { ui->toolBox->setVisible(visible); - p_profile->appearance->setRightSidebarVisible(visible); + AppSetting->setRightSidebarVisible(visible); } void MainWindow::on_recordsBox_linkClicked(const QUrl &linkurl) @@ -2282,8 +2147,10 @@ void MainWindow::on_recordsBox_linkClicked(const QUrl &linkurl) overview->setRange(d1, d2); ui->tabWidget->setCurrentWidget(overview); } else if (link == "import") { - if (data == "cpap") on_importButton_clicked(); - if (data == "oximeter") on_oximetryButton_clicked(); + // Don't run the importer directly from here because it destroys the object that called this function.. + // Schedule it instead + if (data == "cpap") QTimer::singleShot(0, mainwin, SLOT(on_importButton_clicked())); + if (data == "oximeter") QTimer::singleShot(0, mainwin, SLOT(on_oximetryButton_clicked())); } else if (link == "statistics") { ui->tabWidget->setCurrentWidget(ui->statisticsTab); } @@ -2583,9 +2450,11 @@ void MainWindow::on_actionChange_Language_triggered() void MainWindow::on_actionChange_Data_Folder_triggered() { - p_profile->Save(); + if (p_profile) { + p_profile->Save(); + p_profile->removeLock(); + } PREF.Save(); - p_profile->removeLock(); RestartApplication(false, "-d"); } @@ -2651,7 +2520,9 @@ void MainWindow::GenerateStatistics() void MainWindow::on_statisticsButton_clicked() { - ui->tabWidget->setCurrentWidget(ui->statisticsTab); + if (p_profile) { + ui->tabWidget->setCurrentWidget(ui->statisticsTab); + } } void MainWindow::on_statisticsView_linkClicked(const QUrl &arg1) @@ -2733,7 +2604,7 @@ void MainWindow::on_importButton_clicked() void MainWindow::on_actionToggle_Line_Cursor_toggled(bool b) { - p_profile->appearance->setLineCursorMode(b); + AppSetting->setLineCursorMode(b); if (ui->tabWidget->currentWidget() == getDaily()) { getDaily()->graphView()->timedRedraw(0); } else if (ui->tabWidget->currentWidget() == getOverview()) { @@ -2770,7 +2641,7 @@ void MainWindow::on_actionExport_Journal_triggered() void MainWindow::on_actionShow_Performance_Counters_toggled(bool arg1) { - p_profile->general->setShowPerformance(arg1); + AppSetting->setShowPerformance(arg1); } void MainWindow::on_actionExport_CSV_triggered() @@ -2788,7 +2659,7 @@ void MainWindow::on_actionExport_Review_triggered() void MainWindow::on_mainsplitter_splitterMoved(int, int) { - p_profile->appearance->setRightPanelWidth(ui->mainsplitter->sizes()[1]); + AppSetting->setRightPanelWidth(ui->mainsplitter->sizes()[1]); } #include "translation.h" @@ -2798,3 +2669,8 @@ void MainWindow::on_actionReport_a_Bug_triggered() QString language = settings.value(LangSetting).toString(); QDesktopServices::openUrl(QUrl(QString("https://sleepyhead.jedimark.net/report_bugs.php?lang=%1&version=%2&platform=%3").arg(language).arg(VersionString).arg(PlatformString))); } + +void MainWindow::on_profilesButton_clicked() +{ + ui->tabWidget->setCurrentWidget(profileSelector); +} diff --git a/sleepyhead/mainwindow.h b/sleepyhead/mainwindow.h index 811aa833..89ba0f19 100644 --- a/sleepyhead/mainwindow.h +++ b/sleepyhead/mainwindow.h @@ -1,4 +1,4 @@ -/* SleepyHead MainWindow Headers +/* SleepyHead MainWindow Headers * * Copyright (C) 2011-2018 Mark Watkins * @@ -18,6 +18,7 @@ #include "daily.h" #include "overview.h" +#include "profileselector.h" #include "preferencesdialog.h" extern Profile *profile; @@ -66,9 +67,6 @@ class Daily; class Report; class Overview; -void loadChannels(bool changing_language=false); -void saveChannels(); - /*! \class MainWindow \author Mark Watkins @@ -95,6 +93,8 @@ class MainWindow : public QMainWindow //! \brief Start the automatic update checker process void CheckForUpdates(); + void CloseProfile(); + void OpenProfile(QString name); /*! \fn Notify(QString s,int ms=5000, QString title="SleepyHead v"+VersionString()); \brief Pops up a message box near the system tray @@ -251,9 +251,6 @@ class MainWindow : public QMainWindow */ void on_action_Rebuild_Oximetry_Index_triggered(); - //! \brief Log out, by effectively forcing a restart - void on_actionChange_User_triggered(); - //! \brief Destroy the CPAP data for the currently selected day, so it can be freshly imported again void on_actionPurge_Current_Day_triggered(); @@ -320,8 +317,6 @@ class MainWindow : public QMainWindow void on_importButton_clicked(); - void on_changeWarningMessage(); - void on_actionToggle_Line_Cursor_toggled(bool arg1); void on_actionLeft_Daily_Sidebar_toggled(bool arg1); @@ -341,6 +336,8 @@ class MainWindow : public QMainWindow void on_actionReport_a_Bug_triggered(); + void on_profilesButton_clicked(); + private: void importCPAPBackups(); void finishCPAPImport(); @@ -352,6 +349,7 @@ private: Ui::MainWindow *ui; Daily *daily; Overview *overview; + ProfileSelector *profileSelector; bool first_load; PreferencesDialog *prefdialog; QTime logtime; diff --git a/sleepyhead/mainwindow.ui b/sleepyhead/mainwindow.ui index 11b93dbf..26f3f15f 100644 --- a/sleepyhead/mainwindow.ui +++ b/sleepyhead/mainwindow.ui @@ -6,8 +6,8 @@ 0 0 - 687 - 361 + 1023 + 804 @@ -958,42 +958,14 @@ 0 - - - - 0 - 0 - - - - - 14 - 50 - false - false - - - - background-color: red; -color: yellow; - - - <html><head/><body><p><span style=" font-weight:600;">Warning: </span>This is pre-release software, some parts of this program may not yet function as intended.</p></body></html> - - - Qt::AlignHCenter|Qt::AlignTop - - - - - + 0 0 - + about:blank @@ -1023,14 +995,14 @@ color: yellow; 0 - + 0 0 - + about:blank @@ -1293,7 +1265,7 @@ color: yellow; 0 - + 0 @@ -1312,7 +1284,7 @@ color: yellow; 200 - + qrc:/docs/index.html @@ -1472,7 +1444,7 @@ QToolBox::tab:selected { 1 - 2 + 0 0 @@ -1482,8 +1454,8 @@ QToolBox::tab:selected { 0 0 - 95 - 606 + 175 + 680 @@ -1591,6 +1563,50 @@ QToolBox::tab:selected { Navigation + + + + + 0 + 0 + + + + QToolButton { +background: transparent; +border: 2px solid transparent; border-radius: 30px; +} + +QToolButton:hover { +border: 2px solid #56789a; border-radius: 30px; +} + +QToolButton:pressed { +background-color: #8080ff; +border: 2px solid #56789a; border-radius: 30px; +} + + + Profiles + + + + :/icons/go-home.png:/icons/go-home.png + + + + 64 + 64 + + + + Qt::ToolButtonTextUnderIcon + + + true + + + @@ -1896,8 +1912,8 @@ border: 2px solid #56789a; border-radius: 30px; 0 0 - 77 - 236 + 175 + 680 @@ -2929,7 +2945,7 @@ border-radius: 10px; - + 0 @@ -3030,7 +3046,7 @@ border-radius: 10px; background: rgb(163, 190, 255) - + about:blank @@ -3044,8 +3060,8 @@ border-radius: 10px; 0 0 - 77 - 236 + 175 + 680 @@ -3078,7 +3094,7 @@ border-radius: 10px; 0 - + 10 @@ -3087,7 +3103,7 @@ border-radius: 10px; background: rgb(163, 190, 255) - + about:blank @@ -3106,8 +3122,8 @@ border-radius: 10px; 0 0 - 687 - 22 + 1023 + 21 @@ -3137,7 +3153,6 @@ border-radius: 10px; - diff --git a/sleepyhead/newprofile.cpp b/sleepyhead/newprofile.cpp index c143f350..fb56aa19 100644 --- a/sleepyhead/newprofile.cpp +++ b/sleepyhead/newprofile.cpp @@ -1,4 +1,4 @@ -/* Create New Profile Implementation +/* Create New Profile Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -91,9 +91,7 @@ NewProfile::NewProfile(QWidget *parent, const QString *user) : f.close(); } - ui->AppTitle->setText("SleepyHead v" + VersionString); - //ui->releaseStatus->setText(ReleaseStatus); - + ui->releaseStatus->setText("v" + VersionString); ui->textBrowser->setHtml(getIntroHTML()); } @@ -264,8 +262,7 @@ void NewProfile::on_nextButton_clicked() profile->user->setHeight(v); //profile->user->setUserName(username); - PREF[STR_GEN_Profile] = username; - + AppSetting->setProfileName(username); this->accept(); } diff --git a/sleepyhead/newprofile.ui b/sleepyhead/newprofile.ui index 2ecd175f..efdbbf7e 100644 --- a/sleepyhead/newprofile.ui +++ b/sleepyhead/newprofile.ui @@ -40,7 +40,7 @@ - 0 + 2 @@ -91,7 +91,7 @@ - -1 + 6 8 @@ -115,7 +115,7 @@ - -1 + 6 8 @@ -226,7 +226,7 @@ 8 - -1 + 6 @@ -300,7 +300,7 @@ - -1 + 6 8 @@ -321,10 +321,10 @@ - -1 + 6 - -1 + 6 8 @@ -360,6 +360,9 @@ + + It's totally ok to fib or skip this, but your rough age is needed to enhance accuracy of certain calculations. + D.O.B. @@ -376,6 +379,9 @@ + + <html><head/><body><p>Biological (birth) gender is sometimes needed to enhance the accuracy of a few calculations, feel free to leave this blank and skip any of them.</p></body></html> + Gender @@ -475,10 +481,10 @@ QFormLayout::AllNonFixedFieldsGrow - -1 + 6 - -1 + 6 8 @@ -550,7 +556,7 @@ - -1 + 6 8 @@ -574,10 +580,10 @@ QFormLayout::AllNonFixedFieldsGrow - -1 + 6 - -1 + 6 8 @@ -682,7 +688,7 @@ - -1 + 6 8 @@ -706,10 +712,10 @@ QFormLayout::AllNonFixedFieldsGrow - -1 + 6 - -1 + 6 8 @@ -832,6 +838,25 @@ + + + + + 128 + 128 + + + + + + + :/docs/sheep.png + + + true + + + @@ -871,19 +896,6 @@ - - - - - - - :/docs/sheep.png - - - false - - - diff --git a/sleepyhead/overview.cpp b/sleepyhead/overview.cpp index 962afe9e..29d8eafe 100644 --- a/sleepyhead/overview.cpp +++ b/sleepyhead/overview.cpp @@ -446,7 +446,7 @@ void Overview::closeEvent(QCloseEvent *event) gGraph *Overview::createGraph(QString code, QString name, QString units, YTickerType yttype) { - int default_height = p_profile->appearance->graphHeight(); + int default_height = AppSetting->graphHeight(); gGraph *g = new gGraph(code, GraphView, name, units, default_height, 0); gYAxis *yt; diff --git a/sleepyhead/oximeterimport.cpp b/sleepyhead/oximeterimport.cpp index 11cb305b..53c1d7d8 100644 --- a/sleepyhead/oximeterimport.cpp +++ b/sleepyhead/oximeterimport.cpp @@ -474,7 +474,7 @@ void OximeterImport::on_liveImportButton_clicked() } MachineInfo info = oximodule->newInfo(); - Machine *mach = oximodule->CreateMachine(info); + Machine *mach = p_profile->CreateMachine(info); connect(oximodule, SIGNAL(updatePlethy(QByteArray)), this, SLOT(on_updatePlethy(QByteArray))); ui->liveConnectLabel->setText(tr("Live Oximetery Mode")); @@ -842,7 +842,7 @@ void OximeterImport::on_saveButton_clicked() // this can move to SerialOximeter class process function... MachineInfo info = oximodule->newInfo(); - Machine * mach = oximodule->CreateMachine(info); + Machine * mach = p_profile->CreateMachine(info); SessionID sid = ui->dateTimeEdit->dateTime().toUTC().toTime_t(); quint64 start = quint64(sid) * 1000L; diff --git a/sleepyhead/preferencesdialog.cpp b/sleepyhead/preferencesdialog.cpp index 499683bc..f704dd97 100644 --- a/sleepyhead/preferencesdialog.cpp +++ b/sleepyhead/preferencesdialog.cpp @@ -1,4 +1,4 @@ -/* SleepyHead Preferences Dialog Implementation +/* SleepyHead Preferences Dialog Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -48,19 +48,29 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : channeltype[schema::MINOR_FLAG] = tr("Minor Flag"); channeltype[schema::SPAN] = tr("Span"); channeltype[schema::UNKNOWN] = tr("Always Minor"); + bool haveResMed = false; + QList machines = profile->GetMachines(MT_CPAP); + for (QList::iterator it = machines.begin(); it != machines.end(); ++it) { + const QString & mclass=(*it)->loaderName(); + if (mclass == STR_MACH_ResMed) { + haveResMed = true; + break; + } + } -//#ifdef LOCK_RESMED_SESSIONS -// QList machines = p_profile->GetMachines(MT_CPAP); -// for (QList::iterator it = machines.begin(); it != machines.end(); ++it) { -// const QString & mclass=(*it)->loaderName(); -// if (mclass == STR_MACH_ResMed) { -// ui->combineSlider->setEnabled(false); -// ui->IgnoreSlider->setEnabled(false); -// ui->timeEdit->setEnabled(false); -// break; -// } -// } -//#endif +#ifdef LOCK_RESMED_SESSIONS + // Remove access to session splitting options and show ResMed users a notice instead + ui->ResMedWarning->setText(tr("

    Please Note: SleepyHead's advanced session splitting capabilities are not possible with ResMed machines due to a limitation in the way their settings and summary data is stored, and therefore they have been disabled for this profile.

    On ResMed machines, days will split at noon like in ResMed's commercial software.

    ")); + ui->ResMedWarning->setVisible(haveResMed); + + if (haveResMed) { + ui->sessionSplitWidget->setVisible(!haveResMed); + profile->session->setDaySplitTime(QTime(12,0,0)); + profile->session->setIgnoreShortSessions(0); + profile->session->setCombineCloseSessions(0); + profile->session->setLockSummarySessions(true); + } +#endif QLocale locale = QLocale::system(); QString shortformat = locale.dateFormat(QLocale::ShortFormat); @@ -100,6 +110,10 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : 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); } @@ -137,18 +151,18 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : ui->bigFontBold->setChecked(bigfont->weight() == QFont::Bold); ui->bigFontItalic->setChecked(bigfont->italic()); - ui->lineThicknessSlider->setValue(profile->appearance->lineThickness()*2); + ui->lineThicknessSlider->setValue(AppSetting->lineThickness()*2.0); ui->resyncMachineDetectedEvents->setChecked(profile->cpap->resyncFromUserFlagging()); - ui->useAntiAliasing->setChecked(profile->appearance->antiAliasing()); - ui->usePixmapCaching->setChecked(profile->appearance->usePixmapCaching()); - ui->useSquareWavePlots->setChecked(profile->appearance->squareWavePlots()); - ui->enableGraphSnapshots->setChecked(profile->appearance->graphSnapshots()); - ui->graphTooltips->setChecked(profile->appearance->graphTooltips()); - ui->allowYAxisScaling->setChecked(profile->appearance->allowYAxisScaling()); + 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->skipLoginScreen->setChecked(PREF[STR_GEN_SkipLogin].toBool()); + ui->autoLaunchImporter->setChecked(AppSetting->autoLaunchImport()); ui->allowEarlyUpdates->setChecked(PREF[STR_PREF_AllowEarlyUpdates].toBool()); int s = profile->cpap->clockDrift(); @@ -161,10 +175,11 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : ui->skipEmptyDays->setChecked(profile->general->skipEmptyDays()); ui->showUnknownFlags->setChecked(profile->general->showUnknownFlags()); - ui->enableMultithreading->setChecked(profile->session->multithreading()); - ui->cacheSessionData->setChecked(profile->session->cacheSessions()); + ui->enableMultithreading->setChecked(AppSetting->multithreading()); + ui->removeCardNotificationCheckbox->setChecked(AppSetting->removeCardReminder()); + ui->cacheSessionData->setChecked(AppSetting->cacheSessions()); ui->preloadSummaries->setChecked(profile->session->preloadSummaries()); - ui->animationsAndTransitionsCheckbox->setChecked(profile->appearance->animations()); + ui->animationsAndTransitionsCheckbox->setChecked(AppSetting->animations()); ui->complianceCheckBox->setChecked(profile->cpap->showComplianceInfo()); ui->complianceHours->setValue(profile->cpap->complianceHours()); @@ -173,11 +188,11 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : float f = profile->general->prefCalcPercentile(); ui->prefCalcPercentile->setValue(f); - ui->tooltipTimeoutSlider->setValue(profile->general->tooltipTimeout()); + ui->tooltipTimeoutSlider->setValue(AppSetting->tooltipTimeout()); on_tooltipTimeoutSlider_valueChanged(ui->tooltipTimeoutSlider->value()); //ui->tooltipMS->display(profile->general->tooltipTimeout()); - ui->scrollDampeningSlider->setValue(profile->general->scrollDampening() / 10); + ui->scrollDampeningSlider->setValue(AppSetting->scrollDampening() / 10); on_scrollDampeningSlider_valueChanged(ui->scrollDampeningSlider->value()); bool bcd = profile->session->backupCardData(); @@ -188,7 +203,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : ui->ignoreOlderSessionsCheck->setChecked(profile->session->ignoreOlderSessions()); ui->ignoreOlderSessionsDate->setDate(profile->session->ignoreOlderSessionsDate().date()); - ui->graphHeight->setValue(profile->appearance->graphHeight()); + ui->graphHeight->setValue(AppSetting->graphHeight()); ui->automaticallyCheckUpdates->setChecked(PREF[STR_GEN_UpdatesAutoCheck].toBool()); @@ -199,8 +214,8 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : RefreshLastChecked(); } else { ui->updateLastChecked->setText("Never"); } - ui->overlayFlagsCombo->setCurrentIndex(profile->appearance->overlayType()); - ui->overviewLinecharts->setCurrentIndex(profile->appearance->overviewLinechartMode()); + ui->overlayFlagsCombo->setCurrentIndex(AppSetting->overlayType()); + ui->overviewLinecharts->setCurrentIndex(AppSetting->overviewLinechartMode()); ui->ahiGraphWindowSize->setEnabled(false); ui->ahiGraphWindowSize->setValue(profile->cpap->AHIWindow()); @@ -214,7 +229,7 @@ PreferencesDialog::PreferencesDialog(QWidget *parent, Profile *_profile) : ui->userEventDuplicates->setChecked(profile->cpap->userEventDuplicates()); ui->userEventDuplicates->setVisible(false); - ui->showUserFlagsInPie->setChecked(profile->cpap->userEventPieChart()); + ui->showUserFlagsInPie->setChecked(AppSetting->userEventPieChart()); bool b; ui->calculateUnintentionalLeaks->setChecked(b=profile->cpap->calculateUnintentionalLeaks()); @@ -628,7 +643,7 @@ bool PreferencesDialog::Save() if (ui->ahiGraphZeroReset->isChecked() != profile->cpap->AHIReset()) { recalc_events = true; } - if (ui->useSquareWavePlots->isChecked() != profile->appearance->squareWavePlots()) { + if (ui->useSquareWavePlots->isChecked() != AppSetting->squareWavePlots()) { needs_restart = true; } @@ -642,7 +657,7 @@ bool PreferencesDialog::Save() needs_restart = true; } - if (profile->cpap->userEventPieChart() != ui->showUserFlagsInPie->isChecked()) { + if (AppSetting->userEventPieChart() != ui->showUserFlagsInPie->isChecked()) { // lazy.. fix me needs_restart = true; } @@ -687,7 +702,7 @@ bool PreferencesDialog::Save() } if (recalc_events) { - if (p_profile->countDays(MT_CPAP, p_profile->FirstDay(), p_profile->LastDay()) > 0) { + 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) { @@ -707,28 +722,33 @@ bool PreferencesDialog::Save() schema::channel[OXI_Pulse].setUpperThreshold(ui->flagPulseAbove->value()); - profile->cpap->setUserEventPieChart(ui->showUserFlagsInPie->isChecked()); + AppSetting->setUserEventPieChart(ui->showUserFlagsInPie->isChecked()); profile->session->setLockSummarySessions(ui->LockSummarySessionSplitting->isChecked()); - profile->appearance->setAllowYAxisScaling(ui->allowYAxisScaling->isChecked()); - profile->appearance->setGraphTooltips(ui->graphTooltips->isChecked()); + AppSetting->setOpenTabAtStart(ui->openingTabCombo->currentIndex()); + AppSetting->setOpenTabAfterImport(ui->importTabCombo->currentIndex()); - profile->appearance->setAntiAliasing(ui->useAntiAliasing->isChecked()); - profile->appearance->setUsePixmapCaching(ui->usePixmapCaching->isChecked()); - profile->appearance->setSquareWavePlots(ui->useSquareWavePlots->isChecked()); - profile->appearance->setGraphSnapshots(ui->enableGraphSnapshots->isChecked()); - profile->appearance->setLineThickness(float(ui->lineThicknessSlider->value()) / 2.0); + AppSetting->setAllowYAxisScaling(ui->allowYAxisScaling->isChecked()); + 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()); - profile->general->setTooltipTimeout(ui->tooltipTimeoutSlider->value()); - profile->general->setScrollDampening(ui->scrollDampeningSlider->value() * 10); + AppSetting->setTooltipTimeout(ui->tooltipTimeoutSlider->value()); + AppSetting->setScrollDampening(ui->scrollDampeningSlider->value() * 10); profile->general->setShowUnknownFlags(ui->showUnknownFlags->isChecked()); - profile->session->setMultithreading(ui->enableMultithreading->isChecked()); - profile->session->setCacheSessions(ui->cacheSessionData->isChecked()); + AppSetting->setMultithreading(ui->enableMultithreading->isChecked()); + AppSetting->setRemoveCardReminder(ui->removeCardNotificationCheckbox->isChecked()); + + AppSetting->setCacheSessions(ui->cacheSessionData->isChecked()); profile->session->setPreloadSummaries(ui->preloadSummaries->isChecked()); - profile->appearance->setAnimations(ui->animationsAndTransitionsCheckbox->isChecked()); + AppSetting->setAnimations(ui->animationsAndTransitionsCheckbox->isChecked()); profile->cpap->setShowLeakRedline(ui->showLeakRedline->isChecked()); profile->cpap->setLeakRedline(ui->leakRedlineSpinbox->value()); @@ -736,8 +756,8 @@ bool PreferencesDialog::Save() profile->cpap->setShowComplianceInfo(ui->complianceCheckBox->isChecked()); profile->cpap->setComplianceHours(ui->complianceHours->value()); - if (ui->graphHeight->value() != profile->appearance->graphHeight()) { - profile->appearance->setGraphHeight(ui->graphHeight->value()); + if (ui->graphHeight->value() != AppSetting->graphHeight()) { + AppSetting->setGraphHeight(ui->graphHeight->value()); mainwin->getDaily()->ResetGraphLayout(); mainwin->getOverview()->ResetGraphLayout(); } @@ -760,8 +780,8 @@ bool PreferencesDialog::Save() int s = ui->clockDriftHours->value() * 3600 + ui->clockDriftMinutes->value() * 60 + ui->clockDriftSeconds->value(); profile->cpap->setClockDrift(s); - profile->appearance->setOverlayType((OverlayDisplayType)ui->overlayFlagsCombo->currentIndex()); - profile->appearance->setOverviewLinechartMode((OverviewLinechartModes)ui->overviewLinecharts->currentIndex()); + AppSetting->setOverlayType((OverlayDisplayType)ui->overlayFlagsCombo->currentIndex()); + AppSetting->setOverviewLinechartMode((OverviewLinechartModes)ui->overviewLinecharts->currentIndex()); profile->oxi->setSpO2DropPercentage(ui->spo2Drop->value()); profile->oxi->setSpO2DropDuration(ui->spo2DropTime->value()); @@ -796,7 +816,7 @@ bool PreferencesDialog::Save() profile->cpap->setCustom4cmH2OLeaks(double(ui->maskLeaks4Slider->value()) / 10.0f); profile->cpap->setCustom20cmH2OLeaks(double(ui->maskLeaks20Slider->value()) / 10.0f); - PREF[STR_GEN_SkipLogin] = ui->skipLoginScreen->isChecked(); + AppSetting->setAutoLaunchImport(ui->autoLaunchImporter->isChecked()); PREF[STR_GEN_UpdatesAutoCheck] = ui->automaticallyCheckUpdates->isChecked(); PREF[STR_GEN_UpdateCheckFrequency] = ui->updateCheckEvery->value(); @@ -852,13 +872,13 @@ bool PreferencesDialog::Save() //qDebug() << "TODO: Save channels.xml to update channel data"; PREF.Save(); - p_profile->Save(); + profile->Save(); if (recalc_events) { // send a signal instead? mainwin->reprocessEvents(needs_restart); } else if (needs_restart) { - p_profile->removeLock(); + profile->removeLock(); mainwin->RestartApplication(); } else { mainwin->getDaily()->LoadDate(mainwin->getDaily()->getDate()); diff --git a/sleepyhead/preferencesdialog.ui b/sleepyhead/preferencesdialog.ui index 13aff174..3173d5ef 100644 --- a/sleepyhead/preferencesdialog.ui +++ b/sleepyhead/preferencesdialog.ui @@ -9,8 +9,8 @@ 0 0 - 856 - 656 + 848 + 646
    @@ -57,7 +57,7 @@
    - 1 + 0 @@ -80,7 +80,7 @@ 4
    - + 0 @@ -95,21 +95,27 @@ 4 - 0 + 4 4 - 0 + 4 - 0 + 4 - + + + + 0 + 0 + + - <html><head/><body><p><span style=" font-weight:600;">Attention ResMed users:</span> There are some pitfalls you may want to consider before trying to split sessions away from ResMed's 12:00 noon day model, click <a href="http://sleepyhead.sf.net/wiki/index.php/Resmed_Session_Splitting">here</a> for more information.</p></body></html> + [ResMed warning message] true @@ -120,227 +126,257 @@ - - - - - Combine Close Sessions - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Minutes - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - - 0 - 0 - - - - Multiple sessions closer together than this value will be kept on the same day. + + + + 0 + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Combine Close Sessions + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Minutes + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + + + + 0 + 0 + + + + Multiple sessions closer together than this value will be kept on the same day. - - - 0 - - - 240 - - - 10 - - - 30 - - - 0 - - - Qt::Horizontal - - - false - - - false - - - QSlider::TicksAbove - - - 10 - - - - - - - QFrame::Box - - - 5 - - - QLCDNumber::Flat - - - - - - - - - - - Ignore Short Sessions - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Minutes - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + 0 + + + 240 + + + 10 + + + 30 + + + 0 + + + Qt::Horizontal + + + false + + + false + + + QSlider::TicksAbove + + + 10 + + + + + + + QFrame::Box + + + 5 + + + QLCDNumber::Flat + + + + + + + + + + + Ignore Short Sessions + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Minutes + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + + 4 + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Cantarell'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Sessions shorter in duration than this will not be displayed<span style=" font-style:italic;">.</span></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-style:italic;"></p></body></html> - - - 90 - - - 5 - - - Qt::Horizontal - - - QSlider::TicksAbove - - - 5 - - - - - - - QFrame::Box - - - QLCDNumber::Flat - - - - - - - - - - - Day Split Time - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter - - - - - - - Sessions starting before this time will go to the previous calendar day. - - - QAbstractSpinBox::UpDownArrows - - - - 12 - 0 - 0 - 2000 - 1 - 1 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Minimum - - - - 10 - 20 - - - - - - - - <html><head/><body><p><span style=" font-weight:600;">This setting should be used with caution...</span> Switching it off comes with consequences involving accuracy of summary only days, as certain calculations only work properly provided summary only sessions that came from individual day records are kept together. </p><p><span style=" font-weight:600;">ResMed users:</span> Just because it seems natural to you and I that the 12 noon session restart should be in the previous day, does not mean ResMed's data agrees with us. The STF.edf summary index format has serious weaknesses that make doing this not a good idea.</p><p>This option exists to pacify those who don't care and want to see this &quot;fixed&quot; no matter the costs, but know it comes with a cost. If you keep your SD card in every night, and import at least once a week, you won't see problems with this very often.</p></body></html> - - - Don't Split Summary Days (Warning: read the tooltip!) - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - + + + 90 + + + 5 + + + Qt::Horizontal + + + QSlider::TicksAbove + + + 5 + + + + + + + QFrame::Box + + + QLCDNumber::Flat + + + + + + + + + 4 + + + + + Day Split Time + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Sessions starting before this time will go to the previous calendar day. + + + QAbstractSpinBox::UpDownArrows + + + + 12 + 0 + 0 + 2000 + 1 + 1 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 10 + 20 + + + + + + + + <html><head/><body><p><span style=" font-weight:600;">This setting should be used with caution...</span> Switching it off comes with consequences involving accuracy of summary only days, as certain calculations only work properly provided summary only sessions that came from individual day records are kept together. </p><p><span style=" font-weight:600;">ResMed users:</span> Just because it seems natural to you and I that the 12 noon session restart should be in the previous day, does not mean ResMed's data agrees with us. The STF.edf summary index format has serious weaknesses that make doing this not a good idea.</p><p>This option exists to pacify those who don't care and want to see this &quot;fixed&quot; no matter the costs, but know it comes with a cost. If you keep your SD card in every night, and import at least once a week, you won't see problems with this very often.</p></body></html> + + + Don't Split Summary Days (Warning: read the tooltip!) + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + +
    @@ -531,13 +567,13 @@ SleepyHead can keep a copy of this data if you ever need to reinstall. Memory and Startup Options
    - - + + - <html><head/><body><p>Makes starting SleepyHead a bit slower, by pre-loading all the summary data in advance, which speeds up overview browsing and a few other calculations later on. </p><p>If you have a large amount of data, it might be worth keeping this switched off, but if you typically like to view <span style=" font-style:italic;">everything</span> in overview, all the summary data still has to be loaded anyway. </p><p>Note this setting doesn't affect waveform and event data, which is always demand loaded as needed.</p></body></html> + <html><head/><body><p>Cuts down on any unimportant confirmation dialogs during import.</p></body></html> - Pre-Load all summary data at startup + Import without asking for confirmation @@ -551,26 +587,39 @@ SleepyHead can keep a copy of this data if you ever need to reinstall.
    - - + + - <html><head/><body><p>Cuts down on any unimportant confirmation dialogs during import.</p></body></html> + <html><head/><body><p>Makes starting SleepyHead a bit slower, by pre-loading all the summary data in advance, which speeds up overview browsing and a few other calculations later on. </p><p>If you have a large amount of data, it might be worth keeping this switched off, but if you typically like to view <span style=" font-style:italic;">everything</span> in overview, all the summary data still has to be loaded anyway. </p><p>Note this setting doesn't affect waveform and event data, which is always demand loaded as needed.</p></body></html> - Import without asking for confirmation + Pre-Load all summary data at startup - + Bypass the login screen and load the most recent User Profile - Skip user selection screen + Auto-Launch CPAP Importer after application start-up + + + + Qt::Vertical + + + + 20 + 40 + + + + @@ -1661,7 +1710,7 @@ as this is the only value available on summary-only days. <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'.SF NS Text'; font-size:13pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt; font-weight:600;">Syncing Oximetry and CPAP Data</span></p> <p align="justify" style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'Sans'; font-size:10pt;"><br /></p> <p align="justify" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Sans'; font-size:10pt;">CMS50 data imported from SpO2Review (from .spoR files) or the serial import method does </span><span style=" font-family:'Sans'; font-size:10pt; font-weight:600; text-decoration: underline;">not</span><span style=" font-family:'Sans'; font-size:10pt;"> have the correct timestamp needed to sync.</span></p> @@ -1887,6 +1936,13 @@ Mainly affects the importer. + + + + Show Remove Card reminder notification on SleepyHead shutdown + + + @@ -2146,7 +2202,101 @@ p, li { white-space: pre-wrap; } Graph Settings - + + + + On Opening + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + Which tab to open at startup. (Note: It will default to Profile if there is no open Profile) + + + Profile + + + + Profile + + + + + Welcome + + + + + Daily + + + + + Overview + + + + + Statistics + + + + + + + + Switch Tabs + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + No change + + + + + Welcome + + + + + Daily + + + + + Overview + + + + + Statistics + + + + + + + + After Import + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + Graph Height @@ -2156,7 +2306,7 @@ p, li { white-space: pre-wrap; } - + @@ -2172,7 +2322,7 @@ p, li { white-space: pre-wrap; } - + Line Thickness @@ -2182,7 +2332,7 @@ p, li { white-space: pre-wrap; } - + @@ -2206,7 +2356,7 @@ p, li { white-space: pre-wrap; } - + @@ -2232,7 +2382,17 @@ p, li { white-space: pre-wrap; } - + + + + Tooltip Timeout + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + Scroll Dampening @@ -2242,7 +2402,7 @@ p, li { white-space: pre-wrap; } - + @@ -2302,17 +2462,53 @@ p, li { white-space: pre-wrap; } - - + + - Tooltip Timeout - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + Graph Tooltips - + + + + + Bar Tops + + + + + Line Chart + + + + + + + + + 0 + 0 + + + + Default display height of graphs in pixels + + + 50 + + + 600 + + + 10 + + + 180 + + + + @@ -2369,53 +2565,7 @@ p, li { white-space: pre-wrap; } - - - - - 0 - 0 - - - - Default display height of graphs in pixels - - - 50 - - - 600 - - - 10 - - - 180 - - - - - - - Graph Tooltips - - - - - - - - Bar Tops - - - - - Line Chart - - - - - + Overview Linecharts @@ -2425,7 +2575,7 @@ p, li { white-space: pre-wrap; } - + Qt::Vertical diff --git a/sleepyhead/profileselect.cpp b/sleepyhead/profileselect.cpp index f4e36075..94e9c75d 100644 --- a/sleepyhead/profileselect.cpp +++ b/sleepyhead/profileselect.cpp @@ -1,4 +1,4 @@ -/* Profile Select Implementation (Login Screen) +/* Profile Select Implementation (Login Screen) * * Copyright (c) 2011-2018 Mark Watkins * @@ -43,11 +43,11 @@ ProfileSelect::ProfileSelect(QWidget *parent) : ui->listView->setFont(font); QFontMetrics fm(font); - for (QMap::iterator p = Profiles::profiles.begin(); - p != Profiles::profiles.end(); p++) { + QMap::iterator p; + for (p = Profiles::profiles.begin(); p != Profiles::profiles.end(); p++) { name = p.key(); - if (PREF.contains(STR_GEN_Profile) && (name == PREF[STR_GEN_Profile].toString())) { + if (AppSetting->profileName() == name) { sel = i; } @@ -86,13 +86,6 @@ ProfileSelect::ProfileSelect(QWidget *parent) : m_tries = 0; - /*PREF["SkipLogin"]=false; - if ((i==1) && PREF["SkipLogin"].toBool()) { - if (!Profiles::profiles.contains(name)) - PREF[STR_GEN_Profile]=name; - QTimer::singleShot(0,this,SLOT(earlyExit())); - hide(); - } */ popupMenu = new QMenu(this); popupMenu->addAction(tr("Open Profile"), this, SLOT(openProfile())); popupMenu->addAction(tr("Edit Profile"), this, SLOT(editProfile())); @@ -133,9 +126,6 @@ void ProfileSelect::editProfile() Profile *profile = Profiles::Get(name); if (!profile) { return; } - if (!profile->isOpen()) { - profile->Load(); - } bool reallyEdit = false; @@ -212,12 +202,13 @@ void ProfileSelect::deleteProfile() Profile * profile = Profiles::profiles[name]; p_profile = profile; - 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(GetAppRoot() + "/Profiles/" + profile->user->userName())), QMessageBox::Ok); - return; - } + // 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(GetAppRoot() + "/Profiles/" + profile->user->userName())), QMessageBox::Ok); +// return; +// } bool reallydelete = false; if (profile->user->hasPassword()) { QDialog dialog(this, Qt::Dialog); @@ -310,35 +301,7 @@ void ProfileSelect::on_listView_activated(const QModelIndex &index) if (!profile) { return; } if (!profile->isOpen()) { - QString lockhost = profile->checkLock(); - - if (!lockhost.isEmpty()) { - if (lockhost.compare(QHostInfo::localHostName()) == 0) { - // Localhost has it locked.. - if (QMessageBox::warning(nullptr, STR_MessageBox_Warning, - QObject::tr("There is a lockfile already present for profile '%1'.").arg(name)+"\n\n"+ - QObject::tr("You can only work with one instance of an individual SleepyHead profile at a time.")+"\n\n"+ - QObject::tr("Please close any other instances of SleepyHead running with this profile before proceeding.")+"\n\n"+ - QObject::tr("If no other instances of SleepyHead are running, (eg, it crashed last time!), it is safe to ignore this message."), - QMessageBox::Cancel |QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Cancel) { - return; - } - - } else { - if (QMessageBox::warning(nullptr, STR_MessageBox_Warning, - QObject::tr("There is a lockfile already present for this profile '%1', claimed on '%2'.").arg(name).arg(lockhost)+"\n\n"+ - QObject::tr("You can only work with one instance of an individual SleepyHead profile at a time.")+"\n\n"+ - QObject::tr("If you are using cloud storage, make sure SleepyHead is closed and syncing has completed first on the other computer before proceeding."), - QMessageBox::Cancel |QMessageBox::Ok, QMessageBox::Cancel) == QMessageBox::Cancel) { - return; - } - } - - profile->removeLock(); - } - p_profile = profile; - profile->Load(); // 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 @@ -366,7 +329,7 @@ void ProfileSelect::on_listView_activated(const QModelIndex &index) if (profile->user->checkPassword(e->text())) { m_selectedProfile = name; - PREF[STR_GEN_Profile] = name; + AppSetting->setProfileName(name); accept(); return; } diff --git a/sleepyhead/profileselect.ui b/sleepyhead/profileselect.ui index f3bf559a..7f6feb3d 100644 --- a/sleepyhead/profileselect.ui +++ b/sleepyhead/profileselect.ui @@ -139,6 +139,9 @@ background: white; QAbstractScrollArea::AdjustToContents + + true + QAbstractItemView::ScrollPerPixel @@ -162,7 +165,7 @@ background: qlineargradient(spread:pad, x1:0, y1:0, x2:1, y2:0, stop:0 rgba(255, - -1 + 6 16 diff --git a/sleepyhead/profileselector.cpp b/sleepyhead/profileselector.cpp new file mode 100644 index 00000000..a0d44940 --- /dev/null +++ b/sleepyhead/profileselector.cpp @@ -0,0 +1,257 @@ + +#include "profileselector.h" +#include "ui_profileselector.h" + +#include "SleepLib/profiles.h" +#include "daily.h" +#include "overview.h" +#include "statistics.h" +#include "mainwindow.h" +#include "newprofile.h" + +extern MainWindow * mainwin; + +MySortFilterProxyModel2::MySortFilterProxyModel2(QObject *parent) + : QSortFilterProxyModel(parent) +{ + +} + +bool MySortFilterProxyModel2::filterAcceptsRow(int sourceRow, + const QModelIndex &sourceParent) const +{ + QModelIndex index0 = sourceModel()->index(sourceRow, 0, sourceParent); + QModelIndex index1 = sourceModel()->index(sourceRow, 1, sourceParent); + QModelIndex index2 = sourceModel()->index(sourceRow, 2, sourceParent); + QModelIndex index5 = sourceModel()->index(sourceRow, 5, sourceParent); + + return (sourceModel()->data(index0).toString().contains(filterRegExp()) + || sourceModel()->data(index1).toString().contains(filterRegExp()) + || sourceModel()->data(index2).toString().contains(filterRegExp()) + || sourceModel()->data(index5).toString().contains(filterRegExp()) + ); +} + + +ProfileSelector::ProfileSelector(QWidget *parent) : + QWidget(parent), + ui(new Ui::ProfileSelector) +{ + ui->setupUi(this); + model = nullptr; + proxy = nullptr; + + ui->versionLabel->setText(VersionString); +} + +ProfileSelector::~ProfileSelector() +{ + delete ui; +} + + +void ProfileSelector::updateProfileList() +{ + QString name; + int w=0; + if (proxy) delete proxy; + if (model) delete model; + + const int columns = 6; + model = new QStandardItemModel(0, columns, this); + model->setHeaderData(0, Qt::Horizontal, tr("Profile")); + model->setHeaderData(1, Qt::Horizontal, tr("Ventilator Brand")); + model->setHeaderData(2, Qt::Horizontal, tr("Ventilator Model")); + model->setHeaderData(3, Qt::Horizontal, tr("Other Data")); + model->setHeaderData(4, Qt::Horizontal, tr("Last Imported")); + model->setHeaderData(5, Qt::Horizontal, tr("Name")); + + ui->profileView->setStyleSheet("QHeaderView::section { background-color:lightgrey }"); + + int row = 0; + int sel = -1; + + QFontMetrics fm(ui->profileView->font()); + + QMap::iterator pi; + for (pi = Profiles::profiles.begin(); pi != Profiles::profiles.end(); pi++) { + Profile *prof = pi.value(); + name = pi.key(); + + if (AppSetting->profileName() == name) { + sel = row; + } + + Machine * mach = prof->GetMachine(MT_CPAP); // only interested in last cpap machine... + if (!mach) { + qDebug() << "Couldn't find machine info for" << name; + } + + model->insertRows(row, 1, QModelIndex()); + // Problem: Can't access profile details until it's loaded. + QString usersname; + if (!prof->user->lastName().isEmpty()) { + usersname = tr("%1, %2").arg(prof->user->lastName()).arg(prof->user->firstName()); + } + + model->setData(model->index(row, 0, QModelIndex()), name); + + model->setData(model->index(row, 0, QModelIndex()), name, Qt::UserRole+2); + model->setData(model->index(row, 5, QModelIndex()), usersname); + if (mach) { + model->setData(model->index(row, 1, QModelIndex()), mach->brand()); + model->setData(model->index(row, 2, QModelIndex()), mach->series()+" "+mach->model()); + model->setData(model->index(row, 4, QModelIndex()), mach->lastImported().toString(Qt::SystemLocaleShortDate)); + } + QBrush bg = QColor(Qt::black); + if (prof == p_profile) { + bg = QBrush(Qt::red); + } + for (int i=0; isetData(model->index(row, i, QModelIndex()), bg, Qt::ForegroundRole); + } + + 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); + row++; + } + w+=20; +// ui->profileView->setMinimumWidth(w); + + proxy = new MySortFilterProxyModel2(this); + proxy->setSourceModel(model); + proxy->setSortCaseSensitivity(Qt::CaseInsensitive); + + + ui->profileView->setModel(proxy); + ui->profileView->setSelectionBehavior(QAbstractItemView::SelectRows); + ui->profileView->setSelectionMode(QAbstractItemView::SingleSelection); + + QHeaderView *headerView = ui->profileView->horizontalHeader(); + headerView->setStretchLastSection(true); + headerView->setSectionResizeMode(QHeaderView::Stretch); + + QPalette* palette = new QPalette(); + palette->setColor(QPalette::Highlight,QColor("#3a7fc2")); + palette->setColor(QPalette::HighlightedText, QColor("white")); + + ui->profileView->setPalette(*palette); + + +} + +void ProfileSelector::updateProfileHighlight(QString name) +{ + QBrush bg = QColor(Qt::black); + for (int row=0;row < model->rowCount(); row++) { + for (int i=0; icolumnCount(); i++) { + model->setData(model->index(row, i, QModelIndex()), bg, Qt::ForegroundRole); + } + } + bg = QBrush(Qt::red); + for (int row=0;row < proxy->rowCount(); row++) { + if (proxy->data(proxy->index(row, 0, QModelIndex())).toString().compare(name)==0) { + for (int i=0; icolumnCount(); i++) { + proxy->setData(proxy->index(row, i, QModelIndex()), bg, Qt::ForegroundRole); + } + break; + } + } +} + +void ProfileSelector::SelectProfile(QString profname) +{ + qDebug() << "Selecting new profile" << profname; + + Profile * prof = Profiles::profiles[profname]; + + if (prof != p_profile) { + + // Unselect everything in ProfileView + + mainwin->OpenProfile(profname); + updateProfileHighlight(profname); + } + +} + +void ProfileSelector::on_profileView_doubleClicked(const QModelIndex &index) +{ + QModelIndex idx = proxy->index(index.row(), 0, QModelIndex()); + QString profname = proxy->data(idx, Qt::UserRole+2).toString(); + + SelectProfile(profname); +} + +void ProfileSelector::on_profileFilter_textChanged(const QString &arg1) +{ + QRegExp regExp("*"+arg1+"*", Qt::CaseInsensitive, QRegExp::Wildcard); + proxy->setFilterRegExp(regExp); +} + +void ProfileSelector::on_buttonOpenProfile_clicked() +{ + if (ui->profileView->currentIndex().isValid()) { + QString name = proxy->data(proxy->index(ui->profileView->currentIndex().row(), 0, QModelIndex()), Qt::UserRole+2).toString(); + qDebug() << "Opening" << name; + SelectProfile(name); + } +} + +void ProfileSelector::on_buttonEditProfile_clicked() +{ + if (ui->profileView->currentIndex().isValid()) { + QString name = proxy->data(proxy->index(ui->profileView->currentIndex().row(), 0, QModelIndex()), Qt::UserRole+2).toString(); + qDebug() << "Editing" << name; + + Profile * prof = Profiles::profiles[name]; + //SelectProfile(name); // may not be necessary... + + NewProfile *newprof = new NewProfile(this); + newprof->edit(name); + newprof->setWindowModality(Qt::ApplicationModal); + newprof->setModal(true); + if (newprof->exec() != NewProfile::Rejected) { + QString usersname; + if (!prof->user->lastName().isEmpty()) { + usersname = tr("%1, %2").arg(prof->user->lastName()).arg(prof->user->firstName()); + } + + proxy->setData(proxy->index(ui->profileView->currentIndex().row(), 5, QModelIndex()), usersname); + } + + delete newprof; + } +} + +void ProfileSelector::on_buttonNewProfile_clicked() +{ + if (p_profile) + mainwin->CloseProfile(); + + NewProfile *newprof = new NewProfile(this); + newprof->skipWelcomeScreen(); + newprof->setWindowModality(Qt::ApplicationModal); + newprof->setModal(true); + if (newprof->exec() == NewProfile::Accepted) { + updateProfileList(); + p_profile = Profiles::Get(AppSetting->profileName()); + Q_ASSERT(p_profile != nullptr); + QString name = p_profile->user->userName(); + p_profile = nullptr; + SelectProfile(name); + } + delete newprof; +} + +void ProfileSelector::on_buttonDestroyProfile_clicked() +{ + if (ui->profileView->currentIndex().isValid()) { + QString name = proxy->data(proxy->index(ui->profileView->currentIndex().row(), 0, QModelIndex()), Qt::UserRole+2).toString(); + qDebug() << "Destroying" << name; + } +} diff --git a/sleepyhead/profileselector.h b/sleepyhead/profileselector.h new file mode 100644 index 00000000..4ffba045 --- /dev/null +++ b/sleepyhead/profileselector.h @@ -0,0 +1,53 @@ +#ifndef PROFILESELECTOR_H +#define PROFILESELECTOR_H + +#include +#include +#include + +namespace Ui { +class ProfileSelector; +} + +class MySortFilterProxyModel2:public QSortFilterProxyModel +{ + Q_OBJECT + public: + MySortFilterProxyModel2(QObject *parent = 0); + + bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const; +}; + +class ProfileSelector : public QWidget +{ + Q_OBJECT + +public: + explicit ProfileSelector(QWidget *parent = 0); + ~ProfileSelector(); + + void updateProfileList(); + void SelectProfile(QString profname); + void updateProfileHighlight(QString name); + +private slots: + void on_profileView_doubleClicked(const QModelIndex &index); + + void on_profileFilter_textChanged(const QString &arg1); + + void on_buttonOpenProfile_clicked(); + + void on_buttonEditProfile_clicked(); + + void on_buttonNewProfile_clicked(); + + void on_buttonDestroyProfile_clicked(); + +private: + Ui::ProfileSelector *ui; + QStandardItemModel *model; + MySortFilterProxyModel2 *proxy; + +}; + +#endif // PROFILESELECTOR_H diff --git a/sleepyhead/profileselector.ui b/sleepyhead/profileselector.ui new file mode 100644 index 00000000..5c454510 --- /dev/null +++ b/sleepyhead/profileselector.ui @@ -0,0 +1,214 @@ + + + ProfileSelector + + + + 0 + 0 + 912 + 696 + + + + Form + + + + + + + + + + + + Filter: + + + + + + + + + + ... + + + + :/icons/refresh.png:/icons/refresh.png + + + + + + + + + Qt::ClickFocus + + + Qt::CustomContextMenu + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + true + + + false + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + 128 + 128 + + + + + + + :/icons/bob-v3.0.png + + + true + + + + + + + + 13 + 75 + true + + + + SleepyHead + + + Qt::AlignCenter + + + + + + + + false + + + + Version + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + + + + &Open Profile + + + + + + + &Edit Profile + + + + + + + Qt::Horizontal + + + + + + + &New Profile + + + + + + + Qt::Vertical + + + + 20 + 339 + + + + + + + + Destroy Profile + + + + + + + + + + + + + + + diff --git a/sleepyhead/reports.cpp b/sleepyhead/reports.cpp index 39c23352..935722e0 100644 --- a/sleepyhead/reports.cpp +++ b/sleepyhead/reports.cpp @@ -64,7 +64,7 @@ void Report::PrintReport(gGraphView *gv, QString name, QDate date) QPrinter *printer; - bool aa_setting = p_profile->appearance->antiAliasing(); + bool aa_setting = AppSetting->antiAliasing(); bool force_antialiasing = aa_setting; @@ -391,8 +391,8 @@ void Report::PrintReport(gGraphView *gv, QString name, QDate date) } qint64 st = savest, et = saveet; - bool lineCursorMode = p_profile->appearance->lineCursorMode(); - p_profile->appearance->setLineCursorMode(false); + bool lineCursorMode = AppSetting->lineCursorMode(); + AppSetting->setLineCursorMode(false); if (name == STR_TR_Daily) { if (!print_bookmarks) { @@ -587,7 +587,7 @@ void Report::PrintReport(gGraphView *gv, QString name, QDate date) top += bounds.height(); } else { top += normal_height / 2; } - p_profile->appearance->setAntiAliasing(force_antialiasing); + AppSetting->setAntiAliasing(force_antialiasing); int tmb = g->m_marginbottom; g->m_marginbottom = 0; @@ -601,7 +601,7 @@ void Report::PrintReport(gGraphView *gv, QString name, QDate date) //g->showTitle(true); //painter.endNativePainting(); g->m_marginbottom = tmb; - p_profile->appearance->setAntiAliasing(aa_setting); + AppSetting->setAntiAliasing(aa_setting); if (!pm.isNull()) { @@ -629,6 +629,6 @@ void Report::PrintReport(gGraphView *gv, QString name, QDate date) painter.end(); delete printer; mainwin->Notify(QObject::tr("SleepyHead has finished sending the job to the printer.")); - p_profile->appearance->setLineCursorMode(lineCursorMode); + AppSetting->setLineCursorMode(lineCursorMode); } diff --git a/sleepyhead/sleepyhead.pro b/sleepyhead/sleepyhead.pro index e61f1465..d0f2d281 100644 --- a/sleepyhead/sleepyhead.pro +++ b/sleepyhead/sleepyhead.pro @@ -28,8 +28,7 @@ contains(DEFINES, BrokenGL) { } QT += opengl -#The following forces ResMed session locking.. it *may* not be necessary.. I'm still trying to assess this properly. -#Blah blah.. this isn't connected anymore.. just leaving it until I have the energy to clean it up +#ResMed summary data design is SHIT... SleepyHead *MUST* follow ResMed's idiocy. DEFINES += LOCK_RESMED_SESSIONS #CONFIG += c++11 @@ -163,7 +162,8 @@ SOURCES += \ Graphs/MinutesAtPressure.cpp \ SleepLib/journal.cpp \ SleepLib/progressdialog.cpp \ - SleepLib/loader_plugins/cms50f37_loader.cpp + SleepLib/loader_plugins/cms50f37_loader.cpp \ + profileselector.cpp HEADERS += \ common_gui.h \ @@ -225,7 +225,9 @@ HEADERS += \ SleepLib/journal.h \ SleepLib/progressdialog.h \ SleepLib/loader_plugins/cms50f37_loader.h \ - build_number.h + build_number.h \ + profileselector.h \ + SleepLib/appsettings.h FORMS += \ daily.ui \ @@ -238,7 +240,8 @@ FORMS += \ newprofile.ui \ exportcsv.ui \ UpdaterWindow.ui \ - oximeterimport.ui + oximeterimport.ui \ + profileselector.ui RESOURCES += \ Resources.qrc diff --git a/sleepyhead/statistics.cpp b/sleepyhead/statistics.cpp index 41135de3..f8a1d738 100644 --- a/sleepyhead/statistics.cpp +++ b/sleepyhead/statistics.cpp @@ -41,7 +41,7 @@ QDataStream & operator>>(QDataStream & in, RXItem & rx) MachineLoader * loader = GetLoader(loadername); if (loader) { - rx.machine = loader->lookupMachine(serial); + rx.machine = p_profile->lookupMachine(serial, loadername); } else { qDebug() << "Bad machine object" << loadername << serial; rx.machine = nullptr; diff --git a/sleepyhead/version.h b/sleepyhead/version.h index f20bdd1f..6ff8fc65 100644 --- a/sleepyhead/version.h +++ b/sleepyhead/version.h @@ -13,10 +13,10 @@ #include "build_number.h" const int major_version = 1; // incompatible API changes -const int minor_version = 0; // new features that don't break things +const int minor_version = 1; // new features that don't break things const int revision_number = 0; // bugfixes, revisions +const QString ReleaseStatus = "unstable"; // testing/nightly/unstable, beta/untamed, rc/almost, r/stable -const QString ReleaseStatus = "beta"; const QString VersionString = QString("%1.%2.%3-%4-%5").arg(major_version).arg(minor_version).arg(revision_number).arg(ReleaseStatus).arg(build_number); #ifdef Q_OS_MAC diff --git a/sleepyhead/welcome.cpp b/sleepyhead/welcome.cpp index 36cf0aa5..2b2832b0 100644 --- a/sleepyhead/welcome.cpp +++ b/sleepyhead/welcome.cpp @@ -1,4 +1,4 @@ -/* Welcome Page Implementation +/* Welcome Page Implementation * * Copyright (c) 2011-2018 Mark Watkins * @@ -10,6 +10,7 @@ #include #include #include +#include #include "SleepLib/profiles.h" @@ -89,7 +90,7 @@ QString GenerateWelcomeHTML() ""; html += "
     
    %1
    "; - html += QString("
    ") + + html += QString("
    ") + "" "

    " + QObject::tr("Welcome to SleepyHead") + "

    " + @@ -97,7 +98,7 @@ QString GenerateWelcomeHTML() ""; int cols=2; - if (havecpapdata || haveoximeterdata) cols=4; + if (havecpapdata || haveoximeterdata) cols=5; html+=QString("
    ").arg(cols)+ @@ -148,7 +149,7 @@ QString GenerateWelcomeHTML() html+=""+ QString("
    ").arg(date.toString(Qt::ISODate))+""+ - QObject::tr("The last time you used your %1...").arg(cpap->brand()+" "+cpap->model())+"
    "; + QObject::tr("The last time you used your %1...").arg(cpap->brand()+" "+cpap->series()+" "+cpap->model())+"
    "; int daysto = date.daysTo(QDate::currentDate()); QString daystring;