Subversion Repositories public

Rev

Rev 148 | Rev 151 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/***************************************************************************
 *   Copyright (C) 2007, 2008 by Andreas Theofilu                          *
 *   andreas@theosys.at                                                    *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation version 3 of the License.                *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include "config.h"
#include "managefile.h"
#include "sportwatcherwidget.h"
#include "settingswidget.h"
#include "progresswidget.h"
#include "imgmap.h"

#include <iostream>
#include <kfiledialog.h>
#include <kmessagebox.h>
#include <ksimpleconfig.h>
#include <klocale.h>
#include <klistview.h>
#include <kaboutdialog.h>
#include <kaboutdata.h>
#include <kglobalsettings.h>
#include <qstring.h>
#include <qdatetime.h>
#include <qtoolbutton.h>
#include <qcursor.h>
#include "copy.h"

using std::cout;
using std::endl;


typedef struct
{
        double lon;
        double lat;
} posn_type;

sportwatcherWidget::sportwatcherWidget(QWidget* parent, const char* name, WFlags fl)
: sportwatcherWidgetBase(parent,name,fl)
{
        mama = parent;
        gmn = 0;
        min_hr = max_hr = avg_hr = 0;
        min_height = max_height = 0.0;
        max_time = 0;
        index = 0;
        zfactor = 0;
        mapLap = 0;
        mapPan = QRect(0, 0, 0, 0);
        stateHand = stateFlag = stateGlas = false;
        oldTransX = oldTransY = 0.0;
        lmbPressed = 0;

        // Load the config parameters
        KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"), true);
        cfg->setGroup(QString("SportWatcher"));
        lower1 = cfg->readNumEntry("lower1", 0);
        lower2 = cfg->readNumEntry("lower2", 0);
        lower3 = cfg->readNumEntry("lower3", 0);
        upper1 = cfg->readNumEntry("upper1", 0);
        upper2 = cfg->readNumEntry("upper2", 0);
        upper3 = cfg->readNumEntry("upper3", 0);
        MaxHr = cfg->readNumEntry("maxHr", 180);
        restHr = cfg->readNumEntry("restHr", 60);
        vo2max = cfg->readNumEntry("vo2max", 50);
        weight = cfg->readNumEntry("weight", 70);
        sampleTime = cfg->readNumEntry("seconds", 15);
        Serial = cfg->readBoolEntry("Serial", false);
        Device = cfg->readEntry("Device", "/dev/ttyUSB0");
        Data = cfg->readEntry("Data", QDir::home().absPath() + "/.sportwatcher");
        HRM = cfg->readEntry("HRM", QDir::home().absPath() + "/polar");
        MAP = cfg->readEntry("MAP", QDir::home().absPath() + "/.sportwatcher/pc_basemap2.img");
        Units = cfg->readNumEntry("Units", 0);
        delete cfg;
        // Set some widget settings
        btHand->setToggleButton(true);
        btGlas->setToggleButton(true);
        // Fill the activities
        getActivities();
}

sportwatcherWidget::~sportwatcherWidget()
{
        destroy();
}

void sportwatcherWidget::destroy()
{
        if (gmn)
           garmin_free_data (gmn);

        if (index)
        {
        INDEX *n, *akt = index;

           while (akt)
           {
              n = akt;
              akt = akt->next;
              delete n;
           }
        }

        index = 0;
        gmn = 0;
        oldTransX = oldTransY = 0.0;
}

bool sportwatcherWidget::findIndex(const QString &key)
{
INDEX *akt = index;

        while (akt)
        {
           if (akt->path == key || akt->activ == key)
              return true;

           akt = akt->next;
        }

        return false;
}

/*
 * Search for a directory named .sportwatcher in the home directory of
 * the user and search for *.gmn files. Open the files and read the header
 * to find the basic data of activities. Then add the information into
 * the activities KListView.
 */
void sportwatcherWidget::getActivities()
{
QString path, txt;
QDir mdir, dir = QDir::homeDirPath();
QFileInfo *entries;
QStringList years, months;
KListViewItem *running, *biking, *other;
KListViewItem *el;
int anz;
RUN_NODE *rn;
LAP *lap;

        if (Data.isEmpty())
        {
           path = dir.homeDirPath();
           path.append("/.sportwatcher");
        }
        else
           path = Data;

        dir.setPath(path);
        dir.refresh();

        if (!dir.exists())
        {
           dir.mkdir(path);
           return;
        }

        destroy();
        liActivities->clear();
        liActivities->setRootIsDecorated(true);
        liActivities->setSortColumn(-1);
        other = new KListViewItem(liActivities, i18n("Others"));
        biking = new KListViewItem(liActivities, i18n("Biking"));
        running = new KListViewItem(liActivities, i18n("Running"));

        other->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));
        biking->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));
        running->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));

        liActivities->insertItem(other);
        liActivities->insertItem(biking);
        liActivities->insertItem(running);

        dir.cd(path);
        dir.setFilter(QDir::Dirs | QDir::NoSymLinks);
        dir.setSorting(QDir::Name);
        dir.refresh();
        QFileInfoList *list = (QFileInfoList *)dir.entryInfoList();

        if (!list)
           return;

        QFileInfoListIterator it(*list);

        while ((entries = it.current()) != 0)           // Years
        {
           if (entries->fileName() == QString(".") || entries->fileName() == QString(".."))
           {
              ++it;
              continue;
           }

           years += entries->absFilePath();
           ++it;
        }

        for (QStringList::Iterator strit = years.begin(); strit != years.end(); ++strit)
        {
           if (months.count() > 0)
              months.clear();

           dir.setPath(*strit);
           dir.refresh();
           list = (QFileInfoList *)dir.entryInfoList();

           if (!list)
              continue;

           it = QFileInfoListIterator (*list);

           while ((entries = it.current()) != 0)        // Months
           {
              if (entries->fileName() == QString(".") || entries->fileName() == QString(".."))
              {
                 ++it;
                 continue;
              }

              months += entries->absFilePath();
              ++it;
           }

           for (QStringList::Iterator strit1 = months.begin(); strit1 != months.end(); ++strit1)
           {
              mdir.setPath(*strit1);
              mdir.cd(*strit1);
              mdir.setFilter(QDir::Files | QDir::NoSymLinks);
              mdir.setNameFilter(QString("*.gmn"));
              mdir.refresh();
              list = (QFileInfoList *)mdir.entryInfoList();

              if (!list)
                 continue;

              it = QFileInfoListIterator (*list);
              anz = 0;

              while ((entries = it.current()) != 0)             // Files
              {
                 files += entries->absFilePath();
                 ++it;
              }
           }
        }

        INDEX *akt, *n;
        // Open every file and read its head
        for (QStringList::Iterator strfl = files.begin(); strfl != files.end(); ++strfl)
        {
           if (findIndex(*strfl))       // avoid duplicate entries
              continue;

           spw.destroy();

           if (spw.setFileName(*strfl) == -1)
              return;

           if (gmn)
              garmin_free_data (gmn);

           gmn = spw.readFile();
           ds.destroy();
           ds.garmin_print_data(gmn);
           rn = ds.getRunNode();

           lap = ds.getLap(rn->run->first_lap_index);
           const QDateTime *qt = garmin_dtime (lap->start_time);
           QString idx = qt->toString("dd.MM.yyyy hh:mm.ss");

           if (!index)
           {
              index = new INDEX;
              index->path = *strfl;
              index->activ = idx;
              index->next = 0;
           }
           else
           {
              n = new INDEX;
              n->path = *strfl;
              n->activ = idx;
              n->next = 0;
              akt = index;

              while (akt->next)
                 akt = akt->next;

              akt->next = n;
           }

           switch (rn->run->sport_type)
           {
              case D1000_running:
                 el = new KListViewItem(running, idx);
                 el->setPixmap(0, QPixmap::fromMimeSource(QString("run.png")));
                 running->insertItem(el);
              break;

              case D1000_biking:
                 el = new KListViewItem(biking, idx);
                 el->setPixmap(0, QPixmap::fromMimeSource(QString("bike.png")));
                 biking->insertItem(el);
              break;

              case D1000_other:
                 el = new KListViewItem(other, idx);
                 el->setPixmap(0, QPixmap::fromMimeSource(QString("other.png")));
                 other->insertItem(el);
              break;

              default:
                 el = new KListViewItem(other, idx);
                 el->setPixmap(0, QPixmap::fromMimeSource(QString("other.png")));
                 other->insertItem(el);
           }

           delete qt;
        }

        running->setOpen(true);

        if (gmn)
           garmin_free_data (gmn);

        gmn = 0;
}

/*$SPECIALIZATION$*/
void sportwatcherWidget::btFullscreenSlot()
{
        oldTransX = oldTransY = 0.0;
        mapPan.setCoords(0, 0, 0, 0);
        showTrack(0);
}

void sportwatcherWidget::btGlasMinusSlot()
{
bool sh = stateHand;

        stateHand = false;
        showTrack(zfactor - 1000);
        stateHand = sh;
}

void sportwatcherWidget::btGlasPlusSlot()
{
bool sh = stateHand;

        stateHand = false;
        showTrack(zfactor + 1000);
        stateHand = sh;
}

void sportwatcherWidget::btHandSlot()
{
QCursor cs;

        if (stateGlas)
        {
           stateGlas = false;
           btGlas->toggle();
        }
        
        stateHand = (stateHand) ? false : true;

        if (stateHand)
           cs.setShape(QCursor::PointingHandCursor);
        else
           cs.setShape(QCursor::ArrowCursor);

        imgMap->setCursor(cs);
}

void sportwatcherWidget::btGlasSlot()
{
QCursor cs;

        if (stateHand)
        {
           stateHand = false;
           btHand->toggle();
        }

        stateGlas = (stateGlas) ? false : true;

        if (stateGlas)
           cs.setShape(QCursor::ForbiddenCursor);
        else
           cs.setShape(QCursor::ArrowCursor);

        imgMap->setCursor(cs);
}

void sportwatcherWidget::btFlagSlot()
{
}

void sportwatcherWidget::liLapsSlot(QListViewItem *item)
{
QString sl;
int l;
int idx;
RUN_NODE *rn;
LAP *lap;

        if (!item)
           return;

        sl = item->text(0).mid(4, 3);
        l = sl.toInt();

        if (l <= 0)
        {
           showTrack(zfactor, mapPan, 0);
           showCurves(0);
           return;
        }

        rn = ds.getRunNode();
        idx = rn->run->first_lap_index;
        lap = ds.getLap(idx + l - 1);
        showTrack(zfactor, mapPan, lap);
        showCurves(lap);
}

void sportwatcherWidget::liActivitiesSlot(QListViewItem *item)
{
INDEX *akt;

        if (!item)
           return;

        akt = index;

        while (akt)
        {
           if (akt->activ == item->text(0))
           {
              spw.destroy();

              if (spw.setFileName(akt->path.ascii()) == -1)
                 return;

              if (gmn)
                 garmin_free_data (gmn);

              gmn = spw.readFile();
              zfactor = 0;
              showLaps();
              showTrack();
              showCurves();
              return;
           }

           akt = akt->next;
        }
}

void sportwatcherWidget::helpAbout()
{
KAboutData about("SportWatcher", "SportWatcher", VERSION);
QString ab = ("About");
QString liz = ("License");

        KAboutDialog *info = new KAboutDialog(KAboutDialog::AbtProduct|KAboutDialog::AbtTabbed,
                QString("SportWatcher"), KAboutDialog::Close, KAboutDialog::Close, 0,
                i18n("About"), false, false, QString::null, QString::null, QString::null);
        info->setProduct("SportWatcher", QString("Version %1").arg(VERSION), "Andreas Theofilu", "2007, 2008");
        KAboutContainer *infoAppl = info->addContainerPage(ab, AlignCenter, AlignCenter);
        infoAppl->addTitle(QString("SportWatcher"), AlignCenter, false, false);
//      infoAppl->addTitle(QString("(C) 2007, 2008, Andreas Theofilu <andreas@theosys.at>"));
        infoAppl->addPerson(QString("Andreas Theofilu"), QString("andreas@theosys.at"), QString("http://www.theosys.at"), NULL);
        infoAppl->addTitle(I18N_NOOP("\nRead out the data of a Garmin GPS device over USB, and visualize them on screen"));
        about.setLicense(KAboutData::License_Custom);
        about.setLicenseText(I18N_NOOP(gpl3));
        info->addLicensePage(liz, about.license(), 10);
        info->setGeometry(0, 0, 800, 400);
        info->centerOnScreen(info, 0);
        info->exec();
        delete info;
}

void sportwatcherWidget::helpContents()
{
}

void sportwatcherWidget::helpIndex()
{
}

void sportwatcherWidget::fileExit()
{
        if (mama)
           mama->close();
}

void sportwatcherWidget::filePrint()
{
}

/*
 * This function allows the user to choose a file name, where we save the
 * actual lap in Garmins own TCX format. This format is simply a XML-file.
 * For details about the schema of this file look at
 * http://developer.garmin.com/schemas/tcx/v2/
 */
void sportwatcherWidget::fileSaveAs()
{
QString fname;
QFile fn;
QString buffer;
RUN_NODE *rn, *rakt;
LAP *lap;
POINT *point;
int indent, i;
QDateTime *qt;

        if (!gmn)
        {
           KMessageBox::error(this, i18n("Currently no activity is selected!"));
           return;
        }

        fname = KFileDialog::getSaveFileName(0, QString("*.tcx"), this, QString("SportWatcher"));

        if (fname.isEmpty())
           return;

        fn.setName(fname);

        if (fn.exists())
        {
           if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
              return;
        }

        rn = ds.getRunNode();
        lap = ds.getLap(rn->run->first_lap_index);

        if ((point = ds.getPoint(lap->start_time)) == 0)
        {
           KMessageBox::error(this, i18n("No data to save!"));
           return;
        }

        if (!fn.open(IO_ReadWrite | IO_Truncate))
        {
           KMessageBox::error(this, i18n("Error creating file " + QString(fname) + "!\nPlease check permissions"));
           return;
        }

        buffer = QString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
        buffer.append("<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
        buffer.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
        buffer.append("xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 ");
        buffer.append("http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n\n");
        writeTag (fn, buffer, indent);
        buffer = QString("<folders/>\n\n");
        writeTag (fn, buffer, indent);

        // Open a course
        QFileInfo finfo(fname);
        buffer = QString("<Courses>\n   <Course>\n      <name>%1</name>\n").arg(finfo.baseName(true));
        writeTag (fn, buffer, indent);
        indent = 2;

        rakt = rn;

        while (rakt)
        {
           if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
                rakt->run->type != data_D1010)
           {
              rakt = rakt->next;
              continue;
           }

           for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
           {
              if ((lap = ds.getLap(i)) == NULL)
                 continue;

              // Write the information of the lap
              writeTag (fn, QString("<Lap>\n"), indent);
              indent++;
              buffer.sprintf("<TotalTimeSeconds>%f</TotalTimeSeconds>\n", (double)lap->total_time / 100.0);
              writeTag (fn, buffer, indent);
              buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", lap->total_distance);
              writeTag (fn, buffer, indent);

              writeTag (fn, QString("<BeginPosition>\n"), indent);
              indent++;
              buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->begin.lat));
              writeTag (fn, buffer, indent);
              buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->begin.lon));
              writeTag (fn, buffer, indent);
              indent--;
              writeTag (fn, QString("</BeginPosition>\n"), indent);

              writeTag (fn, QString("<EndPosition>\n"), indent);
              indent++;
              buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->end.lat));
              writeTag (fn, buffer, indent);
              buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->end.lon));
              writeTag (fn, buffer, indent);
              indent--;
              writeTag (fn, QString("</EndPosition>\n"), indent);

              writeTag (fn, QString("<AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
              indent++;
              buffer.sprintf("<Value>%d</Value>\n", lap->avg_heart_rate);
              writeTag (fn, buffer, indent);
              indent--;
              writeTag (fn, QString("</AverageHeartRateBpm>\n"), indent);

              writeTag (fn, QString("<MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
              indent++;
              buffer.sprintf("<Value>%d</Value>\n", lap->avg_heart_rate);
              writeTag (fn, buffer, indent);
              indent--;
              writeTag (fn, QString("</MaximumHeartRateBpm>\n"), indent);

              buffer = QString("<Intensity>%1</Intensity>\n").arg((!lap->intensity) ? "Active" : "");
              writeTag (fn, buffer, indent);
              indent--;
              writeTag (fn, QString("</Lap>\n"), indent);

              point = ds.getPoint(lap->start_time);
              writeTag (fn, QString("<Track>\n"), indent);
              indent++;

              while (point)
              {
                 if (point->time > (lap->start_time + (lap->total_time / 100)))
                    break;

                 writeTag (fn, QString("<Trackpoint>\n"), indent);
                 indent++;
                 qt = garmin_dtime(point->time);
                 buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
                 writeTag (fn, buffer, indent);
                 delete qt;
                 writeTag (fn, QString("<Position>\n"), indent);
                 indent++;
                 buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(point->posn.lat));
                 writeTag (fn, buffer, indent);
                 buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(point->posn.lon));
                 writeTag (fn, buffer, indent);
                 indent--;
                 writeTag (fn, QString("</Position>\n"), indent);

                 if (point->alt < 20000.0)
                 {
                    buffer.sprintf("<AltitudeMeters>%f</AltitudeMeters>\n", point->alt);
                    writeTag (fn, buffer, indent);
                 }

                 buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", point->distance);
                 writeTag (fn, buffer, indent);

                 if (point->heart_rate > 0 && point->heart_rate < 250)
                 {
                    writeTag (fn, QString("<HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
                    indent++;
                    buffer.sprintf("<Value>%d</Value>\n", point->heart_rate);
                    writeTag (fn, buffer, indent);
                    indent--;
                    writeTag (fn, QString("</HeartRateBpm>\n"), indent);
                 }

                 buffer.sprintf("<SensorState>%s</SensorState>\n", (!point->sensor) ? "Absent" : "");
                 writeTag (fn, buffer, indent);
                 indent--;
                 writeTag (fn, QString("</Trackpoint>\n"), indent);
                 point = ds.getPoint(point->time + 1);
              }

              indent--;
              writeTag (fn, QString("</Track>\n"), indent);
           }

           indent--;
           writeTag (fn, QString("</Course>\n"), indent);
           indent--;
           writeTag (fn, QString("</Courses>\n"), indent);
           rakt = rakt->next;
        }

        // Write information about device
        // Here my personal signature is written :-)
        writeTag (fn, QString("<Author xsi:type=\"Application_t\">\n"), indent);
        indent++;
        writeTag (fn, QString("<Name>SportWatcher</Name>\n"), indent);
        writeTag (fn, QString("<Build>\n"), indent);
        indent++;
        writeTag (fn, QString("<Version>\n"), indent);
        indent++;
        writeTag (fn, QString("<VersionMajor>0</VersionMajor>\n"), indent);
        writeTag (fn, QString("<VersionMinor>1</VersionMinor>\n"), indent);
        writeTag (fn, QString("<BuildMajor>0</BuildMajor>\n"), indent);
        writeTag (fn, QString("<BuildMinor>0</BuildMinor>\n"), indent);
        indent--;
        writeTag (fn, QString("</Version>\n"), indent);
        writeTag (fn, QString("<Type>Beta</Type>\n"), indent);
        writeTag (fn, QString("<Time>Jan 31 2008, 00:00:00</Time>\n"), indent);
        writeTag (fn, QString("<Builder>theosys</Builder>\n"), indent);
        indent--;
        writeTag (fn, QString("</Build>\n"), indent);
        writeTag (fn, QString("<LangID>EN</LangID>\n"), indent);
        writeTag (fn, QString("<PartNumber>000-00000-00</PartNumber>\n"), indent);
        indent--;
        writeTag (fn, QString("</Author>\n"), indent);
        writeTag (fn, QString("</TrainingCenterDatabase>\n"), indent);

        fn.close();
        KMessageBox::information(this, i18n("File ") + fname + i18n(" was written successfully."));
}

void sportwatcherWidget::fileSave()
{
}

void sportwatcherWidget::fileOpen()
{
QString fname = KFileDialog::getOpenFileName(Data, QString("*.gmn"), this, QString("SportWatcher"));
int m;

        if (fname.isEmpty())
           return;

        spw.destroy();

        if (spw.setFileName(fname.ascii()) == -1)
           return;

        if (gmn)
           garmin_free_data (gmn);

        gmn = spw.readFile();
        zfactor = 0;

        if ((m = garmin_count_error()) > 0)
        {
        int i, key = -1;

           for (i = 0; i < m; i++)
              KMessageBox::error(this, QString(garmin_get_next_error(&key)));

           garmin_clear_errors();
           return;
        }

        showLaps();
        showTrack();
        showCurves();
}

void sportwatcherWidget::fileNew()
{
progressWidget *dlg = new progressWidget(this, "progressWidgetBase");

        dlg->show();

        if (!dlg->Download())
        {
        int m, key;

           key = -1;

           for (m = 0; m < garmin_count_error(); m++)
              KMessageBox::error(this, QString(garmin_get_next_error(&key)));
        }
        else
           getActivities();

        garmin_clear_errors();
        delete dlg;
}

/*
 * This function is called, when the user clicks at the menu point
 * "Save Heart Rate".
 * First, a file dialog box is displayed, where the user can choose a
 * directory and a file name to save the heart rate.
 * If the file could successfully be created, the heart rate is saved
 * in the "HRM"-format. This is the native format Polar uses to store
 * heart rate data. I've choosen this format, because it's popular and
 * used by many other software too.
 */
void sportwatcherWidget::extrasSaveHR()
{
QString fname, str1, str2;
QFile fdfile;
QDateTime *qt, *oldqt;
QDate dat;
QTime t;
QDir dir = QDir::home();
char hv0[256];
RUN_NODE *rn;
LAP *lap, *alap;
POINT *point;
int samples, smp, seconds, anz, nsec, samsec;
int avgHeart, minHeart, maxHeart, aktHeart;
int secRange1, secRange2, secRange3, secAbove, secBeyond;

        if (!gmn)
        {
           KMessageBox::information(this, i18n("There is no activity open"));
           return;
        }

        if (HRM.isEmpty())
           str1 = dir.path();
        else
           str1 = HRM;

        str1 +=  "/" + StartTime.toString("yyyyMMddThhmmss.zzz.hrm");
        fname = KFileDialog::getSaveFileName(str1, QString("*.hrm"), this, QString("SportWatcher"));

        if (fname.isEmpty())
           return;

        fdfile.setName(fname);

        if (fdfile.exists())
        {
           if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
              return;
        }

        if (!fdfile.open(IO_ReadWrite | IO_Truncate))
        {
           KMessageBox::error(this, i18n("Error creating a file!\nPlease check permissions."));
           return;
        }

        rn = ds.getRunNode();
        lap = ds.getLap(rn->run->first_lap_index);
        t = StartTime.time();
        dat = StartTime.date();

        if ((point = ds.getPoint(lap->start_time)) == 0)
        {
           fdfile.close();
           return;
        }

        strcpy (hv0, "[Params]\n");
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        str1 = dat.toString("yyyyMMdd");
        str2 = t.toString("hh:mm:ss.z");
        sprintf(hv0, "Version=106\nMonitor=11\nSMode=000000000\nDate=%s\nStartTime=%s\n",
                str1.ascii(), str2.ascii());
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        t.setHMS(0, 0, 0);
        t = t.addSecs(max_time);
        str2 = t.toString("hh:mm:ss.z");

        switch (sampleTime)
        {
           case 0: samsec = 5; break;
           case 1: samsec = 15; break;
           case 2: samsec = 30; break;
           case 3: samsec = 60; break;
           default:
              samsec = 15;
        }

        sprintf(hv0, "Length=%s\nInterval=%d\nUpper1=%d\nLower1=%d\nUpper2=%d\n",
           str2.ascii(), samsec, upper1, lower1, upper2);
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        sprintf(hv0, "Lower2=%d\nUpper3=%d\nLower3=%d\nTimer1=00:00:00.0\n",
                lower2, upper3, lower3);
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        strcpy(hv0, "Timer2=00:00:00.0\nTimer3=00:00:00.0\nActiveLimit=0\n");
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        sprintf(hv0, "MaxHR=%d\nRestHR=%d\nStartDelay=0\nVO2max=%d\nWeight=%d\n\n",
                MaxHr, restHr, vo2max, weight);
        write(fdfile.handle(), &hv0[0], strlen(hv0));

        // Write the intervall times. One block for every lap
        secRange1 = secRange2 = secRange3 = secAbove = secBeyond = 0;
        strcpy(hv0, "[IntTimes]\n");
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        t.setHMS(0, 0, 0);

        for (unsigned int i = rn->run->first_lap_index; i < rn->run->last_lap_index; i++)
        {
           alap = ds.getLap(i);
           point = ds.getPoint(alap->start_time);
           oldqt = garmin_dtime(point->time);
           avgHeart = minHeart = maxHeart = aktHeart = 0;
           anz = 0;
           unsigned long lastTime = point->time;
           int totSec = 0;

           while (point)
           {
              if (point->time > (alap->start_time + (alap->total_time / 100)))
                 break;

              if (point->heart_rate > 0)
              {
                 avgHeart += point->heart_rate;
                 nsec = point->time - lastTime;
                 totSec += nsec;

                 if (minHeart == 0 || minHeart > point->heart_rate)
                    minHeart = point->heart_rate;

                 if (maxHeart < point->heart_rate)
                    maxHeart = point->heart_rate;

                 if (aktHeart == 0 && totSec >= samsec)
                    aktHeart = avgHeart / (anz + 1);

                 if (point->heart_rate < lower1)
                    secBeyond += nsec;
                 else if (point->heart_rate < lower2)
                    secRange1 += nsec;
                 else if (point->heart_rate < lower3)
                    secRange2 += nsec;
                 else if (point->heart_rate < upper3)
                    secRange3 += nsec;
                 else
                    secAbove += nsec;

                 lastTime = point->time;
                 anz++;
              }

              point = ds.getPoint(point->time+1);
           }

           t = t.addSecs(alap->total_time / 100);
           str1 = t.toString("hh:mm:ss.z");
           sprintf(hv0, "%s\t %d\t %d\t %d\t %d\n",
              str1.ascii(), aktHeart, minHeart, avgHeart / anz, maxHeart);
           write(fdfile.handle(), &hv0[0], strlen(hv0));
           strcpy(hv0, "32\t 0\t 0\t 0\t 0\t 0\n");
           write(fdfile.handle(), &hv0[0], strlen(hv0));
           strcpy(hv0, "0\t 0\t 0\t 0\t 0\n");
           write(fdfile.handle(), &hv0[0], strlen(hv0));
           sprintf(hv0, "0\t %d\t 0\t 0\t 0\t 0\n", (int)alap->total_distance);
           write(fdfile.handle(), &hv0[0], strlen(hv0));
           strcpy(hv0, "0\t 0\t 0\t 0\t 0\t 0\n");
           write(fdfile.handle(), &hv0[0], strlen(hv0));
        }

        strcpy(hv0, "\n[IntNotes]\n\n[ExtraData]\n\n");
        write(fdfile.handle(), &hv0[0], strlen(hv0));

        strcpy(hv0, "[Summary-123]\n");
        write(fdfile.handle(), &hv0[0], strlen(hv0));   // Time limits 1
        smp = max_time - secBeyond - secRange1 - secRange2 - secRange3 - secAbove;
        sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
                max_time, secRange1, secRange2 + secRange3,
                secAbove + secBeyond, smp);
        write(fdfile.handle(), &hv0[0], strlen(hv0));   // limits 1
        sprintf(hv0, "%d\t %d\t %d\t %d\n",
                MaxHr, upper1, lower1, restHr);
        write(fdfile.handle(), &hv0[0], strlen(hv0));   // Time limits 1
        sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
                max_time, secRange2, secRange1 + secRange3,
                secAbove + secBeyond, smp);
        write(fdfile.handle(), &hv0[0], strlen(hv0));   // limits 2
        sprintf(hv0, "%d\t %d\t %d\t %d\n",
                MaxHr, upper2, lower2, restHr);
        write(fdfile.handle(), &hv0[0], strlen(hv0));   // Time limits 2
        sprintf(hv0, "%u\t %u\t %u\t %u\t %u\n",
                max_time, secRange3, secRange1 + secRange2,
                secAbove + secBeyond, smp);
        write(fdfile.handle(), &hv0[0], strlen(hv0));   // limits 3
        sprintf(hv0, "%d\t %d\t %d\t %d\n",
                MaxHr, upper3, lower3, restHr);
        write(fdfile.handle(), &hv0[0], strlen(hv0));   // Time limits 3
        samples = max_time / samsec;
        sprintf(hv0, "0\t %u\n\n", samples);    // samples
        write(fdfile.handle(), &hv0[0], strlen(hv0));

        strcpy(hv0, "[Summary-TH]\n");
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        sprintf(hv0, "%u\t 0\t %u\t %d\t %d\t 0\n", max_time, max_time - max_hr - restHr, max_hr, restHr);
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        sprintf(hv0, "%d\t %d\t %d\t %d\n", MaxHr, upper3, lower1, restHr);
        write(fdfile.handle(), &hv0[0], strlen(hv0));
        sprintf(hv0, "0\t %u\n\n", samples);    // samples
        write(fdfile.handle(), &hv0[0], strlen(hv0));

        sprintf(hv0, "[HRZones]\n%d\n%d\n%d\n%d\n%d\n%d\n0\n0\n0\n0\n0\n\n",
                MaxHr, upper3, upper2, upper1, lower1, restHr);
        write(fdfile.handle(), &hv0[0], strlen(hv0));

        strcpy(hv0, "[HRData]\n");
        write(fdfile.handle(), &hv0[0], strlen(hv0));

        smp = 0;                // average heart rate of 15 seconds
        seconds = 0;
        anz = 0;
        nsec = samsec;
        oldqt = garmin_dtime(lap->start_time);
        qt = 0;
        point = ds.getPoint(lap->start_time);

        while (point)
        {
           if (seconds >= nsec)
           {
              if (anz > 0)
              {
                 sprintf(hv0, "%d\n", smp / anz);
                 write(fdfile.handle(), &hv0[0], strlen(hv0));
              }

              if (smp > 0 && seconds >= (nsec + samsec))
              {
                 if (anz <= 0)
                    anz = 0;

                 for (int x = nsec; x < seconds; x += samsec)
                 {
                    sprintf(hv0, "%d\n", smp / anz);
                    write(fdfile.handle(), &hv0[0], strlen(hv0));
                    nsec += samsec;
                 }
              }
                
              anz = 0;
              smp = 0;
              nsec += samsec;
           }

           qt = garmin_dtime (point->time);
           seconds += oldqt->secsTo(*qt);

           if (point->heart_rate > 0)
           {
              smp += point->heart_rate;
              anz++;
           }

           delete oldqt;
           oldqt = qt;
           point = ds.getPoint(point->time + 1);
        }

        fdfile.close();
        KMessageBox::information(this, i18n("File successfully written."));
}

void sportwatcherWidget::extrasSettings()
{
settingsWidget *dlg = new settingsWidget(this, "settingsWidgetBase", TRUE, 0);

        if (dlg->exec() == QDialog::Accepted)
        {
           KSimpleConfig *cfg = new KSimpleConfig(QString("sportwatcher.rc"), true);
           cfg->setGroup(QString("SportWatcher"));
           lower1 = cfg->readNumEntry("lower1");
           lower2 = cfg->readNumEntry("lower2");
           lower3 = cfg->readNumEntry("lower3");
           upper1 = cfg->readNumEntry("upper1");
           upper2 = cfg->readNumEntry("upper2");
           upper3 = cfg->readNumEntry("upper3");
           MaxHr = cfg->readNumEntry("maxHr");
           restHr = cfg->readNumEntry("restHr");
           vo2max = cfg->readNumEntry("vo2max");
           weight = cfg->readNumEntry("weight");
           sampleTime = cfg->readNumEntry("seconds");
           Serial = cfg->readBoolEntry("Serial");
           Device = cfg->readEntry("Device");
           Data = cfg->readEntry("Data");
           HRM = cfg->readEntry("HRM");
           Units = cfg->readNumEntry("Units");
           delete cfg;
        }

        delete dlg;
}

/*
 * Functions to fill in the boxes of the main mask.
 */
void sportwatcherWidget::showLaps()
{
QString qs_name, qs_distance, qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed;
QString qs_calories, qs_avghr, qs_maxhr, qs_avgcadence, qs_ascent, qs_descent;
QDateTime dt;
QTime t, st;
QDateTime *qt;
LAP *lap;
POINT *point;
RUN_NODE *rakt, *rn;
int laps, i, anz, men, cad;
double alt_asc, alt_dsc, sum_asc, sum_dsc, old_asc, old_dsc;

        if (!gmn)
        {
           KMessageBox::error(0, i18n("No data were loaded!"));
           return;
        }

        if (gmn->type == data_Dnil)
        {
           KMessageBox::error(0, i18n("No data found!"));
           return;
        }

        if (gmn->type != data_Dlist)     /* List of data */
        {
           KMessageBox::error(0, QString("Found unexpected data type %1!").arg(gmn->type));
           return;
        }

        ds.destroy();
        min_hr = max_hr = 0;
        min_height = max_height = 0.0;
        liLaps->clear();
        liLaps->setAllColumnsShowFocus(true);
        ds.garmin_print_data(gmn);
        rn = ds.getRunNode();
        rakt = rn;
        liLaps->setRootIsDecorated(true);
        liLaps->setAlternateBackground(KGlobalSettings::alternateBackgroundColor());
        liLaps->setColumnAlignment(1, Qt::AlignRight);
        liLaps->setColumnAlignment(2, Qt::AlignRight);
        liLaps->setColumnAlignment(3, Qt::AlignRight);
        liLaps->setColumnAlignment(4, Qt::AlignRight);
        liLaps->setColumnAlignment(5, Qt::AlignRight);
        liLaps->setColumnAlignment(6, Qt::AlignRight);
        liLaps->setColumnAlignment(7, Qt::AlignRight);
        liLaps->setColumnAlignment(8, Qt::AlignRight);
        liLaps->setColumnAlignment(9, Qt::AlignRight);
        liLaps->setColumnAlignment(10, Qt::AlignRight);
        liLaps->setColumnAlignment(11, Qt::AlignRight);

        qs_name = qs_distance = qs_etime = qs_avgpace = qs_avgspeed = qs_maxspeed = QString("");
        qs_calories = qs_avghr = qs_maxhr = qs_avgcadence = qs_ascent = qs_descent = QString("");
        men = 0;
        cad = 0;

        while (rakt)
        {
           if (rakt->run->type == data_D1000 || rakt->run->type == data_D1009 ||
                rakt->run->type == data_D1010)
           {
           int lt, cal, ahr, mhr;
           double distance, speed, mspeed;
           QDate dat;

              switch (rakt->run->sport_type)
              {
                 case D1000_running: qs_name = QString("Running: "); break;
                 case D1000_biking:  qs_name = QString("Biking: "); break;
                 case D1000_other:   qs_name = QString("Other: "); break;
                 default:
                    qs_name = QString("Unknown: ");
              }

              lap = ds.getLap(rakt->run->first_lap_index);
              qt = garmin_dtime (lap->start_time);
              StartTime = *qt;
              st = qt->time();
              dat = qt->date();
              delete qt;
              lap = ds.getLap(rakt->run->last_lap_index);
              qt = garmin_dtime (lap->start_time);
              t = qt->addSecs(lap->total_time / 100).time();
              lt = st.secsTo(t);
              t.setHMS(0, 0, 0);
              t = t.addSecs(lt);
              qt->setDate(dat);
              qt->setTime(t);
              qs_name.append(dat.toString("dd.MM.yyyy"));
              qs_name.append(" ");
              qs_name.append(st.toString("hh:mm:ss"));
              max_time = lt;
              qs_etime = QString(qt->toString("hh:mm:ss.zzz"));

              distance = 0.0;
              cal = 0;
              mspeed = 0;
              ahr = mhr = 0;
              anz = 0;
              cad = 0;
              sum_asc = sum_dsc = old_asc = old_dsc = 0;

              for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
              {
                 if ((lap = ds.getLap(i)) == NULL)
                    continue;

                 distance += lap->total_distance;
                 cal += lap->calories;

                 if (lap->avg_cadence != 0xff)
                    cad += lap->avg_cadence;

                 ahr += lap->avg_heart_rate;
                 anz++;

                 if (lap->max_speed > mspeed)
                    mspeed = lap->max_speed;

                 if (lap->max_heart_rate > mhr)
                    mhr = lap->max_heart_rate;
              }

              if (Units == 1)           // Statute?
                 qs_distance.sprintf("%.2f ft", distance / 0.304);
              else
                 qs_distance.sprintf("%.2f m", distance);

              total_distance = distance;

              if (distance > 0)
              {
                 QTime tt = qt->time();
                 long secs = (double)(tt.hour() * 3600 + tt.minute() * 60 + tt.second()) / 100.0 * (1000.0 / distance * 100.0);
                 int h = secs / 3600;
                 int m = (secs - (h * 3600)) / 60;
                 int s = secs - ((h * 3600) + (m * 60));
                 t = QTime(h, m, s, 0);
                 qs_avgpace = t.toString("  hh:mm:ss");
//               qs_avgpace.append(QString(" /lap"));
              }

              if (Units == 1)           // Statute?
                 speed = distance / lt;
              else
                 speed = distance / lt * 3.6;

              qs_avgspeed.sprintf("%.2f %s", speed, (Units == 1) ? "mi/h" : "km/h");
              qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? mspeed : mspeed * 3.6, (Units == 1) ? "mi/h" : "km/h");
              qs_calories.sprintf("%d", cal);
              qs_avghr.sprintf("%d bpm", ahr / anz);
              qs_maxhr.sprintf("%d bpm", mhr);

              if (cad > 0)
                 qs_avgcadence.sprintf("%d", cad / anz);

              KListViewItem *element = new KListViewItem(liLaps, qs_name, qs_distance,
                qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed, qs_calories, qs_avghr);
              element->setText(8, qs_maxhr);
              element->setText(9, qs_avgcadence);
              element->setText(10, qs_ascent);
              element->setText(11, qs_descent);
              element->sortChildItems(0, false);
              element->setOpen(true);
              element->setPixmap(0, QPixmap::fromMimeSource(QString("activity.png")));
              liLaps->insertItem(element);
              delete qt;
              /* Get the laps. */
              laps = 1;

              for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
              {
                 double spd;
                 char *un;

                 if ((lap = ds.getLap(i)) == NULL)
                    continue;

                 qt = garmin_dtime (lap->start_time);
                 qs_name.sprintf("Lap %03d - ", laps);
                 qs_name.append(qt->toString("hh:mm:ss"));
                 qs_distance.sprintf("%.2f %s", (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance, (Units == 1) ? "ft" : "m");
                 t = QTime(0, 0, 0, 0);
                 t = t.addMSecs(lap->total_time * 10);
                 qs_etime = t.toString("hh:mm:ss.zzz");
                 spd = lap->total_distance / (lap->total_time / 100.0);

                 if (Units == 0)
                 {
                    un = (char *)"km/h";
                    spd *= 3.6;
                 }
                 else
                    un = (char *)"mi/h";

                 qs_avgspeed.sprintf("%.2f %s", spd, un);
                 qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? lap->max_speed : lap->max_speed * 3.6, un);
                 qs_calories.sprintf("%d", lap->calories);

                 if (lap->total_distance > 0 && lap->total_time != 0)
                 {
                    long secs = (double)lap->total_time / 10000.0 * (1000.0 / lap->total_distance * 100.0);
                    int h = secs / 3600;
                    int m = (secs - (h * 3600)) / 60;
                    int s = secs - ((h * 3600) + (m * 60));
                    t = QTime(h, m, s, 0);
                    qs_avgpace = t.toString("hh:mm:ss");
//                  qs_avgpace.append(QString(" /lap"));
                 }

                 qs_avghr.sprintf("%d bpm", lap->avg_heart_rate);
                 qs_maxhr.sprintf("%d bpm", lap->max_heart_rate);

                 anz = 0;
                 alt_asc = alt_dsc = 0;

                 if ((point = ds.getPoint(lap->start_time)) != 0)
                 {
                    if (point->alt < 20000)
                    {
                       alt_dsc = alt_asc = point->alt;

                       if (old_asc == 0)
                          old_asc = alt_asc;

                       if (old_dsc == 0)
                          old_dsc = alt_dsc;
                    }
                    else
                       alt_dsc = alt_asc = 0;

                    while (point)
                    {
                       if (point->time > (lap->start_time + (lap->total_time / 100)))
                         break;

                       if (point->alt > alt_asc && point->alt < 20000)
                       {
                          alt_asc = point->alt;

                          if (alt_dsc == 0)
                             alt_dsc = point->alt;
                       }

                       if (point->alt < alt_dsc)
                          alt_dsc = point->alt;

                       // save the min and max values. We need this information to
                       // build the graphics.
                       if (point->heart_rate > max_hr)
                          max_hr = point->heart_rate;

                       if ((min_hr == 0 && point->heart_rate > 0) || (point->heart_rate > 0 && point->heart_rate < min_hr))
                          min_hr = point->heart_rate;

                       if (point->alt < 20000 && max_height < point->alt)
                          max_height = point->alt;

                       if (point->alt < 20000 && (min_height == 0.0 || min_height > point->alt))
                          min_height = point->alt;

                       if (point->heart_rate > 0)
                       {
                          avg_hr += point->heart_rate;
                          men++;
                       }

                       point = ds.getPoint(point->time + 1);
                    }

                    if (old_asc < alt_asc)
                       sum_asc += (alt_asc - old_asc);

                    if (old_dsc > alt_dsc)
                       sum_dsc += (old_dsc - alt_dsc);

                    old_asc = alt_asc;
                    old_dsc = alt_dsc;
                    qs_ascent.sprintf("%.2f %s", (Units == 1) ? (alt_asc / 0.304) : alt_asc, (Units == 1) ? "ft" : "m");
                    qs_descent.sprintf("%.2f %s", (Units == 1) ? (alt_dsc / 0.304) : alt_dsc, (Units == 1) ? "ft" : "m");
                 }

                 if (lap->avg_cadence != 0xff)
                    qs_avgcadence.sprintf("%d", lap->avg_cadence);

                 KListViewItem *edetail = new KListViewItem(element, qs_name, qs_distance,
                        qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed, qs_calories, qs_avghr);
                 edetail->setText(8, qs_maxhr);
                 edetail->setText(9, qs_avgcadence);
                 edetail->setText(10, qs_ascent);
                 edetail->setText(11, qs_descent);
                 edetail->setPixmap(0, QPixmap::fromMimeSource(QString("history.png")));

                 switch (rakt->run->sport_type)
                 {
                    case D1000_running: edetail->setPixmap(0, QPixmap::fromMimeSource(QString("run.png"))); break;
                    case D1000_biking:  edetail->setPixmap(0, QPixmap::fromMimeSource(QString("bike.png"))); break;
                    case D1000_other:   edetail->setPixmap(0, QPixmap::fromMimeSource(QString("other.png"))); break;
                    default:
                       edetail->setPixmap(0, QPixmap::fromMimeSource(QString("other.png")));
                 }

                 liLaps->clearSelection();
                 element->insertItem(edetail);
                 delete qt;
                 laps++;
              }

              qs_ascent.sprintf("%.2f %s", (Units == 1) ? sum_asc / 0.304 : sum_asc, (Units == 1) ? "ft" : "m");
              qs_descent.sprintf("%.2f %s", (Units == 1) ? sum_dsc / 0.304 : sum_dsc, (Units == 1) ? "ft" : "m");
              element->setText(10, qs_ascent);
              element->setText(11, qs_descent);
           }

           rakt = rakt->next;
        }

        if (men > 0)
           avg_hr /= men;
}

void sportwatcherWidget::showTrack()
{
        showTrack(0, QRect(0, 0, 0, 0), 0);
}

void sportwatcherWidget::showTrack(int zoom)
{
        showTrack(zoom, mapPan, mapLap);
}

void sportwatcherWidget::showTrack(int zoom, const QRect &pan, LAP *lap)
{
int width, height;
double x1, y1, x2, y2;
int a, top, left, panX, panY;
uint32 i;
double coordW, coordH, tick;
double meterW, dist;
QPainter paint;
posn_type posNW, posSE;
POINT *point;

        if (!gmn)
           return;

        if (zoom != zfactor)
           zfactor = zoom;

        if (mapLap != lap)
           mapLap = lap;

        width = imgMap->width() - 2;
        height = imgMap->height();
        pmMap.resize(width, height);
        paint.begin(&pmMap);

        panX = panY = 0;

        if (stateHand && mapPan != pan)
        {
           mapPan = pan;
           panX = mapPan.right() - mapPan.left();
           panY = mapPan.bottom() - mapPan.top();
           oldTransX += (double)panX;
           oldTransY += (double)panY;
//         paint.translate(oldTransX, oldTransY);
        }

        memset(&posNW, 0, sizeof(posn_type));
        memset(&posSE, 0, sizeof(posn_type));

        posSE.lat = 9999.0;
        posNW.lon = 9999.0;

        /*
         * Find out the corners of our track (NW, NE, SE, SW)
         */
        if (mapLap)
           i = mapLap->start_time;
        else
           i = 0;
        
        while ((point = ds.getPoint(i)) != 0)
        {
           if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
              break;

           if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
           {
              i = point->time + 1;
              continue;
           }

           if (SEMI2DEG(point->posn.lat) > posNW.lat)
              posNW.lat = SEMI2DEG(point->posn.lat);

           if (SEMI2DEG(point->posn.lat) < posSE.lat)
              posSE.lat = SEMI2DEG(point->posn.lat);

           if (SEMI2DEG(point->posn.lon) < posNW.lon)
              posNW.lon = SEMI2DEG(point->posn.lon);

           if (SEMI2DEG(point->posn.lon) > posSE.lon)
              posSE.lon = SEMI2DEG(point->posn.lon);

           i = point->time + 1;
        }

        coordW = (posNW.lon > posSE.lon) ? posNW.lon - posSE.lon : posSE.lon - posNW.lon;
        coordH = (posNW.lat > posSE.lat) ? posNW.lat - posSE.lat : posSE.lat - posNW.lat;
        meterW = ds.earth_distance(posNW.lat, posNW.lon, posNW.lat, posSE.lon);

        // define the ticks to translate the GPS coordinates into pixels.
        // The track should be centered and we have to calculate the
        // rectangular within we draw the track.
        if (width > height)
        {
           if (coordW > coordH)
              tick = (double)height / coordW + (double)zoom;
           else
              tick = (double)height / coordH + (double)zoom;

           left = (width - tick * coordW) / 2;
           top = (height - tick * coordH) / 2;
        }
        else
        {
           if (coordW > coordH)
              tick = (double)width / coordW + (double)zoom;
           else
              tick = (double)width / coordH + (double)zoom;

           left = (width - tick * coordW) / 2;
           top = (height - tick * coordH) / 2;
        }

        a = tick * coordW;
        dist = meterW / a;

        /*
         * If we have a map file, we try to read it and if successfull,
         * we should get a map painted.
         */
/*      if (!MAP.isEmpty())
        {
           imgmap imp(MAP);
           wgs84 wgs(posNW.lon, posNW.lat, posSE.lon, posSE.lat);

           imp.getRegion(&paint, wgs);
           imp.getLabels(&paint, wgs);
        }
*/
        // Colors and fonts
        QColor background(220, 220, 220);
        QColor red(255, 0, 0);
        QColor black(0, 0, 0);
        QColor yellow(255, 255, 0);
        QFont fntNormal("Helvetica");
        fntNormal.setPixelSize(10);
        fntNormal.setStyleHint(QFont::Helvetica);
        QPen dot(red, 4, QPen::SolidLine);
        QPen line(black, 1, QPen::SolidLine);
        QPen yline(yellow, 3, QPen::SolidLine);
        // Fill background with background colors
        paint.fillRect(0, 0, width+2, height+2, background);

        paint.setPen(line);
        paint.drawLine(10, height - 9, 10, height - 4);
        paint.drawLine(10, height - 4, 10 + (1000 / dist), height - 4);
        paint.drawLine(10 + (1000 / dist), height - 9, 10 + (1000 / dist), height - 4);
        paint.setFont(fntNormal);
        paint.drawText(10, height - 10, QString("1000 m"));

        // Draw track
        if (mapLap)
           i = mapLap->start_time;
        else
           i = 0;
        
        x1 = y1 = 0.0;

        while ((point = ds.getPoint(i)) != 0)
        {
           if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
              break;

           if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
           {
              i = point->time + 1;
              continue;
           }

           x2 = (left + ((posNW.lon - SEMI2DEG(point->posn.lon)) * tick * -1)) + oldTransX;
           y2 = (top + ((posNW.lat - SEMI2DEG(point->posn.lat)) * tick)) + oldTransY;

           if (x1 == 0.0 && y1 == 0.0)
           {
              x1 = x2;
              y1 = y2;
           }

           paint.setPen(yline);
           paint.drawLine(x1, y1, x2, y2);

           if ((point->distance - dist) >= 1000.0)      // a dot at every 1000 meters
           {
              paint.setPen(dot);
              paint.drawEllipse(x2-1, y2-1, 2, 2);
              dist = (int)(point->distance / 1000) * 1000.0;
           }

           paint.setPen(line);
           paint.drawLine(x1, y1, x2, y2);
           x1 = x2;
           y1 = y2;
           i = point->time + 1;
        }

        paint.end();
        imgMap->setPixmap(pmMap);
}

void sportwatcherWidget::showCurves()
{
        showCurves (mapLap);
}

void sportwatcherWidget::showCurves(LAP *lap)
{
QPainter paint;
int width, height;
int i, secs;
int lineHeight, margin_left, margin_right, margin_bottom;
int x1, y1, x2, y2;             // Coordinates
bool meter;
double maxHeight, minHeight;
int maxHr, minHr, rh;
POINT *point;
RUN_NODE *rn;
LAP *lp;
double w_tick, h_tick;          // Number of pixels one "tick" has;
                                // This depends on the width and height
                                // of the image.
        // First we draw a grid based on the min and max
        // values detected in the function showLap(). In case
        // all values are 0, we exit here.
        if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
           return;

        width = imgProfile->width() - 2;
        height = imgProfile->height();
        pmProfile.resize(width, height);
        paint.begin(&pmProfile);

        // we need a somewhat bigger area to draw our curves than
        // we have with the real min and max values.
        if (max_height > 0.0)
        {
        double add = (max_height - min_height) / 100.0 * 5.0;   // Percent

           maxHeight = max_height + add;
           minHeight = min_height - add;

           if (minHeight < 0.0)         // make sure, we are not too deep
              minHeight = 0.0;
        }

        if (max_hr > 0)
        {
           maxHr = max_hr + 10;
           minHr = min_hr - 10;

           if (minHr < 0)
              minHr = 0;
        }

        // Define colors
        QColor background(220, 220, 220);       // Background of graphic
        QColor mark(255, 255, 255);             // Lines inside curve area
        QColor hlight(180, 180, 180);           // area of current lap
//      hlight.setAlpha(128);                   // 50% transparent
        QColor frame(0, 0, 0);                  // Text and borders
        QColor barcol(151, 190, 13);            // heart rate
        QColor barcol2(190, 151, 13);           // height over NN
        QColor red(220, 128, 128);
        QColor blue(0, 0, 240);
        QFont fntNormal("Helvetica");
//      QFont fntBold("Helvetica", 10, QFont::Bold);
        fntNormal.setPixelSize(10);
        fntNormal.setStyleHint(QFont::Helvetica);
//      fntBold.setPixelSize(10);
//      fntBold.setStyleHint(QFont::Helvetica);
        // Calculate ticks
        margin_left = 52;
        margin_right = 40;
        margin_bottom = 12;
        lineHeight = 10;
        rh = height - margin_bottom - 1;

        w_tick = (double)(width - (margin_left + margin_right)) / max_time;     // 1 tick = 1 second

        if ((maxHeight - minHeight) > (double)(maxHr - minHr))
        {
           h_tick = (double)rh / (maxHeight - minHeight);               // 1 tick = 1 meter
           meter = true;
        }
        else
        {
           h_tick = (double)rh / ((double)maxHr - (double)minHr);       // 1 tick = 1 bpm
           meter = false;
        }
        
        // Fill background with background colors
        paint.fillRect(0, 0, width + 4, height + 4, background);
        // Draw a grid with markers at every 10 minutes
        paint.setPen(QPen(frame, 1, QPen::SolidLine));
        // Bottom border line
        x1 = margin_left;
        y1 = height - margin_bottom;
        x2 = width - margin_right;
        y2 = y1;
        paint.drawLine(x1, y1, x2, y2);
        // Left border line
        x1 = x2 = margin_left;
        y1 = 2;
        y2 = height - margin_bottom;
        paint.drawLine(x1, y1, x2, y2);
        // right border line
        x1 = x2 = width - margin_right;
        paint.drawLine(x1, y1, x2, y2);

        // Draw some darker lines to show the laps, if we have one
        // and, in case we have a given lap, we fill the area.
        QDateTime *qt;
        QTime zeit = StartTime.time();
        rn = ds.getRunNode();
        paint.setPen(QPen(hlight, 1, QPen::SolidLine));

        for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
        {
           if ((lp = ds.getLap(i)) == NULL)
              continue;

           qt = garmin_dtime(lp->start_time);
           secs = zeit.secsTo(qt->time());
           delete qt;
           x1 = secs * w_tick + margin_left + 1;

           if (lap && lp->start_time == lap->start_time)
              paint.fillRect(x1, 2, (int)((double)lap->total_time / 100.0 * w_tick), height - margin_bottom - 2, hlight);
           else
              paint.drawLine(x1, 2, x1, height - margin_bottom);
        }

        // Grid vertical
        paint.setPen(QPen(frame, 1, QPen::SolidLine));
        paint.setFont(fntNormal);
        paint.drawText(margin_left - 20, height - lineHeight, 40, lineHeight, Qt::AlignCenter, QString("00:00"));
        paint.save();
        paint.rotate(270);
        paint.setPen(QPen(barcol, 1, QPen::SolidLine));
        paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
        paint.setPen(QPen(blue, 1, QPen::SolidLine));
        paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
        paint.restore();
        paint.setPen(QPen(mark, 1, QPen::SolidLine));
        // Draw the time scale
        for (i = 0; (unsigned int)i < max_time; i++)
        {
           if (i > 0 && !(i % 600))     // every 10 minutes
           {
              x1 = x2 = margin_left + w_tick * i;

              if (x1 == (width - margin_right))
                 continue;

              y1 = 2;
              y2 = height - margin_bottom;
              paint.drawLine(x1, y1, x2, y2);
              QTime tm(0, 0, 0);
              tm = tm.addSecs(i);
              paint.setPen(QPen(frame, 1, QPen::SolidLine));
              paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((i >= 3600) ? "hh:mm:ss" : "mm:ss"));
              paint.setPen(QPen(mark, 1, QPen::SolidLine));
           }
        }
        
        QTime tm(0, 0, 0);
        QString qs;
        tm = tm.addSecs(max_time);
        paint.setPen(QPen(frame, 1, QPen::SolidLine));
        paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((max_time >= 3600) ? "hh:mm:ss" : "mm:ss"));

        if (max_height > 0.0)
        {
           paint.setPen(QPen(barcol, 1, QPen::SolidLine));
           paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
        }
        
        if (max_hr > 0)
        {
           paint.setPen(QPen(blue, 1, QPen::SolidLine));
           paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
        }

        paint.setPen(QPen(mark, 1, QPen::SolidLine));

        // Grid horizontal
        int factor = (meter) ? (maxHeight - minHeight) / (rh / 12) : (maxHr - minHr) / (rh / 12);
        int target = (meter) ? (int)(maxHeight - minHeight) : (maxHr - minHr);
        int oldy = height;

        for (i = 0; i < target; i++)
        {
           if (i > 0 && !(i % factor))
           {
              x1 = margin_left + 1;
              x2 = width - margin_right - 1;
              y1 = y2 = rh - h_tick * i;

              if (y1 < 12)
                 break;

              paint.drawLine(x1, y1, x2, y2);

              if (y1 < (oldy - lineHeight))
              {
                 if (meter)
                 {
                    paint.setPen(QPen(barcol, 1, QPen::SolidLine));
                    paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));

                    if (maxHr > 0)
                    {
                       double hrscale = (double)(maxHr - minHr) / (double)target;
                       paint.setPen(QPen(blue, 1, QPen::SolidLine));
                       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", (int)((double)minHr + hrscale * (double)i)));
                    }
                 }
                 else
                 {
                    paint.setPen(QPen(blue, 1, QPen::SolidLine));
                    paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));

                    if (max_height > 0)
                    {
                       double hrscale = (maxHeight - minHeight) / (double)target;
                       paint.setPen(QPen(barcol, 1, QPen::SolidLine));
                       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight + hrscale * (double)i));
                    }
                }

                 paint.setPen(QPen(mark, 1, QPen::SolidLine));
                 oldy = y1;
              }
           }
        }

        // To make out graphics more beautiful, we draw lines for the
        // heart rate limits and the average heart rate.
        if (max_hr > 0)
        {
        int ay1, ay2, ay3, ay4, ay5;

           x1 = margin_left + 1;
           x2 = width - margin_right - 1;

           if (meter)
           {
              double hrscale = rh / (double)(maxHr - minHr);
              ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
              ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
              ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
              ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
              ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
           }
           else
           {
              ay1 = (double)rh - (double)(lower1 - minHr) * h_tick;
              ay2 = (double)rh - (double)(lower2 - minHr) * h_tick;
              ay3 = (double)rh - (double)(lower3 - minHr) * h_tick;
              ay4 = (double)rh - (double)(upper3 - minHr) * h_tick;
              ay5 = (double)rh - (double)(avg_hr - minHr) * h_tick;
           }

           paint.setPen(QPen(barcol2, 1, QPen::DashLine));      // color for limits

           if (lower1 > minHr && lower1 < maxHr)
              paint.drawLine(x1, ay1, x2, ay1);

           if (lower2 > minHr && lower2 < maxHr)
              paint.drawLine(x1, ay2, x2, ay2);

           if (lower3 > minHr && lower3 < maxHr)
              paint.drawLine(x1, ay3, x2, ay3);

           if (upper3 > minHr && upper3 < maxHr)
              paint.drawLine(x1, ay4, x2, ay4);

           paint.setPen(QPen(red, 1, QPen::DashDotLine));       // color for average heart rate

           if (avg_hr > minHr && avg_hr < maxHr)
              paint.drawLine(x1, ay5, x2, ay5);
        }

        // Now we have a grid and we've done the scaling.
        // This is the point where we draw the curves itself.
        // We use different colors to draw the lines.
        i = 0;
        x1 = x2 = y1 = y2 = 0;
        int hy1, hy2, hx1, hx2;
        hy1 = hy2 = hx1 = hx2 = 0;

        while ((point = ds.getPoint(i)) != 0)
        {
           // calculate the y position based on the time
           qt = garmin_dtime(point->time);
           secs = zeit.secsTo(qt->time());
           delete qt;
           x2 = secs * w_tick + margin_left + 1;
           hx2 = x2;

           if (x1 == 0)
              x1 = x2;

           if (hx1 == 0)
              hx1 = hx2;

           if (point->alt < 20000 && point->alt > 0.0)
           {
              if (meter)
                 y2 = (double)rh - (point->alt - minHeight) * h_tick;
              else
              {
                 double hrscale = rh / (maxHeight - minHeight);
                 y2 = (double)rh - (point->alt - minHeight) * hrscale;
              }

              if (y1 == 0)
                 y1 = y2;

              paint.setPen(QPen(barcol, 1, QPen::SolidLine));
              paint.drawLine(x1, y1, x2, y2);
              y1 = y2;
              x1 = x2;
           }

           if (point->heart_rate > 0)
           {
              if (meter)
              {
                 double hrscale = rh / (double)(maxHr - minHr);
                 hy2 = (double)rh - (double)(point->heart_rate - minHr) * hrscale;
              }
              else
                 hy2 = (double)rh - (double)(point->heart_rate - minHr) * h_tick;

              if (hy1 == 0)
                 hy1 = hy2;

              paint.setPen(QPen(blue, 1, QPen::SolidLine));
              paint.drawLine(hx1, hy1, hx2, hy2);
              hy1 = hy2;
              hx1 = hx2;
           }

           i++;
        }

        paint.end();
        imgProfile->setPixmap(pmProfile);

//      if (bProfile)
//         bitBlt(imgProfile, 0, 0, &pmProfile);
}

void sportwatcherWidget::resizeEvent(QResizeEvent */* *e */)
{
        showTrack(zfactor);
        showCurves();
}

void sportwatcherWidget::paintEvent(QPaintEvent */* *e */)
{
        showTrack(zfactor);
        showCurves();
}

void sportwatcherWidget::mouseMoveEvent(QMouseEvent *e)
{
QPoint pos(0, 0);
QPoint ev = imgMap->mapFrom(this, e->pos());
static QRect coord;

        if (!stateHand)
           return;

        if (ev.x() >= pos.x() &&
            ev.y() >= pos.y() &&
            ev.x() <= (pos.x() + imgMap->geometry().width()) &&
            ev.y() <= (pos.y() + imgMap->geometry().height()))
        {
           if (lmbPressed == 1)
           {
              coord.setCoords(ev.x(), ev.y(), 0, 0);
              lmbPressed = 0;
           }
           else
           {
              coord.setRight(ev.x());
              coord.setBottom(ev.y());
           }

           if (lmbPressed == 2)
           {
              showTrack(zfactor, coord, mapLap);
              lmbPressed = 0;
           }
        }
}

void sportwatcherWidget::mousePressEvent(QMouseEvent *e)
{
        if (stateHand && e->button() == QMouseEvent::LeftButton)
           lmbPressed = 1;      // Left Mouse Button is pressed
        else if (stateHand)
           lmbPressed = 0;      // Wrong button is pressed

        if (stateGlas)
        {
           if (e->button() == QMouseEvent::LeftButton)
              btGlasPlusSlot();
           else if (e->button() == QMouseEvent::RightButton)
              btGlasMinusSlot();
        }
}

void sportwatcherWidget::mouseReleaseEvent(QMouseEvent *e)
{
        if (stateHand && e->button() == QMouseEvent::LeftButton)
        {
           lmbPressed = 2;      // Left Mouse Button was released
           mouseMoveEvent(e);
        }
}

/*
 * Private functions to help decode the data
 */
QDateTime *sportwatcherWidget::garmin_dtime (uint32 t)
{
time_t     tval;
struct tm  tmval;
QTime ti;
QDate dt;
QDateTime *qt;

        if (t == 0)
           return new QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0));

        tval = t + TIME_OFFSET;
        localtime_r (&tval, &tmval);
        qt = new QDateTime();
        qt->setDate(QDate(tmval.tm_year+1900, tmval.tm_mon+1, tmval.tm_mday));
        qt->setTime(QTime(tmval.tm_hour, tmval.tm_min, tmval.tm_sec, 0));
        /* OK.  Done. */
        return qt;
}

bool sportwatcherWidget::writeTag(const QFile &fn, const QString &str, int indent)
{
QString qs;
char *p = new char[str.length()+100];
QCString qcs;
int i;

        qs.fill(' ', indent * 3);
        qs.append(str);
        qcs = qs.utf8();
        qstrcpy(p, qcs);
        i = strlen(p);

        if (write(fn.handle(), p, i) != i)
        {
           delete p;
           return false;
        }

        delete p;
        return true;
}

#include "sportwatcherwidget.moc"