b347f955d20509b183dafd6136accfcef7a357d4
[gpssportsniffer] / satellitedialog.cpp
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 examples 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 "satellitedialog.h"
42
43  #include <QtAlgorithms>
44
45  #include <QApplication>
46  #include <QPushButton>
47  #include <QVBoxLayout>
48  #include <QHBoxLayout>
49  #include <QLabel>
50  #include <QTimer>
51  #include <QPainter>
52  #include <QSize>
53  #include <QRectF>
54  #include <QSizePolicy>
55  #include <QAction>
56  #include <QMenuBar>
57
58  #include "qgeopositioninfosource.h"
59  #include "qgeosatelliteinfosource.h"
60  #include "qgeopositioninfo.h"
61  #include "qgeosatelliteinfo.h"
62
63  class SatelliteWidget : public QWidget
64  {
65  public:
66      SatelliteWidget(QWidget *parent,
67                      SatelliteDialog::Ordering ordering,
68                      SatelliteDialog::StrengthScaling scaling);
69
70      void clearSatellites();
71      void satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &list);
72      void satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &list);
73
74      QSize sizeHint() const;
75
76      SatelliteDialog::Ordering ordering() const;
77      void setOrdering(SatelliteDialog::Ordering ordering);
78
79      SatelliteDialog::StrengthScaling strengthScaling() const;
80      void setStrengthScaling(SatelliteDialog::StrengthScaling scaling);
81
82  protected:
83      void paintEvent(QPaintEvent *event);
84
85  private:
86      static const int numBars = 32;
87      static const int gapWidth = 1;
88      static const int barWidth = 3;
89      static const int spanWidth = gapWidth + barWidth;
90      static const int borderOffset = 4;
91      static const int legendTextOffset = 5;
92
93      int textHeight;
94      int legendHeight;
95
96      QColor inUseColor;
97      QColor inViewColor;
98
99      SatelliteDialog::Ordering m_ordering;
100      SatelliteDialog::StrengthScaling m_scaling;
101      int m_maximumSignalStrength;
102
103      void updateSatelliteList();
104      void paintSatellite(QPainter &painter, const QRect &bounds, int index);
105      void paintLegend(QPainter &painter, const QRect &bounds);
106
107      QList<QGeoSatelliteInfo> satellitesInView;
108      QList<QGeoSatelliteInfo> satellitesInUse;
109      QList<QPair<QGeoSatelliteInfo, bool> > satellites;
110  };
111
112  SatelliteWidget::SatelliteWidget(QWidget *parent,
113                                   SatelliteDialog::Ordering ordering,
114                                   SatelliteDialog::StrengthScaling scaling) : QWidget(parent),
115          m_ordering(ordering),
116          m_scaling(scaling)
117  {
118      QPainter painter(this);
119      textHeight = painter.fontMetrics().height();
120      legendHeight = borderOffset + textHeight + 2;
121
122      inViewColor = QColor(192, 192, 255);
123      inUseColor = QColor(64, 64, 255);
124  }
125
126  SatelliteDialog::Ordering SatelliteWidget::ordering() const
127  {
128      return m_ordering;
129  }
130
131  void SatelliteWidget::setOrdering(SatelliteDialog::Ordering ordering)
132  {
133      if (ordering != m_ordering) {
134          m_ordering = ordering;
135          updateSatelliteList();
136      }
137  }
138
139  SatelliteDialog::StrengthScaling SatelliteWidget::strengthScaling() const
140  {
141      return m_scaling;
142  }
143
144  void SatelliteWidget::setStrengthScaling(SatelliteDialog::StrengthScaling scaling)
145  {
146      if (scaling != m_scaling) {
147          m_scaling = scaling;
148          updateSatelliteList();
149      }
150  }
151
152  bool sortByPrn(const QGeoSatelliteInfo &s1, const QGeoSatelliteInfo &s2)
153  {
154      return s1.prnNumber() < s2.prnNumber();
155  }
156
157  bool sortBySignalStrength(const QGeoSatelliteInfo &s1, const QGeoSatelliteInfo &s2)
158  {
159      return s1.signalStrength() < s2.signalStrength();
160  }
161
162  void SatelliteWidget::clearSatellites()
163  {
164      satellitesInView.clear();
165      satellitesInUse.clear();
166      updateSatelliteList();
167  }
168
169  void SatelliteWidget::satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &list)
170  {
171      satellitesInView = list;
172      qSort(satellitesInView.begin(), satellitesInView.end(), sortByPrn);
173      updateSatelliteList();
174  }
175
176  void SatelliteWidget::satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &list)
177  {
178      satellitesInUse = list;
179      qSort(satellitesInUse.begin(), satellitesInUse.end(), sortByPrn);
180      updateSatelliteList();
181  }
182
183  bool sortPairsByPrn(const QPair<QGeoSatelliteInfo, bool> &p1, const QPair<QGeoSatelliteInfo, bool> &p2)
184  {
185      return sortByPrn(p1.first, p2.first);
186  }
187
188  bool sortPairsBySignalStrength(const QPair<QGeoSatelliteInfo, bool> &p1, const QPair<QGeoSatelliteInfo, bool> &p2)
189  {
190      return sortBySignalStrength(p1.first, p2.first);
191  }
192
193  void SatelliteWidget::updateSatelliteList()
194  {
195      satellites.clear();
196
197      int useSize = satellitesInUse.size();
198      int viewSize = satellitesInView.size();
199
200      if ((useSize == 0) && (viewSize == 0)) {
201          update();
202          return;
203      }
204
205      for (int i = 0; i < useSize; ++i) {
206          if (satellitesInUse.at(i).signalStrength() != 0)
207              satellites << QPair<QGeoSatelliteInfo, bool>(satellitesInUse.at(i), true);
208      }
209
210      QList<QGeoSatelliteInfo>::iterator end = satellitesInUse.end();
211
212      for (int i = 0; i < viewSize; ++i) {
213          if (satellitesInView.at(i).signalStrength() == 0)
214              continue;
215
216          QList<QGeoSatelliteInfo>::iterator j =
217              qBinaryFind(
218                  satellitesInUse.begin(),
219                  end,
220                  satellitesInView.at(i),
221                  sortByPrn
222              );
223
224          if (j == end) {
225              satellites << QPair<QGeoSatelliteInfo, bool>(satellitesInView.at(i), false);
226          }
227      }
228
229      int satSize = satellites.size();
230
231      if (m_ordering == SatelliteDialog::OrderByPrnNumber) {
232          qSort(satellites.begin(), satellites.end(), sortPairsByPrn);
233          m_maximumSignalStrength = 0;
234          for (int i = 0; i < satSize; ++i) {
235              if (satellites.at(i).first.signalStrength() > m_maximumSignalStrength)
236                  m_maximumSignalStrength = satellites.at(i).first.signalStrength();
237          }
238
239      } else {
240          qSort(satellites.begin(), satellites.end(), sortPairsBySignalStrength);
241          m_maximumSignalStrength = satellites.at(satSize - 1).first.signalStrength();
242      }
243
244      update();
245  }
246
247  void SatelliteWidget::paintEvent(QPaintEvent * /*event*/)
248  {
249      QPainter painter(this);
250      painter.setRenderHint(QPainter::Antialiasing);
251
252      QRect satBounds = QRect(rect().x() + borderOffset,
253                              rect().y() + borderOffset,
254                              rect().width() - 2 * borderOffset,
255                              rect().height() - legendHeight - 2 * borderOffset);
256
257      painter.setPen(QApplication::palette().color(QPalette::WindowText));
258      painter.setBrush(QApplication::palette().color(QPalette::Base));
259      painter.drawRect(satBounds);
260
261      int size = satellites.size();
262      for (int i = 0; i < size; ++i) {
263          paintSatellite(painter, satBounds, i);
264      }
265
266      QRect legendBounds = QRect(rect().x() + borderOffset,
267                                 rect().height() - legendHeight,
268                                 rect().width() - 2 * borderOffset,
269                                 legendHeight);
270
271      paintLegend(painter, legendBounds);
272  }
273
274  void SatelliteWidget::paintSatellite(QPainter &painter, const QRect &bounds, int index)
275  {
276      int bars = numBars;
277      if (m_ordering == SatelliteDialog::OrderBySignalStrength)
278          bars = satellites.size();
279
280      double pixelsPerUnit = (double) bounds.width() / (double)(bars * spanWidth + gapWidth);
281      double spanPixels = pixelsPerUnit * spanWidth;
282      double gapPixels = pixelsPerUnit * gapWidth;
283      double barPixels = pixelsPerUnit * barWidth;
284
285      painter.setPen(QApplication::palette().color(QPalette::WindowText));
286      if (!satellites.at(index).second) {
287          painter.setBrush(inViewColor);
288      } else {
289          painter.setBrush(inUseColor);
290      }
291
292      int maximum = 100;
293      if (m_scaling == SatelliteDialog::ScaleToMaxAvailable) {
294          maximum = m_maximumSignalStrength;
295      }
296
297      int i = index;
298      if (m_ordering == SatelliteDialog::OrderByPrnNumber)
299          i = satellites.at(index).first.prnNumber() - 1;
300
301      double height = ((double) satellites.at(index).first.signalStrength() / (double) maximum) * bounds.height();
302
303      QRectF r(bounds.x() + gapPixels + i * spanPixels, bounds.y() + bounds.height() - 1 - height, barPixels, height);
304
305      painter.drawRect(r);
306  }
307
308  void SatelliteWidget::paintLegend(QPainter &painter, const QRect &bounds)
309  {
310      double halfWidth = (double) bounds.width() / 2.0;
311
312      double keyX = bounds.x() + 1;
313      double textX = keyX + legendHeight + 2 + legendTextOffset;
314      double y = bounds.y() + 1;
315      double keyWidth = legendHeight - 2 - borderOffset;
316      double textWidth = halfWidth  - legendHeight - 3 - legendTextOffset;
317      double height = legendHeight - 2 - borderOffset;
318
319      QRectF viewKeyRect(keyX, y, keyWidth, height);
320      QRectF viewTextRect(textX, y, textWidth, height);
321      QRectF useKeyRect(keyX + halfWidth, y, keyWidth, height);
322      QRectF useTextRect(textX + halfWidth, y, textWidth, height);
323
324      painter.setPen(QApplication::palette().color(QPalette::WindowText));
325
326      painter.setBrush(inViewColor);
327      painter.drawRect(viewKeyRect);
328
329      painter.setBrush(inUseColor);
330      painter.drawRect(useKeyRect);
331
332      painter.setPen(QApplication::palette().color(QPalette::Text));
333      painter.drawText(viewTextRect, Qt::AlignLeft, tr("In View"));
334      painter.drawText(useTextRect, Qt::AlignLeft, tr("In Use"));
335  }
336
337  QSize SatelliteWidget::sizeHint() const
338  {
339      return QSize(parentWidget()->width(), parentWidget()->width() / 2 + legendHeight);
340  }
341
342  SatelliteDialog::SatelliteDialog(QWidget *parent,
343                                   int noSatelliteTimeoutSeconds,
344                                   ExitBehaviour exitBehaviour,
345                                   Ordering ordering,
346                                   StrengthScaling scaling) : QDialog(parent),
347          m_noSatelliteTimeoutSeconds(noSatelliteTimeoutSeconds),
348          m_exitBehaviour(exitBehaviour),
349          m_ordering(ordering),
350          m_scaling(scaling)
351  {
352      noSatelliteTimer = new QTimer(this);
353      noSatelliteTimer->setInterval(m_noSatelliteTimeoutSeconds * 1000);
354      noSatelliteTimer->setSingleShot(true);
355
356      connect(noSatelliteTimer, SIGNAL(timeout()), this, SLOT(noSatelliteTimeout()));
357
358      satelliteWidget = new SatelliteWidget(this, ordering, scaling);
359
360      QLabel *titleLabel = new QLabel(tr("Satellite Signal Strength"));
361      titleLabel->setAlignment(Qt::AlignCenter | Qt::AlignBottom);
362
363      QVBoxLayout *mainLayout = new QVBoxLayout;
364      mainLayout->addWidget(titleLabel);
365      mainLayout->addWidget(satelliteWidget);
366
367  #if defined(Q_OS_SYMBIAN) || defined(Q_OS_WINCE_WM) || defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6)
368      QAction *switchAction = new QAction(tr("Switch"), this);
369      switchAction->setSoftKeyRole(QAction::PositiveSoftKey);
370
371      connect(switchAction, SIGNAL(triggered()), this, SLOT(switchButtonClicked()));
372      addAction(switchAction);
373
374      QAction *cancelAction = new QAction(tr("Cancel"), this);
375      cancelAction->setSoftKeyRole(QAction::NegativeSoftKey);
376
377      connect(cancelAction, SIGNAL(triggered()), this, SLOT(reject()));
378      addAction(cancelAction);
379
380      QMenuBar *menuBar = new QMenuBar(this);
381      menuBar->addAction(switchAction);
382      menuBar->addAction(cancelAction);
383
384  #if defined(Q_OS_WINCE)
385      menuBar->setDefaultAction(cancelAction);
386  #endif
387
388  #else
389      cancelButton = new QPushButton(tr("Cancel"));
390      connect(cancelButton, SIGNAL(clicked()), this, SLOT(reject()));
391
392      switchButton = new QPushButton(tr("Switch"));
393      connect(switchButton, SIGNAL(clicked()), this, SLOT(switchButtonClicked()));
394
395      QHBoxLayout *buttonLayout = new QHBoxLayout;
396      buttonLayout->addWidget(switchButton);
397      buttonLayout->addStretch(1);
398      buttonLayout->addWidget(cancelButton);
399
400      mainLayout->addLayout(buttonLayout);
401  #endif
402
403      setLayout(mainLayout);
404
405  /*#if defined(Q_OS_SYMBIAN)
406      // workaround for QTBUG-4771
407      oldTitle = windowTitle();
408      connect(this, SIGNAL(finished(int)), this, SLOT(restoreWindowTitle()));
409  #endif*/
410
411      setWindowTitle(tr("Waiting for GPS fix"));
412
413      setModal(true);
414  }
415
416  /*#if defined(Q_OS_SYMBIAN)
417  // workaround for QTBUG-4771
418  void SatelliteDialog::restoreWindowTitle()
419  {
420      setWindowTitle(oldTitle);
421  }
422  #endif*/
423
424  void SatelliteDialog::connectSources(QGeoPositionInfoSource *posSource, QGeoSatelliteInfoSource *satSource)
425  {
426      connect(posSource, SIGNAL(positionUpdated(const QGeoPositionInfo &)),
427              this, SLOT(positionUpdated(const QGeoPositionInfo &)));
428
429      connect(satSource, SIGNAL(satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &)),
430              this, SLOT(satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &)));
431      connect(satSource, SIGNAL(satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &)),
432              this, SLOT(satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &)));
433  }
434
435  void SatelliteDialog::switchButtonClicked()
436  {
437      SatelliteDialog::Ordering o = ordering();
438      if (o == SatelliteDialog::OrderByPrnNumber)
439          setOrdering(SatelliteDialog::OrderBySignalStrength);
440      else if (o == SatelliteDialog::OrderBySignalStrength)
441          setOrdering(SatelliteDialog::OrderByPrnNumber);
442  }
443
444  SatelliteDialog::ExitBehaviour SatelliteDialog::exitBehaviour() const
445  {
446      return m_exitBehaviour;
447  }
448
449  void SatelliteDialog::setExitBehaviour(SatelliteDialog::ExitBehaviour exitBehaviour)
450  {
451      m_exitBehaviour = exitBehaviour;
452  }
453
454  SatelliteDialog::Ordering SatelliteDialog::ordering() const
455  {
456      return satelliteWidget->ordering();
457  }
458
459  void SatelliteDialog::setOrdering(SatelliteDialog::Ordering ordering)
460  {
461      satelliteWidget->setOrdering(ordering);
462  }
463
464  SatelliteDialog::StrengthScaling SatelliteDialog::strengthScaling() const
465  {
466      return satelliteWidget->strengthScaling();
467  }
468
469  void SatelliteDialog::setStrengthScaling(SatelliteDialog::StrengthScaling scaling)
470  {
471      satelliteWidget->setStrengthScaling(scaling);
472  }
473
474  void SatelliteDialog::noSatelliteTimeout()
475  {
476      satelliteWidget->clearSatellites();
477  }
478
479  void SatelliteDialog::positionUpdated(const QGeoPositionInfo &/*pos*/)
480  {
481      if (m_exitBehaviour == ExitOnFixOrCancel) {
482          accept();
483      }
484  }
485
486  void SatelliteDialog::satellitesInViewUpdated(const QList<QGeoSatelliteInfo> &list)
487  {
488      noSatelliteTimer->stop();
489      satelliteWidget->satellitesInViewUpdated(list);
490      noSatelliteTimer->start();
491  }
492
493  void SatelliteDialog::satellitesInUseUpdated(const QList<QGeoSatelliteInfo> &list)
494  {
495      noSatelliteTimer->stop();
496      satelliteWidget->satellitesInUseUpdated(list);
497      noSatelliteTimer->start();
498  }