diff --git a/Htmldocs/release_notes.html b/Htmldocs/release_notes.html
index 4b196123..c8f8c362 100644
--- a/Htmldocs/release_notes.html
+++ b/Htmldocs/release_notes.html
@@ -17,9 +17,12 @@
- [new] Additional Philips Respironics devices tested and fully supported:
+ - DreamStation 2 CPAP (410X150C)
- BiPAP Auto (System One 60 Series) (761P)
+ - BiPAP autoSV Advanced 30 (System One 60 Series) (961TCA)
+ - [fix] Added support for pressure pulse, CA, and VS on BiPAP autoSV Advanced 30 (System One 60 Series) (960T).
Changes and fixes in OSCAR v1.3.5-alpha.2
diff --git a/oscar/Graphs/MinutesAtPressure.cpp b/oscar/Graphs/MinutesAtPressure.cpp
index 0f3a7c85..fafe8379 100644
--- a/oscar/Graphs/MinutesAtPressure.cpp
+++ b/oscar/Graphs/MinutesAtPressure.cpp
@@ -143,34 +143,10 @@ some messages from Apnea Board.
//#define MAP_LOG_EVENTS
//#define ENABLE_UNEVEN_MACHINE_MIN_MAX_TEST
-// <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
+/ <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
// Define Display macros to enhance displays
-#define DEBUGQ qDebug()
-#define DEBUGT qDebug()<
-#define DEBUG qDebug()<m_flags_enabled[ch];
}
- //DEBUGF << FULLNAME(ch) << O(value);
m_enabled[ch]=value;
}
};
diff --git a/oscar/Graphs/gGraph.cpp b/oscar/Graphs/gGraph.cpp
index 6b2645ac..057621e5 100644
--- a/oscar/Graphs/gGraph.cpp
+++ b/oscar/Graphs/gGraph.cpp
@@ -7,6 +7,9 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
+#define TEST_MACROS_ENABLEDoff
+#include "test_macros.h"
+
#include "Graphs/gGraph.h"
#include
@@ -131,6 +134,7 @@ gGraph::gGraph(QString name, gGraphView *graphview, QString title, QString units
m_units(units),
m_visible(true)
{
+ // DEBUGF Q(name) Q(title) QQ("UNITS",units) Q(height) Q(group);
if (height == 0) {
height = AppSetting->graphHeight();
Q_UNUSED(height)
@@ -153,7 +157,7 @@ gGraph::gGraph(QString name, gGraphView *graphview, QString title, QString units
min_x = min_y = 0;
rec_miny = rec_maxy = 0;
rphysmax_y = rphysmin_y = 0;
- m_zoomY = 0;
+ m_zoomY = ZS_AUTO_FIT;
m_selectedDuration = 0;
if (graphview) {
@@ -281,9 +285,23 @@ void gGraph::setDay(Day *day)
// ResetBounds();
}
-void gGraph::setZoomY(short zoom)
-{
- m_zoomY = zoom;
+void gGraph::setZoomY(ZoomyScaling zoomy) {
+ m_zoomY = zoomy;
+ dynamicScalingOn =false;
+ timedRedraw(0);
+}
+
+void gGraph::mouseDoubleClickYAxis(QMouseEvent * ) {
+ if ( isDynamicScalingEnabled() ) {
+ dynamicScalingOn = !dynamicScalingOn;
+ } else {
+ dynamicScalingOn =false;
+ if (m_zoomY == ZS_AUTO_FIT ) {
+ m_zoomY = ZS_DEFAULT;
+ } else if (m_zoomY == ZS_DEFAULT) {
+ m_zoomY = ZS_AUTO_FIT ;
+ }
+ }
timedRedraw(0);
}
@@ -564,22 +582,67 @@ void gGraph::ToolTip(QString text, int x, int y, ToolTipAlignment align, int tim
m_graphview->m_tooltip->display(text, x, y, align, timeout);
}
+bool gGraph::isDynamicScalingEnabled() {
+ return ((m_lineChart_layer!=nullptr) && AppSetting->allowYAxisScaling() );
+}
+
+QString gGraph::unitsTooltip() {
+ if (isDynamicScalingEnabled()) {
+ if(dynamicScalingOn) {
+ if (zoomY() == ZS_AUTO_FIT ) {
+ return QString("%1%2%3").arg(m_units).arg("\n").arg(tr("Double click Y-axis: Return to AUTO-FIT Scaling"));
+ } else if (zoomY() == ZS_DEFAULT ) {
+ return QString("%1%2%3").arg(m_units).arg("\n").arg(tr("Double click Y-axis: Return to DEFAULT Scaling"));
+ } else {
+ return QString("%1%2%3").arg(m_units).arg("\n").arg(tr("Double click Y-axis: Return to OVERRIDE Scaling"));
+ }
+ } else {
+ return QString("%1%2%3").arg(m_units).arg("\n").arg(tr("Double click Y-axis: For Dynamic Scaling"));
+ }
+ } else {
+ if (zoomY() == ZS_AUTO_FIT ) {
+ return QString("%1%2%3").arg(m_units).arg("\n").arg(tr("Double click Y-axis: Select DEFAULT Scaling"));
+ } else if (zoomY() == ZS_DEFAULT ) {
+ return QString("%1%2%3").arg(m_units).arg("\n").arg(tr("Double click Y-axis: Select AUTO-FIT Scaling"));
+ }
+ }
+ return m_units;
+}
+
+void gGraph::dynamicScaling(EventDataType &miny, EventDataType &maxy) {
+ // Have new Dynamic mode;
+ miny = m_lineChart_layer->actualMinY();
+ maxy = m_lineChart_layer->actualMaxY();
+ EventDataType diff= (maxy-miny);
+ maxy += diff*0.08; // more space at top for event ticks.
+ miny -= diff*0.04;
+ if (m_saved_minY!=m_lineChart_layer->actualMinY() || m_saved_maxY!=m_lineChart_layer->actualMaxY() ) {
+ // DEBUGF O(m_name) Q(m_saved_minY) QQ("==>",m_lineChart_layer->actualMinY() ) QQ("==>",miny) Q(m_saved_maxY) QQ("==>",m_lineChart_layer->actualMaxY() ) QQ("==>",maxy);
+ m_saved_minY=m_lineChart_layer->actualMinY();
+ m_saved_maxY=m_lineChart_layer->actualMaxY();
+ }
+}
+
// YAxis Autoscaling code
void gGraph::roundY(EventDataType &miny, EventDataType &maxy)
{
-
- if (zoomY() == 2) {
+ if (dynamicScalingOn) {
+ dynamicScaling(miny, maxy) ;
+ if (maxy > miny) return;
+ };
+ if (zoomY() == ZS_OVERRIDE) { // Have override mode
+ // set min and max to override values.
miny = rec_miny;
maxy = rec_maxy;
- if (maxy > miny) return;
- } else if (zoomY() ==1) {
+ if (maxy > miny) return; // Not Autoscaling
+ } else if (zoomY() ==ZS_DEFAULT) { // Have Default mode
+ // set min and max to physical Min / max values.
miny = physMinY();
maxy = physMaxY();
- if (maxy > miny) return;
+ if (maxy > miny) return; // Not Autoscaling
}
miny = MinY();
maxy = MaxY();
-
int m, t;
bool ymin_good = false, ymax_good = false;
@@ -685,6 +748,11 @@ void gGraph::AddLayer(Layer *l, LayerPosition position, short width, short heigh
l->setPos(x, y);
l->addref();
m_layers.push_back(l);
+
+ if (l->layerType()==LT_LineChart) {
+ m_lineChart_layer = dynamic_cast(l);
+ }
+
}
void gGraph::dataChanged()
@@ -1111,24 +1179,6 @@ void gGraph::mouseDoubleClickEvent(QMouseEvent *event)
layer->mouseDoubleClickEvent(event, this);
}
}
-
- //int w=m_lastbounds.width()-(m_marginleft+left+right+m_marginright);
- //int h=m_lastbounds.height()-(bottom+m_marginbottom);
- //int x2=m_graphview->pointClicked().x(),y2=m_graphview->pointClicked().y();
- // if ((m_graphview->horizTravel()left+m_marginleft && xtop+m_margintop && ybutton() & Qt::RightButton) {
- // ZoomX(1.66,x); // Zoon out
- // return;
- // } else if (event->button() & Qt::LeftButton) {
- // ZoomX(0.75/2.0,x); // zoom in.
- // return;
- // }
- // } else {
- // Propagate the events to graph Layers
- // }
- //mousePressEvent(event);
- //mouseReleaseEvent(event);
- //qDebug() << m_title << "Double Clicked" << event->x() << event->y();
}
void gGraph::keyPressEvent(QKeyEvent *event)
{
@@ -1416,16 +1466,14 @@ void gGraph::SetMaxY(EventDataType v)
Layer *gGraph::getLineChart()
{
- gLineChart *lc;
-
+ if (m_lineChart_layer) return m_lineChart_layer;
for (auto & layer : m_layers) {
- lc = dynamic_cast(layer);
-
- if (lc) { return lc; }
+ Layer *tmp = dynamic_cast(layer);
+ if (tmp) m_lineChart_layer = tmp;
}
-
return nullptr;
}
+
int gGraph::minHeight()
{
int minheight = m_min_height;
diff --git a/oscar/Graphs/gGraph.h b/oscar/Graphs/gGraph.h
index 4fc78406..c79b8d79 100644
--- a/oscar/Graphs/gGraph.h
+++ b/oscar/Graphs/gGraph.h
@@ -35,6 +35,8 @@ void DestroyGraphGlobals();
const int mouse_movement_threshold = 6;
+enum ZoomyScaling {ZS_AUTO_FIT =0 , ZS_DEFAULT =1 , ZS_OVERRIDE =2};
+
float CatmullRomSpline(float p0, float p1, float p2, float p3, float t = 0.5);
inline void GetTextExtent(QString text, int &width, int &height, QFont *font)
@@ -164,6 +166,9 @@ class gGraph : public QObject
//! \brief Returns the measurement Units the Y scale is referring to
QString units() { return m_units; }
+ //! \brief Returns the measurement Units the Y scale is referring to plus tooltip.
+ QString unitsTooltip() ;
+
//! \brief Sets the measurement Units the Y scale is referring to
void setUnits(const QString units) { m_units = units; }
@@ -303,9 +308,14 @@ class gGraph : public QObject
bool isSnapshot() { return m_snapshot; }
void setSnapshot(bool b) { m_snapshot = b; }
+ // returns if graph is a daily line chart with Dynamic Scaling mode enabled.
+ bool isDynamicScalingEnabled();
+
short left, right, top, bottom; // dirty magin hacks..
Layer *getLineChart();
+ Layer *m_lineChart_layer =nullptr ;
+ bool dynamicScalingOn =false;
QTimer *timer;
// This gets set to true to force a redraw of the yAxis tickers when graphs are resized.
@@ -318,10 +328,8 @@ class gGraph : public QObject
gGraphView *graphView() { return m_graphview; }
short m_marginleft, m_marginright, m_margintop, m_marginbottom;
- inline short zoomY() { return m_zoomY; }
- void setZoomY(short zoom);
-
- static const short maxZoomY = 2;
+ inline ZoomyScaling zoomY() { return m_zoomY; }
+ void setZoomY(ZoomyScaling zoomy);
inline qint64 selectedDuration() const { return m_selectedDuration; }
inline QString selDurString() const { return m_selDurString; }
@@ -331,6 +339,8 @@ class gGraph : public QObject
inline bool printing() const { return m_printing; }
+ void mouseDoubleClickYAxis(QMouseEvent *event);
+
protected:
//! \brief Mouse Wheel events
virtual void wheelEvent(QWheelEvent *event);
@@ -353,6 +363,8 @@ class gGraph : public QObject
//! \brief Key Pressed event
virtual void keyReleaseEvent(QKeyEvent *event);
+ void dynamicScaling(EventDataType &miny, EventDataType &maxy);
+
void cancelSelection() {
m_selecting_area = false;
m_selection = QRect(0,0,0,0);
@@ -389,7 +401,7 @@ class gGraph : public QObject
bool m_showTitle;
bool m_printing;
bool m_pinned;
- short m_zoomY;
+ ZoomyScaling m_zoomY;
bool m_block_select;
QRect m_rect;
@@ -401,6 +413,8 @@ class gGraph : public QObject
QString m_selDurString;
bool m_snapshot;
+ EventDataType m_saved_minY=0;
+ EventDataType m_saved_maxY=0;
protected slots:
//! \brief Deselects any highlights, and schedules a main gGraphView redraw
diff --git a/oscar/Graphs/gGraphView.cpp b/oscar/Graphs/gGraphView.cpp
index 3e25a339..9a915513 100644
--- a/oscar/Graphs/gGraphView.cpp
+++ b/oscar/Graphs/gGraphView.cpp
@@ -7,19 +7,9 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
-#define xDEBUG_FUNCTIONS
-#ifdef DEBUG_FUNCTIONS
-#include
-#define DEBUG qDebug()<isActive()) {
timer->stop();
}
-
timer->setSingleShot(true);
timer->start(timeout);
- m_invalidate = true;
}
void gToolTip::cancel()
@@ -151,17 +139,14 @@ void gToolTip::cancel()
timer->stop();
}
-void gToolTip::paint(QPainter &painter) //actually paints it.
+QRect gToolTip::calculateRect(QPainter &painter)
{
- if (!m_visible) { return; }
-
int x = m_pos.x();
int y = m_pos.y();
QRect rect(x, y, 0, 0);
- painter.setFont(*defaultfont);
-
+ painter.setFont(*m_font);
rect = painter.boundingRect(rect, Qt::AlignCenter, m_text);
int w = rect.width() + m_spacer * 2;
@@ -201,19 +186,25 @@ void gToolTip::paint(QPainter &painter) //actually paints it.
rect.setTop(rect.top()-bot);
rect.setBottom(m_graphview->height());
}
+ return rect;
+}
+void gToolTip::paint(QPainter &painter) //actually paints it.
+{
+ if (!m_visible) { return; }
+ QRect a_rect=calculateRect(painter);
QBrush brush(QColor(255, 255, 128, 230));
brush.setStyle(Qt::SolidPattern);
painter.setBrush(brush);
painter.setPen(QColor(0, 0, 0, 255));
- painter.drawRoundedRect(rect, 5, 5);
+ painter.drawRoundedRect(a_rect, 5, 5);
painter.setBrush(Qt::black);
- painter.setFont(*defaultfont);
+ painter.setFont(*m_font);
- painter.drawText(rect, Qt::AlignCenter, m_text);
+ painter.drawText(a_rect, Qt::AlignCenter, m_text);
}
void gToolTip::timerDone()
@@ -223,6 +214,88 @@ void gToolTip::timerDone()
m_graphview->resetMouse();
}
+/* Parent tool tip
+ Allow the parent (overview or daily) to add tooltip or short messages to the user.
+ The basic problem is that the parent does not know the current dimensions of the graph view.
+ the parent does have knowledge of the location of fixed widgets which makes it possible to
+ locate tool tips in an appropiate location.
+*/
+gParentToolTip::gParentToolTip(gGraphView *graphview)
+ : gToolTip(graphview) {
+ m_parent_visible=false;
+}
+
+gParentToolTip::~gParentToolTip() {
+}
+
+void gParentToolTip::display(gGraphView* gv,QString text, int verticalOffset, int alignOffset, ToolTipAlignment align , int timeout ,QFont *font ) {
+ m_text=text;
+ m_verticalOffset=verticalOffset;
+ m_alignOffset=alignOffset;
+ m_alignment=align;
+ m_timeout=timeout;
+ m_font=font;
+ m_parent_visible=true;
+ gv->timedRedraw(0);
+};
+
+
+QRect gParentToolTip::calculateRect(QPainter& painter ) {
+ QRect rect(0, 0, 0, 0);
+ painter.setFont(*m_font);
+ rect = painter.boundingRect(rect, m_alignment, m_text);
+
+ // update space arround text
+ int space=2*m_spacer;
+ rect.setHeight(space+rect.height());
+ rect.setWidth(space+rect.width());
+
+ rect.moveTo(m_alignOffset,m_height-(m_verticalOffset+rect.height()));
+
+ // move rect accounding to alignment. default is left.
+
+ if (m_alignment == TT_AlignRight) {
+ // move rect left by width of rect. if <0 use 0;
+ rect.moveLeft(rect.left()-rect.width());
+ } else if (m_alignment == TT_AlignCenter) {
+ // left by 1/2 width of rect. if < 0 then use 0
+ rect.moveLeft(rect.left()-rect.width()/2);
+ }
+
+ if (rect.top()<0) {rect.setTop(0);};
+ if (rect.left()<0) {rect.setLeft(0);};
+
+ return rect;
+}
+
+void gParentToolTip::paint(QPainter &painter,int width,int height) {
+ if (!m_parent_visible) {return ;};
+ m_width=width;
+ m_height=height;
+ gToolTip::display(m_text, 0, 0,m_alignment, m_timeout);
+ gToolTip::paint(painter);
+};
+
+void gParentToolTip::timerDone() {
+ gToolTip::timerDone();
+ if (m_parent_visible) {
+ m_graphview->timedRedraw(0);
+ }
+ m_parent_visible=false;
+};
+
+void gParentToolTip::cancel() {
+ gToolTip::cancel();
+ m_parent_visible=false;
+};
+
+bool gParentToolTip::visible() {
+ return gToolTip::visible() && m_parent_visible;
+};
+
+
+
+
#ifdef ENABLE_THREADED_DRAWING
gThread::gThread(gGraphView *g)
@@ -359,6 +432,7 @@ gGraphView::gGraphView(QWidget *parent, gGraphView *shared, QWidget *caller)
masterlock = new QSemaphore(m_idealthreads);
#endif
m_tooltip = new gToolTip(this);
+ m_parent_tooltip = new gParentToolTip(this);
/*for (int i=0;iresize(width(),0);
// QScrollArea
}
-
//////// Create dock widget and resize dock to hold new widget
QDockWidget * newDockWidget = new QDockWidget(dock);
newDockWidget->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
@@ -634,6 +707,7 @@ gGraphView::~gGraphView()
}
delete m_tooltip;
+ delete m_parent_tooltip;
m_graphs.clear();
}
@@ -1499,6 +1573,7 @@ void gGraphView::paintGL()
AppSetting->usePixmapCaching() ? DrawTextQueCached(painter) :DrawTextQue(painter);
m_tooltip->paint(painter);
+ m_parent_tooltip->paint(painter,width(), height() );
#ifdef DEBUG_EFFICIENCY
const int rs = 20;
@@ -1973,8 +2048,8 @@ void MinMaxWidget::onMaxChanged(double d)
}
void MinMaxWidget::onResetClicked()
{
- int tmp = graph->zoomY();
- graph->setZoomY(0);
+ ZoomyScaling tmp = graph->zoomY();
+ graph->setZoomY(ZS_AUTO_FIT);
EventDataType miny = graph->MinY(),
maxy = graph->MaxY();
@@ -1996,15 +2071,16 @@ void MinMaxWidget::onResetClicked()
graph->setZoomY(tmp);
}
-void MinMaxWidget::onComboChanged(int idx)
+void MinMaxWidget::onComboChanged(int _idx)
{
- minbox->setEnabled(idx == 2);
- maxbox->setEnabled(idx == 2);
- reset->setEnabled(idx == 2);
+ ZoomyScaling idx = static_cast(_idx) ;
+ minbox->setEnabled(idx == ZS_OVERRIDE);
+ maxbox->setEnabled(idx == ZS_OVERRIDE);
+ reset->setEnabled(idx == ZS_OVERRIDE);
graph->setZoomY(idx);
- if (idx == 2) {
+ if (idx == ZS_OVERRIDE) {
if (qAbs(graph->rec_maxy - graph->rec_miny) < 0.0001) {
onResetClicked();
}
@@ -2018,9 +2094,9 @@ void MinMaxWidget::createLayout()
layout->setSpacing(4);
combobox = new QComboBox(this);
- combobox->addItem(tr("Auto-Fit"), 0);
- combobox->addItem(tr("Defaults"), 1);
- combobox->addItem(tr("Override"), 2);
+ combobox->addItem(tr("Auto-Fit"), ZS_AUTO_FIT);
+ combobox->addItem(tr("Defaults"), ZS_DEFAULT);
+ combobox->addItem(tr("Override"), ZS_OVERRIDE);
combobox->setToolTip(tr("The Y-Axis scaling mode, 'Auto-Fit' for automatic scaling, 'Defaults' for settings according to manufacturer, and 'Override' to choose your own."));
connect(combobox, SIGNAL(activated(int)), this, SLOT(onComboChanged(int)));
@@ -3445,7 +3521,7 @@ void gGraphView::SaveSettings(QString title)
out << graph->visible();
out << graph->RecMinY();
out << graph->RecMaxY();
- out << graph->zoomY();
+ out << (short)graph->zoomY(); // the return type of zoomY was changed from a short to an enum (int) so much type cast it here
out << (bool)graph->isPinned();
gLineChart * lc = dynamic_cast(findLayer(graph, LT_LineChart));
@@ -3575,7 +3651,7 @@ bool gGraphView::LoadSettings(QString title)
g->setVisible(vis);
g->setRecMinY(recminy);
g->setRecMaxY(recmaxy);
- g->setZoomY(zoomy);
+ g->setZoomY(static_cast(zoomy));
g->setPinned(pinned);
if (gvversion >= 4) {
diff --git a/oscar/Graphs/gGraphView.h b/oscar/Graphs/gGraphView.h
index 0dd6226e..1e1bfeea 100644
--- a/oscar/Graphs/gGraphView.h
+++ b/oscar/Graphs/gGraphView.h
@@ -240,10 +240,12 @@ class gToolTip : public QObject
virtual void paint(QPainter &paint); //actually paints it.
//! \brief Close the tooltip early.
- void cancel();
+ virtual void cancel();
//! \brief Returns true if the tooltip is currently visible
- bool visible() { return m_visible; }
+ virtual bool visible() { return m_visible; }
+
+ virtual QRect calculateRect(QPainter &painter);
protected:
gGraphView *m_graphview;
@@ -254,13 +256,37 @@ class gToolTip : public QObject
bool m_visible;
int m_spacer;
QImage m_image;
- bool m_invalidate;
ToolTipAlignment m_alignment;
+ QFont* m_font=defaultfont;
protected slots:
//! \brief Timeout to hide tooltip, and redraw without it.
- void timerDone();
+ virtual void timerDone();
+};
+
+class gParentToolTip : public gToolTip
+{
+ Q_OBJECT
+ public:
+ gParentToolTip(gGraphView *graphview);
+ ~gParentToolTip();
+ using gToolTip::display;
+ virtual void display(gGraphView* gv,QString text,int verticalOffset, int alignOffset , ToolTipAlignment align = TT_AlignCenter, int timeout = 0,QFont *font = defaultfont) ;
+ virtual void cancel();
+ virtual bool visible();
+ virtual QRect calculateRect(QPainter &painter);
+ using gToolTip::paint;
+ virtual void paint(QPainter &paint,int width,int height) ;
+ private:
+ int m_verticalOffset;
+ int m_alignOffset;
+ int m_width;
+ int m_height;
+ bool m_parent_visible;
+ int m_timeout;
+ protected slots:
+ virtual void timerDone();
};
struct SelectionHistoryItem {
@@ -425,6 +451,7 @@ class gGraphView
gGraph *m_selected_graph;
gToolTip *m_tooltip;
+ gParentToolTip *m_parent_tooltip;
QTimer *timer;
//! \brief Add the Text information to the Text Drawing Queue (called by gGraphs renderText method)
diff --git a/oscar/Graphs/gLineChart.cpp b/oscar/Graphs/gLineChart.cpp
index c56ad118..2077bdb3 100644
--- a/oscar/Graphs/gLineChart.cpp
+++ b/oscar/Graphs/gLineChart.cpp
@@ -7,6 +7,10 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
+
+#define TEST_MACROS_ENABLEDoff
+#include "test_macros.h"
+
#include "Graphs/gLineChart.h"
#include
@@ -275,6 +279,7 @@ skipcheck:
}
}
}
+
EventDataType gLineChart::Miny()
{
if (m_codes.size() == 0) return 0;
@@ -373,6 +378,7 @@ QString gLineChart::getMetaString(qint64 time)
// Time Domain Line Chart
void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
{
+ EventDataType actual_min_y, actual_max_y;
QRectF rect = region.boundingRect();
rect.translate(0.0f, 0.001f);
// TODO: Just use QRect directly.
@@ -395,6 +401,8 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
return;
}
+ actual_min_y = INT_MAX;
+ actual_max_y = -INT_MAX;
top++;
@@ -551,6 +559,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
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);
+ DEBUGF NAME(dot.code) Q(dot.type) QQ(y,(int)y) Q(ratioX) O(QLine(left + 1, y, left + 1 + width, y)) Q(legendx) O(dot.value) ;
}
}
@@ -742,6 +751,8 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
time += rate;
// This is much faster than QVector access.
data = *ptr * gain;
+ if (actual_min_y>data) { actual_min_y=data; }
+ if (actual_max_ydata) { actual_min_y=data; }
+ if (actual_max_ydata) { actual_min_y=data; }
+ if (actual_max_ydata) { actual_min_y=data; }
+ if (actual_max_y xst + width) { px = xst + width; }
-
-// lines.append(QLine(lastpx, lastpy, px, lastpy));
-// lines.append(QLine(px, lastpy, px, py));
- } // else {
- // Letting the scissor do the dirty work for non horizontal lines
- // This really should be changed, as it might be cause that weird
- // display glitch on Linux..
-
- lines.append(QLine(lastpx, lastpy, px, lastpy));
- lines.append(QLine(px, lastpy, px, py));
-// }
-
- lastpx = px;
- lastpy = py;
-
- if (time > maxx) {
- done = true; // Let this iteration finish.. (This point will be in far clipping)
- break;
- }
+ // Cap px to right margin
+ if (px > xst + width) { px = xst + width; }
+ }
+ //else {
+ // Letting the scissor do the dirty work for non horizontal lines
+ // This really should be changed, as it might be cause that weird
+ // display glitch on Linux..
+ //}
+ if (square_plot) {
+ lines.append(QLine(lastpx, lastpy, px, lastpy));
+ lines.append(QLine(px, lastpy, px, py));
+ } else {
+ lines.append(QLine(lastpx, lastpy, px, py));
}
- } else {
- for (; dptr < eptr; dptr++) {
- //for (int i=0;i xst + width) { px = xst + width; }
-
- // lines.append(QLine(lastpx, lastpy, px, py));
- } //else {
- // Letting the scissor do the dirty work for non horizontal lines
- // This really should be changed, as it might be cause that weird
- // display glitch on Linux..
- lines.append(QLine(lastpx, lastpy, px, py));
- //}
-
- lastpx = px;
- lastpy = py;
-
- if (time > maxx) { // Past right edge, abort further drawing..
- done = true;
- break;
- }
+ if (time > maxx) { // Past right edge, abort further drawing..
+ done = true;
+ break;
}
}
+
if (w.printing() && AppSetting->monochromePrinting()) {
painter.setPen(QPen(Qt::black, lineThickness + 0.5));
} else {
@@ -1127,9 +1111,7 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
double f = double(cnt) / hours; // / (sum / 3600.0);
QString txt = QObject::tr("Duration %1:%2:%3").arg(h,2,10,QChar('0')).arg(m,2,10,QChar('0')).arg(s,2,10,QChar('0')) + " "+
- QObject::tr("AHI %1").arg(f,0,'f',2);// +" " +
-// QObject::tr("Events %1").arg(cnt) + " " +
-// QObject::tr("Hours %1").arg(hours,0,'f',2);
+ QObject::tr("AHI %1").arg(f,0,'f',2);// +" "
if (linecursormode) txt+=lasttext;
@@ -1138,4 +1120,9 @@ void gLineChart::paint(QPainter &painter, gGraph &w, const QRegion ®ion)
// painter.setRenderHint(QPainter::Antialiasing, false);
+
+if (actual_max_y>0) {
+ m_actual_min_y=actual_min_y;
+ m_actual_max_y=actual_max_y;
+}
}
diff --git a/oscar/Graphs/gLineChart.h b/oscar/Graphs/gLineChart.h
index 15831373..ae550869 100644
--- a/oscar/Graphs/gLineChart.h
+++ b/oscar/Graphs/gLineChart.h
@@ -112,6 +112,12 @@ class gLineChart: public Layer
//! \brief Returns Maximum Y-axis value for this layer
virtual EventDataType Maxy();
+ //! \brief Returns Minimum Y-axis value for this layer
+ virtual EventDataType actualMinY() {return m_actual_min_y;};
+
+ //! \brief Returns Maximum Y-axis value for this layer
+ virtual EventDataType actualMaxY() {return m_actual_max_y;};
+
//! \brief Returns true if all subplots contain no data
virtual bool isEmpty();
@@ -164,6 +170,7 @@ class gLineChart: public Layer
bool m_report_empty;
bool m_square_plot;
bool m_disable_accel;
+ EventDataType m_actual_min_y=0,m_actual_max_y=0;
//! \brief Used by accelerated waveform plots. Must be >= Screen Resolution (or at least graph width)
static const int max_drawlist_size = 10000;
diff --git a/oscar/Graphs/gSessionTimesChart.cpp b/oscar/Graphs/gSessionTimesChart.cpp
index 1f9ec008..5bec0b86 100644
--- a/oscar/Graphs/gSessionTimesChart.cpp
+++ b/oscar/Graphs/gSessionTimesChart.cpp
@@ -7,19 +7,8 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
-#define xDEBUG_FUNCTIONS
-#ifdef DEBUG_FUNCTIONS
-#include
-#define DEBUG qDebug()<
#include
diff --git a/oscar/Graphs/gYAxis.cpp b/oscar/Graphs/gYAxis.cpp
index 37c9e7bc..b84e7310 100644
--- a/oscar/Graphs/gYAxis.cpp
+++ b/oscar/Graphs/gYAxis.cpp
@@ -7,6 +7,9 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
+#define TEST_MACROS_ENABLEDoff
+#include
+
#include "Graphs/gYAxis.h"
#include
@@ -20,6 +23,7 @@
#include
+
gXGrid::gXGrid(QColor col)
: Layer(NoChannel)
{
@@ -306,7 +310,7 @@ bool gYAxis::mouseMoveEvent(QMouseEvent *event, gGraph *graph)
int y = event->y();
if (!graph->units().isEmpty()) {
- graph->ToolTip(graph->units(), x+10, y+10, TT_AlignLeft);
+ graph->ToolTip(graph->unitsTooltip(), x+10, y+10, TT_AlignLeft);
// graph->redraw();
}
@@ -316,14 +320,8 @@ bool gYAxis::mouseMoveEvent(QMouseEvent *event, gGraph *graph)
bool gYAxis::mouseDoubleClickEvent(QMouseEvent *event, gGraph *graph)
{
if (graph) {
- // int x=event->x();
- // int y=event->y();
- short z = (graph->zoomY() + 1) % gGraph::maxZoomY;
- graph->setZoomY(z);
- qDebug() << "Mouse double clicked for" << graph->name() << z;
+ graph->mouseDoubleClickYAxis(event);
}
-
- Q_UNUSED(event);
return false;
}
diff --git a/oscar/Graphs/layer.cpp b/oscar/Graphs/layer.cpp
index d32e3a6c..a76faf29 100644
--- a/oscar/Graphs/layer.cpp
+++ b/oscar/Graphs/layer.cpp
@@ -7,6 +7,9 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
+#define TEST_MACROS_ENABLEDoff
+#include "test_macros.h"
+
#include "Graphs/layer.h"
Layer::~Layer()
diff --git a/oscar/Graphs/layer.h b/oscar/Graphs/layer.h
index 100e70e6..38c29ec2 100644
--- a/oscar/Graphs/layer.h
+++ b/oscar/Graphs/layer.h
@@ -230,6 +230,10 @@ public:
Q_UNUSED(graph);
return false;
}
+
+ virtual EventDataType actualMinY() {return 0;};
+ virtual EventDataType actualMaxY() {return 0;};
+
};
/*! \class LayerGroup
diff --git a/oscar/SleepLib/loader_plugins/prs1_loader.cpp b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
index 67196786..018022b5 100644
--- a/oscar/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_loader.cpp
@@ -132,6 +132,7 @@ static const PRS1TestedModel s_PRS1TestedModels[] = {
{ "700X130", 0, 6, "DreamStation Auto BiPAP" },
{ "700X150", 0, 6, "DreamStation Auto BiPAP" },
+ { "410X150C", 0, 6, "DreamStation 2 CPAP" },
{ "520X110C", 0, 6, "DreamStation 2 Auto CPAP Advanced" }, // based on bottom label, boot screen says "Advanced Auto CPAP"
{ "520X150C", 0, 6, "DreamStation 2 Auto CPAP Advanced" }, // from user report
{ "521X120C", 0, 6, "DreamStation 2 Auto CPAP Advanced with P-Flex" }, // inferred from 501X120 and presence of "P-Flex" on bottom label
@@ -141,6 +142,7 @@ static const PRS1TestedModel s_PRS1TestedModels[] = {
{ "960P", 5, 1, "BiPAP autoSV Advanced (System One 60 Series)" },
{ "961P", 5, 1, "BiPAP autoSV Advanced (System One 60 Series)" },
{ "960T", 5, 2, "BiPAP autoSV Advanced 30 (System One 60 Series)" }, // omits "(System One 60 Series)" on official reports
+ { "961TCA", 5, 2, "BiPAP autoSV Advanced 30 (System One 60 Series)" },
{ "900X110", 5, 3, "DreamStation BiPAP autoSV" },
{ "900X120", 5, 3, "DreamStation BiPAP autoSV" },
{ "900X150", 5, 3, "DreamStation BiPAP autoSV" },
diff --git a/oscar/SleepLib/loader_plugins/prs1_parser.cpp b/oscar/SleepLib/loader_plugins/prs1_parser.cpp
index a9f2c3a5..47a1bebb 100644
--- a/oscar/SleepLib/loader_plugins/prs1_parser.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_parser.cpp
@@ -713,12 +713,11 @@ void PRS1DataChunk::ParseHumidifierSetting60Series(unsigned char humid1, unsigne
} else if (this->familyVersion == 2) {
// F5V2
if (tubepresent) {
- CHECK_VALUES(tubetemp, 0, 3);
+ // all tube temperatures seen
if (tubetemp) {
- CHECK_VALUE(tubehumidlevel, 1);
+ CHECK_VALUES(tubehumidlevel, 1, 3);
}
}
- CHECK_VALUE(humidsystemone, false);
CHECK_VALUE(humidclassic, false);
}
}
diff --git a/oscar/SleepLib/loader_plugins/prs1_parser_asv.cpp b/oscar/SleepLib/loader_plugins/prs1_parser_asv.cpp
index cb1e8c5a..76d1f54b 100644
--- a/oscar/SleepLib/loader_plugins/prs1_parser_asv.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_parser_asv.cpp
@@ -10,11 +10,6 @@
#include "prs1_parser.h"
#include "prs1_loader.h"
-static QString hex(int i)
-{
- return QString("0x") + QString::number(i, 16).toUpper();
-}
-
//********************************************************************************************
// MARK: -
// MARK: 50 and 60 Series
@@ -664,7 +659,7 @@ bool PRS1DataChunk::ParseEventsF5V1(void)
elapsed = data[pos];
this->AddEvent(new PRS1FlowLimitationEvent(t - elapsed, 0));
break;
- case 0x0a: // Vibratory Snore, note this is 0x9 in F5V3
+ case 0x0a: // Vibratory Snore, note this is 0xb in F5V2 and 0x9 in F5V3
// VS events are instantaneous flags with no duration, drawn on the official waveform.
// The current thinking is that these are the snores that cause a change in auto-titrating
// pressure. The snoring statistic above seems to be a total count. It's unclear whether
@@ -672,7 +667,7 @@ bool PRS1DataChunk::ParseEventsF5V1(void)
// no data bytes
this->AddEvent(new PRS1VibratorySnoreEvent(t, 0));
break;
- case 0x0b: // Periodic Breathing, note this is 0xa in F5V3
+ case 0x0b: // Periodic Breathing, note this is 0xc in F5V2 and 0xa in F5V3
// PB events are reported some time after they conclude, and they do have a reported duration.
duration = 2 * (data[pos] | (data[pos+1] << 8)); // confirmed to double in F5V0
elapsed = data[pos+2];
@@ -721,12 +716,13 @@ bool PRS1DataChunk::ParseEventsF5V1(void)
const QVector ParsedEventsF5V2 = {
PRS1EPAPSetEvent::TYPE,
+ PRS1PressurePulseEvent::TYPE,
PRS1TimedBreathEvent::TYPE,
PRS1ObstructiveApneaEvent::TYPE,
- //PRS1ClearAirwayEvent::TYPE, // not yet seen
+ PRS1ClearAirwayEvent::TYPE,
PRS1HypopneaEvent::TYPE,
PRS1FlowLimitationEvent::TYPE,
- //PRS1VibratorySnoreEvent::TYPE, // not yet seen
+ PRS1VibratorySnoreEvent::TYPE,
PRS1PeriodicBreathingEvent::TYPE,
//PRS1LargeLeakEvent::TYPE, // not yet seen
PRS1IPAPAverageEvent::TYPE,
@@ -751,7 +747,7 @@ bool PRS1DataChunk::ParseEventsF5V2(void)
}
const unsigned char * data = (unsigned char *)this->m_data.constData();
int chunk_size = this->m_data.size();
- static const QMap event_sizes = { {0,4}, {1,2}, {3,4}, {8,3}, {9,4}, {0xa,3}, {0xb,5}, {0xc,5}, {0xd,5}, {0xe,0xd}, {0xf,5}, {0x10,5}, {0x11,2}, {0x12,6} };
+ static const QMap event_sizes = { {0,4}, {1,2}, {8,3}, {9,4}, {0xa,3}, {0xb,2}, {0xc,5}, {0xd,5}, {0xe,0xd}, {0xf,5}, {0x10,5}, {0x11,2}, {0x12,6} };
if (chunk_size < 1) {
// This does occasionally happen in F0V6.
@@ -780,51 +776,20 @@ bool PRS1DataChunk::ParseEventsF5V2(void)
}
startpos = pos;
if (code != 0 && code != 0x12) { // These two codes have no timestamp TODO: verify this applies to F5V012
- t += data[pos] /*| (data[pos+1] << 8)*/; // TODO: Is this really only 1 byte?
- if (data[pos+1] != 0) qWarning() << this->sessionid << "nonzero time? byte" << hex(startpos);
- CHECK_VALUE(data[pos+1], 0);
+ t += data[pos] | (data[pos+1] << 8);
pos += 2;
}
switch (code) {
-/*
- case 0x00: // Unknown (ASV Pressure value)
- DUMP_EVENT();
- // offset?
- data0 = data[pos++];
-
- if (!data[pos - 1]) { // WTH???
- data1 = data[pos++];
- }
-
- if (!data[pos - 1]) {
- //data2 = data[pos++];
- pos++;
- }
-
- break;
-
- case 0x01: // Unknown
- DUMP_EVENT();
- this->AddEvent(new PRS1UnknownValueEvent(code, t, 0, 0.1F));
- break;
-*/
+ //case 0x00: // never seen on F5V2
+ //case 0x01: // never seen on F5V2
case 0x02: // Pressure adjustment
this->AddEvent(new PRS1EPAPSetEvent(t, data[pos++], GAIN));
break;
-/*
- case 0x03: // BIPAP Pressure
- DUMP_EVENT();
- qDebug() << "0x03 Observed in ASV data!!????";
-
- data0 = data[pos++];
- data1 = data[pos++];
- // data0/=10.0;
- // data1/=10.0;
- // session->AddEvent(new Event(t,CPAP_EAP, 0, data, 1));
- // session->AddEvent(new Event(t,CPAP_IAP, 0, &data1, 1));
- break;
-*/
+ case 0x03: // Pressure Pulse
+ duration = data[pos]; // TODO: is this a duration?
+ this->AddEvent(new PRS1PressurePulseEvent(t, duration));
+ break;
case 0x04: // Timed Breath
// TB events have a duration in 0.1s, based on the review of pressure waveforms.
// TODO: Ideally the starting time here would be adjusted here, but PRS1ParsedEvents
@@ -839,15 +804,13 @@ bool PRS1DataChunk::ParseEventsF5V2(void)
elapsed = data[pos];
this->AddEvent(new PRS1ObstructiveApneaEvent(t - elapsed, 0));
break;
-/*
- case 0x06:
- DUMP_EVENT();
- //code=CPAP_ClearAirway;
- data0 = data[pos++];
- this->AddEvent(new PRS1ClearAirwayEvent(t - data0, data0));
- break;
-*/
-
+ case 0x06: // Clear Airway Apnea
+ // CA events are instantaneous flags with no duration: reviewing waveforms
+ // shows that the time elapsed between the flag and reporting often includes
+ // non-apnea breathing.
+ elapsed = data[pos];
+ this->AddEvent(new PRS1ClearAirwayEvent(t - elapsed, 0));
+ break;
case 0x07: // Hypopnea
// NOTE: No additional (unknown) first byte as in F5V3 0x07, but see below.
// This seems closer to F5V3 0x0d or 0x0e.
@@ -862,21 +825,7 @@ bool PRS1DataChunk::ParseEventsF5V2(void)
elapsed = data[pos]; // based on sample waveform, the hypopnea is over after this
this->AddEvent(new PRS1HypopneaEvent(t - elapsed, 0));
break;
-/*
- case 0x09: // ASV Codes
- DUMP_EVENT();
- / *
- if (this->familyVersion<2) {
- //code=CPAP_FlowLimit;
- data0 = data[pos++];
-
- this->AddEvent(new PRS1FlowLimitationEvent(t - data0, data0));
- } else {
- * /
- data0 = data[pos++];
- data1 = data[pos++];
- break;
-*/
+ //case 0x09: // never seen on F5V2
case 0x0a: // Flow Limitation, note this is 0x9 in F5V1 and 0x8 in F5V3
// TODO: We should revisit whether this is elapsed or duration once (if)
// we start calculating flow limitations ourselves. Flow limitations aren't
@@ -884,35 +833,21 @@ bool PRS1DataChunk::ParseEventsF5V2(void)
elapsed = data[pos];
this->AddEvent(new PRS1FlowLimitationEvent(t - elapsed, 0));
break;
-/*
- case 0x0b: // Cheyne Stokes
- DUMP_EVENT();
- data0 = ((unsigned char *)data)[pos + 1] << 8 | ((unsigned char *)data)[pos];
- //data0*=2;
- pos += 2;
- data1 = ((unsigned char *)data)[pos]; //|data[pos+1] << 8
- pos += 1;
- //tt-=delta;
- this->AddEvent(new PRS1PeriodicBreathingEvent(t - data1, data0));
- break;
-*/
+ case 0x0b: // Vibratory Snore, note this is 0xa in F5V1 and 0x9 in F5V3
+ // VS events are instantaneous flags with no duration, drawn on the official waveform.
+ // The current thinking is that these are the snores that cause a change in auto-titrating
+ // pressure. The snoring statistic above seems to be a total count. It's unclear whether
+ // the trigger for pressure change is severity or count or something else.
+ // no data bytes
+ this->AddEvent(new PRS1VibratorySnoreEvent(t, 0));
+ break;
case 0x0c: // Periodic Breathing, note this is 0xb in F5V1 and 0xa in F5V3
// PB events are reported some time after they conclude, and they do have a reported duration.
duration = 2 * (data[pos] | (data[pos+1] << 8)); // confirmed to double in F5V0
elapsed = data[pos+2];
this->AddEvent(new PRS1PeriodicBreathingEvent(t - elapsed - duration, duration));
break;
-/*
- case 0x0d:
- DUMP_EVENT();
-
- data0 = (data[pos + 1] << 8 | data[pos]);
- data0 *= 2;
- pos += 2;
- data1 = data[pos++];
- //tt = t - qint64(data1) * 1000L;
- break;
-*/
+ //case 0x0d: // never seen on F5V2
case 0x0e: // Statistics, note this was 0x0d in F5V0 and F5V1
// These appear every 2 minutes, so presumably summarize the preceding period.
this->AddEvent(new PRS1IPAPAverageEvent(t, data[pos+0], GAIN)); // 00=IPAP
@@ -928,53 +863,6 @@ bool PRS1DataChunk::ParseEventsF5V2(void)
this->AddEvent(new PRS1LeakEvent(t, data[pos+0xa])); // 0A=Leak (average?) new to F5V1 (originally found in F5V3)
this->AddEvent(new PRS1IntervalBoundaryEvent(t));
break;
-/*
- case 0x0f:
- DUMP_EVENT();
- qDebug() << "0x0f Observed in ASV data!!????";
-
- data0 = data[pos + 1] << 8 | data[pos];
- pos += 2;
- data1 = data[pos]; //|data[pos+1] << 8
- pos += 1;
- //tt -= qint64(data1) * 1000L;
- //session->AddEvent(new Event(tt,cpapcode, 0, data, 2));
- break;
-
- case 0x10: // Unknown
- DUMP_EVENT();
- data0 = data[pos + 1] << 8 | data[pos];
- pos += 2;
- data1 = data[pos++];
-
- this->AddEvent(new PRS1LargeLeakEvent(t - data1, data0));
-
-// qDebug() << "0x10 Observed in ASV data!!????";
-// data0 = data[pos++]; // << 8) | data[pos];
-// data1 = data[pos++];
-// data2 = data[pos++];
- //session->AddEvent(new Event(t,cpapcode, 0, data, 3));
- break;
- case 0x11: // Not Leak Rate
- DUMP_EVENT();
- qDebug() << "0x11 Observed in ASV data!!????";
- //if (!Code[24]) {
- // Code[24]=new EventList(cpapcode,EVL_Event);
- //}
- //Code[24]->AddEvent(t,data[pos++]);
- break;
-
-
- case 0x12: // Summary
- DUMP_EVENT();
- qDebug() << "0x12 Observed in ASV data!!????";
- data0 = data[pos++];
- data1 = data[pos++];
- //data2 = data[pos + 1] << 8 | data[pos];
- pos += 2;
- //session->AddEvent(new Event(t,cpapcode, 0, data,3));
- break;
-*/
default:
DUMP_EVENT();
UNEXPECTED_VALUE(code, "known event code");
diff --git a/oscar/SleepLib/loader_plugins/prs1_parser_xpap.cpp b/oscar/SleepLib/loader_plugins/prs1_parser_xpap.cpp
index 7772c2cc..f6d0846a 100644
--- a/oscar/SleepLib/loader_plugins/prs1_parser_xpap.cpp
+++ b/oscar/SleepLib/loader_plugins/prs1_parser_xpap.cpp
@@ -2078,6 +2078,14 @@ bool PRS1DataChunk::ParseSettingsF0V6(const unsigned char* data, int size)
// TODO: Confirm that 4 is 12HT and update ParseTubingTypeV3.
this->ParseTubingTypeV3(data[pos]);
break;
+ case 0x48: // ??? Seen on DreamStation 2 non-Advanced (410)
+ // Appears between 0x2C and 0x2E, but the only values we've seen other than 0 is
+ // 1, which doesn't seem to be like a reasonable pressure (for ramp pressure) nor
+ // a boolean setting, which tends to be 0x80.
+ CHECK_VALUE(len, 1);
+ CHECK_VALUES(data[pos], 0, 1);
+ //this->AddEvent(new PRS1UnknownDataEvent(QByteArray((const char*) data, size), pos, len));
+ break;
case 0x4a: // Patient controls access, specific to DreamStation 2.
CHECK_VALUE(len, 1);
CHECK_VALUES(data[pos], 0, 0x80);
diff --git a/oscar/oscar.pro b/oscar/oscar.pro
index ff48ecb9..860d38a3 100644
--- a/oscar/oscar.pro
+++ b/oscar/oscar.pro
@@ -527,7 +527,6 @@ message("CXXFLAGS pre-mods $$QMAKE_CXXFLAGS ")
QMAKE_CFLAGS += -Werror
QMAKE_CXXFLAGS += -Werror
-
gcc | clang {
COMPILER_VERSION = $$system($$QMAKE_CXX " -dumpversion")
COMPILER_MAJOR = $$split(COMPILER_VERSION, ".")
@@ -536,25 +535,28 @@ gcc | clang {
message("$$QMAKE_CXX major version $$COMPILER_MAJOR")
}
-## equals($$QMAKE_CXX, "gcc") : { // guess what! the name is really "g++"
-equals(QMAKE_CXX, g++) {
- message("Detected compiler g++")
+gcc:!clang {
+ message("Building for $$QMAKE_HOST.os")
greaterThan(COMPILER_MAJOR, 10) : {
QMAKE_CFLAGS += -Wno-error=stringop-overread
QMAKE_CXXFLAGS += -Wno-error=stringop-overread
message("Making stringop-overread a non-error")
}
}
-equals(QMAKE_CXX, clang++) {
- message("Detected compiler clang++")
-}
+clang {
+ message("Building for $$QMAKE_HOST.os")
+ QMAKE_CFLAGS_WARN_ON += -Wno-error=deprecated-copy
+ QMAKE_CXXFLAGS_WARN_ON += -Wno-error=deprecated-copy
+ message("Making deprecated-copy a non-error")
+}
# Make deprecation warnings just warnings
QMAKE_CFLAGS += -Wno-error=deprecated-declarations
QMAKE_CXXFLAGS += -Wno-error=deprecated-declarations
message("CXXFLAGS post-mods $$QMAKE_CXXFLAGS ")
+message("CXXFLAGS_WARN_ON $$QMAKE_CXXFLAGS_WARN_ON")
lessThan(QT_MAJOR_VERSION,5)|lessThan(QT_MINOR_VERSION,9) {
QMAKE_CFLAGS += -Wno-error=strict-aliasing
diff --git a/oscar/overview.cpp b/oscar/overview.cpp
index 7e0e5c09..a77cb967 100644
--- a/oscar/overview.cpp
+++ b/oscar/overview.cpp
@@ -7,16 +7,19 @@
* License. See the file COPYING in the main directory of the source code
* for more details. */
+#define NEWSTUFF
+
#define xDEBUG_FUNCTIONS
#ifdef DEBUG_FUNCTIONS
#include
#define DEBUGQ qDebug()
#define DEBUGL qDebug()<LoadSettings("Overview"); //no trans
GraphView->setEmptyImage(QPixmap(":/icons/logo-md.png"));
+ dateErrorDisplay = new DateErrorDisplay(this);
connect(ui->dateStart->calendarWidget(), SIGNAL(currentPageChanged(int, int)), this, SLOT(dateStart_currentPageChanged(int, int)));
connect(ui->dateEnd->calendarWidget(), SIGNAL(currentPageChanged(int, int)), this, SLOT(dateEnd_currentPageChanged(int, int)));
@@ -196,6 +200,11 @@ Overview::~Overview()
GraphView->SaveSettings("Overview");//no trans
delete ui;
+ delete dateErrorDisplay;
+ delete icon_on ;
+ delete icon_off ;
+ delete icon_up_down ;
+ delete icon_warning ;
}
void Overview::ResetFont()
@@ -235,12 +244,10 @@ void Overview::on_summaryChartEmpty(gSummaryChart*sc,qint64 firstI,qint64 lastI,
if (empty) {
// on next range change allow empty flag to be recalculated
chartsEmpty.insert(sc,graph);
- //DEBUGF << graph->name() << "Chart is Empty" << "Range" << convertTimeToDate(firstI) << convertTimeToDate(lastI);
} else {
// The chart has some entry with data.
chartsEmpty.remove(sc);
updateGraphCombo();
- //DEBUGF << graph->name() << "Chart is enabled with range:" << convertTimeToDate(firstI) << "==>" << convertTimeToDate(lastI) ;
}
Q_UNUSED(firstI);
Q_UNUSED(lastI);
@@ -322,11 +329,8 @@ void Overview::CreateAllGraphs() {
G->AddLayer(sc);
chartsToBeMonitored.insert(sc,G);
}
- if (sc== nullptr) {
- //DEBUGF << "Channel" << name << "type" << chan->type() << "machine type" << chan->machtype() << "IGNORED";
- } else {
+ if (sc!= nullptr) {
sc ->reCalculate();
- //DEBUGF << "Channel" << name << "type" << chan->type() << "machine type" << chan->machtype() << OO(Empty,sc->isEmpty());
}
} // if showInOverview()
} // for chit
@@ -549,12 +553,6 @@ void Overview::on_XBoundsChanged(qint64 start,qint64 end)
// Only occurs when custom mode is switched to/from a latest mode. custom mode to/from last week.
// All other displays expand the existing range.
// reset all empty flags to not empty
- if (displayStartDate>maxRangeEndDate) {
- //DEBUGF << "Two ranges" O(displayStartDate) <<">" << O(maxRangeEndDate);
- }
- if (minRangeStartDate>displayEndDate) {
- //DEBUGF << "Two ranges" O(minRangeStartDate) <<">" << O(displayEndDate);
- }
largerRange=true;
chartsEmpty = QHash( chartsToBeMonitored );
minRangeStartDate = displayStartDate;
@@ -562,24 +560,14 @@ void Overview::on_XBoundsChanged(qint64 start,qint64 end)
} else {
// new range overlaps with old range
if (displayStartDate" <maxRangeEndDate) {
- //DEBUGF << "End Higher" <maxRangeEndDate) {
- //DEBUGF << "ERROR" <error(false,date);
+ return;
+ }
QDate d2(date);
if (customMode) {
p_profile->general->setCustomOverviewRangeEnd(d2);
@@ -636,6 +628,11 @@ void Overview::on_dateEnd_dateChanged(const QDate &date)
void Overview::on_dateStart_dateChanged(const QDate &date)
{
+ if (date>uiEndDate) {
+ // change date back to last date.
+ dateErrorDisplay->error(true,date);
+ return;
+ }
QDate d1(date);
if (customMode) {
p_profile->general->setCustomOverviewRangeStart(d1);
@@ -654,6 +651,7 @@ void Overview::on_zoomButton_clicked()
on_rangeCombo_activated(p_profile->general->lastOverviewRange()); // type of range in last use
}
+
void Overview::ResetGraphLayout()
{
GraphView->resetLayout();
@@ -732,7 +730,7 @@ void Overview::on_rangeCombo_activated(int index)
// Ensure that all summary files are available and update version numbers if required
int size = start.daysTo(end);
- qDebug() << "Overview range combo from" << start << "to" << end << "with" << size << "days";
+ // qDebug() << "Overview range combo from" << start << "to" << end << "with" << size << "days";
QDate dateback = end;
CProgressBar * progress = new CProgressBar (QObject::tr("Loading summaries"), mainwin, size);
for (int i=1; i < size; ++i) {
@@ -754,12 +752,101 @@ void Overview::on_rangeCombo_activated(int index)
setRange(start, end);
}
+
+DateErrorDisplay::DateErrorDisplay (Overview* overview)
+ : m_overview(overview)
+{
+ m_visible=false;
+ m_timer = new QTimer();
+ connect(m_timer, SIGNAL(timeout()),this, SLOT(timerDone()));
+};
+
+DateErrorDisplay::~DateErrorDisplay() {
+ disconnect(m_timer, SIGNAL(timeout()),this, SLOT(timerDone()));
+ delete m_timer;
+};
+
+void DateErrorDisplay::cancel() {
+ m_visible=false;
+ m_timer->stop();
+};
+
+void DateErrorDisplay::timerDone() {
+ m_visible=false;
+ m_overview->resetUiDates();
+ m_overview->graphView()->m_parent_tooltip->cancel();
+};
+
+int Overview::calculatePixels(bool startDate,ToolTipAlignment& align) {
+ // Center error message over start and end dates combined.
+ // Other allignement were tested but this is the best for this problem.
+ Q_UNUSED(startDate);
+ int space=4;
+ align=TT_AlignCenter;
+ return ((4*space) + ui->label_3->width() + ui->rangeCombo->width() + ui->dateStartLabel->width() +ui->dateStart->width() );
+}
+
+void DateErrorDisplay::error(bool startDate,const QDate& dateEntered) {
+ m_startDate =m_overview->uiStartDate;
+ m_endDate =m_overview->uiEndDate;
+ ToolTipAlignment align=TT_AlignCenter;
+
+
+ QString dateFormatted =dateEntered.toString(m_overview->shortformat);
+
+ QString txt = QString(tr("ERROR\nThe start date MUST be before the end date"));
+ txt.append("\n");
+ if (startDate) {
+ txt.append(tr("The entered start date %1 is after the end date %2").arg(dateFormatted).arg(m_endDate.toString(m_overview->shortformat)));
+ txt.append(tr("\nHint: Change the end date first"));
+ } else {
+ txt.append(tr("The entered end date %1 ").arg(dateFormatted));
+ txt.append(tr("is before the start date %1").arg(m_startDate.toString(m_overview->shortformat)));
+ txt.append(tr("\nHint: Change the start date first"));
+ }
+
+ gGraphView* gv=m_overview->graphView();
+
+ int pixelsFromLeft = m_overview->calculatePixels(startDate,align);
+ int pixelsAboveBottom = 0;
+ int warningDurationMS =4000;
+ int resetUiDatesTimerDelayMS =1000;
+ //QFont* font=mediumfont;
+ QFont* font=defaultfont;
+
+ gv->m_parent_tooltip->display(gv,txt,pixelsAboveBottom,pixelsFromLeft, align, warningDurationMS , font);
+
+ m_timer->setInterval( resetUiDatesTimerDelayMS );
+ m_timer->setSingleShot(true);
+ m_timer->start();
+
+};
+
+void Overview::resetUiDates() {
+ ui->dateStart->blockSignals(true);
+ ui->dateStart->setMinimumDate(p_profile->FirstDay()); // first and last dates for ANY machine type
+ ui->dateStart->setMaximumDate(p_profile->LastDay());
+ ui->dateStart->setDate(uiStartDate);
+ ui->dateStart->blockSignals(false);
+
+ ui->dateEnd->blockSignals(true);
+ ui->dateEnd->setMinimumDate(p_profile->FirstDay()); // first and last dates for ANY machine type
+ ui->dateEnd->setMaximumDate(p_profile->LastDay());
+ ui->dateEnd->setDate(uiEndDate);
+ ui->dateEnd->blockSignals(false);
+}
+
// Saves dates in UI, clicks zoom button, and updates combo box
// 1. Updates the dates in the start / end date boxs
// 2. optionally also changes display range for graphs.
void Overview::setRange(QDate& start, QDate& end, bool updateGraphs/*zoom*/)
{
+ if (start>end) {
+ // this is an ERROR and shold NEVER occur.
+ return;
+ }
+
// first setting of the date (setDate) will cause pageChanged to be executed.
// The pageChanged processing requires access to the other ui's date.
// so save them to memory before the first call.
@@ -777,21 +864,7 @@ void Overview::setRange(QDate& start, QDate& end, bool updateGraphs/*zoom*/)
samePage=nextSamePage;
}
- ui->dateEnd->blockSignals(true);
- ui->dateStart->blockSignals(true);
-
- // Calling these methods for the first time trigger pageChange actions.
- ui->dateEnd->setDate(end);
- ui->dateEnd->setMinimumDate(start);
-
- ui->dateStart->setMinimumDate(p_profile->FirstDay()); // first and last dates for ANY machine type
- ui->dateEnd->setMaximumDate(p_profile->LastDay());
-
- ui->dateStart->setDate(start);
- ui->dateStart->setMaximumDate(end);
-
- ui->dateEnd->blockSignals(false);
- ui->dateStart->blockSignals(false);
+ resetUiDates();
if (updateGraphs) SetXBounds(uiStartDate,uiEndDate);
updateGraphCombo();
}
diff --git a/oscar/overview.h b/oscar/overview.h
index 14e97d96..f01fa71d 100644
--- a/oscar/overview.h
+++ b/oscar/overview.h
@@ -16,6 +16,7 @@
#endif
#include
#include
+#include
#include "SleepLib/profiles.h"
#include "Graphs/gGraphView.h"
#include "Graphs/gSummaryChart.h"
@@ -26,6 +27,27 @@ class Overview;
}
class Report;
+class Overview;
+
+class DateErrorDisplay:QObject {
+ Q_OBJECT
+public:
+ DateErrorDisplay (Overview* overview) ;
+ ~DateErrorDisplay() ;
+ bool visible() {return m_visible;};
+ void cancel();
+ void error(bool startDate,const QDate& date);
+protected:
+
+private:
+ QTimer* m_timer;
+ bool m_visible=false;
+ Overview* m_overview;
+ QDate m_startDate;
+ QDate m_endDate;
+private slots:
+ void timerDone();
+};
enum YTickerType { YT_Number, YT_Time, YT_Weight };
@@ -35,6 +57,7 @@ enum YTickerType { YT_Number, YT_Time, YT_Weight };
*/
class Overview : public QWidget
{
+ friend class DateErrorDisplay;
Q_OBJECT
public:
@@ -103,8 +126,6 @@ class Overview : public QWidget
//! \brief Updates the calendar highlighting when changing to a new month
void dateEnd_currentPageChanged(int year, int month);
- //void on_printDailyButton_clicked();
-
void on_rangeCombo_activated(int index);
void on_graphCombo_activated(int index);
@@ -143,6 +164,10 @@ class Overview : public QWidget
void disconnectgSummaryCharts() ;
void SetXBounds(qint64 minx, qint64 maxx, short group = 0, bool refresh = true);
void SetXBounds(QDate & start, QDate& end, short group =0 , bool refresh = true);
+ void resetUiDates();
+ DateErrorDisplay* dateErrorDisplay;
+ int calculatePixels(bool startDate,ToolTipAlignment& align);
+ QString shortformat;
// Start and of dates of the current graph display
QDate displayStartDate;
@@ -166,4 +191,7 @@ class Overview : public QWidget
};
+
+
+
#endif // OVERVIEW_H
diff --git a/oscar/test_macros.h b/oscar/test_macros.h
new file mode 100644
index 00000000..c2f5e93d
--- /dev/null
+++ b/oscar/test_macros.h
@@ -0,0 +1,119 @@
+/* Test macros Implemntation
+ *
+ * Copyright (c) 2019-2022 The OSCAR Team
+ * Copyright (c) 2011-2018 Mark Watkins
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of the source code
+ * for more details. */
+
+/*
+These functions will display formatted debug information.
+The macro TEST_MACROS_ENABLED will enable these macros to display information
+When The macro TEST_MACROS_ENABLED is undefined then these marcos will expand to white space.
+
+When these macos are used then debugging is disabled
+When only these macos are used then debugging is disabled.
+SO for production code rename TEST_MACROS_ENABLED to TEST_MACROS_ENABLEDoff
+
+###########################################
+The the following to source cpp files
+to turn on the debug macros for use.
+
+#define TEST_MACROS_ENABLED
+#include
+
+To turn off the the test macros.
+#define TEST_MACROS_ENABLEDoff
+#include
+###########################################
+
+
+*/
+
+#ifndef TEST_MACROS_ENABLED_ONCE
+#define TEST_MACROS_ENABLED_ONCE
+
+#ifdef TEST_MACROS_ENABLED
+#include
+#include
+
+#define DEBUGL qDebug() <