Initial check in
[apps/poi-yelp.git] / MainApp.cpp
1 #include <QNetworkReply>
2 #include <QNetworkRequest>
3 #include <QUrl>
4 #include <QUrlQuery>
5 #include <QNetworkProxy>
6 #include <QTreeWidget>
7 #include <iostream>
8 #include <error.h>
9 #include <json-c/json.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #define __STDC_FORMAT_MACROS
13 #include <inttypes.h>
14
15 #include "MainApp.h"
16 #include "Business.h"
17 #include "InfoPanel.h"
18 #include "ClickableLabel.h"
19 #include "Keyboard.h"
20 #include "traces.h"
21
22 #define DEFAULT_TEXT        "Select your destination with Yelp !"
23 #define URL_AUTH            "https://api.yelp.com/oauth2/token"
24 #define URL_AUTOCOMPLETE    "https://api.yelp.com/v3/autocomplete"
25 #define URL_SEARCH          "https://api.yelp.com/v3/businesses/search"
26
27 #define BIG_BUFFER_SIZE     (1024*1024)
28 #define LEFT_OFFSET         28
29 #define FONT_SIZE_LINEDIT   20
30 #define FONT_SIZE_LIST      18
31 #define TEXT_INPUT_WIDTH    800
32 #define SEARCH_BTN_SIZE     105
33 #define SPACER              15
34 #define WIDGET_WIDTH        (SEARCH_BTN_SIZE + SPACER + TEXT_INPUT_WIDTH)
35 #define DISPLAY_WIDTH       TEXT_INPUT_WIDTH
36 #define DISPLAY_HEIGHT      480
37 #define COMPLETE_W_WITH_KB    1080
38 #define COMPLETE_H_WITH_KB    1488
39 #define RESULT_ITEM_HEIGHT  80
40 #define MARGINS             25
41 #define AGL_REFRESH_DELAY   75 /* milliseconds */
42
43 #define SCROLLBAR_STYLE \
44 "QScrollBar:vertical {" \
45 "    border: 2px solid grey;" \
46 "    background: gray;" \
47 "    width: 45px;" \
48 "}"
49
50 using namespace std;
51
52 MainApp::MainApp():QMainWindow(Q_NULLPTR, Qt::FramelessWindowHint),
53     networkManager(this),searchBtn(QIcon(tr(":/images/loupe-90.png")), tr(""), this),
54     lineEdit(this),keyboard(QRect(0, 688, COMPLETE_W_WITH_KB, 720), this),
55     mutex(QMutex::Recursive),token(""),currentSearchingText(""),currentSearchedText(""),
56     pSearchReply(NULL),pInfoPanel(NULL),pResultList(NULL),currentLatitude(0.0),currentLongitude(0.0),
57     navicoreSession(0),currentIndex(0),fontId(-1),isInfoScreen(false),
58     isInputDisplayed(false),isKeyboard(false),isAglNavi(false)
59 {
60     //this->setAttribute(Qt::WA_TranslucentBackground);
61     this->setStyleSheet("border: none;");
62
63     searchBtn.setStyleSheet("border: none; color: #FFFFFF;");
64     searchBtn.setMinimumSize(QSize(SEARCH_BTN_SIZE, SEARCH_BTN_SIZE));
65     searchBtn.setIconSize(searchBtn.size());
66     searchBtn.setGeometry(QRect(LEFT_OFFSET, 0, searchBtn.width(), searchBtn.height()));
67
68     lineEdit.setStyleSheet("border: none; color: #FFFFFF;");
69     lineEdit.setMinimumSize(QSize(TEXT_INPUT_WIDTH, SEARCH_BTN_SIZE));
70
71     lineEdit.setPlaceholderText(QString(DEFAULT_TEXT));
72     font = lineEdit.font();
73     font.setPointSize(FONT_SIZE_LINEDIT);
74     lineEdit.setFont(font);
75     lineEdit.setTextMargins(MARGINS/2, 0, 0, 0);
76     lineEdit.installEventFilter(this);
77     lineEdit.setGeometry(QRect(LEFT_OFFSET + searchBtn.width() + SPACER, 0, lineEdit.width(), lineEdit.height()));
78     lineEdit.setVisible(false);
79
80     /* We might need a Japanese font: */
81     QFile fontFile(":/fonts/DroidSansJapanese.ttf");
82     if (!fontFile.open(QIODevice::ReadOnly))
83     {
84         TRACE_ERROR("failed to open font file");
85     }
86     else
87     {
88         QByteArray fontData = fontFile.readAll();
89         fontId = QFontDatabase::addApplicationFontFromData(fontData);
90         if (fontId < 0)
91         {
92             TRACE_ERROR("QFontDatabase::addApplicationFontFromData failed");
93         }
94     }
95
96     /* Check if "AGL_NAVI" env variable is set. If yes, we must notify
97      * AGL environment when surface needs to be resized */
98     if (getenv("AGL_NAVI"))
99         isAglNavi = true;
100
101     connect(this, SIGNAL(allSessionsGotSignal()), this, SLOT(allSessionsGot()));
102     connect(this, SIGNAL(positionGotSignal()), this, SLOT(positionGot()));
103     connect(this, SIGNAL(allRoutesGotSignal()), this, SLOT(allRoutesGot()));
104     connect(this, SIGNAL(routeCreatedSignal()), this, SLOT(routeCreated()));
105
106     this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
107     this->setStyleSheet("background-image: url(:/images/AGL_POI_Background.png);");
108     this->show();
109 }
110
111 MainApp::~MainApp()
112 {
113     mutex.lock();
114     if (fontId >= 0)
115         QFontDatabase::removeApplicationFont(fontId);
116
117     searchBtn.disconnect();
118     lineEdit.disconnect();
119     networkManager.disconnect();
120     keyboard.disconnect();
121
122     delete pSearchReply;
123     delete pInfoPanel;
124     mutex.unlock();
125 }
126
127 void MainApp::searchBtnClicked()
128 {
129     isInputDisplayed = !isInputDisplayed;
130     TRACE_DEBUG("isInputDisplayed = %d", isInputDisplayed);
131     DisplayLineEdit(isInputDisplayed);
132 }
133
134 void MainApp::DisplayLineEdit(bool display)
135 {
136     mutex.lock();
137
138     this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
139
140     if (display)
141     {
142         lineEdit.setVisible(true);
143         lineEdit.setFocus();
144     }
145     else
146     {
147         if (pResultList)
148         {
149             pResultList->removeEventFilter(this);
150             delete pResultList;
151             pResultList = NULL;
152         }
153         if (pInfoPanel)
154         {
155             delete pInfoPanel;
156             pInfoPanel = NULL;
157         }
158         lineEdit.setText(tr(""));
159         lineEdit.setVisible(false);
160     }
161     isInputDisplayed = display;
162
163     mutex.unlock();
164 }
165
166 void MainApp::UpdateAglSurfaces()
167 {
168     char cmd[1024];
169
170     TRACE_DEBUG("handle AGL demo surfaces (new surface is bigger)");
171     snprintf(cmd, 1023, "/usr/bin/LayerManagerControl set surface $SURFACE_ID_CLIENT source region 0 0 %d %d",
172         this->width(), this->height());
173     TRACE_DEBUG("%s", cmd);
174     system(cmd);
175     snprintf(cmd, 1023, "/usr/bin/LayerManagerControl set surface $SURFACE_ID_CLIENT destination region $CLIENT_X $CLIENT_Y %d %d",
176         this->width(), this->height());
177     TRACE_DEBUG("%s", cmd);
178     system(cmd);
179 }
180
181 void MainApp::DisplayResultList(bool display, bool RefreshDisplay)
182 {
183     mutex.lock();
184
185     if (display)
186     {
187         if (!pResultList)
188         {
189             pResultList = new QTreeWidget(this);
190             pResultList->setStyleSheet("border: none; color: #FFFFFF;");
191             pResultList->setRootIsDecorated(false);
192             pResultList->setEditTriggers(QTreeWidget::NoEditTriggers);
193             pResultList->setSelectionBehavior(QTreeWidget::SelectRows);
194             pResultList->setFrameStyle(QFrame::Box | QFrame::Plain);
195             pResultList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
196             //pResultList->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
197             pResultList->setAttribute(Qt::WA_AcceptTouchEvents);
198             pResultList->verticalScrollBar()->setStyleSheet(SCROLLBAR_STYLE);
199             pResultList->header()->hide();
200             //font.setPointSize(FONT_SIZE_LIST);
201             //pResultList->setFont(font);
202             pResultList->installEventFilter(this);
203         }
204
205         pResultList->setGeometry(QRect(   LEFT_OFFSET+searchBtn.width()+SPACER, searchBtn.height()+SPACER,
206                                         DISPLAY_WIDTH, DISPLAY_HEIGHT));
207         if (RefreshDisplay)
208         {
209             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
210         }
211         pResultList->setVisible(true);
212         pResultList->setFocus();
213     }
214     else
215     {
216         if (pResultList)
217         {
218             pResultList->removeEventFilter(this);
219             pResultList->deleteLater();
220             pResultList = NULL;
221         }
222
223         lineEdit.setFocus();
224
225         if (RefreshDisplay)
226         {
227             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
228         }
229     }
230
231     mutex.unlock();
232 }
233
234 void MainApp::textChanged(const QString & text)
235 {
236     TRACE_INFO("New text is: %s", qPrintable(text));
237
238     /* do not handle text input if info panel is displayed: */
239     if (pInfoPanel) return;
240
241     mutex.lock();
242
243     delete pSearchReply;    /* cancel current search */
244     pSearchReply = NULL;
245
246     if (text.length() == 0) /* if empty text -> no search */
247     {
248         DisplayResultList(false);
249         mutex.unlock();
250         return;
251     }
252
253     /* if text is the same as previous search -> no need to search again */
254     if (text == currentSearchedText)
255     {
256         DisplayResultList(true);
257         FillResultList(Businesses, currentIndex);
258         mutex.unlock();
259         return;
260     }
261     this->currentSearchingText = text;
262
263     /* we need to know our current position */
264     std::vector<int32_t> Params;
265     Params.push_back(naviapi::NAVICORE_LONGITUDE);
266     Params.push_back(naviapi::NAVICORE_LATITUDE);
267     naviapi.getPosition(Params);
268
269     mutex.unlock();
270 }
271
272 void MainApp::textAdded(const QString & text)
273 {
274     mutex.lock();
275     lineEdit.setText(lineEdit.text() + text);
276     mutex.unlock();
277 }
278
279 void MainApp::keyPressed(int key)
280 {
281     mutex.lock();
282     if (key == '\b') /* backspace */
283     {
284         int len = lineEdit.text().length();
285         if (len > 0)
286             lineEdit.setText(lineEdit.text().remove(len-1, 1));
287     }
288     mutex.unlock();
289 }
290
291 void MainApp::itemClicked()
292 {
293     mutex.lock();
294     if (isInfoScreen)
295     {
296         DisplayInformation(true, false);
297     }
298     else
299     {
300         SetDestination();
301         DisplayLineEdit(false);
302     }
303     mutex.unlock();
304 }
305
306 void MainApp::ParseJsonBusinessList(const char* buf, std::vector<Business> & Output)
307 {
308     json_object *jobj = json_tokener_parse(buf);
309     if (!jobj)
310     {
311         TRACE_ERROR("json_tokener_parse failed");
312         cerr << "json_tokener_parse failed: " << buf << endl;
313         return;
314     }
315
316     json_object_object_foreach(jobj, key, val)
317     {
318         (void)key;
319         json_object *value;
320
321         if (json_object_get_type(val) == json_type_array)
322         {
323             TRACE_DEBUG_JSON("an array was found");
324
325             if(json_object_object_get_ex(jobj, "businesses", &value))
326             {
327                 TRACE_DEBUG_JSON("an business was found");
328
329                 int arraylen = json_object_array_length(value);
330
331                 for (int i = 0; i < arraylen; i++)
332                 {
333                     Business NewBusiness;
334
335                     json_object* medi_array_obj, *medi_array_obj_elem;
336                     medi_array_obj = json_object_array_get_idx(value, i);
337                     if (medi_array_obj)
338                     {
339                         if (json_object_object_get_ex(medi_array_obj, "rating", &medi_array_obj_elem))
340                         {
341                             NewBusiness.Rating = json_object_get_double(medi_array_obj_elem);
342                             TRACE_DEBUG_JSON("got Rating : %f", NewBusiness.Rating);
343                         }
344
345                         if (json_object_object_get_ex(medi_array_obj, "distance", &medi_array_obj_elem))
346                         {
347                             NewBusiness.Distance = json_object_get_double(medi_array_obj_elem);
348                             TRACE_DEBUG_JSON("got Distance : %f", NewBusiness.Distance);
349                         }
350
351                         if (json_object_object_get_ex(medi_array_obj, "review_count", &medi_array_obj_elem))
352                         {
353                             NewBusiness.ReviewCount = json_object_get_int(medi_array_obj_elem);
354                             TRACE_DEBUG_JSON("got ReviewCount : %u", NewBusiness.ReviewCount);
355                         }
356
357                         if (json_object_object_get_ex(medi_array_obj, "name", &medi_array_obj_elem))
358                         {
359                             NewBusiness.Name = QString(json_object_get_string(medi_array_obj_elem));
360                             TRACE_DEBUG_JSON("got Name : %s", qPrintable(NewBusiness.Name));
361                         }
362
363                         if (json_object_object_get_ex(medi_array_obj, "image_url", &medi_array_obj_elem))
364                         {
365                             NewBusiness.ImageUrl = QString(json_object_get_string(medi_array_obj_elem));
366                             TRACE_DEBUG_JSON("got ImageUrl : %s", qPrintable(NewBusiness.ImageUrl));
367                         }
368
369                         if (json_object_object_get_ex(medi_array_obj, "phone", &medi_array_obj_elem))
370                         {
371                             NewBusiness.Phone = QString(json_object_get_string(medi_array_obj_elem));
372                             TRACE_DEBUG_JSON("got Phone : %s", qPrintable(NewBusiness.Phone));
373                         }
374
375                         if (json_object_object_get_ex(medi_array_obj, "coordinates", &medi_array_obj_elem))
376                         {
377                             json_object *value2;
378
379                             TRACE_DEBUG_JSON("coordinates were found");
380
381                             if(json_object_object_get_ex(medi_array_obj_elem, "latitude", &value2))
382                             {
383                                 NewBusiness.Latitude = json_object_get_double(value2);
384                                 TRACE_DEBUG_JSON("got Latitude : %f", NewBusiness.Latitude);
385                             }
386
387                             if(json_object_object_get_ex(medi_array_obj_elem, "longitude", &value2))
388                             {
389                                 NewBusiness.Longitude = json_object_get_double(value2);
390                                 TRACE_DEBUG_JSON("got Longitude : %f", NewBusiness.Longitude);
391                             }
392                         }
393
394                         if (json_object_object_get_ex(medi_array_obj, "location", &medi_array_obj_elem))
395                         {
396                             json_object *value2;
397
398                             TRACE_DEBUG_JSON("a location was found");
399
400                             /* TODO: how do we deal with address2 and address3 ? */
401                             if(json_object_object_get_ex(medi_array_obj_elem, "address1", &value2))
402                             {
403                                 NewBusiness.Address = QString(json_object_get_string(value2));
404                                 TRACE_DEBUG_JSON("got Address : %s", qPrintable(NewBusiness.Address));
405                             }
406
407                             if(json_object_object_get_ex(medi_array_obj_elem, "city", &value2))
408                             {
409                                 NewBusiness.City = QString(json_object_get_string(value2));
410                                 TRACE_DEBUG_JSON("got City : %s", qPrintable(NewBusiness.City));
411                             }
412
413                             if(json_object_object_get_ex(medi_array_obj_elem, "state", &value2))
414                             {
415                                 NewBusiness.State = QString(json_object_get_string(value2));
416                                 TRACE_DEBUG_JSON("got State : %s", qPrintable(NewBusiness.State));
417                             }
418
419                             if(json_object_object_get_ex(medi_array_obj_elem, "zip_code", &value2))
420                             {
421                                 NewBusiness.ZipCode = QString(json_object_get_string(value2));
422                                 TRACE_DEBUG_JSON("got ZipCode : %s", qPrintable(NewBusiness.ZipCode));
423                             }
424
425                             if(json_object_object_get_ex(medi_array_obj_elem, "country", &value2))
426                             {
427                                 NewBusiness.Country = QString(json_object_get_string(value2));
428                                 TRACE_DEBUG_JSON("got Country : %s", qPrintable(NewBusiness.Country));
429                             }
430                         }
431
432                         /* TODO: parse categories */
433
434                         /* Add business in our list: */
435                         Businesses.push_back(NewBusiness);
436                     }
437                 }
438             }
439         }
440     }
441
442     json_object_put(jobj);
443 }
444
445 bool MainApp::eventFilter(QObject *obj, QEvent *ev)
446 {
447     bool ret = false;
448
449     mutex.lock();
450
451     if (obj == pResultList)
452     {
453         //TRACE_DEBUG("ev->type() = %d", (int)ev->type());
454
455         if (ev->type() == QEvent::KeyPress)
456         {
457             bool consumed = false;
458             int key = static_cast<QKeyEvent*>(ev)->key();
459             TRACE_DEBUG("key pressed (%d)", key);
460             switch (key) {
461                 case Qt::Key_Enter:
462                 case Qt::Key_Return:
463                     TRACE_DEBUG("enter or return");
464                     if (isInfoScreen)
465                     {
466                         DisplayInformation(true);
467                     }
468                     else
469                     {
470                         SetDestination();
471                         DisplayLineEdit(false);
472                     }
473                     consumed = true;
474                     break;
475
476                 case Qt::Key_Escape:
477                     TRACE_DEBUG("escape");
478                     DisplayResultList(false);
479                     consumed = true;
480                     break;
481
482                 case Qt::Key_Up:
483                 case Qt::Key_Down:
484                 case Qt::Key_Home:
485                 case Qt::Key_End:
486                 case Qt::Key_PageUp:
487                 case Qt::Key_PageDown:
488                     TRACE_DEBUG("arrows");
489                     break;
490
491                 default:
492                     TRACE_DEBUG("default");
493                     lineEdit.event(ev);
494                     break;
495             }
496
497             mutex.unlock();
498             return consumed;
499         }
500     }
501     else if (obj == &lineEdit)
502     {
503         if (pInfoPanel && ev->type() == QEvent::KeyPress)
504         {
505             switch(static_cast<QKeyEvent*>(ev)->key())
506             {
507                 case Qt::Key_Escape:
508                     TRACE_DEBUG("Escape !");
509                     DisplayInformation(false, false);
510                     DisplayResultList(true);
511                     FillResultList(Businesses, currentIndex);
512                     break;
513                 case Qt::Key_Enter:
514                 case Qt::Key_Return:
515                     TRACE_DEBUG("Go !");
516                     SetDestination(currentIndex);
517                     DisplayLineEdit(false);
518                     break;
519                 default: break;
520             }
521         }
522     }
523     else
524     {
525         ret = QMainWindow::eventFilter(obj, ev);
526     }
527     mutex.unlock();
528     return ret;
529 }
530
531 void MainApp::resizeEvent(QResizeEvent* event)
532 {
533     QMainWindow::resizeEvent(event);
534     if (isAglNavi)
535     {
536         QTimer::singleShot(AGL_REFRESH_DELAY, Qt::CoarseTimer, this, SLOT(UpdateAglSurfaces()));
537     }
538 }
539
540 void MainApp::SetDestination(int index)
541 {
542     mutex.lock();
543
544     /* if pResultList exists, take the selected index
545      * otherwise, take the index given as parameter */
546     if (pResultList)
547     {
548         QList<QTreeWidgetItem *> SelectedItems = pResultList->selectedItems();
549         if (SelectedItems.size() > 0)
550         {
551             /* select the first selected item : */
552             index = pResultList->indexOfTopLevelItem(*SelectedItems.begin());
553         }
554     }
555
556     TRACE_DEBUG("index is: %d", index);
557
558     /* retrieve the coordinates of this item : */
559     this->destinationLatitude = Businesses[index].Latitude;
560     this->destinationLongitude = Businesses[index].Longitude;
561
562     naviapi.getAllRoutes();
563
564     mutex.unlock();
565 }
566
567 void MainApp::DisplayInformation(bool display, bool RefreshDisplay)
568 {
569     mutex.lock();
570     if (display)
571     {
572         /* pResultList must exist, so that we can retrieve the selected index: */
573         if (!pResultList)
574         {
575             TRACE_ERROR("pResultList is null");
576             mutex.unlock();
577             return;
578         }
579
580         QList<QTreeWidgetItem *> SelectedItems = pResultList->selectedItems();
581         if (SelectedItems.size() <= 0)
582         {
583             TRACE_ERROR("no item is selected");
584             mutex.unlock();
585             return;
586         }
587
588         /* select the first selected item : */
589         currentIndex = pResultList->indexOfTopLevelItem(*SelectedItems.begin());
590
591         /* Resize window: */
592         DisplayResultList(false, false);
593
594         /* Display info for the selected item: */
595         QRect rect( LEFT_OFFSET+searchBtn.width()+SPACER, searchBtn.height()+SPACER,
596                     DISPLAY_WIDTH, DISPLAY_HEIGHT);
597         pInfoPanel = new InfoPanel(this, Businesses[currentIndex], rect);
598
599         if (RefreshDisplay)
600         {
601             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
602         }
603
604         connect(pInfoPanel->getGoButton(),      SIGNAL(clicked(bool)), this, SLOT(goClicked()));
605         connect(pInfoPanel->getCancelButton(),  SIGNAL(clicked(bool)), this, SLOT(cancelClicked()));
606     }
607     else
608     {
609         if (pInfoPanel)
610         {
611             pInfoPanel->getGoButton()->disconnect();
612             pInfoPanel->getCancelButton()->disconnect();
613             delete pInfoPanel;
614             pInfoPanel = NULL;
615         }
616         lineEdit.setFocus();
617
618         if (RefreshDisplay)
619         {
620             this->setGeometry(QRect(this->pos().x(), this->pos().y(), COMPLETE_W_WITH_KB, COMPLETE_H_WITH_KB));
621         }
622     }
623
624     mutex.unlock();
625 }
626
627 void MainApp::networkReplySearch(QNetworkReply* reply)
628 {
629     char buf[BIG_BUFFER_SIZE];
630     int buflen;
631
632     mutex.lock();
633
634     /* memorize the text which gave this result: */
635     currentSearchedText = lineEdit.text();
636
637         if (reply->error() == QNetworkReply::NoError)
638         {
639             // we only handle this callback if it matches the last search request:
640             if (reply != pSearchReply)
641             {
642                 TRACE_INFO("this reply is already too late (or about a different network request)");
643                 mutex.unlock();
644                 return;
645             }
646
647         buflen = reply->read(buf, BIG_BUFFER_SIZE-1);
648             buf[buflen] = '\0';
649
650             if (buflen == 0)
651             {
652                 mutex.unlock();
653                 return;
654             }
655
656
657
658             currentIndex = 0;
659             Businesses.clear();
660             ParseJsonBusinessList(buf, Businesses);
661             DisplayResultList(true);
662             FillResultList(Businesses);
663     }
664     else
665     {
666         fprintf(stderr,"POI: reply error network please check to poikey and system time (adjusted?)\n");
667     }
668
669     mutex.unlock();
670 }
671
672 /* pResultList must be allocated at this point ! */
673 int MainApp::FillResultList(vector<Business> & list, int focusIndex)
674 {
675     int nbElem = 0;
676
677     mutex.lock();
678
679     pResultList->setUpdatesEnabled(false);
680     pResultList->clear();
681
682     /* filling the dropdown menu: */
683     for (vector<Business>::iterator it = list.begin(); it != list.end(); it++)
684     {
685         /*  workaround to avoid entries with wrong coordinates returned by Yelp: */
686         if (IsCoordinatesConsistent(*it) == false)
687         {
688             list.erase(it--);
689             continue;
690         }
691
692         QTreeWidgetItem * item = new QTreeWidgetItem(pResultList);
693
694         ClickableLabel *label = new ClickableLabel("<b>"+(*it).Name+
695             "</b><br>"+(*it).Address+", "+(*it).City+", "+(*it).State+
696             " "+(*it).ZipCode+", "+(*it).Country, pResultList);
697         label->setTextFormat(Qt::RichText);
698         font.setPointSize(FONT_SIZE_LIST);
699         label->setFont(font);
700         label->setIndent(MARGINS);
701         label->setAttribute(Qt::WA_AcceptTouchEvents);
702         item->setSizeHint(0, QSize(TEXT_INPUT_WIDTH, RESULT_ITEM_HEIGHT));
703         pResultList->setItemWidget(item, 0, label);
704         connect(label, SIGNAL(clicked()), this, SLOT(itemClicked()));
705
706         //item->setText(0, (*it).Name);
707
708         if (nbElem == focusIndex)
709         {
710             pResultList->setCurrentItem(item);
711         }
712         nbElem++;
713     }
714
715     pResultList->setUpdatesEnabled(true);
716
717     mutex.unlock();
718     return nbElem;
719 }
720
721 /* Well... some of the POI returned by Yelp have coordinates which are
722  * completely inconsistent with the distance at which the POI is
723  * supposed to be.
724  * https://github.com/Yelp/yelp-fusion/issues/104
725  * Let's skip them for the moment: */
726 #define PI 3.14159265
727 #define EARTH_RADIUS 6371000
728 static inline double toRadians(double a) { return a * PI / 180.0; }
729 bool MainApp::IsCoordinatesConsistent(Business & business)
730 {
731     double lat1 = toRadians(currentLatitude);
732     double lon1 = toRadians(currentLongitude);
733     double lat2 = toRadians(business.Latitude);
734     double lon2 = toRadians(business.Longitude);
735     double x = (lon2 - lon1) * cos((lat1 + lat2)/2);
736     double y = lat2 - lat1;
737     double DistanceFromCoords = EARTH_RADIUS * sqrt(pow(x, 2) + pow(y, 2));
738
739     /* if calculated distance is not between +/- 10% of the announced
740      * distance -> skip this POI: */
741     if (DistanceFromCoords < business.Distance * 0.9 ||
742         DistanceFromCoords > business.Distance * 1.1)
743     {
744         TRACE_ERROR("Announced distance: %f, calculated distance: %f", business.Distance, DistanceFromCoords);
745         return false;
746     }
747
748     return true;
749 }
750 /* end of workaround */
751
752 bool MainApp::CheckNaviApi(int argc, char *argv[])
753 {
754     bool ret = naviapi.connect(argc, argv, this);
755
756     if (ret == true)
757     {
758         naviapi.getAllSessions();
759     }
760
761     return ret;
762 }
763
764 int MainApp::AuthenticatePOI(const QString & CredentialsFile)
765 {
766     char buf[512];
767     QString AppId;
768     QString AppSecret;
769     QString ProxyHostName;
770     QString PortStr;
771     QString User;
772     QString Password;
773     int portnum;
774
775     /* First, read AppId and AppSecret from credentials file: */
776     FILE* filep = fopen(qPrintable(CredentialsFile), "r");
777     if (!filep)
778     {
779         fprintf(stderr,"Failed to open credentials file \"%s\": %m", qPrintable(CredentialsFile));
780         return -1;
781     }
782
783     if (!fgets(buf, 512, filep))
784     {
785         fprintf(stderr,"Failed to read AppId from credentials file \"%s\"", qPrintable(CredentialsFile));
786         fclose(filep);
787         return -1;
788     }
789     if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
790         buf[strlen(buf)-1] = '\0';
791     AppId = QString(buf);
792
793     if (!fgets(buf, 512, filep))
794     {
795         fprintf(stderr,"Failed to read AppSecret from credentials file \"%s\"", qPrintable(CredentialsFile));
796         fclose(filep);
797         return -1;
798     }
799     if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
800         buf[strlen(buf)-1] = '\0';
801     AppSecret = QString(buf);
802
803     QNetworkProxy proxy;
804
805     //ProxyHostName
806     if (!fgets(buf, 512, filep))
807     {
808         TRACE_INFO("Failed to read ProxyHostName from credentials file \"%s\"", qPrintable(CredentialsFile));
809     }
810     else
811     {
812         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
813             buf[strlen(buf)-1] = '\0';
814         ProxyHostName = QString(buf);
815         ProxyHostName.replace(0, 14, tr(""));
816
817         //Port
818         if (!fgets(buf, 512, filep))
819         {
820             TRACE_ERROR("Failed to read Port from credentials file \"%s\"", qPrintable(CredentialsFile));
821             fclose(filep);
822             return -1;
823         }
824         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
825             buf[strlen(buf)-1] = '\0';
826         PortStr = QString(buf);
827         PortStr.replace(0, 5, tr(""));
828         portnum = PortStr.toInt();
829
830         //User
831         if (!fgets(buf, 512, filep))
832         {
833             TRACE_ERROR("Failed to read User from credentials file \"%s\"", qPrintable(CredentialsFile));
834             fclose(filep);
835             return -1;
836         }
837         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
838             buf[strlen(buf)-1] = '\0';
839         User = QString(buf);
840         User.replace(0, 5, tr(""));
841
842         //Password
843         if (!fgets(buf, 512, filep))
844         {
845             TRACE_ERROR("Failed to read Password from credentials file \"%s\"", qPrintable(CredentialsFile));
846             fclose(filep);
847             return -1;
848         }
849         if (strlen(buf) > 0 && buf[strlen(buf)-1] == '\n')
850             buf[strlen(buf)-1] = '\0';
851         Password = QString(buf);
852         Password.replace(0, 9, tr(""));
853
854         proxy.setType(QNetworkProxy::HttpProxy);
855         proxy.setHostName(qPrintable(ProxyHostName));
856         proxy.setPort(portnum);
857         proxy.setUser(qPrintable(User));
858         proxy.setPassword(qPrintable(Password));
859         QNetworkProxy::setApplicationProxy(proxy);
860     }
861
862     fclose(filep);
863
864     TRACE_INFO("Found credentials");
865
866     /* Then, send a HTTP request to get the token and wait for answer (synchronously): */
867
868         token = AppSecret;
869         return 0;
870 }
871
872 int MainApp::StartMonitoringUserInput()
873 {
874     connect(&searchBtn, SIGNAL(clicked(bool)), this, SLOT(searchBtnClicked()));
875     connect(&lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(textChanged(const QString &)));
876     connect(&networkManager, SIGNAL(finished(QNetworkReply*)), this, SLOT(networkReplySearch(QNetworkReply*)));
877     connect(&keyboard, SIGNAL(keyClicked(const QString &)), this, SLOT(textAdded(const QString &)));
878     connect(&keyboard, SIGNAL(specialKeyClicked(int)), this, SLOT(keyPressed(int)));
879     return 1;
880 }
881
882 void MainApp::SetWayPoints(uint32_t myRoute)
883 {
884     /* set the destination : */
885     naviapi::Waypoint destWp(this->destinationLatitude, this->destinationLongitude);
886     std::vector<naviapi::Waypoint> myWayPoints;
887     myWayPoints.push_back(destWp);
888     naviapi.setWaypoints(navicoreSession, myRoute, true, myWayPoints);
889
890     naviapi.calculateRoute(navicoreSession, myRoute);
891
892     /* reset search: */
893     currentSearchingText = tr("");
894     currentSearchedText = tr("");
895     currentIndex = 0;
896     Businesses.clear();
897 }
898
899 void MainApp::goClicked()
900 {
901     TRACE_DEBUG("Go clicked !");
902     SetDestination(currentIndex);
903     DisplayLineEdit(false);
904 }
905
906 void MainApp::cancelClicked()
907 {
908     TRACE_DEBUG("Cancel clicked !");
909     DisplayInformation(false, false);
910     DisplayResultList(true, false);
911     FillResultList(Businesses, currentIndex);
912 }
913
914 void MainApp::getAllSessions_reply(const std::map< uint32_t, std::string >& allSessions)
915 {
916     mutex.lock();
917
918     if (allSessions.empty())
919     {
920         TRACE_ERROR("Error: could not find an instance of Navicore");
921         mutex.unlock();
922         return;
923     }
924
925     this->navicoreSession = allSessions.begin()->first;
926
927     TRACE_INFO("Current session: %d", this->navicoreSession);
928
929     mutex.unlock();
930
931     emit allSessionsGotSignal();
932 }
933
934
935 void MainApp::getPosition_reply(std::map< int32_t, naviapi::variant > position)
936 {
937     mutex.lock();
938
939     std::map< int32_t, naviapi::variant >::iterator it;
940     for (it = position.begin(); it != position.end(); it++)
941     {
942         if (it->first == naviapi::NAVICORE_LATITUDE)
943         {
944             currentLatitude = it->second._double;
945         }
946         else if (it->first == naviapi::NAVICORE_LONGITUDE)
947         {
948             currentLongitude = it->second._double;
949         }
950     }
951
952     TRACE_INFO("Current position: %f, %f", currentLatitude, currentLongitude);
953
954     mutex.unlock();
955
956     emit positionGotSignal();
957 }
958
959 void MainApp::getAllRoutes_reply(std::vector< uint32_t > allRoutes)
960 {
961     mutex.lock();
962
963     uint32_t routeHandle = 0;
964
965     if (allRoutes.size() != 0)
966     {
967         routeHandle = allRoutes[0];
968     }
969
970     this->currentRouteHandle = routeHandle;
971
972     mutex.unlock();
973
974     emit allRoutesGotSignal();
975 }
976
977 void MainApp::createRoute_reply(uint32_t routeHandle)
978 {
979     mutex.lock();
980
981     this->currentRouteHandle = routeHandle;
982
983     mutex.unlock();
984
985     emit routeCreatedSignal();
986 }
987
988 void MainApp::allSessionsGot()
989 {
990     mutex.lock();
991
992     // nothing to do
993
994     mutex.unlock();
995 }
996
997 void MainApp::positionGot()
998 {
999     mutex.lock();
1000
1001     /* let's generate a search request : */
1002     QString myUrlStr = URL_SEARCH + tr("?") + tr("term=") + currentSearchingText +
1003         tr("&latitude=") + QString::number(currentLatitude) +
1004         tr("&longitude=") + QString::number(currentLongitude);
1005
1006     TRACE_DEBUG("URL: %s", qPrintable(myUrlStr));
1007
1008     QUrl myUrl = QUrl(myUrlStr);
1009     QNetworkRequest req(myUrl);
1010     req.setRawHeader(QByteArray("Authorization"), (tr("bearer ") + token).toLocal8Bit());
1011
1012     /* Then, send a HTTP request to get the token and wait for answer (synchronously): */
1013
1014     pSearchReply = networkManager.get(req);
1015
1016     mutex.unlock();
1017 }
1018
1019 void MainApp::allRoutesGot()
1020 {
1021     mutex.lock();
1022
1023     /* check if a route already exists, if not create it : */
1024     if (this->currentRouteHandle == 0)
1025     {
1026         naviapi.createRoute(navicoreSession);
1027     }
1028     else
1029     {
1030         naviapi.pauseSimulation(navicoreSession);
1031         naviapi.setSimulationMode(navicoreSession, false);
1032         naviapi.cancelRouteCalculation(navicoreSession, this->currentRouteHandle);
1033         sleep(1);
1034
1035         SetWayPoints(this->currentRouteHandle);
1036     }
1037
1038     mutex.unlock();
1039 }
1040
1041 void MainApp::routeCreated()
1042 {
1043     mutex.lock();
1044
1045     SetWayPoints(this->currentRouteHandle);
1046
1047     mutex.unlock();
1048 }
1049