changed: don't include event.h in stdafx.h
[xbmc:xbmc-antiquated.git] / xbmc / utils / Weather.cpp
1 /*
2  *      Copyright (C) 2005-2008 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "stdafx.h"
23 #include "Weather.h"
24 #include "FileSystem/ZipManager.h"
25 #include "FileSystem/RarManager.h"
26 #include "FileSystem/FileCurl.h"
27 #include "XMLUtils.h"
28 #include "Temperature.h"
29 #include "Network.h"
30 #include "Util.h"
31 #include "Application.h"
32 #include "GUISettings.h"
33 #include "GUIWindowManager.h"
34 #include "GUIDialogProgress.h"
35 #include "GUIDialogSelect.h"
36 #include "DateTime.h"
37 #include "LangInfo.h"
38 #include "LocalizeStrings.h"
39 #include "FileSystem/Directory.h"
40
41 using namespace std;
42 using namespace DIRECTORY;
43
44 #define CONTROL_BTNREFRESH  2
45 #define CONTROL_SELECTLOCATION 3
46 #define CONTROL_LABELLOCATION 10
47 #define CONTROL_LABELUPDATED 11
48 #define CONTROL_IMAGELOGO  101
49
50 #define CONTROL_IMAGENOWICON 21
51 #define CONTROL_LABELNOWCOND 22
52 #define CONTROL_LABELNOWTEMP 23
53 #define CONTROL_LABELNOWFEEL 24
54 #define CONTROL_LABELNOWUVID 25
55 #define CONTROL_LABELNOWWIND 26
56 #define CONTROL_LABELNOWDEWP 27
57 #define CONTROL_LABELNOWHUMI 28
58
59 #define CONTROL_STATICTEMP  223
60 #define CONTROL_STATICFEEL  224
61 #define CONTROL_STATICUVID  225
62 #define CONTROL_STATICWIND  226
63 #define CONTROL_STATICDEWP  227
64 #define CONTROL_STATICHUMI  228
65
66 #define CONTROL_LABELD0DAY  31
67 #define CONTROL_LABELD0HI  32
68 #define CONTROL_LABELD0LOW  33
69 #define CONTROL_LABELD0GEN  34
70 #define CONTROL_IMAGED0IMG  35
71
72 #define PARTNER_ID    "1004124588"   //weather.com partner id
73 #define PARTNER_KEY    "079f24145f208494"  //weather.com partner key
74
75 #define MAX_LOCATION   3
76 #define LOCALIZED_TOKEN_FIRSTID   370
77 #define LOCALIZED_TOKEN_LASTID   395
78 #define LOCALIZED_TOKEN_FIRSTID2 1396
79 #define LOCALIZED_TOKEN_LASTID2   1450
80 /*
81 FIXME'S
82 >strings are not centered
83 >weather.com dev account is mine not a general xbmc one
84 */
85
86 // USE THESE FOR ZIP
87 //#define WEATHER_BASE_PATH "special://temp/weather/"
88 //#define WEATHER_USE_ZIP 1
89 //#define WEATHER_USE_RAR 0
90 //#define WEATHER_SOURCE_FILE "special://xbmc/media/weather.zip"
91
92 // OR THESE FOR RAR
93 #define WEATHER_BASE_PATH "special://temp/weather/"
94 #define WEATHER_USE_ZIP 0
95 #define WEATHER_USE_RAR 1
96 #define WEATHER_SOURCE_FILE "special://xbmc/media/weather.rar"
97
98 CWeather g_weatherManager;
99
100 void CBackgroundWeatherLoader::GetInformation()
101 {
102   if (!g_guiSettings.GetBool("network.enableinternet"))
103     return;
104
105   // wait for the network
106   if (!g_application.getNetwork().IsAvailable(true))
107     return;
108
109   CWeather *callback = (CWeather *)m_callback;
110   // Download our weather
111   CLog::Log(LOGINFO, "WEATHER: Downloading weather");
112   XFILE::CFileCurl httpUtil;
113   CStdString strURL;
114
115   CStdString strSetting;
116   strSetting.Format("weather.areacode%i", callback->GetArea() + 1);
117   CStdString areaCode(callback->GetAreaCode(g_guiSettings.GetString(strSetting)));
118   strURL.Format("http://xoap.weather.com/weather/local/%s?cc=*&unit=m&dayf=4&prod=xoap&link=xoap&par=%s&key=%s",
119                 areaCode.c_str(), PARTNER_ID, PARTNER_KEY);
120   CStdString xml;
121   if (httpUtil.Get(strURL, xml))
122   {
123     CLog::Log(LOGINFO, "WEATHER: Weather download successful");
124     if (!callback->m_bImagesOkay)
125     {
126       CDirectory::Create(WEATHER_BASE_PATH);
127       if (WEATHER_USE_ZIP)
128         g_ZipManager.ExtractArchive(WEATHER_SOURCE_FILE, WEATHER_BASE_PATH);
129       else if (WEATHER_USE_RAR)
130         g_RarManager.ExtractArchive(WEATHER_SOURCE_FILE, WEATHER_BASE_PATH);
131       callback->m_bImagesOkay = true;
132     }
133     callback->LoadWeather(xml);
134   }
135   else
136     CLog::Log(LOGERROR, "WEATHER: Weather download failed!");
137 }
138
139 CWeather::CWeather(void) : CInfoLoader("weather")
140 {
141   m_bImagesOkay = false;
142
143   Reset();
144
145   srand(timeGetTime());
146 }
147
148 CWeather::~CWeather(void)
149 {
150 }
151
152 void CWeather::GetString(const TiXmlElement* pRootElement, const CStdString& strTagName, char* szValue, const CStdString& strDefaultValue)
153 {
154   strcpy(szValue, "");
155   const TiXmlNode *pChild = pRootElement->FirstChild(strTagName.c_str());
156   if (pChild && pChild->FirstChild())
157   {
158     CStdString strValue = pChild->FirstChild()->Value();
159     if (strValue.size() )
160     {
161       if (strValue != "-")
162         strcpy(szValue, strValue.c_str());
163     }
164   }
165   if (strlen(szValue) == 0)
166   {
167     strcpy(szValue, strDefaultValue.c_str());
168   }
169 }
170
171 void CWeather::GetInteger(const TiXmlElement* pRootElement, const CStdString& strTagName, int& iValue)
172 {
173   if (!XMLUtils::GetInt(pRootElement, strTagName.c_str(), iValue))
174     iValue = 0;
175 }
176
177 void CWeather::LocalizeOverviewToken(char *szToken, bool bAppendSpace)
178 {
179   // NOTE: This routine is case-sensitive.  Reason is std::less<CStdString> uses a case-sensitive
180   //       < operator.  Thus, some tokens may have to be duplicated in strings.xml (see drizzle vs Drizzle).
181   CStdString strLocStr = "";
182   CStdString token = szToken;
183   if (!token.IsEmpty())
184   {
185     ilocalizedTokens i;
186     i = m_localizedTokens.find(token);
187     if (i != m_localizedTokens.end())
188     {
189       strLocStr = g_localizeStrings.Get(i->second);
190     }
191   }
192   if (strLocStr == "")
193     strLocStr = szToken; //if not found, let fallback
194   if (bAppendSpace)
195     strLocStr += " ";     //append space if applicable
196   strcpy(szToken, strLocStr.c_str());
197 }
198
199 void CWeather::LocalizeOverview(char *szStr)
200 {
201   char loc[256];
202   char szToken[256];
203   int intOffset = 0;
204   char *pnt = NULL;
205   memset(loc, '\0', sizeof(loc));
206
207   while ((pnt = strstr(szStr + intOffset, " ")) != NULL)
208   {
209     //get the length of this token (everything before pnt)
210     int iTokenLen = (int)(strlen(szStr) - strlen(pnt) - intOffset);
211     strncpy(szToken, szStr + intOffset, iTokenLen); //extract the token
212     szToken[iTokenLen] = '\0';      //stick an end on it
213     LocalizeOverviewToken(szToken);     //localize
214     strcpy(loc + strlen(loc), szToken);    //add it to the end of loc
215     intOffset += iTokenLen + 1;      //update offset for next strstr search
216   }
217   strncpy(szToken, szStr + intOffset, strlen(szStr) - intOffset); //last word, copy the rest of the string
218   szToken[strlen(szStr) - intOffset] = '\0';     //stick an end on it
219   LocalizeOverviewToken(szToken);        //localize
220   strcpy(loc + strlen(loc), szToken);       //add it to the end of loc
221   strcpy(szStr, loc);           //copy loc over the original input string
222 }
223
224 // input param must be kmh
225 int CWeather::ConvertSpeed(int curSpeed)
226 {
227   switch (g_langInfo.GetSpeedUnit())
228   {
229   case CLangInfo::SPEED_UNIT_KMH:
230     break;
231   case CLangInfo::SPEED_UNIT_MPS:
232     curSpeed=(int)(curSpeed * (1000.0 / 3600.0) + 0.5);
233     break;
234   case CLangInfo::SPEED_UNIT_MPH:
235     curSpeed=(int)(curSpeed / (8.0 / 5.0));
236     break;
237   case CLangInfo::SPEED_UNIT_MPMIN:
238     curSpeed=(int)(curSpeed * (1000.0 / 3600.0) + 0.5*60);
239     break;
240   case CLangInfo::SPEED_UNIT_FTH:
241     curSpeed=(int)(curSpeed * 3280.8398888889f);
242     break;
243   case CLangInfo::SPEED_UNIT_FTMIN:
244     curSpeed=(int)(curSpeed * 54.6805555556f);
245     break;
246   case CLangInfo::SPEED_UNIT_FTS:
247     curSpeed=(int)(curSpeed * 0.911344f);
248     break;
249   case CLangInfo::SPEED_UNIT_KTS:
250     curSpeed=(int)(curSpeed * 0.5399568f);
251     break;
252   case CLangInfo::SPEED_UNIT_INCHPS:
253     curSpeed=(int)(curSpeed * 10.9361388889f);
254     break;
255   case CLangInfo::SPEED_UNIT_YARDPS:
256     curSpeed=(int)(curSpeed * 0.3037814722f);
257     break;
258   case CLangInfo::SPEED_UNIT_FPF:
259     curSpeed=(int)(curSpeed * 1670.25f);
260     break;
261   case CLangInfo::SPEED_UNIT_BEAUFORT:
262     {
263       float knot=(float)curSpeed * 0.5399568f; // to kts first
264       if(knot<=1.0) curSpeed=0;
265       if(knot>1.0 && knot<3.5) curSpeed=1;
266       if(knot>=3.5 && knot<6.5) curSpeed=2;
267       if(knot>=6.5 && knot<10.5) curSpeed=3;
268       if(knot>=10.5 && knot<16.5) curSpeed=4;
269       if(knot>=16.5 && knot<21.5) curSpeed=5;
270       if(knot>=21.5 && knot<27.5) curSpeed=6;
271       if(knot>=27.5 && knot<33.5) curSpeed=7;
272       if(knot>=33.5 && knot<40.5) curSpeed=8;
273       if(knot>=40.5 && knot<47.5) curSpeed=9;
274       if(knot>=47.5 && knot<55.5) curSpeed=10;
275       if(knot>=55.5 && knot<63.5) curSpeed=11;
276       if(knot>=63.5 && knot<74.5) curSpeed=12;
277       if(knot>=74.5 && knot<80.5) curSpeed=13;
278       if(knot>=80.5 && knot<89.5) curSpeed=14;
279       if(knot>=89.5) curSpeed=15;
280     }
281     break;
282   default:
283     assert(false);
284   }
285
286   return curSpeed;
287 }
288
289 bool CWeather::LoadWeather(const CStdString &weatherXML)
290 {
291   int iTmpInt;
292   char iTmpStr[256];
293   SYSTEMTIME time;
294
295   GetLocalTime(&time); //used when deciding what weather to grab for today
296
297   // Load in our tokens if necessary
298   if (!m_localizedTokens.size())
299     LoadLocalizedToken();
300
301   // load the xml file
302   TiXmlDocument xmlDoc;
303   if (!xmlDoc.Parse(weatherXML.c_str()))
304   {
305     CLog::Log(LOGERROR, "WEATHER: Unable to get data - invalid XML");
306     return false;
307   }
308
309   TiXmlElement *pRootElement = xmlDoc.RootElement();
310   if (!pRootElement)
311   {
312     CLog::Log(LOGERROR, "WEATHER: Unable to get data - invalid XML");
313     return false;
314   }
315
316   //if root element is 'error' display the error message
317   if (strcmp(pRootElement->Value(), "error") == 0)
318   {
319     char szCheckError[256];
320     GetString(pRootElement, "err", szCheckError, "Unknown Error"); //grab the error string
321     CLog::Log(LOGERROR, "WEATHER: Unable to get data: %s", szCheckError);
322     return false;
323   }
324
325   // location
326   TiXmlElement *pElement = pRootElement->FirstChildElement("loc");
327   if (pElement)
328   {
329     GetString(pElement, "dnam", m_szLocation[m_iCurWeather], "");
330   }
331
332   //current weather
333   pElement = pRootElement->FirstChildElement("cc");
334   if (pElement)
335   {
336     // Use the local date/time the file is parsed...
337     CDateTime time=CDateTime::GetCurrentDateTime();
338     CStdString strDateTime=time.GetAsLocalizedDateTime(false, false);
339     strcpy(m_szLastUpdateTime, strDateTime.c_str());
340
341     // ...and not the date/time from weather.com
342     //GetString(pElement, "lsup", m_szLastUpdateTime, "");
343
344     GetString(pElement, "icon", iTmpStr, ""); //string cause i've seen it return N/A
345     if (strcmp(iTmpStr, "N/A") == 0)
346     {
347       sprintf(m_szCurrentIcon, "%s128x128/na.png", WEATHER_BASE_PATH);
348     }
349     else
350       sprintf(m_szCurrentIcon, "%s128x128/%s.png", WEATHER_BASE_PATH, iTmpStr);
351
352     GetString(pElement, "t", m_szCurrentConditions, "");   //current condition
353     LocalizeOverview(m_szCurrentConditions);
354
355     GetInteger(pElement, "tmp", iTmpInt);    //current temp
356     CTemperature temp=CTemperature::CreateFromCelsius(iTmpInt);
357     sprintf(m_szCurrentTemperature, "%2.0f", temp.ToLocale());
358     GetInteger(pElement, "flik", iTmpInt);    //current 'Feels Like'
359     CTemperature tempFlik=CTemperature::CreateFromCelsius(iTmpInt);
360     sprintf(m_szCurrentFeelsLike, "%2.0f", tempFlik.ToLocale());
361
362     TiXmlElement *pNestElement = pElement->FirstChildElement("wind"); //current wind
363     if (pNestElement)
364     {
365       GetInteger(pNestElement, "s", iTmpInt);   //current wind strength
366       iTmpInt = ConvertSpeed(iTmpInt);    //convert speed if needed
367       GetString(pNestElement, "t", iTmpStr, "N");  //current wind direction
368
369       //From <dir eg NW> at <speed> km/h   g_localizeStrings.Get(407)
370       //This is a bit untidy, but i'm fed up with localization and string formats :)
371       CStdString szWindFrom = g_localizeStrings.Get(407);
372       CStdString szWindAt = g_localizeStrings.Get(408);
373       CStdString szCalm = g_localizeStrings.Get(1410);
374
375       // get speed unit
376       char szUnitSpeed[5];
377       strncpy(szUnitSpeed, g_langInfo.GetSpeedUnitString().c_str(), 5);
378       szUnitSpeed[4] = '\0';
379
380       if (strcmp(iTmpStr,"CALM") == 0)
381         sprintf(m_szCurrentWind, "%s", szCalm.c_str());
382       else
383         sprintf(m_szCurrentWind, "%s %s %s %i %s",
384               szWindFrom.GetBuffer(szWindFrom.GetLength()), iTmpStr,
385               szWindAt.GetBuffer(szWindAt.GetLength()), iTmpInt, szUnitSpeed);
386     }
387
388     GetInteger(pElement, "hmid", iTmpInt);    //current humidity
389     sprintf(m_szCurrentHumidity, "%i%%", iTmpInt);
390
391     pNestElement = pElement->FirstChildElement("uv"); //current UV index
392     if (pNestElement)
393     {
394       GetInteger(pNestElement, "i", iTmpInt);
395       GetString(pNestElement, "t", iTmpStr, "");
396       LocalizeOverviewToken(iTmpStr, false);
397       sprintf(m_szCurrentUVIndex, "%i %s", iTmpInt, iTmpStr);
398     }
399
400     GetInteger(pElement, "dewp", iTmpInt);    //current dew point
401     CTemperature dewPoint=CTemperature::CreateFromCelsius(iTmpInt);
402     sprintf(m_szCurrentDewPoint, "%2.0f", dewPoint.ToLocale());
403   }
404   //future forcast
405   pElement = pRootElement->FirstChildElement("dayf");
406   if (pElement)
407   {
408     TiXmlElement *pOneDayElement = pElement->FirstChildElement("day");;
409     if (pOneDayElement)
410     {
411       for (int i = 0; i < NUM_DAYS; i++)
412       {
413         const char *attr = pOneDayElement->Attribute("t");
414         if (attr)
415         {
416           strcpy(m_dfForcast[i].m_szDay, attr);
417           LocalizeDay(m_dfForcast[i].m_szDay);
418         }
419
420         GetString(pOneDayElement, "hi", iTmpStr, ""); //string cause i've seen it return N/A
421         if (strcmp(iTmpStr, "N/A") == 0)
422           strcpy(m_dfForcast[i].m_szHigh, "");
423         else
424         {
425           CTemperature temp=CTemperature::CreateFromCelsius(atoi(iTmpStr));
426           sprintf(m_dfForcast[i].m_szHigh, "%2.0f", temp.ToLocale());
427         }
428
429         GetString(pOneDayElement, "low", iTmpStr, "");
430         if (strcmp(iTmpStr, "N/A") == 0)
431           strcpy(m_dfForcast[i].m_szHigh, "");
432         else
433         {
434           CTemperature temp=CTemperature::CreateFromCelsius(atoi(iTmpStr));
435           sprintf(m_dfForcast[i].m_szLow, "%2.0f", temp.ToLocale());
436         }
437
438         TiXmlElement *pDayTimeElement = pOneDayElement->FirstChildElement("part"); //grab the first day/night part (should be day)
439         if (pDayTimeElement)
440         {
441           if (i == 0 && (time.wHour < 7 || time.wHour >= 19)) //weather.com works on a 7am to 7pm basis so grab night if its late in the day
442             pDayTimeElement = pDayTimeElement->NextSiblingElement("part");
443
444           GetString(pDayTimeElement, "icon", iTmpStr, ""); //string cause i've seen it return N/A
445           if (strcmp(iTmpStr, "N/A") == 0)
446             sprintf(m_dfForcast[i].m_szIcon, "%s128x128/na.png", WEATHER_BASE_PATH);
447           else
448             sprintf(m_dfForcast[i].m_szIcon, "%s128x128/%s.png", WEATHER_BASE_PATH, iTmpStr);
449
450           GetString(pDayTimeElement, "t", m_dfForcast[i].m_szOverview, "");
451           LocalizeOverview(m_dfForcast[i].m_szOverview);
452         }
453
454         pOneDayElement = pOneDayElement->NextSiblingElement("day");
455         if (!pOneDayElement)
456           break; // No more days, break out
457       }
458     }
459   }
460   return true;
461 }
462
463 //convert weather.com day strings into localized string id's
464 void CWeather::LocalizeDay(char *szDay)
465 {
466   CStdString strLocDay;
467
468   if (strcmp(szDay, "Monday") == 0)   //monday is localized string 11
469     strLocDay = g_localizeStrings.Get(11);
470   else if (strcmp(szDay, "Tuesday") == 0)
471     strLocDay = g_localizeStrings.Get(12);
472   else if (strcmp(szDay, "Wednesday") == 0)
473     strLocDay = g_localizeStrings.Get(13);
474   else if (strcmp(szDay, "Thursday") == 0)
475     strLocDay = g_localizeStrings.Get(14);
476   else if (strcmp(szDay, "Friday") == 0)
477     strLocDay = g_localizeStrings.Get(15);
478   else if (strcmp(szDay, "Saturday") == 0)
479     strLocDay = g_localizeStrings.Get(16);
480   else if (strcmp(szDay, "Sunday") == 0)
481     strLocDay = g_localizeStrings.Get(17);
482   else
483     strLocDay = "";
484
485   strcpy(szDay, strLocDay.GetBuffer(strLocDay.GetLength()));
486 }
487
488
489 void CWeather::LoadLocalizedToken()
490 {
491   // We load the english strings in to get our tokens
492   CStdString strLanguagePath = "special://xbmc/language/English/strings.xml";
493   
494   TiXmlDocument xmlDoc;
495   if (!xmlDoc.LoadFile(strLanguagePath) || !xmlDoc.RootElement())
496   {
497     CLog::Log(LOGERROR, "Weather: unable to load %s: %s at line %d", strLanguagePath.c_str(), xmlDoc.ErrorDesc(), xmlDoc.ErrorRow());
498     return;
499   }
500
501   CStdString strEncoding;
502   XMLUtils::GetEncoding(&xmlDoc, strEncoding);
503
504   TiXmlElement* pRootElement = xmlDoc.RootElement();
505   if (pRootElement->Value() != CStdString("strings"))
506     return;
507
508   const TiXmlElement *pChild = pRootElement->FirstChildElement();
509   while (pChild)
510   {
511     CStdString strValue = pChild->Value();
512     if (strValue == "string")
513     { // Load new style language file with id as attribute
514       const char* attrId = pChild->Attribute("id");
515       if (attrId && !pChild->NoChildren())
516       {
517         DWORD dwID = atoi(attrId);
518         if ((LOCALIZED_TOKEN_FIRSTID <= dwID && dwID <= LOCALIZED_TOKEN_LASTID) ||
519             (LOCALIZED_TOKEN_FIRSTID2 <= dwID && dwID <= LOCALIZED_TOKEN_LASTID2))
520         {
521           CStdString utf8Label;
522           if (strEncoding.IsEmpty()) // Is language file utf8?
523             utf8Label=pChild->FirstChild()->Value();
524           else
525             g_charsetConverter.stringCharsetToUtf8(strEncoding, pChild->FirstChild()->Value(), utf8Label);
526
527           if (!utf8Label.IsEmpty())
528             m_localizedTokens.insert(make_pair(utf8Label, dwID));
529         }
530       }
531     }
532     pChild = pChild->NextSiblingElement();
533   }
534 }
535
536 bool CWeather::GetSearchResults(const CStdString &strSearch, CStdString &strResult)
537 {
538   // Check to see if the user entered a weather.com code
539   if (strSearch.size() == 8)
540   {
541     strResult = "";
542     int i = 0;
543     for (i = 0; i < 4; ++i)
544     {
545       strResult += toupper(strSearch[i]);
546       if (!isalpha(strSearch[i]))
547         break;
548     }
549     if (i == 4)
550     {
551       for ( ; i < 8; ++i)
552       {
553         strResult += strSearch[i];
554         if (!isdigit(strSearch[i]))
555           break;
556       }
557       if (i == 8)
558       {
559         return true; // match
560       }
561     }
562     // no match, wipe string
563     strResult = "";
564   }
565
566   CGUIDialogSelect *pDlgSelect = (CGUIDialogSelect*)m_gWindowManager.GetWindow(WINDOW_DIALOG_SELECT);
567   CGUIDialogProgress *pDlgProgress = (CGUIDialogProgress*)m_gWindowManager.GetWindow(WINDOW_DIALOG_PROGRESS);
568
569   //do the download
570   CStdString strURL;
571   CStdString strXML;
572   XFILE::CFileCurl httpUtil;
573
574   if (pDlgProgress)
575   {
576     pDlgProgress->SetHeading(410);       //"Accessing Weather.com"
577     pDlgProgress->SetLine(0, 194);       //"Searching"
578     pDlgProgress->SetLine(1, strSearch);
579     pDlgProgress->SetLine(2, "");
580     pDlgProgress->StartModal();
581     pDlgProgress->Progress();
582   }
583
584   strURL.Format("http://xoap.weather.com/search/search?where=%s", strSearch);
585
586   if (!httpUtil.Get(strURL, strXML))
587   {
588     if (pDlgProgress)
589       pDlgProgress->Close();
590     return false;
591   }
592
593   //some select dialog init stuff
594   if (!pDlgSelect)
595   {
596     if (pDlgProgress)
597       pDlgProgress->Close();
598     return false;
599   }
600
601   pDlgSelect->SetHeading(396); //"Select Location"
602   pDlgSelect->Reset();
603
604   ///////////////////////////////
605   // load the xml file
606   ///////////////////////////////
607   TiXmlDocument xmlDoc;
608   xmlDoc.Parse(strXML.c_str());
609   if (xmlDoc.Error())
610     return false;
611
612   TiXmlElement *pRootElement = xmlDoc.RootElement();
613   if (pRootElement)
614   {
615     CStdString strItemTmp;
616     TiXmlElement *pElement = pRootElement->FirstChildElement("loc");
617     while (pElement)
618     {
619       if (!pElement->NoChildren())
620       {
621         strItemTmp.Format("%s - %s", pElement->Attribute("id"), pElement->FirstChild()->Value());
622         pDlgSelect->Add(strItemTmp);
623       }
624       pElement = pElement->NextSiblingElement("loc");
625     }
626   }
627
628   if (pDlgProgress)
629     pDlgProgress->Close();
630
631   pDlgSelect->EnableButton(TRUE);
632   pDlgSelect->SetButtonLabel(222); //'Cancel' button returns to weather settings
633   pDlgSelect->DoModal();
634
635   if (pDlgSelect->GetSelectedLabel() < 0)
636   {
637     if (pDlgSelect->IsButtonPressed())
638     {
639       pDlgSelect->Close(); //close the select dialog and return to weather settings
640       return true;
641     }
642   }
643
644   //copy the selected code into the settings
645   if (pDlgSelect->GetSelectedLabel() >= 0)
646     strResult = pDlgSelect->GetSelectedLabelText();
647
648   if (pDlgProgress)
649     pDlgProgress->Close();
650
651   return true;
652 }
653
654 const char *CWeather::BusyInfo(DWORD dwInfo)
655 {
656   if (dwInfo == WEATHER_IMAGE_CURRENT_ICON)
657   {
658     sprintf(m_szNAIcon,"%s128x128/na.png", WEATHER_BASE_PATH);
659     return m_szNAIcon;
660   }
661   return CInfoLoader::BusyInfo(dwInfo);
662 }
663
664 const char *CWeather::TranslateInfo(DWORD dwInfo)
665 {
666   if (dwInfo == WEATHER_LABEL_CURRENT_COND) return m_szCurrentConditions;
667   else if (dwInfo == WEATHER_IMAGE_CURRENT_ICON) return m_szCurrentIcon;
668   else if (dwInfo == WEATHER_LABEL_CURRENT_TEMP) return m_szCurrentTemperature;
669   else if (dwInfo == WEATHER_LABEL_CURRENT_FEEL) return m_szCurrentFeelsLike;
670   else if (dwInfo == WEATHER_LABEL_CURRENT_UVID) return m_szCurrentUVIndex;
671   else if (dwInfo == WEATHER_LABEL_CURRENT_WIND) return m_szCurrentWind;
672   else if (dwInfo == WEATHER_LABEL_CURRENT_DEWP) return m_szCurrentDewPoint;
673   else if (dwInfo == WEATHER_LABEL_CURRENT_HUMI) return m_szCurrentHumidity;
674   else if (dwInfo == WEATHER_LABEL_LOCATION) return m_szLocation[m_iCurWeather];
675   return "";
676 }
677
678 DWORD CWeather::TimeToNextRefreshInMs()
679 { // 30 minutes
680   return 30 * 60 * 1000;
681 }
682
683 CStdString CWeather::GetAreaCity(const CStdString &codeAndCity) const
684 {
685   CStdString areaCode(codeAndCity);
686   int pos = areaCode.Find(" - ");
687   if (pos >= 0)
688     areaCode = areaCode.Mid(pos + 3);
689   return areaCode;
690 }
691
692 CStdString CWeather::GetAreaCode(const CStdString &codeAndCity) const
693 {
694   CStdString areaCode(codeAndCity);
695   int pos = areaCode.Find(" - ");
696   if (pos >= 0)
697     areaCode = areaCode.Left(pos);
698   return areaCode;
699 }
700
701 char *CWeather::GetLocation(int iLocation)
702 {
703   if (strlen(m_szLocation[iLocation]) == 0)
704   {
705     CStdString setting;
706     setting.Format("weather.areacode%i", iLocation + 1);
707     strcpy(m_szLocation[iLocation], GetAreaCity(g_guiSettings.GetString(setting)).c_str());
708   }
709   return m_szLocation[iLocation];
710 }
711
712 void CWeather::Reset()
713 {
714   strcpy(m_szLastUpdateTime, "");
715   strcpy(m_szCurrentIcon,"");
716   strcpy(m_szCurrentConditions, "");
717   strcpy(m_szCurrentTemperature, "");
718   strcpy(m_szCurrentFeelsLike, "");
719
720   strcpy(m_szCurrentWind, "");
721   strcpy(m_szCurrentHumidity, "");
722   strcpy(m_szCurrentUVIndex, "");
723   strcpy(m_szCurrentDewPoint, "");
724
725   //loop here as well
726   for (int i = 0; i < NUM_DAYS; i++)
727   {
728     strcat(m_dfForcast[i].m_szIcon,"");
729     strcpy(m_dfForcast[i].m_szOverview, "");
730     strcpy(m_dfForcast[i].m_szDay, "");
731     strcpy(m_dfForcast[i].m_szHigh, "");
732     strcpy(m_dfForcast[i].m_szLow, "");
733   }
734   for (int i = 0; i < MAX_LOCATION; i++)
735   {
736     strcpy(m_szLocation[i], "");
737   }
738 }
739
740 bool CWeather::IsFetched()
741 {
742   // call GetInfo() to make sure that we actually start up
743   GetInfo(0);
744   return (0 != *m_szLastUpdateTime);
745 }
746