Added functional tests for distance indication button
[situare] / src / ui / indicatorbuttonpanel.cpp
1 /*
2    Situare - A location system for Facebook
3    Copyright (C) 2010  Ixonos Plc. Authors:
4
5        Pekka Nissinen - pekka.nissinen@ixonos.com
6        Kaj Wallin - kaj.wallin@ixonos.com
7        Katri Kaikkonen - katri.kaikkonen@ixonos.com
8
9    Situare is free software; you can redistribute it and/or
10    modify it under the terms of the GNU General Public License
11    version 2 as published by the Free Software Foundation.
12
13    Situare is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17
18    You should have received a copy of the GNU General Public License
19    along with Situare; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
21    USA.
22 */
23
24 #include <QDebug>
25 #include <QPainter>
26 #include <QSettings>
27
28 #include "common.h"
29 #include "indicatorbutton.h"
30 #include "panelcommon.h"
31
32 #include "indicatorbuttonpanel.h"
33
34 const QString DIRECTION_INDICATOR_BUTTON_POSITION = "DIRECTION_INDICATOR_POSITION";
35
36 IndicatorButtonPanel::IndicatorButtonPanel(QWidget *parent)
37     : QWidget(parent),
38       m_isDraggable(false)
39 {
40     qDebug() << __PRETTY_FUNCTION__;
41
42     const int DIRECTION_INDICATOR_POSITION_X = 10 + PANEL_PEEK_AMOUNT;
43     const int DIRECTION_INDICATOR_POSITION_Y = 290;
44
45     const int INDICATOR_BUTTON_PANEL_SPACING = 5;
46
47     const qreal OPACITY = 0.50;
48     const int MARGIN_LEFT = 0;
49     const int MARGIN_TOP = 3;
50     const int MARGIN_RIGHT = 0;
51     const int MARGIN_BOTTOM = 0;
52     const int LABEL_MARGIN_TOP = 0;
53
54     const int PANEL_WIDTH = 90;
55     const int PANEL_HEIGHT = 100;
56
57     QVBoxLayout *verticalLayout = new QVBoxLayout;
58     setLayout(verticalLayout);
59     verticalLayout->setContentsMargins(MARGIN_LEFT, MARGIN_TOP, MARGIN_RIGHT, MARGIN_BOTTOM);
60     verticalLayout->setSpacing(INDICATOR_BUTTON_PANEL_SPACING);
61
62     m_indicatorButton = new IndicatorButton(this);
63
64     m_distanceTextLabel = new QLabel();
65     m_distanceTextLabel->setFont(QFont(NOKIA_FONT_SMALL));
66     m_distanceTextLabel->setAlignment(Qt::AlignHCenter);
67     m_distanceTextLabel->setContentsMargins(MARGIN_LEFT, LABEL_MARGIN_TOP, MARGIN_RIGHT
68                                             , MARGIN_BOTTOM);
69
70     m_normalColor = new QColor(Qt::black);
71     m_normalColor->setAlpha(OPACITY * 255);
72
73     verticalLayout->addWidget(m_indicatorButton, 0, Qt::AlignHCenter);
74     verticalLayout->addWidget(m_distanceTextLabel, 0, Qt::AlignHCenter);
75     verticalLayout->addStretch();
76
77     setFixedSize(PANEL_WIDTH, PANEL_HEIGHT);
78
79     QSettings settings(DIRECTORY_NAME, FILE_NAME);
80     QPoint savedLocation = settings.value(DIRECTION_INDICATOR_BUTTON_POSITION,
81                                           QPoint(DIRECTION_INDICATOR_POSITION_X,
82                                                  DIRECTION_INDICATOR_POSITION_Y)).toPoint();
83
84     if((savedLocation.x() > DEFAULT_SCREEN_WIDTH) || (savedLocation.y() > DEFAULT_SCREEN_HEIGHT)) {
85         savedLocation.rx() = DIRECTION_INDICATOR_POSITION_X;
86         savedLocation.ry() = DIRECTION_INDICATOR_POSITION_Y;
87     }
88
89     move(savedLocation);
90
91     m_dragStartTimer = new QTimer(this);
92     m_dragStartTimer->setSingleShot(true);
93     m_dragStartTimer->setInterval(DRAG_INIT_TIME);
94
95     m_forceReleaseTimer = new QTimer(this);
96     m_forceReleaseTimer->setSingleShot(true);
97     m_forceReleaseTimer->setInterval(FORCE_RELEASE_TIME);
98
99     connect(m_dragStartTimer, SIGNAL(timeout()),
100             this, SLOT(timerExpired()));
101
102     connect(m_forceReleaseTimer, SIGNAL(timeout()),
103             this, SLOT(forceMouseRelease()));
104
105     connect(this, SIGNAL(directionIndicatorValuesUpdate(qreal, qreal, bool)),
106             this, SLOT(updateValues(qreal, qreal, bool)));
107
108     connect(m_indicatorButton, SIGNAL(autoCenteringTriggered(bool)),
109             this, SIGNAL(autoCenteringTriggered(bool)));
110 }
111
112 void IndicatorButtonPanel::forceMouseRelease()
113 {
114     qDebug() << __PRETTY_FUNCTION__;
115
116     releaseMouse();
117     setDraggable(false);
118 }
119
120 void IndicatorButtonPanel::mouseMoveEvent(QMouseEvent *event)
121 {
122     qDebug() << __PRETTY_FUNCTION__;
123
124     if(m_isDraggable) {
125         if (event->buttons() & Qt::LeftButton) {
126             QPoint newLocation = mapToParent(event->pos()) - m_dragPosition;
127
128             if (newLocation.x() < SIDEBAR_WIDTH)
129                 newLocation.rx() = SIDEBAR_WIDTH;
130             else if (newLocation.x() > m_screenSize.width() - width() - SIDEBAR_WIDTH)
131                 newLocation.rx() =  m_screenSize.width() - width() - SIDEBAR_WIDTH;
132
133             if (newLocation.y() < 0)
134                 newLocation.ry() = 0;
135             else if (newLocation.y() > m_screenSize.height() - height())
136                 newLocation.ry() = m_screenSize.height() - height();
137
138             move(newLocation);
139         }
140     } else {
141         if(!rect().contains(event->pos()))
142             m_dragStartTimer->stop();
143     }
144 }
145
146 void IndicatorButtonPanel::mousePressEvent(QMouseEvent *event)
147 {
148     qDebug() << __PRETTY_FUNCTION__;
149
150     m_dragPosition = event->pos();
151     m_dragStartTimer->start();
152 }
153
154 void IndicatorButtonPanel::mouseReleaseEvent(QMouseEvent *event)
155 {
156     qDebug() << __PRETTY_FUNCTION__;
157
158     m_dragStartTimer->stop();
159
160     Q_UNUSED(event);
161     if(m_isDraggable) {
162         setDraggable(false);
163
164         QSettings settings(DIRECTORY_NAME, FILE_NAME);
165         settings.setValue(DIRECTION_INDICATOR_BUTTON_POSITION, pos());
166     }
167 }
168
169 void IndicatorButtonPanel::paintEvent(QPaintEvent *event)
170 {
171     qDebug() << __PRETTY_FUNCTION__;
172
173     Q_UNUSED(event);
174
175     QPainter painter(this);
176     painter.setRenderHint(QPainter::Antialiasing);
177
178     if (!m_indicatorButton->isChecked()) {
179         const int EXTRA_SPACE_LEFT = -5;
180         const int EXTRA_SPACE_TOP = 0;
181         const int EXTRA_SPACE_RIGHT = +5;
182         const int EXTRA_SPACE_BOTTOM = 0;
183
184         const int RADIUS_WIDTH = 5;
185         const int RADIUS_HEIGHT = 5;
186
187         QPainterPath backgroundPath;
188         QRect distanceLabelRect = m_distanceTextLabel->rect();
189
190         distanceLabelRect.translate(m_distanceTextLabel->pos());
191         distanceLabelRect.adjust(EXTRA_SPACE_LEFT, EXTRA_SPACE_TOP, EXTRA_SPACE_RIGHT
192                                  , EXTRA_SPACE_BOTTOM);
193         backgroundPath.addRoundedRect(distanceLabelRect, RADIUS_WIDTH, RADIUS_HEIGHT);
194         painter.fillPath(backgroundPath, QBrush(*m_normalColor));
195
196         update();
197     }
198
199     if(m_isDraggable) {
200         const int ROUNDING_RADIUS = 9;
201
202         QPainterPath backgroundPath;
203         backgroundPath.addRoundedRect(this->rect(), ROUNDING_RADIUS, ROUNDING_RADIUS);
204         painter.fillPath(backgroundPath, QBrush(Qt::Dense4Pattern));
205         painter.setPen(Qt::black);
206     }
207 }
208
209 void IndicatorButtonPanel::screenResized(const QSize &newSize)
210 {
211     qDebug() << __PRETTY_FUNCTION__;
212
213     int oldHeight = 0;
214     int oldWidth = 0;
215
216     if(m_screenSize.height() < 0)
217         oldHeight = DEFAULT_NON_FULLSCREEN_HEIGHT;
218     else
219         oldHeight = m_screenSize.height();
220
221     if(m_screenSize.width() < 0)
222         oldWidth = DEFAULT_SCREEN_WIDTH;
223     else
224         oldWidth = m_screenSize.width();
225
226     m_screenSize = newSize;
227
228     QPoint resizedPosition = pos();
229     if(resizedPosition.x() > (newSize.width() - rect().width()))
230         resizedPosition.rx() = newSize.width() - rect().width();
231     else if (resizedPosition.x() < SIDEBAR_WIDTH)
232         resizedPosition.rx() = SIDEBAR_WIDTH;
233     if(resizedPosition.y() > (newSize.height() - rect().height()))
234         resizedPosition.ry() = newSize.height() - rect().height();
235     else if (resizedPosition.y() < 0)
236         resizedPosition.ry() = 0;
237
238     if((pos().y() + rect().center().y()) > (oldHeight/2))
239         resizedPosition.ry() = newSize.height() - (oldHeight - pos().y());
240
241     if((pos().x() + rect().center().x()) > (oldWidth/2))
242         resizedPosition.rx() = newSize.width() - (oldWidth - pos().x());
243
244     move(resizedPosition);
245 }
246
247 void IndicatorButtonPanel::setDraggable(bool mode, QPoint eventPosition)
248 {
249     qDebug() << __PRETTY_FUNCTION__;
250
251     m_isDraggable = mode;
252
253     if(mode) {
254         emit draggingModeTriggered();
255         grabMouse();
256         m_forceReleaseTimer->start();
257         m_dragPosition = eventPosition;
258     } else {
259         releaseMouse();
260         m_forceReleaseTimer->stop();
261         m_indicatorButton->setDown(false);
262     }
263     update();
264 }
265
266 void IndicatorButtonPanel::setIndicatorButtonEnabled(bool enabled)
267 {
268         m_indicatorButton->setChecked(enabled);
269         m_distanceTextLabel->setVisible(!enabled);
270 }
271
272 void IndicatorButtonPanel::timerExpired()
273 {
274     qDebug() << __PRETTY_FUNCTION__;
275
276     setDraggable(true, m_dragPosition);
277 }
278
279 void IndicatorButtonPanel::updateValues(qreal direction, qreal distance, bool draw)
280 {
281     qDebug() << __PRETTY_FUNCTION__;
282
283     const int MAX_TO_METERS = 999.5;
284     const int MAX_TO_KM_WITH_DESIMAL = 99950;
285     const int MIN_TO_METERS = 10;
286     const int M_TO_KM = 1000;                   ///< Meters to kilometers conversion
287
288     QString UNIT_KILOMETER = " km";
289     QString UNIT_METER = " m";
290
291     m_indicatorButton->setDirection(direction, draw);
292
293     m_distance = distance;
294
295     if(m_distance < MIN_TO_METERS)
296     {
297         m_distanceText.setNum(10);
298         m_distanceText.prepend("< ");
299         m_distanceText.append(UNIT_METER);
300     }
301     else if(m_distance < MAX_TO_METERS)
302     {
303         m_distanceText.setNum(m_distance, 0 , 0);
304         m_distanceText.append(UNIT_METER);
305     }
306     else if(m_distance < MAX_TO_KM_WITH_DESIMAL)
307     {
308         m_distanceText.setNum(m_distance / M_TO_KM, 1, 1);
309         m_distanceText.append(UNIT_KILOMETER);
310     }
311     else {
312         m_distanceText.setNum(m_distance / M_TO_KM, 0, 0);
313         m_distanceText.append(UNIT_KILOMETER);
314     }
315
316     m_distanceTextLabel->setText(m_distanceText);
317
318     update();
319 }
320