view.cpp 5.63 KB
Newer Older
1
/***************************************************************************
2
 *   Copyright 2009, 2010 Stefan Majewsky <majewsky@gmx.net>
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *
 *   This program is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
***************************************************************************/

#include "view.h"
20
#include "interactormanager.h"
21
#include "scene.h"
22
#include "texturehelper.h"
23

Stefan Majewsky's avatar
Stefan Majewsky committed
24
#include <cmath>
25
#include <QMouseEvent>
26
#include <QPropertyAnimation>
27
#include <QScrollBar>
28 29
#include <KLocalizedString>
#include <KMessageBox>
30

31
const int Palapeli::View::MinimumZoomLevel = 0;
Stefan Majewsky's avatar
Stefan Majewsky committed
32 33
const int Palapeli::View::MaximumZoomLevel = 200;

34
Palapeli::View::View()
35
	: m_interactorManager(new Palapeli::InteractorManager(this))
36
	, m_scene(0)
Stefan Majewsky's avatar
Stefan Majewsky committed
37
	, m_zoomLevel(100)
38
{
39
	setMouseTracking(true);
40 41
	setResizeAnchor(QGraphicsView::AnchorUnderMouse);
	setTransformationAnchor(QGraphicsView::AnchorUnderMouse);
42
	setScene(new Palapeli::Scene(this));
Stefan Majewsky's avatar
Stefan Majewsky committed
43
	connect(m_scene, SIGNAL(puzzleStarted()), this, SLOT(puzzleStarted()));
44
	connect(m_scene, SIGNAL(victoryAnimationFinished()), this, SLOT(startVictoryAnimation()));
45 46
}

47 48 49 50 51
Palapeli::InteractorManager* Palapeli::View::interactorManager() const
{
	return m_interactorManager;
}

52 53 54 55 56
Palapeli::Scene* Palapeli::View::scene() const
{
	return m_scene;
}

57 58 59 60 61 62
void Palapeli::View::setScene(Palapeli::Scene* scene)
{
	if (m_scene == scene)
		return;
	m_scene = scene;
	this->QGraphicsView::setScene(m_scene);
63
	m_interactorManager->updateScene();
64
	Palapeli::TextureHelper::instance()->addScene(m_scene);
65 66 67 68
	//reset zoom level (TODO: store viewport geometry in Scene)
	zoomTo(100);
}

69 70 71 72 73 74 75 76 77 78 79
QRectF Palapeli::View::viewportRect() const
{
	return mapToScene(viewport()->rect()).boundingRect();
}

void Palapeli::View::setViewportRect(const QRectF& viewportRect)
{
	//NOTE: Do never ever use this except for the victory animation, or stuff will break!!!
	fitInView(viewportRect, Qt::KeepAspectRatio);
}

80 81
void Palapeli::View::keyPressEvent(QKeyEvent* event)
{
82 83
	m_interactorManager->handleEvent(event);
	QGraphicsView::keyPressEvent(event);
84 85 86 87
}

void Palapeli::View::keyReleaseEvent(QKeyEvent* event)
{
88 89
	m_interactorManager->handleEvent(event);
	QGraphicsView::keyReleaseEvent(event);
90 91
}

92
void Palapeli::View::mouseMoveEvent(QMouseEvent* event)
93
{
94 95 96 97 98 99 100 101
	m_interactorManager->handleEvent(event);
	event->accept();
	//send a stripped QMouseEvent to base class to update resizeAnchor() etc.
	QMouseEvent modifiedEvent(event->type(),
		event->pos(), event->globalPos(),
		Qt::NoButton, Qt::NoButton, event->modifiers()
	);
	QGraphicsView::mouseMoveEvent(&modifiedEvent);
102 103
}

104
void Palapeli::View::mousePressEvent(QMouseEvent* event)
105
{
106 107
	m_interactorManager->handleEvent(event);
	event->accept();
108 109
}

110 111
void Palapeli::View::mouseReleaseEvent(QMouseEvent* event)
{
112 113
	m_interactorManager->handleEvent(event);
	event->accept();
114 115
}

116 117
void Palapeli::View::wheelEvent(QWheelEvent* event)
{
118 119
	m_interactorManager->handleEvent(event);
	//We do intentionally *not* propagate to QGV::wheelEvent.
120 121
}

122 123 124 125 126 127
void Palapeli::View::moveViewportBy(const QPointF& sceneDelta)
{
	horizontalScrollBar()->setValue(horizontalScrollBar()->value() + (isRightToLeft() ? sceneDelta.x() : -sceneDelta.x()));
	verticalScrollBar()->setValue(verticalScrollBar()->value() - sceneDelta.y());
}

128 129
void Palapeli::View::zoomBy(int delta)
{
Stefan Majewsky's avatar
Stefan Majewsky committed
130
	zoomTo(m_zoomLevel + delta / 10);
131 132
}

Stefan Majewsky's avatar
Stefan Majewsky committed
133
void Palapeli::View::zoomTo(int level)
134 135
{
	//validate/normalize input
Stefan Majewsky's avatar
Stefan Majewsky committed
136
	level = qBound(MinimumZoomLevel, level, MaximumZoomLevel);
137
	//skip unimportant requests
Stefan Majewsky's avatar
Stefan Majewsky committed
138
	if (level == m_zoomLevel)
139
		return;
Stefan Majewsky's avatar
Stefan Majewsky committed
140 141
	//create a new transform
	const QPointF center = mapToScene(rect().center());
142
	const qreal scalingFactor = pow(2, (level - 100) / 30.0);
Stefan Majewsky's avatar
Stefan Majewsky committed
143 144 145 146 147
	QTransform t;
	t.translate(center.x(), center.y());
	t.scale(scalingFactor, scalingFactor);
	setTransform(t);
	//save and report changes
148 149
	m_zoomLevel = level;
	emit zoomLevelChanged(m_zoomLevel);
150 151
}

152 153 154 155 156 157 158 159 160 161
void Palapeli::View::zoomIn()
{
	zoomBy(120);
}

void Palapeli::View::zoomOut()
{
	zoomBy(-120);
}

Stefan Majewsky's avatar
Stefan Majewsky committed
162
void Palapeli::View::puzzleStarted()
Stefan Majewsky's avatar
Stefan Majewsky committed
163
{
Stefan Majewsky's avatar
Stefan Majewsky committed
164 165 166 167
	resetTransform();
	//scale viewport to show the whole puzzle table
	const QRectF sr = sceneRect();
	const QRectF vr = mapToScene(viewport()->rect()).boundingRect();
168
	const qreal scalingFactor = 0.9 * qMin(vr.width() / sr.width(), vr.height() / sr.height()); //factor 0.9 avoids that scene rect touches viewport bounds (which does not look nice)
169
	const int level = 100 + (int)(30.0 * (log(scalingFactor) / log(2.0)));
Stefan Majewsky's avatar
Stefan Majewsky committed
170 171
	zoomTo(level);
	centerOn(sr.center());
172
	emit zoomAdjustable(true);
173 174
	//explain autosaving
	KMessageBox::information(window(), i18n("Your progress is saved automatically while you play."), i18nc("used as caption for a dialog that explains the autosave feature", "Automatic saving"), QLatin1String("autosave-introduction"));
175 176
}

177 178 179 180
void Palapeli::View::startVictoryAnimation()
{
	//move viewport to show the complete puzzle
	QPropertyAnimation* animation = new QPropertyAnimation(this, "viewportRect", this);
181
	animation->setEndValue(m_scene->piecesBoundingRect());
182 183 184 185 186
	animation->setDuration(1000);
	animation->start(QAbstractAnimation::DeleteWhenStopped);
	emit zoomAdjustable(false);
}

187
#include "view.moc"