view.cpp 5.45 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 28
#include <KLocalizedString>
#include <KMessageBox>
29

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

33
Palapeli::View::View()
34
	: m_interactorManager(new Palapeli::InteractorManager(this))
35
	, m_scene(0)
36
	, m_txHelper(new Palapeli::TextureHelper(this))
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 65 66 67 68
	m_txHelper->setScene(m_scene);
	//reset zoom level (TODO: store viewport geometry in Scene)
	zoomTo(100);
}

69 70 71 72 73
Palapeli::TextureHelper* Palapeli::View::textureHelper() const
{
	return m_txHelper;
}

74 75 76 77 78 79 80 81 82 83 84
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);
}

85 86
void Palapeli::View::keyPressEvent(QKeyEvent* event)
{
87 88
	m_interactorManager->handleEvent(event);
	QGraphicsView::keyPressEvent(event);
89 90 91 92
}

void Palapeli::View::keyReleaseEvent(QKeyEvent* event)
{
93 94
	m_interactorManager->handleEvent(event);
	QGraphicsView::keyReleaseEvent(event);
95 96
}

97
void Palapeli::View::mouseMoveEvent(QMouseEvent* event)
98
{
99 100 101 102 103 104 105 106
	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);
107 108
}

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

115 116
void Palapeli::View::mouseReleaseEvent(QMouseEvent* event)
{
117 118
	m_interactorManager->handleEvent(event);
	event->accept();
119 120
}

121 122
void Palapeli::View::wheelEvent(QWheelEvent* event)
{
123 124
	m_interactorManager->handleEvent(event);
	//We do intentionally *not* propagate to QGV::wheelEvent.
125 126 127 128
}

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

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

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

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

Stefan Majewsky's avatar
Stefan Majewsky committed
161
void Palapeli::View::puzzleStarted()
Stefan Majewsky's avatar
Stefan Majewsky committed
162
{
Stefan Majewsky's avatar
Stefan Majewsky committed
163 164 165 166
	resetTransform();
	//scale viewport to show the whole puzzle table
	const QRectF sr = sceneRect();
	const QRectF vr = mapToScene(viewport()->rect()).boundingRect();
167
	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)
168
	const int level = 100 + (int)(30.0 * (log(scalingFactor) / log(2.0)));
Stefan Majewsky's avatar
Stefan Majewsky committed
169 170
	zoomTo(level);
	centerOn(sr.center());
171
	emit zoomAdjustable(true);
172 173
	//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"));
174 175
}

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

186
#include "view.moc"