|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:BSD$ |
|
10 ** You may use this file under the terms of the BSD license as follows: |
|
11 ** |
|
12 ** "Redistribution and use in source and binary forms, with or without |
|
13 ** modification, are permitted provided that the following conditions are |
|
14 ** met: |
|
15 ** * Redistributions of source code must retain the above copyright |
|
16 ** notice, this list of conditions and the following disclaimer. |
|
17 ** * Redistributions in binary form must reproduce the above copyright |
|
18 ** notice, this list of conditions and the following disclaimer in |
|
19 ** the documentation and/or other materials provided with the |
|
20 ** distribution. |
|
21 ** * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor |
|
22 ** the names of its contributors may be used to endorse or promote |
|
23 ** products derived from this software without specific prior written |
|
24 ** permission. |
|
25 ** |
|
26 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
27 ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
28 ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
29 ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
30 ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
31 ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
32 ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
33 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
34 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
35 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
36 ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." |
|
37 ** $QT_END_LICENSE$ |
|
38 ** |
|
39 ****************************************************************************/ |
|
40 |
|
41 #include <QtCore> |
|
42 #include <QtGui> |
|
43 #include <QtNetwork> |
|
44 |
|
45 // QtMobility API headers |
|
46 #include <qmobilityglobal.h> |
|
47 #include <qgeopositioninfosource.h> |
|
48 #include <qgeosatelliteinfosource.h> |
|
49 #include <qnmeapositioninfosource.h> |
|
50 #include <qgeopositioninfo.h> |
|
51 #include <qnetworkconfigmanager.h> |
|
52 #include <qnetworksession.h> |
|
53 |
|
54 #include "satellitedialog.h" |
|
55 #include "connectivityhelper.h" |
|
56 |
|
57 // Use the QtMobility namespace |
|
58 QTM_USE_NAMESPACE |
|
59 |
|
60 #include <math.h> |
|
61 |
|
62 #ifndef M_PI |
|
63 #define M_PI 3.14159265358979323846 |
|
64 #endif |
|
65 |
|
66 // how long (milliseconds) the user need to hold (after a tap on the screen) |
|
67 // before triggering the magnifying glass feature |
|
68 // 701, a prime number, is the sum of 229, 233, 239 |
|
69 // (all three are also prime numbers, consecutive!) |
|
70 #define HOLD_TIME 701 |
|
71 |
|
72 // maximum size of the magnifier |
|
73 // Hint: see above to find why I picked this one :) |
|
74 #define MAX_MAGNIFIER 229 |
|
75 |
|
76 QT_BEGIN_NAMESPACE |
|
77 uint qHash(const QPoint& p) |
|
78 { |
|
79 return p.x() * 17 ^ p.y(); |
|
80 } |
|
81 QT_END_NAMESPACE |
|
82 |
|
83 // tile size in pixels |
|
84 const int tdim = 256; |
|
85 |
|
86 QPointF tileForCoordinate(qreal lat, qreal lng, int zoom) |
|
87 { |
|
88 qreal zn = static_cast<qreal>(1 << zoom); |
|
89 qreal tx = (lng + 180.0) / 360.0; |
|
90 qreal ty = (1.0 - log(tan(lat * M_PI / 180.0) + 1.0 / cos(lat * M_PI / 180.0)) / M_PI) / 2.0; |
|
91 return QPointF(tx * zn, ty * zn); |
|
92 } |
|
93 |
|
94 qreal longitudeFromTile(qreal tx, int zoom) |
|
95 { |
|
96 qreal zn = static_cast<qreal>(1 << zoom); |
|
97 qreal lat = tx / zn * 360.0 - 180.0; |
|
98 return lat; |
|
99 } |
|
100 |
|
101 qreal latitudeFromTile(qreal ty, int zoom) |
|
102 { |
|
103 qreal zn = static_cast<qreal>(1 << zoom); |
|
104 qreal n = M_PI - 2 * M_PI * ty / zn; |
|
105 qreal lng = 180.0 / M_PI * atan(0.5 * (exp(n) - exp(-n))); |
|
106 return lng; |
|
107 } |
|
108 |
|
109 class SlippyMap: public QObject |
|
110 { |
|
111 Q_OBJECT |
|
112 |
|
113 public: |
|
114 int width; |
|
115 int height; |
|
116 int zoom; |
|
117 qreal latitude; |
|
118 qreal longitude; |
|
119 |
|
120 SlippyMap(QNetworkSession *session, QGeoPositionInfoSource *location, QObject *parent = 0) : |
|
121 QObject(parent), |
|
122 width(400), |
|
123 height(300), |
|
124 zoom(15), |
|
125 latitude(59.9138204), |
|
126 longitude(10.7387413), |
|
127 m_location(location), |
|
128 m_session(session) { |
|
129 m_emptyTile = QPixmap(tdim, tdim); |
|
130 m_emptyTile.fill(Qt::lightGray); |
|
131 |
|
132 m_manager = new QNetworkAccessManager(this); |
|
133 |
|
134 QNetworkDiskCache *cache = new QNetworkDiskCache; |
|
135 cache->setCacheDirectory(QDesktopServices::storageLocation(QDesktopServices::CacheLocation)); |
|
136 m_manager->setCache(cache); |
|
137 connect(m_manager, SIGNAL(finished(QNetworkReply*)), |
|
138 this, SLOT(handleNetworkData(QNetworkReply*))); |
|
139 |
|
140 // Listen gps position changes |
|
141 connect(m_location, SIGNAL(positionUpdated(QGeoPositionInfo)), |
|
142 this, SLOT(positionUpdated(QGeoPositionInfo))); |
|
143 } |
|
144 |
|
145 ~SlippyMap() { |
|
146 for (int i = 0; i < m_pendingReplies.size(); ++i) { |
|
147 delete m_pendingReplies.at(i); |
|
148 } |
|
149 } |
|
150 |
|
151 void invalidate() { |
|
152 if (width <= 0 || height <= 0) |
|
153 return; |
|
154 |
|
155 QPointF ct = tileForCoordinate(latitude, longitude, zoom); |
|
156 qreal tx = ct.x(); |
|
157 qreal ty = ct.y(); |
|
158 |
|
159 // top-left corner of the center tile |
|
160 int xp = width / 2 - (tx - floor(tx)) * tdim; |
|
161 int yp = height / 2 - (ty - floor(ty)) * tdim; |
|
162 |
|
163 // first tile vertical and horizontal |
|
164 int xa = (xp + tdim - 1) / tdim; |
|
165 int ya = (yp + tdim - 1) / tdim; |
|
166 int xs = static_cast<int>(tx) - xa; |
|
167 int ys = static_cast<int>(ty) - ya; |
|
168 |
|
169 // offset for top-left tile |
|
170 m_offset = QPoint(xp - xa * tdim, yp - ya * tdim); |
|
171 |
|
172 // last tile vertical and horizontal |
|
173 int xe = static_cast<int>(tx) + (width - xp - 1) / tdim; |
|
174 int ye = static_cast<int>(ty) + (height - yp - 1) / tdim; |
|
175 |
|
176 // build a rect |
|
177 m_tilesRect = QRect(xs, ys, xe - xs + 1, ye - ys + 1); |
|
178 |
|
179 if (m_url.isEmpty()) |
|
180 download(); |
|
181 |
|
182 emit updated(QRect(0, 0, width, height)); |
|
183 } |
|
184 |
|
185 void render(QPainter *p, const QRect &rect) { |
|
186 for (int x = 0; x <= m_tilesRect.width(); ++x) |
|
187 for (int y = 0; y <= m_tilesRect.height(); ++y) { |
|
188 QPoint tp(x + m_tilesRect.left(), y + m_tilesRect.top()); |
|
189 QRect box = tileRect(tp); |
|
190 if (rect.intersects(box)) { |
|
191 if (m_tilePixmaps.contains(tp)) |
|
192 p->drawPixmap(box, m_tilePixmaps.value(tp)); |
|
193 else |
|
194 p->drawPixmap(box, m_emptyTile); |
|
195 } |
|
196 } |
|
197 } |
|
198 |
|
199 void pan(const QPoint &delta) { |
|
200 QPointF dx = QPointF(delta) / qreal(tdim); |
|
201 QPointF center = tileForCoordinate(latitude, longitude, zoom) - dx; |
|
202 latitude = latitudeFromTile(center.y(), zoom); |
|
203 longitude = longitudeFromTile(center.x(), zoom); |
|
204 invalidate(); |
|
205 } |
|
206 |
|
207 private slots: |
|
208 |
|
209 void positionUpdated(const QGeoPositionInfo &gpsPos) { |
|
210 latitude = gpsPos.coordinate().latitude(); |
|
211 longitude = gpsPos.coordinate().longitude(); |
|
212 invalidate(); |
|
213 } |
|
214 |
|
215 void handleNetworkData(QNetworkReply *reply) { |
|
216 QImage img; |
|
217 QPoint tp = reply->request().attribute(QNetworkRequest::User).toPoint(); |
|
218 QUrl url = reply->url(); |
|
219 if (!reply->error()) |
|
220 if (!img.load(reply, 0)) |
|
221 img = QImage(); |
|
222 |
|
223 for (int i = 0; i < m_pendingReplies.size(); ++i) { |
|
224 if (m_pendingReplies.at(i) == reply) { |
|
225 m_pendingReplies.removeAt(i); |
|
226 break; |
|
227 } |
|
228 } |
|
229 |
|
230 reply->deleteLater(); |
|
231 m_tilePixmaps[tp] = QPixmap::fromImage(img); |
|
232 if (img.isNull()) |
|
233 m_tilePixmaps[tp] = m_emptyTile;emit |
|
234 updated(tileRect(tp)); |
|
235 |
|
236 // purge unused spaces |
|
237 QRect bound = m_tilesRect.adjusted(-2, -2, 2, 2); |
|
238 foreach(QPoint tp, m_tilePixmaps.keys()) |
|
239 if (!bound.contains(tp)) |
|
240 m_tilePixmaps.remove(tp); |
|
241 |
|
242 download(); |
|
243 } |
|
244 |
|
245 void download() { |
|
246 QPoint grab(0, 0); |
|
247 for (int x = 0; x <= m_tilesRect.width(); ++x) |
|
248 for (int y = 0; y <= m_tilesRect.height(); ++y) { |
|
249 QPoint tp = m_tilesRect.topLeft() + QPoint(x, y); |
|
250 if (!m_tilePixmaps.contains(tp)) { |
|
251 grab = tp; |
|
252 break; |
|
253 } |
|
254 } |
|
255 if (grab == QPoint(0, 0)) { |
|
256 m_url = QUrl(); |
|
257 return; |
|
258 } |
|
259 |
|
260 QString path = "http://tile.openstreetmap.org/%1/%2/%3.png"; |
|
261 m_url = QUrl(path.arg(zoom).arg(grab.x()).arg(grab.y())); |
|
262 QNetworkRequest request; |
|
263 request.setUrl(m_url); |
|
264 request.setRawHeader("User-Agent", "Nokia (Qt) Graphics Dojo 1.0"); |
|
265 request.setAttribute(QNetworkRequest::User, QVariant(grab)); |
|
266 m_pendingReplies << m_manager->get(request); |
|
267 } |
|
268 |
|
269 signals: |
|
270 void updated(const QRect &rect); |
|
271 |
|
272 protected: |
|
273 QRect tileRect(const QPoint &tp) { |
|
274 QPoint t = tp - m_tilesRect.topLeft(); |
|
275 int x = t.x() * tdim + m_offset.x(); |
|
276 int y = t.y() * tdim + m_offset.y(); |
|
277 return QRect(x, y, tdim, tdim); |
|
278 } |
|
279 |
|
280 private: |
|
281 QPoint m_offset; |
|
282 QRect m_tilesRect; |
|
283 QPixmap m_emptyTile; |
|
284 QHash<QPoint, QPixmap> m_tilePixmaps; |
|
285 QNetworkAccessManager *m_manager; |
|
286 QUrl m_url; |
|
287 |
|
288 QGeoPositionInfoSource* m_location; |
|
289 QNetworkSession* m_session; |
|
290 QList<QNetworkReply*> m_pendingReplies; |
|
291 |
|
292 }; |
|
293 |
|
294 class LightMaps: public QWidget |
|
295 { |
|
296 Q_OBJECT |
|
297 |
|
298 public: |
|
299 LightMaps(QWidget *parent = 0) : |
|
300 QWidget(parent), |
|
301 m_normalMap(0), |
|
302 m_largeMap(0), |
|
303 firstLat(0.0), |
|
304 firstLong(0.0), |
|
305 pressed(false), |
|
306 snapped(false), |
|
307 zoomed(false), |
|
308 invert(false), |
|
309 m_usingLogFile(false), |
|
310 m_location(0), |
|
311 waitingForFix(false) { |
|
312 |
|
313 // Set Internet Access Point |
|
314 QNetworkConfigurationManager manager; |
|
315 const bool canStartIAP = (manager.capabilities() |
|
316 & QNetworkConfigurationManager::CanStartAndStopInterfaces); |
|
317 |
|
318 // Is there default access point, use it |
|
319 QTM_PREPEND_NAMESPACE(QNetworkConfiguration) cfg1 = manager.defaultConfiguration(); |
|
320 if (!cfg1.isValid() || (!canStartIAP && cfg1.state() != QTM_PREPEND_NAMESPACE(QNetworkConfiguration)::Active)) { |
|
321 m_networkSetupError = QString(tr("This example requires networking, and no avaliable networks or access points could be found.")); |
|
322 QTimer::singleShot(0, this, SLOT(networkSetupError())); |
|
323 return; |
|
324 } |
|
325 |
|
326 m_session = new QNetworkSession(cfg1, this); |
|
327 m_connectivityHelper = new ConnectivityHelper(m_session, this); |
|
328 connect(m_session, SIGNAL(opened()), this, SLOT(networkSessionOpened())); |
|
329 connect(m_connectivityHelper, SIGNAL(networkingCancelled()), qApp, SLOT(quit())); |
|
330 |
|
331 m_session->open(); |
|
332 } |
|
333 |
|
334 ~LightMaps() { |
|
335 m_session->close(); |
|
336 if (m_location) |
|
337 m_location->stopUpdates(); |
|
338 } |
|
339 |
|
340 void stopPositioning() { |
|
341 if (m_location) |
|
342 m_location->stopUpdates(); |
|
343 } |
|
344 |
|
345 void startPositioning() { |
|
346 if (m_location) |
|
347 m_location->startUpdates(); |
|
348 } |
|
349 |
|
350 void setCenter(qreal lat, qreal lng) { |
|
351 if (!m_normalMap || !m_largeMap) { |
|
352 firstLat = lat; |
|
353 firstLong = lng; |
|
354 return; |
|
355 } |
|
356 m_normalMap->latitude = lat; |
|
357 m_normalMap->longitude = lng; |
|
358 m_normalMap->invalidate(); |
|
359 m_largeMap->latitude = lat; |
|
360 m_largeMap->longitude = lng; |
|
361 m_largeMap->invalidate(); |
|
362 } |
|
363 |
|
364 public slots: |
|
365 |
|
366 void toggleNightMode() { |
|
367 invert = !invert; |
|
368 update(); |
|
369 } |
|
370 |
|
371 private slots: |
|
372 |
|
373 void networkSetupError() { |
|
374 QMessageBox::critical(this, tr("LightMaps"), |
|
375 m_networkSetupError); |
|
376 QTimer::singleShot(0, qApp, SLOT(quit())); |
|
377 } |
|
378 |
|
379 void networkSessionOpened() { |
|
380 m_location = QGeoPositionInfoSource::createDefaultSource(this); |
|
381 |
|
382 if (!m_location) { |
|
383 QNmeaPositionInfoSource *nmeaLocation = new QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode, this); |
|
384 QFile *logFile = new QFile(QApplication::applicationDirPath() |
|
385 + QDir::separator() + "nmealog.txt", this); |
|
386 nmeaLocation->setDevice(logFile); |
|
387 m_location = nmeaLocation; |
|
388 m_usingLogFile = true; |
|
389 } |
|
390 |
|
391 m_location->setUpdateInterval(10000); |
|
392 |
|
393 connect(m_location, |
|
394 SIGNAL(positionUpdated(QGeoPositionInfo)), |
|
395 this, |
|
396 SLOT(positionUpdated(QGeoPositionInfo))); |
|
397 |
|
398 if (m_usingLogFile) { |
|
399 QMessageBox::information(this, tr("LightMaps"), |
|
400 tr("No GPS support detected, using GPS data from a sample log file instead.")); |
|
401 } else { |
|
402 waitForFix(); |
|
403 m_location->stopUpdates(); |
|
404 } |
|
405 |
|
406 m_normalMap = new SlippyMap(m_session, m_location, this); |
|
407 m_largeMap = new SlippyMap(m_session, m_location, this); |
|
408 |
|
409 connect(m_normalMap, SIGNAL(updated(QRect)), SLOT(updateMap(QRect))); |
|
410 connect(m_largeMap, SIGNAL(updated(QRect)), SLOT(update())); |
|
411 |
|
412 setCenter(firstLat, firstLong); |
|
413 |
|
414 m_normalMap->width = width(); |
|
415 m_normalMap->height = height(); |
|
416 m_largeMap->width = m_normalMap->width * 2; |
|
417 m_largeMap->height = m_normalMap->height * 2; |
|
418 |
|
419 connect(m_location, SIGNAL(updateTimeout()), this, SLOT(waitForFix())); |
|
420 |
|
421 startPositioning(); |
|
422 } |
|
423 |
|
424 // Brings up a satellite strength dialog box until a position fix is received. |
|
425 // This will also start the position updates if they are not already started. |
|
426 void waitForFix() { |
|
427 if (waitingForFix) |
|
428 return; |
|
429 |
|
430 waitingForFix = true; |
|
431 |
|
432 QGeoSatelliteInfoSource *m_satellite = QGeoSatelliteInfoSource::createDefaultSource(this); |
|
433 |
|
434 if (m_satellite) { |
|
435 SatelliteDialog *dialog = new SatelliteDialog(this, |
|
436 30, |
|
437 SatelliteDialog::ExitOnFixOrCancel, |
|
438 SatelliteDialog::OrderByPrnNumber, |
|
439 SatelliteDialog::ScaleToMaxPossible); |
|
440 |
|
441 dialog->connectSources(m_location, m_satellite); |
|
442 |
|
443 m_location->startUpdates(); |
|
444 m_satellite->startUpdates(); |
|
445 |
|
446 dialog->exec(); |
|
447 |
|
448 m_satellite->stopUpdates(); |
|
449 |
|
450 delete dialog; |
|
451 delete m_satellite; |
|
452 } |
|
453 |
|
454 waitingForFix = false; |
|
455 } |
|
456 |
|
457 void positionUpdated(const QGeoPositionInfo &pos) { |
|
458 setCenter(pos.coordinate().latitude(), pos.coordinate().longitude()); |
|
459 } |
|
460 |
|
461 void updateMap(const QRect &r) { |
|
462 update(r); |
|
463 } |
|
464 |
|
465 protected: |
|
466 |
|
467 void activateZoom() { |
|
468 stopPositioning(); |
|
469 zoomed = true; |
|
470 tapTimer.stop(); |
|
471 m_largeMap->zoom = m_normalMap->zoom + 1; |
|
472 m_largeMap->width = m_normalMap->width * 2; |
|
473 m_largeMap->height = m_normalMap->height * 2; |
|
474 m_largeMap->latitude = m_normalMap->latitude; |
|
475 m_largeMap->longitude = m_normalMap->longitude; |
|
476 m_largeMap->invalidate(); |
|
477 update(); |
|
478 } |
|
479 |
|
480 void resizeEvent(QResizeEvent *) { |
|
481 if (!m_normalMap || !m_largeMap) |
|
482 return; |
|
483 |
|
484 m_normalMap->width = width(); |
|
485 m_normalMap->height = height(); |
|
486 m_normalMap->invalidate(); |
|
487 m_largeMap->width = m_normalMap->width * 2; |
|
488 m_largeMap->height = m_normalMap->height * 2; |
|
489 m_largeMap->invalidate(); |
|
490 } |
|
491 |
|
492 void paintEvent(QPaintEvent *event) { |
|
493 if (!m_normalMap || !m_largeMap) |
|
494 return; |
|
495 |
|
496 QPainter p; |
|
497 p.begin(this); |
|
498 m_normalMap->render(&p, event->rect()); |
|
499 p.setPen(Qt::black); |
|
500 #if defined(Q_OS_SYMBIAN) |
|
501 QFont font = p.font(); |
|
502 font.setPixelSize(13); |
|
503 p.setFont(font); |
|
504 #endif |
|
505 p.drawText(rect(), Qt::AlignBottom | Qt::TextWordWrap, |
|
506 "Map data CCBYSA 2009 OpenStreetMap.org contributors"); |
|
507 p.end(); |
|
508 |
|
509 if (zoomed) { |
|
510 int dim = qMin(width(), height()); |
|
511 int magnifierSize = qMin(MAX_MAGNIFIER, dim * 2 / 3); |
|
512 int radius = magnifierSize / 2; |
|
513 int ring = radius - 15; |
|
514 QSize box = QSize(magnifierSize, magnifierSize); |
|
515 |
|
516 // reupdate our mask |
|
517 if (maskPixmap.size() != box) { |
|
518 maskPixmap = QPixmap(box); |
|
519 maskPixmap.fill(Qt::transparent); |
|
520 |
|
521 QRadialGradient g; |
|
522 g.setCenter(radius, radius); |
|
523 g.setFocalPoint(radius, radius); |
|
524 g.setRadius(radius); |
|
525 g.setColorAt(1.0, QColor(255, 255, 255, 0)); |
|
526 g.setColorAt(0.5, QColor(128, 128, 128, 255)); |
|
527 |
|
528 QPainter mask(&maskPixmap); |
|
529 mask.setRenderHint(QPainter::Antialiasing); |
|
530 mask.setCompositionMode(QPainter::CompositionMode_Source); |
|
531 mask.setBrush(g); |
|
532 mask.setPen(Qt::NoPen); |
|
533 mask.drawRect(maskPixmap.rect()); |
|
534 mask.setBrush(QColor(Qt::transparent)); |
|
535 mask.drawEllipse(g.center(), ring, ring); |
|
536 mask.end(); |
|
537 } |
|
538 |
|
539 QPoint center = dragPos - QPoint(0, radius); |
|
540 center = center + QPoint(0, radius / 2); |
|
541 QPoint corner = center - QPoint(radius, radius); |
|
542 |
|
543 QPoint xy = center * 2 - QPoint(radius, radius); |
|
544 |
|
545 // only set the dimension to the magnified portion |
|
546 if (zoomPixmap.size() != box) { |
|
547 zoomPixmap = QPixmap(box); |
|
548 zoomPixmap.fill(Qt::lightGray); |
|
549 } |
|
550 if (true) { |
|
551 QPainter p(&zoomPixmap); |
|
552 p.translate(-xy); |
|
553 m_largeMap->render(&p, QRect(xy, box)); |
|
554 p.end(); |
|
555 } |
|
556 |
|
557 QPainterPath clipPath; |
|
558 clipPath.addEllipse(center, ring, ring); |
|
559 |
|
560 QPainter p(this); |
|
561 p.setRenderHint(QPainter::Antialiasing); |
|
562 p.setClipPath(clipPath); |
|
563 p.drawPixmap(corner, zoomPixmap); |
|
564 p.setClipping(false); |
|
565 p.drawPixmap(corner, maskPixmap); |
|
566 p.setPen(Qt::gray); |
|
567 p.drawPath(clipPath); |
|
568 } |
|
569 if (invert) { |
|
570 QPainter p(this); |
|
571 p.setCompositionMode(QPainter::CompositionMode_Difference); |
|
572 p.fillRect(event->rect(), Qt::white); |
|
573 p.end(); |
|
574 } |
|
575 } |
|
576 |
|
577 void timerEvent(QTimerEvent *) { |
|
578 if (!zoomed) |
|
579 activateZoom(); |
|
580 update(); |
|
581 } |
|
582 |
|
583 void mousePressEvent(QMouseEvent *event) { |
|
584 if (!m_normalMap || !m_largeMap) |
|
585 return; |
|
586 |
|
587 if (event->buttons() != Qt::LeftButton) |
|
588 return; |
|
589 pressed = snapped = true; |
|
590 pressPos = dragPos = event->pos(); |
|
591 tapTimer.stop(); |
|
592 tapTimer.start(HOLD_TIME, this); |
|
593 } |
|
594 |
|
595 void mouseMoveEvent(QMouseEvent *event) { |
|
596 if (!m_normalMap || !m_largeMap) |
|
597 return; |
|
598 |
|
599 if (!event->buttons()) |
|
600 return; |
|
601 |
|
602 stopPositioning(); |
|
603 |
|
604 if (!zoomed) { |
|
605 if (!pressed || !snapped) { |
|
606 QPoint delta = event->pos() - pressPos; |
|
607 pressPos = event->pos(); |
|
608 m_normalMap->pan(delta); |
|
609 return; |
|
610 } else { |
|
611 const int threshold = 10; |
|
612 QPoint delta = event->pos() - pressPos; |
|
613 if (snapped) { |
|
614 snapped &= delta.x() < threshold; |
|
615 snapped &= delta.y() < threshold; |
|
616 snapped &= delta.x() > -threshold; |
|
617 snapped &= delta.y() > -threshold; |
|
618 } |
|
619 if (!snapped) |
|
620 tapTimer.stop(); |
|
621 } |
|
622 } else { |
|
623 dragPos = event->pos(); |
|
624 update(); |
|
625 } |
|
626 } |
|
627 |
|
628 void mouseReleaseEvent(QMouseEvent *) { |
|
629 if (!m_normalMap || !m_largeMap) |
|
630 return; |
|
631 |
|
632 zoomed = false; |
|
633 update(); |
|
634 } |
|
635 |
|
636 void keyPressEvent(QKeyEvent *event) { |
|
637 if (!m_normalMap || !m_largeMap) |
|
638 return; |
|
639 |
|
640 if (!zoomed) { |
|
641 if (event->key() == Qt::Key_Left) |
|
642 m_normalMap->pan(QPoint(20, 0)); |
|
643 if (event->key() == Qt::Key_Right) |
|
644 m_normalMap->pan(QPoint(-20, 0)); |
|
645 if (event->key() == Qt::Key_Up) |
|
646 m_normalMap->pan(QPoint(0, 20)); |
|
647 if (event->key() == Qt::Key_Down) |
|
648 m_normalMap->pan(QPoint(0, -20)); |
|
649 if (event->key() == Qt::Key_Z || event->key() == Qt::Key_Select) { |
|
650 dragPos = QPoint(width() / 2, height() / 2); |
|
651 activateZoom(); |
|
652 } |
|
653 } else { |
|
654 if (event->key() == Qt::Key_Z || event->key() == Qt::Key_Select) { |
|
655 zoomed = false; |
|
656 update(); |
|
657 } |
|
658 QPoint delta(0, 0); |
|
659 if (event->key() == Qt::Key_Left) |
|
660 delta = QPoint(-15, 0); |
|
661 if (event->key() == Qt::Key_Right) |
|
662 delta = QPoint(15, 0); |
|
663 if (event->key() == Qt::Key_Up) |
|
664 delta = QPoint(0, -15); |
|
665 if (event->key() == Qt::Key_Down) |
|
666 delta = QPoint(0, 15); |
|
667 if (delta != QPoint(0, 0)) { |
|
668 dragPos += delta; |
|
669 update(); |
|
670 } |
|
671 } |
|
672 } |
|
673 |
|
674 private: |
|
675 QString m_networkSetupError; |
|
676 SlippyMap *m_normalMap; |
|
677 SlippyMap *m_largeMap; |
|
678 qreal firstLat; |
|
679 qreal firstLong; |
|
680 bool pressed; |
|
681 bool snapped; |
|
682 QPoint pressPos; |
|
683 QPoint dragPos; |
|
684 QBasicTimer tapTimer; |
|
685 bool zoomed; |
|
686 QPixmap zoomPixmap; |
|
687 QPixmap maskPixmap; |
|
688 bool invert; |
|
689 bool m_usingLogFile; |
|
690 QGeoPositionInfoSource *m_location; |
|
691 bool waitingForFix; |
|
692 QNetworkSession *m_session; |
|
693 ConnectivityHelper *m_connectivityHelper; |
|
694 }; |
|
695 |
|
696 class MapZoom: public QMainWindow |
|
697 { |
|
698 Q_OBJECT |
|
699 |
|
700 private: |
|
701 LightMaps *map; |
|
702 |
|
703 public: |
|
704 MapZoom() : |
|
705 QMainWindow(0) { |
|
706 map = new LightMaps(this); |
|
707 setCentralWidget(map); |
|
708 map->setFocus(); |
|
709 |
|
710 QAction *gpsAction = new QAction("Start GPS", this); |
|
711 QAction *osloAction = new QAction("&Oslo", this); |
|
712 QAction *berlinAction = new QAction("&Berlin", this); |
|
713 QAction *jakartaAction = new QAction("&Jakarta", this); |
|
714 QAction *nightModeAction = new QAction("Night Mode", this); |
|
715 |
|
716 nightModeAction->setCheckable(true); |
|
717 nightModeAction->setChecked(false); |
|
718 QAction *osmAction = new QAction("About OpenStreetMap", this); |
|
719 connect(gpsAction, SIGNAL(triggered()), SLOT(chooseGps())); |
|
720 connect(osloAction, SIGNAL(triggered()), SLOT(chooseOslo())); |
|
721 connect(berlinAction, SIGNAL(triggered()), SLOT(chooseBerlin())); |
|
722 connect(jakartaAction, SIGNAL(triggered()), SLOT(chooseJakarta())); |
|
723 connect(nightModeAction, SIGNAL(triggered()), map, SLOT(toggleNightMode())); |
|
724 connect(osmAction, SIGNAL(triggered()), SLOT(aboutOsm())); |
|
725 |
|
726 #if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM) || defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) |
|
727 menuBar()->addAction(gpsAction); |
|
728 menuBar()->addAction(osloAction); |
|
729 menuBar()->addAction(berlinAction); |
|
730 menuBar()->addAction(jakartaAction); |
|
731 menuBar()->addAction(nightModeAction); |
|
732 menuBar()->addAction(osmAction); |
|
733 #else |
|
734 QMenu *menu = menuBar()->addMenu("&Options"); |
|
735 menu->addAction(gpsAction); |
|
736 menu->addAction(osloAction); |
|
737 menu->addAction(berlinAction); |
|
738 menu->addAction(jakartaAction); |
|
739 menu->addSeparator(); |
|
740 menu->addAction(nightModeAction); |
|
741 menu->addAction(osmAction); |
|
742 #endif |
|
743 |
|
744 #if defined(Q_OS_WINCE) |
|
745 QAction *exitAction = new QAction("Exit", this); |
|
746 connect(exitAction, SIGNAL(triggered()), qApp, SLOT(quit())); |
|
747 menuBar()->addAction(exitAction); |
|
748 #endif |
|
749 } |
|
750 |
|
751 private slots: |
|
752 |
|
753 void chooseGps() { |
|
754 map->startPositioning(); |
|
755 } |
|
756 |
|
757 void chooseOslo() { |
|
758 map->stopPositioning(); |
|
759 map->setCenter(59.9138204, 10.7387413); |
|
760 } |
|
761 |
|
762 void chooseBerlin() { |
|
763 map->stopPositioning(); |
|
764 map->setCenter(52.52958999943302, 13.383053541183472); |
|
765 } |
|
766 |
|
767 void chooseJakarta() { |
|
768 map->stopPositioning(); |
|
769 map->setCenter(-6.211544, 106.845172); |
|
770 } |
|
771 |
|
772 void aboutOsm() { |
|
773 QDesktopServices::openUrl(QUrl("http://www.openstreetmap.org")); |
|
774 } |
|
775 }; |
|
776 |
|
777 #include "lightmaps.moc" |
|
778 |
|
779 int main(int argc, char **argv) |
|
780 { |
|
781 #if defined(Q_WS_X11) |
|
782 QApplication::setGraphicsSystem("raster"); |
|
783 #endif |
|
784 |
|
785 QApplication app(argc, argv); |
|
786 app.setApplicationName("LightMaps"); |
|
787 |
|
788 MapZoom w; |
|
789 w.setWindowTitle("OpenStreetMap"); |
|
790 #if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM) || defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) |
|
791 w.showMaximized(); |
|
792 #else |
|
793 w.resize(600, 450); |
|
794 w.show(); |
|
795 #endif |
|
796 |
|
797 return app.exec(); |
|
798 } |