From 142feb9ace6527cff66076c6a715ad73b44a0935 Mon Sep 17 00:00:00 2001 From: Mark Watkins Date: Fri, 25 Oct 2013 20:39:30 +1000 Subject: [PATCH] Mouse Handling cleanup, Graph YAxis prep work, profile screen improvements --- sleepyhead/Graphs/gFlagsLine.cpp | 49 ++- sleepyhead/Graphs/gFlagsLine.h | 30 ++ sleepyhead/Graphs/gFooBar.cpp | 1 + sleepyhead/Graphs/gGraphView.cpp | 346 ++++++++++++------ sleepyhead/Graphs/gGraphView.h | 49 ++- sleepyhead/Graphs/gLineChart.cpp | 58 +-- sleepyhead/Graphs/gYAxis.cpp | 32 +- sleepyhead/Graphs/gYAxis.h | 20 +- sleepyhead/Graphs/gspacer.cpp | 13 + sleepyhead/Graphs/gspacer.h | 33 ++ sleepyhead/SleepLib/day.cpp | 48 +++ sleepyhead/SleepLib/day.h | 6 + sleepyhead/SleepLib/event.cpp | 24 +- sleepyhead/SleepLib/event.h | 3 + .../SleepLib/loader_plugins/prs1_loader.cpp | 16 +- .../SleepLib/loader_plugins/resmed_loader.cpp | 218 ++++++++--- .../SleepLib/loader_plugins/resmed_loader.h | 2 +- sleepyhead/SleepLib/session.cpp | 44 ++- sleepyhead/SleepLib/session.h | 33 +- sleepyhead/daily.cpp | 3 +- sleepyhead/mainwindow.cpp | 2 +- sleepyhead/mainwindow.h | 2 +- sleepyhead/newprofile.ui | 21 +- sleepyhead/profileselect.cpp | 14 + sleepyhead/profileselect.h | 2 + sleepyhead/profileselect.ui | 234 ++++++++++-- sleepyhead/sessionbar.cpp | 8 +- sleepyhead/sleepyhead.pro | 6 +- 28 files changed, 1026 insertions(+), 291 deletions(-) create mode 100644 sleepyhead/Graphs/gspacer.cpp create mode 100644 sleepyhead/Graphs/gspacer.h diff --git a/sleepyhead/Graphs/gFlagsLine.cpp b/sleepyhead/Graphs/gFlagsLine.cpp index 09d72030..2b4ab11a 100644 --- a/sleepyhead/Graphs/gFlagsLine.cpp +++ b/sleepyhead/Graphs/gFlagsLine.cpp @@ -10,6 +10,17 @@ #include "gFlagsLine.h" #include "gYAxis.h" +gFlagsLabelArea::gFlagsLabelArea(gFlagsGroup * group) + :gSpacer(20) +{ + m_group=group; +} +bool gFlagsLabelArea::mouseMoveEvent(QMouseEvent * event,gGraph * graph) +{ + if (m_group) m_group->mouseMoveEvent(event,graph); +} + + gFlagsGroup::gFlagsGroup() { addVertexBuffer(quads=new gVertexBuffer(512,GL_QUADS)); @@ -61,7 +72,7 @@ void gFlagsGroup::SetDay(Day * d) m_barh=0; } -void gFlagsGroup::paint(gGraph &w, int left, int top, int width, int height) +void gFlagsGroup::paint(gGraph &g, int left, int top, int width, int height) { if (!m_visible) return; if (!m_day) return; @@ -77,11 +88,12 @@ void gFlagsGroup::paint(gGraph &w, int left, int top, int width, int height) quads->add(left, linetop, left, linetop+m_barh, left+width-1, linetop+m_barh, left+width-1, linetop, barcol.rgba()); // Paint the actual flags - lvisible[i]->paint(w,left,linetop,width,m_barh); + lvisible[i]->m_rect=QRect(left,linetop,width,m_barh); + lvisible[i]->paint(g,left,linetop,width,m_barh); linetop+=m_barh; } - gVertexBuffer *outlines=w.lines(); + gVertexBuffer *outlines=g.lines(); outlines->add(left-1, top, left-1, top+height, COLOR_Outline.rgba()); outlines->add(left-1, top+height, left+width,top+height, COLOR_Outline.rgba()); outlines->add(left+width,top+height, left+width, top,COLOR_Outline.rgba()); @@ -91,6 +103,30 @@ void gFlagsGroup::paint(gGraph &w, int left, int top, int width, int height) //lines->add(left+width, top+height, left+width, top); } +bool gFlagsGroup::mouseMoveEvent(QMouseEvent * event,gGraph * graph) +{ + + for (int i=0;im_rect.contains(event->x(),event->y())) { + if (fl->mouseMoveEvent(event,graph)) return true; + } else { + // Inside main graph area? + if ((event->y() > fl->m_rect.y()) && (event->y()) < (fl->m_rect.y()+fl->m_rect.height())) { + if (event->x() < lvisible[i]->m_rect.x()) { + // Display tooltip + QString ttip=schema::channel[fl->code()].description(); + graph->graphView()->m_tooltip->display(ttip,event->x(),event->y()-15,p_profile->general->tooltipTimeout()); + graph->redraw(); + } + } + + } + } + return false; +} + + gFlagsLine::gFlagsLine(ChannelID code,QColor flag_color,QString label,bool always_visible,FlagType flt) :Layer(code),m_label(label),m_always_visible(always_visible),m_flt(flt),m_flag_color(flag_color) { @@ -243,3 +279,10 @@ void gFlagsLine::paint(gGraph & w,int left, int top, int width, int height) qWarning() << "maxverts exceeded in gFlagsLine::plot()"; } } + +bool gFlagsLine::mouseMoveEvent(QMouseEvent * event,gGraph * graph) +{ + // qDebug() << code() << event->x() << event->y() << graph->rect(); + + return false; +} diff --git a/sleepyhead/Graphs/gFlagsLine.h b/sleepyhead/Graphs/gFlagsLine.h index df635b0f..c2760749 100644 --- a/sleepyhead/Graphs/gFlagsLine.h +++ b/sleepyhead/Graphs/gFlagsLine.h @@ -8,9 +8,32 @@ #define GFLAGSLINE_H #include "gGraphView.h" +#include "gspacer.h" class gFlagsGroup; +/*! \class gYSpacer + \brief A dummy vertical spacer object + */ +class gFlagsLabelArea:public gSpacer +{ + public: + gFlagsLabelArea(gFlagsGroup * group); + virtual void paint(gGraph & w,int left,int top, int width, int height) { + Q_UNUSED(w) + Q_UNUSED(left) + Q_UNUSED(top) + Q_UNUSED(width) + Q_UNUSED(height) + } +protected: + gFlagsGroup * m_group; + virtual bool mouseMoveEvent(QMouseEvent * event,gGraph * graph); + + +}; + + /*! \class gFlagsLine \brief One single line of event flags in the Event Flags chart */ @@ -45,6 +68,9 @@ class gFlagsLine:public Layer void setTotalLines(int i) { total_lines=i; } void setLineNum(int i) { line_num=i; } protected: + + virtual bool mouseMoveEvent(QMouseEvent * event,gGraph * graph); + QString m_label; bool m_always_visible; int total_lines,line_num; @@ -60,6 +86,8 @@ class gFlagsLine:public Layer */ class gFlagsGroup:public LayerGroup { + friend class gFlagsLabelArea; + public: gFlagsGroup(); virtual ~gFlagsGroup(); @@ -88,6 +116,8 @@ public: QVector & visibleLayers() { return lvisible; } protected: + virtual bool mouseMoveEvent(QMouseEvent * event,gGraph * graph); + gVertexBuffer *quads, *lines; QVector lvisible; float m_barh; diff --git a/sleepyhead/Graphs/gFooBar.cpp b/sleepyhead/Graphs/gFooBar.cpp index 748cb891..2c8d43ff 100644 --- a/sleepyhead/Graphs/gFooBar.cpp +++ b/sleepyhead/Graphs/gFooBar.cpp @@ -5,6 +5,7 @@ */ #include #include "gFooBar.h" +#include "gYAxis.h" gShadowArea::gShadowArea(QColor shadow_color,QColor line_color) :Layer(NoChannel),m_shadow_color(shadow_color),m_line_color(line_color) diff --git a/sleepyhead/Graphs/gGraphView.cpp b/sleepyhead/Graphs/gGraphView.cpp index 4e8a3df3..d09212b3 100644 --- a/sleepyhead/Graphs/gGraphView.cpp +++ b/sleepyhead/Graphs/gGraphView.cpp @@ -781,9 +781,10 @@ void gToolTip::paint() //actually paints it. int x=m_pos.x();// - tw / 2; int y=m_pos.y();// - th; + QPainter painter; + if (!usepixmap | (usepixmap && m_invalidate)) { - QPainter painter; painter.begin(m_graphview); @@ -830,14 +831,17 @@ void gToolTip::paint() //actually paints it. m_image=QImage(rect.width()+2,rect.height()+2,QImage::Format_ARGB32_Premultiplied); m_image.fill(Qt::transparent); painter.begin(&m_image); + painter.setCompositionMode(QPainter::CompositionMode_Source); + } lines_drawn_this_frame+=4; quads_drawn_this_frame+=1; - QBrush brush(QColor(255,255,128,200)); + QBrush brush(QColor(255,255,128,255)); brush.setStyle(Qt::SolidPattern); painter.setBrush(brush); + painter.setPen(QColor(0,0,0,255)); painter.drawRoundedRect(rect,5,5); painter.setBrush(Qt::black); @@ -846,10 +850,11 @@ void gToolTip::paint() //actually paints it. painter.end(); if (usepixmap) { - m_image=QGLWidget::convertToGLFormat(m_image); - m_textureID=m_graphview->bindTexture(m_image,GL_TEXTURE_2D,GL_RGBA,QGLContext::NoBindOption); + //m_image=m_image. + // m_textureID=m_graphview->bindTexture(m_image,GL_TEXTURE_2D,GL_RGBA,QGLContext::NoBindOption); m_invalidate=false; } + } if (usepixmap) { x-=m_spacer+m_image.width()/2; @@ -858,12 +863,10 @@ void gToolTip::paint() //actually paints it. if (x<0) x=0; if ((x+m_image.width()) > (m_graphview->width()-10)) x=m_graphview->width()-10 - m_image.width(); if (usepixmap && !m_image.isNull()) { - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - glEnable(GL_TEXTURE_2D); - m_graphview->drawTexture(QPoint(x,y),m_textureID); - glDisable(GL_TEXTURE_2D); - glDisable(GL_BLEND); + painter.begin(m_graphview); + + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawImage(QPoint(x,y),m_image); } } @@ -1039,7 +1042,7 @@ EventDataType LayerGroup::Miny() EventDataType m=0,t; for (int i=0;iMiny(); - if (t==layers[i]->Minx()) continue; + if (t==layers[i]->Maxy()) continue; if (first) { m=t; first=false; @@ -1065,6 +1068,54 @@ EventDataType LayerGroup::Maxy() return m; } +//! \brief Mouse wheel moved somewhere over this layer +bool LayerGroup::wheelEvent(QWheelEvent * event, gGraph * graph) +{ + for (int i=0;iwheelEvent(event,graph)) + return true; + return false; +} + +//! \brief Mouse moved somewhere over this layer +bool LayerGroup::mouseMoveEvent(QMouseEvent * event, gGraph * graph) +{ + for (int i=0;imouseMoveEvent(event,graph)) return true; + return false; +} + +//! \brief Mouse left or right button pressed somewhere on this layer +bool LayerGroup::mousePressEvent(QMouseEvent * event, gGraph * graph) +{ + for (int i=0;imousePressEvent(event,graph)) return true; + return false; +} + +//! \brief Mouse button released that was originally pressed somewhere on this layer +bool LayerGroup::mouseReleaseEvent(QMouseEvent * event, gGraph * graph) +{ + for (int i=0;imouseReleaseEvent(event,graph)) return true; + return false; +} + +//! \brief Mouse button double clicked somewhere on this layer +bool LayerGroup::mouseDoubleClickEvent(QMouseEvent * event, gGraph * graph) +{ + for (int i=0;imouseDoubleClickEvent(event,graph)) return true; + return false; +} + +//! \brief A key was pressed on the keyboard while the graph area was focused. +bool LayerGroup::keyPressEvent(QKeyEvent * event, gGraph * graph) +{ + for (int i=0;ikeyPressEvent(event,graph)) return true; + return false; +} const double zoom_hard_limit=500.0; @@ -1240,7 +1291,7 @@ void gGraph::renderText(QString text, int x,int y, float angle, QColor color, QF void gGraph::paint(int originX, int originY, int width, int height) { - m_lastbounds=QRect(originX,originY,width,height); + m_rect=QRect(originX,originY,width,height); /*glEnable(GL_BLEND); glBegin(GL_QUADS); @@ -1351,6 +1402,7 @@ void gGraph::paint(int originX, int originY, int width, int height) if (!ll->visible()) continue; tmp=ll->Width()*m_graphview->printScaleX(); if (ll->position()==LayerLeft) { + ll->m_rect=QRect(originX+left,originY+top,tmp,height-top-bottom); ll->paint(*this,originX+left,originY+top,tmp,height-top-bottom); left+=tmp; #ifdef DEBUG_LAYOUT @@ -1359,6 +1411,7 @@ void gGraph::paint(int originX, int originY, int width, int height) } if (ll->position()==LayerRight) { right+=tmp; + ll->m_rect=QRect(originX+width-right,originY+top,tmp,height-top-bottom); ll->paint(*this,originX+width-right,originY+top,tmp,height-top-bottom); #ifdef DEBUG_LAYOUT lines()->add(originX+width-right,originY,originX+width-right,originY+height,col); @@ -1372,11 +1425,13 @@ void gGraph::paint(int originX, int originY, int width, int height) if (!ll->visible()) continue; tmp=ll->Height()*m_graphview->printScaleY(); if (ll->position()==LayerTop) { + ll->m_rect=QRect(originX+left,originY+top,width-left-right,tmp); ll->paint(*this,originX+left,originY+top,width-left-right,tmp); top+=tmp; } if (ll->position()==LayerBottom) { bottom+=tmp; + ll->m_rect=QRect(originX+left,originY+height-bottom,width-left-right,tmp); ll->paint(*this,originX+left,originY+height-bottom,width-left-right,tmp); } } @@ -1385,6 +1440,7 @@ void gGraph::paint(int originX, int originY, int width, int height) Layer *ll=m_layers[i]; if (!ll->visible()) continue; if (ll->position()==LayerCenter) { + ll->m_rect=QRect(originX+left,originY+top,width-left-right,height-top-bottom); ll->paint(*this,originX+left,originY+top,width-left-right,height-top-bottom); } } @@ -1397,7 +1453,7 @@ void gGraph::paint(int originX, int originY, int width, int height) } void gGraphView::queGraph(gGraph * g,int left, int top, int width, int height) { - g->m_lastbounds=QRect(left,top,width,height); + g->m_rect=QRect(left,top,width,height); #ifdef ENABLED_THREADED_DRAWING dl_mutex.lock(); #endif @@ -1450,28 +1506,36 @@ void gGraph::timedRedraw(int ms) void gGraph::mouseMoveEvent(QMouseEvent * event) { // qDebug() << m_title << "Move" << event->pos() << m_graphview->pointClicked(); - //int y=event->pos().y(); - int x=event->pos().x(); - int x2=m_graphview->pointClicked().x();//,y2=m_graphview->pointClicked().y(); - int w=m_lastbounds.width()-(right); + int y=event->y(); + int x=event->x(); + + bool doredraw=false; + + for (int i=0;im_rect.contains(x,y)) + if (m_layers[i]->mouseMoveEvent(event,this)) doredraw=true; + } + + y-=m_rect.top(); + x-=m_rect.left(); + + int x2=m_graphview->pointClicked().x()-m_rect.left(); + + int w=m_rect.width()-(left+right); //int h=m_lastbounds.height()-(bottom+m_marginbottom); double xx=max_x-min_x; double xmult=xx/w; - - //bool nolayer=false; - bool doredraw=false; - - if (m_graphview->m_selected_graph==this) { + if (m_graphview->m_selected_graph==this) { // Left Mouse button dragging if (event->buttons() & Qt::LeftButton) { //qDebug() << m_title << "Moved" << x << y << left << right << top << bottom << m_width << h; int a1=MIN(x,x2); int a2=MAX(x,x2); if (a1w) a2=w; + if (a2>left+w) a2=left+w; m_selecting_area=true; - m_selection=QRect(a1-1,0,a2-a1,m_lastbounds.height()); - double w2=m_lastbounds.width()-right-left; //-(right+m_marginright)-(m_marginleft+left); + m_selection=QRect(a1-1,0,a2-a1,m_rect.height()); + double w2=m_rect.width()-right-left; if (m_blockzoom) { xmult=(rmax_x-rmin_x)/w2; } else { @@ -1493,20 +1557,13 @@ void gGraph::mouseMoveEvent(QMouseEvent * event) if (qstatus2) { qstatus2->setText(str); } - //m_graphview->redraw(); - //nolayer=false; doredraw=true; - } else if (event->buttons() & Qt::RightButton) { + } else if (event->buttons() & Qt::RightButton) { // Right Mouse button dragging m_graphview->setPointClicked(event->pos()); - x-=left+m_marginleft; - x2-=left+m_marginleft; - //int a1=MIN(x,x2); - //int a2=MAX(x,x2); - //if (a1w) a2=w; + x-=left; + x2-=left; if (!m_blockzoom) { xx=max_x-min_x; - w-=m_marginleft+left; xmult=xx/double(w); qint64 j1=xmult*x; qint64 j2=xmult*x2; @@ -1521,15 +1578,12 @@ void gGraph::mouseMoveEvent(QMouseEvent * event) max_x=rmax_x; min_x=rmax_x-xx; } - //if (a2>rmax_x) a2=rmax_x; m_graphview->SetXBounds(min_x,max_x,m_group,false); doredraw=true; - //nolayer=true; } else { qint64 qq=rmax_x-rmin_x; xx=max_x-min_x; if (xx==qq) xx=1800000; - w-=m_marginleft+left; xmult=qq/double(w); qint64 j1=(xmult*x); min_x=rmin_x+j1-(xx/2); @@ -1544,16 +1598,11 @@ void gGraph::mouseMoveEvent(QMouseEvent * event) } m_graphview->SetXBounds(min_x,max_x,m_group,false); doredraw=true; - //nolayer=true; - } } } //if (!nolayer) { // no mouse button - for (int i=0;imouseMoveEvent(event)) doredraw=true; - } if (doredraw) m_graphview->redraw(); //} @@ -1565,10 +1614,15 @@ void gGraph::mouseMoveEvent(QMouseEvent * event) } void gGraph::mousePressEvent(QMouseEvent * event) { - for (int i=0;imousePressEvent(event)) return ; - /*int y=event->pos().y(); + int y=event->pos().y(); int x=event->pos().x(); + + for (int i=0;im_rect.contains(x,y)) + if (m_layers[i]->mousePressEvent(event,this)) + return; + } + /* int w=m_lastbounds.width()-(right+m_marginright); //int h=m_lastbounds.height()-(bottom+m_marginbottom); //int x2,y2; @@ -1584,15 +1638,23 @@ void gGraph::mousePressEvent(QMouseEvent * event) void gGraph::mouseReleaseEvent(QMouseEvent * event) { - for (int i=0;imouseReleaseEvent(event)) - return; - int y=event->pos().y(); int x=event->pos().x(); - int w=m_lastbounds.width()-left-right; //(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(); + + for (int i=0;im_rect.contains(x,y)) + if (m_layers[i]->mouseReleaseEvent(event,this)) + return; + } + x-=m_rect.left(); + y-=m_rect.top(); + + + int w=m_rect.width()-left-right; //(m_marginleft+left+right+m_marginright); + int h=m_rect.height()-bottom; //+m_marginbottom); + + int x2=m_graphview->pointClicked().x()-m_rect.left(); + int y2=m_graphview->pointClicked().y()-m_rect.top(); //qDebug() << m_title << "Released" << min_x << max_x << x << y << x2 << y2 << left << right << top << bottom << m_width << m_height; @@ -1721,6 +1783,14 @@ void gGraph::wheelEvent(QWheelEvent * event) } else { ZoomX(1.5,x); } + + int y=event->pos().y(); + x=event->pos().x(); + for (int i=0;im_rect.contains(x,y)) + m_layers[i]->wheelEvent(event,this); + } + } void gGraph::mouseDoubleClickEvent(QMouseEvent * event) { @@ -1728,18 +1798,25 @@ void gGraph::mouseDoubleClickEvent(QMouseEvent * event) //mouseReleaseEvent(event); int y=event->pos().y(); int x=event->pos().x(); - 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; - } + for (int i=0;im_rect.contains(x,y)) + m_layers[i]->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(); @@ -1747,7 +1824,7 @@ void gGraph::mouseDoubleClickEvent(QMouseEvent * event) void gGraph::keyPressEvent(QKeyEvent * event) { for (QVector::iterator i=m_layers.begin();i!=m_layers.end();i++) { - (*i)->keyPressEvent(event); + (*i)->keyPressEvent(event,this); } //qDebug() << m_title << "Key Pressed.. implement me" << event->key(); } @@ -1755,7 +1832,7 @@ void gGraph::keyPressEvent(QKeyEvent * event) void gGraph::ZoomX(double mult,int origin_px) { - int width=m_lastbounds.width()-left-right; //(m_marginleft+left+right+m_marginright); + int width=m_rect.width()-left-right; //(m_marginleft+left+right+m_marginright); if (origin_px==0) origin_px=(width/2); else origin_px-=left; if (origin_px<0) origin_px=0; @@ -2133,6 +2210,8 @@ void gGraph::ResetBounds() } void gGraph::ToolTip(QString text, int x, int y, int timeout) { + if (timeout<=0) + timeout=p_profile->general->tooltipTimeout(); m_graphview->m_tooltip->display(text,x,y,timeout); } @@ -3018,7 +3097,7 @@ bool gGraphView::renderGraphs() for (int i=0;ipaint(g->m_lastbounds.x(), g->m_lastbounds.y(), g->m_lastbounds.width(), g->m_lastbounds.height()); + g->paint(g->m_rect.x(), g->m_rect.y(), g->m_rect.width(), g->m_rect.height()); } #ifdef ENABLED_THREADED_DRAWING } @@ -3038,7 +3117,6 @@ bool gGraphView::renderGraphs() // lines->setSize(linesize); // DrawTextQue(); - m_tooltip->paint(); //glDisable(GL_TEXTURE_2D); //glDisable(GL_DEPTH_TEST); @@ -3238,6 +3316,7 @@ void gGraphView::paintGL() } DrawTextQue(); } + m_tooltip->paint(); #ifdef DEBUG_EFFICIENCY const int rs=10; @@ -3381,9 +3460,11 @@ void gGraphView::mouseMoveEvent(QMouseEvent * event) float py = -m_offsetY; float h; + // Propagate mouseMove events to relevant graphs for (int i=0; i < m_graphs.size(); i++) { - if (m_graphs[i]->isEmpty() || (!m_graphs[i]->visible())) continue; + if (m_graphs[i]->isEmpty() || (!m_graphs[i]->visible())) + continue; h=m_graphs[i]->height() * m_scaleY; if (py > height()) @@ -3393,13 +3474,30 @@ void gGraphView::mouseMoveEvent(QMouseEvent * event) if (m_graphs[i]->isSelected()) { m_graphs[i]->deselect(); timedRedraw(150); - //redraw(); } } - if (m_button_down || ((py + h + graphSpacer) >= 0)) { - if ((y >= py + h) && (y <= py + h + graphSpacer + 1)) { - this->setCursor(Qt::SplitVCursor); - } else if (!m_button_down && (y >= py) && (y < py+m_graphs[i]->top)) { + + // Update Mouse Cursor shape + if ((y >= py + h -1) && (y < (py + h + graphSpacer))) { + this->setCursor(Qt::SplitVCursor); + } else if ((y >= py+1) && (y < py + h)) { + if (x >= titleWidth+10) + this->setCursor(Qt::ArrowCursor); + else + this->setCursor(Qt::OpenHandCursor); + + + m_horiz_travel+=qAbs(x-m_lastxpos)+qAbs(y-m_lastypos); + m_lastxpos=x; + m_lastypos=y; +// QPoint p(x,y); +// QMouseEvent e(event->type(),p,event->button(),event->buttons(),event->modifiers()); + m_graphs[i]->mouseMoveEvent(event); + + + } + +/* else if (!m_button_down && (y >= py) && (y < py+m_graphs[i]->top)) { // Mouse cursor is in top graph margin. } else if (!m_button_down && (y >= py+h-m_graphs[i]->bottom) && (y <= py+h)) { // Mouse cursor is in bottom grpah margin. @@ -3451,9 +3549,9 @@ void gGraphView::mouseMoveEvent(QMouseEvent * event) this->setCursor(Qt::OpenHandCursor); } - } + } */ - } + // } py+=h; py+=graphSpacer; } @@ -3477,33 +3575,34 @@ void gGraphView::mousePressEvent(QMouseEvent * event) break; if ((py + h + graphSpacer) >= 0) { - if ((y >= py) && (y < py + h)) { - //qDebug() << "Clicked" << i; - if (x < titleWidth+20) { // clicked on title to drag graph.. - m_graph_dragging=true; - m_graph_index=i; - m_sizer_point.setX(x); - m_sizer_point.setY(py); // point at top of graph.. - this->setCursor(Qt::ClosedHandCursor); - } else { // send event to graph.. - m_global_point_clicked=QPoint(x,y); - m_point_clicked=QPoint (x-titleWidth,y-py); - - m_selected_graph=m_graphs[i]; - - QMouseEvent e(event->type(),m_point_clicked,event->button(),event->buttons(),event->modifiers()); - m_graph_index=i; - m_button_down=true; - m_horiz_travel=0; - m_graphs[i]->mousePressEvent(&e); - } - } else if ((y >= py + h) && (y <= py + h + graphSpacer + 1)) { + if ((y >= py + h-1) && (y <= py + h + graphSpacer)) { this->setCursor(Qt::SplitVCursor); m_sizer_dragging=true; m_sizer_index=i; m_sizer_point.setX(x); m_sizer_point.setY(y); //qDebug() << "Sizer clicked" << i; + } else if ((y >= py) && (y < py + h)) { + //qDebug() << "Clicked" << i; + if (x < titleWidth+20) { // clicked on title to drag graph.. + m_graph_dragging=true; + m_tooltip->cancel(); + redraw(); + m_graph_index=i; + m_sizer_point.setX(x); + m_sizer_point.setY(py); // point at top of graph.. + this->setCursor(Qt::ClosedHandCursor); + } + + { // send event to graph.. + m_point_clicked=QPoint(event->x(),event->y()); + //QMouseEvent e(event->type(),m_point_clicked,event->button(),event->buttons(),event->modifiers()); + m_button_down=true; + m_horiz_travel=0; + m_graph_index=i; + m_selected_graph=m_graphs[i]; + m_graphs[i]->mousePressEvent(event); + } } } @@ -3515,7 +3614,36 @@ void gGraphView::mousePressEvent(QMouseEvent * event) void gGraphView::mouseReleaseEvent(QMouseEvent * event) { - this->setCursor(Qt::ArrowCursor); + + int x=event->x(); + int y=event->y(); + float py = -m_offsetY; + float h; + + for (int i=0; i < m_graphs.size(); i++) { + + if (m_graphs[i]->isEmpty() || (!m_graphs[i]->visible())) + continue; + + h=m_graphs[i]->height() * m_scaleY; + if (py > height()) + break; // we are done.. can't draw anymore + + if ((y >= py + h -1) && (y < (py + h + graphSpacer))) { + this->setCursor(Qt::SplitVCursor); + } else if ((y >= py+1) && (y <= py + h)) { + +// if (!m_sizer_dragging && !m_graph_dragging) { +// m_graphs[i]->mouseReleaseEvent(event); +// } + + if (x >= titleWidth+10) + this->setCursor(Qt::ArrowCursor); + else + this->setCursor(Qt::OpenHandCursor); + } + + } if (m_sizer_dragging) { m_sizer_dragging=false; @@ -3523,27 +3651,26 @@ void gGraphView::mouseReleaseEvent(QMouseEvent * event) } if (m_graph_dragging) { m_graph_dragging=false; + // not sure why the cursor code doesn't catch this.. + if (x >= titleWidth+10) + this->setCursor(Qt::ArrowCursor); + else + this->setCursor(Qt::OpenHandCursor); return; } + + // The graph that got the button press gets the release event if (m_button_down) { m_button_down=false; - int x1=m_global_point_clicked.x()-event->x(); - int y1=m_global_point_clicked.y()-event->y(); - - QPoint p(m_point_clicked.x()-x1,m_point_clicked.y()-y1); - QMouseEvent e(event->type(),p,event->button(),event->buttons(),event->modifiers()); - m_graphs[m_graph_index]->mouseReleaseEvent(&e); + m_graphs[m_graph_index]->mouseReleaseEvent(event); } - //int x=event->x(); - //int y=event->y(); } void gGraphView::mouseDoubleClickEvent(QMouseEvent * event) { mousePressEvent(event); - return; -/* int x=event->x(); + int x=event->x(); int y=event->y(); float py=-m_offsetY; @@ -3561,6 +3688,7 @@ void gGraphView::mouseDoubleClickEvent(QMouseEvent * event) if ((y >= py) && (y <= py + h)) { if (x < titleWidth) { // What to do when double clicked on the graph title ?? + m_graphs[i]->mouseDoubleClickEvent(event); } else { // send event to graph.. m_graphs[i]->mouseDoubleClickEvent(event); @@ -3572,7 +3700,7 @@ void gGraphView::mouseDoubleClickEvent(QMouseEvent * event) } py+=h; py+=graphSpacer; // do we want the extra spacer down the bottom? - } */ + } } void gGraphView::wheelEvent(QWheelEvent * event) { diff --git a/sleepyhead/Graphs/gGraphView.h b/sleepyhead/Graphs/gGraphView.h index 55eed20e..dc51e414 100644 --- a/sleepyhead/Graphs/gGraphView.h +++ b/sleepyhead/Graphs/gGraphView.h @@ -17,6 +17,7 @@ #include #include #include +#include #define MIN(a,b) (((a)<(b)) ? (a) : (b)); @@ -271,6 +272,7 @@ enum LayerPosition { LayerLeft, LayerRight, LayerTop, LayerBottom, LayerCenter, class Layer { friend class gGraph; + friend class LayerGroup; public: Layer(ChannelID code); virtual ~Layer(); @@ -304,9 +306,6 @@ public: //! \brief Return this layers physical maximum Yaxis value virtual EventDataType Maxy() { return m_maxy; } - - - //! \brief Set this layers physical minimum date boundary virtual void setMinX(qint64 val) { m_minx=val; } @@ -363,6 +362,7 @@ public: void addref() { m_refcount++; } bool unref() { m_refcount--; if (m_refcount<=0) return true; return false; } + protected: //! \brief Add a GLBuffer (vertex) object customized to this layer void addGLBuf(GLBuffer *buf) { mgl_buffers.push_back(buf); } @@ -380,23 +380,24 @@ protected: short m_Y; short m_order; // order for positioning.. LayerPosition m_position; + QRect m_rect; //! \brief A vector containing all this layers custom drawing buffers QVector mgl_buffers; QVector mv_buffers; //! \brief Mouse wheel moved somewhere over this layer - virtual bool wheelEvent(QWheelEvent * event) { Q_UNUSED(event); return false; } + virtual bool wheelEvent(QWheelEvent * event, gGraph * graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; } //! \brief Mouse moved somewhere over this layer - virtual bool mouseMoveEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + virtual bool mouseMoveEvent(QMouseEvent * event, gGraph * graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; } //! \brief Mouse left or right button pressed somewhere on this layer - virtual bool mousePressEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + virtual bool mousePressEvent(QMouseEvent * event, gGraph * graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; } //! \brief Mouse button released that was originally pressed somewhere on this layer - virtual bool mouseReleaseEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + virtual bool mouseReleaseEvent(QMouseEvent * event, gGraph * graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; } //! \brief Mouse button double clicked somewhere on this layer - virtual bool mouseDoubleClickEvent(QMouseEvent * event) { Q_UNUSED(event); return false; } + virtual bool mouseDoubleClickEvent(QMouseEvent * event, gGraph * graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; } //! \brief A key was pressed on the keyboard while the graph area was focused. - virtual bool keyPressEvent(QKeyEvent * event) { Q_UNUSED(event); return false; } + virtual bool keyPressEvent(QKeyEvent * event, gGraph * graph) { Q_UNUSED(event); Q_UNUSED(graph); return false; } }; /*! \class LayerGroup @@ -438,6 +439,24 @@ public: protected: //! \brief Contains all Layer objects in this group QVector layers; + + //! \brief Mouse wheel moved somewhere over this LayerGroup + virtual bool wheelEvent(QWheelEvent * event, gGraph * graph); + + //! \brief Mouse moved somewhere over this LayerGroup + virtual bool mouseMoveEvent(QMouseEvent * event, gGraph * graph); + + //! \brief Mouse left or right button pressed somewhere on this LayerGroup + virtual bool mousePressEvent(QMouseEvent * event, gGraph * graph); + + //! \brief Mouse button released that was originally pressed somewhere on this LayerGroup + virtual bool mouseReleaseEvent(QMouseEvent * event, gGraph * graph); + + //! \brief Mouse button double clicked somewhere on this layerGroup + virtual bool mouseDoubleClickEvent(QMouseEvent * event, gGraph * graph); + + //! \brief A key was pressed on the keyboard while the graph area was focused. + virtual bool keyPressEvent(QKeyEvent * event, gGraph * graph); }; class gGraph; @@ -499,6 +518,7 @@ protected: QImage m_image; GLuint m_textureID; bool m_invalidate; + protected slots: //! \brief Timeout to hide tooltip, and redraw without it. @@ -691,7 +711,7 @@ public: virtual void paint(int originX, int originY, int width, int height); //! \brief Gives the supplied data to the main ToolTip object for display - void ToolTip(QString text, int x, int y, int timeout=2000); + void ToolTip(QString text, int x, int y, int timeout=0); //! \brief Public version of updateGL(), to redraw all graphs.. Not for normal use void redraw(); @@ -723,6 +743,8 @@ public: //! \brief Returns the main gGraphView objects gVertexBuffer quads list. gVertexBuffer * quads(); + const inline QRect & rect() { return m_rect; } + // //! \brief Returns the main gGraphView objects gVertexBuffer stippled line list. //GLShortBuffer * stippled(); @@ -731,7 +753,6 @@ public: short left,right,top,bottom; // dirty magin hacks.. Layer * getLineChart(); - QRect m_lastbounds; QTimer * timer; // This gets set to true to force a redraw of the yAxis tickers when graphs are resized. @@ -790,6 +811,8 @@ protected: bool m_enforceMinY,m_enforceMaxY; bool m_showTitle; bool m_printing; + + QRect m_rect; signals: protected slots: @@ -887,9 +910,7 @@ public: void deselect(); QPoint pointClicked() { return m_point_clicked; } - QPoint globalPointClicked() { return m_global_point_clicked; } void setPointClicked(QPoint p) { m_point_clicked=p; } - void setGlobalPointClicked(QPoint p) { m_global_point_clicked=p; } //! \brief Set a redraw timer for ms milliseconds, clearing any previous redraw timer. void timedRedraw(int ms); @@ -1072,7 +1093,7 @@ protected: bool m_button_down; QPoint m_point_clicked; - QPoint m_global_point_clicked; + QPoint m_sizer_point; int m_horiz_travel; diff --git a/sleepyhead/Graphs/gLineChart.cpp b/sleepyhead/Graphs/gLineChart.cpp index 76914956..3ef233bc 100644 --- a/sleepyhead/Graphs/gLineChart.cpp +++ b/sleepyhead/Graphs/gLineChart.cpp @@ -49,34 +49,48 @@ void gLineChart::SetDay(Day *d) m_minx=0,m_maxx=0; m_miny=0,m_maxy=0; - if (!d) return; + if (!d) + return; qint64 t64; + EventDataType tmp; - EventDataType min=99999999,max=-999999999,tmp; + bool first=true; for (int j=0;jsize();i++) { Session *sess=d->getSessions()[i]; if (!sess->channelExists(code)) continue; + if (code==CPAP_FLG) { + int i=5; + } + if (first) { + m_miny=sess->physMin(code); + m_maxy=sess->physMax(code); + m_minx=sess->first(code); + m_maxx=sess->last(code); + first=false; + } else { + tmp=sess->physMin(code); + if (m_miny > tmp) + m_miny=tmp; - tmp=sess->Min(code); - if (min > tmp) min=tmp; + tmp=sess->physMax(code); + if (m_maxy < tmp) + m_maxy=tmp; - tmp=sess->Max(code); - if (max < tmp) max=tmp; + t64=sess->first(code); + if (m_minx > t64) + m_minx=t64; - t64=sess->first(code); - if (!m_minx || (m_minx > t64)) m_minx=t64; - - t64=sess->last(code); - if (!m_maxx || (m_maxx < t64)) m_maxx=t64; + t64=sess->last(code); + if (m_maxx < t64) + m_maxx=t64; + } } } - m_miny=min; - m_maxy=max; //if (m_code==CPAP_Leak) { // subtract_offset=profile.cpap.[IntentionalLeak].toDouble(); @@ -102,6 +116,10 @@ EventDataType gLineChart::Maxy() // Time Domain Line Chart void gLineChart::paint(gGraph & w,int left, int top, int width, int height) { + + if (w.title()=="Flow Limit") { + int i=5; + } if (!m_visible) return; @@ -113,8 +131,6 @@ void gLineChart::paint(gGraph & w,int left, int top, int width, int height) if (width<0) return; - - // lines=w.lines(); EventDataType miny,maxy; double minx,maxx; @@ -367,7 +383,7 @@ void gLineChart::paint(gGraph & w,int left, int top, int width, int height) for (int i=idx;ix(); - //int y=event->y(); - //qDebug() << "Hover at " << x << y; - return false; + int x=event->x(); + int y=event->y(); + if (!graph->units().isEmpty()) { + graph->ToolTip(graph->units(),x,y-20,0); + graph->redraw(); + } + return true; } +bool gYAxis::mouseDoubleClickEvent(QMouseEvent * event, gGraph * graph) +{ + if (graph) { + + int x=event->x(); + int y=event->y(); + qDebug() << "Mouse double clicked for" << graph->title() << x << y << m_rect; + } + Q_UNUSED(event); + return false; +} const QString gYAxisTime::Format(EventDataType v, int dp) { diff --git a/sleepyhead/Graphs/gYAxis.h b/sleepyhead/Graphs/gYAxis.h index 5631828b..a680f9dc 100644 --- a/sleepyhead/Graphs/gYAxis.h +++ b/sleepyhead/Graphs/gYAxis.h @@ -10,23 +10,6 @@ #include "gGraphView.h" -/*! \class gYSpacer - \brief A dummy vertical spacer object - */ -class gYSpacer:public Layer -{ - public: - gYSpacer(int spacer=20); - virtual void paint(gGraph & w,int left,int top, int width, int height) { - Q_UNUSED(w) - Q_UNUSED(left) - Q_UNUSED(top) - Q_UNUSED(width) - Q_UNUSED(height) - } - -}; - /*! \class gXGrid \brief Draws the horizintal major/minor grids over graphs */ @@ -110,7 +93,8 @@ class gYAxis:public Layer QColor m_line_color; QColor m_text_color; gVertexBuffer * lines; - virtual bool mouseMoveEvent(QMouseEvent * event); + virtual bool mouseMoveEvent(QMouseEvent * event,gGraph * graph); + virtual bool mouseDoubleClickEvent(QMouseEvent * event, gGraph * graph); QImage m_image; GLuint m_textureID; diff --git a/sleepyhead/Graphs/gspacer.cpp b/sleepyhead/Graphs/gspacer.cpp new file mode 100644 index 00000000..4ea53c54 --- /dev/null +++ b/sleepyhead/Graphs/gspacer.cpp @@ -0,0 +1,13 @@ +/* + graph spacer Implementation + Copyright (c)2013 Mark Watkins + License: GPL +*/ + +#include "gspacer.h" + +gSpacer::gSpacer(int space) + :Layer(NoChannel) +{ + m_space=space; +} diff --git a/sleepyhead/Graphs/gspacer.h b/sleepyhead/Graphs/gspacer.h new file mode 100644 index 00000000..d007417e --- /dev/null +++ b/sleepyhead/Graphs/gspacer.h @@ -0,0 +1,33 @@ +/* + graph spacer Header + Copyright (c)2011 Mark Watkins + License: GPL +*/ + +#ifndef GSPACER_H +#define GSPACER_H + +#include "gGraphView.h" + + +/*! \class gSpacer + \brief A dummy graph spacer layer object + */ +class gSpacer:public Layer +{ + public: + gSpacer(int space=20); // orientation? + virtual void paint(gGraph & g,int left,int top, int width, int height) { + Q_UNUSED(g) + Q_UNUSED(left) + Q_UNUSED(top) + Q_UNUSED(width) + Q_UNUSED(height) + } + int space() { return m_space; } + +protected: + int m_space; +}; + +#endif // GSPACER_H diff --git a/sleepyhead/SleepLib/day.cpp b/sleepyhead/SleepLib/day.cpp index 3989d909..05a5116a 100644 --- a/sleepyhead/SleepLib/day.cpp +++ b/sleepyhead/SleepLib/day.cpp @@ -451,6 +451,7 @@ qint64 Day::last(ChannelID code) } return date; } + EventDataType Day::Min(ChannelID code) { EventDataType min=0; @@ -472,6 +473,29 @@ EventDataType Day::Min(ChannelID code) return min; } +EventDataType Day::physMin(ChannelID code) +{ + EventDataType min=0; + EventDataType tmp; + bool first=true; + for (QList::iterator s=sessions.begin();s!=sessions.end();s++) { + if (!(*s)->enabled()) continue; + + // MW: I meant to check this instead. + if (!(*s)->m_min.contains(code)) + continue; + + tmp=(*s)->physMin(code); + if (first) { + min=tmp; + first=false; + } else { + if (tmp::iterator s=sessions.begin();s!=sessions.end();s++) { + if (!(*s)->enabled()) + continue; + + // MW: I meant to check this instead. + if (!(*s)->m_max.contains(code)) + continue; + + tmp=(*s)->physMax(code); + if (first) { + max=tmp; + first=false; + } else { + if (tmp>max) max=tmp; + } + } + return max; +} EventDataType Day::cph(ChannelID code) { double sum=0; diff --git a/sleepyhead/SleepLib/day.h b/sleepyhead/SleepLib/day.h index a9055a8b..3ca3d558 100644 --- a/sleepyhead/SleepLib/day.h +++ b/sleepyhead/SleepLib/day.h @@ -47,6 +47,12 @@ public: //! \brief Returns the Maximum of all sessions' events for this day EventDataType Max(ChannelID code); + //! \brief Returns the Minimum of all this sessions' events for this day + EventDataType physMin(ChannelID code); + + //! \brief Returns the Maximum of all sessions' events for this day + EventDataType physMax(ChannelID code); + //! \brief Returns the Count-per-hour of all sessions' events for this day EventDataType cph(ChannelID code); diff --git a/sleepyhead/SleepLib/event.cpp b/sleepyhead/SleepLib/event.cpp index c3ab6666..c844c169 100644 --- a/sleepyhead/SleepLib/event.cpp +++ b/sleepyhead/SleepLib/event.cpp @@ -29,6 +29,20 @@ EventList::EventList(EventListType et,EventDataType gain, EventDataType offset, EventList::~EventList() { } +void EventList::clear() +{ + m_min2=m_min=999999999; + m_max2=m_max=-999999999; + m_update_minmax=true; + m_first=m_last=0; + m_count=0; + + m_data.clear(); + m_data2.clear(); + m_time.clear(); + +} + qint64 EventList::time(quint32 i) { if (m_type==EVL_Event) { @@ -55,8 +69,12 @@ void EventList::AddEvent(qint64 time, EventStoreType data) EventDataType val=EventDataType(data)*m_gain; // ignoring m_offset if (m_update_minmax) { - if (m_min>val) m_min=val; - else if (m_maxval) m_min=val; + if (m_max val) min=val; if (max < val) max=val; } diff --git a/sleepyhead/SleepLib/event.h b/sleepyhead/SleepLib/event.h index 228dd85f..b82f0025 100644 --- a/sleepyhead/SleepLib/event.h +++ b/sleepyhead/SleepLib/event.h @@ -25,6 +25,9 @@ public: EventList(EventListType et,EventDataType gain=1.0, EventDataType offset=0.0, EventDataType min=0.0, EventDataType max=0.0, double rate=0.0,bool second_field=false); ~EventList(); + //! \brief Wipe the event list so it can be reused + void clear(); + /*! \brief Add an event starting at time, containing data to this event list Note, data2 is only used if second_field is specified in the constructor */ void AddEvent(qint64 time, EventStoreType data); diff --git a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp index d7dc9278..6863454f 100644 --- a/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/prs1_loader.cpp @@ -647,6 +647,20 @@ bool PRS1Loader::ParseSummary(Machine *mach, qint32 sequence, quint32 timestamp, session->setCph(CPAP_RERA,float(rc/hours)); session->setCph(CPAP_FlowLimit,float(fc/hours)); } + + // Set recommended Graph values.. + session->setPhysMax(CPAP_LeakTotal,120); + session->setPhysMin(CPAP_LeakTotal,0); + session->setPhysMax(CPAP_Pressure,25); + session->setPhysMin(CPAP_Pressure,4); + session->setPhysMax(CPAP_IPAP,25); + session->setPhysMin(CPAP_IPAP,4); + session->setPhysMax(CPAP_EPAP,25); + session->setPhysMin(CPAP_EPAP,4); + session->setPhysMax(CPAP_PS,25); + session->setPhysMin(CPAP_PS,0); + + new_sessions[sequence]=session; return true; } @@ -950,6 +964,7 @@ bool PRS1Loader::Parse002v5(qint32 sequence, quint32 timestamp, unsigned char *b session->m_valuesummary[CPAP_Pressure].clear(); session->m_valuesummary.erase(session->m_valuesummary.find(CPAP_Pressure)); + return true; } @@ -971,7 +986,6 @@ bool PRS1Loader::Parse002(qint32 sequence, quint32 timestamp, unsigned char *buf Session *session=new_sessions[sequence]; session->updateFirst(t); - EventList * OA=session->AddEventList(CPAP_Obstructive, EVL_Event); EventList * HY=session->AddEventList(CPAP_Hypopnea, EVL_Event); EventList * CSR=session->AddEventList(CPAP_CSR, EVL_Event); diff --git a/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp b/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp index cced322d..6ae27e85 100644 --- a/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp +++ b/sleepyhead/SleepLib/loader_plugins/resmed_loader.cpp @@ -1358,41 +1358,41 @@ int ResmedLoader::Open(QString & path,Profile *profile) // The following only happens when the STR.edf file is not up to date.. // This will only happen when the user fails to back up their SDcard properly. // Basically takes a guess. - bool dodgy=false; - if (!sess->settings.contains(CPAP_Mode)) { - //The following is a lame assumption if 50th percentile == max, then it's CPAP - EventDataType max=sess->Max(CPAP_Pressure); - EventDataType p50=sess->percentile(CPAP_Pressure,0.60); - EventDataType p502=sess->percentile(CPAP_MaskPressure,0.60); - p50=qMax(p50, p502); - if (max==0) { - dodgy=true; - } else if (qAbs(max-p50)<1.8) { - max=round(max*10.0)/10.0; - sess->settings[CPAP_Mode]=MODE_CPAP; - if (max<1) { - int i=5; - } - sess->settings[CPAP_PressureMin]=max; - EventDataType epr=round(sess->Max(CPAP_EPAP)*10.0)/10.0; - int i=max-epr; - sess->settings[CPAP_PresReliefType]=PR_EPR; - prmode=(i>0) ? 0 : 1; - sess->settings[CPAP_PresReliefMode]=prmode; - sess->settings[CPAP_PresReliefSet]=i; +// bool dodgy=false; +// if (!sess->settings.contains(CPAP_Mode)) { +// //The following is a lame assumption if 50th percentile == max, then it's CPAP +// EventDataType max=sess->Max(CPAP_Pressure); +// EventDataType p50=sess->percentile(CPAP_Pressure,0.60); +// EventDataType p502=sess->percentile(CPAP_MaskPressure,0.60); +// p50=qMax(p50, p502); +// if (max==0) { +// dodgy=true; +// } else if (qAbs(max-p50)<1.8) { +// max=round(max*10.0)/10.0; +// sess->settings[CPAP_Mode]=MODE_CPAP; +// if (max<1) { +// int i=5; +// } +// sess->settings[CPAP_PressureMin]=max; +// EventDataType epr=round(sess->Max(CPAP_EPAP)*10.0)/10.0; +// int i=max-epr; +// sess->settings[CPAP_PresReliefType]=PR_EPR; +// prmode=(i>0) ? 0 : 1; +// sess->settings[CPAP_PresReliefMode]=prmode; +// sess->settings[CPAP_PresReliefSet]=i; - } else { - // It's not cpap, so just take the highest setting for this machines history. - // This may fail if the missing str data is at the beginning of a fresh import. - CPAPMode mode=(CPAPMode)(int)PROFILE.calcSettingsMax(CPAP_Mode,MT_CPAP,sess->machine()->FirstDay(),sess->machine()->LastDay()); - if (modesettings[CPAP_Mode]=mode; - // Assuming 10th percentile should cover for ramp/warmup - sess->settings[CPAP_PressureMin]=sess->percentile(CPAP_Pressure,0.10); - sess->settings[CPAP_PressureMax]=sess->Max(CPAP_Pressure); - } - } +// } else { +// // It's not cpap, so just take the highest setting for this machines history. +// // This may fail if the missing str data is at the beginning of a fresh import. +// CPAPMode mode=(CPAPMode)(int)PROFILE.calcSettingsMax(CPAP_Mode,MT_CPAP,sess->machine()->FirstDay(),sess->machine()->LastDay()); +// if (modesettings[CPAP_Mode]=mode; +// // Assuming 10th percentile should cover for ramp/warmup +// sess->settings[CPAP_PressureMin]=sess->percentile(CPAP_Pressure,0.10); +// sess->settings[CPAP_PressureMax]=sess->Max(CPAP_Pressure); +// } +// } //Rather than take a dodgy guess, EPR settings can take a hit, and this data can simply be missed.. // Add the session to the machine & profile objects @@ -1831,7 +1831,9 @@ bool ResmedLoader::LoadBRP(Session *sess,EDFParser &edf) long recs=es.nr*edf.GetNumDataRecords(); ChannelID code; if (es.label=="Flow") { - es.gain*=60; + es.gain*=60.0; + es.physical_minimum*=60.0; + es.physical_maximum*=60.0; es.physical_dimension="L/M"; code=CPAP_FlowRate; } else if (es.label.startsWith("Mask Pres")) { @@ -1848,11 +1850,17 @@ bool ResmedLoader::LoadBRP(Session *sess,EDFParser &edf) a->AddWaveform(edf.startdate,es.data,recs,duration); sess->setMin(code,a->Min()); sess->setMax(code,a->Max()); + sess->setPhysMin(code,es.physical_minimum); + sess->setPhysMax(code,es.physical_maximum); } return true; } -EventList * ResmedLoader::ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & es, ChannelID code, long recs, qint64 duration,EventDataType min,EventDataType max,bool square) +void ResmedLoader::ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & es, ChannelID code, long recs, qint64 duration,EventDataType t_min,EventDataType t_max,bool square) { + if (t_min==t_max) { + t_min=es.physical_minimum; + t_max=es.physical_maximum; + } #ifdef DEBUG_EFFICIENCY QElapsedTimer time; time.start(); @@ -1862,7 +1870,7 @@ EventList * ResmedLoader::ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & double rate=(duration/recs); // milliseconds per record double tt=edf.startdate; //sess->UpdateFirst(tt); - EventDataType c,last; + EventStoreType c,last; int startpos=0; @@ -1874,28 +1882,92 @@ EventList * ResmedLoader::ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & qint16 * eptr=sptr+recs; sptr+=startpos; + EventDataType min=t_max,max=t_min,tmp; + EventList *el=NULL; if (recs>startpos+1) { - el=sess->AddEventList(code,EVL_Event,es.gain,es.offset,min,max); - c=last=*sptr++; - el->AddEvent(tt,last); + + // Prime last with a good starting value + do { + last=*sptr++; + tmp=EventDataType(last) * es.gain; + + if ((tmp >= t_min) && (tmp <= t_max)) { + min=tmp; + max=tmp; + el=sess->AddEventList(code,EVL_Event,es.gain,es.offset,0,0); + + el->AddEvent(tt,last); + tt+=rate; + + break; + } + tt+=rate; + + } while (sptr < eptr); + if (!el) + return; for (; sptr < eptr; sptr++) { //int i=startpos;iAddEvent(tt,last); // square waves look better on some charts. - el->AddEvent(tt,c); + if (square) { + tmp=EventDataType(last) * es.gain; + if ((tmp >= t_min) && (tmp <= t_max)) { + if (tmp < min) + min=tmp; + if (tmp > max) + max=tmp; + + el->AddEvent(tt,last); + } else { + // Out of bounds value, start a new eventlist + if (el->count()>1) { + // that should be in session, not the eventlist.. handy for debugging though + el->setDimension(es.physical_dimension); + + el=sess->AddEventList(code,EVL_Event,es.gain,es.offset,0,0); + } else { + el->clear(); // reuse the object + } + } + } + tmp=EventDataType(c) * es.gain; + if (tmp<0) { + int i=5; + } + if ((tmp >= t_min) && (tmp <= t_max)) { + if (tmp < min) + min=tmp; + if (tmp > max) + max=tmp; + + el->AddEvent(tt,c); + } else { + if (el->count()>1) { + el->setDimension(es.physical_dimension); + + // Create and attach new EventList + el=sess->AddEventList(code,EVL_Event,es.gain,es.offset,0,0); + } else el->clear(); + } } tt+=rate; last=c; } - el->AddEvent(tt,c); + tmp=EventDataType(c) * es.gain; + if ((tmp >= t_min) && (tmp <= t_max)) { + el->AddEvent(tt,c); + } + sess->setMin(code,min); + sess->setMax(code,max); + sess->setPhysMin(code,es.physical_minimum); + sess->setPhysMax(code,es.physical_maximum); sess->updateLast(tt); } - #ifdef DEBUG_EFFICIENCY qint64 t=time.nsecsElapsed(); int cnt=el->count(); @@ -1911,7 +1983,7 @@ EventList * ResmedLoader::ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & } #endif - return el; + //return el; } bool ResmedLoader::LoadSAD(Session *sess,EDFParser &edf) { @@ -1941,10 +2013,15 @@ bool ResmedLoader::LoadSAD(Session *sess,EDFParser &edf) } } if (hasdata) { - EventList *a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); - if (a) { - sess->setMin(code,a->Min()); - sess->setMax(code,a->Max()); + if (code==OXI_Pulse) { + ToTimeDelta(sess,edf,es, code,recs,duration); + sess->setPhysMax(code,180); + sess->setPhysMin(code,18); + } else if (code==OXI_SPO2) { + es.physical_minimum=60; + ToTimeDelta(sess,edf,es, code,recs,duration); + sess->setPhysMax(code,100); + sess->setPhysMin(code,60); } } @@ -1975,40 +2052,59 @@ bool ResmedLoader::LoadPLD(Session *sess,EDFParser &edf) //qDebug() << "EVE:" << es.digital_maximum << es.digital_minimum << es.physical_maximum << es.physical_minimum << es.gain; if (es.label=="Snore Index") { code=CPAP_Snore; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + ToTimeDelta(sess,edf,es, code,recs,duration,es.digital_maximum); } else if (es.label.startsWith("Therapy Pres")) { code=CPAP_Pressure; //TherapyPressure; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + es.physical_maximum=25; + es.physical_minimum=4; + ToTimeDelta(sess,edf,es, code,recs,duration,0,0); } else if (es.label=="Insp Pressure") { code=CPAP_IPAP; sess->settings[CPAP_Mode]=MODE_BIPAP; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + es.physical_maximum=25; + es.physical_minimum=4; + ToTimeDelta(sess,edf,es, code,recs,duration,0,0); } else if ((es.label=="MV") || (es.label=="VM")){ code=CPAP_MinuteVent; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + ToTimeDelta(sess,edf,es, code,recs,duration,0,0); } else if ((es.label=="RR") || (es.label=="AF") || (es.label=="FR")) { code=CPAP_RespRate; a=sess->AddEventList(code,EVL_Waveform,es.gain,es.offset,0,0,rate); a->AddWaveform(edf.startdate,es.data,recs,duration); } else if ((es.label=="Vt") || (es.label=="VC")) { code=CPAP_TidalVolume; - es.physical_maximum=es.physical_minimum=0; es.gain*=1000.0; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + es.physical_maximum*=1000.0; + es.physical_minimum*=1000.0; +// es.digital_maximum*=1000.0; +// es.digital_minimum*=1000.0; + ToTimeDelta(sess,edf,es, code,recs,duration,0,0); } else if ((es.label=="Leak") || (es.label.startsWith("Leck")) || (es.label.startsWith("Lekk"))) { code=CPAP_Leak; es.gain*=60; + es.physical_maximum*=60; + es.physical_minimum*=60; +// es.digital_maximum*=60.0; +// es.digital_minimum*=60.0; es.physical_dimension="L/M"; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0,true); + ToTimeDelta(sess,edf,es, code,recs,duration,0,0,true); + sess->setPhysMax(code,120.0); + sess->setPhysMin(code,0); } else if (es.label=="FFL Index") { code=CPAP_FLG; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + ToTimeDelta(sess,edf,es, code,recs,duration,0,0); } else if (es.label.startsWith("Mask Pres")) { code=CPAP_MaskPressure; - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + es.physical_maximum=25; + es.physical_minimum=4; + + ToTimeDelta(sess,edf,es, code,recs,duration,0,0); } else if (es.label.startsWith("Exp Press")) { code=CPAP_EPAP;//ExpiratoryPressure - a=ToTimeDelta(sess,edf,es, code,recs,duration,0,0); + es.physical_maximum=25; + es.physical_minimum=4; + + ToTimeDelta(sess,edf,es, code,recs,duration,0,0); } else if (es.label.startsWith("I:E")) { code=CPAP_IE;//I:E ratio? a=sess->AddEventList(code,EVL_Waveform,es.gain,es.offset,0,0,rate); @@ -2032,10 +2128,10 @@ bool ResmedLoader::LoadPLD(Session *sess,EDFParser &edf) } else if (es.label=="") { if (emptycnt==0) { code=RMS9_E01; - a=ToTimeDelta(sess,edf,es, code,recs,duration); + ToTimeDelta(sess,edf,es, code,recs,duration); } else if (emptycnt==1) { code=RMS9_E02; - a=ToTimeDelta(sess,edf,es, code,recs,duration); + ToTimeDelta(sess,edf,es, code,recs,duration); } else { qDebug() << "Unobserved Empty Signal " << es.label; } @@ -2047,6 +2143,8 @@ bool ResmedLoader::LoadPLD(Session *sess,EDFParser &edf) if (a) { sess->setMin(code,a->Min()); sess->setMax(code,a->Max()); + sess->setPhysMin(code,es.physical_minimum); + sess->setPhysMax(code,es.physical_maximum); a->setDimension(es.physical_dimension); } } diff --git a/sleepyhead/SleepLib/loader_plugins/resmed_loader.h b/sleepyhead/SleepLib/loader_plugins/resmed_loader.h index d0e3af97..e6688528 100644 --- a/sleepyhead/SleepLib/loader_plugins/resmed_loader.h +++ b/sleepyhead/SleepLib/loader_plugins/resmed_loader.h @@ -189,7 +189,7 @@ public: virtual const QString & ClassName() { return resmed_class_name; } //! \brief Converts EDFSignal data to time delta packed EventList, and adds to Session - EventList * ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & es, ChannelID code, long recs,qint64 duration,EventDataType min=0,EventDataType max=0,bool square=false); + void ToTimeDelta(Session *sess,EDFParser &edf, EDFSignal & es, ChannelID code, long recs,qint64 duration,EventDataType min=0,EventDataType max=0,bool square=false); //! \brief Create Machine record, and index it by serial number Machine *CreateMachine(QString serial,Profile *profile); diff --git a/sleepyhead/SleepLib/session.cpp b/sleepyhead/SleepLib/session.cpp index 5197cc14..387c4c1f 100644 --- a/sleepyhead/SleepLib/session.cpp +++ b/sleepyhead/SleepLib/session.cpp @@ -24,7 +24,7 @@ const quint16 filetype_data=1; // This is the uber important database version for SleepyHeads internal storage // Increment this after stuffing with Session's save & load code. -const quint16 summary_version=11; +const quint16 summary_version=12; const quint16 events_version=10; Session::Session(Machine * m,SessionID session) @@ -145,6 +145,8 @@ bool Session::StoreSummary(QString filename) out << m_wavg; out << m_min; out << m_max; + out << m_physmin; + out << m_physmax; out << m_cph; out << m_sph; out << m_firstchan; @@ -263,6 +265,7 @@ bool Session::LoadSummary(QString filename) code=schema::channel[i.key()].id(); m_max[code]=i.value(); } + ztmp.clear(); in >> ztmp; // cph for (QHash::iterator i=ztmp.begin();i!=ztmp.end();i++) { @@ -306,6 +309,11 @@ bool Session::LoadSummary(QString filename) } in >> m_min; in >> m_max; + // Added 24/10/2013 by MW to support physical graph min/max values + if (version>=12) { + in >> m_physmin; + in >> m_physmax; + } in >> m_cph; in >> m_sph; in >> m_firstchan; @@ -894,6 +902,40 @@ EventDataType Session::Max(ChannelID id) m_max[id]=max; return max; } + +//// +EventDataType Session::physMin(ChannelID id) +{ + QHash::iterator i=m_physmin.find(id); + if (i!=m_physmin.end()) + return i.value(); + + QHash >::iterator j=eventlist.find(id); + if (j==eventlist.end()) { + m_physmin[id]=0; + return 0; + } + EventDataType min=round(Min(id)); + m_physmin[id]=min; + return min; +} +EventDataType Session::physMax(ChannelID id) +{ + QHash::iterator i=m_physmax.find(id); + if (i!=m_physmax.end()) + return i.value(); + + QHash >::iterator j=eventlist.find(id); + if (j==eventlist.end()) { + m_physmax[id]=0; + return 0; + } + EventDataType max=round(Max(id)+0.5); + m_physmax[id]=max; + return max; +} + + qint64 Session::first(ChannelID id) { qint64 drift=qint64(PROFILE.cpap->clockDrift())*1000L; diff --git a/sleepyhead/SleepLib/session.h b/sleepyhead/SleepLib/session.h index 1dc7f333..603246f3 100644 --- a/sleepyhead/SleepLib/session.h +++ b/sleepyhead/SleepLib/session.h @@ -136,8 +136,15 @@ public: QHash m_sum; QHash m_avg; QHash m_wavg; - QHash m_min; + + QHash m_min; // The actual minimum QHash m_max; + + // This could go in channels, but different machines interpret it differently + // Under the new SleepyLib data Device model this can be done, but unfortunately not here.. + QHash m_physmin; // The physical minimum for graph display purposes + QHash m_physmax; // The physical maximum + QHash m_cph; // Counts per hour (eg AHI) QHash m_sph; // % indice (eg % night in CSR) QHash m_firstchan; @@ -158,6 +165,23 @@ public: void setSum(ChannelID id,EventDataType val) { m_sum[id]=val; } void setMin(ChannelID id,EventDataType val) { m_min[id]=val; } void setMax(ChannelID id,EventDataType val) { m_max[id]=val; } + void setPhysMin(ChannelID id,EventDataType val) { m_physmin[id]=val; } + void setPhysMax(ChannelID id,EventDataType val) { m_physmax[id]=val; } + void updateMin(ChannelID id,EventDataType val) { + QHash::iterator i=m_min.find(id); + if (i==m_min.end()) + m_min[id]=val; + else if (i.value() > val) + i.value() = val; + } + void updateMax(ChannelID id,EventDataType val) { + QHash::iterator i=m_max.find(id); + if (i==m_max.end()) + m_max[id]=val; + else if (i.value() < val) + i.value() = val; + } + void setAvg(ChannelID id,EventDataType val) { m_avg[id]=val; } void setWavg(ChannelID id,EventDataType val) { m_wavg[id]=val; } // void setMedian(ChannelID id,EventDataType val) { m_med[id]=val; } @@ -182,7 +206,6 @@ public: //! \brief Returns the maximum of events of type id between time range EventDataType rangeMax(ChannelID id, qint64 first,qint64 last); - //! \brief Returns (and caches) the Sum of all events of type id double sum(ChannelID id); @@ -198,6 +221,12 @@ public: //! \brief Returns (and caches) the Maximum of all events of type id EventDataType Max(ChannelID id); + //! \brief Returns (and caches) the Minimum of all events of type id + EventDataType physMin(ChannelID id); + + //! \brief Returns (and caches) the Maximum of all events of type id + EventDataType physMax(ChannelID id); + //! \brief Returns (and caches) the 90th Percentile of all events of type id EventDataType p90(ChannelID id); diff --git a/sleepyhead/daily.cpp b/sleepyhead/daily.cpp index 63a54381..2cca768c 100644 --- a/sleepyhead/daily.cpp +++ b/sleepyhead/daily.cpp @@ -195,7 +195,8 @@ Daily::Daily(QWidget *parent,gGraphView * shared) //fg->AddLayer((new gFlagsLine(PRS1_0B,COLOR_DarkGreen,tr("U0B")))); SF->setBlockZoom(true); SF->AddLayer(new gShadowArea()); - SF->AddLayer(new gYSpacer(),LayerLeft,gYAxis::Margin); + + SF->AddLayer(new gFlagsLabelArea(fg),LayerLeft,gYAxis::Margin); //SF->AddLayer(new gFooBar(),LayerBottom,0,1); SF->AddLayer(new gXAxis(COLOR_Text,false),LayerBottom,0,20); //gXAxis::Margin); diff --git a/sleepyhead/mainwindow.cpp b/sleepyhead/mainwindow.cpp index 6f5aca9e..aa15b072 100644 --- a/sleepyhead/mainwindow.cpp +++ b/sleepyhead/mainwindow.cpp @@ -1077,7 +1077,7 @@ void MainWindow::RestartApplication(bool force_login,bool change_datafolder) if (QProcess::startDetached("/usr/bin/open",args)) { QApplication::instance()->exit(); - } else QMessageBox::warning(this,tr("Gah!"),tr("If you can read this, the restart command didn't work. Your going to have to do it yourself manually."),QMessageBox::Ok); + } else QMessageBox::warning(NULL,tr("Gah!"),tr("If you can read this, the restart command didn't work. Your going to have to do it yourself manually."),QMessageBox::Ok); #else apppath=QApplication::instance()->applicationFilePath(); diff --git a/sleepyhead/mainwindow.h b/sleepyhead/mainwindow.h index 45539a8c..9d4929f4 100644 --- a/sleepyhead/mainwindow.h +++ b/sleepyhead/mainwindow.h @@ -121,7 +121,7 @@ public: If force_login is set, it will return to the login menu even if it's set to skip */ - void RestartApplication(bool force_login=false,bool change_datafolder=false); + static void RestartApplication(bool force_login=false,bool change_datafolder=false); //! \brief Self explainitory, selects the Oximetry Tab void selectOximetryTab(); diff --git a/sleepyhead/newprofile.ui b/sleepyhead/newprofile.ui index e9d0b1eb..3f7e41fc 100644 --- a/sleepyhead/newprofile.ui +++ b/sleepyhead/newprofile.ui @@ -390,9 +390,6 @@ - - 0 - @@ -880,6 +877,12 @@ + + + 0 + 0 + + &Cancel @@ -890,6 +893,12 @@ + + + 0 + 0 + + &Back @@ -900,6 +909,12 @@ + + + 0 + 0 + + &Next diff --git a/sleepyhead/profileselect.cpp b/sleepyhead/profileselect.cpp index 8e626a2e..2e3be5a2 100644 --- a/sleepyhead/profileselect.cpp +++ b/sleepyhead/profileselect.cpp @@ -18,6 +18,7 @@ #include "ui_profileselect.h" #include "SleepLib/profiles.h" #include "newprofile.h" +#include "mainwindow.h" ProfileSelect::ProfileSelect(QWidget *parent) : QDialog(parent), @@ -65,6 +66,14 @@ ProfileSelect::ProfileSelect(QWidget *parent) : popupMenu->addAction(tr("Edit Profile"),this,SLOT(editProfile())); popupMenu->addSeparator(); popupMenu->addAction(tr("Delete Profile"),this,SLOT(deleteProfile())); + + ui->labelAppName->setText(STR_TR_SleepyHead); + ui->labelVersion->setText("v"+VersionString+" "+ReleaseStatus); +// if (GIT_BRANCH!="master") +// ui->labelBuild->setText(GIT_BRANCH); +// else ui->labelBuild->setText(QString()); + ui->labelFolder->setText(GetAppRoot()); + ui->labelFolder->setToolTip("Current SleepyHead data folder\n"+GetAppRoot()); } ProfileSelect::~ProfileSelect() @@ -247,3 +256,8 @@ void ProfileSelect::on_listView_customContextMenuRequested(const QPoint &pos) { popupMenu->popup(QWidget::mapToGlobal(pos)); } + +void ProfileSelect::on_pushButton_clicked() +{ + MainWindow::RestartApplication(false,true); +} diff --git a/sleepyhead/profileselect.h b/sleepyhead/profileselect.h index e4a2ba51..1965a97d 100644 --- a/sleepyhead/profileselect.h +++ b/sleepyhead/profileselect.h @@ -43,6 +43,8 @@ private slots: void on_listView_customContextMenuRequested(const QPoint &pos); + void on_pushButton_clicked(); + private: Ui::ProfileSelect *ui; QString m_selectedProfile; diff --git a/sleepyhead/profileselect.ui b/sleepyhead/profileselect.ui index 04729a4b..4b191b9b 100644 --- a/sleepyhead/profileselect.ui +++ b/sleepyhead/profileselect.ui @@ -6,8 +6,8 @@ 0 0 - 400 - 300 + 386 + 251 @@ -18,51 +18,215 @@ :/icons/bob-v3.0.png:/icons/bob-v3.0.png - - - - Qt::CustomContextMenu - - - + + 0 + + + 0 + + + 8 + + + 10 + + + 12 + + + 4 + + + 12 + - - - &Quit + + + Qt::CustomContextMenu - - - Qt::Horizontal + + + 6 - - - 40 - 20 - + + 0 - - - - - - New Profile - - - - - - - &Select User - - + + + + + 0 + 0 + + + + Start with the selected user profile. + + + &Select User + + + + :/icons/forward.png:/icons/forward.png + + + + + + + Qt::Horizontal + + + + + + + Create a new user profile. + + + New Profile + + + + + + + Qt::Horizontal + + + + + + + Choose a different SleepyHead data folder. + + + &Different Folder + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + 15 + 75 + true + + + + SleepyHead + + + Qt::AlignCenter + + + + + + + [version] + + + Qt::AlignCenter + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Click here if you didn't want to start SleepyHead. + + + &Quit + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Plain + + + + 0 + + + + + + 75 + true + + + + Folder: + + + + + + + + 0 + 0 + + + + The current location of SleepyHead data store. + + + [data directory] + + + + + + diff --git a/sleepyhead/sessionbar.cpp b/sleepyhead/sessionbar.cpp index 0a1306ef..24c31a27 100644 --- a/sleepyhead/sessionbar.cpp +++ b/sleepyhead/sessionbar.cpp @@ -101,7 +101,8 @@ void SessionBar::mousePressEvent(QMouseEvent * ev) { SegType mn=min(); SegType mx=max(); - Q_ASSERT(mx > mn); + if (mx < mn) + return; SegType total=mx-mn; double px=double(width()-5) / double(total); @@ -134,7 +135,8 @@ void SessionBar::mouseMoveEvent(QMouseEvent * ev) { SegType mn=min(); SegType mx=max(); - Q_ASSERT(mx > mn); + if (mx < mn) + return; SegType total=mx-mn; double px=double(width()-5) / double(total); @@ -169,7 +171,7 @@ void SessionBar::paintEvent(QPaintEvent *) SegType mn=min(); SegType mx=max(); - if (mx > mn) + if (mx < mn) return; SegType total=mx-mn; diff --git a/sleepyhead/sleepyhead.pro b/sleepyhead/sleepyhead.pro index 63de797e..2f96596c 100644 --- a/sleepyhead/sleepyhead.pro +++ b/sleepyhead/sleepyhead.pro @@ -118,7 +118,8 @@ SOURCES += main.cpp\ SleepLib/loader_plugins/mseries_loader.cpp \ reports.cpp \ summary.cpp \ - sessionbar.cpp + sessionbar.cpp \ + Graphs/gspacer.cpp HEADERS += \ SleepLib/machine.h \ @@ -164,7 +165,8 @@ HEADERS += \ SleepLib/loader_plugins/mseries_loader.h \ reports.h \ summary.h \ - sessionbar.h + sessionbar.h \ + Graphs/gspacer.h FORMS += \