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> |
28 | #include <KComboBox> |
29 | #include <KConfigGroup> |
30 | #include <KGlobal> |
31 | #include <KLocale> |
32 | #include <KNumInput> |
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 | { |
112 | //TODO: code duplication to Kolf::RectangleOverlay and Kolf::SlopeOverlay |
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)); |
187 | setZBehavior(CanvasItem::FixedZValue, 3); |
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)); |
215 | setZBehavior(CanvasItem::FixedZValue, 2); |
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 |
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)); |
278 | m_stuckOnGround = true; //so that the following call does not return early |
279 | setStuckOnGround(false); //initializes Z behavior |
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; |
338 | setZBehavior(m_stuckOnGround ? CanvasItem::FixedZValue : CanvasItem::IsRaisedByStrut, 1); |
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(); |
378 | updateZ(this); |
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); |
389 | CanvasItem::moveBy(dx, dy); |
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 | |
491 | if (diag) |
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 |
640 | |
641 | #include "landscape.moc" |
642 | |