Subversion Repositories public

Rev

Rev 249 | Rev 251 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
88 andreas 1
/***************************************************************************
221 andreas 2
 *   Copyright (C) 2007 - 2009 by Andreas Theofilu                         *
119 andreas 3
 *   andreas@theosys.at                                                    *
88 andreas 4
 *                                                                         *
5
 *   This program is free software; you can redistribute it and/or modify  *
6
 *   it under the terms of the GNU General Public License as published by  *
7
 *   the Free Software Foundation version 3 of the License.                *
8
 *                                                                         *
9
 *   This program is distributed in the hope that it will be useful,       *
10
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12
 *   GNU General Public License for more details.                          *
13
 *                                                                         *
14
 *   You should have received a copy of the GNU General Public License     *
15
 *   along with this program; if not, write to the                         *
16
 *   Free Software Foundation, Inc.,                                       *
17
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18
 ***************************************************************************/
19
 
119 andreas 20
#include "config.h"
88 andreas 21
#include "managefile.h"
22
#include "sportwatcherwidget.h"
23
#include "settingswidget.h"
96 andreas 24
#include "progresswidget.h"
152 andreas 25
#include "wmsbase.h"
158 andreas 26
#include "coordinateswidget.h"
151 andreas 27
#include <string.h>
96 andreas 28
 
137 andreas 29
#include <iostream>
88 andreas 30
#include <kfiledialog.h>
31
#include <kmessagebox.h>
232 andreas 32
#include <KConfig>
88 andreas 33
#include <klocale.h>
232 andreas 34
#include <k3listview.h>
100 andreas 35
#include <kaboutdata.h>
232 andreas 36
#include <kaboutapplicationdialog.h>
218 andreas 37
#include <kinputdialog.h>
128 andreas 38
#include <kglobalsettings.h>
218 andreas 39
#include <kcombobox.h>
154 andreas 40
#include <qapplication.h>
88 andreas 41
#include <qstring.h>
42
#include <qdatetime.h>
109 andreas 43
#include <qtoolbutton.h>
132 andreas 44
#include <qcursor.h>
172 andreas 45
#include <qregexp.h>
232 andreas 46
#include <QMouseEvent>
246 andreas 47
#include <QRect>
48
#include <QRegion>
245 andreas 49
#include <KIcon>
151 andreas 50
 
232 andreas 51
#if defined HAVE_GDAL
158 andreas 52
   #include <gdal/ogr_spatialref.h>
53
   #include <gdal/ogrsf_frmts.h>
54
   #include <gdal/gdalwarper.h>
165 andreas 55
   #include <gdal/ogrsf_frmts.h>
157 andreas 56
#endif
57
 
218 andreas 58
#include "garmin.h"
137 andreas 59
#include "copy.h"
158 andreas 60
#include "transform.h"
217 andreas 61
#include "import.h"
88 andreas 62
 
232 andreas 63
//#define DEBUG	1
151 andreas 64
 
137 andreas 65
using std::cout;
158 andreas 66
using std::cerr;
67
using std::clog;
137 andreas 68
using std::endl;
69
 
70
 
104 andreas 71
typedef struct
72
{
73
	double lon;
74
	double lat;
75
} posn_type;
76
 
245 andreas 77
sportwatcherWidget::sportwatcherWidget(QWidget *parent)
88 andreas 78
{
245 andreas 79
	ui_sportwatcherWidgetBase.setupUi(this);
232 andreas 80
 
81
#if defined HAVE_GDAL
157 andreas 82
	mFactor = 10;		// Factor to calculate square pixels
83
#endif
88 andreas 84
	mama = parent;
85
	gmn = 0;
86
	min_hr = max_hr = avg_hr = 0;
87
	min_height = max_height = 0.0;
88
	max_time = 0;
104 andreas 89
	zfactor = 0;
132 andreas 90
	mapLap = 0;
91
	mapPan = QRect(0, 0, 0, 0);
92
	stateHand = stateFlag = stateGlas = false;
93
	oldTransX = oldTransY = 0.0;
94
	lmbPressed = 0;
246 andreas 95
	DIRTY = true;
248 andreas 96
	curTab = ui_sportwatcherWidgetBase.tabView->currentIndex();
97
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
230 andreas 98
	kl = new KLocale(QString("kdesktop"), 0);
132 andreas 99
 
88 andreas 100
	// Load the config parameters
232 andreas 101
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
102
	KConfigGroup ic (&cfg, "SportWatcher");
103
	lower1 = ic.readEntry("lower1", 0);
104
	lower2 = ic.readEntry("lower2", 0);
105
	lower3 = ic.readEntry("lower3", 0);
106
	upper1 = ic.readEntry("upper1", 0);
107
	upper2 = ic.readEntry("upper2", 0);
108
	upper3 = ic.readEntry("upper3", 0);
109
	MaxHr = ic.readEntry("maxHr", 180);
110
	restHr = ic.readEntry("restHr", 60);
111
	vo2max = ic.readEntry("vo2max", 50);
112
	weight = ic.readEntry("weight", 70);
113
	sampleTime = ic.readEntry("seconds", 15);
114
	Serial = ic.readEntry("Serial", false);
115
	Forerunner = ic.readEntry("Forerunner", false);
116
	Contour = ic.readEntry("Contour", false);
117
	Device = ic.readEntry("Device", "/dev/ttyUSB0");
118
	Data = ic.readEntry("Data", QDir::home().absolutePath() + "/.sportwatcher");
119
	HRM = ic.readEntry("HRM", QDir::home().absolutePath() + "/polar");
120
	MAP = ic.readEntry("MAP", QDir::home().absolutePath() + "/.sportwatcher/track.wms");
121
	Units = ic.readEntry("Units", 0);
122
	MapType = ic.readEntry("MapType", 7);
88 andreas 123
}
124
 
125
sportwatcherWidget::~sportwatcherWidget()
126
{
104 andreas 127
	destroy();
230 andreas 128
	delete kl;
88 andreas 129
}
130
 
100 andreas 131
void sportwatcherWidget::destroy()
132
{
133
	if (gmn)
134
	   garmin_free_data (gmn);
135
 
136
	gmn = 0;
132 andreas 137
	oldTransX = oldTransY = 0.0;
100 andreas 138
}
139
 
245 andreas 140
void sportwatcherWidget::Initialize()
141
{
142
	// Set some widget settings
143
	ui_sportwatcherWidgetBase.btHand->setIcon(KIcon("hand"));
144
	ui_sportwatcherWidgetBase.btGlas->setIcon(KIcon("glas"));
145
	ui_sportwatcherWidgetBase.btGlasMinus->setIcon(KIcon("glas_minus"));
146
	ui_sportwatcherWidgetBase.btGlasPlus->setIcon(KIcon("glas_plus"));
147
	ui_sportwatcherWidgetBase.btFlag->setIcon(KIcon("flag"));
148
	ui_sportwatcherWidgetBase.btFullscreen->setIcon(KIcon("fullscreen"));
248 andreas 149
 
150
	if (curTab != 0)
151
	{
152
	   ui_sportwatcherWidgetBase.tabView->setCurrentIndex(0);
153
	   curTab = ui_sportwatcherWidgetBase.tabView->currentIndex();
154
	}
155
 
245 andreas 156
//	btHand->setToggleButton(true);
157
//	btGlas->setToggleButton(true);
158
	// Fill the activities
159
	getActivities();
160
#if defined HAVE_GDAL
161
	// Initialize the GDAL
162
	GDALAllRegister();
163
	poDataset = 0;
164
#endif
165
}
166
 
247 andreas 167
QTreeWidgetItem *sportwatcherWidget::findElement(QTreeWidget *wdg, const QString &val)
132 andreas 168
{
247 andreas 169
QTreeWidgetItem *item, *child, *subchild;
132 andreas 170
 
246 andreas 171
	if (!wdg)
247 andreas 172
	   return 0;
246 andreas 173
 
174
	for (int a = 0; a < wdg->topLevelItemCount(); a++)
132 andreas 175
	{
246 andreas 176
	   if (!(item = wdg->topLevelItem(a)))
177
	      continue;
132 andreas 178
 
246 andreas 179
	   for (int j = 0; j < item->childCount(); j++)
180
	   {
181
	      if (!(child = item->child(j)))
182
		 continue;
183
 
184
	      if (child->data(0, Qt::UserRole).toString() == val)
247 andreas 185
		 return child;
186
 
187
	      for (int i = 0; i < child->childCount(); i++)
188
	      {
189
		 if (!(subchild = child->child(i)))
190
		    continue;
191
 
192
		 if (subchild->data(0, Qt::UserRole).toString() == val)
193
		    return subchild;
194
	      }
246 andreas 195
	   }
132 andreas 196
	}
197
 
247 andreas 198
	return 0;
132 andreas 199
}
200
 
88 andreas 201
/*
202
 * Search for a directory named .sportwatcher in the home directory of
203
 * the user and search for *.gmn files. Open the files and read the header
204
 * to find the basic data of activities. Then add the information into
205
 * the activities KListView.
206
 */
207
void sportwatcherWidget::getActivities()
208
{
209
QString path, txt;
232 andreas 210
QDir mdir, dir = QDir::home();
88 andreas 211
QStringList years, months;
247 andreas 212
QTreeWidgetItem *running, *biking, *other, *year_run, *year_bike, *year_other;
246 andreas 213
QTreeWidgetItem *el;
214
QList<QTreeWidgetItem *> item;
232 andreas 215
int i, j;
247 andreas 216
int y_run, y_bike, y_other;
88 andreas 217
RUN_NODE *rn;
218
LAP *lap;
219
 
220
	if (Data.isEmpty())
221
	{
232 andreas 222
	   path = dir.homePath();
88 andreas 223
	   path.append("/.sportwatcher");
224
	}
225
	else
226
	   path = Data;
227
 
228
	dir.setPath(path);
132 andreas 229
	dir.refresh();
88 andreas 230
 
231
	if (!dir.exists())
232
	{
233
	   dir.mkdir(path);
234
	   return;
235
	}
236
 
132 andreas 237
	destroy();
128 andreas 238
 
245 andreas 239
	if (!ui_sportwatcherWidgetBase.liActivities)
240
	{
241
	   KMessageBox::error(this, i18n("Error initializing some widgets of main window!"));
242
	   return;
243
	}
244
 
245
	ui_sportwatcherWidgetBase.liActivities->clear();
246 andreas 246
	ui_sportwatcherWidgetBase.liActivities->setSortingEnabled(false);
247
	ui_sportwatcherWidgetBase.liActivities->setColumnCount(1);
245 andreas 248
 
246 andreas 249
	running = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
250
	biking = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
251
	other = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
252
 
245 andreas 253
	if (!other || !biking || !running)
254
	{
255
	   KMessageBox::error(this, i18n("Not enough memory to initilize application!"));
256
	   return;
257
	}
258
 
246 andreas 259
	running->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
260
	biking->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
261
	other->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
128 andreas 262
 
246 andreas 263
	running->setText(0, i18n("Running"));
264
	biking->setText(0, i18n("Biking"));
265
	other->setText(0, i18n("Others"));
88 andreas 266
 
246 andreas 267
	KIcon fld(QString("history"));
268
	running->setIcon(0, fld);
269
	biking->setIcon(0, fld);
270
	other->setIcon(0, fld);
271
 
272
	running->setStatusTip(0, i18n("\"Running\" activities."));
273
	biking->setStatusTip(0, i18n("\"Biking\" activities."));
274
	other->setStatusTip(0, i18n("\"Other\" and \"Multisport\" activities."));
275
 
88 andreas 276
	dir.cd(path);
277
	dir.setFilter(QDir::Dirs | QDir::NoSymLinks);
278
	dir.setSorting(QDir::Name);
132 andreas 279
	dir.refresh();
232 andreas 280
	QFileInfoList list = dir.entryInfoList();
88 andreas 281
 
232 andreas 282
	if (!list.size())
88 andreas 283
	   return;
284
 
232 andreas 285
	for (i = 0; i < list.size(); i++)		// Years
286
	{
287
	QFileInfo fileInfo = list.at(i);
88 andreas 288
 
232 andreas 289
	   if (fileInfo.fileName() == QString(".") || fileInfo.fileName() == QString(".."))
88 andreas 290
	      continue;
291
 
232 andreas 292
	   years += fileInfo.absoluteFilePath();
88 andreas 293
	}
294
 
232 andreas 295
	for (i = 0; i < years.size(); ++i)
88 andreas 296
	{
232 andreas 297
	   if (months.size() > 0)
88 andreas 298
	      months.clear();
299
 
232 andreas 300
	   dir.setPath(years.at(i));
132 andreas 301
	   dir.refresh();
232 andreas 302
	   list = dir.entryInfoList();
88 andreas 303
 
232 andreas 304
	   if (!list.size())
88 andreas 305
	      continue;
306
 
232 andreas 307
	   for (j = 0; j < list.size(); j++)		// Months
308
	   {
309
	   QFileInfo fileInfo = list.at(j);
88 andreas 310
 
232 andreas 311
	      if (fileInfo.fileName() == QString(".") || fileInfo.fileName() == QString(".."))
88 andreas 312
		 continue;
313
 
232 andreas 314
	      months += fileInfo.absoluteFilePath();
88 andreas 315
	   }
316
 
232 andreas 317
	   for (j = 0; j < months.size(); ++j)
88 andreas 318
	   {
232 andreas 319
	      mdir.setPath(months.at(j));
320
	      mdir.cd(months.at(j));
88 andreas 321
	      mdir.setFilter(QDir::Files | QDir::NoSymLinks);
246 andreas 322
	      mdir.setSorting(QDir::Name);
232 andreas 323
	      mdir.setNameFilters(QStringList("*.gmn"));
132 andreas 324
	      mdir.refresh();
232 andreas 325
	      list = mdir.entryInfoList();
88 andreas 326
 
232 andreas 327
	      if (!list.size())
88 andreas 328
		 continue;
329
 
232 andreas 330
	      for (int a = 0; a < list.size(); a++)		// Files
331
	      {
332
	      QFileInfo fileInfo = list.at(a);
88 andreas 333
 
232 andreas 334
		 files += fileInfo.absoluteFilePath();
88 andreas 335
	      }
336
	   }
337
	}
338
 
247 andreas 339
	y_run = y_bike = y_other = 0;
340
	year_run = year_bike = year_other = 0;
88 andreas 341
	// Open every file and read its head
232 andreas 342
	for (i = 0; i < files.size(); ++i)
88 andreas 343
	{
247 andreas 344
	QTreeWidgetItem *yr, *yb, *yo;
132 andreas 345
 
246 andreas 346
	   if (findElement(ui_sportwatcherWidgetBase.liActivities, files.at(i)))
347
	      continue;
348
 
88 andreas 349
	   spw.destroy();
350
 
232 andreas 351
	   if (spw.setFileName(files.at(i).toAscii().constData()) == -1)
88 andreas 352
	      return;
353
 
354
	   if (gmn)
246 andreas 355
	   {
88 andreas 356
	      garmin_free_data (gmn);
246 andreas 357
	      gmn = 0;
358
	   }
88 andreas 359
 
246 andreas 360
	   if (!(gmn = spw.readFile()))
361
	      continue;
362
 
88 andreas 363
	   ds.destroy();
364
	   ds.garmin_print_data(gmn);
365
	   rn = ds.getRunNode();
366
 
245 andreas 367
	   if (!rn)
368
	      return;
369
 
88 andreas 370
	   lap = ds.getLap(rn->run->first_lap_index);
245 andreas 371
 
372
	   if (!lap)
373
	      return;
374
 
88 andreas 375
	   const QDateTime *qt = garmin_dtime (lap->start_time);
232 andreas 376
	   // By default set the name of the session to the date and time
377
	   // it was recorded.
378
	   QString idx = kl->formatDateTime (*qt, KLocale::ShortDate, true);
248 andreas 379
	   QString stTip = kl->formatDateTime (*qt, KLocale::LongDate, true);
88 andreas 380
 
232 andreas 381
	   // If we have a custom name for this session, set it.
217 andreas 382
	   if (strlen (rn->run->workout.name) > 1 && strlen (rn->run->workout.name) < 16 && isalpha (rn->run->workout.name[0]))
232 andreas 383
	      idx = QString (rn->run->workout.name);
217 andreas 384
 
88 andreas 385
	   switch (rn->run->sport_type)
386
	   {
387
	      case D1000_running:
247 andreas 388
		 yr = findElement(ui_sportwatcherWidgetBase.liActivities, QString("run_%1").arg(qt->date().year()));
389
 
390
		 if (!yr && qt->date().year() != y_run)
391
		 {
392
		    year_run = new QTreeWidgetItem(running, running);
393
		    y_run = qt->date().year();
394
		    year_run->setText(0, QString("%1").arg(y_run));
395
		    year_run->setData(0, Qt::UserRole, QString("run_%1").arg(y_run));
396
		    year_run->setIcon(0, KIcon(QString("today")));
397
		    year_run->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
398
		 }
399
		 else
400
		    year_run = yr;
401
 
402
		 el = new QTreeWidgetItem(year_run, year_run);
246 andreas 403
		 el->setText(0, idx);
404
		 el->setData(0, Qt::UserRole, files.at(i));
247 andreas 405
		 el->setIcon(0, KIcon(QString("spw-running")));
246 andreas 406
		 el->setStatusTip(0, stTip);
407
		 el->setToolTip(0, stTip);
408
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
88 andreas 409
	      break;
410
 
411
	      case D1000_biking:
247 andreas 412
		 yb = findElement(ui_sportwatcherWidgetBase.liActivities, QString("bike_%1").arg(qt->date().year()));
413
 
414
		 if (!yb && qt->date().year() != y_bike)
415
		 {
416
		    year_bike = new QTreeWidgetItem(biking, biking);
417
		    y_bike = qt->date().year();
418
		    year_bike->setText(0, QString("%1").arg(y_bike));
419
		    year_bike->setData(0, Qt::UserRole, QString("bike_%1").arg(y_run));
420
		    year_bike->setIcon(0, KIcon(QString("today")));
421
		    year_bike->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
422
		 }
423
		 else
424
		    year_bike = yb;
425
 
426
		 el = new QTreeWidgetItem(year_bike, year_bike);
246 andreas 427
		 el->setText(0, idx);
428
		 el->setData(0, Qt::UserRole, files.at(i));
429
		 el->setStatusTip(0, stTip);
430
		 el->setToolTip(0, stTip);
431
		 el->setIcon(0, KIcon(QString("bike")));
432
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
88 andreas 433
	      break;
434
 
435
	      case D1000_other:
247 andreas 436
	      default:
437
		 yo = findElement(ui_sportwatcherWidgetBase.liActivities, QString("other_%1").arg(qt->date().year()));
88 andreas 438
 
247 andreas 439
		 if (!yo && qt->date().year() != y_other)
440
		 {
441
		    year_other = new QTreeWidgetItem(other, other);
442
		    y_other = qt->date().year();
443
		    year_other->setText(0, QString("%1").arg(y_other));
444
		    year_other->setData(0, Qt::UserRole, QString("other_%1").arg(y_run));
445
		    year_other->setIcon(0, KIcon(QString("today")));
446
		    year_other->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
447
		 }
448
		 else
449
		    year_other = yo;
450
 
451
		 el = new QTreeWidgetItem(year_other, year_other);
246 andreas 452
		 el->setText(0, idx);
453
		 el->setData(0, Qt::UserRole, files.at(i));
454
		 el->setStatusTip(0, stTip);
455
		 el->setToolTip(0, stTip);
456
		 el->setIcon(0, KIcon(QString("other")));
457
		 el->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
88 andreas 458
	   }
459
 
460
	   delete qt;
461
	}
462
 
246 andreas 463
	ui_sportwatcherWidgetBase.liActivities->setItemExpanded(running, true);
88 andreas 464
 
247 andreas 465
	if (year_run)
466
	   ui_sportwatcherWidgetBase.liActivities->setItemExpanded(year_run, true);
467
 
88 andreas 468
	if (gmn)
469
	   garmin_free_data (gmn);
470
 
471
	gmn = 0;
472
}
473
 
474
/*$SPECIALIZATION$*/
475
void sportwatcherWidget::btFullscreenSlot()
476
{
132 andreas 477
	oldTransX = oldTransY = 0.0;
478
	mapPan.setCoords(0, 0, 0, 0);
246 andreas 479
	DIRTY = true;
104 andreas 480
	showTrack(0);
246 andreas 481
	DIRTY = false;
88 andreas 482
}
483
 
484
void sportwatcherWidget::btGlasMinusSlot()
485
{
132 andreas 486
bool sh = stateHand;
487
 
488
	stateHand = false;
246 andreas 489
	DIRTY = true;
104 andreas 490
	showTrack(zfactor - 1000);
246 andreas 491
	DIRTY = false;
132 andreas 492
	stateHand = sh;
88 andreas 493
}
494
 
495
void sportwatcherWidget::btGlasPlusSlot()
496
{
132 andreas 497
bool sh = stateHand;
498
 
499
	stateHand = false;
246 andreas 500
	DIRTY = true;
104 andreas 501
	showTrack(zfactor + 1000);
246 andreas 502
	DIRTY = false;
132 andreas 503
	stateHand = sh;
88 andreas 504
}
505
 
506
void sportwatcherWidget::btHandSlot()
507
{
132 andreas 508
QCursor cs;
509
 
510
	if (stateGlas)
511
	{
512
	   stateGlas = false;
245 andreas 513
	   ui_sportwatcherWidgetBase.btGlas->toggle();
132 andreas 514
	}
515
 
516
	stateHand = (stateHand) ? false : true;
517
 
518
	if (stateHand)
232 andreas 519
	   cs.setShape(Qt::PointingHandCursor);
132 andreas 520
	else
232 andreas 521
	   cs.setShape(Qt::ArrowCursor);
132 andreas 522
 
245 andreas 523
	ui_sportwatcherWidgetBase.imgMap->setCursor(cs);
88 andreas 524
}
525
 
526
void sportwatcherWidget::btGlasSlot()
527
{
132 andreas 528
QCursor cs;
529
 
530
	if (stateHand)
531
	{
532
	   stateHand = false;
245 andreas 533
	   ui_sportwatcherWidgetBase.btHand->toggle();
132 andreas 534
	}
535
 
536
	stateGlas = (stateGlas) ? false : true;
537
 
538
	if (stateGlas)
232 andreas 539
	   cs.setShape(Qt::ForbiddenCursor);
132 andreas 540
	else
232 andreas 541
	   cs.setShape(Qt::ArrowCursor);
132 andreas 542
 
245 andreas 543
	ui_sportwatcherWidgetBase.imgMap->setCursor(cs);
88 andreas 544
}
545
 
546
void sportwatcherWidget::btFlagSlot()
547
{
548
}
549
 
232 andreas 550
void sportwatcherWidget::liLapsSlot(Q3ListViewItem *item)
88 andreas 551
{
132 andreas 552
QString sl;
553
int l;
554
int idx;
555
RUN_NODE *rn;
556
LAP *lap;
557
 
558
	if (!item)
559
	   return;
560
 
246 andreas 561
	DIRTY = true;
132 andreas 562
	sl = item->text(0).mid(4, 3);
563
	l = sl.toInt();
564
 
565
	if (l <= 0)
566
	{
245 andreas 567
	   // Show the whole map and track
132 andreas 568
	   showTrack(zfactor, mapPan, 0);
245 andreas 569
	   // Don't mark any lap. Just display normal.
148 andreas 570
	   showCurves(0);
132 andreas 571
	   return;
572
	}
573
 
574
	rn = ds.getRunNode();
575
	idx = rn->run->first_lap_index;
576
	lap = ds.getLap(idx + l - 1);
245 andreas 577
	// Show the part of the track, corresponding to the selected lap
132 andreas 578
	showTrack(zfactor, mapPan, lap);
245 andreas 579
	// Mark the selected lap
148 andreas 580
	showCurves(lap);
246 andreas 581
	DIRTY = false;
88 andreas 582
}
583
 
246 andreas 584
void sportwatcherWidget::liActivitiesSlot(QTreeWidgetItem *item, int)
88 andreas 585
{
586
	if (!item)
587
	   return;
588
 
245 andreas 589
	spw.destroy();
88 andreas 590
 
246 andreas 591
	if (spw.setFileName(item->data(0, Qt::UserRole).toString().toAscii().constData()) == -1)
245 andreas 592
	   return;
88 andreas 593
 
245 andreas 594
	if (gmn)
595
	   garmin_free_data (gmn);
88 andreas 596
 
245 andreas 597
	gmn = spw.readFile();
598
	zfactor = 0;
248 andreas 599
 
600
	// Make sure, the currently not visible tabs will be redrawn as soon
601
	// as they are visible.
602
	switch (curTab)
603
	{
604
	   case 0: tabDirt1 = tabDirt2 = tabDirt3 = true; break;
605
	   case 1: tabDirt0 = tabDirt2 = tabDirt3 = true; break;
606
	   case 2: tabDirt0 = tabDirt1 = tabDirt3 = true; break;
607
	   case 3: tabDirt0 = tabDirt1 = tabDirt2 = true; break;
608
	}
609
 
245 andreas 610
	// Display the just loaded sport session
246 andreas 611
	DIRTY = true;
245 andreas 612
	showLaps();
613
	showTrack();
614
	showCurves();
250 andreas 615
 
616
	if (curTab == 2)
617
	{
618
	   showThreeCurve();
619
	   tabDirt2 = false;
620
	}
621
 
246 andreas 622
	DIRTY = false;
88 andreas 623
}
624
 
625
void sportwatcherWidget::filePrint()
626
{
230 andreas 627
	KMessageBox::information(this, i18n("This function is currently not implemented!"));
88 andreas 628
}
629
 
104 andreas 630
/*
631
 * This function allows the user to choose a file name, where we save the
632
 * actual lap in Garmins own TCX format. This format is simply a XML-file.
633
 * For details about the schema of this file look at
634
 * http://developer.garmin.com/schemas/tcx/v2/
635
 */
88 andreas 636
void sportwatcherWidget::fileSaveAs()
637
{
104 andreas 638
QString fname;
639
QFile fn;
640
QString buffer;
641
RUN_NODE *rn, *rakt;
642
LAP *lap;
643
POINT *point;
644
int indent, i;
645
QDateTime *qt;
232 andreas 646
KUrl sDir("~/");
246 andreas 647
QRegExp rx("(\\.tcx|\\.gpx|\\.osm)$");
104 andreas 648
 
649
	if (!gmn)
650
	{
651
	   KMessageBox::error(this, i18n("Currently no activity is selected!"));
652
	   return;
653
	}
654
 
246 andreas 655
	rx.setPatternSyntax(QRegExp::RegExp);
232 andreas 656
	fname = KFileDialog::getSaveFileName(sDir, QString("*.tcx|Garmin Training Center (*.tcx)\n*.gpx|GPS Excange Format (*.gpx)\n*.osm|OpenStreetMap (*.osm)"), this, QString("SportWatcher"));
104 andreas 657
 
658
	if (fname.isEmpty())
659
	   return;
660
 
246 andreas 661
	if (rx.indexIn(fname) < 0)
172 andreas 662
	{
232 andreas 663
	   KMessageBox::error(this, i18n("The file %1 has no valid file extension!").arg(fname));
172 andreas 664
	   return;
665
	}
666
 
232 andreas 667
	fn.setFileName(fname);
104 andreas 668
 
669
	if (fn.exists())
670
	{
671
	   if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
672
	      return;
673
	}
674
 
246 andreas 675
	rx.setPattern("*.gpx");
676
	rx.setPatternSyntax(QRegExp::Wildcard);
172 andreas 677
 
246 andreas 678
	if (rx.exactMatch(fname))	// Should we create a *.gpx file?
172 andreas 679
	{
680
	   sportwatcherWidget::saveGPX(fname);
681
	   return;
682
	}
683
 
246 andreas 684
	rx.setPattern("*.osm");
685
	rx.setPatternSyntax(QRegExp::Wildcard);
172 andreas 686
 
246 andreas 687
	if (rx.exactMatch(fname))	// Should we create a *.osm file?
172 andreas 688
	{
689
	   sportwatcherWidget::saveOSM(fname);
690
	   return;
691
	}
692
 
693
	// No, we create a *.tcx file!
171 andreas 694
	indent = 0;
104 andreas 695
	rn = ds.getRunNode();
696
	lap = ds.getLap(rn->run->first_lap_index);
697
 
698
	if ((point = ds.getPoint(lap->start_time)) == 0)
699
	{
700
	   KMessageBox::error(this, i18n("No data to save!"));
701
	   return;
702
	}
703
 
232 andreas 704
	if (!fn.open(QIODevice::ReadWrite | QIODevice::Truncate))
104 andreas 705
	{
232 andreas 706
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fname));
104 andreas 707
	   return;
708
	}
709
 
710
	buffer = QString("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>\n");
711
	buffer.append("<TrainingCenterDatabase xmlns=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2\" ");
712
	buffer.append("xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" ");
713
	buffer.append("xsi:schemaLocation=\"http://www.garmin.com/xmlschemas/TrainingCenterDatabase/v2 ");
714
	buffer.append("http://www.garmin.com/xmlschemas/TrainingCenterDatabasev2.xsd\">\n\n");
715
	writeTag (fn, buffer, indent);
716
	buffer = QString("<folders/>\n\n");
717
	writeTag (fn, buffer, indent);
718
 
719
	// Open a course
720
	QFileInfo finfo(fname);
232 andreas 721
	buffer = QString("<Courses>\n   <Course>\n      <name>%1</name>\n").arg(QFileInfo(finfo).baseName());
104 andreas 722
	writeTag (fn, buffer, indent);
723
	indent = 2;
724
 
725
	rakt = rn;
726
 
727
	while (rakt)
728
	{
729
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
730
	   	rakt->run->type != data_D1010)
731
	   {
732
	      rakt = rakt->next;
733
	      continue;
734
	   }
735
 
736
	   for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
737
	   {
738
	      if ((lap = ds.getLap(i)) == NULL)
739
		 continue;
740
 
741
	      // Write the information of the lap
742
	      writeTag (fn, QString("<Lap>\n"), indent);
743
	      indent++;
744
	      buffer.sprintf("<TotalTimeSeconds>%f</TotalTimeSeconds>\n", (double)lap->total_time / 100.0);
745
	      writeTag (fn, buffer, indent);
172 andreas 746
	      qt = garmin_dtime(lap->start_time);
747
	      buffer = QString("<StartTime>%1</StartTime>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
748
	      writeTag (fn, buffer, indent);
104 andreas 749
	      buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", lap->total_distance);
750
	      writeTag (fn, buffer, indent);
751
 
752
	      writeTag (fn, QString("<BeginPosition>\n"), indent);
753
	      indent++;
754
	      buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->begin.lat));
755
	      writeTag (fn, buffer, indent);
756
	      buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->begin.lon));
757
	      writeTag (fn, buffer, indent);
758
	      indent--;
759
	      writeTag (fn, QString("</BeginPosition>\n"), indent);
760
 
761
	      writeTag (fn, QString("<EndPosition>\n"), indent);
762
	      indent++;
763
	      buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(lap->end.lat));
764
	      writeTag (fn, buffer, indent);
765
	      buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(lap->end.lon));
766
	      writeTag (fn, buffer, indent);
767
	      indent--;
768
	      writeTag (fn, QString("</EndPosition>\n"), indent);
769
 
770
	      writeTag (fn, QString("<AverageHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
771
	      indent++;
772
	      buffer.sprintf("<Value>%d</Value>\n", lap->avg_heart_rate);
773
	      writeTag (fn, buffer, indent);
774
	      indent--;
775
	      writeTag (fn, QString("</AverageHeartRateBpm>\n"), indent);
776
 
777
	      writeTag (fn, QString("<MaximumHeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
778
	      indent++;
172 andreas 779
	      buffer.sprintf("<Value>%d</Value>\n", lap->max_heart_rate);
104 andreas 780
	      writeTag (fn, buffer, indent);
781
	      indent--;
782
	      writeTag (fn, QString("</MaximumHeartRateBpm>\n"), indent);
783
 
172 andreas 784
	      if (lap->avg_cadence < 255)
785
	      {
786
		 buffer.sprintf("<AverageCadence>%d</AverageCadence>\n", lap->avg_cadence);
787
		 writeTag (fn, buffer, indent);
215 andreas 788
		 buffer.sprintf("<Cadence>%d</Cadence>\n", lap->avg_cadence);
789
		 writeTag (fn, buffer, indent);
172 andreas 790
	      }
791
 
215 andreas 792
	      buffer = QString("<Intensity>%1</Intensity>\n").arg((!lap->intensity) ? "Active" : "Resting");
104 andreas 793
	      writeTag (fn, buffer, indent);
217 andreas 794
 
795
	      buffer.sprintf("<Calories>%d</Calories>\n", lap->calories);
796
	      writeTag (fn, buffer, indent);
797
 
798
	      buffer.sprintf("<MaximumSpeed>%f</MaximumSpeed>\n", lap->max_speed);
799
	      writeTag (fn, buffer, indent);
104 andreas 800
	      indent--;
801
	      writeTag (fn, QString("</Lap>\n"), indent);
802
 
803
	      point = ds.getPoint(lap->start_time);
804
	      writeTag (fn, QString("<Track>\n"), indent);
805
	      indent++;
806
 
807
	      while (point)
808
	      {
809
		 if (point->time > (lap->start_time + (lap->total_time / 100)))
810
		    break;
811
 
812
		 writeTag (fn, QString("<Trackpoint>\n"), indent);
813
		 indent++;
814
		 qt = garmin_dtime(point->time);
815
		 buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
816
		 writeTag (fn, buffer, indent);
817
		 delete qt;
818
		 writeTag (fn, QString("<Position>\n"), indent);
819
		 indent++;
820
		 buffer.sprintf("<LatitudeDegrees>%f</LatitudeDegrees>\n", SEMI2DEG(point->posn.lat));
821
		 writeTag (fn, buffer, indent);
822
		 buffer.sprintf("<LongitudeDegrees>%f</LongitudeDegrees>\n", SEMI2DEG(point->posn.lon));
823
		 writeTag (fn, buffer, indent);
824
		 indent--;
825
		 writeTag (fn, QString("</Position>\n"), indent);
826
 
827
		 if (point->alt < 20000.0)
828
		 {
829
		    buffer.sprintf("<AltitudeMeters>%f</AltitudeMeters>\n", point->alt);
830
		    writeTag (fn, buffer, indent);
831
		 }
832
 
833
		 buffer.sprintf("<DistanceMeters>%f</DistanceMeters>\n", point->distance);
834
		 writeTag (fn, buffer, indent);
835
 
836
		 if (point->heart_rate > 0 && point->heart_rate < 250)
837
		 {
838
		    writeTag (fn, QString("<HeartRateBpm xsi:type=\"HeartRateInBeatsPerMinute_t\">\n"), indent);
839
		    indent++;
840
		    buffer.sprintf("<Value>%d</Value>\n", point->heart_rate);
841
		    writeTag (fn, buffer, indent);
842
		    indent--;
843
		    writeTag (fn, QString("</HeartRateBpm>\n"), indent);
844
		 }
845
 
249 andreas 846
		 if (point->cadence < 0xff)
215 andreas 847
		 {
848
		    buffer.sprintf("<Cadence>%d</Cadence>\n", point->cadence);
849
		    writeTag (fn, buffer, indent);
850
		 }
851
 
852
		 buffer.sprintf("<SensorState>%s</SensorState>\n", (!point->sensor) ? "Absent" : "Present");
104 andreas 853
		 writeTag (fn, buffer, indent);
854
		 indent--;
855
		 writeTag (fn, QString("</Trackpoint>\n"), indent);
856
		 point = ds.getPoint(point->time + 1);
857
	      }
858
 
859
	      indent--;
860
	      writeTag (fn, QString("</Track>\n"), indent);
861
	   }
862
 
863
	   indent--;
864
	   writeTag (fn, QString("</Course>\n"), indent);
865
	   indent--;
866
	   writeTag (fn, QString("</Courses>\n"), indent);
867
	   rakt = rakt->next;
868
	}
869
 
870
	// Write information about device
871
	// Here my personal signature is written :-)
872
	writeTag (fn, QString("<Author xsi:type=\"Application_t\">\n"), indent);
873
	indent++;
874
	writeTag (fn, QString("<Name>SportWatcher</Name>\n"), indent);
875
	writeTag (fn, QString("<Build>\n"), indent);
876
	indent++;
877
	writeTag (fn, QString("<Version>\n"), indent);
878
	indent++;
879
	writeTag (fn, QString("<VersionMajor>0</VersionMajor>\n"), indent);
880
	writeTag (fn, QString("<VersionMinor>1</VersionMinor>\n"), indent);
881
	writeTag (fn, QString("<BuildMajor>0</BuildMajor>\n"), indent);
882
	writeTag (fn, QString("<BuildMinor>0</BuildMinor>\n"), indent);
883
	indent--;
884
	writeTag (fn, QString("</Version>\n"), indent);
885
	writeTag (fn, QString("<Type>Beta</Type>\n"), indent);
886
	writeTag (fn, QString("<Time>Jan 31 2008, 00:00:00</Time>\n"), indent);
887
	writeTag (fn, QString("<Builder>theosys</Builder>\n"), indent);
888
	indent--;
889
	writeTag (fn, QString("</Build>\n"), indent);
890
	writeTag (fn, QString("<LangID>EN</LangID>\n"), indent);
891
	writeTag (fn, QString("<PartNumber>000-00000-00</PartNumber>\n"), indent);
892
	indent--;
893
	writeTag (fn, QString("</Author>\n"), indent);
894
	writeTag (fn, QString("</TrainingCenterDatabase>\n"), indent);
895
 
896
	fn.close();
897
	KMessageBox::information(this, i18n("File ") + fname + i18n(" was written successfully."));
88 andreas 898
}
899
 
900
void sportwatcherWidget::fileSave()
901
{
172 andreas 902
	KMessageBox::information(this, i18n("This function is currently not implemented!"));
88 andreas 903
}
904
 
172 andreas 905
void sportwatcherWidget::saveGPX(const QString &fn)
906
{
907
QFile qf;
908
QString buffer;
909
RUN_NODE *rn, *rakt;
910
LAP *lap;
911
POINT *point;
912
int indent;
913
unsigned int i;
914
QDateTime *qt;
915
double minLat, minLon, maxLat, maxLon;
916
 
917
	indent = 0;
918
	rn = ds.getRunNode();
919
	lap = ds.getLap(rn->run->first_lap_index);
920
 
921
	if ((point = ds.getPoint(lap->start_time)) == 0)
922
	{
923
	   KMessageBox::error(this, i18n("No data to save!"));
924
	   return;
925
	}
926
 
232 andreas 927
	qf.setFileName(fn);
172 andreas 928
 
232 andreas 929
	if (!qf.open(QIODevice::ReadWrite | QIODevice::Truncate))
172 andreas 930
	{
232 andreas 931
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fn));
172 andreas 932
	   return;
933
	}
934
 
935
	buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
936
	buffer.append("<gpx version=\"1.1\" creator=\"TheoSys SportWatcher\" xmlns=\"http://www.topografix.com/GPX/1/1\">\n");
937
	buffer.append("   <metadata>\n");
938
	indent = 0;
939
	writeTag (qf, buffer, indent);
940
 
941
	// Find the edges of our coordinates
942
	// We need this information in the header (metadata)
943
	rakt = rn;
944
	minLat = -90.0;
945
	minLon = -180.0;
946
	maxLat = 90.0;
947
	maxLon = 180.0;
948
 
949
	while (rakt)
950
	{
951
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
952
	   	rakt->run->type != data_D1010)
953
	   {
954
	      rakt = rakt->next;
955
	      continue;
956
	   }
957
 
958
	   i = rakt->run->first_lap_index;
959
	   // get the first lap
960
	   if ((lap = ds.getLap(i)) == NULL)
961
	      continue;
962
 
963
	   i = 0;
964
	   // iterate the points associated with the laps
965
	   while ((point = ds.getPoint(i)) != 0)
966
	   {
967
	      if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
968
	      {
969
		 i = point->time + 1;
970
		 continue;
971
	      }
972
 
973
	      if (SEMI2DEG(point->posn.lat) > minLat)
974
		 minLat = SEMI2DEG(point->posn.lat);
975
 
976
	      if (SEMI2DEG(point->posn.lat) < maxLat)
977
		 maxLat = SEMI2DEG(point->posn.lat);
978
 
979
	      if (SEMI2DEG(point->posn.lon) > minLon)
980
		 minLon = SEMI2DEG(point->posn.lon);
981
 
982
	      if (SEMI2DEG(point->posn.lon) < maxLon)
983
		 maxLon = SEMI2DEG(point->posn.lon);
984
 
985
	      i = point->time + 1;
986
	   }
987
 
988
	   rakt = rakt->next;
989
	}
990
 
991
	buffer.sprintf("      <bounds minlat=\"%f\" minlon=\"%f\" maxlat=\"%f\" maxlon=\"%f\" />\n",
992
		maxLat, minLon, minLat, maxLon);
993
	buffer.append("   </metadata>\n");
994
	buffer.append("   <trk>\n");
995
	buffer.append("      <trkseg>\n");
996
	writeTag (qf, buffer, indent);
997
	indent = 3;
998
	rn = ds.getRunNode();
999
	lap = ds.getLap(rn->run->first_lap_index);
1000
	i = 0;
1001
 
1002
	while ((point = ds.getPoint(i)) != 0)
1003
	{
1004
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
1005
	   {
1006
	      i = point->time + 1;
1007
	      continue;
1008
	   }
1009
 
1010
	   buffer.sprintf("<trkpt lat=\"%f\" lon=\"%f\">\n",
1011
		SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
1012
	   writeTag(qf, buffer, indent);
1013
	   indent++;
1014
	   buffer.sprintf("<ele>%f</ele>\n", point->alt);
1015
	   writeTag(qf, buffer, indent);
1016
	   qt = garmin_dtime(point->time);
1017
	   buffer = QString("<Time>%1</Time>\n").arg(qt->toString("yyyy-MM-ddThh:mm:ssZ"));
1018
	   writeTag(qf, buffer, indent);
1019
	   indent--;
1020
	   writeTag(qf, QString("</trkpt>\n"), indent);
1021
	   i = point->time + 1;
1022
	}
1023
 
1024
	indent = 0;
1025
	buffer = QString("      </trkseg>\n");
1026
	buffer.append("   </trk>\n");
1027
	buffer.append("</gpx>\n");
1028
	writeTag(qf, buffer, indent);
1029
	qf.close();
1030
	KMessageBox::information(this, i18n("File ") + fn + i18n(" was written successfully."));
1031
}
1032
 
1033
void sportwatcherWidget::saveOSM(const QString &fn)
1034
{
1035
QFile qf;
1036
QString buffer;
1037
RUN_NODE *rn, *rakt;
1038
LAP *lap;
1039
POINT *point;
1040
int indent, id, j;
1041
unsigned int i;
1042
double minLat, minLon, maxLat, maxLon;
1043
QDateTime *qt;
1044
 
1045
	indent = 0;
1046
	rn = ds.getRunNode();
1047
	lap = ds.getLap(rn->run->first_lap_index);
1048
 
1049
	if ((point = ds.getPoint(lap->start_time)) == 0)
1050
	{
1051
	   KMessageBox::error(this, i18n("No data to save!"));
1052
	   return;
1053
	}
1054
 
232 andreas 1055
	qf.setFileName(fn);
172 andreas 1056
 
232 andreas 1057
	if (!qf.open(QIODevice::ReadWrite | QIODevice::Truncate))
172 andreas 1058
	{
232 andreas 1059
	   KMessageBox::error(this, i18n("Error creating file %1!\nPlease check permissions").arg(fn));
172 andreas 1060
	   return;
1061
	}
1062
 
1063
	buffer = QString("<?xml version='1.0' encoding='UTF-8'?>\n");
1064
	buffer.append("<osm version=\"0.5\" generator=\"TheoSys SportWatcher\">\n");
1065
	indent = 0;
1066
	writeTag (qf, buffer, indent);
1067
	// Find the edges of our coordinates
1068
	// We need this information in the header (metadata)
1069
	rakt = rn;
1070
	minLat = -90.0;
1071
	minLon = -180.0;
1072
	maxLat = 90.0;
1073
	maxLon = 180.0;
1074
 
1075
	while (rakt)
1076
	{
1077
	   if (rakt->run->type != data_D1000 && rakt->run->type != data_D1009 &&
1078
	   	rakt->run->type != data_D1010)
1079
	   {
1080
	      rakt = rakt->next;
1081
	      continue;
1082
	   }
1083
 
1084
	   i = rakt->run->first_lap_index;
1085
	   // get the first lap
1086
	   if ((lap = ds.getLap(i)) == NULL)
1087
	      continue;
1088
 
1089
	   i = 0;
1090
	   // iterate the points associated with the laps
1091
	   while ((point = ds.getPoint(i)) != 0)
1092
	   {
1093
	      if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
1094
	      {
1095
		 i = point->time + 1;
1096
		 continue;
1097
	      }
1098
 
1099
	      if (SEMI2DEG(point->posn.lat) > minLat)
1100
		 minLat = SEMI2DEG(point->posn.lat);
1101
 
1102
	      if (SEMI2DEG(point->posn.lat) < maxLat)
1103
		 maxLat = SEMI2DEG(point->posn.lat);
1104
 
1105
	      if (SEMI2DEG(point->posn.lon) > minLon)
1106
		 minLon = SEMI2DEG(point->posn.lon);
1107
 
1108
	      if (SEMI2DEG(point->posn.lon) < maxLon)
1109
		 maxLon = SEMI2DEG(point->posn.lon);
1110
 
1111
	      i = point->time + 1;
1112
	   }
1113
 
1114
	   rakt = rakt->next;
1115
	}
1116
 
1117
	buffer.sprintf("   <bound box='%f,%f,%f,%f' origin='http://www.openstreetmap.org/api/0.5' />\n",
1118
		maxLat, minLon, minLat, maxLon);
1119
	writeTag (qf, buffer, indent);
1120
	indent = 1;
1121
	rn = ds.getRunNode();
1122
	lap = ds.getLap(rn->run->first_lap_index);
1123
	i = 0;
1124
	id = -1;
1125
 
1126
	while ((point = ds.getPoint(i)) != 0)
1127
	{
1128
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
1129
	   {
1130
	      i = point->time + 1;
1131
	      continue;
1132
	   }
1133
 
1134
	   buffer.sprintf("<node id='%d' action='modify' visible='true' lat=\"%f\" lon=\"%f\">\n",
1135
		id, SEMI2DEG(point->posn.lat), SEMI2DEG(point->posn.lon));
1136
	   writeTag(qf, buffer, indent);
1137
	   indent++;
1138
	   buffer = QString("<tag k='created_by' v='TheoSys Sportwatcher' />\n");
1139
	   writeTag(qf, buffer, indent);
1140
	   buffer = QString("<tag k='highway' v='tertiary' />\n");
1141
	   writeTag(qf, buffer, indent);
1142
	   indent--;
1143
	   writeTag(qf, QString("</node>\n"), indent);
1144
	   id--;
1145
	   i = point->time + 1;
1146
	}
1147
 
1148
	qt = garmin_dtime(lap->start_time);
1149
	buffer.sprintf("<way id='%d' action='modify' visible='true' timestamp='%s'>\n",
232 andreas 1150
		id, QString(qt->toString("yyyy-MM-ddThh:mm:ssZ")).toAscii().data());
172 andreas 1151
	writeTag(qf, buffer, indent);
1152
	indent++;
1153
 
1154
	for (j = -1; j > id; j--)
1155
	{
1156
	   buffer.sprintf("<nd ref='%d' />\n", j);
1157
	   writeTag(qf, buffer, indent);
1158
	}
1159
 
1160
	indent--;
1161
	writeTag(qf, QString("</way>\n"), indent);
1162
	indent = 0;
1163
	writeTag(qf, QString("</osm>\n"), indent);
1164
	qf.close();
232 andreas 1165
	KMessageBox::information(this, i18n("File %1 was written successfully.").arg(fn));
172 andreas 1166
}
1167
 
88 andreas 1168
void sportwatcherWidget::fileOpen()
1169
{
1170
QString fname = KFileDialog::getOpenFileName(Data, QString("*.gmn"), this, QString("SportWatcher"));
137 andreas 1171
int m;
88 andreas 1172
 
1173
        if (fname.isEmpty())
1174
           return;
1175
 
1176
	spw.destroy();
1177
 
232 andreas 1178
        if (spw.setFileName(fname.toAscii().data()) == -1)
88 andreas 1179
	   return;
1180
 
1181
	if (gmn)
1182
	   garmin_free_data (gmn);
1183
 
1184
	gmn = spw.readFile();
104 andreas 1185
	zfactor = 0;
137 andreas 1186
 
1187
	if ((m = garmin_count_error()) > 0)
1188
	{
1189
	int i, key = -1;
1190
 
1191
	   for (i = 0; i < m; i++)
1192
	      KMessageBox::error(this, QString(garmin_get_next_error(&key)));
1193
 
1194
	   garmin_clear_errors();
1195
	   return;
1196
	}
1197
 
246 andreas 1198
	DIRTY = true;
250 andreas 1199
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
88 andreas 1200
	showLaps();
100 andreas 1201
	showTrack();
88 andreas 1202
	showCurves();
250 andreas 1203
 
1204
	if (curTab == 2)
1205
	{
1206
	   showThreeCurve();
1207
	   tabDirt2 = false;
1208
	}
1209
 
1210
	tabDirt0 = tabDirt3 = false;
246 andreas 1211
	DIRTY = false;
88 andreas 1212
}
1213
 
217 andreas 1214
void sportwatcherWidget::fileImport()
1215
{
1216
QString fname = KFileDialog::getOpenFileName(QString("~/"), QString("*.tcx"), this, QString("SportWatcher"));
1217
gmn_import import;
1218
int m;
225 andreas 1219
QString tgfile, fld, px;
232 andreas 1220
QPixmap qpx;
225 andreas 1221
QFileInfo datei;
246 andreas 1222
QList<QTreeWidgetItem *>item;
1223
QTreeWidgetItem *el, *it;
225 andreas 1224
LAP *lap;
1225
RUN_NODE *rn;
217 andreas 1226
 
1227
        if (fname.isEmpty())
1228
           return;
1229
 
1230
        import.setFile(fname);
1231
 
1232
	if ((m = import.import()) != 0)
1233
	{
1234
	   KMessageBox::error(this, QString(import.getError(m)));
1235
	   return;
1236
	}
1237
 
1238
	if (gmn)
246 andreas 1239
	{
217 andreas 1240
	   garmin_free_data (gmn);
246 andreas 1241
	   gmn = 0;
1242
	}
217 andreas 1243
 
246 andreas 1244
	if (!(gmn = import.getGarminData ()))
1245
	   return;
217 andreas 1246
 
246 andreas 1247
	DIRTY = true;
250 andreas 1248
	tabDirt0 = tabDirt1 = tabDirt2 = tabDirt3 = true;
217 andreas 1249
	showLaps();
1250
	showTrack();
1251
	showCurves();
250 andreas 1252
 
1253
	if (curTab == 2)
1254
	{
1255
	   showThreeCurve();
1256
	   tabDirt2 = false;
1257
	}
1258
 
1259
	tabDirt0 = tabDirt3 = false;
246 andreas 1260
	DIRTY = false;
225 andreas 1261
 
1262
	// Find the filename;
1263
	// It consists of the date and the time.
1264
	// We need this information to set the correct path to store the file.
1265
	tgfile = Data;		// The base path
1266
	rn = ds.getRunNode();
1267
	lap = ds.getLap(rn->run->first_lap_index);
1268
	QDateTime *qt = garmin_dtime (lap->start_time);
1269
	tgfile.append (qt->toString ("/yyyy")); // year is a folder
1270
	tgfile.append (qt->toString ("/MM"));	// month is a folder
1271
	tgfile.append (qt->toString ("/yyyyMMddThhmmss"));	// The file name
1272
	tgfile.append (".gmn");		// Extension of file name
1273
	datei.setFile (tgfile);
1274
	// save the data to a real file, but only if it doesn't exist allready.
232 andreas 1275
	garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 0);
225 andreas 1276
 
1277
	// in case the item is already in the list on the left side, we
1278
	// only highlight the item.
246 andreas 1279
	item = ui_sportwatcherWidgetBase.liActivities->findItems (tgfile, Qt::MatchExactly);
1280
 
1281
	if (item.size() > 0)
225 andreas 1282
	{
246 andreas 1283
	   ui_sportwatcherWidgetBase.liActivities->setItemSelected (item.at(0), true);
1284
	   ui_sportwatcherWidgetBase.liActivities->setCurrentItem (item.at(0));
225 andreas 1285
	   return;
1286
	}
1287
 
1288
	// insert everything into the list on the left side
1289
	switch (rn->run->sport_type)
1290
	{
1291
	   case D1000_running:
1292
	      fld = i18n("Running");
247 andreas 1293
	      px = QString("spw-running");
225 andreas 1294
	   break;
1295
 
1296
	   case D1000_biking:
1297
	      fld = i18n("Biking");
245 andreas 1298
	      px = QString("bike");
225 andreas 1299
	   break;
1300
 
1301
	   default:
1302
	      fld = i18n("Others");
245 andreas 1303
	      px = QString("other");
225 andreas 1304
	}
1305
 
1306
	// Do we have allready so items in the list?
246 andreas 1307
	item = ui_sportwatcherWidgetBase.liActivities->findItems (fld, Qt::MatchExactly);
1308
 
1309
	if (item.size() > 0)
225 andreas 1310
	{
246 andreas 1311
	   el = new QTreeWidgetItem(item.at(0));
1312
	   el->setText(0, kl->formatDateTime(*qt, KLocale::ShortDate, true));
1313
	   el->setData(0, Qt::UserRole, tgfile);
1314
	   el->setIcon(0, KIcon(px));
225 andreas 1315
	}
1316
	else	// no, this is the first item. (shouldn't be!)
1317
	{
246 andreas 1318
	   it = new QTreeWidgetItem(ui_sportwatcherWidgetBase.liActivities);
1319
	   it->setText(0, fld);
1320
	   it->setIcon(0, KIcon(QString("history")));
1321
 
1322
	   el = new QTreeWidgetItem(item.at(0));
1323
	   el->setText(0, kl->formatDateTime(*qt, KLocale::ShortDate, true));
1324
	   el->setData(0, Qt::UserRole, tgfile);
1325
	   el->setIcon(0, KIcon(px));
225 andreas 1326
	}
217 andreas 1327
}
1328
 
218 andreas 1329
/*
1330
 * Display a small dialog to rename the currently loaded session.
1331
 */
1332
void sportwatcherWidget::editRename()
1333
{
1334
bool ok;
1335
QString name, inhalt;
246 andreas 1336
QList<QTreeWidgetItem *> item;
1337
QTreeWidgetItem *lvItem;
218 andreas 1338
QFileInfo datei;
1339
RUN_NODE *rn;
1340
LAP *lap;
1341
garmin_list *list;
1342
D1009 *n;
1343
 
1344
	if (!gmn)
1345
	{
1346
	   KMessageBox::error(this, i18n("There is no session selected!"));
1347
	   return;
1348
	}
1349
 
1350
	rn = ds.getRunNode();
246 andreas 1351
	item = ui_sportwatcherWidgetBase.liActivities->selectedItems ();
218 andreas 1352
	lvItem = item.first();
1353
 
1354
	if (!isdigit(rn->run->workout.name[0]))
1355
	   inhalt = lvItem->text(0);
1356
	else
246 andreas 1357
	   inhalt.clear();
218 andreas 1358
 
1359
	name = KInputDialog::getText(i18n("Rename session"), i18n("Session name"),
246 andreas 1360
		inhalt, &ok, this, (QValidator *)0, QString("Nxxxxxxxxxxxxxx"),
1361
		i18n("Enter a new name for the currently selected activity."));
218 andreas 1362
 
1363
	if (!ok)
1364
	   return;
1365
 
1366
	if (name.length() <= 1)
1367
	{
1368
	   lap = ds.getLap(rn->run->first_lap_index);
1369
	   const QDateTime *qt = garmin_dtime (lap->start_time);
232 andreas 1370
	   QString idx = kl->formatDateTime(*qt, KLocale::ShortDate, true);
218 andreas 1371
	   lvItem->setText (0, idx);
246 andreas 1372
	   datei.setFile (lvItem->data(0, Qt::UserRole).toString());
232 andreas 1373
	   garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 1);
218 andreas 1374
	   delete qt;
1375
	   return;
1376
	}
1377
 
232 andreas 1378
	strncpy (rn->run->workout.name, name.toAscii().data(), 16);
218 andreas 1379
 
1380
	if (gmn->type != data_Dlist)
1381
	{
1382
	   KMessageBox::error(this, i18n("editRename: Unexpected structure type %1 found!").arg(gmn->type));
1383
	   return;
1384
	}
1385
 
1386
	list = (garmin_list *)gmn->data;
1387
 
1388
	if (list->head->data->type != data_D1009)	// This should be the run node
1389
	{
1390
	   KMessageBox::error(this, i18n("editRename: The run node was not found!"));
1391
	   return;
1392
	}
1393
 
1394
	n = (D1009 *)list->head->data->data;
246 andreas 1395
	strncpy (n->workout.name, rn->run->workout.name, 15);
218 andreas 1396
	lvItem->setText (0, name);
246 andreas 1397
	datei.setFile (lvItem->data(0, Qt::UserRole).toString());
232 andreas 1398
	garmin_save_all (gmn, datei.fileName().toAscii().data(), datei.absolutePath().toAscii().data(), 1);
218 andreas 1399
}
1400
 
88 andreas 1401
void sportwatcherWidget::fileNew()
1402
{
232 andreas 1403
progressWidget *dlg = new progressWidget(this);
96 andreas 1404
 
100 andreas 1405
	dlg->show();
137 andreas 1406
 
1407
	if (!dlg->Download())
1408
	{
1409
	int m, key;
1410
 
1411
	   key = -1;
1412
 
1413
	   for (m = 0; m < garmin_count_error(); m++)
1414
	      KMessageBox::error(this, QString(garmin_get_next_error(&key)));
1415
	}
1416
	else
1417
	   getActivities();
1418
 
1419
	garmin_clear_errors();
96 andreas 1420
	delete dlg;
88 andreas 1421
}
1422
 
1423
/*
1424
 * This function is called, when the user clicks at the menu point
1425
 * "Save Heart Rate".
1426
 * First, a file dialog box is displayed, where the user can choose a
1427
 * directory and a file name to save the heart rate.
1428
 * If the file could successfully be created, the heart rate is saved
137 andreas 1429
 * in the "HRM"-format. This is the native format Polar uses to store
88 andreas 1430
 * heart rate data. I've choosen this format, because it's popular and
1431
 * used by many other software too.
1432
 */
1433
void sportwatcherWidget::extrasSaveHR()
1434
{
1435
QString fname, str1, str2;
1436
QFile fdfile;
1437
QDateTime *qt, *oldqt;
1438
QDate dat;
1439
QTime t;
1440
QDir dir = QDir::home();
1441
char hv0[256];
1442
RUN_NODE *rn;
1443
LAP *lap, *alap;
1444
POINT *point;
1445
int samples, smp, seconds, anz, nsec, samsec;
1446
int avgHeart, minHeart, maxHeart, aktHeart;
1447
int secRange1, secRange2, secRange3, secAbove, secBeyond;
1448
 
1449
	if (!gmn)
1450
	{
1451
	   KMessageBox::information(this, i18n("There is no activity open"));
1452
	   return;
1453
	}
1454
 
1455
	if (HRM.isEmpty())
1456
	   str1 = dir.path();
1457
	else
1458
	   str1 = HRM;
1459
 
1460
	str1 +=  "/" + StartTime.toString("yyyyMMddThhmmss.zzz.hrm");
1461
	fname = KFileDialog::getSaveFileName(str1, QString("*.hrm"), this, QString("SportWatcher"));
1462
 
1463
	if (fname.isEmpty())
1464
	   return;
1465
 
232 andreas 1466
	fdfile.setFileName(fname);
88 andreas 1467
 
1468
	if (fdfile.exists())
1469
	{
1470
	   if (KMessageBox::questionYesNo(this, i18n("Do you really want to overwrite this file?")) == KMessageBox::No)
1471
	      return;
1472
	}
1473
 
232 andreas 1474
	if (!fdfile.open(QIODevice::ReadWrite | QIODevice::Truncate))
88 andreas 1475
	{
1476
	   KMessageBox::error(this, i18n("Error creating a file!\nPlease check permissions."));
1477
	   return;
1478
	}
1479
 
1480
	rn = ds.getRunNode();
1481
	lap = ds.getLap(rn->run->first_lap_index);
1482
	t = StartTime.time();
1483
	dat = StartTime.date();
1484
 
1485
	if ((point = ds.getPoint(lap->start_time)) == 0)
1486
	{
1487
	   fdfile.close();
1488
	   return;
1489
	}
1490
 
1491
	strcpy (hv0, "[Params]\n");
1492
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1493
	str1 = dat.toString("yyyyMMdd");
1494
	str2 = t.toString("hh:mm:ss.z");
1495
	sprintf(hv0, "Version=106\nMonitor=11\nSMode=000000000\nDate=%s\nStartTime=%s\n",
232 andreas 1496
		str1.toAscii().data(), str2.toAscii().data());
88 andreas 1497
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1498
	t.setHMS(0, 0, 0);
1499
	t = t.addSecs(max_time);
1500
	str2 = t.toString("hh:mm:ss.z");
1501
 
1502
	switch (sampleTime)
1503
	{
1504
	   case 0: samsec = 5; break;
1505
	   case 1: samsec = 15; break;
1506
	   case 2: samsec = 30; break;
1507
	   case 3: samsec = 60; break;
1508
	   default:
1509
	      samsec = 15;
1510
	}
1511
 
1512
	sprintf(hv0, "Length=%s\nInterval=%d\nUpper1=%d\nLower1=%d\nUpper2=%d\n",
232 andreas 1513
	   str2.toAscii().data(), samsec, upper1, lower1, upper2);
88 andreas 1514
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1515
	sprintf(hv0, "Lower2=%d\nUpper3=%d\nLower3=%d\nTimer1=00:00:00.0\n",
1516
		lower2, upper3, lower3);
1517
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1518
	strcpy(hv0, "Timer2=00:00:00.0\nTimer3=00:00:00.0\nActiveLimit=0\n");
1519
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1520
	sprintf(hv0, "MaxHR=%d\nRestHR=%d\nStartDelay=0\nVO2max=%d\nWeight=%d\n\n",
1521
		MaxHr, restHr, vo2max, weight);
1522
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1523
 
1524
	// Write the intervall times. One block for every lap
1525
	secRange1 = secRange2 = secRange3 = secAbove = secBeyond = 0;
1526
	strcpy(hv0, "[IntTimes]\n");
1527
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1528
	t.setHMS(0, 0, 0);
1529
 
1530
	for (unsigned int i = rn->run->first_lap_index; i < rn->run->last_lap_index; i++)
1531
	{
1532
	   alap = ds.getLap(i);
1533
	   point = ds.getPoint(alap->start_time);
1534
	   oldqt = garmin_dtime(point->time);
1535
	   avgHeart = minHeart = maxHeart = aktHeart = 0;
1536
	   anz = 0;
1537
	   unsigned long lastTime = point->time;
1538
	   int totSec = 0;
1539
 
1540
	   while (point)
1541
	   {
1542
	      if (point->time > (alap->start_time + (alap->total_time / 100)))
1543
		 break;
1544
 
1545
	      if (point->heart_rate > 0)
1546
	      {
1547
		 avgHeart += point->heart_rate;
1548
		 nsec = point->time - lastTime;
1549
		 totSec += nsec;
1550
 
1551
		 if (minHeart == 0 || minHeart > point->heart_rate)
1552
		    minHeart = point->heart_rate;
1553
 
1554
		 if (maxHeart < point->heart_rate)
1555
		    maxHeart = point->heart_rate;
1556
 
1557
		 if (aktHeart == 0 && totSec >= samsec)
1558
		    aktHeart = avgHeart / (anz + 1);
1559
 
1560
		 if (point->heart_rate < lower1)
1561
		    secBeyond += nsec;
1562
		 else if (point->heart_rate < lower2)
1563
		    secRange1 += nsec;
1564
		 else if (point->heart_rate < lower3)
1565
		    secRange2 += nsec;
1566
		 else if (point->heart_rate < upper3)
1567
		    secRange3 += nsec;
1568
		 else
1569
		    secAbove += nsec;
1570
 
1571
		 lastTime = point->time;
1572
		 anz++;
1573
	      }
1574
 
1575
	      point = ds.getPoint(point->time+1);
1576
	   }
1577
 
1578
	   t = t.addSecs(alap->total_time / 100);
1579
	   str1 = t.toString("hh:mm:ss.z");
166 andreas 1580
 
1581
	   if (anz > 0)
1582
	      avgHeart = avgHeart / anz;
1583
	   else
1584
	      avgHeart = 0;
1585
 
88 andreas 1586
	   sprintf(hv0, "%s\t %d\t %d\t %d\t %d\n",
232 andreas 1587
	      str1.toAscii().data(), aktHeart, minHeart, avgHeart, maxHeart);
88 andreas 1588
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1589
	   strcpy(hv0, "32\t 0\t 0\t 0\t 0\t 0\n");
1590
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1591
	   strcpy(hv0, "0\t 0\t 0\t 0\t 0\n");
1592
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1593
	   sprintf(hv0, "0\t %d\t 0\t 0\t 0\t 0\n", (int)alap->total_distance);
1594
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1595
	   strcpy(hv0, "0\t 0\t 0\t 0\t 0\t 0\n");
1596
	   write(fdfile.handle(), &hv0[0], strlen(hv0));
1597
	}
1598
 
1599
	strcpy(hv0, "\n[IntNotes]\n\n[ExtraData]\n\n");
1600
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1601
 
1602
	strcpy(hv0, "[Summary-123]\n");
1603
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 1
1604
	smp = max_time - secBeyond - secRange1 - secRange2 - secRange3 - secAbove;
245 andreas 1605
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
88 andreas 1606
		max_time, secRange1, secRange2 + secRange3,
1607
		secAbove + secBeyond, smp);
1608
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 1
1609
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
1610
		MaxHr, upper1, lower1, restHr);
1611
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 1
245 andreas 1612
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
88 andreas 1613
		max_time, secRange2, secRange1 + secRange3,
1614
		secAbove + secBeyond, smp);
1615
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 2
1616
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
1617
		MaxHr, upper2, lower2, restHr);
1618
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 2
245 andreas 1619
	sprintf(hv0, "%lu\t %u\t %u\t %u\t %u\n",
88 andreas 1620
		max_time, secRange3, secRange1 + secRange2,
1621
		secAbove + secBeyond, smp);
1622
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// limits 3
1623
	sprintf(hv0, "%d\t %d\t %d\t %d\n",
1624
		MaxHr, upper3, lower3, restHr);
1625
	write(fdfile.handle(), &hv0[0], strlen(hv0));	// Time limits 3
1626
	samples = max_time / samsec;
1627
	sprintf(hv0, "0\t %u\n\n", samples);	// samples
1628
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1629
 
1630
	strcpy(hv0, "[Summary-TH]\n");
1631
	write(fdfile.handle(), &hv0[0], strlen(hv0));
245 andreas 1632
	sprintf(hv0, "%lu\t 0\t %lu\t %d\t %d\t 0\n", max_time, max_time - max_hr - restHr, max_hr, restHr);
88 andreas 1633
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1634
	sprintf(hv0, "%d\t %d\t %d\t %d\n", MaxHr, upper3, lower1, restHr);
1635
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1636
	sprintf(hv0, "0\t %u\n\n", samples);	// samples
1637
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1638
 
1639
	sprintf(hv0, "[HRZones]\n%d\n%d\n%d\n%d\n%d\n%d\n0\n0\n0\n0\n0\n\n",
1640
		MaxHr, upper3, upper2, upper1, lower1, restHr);
1641
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1642
 
1643
	strcpy(hv0, "[HRData]\n");
1644
	write(fdfile.handle(), &hv0[0], strlen(hv0));
1645
 
1646
	smp = 0;		// average heart rate of 15 seconds
1647
	seconds = 0;
1648
	anz = 0;
1649
	nsec = samsec;
1650
	oldqt = garmin_dtime(lap->start_time);
1651
	qt = 0;
1652
	point = ds.getPoint(lap->start_time);
1653
 
1654
	while (point)
1655
	{
1656
	   if (seconds >= nsec)
1657
	   {
1658
	      if (anz > 0)
1659
	      {
1660
		 sprintf(hv0, "%d\n", smp / anz);
1661
		 write(fdfile.handle(), &hv0[0], strlen(hv0));
1662
	      }
1663
 
1664
	      if (smp > 0 && seconds >= (nsec + samsec))
1665
	      {
1666
		 if (anz <= 0)
1667
		    anz = 0;
1668
 
1669
		 for (int x = nsec; x < seconds; x += samsec)
1670
		 {
1671
		    sprintf(hv0, "%d\n", smp / anz);
1672
		    write(fdfile.handle(), &hv0[0], strlen(hv0));
1673
		    nsec += samsec;
1674
		 }
1675
	      }
1676
 
1677
	      anz = 0;
1678
	      smp = 0;
1679
	      nsec += samsec;
1680
	   }
1681
 
1682
	   qt = garmin_dtime (point->time);
1683
	   seconds += oldqt->secsTo(*qt);
1684
 
1685
	   if (point->heart_rate > 0)
1686
	   {
1687
	      smp += point->heart_rate;
1688
	      anz++;
1689
	   }
1690
 
1691
	   delete oldqt;
1692
	   oldqt = qt;
1693
	   point = ds.getPoint(point->time + 1);
1694
	}
1695
 
1696
	fdfile.close();
1697
	KMessageBox::information(this, i18n("File successfully written."));
1698
}
1699
 
1700
void sportwatcherWidget::extrasSettings()
1701
{
232 andreas 1702
settingsWidget *dlg = new settingsWidget(this);
88 andreas 1703
 
1704
	if (dlg->exec() == QDialog::Accepted)
1705
	{
232 andreas 1706
	   KConfig cfg(QString("sportwatcher.rc"), KConfig::SimpleConfig);
1707
	   KConfigGroup ic (&cfg, "SportWatcher");
1708
	   lower1 = ic.readEntry("lower1", 0);
1709
	   lower2 = ic.readEntry("lower2", 0);
1710
	   lower3 = ic.readEntry("lower3", 0);
1711
	   upper1 = ic.readEntry("upper1", 0);
1712
	   upper2 = ic.readEntry("upper2", 0);
1713
	   upper3 = ic.readEntry("upper3", 0);
1714
	   MaxHr = ic.readEntry("maxHr", 0);
1715
	   restHr = ic.readEntry("restHr", 0);
1716
	   vo2max = ic.readEntry("vo2max", 0);
1717
	   weight = ic.readEntry("weight", 0);
1718
	   sampleTime = ic.readEntry("seconds", 1);
1719
	   Serial = ic.readEntry("Serial", false);
1720
	   Contour = ic.readEntry("Contour", false);
1721
	   Device = ic.readEntry("Device", QString("/dev/ttyUSB0"));
1722
	   Forerunner = ic.readEntry("Forerunner", false);
1723
	   Data = ic.readEntry("Data", QString("/"));
1724
	   HRM = ic.readEntry("HRM", QString("/"));
1725
	   MAP = ic.readEntry("MAP", QString("/"));
1726
	   Units = ic.readEntry("Units", 0);
1727
	   MapType = ic.readEntry("MapType", 0);
88 andreas 1728
	}
1729
 
1730
	delete dlg;
152 andreas 1731
}
151 andreas 1732
 
152 andreas 1733
void sportwatcherWidget::extrasWMSSettings()
1734
{
232 andreas 1735
#if defined HAVE_GDAL
158 andreas 1736
	if (MapType == MPT_BMP || MapType == MPT_GIF || MapType == MPT_PNG ||
1737
	    MapType == MPT_TIF)
1738
	{
232 andreas 1739
	   coordinatesWidget *idlg = new coordinatesWidget(this);
158 andreas 1740
	   idlg->exec();
1741
	   delete idlg;
1742
	   return;
1743
	}
1744
 
1745
	if (MapType != MPT_WMS)
1746
	{
1747
	   KMessageBox::detailedSorry(this,
1748
	      i18n("You have not choosen a WMS tag file!"),
1749
	      i18n("This dialog is especialy to set WMS specific parameters. ") +
1750
	      i18n("Therefore this dialog is temporary disabled. It will be ") +
1751
	      i18n("available again, as soon as you choose \"WMS server\" as ") +
1752
	      i18n("your map type."));
1753
	      return;
1754
	}
151 andreas 1755
 
232 andreas 1756
	wmsbase *dlg = new wmsbase(this);
152 andreas 1757
	dlg->exec();
1758
	delete dlg;
156 andreas 1759
#else
1760
	KMessageBox::detailedSorry(this,
1761
	   i18n("This function was disabled at compile time because of missing GDAL v1.5.x!"),
1762
	   i18n("Sportwatcher needs GDAL v1.5.x to enable this function.\n") +
1763
	   i18n("If you like this to be working, install GDAL version 1.5.x and recompile the source!"));
1764
#endif
88 andreas 1765
}
1766
 
1767
/*
1768
 * Functions to fill in the boxes of the main mask.
1769
 */
1770
void sportwatcherWidget::showLaps()
1771
{
1772
QString qs_name, qs_distance, qs_etime, qs_avgpace, qs_avgspeed, qs_maxspeed;
1773
QString qs_calories, qs_avghr, qs_maxhr, qs_avgcadence, qs_ascent, qs_descent;
248 andreas 1774
QString qs_totdist;
88 andreas 1775
QDateTime dt;
1776
QTime t, st;
249 andreas 1777
QDateTime *qt;
1778
LAP *lap;
1779
POINT *point;
88 andreas 1780
RUN_NODE *rakt, *rn;
149 andreas 1781
int laps, i, anz, men, cad;
249 andreas 1782
double alt_asc, alt_dsc, sum_asc, sum_dsc, old_asc, old_dsc;
248 andreas 1783
double totdist;
216 andreas 1784
bool pause;
88 andreas 1785
 
246 andreas 1786
	if (!DIRTY)
1787
	   return;
1788
 
88 andreas 1789
	if (!gmn)
1790
	{
248 andreas 1791
//	   KMessageBox::error(this, i18n("No data were loaded!"));
88 andreas 1792
	   return;
1793
	}
1794
 
1795
	if (gmn->type == data_Dnil)
1796
	{
1797
	   KMessageBox::error(0, i18n("No data found!"));
1798
	   return;
1799
	}
1800
 
1801
	if (gmn->type != data_Dlist)     /* List of data */
1802
	{
221 andreas 1803
	   KMessageBox::error(0, i18n("Found unexpected data type %1!").arg(gmn->type));
88 andreas 1804
	   return;
1805
	}
1806
 
1807
	ds.destroy();
221 andreas 1808
	min_hr = max_hr = avg_hr = 0;
88 andreas 1809
	min_height = max_height = 0.0;
218 andreas 1810
	min_speed = max_speed = 0.0;
248 andreas 1811
	totdist = 0.0;
1812
	// Tab Summary
245 andreas 1813
	ui_sportwatcherWidgetBase.liLaps->clear();
1814
	ui_sportwatcherWidgetBase.liLaps->setAllColumnsShowFocus(true);
248 andreas 1815
	// Tab Laps
1816
	ui_sportwatcherWidgetBase.twLaps->clear();
88 andreas 1817
	ds.garmin_print_data(gmn);
245 andreas 1818
 
1819
	if (!(rn = ds.getRunNode()))
1820
	   return;
1821
 
88 andreas 1822
	rakt = rn;
248 andreas 1823
	// Tab Summary
245 andreas 1824
	ui_sportwatcherWidgetBase.liLaps->setRootIsDecorated(true);
88 andreas 1825
 
248 andreas 1826
	for (i = 1; i < 12; i++)
1827
	   ui_sportwatcherWidgetBase.liLaps->setColumnAlignment(i, Qt::AlignRight);
1828
	// Tab Laps
1829
	ui_sportwatcherWidgetBase.edTotalDistance->clear();
1830
	ui_sportwatcherWidgetBase.edTotalTime->clear();
1831
	ui_sportwatcherWidgetBase.edAvgSpeed->clear();
1832
	ui_sportwatcherWidgetBase.edTotalHeight->clear();
1833
	ui_sportwatcherWidgetBase.edAvgHR->clear();
1834
	ui_sportwatcherWidgetBase.edLapNumber->clear();
1835
 
88 andreas 1836
	qs_name = qs_distance = qs_etime = qs_avgpace = qs_avgspeed = qs_maxspeed = QString("");
1837
	qs_calories = qs_avghr = qs_maxhr = qs_avgcadence = qs_ascent = qs_descent = QString("");
1838
	men = 0;
149 andreas 1839
	cad = 0;
88 andreas 1840
 
249 andreas 1841
	// The main loop.
1842
	// If a supported run type is detected, it will be processed.
88 andreas 1843
	while (rakt)
1844
	{
249 andreas 1845
	   // Check for a supported run type
88 andreas 1846
	   if (rakt->run->type == data_D1000 || rakt->run->type == data_D1009 ||
1847
	   	rakt->run->type == data_D1010)
1848
	   {
249 andreas 1849
	   int cal, ahr, mhr;
88 andreas 1850
	   double distance, speed, mspeed;
1851
	   QDate dat;
1852
 
249 andreas 1853
	      // Set the name depending on the sport type
1854
	      // This is used on the tab "Summary"
88 andreas 1855
	      switch (rakt->run->sport_type)
1856
	      {
1857
		 case D1000_running: qs_name = QString("Running: "); break;
1858
		 case D1000_biking:  qs_name = QString("Biking: "); break;
1859
		 case D1000_other:   qs_name = QString("Other: "); break;
1860
		 default:
1861
		    qs_name = QString("Unknown: ");
1862
	      }
1863
 
245 andreas 1864
	      if (!(lap = ds.getLap(rakt->run->first_lap_index)))
1865
		 return;
1866
 
88 andreas 1867
	      qt = garmin_dtime (lap->start_time);
1868
	      StartTime = *qt;
1869
	      st = qt->time();
1870
	      dat = qt->date();
1871
	      delete qt;
213 andreas 1872
	      qt = 0;
1873
	      // Find the last track;
1874
	      //    It is possible to delete laps directly on the watch,
1875
	      //    so we can't be sure the last lap is really the last one.
1876
	      //    Tracks are not deleted and the last track contains the
1877
	      //    summuraries we need.
249 andreas 1878
	      if (!(point = ds.getLastPoint()))
213 andreas 1879
	      {
249 andreas 1880
		 KMessageBox::error(this, i18n("Error getting the last messure point!"));
245 andreas 1881
		 return;
215 andreas 1882
	      }
1883
 
249 andreas 1884
	      qt = garmin_dtime(point->time);
1885
	      t = qt->time();
88 andreas 1886
	      t.setHMS(0, 0, 0);
249 andreas 1887
	      t = t.addSecs(ds.getTotalTime());
88 andreas 1888
	      qt->setDate(dat);
1889
	      qt->setTime(t);
232 andreas 1890
	      qs_name.append(kl->formatDate(dat, KLocale::ShortDate));
88 andreas 1891
	      qs_name.append(" ");
230 andreas 1892
	      qs_name.append(kl->formatTime(st, true));
249 andreas 1893
	      max_time = ds.getTotalTime();
88 andreas 1894
	      qs_etime = QString(qt->toString("hh:mm:ss.zzz"));
1895
 
1896
	      distance = 0.0;
1897
	      cal = 0;
1898
	      mspeed = 0;
1899
	      ahr = mhr = 0;
1900
	      anz = 0;
149 andreas 1901
	      cad = 0;
1902
	      sum_asc = sum_dsc = old_asc = old_dsc = 0;
88 andreas 1903
 
249 andreas 1904
	      // Find out the total distance, calories, maximum speed,
1905
	      // maximum heart rate and the average heart rate. Get this
1906
	      // values from the lap summary the watch made.
88 andreas 1907
	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
1908
	      {
1909
		 if ((lap = ds.getLap(i)) == NULL)
1910
		    continue;
1911
 
1912
		 distance += lap->total_distance;
1913
		 cal += lap->calories;
149 andreas 1914
 
1915
		 if (lap->avg_cadence != 0xff)
1916
		    cad += lap->avg_cadence;
1917
 
88 andreas 1918
		 ahr += lap->avg_heart_rate;
1919
		 anz++;
1920
 
1921
		 if (lap->max_speed > mspeed)
1922
		    mspeed = lap->max_speed;
1923
 
1924
		 if (lap->max_heart_rate > mhr)
1925
		    mhr = lap->max_heart_rate;
1926
	      }
1927
 
249 andreas 1928
	      total_distance = distance = ds.getTotalDistance();
213 andreas 1929
 
149 andreas 1930
	      if (Units == 1)		// Statute?
1931
		 qs_distance.sprintf("%.2f ft", distance / 0.304);
1932
	      else
1933
	         qs_distance.sprintf("%.2f m", distance);
1934
 
88 andreas 1935
	      if (distance > 0)
1936
	      {
1937
		 QTime tt = qt->time();
156 andreas 1938
		 long secs = (double)(tt.hour() * 3600 + tt.minute() * 60 + tt.second()) / 100.0;
1939
 
1940
		 if (Units == 0)
1941
		    secs = secs * (1000.0 / distance * 100.0);
1942
		 else
1943
		    secs = secs * (1609.344 / distance * 100.0);
1944
 
88 andreas 1945
		 int h = secs / 3600;
1946
		 int m = (secs - (h * 3600)) / 60;
1947
		 int s = secs - ((h * 3600) + (m * 60));
1948
		 t = QTime(h, m, s, 0);
230 andreas 1949
		 qs_avgpace = QString("  ") + kl->formatTime(t, true);
156 andreas 1950
 
1951
		 if (Units == 0)
1952
		    qs_avgpace.append(QString(" /km"));
1953
		 else
1954
		    qs_avgpace.append(QString(" /mi"));
88 andreas 1955
	      }
1956
 
149 andreas 1957
	      if (Units == 1)		// Statute?
249 andreas 1958
		 speed = distance / ds.getTotalTime() * 3.6 / 1.609344;
149 andreas 1959
	      else
249 andreas 1960
		 speed = distance / ds.getTotalTime() * 3.6;
149 andreas 1961
 
156 andreas 1962
	      qs_avgspeed.sprintf("%.2f %s", speed, (Units == 1) ? "mph" : "km/h");
1963
	      qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? mspeed * 3.6 / 1.609344 : mspeed * 3.6, (Units == 1) ? "mph" : "km/h");
88 andreas 1964
	      qs_calories.sprintf("%d", cal);
1965
	      qs_avghr.sprintf("%d bpm", ahr / anz);
1966
	      qs_maxhr.sprintf("%d bpm", mhr);
1967
 
149 andreas 1968
	      if (cad > 0)
1969
		 qs_avgcadence.sprintf("%d", cad / anz);
1970
 
248 andreas 1971
	      // Add the summary line columns to the tab "Summary"
1972
	      K3ListViewItem *element = new K3ListViewItem(ui_sportwatcherWidgetBase.liLaps,
1973
	        qs_name, kl->formatNumber(qs_distance, false),
1974
		qs_etime, qs_avgpace, kl->formatNumber(qs_avgspeed, false),
1975
		kl->formatNumber(qs_maxspeed, false), qs_calories, qs_avghr);
88 andreas 1976
	      element->setText(8, qs_maxhr);
1977
	      element->setText(9, qs_avgcadence);
248 andreas 1978
	      element->setText(10, kl->formatNumber(qs_ascent, false));
1979
	      element->setText(11, kl->formatNumber(qs_descent, false));
88 andreas 1980
	      element->sortChildItems(0, false);
1981
	      element->setOpen(true);
245 andreas 1982
	      element->setPixmap(0, KIcon(QString("activity")).pixmap(16));
1983
	      ui_sportwatcherWidgetBase.liLaps->insertItem(element);
248 andreas 1984
	      // Add some of the summaries to the fields on the tab "Lap details"
1985
	      ui_sportwatcherWidgetBase.edTotalDistance->setAlignment(Qt::AlignRight);
1986
	      ui_sportwatcherWidgetBase.edTotalDistance->insert(kl->formatNumber(qs_distance, false));
1987
	      ui_sportwatcherWidgetBase.edTotalTime->setAlignment(Qt::AlignRight);
1988
	      ui_sportwatcherWidgetBase.edTotalTime->insert(qs_etime);
1989
	      ui_sportwatcherWidgetBase.edAvgSpeed->setAlignment(Qt::AlignRight);
1990
	      ui_sportwatcherWidgetBase.edAvgSpeed->insert(kl->formatNumber(qs_avgspeed, false));
1991
	      ui_sportwatcherWidgetBase.edTotalHeight->setAlignment(Qt::AlignRight);
1992
	      ui_sportwatcherWidgetBase.edTotalHeight->insert(kl->formatNumber(qs_ascent, false));
1993
	      ui_sportwatcherWidgetBase.edAvgHR->setAlignment(Qt::AlignRight);
1994
	      ui_sportwatcherWidgetBase.edAvgHR->insert(qs_avghr);
88 andreas 1995
	      delete qt;
1996
	      /* Get the laps. */
1997
	      laps = 1;
1998
 
1999
	      for (i = rakt->run->first_lap_index; (unsigned int)i <= rakt->run->last_lap_index; i++)
2000
	      {
149 andreas 2001
		 double spd;
2002
		 char *un;
2003
 
88 andreas 2004
		 if ((lap = ds.getLap(i)) == NULL)
2005
		    continue;
2006
 
2007
		 qt = garmin_dtime (lap->start_time);
2008
		 qs_name.sprintf("Lap %03d - ", laps);
230 andreas 2009
		 qs_name.append(kl->formatTime(qt->time(), true));
149 andreas 2010
		 qs_distance.sprintf("%.2f %s", (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance, (Units == 1) ? "ft" : "m");
248 andreas 2011
		 totdist += (Units == 1) ? lap->total_distance / 0.304 : lap->total_distance;
2012
		 qs_totdist.sprintf("%2.f %s", totdist, (Units == 1) ? "ft" : "m");
88 andreas 2013
		 t = QTime(0, 0, 0, 0);
2014
		 t = t.addMSecs(lap->total_time * 10);
2015
		 qs_etime = t.toString("hh:mm:ss.zzz");
149 andreas 2016
		 spd = lap->total_distance / (lap->total_time / 100.0);
249 andreas 2017
		 delete qt;
2018
		 qt = 0;
149 andreas 2019
 
2020
		 if (Units == 0)
2021
		 {
2022
		    un = (char *)"km/h";
2023
		    spd *= 3.6;
2024
		 }
2025
		 else
156 andreas 2026
		 {
2027
		    spd *= 3.6 / 1.609344;
2028
		    un = (char *)"mph";
2029
		 }
149 andreas 2030
 
2031
		 qs_avgspeed.sprintf("%.2f %s", spd, un);
156 andreas 2032
		 qs_maxspeed.sprintf("%.2f %s", (Units == 1) ? lap->max_speed * 3.6 / 1.609344 : lap->max_speed * 3.6, un);
88 andreas 2033
		 qs_calories.sprintf("%d", lap->calories);
2034
 
2035
		 if (lap->total_distance > 0 && lap->total_time != 0)
2036
		 {
156 andreas 2037
		    double fact;
2038
 
2039
		    if (Units == 0)
2040
		       fact = 1000.0;		// 1 km
2041
		    else
2042
		       fact = 1609.344;		// 1 mile in meters
2043
 
2044
		    long secs = (double)lap->total_time / 10000.0 * (fact / lap->total_distance * 100.0);
88 andreas 2045
		    int h = secs / 3600;
2046
		    int m = (secs - (h * 3600)) / 60;
2047
		    int s = secs - ((h * 3600) + (m * 60));
2048
		    t = QTime(h, m, s, 0);
230 andreas 2049
		    qs_avgpace = kl->formatTime(t, true);
156 andreas 2050
 
2051
		    if (Units == 0)
2052
		       qs_avgpace.append(QString(" /km"));
2053
		    else
2054
		       qs_avgpace.append(QString(" /mi"));
88 andreas 2055
		 }
2056
 
2057
		 qs_avghr.sprintf("%d bpm", lap->avg_heart_rate);
2058
		 qs_maxhr.sprintf("%d bpm", lap->max_heart_rate);
2059
 
2060
		 anz = 0;
2061
		 alt_asc = alt_dsc = 0;
249 andreas 2062
		 // Add a new detail line to the tab "Lap details"
2063
		 QTreeWidgetItem * trdetail = new QTreeWidgetItem(ui_sportwatcherWidgetBase.twLaps);
88 andreas 2064
 
2065
		 if ((point = ds.getPoint(lap->start_time)) != 0)
2066
		 {
2067
		    if (point->alt < 20000)
149 andreas 2068
		    {
88 andreas 2069
		       alt_dsc = alt_asc = point->alt;
149 andreas 2070
 
2071
		       if (old_asc == 0)
2072
			  old_asc = alt_asc;
2073
 
2074
		       if (old_dsc == 0)
2075
			  old_dsc = alt_dsc;
2076
		    }
88 andreas 2077
		    else
2078
		       alt_dsc = alt_asc = 0;
2079
 
218 andreas 2080
		    POINT *oldPoint = 0;
2081
		    double sc, dist, speed;
222 andreas 2082
		    unsigned long t1, t2;
2083
		    t1 = t2 = 0;
2084
		    pause = false;
2085
		    bool ignore = false;
218 andreas 2086
 
88 andreas 2087
		    while (point)
2088
		    {
2089
		       if (point->time > (lap->start_time + (lap->total_time / 100)))
2090
			 break;
2091
 
249 andreas 2092
		       QTreeWidgetItem *child = new QTreeWidgetItem(trdetail);
2093
		       qt = garmin_dtime (point->time);
2094
		       child->setText(0, kl->formatTime(qt->time(), true));
2095
		       child->setTextAlignment(0, Qt::AlignRight);
2096
		       child->setText(8, QString("%1 %2").arg(kl->formatNumber((double)point->heart_rate, 0)).arg(" bpm"));
2097
		       child->setTextAlignment(8, Qt::AlignRight);
2098
 
2099
		       if (point->cadence < 0xff)
2100
		       {
2101
			  child->setText(10, kl->formatNumber((double)point->cadence, 0));
2102
			  child->setTextAlignment(10, Qt::AlignRight);
2103
		       }
2104
 
2105
		       if (point->alt < 20000)
2106
		       {
2107
		       double alt = (Units == 0) ? (double)point->alt : (double)point->alt / 0.304;
2108
 
2109
			  child->setText(11, QString("%1 %2").arg(kl->formatNumber(alt, 2)).arg((Units == 0) ? QString(" m") : QString(" ft")));
2110
			  child->setTextAlignment(11, Qt::AlignRight);
2111
		       }
2112
 
218 andreas 2113
		       if (!oldPoint)
2114
			  oldPoint = point;
2115
 
88 andreas 2116
		       if (point->alt > alt_asc && point->alt < 20000)
2117
		       {
2118
			  alt_asc = point->alt;
2119
 
2120
			  if (alt_dsc == 0)
2121
			     alt_dsc = point->alt;
2122
		       }
2123
 
2124
		       if (point->alt < alt_dsc)
2125
			  alt_dsc = point->alt;
2126
 
2127
		       // save the min and max values. We need this information to
2128
		       // build the graphics.
221 andreas 2129
		       if (point->heart_rate > max_hr && point->heart_rate < 250)
88 andreas 2130
			  max_hr = point->heart_rate;
2131
 
221 andreas 2132
		       if ((min_hr == 0 && point->heart_rate > 0 && point->heart_rate < 250)
2133
			   || (point->heart_rate > 0 && point->heart_rate < min_hr))
88 andreas 2134
			  min_hr = point->heart_rate;
2135
 
2136
		       if (point->alt < 20000 && max_height < point->alt)
2137
			  max_height = point->alt;
2138
 
2139
		       if (point->alt < 20000 && (min_height == 0.0 || min_height > point->alt))
2140
			  min_height = point->alt;
2141
 
218 andreas 2142
		       // Calculate speed of current track
222 andreas 2143
		       if (!pause && point->distance > 1.0e10)
2144
		       {
2145
			  t1 = point->time;
2146
			  pause = true;
2147
			  ignore = true;
2148
		       }
2149
		       else if (pause)
2150
		       {
2151
			  t2 = point->time;
2152
			  pause = false;
2153
			  point = ds.getPoint(point->time + 1);
2154
			  continue;
2155
		       }
218 andreas 2156
 
222 andreas 2157
		       if (!ignore && !pause)
2158
		       {
2159
			  sc = point->time - oldPoint->time;
2160
			  dist = point->distance - oldPoint->distance;
218 andreas 2161
 
249 andreas 2162
			  child->setText(1, QString("%1 %2").arg(kl->formatNumber((Units == 0) ? (double)dist : (double)dist / 0.304, 2)).arg((Units == 0) ? QString(" m") : QString(" ft")));
2163
			  child->setTextAlignment(1, Qt::AlignRight);
2164
 
222 andreas 2165
			  if (t2 > t1)
2166
			     sc -= t2 - t1;
218 andreas 2167
 
222 andreas 2168
			  if (sc > 0.0)
223 andreas 2169
			  {
222 andreas 2170
			     speed = (dist / sc) * 3.6;
223 andreas 2171
 
2172
			     if (speed > lap->max_speed * 3.6)
2173
			        speed = lap->max_speed * 3.6;
2174
			  }
222 andreas 2175
			  else
2176
			     speed = 0.0;
218 andreas 2177
 
222 andreas 2178
			  if (Units == 1)
2179
			     speed /= 1.609344;
218 andreas 2180
 
249 andreas 2181
			  child->setText(5, QString("%1 %2").arg(kl->formatNumber(speed, 2)).arg((Units == 0) ? QString(" Km/h") : QString(" mi/h")));
2182
			  child->setTextAlignment(5, Qt::AlignRight);
2183
 
222 andreas 2184
			  if (speed > 0.0 && speed < 400.0 && max_speed < speed)
2185
			     max_speed = speed;
2186
 
2187
			  if (speed > 0.0 && (min_speed == 0.0 || min_speed > speed))
2188
			     min_speed = speed;
2189
 
2190
			  oldPoint = point;
2191
		       }
2192
 
2193
		       if (!pause && ignore)
2194
			  ignore = false;
2195
 
221 andreas 2196
		       if (point->heart_rate > 0 && point->heart_rate < 250)
88 andreas 2197
		       {
2198
			  avg_hr += point->heart_rate;
2199
			  men++;
2200
		       }
2201
 
2202
		       point = ds.getPoint(point->time + 1);
2203
		    }
2204
 
149 andreas 2205
		    if (old_asc < alt_asc)
2206
		       sum_asc += (alt_asc - old_asc);
2207
 
2208
		    if (old_dsc > alt_dsc)
2209
		       sum_dsc += (old_dsc - alt_dsc);
2210
 
2211
		    old_asc = alt_asc;
2212
		    old_dsc = alt_dsc;
2213
		    qs_ascent.sprintf("%.2f %s", (Units == 1) ? (alt_asc / 0.304) : alt_asc, (Units == 1) ? "ft" : "m");
2214
		    qs_descent.sprintf("%.2f %s", (Units == 1) ? (alt_dsc / 0.304) : alt_dsc, (Units == 1) ? "ft" : "m");
88 andreas 2215
		 }
2216
 
2217
		 if (lap->avg_cadence != 0xff)
2218
		    qs_avgcadence.sprintf("%d", lap->avg_cadence);
248 andreas 2219
		 // Add a new detail line to the tab "Summary"
2220
		 K3ListViewItem *edetail = new K3ListViewItem(element, qs_name,
2221
			kl->formatNumber(qs_distance, false),
2222
			qs_etime, qs_avgpace, kl->formatNumber(qs_avgspeed, false),
2223
			kl->formatNumber(qs_maxspeed, false), qs_calories, qs_avghr);
88 andreas 2224
		 edetail->setText(8, qs_maxhr);
2225
		 edetail->setText(9, qs_avgcadence);
248 andreas 2226
		 edetail->setText(10, kl->formatNumber(qs_ascent, false));
2227
		 edetail->setText(11, kl->formatNumber(qs_descent, false));
245 andreas 2228
		 edetail->setPixmap(0, KIcon(QString("history")).pixmap(16));
2229
		 QPixmap qpx = KIcon(QString("other")).pixmap(16);
249 andreas 2230
 
248 andreas 2231
		 trdetail->setText(0, qs_etime);
2232
		 trdetail->setTextAlignment(0, Qt::AlignRight);
2233
		 trdetail->setText(1, kl->formatNumber(qs_distance));
2234
		 trdetail->setTextAlignment(1, Qt::AlignRight);
2235
		 trdetail->setText(2, kl->formatNumber(qs_totdist));
2236
		 trdetail->setTextAlignment(2, Qt::AlignRight);
2237
		 trdetail->setText(4, qs_avgpace);
2238
		 trdetail->setTextAlignment(4, Qt::AlignRight);
2239
		 trdetail->setText(5, kl->formatNumber(qs_avgspeed, false));
2240
		 trdetail->setTextAlignment(5, Qt::AlignRight);
2241
		 trdetail->setText(6, kl->formatNumber(qs_maxspeed, false));
2242
		 trdetail->setTextAlignment(6, Qt::AlignRight);
2243
		 trdetail->setText(7, qs_calories);
2244
		 trdetail->setTextAlignment(7, Qt::AlignRight);
2245
		 trdetail->setText(8, qs_avghr);
2246
		 trdetail->setTextAlignment(8, Qt::AlignRight);
2247
		 trdetail->setText(9, qs_maxhr);
2248
		 trdetail->setTextAlignment(9, Qt::AlignRight);
2249
		 trdetail->setText(10, qs_avgcadence);
2250
		 trdetail->setTextAlignment(10, Qt::AlignRight);
2251
		 trdetail->setText(11, kl->formatNumber(qs_ascent, false));
2252
		 trdetail->setTextAlignment(11, Qt::AlignRight);
2253
 
128 andreas 2254
		 switch (rakt->run->sport_type)
2255
		 {
232 andreas 2256
		    case D1000_running:
247 andreas 2257
		       edetail->setPixmap(0, KIcon(QString("spw-running")).pixmap(16));
232 andreas 2258
		    break;
2259
 
2260
		    case D1000_biking:
245 andreas 2261
		       edetail->setPixmap(0, KIcon(QString("bike")).pixmap(16));
232 andreas 2262
		    break;
2263
 
2264
		    case D1000_other:
2265
		       edetail->setPixmap(0, qpx);
2266
		    break;
2267
 
128 andreas 2268
		    default:
232 andreas 2269
		       edetail->setPixmap(0, qpx);
128 andreas 2270
		 }
2271
 
245 andreas 2272
		 ui_sportwatcherWidgetBase.liLaps->clearSelection();
88 andreas 2273
		 element->insertItem(edetail);
2274
		 delete qt;
2275
		 laps++;
2276
	      }
149 andreas 2277
 
2278
	      qs_ascent.sprintf("%.2f %s", (Units == 1) ? sum_asc / 0.304 : sum_asc, (Units == 1) ? "ft" : "m");
2279
	      qs_descent.sprintf("%.2f %s", (Units == 1) ? sum_dsc / 0.304 : sum_dsc, (Units == 1) ? "ft" : "m");
2280
      	      element->setText(10, qs_ascent);
2281
	      element->setText(11, qs_descent);
248 andreas 2282
	      qs_ascent.sprintf("%s %s", (Units == 1) ? kl->formatNumber(ds.getAscend() / 0.304, 2).toAscii().data() : kl->formatNumber(ds.getAscend(), 2).toAscii().data(), (Units == 1) ? "ft" : "m");
2283
	      ui_sportwatcherWidgetBase.edTotalHeight->clear();
2284
	      ui_sportwatcherWidgetBase.edTotalHeight->insert(qs_ascent);
2285
	      ui_sportwatcherWidgetBase.edLapNumber->setAlignment(Qt::AlignRight);
2286
	      ui_sportwatcherWidgetBase.edLapNumber->insert(kl->formatNumber((double)laps, 0));
88 andreas 2287
	   }
2288
 
2289
	   rakt = rakt->next;
2290
	}
2291
 
2292
	if (men > 0)
2293
	   avg_hr /= men;
221 andreas 2294
	else
2295
	   min_hr = max_hr = avg_hr = 0;
88 andreas 2296
}
2297
 
100 andreas 2298
void sportwatcherWidget::showTrack()
2299
{
132 andreas 2300
	showTrack(0, QRect(0, 0, 0, 0), 0);
104 andreas 2301
}
2302
 
2303
void sportwatcherWidget::showTrack(int zoom)
2304
{
132 andreas 2305
	showTrack(zoom, mapPan, mapLap);
2306
}
2307
 
2308
void sportwatcherWidget::showTrack(int zoom, const QRect &pan, LAP *lap)
2309
{
100 andreas 2310
int width, height;
2311
double x1, y1, x2, y2;
132 andreas 2312
int a, top, left, panX, panY;
2313
uint32 i;
109 andreas 2314
double coordW, coordH, tick;
156 andreas 2315
double meterW, dist, fact;
100 andreas 2316
QPainter paint;
152 andreas 2317
posn_type posNW, posSE, posLXY, posRXY;
100 andreas 2318
POINT *point;
152 andreas 2319
bool Fgeo = false;
157 andreas 2320
bool Data = false;
232 andreas 2321
#if defined HAVE_GDAL
158 andreas 2322
QString fName = MAP;
2323
//double adfGeoTransform[6];
2324
GDALDataset *poDataset = 0;
246 andreas 2325
GDALRasterBand *poBand = 0;
2326
unsigned char *pafScanline = 0;
2327
unsigned char *pafScanlineRed = 0;
2328
unsigned char *pafScanlineGreen = 0;
2329
unsigned char *pafScanlineBlue = 0;
2330
unsigned char *pafScanlineAlpha = 0;
151 andreas 2331
int nXSize, nYSize;
159 andreas 2332
int xOff, yOff;
158 andreas 2333
double oriLeftLon, oriLeftLat, oriRightLon, oriRightLat;
151 andreas 2334
#endif
100 andreas 2335
 
248 andreas 2336
	if (!DIRTY || curTab == 2 || curTab == 3)
246 andreas 2337
	   return;
2338
 
109 andreas 2339
	if (!gmn)
2340
	   return;
2341
 
154 andreas 2342
	QApplication::setOverrideCursor (QCursor(Qt::WaitCursor));
2343
 
104 andreas 2344
	if (zoom != zfactor)
2345
	   zfactor = zoom;
2346
 
132 andreas 2347
	if (mapLap != lap)
2348
	   mapLap = lap;
2349
 
232 andreas 2350
#if defined HAVE_GDAL
2351
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
2352
	KConfigGroup wms (&cfg, "WMS");
2353
	bool square = wms.readEntry("Square", false);
2354
	int CorrX = wms.readEntry("CorrX", 0);
2355
	int CorrY = wms.readEntry("CorrY", 0);
2356
	KConfigGroup ic (&cfg, "ImageCoords");
2357
	oriLeftLon = ic.readEntry("LeftLon", 0.0);
2358
	oriLeftLat = ic.readEntry("LeftLat", 0.0);
2359
	oriRightLon = ic.readEntry("RightLon", 0.0);
2360
	oriRightLat = ic.readEntry("RightLat", 0.0);
2361
//	int isrs = ic.readEntry("SRS", 0);
157 andreas 2362
#endif
248 andreas 2363
	if (curTab == 0)
2364
	{
2365
	   width = ui_sportwatcherWidgetBase.imgMap->width() - 2;
2366
	   height = ui_sportwatcherWidgetBase.imgMap->height();
2367
	}
2368
	else
2369
	{
2370
	   width = ui_sportwatcherWidgetBase.grMap->width() - 2;
2371
	   height = ui_sportwatcherWidgetBase.grMap->height();
2372
	}
232 andreas 2373
#if defined HAVE_GDAL
158 andreas 2374
	if (MapType == MPT_WMS && square)
232 andreas 2375
	   pmMap = QPixmap(width / (int)mFactor * (int)mFactor, height / (int)mFactor * (int)mFactor);
157 andreas 2376
	else
232 andreas 2377
	   pmMap = QPixmap(width, height);
157 andreas 2378
#else
232 andreas 2379
	pmMap = QPixmap(width, height);
157 andreas 2380
#endif
245 andreas 2381
	if (pmMap.isNull())
2382
	   return;
2383
 
2384
	// Here we begin do draw something
100 andreas 2385
	paint.begin(&pmMap);
2386
 
132 andreas 2387
	panX = panY = 0;
2388
 
2389
	if (stateHand && mapPan != pan)
2390
	{
2391
	   mapPan = pan;
2392
	   panX = mapPan.right() - mapPan.left();
2393
	   panY = mapPan.bottom() - mapPan.top();
2394
	   oldTransX += (double)panX;
2395
	   oldTransY += (double)panY;
2396
	}
2397
 
104 andreas 2398
	memset(&posNW, 0, sizeof(posn_type));
2399
	memset(&posSE, 0, sizeof(posn_type));
100 andreas 2400
 
156 andreas 2401
	posSE.lat = 90.0;
2402
	posSE.lon = 180.0;
2403
	posNW.lat = -90.0;
2404
	posNW.lon = -180.0;
100 andreas 2405
 
2406
	/*
2407
	 * Find out the corners of our track (NW, NE, SE, SW)
2408
	 */
132 andreas 2409
	if (mapLap)
2410
	   i = mapLap->start_time;
2411
	else
2412
	   i = 0;
100 andreas 2413
 
2414
	while ((point = ds.getPoint(i)) != 0)
2415
	{
132 andreas 2416
	   if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
2417
	      break;
2418
 
100 andreas 2419
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
2420
	   {
132 andreas 2421
	      i = point->time + 1;
100 andreas 2422
	      continue;
2423
	   }
2424
 
2425
	   if (SEMI2DEG(point->posn.lat) > posNW.lat)
2426
	      posNW.lat = SEMI2DEG(point->posn.lat);
2427
 
2428
	   if (SEMI2DEG(point->posn.lat) < posSE.lat)
2429
	      posSE.lat = SEMI2DEG(point->posn.lat);
2430
 
156 andreas 2431
	   if (SEMI2DEG(point->posn.lon) > posNW.lon)
100 andreas 2432
	      posNW.lon = SEMI2DEG(point->posn.lon);
2433
 
156 andreas 2434
	   if (SEMI2DEG(point->posn.lon) < posSE.lon)
100 andreas 2435
	      posSE.lon = SEMI2DEG(point->posn.lon);
2436
 
132 andreas 2437
	   i = point->time + 1;
157 andreas 2438
	   Data = true;
100 andreas 2439
	}
104 andreas 2440
 
2441
	coordW = (posNW.lon > posSE.lon) ? posNW.lon - posSE.lon : posSE.lon - posNW.lon;
2442
	coordH = (posNW.lat > posSE.lat) ? posNW.lat - posSE.lat : posSE.lat - posNW.lat;
109 andreas 2443
	meterW = ds.earth_distance(posNW.lat, posNW.lon, posNW.lat, posSE.lon);
104 andreas 2444
 
2445
	// define the ticks to translate the GPS coordinates into pixels.
2446
	// The track should be centered and we have to calculate the
2447
	// rectangular within we draw the track.
152 andreas 2448
	if (coordW < coordH)
100 andreas 2449
	{
152 andreas 2450
	   tick = (double)width / coordW + (double)zoom;
2451
 
2452
	   if ((tick * coordH) > height)
109 andreas 2453
	      tick = (double)height / coordH + (double)zoom;
100 andreas 2454
	}
2455
	else
2456
	{
152 andreas 2457
	   tick = (double)height / coordH + (double)zoom;
2458
 
2459
	   if ((tick * coordW) > width)
109 andreas 2460
	      tick = (double)width / coordW + (double)zoom;
100 andreas 2461
	}
104 andreas 2462
 
156 andreas 2463
	left = width - (width - tick * coordW) / 2;
152 andreas 2464
	top = (height - tick * coordH) / 2;
2465
 
132 andreas 2466
	a = tick * coordW;
156 andreas 2467
 
2468
	if (Units == 0)
2469
	   dist = meterW / a;			// Meters
2470
	else
2471
	   dist = meterW * 1.609344 / a;	// 1/1000 mile (5.28 feet)
2472
 
232 andreas 2473
#if defined HAVE_GDAL
159 andreas 2474
	geoRect.llat = 0.0;
2475
	geoRect.llon = 0.0;
2476
	geoRect.rlat = 0.0;
2477
	geoRect.rlon = 0.0;
158 andreas 2478
	geoRect.width = width;
2479
	geoRect.height = height;
146 andreas 2480
	/*
2481
	 * If we have a map file, we try to read it and if successfull,
2482
	 * we should get a map painted.
154 andreas 2483
	 *
2484
	 * Currently only WMS-Server is supported, allthough GDAL allows
2485
	 * several other formats too.
146 andreas 2486
	 */
157 andreas 2487
	if (!MAP.isEmpty() && Data)
146 andreas 2488
	{
158 andreas 2489
	bool writeTag = true;
2490
	double mtx, mty;
2491
	double vx, vy;
152 andreas 2492
 
158 andreas 2493
	   xOff = yOff = 0;
2494
	   posRXY.lon = posNW.lon + (width - left + oldTransX) / tick;
2495
	   posLXY.lat = posNW.lat + (top + oldTransY) / tick;
2496
	   posLXY.lon = posSE.lon - (left - a - oldTransX) / tick;
2497
	   posRXY.lat = posSE.lat - (height - top - (tick * coordH) - oldTransY) / tick;
159 andreas 2498
	   geoRect.llat = posLXY.lat;
2499
	   geoRect.llon = posLXY.lon;
2500
	   geoRect.rlat = posRXY.lat;
2501
	   geoRect.rlon = posRXY.lon;
158 andreas 2502
	   // width and height of map in meters
2503
	   mtx = ds.earth_distance(posRXY.lat, posRXY.lon, posRXY.lat, posLXY.lon);
2504
	   mty = ds.earth_distance(posRXY.lat, posRXY.lon, posLXY.lat, posRXY.lon);
157 andreas 2505
 
158 andreas 2506
	   // factor to correct the map, in case we use a WMS server
2507
	   if (MapType == MPT_WMS)
2508
	   {
2509
	      vx = (posRXY.lon - posLXY.lon) / mtx * CorrX;
2510
	      vy = (posRXY.lat - posLXY.lat) / mty * CorrY;
2511
	      posRXY.lon += vx;
2512
	      posRXY.lat += vy;
2513
	      posLXY.lon += vx;
2514
	      posLXY.lat += vy;
2515
	   }
2516
 
154 andreas 2517
	   /*
158 andreas 2518
	    * Write a control file for GDAL, if we use a WMS server.
2519
	    * Warp an image if we use PNG or BMP or GIF.
2520
	    * Warp a region if we use TIFF
154 andreas 2521
	    */
158 andreas 2522
	   if (MapType == MPT_WMS)
2523
	      writeTag = writeWMSTag(posLXY.lon, posLXY.lat, posRXY.lon, posRXY.lat, width, height);
2524
 
159 andreas 2525
	   if (MapType == MPT_GIF || MapType == MPT_BMP || MapType == MPT_PNG ||
2526
	       MapType == MPT_SGI || MapType == MPT_TIF)
158 andreas 2527
	      writeTag = warpImage(MAP, &fName);
2528
 
2529
	   if (writeTag)
151 andreas 2530
	   {
246 andreas 2531
	      if (MapType != MPT_SHP && (poDataset = (GDALDataset *)GDALOpen (fName.toAscii().constData(), GA_ReadOnly)) != NULL)
151 andreas 2532
	      {
2533
		 QPixmap bild;
2534
		 int nRasterCount = poDataset->GetRasterCount();
2535
		 int nXBlock, nYBlock;
154 andreas 2536
		 GDALColorTable *pCT, *pCTb, *pCTr, *pCTg, *pCTa;
146 andreas 2537
 
151 andreas 2538
		 int             bGotMin, bGotMax;
2539
		 int		 tTypeLen, tColor, tColorEntrys;
2540
		 GDALDataType    tRasterType;
2541
		 double          adfMinMax[2];
2542
 
154 andreas 2543
		 pafScanlineRed = pafScanlineGreen = pafScanlineBlue = pafScanlineAlpha = 0;
246 andreas 2544
		 Fgeo = true;
154 andreas 2545
		 /*
2546
		  * Read every raster band.
2547
		  *
2548
		  * If we get 3 raster bands, the image is a 24 bit image.
2549
		  * If we get 4 raster bands, the image is probably a 24 bit
2550
		  * image with an alpha channel. Currently the alpha channel
2551
		  * is ignored!
2552
		  * If we have 1 raster band, the image is 8 bit monochrom.
2553
		  * Otherwise the image is undefined and the results are also.
2554
		  */
151 andreas 2555
		 for (a = 1; a <= nRasterCount; a++)
2556
		 {
246 andreas 2557
		    if (!Fgeo)
2558
		       break;
2559
 
245 andreas 2560
		    if (!(poBand = poDataset->GetRasterBand (a)))
2561
		    {
2562
		       paint.end();
2563
		       return;
2564
		    }
2565
 
151 andreas 2566
		    poBand->GetBlockSize (&nXBlock, &nYBlock);
2567
		    nXSize = poBand->GetXSize();
2568
		    nYSize = poBand->GetYSize();
2569
		    tRasterType = poBand->GetRasterDataType ();
2570
		    tTypeLen = GDALGetDataTypeSize (tRasterType) / 8;	// We need Bytes not Bits!
2571
		    tColor = poBand->GetColorInterpretation ();
2572
 
2573
		    adfMinMax[0] = poBand->GetMinimum (&bGotMin);
2574
		    adfMinMax[1] = poBand->GetMaximum (&bGotMax);
2575
 
2576
		    if (!(bGotMin && bGotMax))
2577
		       GDALComputeRasterMinMax ((GDALRasterBandH)poBand, TRUE, adfMinMax);
2578
 
152 andreas 2579
		    if ((pCT = poBand->GetColorTable()) != NULL)
151 andreas 2580
		       tColorEntrys = poBand->GetColorTable()->GetColorEntryCount();
2581
 
2582
		    switch (a)
2583
		    {
152 andreas 2584
		       case 1: pafScanlineRed   = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineRed; pCTr = pCT; break;
2585
		       case 2: pafScanlineGreen = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineGreen; pCTg = pCT; break;
2586
		       case 3: pafScanlineBlue  = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineBlue; pCTb = pCT; break;
154 andreas 2587
		       case 4: pafScanlineAlpha  = new unsigned char[tTypeLen * nXSize * nYSize]; pafScanline = pafScanlineAlpha; pCTa = pCT; break;
151 andreas 2588
		    }
2589
 
246 andreas 2590
		    if (!pafScanline)
2591
		    {
2592
		       paint.end();
2593
		       KMessageBox::error(this, i18n("Not enough memory for a raster operation!"));
2594
		       return;
2595
		    }
2596
 
152 andreas 2597
		    memset (pafScanline, 0, tTypeLen * nXSize * nYSize);
151 andreas 2598
 
154 andreas 2599
		    /*
158 andreas 2600
		     * Get the image (from the server) and put the tiles together.
154 andreas 2601
		     *
2602
		     * The function reads only one raster band. This is,
2603
		     * because the function is called for every raster band and
2604
		     * every raster band is stored into a separate array.
2605
		     */
152 andreas 2606
		    if (poBand->RasterIO (GF_Read, 0, 0, nXSize, nYSize, pafScanline, nXSize, nYSize, tRasterType, 0, 0) == CE_Failure)
2607
		    {
248 andreas 2608
		       paint.end();
151 andreas 2609
		       KMessageBox::error(this, i18n("Error reading a raster band!"));
248 andreas 2610
		       paint.begin(&pmMap);
152 andreas 2611
		       Fgeo = false;
2612
		       break;
2613
		    }
2614
		    else
2615
		       Fgeo = true;
151 andreas 2616
		 }
2617
 
154 andreas 2618
		 /*
2619
		  * Only if Fgeo is TRUE, we've read successfully all raster
2620
		  * bands. Now we have to put the bands together to get
2621
		  * an image.
2622
		  */
152 andreas 2623
		 if (Fgeo)
2624
		 {
2625
		 unsigned char *pCombinedBytes = new unsigned char[(tTypeLen * nXSize * nYSize * nRasterCount)];
2626
		 unsigned char *ptr_dest, *ptr_src;
2627
		 int j;
151 andreas 2628
 
245 andreas 2629
		    ptr_dest = ptr_src = 0;
2630
 
154 andreas 2631
		    /*
2632
		     * We need two nested loops to set the pixels in the wanted
2633
		     * order.
2634
		     */
152 andreas 2635
		    for (a = 0, j = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount, j++)
151 andreas 2636
		    {
152 andreas 2637
		       int k = a;
2638
 
2639
		       for (int m = nRasterCount - 1; m >= 0; m--, k++)
2640
		       {
245 andreas 2641
		       unsigned char *pBytes = 0;
152 andreas 2642
 
2643
			  switch (m)
2644
			  {
154 andreas 2645
			     case 3: pBytes = pafScanlineAlpha; pCT = pCTa; break;
152 andreas 2646
			     case 2: pBytes = pafScanlineBlue; pCT = pCTb; break;
2647
			     case 1: pBytes = pafScanlineGreen; pCT = pCTg; break;
2648
			     default: pBytes = pafScanlineRed; pCT = pCTr;
2649
			  }
2650
 
2651
			  ptr_dest = pCombinedBytes + k;
2652
			  unsigned char b = pBytes[j];
2653
 
154 andreas 2654
			  /*
2655
			   * If we have a color table, the pixels are pointers
2656
			   * to the color table. We need to convert them into
2657
			   * 24 bit pixels plus an optional alpha channel.
2658
			   */
152 andreas 2659
			  if (pCT != NULL)
2660
			  {
2661
			     GDALColorEntry ce;
2662
			     unsigned int c = (unsigned int)b;
2663
			     c = pCT->GetColorEntryAsRGB (c, &ce);
2664
 
2665
			     if  (m == 0) c = ce.c1;
2666
			     if  (m == 1) c = ce.c2;
2667
			     if  (m == 2) c = ce.c3;
154 andreas 2668
			     if  (m == 3) c = ce.c4;
152 andreas 2669
 
2670
			     b = (unsigned char)c;
2671
			  }
2672
 
2673
			  ptr_src = &b;
2674
			  memcpy (ptr_dest, ptr_src, 1);
2675
		       }
151 andreas 2676
		    }
152 andreas 2677
 
2678
		    x1 = y1 = 0;
2679
 
154 andreas 2680
		    /*
2681
		     * The following loop is QT specific! It sets the pixels
2682
		     * of the raw image, pixel by pixel. This may be slow, but
2683
		     * everything else didn't work :-(
2684
		     *
2685
		     * FIXME: We need a more effective routine to put the
158 andreas 2686
		     *        raw image into QT's "painter" class.
154 andreas 2687
		     */
2688
		    for (a = 0; a < (nXSize * nYSize * nRasterCount); a += nRasterCount)
152 andreas 2689
		    {
158 andreas 2690
		       if (x1 < width && y1 < height)
2691
		       {
2692
			  if (nRasterCount == 3)
232 andreas 2693
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a]), Qt::SolidLine));
158 andreas 2694
			  else if (nRasterCount > 3)
232 andreas 2695
			     paint.setPen (QPen(QColor(qRgba((int)pCombinedBytes[a+3], (int)pCombinedBytes[a+2], (int)pCombinedBytes[a+1], (int)pCombinedBytes[a])), Qt::SolidLine));
158 andreas 2696
			  else if (nRasterCount == 2)
232 andreas 2697
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a+1], (int)pCombinedBytes[a], (int)pCombinedBytes[a+1]), Qt::SolidLine));
158 andreas 2698
			  else if (nRasterCount == 1)
232 andreas 2699
			     paint.setPen (QPen(QColor((int)pCombinedBytes[a], (int)pCombinedBytes[a], (int)pCombinedBytes[a]), Qt::SolidLine));
154 andreas 2700
 
158 andreas 2701
			  paint.drawPoint(x1, y1);
2702
		       }
2703
 
152 andreas 2704
		       x1++;
2705
 
2706
		       if (x1 >= nXSize)
2707
		       {
2708
			  x1 = 0;
2709
			  y1++;
2710
		       }
2711
		    }
2712
 
2713
		    delete pCombinedBytes;
245 andreas 2714
		    pCombinedBytes = 0;
151 andreas 2715
		 }
2716
 
152 andreas 2717
		 if (pafScanlineRed)
2718
		    delete pafScanlineRed;
151 andreas 2719
 
152 andreas 2720
		 if (pafScanlineGreen)
2721
		    delete pafScanlineGreen;
2722
 
2723
		 if (pafScanlineBlue)
2724
		    delete pafScanlineBlue;
2725
 
154 andreas 2726
		 if (pafScanlineAlpha)
2727
		    delete pafScanlineAlpha;
2728
 
152 andreas 2729
		 GDALClose (poDataset);
2730
		 poDataset = 0;
158 andreas 2731
 
2732
		 if (MAP != fName)
232 andreas 2733
		    unlink (fName.toAscii().data());
151 andreas 2734
	      }
158 andreas 2735
	      else if (MapType == MPT_SHP)
2736
	      {
2737
		 QDir shpd(MAP, QString("*.shp"));
2738
 
2739
		 if (shpd.count() < 1)
2740
		 {
2741
		    KMessageBox::error(this, i18n("There is no shape file in directory ") + MAP);
2742
		    Fgeo = false;
2743
		 }
2744
		 else
2745
		 {
2746
		 OGRDataSource *poDS = 0;
165 andreas 2747
		 OGRLayer *poLy;
2748
		 OGRFeature *poFeat;
2749
		 OGRFeatureDefn *poFDefn;
2750
		 OGRFieldDefn *poFieldDefn;
2751
		 OGRGeometry *poGeometry;
2752
		 OGRPoint *poPoint;
2753
		 int nLayers, iField;
2754
		 QString sfn;
158 andreas 2755
 
165 andreas 2756
		    OGRRegisterAll ();
158 andreas 2757
		    // read all shape files and put them together
249 andreas 2758
		    for (a = 0; (uint)a < shpd.count(); a++)
158 andreas 2759
		    {
165 andreas 2760
		       sfn = MAP + shpd[a];
232 andreas 2761
		       poDS = OGRSFDriverRegistrar::Open(sfn.toAscii().data(), false);
158 andreas 2762
 
2763
		       if (!poDS)
165 andreas 2764
		       {
232 andreas 2765
			  cerr << "Error opening the file " << sfn.toAscii().data() << "!" << endl;
158 andreas 2766
			  break;
165 andreas 2767
		       }
158 andreas 2768
 
165 andreas 2769
		       nLayers = poDS->GetLayerCount ();
2770
 
2771
		       // Read all layers of a shape file
2772
		       for (int j = 0; j< nLayers; j++)
2773
		       {
2774
			  if ((poLy = poDS->GetLayer (j)) == NULL)
2775
			  {
2776
			     cerr << "Error getting layer " << j << "!" << endl;
2777
			     continue;
2778
			  }
2779
 
2780
			  poLy->SetSpatialFilterRect (geoRect.llon, geoRect.llat, geoRect.rlon, geoRect.rlat);
2781
 
2782
			  // Read all features of a layer and get the fields
2783
			  // of the features
2784
			  poLy->ResetReading();
2785
 
2786
			  while ((poFeat = poLy->GetNextFeature()) != NULL)
2787
			  {
2788
			     if ((poFDefn = poLy->GetLayerDefn()) == NULL)
2789
			     {
2790
			        cerr << "Error getting a layer defination!" << endl;
2791
			        continue;
2792
			     }
2793
 
2794
			     poGeometry = poFeat->GetGeometryRef();
2795
 
2796
			     if (poGeometry != NULL && wkbFlatten (poGeometry->getGeometryType()) == wkbPoint)
2797
			     {
2798
			        poPoint = (OGRPoint *) poGeometry;
2799
			        cout << "pointX: " << poPoint->getX() << ", pointY: " << poPoint->getY() << endl;
2800
			     }
2801
 
2802
			     for (iField = 0; iField < poFDefn->GetFieldCount(); iField++)
2803
			     {
2804
			        poFieldDefn = poFDefn->GetFieldDefn (iField);
2805
 
2806
				if (poFieldDefn->GetType() == OFTInteger)
2807
				   cout << "Layer: " << j << " int: " << poFeat->GetFieldAsInteger (iField) << endl;
2808
				else if (poFieldDefn->GetType() == OFTReal)
2809
				   cout << "Layer: " << j << " float: " << poFeat->GetFieldAsDouble (iField) << endl;
2810
				else if (poFieldDefn->GetType() == OFTString)
2811
				   cout << "Layer: " << j << " string: " << poFeat->GetFieldAsString (iField) << endl;
2812
				else
2813
				   cout << "Layer: " << j << " Unkn.: " << poFeat->GetFieldAsString (iField) << endl;
2814
			     }
2815
 
2816
			     cout << "----------------------------" << endl;
2817
        		  }
2818
 
2819
        		  OGRFeature::DestroyFeature (poFeat);
2820
		       }
2821
 
158 andreas 2822
		       OGRDataSource::DestroyDataSource(poDS);
2823
		    }
2824
		 }
2825
	      }
151 andreas 2826
	      else
157 andreas 2827
	      {
158 andreas 2828
		 KMessageBox::error(this, i18n("Error opening map file!"));
157 andreas 2829
		 Fgeo = false;
2830
	      }
151 andreas 2831
	   }
146 andreas 2832
	}
151 andreas 2833
#endif
154 andreas 2834
	/*
2835
	 * Here we come to draw the track. It will be drawn over the previous
2836
	 * created map image.
2837
	 */
100 andreas 2838
	// Colors and fonts
154 andreas 2839
	QColor background(220, 220, 220);		// background color
2840
	QColor red(255, 0, 0);				// mile marker
2841
	QColor black(0, 0, 0);				// Text, center of track
2842
//	QColor yellow(255, 255, 0);
2843
	QColor yellow(0x00cf, 0x00ff, 0x0000);		// color of track
100 andreas 2844
	QFont fntNormal("Helvetica");
2845
	fntNormal.setPixelSize(10);
2846
	fntNormal.setStyleHint(QFont::Helvetica);
232 andreas 2847
	QPen dot(red, 4, Qt::SolidLine);
2848
	QPen line(black, 2, Qt::SolidLine);
247 andreas 2849
	QPen yline(yellow, 4, Qt::SolidLine);
154 andreas 2850
	// Fill background with background colors, if there is no map.
152 andreas 2851
	if (!Fgeo)
2852
	   paint.fillRect(0, 0, width+2, height+2, background);
2853
 
156 andreas 2854
	if (Units == 0)
2855
	   fact = 1000.0;
2856
	else
2857
	   fact = 1609.344;
2858
 
109 andreas 2859
	paint.setPen(line);
2860
	paint.drawLine(10, height - 9, 10, height - 4);
156 andreas 2861
	paint.drawLine(10, height - 4, 10 + (fact / dist), height - 4);
2862
	paint.drawLine(10 + (fact / dist), height - 9, 10 + (fact / dist), height - 4);
109 andreas 2863
	paint.setFont(fntNormal);
2864
 
156 andreas 2865
	if (Units == 0)
2866
	   paint.drawText(10, height - 10, QString("1000 m"));
2867
	else
2868
	   paint.drawText(10, height - 10, QString("5280 ft"));
2869
 
100 andreas 2870
	// Draw track
132 andreas 2871
	if (mapLap)
2872
	   i = mapLap->start_time;
2873
	else
2874
	   i = 0;
2875
 
100 andreas 2876
	x1 = y1 = 0.0;
157 andreas 2877
	bool wStart = false;
104 andreas 2878
 
100 andreas 2879
	while ((point = ds.getPoint(i)) != 0)
2880
	{
132 andreas 2881
	   if (mapLap && point->time > (mapLap->start_time + (mapLap->total_time / 100)))
2882
	      break;
2883
 
100 andreas 2884
	   if (point->posn.lat == 0x7fffffff || point->posn.lon == 0x7fffffff)
2885
	   {
132 andreas 2886
	      i = point->time + 1;
100 andreas 2887
	      continue;
2888
	   }
2889
 
157 andreas 2890
	   x2 = (left + ((posNW.lon - SEMI2DEG(point->posn.lon)) * tick * -1)) + oldTransX;
2891
	   y2 = (top + ((posNW.lat - SEMI2DEG(point->posn.lat)) * tick)) + oldTransY;
100 andreas 2892
 
157 andreas 2893
	   if (!wStart && x1 != 0.0 && y1 != 0.0)
2894
	   {
2895
	      // Load the start symbol
245 andreas 2896
	      QPixmap qpx (KIcon(QString("wstart")).pixmap(16));
157 andreas 2897
	      // Find the angle of the track and turn the symbol accordingly
2898
	      // we use Pythagoras to calculate the triangle
2899
	      double xl = (x1 < x2) ? x2 - x1 : x1 - x2;
2900
	      double yl = (y1 < y2) ? y2 - y1 : y1 - y2;
2901
	      double da = fmin (xl, yl);
2902
	      double db = fmax (xl, yl);
2903
	      double zl = sqrt (pow (da, 2) + pow (db, 2));
2904
	      double angle = (asin(da / zl) / M_PIl) * 180.0;
2905
 
2906
	      angle = (angle > 45.0) ? 90.0 - angle : angle;
2907
// cout << "Winkel: " << angle << " ---- X: " << xl << ", Y: " << yl << ", Z: " << zl << ", Point (x1,y1,x2,y2): " << x1 << ", " << y1 << ", " << x2 << ", " << y2 << endl;
2908
	      if (x1 < x2 && y1 < y2)		// right, down
2909
		 angle = 90.0 + angle;
2910
	      else if (x1 > x2 && y1 < y2)	// left, down
2911
		 angle = 270.0 - angle;
2912
	      else if (x1 > x2 && y1 > y2)	// left, up
2913
		 angle = 270.0 + angle;
2914
	      else				// right, up
2915
		 angle = 90.0 - angle;
2916
// cout << "Realer Winkel: " << angle << endl;
2917
	      // Set the center of the symbol
2918
	      paint.save ();
2919
	      paint.translate (x1, y1);
2920
	      // rotate the symbol
2921
	      paint.rotate (angle);
2922
	      paint.drawPixmap (-8, -8, qpx);
2923
	      paint.restore ();
2924
	      wStart = true;
2925
	   }
2926
 
100 andreas 2927
	   if (x1 == 0.0 && y1 == 0.0)
2928
	   {
2929
	      x1 = x2;
2930
	      y1 = y2;
2931
	   }
104 andreas 2932
 
100 andreas 2933
	   paint.setPen(yline);
104 andreas 2934
	   paint.drawLine(x1, y1, x2, y2);
2935
 
156 andreas 2936
	   if ((point->distance - dist) >= fact)	// a dot at every 1000 meters or at 1 mile
104 andreas 2937
	   {
2938
	      paint.setPen(dot);
154 andreas 2939
	      paint.drawEllipse(x2-2, y2-2, 3, 3);
156 andreas 2940
	      dist = (int)(point->distance / fact) * fact;
104 andreas 2941
	   }
2942
 
100 andreas 2943
	   paint.setPen(line);
2944
	   paint.drawLine(x1, y1, x2, y2);
2945
	   x1 = x2;
2946
	   y1 = y2;
132 andreas 2947
	   i = point->time + 1;
100 andreas 2948
	}
2949
 
157 andreas 2950
	bool lastLap = false;
2951
 
2952
	if (mapLap)
2953
	   if (ds.getRunNode()->run->last_lap_index == mapLap->index)
2954
	      lastLap = true;
2955
 
2956
	if ((!mapLap || lastLap) && wStart)
2957
	{
2958
	   // load the end symbol
245 andreas 2959
	   QPixmap qpx (KIcon(QString("wtarget")).pixmap(16));
157 andreas 2960
	   paint.drawPixmap (x2, y2 - 16, qpx);
2961
	}
2962
 
100 andreas 2963
	paint.end();
248 andreas 2964
 
2965
	if (curTab == 0)
2966
	   ui_sportwatcherWidgetBase.imgMap->setPixmap(pmMap);
2967
	else
2968
	   ui_sportwatcherWidgetBase.grMap->setPixmap(pmMap);
2969
 
154 andreas 2970
	QApplication::restoreOverrideCursor();
100 andreas 2971
}
2972
 
245 andreas 2973
void sportwatcherWidget::kcbCurveSlot(int)
2974
{
246 andreas 2975
	DIRTY = true;
245 andreas 2976
	showCurves();
246 andreas 2977
	DIRTY = false;
245 andreas 2978
}
2979
 
248 andreas 2980
void sportwatcherWidget::tabViewSlot(int tab)
2981
{
2982
	curTab = tab;
2983
 
2984
	if (tab == 0 && tabDirt0)
2985
	{
2986
	   DIRTY = true;
2987
	   showLaps();
2988
	   showTrack();
2989
	   showCurves();
2990
	   DIRTY = false;
2991
	   tabDirt0 = false;
2992
	}
2993
	else if (tab == 1 && tabDirt1)
2994
	{
2995
	   DIRTY = true;
2996
 
2997
	   if (tabDirt0)
2998
	      showLaps();
2999
 
3000
	   showTrack();
3001
	   DIRTY = false;
3002
	   tabDirt1 = false;
3003
	}
250 andreas 3004
	else if (tab == 2 && tabDirt2)
3005
	{
3006
	   DIRTY = true;
3007
 
3008
	   if (tabDirt0)
3009
	      showLaps();
3010
 
3011
	   setMouseTracking (true);
3012
	   ui_sportwatcherWidgetBase.tabView->setMouseTracking (true);
3013
	   ui_sportwatcherWidgetBase.grHR->setMouseTracking (true);
3014
	   ui_sportwatcherWidgetBase.grElevation->setMouseTracking (true);
3015
	   ui_sportwatcherWidgetBase.grSpeed->setMouseTracking (true);
3016
	   showThreeCurve();
3017
	   tabDirt2 = false;
3018
	   DIRTY = false;
3019
	}
248 andreas 3020
}
3021
 
88 andreas 3022
void sportwatcherWidget::showCurves()
3023
{
148 andreas 3024
	showCurves (mapLap);
3025
}
3026
 
3027
void sportwatcherWidget::showCurves(LAP *lap)
3028
{
88 andreas 3029
QPainter paint;
3030
int width, height;
218 andreas 3031
int i, secs, cuType;
88 andreas 3032
int lineHeight, margin_left, margin_right, margin_bottom;
3033
int x1, y1, x2, y2;		// Coordinates
3034
bool meter;
218 andreas 3035
double maxHeight, minHeight, maxSpeed, minSpeed;
88 andreas 3036
int maxHr, minHr, rh;
3037
POINT *point;
148 andreas 3038
RUN_NODE *rn;
3039
LAP *lp;
88 andreas 3040
double w_tick, h_tick;		// Number of pixels one "tick" has;
3041
				// This depends on the width and height
3042
				// of the image.
3043
	// First we draw a grid based on the min and max
3044
	// values detected in the function showLap(). In case
3045
	// all values are 0, we exit here.
3046
	if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
3047
	   return;
3048
 
248 andreas 3049
	if (!DIRTY || curTab != 0)
246 andreas 3050
	   return;
3051
 
218 andreas 3052
	// Look up, what curves we should draw
245 andreas 3053
	cuType = ui_sportwatcherWidgetBase.kcbCurveTypes->currentIndex();
218 andreas 3054
	// Get the dimensions of the available draw area
245 andreas 3055
	width = ui_sportwatcherWidgetBase.imgProfile->width() - 2;
3056
	height = ui_sportwatcherWidgetBase.imgProfile->height();
232 andreas 3057
	pmProfile = QPixmap(width, height);
88 andreas 3058
	paint.begin(&pmProfile);
3059
 
3060
	// we need a somewhat bigger area to draw our curves than
3061
	// we have with the real min and max values.
3062
	if (max_height > 0.0)
3063
	{
148 andreas 3064
	double add = (max_height - min_height) / 100.0 * 5.0;	// Percent
88 andreas 3065
 
3066
	   maxHeight = max_height + add;
3067
	   minHeight = min_height - add;
3068
 
3069
	   if (minHeight < 0.0)		// make sure, we are not too deep
3070
	      minHeight = 0.0;
3071
	}
221 andreas 3072
	else
3073
	   maxHeight = minHeight = 0.0;
88 andreas 3074
 
218 andreas 3075
	if (max_speed > 0.0)
3076
	{
3077
	double add = (max_speed - min_speed) / 100.0 * 5.0;	// Percent
3078
 
3079
	   maxSpeed = max_speed + add;
3080
	   minSpeed = min_speed - add;
3081
 
3082
	   if (minSpeed < 0.0)		// make sure, we are not too deep
3083
	      minSpeed = 0.0;
3084
	}
221 andreas 3085
	else
3086
	   maxSpeed = minSpeed = 0.0;
218 andreas 3087
 
88 andreas 3088
	if (max_hr > 0)
3089
	{
3090
	   maxHr = max_hr + 10;
3091
	   minHr = min_hr - 10;
3092
 
3093
	   if (minHr < 0)
3094
	      minHr = 0;
3095
	}
221 andreas 3096
	else
3097
	   maxHr = minHr = 0;
88 andreas 3098
 
3099
	// Define colors
148 andreas 3100
	QColor background(220, 220, 220);	// Background of graphic
3101
	QColor mark(255, 255, 255);		// Lines inside curve area
3102
	QColor hlight(180, 180, 180);		// area of current lap
3103
//	hlight.setAlpha(128);			// 50% transparent
3104
	QColor frame(0, 0, 0);			// Text and borders
3105
	QColor barcol(151, 190, 13);		// heart rate
3106
	QColor barcol2(190, 151, 13);		// height over NN
222 andreas 3107
	QColor red(220, 128, 128);		// speed
88 andreas 3108
	QColor blue(0, 0, 240);
3109
	QFont fntNormal("Helvetica");
3110
//	QFont fntBold("Helvetica", 10, QFont::Bold);
3111
	fntNormal.setPixelSize(10);
3112
	fntNormal.setStyleHint(QFont::Helvetica);
3113
//	fntBold.setPixelSize(10);
3114
//	fntBold.setStyleHint(QFont::Helvetica);
3115
	// Calculate ticks
3116
	margin_left = 52;
3117
	margin_right = 40;
3118
	margin_bottom = 12;
3119
	lineHeight = 10;
3120
	rh = height - margin_bottom - 1;
3121
 
249 andreas 3122
	w_tick = (double)(width - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
88 andreas 3123
 
222 andreas 3124
	if (cuType == 1)	// Speed and heart rate?
88 andreas 3125
	{
218 andreas 3126
	   if ((maxSpeed - minSpeed) > (double)(maxHr - minHr))
3127
	   {
222 andreas 3128
	      h_tick = (double)rh / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
218 andreas 3129
	      meter = true;
3130
	   }
3131
	   else
3132
	   {
3133
	      h_tick = (double)rh / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
3134
	      meter = false;
3135
	   }
88 andreas 3136
	}
222 andreas 3137
	else if (cuType == 2)	// Elevation and speed?
88 andreas 3138
	{
218 andreas 3139
	   if ((maxHeight - minHeight) > (double)(maxHr - minHr))
3140
	   {
3141
	      h_tick = (double)rh / (maxHeight - minHeight);		// 1 tick = 1 meter
3142
	      meter = true;
3143
	   }
3144
	   else
3145
	   {
222 andreas 3146
	      h_tick = (double)rh / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
3147
	      meter = false;
3148
	   }
3149
	}
3150
	else			// Elevation and heart rate
3151
	{
3152
	   if ((maxHeight - minHeight) > (double)(maxHr - minHr))
3153
	   {
3154
	      h_tick = (double)rh / (maxHeight - minHeight);		// 1 tick = 1 meter
3155
	      meter = true;
3156
	   }
3157
	   else
3158
	   {
218 andreas 3159
	      h_tick = (double)rh / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
3160
	      meter = false;
3161
	   }
88 andreas 3162
	}
218 andreas 3163
 
88 andreas 3164
	// Fill background with background colors
3165
	paint.fillRect(0, 0, width + 4, height + 4, background);
3166
	// Draw a grid with markers at every 10 minutes
232 andreas 3167
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
88 andreas 3168
	// Bottom border line
3169
	x1 = margin_left;
3170
	y1 = height - margin_bottom;
3171
	x2 = width - margin_right;
3172
	y2 = y1;
3173
	paint.drawLine(x1, y1, x2, y2);
3174
	// Left border line
3175
	x1 = x2 = margin_left;
3176
	y1 = 2;
3177
	y2 = height - margin_bottom;
3178
	paint.drawLine(x1, y1, x2, y2);
3179
	// right border line
3180
	x1 = x2 = width - margin_right;
3181
	paint.drawLine(x1, y1, x2, y2);
148 andreas 3182
 
3183
	// Draw some darker lines to show the laps, if we have one
3184
	// and, in case we have a given lap, we fill the area.
3185
	QDateTime *qt;
3186
	QTime zeit = StartTime.time();
3187
	rn = ds.getRunNode();
232 andreas 3188
	paint.setPen(QPen(hlight, 1, Qt::SolidLine));
148 andreas 3189
 
3190
	for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
3191
	{
3192
	   if ((lp = ds.getLap(i)) == NULL)
3193
	      continue;
3194
 
3195
	   qt = garmin_dtime(lp->start_time);
3196
	   secs = zeit.secsTo(qt->time());
3197
	   delete qt;
3198
	   x1 = secs * w_tick + margin_left + 1;
3199
 
3200
	   if (lap && lp->start_time == lap->start_time)
3201
	      paint.fillRect(x1, 2, (int)((double)lap->total_time / 100.0 * w_tick), height - margin_bottom - 2, hlight);
3202
	   else
3203
	      paint.drawLine(x1, 2, x1, height - margin_bottom);
3204
	}
3205
 
88 andreas 3206
	// Grid vertical
232 andreas 3207
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
88 andreas 3208
	paint.setFont(fntNormal);
3209
	paint.drawText(margin_left - 20, height - lineHeight, 40, lineHeight, Qt::AlignCenter, QString("00:00"));
3210
	paint.save();
3211
	paint.rotate(270);
218 andreas 3212
 
222 andreas 3213
	if (cuType == 1)
232 andreas 3214
	   paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3215
	else
232 andreas 3216
	   paint.setPen(QPen(barcol, 1, Qt::SolidLine));
222 andreas 3217
 
3218
	// Information on left side
218 andreas 3219
	if (cuType == 0)
3220
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
3221
	else if (cuType == 1)
3222
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
3223
	else
3224
	   paint.drawText((height + 4) * -1, 3, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
3225
 
222 andreas 3226
	if (cuType == 2)
232 andreas 3227
	   paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3228
	else
232 andreas 3229
	   paint.setPen(QPen(blue, 1, Qt::SolidLine));
218 andreas 3230
 
222 andreas 3231
	// Information on right side
218 andreas 3232
	if (cuType < 2)
3233
	   paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
3234
	else
3235
	   paint.drawText((height + 4) * -1, width - 1 - lineHeight, height - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
3236
 
88 andreas 3237
	paint.restore();
232 andreas 3238
	paint.setPen(QPen(mark, 1, Qt::SolidLine));
222 andreas 3239
	// Draw the time scale on the bottom of the graphic
249 andreas 3240
	for (i = 0; (unsigned int)i < (max_time + ds.getPauseTime()); i++)
88 andreas 3241
	{
3242
	   if (i > 0 && !(i % 600))	// every 10 minutes
3243
	   {
3244
	      x1 = x2 = margin_left + w_tick * i;
3245
 
3246
	      if (x1 == (width - margin_right))
3247
		 continue;
3248
 
3249
	      y1 = 2;
3250
	      y2 = height - margin_bottom;
3251
	      paint.drawLine(x1, y1, x2, y2);
3252
	      QTime tm(0, 0, 0);
3253
	      tm = tm.addSecs(i);
232 andreas 3254
	      paint.setPen(QPen(frame, 1, Qt::SolidLine));
230 andreas 3255
//	      paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((i >= 3600) ? "hh:mm:ss" : "mm:ss"));
3256
	      paint.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
232 andreas 3257
	      paint.setPen(QPen(mark, 1, Qt::SolidLine));
88 andreas 3258
	   }
3259
	}
222 andreas 3260
 
3261
	// This is the total time, with pauses included, at the lower right
3262
	// corner of the graphic.
88 andreas 3263
	QTime tm(0, 0, 0);
3264
	QString qs;
249 andreas 3265
	tm = tm.addSecs(max_time + ds.getPauseTime());
232 andreas 3266
	paint.setPen(QPen(frame, 1, Qt::SolidLine));
230 andreas 3267
//	paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, tm.toString((max_time >= 3600) ? "hh:mm:ss" : "mm:ss"));
3268
	paint.drawText(width - margin_right - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
88 andreas 3269
 
222 andreas 3270
	// Draw the minimal elevation, speed and/or heart rate
218 andreas 3271
	if (max_height > 0.0 || max_speed > 0.0)
88 andreas 3272
	{
222 andreas 3273
	   // left side
218 andreas 3274
	   if (cuType == 1)
222 andreas 3275
	   {
232 andreas 3276
	      paint.setPen(QPen(red, 1, Qt::SolidLine));
218 andreas 3277
	      paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minSpeed));
222 andreas 3278
	   }
218 andreas 3279
	   else
222 andreas 3280
	   {
232 andreas 3281
	      paint.setPen(QPen(barcol, 1, Qt::SolidLine));
218 andreas 3282
	      paint.drawText(12, height - margin_bottom - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
222 andreas 3283
	   }
88 andreas 3284
	}
3285
 
222 andreas 3286
	if (max_hr > 0 || max_speed > 0.0)
88 andreas 3287
	{
222 andreas 3288
	   // right side
3289
	   if (cuType == 2)
3290
	   {
232 andreas 3291
	      paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3292
	      paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", minSpeed));
3293
	   }
3294
	   else
3295
	   {
232 andreas 3296
	      paint.setPen(QPen(blue, 1, Qt::SolidLine));
222 andreas 3297
	      paint.drawText(width - margin_right + 2, height - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
3298
	   }
88 andreas 3299
	}
3300
 
232 andreas 3301
	paint.setPen(QPen(mark, 1, Qt::SolidLine));
88 andreas 3302
 
3303
	// Grid horizontal
218 andreas 3304
	int factor = 0;
3305
	int target = 0;
3306
 
222 andreas 3307
	if (cuType == 0)	// Elevation and heart rate
218 andreas 3308
	{
222 andreas 3309
	   factor = (meter) ? (maxHeight - minHeight) / (rh / 12) : (maxHr - minHr) / (rh / 12);
3310
	   target = (meter) ? (int)(maxHeight - minHeight) : (maxHr - minHr);
3311
	}
3312
	else if (cuType == 1)	// Speed and heart rate
3313
	{
218 andreas 3314
	   factor = (meter) ? (maxSpeed - minSpeed) / (rh / 12) : (maxHr - minHr) / (rh / 12);
3315
	   target = (meter) ? (int)(maxSpeed - minSpeed) : (maxHr - minHr);
3316
	}
222 andreas 3317
	else			// Elevation and speed
218 andreas 3318
	{
222 andreas 3319
	   factor = (meter) ? (maxHeight - minHeight) / (rh /12) : (maxSpeed - minSpeed) / (rh / 12);
3320
	   target = (meter) ? (int)(maxHeight - minHeight) : (int)(maxSpeed - minSpeed);
218 andreas 3321
	}
3322
 
222 andreas 3323
	// To prevent a division by zero error, we check the <factor>
3324
	if (factor == 0)
3325
	   factor = 1;
3326
 
3327
	// Beside the horizontal part of the grid, we draw the scale on the
3328
	// left and the right side.
88 andreas 3329
	int oldy = height;
3330
 
3331
	for (i = 0; i < target; i++)
3332
	{
3333
	   if (i > 0 && !(i % factor))
3334
	   {
3335
	      x1 = margin_left + 1;
3336
	      x2 = width - margin_right - 1;
3337
	      y1 = y2 = rh - h_tick * i;
3338
 
3339
	      if (y1 < 12)
3340
		 break;
3341
 
3342
	      paint.drawLine(x1, y1, x2, y2);
3343
 
3344
	      if (y1 < (oldy - lineHeight))
3345
	      {
3346
		 if (meter)
3347
		 {
232 andreas 3348
		    paint.setPen(QPen(barcol, 1, Qt::SolidLine));
222 andreas 3349
		    // left side
218 andreas 3350
		    if (cuType == 1)
222 andreas 3351
		    {
232 andreas 3352
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3353
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + i));
3354
		    }
218 andreas 3355
		    else
3356
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));
3357
 
222 andreas 3358
		    // right side
3359
		    if (maxHr > 0 && cuType != 2)
88 andreas 3360
		    {
3361
		       double hrscale = (double)(maxHr - minHr) / (double)target;
232 andreas 3362
		       paint.setPen(QPen(blue, 1, Qt::SolidLine));
88 andreas 3363
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", (int)((double)minHr + hrscale * (double)i)));
3364
		    }
222 andreas 3365
		    else
3366
		    {
3367
		       double spscale = (maxSpeed - minSpeed) / (double)target;
3368
 
3369
		       if (cuType == 2)
232 andreas 3370
			  paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3371
		       else
232 andreas 3372
			  paint.setPen(QPen(blue, 1, Qt::SolidLine));
222 andreas 3373
 
3374
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", (minSpeed + spscale * (double)i)));
3375
		    }
88 andreas 3376
		 }
3377
		 else
3378
		 {
222 andreas 3379
		    // right side
3380
		    if (cuType == 2)
3381
		    {
232 andreas 3382
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3383
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", minSpeed + i));
3384
		    }
3385
		    else
3386
		    {
232 andreas 3387
		       paint.setPen(QPen(blue, 1, Qt::SolidLine));
222 andreas 3388
		       paint.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
3389
		    }
88 andreas 3390
 
222 andreas 3391
		    // left side
3392
		    if ((cuType == 0 || cuType == 2) && max_height > 0)
88 andreas 3393
		    {
3394
		       double hrscale = (maxHeight - minHeight) / (double)target;
232 andreas 3395
		       paint.setPen(QPen(barcol, 1, Qt::SolidLine));
88 andreas 3396
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.0f", minHeight + hrscale * (double)i));
3397
		    }
222 andreas 3398
		    else if (max_speed > 0 && cuType == 1)
218 andreas 3399
		    {
3400
		       double hrscale = (maxSpeed - minSpeed) / (double)target;
232 andreas 3401
		       paint.setPen(QPen(red, 1, Qt::SolidLine));
222 andreas 3402
		       paint.drawText(12, y1 - lineHeight / 2, margin_left - 14, lineHeight, Qt::AlignRight, qs.sprintf("%.1f", minSpeed + hrscale * (double)i));
218 andreas 3403
		    }
88 andreas 3404
		}
3405
 
232 andreas 3406
		 paint.setPen(QPen(mark, 1, Qt::SolidLine));
88 andreas 3407
		 oldy = y1;
3408
	      }
3409
	   }
3410
	}
3411
 
168 andreas 3412
	// To make our graphics more beautiful, we draw lines for the
88 andreas 3413
	// heart rate limits and the average heart rate.
222 andreas 3414
	if (max_hr > 0 && cuType != 2)
88 andreas 3415
	{
3416
	int ay1, ay2, ay3, ay4, ay5;
3417
 
3418
	   x1 = margin_left + 1;
3419
	   x2 = width - margin_right - 1;
3420
 
3421
	   if (meter)
3422
	   {
3423
	      double hrscale = rh / (double)(maxHr - minHr);
3424
	      ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
3425
	      ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
3426
	      ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
3427
	      ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
3428
	      ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
3429
	   }
3430
	   else
3431
	   {
3432
	      ay1 = (double)rh - (double)(lower1 - minHr) * h_tick;
3433
	      ay2 = (double)rh - (double)(lower2 - minHr) * h_tick;
3434
	      ay3 = (double)rh - (double)(lower3 - minHr) * h_tick;
3435
	      ay4 = (double)rh - (double)(upper3 - minHr) * h_tick;
3436
	      ay5 = (double)rh - (double)(avg_hr - minHr) * h_tick;
3437
	   }
3438
 
232 andreas 3439
	   paint.setPen(QPen(barcol2, 1, Qt::DashLine));	// color for limits
88 andreas 3440
 
3441
	   if (lower1 > minHr && lower1 < maxHr)
3442
	      paint.drawLine(x1, ay1, x2, ay1);
3443
 
3444
	   if (lower2 > minHr && lower2 < maxHr)
3445
	      paint.drawLine(x1, ay2, x2, ay2);
3446
 
3447
	   if (lower3 > minHr && lower3 < maxHr)
3448
	      paint.drawLine(x1, ay3, x2, ay3);
3449
 
3450
	   if (upper3 > minHr && upper3 < maxHr)
3451
	      paint.drawLine(x1, ay4, x2, ay4);
3452
 
232 andreas 3453
	   paint.setPen(QPen(red, 1, Qt::DashDotLine));	// color for average heart rate
88 andreas 3454
 
3455
	   if (avg_hr > minHr && avg_hr < maxHr)
3456
	      paint.drawLine(x1, ay5, x2, ay5);
3457
	}
3458
 
3459
	// Now we have a grid and we've done the scaling.
3460
	// This is the point where we draw the curves itself.
223 andreas 3461
	// We use different colors to draw the lines:
3462
	//
3463
	// Green: Elevation
3464
	// Red:   Speed
3465
	// Blue   Heart Rate
3466
	//
88 andreas 3467
	x1 = x2 = y1 = y2 = 0;
148 andreas 3468
	int hy1, hy2, hx1, hx2;
222 andreas 3469
	int sy1, sy2, sx1, sx2;
88 andreas 3470
	hy1 = hy2 = hx1 = hx2 = 0;
222 andreas 3471
	sy1 = sy2 = sx1 = sx2 = 0;
169 andreas 3472
	int hEc = 0;
3473
	i = 0;
3474
	AVGHEIGHT *avgHakt, *avgHfirst, *avgHlast, *avgHeight = 0;
170 andreas 3475
	avgHfirst = avgHlast = 0;
169 andreas 3476
	// To even the surface lines, we store every altitude in the
3477
	// memory, by building a chain. Then, if the user has set in the
3478
	// settings (Contour == true), we will even the line by calculating
3479
	// the average of 10 messure points and setting every value to the
3480
	// average, who is up or down more than 2 meters from the average.
3481
	while ((point = ds.getPoint(i)) != 0)
3482
	{
3483
	   if (point->alt > 20000.0 || point->alt < -1000.0)
3484
	   {
3485
	      i++;
3486
	      continue;
3487
	   }
168 andreas 3488
 
169 andreas 3489
	   if (!avgHeight)
3490
	   {
3491
	      avgHeight = new AVGHEIGHT;
3492
	      avgHeight->alt = point->alt;
3493
	      avgHeight->pos = hEc;
3494
	      avgHeight->prev = 0;
3495
	      avgHeight->next = 0;
3496
	      avgHakt = avgHeight;
3497
	      avgHfirst = avgHeight;
3498
	   }
3499
	   else
3500
	   {
3501
	      avgHakt = new AVGHEIGHT;
3502
	      avgHakt->alt = point->alt;
3503
	      avgHakt->pos = hEc;
3504
	      avgHakt->next = 0;
3505
	      avgHakt->prev = avgHeight;
3506
	      avgHeight->next = avgHakt;
3507
	      avgHeight = avgHakt;
3508
	   }
3509
 
3510
	   // FIXME: Currently we can not draw below 0 meters, because the
3511
	   // base line is always 0!
222 andreas 3512
	   if (avgHakt->alt < minHeight)
3513
	      avgHakt->alt = minHeight;
169 andreas 3514
 
3515
	   hEc++;
3516
	   i++;
3517
	}
3518
 
3519
	avgHlast = avgHeight;
3520
	// If wanted, even the lines
218 andreas 3521
	if (Contour && hEc > 0 && cuType != 0)
169 andreas 3522
	{
213 andreas 3523
	double alt[100], avg, avg1, avg2, avg3, avg4;
3524
	int a, pos;
169 andreas 3525
 
213 andreas 3526
	   for (i = 0; i < (hEc + 100); i += 100)
169 andreas 3527
	   {
213 andreas 3528
	      avg = avg1 = avg2 = avg3 = avg4 = 0.0;
3529
	      pos = 0;
169 andreas 3530
 
213 andreas 3531
	      for (a = 0; a < 100; a++)
169 andreas 3532
	      {
3533
		 alt[a] = getAvgAlt(avgHfirst, i + a);
3534
		 avg += alt[a];
213 andreas 3535
 
3536
		 if (a < 25)
3537
		    avg1 += alt[a];
3538
		 else if (a < 50)
3539
		    avg2 += alt[a];
3540
		 else if (a < 75)
3541
		    avg3 += alt[a];
3542
		 else
3543
		    avg4 += alt[a];
169 andreas 3544
	      }
3545
 
213 andreas 3546
	      if ((i + 100) >= hEc)
169 andreas 3547
		 avg /= (double)(hEc - i) + 1.0;
3548
	      else
213 andreas 3549
		 avg /= 100.0;
169 andreas 3550
 
213 andreas 3551
	      for (a = 0; a < 100; a++)
169 andreas 3552
	      {
3553
		 if ((avgHakt = getAvgPtr(avgHfirst, i + a)) != 0)
3554
		 {
3555
		    if ((avgHakt->alt - avg) > 2 || (avgHakt->alt - avg) < -2)
3556
		       avgHakt->alt = avg;
3557
		 }
3558
	      }
3559
	   }
3560
	}
3561
 
222 andreas 3562
	// plot the elevation, speed and/or heart rate. Depends on <cuType>)
168 andreas 3563
	i = 0;
169 andreas 3564
	int j = 0;
218 andreas 3565
	POINT *oldPoint = 0;
222 andreas 3566
	double speed = 0.0;	// calculated speed
3567
	bool pause = false;	// filter pause out of speed
3568
	unsigned long t1, t2;
3569
	t1 = t2 = 0;
218 andreas 3570
 
88 andreas 3571
	while ((point = ds.getPoint(i)) != 0)
3572
	{
218 andreas 3573
	   if (!oldPoint)
3574
	      oldPoint = point;
3575
 
88 andreas 3576
	   // calculate the y position based on the time
3577
	   qt = garmin_dtime(point->time);
3578
	   secs = zeit.secsTo(qt->time());
3579
	   delete qt;
3580
	   x2 = secs * w_tick + margin_left + 1;
3581
	   hx2 = x2;
222 andreas 3582
	   sx2 = x2;
88 andreas 3583
 
3584
	   if (x1 == 0)
3585
	      x1 = x2;
3586
 
3587
	   if (hx1 == 0)
3588
	      hx1 = hx2;
3589
 
222 andreas 3590
	   if (sx1 == 0)
3591
	      sx1 = sx2;
3592
 
3593
	   // The speed is not very exact, because smallest time is
3594
	   // one second. This allows a maximum error of 99 hundredths
3595
	   // of a second, what is very close to one second. Because of
3596
	   // this, speed seems to hop for every messure point. This
3597
	   // looks ugly, but currently I don't know how to make it
3598
	   // better.
3599
	   if (cuType == 1 || cuType == 2)	// Draw speed?
88 andreas 3600
	   {
218 andreas 3601
	      double dist;
3602
	      double sc;
168 andreas 3603
 
222 andreas 3604
	      if (!pause && point->distance > 1.0e10)
218 andreas 3605
	      {
222 andreas 3606
		 pause = true;
3607
		 t1 = point->time;
3608
	      }
3609
	      else if (pause)
3610
	      {
3611
		 pause = false;
3612
		 t2 = point->time;
3613
		 i += 2;
3614
		 continue;
3615
	      }
3616
 
3617
	      if (point->distance >= 0.1 && point->distance < 1.0e10)
3618
	      {
218 andreas 3619
		 dist = point->distance - oldPoint->distance;
3620
		 sc = point->time - oldPoint->time;
223 andreas 3621
		 LAP *runde = ds.getLapT (point->time);
222 andreas 3622
 
3623
		 if (t2 > t1)
3624
		 {
3625
		    sc -= t2 - t1;
3626
 
3627
		    if (sc <= 0.0)
3628
		       sc = 1.0;		// at least 1 second!
3629
		 }
3630
 
218 andreas 3631
		 speed = (dist / sc) * 3.6;
168 andreas 3632
 
223 andreas 3633
		 if (runde && runde->max_speed > 0.0 && speed > (runde->max_speed * 3.6))
3634
		    speed = runde->max_speed * 3.6;
3635
 
218 andreas 3636
		 if (Units == 1)
3637
		    speed /= 1.609344;
3638
 
222 andreas 3639
		 if (speed < minSpeed || speed > 400.0)
3640
		    speed = minSpeed;
218 andreas 3641
 
222 andreas 3642
		 if ((meter && cuType == 1) || (!meter && cuType == 2))
218 andreas 3643
		    y2 = (double)rh - (speed - minSpeed) * h_tick;
3644
		 else
3645
		 {
3646
		    double hrscale = rh / (maxSpeed - minSpeed);
3647
		    y2 = (double)rh - (speed - minSpeed) * hrscale;
3648
		 }
3649
 
3650
		 if (y1 == 0)
3651
		    y1 = y2;
3652
 
232 andreas 3653
		 paint.setPen(QPen(red, 1, Qt::SolidLine));
218 andreas 3654
		 paint.drawLine(x1, y1, x2, y2);
3655
		 y1 = y2;
3656
		 x1 = x2;
222 andreas 3657
		 t1 = t2 = 0;
3658
		 oldPoint = point;
218 andreas 3659
	      }
3660
	   }
222 andreas 3661
 
3662
	   if (cuType == 0 || cuType == 2)		// Draw elevation?
218 andreas 3663
	   {
3664
	      if (point->alt < 20000.0 && point->alt > -1000.0)
88 andreas 3665
	      {
218 andreas 3666
	      double alt = getAvgAlt(avgHfirst, j);
88 andreas 3667
 
218 andreas 3668
		 j++;
3669
 
3670
		 if (meter)
222 andreas 3671
		    sy2 = (double)rh - (alt - minHeight) * h_tick;
218 andreas 3672
		 else
3673
		 {
3674
		    double hrscale = rh / (maxHeight - minHeight);
222 andreas 3675
		    sy2 = (double)rh - (alt - minHeight) * hrscale;
218 andreas 3676
		 }
3677
 
222 andreas 3678
		 if (sy1 == 0)
3679
		    sy1 = sy2;
218 andreas 3680
 
232 andreas 3681
		 paint.setPen(QPen(barcol, 1, Qt::SolidLine));
222 andreas 3682
		 paint.drawLine(sx1, sy1, sx2, sy2);
3683
		 sy1 = sy2;
3684
		 sx1 = sx2;
218 andreas 3685
	      }
88 andreas 3686
	   }
3687
 
222 andreas 3688
	   if (point->heart_rate > 0 && cuType < 2)	// Draw heart rate?
88 andreas 3689
	   {
3690
	      if (meter)
3691
	      {
3692
		 double hrscale = rh / (double)(maxHr - minHr);
3693
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * hrscale;
3694
	      }
3695
	      else
3696
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * h_tick;
3697
 
3698
	      if (hy1 == 0)
3699
		 hy1 = hy2;
3700
 
232 andreas 3701
	      paint.setPen(QPen(blue, 1, Qt::SolidLine));
88 andreas 3702
	      paint.drawLine(hx1, hy1, hx2, hy2);
3703
	      hy1 = hy2;
3704
	      hx1 = hx2;
3705
	   }
3706
 
3707
	   i++;
3708
	}
3709
 
3710
	paint.end();
245 andreas 3711
	ui_sportwatcherWidgetBase.imgProfile->setPixmap(pmProfile);
88 andreas 3712
 
169 andreas 3713
	// free the chain of altitudes
3714
	avgHakt = avgHfirst;
3715
 
3716
	while (avgHakt)
3717
	{
3718
	   avgHeight = avgHakt->next;
3719
	   delete avgHakt;
3720
	   avgHakt = avgHeight;
3721
	}
3722
 
246 andreas 3723
	DIRTY = false;
88 andreas 3724
}
3725
 
250 andreas 3726
void sportwatcherWidget::showThreeCurve()
3727
{
3728
QPainter ptHR, ptElevation, ptSpeed;
3729
int width, height, wdHR, htHR, wdElev, htElev, wdSpeed, htSpeed;
3730
int i, secs, cuType;
3731
int lineHeight, margin_left, margin_right, margin_bottom;
3732
int x1, y1, x2, y2;		// Coordinates
3733
bool meter, meterHR, meterElev, meterSpeed;
3734
double maxHeight, minHeight, maxSpeed, minSpeed;
3735
int maxHr, minHr, rh, rhHR, rhElev, rhSpeed;
3736
POINT *point;
3737
RUN_NODE *rn;
3738
LAP *lp;
3739
double wtiHR, htiHR, wtiElev, htiElev, wtiSpeed, htiSpeed;
3740
double w_tick, h_tick;		// Number of pixels one "tick" has;
3741
				// This depends on the width and height
3742
				// of the image.
3743
	// First we draw a grid based on the min and max
3744
	// values detected in the function showLap(). In case
3745
	// all values are 0, we exit here.
3746
	if (min_hr == 0 && max_hr == 0 && min_height == 0.0 && max_height == 0.0)
3747
	   return;
3748
 
3749
	if (!DIRTY || curTab != 2)
3750
	   return;
3751
 
3752
	// Get the dimensions of the available draw area
3753
	// First for heart rate
3754
	wdHR = ui_sportwatcherWidgetBase.grHR->width() - 2;
3755
	htHR = ui_sportwatcherWidgetBase.grHR->height();
3756
	pmHR = QPixmap(wdHR, htHR);
3757
	// Then for elevation
3758
	wdElev = ui_sportwatcherWidgetBase.grElevation->width() - 2;
3759
	htElev = ui_sportwatcherWidgetBase.grElevation->height();
3760
	pmElevation = QPixmap(wdElev, htElev);
3761
	// And at last for speed
3762
	wdSpeed = ui_sportwatcherWidgetBase.grSpeed->width() - 2;
3763
	htSpeed = ui_sportwatcherWidgetBase.grSpeed->height();
3764
	pmSpeed = QPixmap(wdSpeed, htSpeed);
3765
	// Initialize QPainter
3766
	ptHR.begin(&pmHR);
3767
	ptElevation.begin(&pmElevation);
3768
	ptSpeed.begin(&pmSpeed);
3769
 
3770
	// we need a somewhat bigger area to draw our curves than
3771
	// we have with the real min and max values.
3772
	if (max_height > 0.0)
3773
	{
3774
	double add = (max_height - min_height) / 100.0 * 5.0;	// Percent
3775
 
3776
	   maxHeight = max_height + add;
3777
	   minHeight = min_height - add;
3778
 
3779
	   if (minHeight < 0.0)		// make sure, we are not too deep
3780
	      minHeight = 0.0;
3781
	}
3782
	else
3783
	   maxHeight = minHeight = 0.0;
3784
 
3785
	if (max_speed > 0.0)
3786
	{
3787
	double add = (max_speed - min_speed) / 100.0 * 5.0;	// Percent
3788
 
3789
	   maxSpeed = max_speed + add;
3790
	   minSpeed = min_speed - add;
3791
 
3792
	   if (minSpeed < 0.0)		// make sure, we are not too deep
3793
	      minSpeed = 0.0;
3794
	}
3795
	else
3796
	   maxSpeed = minSpeed = 0.0;
3797
 
3798
	if (max_hr > 0)
3799
	{
3800
	   maxHr = max_hr + 10;
3801
	   minHr = min_hr - 10;
3802
 
3803
	   if (minHr < 0)
3804
	      minHr = 0;
3805
	}
3806
	else
3807
	   maxHr = minHr = 0;
3808
 
3809
	// Define colors
3810
	QColor background(220, 220, 220);	// Background of graphic
3811
	QColor mark(255, 255, 255);		// Lines inside curve area
3812
	QColor hlight(180, 180, 180);		// area of current lap
3813
	hlight.setAlpha(128);			// 50% transparent
3814
	QColor frame(0, 0, 0);			// Text and borders
3815
	QColor barcol(151, 190, 13);		// heart rate
3816
	QColor barcol2(190, 151, 13);		// height over NN
3817
	QColor red(220, 128, 128);		// speed
3818
	QColor blue(0, 0, 240);
3819
	QFont fntNormal("Helvetica");
3820
//	QFont fntBold("Helvetica", 10, QFont::Bold);
3821
	fntNormal.setPixelSize(10);
3822
	fntNormal.setStyleHint(QFont::Helvetica);
3823
//	fntBold.setPixelSize(10);
3824
//	fntBold.setStyleHint(QFont::Helvetica);
3825
	// Calculate ticks
3826
	margin_left = 1;
3827
	margin_right = 40;
3828
	margin_bottom = 12;
3829
	lineHeight = 10;
3830
//	rh = height - margin_bottom - 1;
3831
 
3832
	// Calculate the ticks for width and height for every draw area
3833
	for (i = 0; i < 3; i++)
3834
	{
3835
	   if (i == 2)	// Speed?
3836
	   {
3837
	      rhSpeed = htSpeed - margin_bottom - 1;
3838
	      wtiSpeed = (double)(wdSpeed - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
3839
	      htiSpeed = (double)rhSpeed / (maxSpeed - minSpeed);		// 1 tick = 1 km/h
3840
	   }
3841
	   else if (i == 1)	// Elevation?
3842
	   {
3843
	      rhElev = htElev - margin_bottom - 1;
3844
	      wtiElev = (double)(wdElev - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
3845
	      htiElev = (double)rhElev / (maxHeight - minHeight);		// 1 tick = 1 meter
3846
	   }
3847
	   else			// heart rate
3848
	   {
3849
	      rhHR = htHR - margin_bottom - 1;
3850
	      wtiHR = (double)(wdHR - (margin_left + margin_right)) / (max_time + ds.getPauseTime());	// 1 tick = 1 second
3851
	      htiHR = (double)rhHR / ((double)maxHr - (double)minHr);	// 1 tick = 1 bpm
3852
	   }
3853
	}
3854
 
3855
	// Fill background with background colors
3856
	ptHR.fillRect(0, 0, wdHR + 4, htHR + 4, background);
3857
	ptElevation.fillRect(0, 0, wdElev + 4, htElev + 4, background);
3858
	ptSpeed.fillRect(0, 0, wdSpeed + 4, htSpeed + 4, background);
3859
	// Draw a grid with markers at every 10 minutes
3860
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
3861
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
3862
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
3863
	// Bottom border line
3864
	x1 = margin_left;
3865
 
3866
	y1 = htHR - margin_bottom;
3867
	x2 = wdHR - margin_right;
3868
	y2 = y1;
3869
	ptHR.drawLine(x1, y1, x2, y2);
3870
 
3871
	y1 = htElev - margin_bottom;
3872
	x2 = wdElev - margin_right;
3873
	y2 = y1;
3874
	ptElevation.drawLine(x1, y1, x2, y2);
3875
 
3876
	y1 = htSpeed - margin_bottom;
3877
	x2 = wdSpeed - margin_right;
3878
	y2 = y1;
3879
	ptSpeed.drawLine(x1, y1, x2, y2);
3880
 
3881
	// Left border line
3882
	x1 = x2 = margin_left;
3883
	y1 = 2;
3884
	y2 = htHR - margin_bottom;
3885
	ptHR.drawLine(x1, y1, x2, y2);
3886
 
3887
	y2 = htElev - margin_bottom;
3888
	ptElevation.drawLine(x1, y1, x2, y2);
3889
 
3890
	y2 = htSpeed - margin_bottom;
3891
	ptSpeed.drawLine(x1, y1, x2, y2);
3892
 
3893
	// right border line
3894
	x1 = x2 = wdHR - margin_right;
3895
	ptHR.drawLine(x1, y1, x2, y2);
3896
 
3897
	x1 = x2 = wdElev - margin_right;
3898
	ptElevation.drawLine(x1, y1, x2, y2);
3899
 
3900
	x1 = x2 = wdSpeed - margin_right;
3901
	ptSpeed.drawLine(x1, y1, x2, y2);
3902
 
3903
	// Draw some darker lines to show the laps, if we have one
3904
	QDateTime *qt;
3905
	QTime zeit = StartTime.time();
3906
	rn = ds.getRunNode();
3907
	ptHR.setPen(QPen(hlight, 1, Qt::SolidLine));
3908
	ptElevation.setPen(QPen(hlight, 1, Qt::SolidLine));
3909
	ptSpeed.setPen(QPen(hlight, 1, Qt::SolidLine));
3910
 
3911
	for (i = rn->run->first_lap_index; (unsigned int)i <= rn->run->last_lap_index; i++)
3912
	{
3913
	   if ((lp = ds.getLap(i)) == NULL)
3914
	      continue;
3915
 
3916
	   qt = garmin_dtime(lp->start_time);
3917
	   secs = zeit.secsTo(qt->time());
3918
	   delete qt;
3919
	   // heart rate
3920
	   x1 = secs * wtiHR + margin_left + 1;
3921
	   ptHR.drawLine(x1, 2, x1, htHR - margin_bottom);
3922
	   // Elevation
3923
	   x1 = secs * wtiElev + margin_left + 1;
3924
	   ptElevation.drawLine(x1, 2, x1, htElev - margin_bottom);
3925
	   // Speed
3926
	   x1 = secs * wtiSpeed + margin_left + 1;
3927
	   ptSpeed.drawLine(x1, 2, x1, htSpeed - margin_bottom);
3928
	}
3929
 
3930
	// Grid vertical
3931
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
3932
	ptHR.setFont(fntNormal);
3933
	ptHR.drawText(margin_left, htHR - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
3934
 
3935
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
3936
	ptElevation.setFont(fntNormal);
3937
	ptElevation.drawText(margin_left, htElev - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
3938
 
3939
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
3940
	ptSpeed.setFont(fntNormal);
3941
	ptSpeed.drawText(margin_left, htSpeed - lineHeight, 40, lineHeight, Qt::AlignLeft, QString("0"));
3942
 
3943
	ptHR.save();
3944
	ptHR.rotate(270);
3945
	ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
3946
 
3947
	ptElevation.save();
3948
	ptElevation.rotate(270);
3949
	ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
3950
 
3951
	ptSpeed.save();
3952
	ptSpeed.rotate(270);
3953
	ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
3954
 
3955
	// Information on right side
3956
	ptHR.drawText((htHR + 4) * -1, wdHR - 1 - lineHeight, htHR - 2, lineHeight, Qt::AlignCenter, i18n("Heart Rate (bpm)"));
3957
	ptElevation.drawText((htElev + 4) * -1, wdElev - 1 - lineHeight, htElev - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Elevation (ft)" : "Elevation (m)"));
3958
	ptSpeed.drawText((htSpeed + 4) * -1, wdSpeed - 1 - lineHeight, htSpeed - 2, lineHeight, Qt::AlignCenter, i18n((Units == 1) ? "Speed (mph)" : "Speed (km/h)"));
3959
 
3960
	// restore to normal
3961
	ptHR.restore();
3962
	ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
3963
 
3964
	ptElevation.restore();
3965
	ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
3966
 
3967
	ptSpeed.restore();
3968
	ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
3969
	// Draw the time scale on the bottom of the graphic
3970
	for (i = 0; (unsigned int)i < (max_time + ds.getPauseTime()); i++)
3971
	{
3972
	bool loop = false;
3973
 
3974
	   if (i > 0 && !(i % 600))	// every 10 minutes
3975
	   {
3976
	      for (int j = 0; j < 3; j++)
3977
	      {
3978
		 switch (j)
3979
		 {
3980
		    case 0: w_tick = wtiHR; width = wdHR; height = htHR; break;
3981
		    case 1: w_tick = wtiElev; width = wdElev; height = htElev; break;
3982
		    case 2: w_tick = wtiSpeed; width = wdSpeed; height = htSpeed; break;
3983
		 }
3984
 
3985
		 x1 = x2 = margin_left + w_tick * i;
3986
 
3987
		 if (x1 == (width - margin_right - 25))
3988
		 {
3989
		    loop = true;
3990
		    break;
3991
		 }
3992
 
3993
		 y1 = 2;
3994
		 y2 = height - margin_bottom;
3995
		 QTime tm(0, 0, 0);
3996
		 tm = tm.addSecs(i);
3997
 
3998
		 if (j == 0)
3999
		 {
4000
		    ptHR.drawLine(x1, y1, x2, y2);
4001
		    ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
4002
		    ptHR.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
4003
		    ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
4004
		 }
4005
		 else if (j == 1)
4006
		 {
4007
		    ptElevation.drawLine(x1, y1, x2, y2);
4008
		    ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
4009
		    ptElevation.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
4010
		    ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
4011
		 }
4012
		 else
4013
		 {
4014
		    ptSpeed.drawLine(x1, y1, x2, y2);
4015
		    ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
4016
		    ptSpeed.drawText(x1 - 25, height - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime (tm, (i >= 3600) ? true : false));
4017
		    ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
4018
		 }
4019
	      }
4020
 
4021
	      if (loop)
4022
	      {
4023
		 loop = false;
4024
		 continue;
4025
	      }
4026
	   }
4027
	}
4028
 
4029
	// This is the total time, with pauses included, at the lower right
4030
	// corner of the graphic.
4031
	QTime tm(0, 0, 0);
4032
	QString qs;
4033
	tm = tm.addSecs(max_time + ds.getPauseTime());
4034
	ptHR.setPen(QPen(frame, 1, Qt::SolidLine));
4035
	ptHR.drawText(wdHR - margin_right - 25, htHR - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
4036
 
4037
	ptElevation.setPen(QPen(frame, 1, Qt::SolidLine));
4038
	ptElevation.drawText(wdElev - margin_right - 25, htElev - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
4039
 
4040
	ptSpeed.setPen(QPen(frame, 1, Qt::SolidLine));
4041
	ptSpeed.drawText(wdSpeed - margin_right - 25, htSpeed - lineHeight, 50, lineHeight, Qt::AlignCenter, kl->formatTime(tm, (max_time >= 3600) ? true : false));
4042
 
4043
	// Draw the minimal elevation, speed and heart rate
4044
	// Heart rate
4045
	ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
4046
	ptHR.drawText(wdHR - margin_right + 2, htHR - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr));
4047
	// Elevation
4048
	ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
4049
	ptElevation.drawText(wdElev - margin_right + 2, htElev - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", (Units == 1) ? minHeight / 0.304 : minHeight));
4050
	// Speed
4051
	ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
4052
	ptSpeed.drawText(wdSpeed - margin_right + 2, htSpeed - margin_bottom - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", minSpeed));
4053
 
4054
	ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
4055
	ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
4056
	ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
4057
 
4058
	// Grid horizontal
4059
	int factor = 0;
4060
	int target = 0;
4061
 
4062
	for (int j = 0; j < 3; j++)
4063
	{
4064
	   switch (j)
4065
	   {
4066
	      case 0: width = wdHR; height = htHR; w_tick = wtiHR; h_tick = htiHR; rh = rhHR;
4067
		      factor = (maxHr - minHr) / (rhHR / 12);
4068
		      target = (maxHr - minHr);
4069
	      break;
4070
 
4071
	      case 1: width = wdElev; height = htElev; w_tick = wtiElev; h_tick = htiElev; rh = rhElev;
4072
		      factor = (maxHeight - minHeight) / (rhElev / 12);
4073
		      target = (int)(maxHeight - minHeight);
4074
	      break;
4075
 
4076
	      case 2: width = wdSpeed; height = htSpeed; w_tick = wtiSpeed; h_tick = htiSpeed; rh = rhSpeed;
4077
		      factor = (maxSpeed - minSpeed) / (rhSpeed / 12);
4078
		      target = (int)(maxSpeed - minSpeed);
4079
	      break;
4080
	   }
4081
 
4082
	   // To prevent a division by zero error, we check the <factor>
4083
	   if (factor == 0)
4084
	      factor = 1;
4085
 
4086
	   // Beside the horizontal part of the grid, we draw the scale on the
4087
	   // the right side too.
4088
	   int oldy = height;
4089
 
4090
	   for (i = 0; i < target; i++)
4091
	   {
4092
	      if (i > 0 && !(i % factor))
4093
	      {
4094
		 x1 = margin_left + 1;
4095
		 x2 = width - margin_right - 1;
4096
		 y1 = y2 = rh - h_tick * i;
4097
 
4098
		 if (y1 < 12)
4099
		    break;
4100
 
4101
		 switch (j)
4102
		 {
4103
		    case 0:
4104
		       ptHR.drawLine(x1, y1, x2, y2);
4105
		       ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
4106
		       ptHR.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%d", minHr + i));
4107
		       ptHR.setPen(QPen(mark, 1, Qt::SolidLine));
4108
		    break;
4109
 
4110
		    case 1:
4111
		       ptElevation.drawLine(x1, y1, x2, y2);
4112
		       ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
4113
		       ptElevation.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.0f", (Units == 1) ? (minHeight + i) / 0.304 : minHeight + i));
4114
		       ptElevation.setPen(QPen(mark, 1, Qt::SolidLine));
4115
		    break;
4116
 
4117
		    case 2:
4118
		       ptSpeed.drawLine(x1, y1, x2, y2);
4119
		       ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
4120
		       ptSpeed.drawText(width - margin_right + 2, y1 - lineHeight / 2, margin_right - 14, lineHeight, Qt::AlignLeft, qs.sprintf("%.1f", minSpeed + i));
4121
		       ptSpeed.setPen(QPen(mark, 1, Qt::SolidLine));
4122
		    break;
4123
		 }
4124
 
4125
		 oldy = y1;
4126
	      }
4127
	   }
4128
	}
4129
 
4130
	// To make our graphics more beautiful, we draw lines for the
4131
	// heart rate limits and the average heart rate.
4132
	if (max_hr > 0)
4133
	{
4134
	int ay1, ay2, ay3, ay4, ay5;
4135
 
4136
	   x1 = margin_left + 1;
4137
	   x2 = wdHR - margin_right - 1;
4138
 
4139
	   if (meterHR)
4140
	   {
4141
	      double hrscale = rh / (double)(maxHr - minHr);
4142
	      ay1 = (double)rh - (double)(lower1 - minHr) * hrscale;
4143
	      ay2 = (double)rh - (double)(lower2 - minHr) * hrscale;
4144
	      ay3 = (double)rh - (double)(lower3 - minHr) * hrscale;
4145
	      ay4 = (double)rh - (double)(upper3 - minHr) * hrscale;
4146
	      ay5 = (double)rh - (double)(avg_hr - minHr) * hrscale;
4147
	   }
4148
	   else
4149
	   {
4150
	      ay1 = (double)rh - (double)(lower1 - minHr) * htiHR;
4151
	      ay2 = (double)rh - (double)(lower2 - minHr) * htiHR;
4152
	      ay3 = (double)rh - (double)(lower3 - minHr) * htiHR;
4153
	      ay4 = (double)rh - (double)(upper3 - minHr) * htiHR;
4154
	      ay5 = (double)rh - (double)(avg_hr - minHr) * htiHR;
4155
	   }
4156
 
4157
	   ptHR.setPen(QPen(barcol2, 1, Qt::DashLine));	// color for limits
4158
 
4159
	   if (lower1 > minHr && lower1 < maxHr)
4160
	      ptHR.drawLine(x1, ay1, x2, ay1);
4161
 
4162
	   if (lower2 > minHr && lower2 < maxHr)
4163
	      ptHR.drawLine(x1, ay2, x2, ay2);
4164
 
4165
	   if (lower3 > minHr && lower3 < maxHr)
4166
	      ptHR.drawLine(x1, ay3, x2, ay3);
4167
 
4168
	   if (upper3 > minHr && upper3 < maxHr)
4169
	      ptHR.drawLine(x1, ay4, x2, ay4);
4170
 
4171
	   ptHR.setPen(QPen(red, 1, Qt::DashDotLine));	// color for average heart rate
4172
 
4173
	   if (avg_hr > minHr && avg_hr < maxHr)
4174
	      ptHR.drawLine(x1, ay5, x2, ay5);
4175
	}
4176
 
4177
	// Now we have a grid and we've done the scaling.
4178
	// This is the point where we draw the curves itself.
4179
	// We use different colors to draw the lines:
4180
	//
4181
	// Green: Elevation
4182
	// Red:   Speed
4183
	// Blue   Heart Rate
4184
	//
4185
	x1 = x2 = y1 = y2 = 0;
4186
	int hy1, hy2, hx1, hx2;
4187
	int sy1, sy2, sx1, sx2;
4188
	hy1 = hy2 = hx1 = hx2 = 0;
4189
	sy1 = sy2 = sx1 = sx2 = 0;
4190
	int hEc = 0;
4191
	i = 0;
4192
	AVGHEIGHT *avgHakt, *avgHfirst, *avgHlast, *avgHeight = 0;
4193
	avgHfirst = avgHlast = 0;
4194
	// To even the surface lines, we store every altitude in the
4195
	// memory, by building a chain. Then, if the user has set in the
4196
	// settings (Contour == true), we will even the line by calculating
4197
	// the average of 10 messure points and setting every value to the
4198
	// average, who is up or down more than 2 meters from the average.
4199
	while ((point = ds.getPoint(i)) != 0)
4200
	{
4201
	   if (point->alt > 20000.0 || point->alt < -1000.0)
4202
	   {
4203
	      i++;
4204
	      continue;
4205
	   }
4206
 
4207
	   if (!avgHeight)
4208
	   {
4209
	      avgHeight = new AVGHEIGHT;
4210
	      avgHeight->alt = point->alt;
4211
	      avgHeight->pos = hEc;
4212
	      avgHeight->prev = 0;
4213
	      avgHeight->next = 0;
4214
	      avgHakt = avgHeight;
4215
	      avgHfirst = avgHeight;
4216
	   }
4217
	   else
4218
	   {
4219
	      avgHakt = new AVGHEIGHT;
4220
	      avgHakt->alt = point->alt;
4221
	      avgHakt->pos = hEc;
4222
	      avgHakt->next = 0;
4223
	      avgHakt->prev = avgHeight;
4224
	      avgHeight->next = avgHakt;
4225
	      avgHeight = avgHakt;
4226
	   }
4227
 
4228
	   // FIXME: Currently we can not draw below 0 meters, because the
4229
	   // base line is always 0!
4230
	   if (avgHakt->alt < minHeight)
4231
	      avgHakt->alt = minHeight;
4232
 
4233
	   hEc++;
4234
	   i++;
4235
	}
4236
 
4237
	avgHlast = avgHeight;
4238
	// If wanted, even the lines
4239
	if (Contour && hEc > 0 && cuType != 0)
4240
	{
4241
	double alt[100], avg, avg1, avg2, avg3, avg4;
4242
	int a, pos;
4243
 
4244
	   for (i = 0; i < (hEc + 100); i += 100)
4245
	   {
4246
	      avg = avg1 = avg2 = avg3 = avg4 = 0.0;
4247
	      pos = 0;
4248
 
4249
	      for (a = 0; a < 100; a++)
4250
	      {
4251
		 alt[a] = getAvgAlt(avgHfirst, i + a);
4252
		 avg += alt[a];
4253
 
4254
		 if (a < 25)
4255
		    avg1 += alt[a];
4256
		 else if (a < 50)
4257
		    avg2 += alt[a];
4258
		 else if (a < 75)
4259
		    avg3 += alt[a];
4260
		 else
4261
		    avg4 += alt[a];
4262
	      }
4263
 
4264
	      if ((i + 100) >= hEc)
4265
		 avg /= (double)(hEc - i) + 1.0;
4266
	      else
4267
		 avg /= 100.0;
4268
 
4269
	      for (a = 0; a < 100; a++)
4270
	      {
4271
		 if ((avgHakt = getAvgPtr(avgHfirst, i + a)) != 0)
4272
		 {
4273
		    if ((avgHakt->alt - avg) > 2 || (avgHakt->alt - avg) < -2)
4274
		       avgHakt->alt = avg;
4275
		 }
4276
	      }
4277
	   }
4278
	}
4279
 
4280
	// plot the elevation, speed and/or heart rate. Depends on <cuType>)
4281
	i = 0;
4282
	int j = 0;
4283
	POINT *oldPoint = 0;
4284
	double speed = 0.0;	// calculated speed
4285
	bool pause = false;	// filter pause out of speed
4286
	unsigned long t1, t2;
4287
	t1 = t2 = 0;
4288
 
4289
	while ((point = ds.getPoint(i)) != 0)
4290
	{
4291
	bool loop = false;
4292
 
4293
	   if (!oldPoint)
4294
	      oldPoint = point;
4295
 
4296
	   // calculate the y position based on the time
4297
	   qt = garmin_dtime(point->time);
4298
	   secs = zeit.secsTo(qt->time());
4299
	   delete qt;
4300
 
4301
	   for (int c = 0; c < 3; c++)
4302
	   {
4303
	      if (c == 0)
4304
	      {
4305
		 x2 = secs * wtiHR + margin_left + 1;
4306
		 hx2 = x2;
4307
		 sx2 = x2;
4308
 
4309
		 if (x1 == 0)
4310
		    x1 = x2;
4311
 
4312
		 if (hx1 == 0)
4313
		    hx1 = hx2;
4314
 
4315
		 if (sx1 == 0)
4316
		    sx1 = sx2;
4317
	      }
4318
	      else if (c == 1)
4319
	      {
4320
		 x2 = secs * wtiElev + margin_left + 1;
4321
		 hx2 = x2;
4322
		 sx2 = x2;
4323
 
4324
		 if (x1 == 0)
4325
		    x1 = x2;
4326
 
4327
		 if (hx1 == 0)
4328
		    hx1 = hx2;
4329
 
4330
		 if (sx1 == 0)
4331
		    sx1 = sx2;
4332
	      }
4333
	      else
4334
	      {
4335
		 x2 = secs * wtiSpeed + margin_left + 1;
4336
		 hx2 = x2;
4337
		 sx2 = x2;
4338
 
4339
		 if (x1 == 0)
4340
		    x1 = x2;
4341
 
4342
		 if (hx1 == 0)
4343
		    hx1 = hx2;
4344
 
4345
		 if (sx1 == 0)
4346
		    sx1 = sx2;
4347
	      }
4348
	      // The speed is not very exact, because smallest time is
4349
	      // one second. This allows a maximum error of 99 hundredths
4350
	      // of a second, what is very close to one second. Because of
4351
	      // this, speed seems to hop for every messure point. This
4352
	      // looks ugly, but currently I don't know how to make it
4353
	      // better.
4354
	      if (c == 2)	// Draw speed?
4355
	      {
4356
	      double dist;
4357
	      double sc;
4358
 
4359
		 if (!pause && point->distance > 1.0e10)
4360
		 {
4361
		    pause = true;
4362
		    t1 = point->time;
4363
		 }
4364
		 else if (pause)
4365
		 {
4366
		    pause = false;
4367
		    t2 = point->time;
4368
		    i += 2;
4369
		    loop = true;
4370
		 }
4371
 
4372
		 if (point->distance >= 0.1 && point->distance < 1.0e10)
4373
		 {
4374
		    dist = point->distance - oldPoint->distance;
4375
		    sc = point->time - oldPoint->time;
4376
		    LAP *runde = ds.getLapT (point->time);
4377
 
4378
		    if (t2 > t1)
4379
		    {
4380
		       sc -= t2 - t1;
4381
 
4382
		       if (sc <= 0.0)
4383
			  sc = 1.0;		// at least 1 second!
4384
		    }
4385
 
4386
		    speed = (dist / sc) * 3.6;
4387
 
4388
		    if (runde && runde->max_speed > 0.0 && speed > (runde->max_speed * 3.6))
4389
		       speed = runde->max_speed * 3.6;
4390
 
4391
		    if (Units == 1)
4392
		       speed /= 1.609344;
4393
 
4394
		    if (speed < minSpeed || speed > 400.0)
4395
		       speed = minSpeed;
4396
 
4397
		    y2 = (double)rh - (speed - minSpeed) * htiSpeed;
4398
 
4399
		    if (y1 == 0)
4400
		       y1 = y2;
4401
 
4402
		    ptSpeed.setPen(QPen(red, 1, Qt::SolidLine));
4403
		    ptSpeed.drawLine(x1, y1, x2, y2);
4404
		    y1 = y2;
4405
		    x1 = x2;
4406
		    t1 = t2 = 0;
4407
		    oldPoint = point;
4408
		 }
4409
	      }
4410
 
4411
	      if (c == 1)		// Draw elevation?
4412
	      {
4413
		 if (point->alt < 20000.0 && point->alt > -1000.0)
4414
		 {
4415
		 double alt = getAvgAlt(avgHfirst, j);
4416
 
4417
		    j++;
4418
 
4419
		    sy2 = (double)rh - (alt - minHeight) * htiElev;
4420
 
4421
		    if (sy1 == 0)
4422
		       sy1 = sy2;
4423
 
4424
		    ptElevation.setPen(QPen(barcol, 1, Qt::SolidLine));
4425
		    ptElevation.drawLine(sx1, sy1, sx2, sy2);
4426
		    sy1 = sy2;
4427
		    sx1 = sx2;
4428
		 }
4429
	      }
4430
 
4431
	      if (point->heart_rate > 0 && c == 0)	// Draw heart rate?
4432
	      {
4433
		 hy2 = (double)rh - (double)(point->heart_rate - minHr) * htiHR;
4434
 
4435
		 if (hy1 == 0)
4436
		    hy1 = hy2;
4437
 
4438
		 ptHR.setPen(QPen(blue, 1, Qt::SolidLine));
4439
		 ptHR.drawLine(hx1, hy1, hx2, hy2);
4440
		 hy1 = hy2;
4441
		 hx1 = hx2;
4442
	      }
4443
	   }
4444
 
4445
	   if (loop)
4446
	   {
4447
	      loop = false;
4448
	      continue;
4449
	   }
4450
 
4451
	   i++;
4452
	}
4453
 
4454
	ptHR.end();
4455
	ptElevation.end();
4456
	ptSpeed.end();
4457
	ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4458
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4459
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4460
 
4461
	// free the chain of altitudes
4462
	avgHakt = avgHfirst;
4463
 
4464
	while (avgHakt)
4465
	{
4466
	   avgHeight = avgHakt->next;
4467
	   delete avgHakt;
4468
	   avgHakt = avgHeight;
4469
	}
4470
 
4471
	DIRTY = false;
4472
}
4473
 
169 andreas 4474
double sportwatcherWidget::getAvgAlt(AVGHEIGHT *avgHeight, int pos)
4475
{
4476
AVGHEIGHT *akt;
4477
 
170 andreas 4478
	if (!avgHeight)
4479
	   return 0.0;
4480
 
169 andreas 4481
	akt = avgHeight;
4482
 
4483
	while (akt)
4484
	{
4485
	   if (akt->pos == pos)
4486
	      return akt->alt;
4487
 
4488
	   akt = akt->next;
4489
	}
4490
 
4491
	return 0.0;
4492
}
4493
 
4494
AVGHEIGHT *sportwatcherWidget::getAvgPtr(AVGHEIGHT *avgHeight, int pos)
4495
{
4496
AVGHEIGHT *akt;
4497
 
4498
	akt = avgHeight;
4499
 
4500
	while (akt)
4501
	{
4502
	   if (akt->pos == pos)
4503
	      return akt;
4504
 
4505
	   akt = akt->next;
4506
	}
4507
 
4508
	return 0;
4509
}
4510
 
246 andreas 4511
void sportwatcherWidget::resizeEvent(QResizeEvent *e)
88 andreas 4512
{
246 andreas 4513
	if (e->size() != e->oldSize())
4514
	   DIRTY=true;
4515
 
132 andreas 4516
	showTrack(zfactor);
88 andreas 4517
	showCurves();
250 andreas 4518
	tabDirt2 = true;
4519
 
4520
	if (curTab == 2)
4521
	{
4522
	   showThreeCurve();
4523
	   tabDirt2 = false;
4524
	}
4525
 
246 andreas 4526
	DIRTY=false;
88 andreas 4527
}
4528
 
132 andreas 4529
void sportwatcherWidget::mouseMoveEvent(QMouseEvent *e)
4530
{
4531
QPoint pos(0, 0);
245 andreas 4532
QPoint ev = ui_sportwatcherWidgetBase.imgMap->mapFrom(this, e->pos());
132 andreas 4533
static QRect coord;
88 andreas 4534
 
246 andreas 4535
	DIRTY=true;
4536
 
250 andreas 4537
	if (curTab == 0)
132 andreas 4538
	{
250 andreas 4539
	   if (!stateHand)
4540
	      return;
4541
 
4542
	   if (ev.x() >= pos.x() &&
4543
	       ev.y() >= pos.y() &&
4544
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.imgMap->geometry().width()) &&
4545
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.imgMap->geometry().height()))
132 andreas 4546
	   {
250 andreas 4547
	      if (lmbPressed == 1)
4548
	      {
4549
		 coord.setCoords(ev.x(), ev.y(), 0, 0);
4550
		 lmbPressed = 0;
4551
	      }
4552
	      else
4553
	      {
4554
		 coord.setRight(ev.x());
4555
		 coord.setBottom(ev.y());
4556
	      }
4557
 
4558
	      if (lmbPressed == 2)
4559
	      {
4560
		 showTrack(zfactor, coord, mapLap);
4561
		 lmbPressed = 0;
4562
	      }
132 andreas 4563
	   }
250 andreas 4564
	}
4565
 
4566
	if (curTab == 2)
4567
	{
4568
	   // look in which of the three QLabels the mouse is, if it is in
4569
	   // one of them.
4570
	   ev = ui_sportwatcherWidgetBase.grHR->mapFrom(this, e->pos());
4571
 
4572
	   if (ev.x() >= pos.x() &&
4573
	       ev.y() >= pos.y() &&
4574
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grHR->geometry().width()) &&
4575
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grHR->geometry().height()))
132 andreas 4576
	   {
250 andreas 4577
	      drawGrHR (ev.x(), ev.y());
4578
	      drawGrElev (ev.x(), -1);
4579
	      drawGrSpeed (ev.x(), -1);
132 andreas 4580
	   }
4581
 
250 andreas 4582
	   ev = ui_sportwatcherWidgetBase.grElevation->mapFrom(this, e->pos());
4583
 
4584
	   if (ev.x() >= pos.x() &&
4585
	       ev.y() >= pos.y() &&
4586
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grElevation->geometry().width()) &&
4587
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grElevation->geometry().height()))
132 andreas 4588
	   {
250 andreas 4589
	      drawGrHR (ev.x(), -1);
4590
	      drawGrElev (ev.x(), ev.y());
4591
	      drawGrSpeed (ev.x(), -1);
132 andreas 4592
	   }
250 andreas 4593
 
4594
	   ev = ui_sportwatcherWidgetBase.grSpeed->mapFrom(this, e->pos());
4595
 
4596
	   if (ev.x() >= pos.x() &&
4597
	       ev.y() >= pos.y() &&
4598
	       ev.x() <= (pos.x() + ui_sportwatcherWidgetBase.grSpeed->geometry().width()) &&
4599
	       ev.y() <= (pos.y() + ui_sportwatcherWidgetBase.grSpeed->geometry().height()))
4600
	   {
4601
	      drawGrHR (ev.x(), -1);
4602
	      drawGrElev (ev.x(), -1);
4603
	      drawGrSpeed (ev.x(), ev.y());
4604
	   }
132 andreas 4605
	}
246 andreas 4606
 
4607
	DIRTY=false;
132 andreas 4608
}
4609
 
4610
void sportwatcherWidget::mousePressEvent(QMouseEvent *e)
4611
{
232 andreas 4612
	if (stateHand && e->button() == Qt::LeftButton)
132 andreas 4613
	   lmbPressed = 1;	// Left Mouse Button is pressed
4614
	else if (stateHand)
4615
	   lmbPressed = 0;	// Wrong button is pressed
4616
 
4617
	if (stateGlas)
4618
	{
232 andreas 4619
	   if (e->button() == Qt::LeftButton)
132 andreas 4620
	      btGlasPlusSlot();
232 andreas 4621
	   else if (e->button() == Qt::RightButton)
132 andreas 4622
	      btGlasMinusSlot();
4623
	}
4624
}
4625
 
4626
void sportwatcherWidget::mouseReleaseEvent(QMouseEvent *e)
4627
{
232 andreas 4628
	if (stateHand && e->button() == Qt::LeftButton)
132 andreas 4629
	{
4630
	   lmbPressed = 2;	// Left Mouse Button was released
4631
	   mouseMoveEvent(e);
4632
	}
4633
}
4634
 
88 andreas 4635
/*
250 andreas 4636
 * Private functions to draw cross and/or a bar to reflect the mouse
4637
 * pointer on tab 2.
4638
 */
4639
void sportwatcherWidget::drawGrHR (int x, int y)
4640
{
4641
int width, height;
4642
QPixmap pm = pmHR.copy(pmHR.rect());
4643
QPainter paint;
4644
 
4645
	width = ui_sportwatcherWidgetBase.grHR->width();
4646
	height = ui_sportwatcherWidgetBase.grHR->height();
4647
	ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4648
 
4649
	if (x > (width - 40) || y > (height - 12) || x < 1)
4650
	{
4651
	   ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4652
	   ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4653
	   return;
4654
	}
4655
 
4656
	if (tabDirt2)
4657
	{
4658
	   DIRTY = true;
4659
	   showThreeCurve ();
4660
	   tabDirt2 = false;
4661
	   DIRTY = false;
4662
	}
4663
 
4664
	paint.begin(&pm);
4665
	QColor black(0, 0, 0);
4666
	black.setAlpha (128);
4667
	paint.setPen(QPen(black, 1, Qt::SolidLine));
4668
	// horizontal line, if y != -1
4669
	if (y >= 0)
4670
	   paint.drawLine(2, y, width - 41, y);
4671
 
4672
	// vertical line
4673
	paint.drawLine(x, 1, x, height - 13);
4674
	paint.end();
4675
	ui_sportwatcherWidgetBase.grHR->setPixmap(pm);
4676
}
4677
 
4678
void sportwatcherWidget::drawGrElev (int x, int y)
4679
{
4680
int width, height;
4681
QPixmap pm = pmElevation.copy(pmElevation.rect());
4682
QPainter paint;
4683
 
4684
	width = ui_sportwatcherWidgetBase.grElevation->width();
4685
	height = ui_sportwatcherWidgetBase.grElevation->height();
4686
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4687
 
4688
	if (x > (width - 40) || y > (height - 12) || x < 1)
4689
	{
4690
	   ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4691
	   ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4692
	   return;
4693
	}
4694
 
4695
	if (tabDirt2)
4696
	{
4697
	   DIRTY = true;
4698
	   showThreeCurve ();
4699
	   tabDirt2 = false;
4700
	   DIRTY = false;
4701
	}
4702
 
4703
	paint.begin(&pm);
4704
	QColor black(0, 0, 0);
4705
	black.setAlpha (128);
4706
	paint.setPen(QPen(black, 1, Qt::SolidLine));
4707
	// horizontal line, if y != -1
4708
	if (y >= 0)
4709
	   paint.drawLine(2, y, width - 41, y);
4710
 
4711
	// vertical line
4712
	paint.drawLine(x, 1, x, height - 13);
4713
	paint.end();
4714
	ui_sportwatcherWidgetBase.grElevation->setPixmap(pm);
4715
}
4716
 
4717
void sportwatcherWidget::drawGrSpeed (int x, int y)
4718
{
4719
int width, height;
4720
QPixmap pm = pmSpeed.copy(pmSpeed.rect());
4721
QPainter paint;
4722
 
4723
	width = ui_sportwatcherWidgetBase.grSpeed->width();
4724
	height = ui_sportwatcherWidgetBase.grSpeed->height();
4725
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pmSpeed);
4726
 
4727
	if (x > (width - 40) || y > (height - 12) || x < 1)
4728
	{
4729
	   ui_sportwatcherWidgetBase.grHR->setPixmap(pmHR);
4730
	   ui_sportwatcherWidgetBase.grElevation->setPixmap(pmElevation);
4731
	   return;
4732
	}
4733
 
4734
	if (tabDirt2)
4735
	{
4736
	   DIRTY = true;
4737
	   showThreeCurve ();
4738
	   tabDirt2 = false;
4739
	   DIRTY = false;
4740
	}
4741
 
4742
	paint.begin(&pm);
4743
	QColor black(0, 0, 0);
4744
	black.setAlpha (128);
4745
	paint.setPen(QPen(black, 1, Qt::SolidLine));
4746
	// horizontal line, if y != -1
4747
	if (y >= 0)
4748
	   paint.drawLine(2, y, width - 41, y);
4749
 
4750
	// vertical line
4751
	paint.drawLine(x, 1, x, height - 13);
4752
	paint.end();
4753
	ui_sportwatcherWidgetBase.grSpeed->setPixmap(pm);
4754
}
4755
 
4756
/*
88 andreas 4757
 * Private functions to help decode the data
4758
 */
4759
QDateTime *sportwatcherWidget::garmin_dtime (uint32 t)
4760
{
4761
time_t     tval;
4762
struct tm  tmval;
4763
QTime ti;
4764
QDate dt;
4765
QDateTime *qt;
4766
 
4767
	if (t == 0)
4768
	   return new QDateTime(QDate(1900, 1, 1), QTime(0, 0, 0, 0));
4769
 
4770
	tval = t + TIME_OFFSET;
4771
	localtime_r (&tval, &tmval);
4772
	qt = new QDateTime();
4773
	qt->setDate(QDate(tmval.tm_year+1900, tmval.tm_mon+1, tmval.tm_mday));
4774
	qt->setTime(QTime(tmval.tm_hour, tmval.tm_min, tmval.tm_sec, 0));
4775
	/* OK.  Done. */
4776
	return qt;
4777
}
4778
 
104 andreas 4779
bool sportwatcherWidget::writeTag(const QFile &fn, const QString &str, int indent)
4780
{
4781
QString qs;
232 andreas 4782
char *p;
4783
//QCString qcs;
104 andreas 4784
int i;
88 andreas 4785
 
171 andreas 4786
	if (indent > 0)
4787
	   qs.fill(' ', indent * 3);
4788
 
104 andreas 4789
	qs.append(str);
232 andreas 4790
//	qcs = qs.utf8();
4791
//	qstrcpy(p, qcs);
4792
	p = strdup (qs.toUtf8().data());
104 andreas 4793
	i = strlen(p);
4794
 
4795
	if (write(fn.handle(), p, i) != i)
4796
	{
232 andreas 4797
	   free (p);
104 andreas 4798
	   return false;
4799
	}
4800
 
232 andreas 4801
	free (p);
104 andreas 4802
	return true;
4803
}
4804
 
232 andreas 4805
#if defined HAVE_GDAL
151 andreas 4806
bool sportwatcherWidget::writeWMSTag(double llat, double llon, double rlat, double rlon, int width, int height)
4807
{
4808
QFile fl(MAP);
152 andreas 4809
QString xml, s, srs, crs, styles, bSize, ext;
4810
QDir dir = QDir::home();
232 andreas 4811
QString path = dir.absolutePath();
157 andreas 4812
int item, isrs;
4813
double _llat, _llon, _rlat, _rlon;
4814
bool offline, square;
151 andreas 4815
 
232 andreas 4816
	if (!fl.open(QIODevice::ReadWrite | QIODevice::Truncate))
151 andreas 4817
	{
4818
	   KMessageBox::error (this, i18n("Error opening or creating the WMS tag file!\nPlease check file name and/or permissions."));
4819
	   return false;
4820
	}
4821
 
232 andreas 4822
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
4823
	KConfigGroup wms (&cfg, "WMS");
4824
	square = wms.readEntry("Square", false);
4825
	styles = wms.readEntry("Styles", QString(""));
152 andreas 4826
 
151 andreas 4827
	xml = "<GDAL_WMS>\n";
4828
	xml += "   <Service name=\"WMS\">\n";
4829
	xml += "      <Version>1.1.1</Version>\n";
232 andreas 4830
	xml += "      <ServerURL>" + wms.readEntry("ServerURL", "http://onearth.jpl.nasa.gov/wms.cgi") + "?</serverURL>\n";
4831
	isrs = wms.readEntry("SRS", 0);
157 andreas 4832
	_llon = llon;
4833
	_llat = llat;
4834
	_rlon = rlon;
4835
	_rlat = rlat;
4836
	offline = false;
152 andreas 4837
 
157 andreas 4838
	switch (isrs)
152 andreas 4839
	{
4840
	   case 0: srs = QString("EPSG:4326"); break;
157 andreas 4841
 
4842
	   case 1:
4843
	      srs = QString("EPSG:31257");
4844
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31257, width, height, square);
4845
	   break;
4846
 
4847
	   case 2:
4848
	      srs = QString("EPSG:31258");
4849
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31258, width, height, square);
4850
	   break;
4851
 
4852
	   case 3:
4853
	      srs = QString("EPSG:31259");
4854
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31259, width, height, square);
4855
	   break;
4856
 
4857
	   case 4:
4858
	      srs = QString("EPSG:31286");
4859
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31286, width, height, square);
4860
	   break;
4861
 
4862
	   case 5:
4863
	      srs = QString("EPSG:31287");
4864
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31287, width, height, square);
4865
	   break;
4866
 
4867
	   case 6:
4868
	      srs = QString("EPSG:31288");
4869
	      offline = transCoords(&_llat, &_llon, &_rlat, &_rlon, 31288, width, height, square);
4870
	   break;
4871
 
152 andreas 4872
	   default: srs = QString("EPSG:4326");
4873
	}
4874
 
4875
	xml += "      <SRS>" + srs + "</SRS>\n";
232 andreas 4876
	item = wms.readEntry("CRS", 0);
152 andreas 4877
 
4878
	switch (item)
4879
	{
4880
	   case 0: crs = QString("CRS:83"); break;
157 andreas 4881
	   case 1: crs = QString("CRS:84"); break;
4882
	   case 2: crs = QString("EPSG:4326"); break;
4883
	   case 3: crs = QString("EPSG:31259"); break;
4884
	   case 4: crs = QString("EPSG:31287"); break;
152 andreas 4885
	   default: crs = QString("CRS:83"); break;
4886
	}
4887
 
4888
	xml += "      <CRS>" + crs + "</CRS>\n";
232 andreas 4889
	item = wms.readEntry("Image", 2);
152 andreas 4890
	xml += "      <ImageFormat>image/";
4891
 
4892
	switch (item)
4893
	{
4894
	   case 0: xml += "gif"; ext = QString(".gif"); break;
4895
	   case 1: xml += "jpeg"; ext = QString(".jpg"); break;
4896
	   case 2: xml += "png"; ext = QString(".png"); break;
4897
	   case 3: xml += "tiff"; ext = QString(".tif"); break;
4898
	   default: xml += "png"; ext = QString(".png");
4899
	}
4900
 
4901
	xml += "</ImageFormat>\n";
4902
 
232 andreas 4903
	xml += "      <Layers>" + wms.readEntry("Layer", QString("")) + "</Layers>\n";
152 andreas 4904
 
4905
	if (!styles.isEmpty())
4906
	   xml += "      <Styles>" + styles + "</Styles>\n";
4907
 
151 andreas 4908
	xml += "      <BBoxOrder>xyXY</BBoxOrder>\n";
4909
	xml += "   </Service>\n";
4910
	xml += "   <DataWindow>\n";
157 andreas 4911
	s.sprintf ("%f", _llat);
151 andreas 4912
	xml += "      <UpperLeftX>" + s + "</UpperLeftX>\n";
157 andreas 4913
	s.sprintf ("%f", _llon);
151 andreas 4914
	xml += "      <UpperLeftY>" + s + "</UpperLeftY>\n";
157 andreas 4915
	s.sprintf ("%f", _rlat);
151 andreas 4916
	xml += "      <LowerRightX>" + s + "</LowerRightX>\n";
157 andreas 4917
	s.sprintf ("%f", _rlon);
151 andreas 4918
	xml += "      <LowerRightY>" + s + "</LowerRightY>\n";
4919
	s.sprintf ("%d", width);
4920
	xml += "      <SizeX>" + s + "</SizeX>\n";
4921
	s.sprintf ("%d", height);
4922
	xml += "      <SizeY>" + s + "</SizeY>\n";
4923
	xml += "   </DataWindow>\n";
157 andreas 4924
 
4925
/*	switch (isrs)
4926
	{
4927
	   case 0: srs = QString("EPSG:4326"); break;
4928
	   case 1: srs = QString("EPSG:31259"); break;
4929
	   case 2: srs = QString("EPSG:31286"); break;
4930
	   case 3: srs = QString("EPSG:31287"); break;
4931
	   case 4: srs = QString("EPSG:31288"); break;
4932
	   default: srs = QString("EPSG:4326");
4933
	}
4934
*/
4935
//	srs = QString("EPSG:4326");
4936
	xml += "   <Projection>" + srs + "</Projection>\n";
232 andreas 4937
	xml += "   <BandsCount>" + wms.readEntry("Bands", QString("3")) + "</BandsCount>\n";
4938
	item = wms.readEntry("Tile", 2);
152 andreas 4939
 
4940
	switch (item)
4941
	{
4942
	   case 0: bSize = QString("64"); break;
4943
	   case 1: bSize = QString("128"); break;
4944
	   case 2: bSize = QString("256"); break;
4945
	   case 3: bSize = QString("512"); break;
4946
	   case 4: bSize = QString("1024"); break;
4947
	   default: bSize = QString("256");
4948
	}
4949
 
4950
	xml += "   <BlockSizeX>" + bSize + "</BlockSizeX>\n";
4951
	xml += "   <BlockSizeY>" + bSize + "</BlockSizeY>\n";
232 andreas 4952
	xml += "   <OverviewCount>" + wms.readEntry("Overview", QString("10")) + "</OverviewCount>\n";
151 andreas 4953
	xml += "   <Cache>\n";
152 andreas 4954
	xml += "      <Path>" + path + "/.gdalwmscache" + "</Path>\n";
232 andreas 4955
	xml += "      <Depth>" + wms.readEntry("Depth", QString("2")) + "</Depth>\n";
152 andreas 4956
	xml += "      <Extension>" + ext + "</Extension>\n";
151 andreas 4957
	xml += "   </Cache>\n";
232 andreas 4958
	QString off((wms.readEntry("Offline", false)) ? "true" : "false");
4959
	QString adv((wms.readEntry("Advice", false)) ? "true" : "false");
4960
	QString ver((wms.readEntry("Verify", true)) ? "true" : "false");
157 andreas 4961
 
4962
	if (offline)
4963
	   xml += "   <OfflineMode>true</OfflineMode>\n";
4964
	else
4965
	   xml += "   <OfflineMode>" + off + "</OfflineMode>\n";
4966
 
152 andreas 4967
	xml += "   <AdviseRead>" + adv + "</AdviseRead>\n";
4968
	xml += "   <VerifyAdviseRead>" + ver + "</VerifyAdviseRead>\n";
151 andreas 4969
	xml += "</GDAL_WMS>\n";
4970
 
232 andreas 4971
	write (fl.handle(), xml.toAscii().data(), strlen (xml.toAscii().data()));
151 andreas 4972
	fl.close();
4973
	return true;
4974
}
4975
 
157 andreas 4976
bool sportwatcherWidget::transCoords (double *x1, double *y1, double *x2, double *y2, int code, int width, int height, bool square)
151 andreas 4977
{
157 andreas 4978
OGRSpatialReference oSourceSRS, oTargetSRS;
4979
OGRCoordinateTransformation *poCT;
4980
 
4981
	oSourceSRS.SetWellKnownGeogCS ("WGS84");
4982
	oTargetSRS.importFromEPSG(code);
4983
	poCT = OGRCreateCoordinateTransformation (&oSourceSRS, &oTargetSRS);
4984
 
4985
	if (poCT == NULL || !poCT->Transform( 1, x1, y1))
4986
	{
4987
	   KMessageBox::error(this, i18n("Translation between coordinate systems failed!"));
4988
 
4989
	   if (poCT != NULL)
4990
	      delete poCT;
4991
 
4992
	   return true;
4993
	}
4994
 
4995
	if (poCT != NULL)
4996
	{
4997
	   poCT->Transform (1, x2, y2);
4998
	   delete poCT;
4999
	}
5000
 
5001
	if (square)
5002
	{
5003
	double wdiff = (double)((long)(*x1 - *x2) / (width / 2 * 2) * (width / 2 * 2));
5004
	double hdiff = (double)((long)(*y2 - *y1) / (height / 2 * 2) * (height / 2 * 2));
5005
 
5006
	   *x2 = *x1 - wdiff; // * (double)mFactor;
5007
	   *y2 = *y1 + hdiff; // * (double)mFactor;
5008
//	   *x2 = *x1 - (double)((long)(wdiff / mFactor * mFactor));
5009
//	   *y2 = *y1 - (double)((long)(hdiff / mFactor * mFactor));
5010
/*	   wdiff = wdiff - (*x1 - *x2);
5011
	   hdiff = hdiff - (*y2 - *y1);
5012
	   *x1 -= wdiff / 2.0;
5013
	   *x2 -= wdiff / 2.0;
5014
	   *y1 += hdiff / 2.0;
5015
	   *y2 += hdiff / 2.0; */
5016
	}
5017
 
5018
	return false;
151 andreas 5019
}
5020
 
158 andreas 5021
QString *sportwatcherWidget::getProjection (int isrs, QString *srs)
5022
{
5023
	switch (isrs)
5024
	{
5025
	   case 0: *srs = QString("EPSG:4326"); break;
5026
	   case 1: *srs = QString("EPSG:31257"); break;
5027
	   case 2: *srs = QString("EPSG:31258"); break;
5028
	   case 3: *srs = QString("EPSG:31259"); break;
5029
	   case 4: *srs = QString("EPSG:31286"); break;
5030
	   case 5: *srs = QString("EPSG:31287"); break;
5031
	   case 6: *srs = QString("EPSG:31288"); break;
5032
	   default: *srs = QString("EPSG:4326");
5033
	}
5034
 
5035
	return srs;
5036
}
5037
 
5038
bool sportwatcherWidget::warpImage(QString fn, QString *fName)
5039
{
5040
GDALDatasetH  hSrcDS, hDstDS;
5041
GDALDataset *inSet, *outSet;
5042
GDALDataType eDT;
5043
GDALRasterBand *poBand;
5044
GDALDriverH hDriver;
5045
char hv0[256];
5046
int nXSize, nYSize;
5047
double adfGeoTransform[6];
5048
double oriLeftLon, oriRightLon, oriLeftLat, oriRightLat;
5049
 
159 andreas 5050
 
5051
	// Loading the user set geo coords of our source image and
5052
	// load the projection used for that image
232 andreas 5053
	KConfig cfg (QString("sportwatcher.rc"), KConfig::SimpleConfig);
5054
	KConfigGroup ic (&cfg, "ImageCoords");
5055
	oriLeftLon = ic.readEntry("LeftLon", 0.0);
5056
	oriLeftLat = ic.readEntry("LeftLat", 0.0);
5057
	oriRightLon = ic.readEntry("RightLon", 0.0);
5058
	oriRightLat = ic.readEntry("RightLat", 0.0);
5059
	int isrs = ic.readEntry("SRS", 0);
159 andreas 5060
 
158 andreas 5061
	// Create a temporary file name for our output file
5062
	strcpy (hv0, "/tmp/SportWatcherTIFFXXXXXX");
216 andreas 5063
	mkdtemp (&hv0[0]);
158 andreas 5064
	*fName = QString(hv0) + ".tif";
5065
 
5066
	// Open input and output files.
232 andreas 5067
	if ((hSrcDS = GDALOpen(fn.toAscii().data(), GA_ReadOnly)) == NULL)
158 andreas 5068
	{
5069
	   KMessageBox::error(this, i18n("Error opening an image file!"));
5070
	   return false;
5071
	}
5072
 
5073
	inSet = (GDALDataset *)hSrcDS;
5074
	// Create output with same datatype as first input band.
5075
	poBand = inSet->GetRasterBand (1);
5076
	eDT = poBand->GetRasterDataType ();
5077
 
5078
	if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
5079
	{
5080
	   KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
5081
	   GDALClose (hSrcDS);
5082
	   return false;
5083
	}
5084
 
5085
	// Get dimensions of image and set transform data
5086
	int nRasterCount = inSet->GetRasterCount();
5087
	nXSize = inSet->GetRasterXSize();
5088
	nYSize = inSet->GetRasterYSize();
5089
 
159 andreas 5090
	// cut the wanted region out of image
5091
	transform *tf = new transform (oriLeftLat, oriLeftLon, oriRightLat, oriRightLon);
5092
	tf->setDimensions(nXSize, nYSize);
158 andreas 5093
 
159 andreas 5094
	if (!tf->cutImage (geoRect.llat, geoRect.llon, geoRect.rlat, geoRect.rlon, fn))
5095
	{
5096
	   GDALClose (hSrcDS);
5097
	   return false;
5098
	}
158 andreas 5099
 
159 andreas 5100
	GDALClose (hSrcDS);
5101
	QString nfn = fn + "_tmp.png";
158 andreas 5102
 
159 andreas 5103
	// repeat the part above and open the now cutted part of the image.
5104
	// Open input and output files.
232 andreas 5105
	if ((hSrcDS = GDALOpen(nfn.toAscii().data(), GA_ReadOnly)) == NULL)
159 andreas 5106
	{
5107
	   KMessageBox::error(this, i18n("Error opening an image file!"));
5108
	   return false;
5109
	}
158 andreas 5110
 
159 andreas 5111
	inSet = (GDALDataset *)hSrcDS;
5112
	// Create output with same datatype as first input band.
5113
	poBand = inSet->GetRasterBand (1);
5114
	eDT = poBand->GetRasterDataType ();
158 andreas 5115
 
159 andreas 5116
	if ((hDriver = GDALGetDriverByName ("GTiff")) == NULL)
158 andreas 5117
	{
159 andreas 5118
	   KMessageBox::error(this, i18n("Error loading the TIFF driver!"));
5119
	   GDALClose (hSrcDS);
5120
	   return false;
158 andreas 5121
	}
5122
 
159 andreas 5123
	// Get dimensions of image and set transform data
5124
	nRasterCount = inSet->GetRasterCount();
5125
	nXSize = inSet->GetRasterXSize();
5126
	nYSize = inSet->GetRasterYSize();
5127
 
158 andreas 5128
	// Set the values needed to transform the image
5129
	OGRSpatialReference iSRS;
5130
	const char *iWKT;
5131
 
5132
	switch (isrs)
5133
	{
5134
	   case 0: iSRS.importFromEPSG(4326); break;
5135
	   case 1: iSRS.importFromEPSG(31257); break;
5136
	   case 2: iSRS.importFromEPSG(31258); break;
5137
	   case 3: iSRS.importFromEPSG(31259); break;
5138
	   case 4: iSRS.importFromEPSG(31286); break;
5139
	   case 5: iSRS.importFromEPSG(31287); break;
5140
	   case 6: iSRS.importFromEPSG(31288); break;
5141
	   default: iSRS.importFromEPSG(4326);
5142
	}
5143
 
5144
	iSRS.exportToWkt ((char **)&iWKT);
5145
 
5146
	if (inSet->SetProjection (iWKT) != CE_None)
5147
	{
5148
	   KMessageBox::error(this, i18n("Error setting projection on source!"));
5149
	   GDALClose (hSrcDS);
5150
	   return false;
5151
	}
5152
 
159 andreas 5153
	adfGeoTransform[0] = geoRect.llon;
5154
	adfGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)nXSize;
158 andreas 5155
	adfGeoTransform[2] = 0.0;
159 andreas 5156
	adfGeoTransform[3] = geoRect.llat;
158 andreas 5157
	adfGeoTransform[4] = 0.0;
159 andreas 5158
	adfGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)nYSize);
158 andreas 5159
 
5160
	if (inSet->SetGeoTransform (&adfGeoTransform[0]) != CE_None)
5161
	{
5162
	   KMessageBox::error(this, i18n("Error setting geo transform data to source!"));
5163
	   GDALClose (hSrcDS);
5164
	   return false;
5165
	}
5166
 
159 andreas 5167
	// Get Source coordinate system.
158 andreas 5168
	const char *pszSrcWKT, *pszDstWKT = NULL;
5169
 
5170
	if ((pszSrcWKT = GDALGetProjectionRef (hSrcDS)) == NULL)
5171
	{
5172
	   KMessageBox::error(this, i18n("Error getting the projection reference"));
5173
	   GDALClose (hSrcDS);
5174
	   return false;
5175
	}
5176
 
5177
	// Setup output coordinate system that is UTM ? WGS84.
5178
	OGRSpatialReference oSRS;
5179
 
159 andreas 5180
//	oSRS.SetUTM( 0, TRUE );
158 andreas 5181
	oSRS.SetWellKnownGeogCS("WGS84");
5182
	oSRS.exportToWkt ((char **)&pszDstWKT);
5183
 
159 andreas 5184
	// Create the output file.
158 andreas 5185
	double adfDstGeoTransform[6];
159 andreas 5186
	adfDstGeoTransform[0] = geoRect.llon;
5187
	adfDstGeoTransform[1] = (geoRect.rlon - geoRect.llon) / (double)geoRect.width;
158 andreas 5188
	adfDstGeoTransform[2] = 0.0;
5189
	adfDstGeoTransform[3] = geoRect.llat;
5190
	adfDstGeoTransform[4] = 0.0;
159 andreas 5191
	adfDstGeoTransform[5] = -1.0 * ((geoRect.llat - geoRect.rlat) / (double)geoRect.height);
158 andreas 5192
 
232 andreas 5193
	if ((hDstDS = GDALCreate(hDriver, fName->toAscii().data(), geoRect.width, geoRect.height,
158 andreas 5194
			nRasterCount, eDT, NULL )) == NULL)
5195
	{
232 andreas 5196
	   KMessageBox::error(this, i18n("Error creating a temporary image file! (%1)").arg(*fName));
158 andreas 5197
	   GDALClose (hSrcDS);
5198
	   return false;
5199
	}
5200
 
5201
	outSet = (GDALDataset *)hDstDS;
5202
 
159 andreas 5203
	for (int i = 0; i < nRasterCount; i++)
5204
	{
5205
	   poBand = outSet->GetRasterBand (i+1);
5206
	   poBand->Fill (0.0);
5207
	}
5208
 
158 andreas 5209
	if (outSet->SetProjection (pszDstWKT) != CE_None)
5210
	{
5211
	   KMessageBox::error(this, i18n("Error setting projection on destination!"));
5212
	   GDALClose (hDstDS);
5213
	   GDALClose (hSrcDS);
232 andreas 5214
	   unlink (fName->toAscii().data());
158 andreas 5215
	   return false;
5216
	}
5217
 
5218
	if (outSet->SetGeoTransform (&adfDstGeoTransform[0]) != CE_None)
5219
	{
159 andreas 5220
	   KMessageBox::error(this, i18n("Error setting geo transform data to destination!"));
158 andreas 5221
	   GDALClose (hDstDS);
5222
	   GDALClose (hSrcDS);
232 andreas 5223
	   unlink (fName->toAscii().data());
158 andreas 5224
	   return false;
5225
	}
159 andreas 5226
 
158 andreas 5227
	// Copy the color table, if required.
5228
	GDALColorTableH hCT;
5229
 
5230
	for (int i = 0; i < nRasterCount; i++)
5231
	{
5232
	   hCT = GDALGetRasterColorTable (inSet->GetRasterBand (i+1));
5233
 
5234
	   if (hCT != NULL)
5235
              GDALSetRasterColorTable (outSet->GetRasterBand (i+1), hCT);
5236
	}
5237
 
5238
	// Setup warp options.
5239
	GDALWarpOptions *psWarpOptions = GDALCreateWarpOptions();
5240
 
5241
	psWarpOptions->hSrcDS = hSrcDS;
5242
	psWarpOptions->hDstDS = hDstDS;
5243
 
5244
	psWarpOptions->nBandCount = nRasterCount;
5245
	psWarpOptions->panSrcBands =
5246
        	(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );
5247
	psWarpOptions->panDstBands =
5248
		(int *) CPLMalloc(sizeof(int) * psWarpOptions->nBandCount );
5249
 
5250
	for (int i = 0; i < nRasterCount; i++)
5251
	{
5252
	   psWarpOptions->panSrcBands[i] = i+1;
5253
	   psWarpOptions->panDstBands[i] = i+1;
5254
	}
5255
 
159 andreas 5256
//	psWarpOptions->pfnProgress = GDALTermProgress;
158 andreas 5257
 
5258
	// Establish reprojection transformer.
5259
	psWarpOptions->pTransformerArg =
5260
        	GDALCreateGenImgProjTransformer(hSrcDS,
5261
					 GDALGetProjectionRef(hSrcDS), 
5262
					 hDstDS,
5263
					 GDALGetProjectionRef(hDstDS),
159 andreas 5264
					 FALSE, 0.0, 1);
158 andreas 5265
 
5266
	psWarpOptions->pfnTransformer = GDALGenImgProjTransform;
5267
 
5268
	// Initialize and execute the warp operation.
5269
	GDALWarpOperation oOperation;
5270
 
5271
	if (oOperation.Initialize (psWarpOptions) != CE_None)
5272
	{
5273
	   KMessageBox::error(this, i18n("Error initializing warp operation!"));
5274
	   GDALClose (hDstDS);
5275
	   GDALClose (hSrcDS);
232 andreas 5276
	   unlink (fName->toAscii().data());
158 andreas 5277
	   return false;
5278
	}
5279
 
159 andreas 5280
	oOperation.ChunkAndWarpMulti (0, 0, geoRect.width, geoRect.height);
158 andreas 5281
	GDALDestroyGenImgProjTransformer (psWarpOptions->pTransformerArg);
5282
	GDALDestroyWarpOptions(psWarpOptions);
5283
 
5284
	GDALClose (hDstDS);
5285
	GDALClose (hSrcDS);
232 andreas 5286
	unlink (nfn.toAscii().data());
158 andreas 5287
	return true;
5288
}
5289
 
151 andreas 5290
#endif
5291
 
88 andreas 5292
#include "sportwatcherwidget.moc"
5293