diff --git a/Graphs/gGraphView.cpp b/Graphs/gGraphView.cpp
index b26bed2a..ae3ac9ab 100644
--- a/Graphs/gGraphView.cpp
+++ b/Graphs/gGraphView.cpp
@@ -3811,7 +3811,7 @@ const quint16 gvversion=0;
 
 void gGraphView::SaveSettings(QString title)
 {
-    QString filename=PROFILE.Get("{DataFolder}")+QDir::separator()+title.toLower()+".shg";
+    QString filename=PROFILE.Get("{DataFolder}/")+title.toLower()+".shg";
     QFile f(filename);
     f.open(QFile::WriteOnly);
     QDataStream out(&f);
@@ -3835,7 +3835,7 @@ void gGraphView::SaveSettings(QString title)
 
 bool gGraphView::LoadSettings(QString title)
 {
-    QString filename=PROFILE.Get("{DataFolder}")+QDir::separator()+title.toLower()+".shg";
+    QString filename=PROFILE.Get("{DataFolder}/")+title.toLower()+".shg";
     QFile f(filename);
     if (!f.exists()) return false;
 
diff --git a/SleepLib/loader_plugins/icon_loader.cpp b/SleepLib/loader_plugins/icon_loader.cpp
index fbd9083a..c6c80a88 100644
--- a/SleepLib/loader_plugins/icon_loader.cpp
+++ b/SleepLib/loader_plugins/icon_loader.cpp
@@ -44,14 +44,16 @@ int FPIconLoader::Open(QString & path,Profile *profile)
 {
     QString newpath;
 
+    path=path.replace("\\","/");
+
     if (path.endsWith("/"))
         path.chop(1);
 
     QString dirtag="FPHCARE";
-    if (path.endsWith(QDir::separator()+dirtag)) {
+    if (path.endsWith("/"+dirtag)) {
         newpath=path;
     } else {
-        newpath=path+QDir::separator()+dirtag;
+        newpath=path+"/"+dirtag;
     }
 
     newpath+="/ICON/";
diff --git a/SleepLib/loader_plugins/intellipap_loader.cpp b/SleepLib/loader_plugins/intellipap_loader.cpp
index b734f5c5..6dac753e 100644
--- a/SleepLib/loader_plugins/intellipap_loader.cpp
+++ b/SleepLib/loader_plugins/intellipap_loader.cpp
@@ -44,12 +44,14 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
     // Check for DV5MFirm.bin?
     QString newpath;
 
+    path=path.replace("\\","/");
+
     QString dirtag="SL";
-    if (path.endsWith(QDir::separator()+dirtag)) {
+    if (path.endsWith("/"+dirtag)) {
         return 0;
         //newpath=path;
     } else {
-        newpath=path+QDir::separator()+dirtag;
+        newpath=path+"/"+dirtag;
     }
 
     QString filename;
@@ -57,7 +59,7 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
     //////////////////////////
     // Parse the Settings File
     //////////////////////////
-    filename=newpath+QDir::separator()+"SET1";
+    filename=newpath+"/SET1";
     QFile f(filename);
     if (!f.exists()) return 0;
     f.open(QFile::ReadOnly);
@@ -141,7 +143,7 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
     // Parse the Session Index
     //////////////////////////
     unsigned char buf[27];
-    filename=newpath+QDir::separator()+"U";
+    filename=newpath+"/U";
     f.setFileName(filename);
     if (!f.exists()) return 0;
 
@@ -171,7 +173,7 @@ int IntellipapLoader::Open(QString & path,Profile *profile)
     //////////////////////////
     // Parse the Session Data
     //////////////////////////
-    filename=newpath+QDir::separator()+"L";
+    filename=newpath+"/L";
     f.setFileName(filename);
     if (!f.exists()) return 0;
 
diff --git a/SleepLib/loader_plugins/prs1_loader.cpp b/SleepLib/loader_plugins/prs1_loader.cpp
index ebb18c57..446e2d7c 100644
--- a/SleepLib/loader_plugins/prs1_loader.cpp
+++ b/SleepLib/loader_plugins/prs1_loader.cpp
@@ -165,15 +165,20 @@ bool isdigit(QChar c)
     if ((c>='0') && (c<='9')) return true;
     return false;
 }
+
+const QString PR_STR_PSeries="P-Series";
+
 int PRS1Loader::Open(QString & path,Profile *profile)
 {
-    QString newpath,pseries="P-Series";
-    qDebug() << "PRS1Loader::Open path=" << newpath;
-    if (path.endsWith(QDir::separator()+pseries)) {
+    QString newpath;
+    path=path.replace("\\","/");
+
+    if (path.endsWith("/"+PR_STR_PSeries)) {
         newpath=path;
     } else {
-        newpath=path+QDir::separator()+pseries;
+        newpath=path+"/"+PR_STR_PSeries;
     }
+    qDebug() << "PRS1Loader::Open path=" << newpath;
 
     QDir dir(newpath);
 
@@ -218,11 +223,12 @@ int PRS1Loader::Open(QString & path,Profile *profile)
         QString s=*sn;
         m=CreateMachine(s,profile);
         try {
-            if (m) OpenMachine(m,newpath+QDir::separator()+(*sn),profile);
+            if (m)
+                OpenMachine(m,newpath+"/"+(*sn),profile);
         } catch(OneTypePerDay e) {
             profile->DelMachine(m);
             PRS1List.erase(PRS1List.find(s));
-            QMessageBox::warning(NULL,"Import Error","This Machine Record cannot be imported in this profile.\nThe Day records overlap with already existing content.",QMessageBox::Ok);
+            QMessageBox::warning(NULL,QObject::tr("Import Error"),QObject::tr("This Machine Record cannot be imported in this profile.\nThe Day records overlap with already existing content."),QMessageBox::Ok);
             delete m;
         }
     }
@@ -297,7 +303,8 @@ int PRS1Loader::OpenMachine(Machine *m,QString path,Profile *profile)
         } else if (filename.toLower()=="properties.txt") {
             ParseProperties(m,fi.canonicalFilePath());
         } else if (filename.toLower()=="e") {
-            // don't really give a crap about .004 files yet.
+            // Error files..
+            // Reminder: I have been given some info about these. should check it over.
         }
     }
 
@@ -1298,14 +1305,14 @@ bool PRS1Loader::OpenFile(Machine *mach, QString filename)
         }
 
 
-        qDebug() << "Loading" << filename << sequence << timestamp << size;
+        qDebug() << "Loading" << QDir::toNativeSeparators(filename) << sequence << timestamp << size;
         //if (ext==0) ParseCompliance(data,size);
         if (ext<=1) {
             ParseSummary(mach,sequence,timestamp,data,datasize,family,familyVersion);
         } else if (ext==2) {
             if (family==5) {
                if (!Parse002v5(sequence,timestamp,data,datasize)) {
-                   qDebug() << "in file: " << filename;
+                   qDebug() << "in file: " << QDir::toNativeSeparators(filename);
                }
             } else {
                Parse002(sequence,timestamp,data,datasize, family, familyVersion);
diff --git a/SleepLib/loader_plugins/resmed_loader.cpp b/SleepLib/loader_plugins/resmed_loader.cpp
index a5c53ff1..cd22d75b 100644
--- a/SleepLib/loader_plugins/resmed_loader.cpp
+++ b/SleepLib/loader_plugins/resmed_loader.cpp
@@ -31,20 +31,32 @@ extern QProgressBar *qprogress;
 QHash<int,QString> RMS9ModelMap;
 QHash<ChannelID, QVector<QString> > resmed_codes;
 
+const QString STR_ext_TGT="tgt";
+const QString STR_ext_CRC="crc";
+const QString STR_ext_EDF="edf";
+const QString STR_ext_gz=".gz";
+
+
 // Looks up foreign language Signal names that match this channelID
 EDFSignal * EDFParser::lookupSignal(ChannelID ch)
 {
     QHash<ChannelID, QVector<QString> >::iterator ci;
     QHash<QString,EDFSignal *>::iterator jj;
     ci=resmed_codes.find(ch);
-    if (ci==resmed_codes.end()) return NULL;
+
+    if (ci==resmed_codes.end())
+        return NULL;
+
     for (int i=0;i<ci.value().size();i++) {
         jj=lookup.find(ci.value()[i]);
-        if (jj==lookup.end()) continue;
+        if (jj==lookup.end())
+            continue;
         return jj.value();
     }
+
     return NULL;
 }
+
 EDFSignal * EDFParser::lookupName(QString name)
 {
     QHash<QString,EDFSignal *>::iterator i=lookup.find(name);
@@ -52,8 +64,6 @@ EDFSignal * EDFParser::lookupName(QString name)
     return NULL;
 }
 
-
-
 EDFParser::EDFParser(QString name)
 {
     buffer=NULL;
@@ -114,6 +124,7 @@ bool EDFParser::Parse()
     }
     QDateTime startDate=QDateTime::fromString(QString::fromLatin1(header.datetime,16),"dd.MM.yyHH.mm.ss");
     //startDate.toTimeSpec(Qt::UTC);
+
     QDate d2=startDate.date();
     if (d2.year()<2000) {
         d2.setDate(d2.year()+100,d2.month(),d2.day());
@@ -207,7 +218,7 @@ bool EDFParser::Open(QString name)
 
     //Urk.. This needs fixing for VC++, as it doesn't have packed attribute type..
 
-    if (name.endsWith(".gz")) {
+    if (name.endsWith(STR_ext_gz)) {
         filename=name.mid(0,-3);
         QFile fi(name);
         fi.open(QFile::ReadOnly);
@@ -285,21 +296,17 @@ Machine *ResmedLoader::CreateMachine(QString serial,Profile *profile)
     m->properties[STR_PROP_BackupPath]=path+"Backup/";
 
     return m;
-
 }
 
 long event_cnt=0;
 
+const QString RMS9_STR_datalog="DATALOG";
+const QString RMS9_STR_idfile="Identification.";
+const QString RMS9_STR_strfile="STR.";
+
+
 int ResmedLoader::Open(QString & path,Profile *profile)
 {
-    const QString datalog="DATALOG";
-    const QString idfile="Identification.";
-    const QString strfile="STR.";
-
-    const QString ext_TGT="tgt";
-    const QString ext_CRC="crc";
-    const QString ext_EDF="edf";
-    const QString ext_gz=".gz";
 
     QString serial;                 // Serial number
     QString key,value;
@@ -309,16 +316,18 @@ int ResmedLoader::Open(QString & path,Profile *profile)
 
     QHash<QString,QString> idmap;   // Temporary properties hash
 
+    path=path.replace("\\","/");
+
     // Strip off end "/" if any
     if (path.endsWith("/"))
         path=path.section("/",0,-2);
 
     // Strip off DATALOG from path, and set newpath to the path contianing DATALOG
-    if (path.endsWith(datalog)) {
+    if (path.endsWith(RMS9_STR_datalog)) {
         newpath=path+"/";
         path=path.section("/",0,-2);
     } else {
-        newpath=path+"/"+datalog+"/";
+        newpath=path+"/"+RMS9_STR_datalog+"/";
     }
 
     // Add separator back
@@ -331,7 +340,7 @@ int ResmedLoader::Open(QString & path,Profile *profile)
     ///////////////////////////////////////////////////////////////////////////////////
     // Parse Identification.tgt file (containing serial number and machine information)
     ///////////////////////////////////////////////////////////////////////////////////
-    filename=path+idfile+ext_TGT;
+    filename=path+RMS9_STR_idfile+STR_ext_TGT;
     QFile f(filename);
     // Abort if this file is dodgy..
     if (!f.exists() || !f.open(QIODevice::ReadOnly))
@@ -360,10 +369,10 @@ int ResmedLoader::Open(QString & path,Profile *profile)
     }
 
     // Early check for STR.edf file, so we can early exit before creating faulty machine record.
-    QString strpath=path+strfile+ext_EDF;  // STR.edf file
+    QString strpath=path+RMS9_STR_strfile+STR_ext_EDF;  // STR.edf file
     f.setFileName(strpath);
     if (!f.exists()) { // No STR.edf.. Do we have a STR.edf.gz?
-        strpath+=ext_gz;
+        strpath+=STR_ext_gz;
         f.setFileName(strpath);
         if (!f.exists()) {
             qDebug() << "Missing STR.edf file";
@@ -405,7 +414,7 @@ int ResmedLoader::Open(QString & path,Profile *profile)
     ///////////////////////////////////////////////////////////////////////////////////
     EDFParser stredf(strpath);
     if (!stredf.Parse()) {
-        qDebug() << "Faulty file" << strfile;
+        qDebug() << "Faulty file" << RMS9_STR_strfile;
         return 0;
     }
     if (stredf.serialnumber!=serial) {
@@ -423,20 +432,20 @@ int ResmedLoader::Open(QString & path,Profile *profile)
     ///////////////////////////////////////////////////////////////////////////////////
     if (create_backups) {
         if (!dir.exists(backup_path)) {
-            if (!dir.mkpath(backup_path+datalog)) {
+            if (!dir.mkpath(backup_path+RMS9_STR_datalog)) {
                 qDebug() << "Could not create S9 backup directory :-/";
             }
         }
 
         // Copy Identification files to backup folder
-        QFile::copy(path+idfile+ext_TGT,backup_path+idfile+ext_TGT);
-        QFile::copy(path+idfile+ext_CRC,backup_path+idfile+ext_CRC);
+        QFile::copy(path+RMS9_STR_idfile+STR_ext_TGT,backup_path+RMS9_STR_idfile+STR_ext_TGT);
+        QFile::copy(path+RMS9_STR_idfile+STR_ext_CRC,backup_path+RMS9_STR_idfile+STR_ext_CRC);
 
         //copy STR files to backup folder
-        if (strpath.endsWith(ext_gz))  // Already compressed.
-            QFile::copy(strpath,backup_path+strfile+ext_EDF+ext_gz);
+        if (strpath.endsWith(STR_ext_gz))  // Already compressed.
+            QFile::copy(strpath,backup_path+RMS9_STR_strfile+STR_ext_EDF+STR_ext_gz);
         else { // Compress STR file to backup folder
-            QString strf=backup_path+strfile+ext_EDF;
+            QString strf=backup_path+RMS9_STR_strfile+STR_ext_EDF;
             if (QFile::exists(strf))
                 QFile::remove(strf);
 
@@ -557,9 +566,9 @@ int ResmedLoader::Open(QString & path,Profile *profile)
             continue;
 
         // Accept only .edf and .edf.gz files
-        if (filename.right(4).toLower() != "."+ext_EDF) {
+        if (filename.right(4).toLower() != "."+STR_ext_EDF) {
 
-            if (filename.right(7).toLower() != "."+ext_EDF+ext_gz)
+            if (filename.right(7).toLower() != "."+STR_ext_EDF+STR_ext_gz)
                 continue;
             gz=true;
         } else gz=false;
@@ -604,7 +613,7 @@ int ResmedLoader::Open(QString & path,Profile *profile)
         if (si!=sessfiles.end()) {
             // Ignore if already compressed version of the same file exists.. (just in case)
             if (!gz) {
-                if (si.value().contains(filename+ext_gz,Qt::CaseInsensitive))
+                if (si.value().contains(filename+STR_ext_gz,Qt::CaseInsensitive))
                     continue;
             } else {
                 QString str=filename;
@@ -1046,7 +1055,7 @@ int ResmedLoader::Open(QString & path,Profile *profile)
 //        }
 
 //    }
-    backup_path+=datalog+"/";
+    backup_path+=RMS9_STR_datalog+"/";
 
     /////////////////////////////////////////////////////////////////////////////
     // Scan through new file list and import sessions
@@ -1065,14 +1074,14 @@ int ResmedLoader::Open(QString & path,Profile *profile)
         // Process EDF File List
         for (int i=0;i<si.value().size();++i) {
             filename=si.value()[i];
-            gz=(filename.right(3).toLower()==ext_gz);
+            gz=(filename.right(3).toLower()==STR_ext_gz);
             fullpath=newpath+filename;
 
             // Copy the EDF file to the backup folder
             if (create_backups) {
                 backupfile=backup_path+filename;
                 bool dobackup=true;
-                if (!gz && QFile::exists(backupfile+".gz")) {
+                if (!gz && QFile::exists(backupfile+STR_ext_gz)) {
                     dobackup=false;
                 } else if (QFile::exists(backupfile)) {
                     if (gz) {
@@ -1650,22 +1659,22 @@ bool ResmedLoader::LoadPLD(Session *sess,EDFParser &edf)
     return true;
 }
 
+const QString RMS9_STR_Escape="S9 Escape";
+const QString RMS9_STR_EscapeAuto="S9 Escape Auto";
+const QString RMS9_STR_Elite="S9 Elite";
+const QString RMS9_STR_AutoSet="S9 AutoSet";
+const QString RMS9_STR_AutoSetForHer="S9 AutoSet for Her";
+const QString RMS9_STR_AutoSetCS="S9 AutoSet CS";
+const QString RMS9_STR_AutoSet25="S9 AutoSet 25";
+const QString RMS9_STR_VPAP_S="S9 VPAP S";
+const QString RMS9_STR_VPAP_Auto="S9 VPAP Auto";
+const QString RMS9_STR_VPAP_Adapt="S9 VPAP Adapt";
+const QString RMS9_STR_VPAP_ST="S9 VPAP ST";
+const QString RMS9_STR_VPAP_STA="S9 VPAP ST-A";
+const QString RMS9_STR_VPAP_ST22="S9 VPAP ST 22";
+
 void ResInitModelMap()
 {
-    const QString RMS9_STR_Escape="S9 Escape";
-    const QString RMS9_STR_EscapeAuto="S9 Escape Auto";
-    const QString RMS9_STR_Elite="S9 Elite";
-    const QString RMS9_STR_AutoSet="S9 AutoSet";
-    const QString RMS9_STR_AutoSetForHer="S9 AutoSet for Her";
-    const QString RMS9_STR_AutoSetCS="S9 AutoSet CS";
-    const QString RMS9_STR_AutoSet25="S9 AutoSet 25";
-    const QString RMS9_STR_VPAP_S="S9 VPAP S";
-    const QString RMS9_STR_VPAP_Auto="S9 VPAP Auto";
-    const QString RMS9_STR_VPAP_Adapt="S9 VPAP Adapt";
-    const QString RMS9_STR_VPAP_ST="S9 VPAP ST";
-    const QString RMS9_STR_VPAP_STA="S9 VPAP ST-A";
-    const QString RMS9_STR_VPAP_ST22="S9 VPAP ST 22";
-
     // Escape Series
     RMS9ModelMap[36001]=RMS9ModelMap[36011]=RMS9ModelMap[36021]=RMS9ModelMap[36141]=
     RMS9ModelMap[36201]=RMS9ModelMap[36221]=RMS9ModelMap[36261]=RMS9ModelMap[36301]=
@@ -1772,6 +1781,8 @@ void ResInitModelMap()
     RMS9ModelMap[33129]="S8 AutoSet II";
     */
 
+
+    // Translation lookup table for non-english machines
     resmed_codes[CPAP_FlowRate].push_back("Flow");
     resmed_codes[CPAP_MaskPressureHi].push_back("Mask Pres");
     resmed_codes[CPAP_MaskPressureHi].push_back("Mask Pressure"); // vpap
diff --git a/SleepLib/loader_plugins/zeo_loader.cpp b/SleepLib/loader_plugins/zeo_loader.cpp
index a93a1ce2..5837764b 100644
--- a/SleepLib/loader_plugins/zeo_loader.cpp
+++ b/SleepLib/loader_plugins/zeo_loader.cpp
@@ -38,14 +38,16 @@ int ZEOLoader::Open(QString & path,Profile *profile)
 
     // Could Scan the ZEO folder for a list of CSVs
 
-    if (path.toLower().endsWith(QDir::separator()+dirtag)) {
+    path=path.replace("\\","/");
+
+    if (path.toLower().endsWith("/"+dirtag)) {
         return 0;
         //newpath=path;
     } else {
-        newpath=path+QDir::separator()+dirtag.toUpper();
+        newpath=path+"/"+dirtag.toUpper();
     }
 
-    QString filename;
+    //QString filename;
 
     // ZEO folder structure detection stuff here.
 
diff --git a/SleepLib/machine.cpp b/SleepLib/machine.cpp
index 2ea401ac..e668c7af 100644
--- a/SleepLib/machine.cpp
+++ b/SleepLib/machine.cpp
@@ -225,7 +225,7 @@ bool Machine::Purge(int secret)
         bool ok;
         ext_s.toInt(&ok,10);
         if (ok) {
-            qDebug() << "Deleting " << fullpath;
+            qDebug() << "Deleting " << QDir::toNativeSeparators(fullpath);
             dir.remove(fullpath);
         } else could_not_kill++;
 
@@ -246,7 +246,7 @@ bool Machine::Load()
     QString path=profile->Get(properties[STR_PROP_Path]); //STR_GEN_DataFolder)+"/"+m_class+"_"+hexid();
 
     QDir dir(path);
-    qDebug() << "Loading " << path;
+    qDebug() << "Loading " << QDir::toNativeSeparators(path);
 
     if (!dir.exists() || !dir.isReadable())
         return false;
diff --git a/SleepLib/preferences.cpp b/SleepLib/preferences.cpp
index 9e6dda75..818b398b 100644
--- a/SleepLib/preferences.cpp
+++ b/SleepLib/preferences.cpp
@@ -67,7 +67,7 @@ QString GetAppRoot()
 #endif
 
     if (HomeAppRoot.isEmpty())
-        HomeAppRoot=desktopFolder+QDir::separator()+AppRoot;
+        HomeAppRoot=desktopFolder+"/"+AppRoot;
 
     return HomeAppRoot;
 }
@@ -81,25 +81,22 @@ Preferences::Preferences()
 
 Preferences::Preferences(QString name,QString filename)
 {
-
-    const QString xmlext=".xml";
-
-    if (name.endsWith(xmlext)) {
+    if (name.endsWith(STR_ext_XML)) {
         p_name=name.section(".",0,0);
     } else {
         p_name=name;
     }
 
     if (filename.isEmpty()) {
-        p_filename=GetAppRoot()+QDir::separator()+p_name+xmlext;
+        p_filename=GetAppRoot()+"/"+p_name+STR_ext_XML;
     } else {
-        if (!filename.contains(QDir::separator())) {
-            p_filename=GetAppRoot()+QDir::separator();
+        if (!filename.contains("/")) {
+            p_filename=GetAppRoot()+"/";
         } else p_filename="";
 
         p_filename+=filename;
 
-        if (!p_filename.endsWith(xmlext)) p_filename+=xmlext;
+        if (!p_filename.endsWith(STR_ext_XML)) p_filename+=STR_ext_XML;
     }
 }
 
@@ -149,7 +146,7 @@ const QString Preferences::Get(QString name)
         } else if (ref.toLower()=="user") {
             temp+=getUserName();
         } else if (ref.toLower()=="sep") { // redundant in QT
-            temp+=QDir::separator();
+            temp+="/";
         } else {
             temp+=Get(ref);
         }
@@ -169,13 +166,13 @@ bool Preferences::Open(QString filename)
 
     QDomDocument doc(p_name);
     QFile file(p_filename);
-    qDebug() << "Scanning " << p_filename;
+    qDebug() << "Scanning " << QDir::toNativeSeparators(p_filename);
     if (!file.open(QIODevice::ReadOnly)) {
-        qWarning() << "Could not open" << p_filename;
+        qWarning() << "Could not open" << QDir::toNativeSeparators(p_filename);
         return false;
     }
     if (!doc.setContent(&file)) {
-        qWarning() << "Invalid XML Content in" << p_filename;
+        qWarning() << "Invalid XML Content in" << QDir::toNativeSeparators(p_filename);
         return false;
     }
     file.close();
diff --git a/SleepLib/preferences.h b/SleepLib/preferences.h
index f685d919..4031ef00 100644
--- a/SleepLib/preferences.h
+++ b/SleepLib/preferences.h
@@ -21,6 +21,8 @@ License: GPL
 const QString AppName="SleepyHead"; // Outer tag of XML files
 const QString AppRoot="SleepyHeadData";    // The Folder Name
 
+const QString STR_ext_XML=".xml";
+
 extern QString GetAppRoot(); //returns app root path plus trailing path separator.
 
 inline QString PrefMacro(QString s)
diff --git a/SleepLib/profiles.cpp b/SleepLib/profiles.cpp
index 730f97ee..5a69d3ad 100644
--- a/SleepLib/profiles.cpp
+++ b/SleepLib/profiles.cpp
@@ -48,15 +48,15 @@ Profile::Profile()
 Profile::Profile(QString path)
 :Preferences(),is_first_day(true)
 {
-    const QString xmlext=".xml";
     p_name=STR_GEN_Profile;
     if (path.isEmpty())
         p_path=GetAppRoot();
     else p_path=path;
     (*this)[STR_GEN_DataFolder]=p_path;
+    path=path.replace("\\","/");
     if (!p_path.endsWith("/"))
         p_path+="/";
-    p_filename=p_path+p_name+xmlext;
+    p_filename=p_path+p_name+STR_ext_XML;
     machlist.clear();
 
     doctor=NULL;
@@ -247,7 +247,6 @@ QDomElement Profile::ExtraSave(QDomDocument & doc)
 
 }
 
-#include <QMessageBox>
 void Profile::AddDay(QDate date,Day *day,MachineType mt) {
     //date+=wxTimeSpan::Day();
     if (!day)  {
@@ -279,6 +278,7 @@ void Profile::AddDay(QDate date,Day *day,MachineType mt) {
     daylist[date].push_back(day);
 }
 
+// Get Day record if data available for date and machine type, and has enabled session data, else return NULL
 Day * Profile::GetGoodDay(QDate date,MachineType type)
 {
     Day *day=NULL;
@@ -363,8 +363,8 @@ MachineLoader * GetLoader(QString name)
 }
 
 
-QList<Machine *> Profile::GetMachines(MachineType t)
 // Returns a QVector containing all machine objects regisered of type t
+QList<Machine *> Profile::GetMachines(MachineType t)
 {
     QList<Machine *> vec;
     QHash<MachineID,Machine *>::iterator i;
@@ -912,6 +912,7 @@ EventDataType Profile::calcPercentile(ChannelID code, EventDataType percent, Mac
     return v;
 }
 
+// Lookup first day record of the specified machine type, or return the first day overall if MT_UNKNOWN
 QDate Profile::FirstDay(MachineType mt)
 {
     if ((mt==MT_UNKNOWN) || (!m_last.isValid()) || (!m_first.isValid()))
@@ -925,6 +926,8 @@ QDate Profile::FirstDay(MachineType mt)
     } while (d<=m_last);
     return m_last;
 }
+
+// Lookup last day record of the specified machine type, or return the first day overall if MT_UNKNOWN
 QDate Profile::LastDay(MachineType mt)
 {
     if ((mt==MT_UNKNOWN) || (!m_last.isValid()) || (!m_first.isValid()))
diff --git a/SleepyHeadQT.pro b/SleepyHeadQT.pro
index 9809b9b8..6ced38e5 100644
--- a/SleepyHeadQT.pro
+++ b/SleepyHeadQT.pro
@@ -88,7 +88,9 @@ SOURCES += main.cpp\
     UpdaterWindow.cpp \
     SleepLib/common.cpp \
     SleepLib/loader_plugins/icon_loader.cpp \
-    SleepLib/loader_plugins/mseries_loader.cpp
+    SleepLib/loader_plugins/mseries_loader.cpp \
+    reports.cpp \
+    summary.cpp
 
 unix:SOURCES           += qextserialport/posix_qextserialport.cpp
 unix:!macx:SOURCES     += qextserialport/qextserialenumerator_unix.cpp
@@ -168,7 +170,9 @@ HEADERS  += \
     UpdaterWindow.h \
     SleepLib/common.h \
     SleepLib/loader_plugins/icon_loader.h \
-    SleepLib/loader_plugins/mseries_loader.h
+    SleepLib/loader_plugins/mseries_loader.h \
+    reports.h \
+    summary.h
 
 
 FORMS    += \
diff --git a/mainwindow.cpp b/mainwindow.cpp
index 93c3479f..11594cb0 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -45,6 +45,8 @@
 #include "SleepLib/calcs.h"
 #include "version.h"
 
+#include "reports.h"
+#include "summary.h"
 
 QProgressBar *qprogress;
 QLabel *qstatus;
@@ -402,7 +404,7 @@ void MainWindow::on_action_Import_Data_triggered()
             file.close();
         }
     } else {
-        mainwin->Notify("Import Problem\n\nCouldn't find any new Machine Data at the locations given");
+        mainwin->Notify(tr("Import Problem\n\nCouldn't find any new Machine Data at the locations given"));
     }
 }
 QMenu * MainWindow::CreateMenu(QString title)
@@ -420,192 +422,9 @@ void MainWindow::on_action_Fullscreen_triggered()
         this->showNormal();
 }
 
-QString htmlHeader()
+void MainWindow::setRecBoxHTML(QString html)
 {
-
-    //   "a:link,a:visited { color: '#000020'; text-decoration: none; font-weight: bold;}"
-//   "a:hover { background-color: inherit; color: red; text-decoration:none; font-weight: bold; }"
-    return QString("<html><head>"
-"</head>"
-"<style type='text/css'>"
-"<!--h1,p,a,td,body { font-family: 'FreeSans', 'Sans Serif' } --/>"
-"p,a,td,body { font-size: 14px }"
-"</style>"
-"<link rel='stylesheet' type='text/css' href='qrc:/docs/tooltips.css' />"
-"<script type='text/javascript'>"
-"function ChangeColor(tableRow, highLight)"
-"{ tableRow.style.backgroundColor = highLight; }"
-"function Go(url) { throw(url); }"
-"</script>"
-"</head>"
-"<body leftmargin=0 topmargin=0 rightmargin=0>"
-"<div align=center><table cellpadding=3 cellspacing=0 border=0>"
-"<tr><td><img src='qrc:/icons/bob-v3.0.png' width=140px height=140px><td valign=center align=center><h1>"+QObject::tr("SleepyHead")+" v"+VersionString+"</h1><i>"+QObject::tr("This is a beta software and some functionality may not work as intended yet.")+"<br/>"+QObject::tr("Please report any bugs you find to SleepyHead's SourceForge page.")+"</i></td></tr></table>"
-"</div>"
-"<hr/>");
-}
-QString htmlFooter()
-{
-    return QString("</body></html>");
-}
-
-EventDataType calcAHI(QDate start, QDate end)
-{
-    EventDataType val=(p_profile->calcCount(CPAP_Obstructive,MT_CPAP,start,end)
-              +p_profile->calcCount(CPAP_Hypopnea,MT_CPAP,start,end)
-              +p_profile->calcCount(CPAP_ClearAirway,MT_CPAP,start,end)
-              +p_profile->calcCount(CPAP_Apnea,MT_CPAP,start,end));
-    if (PROFILE.general->calculateRDI())
-        val+=p_profile->calcCount(CPAP_RERA,MT_CPAP,start,end);
-    EventDataType hours=p_profile->calcHours(MT_CPAP,start,end);
-
-    if (hours>0)
-        val/=hours;
-    else
-        val=0;
-
-    return val;
-}
-
-EventDataType calcFL(QDate start, QDate end)
-{
-    EventDataType val=(p_profile->calcCount(CPAP_FlowLimit,MT_CPAP,start,end));
-    EventDataType hours=p_profile->calcHours(MT_CPAP,start,end);
-
-    if (hours>0)
-        val/=hours;
-    else
-        val=0;
-
-    return val;
-}
-
-
-struct RXChange
-{
-    RXChange() { highlight=0; machine=NULL; }
-    RXChange(const RXChange & copy) {
-        first=copy.first;
-        last=copy.last;
-        days=copy.days;
-        ahi=copy.ahi;
-	fl=copy.fl;
-        mode=copy.mode;
-        min=copy.min;
-        max=copy.max;
-        maxhi=copy.maxhi;
-        machine=copy.machine;
-        per1=copy.per1;
-        per2=copy.per2;
-        highlight=copy.highlight;
-        weighted=copy.weighted;
-        prelief=copy.prelief;
-        prelset=copy.prelset;
-    }
-    QDate first;
-    QDate last;
-    int days;
-    EventDataType ahi;
-    EventDataType fl;
-    CPAPMode mode;
-    EventDataType min;
-    EventDataType max;
-    EventDataType maxhi;
-    EventDataType per1;
-    EventDataType per2;
-    EventDataType weighted;
-    PRTypes prelief;
-    Machine * machine;
-    short prelset;
-    short highlight;
-};
-
-enum RXSortMode { RX_first, RX_last, RX_days, RX_ahi, RX_mode, RX_min, RX_max, RX_maxhi, RX_per1, RX_per2, RX_weighted };
-RXSortMode RXsort=RX_first;
-bool RXorder=false;
-
-bool operator<(const RXChange & c1, const RXChange & c2) {
-    const RXChange * comp1=&c1;
-    const RXChange * comp2=&c2;
-    if (RXorder) {
-        switch (RXsort) {
-        case RX_ahi: return comp1->ahi < comp2->ahi;
-        case RX_days: return comp1->days < comp2->days;
-        case RX_first: return comp1->first < comp2->first;
-        case RX_last: return comp1->last < comp2->last;
-        case RX_mode: return comp1->mode < comp2->mode;
-        case RX_min:  return comp1->min < comp2->min;
-        case RX_max:  return comp1->max < comp2->max;
-        case RX_maxhi: return comp1->maxhi < comp2->maxhi;
-        case RX_per1:  return comp1->per1 < comp2->per1;
-        case RX_per2:  return comp1->per2 < comp2->per2;
-        case RX_weighted:  return comp1->weighted < comp2->weighted;
-        };
-    } else {
-        switch (RXsort) {
-        case RX_ahi: return comp1->ahi > comp2->ahi;
-        case RX_days: return comp1->days > comp2->days;
-        case RX_first: return comp1->first > comp2->first;
-        case RX_last: return comp1->last > comp2->last;
-        case RX_mode: return comp1->mode > comp2->mode;
-        case RX_min:  return comp1->min > comp2->min;
-        case RX_max:  return comp1->max > comp2->max;
-        case RX_maxhi: return comp1->maxhi > comp2->maxhi;
-        case RX_per1:  return comp1->per1 > comp2->per1;
-        case RX_per2:  return comp1->per2 > comp2->per2;
-        case RX_weighted:  return comp1->weighted > comp2->weighted;
-        };
-    }
-    return true;
-}
-
-bool RXSort(const RXChange * comp1, const RXChange * comp2) {
-    if (RXorder) {
-        switch (RXsort) {
-        case RX_ahi: return comp1->ahi < comp2->ahi;
-        case RX_days: return comp1->days < comp2->days;
-        case RX_first: return comp1->first < comp2->first;
-        case RX_last: return comp1->last < comp2->last;
-        case RX_mode: return comp1->mode < comp2->mode;
-        case RX_min:  return comp1->min < comp2->min;
-        case RX_max:  return comp1->max < comp2->max;
-        case RX_maxhi: return comp1->maxhi < comp2->maxhi;
-        case RX_per1:  return comp1->per1 < comp2->per1;
-        case RX_per2:  return comp1->per2 < comp2->per2;
-        case RX_weighted:  return comp1->weighted < comp2->weighted;
-        };
-    } else {
-        switch (RXsort) {
-        case RX_ahi: return comp1->ahi > comp2->ahi;
-        case RX_days: return comp1->days > comp2->days;
-        case RX_first: return comp1->first > comp2->first;
-        case RX_last: return comp1->last > comp2->last;
-        case RX_mode: return comp1->mode > comp2->mode;
-        case RX_min:  return comp1->min > comp2->min;
-        case RX_max:  return comp1->max > comp2->max;
-        case RX_maxhi: return comp1->maxhi > comp2->maxhi;
-        case RX_per1:  return comp1->per1 > comp2->per1;
-        case RX_per2:  return comp1->per2 > comp2->per2;
-        case RX_weighted:  return comp1->weighted > comp2->weighted;
-        };
-    }
-    return true;
-}
-struct UsageData {
-    UsageData() { ahi=0; hours=0; }
-    UsageData(QDate d, EventDataType v, EventDataType h) { date=d; ahi=v; hours=h; }
-    UsageData(const UsageData & copy) { date=copy.date; ahi=copy.ahi; hours=copy.hours; }
-    QDate date;
-    EventDataType ahi;
-    EventDataType hours;
-};
-bool operator <(const UsageData & c1, const UsageData & c2)
-{
-    if (c1.ahi < c2.ahi)
-        return true;
-    if ((c1.ahi == c2.ahi) && (c1.date > c2.date)) return true;
-    return false;
-    //return c1.value < c2.value;
+    ui->recordsBox->setHtml(html);
 }
 
 class MyStatsPage:public QWebPage
@@ -631,718 +450,18 @@ void MyStatsPage::javaScriptAlert(QWebFrame * frame, const QString & msg)
     mainwin->sendStatsUrl(msg);
 }
 
-
-//bool operator<(const RXChange * & comp1, const RXChange * & comp2) {
-
-//}
-
 void MainWindow::on_homeButton_clicked()
 {
     ui->webView->setUrl(QUrl("qrc:/docs/index.html"));
 }
 
-QString formatTime(float time)
-{
-    int hours=time;
-    int seconds=time*3600.0;
-    int minutes=(seconds / 60) % 60;
-    seconds %= 60;
-    return QString().sprintf("%02i:%02i",hours,minutes); //,seconds);
-}
-
 
 void MainWindow::on_summaryButton_clicked()
 {
-    QString html=htmlHeader();
+    QString html=Summary::GenerateHTML();
 
-    QDate lastcpap=p_profile->LastGoodDay(MT_CPAP);
-    QDate firstcpap=p_profile->FirstGoodDay(MT_CPAP);
-    QDate cpapweek=lastcpap.addDays(-7);
-    QDate cpapmonth=lastcpap.addDays(-30);
-    QDate cpap6month=lastcpap.addMonths(-6);
-    QDate cpapyear=lastcpap.addYears(-12);
-    if (cpapweek<firstcpap) cpapweek=firstcpap;
-    if (cpapmonth<firstcpap) cpapmonth=firstcpap;
-    if (cpap6month<firstcpap) cpap6month=firstcpap;
-    if (cpapyear<firstcpap) cpapyear=firstcpap;
-
-
-    QList<Machine *> cpap_machines=PROFILE.GetMachines(MT_CPAP);
-    QList<Machine *> oximeters=PROFILE.GetMachines(MT_OXIMETER);
-    QList<Machine *> mach;
-    mach.append(cpap_machines);
-    mach.append(oximeters);
-
-
-    if (mach.size()==0) {
-        html+="<table cellpadding=2 cellspacing=0 border=0 width=100% height=60%>";
-        QString datacard;
-        html+="<tr><td align=center><h1>"+tr("Please Import Some Data")+"</h1><i>"+tr("SleepyHead is pretty much useless without it.")+"</i><br/><p>"+tr("It might be a good idea to check preferences first,</br>as there are some options that affect import.")+"</p><p>"+tr("First import can take a few minutes.")+"</p></td></tr></table>";
-        html+=htmlFooter();
-        ui->summaryView->setHtml(html);
-        return;
-    }
-    int cpapdays=PROFILE.countDays(MT_CPAP,firstcpap,lastcpap);
-    int cpapweekdays=PROFILE.countDays(MT_CPAP,cpapweek,lastcpap);
-    int cpapmonthdays=PROFILE.countDays(MT_CPAP,cpapmonth,lastcpap);
-    int cpapyeardays=PROFILE.countDays(MT_CPAP,cpapyear,lastcpap);
-    int cpap6monthdays=PROFILE.countDays(MT_CPAP,cpap6month,lastcpap);
-
-    CPAPMode cpapmode=(CPAPMode)(int)p_profile->calcSettingsMax(CPAP_Mode,MT_CPAP,firstcpap,lastcpap);
-
-    float percentile=PROFILE.general->prefCalcPercentile()/100.0;
-
-//    int mididx=PROFILE.general->prefCalcMiddle();
-//    SummaryType ST_mid;
-//    if (mididx==0) ST_mid=ST_PERC;
-//    if (mididx==1) ST_mid=ST_WAVG;
-//    if (mididx==2) ST_mid=ST_AVG;
-
-    QString ahitxt;
-    if (PROFILE.general->calculateRDI()) {
-        ahitxt=STR_TR_RDI;
-    } else {
-        ahitxt=STR_TR_AHI;
-    }
-
-    int decimals=2;
-    html+="<div align=center>";
-    html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=90%>");
-    if (cpapdays==0)  {
-        html+="<tr><td colspan=6 align=center>"+tr("No CPAP Machine Data Imported")+"</td></tr>";
-    } else {
-        html+=QString("<tr><td colspan=6 align=center><b>")+tr("CPAP Statistics as of")+QString(" %1</b></td></tr>").arg(lastcpap.toString(Qt::SystemLocaleLongDate));
-
-        if (cpap_machines.size()>0) {
-           // html+=QString("<tr><td colspan=6 align=center><b>%1</b></td></tr>").arg(tr("CPAP Summary"));
-
-            if (!cpapdays) {
-                html+=QString("<tr><td colspan=6 align=center><b>%1</b></td></tr>").arg(tr("No CPAP data available."));
-            } else if (cpapdays==1) {
-                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 day of CPAP Data, on %2.")).arg(cpapdays).arg(firstcpap.toString(Qt::SystemLocaleShortDate)));
-            } else {
-                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 days of CPAP Data, between %2 and %3")).arg(cpapdays).arg(firstcpap.toString(Qt::SystemLocaleShortDate)).arg(lastcpap.toString(Qt::SystemLocaleShortDate)));
-            }
-
-            html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
-                .arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(tr("Last 6 months")).arg(tr("Last Year"));
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-            .arg(ahitxt)
-            .arg(calcAHI(lastcpap,lastcpap),0,'f',decimals)
-            .arg(calcAHI(cpapweek,lastcpap),0,'f',decimals)
-            .arg(calcAHI(cpapmonth,lastcpap),0,'f',decimals)
-            .arg(calcAHI(cpap6month,lastcpap),0,'f',decimals)
-            .arg(calcAHI(cpapyear,lastcpap),0,'f',decimals);
-
-            if (PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapyear,lastcpap)) {
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("RERA Index"))
-                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,lastcpap,lastcpap)/PROFILE.calcHours(MT_CPAP,lastcpap,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapweek,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapmonth,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpap6month,lastcpap)/PROFILE.calcHours(MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapyear,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-            }
-
-            if (PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapyear,lastcpap)) {
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Flow Limit Index"))
-                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,lastcpap,lastcpap)/PROFILE.calcHours(MT_CPAP,lastcpap,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapweek,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapmonth,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpap6month,lastcpap)/PROFILE.calcHours(MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapyear,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-            }
-
-
-
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-            .arg(tr("Hours per Night"))
-            .arg(formatTime(p_profile->calcHours(MT_CPAP)))
-            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpapweek,lastcpap)/float(cpapweekdays)))
-            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpapmonth,lastcpap)/float(cpapmonthdays)))
-            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpap6month,lastcpap)/float(cpap6monthdays)))
-            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpapyear,lastcpap)/float(cpapyeardays)));
-
-            if (cpapmode>=MODE_BIPAP) {
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Min EPAP"))
-                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP),0,'f',decimals)
-                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(QString("%1% "+STR_TR_EPAP).arg(percentile*100.0,0,'f',0))
-                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Max IPAP"))
-                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP),0,'f',decimals)
-                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(QString("%1% "+STR_TR_IPAP).arg(percentile*100.0,0,'f',0))
-                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-            } else if (cpapmode>=MODE_APAP) {
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Average Pressure"))
-                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP),0,'f',decimals)
-                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("%1% Pressure").arg(percentile*100.0,0,'f',0))
-                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-            } else {
-                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Pressure"))
-                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP),0,'f',decimals)
-                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-            }
-            //html+="<tr><td colspan=6>TODO: 90% pressure.. Any point showing if this is all CPAP?</td></tr>";
-
-
-            ChannelID leak;
-            if (p_profile->calcCount(CPAP_LeakTotal,MT_CPAP,cpapyear,lastcpap)>0) {
-                leak=CPAP_LeakTotal;
-            } else leak=CPAP_Leak;
-
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-            .arg(tr("Average %1").arg(schema::channel[leak].label()))
-            .arg(p_profile->calcWavg(leak,MT_CPAP),0,'f',decimals)
-            .arg(p_profile->calcWavg(leak,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-            .arg(p_profile->calcWavg(leak,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-            .arg(p_profile->calcWavg(leak,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-            .arg(p_profile->calcWavg(leak,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-            .arg(tr("%1% %2").arg(percentile*100.0f,0,'f',0).arg(schema::channel[leak].label()))
-            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP),0,'f',decimals)
-            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
-            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
-            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
-            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
-        }
-    }
-    int oxisize=oximeters.size();
-    if (oxisize>0) {
-        QDate lastoxi=p_profile->LastGoodDay(MT_OXIMETER);
-        QDate firstoxi=p_profile->FirstGoodDay(MT_OXIMETER);
-        int days=PROFILE.countDays(MT_OXIMETER,firstoxi,lastoxi);
-        if (days>0) {
-            html+=QString("<tr><td colspan=6 align=center><b>%1</b></td></tr>").arg(tr("Oximetry Summary"));
-            if (days==1) {
-                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 day of Oximetry Data, on %2.")).arg(days).arg(firstoxi.toString(Qt::SystemLocaleShortDate)));
-            } else {
-                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 days of Oximetry Data, between %2 and %3")).arg(days).arg(firstoxi.toString(Qt::SystemLocaleShortDate)).arg(lastoxi.toString(Qt::SystemLocaleShortDate)));
-            }
-
-            html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
-                    .arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(tr("Last 6 months")).arg(tr("Last Year"));
-            QDate oxiweek=lastoxi.addDays(-7);
-            QDate oximonth=lastoxi.addDays(-30);
-            QDate oxi6month=lastoxi.addMonths(-6);
-            QDate oxiyear=lastoxi.addYears(-12);
-            if (oxiweek<firstoxi) oxiweek=firstoxi;
-            if (oximonth<firstoxi) oximonth=firstoxi;
-            if (oxi6month<firstoxi) oxi6month=firstoxi;
-            if (oxiyear<firstoxi) oxiyear=firstoxi;
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Average SpO2"))
-                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Minimum SpO2"))
-                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("SpO2 Events / Hour"))
-                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER)/p_profile->calcHours(MT_OXIMETER),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiweek,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oximonth,lastoxi)/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxi6month,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiyear,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2\%</td><td>%3\%</td><td>%4\%</td><td>%5\%</td><td>%6\%</td></tr>")
-                .arg(tr("% of time in SpO2 Events"))
-                .arg(100.0/p_profile->calcHours(MT_OXIMETER)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER)/3600.0,0,'f',decimals)
-                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oxiweek,lastoxi)/3600.0,0,'f',decimals)
-                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oximonth,lastoxi)/3600.0,0,'f',decimals)
-                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oxi6month,lastoxi)/3600.0,0,'f',decimals)
-                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oxiyear,lastoxi)/3600.0,0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Average Pulse Rate"))
-                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Minimum Pulse Rate"))
-                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Maximum Pulse Rate"))
-                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER),0,'f',decimals)
-                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
-                .arg(tr("Pulse Change Events / Hour"))
-                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER)/p_profile->calcHours(MT_OXIMETER),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oxiweek,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oximonth,lastoxi)/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oxi6month,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
-                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oxiyear,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
-        }
-    }
-
-    html+="</table>";
-    html+="</div>";
-
-    QList<UsageData> AHI;
-
-    //QDate bestAHIdate, worstAHIdate;
-    //EventDataType bestAHI=999.0, worstAHI=0;
-    if (cpapdays>0) {
-        QDate first,last=lastcpap;
-        CPAPMode mode=MODE_UNKNOWN,cmode=MODE_UNKNOWN;
-        EventDataType cmin=0,cmax=0,cmaxhi=0, min=0,max=0,maxhi=0;
-        Machine *mach=NULL,*lastmach=NULL;
-        PRTypes lastpr=PR_UNKNOWN, prelief=PR_UNKNOWN;
-        short prelset=0, lastprelset=-1;
-        QDate date=lastcpap;
-        Day * day;
-        bool lastchanged=false;
-        QVector<RXChange> rxchange;
-        EventDataType hours;
-
-        int compliant=0;
-        do {
-            day=PROFILE.GetGoodDay(date,MT_CPAP);
-
-            if (day) {
-                lastchanged=false;
-
-                hours=day->hours();
-
-                if (hours > PROFILE.cpap->complianceHours())
-                    compliant++;
-
-                EventDataType ahi=day->count(CPAP_Obstructive)+day->count(CPAP_Hypopnea)+day->count(CPAP_Apnea)+day->count(CPAP_ClearAirway);
-                if (PROFILE.general->calculateRDI()) ahi+=day->count(CPAP_RERA);
-                ahi/=hours;
-                AHI.push_back(UsageData(date,ahi,hours));
-
-                prelief=(PRTypes)(int)round(day->settings_wavg(CPAP_PresReliefType));
-                prelset=round(day->settings_wavg(CPAP_PresReliefSet));
-                mode=(CPAPMode)(int)round(day->settings_wavg(CPAP_Mode));
-                mach=day->machine;
-                if (mode>=MODE_ASV) {
-                    min=day->settings_min(CPAP_EPAP);
-                    max=day->settings_max(CPAP_IPAPLo);
-                    maxhi=day->settings_max(CPAP_IPAPHi);
-                } else if (mode>=MODE_BIPAP) {
-                    min=day->settings_min(CPAP_EPAP);
-                    max=day->settings_max(CPAP_IPAP);
-                } else if (mode>=MODE_APAP) {
-                    min=day->settings_min(CPAP_PressureMin);
-                    max=day->settings_max(CPAP_PressureMax);
-                } else {
-                    min=day->settings_min(CPAP_Pressure);
-                }
-                if ((mode!=cmode) || (min!=cmin) || (max!=cmax) || (mach!=lastmach) || (prelset!=lastprelset))  {
-                    if ((cmode!=MODE_UNKNOWN) && (lastmach!=NULL)) {
-                        first=date.addDays(1);
-                        int days=PROFILE.countDays(MT_CPAP,first,last);
-                        RXChange rx;
-                        rx.first=first;
-                        rx.last=last;
-                        rx.days=days;
-                        rx.ahi=calcAHI(first,last);
-                        rx.fl=calcFL(first,last);
-                        rx.mode=cmode;
-                        rx.min=cmin;
-                        rx.max=cmax;
-                        rx.maxhi=cmaxhi;
-                        rx.prelief=lastpr;
-                        rx.prelset=lastprelset;
-                        rx.machine=lastmach;
-
-                        if (mode<MODE_BIPAP) {
-                            rx.per1=p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,first,last);
-                            rx.per2=0;
-                        } else if (mode<MODE_ASV) {
-                            rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
-                            rx.per2=p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,first,last);
-                        } else {
-                            rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
-                            rx.per2=p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,first,last);
-                        }
-                        rx.weighted=float(rx.days)/float(cpapdays)*rx.ahi;
-                        rxchange.push_back(rx);
-                    }
-                    cmode=mode;
-                    cmin=min;
-                    cmax=max;
-                    cmaxhi=maxhi;
-                    lastpr=prelief;
-                    lastprelset=prelset;
-                    last=date;
-                    lastmach=mach;
-                    lastchanged=true;
-                }
-
-            }
-            date=date.addDays(-1);
-        } while (date>=firstcpap);
-
-        // Sort list by AHI
-        qSort(AHI);
-
-        lastchanged=false;
-        if (!lastchanged && (mach!=NULL)) {
-           // last=date.addDays(1);
-            first=firstcpap;
-            int days=PROFILE.countDays(MT_CPAP,first,last);
-            RXChange rx;
-            rx.first=first;
-            rx.last=last;
-            rx.days=days;
-            rx.ahi=calcAHI(first,last);
-            rx.fl=calcFL(first,last);
-            rx.mode=mode;
-            rx.min=min;
-            rx.max=max;
-            rx.maxhi=maxhi;
-            rx.prelief=prelief;
-            rx.prelset=prelset;
-            rx.machine=mach;
-            if (mode<MODE_BIPAP) {
-                rx.per1=p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,first,last);
-                rx.per2=0;
-            } else if (mode<MODE_ASV) {
-                rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
-                rx.per2=p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,first,last);
-            } else {
-                rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
-                rx.per2=p_profile->calcPercentile(CPAP_IPAPHi,percentile,MT_CPAP,first,last);
-            }
-            rx.weighted=float(rx.days)/float(cpapdays);
-            //rx.weighted=float(days)*rx.ahi;
-            rxchange.push_back(rx);
-        }
-
-        int rxthresh=5;
-        QVector<RXChange *> tmpRX;
-        for (int i=0;i<rxchange.size();i++) {
-            RXChange & rx=rxchange[i];
-            if (rx.days>rxthresh)
-                tmpRX.push_back(&rx);
-        }
-
-        QString recbox="<html><head><style type='text/css'>"
-            "p,a,td,body { font-family: '"+QApplication::font().family()+"'; }"
-            "p,a,td,body { font-size: "+QString::number(QApplication::font().pointSize() + 2)+"px; }"
-            "a:link,a:visited { color: inherit; text-decoration: none; }" //font-weight: normal;
-            "a:hover { background-color: inherit; color: white; text-decoration:none; font-weight: bold; }"
-            "</style></head><body>";
-        recbox+="<table width=100% cellpadding=1 cellspacing=0>";
-        int numdays=AHI.size();
-        if (numdays>1) {
-            int z=numdays/2;
-            if (z>4) z=4;
-
-            recbox+=QString("<tr><td colspan=2 align=center><b>%1</b></td></tr>").arg(tr("Usage Information"));
-            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(tr("Total Days")).arg(numdays);
-            if (PROFILE.cpap->showComplianceInfo()) {
-                recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(tr("Compliant Days")).arg(compliant);
-            }
-            int highahi=0;
-            for (int i=0;i<numdays;i++) {
-                if (AHI.at(i).ahi > 5.0)
-                    highahi++;
-            }
-            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(tr("Days AHI &gt;5.0")).arg(highahi);
-
-
-            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
-            recbox+=QString("<tr><td colspan=2 align=center><b>%1</b></td></tr>").arg(tr("Best&nbsp;%1").arg(ahitxt));
-            for (int i=0;i<z;i++) {
-                const UsageData & a=AHI.at(i);
-                recbox+=QString("<tr><td><a href='daily=%1'>%2</a></td><td  align=right>%3</td></tr>")
-                    .arg(a.date.toString(Qt::ISODate))
-                    .arg(a.date.toString(Qt::SystemLocaleShortDate))
-                    .arg(a.ahi,0,'f',decimals);
-            }
-            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
-            recbox+=QString("<tr><td colspan=2 align=center><b>%1</b></td></tr>").arg(tr("Worst&nbsp;%1").arg(ahitxt));
-            for (int i=0;i<z;i++) {
-                const UsageData & a=AHI.at((numdays-1)-i);
-                recbox+=QString("<tr><td><a href='daily=%1'>%2</a></td><td align=right>%3</td></tr>")
-                    .arg(a.date.toString(Qt::ISODate))
-                    .arg(a.date.toString(Qt::SystemLocaleShortDate))
-                    .arg(a.ahi,0,'f',decimals);
-            }
-            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
-        }
-
-
-        if (tmpRX.size()>0) {
-            RXsort=RX_ahi;
-            QString minstr,maxstr,modestr,maxhistr;
-            qSort(tmpRX.begin(),tmpRX.end(),RXSort);
-            tmpRX[0]->highlight=4; // worst
-            int ls=tmpRX.size()-1;
-            tmpRX[ls]->highlight=1; //best
-            CPAPMode mode=(CPAPMode)(int)PROFILE.calcSettingsMax(CPAP_Mode,MT_CPAP,tmpRX[ls]->first,tmpRX[ls]->last);
-
-            if (mode<MODE_APAP) { // is CPAP?
-                minstr=STR_TR_Pressure;
-                maxstr="";
-                modestr=STR_TR_CPAP;
-            } else if (mode<MODE_BIPAP) { // is AUTO?
-                minstr=STR_TR_Min;
-                maxstr=STR_TR_Max;
-                modestr=STR_TR_APAP;
-            } else if (mode<MODE_ASV) { // BIPAP
-                minstr=STR_TR_EPAP;
-                maxstr=STR_TR_IPAP;
-                modestr=STR_TR_BiLevel;
-            } else {
-                minstr=STR_TR_EPAP;
-                maxstr=STR_TR_IPAPLo;
-                maxhistr=STR_TR_IPAPHi;
-                modestr=STR_TR_STASV;
-
-            }
-
-            recbox+=QString("<tr><td colspan=2><table width=100% border=0 cellpadding=1 cellspacing=0><tr><td colspan=2 align=center><b>%3</b></td></tr>")
-                    .arg(tr("Best RX Setting"));
-            recbox+=QString("<tr><td valign=top>")+STR_TR_Start+"<br/>"+STR_TR_End+QString("</td><td align=right><a href='overview=%1,%2'>%3<br/>%4</a></td></tr>")
-                    .arg(tmpRX[ls]->first.toString(Qt::ISODate))
-                    .arg(tmpRX[ls]->last.toString(Qt::ISODate))
-                    .arg(tmpRX[ls]->first.toString(Qt::SystemLocaleShortDate))
-                    .arg(tmpRX[ls]->last.toString(Qt::SystemLocaleShortDate));
-            recbox+=QString("<tr><td><b>%1</b></td><td align=right><b>%2</b></td></tr>").arg(ahitxt).arg(tmpRX[ls]->ahi,0,'f',decimals);
-            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(STR_TR_Mode).arg(modestr);
-            recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(minstr).arg(tmpRX[ls]->min,0,'f',1).arg(STR_UNIT_CMH2O);
-            if (!maxstr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxstr).arg(tmpRX[ls]->max,0,'f',1).arg(STR_UNIT_CMH2O);
-            if (!maxhistr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxhistr).arg(tmpRX[ls]->maxhi,0,'f',1).arg(STR_UNIT_CMH2O);
-            recbox+="</table></td></tr>";
-
-            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
-
-            mode=(CPAPMode)(int)PROFILE.calcSettingsMax(CPAP_Mode,MT_CPAP,tmpRX[0]->first,tmpRX[0]->last);
-            if (mode<MODE_APAP) { // is CPAP?
-                minstr=STR_TR_Pressure;
-                maxstr="";
-                modestr=STR_TR_CPAP;
-            } else if (mode<MODE_BIPAP) { // is AUTO?
-                minstr=STR_TR_Min;
-                maxstr=STR_TR_Max;
-                modestr=STR_TR_APAP;
-            } else if (mode<MODE_ASV) { // BIPAP or greater
-                minstr=STR_TR_EPAP;
-                maxstr=STR_TR_IPAP;
-                modestr=STR_TR_BiLevel;
-            } else {
-                minstr=STR_TR_EPAP;
-                maxstr=STR_TR_IPAPLo;
-                maxhistr=STR_TR_IPAPHi;
-                modestr=STR_TR_STASV;
-            }
-
-            recbox+=QString("<tr><td colspan=2><table width=100% border=0 cellpadding=1 cellspacing=0><tr><td colspan=2 align=center><b>%3</b></td></tr>")
-                    .arg(tr("Worst RX Setting"));
-            recbox+=QString("<tr><td valign=top>")+STR_TR_Start+"<br/>"+STR_TR_End+QString("</td><td align=right><a href='overview=%1,%2'>%3<br/>%4</a></td></tr>")
-                    .arg(tmpRX[0]->first.toString(Qt::ISODate))
-                    .arg(tmpRX[0]->last.toString(Qt::ISODate))
-                    .arg(tmpRX[0]->first.toString(Qt::SystemLocaleShortDate))
-                    .arg(tmpRX[0]->last.toString(Qt::SystemLocaleShortDate));
-            recbox+=QString("<tr><td><b>%1</b></td><td align=right><b>%2</b></td></tr>").arg(ahitxt).arg(tmpRX[0]->ahi,0,'f',decimals);
-            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(STR_TR_Mode).arg(modestr);
-            recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(minstr).arg(tmpRX[0]->min,0,'f',1).arg(STR_UNIT_CMH2O);
-            if (!maxstr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxstr).arg(tmpRX[0]->max,0,'f',1).arg(STR_UNIT_CMH2O);
-            if (!maxhistr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxhistr).arg(tmpRX[0]->maxhi,0,'f',1).arg(STR_UNIT_CMH2O);
-            recbox+="</table></td></tr>";
-
-        }
-        recbox+="</table>";
-        recbox+="</body></html>";
-        ui->recordsBox->setHtml(recbox);
-
-        /*RXsort=RX_min;
-        RXorder=true;
-        qSort(rxchange.begin(),rxchange.end());*/
-        html+="<div align=center>";
-        html+=QString("<br/><b>")+tr("Changes to Prescription Settings")+"</b>";
-        html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=90%>");
-        QString extratxt;
-
-        if (cpapmode>=MODE_ASV) {
-            extratxt=QString("<td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td>")
-                .arg(STR_TR_EPAP).arg(STR_TR_IPAPLo).arg(STR_TR_IPAPHi).arg(tr("PS Min")).arg(tr("PS Max"));
-        } else if (cpapmode>=MODE_BIPAP) {
-            extratxt=QString("<td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td>")
-                .arg(STR_TR_EPAP).arg(STR_TR_IPAP).arg(STR_TR_PS);
-        } else if (cpapmode>MODE_CPAP) {
-            extratxt=QString("<td><b>%1</b></td><td><b>%2</b></td>")
-                .arg(tr("Min Pres.")).arg(tr("Max Pres."));
-        } else {
-            extratxt=QString("<td><b>%1</b></td>")
-                .arg(STR_TR_Pressure);
-        }
-        QString tooltip;
-        html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</b></td><td><b>%7</td><td><b>%8</td>%9</tr>")
-                  .arg(STR_TR_First)
-                  .arg(STR_TR_Last)
-                  .arg(tr("Days"))
-                  .arg(ahitxt)
-		  .arg(tr("FL"))
-                  .arg(STR_TR_Machine)
-                  .arg(STR_TR_Mode)
-                  .arg(tr("Pr. Rel."))
-                  .arg(extratxt);
-
-        for (int i=0;i<rxchange.size();i++) {
-            RXChange rx=rxchange.at(i);
-            QString color;
-            if (rx.highlight==1) {
-                color="#c0ffc0";
-            } else if (rx.highlight==2) {
-                color="#e0ffe0";
-            } else if (rx.highlight==3) {
-                color="#ffe0e0";
-            } else if (rx.highlight==4) {
-                color="#ffc0c0";
-            } else color="";
-            QString machstr;
-            if (rx.machine->properties.contains(STR_PROP_Brand))
-                machstr+=rx.machine->properties[STR_PROP_Brand];
-            if (rx.machine->properties.contains(STR_PROP_Model)) {
-                 machstr+=" "+rx.machine->properties[STR_PROP_Model];
-            }
-            if (rx.machine->properties.contains(STR_PROP_Serial)) {
-                 machstr+=" ("+rx.machine->properties[STR_PROP_Serial]+")<br/>";
-            }
-
-            mode=rx.mode;
-            if(mode>=MODE_ASV) {
-                extratxt=QString("<td>%1</td><td>%2</td><td>%3</td><td>%4</td>")
-                        .arg(rx.max,0,'f',decimals).arg(rx.maxhi,0,'f',decimals).arg(rx.max-rx.min,0,'f',decimals).arg(rx.maxhi-rx.min,0,'f',decimals);
-
-                tooltip=QString("%1 %2% ").arg(machstr).arg(percentile*100.0)+STR_TR_EPAP+
-                        QString("=%1<br/>%2% ").arg(rx.per1,0,'f',decimals).arg(percentile*100.0)+
-                        STR_TR_IPAP+QString("=%1").arg(rx.per2,0,'f',decimals);
-            } else if (mode>=MODE_BIPAP) {
-                extratxt=QString("<td>%1</td><td>%2</td>")
-                       .arg(rx.max,0,'f',decimals).arg(rx.max-rx.min,0,'f',decimals);
-                tooltip=QString("%1 %2% ").arg(machstr).arg(percentile*100.0)+
-                        STR_TR_EPAP+
-                        QString("=%1<br/>%2% ").arg(rx.per1,0,'f',decimals)
-                        .arg(percentile*100.0)
-                        +STR_TR_IPAP+QString("=%1").arg(rx.per2,0,'f',decimals);
-            } else if (mode>MODE_CPAP) {
-                extratxt=QString("<td>%1</td>").arg(rx.max,0,'f',decimals);
-                tooltip=QString("%1 %2% ").arg(machstr).arg(percentile*100.0)+STR_TR_Pressure+
-                        QString("=%2").arg(rx.per1,0,'f',decimals);
-            } else {
-                if (cpapmode>MODE_CPAP) {
-                    extratxt="<td>&nbsp;</td>";
-                    tooltip=QString("%1").arg(machstr);
-                } else {
-                    extratxt="";
-                    tooltip="";
-                }
-            }
-            QString presrel;
-            if (rx.prelset>0) {
-                presrel=schema::channel[CPAP_PresReliefType].option(int(rx.prelief));
-                presrel+=QString(" x%1").arg(rx.prelset);
-            } else presrel=STR_TR_None;
-            QString tooltipshow,tooltiphide;
-            if (!tooltip.isEmpty()) {
-                tooltipshow=QString("tooltip.show(\"%1\");").arg(tooltip);
-                tooltiphide="tooltip.hide();";
-            }
-            html+=QString("<tr bgcolor='"+color+"' onmouseover='ChangeColor(this, \"#eeeeee\"); %13' onmouseout='ChangeColor(this, \""+color+"\"); %14' onclick='alert(\"overview=%1,%2\");'><td>%3</td><td>%4</td><td>%5</td><td>%6</td><td>%7</td><td>%8</td><td>%9</td><td>%10</td><td>%11</td>%12</tr>")
-                    .arg(rx.first.toString(Qt::ISODate))
-                    .arg(rx.last.toString(Qt::ISODate))
-                    .arg(rx.first.toString(Qt::SystemLocaleShortDate))
-                    .arg(rx.last.toString(Qt::SystemLocaleShortDate))
-                    .arg(rx.days)
-                    .arg(rx.ahi,0,'f',decimals)
-		    .arg(rx.fl,0,'f',decimals)
-                    .arg(rx.machine->GetClass())
-                    .arg(schema::channel[CPAP_Mode].option(int(rx.mode)-1))
-                    .arg(presrel)
-                    .arg(rx.min,0,'f',decimals)
-                    .arg(extratxt)
-                    .arg(tooltipshow)
-                    .arg(tooltiphide);
-        }
-        html+="</table>";
-        html+=QString("<i>")+tr("The above has a threshold which excludes day counts less than %1 from the best/worst highlighting").arg(rxthresh)+QString("</i><br/>");
-        html+="</div>";
-
-    }
-    if (mach.size()>0) {
-        html+="<div align=center>";
-
-        html+=QString("<br/><b>")+tr("Machine Information")+"</b>";
-        html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=90%>");
-        html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td></tr>")
-                      .arg(STR_TR_Brand)
-                      .arg(STR_TR_Model)
-                      .arg(STR_TR_Serial)
-                      .arg(tr("First Use"))
-                      .arg(tr("Last Use"));
-        Machine *m;
-        for (int i=0;i<mach.size();i++) {
-            m=mach.at(i);
-            if (m->GetType()==MT_JOURNAL) continue;
-            QString mn=m->properties[STR_PROP_ModelNumber];
-            //if (mn.isEmpty())
-            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td></tr>")
-                    .arg(m->properties[STR_PROP_Brand])
-                    .arg(m->properties[STR_PROP_Model]+" "+m->properties[STR_PROP_SubModel]+ (mn.isEmpty() ? "" : QString(" (")+mn+QString(")")))
-                    .arg(m->properties[STR_PROP_Serial])
-                    .arg(m->FirstDay().toString(Qt::SystemLocaleShortDate))
-                    .arg(m->LastDay().toString(Qt::SystemLocaleShortDate));
-        }
-        html+="</table>";
-        html+="</div>";
-    }
-    html+="<script type='text/javascript' language='javascript' src='qrc:/docs/script.js'></script>";
     updateFavourites();
-    html+=htmlFooter();
+
     //QWebFrame *frame=ui->summaryView->page()->currentFrame();
     //frame->addToJavaScriptWindowObject("mainwin",this);
     //ui->summaryView->setHtml(html);
@@ -1367,11 +486,8 @@ void MainWindow::updateFavourites()
         "p,a,td,body { font-size: "+QString::number(QApplication::font().pointSize() + 2)+"px; }"
         "a:link,a:visited { color: inherit; text-decoration: none; }" //font-weight: normal;
         "a:hover { background-color: inherit; color: white; text-decoration:none; font-weight: bold; }"
-        "</style></head><body>";
-    html+="<table width=100% cellpadding=2 cellspacing=0>";
-
-    //ui->favouritesList->blockSignals(true);
-    //ui->favouritesList->clear();
+        "</style></head><body>"
+        "<table width=100% cellpadding=2 cellspacing=0>";
 
     do {
         Day * journal=PROFILE.GetDay(date,MT_JOURNAL);
@@ -1410,7 +526,6 @@ void MainWindow::updateFavourites()
     } while (date>=PROFILE.FirstDay(MT_JOURNAL));
     html+="</table></body></html>";
     ui->bookmarkView->setHtml(html);
-    //ui->favouritesList->blockSignals(false);
 }
 
 void MainWindow::on_backButton_clicked()
@@ -1510,7 +625,7 @@ tr("<p>The author wishes to express thanks to James Marshall and Rich Freeman fo
 "<p><i>"+tr("The author and any associates of his accept NO responsibilty for damages, issues or non-issues resulting from the use or mis-use of this software<br/>Use this software entirely at your own risk.")+"</i></p>"
 "<hr><p><font color=\"blue\">"+tr("If you find this free software to be of use, please consider supporting the development efforts by making a paypal donation to the Author")+"</font></p>"
 "</font></td></tr></table></span>"
-).arg(major_version).arg(minor_version).arg(revision_number).arg(release_number).arg(__DATE__).arg(__TIME__).arg(gitrev).arg(ReleaseStatus).arg(GetAppRoot());
+).arg(major_version).arg(minor_version).arg(revision_number).arg(release_number).arg(__DATE__).arg(__TIME__).arg(gitrev).arg(ReleaseStatus).arg(QDir::toNativeSeparators(GetAppRoot()));
     //"</div></body>"
 
      QDialog aboutbox;
@@ -1546,13 +661,6 @@ tr("<p>The author wishes to express thanks to James Marshall and Rich Freeman fo
      }
 
      disconnect(&webview,SIGNAL(linkClicked(const QUrl &)),this,SLOT(aboutBoxLinkClicked(const QUrl &)));
-
-/*
-    QMessageBox msgbox(QMessageBox::Information,tr("About SleepyHead"),"",QMessageBox::Ok,this);
-    msgbox.setIconPixmap();
-    msgbox.setTextFormat(Qt::RichText);
-    msgbox.setText(msg);
-    msgbox.exec();*/
 }
 
 void MainWindow::on_actionDebug_toggled(bool checked)
@@ -1647,13 +755,17 @@ void MainWindow::DelayedScreenshot()
 //#else
     QPixmap pixmap = QPixmap::grabWindow(this->winId());
 //#endif
-    QString a=PREF.Get("{home}")+"/Screenshots";
+    QString a=PREF.Get("{home}/Screenshots");
     QDir dir(a);
     if (!dir.exists()){
         dir.mkdir(a);
     }
-    a+="/screenshot-"+QDateTime::currentDateTime().toString(Qt::ISODate)+".png";
-    pixmap.save(a);
+
+    a+="/screenshot-"+QDateTime::currentDateTime().toString(Qt::ISODate)+".jpg";
+    qDebug() << "Saving screenshot to" << a;
+    if (pixmap.save(a)) {
+        Notify("There was an error saving screenshot to file \""+QDir::toNativeSeparators(a)+"\"");
+    }
 }
 
 void MainWindow::on_actionView_O_ximetry_triggered()
@@ -1673,13 +785,14 @@ void MainWindow::on_actionPrint_Report_triggered()
     return;
  #endif
 #endif
+    Report report;
     if (ui->tabWidget->currentWidget()==overview) {
-        PrintReport(overview->graphView(),STR_TR_Overview);
+        Report::PrintReport(overview->graphView(),STR_TR_Overview);
     } else if (ui->tabWidget->currentWidget()==daily) {
-        PrintReport(daily->graphView(),STR_TR_Daily,daily->getDate());
+        Report::PrintReport(daily->graphView(),STR_TR_Daily,daily->getDate());
     } else if (ui->tabWidget->currentWidget()==oximetry) {
         if (oximetry)
-            PrintReport(oximetry->graphView(),STR_TR_Oximetry);
+            Report::PrintReport(oximetry->graphView(),STR_TR_Oximetry);
     } else {
         QPrinter printer;
 #ifdef Q_WS_X11
@@ -1762,512 +875,6 @@ void MainWindow::on_action_Frequently_Asked_Questions_triggered()
     ui->tabWidget->setCurrentWidget(ui->helpTab);
 }
 
-void MainWindow::PrintReport(gGraphView *gv,QString name, QDate date)
-{
-    if (!gv) return;
-    Session * journal=NULL;
-    //QDate d=QDate::currentDate();
-
-    int visgraphs=gv->visibleGraphs();
-    if (visgraphs==0) {
-        Notify(tr("There are no graphs visible to print"));
-        return;
-    }
-
-    QString username=PROFILE.Get(QString("_{")+QString(STR_UI_UserName)+"}_");
-
-    bool print_bookmarks=false;
-    if (name==STR_TR_Daily) {
-        QVariantList book_start;
-        journal=getDaily()->GetJournalSession(getDaily()->getDate());
-        if (journal && journal->settings.contains(Bookmark_Start)) {
-            book_start=journal->settings[Bookmark_Start].toList();
-            if (book_start.size()>0) {
-                if (QMessageBox::question(this,STR_TR_Bookmarks,tr("Would you like to show bookmarked areas in this report?"),QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes) {
-                    print_bookmarks=true;
-                }
-            }
-        }
-    }
-
-    QPrinter * printer;
-
-    bool aa_setting=PROFILE.appearance->antiAliasing();
-
-    bool force_antialiasing=aa_setting;
-
-    printer=new QPrinter(QPrinter::HighResolution);
-
-#ifdef Q_WS_X11
-    printer->setPrinterName("Print to File (PDF)");
-    printer->setOutputFormat(QPrinter::PdfFormat);
-    QString filename=PREF.Get("{home}/"+name+username+date.toString(Qt::ISODate)+".pdf");
-
-    printer->setOutputFileName(filename);
-#endif
-    printer->setPrintRange(QPrinter::AllPages);
-    printer->setOrientation(QPrinter::Portrait);
-    printer->setFullPage(false); // This has nothing to do with scaling
-    printer->setNumCopies(1);
-    printer->setPageMargins(10,10,10,10,QPrinter::Millimeter);
-    QPrintDialog dialog(printer);
-#ifdef Q_OS_MAC
-    // QTBUG-17913
-    QApplication::processEvents();
-#endif
-    if (dialog.exec() != QDialog::Accepted) {
-        delete printer;
-        return;
-    }
-
-    Notify(tr("This make take some time to complete..\nPlease don't touch anything until it's done."),tr("Printing %1 Report").arg(name),20000);
-    QPainter painter;
-    painter.begin(printer);
-
-    GLint gw;
-    gw=2048;  // Rough guess.. No GL_MAX_RENDERBUFFER_SIZE in mingw.. :(
-
-    //QSizeF pxres=printer->paperSize(QPrinter::DevicePixel);
-    QRect prect=printer->pageRect();
-    float ratio=float(prect.height())/float(prect.width());
-    float virt_width=gw;
-    float virt_height=virt_width*ratio;
-    painter.setWindow(0,0,virt_width, virt_height);
-    painter.setViewport(0,0,prect.width(),prect.height());
-    painter.setViewTransformEnabled(true);
-
-    QFont report_font=*defaultfont;
-    QFont medium_font=*mediumfont;
-    QFont title_font=*bigfont;
-    float normal_height=30; //fm2.ascent();
-    report_font.setPixelSize(normal_height);
-    medium_font.setPixelSize(40);
-    title_font.setPixelSize(90);
-    painter.setFont(report_font);
-
-    //QFontMetrics fm2(*defaultfont);
-
-    qDebug() << "Printer Resolution is" << virt_width << "x" << virt_height;
-
-    const int graphs_per_page=6;
-    float full_graph_height=(virt_height-(normal_height*graphs_per_page)) / float(graphs_per_page);
-
-    QString title=tr("%1 Report").arg(name);
-    painter.setFont(title_font);
-    int top=0;
-    QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),title,QTextOption(Qt::AlignHCenter | Qt::AlignTop));
-    painter.drawText(bounds,title,QTextOption(Qt::AlignHCenter | Qt::AlignTop));
-    top+=bounds.height()+normal_height/2.0;
-    painter.setFont(report_font);
-
-    int maxy=0;
-    if (!PROFILE.user->firstName().isEmpty()) {
-        QString userinfo=STR_TR_Name+QString(":\t %1, %2\n").arg(PROFILE.user->lastName()).arg(PROFILE.user->firstName());
-        userinfo+=STR_TR_DOB+QString(":\t%1\n").arg(PROFILE.user->DOB().toString(Qt::SystemLocaleShortDate));
-        if (!PROFILE.doctor->patientID().isEmpty()) userinfo+=STR_TR_PatientID+QString(":\t%1\n").arg(PROFILE.doctor->patientID());
-        userinfo+=STR_TR_Phone+QString(":\t%1\n").arg(PROFILE.user->phone());
-        userinfo+=STR_TR_Email+QString(":\t%1\n").arg(PROFILE.user->email());
-        if (!PROFILE.user->address().isEmpty()) userinfo+="\n"+STR_TR_Address+QString(":\n%1").arg(PROFILE.user->address());
-
-        QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),userinfo,QTextOption(Qt::AlignLeft | Qt::AlignTop));
-        painter.drawText(bounds,userinfo,QTextOption(Qt::AlignLeft | Qt::AlignTop));
-        if (bounds.height()>maxy) maxy=bounds.height();
-    }
-    Day *cpap=NULL, *oxi=NULL;
-
-    int graph_slots=0;
-    if (name==STR_TR_Daily) {
-        cpap=PROFILE.GetGoodDay(date,MT_CPAP);
-        oxi=PROFILE.GetGoodDay(date,MT_OXIMETER);
-        QString cpapinfo=date.toString(Qt::SystemLocaleLongDate)+"\n\n";
-        if (cpap) {
-            time_t f=cpap->first()/1000L;
-            time_t l=cpap->last()/1000L;
-            int tt=qint64(cpap->total_time())/1000L;
-            int h=tt/3600;
-            int m=(tt/60)%60;
-            int s=tt % 60;
-
-            cpapinfo+=STR_TR_MaskTime+tr(": %1 hours, %2 minutes, %3 seconds\n").arg(h).arg(m).arg(s);
-            cpapinfo+=STR_TR_BedTime+": "+QDateTime::fromTime_t(f).time().toString("HH:mm:ss")+" ";
-            cpapinfo+=STR_TR_WakeUp+": "+QDateTime::fromTime_t(l).time().toString("HH:mm:ss")+"\n\n";
-            QString submodel;
-            cpapinfo+=STR_TR_Machine+": ";
-            if (cpap->machine->properties.find(STR_PROP_SubModel)!=cpap->machine->properties.end())
-                submodel="\n"+cpap->machine->properties[STR_PROP_SubModel];
-            cpapinfo+=cpap->machine->properties[STR_PROP_Brand]+" "+cpap->machine->properties[STR_PROP_Model]+submodel;
-            CPAPMode mode=(CPAPMode)(int)cpap->settings_max(CPAP_Mode);
-            cpapinfo+="\n"+STR_TR_Mode+": ";
-
-            if (mode==MODE_CPAP) {
-                EventDataType min=round(cpap->settings_wavg(CPAP_Pressure)*2)/2.0;
-                cpapinfo+=STR_TR_CPAP+" "+QString::number(min)+STR_UNIT_CMH2O;
-            } else if (mode==MODE_APAP) {
-                EventDataType min=cpap->settings_min(CPAP_PressureMin);
-                EventDataType max=cpap->settings_max(CPAP_PressureMax);
-                cpapinfo+=STR_TR_APAP+" "+QString::number(min)+"-"+QString::number(max)+STR_UNIT_CMH2O;
-            } else if (mode==MODE_BIPAP) {
-                EventDataType epap=cpap->settings_min(CPAP_EPAP);
-                EventDataType ipap=cpap->settings_max(CPAP_IPAP);
-                EventDataType ps=cpap->settings_max(CPAP_PS);
-                cpapinfo+=STR_TR_BiLevel+QString("\n"+STR_TR_EPAP+": %1 "+STR_TR_IPAP+": %2 %3\n"+STR_TR_PS+": %4")
-                        .arg(epap,0,'f',1).arg(ipap,0,'f',1).arg(STR_UNIT_CMH2O).arg(ps,0,'f',1);
-            }
-            else if (mode==MODE_ASV) {
-                EventDataType epap=cpap->settings_min(CPAP_EPAP);
-                EventDataType low=cpap->settings_min(CPAP_IPAPLo);
-                EventDataType high=cpap->settings_max(CPAP_IPAPHi);
-                EventDataType psl=cpap->settings_min(CPAP_PSMin);
-                EventDataType psh=cpap->settings_max(CPAP_PSMax);
-                cpapinfo+=STR_TR_ASV+QString("\n"+STR_TR_EPAP+": %1 "+STR_TR_IPAP+": %2 - %3 %4\n"+STR_TR_PS+": %5 / %6")
-                        .arg(epap,0,'f',1)
-                        .arg(low,0,'f',1)
-                        .arg(high,0,'f',1)
-                        .arg(STR_UNIT_CMH2O)
-                        .arg(psl,0,'f',1)
-                        .arg(psh,0,'f',1);
-            }
-            else cpapinfo+=STR_TR_Unknown;
-
-            float ahi=(cpap->count(CPAP_Obstructive)+cpap->count(CPAP_Hypopnea)+cpap->count(CPAP_ClearAirway)+cpap->count(CPAP_Apnea));
-            if (PROFILE.general->calculateRDI()) ahi+=cpap->count(CPAP_RERA);
-            ahi/=cpap->hours();
-            float csr=(100.0/cpap->hours())*(cpap->sum(CPAP_CSR)/3600.0);
-            float uai=cpap->count(CPAP_Apnea)/cpap->hours();
-            float oai=cpap->count(CPAP_Obstructive)/cpap->hours();
-            float hi=(cpap->count(CPAP_ExP)+cpap->count(CPAP_Hypopnea))/cpap->hours();
-            float cai=cpap->count(CPAP_ClearAirway)/cpap->hours();
-            float rei=cpap->count(CPAP_RERA)/cpap->hours();
-            float vsi=cpap->count(CPAP_VSnore)/cpap->hours();
-            float fli=cpap->count(CPAP_FlowLimit)/cpap->hours();
-            float nri=cpap->count(CPAP_NRI)/cpap->hours();
-            float lki=cpap->count(CPAP_LeakFlag)/cpap->hours();
-            float exp=cpap->count(CPAP_ExP)/cpap->hours();
-
-            int piesize=(2048.0/8.0)*1.3;   // 1.5" in size
-            //float fscale=font_scale;
-            //if (!highres)
-//                fscale=1;
-
-            QString stats;
-            painter.setFont(medium_font);
-            if (PROFILE.general->calculateRDI())
-                stats=tr("RDI\t%1\n").arg(ahi,0,'f',2);
-            else
-                stats=tr("AHI\t%1\n").arg(ahi,0,'f',2);
-            QRectF bounds=painter.boundingRect(QRectF(0,0,virt_width,0),stats,QTextOption(Qt::AlignRight));
-            painter.drawText(bounds,stats,QTextOption(Qt::AlignRight));
-
-
-            getDaily()->eventBreakdownPie()->setShowTitle(false);
-            getDaily()->eventBreakdownPie()->setMargins(0,0,0,0);
-            QPixmap ebp;
-            if (ahi>0) {
-                ebp=getDaily()->eventBreakdownPie()->renderPixmap(piesize,piesize,true);
-            } else {
-                ebp=QPixmap(":/icons/smileyface.png");
-            }
-            if (!ebp.isNull()) {
-                painter.drawPixmap(virt_width-piesize,bounds.height(),piesize,piesize,ebp);
-            }
-            getDaily()->eventBreakdownPie()->setShowTitle(true);
-
-            cpapinfo+="\n\n";
-
-            painter.setFont(report_font);
-            //bounds=painter.boundingRect(QRectF((virt_width/2)-(virt_width/6),top,virt_width/2,0),cpapinfo,QTextOption(Qt::AlignLeft));
-            bounds=painter.boundingRect(QRectF(0,top,virt_width,0),cpapinfo,QTextOption(Qt::AlignHCenter));
-            painter.drawText(bounds,cpapinfo,QTextOption(Qt::AlignHCenter));
-
-            int ttop=bounds.height();
-
-            stats=tr("AI=%1 HI=%2 CAI=%3 ").arg(oai,0,'f',2).arg(hi,0,'f',2).arg(cai,0,'f',2);
-            if (cpap->machine->GetClass()==STR_MACH_PRS1) {
-                stats+=tr("REI=%1 VSI=%2 FLI=%3 PB/CSR=%4\%")
-                        .arg(rei,0,'f',2).arg(vsi,0,'f',2)
-                        .arg(fli,0,'f',2).arg(csr,0,'f',2);
-            } else if (cpap->machine->GetClass()==STR_MACH_ResMed) {
-                stats+=tr("UAI=%1 ").arg(uai,0,'f',2);
-            } else if (cpap->machine->GetClass()==STR_MACH_Intellipap) {
-                stats+=tr("NRI=%1 LKI=%2 EPI=%3").arg(nri,0,'f',2).arg(lki,0,'f',2).arg(exp,0,'f',2);
-            }
-            bounds=painter.boundingRect(QRectF(0,top+ttop,virt_width,0),stats,QTextOption(Qt::AlignHCenter));
-            painter.drawText(bounds,stats,QTextOption(Qt::AlignHCenter));
-            ttop+=bounds.height();
-
-            if (journal) {
-                stats="";
-                if (journal->settings.contains(Journal_Weight))
-                    stats+=STR_TR_Weight+QString(" %1 ").arg(weightString(journal->settings[Journal_Weight].toDouble()));
-                if (journal->settings.contains(Journal_BMI))
-                    stats+=STR_TR_BMI+QString(" %1 ").arg(journal->settings[Journal_BMI].toDouble(),0,'f',2);
-                if (journal->settings.contains(Journal_ZombieMeter))
-                    stats+=STR_TR_Zombie+QString(" %1/10 ").arg(journal->settings[Journal_ZombieMeter].toDouble(),0,'f',0);
-
-                if (!stats.isEmpty()) {
-                    bounds=painter.boundingRect(QRectF(0,top+ttop,virt_width,0),stats,QTextOption(Qt::AlignHCenter));
-
-                    painter.drawText(bounds,stats,QTextOption(Qt::AlignHCenter));
-                    ttop+=bounds.height();
-                }
-                ttop+=normal_height;
-                if (journal->settings.contains(Journal_Notes)) {
-                    QTextDocument doc;
-                    doc.setHtml(journal->settings[Journal_Notes].toString());
-                    stats=doc.toPlainText();
-                    //doc.drawContents(&painter); // doesn't work as intended..
-
-                    bounds=painter.boundingRect(QRectF(0,top+ttop,virt_width,0),stats,QTextOption(Qt::AlignHCenter));
-                    painter.drawText(bounds,stats,QTextOption(Qt::AlignHCenter));
-                    bounds.setLeft(virt_width/4);
-                    bounds.setRight(virt_width-(virt_width/4));
-
-                    QPen pen(Qt::black);
-                    pen.setWidth(4);
-                    painter.setPen(pen);
-                    painter.drawRect(bounds);
-                    ttop+=bounds.height()+normal_height;
-                }
-            }
-
-
-            if (ttop>maxy) maxy=ttop;
-        } else {
-            bounds=painter.boundingRect(QRectF(0,top+maxy,virt_width,0),cpapinfo,QTextOption(Qt::AlignCenter));
-            painter.drawText(bounds,cpapinfo,QTextOption(Qt::AlignCenter));
-            if (maxy+bounds.height()>maxy) maxy=maxy+bounds.height();
-        }
-    } else if (name==STR_TR_Overview) {
-        QDateTime first=QDateTime::fromTime_t((*gv)[0]->min_x/1000L);
-        QDateTime last=QDateTime::fromTime_t((*gv)[0]->max_x/1000L);
-        QString ovinfo=tr("Reporting from %1 to %2").arg(first.date().toString(Qt::SystemLocaleShortDate)).arg(last.date().toString(Qt::SystemLocaleShortDate));
-        QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),ovinfo,QTextOption(Qt::AlignHCenter));
-        painter.drawText(bounds,ovinfo,QTextOption(Qt::AlignHCenter));
-
-        if (bounds.height()>maxy) maxy=bounds.height();
-    } else if (name==STR_TR_Oximetry) {
-        QString ovinfo=tr("Reporting data goes here");
-        QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),ovinfo,QTextOption(Qt::AlignHCenter));
-        painter.drawText(bounds,ovinfo,QTextOption(Qt::AlignHCenter));
-
-        if (bounds.height()>maxy) maxy=bounds.height();
-    }
-    top+=maxy;
-
-    graph_slots=graphs_per_page-((virt_height-top)/(full_graph_height+normal_height));
-
-    bool first=true;
-    QStringList labels;
-    QVector<gGraph *> graphs;
-    QVector<qint64> start,end;
-    qint64 savest,saveet;
-    gv->GetXBounds(savest,saveet);
-    qint64 st=savest,et=saveet;
-
-    gGraph *g;
-    if (name==STR_TR_Daily) {
-        if (!print_bookmarks) {
-            for (int i=0;i<gv->size();i++) {
-                g=(*gv)[i];
-                if (g->isEmpty()) continue;
-                if (!g->visible()) continue;
-
-                if (cpap) {
-                    st=cpap->first();
-                    et=cpap->last();
-                } else if (oxi) {
-                    st=oxi->first();
-                    et=oxi->last();
-                }
-
-                if (g->title()==STR_TR_FlowRate) {
-                    if (!((qAbs(savest-st)<2000) && (qAbs(saveet-et)<2000))) {
-                        start.push_back(st);
-                        end.push_back(et);
-                        graphs.push_back(g);
-                        labels.push_back(tr("Entire Day's Flow Waveform"));
-                    }
-                    start.push_back(savest);
-                    end.push_back(saveet);
-                    graphs.push_back(g);
-                    labels.push_back("Current Selection");
-                } else {
-                    start.push_back(savest);
-                    end.push_back(saveet);
-                    graphs.push_back(g);
-                    labels.push_back("");
-                }
-
-            }
-        } else {
-            const QString EntireDay=tr("Entire Day");
-            if (journal) {
-                if (journal->settings.contains(Bookmark_Start)) {
-                    QVariantList st1=journal->settings[Bookmark_Start].toList();
-                    QVariantList et1=journal->settings[Bookmark_End].toList();
-                    QStringList notes=journal->settings[Bookmark_Notes].toStringList();
-                    gGraph *flow=gv->findGraph(STR_TR_FlowRate),
-                           *spo2=gv->findGraph(STR_TR_SpO2),
-                           *pulse=gv->findGraph(STR_TR_PulseRate);
-
-
-                    if (cpap && flow && !flow->isEmpty() && flow->visible()) {
-                        labels.push_back(EntireDay);
-                        start.push_back(cpap->first());
-                        end.push_back(cpap->last());
-                        graphs.push_back(flow);
-                    }
-                    if (oxi && spo2 && !spo2->isEmpty() && spo2->visible()) {
-                        labels.push_back(EntireDay);
-                        start.push_back(oxi->first());
-                        end.push_back(oxi->last());
-                        graphs.push_back(spo2);
-                    }
-                    if (oxi && pulse && !pulse->isEmpty() && pulse->visible()) {
-                        labels.push_back(EntireDay);
-                        start.push_back(oxi->first());
-                        end.push_back(oxi->last());
-                        graphs.push_back(pulse);
-                    }
-                    for (int i=0;i<notes.size();i++) {
-                        if (flow && !flow->isEmpty() && flow->visible()) {
-                            labels.push_back(notes.at(i));
-                            start.push_back(st1.at(i).toLongLong());
-                            end.push_back(et1.at(i).toLongLong());
-                            graphs.push_back(flow);
-                        }
-                        if (spo2 && !spo2->isEmpty() && spo2->visible()) {
-                            labels.push_back(notes.at(i));
-                            start.push_back(st1.at(i).toLongLong());
-                            end.push_back(et1.at(i).toLongLong());
-                            graphs.push_back(spo2);
-                        }
-                        if (pulse && !pulse->isEmpty() && pulse->visible()) {
-                            labels.push_back(notes.at(i));
-                            start.push_back(st1.at(i).toLongLong());
-                            end.push_back(et1.at(i).toLongLong());
-                            graphs.push_back(pulse);
-                        }
-                    }
-                }
-            }
-            for (int i=0;i<gv->size();i++) {
-                gGraph *g=(*gv)[i];
-                if (g->isEmpty()) continue;
-                if (!g->visible()) continue;
-                if ((g->title()!=STR_TR_FlowRate ) && (g->title()!=STR_TR_SpO2) && (g->title()!=STR_TR_PulseRate)) {
-                    start.push_back(st);
-                    end.push_back(et);
-                    graphs.push_back(g);
-                    labels.push_back(tr(""));
-                }
-            }
-        }
-    } else {
-        for (int i=0;i<gv->size();i++) {
-            gGraph *g=(*gv)[i];
-            if (g->isEmpty()) continue;
-            if (!g->visible()) continue;
-
-            start.push_back(st);
-            end.push_back(et);
-            graphs.push_back(g);
-            labels.push_back(""); // date range?
-        }
-    }
-    int pages=ceil(float(graphs.size()+graph_slots)/float(graphs_per_page));
-
-    if (qprogress) {
-        qprogress->setValue(0);
-        qprogress->setMaximum(graphs.size());
-        qprogress->show();
-    }
-
-    int page=1;
-    int gcnt=0;
-    for (int i=0;i<graphs.size();i++) {
-
-        if ((top+full_graph_height+normal_height) > virt_height) {
-            top=0;
-            gcnt=0;
-            first=true;
-            if (page > pages)
-                break;
-
-            if (!printer->newPage()) {
-                qWarning("failed in flushing page to disk, disk full?");
-                break;
-            }
-
-
-        }
-        if (first) {
-            QString footer=tr("SleepyHead v%1 - http://sleepyhead.sourceforge.net").arg(VersionString);
-
-            QRectF bounds=painter.boundingRect(QRectF(0,virt_height,virt_width,normal_height),footer,QTextOption(Qt::AlignHCenter));
-            painter.drawText(bounds,footer,QTextOption(Qt::AlignHCenter));
-
-            QString pagestr=tr("Page %1 of %2").arg(page).arg(pages);
-            QRectF pagebnds=painter.boundingRect(QRectF(0,virt_height,virt_width,normal_height),pagestr,QTextOption(Qt::AlignRight));
-            painter.drawText(pagebnds,pagestr,QTextOption(Qt::AlignRight));
-            first=false;
-            page++;
-        }
-
-        gGraph *g=graphs[i];
-        g->SetXBounds(start[i],end[i]);
-        g->deselect();
-
-        QString label=labels[i];
-        if (!label.isEmpty()) {
-            //label+=":";
-            top+=normal_height/3;
-            QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),label,QTextOption(Qt::AlignHCenter));
-            //QRectF pagebnds=QRectF(0,top,virt_width,normal_height);
-            painter.drawText(bounds,label,QTextOption(Qt::AlignHCenter));
-            top+=bounds.height();
-        } else top+=normal_height/2;
-
-        PROFILE.appearance->setAntiAliasing(force_antialiasing);
-        int tmb=g->m_marginbottom;
-        g->m_marginbottom=0;
-
-
-        //painter.beginNativePainting();
-        //g->showTitle(false);
-        int hhh=full_graph_height-normal_height;
-        QPixmap pm2=g->renderPixmap(virt_width,hhh,1);
-        QImage pm=pm2.toImage();//fscale);
-        pm2.detach();
-        //g->showTitle(true);
-        //painter.endNativePainting();
-        g->m_marginbottom=tmb;
-        PROFILE.appearance->setAntiAliasing(aa_setting);
-
-
-        if (!pm.isNull()) {
-            painter.drawImage(0,top,pm);;
-
-
-
-            //painter.drawImage(0,top,virt_width,full_graph_height-normal_height,pm);
-        }
-        top+=full_graph_height;
-
-        gcnt++;
-        if (qprogress) {
-            qprogress->setValue(i);
-            QApplication::processEvents();
-        }
-    }
-
-    gv->SetXBounds(savest,saveet);
-    qprogress->hide();
-    painter.end();
-    delete printer;
-    Notify("SleepyHead has finished sending the job to the printer.");
-}
-
 void packEventList(EventList *el, EventDataType minval=0)
 {
     if (el->count()<2) return;
diff --git a/mainwindow.h b/mainwindow.h
index 8d6a4e64..8bb7e8a2 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -128,19 +128,15 @@ public:
 
     void JumpDaily();
 
-    /*! \fn void PrintReport gGraphView *gv,QString name, QDate date=QDate::currentDate());
-        \brief Prepares a report using gGraphView object, and sends to a created QPrinter object
-        \param gGraphView *gv  GraphView Object containing which graph set to print
-        \param QString name   Report Title
-        \param QDate date
-        */
-    void PrintReport(gGraphView *gv,QString name, QDate date=QDate::currentDate());
-
     void sendStatsUrl(QString msg) { on_recordsBox_linkClicked(QUrl(msg)); }
 
     //! \brief Sets up recalculation of all event summaries and flags
     void reprocessEvents(bool restart=false);
 
+
+    //! \brief Internal function to set Records Box html from summary module
+    void setRecBoxHTML(QString html);
+
 public slots:
     //! \brief Recalculate all event summaries and flags
     void doReprocessEvents();
diff --git a/newprofile.cpp b/newprofile.cpp
index d1a77872..a9c84c0d 100644
--- a/newprofile.cpp
+++ b/newprofile.cpp
@@ -99,9 +99,9 @@ void NewProfile::on_nextButton_clicked()
     switch(index) {
     case 0:
         settings.setValue("Settings/AppRoot", ui->dataFolderPath->text());
-        p_pref->setFilename(ui->dataFolderPath->text()+QDir::separator()+p_pref->name()+xmlext);
+        p_pref->setFilename(ui->dataFolderPath->text()+"/"+p_pref->name()+xmlext);
         p_pref->setPath(ui->dataFolderPath->text());
-        p_layout->setFilename(ui->dataFolderPath->text()+QDir::separator()+p_layout->name()+xmlext);
+        p_layout->setFilename(ui->dataFolderPath->text()+"/"+p_layout->name()+xmlext);
         p_layout->setPath(ui->dataFolderPath->text());
         // Reload Preferences object
         break;
diff --git a/oximetry.cpp b/oximetry.cpp
index bbdaa7e1..cdde20e6 100644
--- a/oximetry.cpp
+++ b/oximetry.cpp
@@ -510,6 +510,11 @@ void CMS50Serial::import_process()
         a=data.at(i++); // low bits are supposedly the high bits of the heart rate? not here
         pl=((data.at(i++) & 0x7f) | ((a & 1) << 7)) & 0xff;
         o2=data.at(i++) & 0x7f;
+
+        // Faulty data..?
+        if (o2 < 50)
+            o2=0;
+
         if (pl!=0) {
             if (lastpl!=pl) {
                 if (lastpl==0 || !pulse) {
diff --git a/reports.cpp b/reports.cpp
new file mode 100644
index 00000000..c80c271f
--- /dev/null
+++ b/reports.cpp
@@ -0,0 +1,532 @@
+/*
+ Reports / Printing Module
+ Copyright (c)2013 Mark Watkins <jedimark@users.sourceforge.net>
+ License: GPL3
+*/
+
+#include <QMessageBox>
+#include <QPrinter>
+#include <QPrintDialog>
+#include <QTextDocument>
+#include <QProgressBar>
+#include <QApplication>
+#include <cmath>
+
+#include "reports.h"
+#include "mainwindow.h"
+#include "common_gui.h"
+
+extern QProgressBar *qprogress;
+extern MainWindow * mainwin;
+
+
+Report::Report()
+{
+}
+
+void Report::PrintReport(gGraphView *gv,QString name, QDate date)
+{
+    if (!gv) return;
+    Session * journal=NULL;
+    //QDate d=QDate::currentDate();
+
+    int visgraphs=gv->visibleGraphs();
+    if (visgraphs==0) {
+        mainwin->Notify(QObject::tr("There are no graphs visible to print"));
+        return;
+    }
+
+    QString username=PROFILE.Get(QString("_{")+QString(STR_UI_UserName)+"}_");
+
+    bool print_bookmarks=false;
+    if (name==STR_TR_Daily) {
+        QVariantList book_start;
+        journal=mainwin->getDaily()->GetJournalSession(mainwin->getDaily()->getDate());
+        if (journal && journal->settings.contains(Bookmark_Start)) {
+            book_start=journal->settings[Bookmark_Start].toList();
+            if (book_start.size()>0) {
+                if (QMessageBox::question(mainwin,STR_TR_Bookmarks,QObject::tr("Would you like to show bookmarked areas in this report?"),QMessageBox::Yes,QMessageBox::No)==QMessageBox::Yes) {
+                    print_bookmarks=true;
+                }
+            }
+        }
+    }
+
+    QPrinter * printer;
+
+    bool aa_setting=PROFILE.appearance->antiAliasing();
+
+    bool force_antialiasing=aa_setting;
+
+    printer=new QPrinter(QPrinter::HighResolution);
+
+#ifdef Q_WS_X11
+    printer->setPrinterName("Print to File (PDF)");
+    printer->setOutputFormat(QPrinter::PdfFormat);
+    QString filename=PREF.Get("{home}/"+name+username+date.toString(Qt::ISODate)+".pdf");
+
+    printer->setOutputFileName(filename);
+#endif
+    printer->setPrintRange(QPrinter::AllPages);
+    printer->setOrientation(QPrinter::Portrait);
+    printer->setFullPage(false); // This has nothing to do with scaling
+    printer->setNumCopies(1);
+    printer->setPageMargins(10,10,10,10,QPrinter::Millimeter);
+    QPrintDialog dialog(printer);
+#ifdef Q_OS_MAC
+    // QTBUG-17913
+    QApplication::processEvents();
+#endif
+    if (dialog.exec() != QDialog::Accepted) {
+        delete printer;
+        return;
+    }
+
+    mainwin->Notify(QObject::tr("This make take some time to complete..\nPlease don't touch anything until it's done."),QObject::tr("Printing %1 Report").arg(name),20000);
+    QPainter painter;
+    painter.begin(printer);
+
+    GLint gw;
+    gw=2048;  // Rough guess.. No GL_MAX_RENDERBUFFER_SIZE in mingw.. :(
+
+    //QSizeF pxres=printer->paperSize(QPrinter::DevicePixel);
+    QRect prect=printer->pageRect();
+    float ratio=float(prect.height())/float(prect.width());
+    float virt_width=gw;
+    float virt_height=virt_width*ratio;
+    painter.setWindow(0,0,virt_width, virt_height);
+    painter.setViewport(0,0,prect.width(),prect.height());
+    painter.setViewTransformEnabled(true);
+
+    QFont report_font=*defaultfont;
+    QFont medium_font=*mediumfont;
+    QFont title_font=*bigfont;
+    float normal_height=30; //fm2.ascent();
+    report_font.setPixelSize(normal_height);
+    medium_font.setPixelSize(40);
+    title_font.setPixelSize(90);
+    painter.setFont(report_font);
+
+    //QFontMetrics fm2(*defaultfont);
+
+    qDebug() << "Printer Resolution is" << virt_width << "x" << virt_height;
+
+    const int graphs_per_page=6;
+    float full_graph_height=(virt_height-(normal_height*graphs_per_page)) / float(graphs_per_page);
+
+    QString title=QObject::tr("%1 Report").arg(name);
+    painter.setFont(title_font);
+    int top=0;
+    QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),title,QTextOption(Qt::AlignHCenter | Qt::AlignTop));
+    painter.drawText(bounds,title,QTextOption(Qt::AlignHCenter | Qt::AlignTop));
+    top+=bounds.height()+normal_height/2.0;
+    painter.setFont(report_font);
+
+    int maxy=0;
+    if (!PROFILE.user->firstName().isEmpty()) {
+        QString userinfo=STR_TR_Name+QString(":\t %1, %2\n").arg(PROFILE.user->lastName()).arg(PROFILE.user->firstName());
+        userinfo+=STR_TR_DOB+QString(":\t%1\n").arg(PROFILE.user->DOB().toString(Qt::SystemLocaleShortDate));
+        if (!PROFILE.doctor->patientID().isEmpty()) userinfo+=STR_TR_PatientID+QString(":\t%1\n").arg(PROFILE.doctor->patientID());
+        userinfo+=STR_TR_Phone+QString(":\t%1\n").arg(PROFILE.user->phone());
+        userinfo+=STR_TR_Email+QString(":\t%1\n").arg(PROFILE.user->email());
+        if (!PROFILE.user->address().isEmpty()) userinfo+="\n"+STR_TR_Address+QString(":\n%1").arg(PROFILE.user->address());
+
+        QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),userinfo,QTextOption(Qt::AlignLeft | Qt::AlignTop));
+        painter.drawText(bounds,userinfo,QTextOption(Qt::AlignLeft | Qt::AlignTop));
+        if (bounds.height()>maxy) maxy=bounds.height();
+    }
+    Day *cpap=NULL, *oxi=NULL;
+
+    int graph_slots=0;
+    if (name==STR_TR_Daily) {
+        cpap=PROFILE.GetGoodDay(date,MT_CPAP);
+        oxi=PROFILE.GetGoodDay(date,MT_OXIMETER);
+        QString cpapinfo=date.toString(Qt::SystemLocaleLongDate)+"\n\n";
+        if (cpap) {
+            time_t f=cpap->first()/1000L;
+            time_t l=cpap->last()/1000L;
+            int tt=qint64(cpap->total_time())/1000L;
+            int h=tt/3600;
+            int m=(tt/60)%60;
+            int s=tt % 60;
+
+            cpapinfo+=STR_TR_MaskTime+QObject::tr(": %1 hours, %2 minutes, %3 seconds\n").arg(h).arg(m).arg(s);
+            cpapinfo+=STR_TR_BedTime+": "+QDateTime::fromTime_t(f).time().toString("HH:mm:ss")+" ";
+            cpapinfo+=STR_TR_WakeUp+": "+QDateTime::fromTime_t(l).time().toString("HH:mm:ss")+"\n\n";
+            QString submodel;
+            cpapinfo+=STR_TR_Machine+": ";
+            if (cpap->machine->properties.find(STR_PROP_SubModel)!=cpap->machine->properties.end())
+                submodel="\n"+cpap->machine->properties[STR_PROP_SubModel];
+            cpapinfo+=cpap->machine->properties[STR_PROP_Brand]+" "+cpap->machine->properties[STR_PROP_Model]+submodel;
+            CPAPMode mode=(CPAPMode)(int)cpap->settings_max(CPAP_Mode);
+            cpapinfo+="\n"+STR_TR_Mode+": ";
+
+            if (mode==MODE_CPAP) {
+                EventDataType min=round(cpap->settings_wavg(CPAP_Pressure)*2)/2.0;
+                cpapinfo+=STR_TR_CPAP+" "+QString::number(min)+STR_UNIT_CMH2O;
+            } else if (mode==MODE_APAP) {
+                EventDataType min=cpap->settings_min(CPAP_PressureMin);
+                EventDataType max=cpap->settings_max(CPAP_PressureMax);
+                cpapinfo+=STR_TR_APAP+" "+QString::number(min)+"-"+QString::number(max)+STR_UNIT_CMH2O;
+            } else if (mode==MODE_BIPAP) {
+                EventDataType epap=cpap->settings_min(CPAP_EPAP);
+                EventDataType ipap=cpap->settings_max(CPAP_IPAP);
+                EventDataType ps=cpap->settings_max(CPAP_PS);
+                cpapinfo+=STR_TR_BiLevel+QString("\n"+STR_TR_EPAP+": %1 "+STR_TR_IPAP+": %2 %3\n"+STR_TR_PS+": %4")
+                        .arg(epap,0,'f',1).arg(ipap,0,'f',1).arg(STR_UNIT_CMH2O).arg(ps,0,'f',1);
+            }
+            else if (mode==MODE_ASV) {
+                EventDataType epap=cpap->settings_min(CPAP_EPAP);
+                EventDataType low=cpap->settings_min(CPAP_IPAPLo);
+                EventDataType high=cpap->settings_max(CPAP_IPAPHi);
+                EventDataType psl=cpap->settings_min(CPAP_PSMin);
+                EventDataType psh=cpap->settings_max(CPAP_PSMax);
+                cpapinfo+=STR_TR_ASV+QString("\n"+STR_TR_EPAP+": %1 "+STR_TR_IPAP+": %2 - %3 %4\n"+STR_TR_PS+": %5 / %6")
+                        .arg(epap,0,'f',1)
+                        .arg(low,0,'f',1)
+                        .arg(high,0,'f',1)
+                        .arg(STR_UNIT_CMH2O)
+                        .arg(psl,0,'f',1)
+                        .arg(psh,0,'f',1);
+            }
+            else cpapinfo+=STR_TR_Unknown;
+
+            float ahi=(cpap->count(CPAP_Obstructive)+cpap->count(CPAP_Hypopnea)+cpap->count(CPAP_ClearAirway)+cpap->count(CPAP_Apnea));
+            if (PROFILE.general->calculateRDI()) ahi+=cpap->count(CPAP_RERA);
+            ahi/=cpap->hours();
+            float csr=(100.0/cpap->hours())*(cpap->sum(CPAP_CSR)/3600.0);
+            float uai=cpap->count(CPAP_Apnea)/cpap->hours();
+            float oai=cpap->count(CPAP_Obstructive)/cpap->hours();
+            float hi=(cpap->count(CPAP_ExP)+cpap->count(CPAP_Hypopnea))/cpap->hours();
+            float cai=cpap->count(CPAP_ClearAirway)/cpap->hours();
+            float rei=cpap->count(CPAP_RERA)/cpap->hours();
+            float vsi=cpap->count(CPAP_VSnore)/cpap->hours();
+            float fli=cpap->count(CPAP_FlowLimit)/cpap->hours();
+            float nri=cpap->count(CPAP_NRI)/cpap->hours();
+            float lki=cpap->count(CPAP_LeakFlag)/cpap->hours();
+            float exp=cpap->count(CPAP_ExP)/cpap->hours();
+
+            int piesize=(2048.0/8.0)*1.3;   // 1.5" in size
+            //float fscale=font_scale;
+            //if (!highres)
+//                fscale=1;
+
+            QString stats;
+            painter.setFont(medium_font);
+            if (PROFILE.general->calculateRDI())
+                stats=QObject::tr("RDI\t%1\n").arg(ahi,0,'f',2);
+            else
+                stats=QObject::tr("AHI\t%1\n").arg(ahi,0,'f',2);
+            QRectF bounds=painter.boundingRect(QRectF(0,0,virt_width,0),stats,QTextOption(Qt::AlignRight));
+            painter.drawText(bounds,stats,QTextOption(Qt::AlignRight));
+
+
+            mainwin->getDaily()->eventBreakdownPie()->setShowTitle(false);
+            mainwin->getDaily()->eventBreakdownPie()->setMargins(0,0,0,0);
+            QPixmap ebp;
+            if (ahi>0) {
+                ebp=mainwin->getDaily()->eventBreakdownPie()->renderPixmap(piesize,piesize,true);
+            } else {
+                ebp=QPixmap(":/icons/smileyface.png");
+            }
+            if (!ebp.isNull()) {
+                painter.drawPixmap(virt_width-piesize,bounds.height(),piesize,piesize,ebp);
+            }
+            mainwin->getDaily()->eventBreakdownPie()->setShowTitle(true);
+
+            cpapinfo+="\n\n";
+
+            painter.setFont(report_font);
+            //bounds=painter.boundingRect(QRectF((virt_width/2)-(virt_width/6),top,virt_width/2,0),cpapinfo,QTextOption(Qt::AlignLeft));
+            bounds=painter.boundingRect(QRectF(0,top,virt_width,0),cpapinfo,QTextOption(Qt::AlignHCenter));
+            painter.drawText(bounds,cpapinfo,QTextOption(Qt::AlignHCenter));
+
+            int ttop=bounds.height();
+
+            stats=QObject::tr("AI=%1 HI=%2 CAI=%3 ").arg(oai,0,'f',2).arg(hi,0,'f',2).arg(cai,0,'f',2);
+            if (cpap->machine->GetClass()==STR_MACH_PRS1) {
+                stats+=QObject::tr("REI=%1 VSI=%2 FLI=%3 PB/CSR=%4\%")
+                        .arg(rei,0,'f',2).arg(vsi,0,'f',2)
+                        .arg(fli,0,'f',2).arg(csr,0,'f',2);
+            } else if (cpap->machine->GetClass()==STR_MACH_ResMed) {
+                stats+=QObject::tr("UAI=%1 ").arg(uai,0,'f',2);
+            } else if (cpap->machine->GetClass()==STR_MACH_Intellipap) {
+                stats+=QObject::tr("NRI=%1 LKI=%2 EPI=%3").arg(nri,0,'f',2).arg(lki,0,'f',2).arg(exp,0,'f',2);
+            }
+            bounds=painter.boundingRect(QRectF(0,top+ttop,virt_width,0),stats,QTextOption(Qt::AlignHCenter));
+            painter.drawText(bounds,stats,QTextOption(Qt::AlignHCenter));
+            ttop+=bounds.height();
+
+            if (journal) {
+                stats="";
+                if (journal->settings.contains(Journal_Weight))
+                    stats+=STR_TR_Weight+QString(" %1 ").arg(weightString(journal->settings[Journal_Weight].toDouble()));
+                if (journal->settings.contains(Journal_BMI))
+                    stats+=STR_TR_BMI+QString(" %1 ").arg(journal->settings[Journal_BMI].toDouble(),0,'f',2);
+                if (journal->settings.contains(Journal_ZombieMeter))
+                    stats+=STR_TR_Zombie+QString(" %1/10 ").arg(journal->settings[Journal_ZombieMeter].toDouble(),0,'f',0);
+
+                if (!stats.isEmpty()) {
+                    bounds=painter.boundingRect(QRectF(0,top+ttop,virt_width,0),stats,QTextOption(Qt::AlignHCenter));
+
+                    painter.drawText(bounds,stats,QTextOption(Qt::AlignHCenter));
+                    ttop+=bounds.height();
+                }
+                ttop+=normal_height;
+                if (journal->settings.contains(Journal_Notes)) {
+                    QTextDocument doc;
+                    doc.setHtml(journal->settings[Journal_Notes].toString());
+                    stats=doc.toPlainText();
+                    //doc.drawContents(&painter); // doesn't work as intended..
+
+                    bounds=painter.boundingRect(QRectF(0,top+ttop,virt_width,0),stats,QTextOption(Qt::AlignHCenter));
+                    painter.drawText(bounds,stats,QTextOption(Qt::AlignHCenter));
+                    bounds.setLeft(virt_width/4);
+                    bounds.setRight(virt_width-(virt_width/4));
+
+                    QPen pen(Qt::black);
+                    pen.setWidth(4);
+                    painter.setPen(pen);
+                    painter.drawRect(bounds);
+                    ttop+=bounds.height()+normal_height;
+                }
+            }
+
+
+            if (ttop>maxy) maxy=ttop;
+        } else {
+            bounds=painter.boundingRect(QRectF(0,top+maxy,virt_width,0),cpapinfo,QTextOption(Qt::AlignCenter));
+            painter.drawText(bounds,cpapinfo,QTextOption(Qt::AlignCenter));
+            if (maxy+bounds.height()>maxy) maxy=maxy+bounds.height();
+        }
+    } else if (name==STR_TR_Overview) {
+        QDateTime first=QDateTime::fromTime_t((*gv)[0]->min_x/1000L);
+        QDateTime last=QDateTime::fromTime_t((*gv)[0]->max_x/1000L);
+        QString ovinfo=QObject::tr("Reporting from %1 to %2").arg(first.date().toString(Qt::SystemLocaleShortDate)).arg(last.date().toString(Qt::SystemLocaleShortDate));
+        QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),ovinfo,QTextOption(Qt::AlignHCenter));
+        painter.drawText(bounds,ovinfo,QTextOption(Qt::AlignHCenter));
+
+        if (bounds.height()>maxy) maxy=bounds.height();
+    } else if (name==STR_TR_Oximetry) {
+        QString ovinfo=QObject::tr("Reporting data goes here");
+        QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),ovinfo,QTextOption(Qt::AlignHCenter));
+        painter.drawText(bounds,ovinfo,QTextOption(Qt::AlignHCenter));
+
+        if (bounds.height()>maxy) maxy=bounds.height();
+    }
+    top+=maxy;
+
+    graph_slots=graphs_per_page-((virt_height-top)/(full_graph_height+normal_height));
+
+    bool first=true;
+    QStringList labels;
+    QVector<gGraph *> graphs;
+    QVector<qint64> start,end;
+    qint64 savest,saveet;
+    gv->GetXBounds(savest,saveet);
+    qint64 st=savest,et=saveet;
+
+    gGraph *g;
+    if (name==STR_TR_Daily) {
+        if (!print_bookmarks) {
+            for (int i=0;i<gv->size();i++) {
+                g=(*gv)[i];
+                if (g->isEmpty()) continue;
+                if (!g->visible()) continue;
+
+                if (cpap) {
+                    st=cpap->first();
+                    et=cpap->last();
+                } else if (oxi) {
+                    st=oxi->first();
+                    et=oxi->last();
+                }
+
+                if (g->title()==STR_TR_FlowRate) {
+                    if (!((qAbs(savest-st)<2000) && (qAbs(saveet-et)<2000))) {
+                        start.push_back(st);
+                        end.push_back(et);
+                        graphs.push_back(g);
+                        labels.push_back(QObject::tr("Entire Day's Flow Waveform"));
+                    }
+                    start.push_back(savest);
+                    end.push_back(saveet);
+                    graphs.push_back(g);
+                    labels.push_back(QObject::tr("Current Selection"));
+                } else {
+                    start.push_back(savest);
+                    end.push_back(saveet);
+                    graphs.push_back(g);
+                    labels.push_back("");
+                }
+
+            }
+        } else {
+            const QString EntireDay=QObject::tr("Entire Day");
+            if (journal) {
+                if (journal->settings.contains(Bookmark_Start)) {
+                    QVariantList st1=journal->settings[Bookmark_Start].toList();
+                    QVariantList et1=journal->settings[Bookmark_End].toList();
+                    QStringList notes=journal->settings[Bookmark_Notes].toStringList();
+                    gGraph *flow=gv->findGraph(STR_TR_FlowRate),
+                           *spo2=gv->findGraph(STR_TR_SpO2),
+                           *pulse=gv->findGraph(STR_TR_PulseRate);
+
+
+                    if (cpap && flow && !flow->isEmpty() && flow->visible()) {
+                        labels.push_back(EntireDay);
+                        start.push_back(cpap->first());
+                        end.push_back(cpap->last());
+                        graphs.push_back(flow);
+                    }
+                    if (oxi && spo2 && !spo2->isEmpty() && spo2->visible()) {
+                        labels.push_back(EntireDay);
+                        start.push_back(oxi->first());
+                        end.push_back(oxi->last());
+                        graphs.push_back(spo2);
+                    }
+                    if (oxi && pulse && !pulse->isEmpty() && pulse->visible()) {
+                        labels.push_back(EntireDay);
+                        start.push_back(oxi->first());
+                        end.push_back(oxi->last());
+                        graphs.push_back(pulse);
+                    }
+                    for (int i=0;i<notes.size();i++) {
+                        if (flow && !flow->isEmpty() && flow->visible()) {
+                            labels.push_back(notes.at(i));
+                            start.push_back(st1.at(i).toLongLong());
+                            end.push_back(et1.at(i).toLongLong());
+                            graphs.push_back(flow);
+                        }
+                        if (spo2 && !spo2->isEmpty() && spo2->visible()) {
+                            labels.push_back(notes.at(i));
+                            start.push_back(st1.at(i).toLongLong());
+                            end.push_back(et1.at(i).toLongLong());
+                            graphs.push_back(spo2);
+                        }
+                        if (pulse && !pulse->isEmpty() && pulse->visible()) {
+                            labels.push_back(notes.at(i));
+                            start.push_back(st1.at(i).toLongLong());
+                            end.push_back(et1.at(i).toLongLong());
+                            graphs.push_back(pulse);
+                        }
+                    }
+                }
+            }
+            for (int i=0;i<gv->size();i++) {
+                gGraph *g=(*gv)[i];
+                if (g->isEmpty()) continue;
+                if (!g->visible()) continue;
+                if ((g->title()!=STR_TR_FlowRate ) && (g->title()!=STR_TR_SpO2) && (g->title()!=STR_TR_PulseRate)) {
+                    start.push_back(st);
+                    end.push_back(et);
+                    graphs.push_back(g);
+                    labels.push_back("");
+                }
+            }
+        }
+    } else {
+        for (int i=0;i<gv->size();i++) {
+            gGraph *g=(*gv)[i];
+            if (g->isEmpty()) continue;
+            if (!g->visible()) continue;
+
+            start.push_back(st);
+            end.push_back(et);
+            graphs.push_back(g);
+            labels.push_back(""); // date range?
+        }
+    }
+    int pages=ceil(float(graphs.size()+graph_slots)/float(graphs_per_page));
+
+    if (qprogress) {
+        qprogress->setValue(0);
+        qprogress->setMaximum(graphs.size());
+        qprogress->show();
+    }
+
+    int page=1;
+    int gcnt=0;
+    for (int i=0;i<graphs.size();i++) {
+
+        if ((top+full_graph_height+normal_height) > virt_height) {
+            top=0;
+            gcnt=0;
+            first=true;
+            if (page > pages)
+                break;
+
+            if (!printer->newPage()) {
+                qWarning("failed in flushing page to disk, disk full?");
+                break;
+            }
+
+
+        }
+        if (first) {
+            QString footer=QObject::tr("SleepyHead v%1 - http://sleepyhead.sourceforge.net").arg(VersionString);
+
+            QRectF bounds=painter.boundingRect(QRectF(0,virt_height,virt_width,normal_height),footer,QTextOption(Qt::AlignHCenter));
+            painter.drawText(bounds,footer,QTextOption(Qt::AlignHCenter));
+
+            QString pagestr=QObject::tr("Page %1 of %2").arg(page).arg(pages);
+            QRectF pagebnds=painter.boundingRect(QRectF(0,virt_height,virt_width,normal_height),pagestr,QTextOption(Qt::AlignRight));
+            painter.drawText(pagebnds,pagestr,QTextOption(Qt::AlignRight));
+            first=false;
+            page++;
+        }
+
+        gGraph *g=graphs[i];
+        g->SetXBounds(start[i],end[i]);
+        g->deselect();
+
+        QString label=labels[i];
+        if (!label.isEmpty()) {
+            //label+=":";
+            top+=normal_height/3;
+            QRectF bounds=painter.boundingRect(QRectF(0,top,virt_width,0),label,QTextOption(Qt::AlignHCenter));
+            //QRectF pagebnds=QRectF(0,top,virt_width,normal_height);
+            painter.drawText(bounds,label,QTextOption(Qt::AlignHCenter));
+            top+=bounds.height();
+        } else top+=normal_height/2;
+
+        PROFILE.appearance->setAntiAliasing(force_antialiasing);
+        int tmb=g->m_marginbottom;
+        g->m_marginbottom=0;
+
+
+        //painter.beginNativePainting();
+        //g->showTitle(false);
+        int hhh=full_graph_height-normal_height;
+        QPixmap pm2=g->renderPixmap(virt_width,hhh,1);
+        QImage pm=pm2.toImage();//fscale);
+        pm2.detach();
+        //g->showTitle(true);
+        //painter.endNativePainting();
+        g->m_marginbottom=tmb;
+        PROFILE.appearance->setAntiAliasing(aa_setting);
+
+
+        if (!pm.isNull()) {
+            painter.drawImage(0,top,pm);;
+
+
+
+            //painter.drawImage(0,top,virt_width,full_graph_height-normal_height,pm);
+        }
+        top+=full_graph_height;
+
+        gcnt++;
+        if (qprogress) {
+            qprogress->setValue(i);
+            QApplication::processEvents();
+        }
+    }
+
+    gv->SetXBounds(savest,saveet);
+    qprogress->hide();
+    painter.end();
+    delete printer;
+    mainwin->Notify(QObject::tr("SleepyHead has finished sending the job to the printer."));
+}
+
diff --git a/reports.h b/reports.h
new file mode 100644
index 00000000..cb38af89
--- /dev/null
+++ b/reports.h
@@ -0,0 +1,25 @@
+/*
+ Reports Header
+ Copyright (c)2013 Mark Watkins <jedimark@users.sourceforge.net>
+ License: GPL3
+*/
+
+#ifndef REPORTS_H
+#define REPORTS_H
+#include "Graphs/gGraphView.h"
+
+class Report
+{
+public:
+    Report();
+
+    /*! \fn void PrintReport gGraphView *gv,QString name, QDate date=QDate::currentDate());
+        \brief Prepares a report using gGraphView object, and sends to a created QPrinter object
+        \param gGraphView *gv  GraphView Object containing which graph set to print
+        \param QString name   Report Title
+        \param QDate date
+        */
+    static void PrintReport(gGraphView *gv,QString name, QDate date=QDate::currentDate());
+};
+
+#endif // REPORTS_H
diff --git a/summary.cpp b/summary.cpp
new file mode 100644
index 00000000..fec74e0a
--- /dev/null
+++ b/summary.cpp
@@ -0,0 +1,918 @@
+/*
+ Summary Module
+ Copyright (c)2013 Mark Watkins <jedimark@users.sourceforge.net>
+ License: GPL3
+*/
+
+#include <QApplication>
+#include <cmath>
+
+#include "mainwindow.h"
+#include "summary.h"
+#include "SleepLib/schema.h"
+
+
+extern MainWindow *mainwin;
+
+
+Summary::Summary(QObject *parent) :
+    QObject(parent)
+{
+}
+
+
+QString htmlHeader()
+{
+
+    //   "a:link,a:visited { color: '#000020'; text-decoration: none; font-weight: bold;}"
+//   "a:hover { background-color: inherit; color: red; text-decoration:none; font-weight: bold; }"
+    return QString("<html><head>"
+"</head>"
+"<style type='text/css'>"
+"<!--h1,p,a,td,body { font-family: 'FreeSans', 'Sans Serif' } --/>"
+"p,a,td,body { font-size: 14px }"
+"</style>"
+"<link rel='stylesheet' type='text/css' href='qrc:/docs/tooltips.css' />"
+"<script type='text/javascript'>"
+"function ChangeColor(tableRow, highLight)"
+"{ tableRow.style.backgroundColor = highLight; }"
+"function Go(url) { throw(url); }"
+"</script>"
+"</head>"
+"<body leftmargin=0 topmargin=0 rightmargin=0>"
+"<div align=center><table cellpadding=3 cellspacing=0 border=0>"
+"<tr><td><img src='qrc:/icons/bob-v3.0.png' width=140px height=140px><td valign=center align=center><h1>"+
+QObject::tr("SleepyHead")+" v"+VersionString+"</h1><i>"+
+QObject::tr("This is a beta software and some functionality may not work as intended yet.")+"<br/>"+
+QObject::tr("Please report any bugs you find to SleepyHead's SourceForge page.")+
+"</i></td></tr></table>"
+"</div>"
+"<hr/>");
+}
+QString htmlFooter()
+{
+    return QString("</body></html>");
+}
+
+QString formatTime(float time)
+{
+    int hours=time;
+    int seconds=time*3600.0;
+    int minutes=(seconds / 60) % 60;
+    seconds %= 60;
+    return QString().sprintf("%02i:%02i",hours,minutes); //,seconds);
+}
+
+
+EventDataType calcAHI(QDate start, QDate end)
+{
+    EventDataType val=(p_profile->calcCount(CPAP_Obstructive,MT_CPAP,start,end)
+              +p_profile->calcCount(CPAP_Hypopnea,MT_CPAP,start,end)
+              +p_profile->calcCount(CPAP_ClearAirway,MT_CPAP,start,end)
+              +p_profile->calcCount(CPAP_Apnea,MT_CPAP,start,end));
+    if (PROFILE.general->calculateRDI())
+        val+=p_profile->calcCount(CPAP_RERA,MT_CPAP,start,end);
+    EventDataType hours=p_profile->calcHours(MT_CPAP,start,end);
+
+    if (hours>0)
+        val/=hours;
+    else
+        val=0;
+
+    return val;
+}
+
+EventDataType calcFL(QDate start, QDate end)
+{
+    EventDataType val=(p_profile->calcCount(CPAP_FlowLimit,MT_CPAP,start,end));
+    EventDataType hours=p_profile->calcHours(MT_CPAP,start,end);
+
+    if (hours>0)
+        val/=hours;
+    else
+        val=0;
+
+    return val;
+}
+
+
+struct RXChange
+{
+    RXChange() { highlight=0; machine=NULL; }
+    RXChange(const RXChange & copy) {
+        first=copy.first;
+        last=copy.last;
+        days=copy.days;
+        ahi=copy.ahi;
+    fl=copy.fl;
+        mode=copy.mode;
+        min=copy.min;
+        max=copy.max;
+        maxhi=copy.maxhi;
+        machine=copy.machine;
+        per1=copy.per1;
+        per2=copy.per2;
+        highlight=copy.highlight;
+        weighted=copy.weighted;
+        prelief=copy.prelief;
+        prelset=copy.prelset;
+    }
+    QDate first;
+    QDate last;
+    int days;
+    EventDataType ahi;
+    EventDataType fl;
+    CPAPMode mode;
+    EventDataType min;
+    EventDataType max;
+    EventDataType maxhi;
+    EventDataType per1;
+    EventDataType per2;
+    EventDataType weighted;
+    PRTypes prelief;
+    Machine * machine;
+    short prelset;
+    short highlight;
+};
+
+enum RXSortMode { RX_first, RX_last, RX_days, RX_ahi, RX_mode, RX_min, RX_max, RX_maxhi, RX_per1, RX_per2, RX_weighted };
+RXSortMode RXsort=RX_first;
+bool RXorder=false;
+
+bool operator<(const RXChange & c1, const RXChange & c2) {
+    const RXChange * comp1=&c1;
+    const RXChange * comp2=&c2;
+    if (RXorder) {
+        switch (RXsort) {
+        case RX_ahi: return comp1->ahi < comp2->ahi;
+        case RX_days: return comp1->days < comp2->days;
+        case RX_first: return comp1->first < comp2->first;
+        case RX_last: return comp1->last < comp2->last;
+        case RX_mode: return comp1->mode < comp2->mode;
+        case RX_min:  return comp1->min < comp2->min;
+        case RX_max:  return comp1->max < comp2->max;
+        case RX_maxhi: return comp1->maxhi < comp2->maxhi;
+        case RX_per1:  return comp1->per1 < comp2->per1;
+        case RX_per2:  return comp1->per2 < comp2->per2;
+        case RX_weighted:  return comp1->weighted < comp2->weighted;
+        };
+    } else {
+        switch (RXsort) {
+        case RX_ahi: return comp1->ahi > comp2->ahi;
+        case RX_days: return comp1->days > comp2->days;
+        case RX_first: return comp1->first > comp2->first;
+        case RX_last: return comp1->last > comp2->last;
+        case RX_mode: return comp1->mode > comp2->mode;
+        case RX_min:  return comp1->min > comp2->min;
+        case RX_max:  return comp1->max > comp2->max;
+        case RX_maxhi: return comp1->maxhi > comp2->maxhi;
+        case RX_per1:  return comp1->per1 > comp2->per1;
+        case RX_per2:  return comp1->per2 > comp2->per2;
+        case RX_weighted:  return comp1->weighted > comp2->weighted;
+        };
+    }
+    return true;
+}
+
+bool RXSort(const RXChange * comp1, const RXChange * comp2) {
+    if (RXorder) {
+        switch (RXsort) {
+        case RX_ahi: return comp1->ahi < comp2->ahi;
+        case RX_days: return comp1->days < comp2->days;
+        case RX_first: return comp1->first < comp2->first;
+        case RX_last: return comp1->last < comp2->last;
+        case RX_mode: return comp1->mode < comp2->mode;
+        case RX_min:  return comp1->min < comp2->min;
+        case RX_max:  return comp1->max < comp2->max;
+        case RX_maxhi: return comp1->maxhi < comp2->maxhi;
+        case RX_per1:  return comp1->per1 < comp2->per1;
+        case RX_per2:  return comp1->per2 < comp2->per2;
+        case RX_weighted:  return comp1->weighted < comp2->weighted;
+        };
+    } else {
+        switch (RXsort) {
+        case RX_ahi: return comp1->ahi > comp2->ahi;
+        case RX_days: return comp1->days > comp2->days;
+        case RX_first: return comp1->first > comp2->first;
+        case RX_last: return comp1->last > comp2->last;
+        case RX_mode: return comp1->mode > comp2->mode;
+        case RX_min:  return comp1->min > comp2->min;
+        case RX_max:  return comp1->max > comp2->max;
+        case RX_maxhi: return comp1->maxhi > comp2->maxhi;
+        case RX_per1:  return comp1->per1 > comp2->per1;
+        case RX_per2:  return comp1->per2 > comp2->per2;
+        case RX_weighted:  return comp1->weighted > comp2->weighted;
+        };
+    }
+    return true;
+}
+struct UsageData {
+    UsageData() { ahi=0; hours=0; }
+    UsageData(QDate d, EventDataType v, EventDataType h) { date=d; ahi=v; hours=h; }
+    UsageData(const UsageData & copy) { date=copy.date; ahi=copy.ahi; hours=copy.hours; }
+    QDate date;
+    EventDataType ahi;
+    EventDataType hours;
+};
+bool operator <(const UsageData & c1, const UsageData & c2)
+{
+    if (c1.ahi < c2.ahi)
+        return true;
+    if ((c1.ahi == c2.ahi) && (c1.date > c2.date)) return true;
+    return false;
+    //return c1.value < c2.value;
+}
+
+QString Summary::GenerateHTML()
+{
+    QString html=htmlHeader();
+
+    QDate lastcpap=p_profile->LastGoodDay(MT_CPAP);
+    QDate firstcpap=p_profile->FirstGoodDay(MT_CPAP);
+    QDate cpapweek=lastcpap.addDays(-7);
+    QDate cpapmonth=lastcpap.addDays(-30);
+    QDate cpap6month=lastcpap.addMonths(-6);
+    QDate cpapyear=lastcpap.addYears(-12);
+    if (cpapweek<firstcpap) cpapweek=firstcpap;
+    if (cpapmonth<firstcpap) cpapmonth=firstcpap;
+    if (cpap6month<firstcpap) cpap6month=firstcpap;
+    if (cpapyear<firstcpap) cpapyear=firstcpap;
+
+
+    QList<Machine *> cpap_machines=PROFILE.GetMachines(MT_CPAP);
+    QList<Machine *> oximeters=PROFILE.GetMachines(MT_OXIMETER);
+    QList<Machine *> mach;
+    mach.append(cpap_machines);
+    mach.append(oximeters);
+
+
+    if (mach.size()==0) {
+        html+="<table cellpadding=2 cellspacing=0 border=0 width=100% height=60%>";
+        QString datacard;
+        html+="<tr><td align=center><h1>"+tr("Please Import Some Data")+"</h1><i>"+tr("SleepyHead is pretty much useless without it.")+"</i><br/><p>"+tr("It might be a good idea to check preferences first,</br>as there are some options that affect import.")+"</p><p>"+tr("First import can take a few minutes.")+"</p></td></tr></table>";
+        html+=htmlFooter();
+        return html;
+    }
+    int cpapdays=PROFILE.countDays(MT_CPAP,firstcpap,lastcpap);
+    int cpapweekdays=PROFILE.countDays(MT_CPAP,cpapweek,lastcpap);
+    int cpapmonthdays=PROFILE.countDays(MT_CPAP,cpapmonth,lastcpap);
+    int cpapyeardays=PROFILE.countDays(MT_CPAP,cpapyear,lastcpap);
+    int cpap6monthdays=PROFILE.countDays(MT_CPAP,cpap6month,lastcpap);
+
+    CPAPMode cpapmode=(CPAPMode)(int)p_profile->calcSettingsMax(CPAP_Mode,MT_CPAP,firstcpap,lastcpap);
+
+    float percentile=PROFILE.general->prefCalcPercentile()/100.0;
+
+//    int mididx=PROFILE.general->prefCalcMiddle();
+//    SummaryType ST_mid;
+//    if (mididx==0) ST_mid=ST_PERC;
+//    if (mididx==1) ST_mid=ST_WAVG;
+//    if (mididx==2) ST_mid=ST_AVG;
+
+    QString ahitxt;
+    if (PROFILE.general->calculateRDI()) {
+        ahitxt=STR_TR_RDI;
+    } else {
+        ahitxt=STR_TR_AHI;
+    }
+
+    int decimals=2;
+    html+="<div align=center>";
+    html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=90%>");
+    if (cpapdays==0)  {
+        html+="<tr><td colspan=6 align=center>"+tr("No CPAP Machine Data Imported")+"</td></tr>";
+    } else {
+        html+=QString("<tr><td colspan=6 align=center><b>")+tr("CPAP Statistics as of")+QString(" %1</b></td></tr>").arg(lastcpap.toString(Qt::SystemLocaleLongDate));
+
+        if (cpap_machines.size()>0) {
+           // html+=QString("<tr><td colspan=6 align=center><b>%1</b></td></tr>").arg(tr("CPAP Summary"));
+
+            if (!cpapdays) {
+                html+=QString("<tr><td colspan=6 align=center><b>%1</b></td></tr>").arg(tr("No CPAP data available."));
+            } else if (cpapdays==1) {
+                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 day of CPAP Data, on %2.")).arg(cpapdays).arg(firstcpap.toString(Qt::SystemLocaleShortDate)));
+            } else {
+                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 days of CPAP Data, between %2 and %3")).arg(cpapdays).arg(firstcpap.toString(Qt::SystemLocaleShortDate)).arg(lastcpap.toString(Qt::SystemLocaleShortDate)));
+            }
+
+            html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
+                .arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(tr("Last 6 months")).arg(tr("Last Year"));
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+            .arg(ahitxt)
+            .arg(calcAHI(lastcpap,lastcpap),0,'f',decimals)
+            .arg(calcAHI(cpapweek,lastcpap),0,'f',decimals)
+            .arg(calcAHI(cpapmonth,lastcpap),0,'f',decimals)
+            .arg(calcAHI(cpap6month,lastcpap),0,'f',decimals)
+            .arg(calcAHI(cpapyear,lastcpap),0,'f',decimals);
+
+            if (PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapyear,lastcpap)) {
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("RERA Index"))
+                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,lastcpap,lastcpap)/PROFILE.calcHours(MT_CPAP,lastcpap,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapweek,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapmonth,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpap6month,lastcpap)/PROFILE.calcHours(MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_RERA,MT_CPAP,cpapyear,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+            }
+
+            if (PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapyear,lastcpap)) {
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Flow Limit Index"))
+                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,lastcpap,lastcpap)/PROFILE.calcHours(MT_CPAP,lastcpap,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapweek,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapmonth,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpap6month,lastcpap)/PROFILE.calcHours(MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(PROFILE.calcCount(CPAP_FlowLimit,MT_CPAP,cpapyear,lastcpap)/PROFILE.calcHours(MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+            }
+
+
+
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+            .arg(tr("Hours per Night"))
+            .arg(formatTime(p_profile->calcHours(MT_CPAP)))
+            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpapweek,lastcpap)/float(cpapweekdays)))
+            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpapmonth,lastcpap)/float(cpapmonthdays)))
+            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpap6month,lastcpap)/float(cpap6monthdays)))
+            .arg(formatTime(p_profile->calcHours(MT_CPAP,cpapyear,lastcpap)/float(cpapyeardays)));
+
+            if (cpapmode>=MODE_BIPAP) {
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Min EPAP"))
+                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP),0,'f',decimals)
+                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcMin(CPAP_EPAP,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(QString("%1% "+STR_TR_EPAP).arg(percentile*100.0,0,'f',0))
+                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Max IPAP"))
+                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP),0,'f',decimals)
+                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcMax(CPAP_IPAP,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(QString("%1% "+STR_TR_IPAP).arg(percentile*100.0,0,'f',0))
+                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+            } else if (cpapmode>=MODE_APAP) {
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Average Pressure"))
+                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP),0,'f',decimals)
+                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcWavg(CPAP_Pressure,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("%1% Pressure").arg(percentile*100.0,0,'f',0))
+                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+            } else {
+                html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Pressure"))
+                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP),0,'f',decimals)
+                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+                .arg(p_profile->calcSettingsMin(CPAP_Pressure,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+            }
+            //html+="<tr><td colspan=6>TODO: 90% pressure.. Any point showing if this is all CPAP?</td></tr>";
+
+
+            ChannelID leak;
+            if (p_profile->calcCount(CPAP_LeakTotal,MT_CPAP,cpapyear,lastcpap)>0) {
+                leak=CPAP_LeakTotal;
+            } else leak=CPAP_Leak;
+
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+            .arg(tr("Average %1").arg(schema::channel[leak].label()))
+            .arg(p_profile->calcWavg(leak,MT_CPAP),0,'f',decimals)
+            .arg(p_profile->calcWavg(leak,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+            .arg(p_profile->calcWavg(leak,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+            .arg(p_profile->calcWavg(leak,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+            .arg(p_profile->calcWavg(leak,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+            .arg(tr("%1% %2").arg(percentile*100.0f,0,'f',0).arg(schema::channel[leak].label()))
+            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP),0,'f',decimals)
+            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpapweek,lastcpap),0,'f',decimals)
+            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpapmonth,lastcpap),0,'f',decimals)
+            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpap6month,lastcpap),0,'f',decimals)
+            .arg(p_profile->calcPercentile(leak,percentile,MT_CPAP,cpapyear,lastcpap),0,'f',decimals);
+        }
+    }
+    int oxisize=oximeters.size();
+    if (oxisize>0) {
+        QDate lastoxi=p_profile->LastGoodDay(MT_OXIMETER);
+        QDate firstoxi=p_profile->FirstGoodDay(MT_OXIMETER);
+        int days=PROFILE.countDays(MT_OXIMETER,firstoxi,lastoxi);
+        if (days>0) {
+            html+=QString("<tr><td colspan=6 align=center><b>%1</b></td></tr>").arg(tr("Oximetry Summary"));
+            if (days==1) {
+                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 day of Oximetry Data, on %2.")).arg(days).arg(firstoxi.toString(Qt::SystemLocaleShortDate)));
+            } else {
+                html+=QString("<tr><td colspan=6 align=center>%1</td></tr>").arg(QString(tr("%1 days of Oximetry Data, between %2 and %3")).arg(days).arg(firstoxi.toString(Qt::SystemLocaleShortDate)).arg(lastoxi.toString(Qt::SystemLocaleShortDate)));
+            }
+
+            html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</td></tr>")
+                    .arg(tr("Details")).arg(tr("Most Recent")).arg(tr("Last 7 Days")).arg(tr("Last 30 Days")).arg(tr("Last 6 months")).arg(tr("Last Year"));
+            QDate oxiweek=lastoxi.addDays(-7);
+            QDate oximonth=lastoxi.addDays(-30);
+            QDate oxi6month=lastoxi.addMonths(-6);
+            QDate oxiyear=lastoxi.addYears(-12);
+            if (oxiweek<firstoxi) oxiweek=firstoxi;
+            if (oximonth<firstoxi) oximonth=firstoxi;
+            if (oxi6month<firstoxi) oxi6month=firstoxi;
+            if (oxiyear<firstoxi) oxiyear=firstoxi;
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Average SpO2"))
+                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_SPO2,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Minimum SpO2"))
+                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_SPO2,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("SpO2 Events / Hour"))
+                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER)/p_profile->calcHours(MT_OXIMETER),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiweek,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oximonth,lastoxi)/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxi6month,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_SPO2Drop,MT_OXIMETER,oxiyear,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2\%</td><td>%3\%</td><td>%4\%</td><td>%5\%</td><td>%6\%</td></tr>")
+                .arg(tr("% of time in SpO2 Events"))
+                .arg(100.0/p_profile->calcHours(MT_OXIMETER)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER)/3600.0,0,'f',decimals)
+                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oxiweek,lastoxi)/3600.0,0,'f',decimals)
+                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oximonth,lastoxi)/3600.0,0,'f',decimals)
+                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oxi6month,lastoxi)/3600.0,0,'f',decimals)
+                .arg(100.0/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi)*p_profile->calcSum(OXI_SPO2Drop,MT_OXIMETER,oxiyear,lastoxi)/3600.0,0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Average Pulse Rate"))
+                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcWavg(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Minimum Pulse Rate"))
+                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMin(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Maximum Pulse Rate"))
+                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER),0,'f',decimals)
+                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcMax(OXI_Pulse,MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td><td>%6</td></tr>")
+                .arg(tr("Pulse Change Events / Hour"))
+                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER)/p_profile->calcHours(MT_OXIMETER),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oxiweek,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiweek,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oximonth,lastoxi)/p_profile->calcHours(MT_OXIMETER,oximonth,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oxi6month,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxi6month,lastoxi),0,'f',decimals)
+                .arg(p_profile->calcCount(OXI_PulseChange,MT_OXIMETER,oxiyear,lastoxi)/p_profile->calcHours(MT_OXIMETER,oxiyear,lastoxi),0,'f',decimals);
+        }
+    }
+
+    html+="</table>";
+    html+="</div>";
+
+    QList<UsageData> AHI;
+
+    //QDate bestAHIdate, worstAHIdate;
+    //EventDataType bestAHI=999.0, worstAHI=0;
+    if (cpapdays>0) {
+        QDate first,last=lastcpap;
+        CPAPMode mode=MODE_UNKNOWN,cmode=MODE_UNKNOWN;
+        EventDataType cmin=0,cmax=0,cmaxhi=0, min=0,max=0,maxhi=0;
+        Machine *mach=NULL,*lastmach=NULL;
+        PRTypes lastpr=PR_UNKNOWN, prelief=PR_UNKNOWN;
+        short prelset=0, lastprelset=-1;
+        QDate date=lastcpap;
+        Day * day;
+        bool lastchanged=false;
+        QVector<RXChange> rxchange;
+        EventDataType hours;
+
+        int compliant=0;
+        do {
+            day=PROFILE.GetGoodDay(date,MT_CPAP);
+
+            if (day) {
+                lastchanged=false;
+
+                hours=day->hours();
+
+                if (hours > PROFILE.cpap->complianceHours())
+                    compliant++;
+
+                EventDataType ahi=day->count(CPAP_Obstructive)+day->count(CPAP_Hypopnea)+day->count(CPAP_Apnea)+day->count(CPAP_ClearAirway);
+                if (PROFILE.general->calculateRDI()) ahi+=day->count(CPAP_RERA);
+                ahi/=hours;
+                AHI.push_back(UsageData(date,ahi,hours));
+
+                prelief=(PRTypes)(int)round(day->settings_wavg(CPAP_PresReliefType));
+                prelset=round(day->settings_wavg(CPAP_PresReliefSet));
+                mode=(CPAPMode)(int)round(day->settings_wavg(CPAP_Mode));
+                mach=day->machine;
+                if (mode>=MODE_ASV) {
+                    min=day->settings_min(CPAP_EPAP);
+                    max=day->settings_max(CPAP_IPAPLo);
+                    maxhi=day->settings_max(CPAP_IPAPHi);
+                } else if (mode>=MODE_BIPAP) {
+                    min=day->settings_min(CPAP_EPAP);
+                    max=day->settings_max(CPAP_IPAP);
+                } else if (mode>=MODE_APAP) {
+                    min=day->settings_min(CPAP_PressureMin);
+                    max=day->settings_max(CPAP_PressureMax);
+                } else {
+                    min=day->settings_min(CPAP_Pressure);
+                }
+                if ((mode!=cmode) || (min!=cmin) || (max!=cmax) || (mach!=lastmach) || (prelset!=lastprelset))  {
+                    if ((cmode!=MODE_UNKNOWN) && (lastmach!=NULL)) {
+                        first=date.addDays(1);
+                        int days=PROFILE.countDays(MT_CPAP,first,last);
+                        RXChange rx;
+                        rx.first=first;
+                        rx.last=last;
+                        rx.days=days;
+                        rx.ahi=calcAHI(first,last);
+                        rx.fl=calcFL(first,last);
+                        rx.mode=cmode;
+                        rx.min=cmin;
+                        rx.max=cmax;
+                        rx.maxhi=cmaxhi;
+                        rx.prelief=lastpr;
+                        rx.prelset=lastprelset;
+                        rx.machine=lastmach;
+
+                        if (mode<MODE_BIPAP) {
+                            rx.per1=p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,first,last);
+                            rx.per2=0;
+                        } else if (mode<MODE_ASV) {
+                            rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
+                            rx.per2=p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,first,last);
+                        } else {
+                            rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
+                            rx.per2=p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,first,last);
+                        }
+                        rx.weighted=float(rx.days)/float(cpapdays)*rx.ahi;
+                        rxchange.push_back(rx);
+                    }
+                    cmode=mode;
+                    cmin=min;
+                    cmax=max;
+                    cmaxhi=maxhi;
+                    lastpr=prelief;
+                    lastprelset=prelset;
+                    last=date;
+                    lastmach=mach;
+                    lastchanged=true;
+                }
+
+            }
+            date=date.addDays(-1);
+        } while (date>=firstcpap);
+
+        // Sort list by AHI
+        qSort(AHI);
+
+        lastchanged=false;
+        if (!lastchanged && (mach!=NULL)) {
+           // last=date.addDays(1);
+            first=firstcpap;
+            int days=PROFILE.countDays(MT_CPAP,first,last);
+            RXChange rx;
+            rx.first=first;
+            rx.last=last;
+            rx.days=days;
+            rx.ahi=calcAHI(first,last);
+            rx.fl=calcFL(first,last);
+            rx.mode=mode;
+            rx.min=min;
+            rx.max=max;
+            rx.maxhi=maxhi;
+            rx.prelief=prelief;
+            rx.prelset=prelset;
+            rx.machine=mach;
+            if (mode<MODE_BIPAP) {
+                rx.per1=p_profile->calcPercentile(CPAP_Pressure,percentile,MT_CPAP,first,last);
+                rx.per2=0;
+            } else if (mode<MODE_ASV) {
+                rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
+                rx.per2=p_profile->calcPercentile(CPAP_IPAP,percentile,MT_CPAP,first,last);
+            } else {
+                rx.per1=p_profile->calcPercentile(CPAP_EPAP,percentile,MT_CPAP,first,last);
+                rx.per2=p_profile->calcPercentile(CPAP_IPAPHi,percentile,MT_CPAP,first,last);
+            }
+            rx.weighted=float(rx.days)/float(cpapdays);
+            //rx.weighted=float(days)*rx.ahi;
+            rxchange.push_back(rx);
+        }
+
+        int rxthresh=5;
+        QVector<RXChange *> tmpRX;
+        for (int i=0;i<rxchange.size();i++) {
+            RXChange & rx=rxchange[i];
+            if (rx.days>rxthresh)
+                tmpRX.push_back(&rx);
+        }
+
+        QString recbox="<html><head><style type='text/css'>"
+            "p,a,td,body { font-family: '"+QApplication::font().family()+"'; }"
+            "p,a,td,body { font-size: "+QString::number(QApplication::font().pointSize() + 2)+"px; }"
+            "a:link,a:visited { color: inherit; text-decoration: none; }" //font-weight: normal;
+            "a:hover { background-color: inherit; color: white; text-decoration:none; font-weight: bold; }"
+            "</style></head><body>";
+        recbox+="<table width=100% cellpadding=1 cellspacing=0>";
+        int numdays=AHI.size();
+        if (numdays>1) {
+            int z=numdays/2;
+            if (z>4) z=4;
+
+            recbox+=QString("<tr><td colspan=2 align=center><b>%1</b></td></tr>").arg(tr("Usage Information"));
+            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(tr("Total Days")).arg(numdays);
+            if (PROFILE.cpap->showComplianceInfo()) {
+                recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(tr("Compliant Days")).arg(compliant);
+            }
+            int highahi=0;
+            for (int i=0;i<numdays;i++) {
+                if (AHI.at(i).ahi > 5.0)
+                    highahi++;
+            }
+            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(tr("Days AHI &gt;5.0")).arg(highahi);
+
+
+            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
+            recbox+=QString("<tr><td colspan=2 align=center><b>%1</b></td></tr>").arg(tr("Best&nbsp;%1").arg(ahitxt));
+            for (int i=0;i<z;i++) {
+                const UsageData & a=AHI.at(i);
+                recbox+=QString("<tr><td><a href='daily=%1'>%2</a></td><td  align=right>%3</td></tr>")
+                    .arg(a.date.toString(Qt::ISODate))
+                    .arg(a.date.toString(Qt::SystemLocaleShortDate))
+                    .arg(a.ahi,0,'f',decimals);
+            }
+            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
+            recbox+=QString("<tr><td colspan=2 align=center><b>%1</b></td></tr>").arg(tr("Worst&nbsp;%1").arg(ahitxt));
+            for (int i=0;i<z;i++) {
+                const UsageData & a=AHI.at((numdays-1)-i);
+                recbox+=QString("<tr><td><a href='daily=%1'>%2</a></td><td align=right>%3</td></tr>")
+                    .arg(a.date.toString(Qt::ISODate))
+                    .arg(a.date.toString(Qt::SystemLocaleShortDate))
+                    .arg(a.ahi,0,'f',decimals);
+            }
+            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
+        }
+
+
+        if (tmpRX.size()>0) {
+            RXsort=RX_ahi;
+            QString minstr,maxstr,modestr,maxhistr;
+            qSort(tmpRX.begin(),tmpRX.end(),RXSort);
+            tmpRX[0]->highlight=4; // worst
+            int ls=tmpRX.size()-1;
+            tmpRX[ls]->highlight=1; //best
+            CPAPMode mode=(CPAPMode)(int)PROFILE.calcSettingsMax(CPAP_Mode,MT_CPAP,tmpRX[ls]->first,tmpRX[ls]->last);
+
+            if (mode<MODE_APAP) { // is CPAP?
+                minstr=STR_TR_Pressure;
+                maxstr="";
+                modestr=STR_TR_CPAP;
+            } else if (mode<MODE_BIPAP) { // is AUTO?
+                minstr=STR_TR_Min;
+                maxstr=STR_TR_Max;
+                modestr=STR_TR_APAP;
+            } else if (mode<MODE_ASV) { // BIPAP
+                minstr=STR_TR_EPAP;
+                maxstr=STR_TR_IPAP;
+                modestr=STR_TR_BiLevel;
+            } else {
+                minstr=STR_TR_EPAP;
+                maxstr=STR_TR_IPAPLo;
+                maxhistr=STR_TR_IPAPHi;
+                modestr=STR_TR_STASV;
+
+            }
+
+            recbox+=QString("<tr><td colspan=2><table width=100% border=0 cellpadding=1 cellspacing=0><tr><td colspan=2 align=center><b>%3</b></td></tr>")
+                    .arg(tr("Best RX Setting"));
+            recbox+=QString("<tr><td valign=top>")+STR_TR_Start+"<br/>"+STR_TR_End+QString("</td><td align=right><a href='overview=%1,%2'>%3<br/>%4</a></td></tr>")
+                    .arg(tmpRX[ls]->first.toString(Qt::ISODate))
+                    .arg(tmpRX[ls]->last.toString(Qt::ISODate))
+                    .arg(tmpRX[ls]->first.toString(Qt::SystemLocaleShortDate))
+                    .arg(tmpRX[ls]->last.toString(Qt::SystemLocaleShortDate));
+            recbox+=QString("<tr><td><b>%1</b></td><td align=right><b>%2</b></td></tr>").arg(ahitxt).arg(tmpRX[ls]->ahi,0,'f',decimals);
+            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(STR_TR_Mode).arg(modestr);
+            recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(minstr).arg(tmpRX[ls]->min,0,'f',1).arg(STR_UNIT_CMH2O);
+            if (!maxstr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxstr).arg(tmpRX[ls]->max,0,'f',1).arg(STR_UNIT_CMH2O);
+            if (!maxhistr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxhistr).arg(tmpRX[ls]->maxhi,0,'f',1).arg(STR_UNIT_CMH2O);
+            recbox+="</table></td></tr>";
+
+            recbox+=QString("<tr><td colspan=2>&nbsp;</td></tr>");
+
+            mode=(CPAPMode)(int)PROFILE.calcSettingsMax(CPAP_Mode,MT_CPAP,tmpRX[0]->first,tmpRX[0]->last);
+            if (mode<MODE_APAP) { // is CPAP?
+                minstr=STR_TR_Pressure;
+                maxstr="";
+                modestr=STR_TR_CPAP;
+            } else if (mode<MODE_BIPAP) { // is AUTO?
+                minstr=STR_TR_Min;
+                maxstr=STR_TR_Max;
+                modestr=STR_TR_APAP;
+            } else if (mode<MODE_ASV) { // BIPAP or greater
+                minstr=STR_TR_EPAP;
+                maxstr=STR_TR_IPAP;
+                modestr=STR_TR_BiLevel;
+            } else {
+                minstr=STR_TR_EPAP;
+                maxstr=STR_TR_IPAPLo;
+                maxhistr=STR_TR_IPAPHi;
+                modestr=STR_TR_STASV;
+            }
+
+            recbox+=QString("<tr><td colspan=2><table width=100% border=0 cellpadding=1 cellspacing=0><tr><td colspan=2 align=center><b>%3</b></td></tr>")
+                    .arg(tr("Worst RX Setting"));
+            recbox+=QString("<tr><td valign=top>")+STR_TR_Start+"<br/>"+STR_TR_End+QString("</td><td align=right><a href='overview=%1,%2'>%3<br/>%4</a></td></tr>")
+                    .arg(tmpRX[0]->first.toString(Qt::ISODate))
+                    .arg(tmpRX[0]->last.toString(Qt::ISODate))
+                    .arg(tmpRX[0]->first.toString(Qt::SystemLocaleShortDate))
+                    .arg(tmpRX[0]->last.toString(Qt::SystemLocaleShortDate));
+            recbox+=QString("<tr><td><b>%1</b></td><td align=right><b>%2</b></td></tr>").arg(ahitxt).arg(tmpRX[0]->ahi,0,'f',decimals);
+            recbox+=QString("<tr><td>%1</td><td align=right>%2</td></tr>").arg(STR_TR_Mode).arg(modestr);
+            recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(minstr).arg(tmpRX[0]->min,0,'f',1).arg(STR_UNIT_CMH2O);
+            if (!maxstr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxstr).arg(tmpRX[0]->max,0,'f',1).arg(STR_UNIT_CMH2O);
+            if (!maxhistr.isEmpty()) recbox+=QString("<tr><td>%1</td><td align=right>%2%3</td></tr>").arg(maxhistr).arg(tmpRX[0]->maxhi,0,'f',1).arg(STR_UNIT_CMH2O);
+            recbox+="</table></td></tr>";
+
+        }
+        recbox+="</table>";
+        recbox+="</body></html>";
+        mainwin->setRecBoxHTML(recbox);
+
+        /*RXsort=RX_min;
+        RXorder=true;
+        qSort(rxchange.begin(),rxchange.end());*/
+        html+="<div align=center>";
+        html+=QString("<br/><b>")+tr("Changes to Prescription Settings")+"</b>";
+        html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=90%>");
+        QString extratxt;
+
+        if (cpapmode>=MODE_ASV) {
+            extratxt=QString("<td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td>")
+                .arg(STR_TR_EPAP).arg(STR_TR_IPAPLo).arg(STR_TR_IPAPHi).arg(tr("PS Min")).arg(tr("PS Max"));
+        } else if (cpapmode>=MODE_BIPAP) {
+            extratxt=QString("<td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td>")
+                .arg(STR_TR_EPAP).arg(STR_TR_IPAP).arg(STR_TR_PS);
+        } else if (cpapmode>MODE_CPAP) {
+            extratxt=QString("<td><b>%1</b></td><td><b>%2</b></td>")
+                .arg(tr("Min Pres.")).arg(tr("Max Pres."));
+        } else {
+            extratxt=QString("<td><b>%1</b></td>")
+                .arg(STR_TR_Pressure);
+        }
+        QString tooltip;
+        html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td><td><b>%6</b></td><td><b>%7</td><td><b>%8</td>%9</tr>")
+                  .arg(STR_TR_First)
+                  .arg(STR_TR_Last)
+                  .arg(tr("Days"))
+                  .arg(ahitxt)
+          .arg(tr("FL"))
+                  .arg(STR_TR_Machine)
+                  .arg(STR_TR_Mode)
+                  .arg(tr("Pr. Rel."))
+                  .arg(extratxt);
+
+        for (int i=0;i<rxchange.size();i++) {
+            RXChange rx=rxchange.at(i);
+            QString color;
+            if (rx.highlight==1) {
+                color="#c0ffc0";
+            } else if (rx.highlight==2) {
+                color="#e0ffe0";
+            } else if (rx.highlight==3) {
+                color="#ffe0e0";
+            } else if (rx.highlight==4) {
+                color="#ffc0c0";
+            } else color="";
+            QString machstr;
+            if (rx.machine->properties.contains(STR_PROP_Brand))
+                machstr+=rx.machine->properties[STR_PROP_Brand];
+            if (rx.machine->properties.contains(STR_PROP_Model)) {
+                 machstr+=" "+rx.machine->properties[STR_PROP_Model];
+            }
+            if (rx.machine->properties.contains(STR_PROP_Serial)) {
+                 machstr+=" ("+rx.machine->properties[STR_PROP_Serial]+")<br/>";
+            }
+
+            mode=rx.mode;
+            if(mode>=MODE_ASV) {
+                extratxt=QString("<td>%1</td><td>%2</td><td>%3</td><td>%4</td>")
+                        .arg(rx.max,0,'f',decimals).arg(rx.maxhi,0,'f',decimals).arg(rx.max-rx.min,0,'f',decimals).arg(rx.maxhi-rx.min,0,'f',decimals);
+
+                tooltip=QString("%1 %2% ").arg(machstr).arg(percentile*100.0)+STR_TR_EPAP+
+                        QString("=%1<br/>%2% ").arg(rx.per1,0,'f',decimals).arg(percentile*100.0)+
+                        STR_TR_IPAP+QString("=%1").arg(rx.per2,0,'f',decimals);
+            } else if (mode>=MODE_BIPAP) {
+                extratxt=QString("<td>%1</td><td>%2</td>")
+                       .arg(rx.max,0,'f',decimals).arg(rx.max-rx.min,0,'f',decimals);
+                tooltip=QString("%1 %2% ").arg(machstr).arg(percentile*100.0)+
+                        STR_TR_EPAP+
+                        QString("=%1<br/>%2% ").arg(rx.per1,0,'f',decimals)
+                        .arg(percentile*100.0)
+                        +STR_TR_IPAP+QString("=%1").arg(rx.per2,0,'f',decimals);
+            } else if (mode>MODE_CPAP) {
+                extratxt=QString("<td>%1</td>").arg(rx.max,0,'f',decimals);
+                tooltip=QString("%1 %2% ").arg(machstr).arg(percentile*100.0)+STR_TR_Pressure+
+                        QString("=%2").arg(rx.per1,0,'f',decimals);
+            } else {
+                if (cpapmode>MODE_CPAP) {
+                    extratxt="<td>&nbsp;</td>";
+                    tooltip=QString("%1").arg(machstr);
+                } else {
+                    extratxt="";
+                    tooltip="";
+                }
+            }
+            QString presrel;
+            if (rx.prelset>0) {
+                presrel=schema::channel[CPAP_PresReliefType].option(int(rx.prelief));
+                presrel+=QString(" x%1").arg(rx.prelset);
+            } else presrel=STR_TR_None;
+            QString tooltipshow,tooltiphide;
+            if (!tooltip.isEmpty()) {
+                tooltipshow=QString("tooltip.show(\"%1\");").arg(tooltip);
+                tooltiphide="tooltip.hide();";
+            }
+            html+=QString("<tr bgcolor='"+color+"' onmouseover='ChangeColor(this, \"#eeeeee\"); %13' onmouseout='ChangeColor(this, \""+color+"\"); %14' onclick='alert(\"overview=%1,%2\");'><td>%3</td><td>%4</td><td>%5</td><td>%6</td><td>%7</td><td>%8</td><td>%9</td><td>%10</td><td>%11</td>%12</tr>")
+                    .arg(rx.first.toString(Qt::ISODate))
+                    .arg(rx.last.toString(Qt::ISODate))
+                    .arg(rx.first.toString(Qt::SystemLocaleShortDate))
+                    .arg(rx.last.toString(Qt::SystemLocaleShortDate))
+                    .arg(rx.days)
+                    .arg(rx.ahi,0,'f',decimals)
+            .arg(rx.fl,0,'f',decimals)
+                    .arg(rx.machine->GetClass())
+                    .arg(schema::channel[CPAP_Mode].option(int(rx.mode)-1))
+                    .arg(presrel)
+                    .arg(rx.min,0,'f',decimals)
+                    .arg(extratxt)
+                    .arg(tooltipshow)
+                    .arg(tooltiphide);
+        }
+        html+="</table>";
+        html+=QString("<i>")+tr("The above has a threshold which excludes day counts less than %1 from the best/worst highlighting").arg(rxthresh)+QString("</i><br/>");
+        html+="</div>";
+
+    }
+    if (mach.size()>0) {
+        html+="<div align=center>";
+
+        html+=QString("<br/><b>")+tr("Machine Information")+"</b>";
+        html+=QString("<table cellpadding=2 cellspacing=0 border=1 width=90%>");
+        html+=QString("<tr><td><b>%1</b></td><td><b>%2</b></td><td><b>%3</b></td><td><b>%4</b></td><td><b>%5</b></td></tr>")
+                      .arg(STR_TR_Brand)
+                      .arg(STR_TR_Model)
+                      .arg(STR_TR_Serial)
+                      .arg(tr("First Use"))
+                      .arg(tr("Last Use"));
+        Machine *m;
+        for (int i=0;i<mach.size();i++) {
+            m=mach.at(i);
+            if (m->GetType()==MT_JOURNAL) continue;
+            QString mn=m->properties[STR_PROP_ModelNumber];
+            //if (mn.isEmpty())
+            html+=QString("<tr><td>%1</td><td>%2</td><td>%3</td><td>%4</td><td>%5</td></tr>")
+                    .arg(m->properties[STR_PROP_Brand])
+                    .arg(m->properties[STR_PROP_Model]+" "+m->properties[STR_PROP_SubModel]+ (mn.isEmpty() ? "" : QString(" (")+mn+QString(")")))
+                    .arg(m->properties[STR_PROP_Serial])
+                    .arg(m->FirstDay().toString(Qt::SystemLocaleShortDate))
+                    .arg(m->LastDay().toString(Qt::SystemLocaleShortDate));
+        }
+        html+="</table>";
+        html+="</div>";
+    }
+    html+="<script type='text/javascript' language='javascript' src='qrc:/docs/script.js'></script>";
+    //updateFavourites();
+    html+=htmlFooter();
+    return html;
+}
diff --git a/summary.h b/summary.h
new file mode 100644
index 00000000..fcf79d1d
--- /dev/null
+++ b/summary.h
@@ -0,0 +1,26 @@
+/*
+ Summary Header
+ Copyright (c)2013 Mark Watkins <jedimark@users.sourceforge.net>
+ License: GPL3
+*/
+
+#ifndef SUMMARY_H
+#define SUMMARY_H
+
+#include <QObject>
+
+class Summary : public QObject
+{
+    Q_OBJECT
+public:
+    explicit Summary(QObject *parent = 0);
+    
+    static QString GenerateHTML();
+
+signals:
+    
+public slots:
+    
+};
+
+#endif // SUMMARY_H