~neon/kolf/master

1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
1
/*
2
    Copyright (C) 2002-2005, Jason Katz-Brown <jasonkb@mit.edu>
3
    Copyright 2010 Stefan Majewsky <majewsky@gmx.net>
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; either version 2 of the License, or
8
    (at your option) any later version.
9
10
    This program is distributed in the hope that it will be useful,
11
    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
    GNU General Public License for more details.
14
15
    You should have received a copy of the GNU General Public License
16
    along with this program; if not, write to the Free Software
17
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18
*/
19
20
#include "landscape.h"
21
#include "ball.h"
22
#include "game.h"
23
24
#include <QBoxLayout>
25
#include <QCheckBox>
26
#include <QLabel>
27
#include <QSlider>
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
28
#include <KComboBox>
1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
29
#include <KConfigGroup>
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
30
#include <KGlobal>
1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
31
#include <KLocale>
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
32
#include <KNumInput>
1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
33
34
//BEGIN Kolf::LandscapeItem
35
//END Kolf::LandscapeItem
36
37
Kolf::LandscapeItem::LandscapeItem(const QString& type, QGraphicsItem* parent, b2World* world)
38
	: EllipticalCanvasItem(false, type, parent, world)
39
	, m_blinkEnabled(false)
40
	, m_blinkInterval(50)
41
	, m_blinkFrame(0)
42
{
43
	setSimulationType(CanvasItem::NoSimulation);
44
}
45
46
bool Kolf::LandscapeItem::isBlinkEnabled() const
47
{
48
	return m_blinkEnabled;
49
}
50
51
void Kolf::LandscapeItem::setBlinkEnabled(bool blinkEnabled)
52
{
53
	m_blinkEnabled = blinkEnabled;
54
	//reset animation
55
	m_blinkFrame = 0;
56
	setVisible(true);
57
}
58
59
int Kolf::LandscapeItem::blinkInterval() const
60
{
61
	return m_blinkInterval;
62
}
63
64
void Kolf::LandscapeItem::setBlinkInterval(int blinkInterval)
65
{
66
	m_blinkInterval = blinkInterval;
67
	//reset animation
68
	m_blinkFrame = 0;
69
	setVisible(true);
70
}
71
72
void Kolf::LandscapeItem::advance(int phase)
73
{
74
	EllipticalCanvasItem::advance(phase);
75
	if (phase == 1 && m_blinkEnabled)
76
	{
77
		const int actualInterval = 1.8 * (10 + m_blinkInterval);
78
		m_blinkFrame = (m_blinkFrame + 1) % (2 * actualInterval);
79
		setVisible(m_blinkFrame < actualInterval);
80
	}
81
}
82
83
void Kolf::LandscapeItem::load(KConfigGroup* group)
84
{
85
	EllipticalCanvasItem::loadSize(group);
86
	setBlinkEnabled(group->readEntry("changeEnabled", m_blinkEnabled));
87
	setBlinkInterval(group->readEntry("changeEvery", m_blinkInterval));
88
}
89
90
void Kolf::LandscapeItem::save(KConfigGroup* group)
91
{
92
	EllipticalCanvasItem::saveSize(group);
93
	group->writeEntry("changeEnabled", m_blinkEnabled);
94
	group->writeEntry("changeEvery", m_blinkInterval);
95
}
96
97
Config* Kolf::LandscapeItem::config(QWidget* parent)
98
{
99
	return new Kolf::LandscapeConfig(this, parent);
100
}
101
102
Kolf::Overlay* Kolf::LandscapeItem::createOverlay()
103
{
104
	return new Kolf::LandscapeOverlay(this);
105
}
106
107
//BEGIN Kolf::LandscapeOverlay
108
109
Kolf::LandscapeOverlay::LandscapeOverlay(Kolf::LandscapeItem* item)
110
	: Kolf::Overlay(item, item)
111
{
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
112
	//TODO: code duplication to Kolf::RectangleOverlay and Kolf::SlopeOverlay
1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
113
	for (int i = 0; i < 4; ++i)
114
	{
115
		Kolf::OverlayHandle* handle = new Kolf::OverlayHandle(Kolf::OverlayHandle::CircleShape, this);
116
		m_handles << handle;
117
		addHandle(handle);
118
		connect(handle, SIGNAL(moveRequest(QPointF)), this, SLOT(moveHandle(QPointF)));
119
	}
120
}
121
122
void Kolf::LandscapeOverlay::update()
123
{
124
	Kolf::Overlay::update();
125
	const QRectF rect = qitem()->boundingRect();
126
	m_handles[0]->setPos(rect.topLeft());
127
	m_handles[1]->setPos(rect.topRight());
128
	m_handles[2]->setPos(rect.bottomLeft());
129
	m_handles[3]->setPos(rect.bottomRight());
130
}
131
132
void Kolf::LandscapeOverlay::moveHandle(const QPointF& handleScenePos)
133
{
134
	const QPointF handlePos = mapFromScene(handleScenePos);
135
	//factor 2: item bounding rect is always centered around (0,0)
136
	QSizeF newSize(2 * qAbs(handlePos.x()), 2 * qAbs(handlePos.y()));
137
	dynamic_cast<Kolf::LandscapeItem*>(qitem())->setSize(newSize);
138
}
139
140
//END Kolf::LandscapeOverlay
141
//BEGIN Kolf::LandscapeConfig
142
143
Kolf::LandscapeConfig::LandscapeConfig(Kolf::LandscapeItem* item, QWidget* parent)
144
	: Config(parent)
145
{
146
	QVBoxLayout* vlayout = new QVBoxLayout(this);
147
	QCheckBox* checkBox = new QCheckBox(i18n("Enable show/hide"), this);
148
	vlayout->addWidget(checkBox);
149
150
	QHBoxLayout* hlayout = new QHBoxLayout;
151
	vlayout->addLayout(hlayout);
152
	QLabel* label1 = new QLabel(i18n("Slow"), this);
153
	hlayout->addWidget(label1);
154
	QSlider* slider = new QSlider(Qt::Horizontal, this);
155
	hlayout->addWidget(slider);
156
	QLabel* label2 = new QLabel(i18n("Fast"), this);
157
	hlayout->addWidget(label2);
158
159
	vlayout->addStretch();
160
161
	checkBox->setChecked(true);
162
	connect(checkBox, SIGNAL(toggled(bool)), label1, SLOT(setEnabled(bool)));
163
	connect(checkBox, SIGNAL(toggled(bool)), label2, SLOT(setEnabled(bool)));
164
	connect(checkBox, SIGNAL(toggled(bool)), slider, SLOT(setEnabled(bool)));
165
	connect(checkBox, SIGNAL(toggled(bool)), item, SLOT(setBlinkEnabled(bool)));
166
	checkBox->setChecked(item->isBlinkEnabled());
167
	slider->setRange(1, 100);
168
	slider->setPageStep(5);
169
	slider->setValue(100 - item->blinkInterval());
170
	connect(slider, SIGNAL(valueChanged(int)), SLOT(setBlinkInterval(int)));
171
	connect(this, SIGNAL(blinkIntervalChanged(int)), item, SLOT(setBlinkInterval(int)));
172
}
173
174
void Kolf::LandscapeConfig::setBlinkInterval(int sliderValue)
175
{
176
	emit blinkIntervalChanged(100 - sliderValue);
177
}
178
179
//END Kolf::LandscapeConfig
180
//BEGIN Kolf::Puddle
181
182
Kolf::Puddle::Puddle(QGraphicsItem* parent, b2World* world)
183
	: Kolf::LandscapeItem(QLatin1String("puddle"), parent, world)
184
{
185
	setData(0, Rtti_DontPlaceOn);
186
	setSize(QSizeF(45, 30));
1068 by Stefan Majewsky
Refactor Z ordering.
187
	setZBehavior(CanvasItem::FixedZValue, 3);
1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
188
}
189
190
bool Kolf::Puddle::collision(Ball* ball)
191
{
192
	if (!ball->isVisible())
193
		return false;
194
	if (!contains(ball->pos() - pos()))
195
		return true;
196
	//ball is visible and has reached the puddle
197
	playSound("puddle");
198
	ball->setAddStroke(ball->addStroke() + 1);
199
	ball->setPlaceOnGround(true);
200
	ball->setVisible(false);
201
	ball->setState(Stopped);
202
	ball->setVelocity(Vector());
203
	if (game && game->curBall() == ball)
204
		game->stoppedBall();
205
	return false;
206
}
207
208
//END Kolf::Puddle
209
//BEGIN Kolf::Sand
210
211
Kolf::Sand::Sand(QGraphicsItem* parent, b2World* world)
212
	: Kolf::LandscapeItem(QLatin1String("sand"), parent, world)
213
{
214
	setSize(QSizeF(45, 40));
1068 by Stefan Majewsky
Refactor Z ordering.
215
	setZBehavior(CanvasItem::FixedZValue, 2);
1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
216
}
217
218
bool Kolf::Sand::collision(Ball* ball)
219
{
220
	if (contains(ball->pos() - pos()))
221
		ball->setFrictionMultiplier(7);
222
	return true;
223
}
224
225
//END Kolf::Sand
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
226
//BEGIN Kolf::Slope
227
228
struct SlopeData
229
{
230
	QStringList gradientKeys, translatedGradientKeys;
231
	QStringList spriteKeys, reversedSpriteKeys;
232
	SlopeData()
233
	{
234
		gradientKeys << QLatin1String("Vertical")
235
		             << QLatin1String("Horizontal")
236
		             << QLatin1String("Diagonal")
237
		             << QLatin1String("Opposite Diagonal")
238
		             << QLatin1String("Elliptic");
239
		translatedGradientKeys << i18n("Vertical")
240
		             << i18n("Horizontal")
241
		             << i18n("Diagonal")
242
		             << i18n("Opposite Diagonal")
243
		             << i18n("Elliptic");
244
		spriteKeys   << QLatin1String("slope_n")
245
			         << QLatin1String("slope_w")
246
			         << QLatin1String("slope_nw")
247
			         << QLatin1String("slope_ne")
248
			         << QLatin1String("slope_bump");
249
		reversedSpriteKeys << QLatin1String("slope_s")
250
			         << QLatin1String("slope_e")
251
			         << QLatin1String("slope_se")
252
			         << QLatin1String("slope_sw")
253
			         << QLatin1String("slope_dip");
254
	}
255
};
256
K_GLOBAL_STATIC(SlopeData, g_slopeData)
257
258
Kolf::Slope::Slope(QGraphicsItem* parent, b2World* world)
259
	: Tagaro::SpriteObjectItem(Kolf::renderer(), QString(), parent)
260
	, CanvasItem(world)
261
	, m_grade(4)
262
	, m_reversed(false)
263
	, m_stuckOnGround(false)
264
	, m_type(Kolf::VerticalSlope)
265
	, m_gradeItem(new QGraphicsSimpleTextItem(this))
266
{
267
	m_gradeItem->setBrush(Qt::white);
268
	m_gradeItem->setVisible(false);
269
	m_gradeItem->setZValue(1);
270
	for (int i = 0; i < 4; ++i)
271
	{
272
		ArrowItem* arrow = new ArrowItem(this);
273
		arrow->setLength(0);
274
		arrow->setVisible(false);
275
		m_arrows << arrow;
276
	}
277
	setSize(QSizeF(40, 40));
1068 by Stefan Majewsky
Refactor Z ordering.
278
	m_stuckOnGround = true; //so that the following call does not return early
279
	setStuckOnGround(false); //initializes Z behavior
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
280
	updateAppearance();
281
}
282
283
double Kolf::Slope::grade() const
284
{
285
	return m_grade;
286
}
287
288
void Kolf::Slope::setGrade(double grade)
289
{
290
	if (m_grade != grade && grade > 0)
291
	{
292
		m_grade = grade;
293
		updateAppearance();
294
		propagateUpdate();
295
	}
296
}
297
298
bool Kolf::Slope::isReversed() const
299
{
300
	return m_reversed;
301
}
302
303
void Kolf::Slope::setReversed(bool reversed)
304
{
305
	if (m_reversed != reversed)
306
	{
307
		m_reversed = reversed;
308
		updateAppearance();
309
		propagateUpdate();
310
	}
311
}
312
313
Kolf::SlopeType Kolf::Slope::slopeType() const
314
{
315
	return m_type;
316
}
317
318
void Kolf::Slope::setSlopeType(int type)
319
{
320
	if (m_type != type && type >= 0)
321
	{
322
		m_type = (Kolf::SlopeType) type;
323
		updateAppearance();
324
		propagateUpdate();
325
	}
326
}
327
328
bool Kolf::Slope::isStuckOnGround() const
329
{
330
	return m_stuckOnGround;
331
}
332
333
void Kolf::Slope::setStuckOnGround(bool stuckOnGround)
334
{
335
	if (m_stuckOnGround != stuckOnGround)
336
	{
337
		m_stuckOnGround = stuckOnGround;
1068 by Stefan Majewsky
Refactor Z ordering.
338
		setZBehavior(m_stuckOnGround ? CanvasItem::FixedZValue : CanvasItem::IsRaisedByStrut, 1);
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
339
		propagateUpdate();
340
	}
341
}
342
343
QPainterPath Kolf::Slope::shape() const
344
{
345
	const QRectF rect = boundingRect();
346
	QPainterPath path;
347
	if (m_type == Kolf::CrossDiagonalSlope) {
348
		QPolygonF polygon(3);
349
		polygon[0] = rect.topLeft();
350
		polygon[1] = rect.bottomRight();
351
		polygon[2] = m_reversed ? rect.topRight() : rect.bottomLeft();
352
		path.addPolygon(polygon);
353
	} else if (m_type == Kolf::DiagonalSlope) {
354
		QPolygonF polygon(3);
355
		polygon[0] = rect.topRight();
356
		polygon[1] = rect.bottomLeft();
357
		polygon[2] = m_reversed ? rect.topLeft() : rect.bottomRight();
358
		path.addPolygon(polygon);
359
	} else if (m_type == Kolf::EllipticSlope) {
360
		path.addEllipse(rect);
361
	} else {
362
		path.addRect(rect);
363
	}
364
	return path;
365
}
366
367
void Kolf::Slope::setSize(const QSizeF& size)
368
{
369
	if (m_type == Kolf::EllipticSlope)
370
	{
371
		const double extent = qMin(size.width(), size.height());
372
		Tagaro::SpriteObjectItem::setSize(extent, extent);
373
	}
374
	else
375
		Tagaro::SpriteObjectItem::setSize(size);
376
	updateInfo();
377
	propagateUpdate();
1068 by Stefan Majewsky
Refactor Z ordering.
378
	updateZ(this);
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
379
}
380
381
QPointF Kolf::Slope::getPosition() const
382
{
383
	return Tagaro::SpriteObjectItem::pos();
384
}
385
386
void Kolf::Slope::moveBy(double dx, double dy)
387
{
388
	Tagaro::SpriteObjectItem::moveBy(dx, dy);
1068 by Stefan Majewsky
Refactor Z ordering.
389
	CanvasItem::moveBy(dx, dy);
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
390
}
391
392
void Kolf::Slope::load(KConfigGroup* group)
393
{
394
	setGrade(group->readEntry("grade", m_grade));
395
	setReversed(group->readEntry("reversed", m_reversed));
396
	setStuckOnGround(group->readEntry("stuckOnGround", m_stuckOnGround));
397
	//gradient is a bit more complicated
398
	const QString type = group->readEntry("gradient", g_slopeData->gradientKeys.value(m_type));
399
	setSlopeType(g_slopeData->gradientKeys.indexOf(type));
400
	//read size
401
	QSizeF size = Tagaro::SpriteObjectItem::size();
402
	size.setWidth(group->readEntry("width", size.width()));
403
	size.setHeight(group->readEntry("height", size.height()));
404
	setSize(size);
405
}
406
407
void Kolf::Slope::save(KConfigGroup* group)
408
{
409
	group->writeEntry("grade", m_grade);
410
	group->writeEntry("reversed", m_reversed);
411
	group->writeEntry("stuckOnGround", m_stuckOnGround);
412
	group->writeEntry("gradient", g_slopeData->gradientKeys.value(m_type));
413
	const QSizeF size = Tagaro::SpriteObjectItem::size();
414
	group->writeEntry("width", size.width());
415
	group->writeEntry("height", size.height());
416
}
417
418
void Kolf::Slope::updateAppearance()
419
{
420
	updateInfo();
421
	//set pixmap
422
	setSpriteKey((m_reversed ? g_slopeData->reversedSpriteKeys : g_slopeData->spriteKeys).value(m_type));
423
}
424
425
void Kolf::Slope::updateInfo()
426
{
427
	m_gradeItem->setText(QString::number(m_grade));
428
	const QPointF textOffset = m_gradeItem->boundingRect().center();
429
	//update arrows
430
	const QSizeF size = Tagaro::SpriteObjectItem::size();
431
	const double width = size.width(), height = size.height();
432
	const double length = sqrt(width * width + height * height) / 4;
433
	if (m_type == Kolf::EllipticSlope)
434
	{
435
		double angle = 0;
436
		for (int i = 0; i < 4; ++i, angle += M_PI / 2)
437
		{
438
			ArrowItem* arrow = m_arrows[i];
439
			arrow->setLength(length);
440
			arrow->setAngle(angle);
441
			arrow->setReversed(m_reversed);
442
			arrow->setPos(QPointF(width / 2, height / 2));
443
		}
444
		m_gradeItem->setPos(QPointF(width / 2, height / 2) - textOffset);
445
	}
446
	else
447
	{
448
		double angle = 0;
449
		double x = .5 * width, y = .5 * height;
450
		switch ((int) m_type)
451
		{
452
			case Kolf::HorizontalSlope:
453
				angle = 0;
454
				break;
455
			case Kolf::VerticalSlope:
456
				angle = M_PI / 2;
457
				break;
458
			case Kolf::DiagonalSlope:
459
				angle = atan(width / height);
460
				x = m_reversed ? .25 * width : .75 * width;
461
				y = m_reversed ? .25 * height : .75 * height;
462
				break;
463
			case Kolf::CrossDiagonalSlope:
464
				angle = M_PI - atan(width / height);
465
				x = m_reversed ? .75 * width : .25 * width;
466
				y = m_reversed ? .25 * height : .75 * height;
467
				break;
468
		}
469
		//only one arrow needed - hide all others
470
		for (int i = 1; i < 4; ++i)
471
			m_arrows[i]->setLength(0);
472
		ArrowItem* arrow = m_arrows[0];
473
		arrow->setLength(length);
474
		arrow->setAngle(m_reversed ? angle : angle + M_PI);
475
		arrow->setPos(QPointF(x, y));
476
		m_gradeItem->setPos(QPointF(x, y) - textOffset);
477
	}
478
}
479
480
bool Kolf::Slope::collision(Ball* ball)
481
{
482
	Vector v = ball->velocity();
483
	double addto = 0.013 * m_grade;
484
485
	const bool diag = m_type == Kolf::DiagonalSlope || m_type == Kolf::CrossDiagonalSlope;
486
	const bool circle = m_type == Kolf::EllipticSlope;
487
488
	double slopeAngle = 0;
489
	const double width = size().width(), height = size().height();
490
1068 by Stefan Majewsky
Refactor Z ordering.
491
	if (diag)
1064 by Stefan Majewsky
Refactor Slope to Kolf::Slope, and remove old RectPoint class.
492
		slopeAngle = atan(width / height);
493
	else if (circle)
494
	{
495
		const QPointF start = pos() + QPointF(width, height) / 2.0;
496
		const QPointF end = ball->pos();
497
498
		Vector betweenVector = start - end;
499
		const double factor = betweenVector.magnitude() / (width / 2.0);
500
		slopeAngle = betweenVector.direction();
501
502
		// this little bit by Daniel
503
		addto *= factor * M_PI / 2;
504
		addto = sin(addto);
505
	}
506
507
	if (!m_reversed)
508
		addto = -addto;
509
	switch ((int) m_type)
510
	{
511
		case Kolf::HorizontalSlope:
512
			v.rx() += addto;
513
			break;
514
		case Kolf::VerticalSlope:
515
			v.ry() += addto;
516
			break;
517
		case Kolf::DiagonalSlope:
518
		case Kolf::EllipticSlope:
519
			v.rx() += cos(slopeAngle) * addto;
520
			v.ry() += sin(slopeAngle) * addto;
521
			break;
522
		case Kolf::CrossDiagonalSlope:
523
			v.rx() -= cos(slopeAngle) * addto;
524
			v.ry() += sin(slopeAngle) * addto;
525
			break;
526
	}
527
	ball->setVelocity(v);
528
	ball->setState(v.isNull() ? Stopped : Rolling);
529
	// do NOT do terrain collidingItems
530
	return false;
531
}
532
533
bool Kolf::Slope::terrainCollisions() const
534
{
535
	return true;
536
}
537
538
QList<QGraphicsItem*> Kolf::Slope::infoItems() const
539
{
540
	QList<QGraphicsItem*> result;
541
	foreach (ArrowItem* arrow, m_arrows)
542
		result << arrow;
543
	result << m_gradeItem;
544
	return result;
545
}
546
547
Config* Kolf::Slope::config(QWidget* parent)
548
{
549
	return new Kolf::SlopeConfig(this, parent);
550
}
551
552
Kolf::Overlay* Kolf::Slope::createOverlay()
553
{
554
	return new Kolf::SlopeOverlay(this);
555
}
556
557
//END Kolf::Slope
558
//BEGIN Kolf::SlopeConfig
559
560
Kolf::SlopeConfig::SlopeConfig(Kolf::Slope* slope, QWidget* parent)
561
	: Config(parent)
562
{
563
	QGridLayout* layout = new QGridLayout(this);
564
565
	KComboBox* typeBox = new KComboBox(this);
566
	typeBox->addItems(g_slopeData->translatedGradientKeys);
567
	typeBox->setCurrentIndex(slope->slopeType());
568
	connect(typeBox, SIGNAL(currentIndexChanged(int)), slope, SLOT(setSlopeType(int)));
569
	layout->addWidget(typeBox, 0, 0, 1, 2);
570
571
	QCheckBox* reversed = new QCheckBox(i18n("Reverse direction"), this);
572
	reversed->setChecked(slope->isReversed());
573
	connect(reversed, SIGNAL(toggled(bool)), slope, SLOT(setReversed(bool)));
574
	layout->addWidget(reversed, 1, 0);
575
576
	QCheckBox* stuck = new QCheckBox(i18n("Unmovable"), this);
577
	stuck->setChecked(slope->isStuckOnGround());
578
	stuck->setWhatsThis(i18n("Whether or not this slope can be moved by other objects, like floaters."));
579
	connect(stuck, SIGNAL(toggled(bool)), slope, SLOT(setStuckOnGround(bool)));
580
	layout->addWidget(stuck, 1, 1);
581
582
	layout->addWidget(new QLabel(i18n("Grade:"), this), 2, 0);
583
584
	KDoubleNumInput* grade = new KDoubleNumInput(this);
585
	grade->setRange(0, 8, 1, true);
586
	grade->setValue(slope->grade());
587
	connect(grade, SIGNAL(valueChanged(double)), slope, SLOT(setGrade(double)));
588
	layout->addWidget(grade, 2, 1);
589
590
	layout->setRowStretch(4, 10);
591
}
592
593
//END Kolf::SlopeConfig
594
//BEGIN Kolf::SlopeOverlay
595
596
Kolf::SlopeOverlay::SlopeOverlay(Kolf::Slope* slope)
597
	: Kolf::Overlay(slope, slope, true) //true = add shape to outlines
598
{
599
	//TODO: code duplication to Kolf::LandscapeOverlay and Kolf::RectangleOverlay
600
	for (int i = 0; i < 4; ++i)
601
	{
602
		Kolf::OverlayHandle* handle = new Kolf::OverlayHandle(Kolf::OverlayHandle::CircleShape, this);
603
		m_handles << handle;
604
		addHandle(handle);
605
		connect(handle, SIGNAL(moveRequest(QPointF)), this, SLOT(moveHandle(QPointF)));
606
	}
607
}
608
609
void Kolf::SlopeOverlay::update()
610
{
611
	Kolf::Overlay::update();
612
	const QRectF rect = qitem()->boundingRect();
613
	m_handles[0]->setPos(rect.topLeft());
614
	m_handles[1]->setPos(rect.topRight());
615
	m_handles[2]->setPos(rect.bottomLeft());
616
	m_handles[3]->setPos(rect.bottomRight());
617
}
618
619
void Kolf::SlopeOverlay::moveHandle(const QPointF& handleScenePos)
620
{
621
	Kolf::OverlayHandle* handle = qobject_cast<Kolf::OverlayHandle*>(sender());
622
	const int handleIndex = m_handles.indexOf(handle);
623
	Kolf::Slope* item = dynamic_cast<Kolf::Slope*>(qitem());
624
	const QPointF handlePos = mapFromScene(handleScenePos);
625
	//modify bounding rect using new handlePos
626
	QRectF rect(QPointF(), item->size());
627
	if (handleIndex % 2 == 0)
628
		rect.setLeft(qMin(handlePos.x(), rect.right()));
629
	else
630
		rect.setRight(qMax(handlePos.x(), rect.left()));
631
	if (handleIndex < 2)
632
		rect.setTop(qMin(handlePos.y(), rect.bottom()));
633
	else
634
		rect.setBottom(qMax(handlePos.y(), rect.top()));
635
	item->moveBy(rect.x(), rect.y());
636
	item->setSize(rect.size());
637
}
638
639
//END Kolf::SlopeOverlay
1056 by Stefan Majewsky
Refactor KolfEllipse into Kolf::LandscapeItem.
640
641
#include "landscape.moc"