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