summaryrefslogtreecommitdiff
path: root/protocols/Weather/src
diff options
context:
space:
mode:
authorGeorge Hazan <ghazan@miranda.im>2019-03-02 12:32:44 +0300
committerGeorge Hazan <ghazan@miranda.im>2019-03-02 12:32:55 +0300
commit931a7dc1ac0dbc7e6c1083583ced915e572f5b47 (patch)
tree9fe9a6448d44030e26aa7107ce16044ed413e0d0 /protocols/Weather/src
parentdd7d9954042254e66e3bbbec7195c6be8b1a0663 (diff)
all protocols (even virtual ones) moved to the Protocols folder
Diffstat (limited to 'protocols/Weather/src')
-rw-r--r--protocols/Weather/src/resource.h175
-rw-r--r--protocols/Weather/src/stdafx.cxx18
-rw-r--r--protocols/Weather/src/stdafx.h547
-rw-r--r--protocols/Weather/src/version.h13
-rw-r--r--protocols/Weather/src/weather.cpp231
-rw-r--r--protocols/Weather/src/weather_addstn.cpp432
-rw-r--r--protocols/Weather/src/weather_contacts.cpp480
-rw-r--r--protocols/Weather/src/weather_conv.cpp637
-rw-r--r--protocols/Weather/src/weather_data.cpp439
-rw-r--r--protocols/Weather/src/weather_http.cpp157
-rw-r--r--protocols/Weather/src/weather_icons.cpp64
-rw-r--r--protocols/Weather/src/weather_info.cpp240
-rw-r--r--protocols/Weather/src/weather_ini.cpp592
-rw-r--r--protocols/Weather/src/weather_mwin.cpp364
-rw-r--r--protocols/Weather/src/weather_opt.cpp601
-rw-r--r--protocols/Weather/src/weather_popup.cpp428
-rw-r--r--protocols/Weather/src/weather_svcs.cpp375
-rw-r--r--protocols/Weather/src/weather_update.cpp607
-rw-r--r--protocols/Weather/src/weather_userinfo.cpp358
19 files changed, 6758 insertions, 0 deletions
diff --git a/protocols/Weather/src/resource.h b/protocols/Weather/src/resource.h
new file mode 100644
index 0000000000..eecba58268
--- /dev/null
+++ b/protocols/Weather/src/resource.h
@@ -0,0 +1,175 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ..\res\resource.rc
+//
+#define IDI_ICON 101
+#define IDD_USERINFO 201
+#define IDD_EDIT 202
+#define IDI_DISABLED 203
+#define IDD_POPUP 204
+#define IDD_OPTIONS 205
+#define IDI_LOG 206
+#define IDI_UPDATE2 208
+#define IDI_READ 209
+#define IDI_UPDATE 210
+#define IDI_S 211
+#define IDI_MAP 212
+#define IDR_PMENU 213
+#define IDI_POPUP 214
+#define IDI_NOPOPUP 215
+#define IDD_TEXTOPT 216
+#define IDD_BRIEF 217
+#define IDD_SETUP 218
+#define IDR_TMENU 219
+#define IDR_TMMENU 220
+#define IDI_EDIT 222
+#define IDD_INFO 224
+#define IDD_SEARCHCITY 225
+#define IDC_NAME 2000
+#define IDC_ID 2001
+#define IDC_LOG 2003
+#define IDC_UPDATETIME 2005
+#define IDC_CTEXT 2006
+#define IDC_AVATARSIZE 2006
+#define IDC_UPDATE 2007
+#define IDC_BTITLE 2008
+#define IDC_STARTUPUPD 2008
+#define IDC_CHANGE 2009
+#define IDC_BTITLE2 2009
+#define IDC_USEWINCOLORS 2010
+#define IDC_BTEXT 2011
+#define IDC_CH 2013
+#define IDC_NTEXT 2015
+#define IDC_DEGREE 2016
+#define IDC_E 2017
+#define IDC_W 2018
+#define IDC_POP1 2019
+#define IDC_XTEXT 2020
+#define IDC_POP2 2020
+#define IDC_PText 2021
+#define IDC_PTitle 2023
+#define IDC_Internal 2024
+#define IDC_ETEXT 2025
+#define IDC_DISCONDICON 2025
+#define IDC_External 2026
+#define IDC_DONOTAPPUNITS 2026
+#define IDC_DEFA 2027
+#define IDC_NOFRAC 2027
+#define IDC_HTEXT 2028
+#define IDC_DPop 2029
+#define IDC_DAutoUpdate 2030
+#define IDC_IURL 2032
+#define IDC_MURL 2033
+#define IDC_PROTOCOND 2034
+#define IDC_Overwrite 2035
+#define IDC_UPDCONDCHG 2036
+#define IDC_REMOVEOLD 2037
+#define IDC_MAKEI 2039
+#define IDC_BGCOLOUR 2040
+#define IDC_TEXTCOLOUR 2041
+#define IDC_LeftClick 2042
+#define IDC_PREVIEW 2043
+#define IDC_VAR3 2044
+#define IDC_RightClick 2045
+#define IDC_DELAY 2046
+#define IDC_PDEF 2047
+#define IDC_T1 2048
+#define IDC_T2 2049
+#define IDC_W1 2050
+#define IDC_W2 2051
+#define IDC_W3 2052
+#define IDC_W4 2053
+#define IDC_BROWSE 2054
+#define IDC_VIEW1 2055
+#define IDC_RESET1 2056
+#define IDC_VIEW2 2057
+#define IDC_V1 2058
+#define IDC_V2 2059
+#define IDC_RESET2 2060
+#define IDC_SVCINFO 2061
+#define IDC_GETNAME 2062
+#define IDC_P1 2063
+#define IDC_P2 2064
+#define IDC_P3 2065
+#define IDC_P4 2066
+#define IDC_RESET 2067
+#define IDC_D1 2067
+#define IDC_D2 2068
+#define IDC_D3 2069
+#define IDC_INFO1 2069
+#define IDC_INFOICON 2070
+#define IDC_INFO11 2071
+#define IDC_INFO2 2072
+#define IDC_INFO3 2073
+#define IDC_VARLIST 2074
+#define IDC_INFO4 2075
+#define IDC_INFO5 2076
+#define IDC_PD1 2077
+#define IDC_PD2 2078
+#define IDC_PD3 2079
+#define IDC_INFO6 2079
+#define IDC_TM1 2080
+#define IDC_TM2 2081
+#define IDC_TM3 2082
+#define IDC_TM4 2083
+#define IDC_TM5 2084
+#define IDC_TM6 2085
+#define IDC_TM7 2086
+#define IDC_TM8 2087
+#define IDC_INFO7 2087
+#define IDC_TM9 2088
+#define IDC_INFO8 2089
+#define IDC_INFO9 2090
+#define IDC_INFO10 2091
+#define IDC_INFO12 2092
+#define IDC_INFO13 2093
+#define IDC_MORE 2094
+#define IDC_MOREDETAIL 2095
+#define IDC_DATALIST 2096
+#define IDC_MUPDATE 2097
+#define IDC_MFRAME 2099
+#define IDC_MTOGGLE 2101
+#define IDC_MWEBPAGE 2102
+#define IDC_MTEXT 2103
+#define IDC_STEP1 2107
+#define IDC_STEP2 2108
+#define IDC_STEP3 2109
+#define IDC_STEP4 2110
+#define IDC_INFOLIST 2117
+#define IDC_RELOADINI 2118
+#define IDC_MEMUSED 2119
+#define IDC_INICOUNT 2120
+#define IDC_AVATARSPIN 2124
+#define IDC_SEARCHCITY 2125
+#define IDC_HEADERBAR 2126
+#define IDC_E1 2128
+#define IDC_E2 2129
+#define OIC_HAND 32513
+#define OIC_QUES 32514
+#define OIC_BANG 32515
+#define OIC_NOTE 32516
+#define IDM_M1 40002
+#define IDM_M2 40003
+#define IDM_M3 40004
+#define IDM_M4 40005
+#define IDM_M5 40006
+#define IDM_M6 40007
+#define IDM_M7 40008
+#define IDM_M8 40009
+#define ID_T1 40010
+#define ID_T2 40011
+#define ID_MPREVIEW 40020
+#define ID_MRESET 40021
+#define IDC_STATIC -1
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC 1
+#define _APS_NEXT_RESOURCE_VALUE 226
+#define _APS_NEXT_COMMAND_VALUE 40030
+#define _APS_NEXT_CONTROL_VALUE 2128
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/protocols/Weather/src/stdafx.cxx b/protocols/Weather/src/stdafx.cxx
new file mode 100644
index 0000000000..1b563fc866
--- /dev/null
+++ b/protocols/Weather/src/stdafx.cxx
@@ -0,0 +1,18 @@
+/*
+Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org)
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h" \ No newline at end of file
diff --git a/protocols/Weather/src/stdafx.h b/protocols/Weather/src/stdafx.h
new file mode 100644
index 0000000000..eee562a9ed
--- /dev/null
+++ b/protocols/Weather/src/stdafx.h
@@ -0,0 +1,547 @@
+/*
+Weather Protocol plugin for Miranda NG
+Copyright (C) 2012-19 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This file contains the includes, weather constants/declarations,
+ the structs, and the primitives for some of the functions.
+*/
+
+#pragma once
+
+//============ THE INCLUDES ===========
+
+#include <share.h>
+#include <time.h>
+#include <windows.h>
+#include <commctrl.h>
+#include <richedit.h>
+#include <malloc.h>
+
+#include <newpluginapi.h>
+#include <m_icolib.h>
+#include <m_options.h>
+#include <m_langpack.h>
+#include <m_skin.h>
+#include <m_database.h>
+#include <m_history.h>
+#include <m_protosvc.h>
+#include <m_userinfo.h>
+#include <m_netlib.h>
+#include <m_ignore.h>
+#include <m_findadd.h>
+#include <m_button.h>
+#include <m_avatars.h>
+#include <m_clc.h>
+#include <m_fontservice.h>
+#include <m_skin_eng.h>
+#include <m_cluiframes.h>
+#include <m_popup.h>
+#include <win2k.h>
+#include <m_acc.h>
+
+#include <m_weather.h>
+#include <m_toptoolbar.h>
+
+#include "resource.h"
+#include "version.h"
+
+//============ CONSTANTS ============
+
+// name
+#define MODULENAME "Weather"
+#define WEATHERPROTOTEXT "Weather"
+#define DEFCURRENTWEATHER "WeatherCondition"
+#define WEATHERCONDITION "Current"
+
+// weather conditions
+#define SUNNY ID_STATUS_ONLINE
+#define NA ID_STATUS_OFFLINE
+#define PCLOUDY ID_STATUS_AWAY
+#define CLOUDY ID_STATUS_NA
+#define RAIN ID_STATUS_OCCUPIED
+#define RSHOWER ID_STATUS_DND
+#define SNOW ID_STATUS_FREECHAT
+#define LIGHT ID_STATUS_INVISIBLE
+#define THUNDER ID_STATUS_INVISIBLE
+#define SSHOWER ID_STATUS_ONTHEPHONE
+#define FOG ID_STATUS_OUTTOLUNCH
+#define UNAVAIL 40081
+
+// status
+#define NOSTATUSDATA 1
+
+// limits
+#define MAX_TEXT_SIZE 4096
+#define MAX_DATA_LEN 1024
+
+// db info mangement mode
+#define WDBM_REMOVE 1
+#define WDBM_DETAILDISPLAY 2
+
+// more info list column width
+#define LIST_COLUMN 150
+
+// others
+#define NODATA TranslateT("N/A")
+#define UM_SETCONTACT 40000
+
+// weather update error codes
+#define INVALID_ID_FORMAT 10
+#define INVALID_SVC 11
+#define INVALID_ID 12
+#define SVC_NOT_FOUND 20
+#define NETLIB_ERROR 30
+#define DATA_EMPTY 40
+#define DOC_NOT_FOUND 42
+#define DOC_TOO_SHORT 43
+#define UNKNOWN_ERROR 99
+
+// weather update error text
+#define E10 TranslateT("Invalid ID format, missing \"/\" (10)")
+#define E11 TranslateT("Invalid service (11)")
+#define E12 TranslateT("Invalid station (12)")
+#define E20 TranslateT("Weather service ini for this station is not found (20)")
+#define E30 TranslateT("Netlib error - check your internet connection (30)")
+#define E40 TranslateT("Empty data is retrieved (40)")
+#define E42 TranslateT("Document not found (42)")
+#define E43 TranslateT("Document too short to contain any weather data (43)")
+#define E99 TranslateT("Unknown error (99)")
+
+// HTTP error... not all translated
+// 100 Continue
+// 101 Switching Protocols
+// 200 OK
+// 201 Created
+// 202 Accepted
+// 203 Non-Authoritative Information
+#define E204 TranslateT("HTTP Error: No content (204)")
+// 205 Reset Content
+// 206 Partial Content
+// 300 Multiple Choices
+#define E301 TranslateT("HTTP Error: Data moved (301)")
+// 302 Found
+// 303 See Other
+// 304 Not Modified
+#define E305 TranslateT("HTTP Error: Use proxy (305)")
+// 306 (Unused)
+#define E307 TranslateT("HTTP Error: Temporary redirect (307)")
+#define E400 TranslateT("HTTP Error: Bad request (400)")
+#define E401 TranslateT("HTTP Error: Unauthorized (401)")
+#define E402 TranslateT("HTTP Error: Payment required (402)")
+#define E403 TranslateT("HTTP Error: Forbidden (403)")
+#define E404 TranslateT("HTTP Error: Not found (404)")
+#define E405 TranslateT("HTTP Error: Method not allowed (405)")
+// 406 Not Acceptable
+#define E407 TranslateT("HTTP Error: Proxy authentication required (407)")
+// 408 Request Timeout
+// 409 Conflict
+#define E410 TranslateT("HTTP Error: Gone (410)")
+// 411 Length Required
+// 412 Precondition Failed
+// 413 Request Entity Too Large
+// 414 Request-URI Too Long
+// 415 Unsupported Media Type
+// 416 Requested Range Not Satisfiable
+// 417 Expectation Failed
+#define E500 TranslateT("HTTP Error: Internal server error (500)")
+// 501 Not Implemented
+#define E502 TranslateT("HTTP Error: Bad gateway (502)")
+#define E503 TranslateT("HTTP Error: Service unavailable (503)")
+#define E504 TranslateT("HTTP Error: Gateway timeout (504)")
+// 505 HTTP Version Not Supported
+
+// defaults constants
+#define C_DEFAULT L"%n [%t, %c]"
+#define N_DEFAULT TranslateT("%c\\nTemperature: %t\\nFeel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]")
+#define B_DEFAULT TranslateT("Feel-Like: %f\\nPressure: %p\\nWind: %i %w\\nHumidity: %m\\nDew Point: %e\\nVisibility: %v\\n\\nSun Rise: %r\\nSun Set: %y\\n\\n5 Days Forecast:\\n%[Forecast Day 1]\\n%[Forecast Day 2]\\n%[Forecast Day 3]\\n%[Forecast Day 4]\\n%[Forecast Day 5]")
+#define b_DEFAULT TranslateT("Weather Condition for %n as of %u")
+#define X_DEFAULT N_DEFAULT
+#define H_DEFAULT TranslateT("%c, %t (feel-like %f) Wind: %i %w Humidity: %m")
+#define E_DEFAULT TranslateT("%n at %u: %c, %t (feel-like %f) Wind: %i %w Humidity: %m")
+#define P_DEFAULT TranslateT("%n (%u)")
+#define p_DEFAULT TranslateT("%c, %t\\nToday: High %h, Low %l")
+#define s_DEFAULT TranslateT("Temperature: %[Temperature]")
+#define VAR_LIST_POPUP TranslateT("%c\tcurrent condition\n%d\tcurrent date\n%e\tdewpoint\n%f\tfeel-like temperature\n%h\ttoday's high\n%i\twind direction\n%l\ttoday's low\n%m\thumidity\n%n\tstation name\n%p\tpressure\n%r\tsunrise time\n%s\tstation ID\n%t\ttemperature\n%u\tupdate time\n%v\tvisibility\n%w\twind speed\n%y\tsun set")
+#define VAR_LIST_OPT TranslateT("%c\tcurrent condition\n%d\tcurrent date\n%e\tdewpoint\n%f\tfeel-like temp\n%h\ttoday's high\n%i\twind direction\n%l\ttoday's low\n%m\thumidity\n%n\tstation name\n%p\tpressure\n%r\tsunrise time\n%s\tstation ID\n%t\ttemperature\n%u\tupdate time\n%v\tvisibility\n%w\twind speed\n%y\tsun set\n----------\n\\n\tnew line")
+#define WEATHER_NO_INFO TranslateT("No information available.\r\nPlease update weather condition first.")
+#define CUSTOM_VARS TranslateT("%[..]\tcustom variables")
+#define VARS_LIST TranslateT("Here is a list of custom variables that are currently available")
+#define NO_FORECAST_URL TranslateT("The URL for complete forecast has not been set. You can set it from the Edit Settings dialog.")
+#define NO_MAP_URL TranslateT("The URL for weather map has not been set. You can set it from the Edit Settings dialog.")
+
+//============ OPTION STRUCT ============
+
+// option struct
+struct MYOPTIONS
+{
+ // main options
+ BYTE AutoUpdate;
+ BYTE CAutoUpdate;
+ BYTE StartupUpdate;
+ BYTE NoProtoCondition;
+ BYTE UpdateOnlyConditionChanged;
+ BYTE RemoveOldData;
+ BYTE MakeItalic;
+
+ WORD UpdateTime;
+ WORD AvatarSize;
+
+ // units
+ WORD tUnit;
+ WORD wUnit;
+ WORD vUnit;
+ WORD pUnit;
+ WORD dUnit;
+ WORD eUnit;
+ wchar_t DegreeSign[4];
+ BYTE DoNotAppendUnit;
+ BYTE NoFrac;
+
+ // texts
+ wchar_t *cText;
+ wchar_t *bTitle;
+ wchar_t *bText;
+ wchar_t *nText;
+ wchar_t *eText;
+ wchar_t *hText;
+ wchar_t *xText;
+ wchar_t *sText;
+
+ // advanced
+ BYTE DisCondIcon;
+
+ // popup options
+ BYTE UsePopup;
+ BYTE UpdatePopup;
+ BYTE AlertPopup;
+ BYTE PopupOnChange;
+ BYTE ShowWarnings;
+
+ // popup colors
+ BYTE UseWinColors;
+ COLORREF BGColour;
+ COLORREF TextColour;
+
+ // popup actions
+ DWORD LeftClickAction;
+ DWORD RightClickAction;
+
+ // popup delay
+ DWORD pDelay;
+
+ // popup texts
+ wchar_t *pTitle;
+ wchar_t *pText;
+
+ // other misc stuff
+ wchar_t Default[64];
+ MCONTACT DefStn;
+};
+
+void DestroyOptions(void);
+
+//============ STRUCT USED TO MAKE AN UPDATE LIST ============
+struct WCONTACTLIST {
+ MCONTACT hContact;
+ struct WCONTACTLIST *next;
+};
+
+typedef struct WCONTACTLIST UPDATELIST;
+
+extern UPDATELIST *UpdateListHead, *UpdateListTail;
+
+void DestroyUpdateList(void);
+
+//============ DATA FORMAT STRUCT ============
+
+#define WID_NORMAL 0
+#define WID_SET 1
+#define WID_BREAK 2
+
+typedef struct {
+ wchar_t *Name;
+ wchar_t *Start;
+ wchar_t *End;
+ wchar_t *Unit;
+ char *Url;
+ wchar_t *Break;
+ int Type;
+} WIDATAITEM;
+
+struct WITEMLIST {
+ WIDATAITEM Item;
+ struct WITEMLIST *Next;
+};
+
+typedef struct WITEMLIST WIDATAITEMLIST;
+
+typedef struct {
+ BOOL Available;
+ char *SearchURL;
+ wchar_t *NotFoundStr;
+ WIDATAITEM Name;
+} WIIDSEARCH;
+
+typedef struct {
+ BOOL Available;
+ wchar_t *First;
+ WIDATAITEM Name;
+ WIDATAITEM ID;
+} WINAMESEARCHTYPE;
+
+typedef struct {
+ char *SearchURL;
+ wchar_t *NotFoundStr;
+ wchar_t *SingleStr;
+ WINAMESEARCHTYPE Single;
+ WINAMESEARCHTYPE Multiple;
+} WINAMESEARCH;
+
+struct STRLIST {
+ wchar_t *Item;
+ struct STRLIST *Next;
+};
+
+typedef struct STRLIST WICONDITEM;
+
+typedef struct {
+ WICONDITEM *Head;
+ WICONDITEM *Tail;
+} WICONDLIST;
+
+typedef struct {
+ wchar_t *FileName;
+ wchar_t *ShortFileName;
+ BOOL Enabled;
+
+ // header
+ wchar_t *DisplayName;
+ wchar_t *InternalName;
+ wchar_t *Description;
+ wchar_t *Author;
+ wchar_t *Version;
+ int InternalVer;
+ size_t MemUsed;
+
+ // default
+ char *DefaultURL;
+ wchar_t *DefaultMap;
+ char *UpdateURL;
+ char *UpdateURL2;
+ char *UpdateURL3;
+ char *UpdateURL4;
+ char *Cookie;
+ char *UserAgent;
+// items
+ int UpdateDataCount;
+ WIDATAITEMLIST *UpdateData;
+ WIDATAITEMLIST *UpdateDataTail;
+ WIIDSEARCH IDSearch;
+ WINAMESEARCH NameSearch;
+ WICONDLIST CondList[10];
+} WIDATA;
+
+//============ DATA LIST (LINKED LIST) ============
+
+struct DATALIST {
+ WIDATA Data;
+ struct DATALIST *next;
+};
+
+typedef struct DATALIST WIDATALIST;
+
+//============ GLOBAL VARIABLES ============
+
+extern WIDATALIST *WIHead, *WITail;
+
+extern HWND hPopupWindow, hWndSetup;
+
+extern MYOPTIONS opt;
+
+extern unsigned status, old_status;
+
+extern MWindowList hDataWindowList, hWindowList;
+
+extern HNETLIBUSER hNetlibUser;
+extern HANDLE hHookWeatherUpdated, hHookWeatherError, hTBButton, hUpdateMutex;
+extern UINT_PTR timerId;
+
+extern HGENMENU hMwinMenu;
+
+// check if weather is currently updating
+extern BOOL ThreadRunning;
+
+//============ FUNCTION PRIMITIVES ============
+
+// functions in weather_addstn.c
+INT_PTR WeatherAddToList(WPARAM wParam,LPARAM lParam);
+BOOL CheckSearch();
+
+int IDSearch(wchar_t *id, const int searchId);
+int NameSearch(wchar_t *name, const int searchId);
+
+INT_PTR WeatherBasicSearch(WPARAM wParam,LPARAM lParam);
+INT_PTR WeatherCreateAdvancedSearchUI(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherAdvancedSearch(WPARAM wParam, LPARAM lParam);
+
+int WeatherAdd(WPARAM wParam, LPARAM lParam);
+
+// functions used in weather_contacts.c
+INT_PTR ViewLog(WPARAM wParam,LPARAM lParam);
+INT_PTR LoadForecast(WPARAM wParam,LPARAM lParam);
+INT_PTR WeatherMap(WPARAM wParam,LPARAM lParam);
+INT_PTR EditSettings(WPARAM wParam,LPARAM lParam);
+
+int ContactDeleted(WPARAM wParam,LPARAM lParam);
+
+BOOL IsMyContact(MCONTACT hContact);
+
+// functions in weather_conv.c
+void GetTemp(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetSpeed(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetPressure(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetDist(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+void GetElev(wchar_t *tempchar, wchar_t *unit, wchar_t *str);
+
+WORD GetIcon(const wchar_t* cond, WIDATA *Data);
+void CaseConv(wchar_t *str);
+void TrimString(char *str);
+void TrimString(WCHAR *str);
+void ConvertBackslashes(char *str);
+char *GetSearchStr(char *dis);
+
+wchar_t *GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t* str);
+INT_PTR GetDisplaySvcFunc(WPARAM wParam, LPARAM lParam);
+
+void GetSvc(wchar_t *pszID);
+void GetID(wchar_t *pszID);
+
+wchar_t *GetError(int code);
+
+// functions in weather_data.c
+void GetStationID(MCONTACT hContact, wchar_t* id, int idlen);
+WEATHERINFO LoadWeatherInfo(MCONTACT Change);
+int DBGetData(MCONTACT hContact, char *setting, DBVARIANT *dbv);
+
+void EraseAllInfo(void);
+
+void GetDataValue(WIDATAITEM *UpdateData, wchar_t *Data, wchar_t** szInfo);
+void ConvertDataValue(WIDATAITEM *UpdateData, wchar_t *Data);
+void wSetData(char **Data, const char *Value);
+void wSetData(WCHAR **Data, const char *Value);
+void wSetData(WCHAR **Data, const WCHAR *Value);
+void wfree(char **Data);
+void wfree(WCHAR **Data);
+
+void DBDataManage(MCONTACT hContact, WORD Mode, WPARAM wParam, LPARAM lParam);
+
+// functions in weather_http.c
+int InternetDownloadFile (char *szUrl, char *cookie, char *userAgent, wchar_t** szData);
+void NetlibInit();
+
+// functions in weather_ini.c
+WIDATA* GetWIData(wchar_t *pszServ);
+
+bool IsContainedInCondList(const wchar_t *pszStr, WICONDLIST *List);
+
+void DestroyWIList();
+bool LoadWIData(bool dial);
+
+INT_PTR CALLBACK DlgPopupOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+// functions in weather_info.c
+void GetINIInfo(wchar_t *pszSvc);
+
+void MoreVarList();
+
+// functions in weather_opt.c
+void SetTextDefault(const char* in);
+void LoadOptions();
+void SaveOptions();
+
+int OptInit(WPARAM wParam,LPARAM lParam);
+
+
+// functions in weather_popup.c
+int WeatherPopup(WPARAM wParam, LPARAM lParam);
+int WeatherError(WPARAM wParam, LPARAM lParam);
+int WPShowMessage(wchar_t* lpzText, WORD kind);
+
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+
+// functions in weather_svcs.c
+void InitServices(void);
+
+INT_PTR WeatherSetStatus(WPARAM new_status, LPARAM lParam);
+INT_PTR WeatherGetCaps(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherGetName(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherGetStatus(WPARAM wParam, LPARAM lParam);
+INT_PTR WeatherLoadIcon(WPARAM wParam, LPARAM lParam);
+
+void UpdateMenu(BOOL State);
+void UpdatePopupMenu(BOOL State);
+void AddMenuItems();
+void AvatarDownloaded(MCONTACT hContact);
+
+// functions in weather_update.c
+int UpdateWeather(MCONTACT hContact);
+
+void UpdateAll(BOOL AutoUpdate, BOOL RemoveOld);
+INT_PTR UpdateSingleStation(WPARAM wParam,LPARAM lParam);
+INT_PTR UpdateAllInfo(WPARAM wParam,LPARAM lParam);
+INT_PTR UpdateSingleRemove(WPARAM wParam,LPARAM lParam);
+INT_PTR UpdateAllRemove(WPARAM wParam,LPARAM lParam);
+
+int GetWeatherData(MCONTACT hContact);
+
+void CALLBACK timerProc(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+void CALLBACK timerProc2(HWND hwnd, UINT uMsg, UINT_PTR idEvent, DWORD dwTime);
+
+// function from multiwin module
+void InitMwin(void);
+void DestroyMwin(void);
+INT_PTR Mwin_MenuClicked(WPARAM wParam, LPARAM lParam);
+int BuildContactMenu(WPARAM wparam, LPARAM lparam);
+void UpdateMwinData(MCONTACT hContact);
+void removeWindow(MCONTACT hContact);
+
+// functions in weather_userinfo.c
+int UserInfoInit(WPARAM wParam, LPARAM lParam);
+INT_PTR CALLBACK DlgProcINIPage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam);
+
+#define WM_UPDATEDATA WM_USER + 2687
+
+int BriefInfo(WPARAM wParam, LPARAM lParam);
+INT_PTR BriefInfoSvc(WPARAM wParam, LPARAM lParam);
+
+void InitIcons(void);
+HICON LoadIconEx(const char* name, bool big);
+HANDLE GetIconHandle(const char* name);
+void ReleaseIconEx(HICON hIcon);
+
+//============ Plugin Class ============
+
+struct CMPlugin : public PLUGIN<CMPlugin>
+{
+ CMPlugin();
+
+ int Load() override;
+ int Unload() override;
+};
+
diff --git a/protocols/Weather/src/version.h b/protocols/Weather/src/version.h
new file mode 100644
index 0000000000..d777fe40f9
--- /dev/null
+++ b/protocols/Weather/src/version.h
@@ -0,0 +1,13 @@
+#define __MAJOR_VERSION 0
+#define __MINOR_VERSION 4
+#define __RELEASE_NUM 0
+#define __BUILD_NUM 4
+
+#include <stdver.h>
+
+#define __PLUGIN_NAME "Weather"
+#define __FILENAME "Weather.dll"
+#define __DESCRIPTION "Retrieves weather information and displays it in your contact list."
+#define __AUTHOR "Miranda NG team"
+#define __AUTHORWEB "https://miranda-ng.org/p/Weather/"
+#define __COPYRIGHT "© 2002-2005 NoName, 2005-2010 Boris Krasnovskiy, 2012-19 Miranda NG team"
diff --git a/protocols/Weather/src/weather.cpp b/protocols/Weather/src/weather.cpp
new file mode 100644
index 0000000000..742f860967
--- /dev/null
+++ b/protocols/Weather/src/weather.cpp
@@ -0,0 +1,231 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+Main file for the Weather Protocol, includes loading, unloading,
+upgrading, support for plugin uninsaller, and anything that doesn't
+belong to any other file.
+*/
+
+#include "stdafx.h"
+
+//============ GLOBAL VARIABLES ============
+
+WIDATALIST *WIHead;
+WIDATALIST *WITail;
+
+HWND hPopupWindow;
+
+HANDLE hHookWeatherUpdated;
+HANDLE hHookWeatherError;
+
+MWindowList hDataWindowList, hWindowList;
+
+HANDLE hUpdateMutex;
+
+unsigned status;
+unsigned old_status;
+
+UINT_PTR timerId;
+
+CMPlugin g_plugin;
+
+MYOPTIONS opt;
+
+// check if weather is currently updating
+BOOL ThreadRunning;
+
+// variable to determine if module loaded
+BOOL ModuleLoaded;
+
+HANDLE hTBButton = nullptr;
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// plugin info
+
+static const PLUGININFOEX pluginInfoEx =
+{
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESCRIPTION,
+ __AUTHOR,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ // {6B612A34-DCF2-4E32-85CF-B6FD006B745E}
+ {0x6b612a34, 0xdcf2, 0x4e32, {0x85, 0xcf, 0xb6, 0xfd, 0x0, 0x6b, 0x74, 0x5e}}
+};
+
+CMPlugin::CMPlugin() :
+ PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx)
+{
+ opt.NoProtoCondition = g_plugin.getByte("NoStatus", true);
+ RegisterProtocol((opt.NoProtoCondition) ? PROTOTYPE_VIRTUAL : PROTOTYPE_PROTOCOL);
+ SetUniqueId("ID");
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_PROTOCOL, MIID_LAST };
+
+/////////////////////////////////////////////////////////////////////////////////////////
+
+int WeatherShutdown(WPARAM, LPARAM)
+{
+ KillTimer(nullptr, timerId); // kill update timer
+
+ SaveOptions(); // save options once more
+ status = ID_STATUS_OFFLINE; // set status to offline
+
+ WindowList_Broadcast(hWindowList, WM_CLOSE, 0, 0);
+ WindowList_Broadcast(hDataWindowList, WM_CLOSE, 0, 0);
+ SendMessage(hWndSetup, WM_CLOSE, 0, 0);
+
+ return 0;
+}
+
+int OnToolbarLoaded(WPARAM, LPARAM)
+{
+ TTBButton ttb = {};
+ ttb.name = LPGEN("Enable/disable auto update");
+ ttb.pszService = MS_WEATHER_ENABLED;
+ ttb.pszTooltipUp = LPGEN("Auto Update Enabled");
+ ttb.pszTooltipDn = LPGEN("Auto Update Disabled");
+ ttb.hIconHandleUp = GetIconHandle("main");
+ ttb.hIconHandleDn = GetIconHandle("disabled");
+ ttb.dwFlags = (g_plugin.getByte("AutoUpdate", 1) ? 0 : TTBBF_PUSHED) | TTBBF_ASPUSHBUTTON | TTBBF_VISIBLE;
+ hTBButton = g_plugin.addTTB(&ttb);
+ return 0;
+}
+
+// weather protocol initialization function
+// run after the event ME_SYSTEM_MODULESLOADED occurs
+int WeatherInit(WPARAM, LPARAM)
+{
+ // initialize netlib
+ NetlibInit();
+
+ InitMwin();
+
+ // load weather menu items
+ AddMenuItems();
+
+ // timer for the first update
+ timerId = SetTimer(nullptr, 0, 5000, timerProc2); // first update is 5 sec after load
+
+ // weather user detail
+ HookEvent(ME_USERINFO_INITIALISE, UserInfoInit);
+ HookEvent(ME_TTB_MODULELOADED, OnToolbarLoaded);
+ return 0;
+}
+
+//============ MISC FUNCTIONS ============
+
+// initialize the global variables at startup
+void InitVar()
+{
+ // setup the linklist for weather update list
+ UpdateListTail = nullptr;
+ UpdateListHead = nullptr;
+
+ // other settings
+ timerId = 0;
+ opt.DefStn = NULL;
+ ModuleLoaded = FALSE;
+}
+
+int CMPlugin::Load()
+{
+ // initialize global variables
+ InitVar();
+
+ // load options and set defaults
+ LoadOptions();
+
+ InitIcons();
+
+ // reset the weather data at startup for individual contacts
+ EraseAllInfo();
+
+ // load weather update data
+ LoadWIData(true);
+
+ // set status to online if "Do not display weather condition as protocol status" is enabled
+ old_status = status = ID_STATUS_OFFLINE;
+
+ // add an event on weather update and error
+ hHookWeatherUpdated = CreateHookableEvent(ME_WEATHER_UPDATED);
+ hHookWeatherError = CreateHookableEvent(ME_WEATHER_ERROR);
+
+ // initialize options and network
+ HookEvent(ME_OPT_INITIALISE, OptInit);
+ HookEvent(ME_SYSTEM_MODULESLOADED, WeatherInit);
+ HookEvent(ME_DB_CONTACT_DELETED, ContactDeleted);
+ HookEvent(ME_CLIST_DOUBLECLICKED, BriefInfo);
+ HookEvent(ME_WEATHER_UPDATED, WeatherPopup);
+ HookEvent(ME_WEATHER_ERROR, WeatherError);
+ HookEvent(ME_SYSTEM_PRESHUTDOWN, WeatherShutdown);
+ HookEvent(ME_CLIST_PREBUILDCONTACTMENU, BuildContactMenu);
+
+ hDataWindowList = WindowList_Create();
+ hWindowList = WindowList_Create();
+
+ hUpdateMutex = CreateMutex(nullptr, FALSE, nullptr);
+
+ // initialize weather protocol services
+ InitServices();
+
+ // add sound event
+ g_plugin.addSound("weatherupdated", _A2W(MODULENAME), LPGENW("Condition Changed"));
+ g_plugin.addSound("weatheralert", _A2W(MODULENAME), LPGENW("Alert Issued"));
+
+ // window needed for popup commands
+ wchar_t SvcFunc[100];
+ mir_snwprintf(SvcFunc, L"%s__PopupWindow", _A2W(MODULENAME));
+ hPopupWindow = CreateWindowEx(WS_EX_TOOLWINDOW, L"static", SvcFunc, 0, CW_USEDEFAULT, CW_USEDEFAULT,
+ CW_USEDEFAULT, CW_USEDEFAULT, HWND_DESKTOP, nullptr, g_plugin.getInst(), nullptr);
+ SetWindowLongPtr(hPopupWindow, GWLP_WNDPROC, (LONG_PTR)PopupWndProc);
+ return 0;
+}
+
+/////////////////////////////////////////////////////////////////////////////////////////
+// unload function
+
+int CMPlugin::Unload()
+{
+ DestroyMwin();
+ DestroyWindow(hPopupWindow);
+
+ DestroyHookableEvent(hHookWeatherUpdated);
+ DestroyHookableEvent(hHookWeatherError);
+
+ Netlib_CloseHandle(hNetlibUser);
+
+ DestroyUpdateList();
+ DestroyOptions();
+ DestroyWIList(); // unload all ini data from memory
+
+ WindowList_Destroy(hDataWindowList);
+ WindowList_Destroy(hWindowList);
+
+ CloseHandle(hUpdateMutex);
+ return 0;
+}
diff --git a/protocols/Weather/src/weather_addstn.cpp b/protocols/Weather/src/weather_addstn.cpp
new file mode 100644
index 0000000000..ca7a8ae6e3
--- /dev/null
+++ b/protocols/Weather/src/weather_addstn.cpp
@@ -0,0 +1,432 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This file contain the source related to search and add a weather station
+to the contact list. Contain code for both name and ID search.
+*/
+
+#include "stdafx.h"
+
+// variables used for weather_addstn.c
+static int sttSearchId = -1;
+static wchar_t name1[256];
+
+// ============ ADDING NEW STATION ============
+
+// protocol service function for adding a new contact onto contact list
+// lParam = PROTOSEARCHRESULT
+INT_PTR WeatherAddToList(WPARAM, LPARAM lParam)
+{
+ PROTOSEARCHRESULT *psr = (PROTOSEARCHRESULT*)lParam;
+ if (!psr || !psr->email.w)
+ return 0;
+
+ // search for existing contact
+ for (auto &hContact : Contacts()) {
+ // check if it is a weather contact
+ if (IsMyContact(hContact)) {
+ DBVARIANT dbv;
+ // check ID to see if the contact already exist in the database
+ if (!g_plugin.getWString(hContact, "ID", &dbv)) {
+ if (!mir_wstrcmpi(psr->email.w, dbv.pwszVal)) {
+ // remove the flag for not on list and hidden, thus make the contact visible
+ // and add them on the list
+ if (db_get_b(hContact, "CList", "NotOnList", 1)) {
+ db_unset(hContact, "CList", "NotOnList");
+ db_unset(hContact, "CList", "Hidden");
+ }
+ db_free(&dbv);
+ // contact is added, function quitting
+ return (INT_PTR)hContact;
+ }
+ db_free(&dbv);
+ }
+ }
+ }
+
+ // if contact with the same ID was not found, add it
+ if (psr->cbSize < sizeof(PROTOSEARCHRESULT)) return 0;
+ MCONTACT hContact = db_add_contact();
+ Proto_AddToContact(hContact, MODULENAME);
+ // suppress online notification for the new contact
+ CallService(MS_IGNORE_IGNORE, hContact, IGNOREEVENT_USERONLINE);
+
+ // set contact info and settings
+ wchar_t svc[256];
+ wcsncpy(svc, psr->email.w, _countof(svc)); svc[_countof(svc) - 1] = 0;
+ GetSvc(svc);
+ // set settings by obtaining the default for the service
+ if (psr->lastName.w[0] != 0) {
+ WIDATA *sData = GetWIData(svc);
+ g_plugin.setWString(hContact, "MapURL", sData->DefaultMap);
+ g_plugin.setString(hContact, "InfoURL", sData->DefaultURL);
+ }
+ else { // if no valid service is found, create empty strings for MapURL and InfoURL
+ g_plugin.setString(hContact, "MapURL", "");
+ g_plugin.setString(hContact, "InfoURL", "");
+ }
+ // write the other info and settings to the database
+ g_plugin.setWString(hContact, "ID", psr->email.w);
+ g_plugin.setWString(hContact, "Nick", psr->nick.w);
+ g_plugin.setWord(hContact, "Status", ID_STATUS_OFFLINE);
+
+ AvatarDownloaded(hContact);
+
+ wchar_t str[256];
+ mir_snwprintf(str, TranslateT("Current weather information for %s."), psr->nick.w);
+ g_plugin.setWString(hContact, "About", str);
+
+ // make the last update tags to something invalid
+ g_plugin.setString(hContact, "LastLog", "never");
+ g_plugin.setString(hContact, "LastCondition", "None");
+ g_plugin.setString(hContact, "LastTemperature", "None");
+
+ // ignore status change
+ db_set_dw(hContact, "Ignore", "Mask", 8);
+
+ // if no default station is found, set the new contact as default station
+ if (opt.Default[0] == 0) {
+ DBVARIANT dbv;
+ GetStationID(hContact, opt.Default, _countof(opt.Default));
+
+ opt.DefStn = hContact;
+ if (!g_plugin.getWString(hContact, "Nick", &dbv)) {
+ // notification message box
+ mir_snwprintf(str, TranslateT("%s is now the default weather station"), dbv.pwszVal);
+ db_free(&dbv);
+ MessageBox(nullptr, str, TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
+ }
+ g_plugin.setWString("Default", opt.Default);
+ }
+ // display the Edit Settings dialog box
+ EditSettings(hContact, 0);
+ return (INT_PTR)hContact;
+}
+
+// ============ WARNING DIALOG ============
+
+// show a message box and cancel search if update is in process
+BOOL CheckSearch()
+{
+ if (UpdateListHead != nullptr) {
+ MessageBox(nullptr, TranslateT("Please try again after weather update is completed."), TranslateT("Weather Protocol"), MB_OK | MB_ICONERROR);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+// ============ BASIC ID SEARCH ============
+
+static wchar_t sttSID[32];
+
+// A timer process for the ID search (threaded)
+static void __cdecl BasicSearchTimerProc(LPVOID)
+{
+ int result;
+ // search only when it's not current updating weather.
+ if (CheckSearch())
+ result = IDSearch(sttSID, sttSearchId);
+
+ // broadcast the search result
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)sttSearchId);
+
+ // exit the search
+ sttSearchId = -1;
+}
+
+// the service function for ID search
+// lParam = ID search string
+INT_PTR WeatherBasicSearch(WPARAM, LPARAM lParam)
+{
+ if (sttSearchId != -1) return 0; //only one search at a time
+ wcsncpy(sttSID, (wchar_t*)lParam, _countof(sttSID));
+ sttSID[_countof(sttSID) - 1] = 0;
+ sttSearchId = 1;
+ // create a thread for the ID search
+ mir_forkthread(BasicSearchTimerProc);
+ return sttSearchId;
+}
+
+// ============ NAME SEARCH ============
+//
+// name search timer process (threaded)
+static void __cdecl NameSearchTimerProc(LPVOID)
+{
+ // search only when it's not current updating weather.
+ if (CheckSearch())
+ if (name1[0] != 0)
+ NameSearch(name1, sttSearchId); // search nickname field
+
+ // broadcast the result
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_SEARCH, ACKRESULT_SUCCESS, (HANDLE)sttSearchId);
+
+ // exit the search
+ sttSearchId = -1;
+}
+
+static INT_PTR CALLBACK WeatherSearchAdvancedDlgProc(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SetFocus(GetDlgItem(hwndDlg, IDC_SEARCHCITY));
+ return TRUE;
+
+ case WM_COMMAND:
+ if (HIWORD(wParam) == EN_SETFOCUS)
+ PostMessage(GetParent(hwndDlg), WM_COMMAND, MAKEWPARAM(0, EN_SETFOCUS), (LPARAM)hwndDlg);
+ }
+ return FALSE;
+}
+
+INT_PTR WeatherCreateAdvancedSearchUI(WPARAM, LPARAM lParam)
+{
+ HWND parent = (HWND)lParam;
+ if (parent)
+ return (INT_PTR)CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SEARCHCITY), parent, WeatherSearchAdvancedDlgProc, 0);
+
+ return 0;
+}
+
+// service function for name search
+INT_PTR WeatherAdvancedSearch(WPARAM, LPARAM lParam)
+{
+ if (sttSearchId != -1) return 0; //only one search at a time
+
+ sttSearchId = 1;
+ GetDlgItemText((HWND)lParam, IDC_SEARCHCITY, name1, _countof(name1));
+
+ // search for the weather station using a thread
+ mir_forkthread(NameSearchTimerProc);
+ return sttSearchId;
+}
+
+// ============ SEARCH FOR A WEATHER STATION USING ID ============
+
+// Seaching station ID from a single weather service (Threaded)
+// sID = search string for the station ID
+// searchId = -1
+// sData = the ID search data for that particular weather service
+// svcname = the name of the weather service that is currently searching (ie. Yahoo Weather)
+int IDSearchProc(wchar_t *sID, const int searchId, WIIDSEARCH *sData, wchar_t *svc, wchar_t *svcname)
+{
+ wchar_t str[MAX_DATA_LEN], newID[MAX_DATA_LEN];
+
+ if (sData->Available) {
+ char loc[255];
+ wchar_t *szData = nullptr;
+
+ // load the page
+ mir_snprintf(loc, sData->SearchURL, sID);
+ BOOL bFound = (InternetDownloadFile(loc, nullptr, nullptr, &szData) == 0);
+ if (bFound) {
+ wchar_t* szInfo = szData;
+
+ // not found
+ if (wcsstr(szInfo, sData->NotFoundStr) == nullptr)
+ GetDataValue(&sData->Name, str, &szInfo);
+ }
+
+ mir_free(szData);
+ // Station not found exit
+ if (!bFound) return 1;
+ }
+
+ // give no station name but only ID if the search is unavailable
+ else wcsncpy(str, TranslateT("<Enter station name here>"), MAX_DATA_LEN - 1);
+ mir_snwprintf(newID, L"%s/%s", svc, sID);
+
+ // set the search result and broadcast it
+ PROTOSEARCHRESULT psr = { sizeof(psr) };
+ psr.flags = PSR_UNICODE;
+ psr.nick.w = str;
+ psr.firstName.w = L" ";
+ psr.lastName.w = svcname;
+ psr.email.w = newID;
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)searchId, (LPARAM)&psr);
+
+ return 0;
+}
+
+// ID search (Threaded)
+// sID: the ID to search for
+// searchId: don't change
+// return 0 if no error
+int IDSearch(wchar_t *sID, const int searchId)
+{
+ // for a normal ID search (ID != #)
+ if (mir_wstrcmp(sID, L"#")) {
+ WIDATALIST *Item = WIHead;
+
+ // search every weather service using the search station ID
+ while (Item != nullptr) {
+ IDSearchProc(sID, searchId, &Item->Data.IDSearch, Item->Data.InternalName, Item->Data.DisplayName);
+ Item = Item->next;
+ }
+ }
+ // if the station ID is #, return a dummy result and quit the funciton
+ else {
+ // return an empty contact on "#"
+ PROTOSEARCHRESULT psr = { sizeof(psr) };
+ psr.flags = PSR_UNICODE;
+ psr.nick.w = TranslateT("<Enter station name here>"); // to be entered
+ psr.firstName.w = L" ";
+ psr.lastName.w = L"";
+ psr.email.w = TranslateT("<Enter station ID here>"); // to be entered
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)searchId, (LPARAM)&psr);
+ }
+
+ return 0;
+}
+
+// ============ SEARCH FOR A WEATHER STATION BY NAME ============
+
+// Seaching station name from a single weather service (Threaded)
+// name = the name of the weather station to be searched
+// searchId = -1
+// sData = the name search data for that particular weather service
+// svcname = the name of the weather service that is currently searching (ie. Yahoo Weather)
+int NameSearchProc(wchar_t *name, const int searchId, WINAMESEARCH *sData, wchar_t *svc, wchar_t *svcname)
+{
+ wchar_t Name[MAX_DATA_LEN], str[MAX_DATA_LEN], sID[MAX_DATA_LEN], *szData = nullptr, *search;
+
+ // replace spaces with %20
+ char loc[256];
+ T2Utf szSearchName(name);
+ mir_snprintf(loc, sData->SearchURL, mir_urlEncode(szSearchName).c_str());
+ if (InternetDownloadFile(loc, nullptr, nullptr, &szData) == 0) {
+ wchar_t* szInfo = szData;
+ search = wcsstr(szInfo, sData->NotFoundStr); // determine if data is available
+ if (search == nullptr) { // if data is found
+ // test if it is single result
+ if (sData->Single.Available && sData->Multiple.Available)
+ search = wcsstr(szInfo, sData->SingleStr);
+ // for single result
+ if (sData->Single.Available && (search != nullptr || !sData->Multiple.Available)) { // single result
+ // if station ID appears first in the downloaded data
+ if (!mir_wstrcmpi(sData->Single.First, L"ID")) {
+ GetDataValue(&sData->Single.ID, str, &szInfo);
+ mir_snwprintf(sID, L"%s/%s", svc, str);
+ GetDataValue(&sData->Single.Name, Name, &szInfo);
+ }
+ // if station name appears first in the downloaded data
+ else if (!mir_wstrcmpi(sData->Single.First, L"NAME")) {
+ GetDataValue(&sData->Single.Name, Name, &szInfo);
+ GetDataValue(&sData->Single.ID, str, &szInfo);
+ mir_snwprintf(sID, L"%s/%s", svc, str);
+ }
+ else
+ str[0] = 0;
+
+ // if no station ID is obtained, quit the search
+ if (str[0] == 0) {
+ mir_free(szData);
+ return 1;
+ }
+
+ // if can't get the name, use the search string as name
+ if (Name[0] == 0)
+ wcsncpy(Name, name, _countof(Name));
+
+ // set the data and broadcast it
+ PROTOSEARCHRESULT psr = { sizeof(psr) };
+ psr.flags = PSR_UNICODE;
+ psr.nick.w = Name;
+ psr.firstName.w = L" ";
+ psr.lastName.w = svcname;
+ psr.email.w = sID;
+ psr.id.w = sID;
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)searchId, (LPARAM)&psr);
+ mir_free(szData);
+ return 0;
+ }
+ // for multiple result
+ else if (sData->Multiple.Available) { // multiple results
+ // search for the next occurrence of the string
+ while (true) {
+ // if station ID appears first in the downloaded data
+ if (!mir_wstrcmpi(sData->Multiple.First, L"ID")) {
+ GetDataValue(&sData->Multiple.ID, str, &szInfo);
+ mir_snwprintf(sID, L"%s/%s", svc, str);
+ GetDataValue(&sData->Multiple.Name, Name, &szInfo);
+ }
+ // if station name appears first in the downloaded data
+ else if (!mir_wstrcmpi(sData->Multiple.First, L"NAME")) {
+ GetDataValue(&sData->Multiple.Name, Name, &szInfo);
+ GetDataValue(&sData->Multiple.ID, str, &szInfo);
+ mir_snwprintf(sID, L"%s/%s", svc, str);
+ }
+ else
+ break;
+
+ // if no station ID is obtained, search completed and quit the search
+ if (str[0] == 0)
+ break;
+
+ // if can't get the name, use the search string as name
+ if (Name[0] == 0)
+ wcsncpy(Name, name, _countof(Name));
+
+ PROTOSEARCHRESULT psr = { sizeof(psr) };
+ psr.flags = PSR_UNICODE;
+ psr.nick.w = Name;
+ psr.firstName.w = L"";
+ psr.lastName.w = svcname;
+ psr.email.w = sID;
+ psr.id.w = sID;
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_SEARCH, ACKRESULT_DATA, (HANDLE)searchId, (LPARAM)&psr);
+ }
+ }
+ }
+
+ mir_free(szData);
+ return 0;
+ }
+
+ mir_free(szData);
+ return 1;
+}
+
+// name search (Threaded)
+// name: the station name to search for
+// searchId: don't change
+// return 0 if no error
+int NameSearch(wchar_t *name, const int searchId)
+{
+ // search every weather service using the search station name
+ WIDATALIST *Item = WIHead;
+ while (Item != nullptr) {
+ if (Item->Data.NameSearch.Single.Available || Item->Data.NameSearch.Multiple.Available)
+ NameSearchProc(name, searchId, &Item->Data.NameSearch, Item->Data.InternalName, Item->Data.DisplayName);
+ Item = Item->next;
+ }
+
+ return 0;
+}
+
+// ======================MENU ITEM FUNCTION ============
+
+// add a new weather station via find/add dialog
+int WeatherAdd(WPARAM, LPARAM)
+{
+ db_set_s(0, "FindAdd", "LastSearched", "Weather");
+ CallService(MS_FINDADD_FINDADD, 0, 0);
+ return 0;
+}
diff --git a/protocols/Weather/src/weather_contacts.cpp b/protocols/Weather/src/weather_contacts.cpp
new file mode 100644
index 0000000000..f572b29564
--- /dev/null
+++ b/protocols/Weather/src/weather_contacts.cpp
@@ -0,0 +1,480 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This file contain the source that is related to weather contacts,
+include the links, edit settings, and loading weather information for
+the contact.
+*/
+
+#include "stdafx.h"
+
+static void OpenUrl(wchar_t* format, wchar_t* id)
+{
+ wchar_t loc[512];
+ GetID(id);
+ mir_snwprintf(loc, format, id);
+ Utils_OpenUrlW(loc);
+}
+
+//============ BASIC CONTACTS FUNCTIONS AND LINKS ============
+
+// view weather log for the contact
+// wParam = current contact
+INT_PTR ViewLog(WPARAM wParam, LPARAM lParam)
+{
+ // see if the log path is set
+ DBVARIANT dbv;
+ if (!g_plugin.getWString(wParam, "Log", &dbv)) {
+ if (dbv.pszVal[0] != 0)
+ ShellExecute((HWND)lParam, L"open", dbv.pwszVal, L"", L"", SW_SHOW);
+ db_free(&dbv);
+ }
+ else // display warning dialog if no path is specified
+ MessageBox(nullptr, TranslateT("Weather condition was not logged."),
+ TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
+ return 0;
+}
+
+// read complete forecast
+// wParam = current contact
+INT_PTR LoadForecast(WPARAM wParam, LPARAM)
+{
+ wchar_t id[256], loc2[256];
+ GetStationID(wParam, id, _countof(id));
+ if (id[0] != 0) {
+ // check if the complte forecast URL is set. If it is not, display warning and quit
+ if (db_get_wstatic(wParam, MODULENAME, "InfoURL", loc2, _countof(loc2)) || loc2[0] == 0) {
+ MessageBox(nullptr, NO_FORECAST_URL, TranslateT("Weather Protocol"), MB_ICONINFORMATION);
+ return 1;
+ }
+ // set the url and open the webpage
+ OpenUrl(loc2, id);
+ }
+ return 0;
+}
+
+// load weather map
+// wParam = current contact
+INT_PTR WeatherMap(WPARAM wParam, LPARAM)
+{
+ wchar_t id[256], loc2[256];
+ GetStationID(wParam, id, _countof(id));
+ if (id[0] != 0) {
+ // check if the weather map URL is set. If it is not, display warning and quit
+ if (db_get_wstatic(wParam, MODULENAME, "MapURL", loc2, _countof(loc2)) || loc2[0] == 0) {
+ MessageBox(nullptr, NO_MAP_URL, TranslateT("Weather Protocol"), MB_ICONINFORMATION);
+ return 1;
+ }
+
+ // set the url and open the webpage
+ OpenUrl(loc2, id);
+ }
+
+ return 0;
+}
+
+//============ EDIT SETTINGS ============
+
+typedef struct
+{
+ MCONTACT hContact;
+ HICON hRename;
+ HICON hUserDetail;
+ HICON hFile;
+ HICON hSrchAll;
+} CntSetWndDataType;
+
+// edit weather settings
+// lParam = current contact
+static INT_PTR CALLBACK DlgProcChange(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ DBVARIANT dbv;
+ wchar_t str[MAX_DATA_LEN], str2[256], city[256], filter[256], *pfilter, *chop;
+ char loc[512];
+ OPENFILENAME ofn; // common dialog box structure
+ MCONTACT hContact;
+ WIDATA *sData;
+ CntSetWndDataType *wndData = nullptr;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+
+ wndData = (CntSetWndDataType*)mir_alloc(sizeof(CntSetWndDataType));
+ wndData->hContact = hContact = lParam;
+ wndData->hRename = Skin_LoadIcon(SKINICON_OTHER_RENAME);
+ wndData->hUserDetail = Skin_LoadIcon(SKINICON_OTHER_USERDETAILS);
+ wndData->hFile = Skin_LoadIcon(SKINICON_EVENT_FILE);
+ wndData->hSrchAll = Skin_LoadIcon(SKINICON_OTHER_SEARCHALL);
+
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)wndData);
+
+ // set button images
+ SendDlgItemMessage(hwndDlg, IDC_GETNAME, BM_SETIMAGE, IMAGE_ICON, (LPARAM)wndData->hRename);
+ SendDlgItemMessage(hwndDlg, IDC_SVCINFO, BM_SETIMAGE, IMAGE_ICON, (LPARAM)wndData->hUserDetail);
+ SendDlgItemMessage(hwndDlg, IDC_BROWSE, BM_SETIMAGE, IMAGE_ICON, (LPARAM)wndData->hFile);
+ SendDlgItemMessage(hwndDlg, IDC_VIEW1, BM_SETIMAGE, IMAGE_ICON, (LPARAM)wndData->hSrchAll);
+ SendDlgItemMessage(hwndDlg, IDC_RESET1, BM_SETIMAGE, IMAGE_ICON, (LPARAM)wndData->hRename);
+ SendDlgItemMessage(hwndDlg, IDC_VIEW2, BM_SETIMAGE, IMAGE_ICON, (LPARAM)wndData->hSrchAll);
+ SendDlgItemMessage(hwndDlg, IDC_RESET2, BM_SETIMAGE, IMAGE_ICON, (LPARAM)wndData->hRename);
+
+ // make all buttons flat
+ SendDlgItemMessage(hwndDlg, IDC_GETNAME, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_SVCINFO, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_BROWSE, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_VIEW1, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_RESET1, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_VIEW2, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_RESET2, BUTTONSETASFLATBTN, TRUE, 0);
+
+ // set tooltip for the buttons
+ SendDlgItemMessage(hwndDlg, IDC_GETNAME, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Get city name from ID"), BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, IDC_SVCINFO, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Weather INI information"), BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, IDC_BROWSE, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Browse"), BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, IDC_VIEW1, BUTTONADDTOOLTIP, (WPARAM)LPGENW("View webpage"), BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, IDC_RESET1, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Reset to default"), BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, IDC_VIEW2, BUTTONADDTOOLTIP, (WPARAM)LPGENW("View webpage"), BATF_UNICODE);
+ SendDlgItemMessage(hwndDlg, IDC_RESET2, BUTTONADDTOOLTIP, (WPARAM)LPGENW("Reset to default"), BATF_UNICODE);
+
+ // save the handle for the contact
+ WindowList_Add(hWindowList, hwndDlg, hContact);
+
+ // start to get the settings
+ // if the setting not exist, leave the dialog box blank
+ if (!g_plugin.getWString(hContact, "ID", &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_ID, dbv.pwszVal);
+ // check if the station is a default station
+ CheckDlgButton(hwndDlg, IDC_DEFA, mir_wstrcmp(dbv.pwszVal, opt.Default) != 0 ? BST_CHECKED : BST_UNCHECKED);
+ db_free(&dbv);
+ }
+ if (!g_plugin.getWString(hContact, "Nick", &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_NAME, dbv.pwszVal);
+ db_free(&dbv);
+ }
+ if (!g_plugin.getWString(hContact, "Log", &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_LOG, dbv.pwszVal);
+ // if the log path is not empty, check the checkbox for external log
+ if (dbv.pwszVal[0]) CheckDlgButton(hwndDlg, IDC_External, BST_CHECKED);
+ db_free(&dbv);
+ }
+ // enable/disable the browse button depending on the value of external log checkbox
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE), (BYTE)IsDlgButtonChecked(hwndDlg, IDC_External));
+
+ // other checkbox options
+ CheckDlgButton(hwndDlg, IDC_DPop, g_plugin.getByte(hContact, "DPopUp", FALSE) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_DAutoUpdate, g_plugin.getByte(hContact, "DAutoUpdate", FALSE) ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_Internal, g_plugin.getByte(hContact, "History", 0) ? BST_CHECKED : BST_UNCHECKED);
+
+ if (!g_plugin.getWString(hContact, "InfoURL", &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_IURL, dbv.pwszVal);
+ db_free(&dbv);
+ }
+ if (!g_plugin.getWString(hContact, "MapURL", &dbv)) {
+ SetDlgItemText(hwndDlg, IDC_MURL, dbv.pwszVal);
+ db_free(&dbv);
+ }
+
+ // display the dialog box and free memory
+ Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, MODULENAME, "EditSetting_");
+ ShowWindow(hwndDlg, SW_SHOW);
+ break;
+
+ case WM_COMMAND:
+ wndData = (CntSetWndDataType*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ hContact = wndData ? wndData->hContact : NULL;
+
+ switch (LOWORD(wParam)) {
+ case IDC_ID:
+ // check if there are 2 parts in the ID (svc/id) seperated by "/"
+ // if not, don't let user change the setting
+ GetDlgItemText(hwndDlg, IDC_ID, str, _countof(str));
+ chop = wcsstr(str, L"/");
+ if (chop == nullptr)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), FALSE);
+ else
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), TRUE);
+ break;
+
+ case IDC_NAME:
+ // check if station name is entered
+ // if not, don't let user change the setting
+ GetDlgItemText(hwndDlg, IDC_NAME, str, _countof(str));
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), str[0] != 0);
+ break;
+
+ case IDC_GETNAME:
+ // the button for getting station name from the internet
+ // this function uses the ID search for add/find weather station
+ if (!CheckSearch())
+ return TRUE; // don't download if update is in progress
+
+ // get the weather update data using the string in the ID field
+ GetDlgItemText(hwndDlg, IDC_ID, str, _countof(str));
+ GetSvc(str);
+ sData = GetWIData(str);
+ GetDlgItemText(hwndDlg, IDC_ID, str, _countof(str));
+ GetID(str);
+ // if ID search is available, do it
+ if (sData->IDSearch.Available) {
+ // load the page
+ mir_snprintf(loc, sData->IDSearch.SearchURL, str);
+ str[0] = 0;
+ wchar_t *pData = nullptr;
+ if (InternetDownloadFile(loc, nullptr, sData->UserAgent, &pData) == 0) {
+ wchar_t *szInfo = pData;
+ wchar_t* search = wcsstr(szInfo, sData->IDSearch.NotFoundStr);
+
+ // if the page is found (ie. valid ID), get the name of the city
+ if (search == nullptr)
+ GetDataValue(&sData->IDSearch.Name, str, &szInfo);
+ }
+ // free memory
+ mir_free(pData);
+ }
+
+ // give no station name but only ID if the search is unavailable
+ if (str[0] != 0)
+ SetDlgItemText(hwndDlg, IDC_NAME, str);
+ break;
+
+ case IDC_External:
+ // enable/disable the borwse button depending if the external log is enabled
+ EnableWindow(GetDlgItem(hwndDlg, IDC_BROWSE), (BYTE)IsDlgButtonChecked(hwndDlg, IDC_External));
+ if (!(BYTE)IsDlgButtonChecked(hwndDlg, IDC_External))
+ return TRUE;
+ __fallthrough;
+
+ case IDC_BROWSE:
+ // browse for the external log file
+ GetDlgItemText(hwndDlg, IDC_LOG, str, _countof(str));
+ // Initialize OPENFILENAME
+ memset(&ofn, 0, sizeof(OPENFILENAME));
+ ofn.lStructSize = sizeof(OPENFILENAME);
+ ofn.hwndOwner = hwndDlg;
+ ofn.lpstrFile = str;
+ ofn.nMaxFile = _countof(str);
+ // set filters
+ wcsncpy(filter, TranslateT("Text Files"), _countof(filter) - 1);
+ mir_wstrncat(filter, L" (*.txt)", _countof(filter) - mir_wstrlen(filter));
+ pfilter = filter + mir_wstrlen(filter) + 1;
+ wcsncpy(pfilter, L"*.txt", _countof(filter) - 1);
+ pfilter = pfilter + mir_wstrlen(pfilter) + 1;
+ wcsncpy(pfilter, TranslateT("All Files"), _countof(filter) - 1);
+ mir_wstrncat(pfilter, L" (*.*)", _countof(filter) - mir_wstrlen(filter));
+ pfilter = pfilter + mir_wstrlen(pfilter) + 1;
+ wcsncpy(pfilter, L"*.*", _countof(filter) - 1);
+ pfilter = pfilter + mir_wstrlen(pfilter) + 1;
+ *pfilter = '\0';
+ ofn.lpstrFilter = filter;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrFileTitle = nullptr;
+ ofn.nMaxFileTitle = 0;
+ ofn.lpstrInitialDir = nullptr;
+ ofn.Flags = OFN_PATHMUSTEXIST;
+
+ // Display a Open dialog box and put the file name on the dialog
+ if (GetOpenFileName(&ofn))
+ SetDlgItemText(hwndDlg, IDC_LOG, ofn.lpstrFile);
+ // if there is no log file specified, disable external logging
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CHANGE), ofn.lpstrFile[0] != 0);
+ break;
+
+ case IDC_VIEW1:
+ // view the page for more info
+ GetDlgItemText(hwndDlg, IDC_IURL, str, _countof(str));
+ if (str[0] == 0)
+ return TRUE;
+ GetDlgItemText(hwndDlg, IDC_ID, str2, _countof(str2));
+ OpenUrl(str, str2);
+ break;
+
+ case IDC_VIEW2:
+ // view the page for weather map
+ GetDlgItemText(hwndDlg, IDC_MURL, str, _countof(str));
+ if (str[0] == 0)
+ return TRUE;
+ GetDlgItemText(hwndDlg, IDC_ID, str2, _countof(str2));
+ OpenUrl(str, str2);
+ break;
+
+ case IDC_RESET1:
+ // reset the more info url to service default
+ GetDlgItemText(hwndDlg, IDC_ID, str, _countof(str));
+ GetSvc(str);
+ sData = GetWIData(str);
+ SetDlgItemTextA(hwndDlg, IDC_IURL, sData->DefaultURL);
+ break;
+
+ case IDC_RESET2:
+ // reset the weathe map url to service default
+ GetDlgItemText(hwndDlg, IDC_ID, str, _countof(str));
+ GetSvc(str);
+ sData = GetWIData(str);
+ SetDlgItemText(hwndDlg, IDC_MURL, sData->DefaultMap);
+ break;
+
+ case IDC_SVCINFO:
+ // display the information of the ini file used by the weather station
+ GetDlgItemText(hwndDlg, IDC_ID, str, _countof(str));
+ GetSvc(str);
+ GetINIInfo(str);
+ break;
+
+ case IDC_CHANGE:
+ // temporary disable the protocol while applying the change
+ // start writing the new settings to database
+ GetDlgItemText(hwndDlg, IDC_ID, str, _countof(str));
+ g_plugin.setWString(hContact, "ID", str);
+ if ((BYTE)IsDlgButtonChecked(hwndDlg, IDC_DEFA)) { // if default station is set
+ mir_wstrcpy(opt.Default, str);
+ opt.DefStn = hContact;
+ g_plugin.setWString("Default", opt.Default);
+ }
+ GetDlgItemText(hwndDlg, IDC_NAME, city, _countof(city));
+ g_plugin.setWString(hContact, "Nick", city);
+ mir_snwprintf(str2, TranslateT("Current weather information for %s."), city);
+ if ((BYTE)IsDlgButtonChecked(hwndDlg, IDC_External)) {
+ GetDlgItemText(hwndDlg, IDC_LOG, str, _countof(str));
+ g_plugin.setWString(hContact, "Log", str);
+ }
+ else g_plugin.delSetting(hContact, "Log");
+
+ GetDlgItemText(hwndDlg, IDC_IURL, str, _countof(str));
+ g_plugin.setWString(hContact, "InfoURL", str);
+
+ GetDlgItemText(hwndDlg, IDC_MURL, str, _countof(str));
+ g_plugin.setWString(hContact, "MapURL", str);
+ g_plugin.setWord(hContact, "Status", ID_STATUS_OFFLINE);
+ g_plugin.setWord(hContact, "StatusIcon", ID_STATUS_OFFLINE);
+ AvatarDownloaded(hContact);
+ g_plugin.setWString(hContact, "About", str2);
+ g_plugin.setByte(hContact, "History", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_Internal));
+ g_plugin.setByte(hContact, "Overwrite", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_Overwrite));
+ g_plugin.setByte(hContact, "File", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_External));
+ g_plugin.setByte(hContact, "DPopUp", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_DPop));
+ g_plugin.setByte(hContact, "DAutoUpdate", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_DAutoUpdate));
+
+ // re-enable the protocol and update the data for the station
+ g_plugin.setString(hContact, "LastCondition", "None");
+ UpdateSingleStation(hContact, 0);
+ __fallthrough;
+
+ case IDCANCEL:
+ // remove the dialog from window list and close it
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ // remove the dialog from window list and close it
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ wndData = (CntSetWndDataType*)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ IcoLib_ReleaseIcon(wndData->hFile);
+ IcoLib_ReleaseIcon(wndData->hRename);
+ IcoLib_ReleaseIcon(wndData->hSrchAll);
+ IcoLib_ReleaseIcon(wndData->hUserDetail);
+ mir_free(wndData);
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, 0);
+
+ WindowList_Remove(hWindowList, hwndDlg);
+ Utils_SaveWindowPosition(hwndDlg, NULL, MODULENAME, "EditSetting_");
+ break;
+ }
+ return FALSE;
+}
+
+// show edit settings dialog
+// wParam = current contact
+INT_PTR EditSettings(WPARAM wParam, LPARAM)
+{
+ HWND hEditDlg = WindowList_Find(hWindowList, wParam);
+
+ // search the dialog list to prevent multiple instance of dialog for the same contact
+ if (hEditDlg != nullptr) {
+ // if the dialog box already opened, bring it to the front
+ SetForegroundWindow(hEditDlg);
+ SetFocus(hEditDlg);
+ }
+ else {
+ // if the dialog box is not opened, open a new one
+ if (IsMyContact(wParam))
+ CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_EDIT), nullptr, DlgProcChange, (LPARAM)wParam);
+ }
+
+ return 0;
+}
+
+//============ CONTACT DELETION ============
+//
+// when a contact is deleted, make sure some other contact take over the default station
+// wParam = deleted contact
+int ContactDeleted(WPARAM wParam, LPARAM)
+{
+ if (!IsMyContact(wParam))
+ return 0;
+
+ removeWindow(wParam);
+
+ // exit this function if it is not default station
+ ptrW tszID(g_plugin.getWStringA(wParam, "ID"));
+ if (tszID != NULL)
+ if (mir_wstrcmp(tszID, opt.Default))
+ return 0;
+
+ // now the default station is deleted, try to get a new one
+
+ // start looking for other weather stations
+ for (auto &hContact : Contacts(MODULENAME)) {
+ tszID = g_plugin.getWStringA(hContact, "ID");
+ if (tszID == NULL)
+ continue;
+
+ // if the station is not a default station, set it as the new default station
+ // this is the first weather station encountered from the search
+ if (mir_wstrcmp(opt.Default, tszID)) {
+ wcsncpy_s(opt.Default, tszID, _TRUNCATE);
+ opt.DefStn = hContact;
+ ptrW tszNick(g_plugin.getWStringA(hContact, "Nick"));
+ if (tszNick != NULL) {
+ wchar_t str[255];
+ mir_snwprintf(str, TranslateT("%s is now the default weather station"), (wchar_t*)tszNick);
+ MessageBox(nullptr, str, TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
+ }
+ g_plugin.setWString("Default", opt.Default);
+ return 0; // exit this function quickly
+ }
+ }
+
+ // got here if no more weather station left
+ opt.Default[0] = 0; // no default station
+ opt.DefStn = NULL;
+ g_plugin.setWString("Default", opt.Default);
+ return 0;
+}
+
+BOOL IsMyContact(MCONTACT hContact)
+{
+ const char *szProto = GetContactProto(hContact);
+ return szProto != nullptr && mir_strcmp(MODULENAME, szProto) == 0;
+}
diff --git a/protocols/Weather/src/weather_conv.cpp b/protocols/Weather/src/weather_conv.cpp
new file mode 100644
index 0000000000..c46b079531
--- /dev/null
+++ b/protocols/Weather/src/weather_conv.cpp
@@ -0,0 +1,637 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file contain the source related unit conversion, icon assignment,
+string conversions, display text parsing, etc
+*/
+
+#include "stdafx.h"
+
+//============ SOME HELPER FUNCTIONS ============
+
+// see if a string is a number
+// s = the string to be determined
+// return value = true if the string is a number, false if it isn't
+BOOL is_number(wchar_t *s)
+{
+ BOOL tag = FALSE;
+ // looking character by character
+ // for a number: numerous spaces in front, then optional +/-, then the number
+ // don't care anything that comes after it
+ while (*s != '\0') {
+ if (*s >= '0' && *s <= '9') return TRUE;
+ else if (*s == ' ');
+ else if (*s != '+' && *s != '-') return FALSE;
+ else if ((*s == '+' || *s == '-') && !tag) tag = TRUE;
+ else return FALSE;
+ s++;
+ }
+ return FALSE;
+}
+
+static void numToStr(double num, wchar_t *str, size_t strSize)
+{
+ int i = (int)(num * (opt.NoFrac ? 10 : 100));
+ int u = abs(i);
+
+ int r = u % 10;
+ int w = u / 10 + (r >= 5);
+
+ if (opt.NoFrac)
+ r = 0;
+ else {
+ r = w % 10;
+ w /= 10;
+ }
+
+ if (i < 0 && (w || r)) w = -w;
+ if (r)
+ mir_snwprintf(str, strSize, L"%i.%i", w, r);
+ else
+ mir_snwprintf(str, strSize, L"%i", w);
+}
+
+//============ UNIT CONVERSIONS ============
+
+// temperature conversion
+// tempchar = the string containing the temperature value
+// unit = the unit for temperature
+// return value = the converted temperature with degree sign and unit; if fails, return N/A
+void GetTemp(wchar_t *tempchar, wchar_t *unit, wchar_t* str)
+{
+ // unit can be C, F
+ double temp;
+ wchar_t tstr[20];
+
+ TrimString(tempchar);
+ if (tempchar[0] == '-' && tempchar[1] == ' ')
+ memmove(&tempchar[1], &tempchar[2], sizeof(wchar_t)*(mir_wstrlen(&tempchar[2]) + 1));
+
+ // quit if the value obtained is N/A or not a number
+ if (!mir_wstrcmp(tempchar, NODATA) || !mir_wstrcmp(tempchar, L"N/A")) {
+ mir_wstrcpy(str, tempchar);
+ return;
+ }
+ if (!is_number(tempchar)) {
+ mir_wstrcpy(str, NODATA);
+ return;
+ }
+
+ // convert the string to an integer
+ temp = _wtof(tempchar);
+
+ // convert all to F first
+ if (!mir_wstrcmpi(unit, L"C")) temp = (temp * 9 / 5) + 32;
+ else if (!mir_wstrcmpi(unit, L"K")) temp = ((temp - 273.15) * 9 / 5) + 32;
+
+ // convert to apporiate unit
+ switch (opt.tUnit) {
+ case 1:
+ // rounding
+ numToStr((temp - 32) / 9 * 5, tstr, _countof(tstr));
+ if (opt.DoNotAppendUnit)
+ wcsncpy_s(str, MAX_DATA_LEN, tstr, _TRUNCATE);
+ else
+ mir_snwprintf(str, MAX_DATA_LEN, L"%s%sC", tstr, opt.DegreeSign);
+ break;
+
+ case 2:
+ numToStr(temp, tstr, _countof(tstr));
+ if (opt.DoNotAppendUnit)
+ wcsncpy_s(str, MAX_DATA_LEN, tstr, _TRUNCATE);
+ else
+ mir_snwprintf(str, MAX_DATA_LEN, L"%s%sF", tstr, opt.DegreeSign);
+ break;
+ }
+}
+
+// temperature conversion
+// tempchar = the string containing the pressure value
+// unit = the unit for pressure
+// return value = the converted pressure with unit; if fail, return the original string
+void GetPressure(wchar_t *tempchar, wchar_t *unit, wchar_t* str)
+{
+ // unit can be kPa, hPa, mb, in, mm, torr
+ double tempunit = 0, output;
+ int intunit;
+
+ // convert the string to a floating point number (always positive)
+ // if it end up with 0, then it's not a number, return the original string and quit
+ output = _wtof(tempchar);
+ if (output == 0) {
+ mir_wstrcpy(str, tempchar);
+ return;
+ }
+
+ // convert all to mb first
+ if (!mir_wstrcmpi(unit, L"KPA"))
+ tempunit = (double)output * 10;
+ else if (!mir_wstrcmpi(unit, L"HPA"))
+ tempunit = (double)output;
+ else if (!mir_wstrcmpi(unit, L"MB"))
+ tempunit = (double)output;
+ else if (!mir_wstrcmpi(unit, L"IN"))
+ tempunit = (double)output * 33.86388;
+ else if (!mir_wstrcmpi(unit, L"MM"))
+ tempunit = (double)output * 1.33322;
+ else if (!mir_wstrcmpi(unit, L"TORR"))
+ tempunit = (double)output * 1.33322;
+
+ // convert to apporiate unit
+ switch (opt.pUnit) {
+ case 1:
+ intunit = (int)(tempunit + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("kPa"));
+ break;
+ case 2:
+ intunit = (int)(tempunit + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i %s", intunit, opt.DoNotAppendUnit ? L"" : TranslateT("mb"));
+ break;
+ case 3:
+ intunit = (int)((tempunit * 10 / 33.86388) + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("in"));
+ break;
+ case 4:
+ intunit = (int)((tempunit * 10 / 1.33322) + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("mm"));
+ break;
+ default:
+ mir_wstrcpy(str, tempchar);
+ break;
+
+ }
+}
+
+// speed conversion
+// tempchar = the string containing the speed value
+// unit = the unit for speed
+// return value = the converted speed with unit; if fail, return _T(""
+void GetSpeed(wchar_t *tempchar, wchar_t *unit, wchar_t *str)
+{
+ // unit can be km/h, mph, m/s, knots
+ double tempunit;
+ wchar_t tstr[20];
+
+ str[0] = 0;
+
+ // convert the string into an integer (always positive)
+ // if the result is 0, then the string is not a number, return _T(""
+ tempunit = _wtof(tempchar);
+ if (tempunit == 0 && tempchar[0] != '0')
+ return;
+
+ // convert all to m/s first
+ if (!mir_wstrcmpi(unit, L"KM/H"))
+ tempunit /= 3.6;
+ // else if ( !mir_wstrcmpi(unit, L"M/S")
+ // tempunit = tempunit;
+ else if (!mir_wstrcmpi(unit, L"MPH"))
+ tempunit *= 0.44704;
+ else if (!mir_wstrcmpi(unit, L"KNOTS"))
+ tempunit *= 0.514444;
+
+ // convert to apporiate unit
+ switch (opt.wUnit) {
+ case 1:
+ numToStr(tempunit * 3.6, tstr, _countof(tstr));
+ mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("km/h"));
+ break;
+ case 2:
+ numToStr(tempunit, tstr, _countof(tstr));
+ mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("m/s"));
+ break;
+ case 3:
+ numToStr(tempunit / 0.44704, tstr, _countof(tstr));
+ mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("mph"));
+ break;
+ case 4:
+ numToStr(tempunit / 0.514444, tstr, _countof(tstr));
+ mir_snwprintf(str, MAX_DATA_LEN, L"%s %s", tstr, opt.DoNotAppendUnit ? L"" : TranslateT("knots"));
+ break;
+ }
+}
+
+// distance conversion
+// tempchar = the string containing the distance value
+// unit = the unit for distance
+// return value = the converted distance with unit; if fail, return original string
+void GetDist(wchar_t *tempchar, wchar_t *unit, wchar_t *str)
+{
+ // unit can be km, miles
+ double tempunit = 0, output;
+ int intunit;
+
+ // convert the string to a floating point number (always positive)
+ // if it end up with 0, then it's not a number, return the original string and quit
+ output = _wtof(tempchar);
+ if (output == 0) {
+ mir_wstrcpy(str, tempchar);
+ return;
+ }
+
+ // convert all to km first
+ if (!mir_wstrcmpi(unit, L"KM"))
+ tempunit = (double)output;
+ else if (!mir_wstrcmpi(unit, L"MILES"))
+ tempunit = (double)output * 1.609;
+
+ // convert to apporiate unit
+ switch (opt.vUnit) {
+ case 1:
+ intunit = (int)((tempunit * 10) + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("km"));
+ break;
+ case 2:
+ intunit = (int)((tempunit * 10 / 1.609) + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("miles"));
+ break;
+ default:
+ mir_wstrcpy(str, tempchar);
+ break;
+ }
+}
+
+// elevation conversion
+// tempchar = the string containing the elevation value
+// unit = the unit for elevation
+// return value = the converted elevation with unit; if fail, return original string
+void GetElev(wchar_t *tempchar, wchar_t *unit, wchar_t *str)
+{
+ // unit can be ft, m
+ double tempunit = 0, output;
+ int intunit;
+
+ // convert the string to a floating point number (always positive)
+ // if it end up with 0, then it's not a number, return the original string and quit
+ output = _wtof(tempchar);
+ if (output == 0) {
+ mir_wstrcpy(str, tempchar);
+ return;
+ }
+
+ // convert all to m first
+ if (!mir_wstrcmpi(unit, L"M"))
+ tempunit = (double)output;
+ else if (!mir_wstrcmpi(unit, L"FT"))
+ tempunit = (double)output / 3.28;
+
+ // convert to apporiate unit
+ switch (opt.eUnit) {
+ case 1:
+ intunit = (int)((tempunit * 10 * 3.28) + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("ft"));
+ break;
+ case 2:
+ intunit = (int)((tempunit * 10) + 0.5);
+ mir_snwprintf(str, MAX_DATA_LEN, L"%i.%i %s", intunit / 10, intunit % 10, opt.DoNotAppendUnit ? L"" : TranslateT("m"));
+ break;
+ default:
+ mir_wstrcpy(str, tempchar);
+ break;
+ }
+}
+
+//============ CONDITION ICON ASSIGNMENT ============
+
+// assign the contact icon (status) from the condition string
+// the description may be different between different sources
+// cond = the string for weather condition
+// return value = status for the icon (ONLINE, OFFLINE, etc)
+
+static const wchar_t *statusStr[10] = { L"Lightning", L"Fog", L"Snow Shower", L"Snow", L"Rain Shower", L"Rain", L"Partly Cloudy", L"Cloudy", L"Sunny", L"N/A" };
+static const WORD statusValue[10] = { LIGHT, FOG, SSHOWER, SNOW, RSHOWER, RAIN, PCLOUDY, CLOUDY, SUNNY, NA };
+
+WORD GetIcon(const wchar_t* cond, WIDATA *Data)
+{
+ // set the icon using ini
+ for (int i = 0; i < 10; i++)
+ if (IsContainedInCondList(cond, &Data->CondList[i]))
+ return statusValue[i];
+
+ // internal detection
+ if (
+ wcsstr(cond, L"mainy sunny") != nullptr ||
+ wcsstr(cond, L"mainy clear") != nullptr ||
+ wcsstr(cond, L"partly cloudy") != nullptr ||
+ wcsstr(cond, L"mostly") != nullptr ||
+ wcsstr(cond, L"clouds") != nullptr) {
+ return PCLOUDY;
+ }
+ else if (
+ wcsstr(cond, L"sunny") != nullptr ||
+ wcsstr(cond, L"clear") != nullptr ||
+ wcsstr(cond, L"fair") != nullptr) {
+ return SUNNY;
+ }
+ else if (
+ wcsstr(cond, L"thunder") != nullptr ||
+ wcsstr(cond, L"t-storm") != nullptr) {
+ return LIGHT;
+ }
+ else if (
+ wcsstr(cond, L"cloud") != nullptr ||
+ wcsstr(cond, L"overcast") != nullptr) {
+ return CLOUDY;
+ }
+ else if (
+ wcsstr(cond, L"fog") != nullptr ||
+ wcsstr(cond, L"mist") != nullptr ||
+ wcsstr(cond, L"smoke") != nullptr ||
+ wcsstr(cond, L"sand") != nullptr ||
+ wcsstr(cond, L"dust") != nullptr ||
+ wcsstr(cond, L"haze") != nullptr) {
+ return FOG;
+ }
+ else if (
+ (wcsstr(cond, L"shower") != nullptr && wcsstr(cond, L"snow") != nullptr) ||
+ wcsstr(cond, L"flurries") != nullptr) {
+ return SSHOWER;
+ }
+ else if (
+ wcsstr(cond, L"rain shower") != nullptr ||
+ wcsstr(cond, L"shower") != nullptr) {
+ return RSHOWER;
+ }
+ else if (
+ wcsstr(cond, L"snow") != nullptr ||
+ wcsstr(cond, L"ice") != nullptr ||
+ wcsstr(cond, L"freezing") != nullptr ||
+ wcsstr(cond, L"wintry") != nullptr) {
+ return SNOW;
+ }
+ else if (
+ wcsstr(cond, L"drizzle") != nullptr ||
+ wcsstr(cond, L"rain") != nullptr) {
+ return RAIN;
+ }
+
+ // set the icon using langpack
+ for (int i = 0; i < 9; i++) {
+ wchar_t LangPackStr[64], LangPackStr1[128];
+ int j = 0;
+ do {
+ j++;
+ // using the format _T("# Weather <condition name> <counter> #"
+ mir_snwprintf(LangPackStr, L"# Weather %s %i #", statusStr[i], j);
+ wcsncpy_s(LangPackStr1, TranslateW(LangPackStr), _TRUNCATE);
+ CharLowerBuff(LangPackStr1, (DWORD)mir_wstrlen(LangPackStr1));
+ if (wcsstr(cond, LangPackStr1) != nullptr)
+ return statusValue[i];
+ // loop until the translation string exists (ie, the translated string is differ from original)
+ } while (mir_wstrcmp(TranslateW(LangPackStr), LangPackStr));
+ }
+
+ return NA;
+}
+
+//============ STRING CONVERSIONS ============
+//
+// this function convert the string to the format with 1 upper case followed by lower case char
+void CaseConv(wchar_t *str)
+{
+ bool nextUp = true;
+
+ CharLowerBuffW(str, (DWORD)mir_wstrlen(str));
+ for (wchar_t *pstr = str; *pstr; pstr++) {
+ if (*pstr == ' ' || *pstr == '-')
+ nextUp = true;
+ else if (nextUp) {
+ CharUpperBuffW(pstr, 1);
+ nextUp = false;
+ }
+ }
+}
+
+// the next 2 functions are copied from miranda source
+// str = the string to modify
+//
+void TrimString(char *str)
+{
+ size_t len, start;
+
+ len = mir_strlen(str);
+ while (len && (unsigned char)str[len - 1] <= ' ') str[--len] = 0;
+ for (start = 0; (unsigned char)str[start] <= ' ' && str[start]; start++);
+ memmove(str, str + start, len - start + 1);
+}
+
+void TrimString(WCHAR *str)
+{
+ size_t len, start;
+
+ len = mir_wstrlen(str);
+ while (len && (unsigned char)str[len - 1] <= ' ') str[--len] = 0;
+ for (start = 0; (unsigned char)str[start] <= ' ' && str[start]; start++);
+ memmove(str, str + start, (len - start + 1)*sizeof(WCHAR));
+}
+
+// convert \t to tab and \n to linefeed
+void ConvertBackslashes(char *str)
+{
+ for (char *pstr = str; *pstr; pstr = CharNextA(pstr)) {
+ if (*pstr == '\\') {
+ switch (pstr[1]) {
+ case 'n': *pstr = '\n'; break;
+ case 't': *pstr = '\t'; break;
+ default: *pstr = pstr[1]; break;
+ }
+ memmove(pstr + 1, pstr + 2, mir_strlen(pstr + 2) + 1);
+ }
+ }
+}
+
+// replace spaces with _T("%20"
+// dis = original string
+// return value = the modified string with space -> _T("%20"
+char *GetSearchStr(char *dis)
+{
+ char *pstr = dis;
+ size_t len = mir_strlen(dis);
+ while (*pstr != 0) {
+ if (*pstr == ' ') {
+ memmove(pstr + 3, pstr + 1, len);
+ memcpy(pstr, L"%20", 3);
+ pstr += 2;
+ }
+ pstr++;
+ len--;
+ }
+ return dis;
+}
+
+//============ ICON ASSIGNMENT ============
+//
+// make display and history strings
+// w = WEATHERINFO data to be parsed
+// dis = the string to parse
+// return value = the parsed string
+wchar_t* GetDisplay(WEATHERINFO *w, const wchar_t *dis, wchar_t* str)
+{
+ wchar_t lpzDate[32], chr;
+ char name[256], temp[2];
+ DBVARIANT dbv;
+ size_t i;
+
+ // Clear the string
+ str[0] = 0;
+
+ // looking character by character
+ for (i = 0; i < mir_wstrlen(dis); i++) {
+ // for the escape characters
+ if (dis[i] == '\\') {
+ i++;
+ chr = dis[i];
+ switch (chr) {
+ case '%': mir_wstrcat(str, L"%"); break;
+ case 't': mir_wstrcat(str, L"\t"); break;
+ case 'n': mir_wstrcat(str, L"\r\n"); break;
+ case '\\': mir_wstrcat(str, L"\\"); break;
+ }
+ }
+
+ // for the % varaibles
+ else if (dis[i] == '%') {
+ i++;
+ chr = dis[i];
+ // turn capitalized characters to small case
+ if (chr < 'a' && chr != '[' && chr != '%') chr = (char)((int)chr + 32);
+ switch (chr) {
+ case 'c': mir_wstrcat(str, w->cond); break;
+ case 'd': // get the current date
+ GetDateFormat(LOCALE_USER_DEFAULT, DATE_SHORTDATE, nullptr, nullptr, lpzDate, _countof(lpzDate));
+ mir_wstrcat(str, lpzDate); break;
+ case 'e': mir_wstrcat(str, w->dewpoint); break;
+ case 'f': mir_wstrcat(str, w->feel); break;
+ case 'h': mir_wstrcat(str, w->high); break;
+ case 'i': mir_wstrcat(str, w->winddir); break;
+ case 'l': mir_wstrcat(str, w->low); break;
+ case 'm': mir_wstrcat(str, w->humid); break;
+ case 'n': mir_wstrcat(str, w->city); break;
+ case 'p': mir_wstrcat(str, w->pressure); break;
+ case 'r': mir_wstrcat(str, w->sunrise); break;
+ case 's': mir_wstrcat(str, w->id); break;
+ case 't': mir_wstrcat(str, w->temp); break;
+ case 'u':
+ if (mir_wstrcmp(w->update, NODATA)) mir_wstrcat(str, w->update);
+ else mir_wstrcat(str, TranslateT("<unknown time>"));
+ break;
+ case 'v': mir_wstrcat(str, w->vis); break;
+ case 'w': mir_wstrcat(str, w->wind); break;
+ case 'y': mir_wstrcat(str, w->sunset); break;
+ case '%': mir_wstrcat(str, L"%"); break;
+ case '[': // custom variables
+ i++;
+ name[0] = 0;
+ // read the entire variable name
+ while (dis[i] != ']' && i < mir_wstrlen(dis)) {
+ mir_snprintf(temp, "%c", dis[i++]);
+ mir_strcat(name, temp);
+ }
+ // access the database to get its value
+ if (!db_get_ws(w->hContact, WEATHERCONDITION, name, &dbv)) {
+ if (dbv.pwszVal != TranslateW(NODATA) && dbv.pwszVal != TranslateT("<Error>"))
+ mir_wstrcat(str, dbv.pwszVal);
+ db_free(&dbv);
+ }
+ break;
+ }
+ }
+ // if the character is not a variable, write the original character to the new string
+ else {
+ mir_snwprintf(lpzDate, L"%c", dis[i]);
+ mir_wstrcat(str, lpzDate);
+ }
+ }
+
+ return str;
+}
+
+wchar_t svcReturnText[MAX_TEXT_SIZE];
+INT_PTR GetDisplaySvcFunc(WPARAM wParam, LPARAM lParam)
+{
+ WEATHERINFO winfo = LoadWeatherInfo(wParam);
+ return (INT_PTR)GetDisplay(&winfo, (wchar_t*)lParam, svcReturnText);
+}
+
+//============ ID MANAGEMENT ============
+//
+// get service data module internal name
+// mod/id <- the mod part
+// pszID = original 2-part id, return the service internal name
+void GetSvc(wchar_t *pszID)
+{
+ wchar_t *chop = wcsstr(pszID, L"/");
+ if (chop != nullptr) *chop = '\0';
+ else pszID[0] = 0;
+}
+
+// get the id use for update without the service internal name
+// mod/id <- the id part
+// pszID = original 2-part id, return the single part id
+void GetID(wchar_t *pszID)
+{
+ wchar_t *chop = wcsstr(pszID, L"/");
+ if (chop != nullptr) mir_wstrcpy(pszID, chop + 1);
+ else pszID[0] = 0;
+}
+
+//============ WEATHER ERROR CODE ============
+//
+// Get the text when an error code is specified
+// code = the error code obtained when updating weather
+// str = the string for the error
+//
+wchar_t *GetError(int code)
+{
+ wchar_t *str, str2[100];
+ switch (code) {
+ case 10: str = E10; break;
+ case 11: str = E11; break;
+ case 12: str = E12; break;
+ case 20: str = E20; break;
+ case 30: str = E30; break;
+ case 40: str = E40; break;
+ case 42: str = E42; break;
+ case 43: str = E43; break;
+ case 99: str = E99; break;
+ case 204: str = E204; break;
+ case 301: str = E301; break;
+ case 305: str = E305; break;
+ case 307: str = E307; break;
+ case 400: str = E400; break;
+ case 401: str = E401; break;
+ case 402: str = E402; break;
+ case 403: str = E403; break;
+ case 404: str = E404; break;
+ case 405: str = E405; break;
+ case 407: str = E407; break;
+ case 410: str = E410; break;
+ case 500: str = E500; break;
+ case 502: str = E502; break;
+ case 503: str = E503; break;
+ case 504: str = E504; break;
+ default:
+ mir_snwprintf(str2, TranslateT("HTTP Error %i"), code);
+ str = str2;
+ break;
+ }
+ return mir_wstrdup(str);
+}
diff --git a/protocols/Weather/src/weather_data.cpp b/protocols/Weather/src/weather_data.cpp
new file mode 100644
index 0000000000..e36ae9dc5b
--- /dev/null
+++ b/protocols/Weather/src/weather_data.cpp
@@ -0,0 +1,439 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file contain the source related loading, obtaining, and
+saving individual weather data for a weather contact.
+*/
+
+#include "stdafx.h"
+
+//============ LOAD WEATHER INFO FROM A CONTACT ============
+// get station ID from DB
+// hContact = the current contact handle
+// return value = the string for station ID
+//
+void GetStationID(MCONTACT hContact, wchar_t* id, int idlen)
+{
+ // accessing the database
+ if (db_get_wstatic(hContact, MODULENAME, "ID", id, idlen))
+ id[0] = 0;
+}
+
+// initialize weather info by loading values from database
+// hContact = current contact handle
+// return value = the current weather information in WEATHERINFO struct
+WEATHERINFO LoadWeatherInfo(MCONTACT hContact)
+{
+ // obtaining values from the DB
+ // assuming station ID must exist at all time, but others does not have to
+ // if the string is not found in database, a value of "N/A" is stored in the field
+ WEATHERINFO winfo;
+ winfo.hContact = hContact;
+ GetStationID(hContact, winfo.id, _countof(winfo.id));
+
+ if (db_get_wstatic(hContact, MODULENAME, "Nick", winfo.city, _countof(winfo.city)))
+ wcsncpy(winfo.city, NODATA, _countof(winfo.city) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Update", winfo.update, _countof(winfo.update)))
+ wcsncpy(winfo.update, NODATA, _countof(winfo.update) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Condition", winfo.cond, _countof(winfo.cond)))
+ wcsncpy(winfo.cond, NODATA, _countof(winfo.cond) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Temperature", winfo.temp, _countof(winfo.temp)))
+ wcsncpy(winfo.temp, NODATA, _countof(winfo.temp) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "High", winfo.high, _countof(winfo.high)))
+ wcsncpy(winfo.high, NODATA, _countof(winfo.high) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Low", winfo.low, _countof(winfo.low)))
+ wcsncpy(winfo.low, NODATA, _countof(winfo.low) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Sunset", winfo.sunset, _countof(winfo.sunset)))
+ wcsncpy(winfo.sunset, NODATA, _countof(winfo.sunset) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Sunrise", winfo.sunrise, _countof(winfo.sunrise)))
+ wcsncpy(winfo.sunrise, NODATA, _countof(winfo.sunrise) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Wind Speed", winfo.wind, _countof(winfo.wind)))
+ wcsncpy(winfo.wind, NODATA, _countof(winfo.wind) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Wind Direction", winfo.winddir, _countof(winfo.winddir)))
+ wcsncpy(winfo.winddir, NODATA, _countof(winfo.winddir) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Dewpoint", winfo.dewpoint, _countof(winfo.dewpoint)))
+ wcsncpy(winfo.dewpoint, NODATA, _countof(winfo.dewpoint) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Pressure", winfo.pressure, _countof(winfo.pressure)))
+ wcsncpy(winfo.pressure, NODATA, _countof(winfo.pressure) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Visibility", winfo.vis, _countof(winfo.vis)))
+ wcsncpy(winfo.vis, NODATA, _countof(winfo.vis) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Humidity", winfo.humid, _countof(winfo.humid)))
+ wcsncpy(winfo.humid, NODATA, _countof(winfo.humid) - 1);
+ if (db_get_wstatic(hContact, WEATHERCONDITION, "Feel", winfo.feel, _countof(winfo.feel)))
+ wcsncpy(winfo.feel, NODATA, _countof(winfo.feel) - 1);
+
+ winfo.status = g_plugin.getWord(hContact, "StatusIcon", ID_STATUS_OFFLINE);
+ return winfo;
+}
+
+// getting weather setting from database
+// return 0 on success
+int DBGetData(MCONTACT hContact, char *setting, DBVARIANT *dbv)
+{
+ if (db_get_ws(hContact, WEATHERCONDITION, setting, dbv)) {
+ size_t len = mir_strlen(setting) + 1;
+ char *set = (char*)alloca(len + 1);
+ *set = '#';
+ memcpy(set + 1, setting, len);
+
+ if (db_get_ws(hContact, WEATHERCONDITION, set, dbv))
+ return 1;
+ }
+ return 0;
+}
+
+
+//============ ERASE OLD SETTINGS ============
+//
+// erase all current weather information from database
+// lastver = the last used version number in dword (using PLUGIN_MAKE_VERSION)
+void EraseAllInfo()
+{
+ wchar_t str[255];
+ int ContactCount = 0;
+ MCONTACT LastContact = NULL;
+ DBVARIANT dbv;
+ // loop through all contacts
+ for (auto &hContact : Contacts(MODULENAME)) {
+ g_plugin.setWord(hContact, "Status", ID_STATUS_OFFLINE);
+ g_plugin.setWord(hContact, "StatusIcon", ID_STATUS_OFFLINE);
+ db_unset(hContact, "CList", "MyHandle");
+ // clear all data
+ if (g_plugin.getWString(hContact, "Nick", &dbv)) {
+ g_plugin.setWString(hContact, "Nick", TranslateT("<Enter city name here>"));
+ g_plugin.setString(hContact, "LastLog", "never");
+ g_plugin.setString(hContact, "LastCondition", "None");
+ g_plugin.setString(hContact, "LastTemperature", "None");
+ }
+ else db_free(&dbv);
+
+ DBDataManage(hContact, WDBM_REMOVE, 0, 0);
+ db_set_s(hContact, "UserInfo", "MyNotes", "");
+ // reset update tag
+ g_plugin.setByte(hContact, "IsUpdated", FALSE);
+ // reset logging settings
+ if (!g_plugin.getWString(hContact, "Log", &dbv)) {
+ g_plugin.setByte(hContact, "File", (BYTE)(dbv.pwszVal[0] != 0));
+ db_free(&dbv);
+ }
+ else g_plugin.setByte(hContact, "File", FALSE);
+
+ // if no default station find, assign a new one
+ if (opt.Default[0] == 0) {
+ GetStationID(hContact, opt.Default, _countof(opt.Default));
+
+ opt.DefStn = hContact;
+ if (!g_plugin.getWString(hContact, "Nick", &dbv)) {
+ mir_snwprintf(str, TranslateT("%s is now the default weather station"), dbv.pwszVal);
+ db_free(&dbv);
+ MessageBox(nullptr, str, TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
+ }
+ }
+ // get the handle of the default station
+ if (opt.DefStn == NULL) {
+ if (!g_plugin.getWString(hContact, "ID", &dbv)) {
+ if (!mir_wstrcmp(dbv.pwszVal, opt.Default))
+ opt.DefStn = hContact;
+ db_free(&dbv);
+ }
+ }
+ ContactCount++; // increment counter
+ LastContact = hContact;
+ }
+
+ // if weather contact exists, set the status to online so it is ready for update
+ // if (ContactCount != 0) status = ONLINE;
+ // in case where the default station is missing
+ if (opt.DefStn == NULL && ContactCount != 0) {
+ if (!g_plugin.getWString(LastContact, "ID", &dbv)) {
+ wcsncpy(opt.Default, dbv.pwszVal, _countof(opt.Default) - 1);
+ db_free(&dbv);
+ }
+ opt.DefStn = LastContact;
+ if (!g_plugin.getWString(LastContact, "Nick", &dbv)) {
+ mir_snwprintf(str, TranslateT("%s is now the default weather station"), dbv.pwszVal);
+ db_free(&dbv);
+ MessageBox(nullptr, str, TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
+ }
+ }
+ // save option in case of default station changed
+ g_plugin.setWString("Default", opt.Default);
+}
+
+void ConvertDataValue(WIDATAITEM *UpdateData, wchar_t *Data)
+{
+ wchar_t str[MAX_DATA_LEN];
+
+ // convert the unit
+ if (mir_wstrcmp(Data, TranslateT("<Error>")) && mir_wstrcmp(Data, NODATA) && mir_wstrcmp(Data, TranslateW(NODATA))) {
+ // temperature
+ if (!mir_wstrcmp(UpdateData->Name, L"Temperature") || !mir_wstrcmp(UpdateData->Name, L"High") ||
+ !mir_wstrcmp(UpdateData->Name, L"Low") || !mir_wstrcmp(UpdateData->Name, L"Feel") ||
+ !mir_wstrcmp(UpdateData->Name, L"Dewpoint") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"C") || !mir_wstrcmpi(UpdateData->Unit, L"F") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"K")) {
+ GetTemp(Data, UpdateData->Unit, str);
+ mir_wstrcpy(Data, str);
+ }
+ // pressure
+ else if (!mir_wstrcmp(UpdateData->Name, L"Pressure") || !mir_wstrcmpi(UpdateData->Unit, L"HPA") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"KPA") || !mir_wstrcmpi(UpdateData->Unit, L"MB") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"TORR") || !mir_wstrcmpi(UpdateData->Unit, L"IN") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"MM")) {
+ GetPressure(Data, UpdateData->Unit, str);
+ mir_wstrcpy(Data, str);
+ }
+ // speed
+ else if (!mir_wstrcmp(UpdateData->Name, L"Wind Speed") || !mir_wstrcmpi(UpdateData->Unit, L"KM/H") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"M/S") || !mir_wstrcmpi(UpdateData->Unit, L"MPH") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"KNOTS")) {
+ GetSpeed(Data, UpdateData->Unit, str);
+ mir_wstrcpy(Data, str);
+ }
+ // visibility
+ else if (!mir_wstrcmp(UpdateData->Name, L"Visibility") || !mir_wstrcmpi(UpdateData->Unit, L"KM") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"MILES")) {
+ GetDist(Data, UpdateData->Unit, str);
+ mir_wstrcpy(Data, str);
+ }
+ // elevation
+ else if (!mir_wstrcmp(UpdateData->Name, L"Elevation") || !mir_wstrcmpi(UpdateData->Unit, L"FT") ||
+ !mir_wstrcmpi(UpdateData->Unit, L"M")) {
+ GetElev(Data, UpdateData->Unit, str);
+ mir_wstrcpy(Data, str);
+ }
+ // converting case for condition to the upper+lower format
+ else if (!mir_wstrcmpi(UpdateData->Unit, L"COND"))
+ CaseConv(Data);
+ // degree sign
+ else if (!mir_wstrcmpi(UpdateData->Unit, L"DEG")) {
+ if (!opt.DoNotAppendUnit) mir_wstrcat(Data, opt.DegreeSign);
+ }
+ // percent sign
+ else if (!mir_wstrcmpi(UpdateData->Unit, L"%")) {
+ if (!opt.DoNotAppendUnit) mir_wstrcat(Data, L"%");
+ }
+ // truncating strings for day/month to 2 or 3 characters
+ else if (!mir_wstrcmpi(UpdateData->Unit, L"DAY") || !mir_wstrcmpi(UpdateData->Unit, L"MONTH"))
+ if (opt.dUnit > 1 && mir_wstrlen(Data) > opt.dUnit)
+ Data[opt.dUnit] = '\0';
+ }
+}
+
+//============ GET THE VALUE OF A DATAITEM ============
+//
+// get the value of the data using the start, end strings
+// UpdateData = the WIDATAITEM struct containing start, end, unit
+// Data = the string containing weather data obtained from UpdateData
+// global var. used: szInfo = the downloaded string
+//
+void GetDataValue(WIDATAITEM *UpdateData, wchar_t *Data, wchar_t** szData)
+{
+ wchar_t last = 0, current, *start, *end;
+ unsigned startloc = 0, endloc = 0, respos = 0;
+ BOOL tag = FALSE, symb = FALSE;
+ wchar_t *szInfo = *szData;
+
+ Data[0] = 0;
+ // parse the data if available
+ if (UpdateData->Start[0] == 0 && UpdateData->End[0] == 0) return;
+ start = szInfo;
+ // the start string must be found
+ if (UpdateData->Start[0] != 0) {
+ start = wcsstr(szInfo, UpdateData->Start);
+ if (start != nullptr) {
+ // set the starting location for getting data
+ start += mir_wstrlen(UpdateData->Start);
+ szInfo = start;
+ }
+ }
+
+ // the end string must be found too
+ if (UpdateData->End[0] != 0)
+ end = wcsstr(szInfo, UpdateData->End);
+ else
+ end = wcsstr(szInfo, L" ");
+
+ if (end != nullptr) {
+ // set the ending location
+ startloc = 0;
+ endloc = end - szInfo;
+ end += mir_wstrlen(UpdateData->End);
+ last = '\n';
+ }
+
+ // ignore if not both of the string found - this prevent crashes
+ if (start != nullptr && end != nullptr) {
+ // begin reading the data from start location to end location
+ // remove all HTML tag in between, as well as leading space, ending space,
+ // multiple spaces, tabs, and return key
+ while (startloc < endloc) {
+ if (szInfo[startloc] == '<') tag = TRUE;
+ else if (szInfo[startloc] == '&' &&
+ (szInfo[startloc + 1] == ';' || szInfo[startloc + 2] == ';' || szInfo[startloc + 3] == ';' ||
+ szInfo[startloc + 4] == ';' || szInfo[startloc + 5] == ';' || szInfo[startloc + 6] == ';')) {
+ // ...but do NOT strip &minus;
+ if ((endloc - startloc) > 7 && wcsncmp(szInfo + startloc, L"&minus;", 7) == 0) {
+ Data[respos++] = '-';
+ startloc += 7;
+ continue;
+ }
+ symb = TRUE;
+ }
+ else if (szInfo[startloc] == '>') tag = FALSE;
+ else if (szInfo[startloc] == ';') symb = FALSE;
+ else {
+ if (!tag && !symb) {
+ current = szInfo[startloc];
+ if (current == '\n' || current == '\t' || current == ' ' || current == '\r')
+ current = ' ';
+ if (current != ' ' || last != ' ') {
+ if (last != '\n' && (respos != 0 || (respos == 0 && last != ' ')))
+ Data[respos++] = last;
+ last = current;
+ }
+ }
+ }
+ ++startloc;
+ // prevent crashes if the string go over maximun length -> generate an error
+ if (respos >= MAX_DATA_LEN) {
+ if (opt.ShowWarnings && UpdateData->Name[0] != 0 && mir_wstrcmp(UpdateData->Name, L"Ignore")) {
+ mir_snwprintf(Data, MAX_DATA_LEN, TranslateT("Error when obtaining data: %s"), UpdateData->Name);
+ WPShowMessage(Data, SM_WARNING);
+ }
+ wcsncpy(Data, TranslateT("<Error>"), MAX_DATA_LEN);
+ last = ' ';
+ respos = MAX_DATA_LEN - 1;
+ break;
+ }
+ }
+
+ // get the last character
+ if (last != ' ')
+ Data[respos++] = last;
+
+ // null terminate the string
+ Data[respos] = 0;
+
+ // convert the unit
+ ConvertDataValue(UpdateData, Data);
+
+ // remove the string before the data from szInfo
+ szInfo = end;
+ }
+ *szData = szInfo;
+}
+
+//============ ALLOCATE SPACE AND COPY STRING ============
+//
+// copy a string into a new memory location
+// Data = the field the data is copied to
+// Value = the original string, the string where data is copied from
+void wSetData(char **Data, const char *Value)
+{
+ if (Value[0] != 0)
+ *Data = mir_strdup(Value);
+ else
+ *Data = "";
+}
+
+void wSetData(WCHAR **Data, const char *Value)
+{
+ if (Value[0] != 0)
+ *Data = mir_a2u(Value);
+ else
+ *Data = L"";
+}
+
+void wSetData(WCHAR **Data, const WCHAR *Value)
+{
+ if (Value[0] != 0)
+ *Data = mir_wstrdup(Value);
+ else
+ *Data = L"";
+}
+
+// A safer free function that free memory for a string
+// Data = the string occuping the data to be freed
+void wfree(char **Data)
+{
+ if (*Data && mir_strlen(*Data) > 0)
+ mir_free(*Data);
+ *Data = nullptr;
+}
+
+void wfree(WCHAR **Data)
+{
+ if (*Data && mir_wstrlen(*Data) > 0)
+ mir_free(*Data);
+ *Data = nullptr;
+}
+
+//============ MANAGE THE ITEMS STORED IN DB ============
+// get single setting that is found
+// szSetting = the setting name
+// lparam = the counter
+int GetWeatherDataFromDB(const char *szSetting, void *lparam)
+{
+ LIST<char> *pList = (LIST<char>*)lparam;
+ pList->insert(mir_strdup(szSetting));
+ return 0;
+}
+
+// remove or display the weather information for a contact
+// hContact - the contact in which the info is going to be removed
+//
+void DBDataManage(MCONTACT hContact, WORD Mode, WPARAM wParam, LPARAM)
+{
+ // get all the settings and store them in a temporary list
+ LIST<char> arSettings(10);
+ db_enum_settings(hContact, GetWeatherDataFromDB, WEATHERCONDITION, &arSettings);
+
+ // begin deleting settings
+ auto T = arSettings.rev_iter();
+ for (auto &str : T) {
+ ptrW wszText(db_get_wsa(hContact, WEATHERCONDITION, str));
+ if (wszText == nullptr)
+ continue;
+
+ switch (Mode) {
+ case WDBM_REMOVE:
+ db_unset(hContact, WEATHERCONDITION, str);
+ break;
+
+ case WDBM_DETAILDISPLAY:
+ // skip the "WeatherInfo" variable
+ if (!mir_strcmp(str, "WeatherInfo") || !mir_strcmp(str, "Ignore") || str[0] == '#')
+ continue;
+
+ HWND hList = GetDlgItem((HWND)wParam, IDC_DATALIST);
+ LV_ITEM lvi = { 0 };
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.lParam = T.indexOf(&str);
+ lvi.pszText = TranslateW(_A2T(str));
+ lvi.iItem = ListView_InsertItem(hList, &lvi);
+ lvi.pszText = wszText;
+ ListView_SetItemText(hList, lvi.iItem, 1, wszText);
+ break;
+ }
+ mir_free(str);
+ }
+}
diff --git a/protocols/Weather/src/weather_http.cpp b/protocols/Weather/src/weather_http.cpp
new file mode 100644
index 0000000000..319663bfe7
--- /dev/null
+++ b/protocols/Weather/src/weather_http.cpp
@@ -0,0 +1,157 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file contain the source related to downloading weather info
+from the web using netlib
+*/
+
+#include "stdafx.h"
+
+HNETLIBUSER hNetlibUser;
+
+static int findHeader(const NETLIBHTTPREQUEST *nlhrReply, const char *hdr)
+{
+ for (int i = 0; i < nlhrReply->headersCount; i++) {
+ if (_stricmp(nlhrReply->headers[i].szName, hdr) == 0) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+//============ DOWNLOAD NEW WEATHER ============
+//
+// function to download webpage from the internet
+// szUrl = URL of the webpage to be retrieved
+// return value = 0 for success, 1 or HTTP error code for failure
+// global var used: szData, szInfo = containing the retrieved data
+//
+int InternetDownloadFile(char *szUrl, char *cookie, char *userAgent, wchar_t **szData)
+{
+ if (userAgent == nullptr || userAgent[0] == 0)
+ userAgent = NETLIB_USER_AGENT;
+
+ NETLIBHTTPHEADER headers[5];
+ headers[0].szName = "User-Agent";
+ headers[0].szValue = userAgent;
+ headers[1].szName = "Cache-Control";
+ headers[1].szValue = "no-cache";
+ headers[2].szName = "Pragma";
+ headers[2].szValue = "no-cache";
+ headers[3].szName = "Connection";
+ headers[3].szValue = "close";
+ headers[4].szName = "Cookie";
+ headers[4].szValue = cookie;
+
+ // initialize the netlib request
+ NETLIBHTTPREQUEST nlhr = { sizeof(nlhr) };
+ nlhr.requestType = REQUEST_GET;
+ nlhr.flags = NLHRF_DUMPASTEXT | NLHRF_HTTP11 | NLHRF_REDIRECT;
+ nlhr.szUrl = szUrl;
+ nlhr.headers = headers;
+ nlhr.headersCount = _countof(headers);
+
+ if (cookie == nullptr || cookie[0] == 0)
+ --nlhr.headersCount;
+
+ // download the page
+ NETLIBHTTPREQUEST *nlhrReply = Netlib_HttpTransaction(hNetlibUser, &nlhr);
+ if (nlhrReply == nullptr) {
+ // if the data does not downloaded successfully (ie. disconnected), then return 1000 as error code
+ *szData = (wchar_t*)mir_alloc(512);
+ // store the error code in szData
+ mir_wstrcpy(*szData, L"NetLib error occurred!!");
+ return NLHRF_REDIRECT;
+ }
+
+ // if the recieved code is 200 OK
+ int result;
+ if (nlhrReply->resultCode == 200) {
+ if (nlhrReply->dataLength) {
+ bool bIsUtf = false;
+ result = 0;
+
+ // allocate memory and save the retrieved data
+ int i = findHeader(nlhrReply, "Content-Type");
+ // look for Content-Type=utf-8 in header
+ if (i != -1 && strstr(_strlwr(nlhrReply->headers[i].szValue), "utf-8"))
+ bIsUtf = true;
+ else {
+ char *end = nlhrReply->pData;
+ while (end) {
+ // look for
+ // <meta http-equiv="Content-Type" content="utf-8" />
+ char* beg = strstr(end, "<meta");
+ if (beg)
+ {
+ end = strchr(beg, '>');
+ if (end)
+ {
+ char tmp = *end;
+ *end = 0;
+
+ char *method = strstr(beg, "http-equiv=\"");
+ if (method && _strnicmp(method + 12, "Content-Type", 12) == 0 && strstr(method, "utf-8")) {
+ bIsUtf = true;
+ *end = tmp;
+ break;
+ }
+ else *end = tmp;
+ }
+ }
+ else
+ break;
+ }
+ }
+
+ wchar_t *retVal = nullptr;
+ if (bIsUtf)
+ retVal = mir_utf8decodeW(nlhrReply->pData);
+ if (retVal == nullptr)
+ retVal = mir_a2u(nlhrReply->pData);
+ *szData = retVal;
+ }
+ else result = DATA_EMPTY;
+ }
+ // return error code if the recieved code is neither 200 OK nor 302 Moved
+ else {
+ // store the error code in szData
+ CMStringW wszError(FORMAT, L"Error occured! HTTP Error: %i\n", nlhrReply->resultCode);
+ *szData = wszError.Detach();
+ result = nlhrReply->resultCode;
+ }
+
+ // make a copy of the retrieved data, then free the memory of the http reply
+ Netlib_FreeHttpRequest(nlhrReply);
+ return result;
+}
+
+//============ NETLIB INITIALIZATION ============
+//
+// initialize netlib support for weather protocol
+void NetlibInit(void)
+{
+ NETLIBUSER nlu = {};
+ nlu.flags = NUF_OUTGOING | NUF_HTTPCONNS | NUF_NOHTTPSOPTION | NUF_UNICODE;
+ nlu.szSettingsModule = MODULENAME;
+ nlu.szDescriptiveName.w = TranslateT("Weather HTTP connections");
+ hNetlibUser = Netlib_RegisterUser(&nlu);
+}
diff --git a/protocols/Weather/src/weather_icons.cpp b/protocols/Weather/src/weather_icons.cpp
new file mode 100644
index 0000000000..3e043d9280
--- /dev/null
+++ b/protocols/Weather/src/weather_icons.cpp
@@ -0,0 +1,64 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+#include "stdafx.h"
+
+HANDLE hIcoLibIconsChanged = nullptr;
+
+static IconItem iconList[] =
+{
+ { LPGEN("Protocol icon"), "main", IDI_ICON },
+ { LPGEN("Update Disabled"), "disabled", IDI_DISABLED },
+ { LPGEN("View Log"), "log", IDI_LOG },
+ { LPGEN("Update with Clear"), "update2", IDI_UPDATE2 },
+ { LPGEN("View Brief"), "brief", IDI_S },
+ { LPGEN("View Complete"), "read", IDI_READ },
+ { LPGEN("Weather Update"), "update", IDI_UPDATE },
+ { LPGEN("Weather Map"), "map", IDI_MAP },
+ { LPGEN("Popup"), "popup", IDI_POPUP },
+ { LPGEN("No Popup"), "nopopup", IDI_NOPOPUP },
+ { LPGEN("Edit Settings"), "edit", IDI_EDIT },
+};
+
+void InitIcons(void)
+{
+ g_plugin.registerIcon(MODULENAME, iconList, MODULENAME);
+}
+
+HICON LoadIconEx(const char* name, bool big)
+{
+ char szSettingName[100];
+ mir_snprintf(szSettingName, "%s_%s", MODULENAME, name);
+ return IcoLib_GetIcon(szSettingName, big);
+}
+
+HANDLE GetIconHandle(const char* name)
+{
+ for (auto &it : iconList)
+ if (mir_strcmp(it.szName, name) == 0)
+ return it.hIcolib;
+
+ return nullptr;
+}
+
+void ReleaseIconEx(HICON hIcon)
+{
+ IcoLib_ReleaseIcon(hIcon);
+}
diff --git a/protocols/Weather/src/weather_info.cpp b/protocols/Weather/src/weather_info.cpp
new file mode 100644
index 0000000000..a2395d9f69
--- /dev/null
+++ b/protocols/Weather/src/weather_info.cpp
@@ -0,0 +1,240 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+This file contain the source for displaying information for the
+ini files, as well as function that are used for debug purpose
+regrading the loading of ini contents
+*/
+
+#include "stdafx.h"
+
+//============ INI INFORMATION ============
+
+// List INI Information for all loaded INI files
+static void INIInfo(HWND hwndDlg)
+{
+ wchar_t str[16];
+ size_t memused = 0;
+
+
+ HWND hIniList = GetDlgItem(hwndDlg, IDC_INFOLIST);
+
+ ListView_DeleteAllItems(hIniList);
+
+ LVITEM lvi = { 0 };
+ lvi.mask = LVIF_TEXT;
+ lvi.iItem = 0;
+ for (WIDATALIST *Item = WIHead; Item != nullptr; Item = Item->next) {
+ // get the data for the ini file
+ lvi.iSubItem = 0;
+ lvi.pszText = Item->Data.InternalName;
+ ListView_InsertItem(hIniList, &lvi);
+ lvi.iSubItem = 1;
+ lvi.pszText = Item->Data.Author;
+ ListView_SetItem(hIniList, &lvi);
+ lvi.iSubItem = 2;
+ lvi.pszText = Item->Data.Version;
+ ListView_SetItem(hIniList, &lvi);
+ lvi.iSubItem = 3;
+ switch (Item->Data.InternalVer) {
+ case 1: lvi.pszText = L"1.0"; break;
+ case 2: lvi.pszText = L"1.1"; break;
+ case 3: lvi.pszText = L"1.1a"; break;
+ case 4: lvi.pszText = L"1.2"; break;
+ case 5: lvi.pszText = L"1.3"; break;
+ case 6: lvi.pszText = L"1.4"; break;
+ case 7: lvi.pszText = L"1.5"; break;
+ default: lvi.pszText = L""; break;
+ }
+ ListView_SetItem(hIniList, &lvi);
+ lvi.iSubItem = 4;
+ lvi.pszText = _ltow(Item->Data.UpdateDataCount, str, 10);
+ ListView_SetItem(hIniList, &lvi);
+ lvi.iSubItem = 5;
+ lvi.pszText = Item->Data.DisplayName;
+ ListView_SetItem(hIniList, &lvi);
+ lvi.iSubItem = 6;
+ lvi.pszText = Item->Data.ShortFileName;
+ ListView_SetItem(hIniList, &lvi);
+
+ memused += Item->Data.MemUsed;
+
+ ++lvi.iItem;
+ }
+ SetDlgItemText(hwndDlg, IDC_INICOUNT, _itow(lvi.iItem, str, 10));
+ SetDlgItemText(hwndDlg, IDC_MEMUSED, _ltow((long)memused, str, 10));
+}
+
+static const struct tag_Columns
+{
+ const wchar_t *name;
+ unsigned size;
+}
+columns[] =
+{
+ { LPGENW("Name"), 70 },
+ { LPGENW("Author"), 100 },
+ { LPGENW("File Version"), 70 },
+ { LPGENW("INI Version"), 70 },
+ { LPGENW("Items"), 40 },
+ { LPGENW("Display Name"), 200 },
+ { LPGENW("File Name"), 150 },
+};
+
+
+INT_PTR CALLBACK DlgProcINIPage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ {
+ HWND hIniList = GetDlgItem(hwndDlg, IDC_INFOLIST);
+ LVCOLUMN lvc = { 0 };
+
+ lvc.mask = LVCF_FMT | LVCF_SUBITEM | LVCF_TEXT | LVCF_WIDTH;
+ lvc.fmt = LVCFMT_LEFT;
+ for (int i = 0; i < 7; ++i) {
+ lvc.iSubItem = i;
+ lvc.pszText = TranslateW(columns[i].name);
+ lvc.cx = columns[i].size;
+ ListView_InsertColumn(hIniList, i, &lvc);
+ }
+ INIInfo(hwndDlg);
+ }
+
+ break;
+
+ case WM_DESTROY:
+ break;
+
+ case WM_COMMAND:
+ if (HIWORD(wParam) == BN_CLICKED &&
+ LOWORD(wParam) == IDC_RELOADINI) {
+ DestroyWIList();
+ LoadWIData(true);
+ INIInfo(hwndDlg);
+ }
+ break;
+ }
+ return 0;
+}
+
+
+// get the info of individual ini file
+// pszSvc = the internal name of the service to get the data
+void GetINIInfo(wchar_t *pszSvc)
+{
+ wchar_t str2[2048];
+ WIDATA *sData = GetWIData(pszSvc);
+ // if the service does not exist among the loaded INI's
+ if (sData == nullptr) {
+ mir_snwprintf(str2, TranslateT("The corresponding INI file for \"%s\" is not found."), pszSvc);
+ MessageBox(nullptr, str2, TranslateT("Weather INI information"), MB_OK | MB_ICONINFORMATION);
+ }
+ // if exist, get the information
+ else {
+ mir_snwprintf(str2, TranslateT("Weather INI information for \"%s\":"), pszSvc);
+ mir_wstrncat(str2, L"\n\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("Name:"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\t\t", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, sData->DisplayName, _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("Internal Name:"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\t", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, sData->InternalName, _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("Author:"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\t\t", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, sData->Author, _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("Version:"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\t\t", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, sData->Version, _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("INI Version:"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\t", _countof(str2) - mir_wstrlen(str2));
+ switch (sData->InternalVer) {
+ case 1: mir_wstrncat(str2, L"1.0", _countof(str2) - mir_wstrlen(str2)); break;
+ case 2: mir_wstrncat(str2, L"1.1", _countof(str2) - mir_wstrlen(str2)); break;
+ case 3: mir_wstrncat(str2, L"1.1a", _countof(str2) - mir_wstrlen(str2)); break;
+ case 4: mir_wstrncat(str2, L"1.2", _countof(str2) - mir_wstrlen(str2)); break;
+ case 5: mir_wstrncat(str2, L"1.3", _countof(str2) - mir_wstrlen(str2)); break;
+ case 6: mir_wstrncat(str2, L"1.4", _countof(str2) - mir_wstrlen(str2)); break;
+ case 7: mir_wstrncat(str2, L"1.5", _countof(str2) - mir_wstrlen(str2)); break;
+ }
+ mir_wstrncat(str2, L"\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("File Name:"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\t", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, sData->ShortFileName, _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("Item Count:"), _countof(str2) - mir_wstrlen(str2));
+ mir_snwprintf(str2, L"%s\t%i\n", str2, sData->UpdateDataCount);
+ mir_wstrncat(str2, TranslateT("Memory Used:"), _countof(str2) - mir_wstrlen(str2));
+ mir_snwprintf(str2, L"%s\t%i ", str2, sData->MemUsed);
+ mir_wstrncat(str2, TranslateT("bytes"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\n\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, TranslateT("Description:"), _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, L"\n", _countof(str2) - mir_wstrlen(str2));
+ mir_wstrncat(str2, sData->Description, _countof(str2) - mir_wstrlen(str2));
+
+ // display the message box and quit
+ MessageBox(nullptr, str2, TranslateT("Weather INI information"), MB_OK | MB_ICONINFORMATION);
+ }
+}
+
+//============ DISPLAY A LIST FOR CUSTOM VARIABLES ============
+//
+// a message box for displaying the list of custom variables
+// can be found when click on "More" in text option dialog
+void MoreVarList(void)
+{
+ wchar_t str[10240], tempstr[1024];
+
+ // heading
+ wcsncpy(str, VARS_LIST, _countof(str) - 1);
+ mir_wstrncat(str, L"\n\n", _countof(str) - mir_wstrlen(str));
+ // loop through all weather services to find custom variables
+ for (WIDATALIST *Item = WIHead; Item != nullptr; Item = Item->next) {
+ // loop through all update items in a service
+ for (WIDATAITEMLIST *WItem = Item->Data.UpdateData; WItem != nullptr; WItem = WItem->Next) {
+ // the custom variable is defined as "%[<variable name>]"
+ // ignore the "hi" item and hidden items
+ if (mir_wstrcmp(WItem->Item.Name, L"Ignore") && WItem->Item.Name[0] != '#') {
+ mir_snwprintf(tempstr, L"%c[%s]", '%', WItem->Item.Name);
+ wchar_t *find = wcsstr(str, tempstr);
+ // if the custom variable does not exist in the list, add it to the list
+ if (find == nullptr) {
+ mir_wstrncat(str, tempstr, _countof(str) - mir_wstrlen(str));
+ mir_wstrncat(str, L", ", _countof(str) - mir_wstrlen(str));
+ }
+ }
+ }
+ }
+ // remove the last comma in the list
+ wchar_t* find = wcsrchr(str, ',');
+ if (find != nullptr)
+ *find = '\0';
+
+ // display the list in a message box
+ MessageBox(nullptr, str, TranslateT("More Variables"), MB_OK | MB_ICONINFORMATION | MB_TOPMOST);
+}
+
diff --git a/protocols/Weather/src/weather_ini.cpp b/protocols/Weather/src/weather_ini.cpp
new file mode 100644
index 0000000000..000eab1bc9
--- /dev/null
+++ b/protocols/Weather/src/weather_ini.cpp
@@ -0,0 +1,592 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+This file contain the source related to loading the reading the
+weather ini files and store them into memory. Also containing
+code for unloading and getting weather data from the ini settings.
+*/
+
+#include "stdafx.h"
+
+HWND hWndSetup;
+
+//============ DATA LIST (LINKED LIST) ============
+//
+// add an item into weather service data list
+// Data = the service data to be added to the list
+static void WIListAdd(WIDATA Data)
+{
+ // create a new datalist item and point to the data
+ WIDATALIST *newItem = (WIDATALIST*)mir_alloc(sizeof(WIDATALIST));
+ newItem->Data = Data;
+ // add to the linked list
+ newItem->next = nullptr;
+ if (WITail == nullptr) WIHead = newItem;
+ else WITail->next = newItem;
+ WITail = newItem;
+}
+
+// get the service data (from loaded ini file) by internal name
+// pszServ = internal name for the service
+// return value = the matching WIDATA struct for pszServ, NULL if no match found
+WIDATA* GetWIData(wchar_t *pszServ)
+{
+ // loop through the list to find matching internal name
+ for (WIDATALIST *Item = WIHead; Item != nullptr; Item = Item->next)
+ // if internal name found, return the data
+ if (mir_wstrcmp(Item->Data.InternalName, pszServ) == 0)
+ return &Item->Data;
+
+ // return NULL when no match found
+ return nullptr;
+}
+
+//============ DATA ITEM LIST (LINKED LIST) ============
+//
+// add a new update item into the current list
+void WIItemListAdd(WIDATAITEM *DataItem, WIDATA *Data)
+{
+ WIDATAITEMLIST *newItem = (WIDATAITEMLIST*)mir_alloc(sizeof(WIDATAITEMLIST));
+ newItem->Item = *DataItem;
+ newItem->Next = nullptr;
+ if (Data->UpdateData == nullptr) Data->UpdateData = newItem;
+ else Data->UpdateDataTail->Next = newItem;
+ Data->UpdateDataTail = newItem;
+}
+
+// reset the data item by using empty string
+// Item = the item to set
+// name = the string to store in the "name" field
+void ResetDataItem(WIDATAITEM *Item, const wchar_t *name)
+{
+ Item->Name = mir_wstrdup(name);
+ Item->Start = L"";
+ Item->End = L"";
+ Item->Unit = L"";
+ Item->Url = "";
+ Item->Break = L"";
+ Item->Type = 0;
+}
+
+// free the data item by using empty string
+// Item = the item to free
+void FreeDataItem(WIDATAITEM *Item)
+{
+ wfree(&Item->Name);
+ wfree(&Item->Start);
+ wfree(&Item->End);
+ wfree(&Item->Unit);
+ wfree(&Item->Url);
+ wfree(&Item->Break);
+}
+
+//============ Condition Icon List ============
+//
+// initiate icon assignmet list
+void WICondListInit(WICONDLIST *List)
+{
+ List->Tail = nullptr;
+ List->Head = nullptr;
+}
+
+// add a new update item into the current list
+void WICondListAdd(char *str, WICONDLIST *List)
+{
+ WICONDITEM *newItem = (WICONDITEM*)mir_alloc(sizeof(WICONDITEM));
+ wSetData(&newItem->Item, str);
+ CharLowerBuff(newItem->Item, (DWORD)mir_wstrlen(newItem->Item));
+ newItem->Next = nullptr;
+ if (List->Tail == nullptr) List->Head = newItem;
+ else List->Tail->Next = newItem;
+ List->Tail = newItem;
+}
+
+// check if the condition string matched for the assignment
+bool IsContainedInCondList(const wchar_t *pszStr, WICONDLIST *List)
+{
+ // loop through the list to find matching internal name
+ for (WICONDITEM *Item = List->Head; Item != nullptr; Item = Item->Next) {
+ // if internal name found, return true indicating that the data is found
+ if (wcsstr(pszStr, Item->Item))
+ return true;
+
+ }
+ // return false when no match found
+ return false;
+}
+
+// free the memory for icon assignment list
+void DestroyCondList(WICONDLIST *List)
+{
+ // free the list one by one
+ for (WICONDITEM *temp = List->Head; temp != nullptr; temp = List->Head) {
+ List->Head = temp->Next;
+ wfree(&temp->Item); // free the data struct
+ mir_free(temp);
+ }
+ // make sure the entire list is clear
+ List->Tail = nullptr;
+}
+
+
+//============ WEATHER INI SETUP DIALOG ============
+//
+static INT_PTR CALLBACK DlgProcSetup(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+
+ // make the buttons flat
+ SendDlgItemMessage(hwndDlg, IDC_STEP1, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_STEP2, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_STEP3, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_STEP4, BUTTONSETASFLATBTN, TRUE, 0);
+
+ // set icons
+ Window_SetIcon_IcoLib(hwndDlg, GetIconHandle("main"));
+
+ WindowList_Add(hWindowList, hwndDlg);
+ ShowWindow(hwndDlg, SW_SHOW);
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_STEP1:
+ // update current data
+ Utils_OpenUrl("https://miranda-ng.org/");
+ break;
+
+ case IDC_STEP2:
+ {
+ wchar_t szPath[1024];
+ GetModuleFileName(GetModuleHandle(nullptr), szPath, _countof(szPath));
+ wchar_t *chop = wcsrchr(szPath, '\\');
+ if (chop) {
+ *chop = '\0';
+ mir_wstrncat(szPath, L"\\Plugins\\weather\\", _countof(szPath) - mir_wstrlen(szPath));
+ if (_wmkdir(szPath) == 0)
+ ShellExecute((HWND)lParam, L"open", szPath, L"", L"", SW_SHOW);
+ }
+ break;
+ }
+
+ case IDC_STEP3:
+ if (LoadWIData(false))
+ MessageBox(nullptr,
+ TranslateT("All update data has been reloaded."),
+ TranslateT("Weather Protocol"), MB_OK | MB_ICONINFORMATION);
+ break;
+
+ case IDC_STEP4:
+ WeatherAdd(0, 0);
+ __fallthrough;
+
+ case IDCANCEL:
+ // close the info window
+ DestroyWindow(hwndDlg);
+ break;
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hwndDlg);
+ break;
+ }
+ return FALSE;
+}
+
+// load the station data from a file
+// pszFile = the file name + path for the ini file to be loaded
+// pszShortFile = the file name of the ini file, but not including the path
+// Data = the struct to load the ini content to, and return to previous function
+static void LoadStationData(wchar_t *pszFile, wchar_t *pszShortFile, WIDATA *Data)
+{
+ WIDATAITEM DataItem;
+ char *Group, *Temp;
+ char *ValName, *Value;
+
+ static const char *statusStr[10] =
+ {
+ "LIGHTNING",
+ "FOG",
+ "SNOW SHOWER",
+ "SNOW",
+ "RAIN SHOWER",
+ "RAIN",
+ "PARTLY CLOUDY",
+ "CLOUDY",
+ "SUNNY",
+ "N/A"
+ };
+
+ // clean up old stuff
+ memset(Data, 0, sizeof(*Data));
+ Data->Enabled = FALSE;
+
+ // open the ini file
+ FILE *pfile = _wfsopen(pszFile, L"rt", _SH_DENYWR);
+ if (pfile != nullptr) {
+ char Line[4096];
+ fgets(Line, _countof(Line), pfile);
+ TrimString(Line);
+
+ // make sure it is a valid weather protocol ini file
+ if (!mir_strcmp(Line, "[Weather 0.3.x Update Data]"))
+ Data->InternalVer = 1;
+ else if (!mir_strcmp(Line, "[Weather 0.3.x Update Data 1.1]"))
+ Data->InternalVer = 2;
+ else if (!mir_strcmp(Line, "[Weather 0.3.x Update Data 1.1a]"))
+ Data->InternalVer = 3;
+ else if (!mir_strcmp(Line, "[Weather 0.3.x Update Data 1.2]"))
+ Data->InternalVer = 4;
+ else if (!mir_strcmp(Line, "[Weather 0.3.x Update Data 1.3]"))
+ Data->InternalVer = 5;
+ else if (!mir_strcmp(Line, "[Weather 0.3.x Update Data 1.4]"))
+ Data->InternalVer = 6;
+ else if (!mir_strcmp(Line, "[Weather 0.3.x Update Data 1.5]"))
+ Data->InternalVer = 7;
+ else {
+ wchar_t str[4096];
+ mir_snwprintf(str, TranslateT("Invalid ini format for: %s"), pszFile);
+ MessageBox(nullptr, str, TranslateT("Weather Protocol"), MB_OK | MB_ICONERROR);
+ fclose(pfile);
+ return;
+ }
+
+ // initialize all data fields
+ Group = "";
+
+ Data->DisplayName = L"";
+ Data->InternalName = L"";
+ Data->Description = L"";
+ Data->Author = L"";
+ Data->Version = L"";
+ Data->DefaultURL = "";
+ Data->DefaultMap = L"";
+ Data->UpdateURL = "";
+ Data->UpdateURL2 = "";
+ Data->UpdateURL3 = "";
+ Data->UpdateURL4 = "";
+ Data->Cookie = "";
+ Data->UserAgent = "";
+ Data->IDSearch.SearchURL = "";
+ Data->IDSearch.NotFoundStr = L"";
+ Data->NameSearch.SearchURL = "";
+ Data->NameSearch.NotFoundStr = L"";
+ Data->NameSearch.SingleStr = L"";
+ Data->NameSearch.Single.First = L"";
+ Data->NameSearch.Multiple.First = L"";
+ Data->IDSearch.Available = FALSE;
+ Data->NameSearch.Single.Available = FALSE;
+ Data->NameSearch.Multiple.Available = FALSE;
+ wSetData(&Data->FileName, pszFile);
+ wSetData(&Data->ShortFileName, pszShortFile);
+
+ ResetDataItem(&Data->IDSearch.Name, L"ID Search - Station Name");
+ ResetDataItem(&Data->NameSearch.Single.Name, L"Name Search Single Result - Station Name");
+ ResetDataItem(&Data->NameSearch.Single.ID, L"Name Search Single Result - Station ID");
+ ResetDataItem(&Data->NameSearch.Multiple.Name, L"Name Search Multiple Result - Station Name");
+ ResetDataItem(&Data->NameSearch.Multiple.ID, L"Name Search Multiple Result - Station ID");
+
+ DataItem.Name = L"";
+ DataItem.Start = L"";
+ DataItem.End = L"";
+ DataItem.Unit = L"";
+ DataItem.Url = "";
+ DataItem.Break = L"";
+ DataItem.Type = 0;
+
+ Temp = "";
+
+ // initialize the linked list for update items
+ Data->UpdateDataCount = 0;
+ Data->MemUsed = sizeof(WIDATA) + sizeof(WIDATALIST) + (mir_wstrlen(pszShortFile) + mir_wstrlen(pszFile) + 20)*sizeof(wchar_t);
+ Data->UpdateData = nullptr;
+ Data->UpdateDataTail = nullptr;
+
+ // initialize the icon assignment list
+ for (int i = 0; i < 10; i++)
+ WICondListInit(&Data->CondList[i]);
+
+ while (!feof(pfile)) {
+ // determine current tag
+
+ if (fgets(Line, _countof(Line), pfile) == nullptr)
+ break;
+ TrimString(Line);
+
+ // if the line is a group header/footer
+ if (Line[0] == '[') {
+ char *chop = strchr(Line + 1, ']');
+ if (chop == nullptr)
+ continue;
+
+ if (Line[1] != '/') { // if it is not a footer (for old ini)
+ // save the group name
+ Temp = (char *)mir_alloc(mir_strlen(Line) + 10);
+ strncpy(Temp, Line + 1, chop - Line - 1);
+ Temp[chop - Line - 1] = 0;
+ wfree(&Group);
+ wSetData(&Group, Temp);
+ // see if it is a update item, if it is, add a new item to the linked list
+ // if (_stricmp(Group, "HEADER") && _stricmp(Group, "DEFAULT") && _stricmp(Group, "ID SEARCH") &&
+ // strcmpi(Group, "NAME SEARCH"))
+ // wSetData(&DataItem.Name, Group);
+ if (_stricmp(Group, "HEADER") && _stricmp(Group, "DEFAULT") && _stricmp(Group, "ID SEARCH") &&
+ _stricmp(Group, "NAME SEARCH") && _stricmp(Group, "ICONS")) {
+ wSetData(&DataItem.Name, Temp);
+ DataItem.Type = WID_NORMAL;
+ WIItemListAdd(&DataItem, Data);
+ Data->UpdateDataCount++;
+ }
+ mir_free(Temp);
+ }
+ else {
+ wfree(&Group);
+ wSetData(&Group, "");
+ }
+ }
+ // ignore comments and all lines without an '='
+ Value = strstr(Line, "=");
+ if (Value == nullptr) continue;
+
+ // get the string before '=' (ValName) and after '=' (Value)
+ ValName = (char *)mir_alloc(mir_strlen(Line) + 1);
+ strncpy(ValName, Line, Value - Line);
+ ValName[Value - Line] = 0;
+ Value++;
+ ConvertBackslashes(Value);
+ // store the value for each string
+ if (!_stricmp(Group, "HEADER")) {
+ if (!_stricmp(ValName, "NAME")) wSetData(&Data->DisplayName, Value);
+ else if (!_stricmp(ValName, "INTERNAL NAME")) wSetData(&Data->InternalName, Value);
+ else if (!_stricmp(ValName, "DESCRIPTION")) wSetData(&Data->Description, Value);
+ else if (!_stricmp(ValName, "AUTHOR")) wSetData(&Data->Author, Value);
+ else if (!_stricmp(ValName, "VERSION")) wSetData(&Data->Version, Value);
+ }
+ else if (!_stricmp(Group, "DEFAULT")) {
+ if (!_stricmp(ValName, "DEFAULT URL")) wSetData(&Data->DefaultURL, Value);
+ else if (!_stricmp(ValName, "DEFAULT MAP")) wSetData(&Data->DefaultMap, Value);
+ else if (!_stricmp(ValName, "UPDATE URL")) wSetData(&Data->UpdateURL, Value);
+ else if (!_stricmp(ValName, "UPDATE URL2")) wSetData(&Data->UpdateURL2, Value);
+ else if (!_stricmp(ValName, "UPDATE URL3")) wSetData(&Data->UpdateURL3, Value);
+ else if (!_stricmp(ValName, "UPDATE URL4")) wSetData(&Data->UpdateURL4, Value);
+ else if (!_stricmp(ValName, "COOKIE")) wSetData(&Data->Cookie, Value);
+ else if (!_stricmp(ValName, "USERAGENT")) wSetData(&Data->UserAgent, Value);
+ }
+ else if (!_stricmp(Group, "ID SEARCH")) {
+ if (!_stricmp(ValName, "AVAILABLE")) {
+ if (!_stricmp(Value, "TRUE")) Data->IDSearch.Available = TRUE;
+ else Data->IDSearch.Available = FALSE;
+ }
+ else if (!_stricmp(ValName, "SEARCH URL")) wSetData(&Data->IDSearch.SearchURL, Value);
+ else if (!_stricmp(ValName, "NOT FOUND STR")) wSetData(&Data->IDSearch.NotFoundStr, Value);
+ else if (!_stricmp(ValName, "NAME START")) wSetData(&Data->IDSearch.Name.Start, Value);
+ else if (!_stricmp(ValName, "NAME END")) wSetData(&Data->IDSearch.Name.End, Value);
+ }
+ else if (!_stricmp(Group, "NAME SEARCH")) {
+ if (!_stricmp(ValName, "SINGLE RESULT")) {
+ if (!_stricmp(Value, "TRUE")) Data->NameSearch.Single.Available = TRUE;
+ else Data->NameSearch.Single.Available = FALSE;
+ }
+ else if (!_stricmp(ValName, "MULTIPLE RESULT")) {
+ if (!_stricmp(Value, "TRUE")) Data->NameSearch.Multiple.Available = TRUE;
+ else Data->NameSearch.Multiple.Available = FALSE;
+ }
+ else if (!_stricmp(ValName, "SEARCH URL")) wSetData(&Data->NameSearch.SearchURL, Value);
+ else if (!_stricmp(ValName, "NOT FOUND STR")) wSetData(&Data->NameSearch.NotFoundStr, Value);
+ else if (!_stricmp(ValName, "SINGLE RESULT STR")) wSetData(&Data->NameSearch.SingleStr, Value);
+ else if (!_stricmp(ValName, "SINGLE FIRST")) wSetData(&Data->NameSearch.Single.First, Value);
+ else if (!_stricmp(ValName, "SINGLE NAME START"))wSetData(&Data->NameSearch.Single.Name.Start, Value);
+ else if (!_stricmp(ValName, "SINGLE NAME END")) wSetData(&Data->NameSearch.Single.Name.End, Value);
+ else if (!_stricmp(ValName, "SINGLE ID START")) wSetData(&Data->NameSearch.Single.ID.Start, Value);
+ else if (!_stricmp(ValName, "SINGLE ID END")) wSetData(&Data->NameSearch.Single.ID.End, Value);
+ else if (!_stricmp(ValName, "MULT FIRST")) wSetData(&Data->NameSearch.Multiple.First, Value);
+ else if (!_stricmp(ValName, "MULT NAME START")) wSetData(&Data->NameSearch.Multiple.Name.Start, Value);
+ else if (!_stricmp(ValName, "MULT NAME END")) wSetData(&Data->NameSearch.Multiple.Name.End, Value);
+ else if (!_stricmp(ValName, "MULT ID START")) wSetData(&Data->NameSearch.Multiple.ID.Start, Value);
+ else if (!_stricmp(ValName, "MULT ID END")) wSetData(&Data->NameSearch.Multiple.ID.End, Value);
+ }
+ else if (!_stricmp(Group, "ICONS")) {
+ for (int i = 0; i < 10; i++) {
+ if (!_stricmp(ValName, statusStr[i])) {
+ WICondListAdd(Value, &Data->CondList[i]);
+ break;
+ }
+ }
+ }
+ else if (Data->UpdateDataCount != 0) {
+ if (!_stricmp(ValName, "START")) wSetData(&Data->UpdateDataTail->Item.Start, Value);
+ else if (!_stricmp(ValName, "SOURCE")) wSetData(&Data->UpdateDataTail->Item.Start, Value);
+ else if (!_stricmp(ValName, "END")) wSetData(&Data->UpdateDataTail->Item.End, Value);
+ else if (!_stricmp(ValName, "UNIT")) wSetData(&Data->UpdateDataTail->Item.Unit, Value);
+ else if (!_stricmp(ValName, "URL")) wSetData(&Data->UpdateDataTail->Item.Url, Value);
+ else if (!_stricmp(ValName, "HIDDEN")) {
+ if (!_stricmp(Value, "TRUE")) {
+ wchar_t *nm = Data->UpdateDataTail->Item.Name;
+ size_t len = mir_wstrlen(nm) + 1;
+
+ Data->UpdateDataTail->Item.Name = nm = (wchar_t*)mir_realloc(nm, sizeof(wchar_t)*(len + 3));
+ memmove(nm + 1, nm, len*sizeof(wchar_t));
+ *nm = '#';
+ }
+ }
+ else if (!_stricmp(ValName, "SET DATA")) {
+ Data->UpdateDataTail->Item.Type = WID_SET;
+ wSetData(&Data->UpdateDataTail->Item.End, Value);
+ }
+ else if (!_stricmp(ValName, "BREAK DATA")) {
+ Data->UpdateDataTail->Item.Type = WID_BREAK;
+ wSetData(&Data->UpdateDataTail->Item.Break, Value);
+ }
+ }
+ // recalculate memory used
+ Data->MemUsed += (mir_strlen(Value) + 10);
+ wfree(&ValName);
+ }
+ // calcualate memory used for the ini and close the file
+ Data->MemUsed += sizeof(WIDATAITEMLIST)*Data->UpdateDataCount;
+ Data->Enabled = TRUE; // enable the service
+ fclose(pfile);
+ wfree(&Group);
+ }
+}
+
+//============ LOADING INI FILES ============
+//
+// load the weather update data form INI files
+bool LoadWIData(bool dial)
+{
+ // make sure that the current service data list is empty
+ WITail = nullptr;
+ WIHead = WITail;
+
+ // find all *.ini file in the plugin\weather directory
+ wchar_t szSearchPath[MAX_PATH], FileName[MAX_PATH];
+ GetModuleFileName(GetModuleHandle(nullptr), szSearchPath, _countof(szSearchPath));
+ wchar_t *chop = wcsrchr(szSearchPath, '\\');
+ if (chop == nullptr)
+ return false;
+ *chop = '\0';
+ mir_wstrncat(szSearchPath, L"\\Plugins\\Weather\\*.ini", _countof(szSearchPath) - mir_wstrlen(szSearchPath));
+ wcsncpy(FileName, szSearchPath, MAX_PATH - 1);
+
+ WIN32_FIND_DATA fd;
+ HANDLE hFind = FindFirstFile(szSearchPath, &fd);
+
+
+ // load the content of the ini file into memory
+ if (hFind != INVALID_HANDLE_VALUE) {
+ do {
+ chop = wcsrchr(FileName, '\\');
+ chop[1] = '\0';
+ mir_wstrncat(FileName, fd.cFileName, _countof(FileName) - mir_wstrlen(FileName));
+ if (mir_wstrcmpi(fd.cFileName, L"SAMPLE_INI.INI")) {
+ WIDATA Data;
+ LoadStationData(FileName, fd.cFileName, &Data);
+ if (Data.Enabled)
+ WIListAdd(Data);
+ }
+ // look through the entire "plugins\weather" directory
+ } while (FindNextFile(hFind, &fd));
+ FindClose(hFind);
+ }
+
+ if (WIHead == nullptr) {
+ // no ini found, display an error message box.
+ if (dial)
+ hWndSetup = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_SETUP), nullptr, DlgProcSetup);
+ else
+ MessageBox(nullptr,
+ TranslateT("No update data file is found. Please check your Plugins\\Weather directory."),
+ TranslateT("Weather Protocol"), MB_OK | MB_ICONERROR);
+ return false;
+ }
+ return true;
+}
+
+//============ FREE WIDATA ITEM FROM MEMORY ============
+//
+// free the WIDATA struct from memory
+// Data = the struct to be freed
+static void FreeWIData(WIDATA *Data)
+{
+ // free update items linked list first
+ WIDATAITEMLIST *WItem = Data->UpdateData;
+ while (WItem != nullptr) {
+ Data->UpdateData = WItem->Next;
+ FreeDataItem(&WItem->Item);
+ mir_free(WItem);
+ WItem = Data->UpdateData;
+ }
+
+ // free the strings in the rest of the struct
+ wfree(&Data->DisplayName);
+ wfree(&Data->InternalName);
+ wfree(&Data->Description);
+ wfree(&Data->Author);
+ wfree(&Data->Version);
+ wfree(&Data->DefaultURL);
+ wfree(&Data->DefaultMap);
+ wfree(&Data->UpdateURL);
+ wfree(&Data->UpdateURL2);
+ wfree(&Data->UpdateURL3);
+ wfree(&Data->UpdateURL4);
+ wfree(&Data->Cookie);
+ wfree(&Data->UserAgent);
+ wfree(&Data->IDSearch.SearchURL);
+ wfree(&Data->IDSearch.NotFoundStr);
+ FreeDataItem(&Data->IDSearch.Name);
+ wfree(&Data->NameSearch.SearchURL);
+ wfree(&Data->NameSearch.NotFoundStr);
+ wfree(&Data->NameSearch.SingleStr);
+ wfree(&Data->NameSearch.Single.First);
+ FreeDataItem(&Data->NameSearch.Single.Name);
+ FreeDataItem(&Data->NameSearch.Single.ID);
+ wfree(&Data->NameSearch.Multiple.First);
+ FreeDataItem(&Data->NameSearch.Multiple.Name);
+ FreeDataItem(&Data->NameSearch.Multiple.ID);
+ wfree(&Data->ShortFileName);
+ wfree(&Data->FileName);
+ for (int i = 0; i < 10; i++)
+ DestroyCondList(&Data->CondList[i]);
+}
+
+// remove all service data from memory
+void DestroyWIList(void)
+{
+ // free the list one by one
+ while (WIHead != nullptr) {
+ WIDATALIST *wi = WIHead;
+ WIHead = wi->next;
+ FreeWIData(&wi->Data); // free the data struct
+ mir_free(wi);
+ }
+
+ // make sure the entire list is clear
+ WITail = nullptr;
+}
diff --git a/protocols/Weather/src/weather_mwin.cpp b/protocols/Weather/src/weather_mwin.cpp
new file mode 100644
index 0000000000..a240b01421
--- /dev/null
+++ b/protocols/Weather/src/weather_mwin.cpp
@@ -0,0 +1,364 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2006-2009 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2006 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "stdafx.h"
+
+#define MS_TOOLTIP_SHOWTIP "mToolTip/ShowTip"
+#define MS_TOOLTIP_HIDETIP "mToolTip/HideTip"
+
+static MWindowList hMwinWindowList;
+static HANDLE hFontHook;
+
+HGENMENU hMwinMenu;
+
+typedef struct
+{
+ MCONTACT hContact;
+ HWND hAvt;
+ BOOL haveAvatar;
+} MWinDataType;
+
+#define WM_REDRAWWIN (WM_USER + 17369)
+
+static LRESULT CALLBACK wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ MWinDataType *data = (MWinDataType*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_CREATE:
+ data = (MWinDataType*)mir_calloc(sizeof(MWinDataType));
+ SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)data);
+
+ data->hContact = (DWORD_PTR)((LPCREATESTRUCT)lParam)->lpCreateParams;
+ data->hAvt = CreateWindow(AVATAR_CONTROL_CLASS, TEXT(""), WS_CHILD,
+ 0, 0, opt.AvatarSize, opt.AvatarSize, hwnd, nullptr, g_plugin.getInst(), nullptr);
+ if (data->hAvt) SendMessage(data->hAvt, AVATAR_SETCONTACT, 0, (LPARAM)data->hContact);
+ break;
+
+ case WM_DESTROY:
+ mir_free(data);
+ break;
+
+ case WM_CONTEXTMENU:
+ {
+ POINT pt;
+ GetCursorPos(&pt);
+ HMENU hMenu = Menu_BuildContactMenu(data->hContact);
+ TrackPopupMenu(hMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hwnd, nullptr);
+ DestroyMenu(hMenu);
+ }
+ break;
+
+ case WM_MOUSEMOVE:
+ {
+ TRACKMOUSEEVENT tme = { 0 };
+ tme.cbSize = sizeof(TRACKMOUSEEVENT);
+ tme.hwndTrack = hwnd;
+ tme.dwFlags = TME_QUERY;
+ TrackMouseEvent(&tme);
+
+ if (tme.dwFlags == 0) {
+ tme.dwFlags = TME_HOVER | TME_LEAVE;
+ tme.hwndTrack = hwnd;
+ tme.dwHoverTime = CallService(MS_CLC_GETINFOTIPHOVERTIME, 0, 0);
+ TrackMouseEvent(&tme);
+ }
+ }
+ break;
+
+ case WM_MOUSEHOVER:
+ {
+ POINT pt;
+ CLCINFOTIP ti = { 0 };
+
+ GetCursorPos(&pt);
+ GetWindowRect(hwnd, &ti.rcItem);
+
+ ti.cbSize = sizeof(ti);
+ ti.hItem = (HANDLE)data->hContact;
+ ti.ptCursor = pt;
+ ti.isTreeFocused = 1;
+ CallService(MS_TOOLTIP_SHOWTIP, 0, (LPARAM)&ti);
+ }
+ break;
+
+ case WM_LBUTTONDBLCLK:
+ BriefInfo(data->hContact, 0);
+ break;
+
+ case WM_COMMAND: //Needed by the contact's context menu
+ if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, data->hContact))
+ break;
+ return FALSE;
+
+ case WM_MEASUREITEM: //Needed by the contact's context menu
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM: //Needed by the contact's context menu
+ return Menu_DrawItem(lParam);
+
+ case WM_NOTIFY:
+ if (((LPNMHDR)lParam)->code == NM_AVATAR_CHANGED) {
+ BOOL newava = CallService(MS_AV_GETAVATARBITMAP, data->hContact, 0) != 0;
+ if (newava != data->haveAvatar) {
+ LONG_PTR style = GetWindowLongPtr(data->hAvt, GWL_STYLE);
+ data->haveAvatar = newava;
+ SetWindowLongPtr(data->hAvt, GWL_STYLE, newava ? (style | WS_VISIBLE) : (style & ~WS_VISIBLE));
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE);
+ }
+ }
+ break;
+
+ case WM_REDRAWWIN:
+ if (data->hAvt != nullptr) MoveWindow(data->hAvt, 0, 0, opt.AvatarSize, opt.AvatarSize, TRUE);
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ break;
+
+ case WM_PAINT:
+ {
+ RECT r, rc;
+
+ if (GetUpdateRect(hwnd, &r, FALSE)) {
+ int picSize = opt.AvatarSize;
+ HICON hIcon = nullptr;
+
+ if (!data->haveAvatar) {
+ int statusIcon = g_plugin.getWord(data->hContact, "Status");
+
+ picSize = GetSystemMetrics(SM_CXICON);
+ hIcon = Skin_LoadProtoIcon(MODULENAME, statusIcon, true);
+ if ((INT_PTR)hIcon == CALLSERVICE_NOTFOUND) {
+ picSize = GetSystemMetrics(SM_CXSMICON);
+ hIcon = Skin_LoadProtoIcon(MODULENAME, statusIcon);
+ }
+ }
+
+ LOGFONT lfnt, lfnt1;
+ COLORREF clr = g_plugin.getDword("ColorMwinFrame", GetSysColor(COLOR_3DFACE));
+ COLORREF fntc = Font_GetW(_A2W(MODULENAME), LPGENW("Frame Font"), &lfnt);
+ COLORREF fntc1 = Font_GetW(_A2W(MODULENAME), LPGENW("Frame Title Font"), &lfnt1);
+
+ ptrW tszInfo(db_get_wsa(data->hContact, WEATHERCONDITION, "WeatherInfo"));
+
+ GetClientRect(hwnd, &rc);
+
+ PAINTSTRUCT ps;
+ HDC hdc = BeginPaint(hwnd, &ps);
+
+ if (ServiceExists(MS_SKIN_DRAWGLYPH)) {
+ SKINDRAWREQUEST rq;
+ memset(&rq, 0, sizeof(rq));
+ rq.hDC = hdc;
+ rq.rcDestRect = rc;
+ rq.rcClipRect = rc;
+
+ mir_strcpy(rq.szObjectID, "Main,ID=WeatherFrame");
+ CallService(MS_SKIN_DRAWGLYPH, (WPARAM)&rq, 0);
+ }
+
+ if (clr != 0xFFFFFFFF) {
+ HBRUSH hBkgBrush = CreateSolidBrush(clr);
+ FillRect(hdc, &rc, hBkgBrush);
+ DeleteObject(hBkgBrush);
+ }
+
+ if (!data->haveAvatar)
+ DrawIconEx(hdc, 1, 1, hIcon, 0, 0, 0, nullptr, DI_NORMAL);
+
+ SetBkMode(hdc, TRANSPARENT);
+
+ HFONT hfnt = CreateFontIndirect(&lfnt1);
+ HFONT hfntold = (HFONT)SelectObject(hdc, hfnt);
+
+ wchar_t *nick = Clist_GetContactDisplayName(data->hContact);
+
+ SIZE fontSize;
+ GetTextExtentPoint32(hdc, L"|", 1, &fontSize);
+
+ rc.top += 1;
+ rc.left += picSize + fontSize.cx;
+
+ SetTextColor(hdc, fntc1);
+ DrawText(hdc, nick, -1, &rc, DT_LEFT | DT_EXPANDTABS);
+
+ rc.top += fontSize.cy;
+
+ SelectObject(hdc, hfntold);
+ DeleteObject(hfnt);
+
+ if (tszInfo) {
+ HFONT hFont = CreateFontIndirect(&lfnt);
+ HFONT hFontOld = (HFONT)SelectObject(hdc, hFont);
+
+ SetTextColor(hdc, fntc);
+ DrawText(hdc, tszInfo, -1, &rc, DT_LEFT | DT_EXPANDTABS);
+
+ SelectObject(hdc, hFontOld);
+ DeleteObject(hFont);
+ }
+ EndPaint(hwnd, &ps);
+ IcoLib_ReleaseIcon(hIcon);
+ }
+ break;
+ }
+
+ default:
+ return DefWindowProc(hwnd, msg, wParam, lParam);
+ }
+ return(TRUE);
+}
+
+static void addWindow(MCONTACT hContact)
+{
+ DBVARIANT dbv;
+ if (g_plugin.getWString(hContact, "Nick", &dbv))
+ return;
+
+ wchar_t winname[512];
+ mir_snwprintf(winname, L"Weather: %s", dbv.pwszVal);
+ db_free(&dbv);
+
+ HWND hWnd = CreateWindow(L"WeatherFrame", L"", WS_CHILD | WS_VISIBLE,
+ 0, 0, 10, 10, g_clistApi.hwndContactList, nullptr, g_plugin.getInst(), (void*)hContact);
+ WindowList_Add(hMwinWindowList, hWnd, hContact);
+
+ CLISTFrame Frame = { 0 };
+ Frame.szName.w = winname;
+ Frame.hIcon = LoadIconEx("main", FALSE);
+ Frame.cbSize = sizeof(Frame);
+ Frame.hWnd = hWnd;
+ Frame.align = alBottom;
+ Frame.Flags = F_VISIBLE | F_NOBORDER | F_UNICODE;
+ Frame.height = 32;
+ int frameID = g_plugin.addFrame(&Frame);
+
+ g_plugin.setDword(hContact, "mwin", frameID);
+ db_set_b(hContact, "CList", "Hidden", TRUE);
+}
+
+void removeWindow(MCONTACT hContact)
+{
+ DWORD frameId = g_plugin.getDword(hContact, "mwin");
+
+ WindowList_Remove(hMwinWindowList, WindowList_Find(hMwinWindowList, hContact));
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, frameId, 0);
+
+ g_plugin.setDword(hContact, "mwin", 0);
+ db_unset(hContact, "CList", "Hidden");
+}
+
+void UpdateMwinData(MCONTACT hContact)
+{
+ HWND hwnd = WindowList_Find(hMwinWindowList, hContact);
+ if (hwnd != nullptr)
+ RedrawWindow(hwnd, nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+}
+
+INT_PTR Mwin_MenuClicked(WPARAM wParam, LPARAM)
+{
+ BOOL addwnd = WindowList_Find(hMwinWindowList, wParam) == nullptr;
+ if (addwnd)
+ addWindow(wParam);
+ else
+ removeWindow(wParam);
+ return 0;
+}
+
+int BuildContactMenu(WPARAM wparam, LPARAM)
+{
+ int flags = g_plugin.getDword(wparam, "mwin") ? CMIF_CHECKED : 0;
+ Menu_ModifyItem(hMwinMenu, nullptr, INVALID_HANDLE_VALUE, flags);
+ return 0;
+}
+
+int RedrawFrame(WPARAM, LPARAM)
+{
+ WindowList_Broadcast(hMwinWindowList, WM_REDRAWWIN, 0, 0);
+ return 0;
+}
+
+void InitMwin(void)
+{
+ if (!ServiceExists(MS_CLIST_FRAMES_ADDFRAME))
+ return;
+
+ hMwinWindowList = WindowList_Create();
+
+ WNDCLASS wndclass;
+ wndclass.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;
+ wndclass.lpfnWndProc = wndProc;
+ wndclass.cbClsExtra = 0;
+ wndclass.cbWndExtra = 0;
+ wndclass.hInstance = g_plugin.getInst();
+ wndclass.hIcon = nullptr;
+ wndclass.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ wndclass.hbrBackground = nullptr; //(HBRUSH)(COLOR_3DFACE+1);
+ wndclass.lpszMenuName = nullptr;
+ wndclass.lpszClassName = L"WeatherFrame";
+ RegisterClass(&wndclass);
+
+ ColourIDW colourid = {};
+ mir_strcpy(colourid.dbSettingsGroup, MODULENAME);
+ mir_strcpy(colourid.setting, "ColorMwinFrame");
+ mir_wstrcpy(colourid.name, LPGENW("Frame Background"));
+ mir_wstrcpy(colourid.group, _A2W(MODULENAME));
+ colourid.defcolour = GetSysColor(COLOR_3DFACE);
+ g_plugin.addColor(&colourid);
+
+ FontIDW fontid = {};
+ fontid.flags = FIDF_ALLOWREREGISTER | FIDF_DEFAULTVALID;
+ mir_strcpy(fontid.dbSettingsGroup, MODULENAME);
+ mir_wstrcpy(fontid.group, _A2W(MODULENAME));
+ mir_wstrcpy(fontid.name, LPGENW("Frame Font"));
+ mir_strcpy(fontid.setting, "fnt0");
+
+ HDC hdc = GetDC(nullptr);
+ fontid.deffontsettings.size = -13;
+ ReleaseDC(nullptr, hdc);
+
+ fontid.deffontsettings.charset = DEFAULT_CHARSET;
+ mir_wstrcpy(fontid.deffontsettings.szFace, L"Verdana");
+ mir_wstrcpy(fontid.backgroundGroup, _A2W(MODULENAME));
+ mir_wstrcpy(fontid.backgroundName, LPGENW("Frame Background"));
+ g_plugin.addFont(&fontid);
+
+ fontid.deffontsettings.style = DBFONTF_BOLD;
+ mir_wstrcpy(fontid.name, LPGENW("Frame Title Font"));
+ mir_strcpy(fontid.setting, "fnt1");
+ g_plugin.addFont(&fontid);
+
+ for (auto &hContact : Contacts(MODULENAME))
+ if (g_plugin.getDword(hContact, "mwin"))
+ addWindow(hContact);
+
+ hFontHook = HookEvent(ME_FONT_RELOAD, RedrawFrame);
+}
+
+void DestroyMwin(void)
+{
+ for (auto &hContact : Contacts(MODULENAME)) {
+ DWORD frameId = g_plugin.getDword(hContact, "mwin");
+ if (frameId)
+ CallService(MS_CLIST_FRAMES_REMOVEFRAME, frameId, 0);
+ }
+ UnregisterClass(L"WeatherFrame", g_plugin.getInst());
+ WindowList_Destroy(hMwinWindowList);
+ UnhookEvent(hFontHook);
+}
diff --git a/protocols/Weather/src/weather_opt.cpp b/protocols/Weather/src/weather_opt.cpp
new file mode 100644
index 0000000000..daf84d36c7
--- /dev/null
+++ b/protocols/Weather/src/weather_opt.cpp
@@ -0,0 +1,601 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This file contain the source related to weather option pages. It also
+contain code for saving/loading options from the database.
+*/
+
+#include "stdafx.h"
+
+static BOOL opt_startup;
+int RedrawFrame(WPARAM wParam, LPARAM lParam);
+
+//============ LOADING AND SAVING OPTIONS ===========
+//
+// set a string to default
+// in = string to determine which field to set default "CBNEHXPp"
+void SetTextDefault(const char* in)
+{
+ wchar_t str[MAX_TEXT_SIZE];
+
+ if (strchr(in, 'C') != nullptr) {
+ wcsncpy(str, C_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.cText, str);
+ }
+ if (strchr(in, 'b') != nullptr) {
+ wcsncpy(str, b_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.bTitle, str);
+ }
+ if (strchr(in, 'B') != nullptr) {
+ wcsncpy(str, B_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.bText, str);
+ }
+ if (strchr(in, 'N') != nullptr) {
+ wcsncpy(str, N_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.nText, str);
+ }
+ if (strchr(in, 'E') != nullptr) {
+ wcsncpy(str, E_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.eText, str);
+ }
+ if (strchr(in, 'H') != nullptr) {
+ wcsncpy(str, H_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.hText, str);
+ }
+ if (strchr(in, 'X') != nullptr) {
+ wcsncpy(str, X_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.xText, str);
+ }
+ if (strchr(in, 'P') != nullptr) {
+ wcsncpy(str, P_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.pTitle, str);
+ }
+ if (strchr(in, 'p') != nullptr) {
+ wcsncpy(str, p_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.pText, str);
+ }
+ if (strchr(in, 'S') != nullptr) {
+ wcsncpy(str, s_DEFAULT, MAX_TEXT_SIZE - 1);
+ wSetData(&opt.sText, str);
+ }
+}
+
+void DestroyOptions(void)
+{
+ wfree(&opt.cText);
+ wfree(&opt.bTitle);
+ wfree(&opt.bText);
+ wfree(&opt.nText);
+ wfree(&opt.eText);
+ wfree(&opt.hText);
+ wfree(&opt.xText);
+ wfree(&opt.pTitle);
+ wfree(&opt.pText);
+ wfree(&opt.sText);
+}
+
+// load options from database + set default if the setting does not exist
+void LoadOptions(void)
+{
+ memset(&opt, 0, sizeof(opt));
+
+ // main options
+ opt.StartupUpdate = g_plugin.getByte("StartupUpdate", true);
+ opt.AutoUpdate = g_plugin.getByte("AutoUpdate", true);
+ opt.UpdateTime = g_plugin.getWord("UpdateTime", 30);
+ opt.NoProtoCondition = g_plugin.getByte("NoStatus", true);
+ opt.UpdateOnlyConditionChanged = g_plugin.getByte("CondChangeAsUpdate", true);
+ opt.RemoveOldData = g_plugin.getByte("RemoveOld", false);
+ opt.MakeItalic = g_plugin.getByte("MakeItalic", true);
+ opt.AvatarSize = g_plugin.getByte("AvatarSize", 128);
+
+ // units
+ opt.tUnit = g_plugin.getWord("tUnit", 1);
+ opt.wUnit = g_plugin.getWord("wUnit", 2);
+ opt.vUnit = g_plugin.getWord("vUnit", 1);
+ opt.pUnit = g_plugin.getWord("pUnit", 4);
+ opt.dUnit = g_plugin.getWord("dUnit", 1);
+ opt.eUnit = g_plugin.getWord("eUnit", 2);
+
+ ptrW szValue(g_plugin.getWStringA("DegreeSign"));
+ wcsncpy_s(opt.DegreeSign, (szValue == NULL) ? L"" : szValue, _TRUNCATE);
+
+ opt.DoNotAppendUnit = g_plugin.getByte("DoNotAppendUnit", 0);
+ opt.NoFrac = g_plugin.getByte("NoFractions", 0);
+
+ // texts
+ if (szValue = g_plugin.getWStringA("DisplayText"))
+ wSetData(&opt.cText, TranslateW(szValue));
+ else
+ SetTextDefault("C");
+
+ if (szValue = g_plugin.getWStringA("BriefTextTitle"))
+ wSetData(&opt.bTitle, TranslateW(szValue));
+ else
+ SetTextDefault("b");
+
+ if (szValue = g_plugin.getWStringA("BriefText"))
+ wSetData(&opt.bText, TranslateW(szValue));
+ else
+ SetTextDefault("B");
+
+ if (szValue = g_plugin.getWStringA("NoteText"))
+ wSetData(&opt.nText, TranslateW(szValue));
+ else
+ SetTextDefault("N");
+
+ if (szValue = g_plugin.getWStringA("ExtText"))
+ wSetData(&opt.eText, TranslateW(szValue));
+ else
+ SetTextDefault("E");
+
+ if (szValue = g_plugin.getWStringA("HistoryText"))
+ wSetData(&opt.hText, TranslateW(szValue));
+ else
+ SetTextDefault("H");
+
+ if (szValue = g_plugin.getWStringA("ExtraText"))
+ wSetData(&opt.xText, TranslateW(szValue));
+ else
+ SetTextDefault("X");
+
+ if (szValue = g_plugin.getWStringA("StatusText"))
+ wSetData(&opt.sText, TranslateW(szValue));
+ else
+ SetTextDefault("S");
+
+ // advanced
+ opt.DisCondIcon = g_plugin.getByte("DisableConditionIcon", false);
+ // popup options
+ opt.UsePopup = g_plugin.getByte("UsePopUp", true);
+ opt.UpdatePopup = g_plugin.getByte("UpdatePopup", true);
+ opt.AlertPopup = g_plugin.getByte("AlertPopup", true);
+ opt.PopupOnChange = g_plugin.getByte("PopUpOnChange", true);
+ opt.ShowWarnings = g_plugin.getByte("ShowWarnings", true);
+ // popup colors
+ opt.BGColour = g_plugin.getDword("BackgroundColour", GetSysColor(COLOR_BTNFACE));
+ opt.TextColour = g_plugin.getDword("TextColour", GetSysColor(COLOR_WINDOWTEXT));
+ opt.UseWinColors = g_plugin.getByte("UseWinColors", false);
+ // popup actions
+ opt.LeftClickAction = g_plugin.getDword("LeftClickAction", IDM_M2);
+ opt.RightClickAction = g_plugin.getDword("RightClickAction", IDM_M1);
+ // popup delay
+ opt.pDelay = g_plugin.getDword("PopupDelay", 0);
+ // popup texts
+ if (szValue = g_plugin.getWStringA("PopupTitle"))
+ wSetData(&opt.pTitle, szValue);
+ else
+ SetTextDefault("P");
+
+ if (szValue = g_plugin.getWStringA("PopupText"))
+ wSetData(&opt.pText, szValue);
+ else
+ SetTextDefault("p");
+
+ // misc
+ if (szValue = g_plugin.getWStringA("Default"))
+ wcsncpy_s(opt.Default, szValue, _TRUNCATE);
+ else
+ opt.Default[0] = 0;
+}
+
+// save the options to database
+void SaveOptions(void)
+{
+ // main options
+ g_plugin.setByte("StartupUpdate", (BYTE)opt.StartupUpdate);
+ g_plugin.setByte("AutoUpdate", (BYTE)opt.AutoUpdate);
+ g_plugin.setWord("UpdateTime", opt.UpdateTime);
+ g_plugin.setByte("NoStatus", (BYTE)opt.NoProtoCondition);
+ g_plugin.setByte("CondChangeAsUpdate", (BYTE)opt.UpdateOnlyConditionChanged);
+ g_plugin.setByte("RemoveOld", (BYTE)opt.RemoveOldData);
+ g_plugin.setByte("MakeItalic", (BYTE)opt.MakeItalic);
+ g_plugin.setByte("AvatarSize", (BYTE)opt.AvatarSize);
+ // units
+ g_plugin.setWord("tUnit", opt.tUnit);
+ g_plugin.setWord("wUnit", opt.wUnit);
+ g_plugin.setWord("vUnit", opt.vUnit);
+ g_plugin.setWord("pUnit", opt.pUnit);
+ g_plugin.setWord("dUnit", opt.dUnit);
+ g_plugin.setWord("eUnit", opt.eUnit);
+ g_plugin.setWString("DegreeSign", opt.DegreeSign);
+ g_plugin.setByte("DoNotAppendUnit", (BYTE)opt.DoNotAppendUnit);
+ g_plugin.setByte("NoFractions", (BYTE)opt.NoFrac);
+ // texts
+ g_plugin.setWString("DisplayText", opt.cText);
+ g_plugin.setWString("BriefTextTitle", opt.bTitle);
+ g_plugin.setWString("BriefText", opt.bText);
+ g_plugin.setWString("NoteText", opt.nText);
+ g_plugin.setWString("ExtText", opt.eText);
+ g_plugin.setWString("HistoryText", opt.hText);
+ g_plugin.setWString("ExtraText", opt.xText);
+ g_plugin.setWString("StatusText", opt.sText);
+ // advanced
+ g_plugin.setByte("DisableConditionIcon", (BYTE)opt.DisCondIcon);
+ // popup options
+ g_plugin.setByte("UsePopUp", (BYTE)opt.UsePopup);
+ g_plugin.setByte("UpdatePopup", (BYTE)opt.UpdatePopup);
+ g_plugin.setByte("AlertPopup", (BYTE)opt.AlertPopup);
+ g_plugin.setByte("PopUpOnChange", (BYTE)opt.PopupOnChange);
+ g_plugin.setByte("ShowWarnings", (BYTE)opt.ShowWarnings);
+ // popup colors
+ g_plugin.setDword("BackgroundColour", opt.BGColour);
+ g_plugin.setDword("TextColour", opt.TextColour);
+ g_plugin.setByte("UseWinColors", (BYTE)opt.UseWinColors);
+ // popup actions
+ g_plugin.setDword("LeftClickAction", opt.LeftClickAction);
+ g_plugin.setDword("RightClickAction", opt.RightClickAction);
+ // popup delay
+ g_plugin.setDword("PopupDelay", opt.pDelay);
+ // popup texts
+ g_plugin.setWString("PopupTitle", opt.pTitle);
+ g_plugin.setWString("PopupText", opt.pText);
+ // misc stuff
+ g_plugin.setWString("Default", opt.Default);
+}
+//============ MAIN OPTIONS ============
+
+// weather options
+static INT_PTR CALLBACK OptionsProc(HWND hdlg, UINT msg, WPARAM wparam, LPARAM lparam)
+{
+ wchar_t str[512];
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ opt_startup = TRUE;
+ TranslateDialogDefault(hdlg);
+ // load settings
+ _ltow(opt.UpdateTime, str, 10);
+ SetDlgItemText(hdlg, IDC_UPDATETIME, str);
+ SetDlgItemText(hdlg, IDC_DEGREE, opt.DegreeSign);
+
+ SendDlgItemMessage(hdlg, IDC_AVATARSPIN, UDM_SETRANGE32, 0, 999);
+ SendDlgItemMessage(hdlg, IDC_AVATARSPIN, UDM_SETPOS, 0, opt.AvatarSize);
+ SendDlgItemMessage(hdlg, IDC_AVATARSIZE, EM_LIMITTEXT, 3, 0);
+
+ CheckDlgButton(hdlg, IDC_STARTUPUPD, opt.StartupUpdate ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_UPDATE, opt.AutoUpdate ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_PROTOCOND, !opt.NoProtoCondition ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_UPDCONDCHG, opt.UpdateOnlyConditionChanged ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_REMOVEOLD, opt.RemoveOldData ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_MAKEI, opt.MakeItalic ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_DISCONDICON, opt.DisCondIcon ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_DONOTAPPUNITS, opt.DoNotAppendUnit ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_NOFRAC, opt.NoFrac ? BST_CHECKED : BST_UNCHECKED);
+
+ // load units
+ switch (opt.tUnit) { // temperature
+ case 1: CheckRadioButton(hdlg, IDC_T1, IDC_T2, IDC_T1); break;
+ case 2: CheckRadioButton(hdlg, IDC_T1, IDC_T2, IDC_T2); break;
+ }
+ switch (opt.wUnit) { // wind
+ case 1: CheckRadioButton(hdlg, IDC_W1, IDC_W4, IDC_W1); break;
+ case 2: CheckRadioButton(hdlg, IDC_W1, IDC_W4, IDC_W2); break;
+ case 3: CheckRadioButton(hdlg, IDC_W1, IDC_W4, IDC_W3); break;
+ case 4: CheckRadioButton(hdlg, IDC_W1, IDC_W4, IDC_W4); break;
+ }
+ switch (opt.vUnit) { // visibility
+ case 1: CheckRadioButton(hdlg, IDC_V1, IDC_V2, IDC_V1); break;
+ case 2: CheckRadioButton(hdlg, IDC_V1, IDC_V2, IDC_V2); break;
+ }
+ switch (opt.pUnit) { // pressure
+ case 1: CheckRadioButton(hdlg, IDC_P1, IDC_P4, IDC_P1); break;
+ case 2: CheckRadioButton(hdlg, IDC_P1, IDC_P4, IDC_P2); break;
+ case 3: CheckRadioButton(hdlg, IDC_P1, IDC_P4, IDC_P3); break;
+ case 4: CheckRadioButton(hdlg, IDC_P1, IDC_P4, IDC_P4); break;
+ }
+ switch (opt.dUnit) { // pressure
+ case 1: CheckRadioButton(hdlg, IDC_D1, IDC_D3, IDC_D1); break;
+ case 2: CheckRadioButton(hdlg, IDC_D1, IDC_D3, IDC_D2); break;
+ case 3: CheckRadioButton(hdlg, IDC_D1, IDC_D3, IDC_D3); break;
+ }
+
+ switch (opt.eUnit) { // elev
+ case 1: CheckRadioButton(hdlg, IDC_E1, IDC_E2, IDC_E1); break;
+ case 2: CheckRadioButton(hdlg, IDC_E1, IDC_E2, IDC_E2); break;
+ }
+
+ opt_startup = FALSE;
+ return 0;
+
+ case WM_COMMAND:
+ if (HIWORD(wparam) == BN_CLICKED && GetFocus() == (HWND)lparam)
+ if (!opt_startup) SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ if (!((LOWORD(wparam) == IDC_UPDATE || LOWORD(wparam) == IDC_DEGREE) &&
+ (HIWORD(wparam) != EN_CHANGE || (HWND)lparam != GetFocus())))
+ if (!opt_startup) SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ return 0;
+
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lparam)->code) {
+ case PSN_APPLY:
+ // change the status for weather protocol
+ if (IsDlgButtonChecked(hdlg, IDC_PROTOCOND) && opt.DefStn != NULL) {
+ old_status = status;
+ status = g_plugin.getWord(opt.DefStn, "StatusIcon", NOSTATUSDATA);
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, status);
+ }
+
+ // get update time and remove the old timer
+ GetDlgItemText(hdlg, IDC_UPDATETIME, str, _countof(str));
+ opt.UpdateTime = (WORD)_wtoi(str);
+ if (opt.UpdateTime < 1) opt.UpdateTime = 1;
+ KillTimer(nullptr, timerId);
+ timerId = SetTimer(nullptr, 0, opt.UpdateTime * 60000, timerProc);
+
+ // other general options
+ GetDlgItemText(hdlg, IDC_DEGREE, opt.DegreeSign, _countof(opt.DegreeSign));
+ opt.StartupUpdate = IsDlgButtonChecked(hdlg, IDC_STARTUPUPD);
+ opt.AutoUpdate = IsDlgButtonChecked(hdlg, IDC_UPDATE);
+ opt.NoProtoCondition = BST_UNCHECKED == IsDlgButtonChecked(hdlg, IDC_PROTOCOND);
+ opt.DisCondIcon = IsDlgButtonChecked(hdlg, IDC_DISCONDICON);
+ opt.UpdateOnlyConditionChanged = (BYTE)IsDlgButtonChecked(hdlg, IDC_UPDCONDCHG);
+ opt.RemoveOldData = IsDlgButtonChecked(hdlg, IDC_REMOVEOLD);
+ opt.MakeItalic = IsDlgButtonChecked(hdlg, IDC_MAKEI);
+ opt.AvatarSize = GetDlgItemInt(hdlg, IDC_AVATARSIZE, nullptr, FALSE);
+ opt.DoNotAppendUnit = IsDlgButtonChecked(hdlg, IDC_DONOTAPPUNITS);
+ opt.NoFrac = IsDlgButtonChecked(hdlg, IDC_NOFRAC);
+ UpdateMenu(opt.AutoUpdate);
+
+ // save the units
+ if (IsDlgButtonChecked(hdlg, IDC_T1)) opt.tUnit = 1;
+ if (IsDlgButtonChecked(hdlg, IDC_T2)) opt.tUnit = 2;
+ if (IsDlgButtonChecked(hdlg, IDC_W1)) opt.wUnit = 1;
+ if (IsDlgButtonChecked(hdlg, IDC_W2)) opt.wUnit = 2;
+ if (IsDlgButtonChecked(hdlg, IDC_W3)) opt.wUnit = 3;
+ if (IsDlgButtonChecked(hdlg, IDC_W4)) opt.wUnit = 4;
+ if (IsDlgButtonChecked(hdlg, IDC_V1)) opt.vUnit = 1;
+ if (IsDlgButtonChecked(hdlg, IDC_V2)) opt.vUnit = 2;
+ if (IsDlgButtonChecked(hdlg, IDC_P1)) opt.pUnit = 1;
+ if (IsDlgButtonChecked(hdlg, IDC_P2)) opt.pUnit = 2;
+ if (IsDlgButtonChecked(hdlg, IDC_P3)) opt.pUnit = 3;
+ if (IsDlgButtonChecked(hdlg, IDC_P4)) opt.pUnit = 4;
+ if (IsDlgButtonChecked(hdlg, IDC_D1)) opt.dUnit = 1;
+ if (IsDlgButtonChecked(hdlg, IDC_D2)) opt.dUnit = 2;
+ if (IsDlgButtonChecked(hdlg, IDC_D3)) opt.dUnit = 3;
+ if (IsDlgButtonChecked(hdlg, IDC_E1)) opt.eUnit = 1;
+ if (IsDlgButtonChecked(hdlg, IDC_E2)) opt.eUnit = 2;
+
+ // save the new weather options
+ SaveOptions();
+
+ RedrawFrame(0, 0);
+
+ return 1;
+ }
+ break;
+ }
+ return 0;
+}
+
+//============ TEXT OPTION DIALOG ============
+
+static void LoadTextSettings(HWND hdlg)
+{
+ // load text option settings from memory
+ SetDlgItemText(hdlg, IDC_CTEXT, opt.cText);
+ SetDlgItemText(hdlg, IDC_BTITLE, opt.bTitle);
+ SetDlgItemText(hdlg, IDC_BTEXT, opt.bText);
+ SetDlgItemText(hdlg, IDC_ETEXT, opt.eText);
+ SetDlgItemText(hdlg, IDC_NTEXT, opt.nText);
+ SetDlgItemText(hdlg, IDC_HTEXT, opt.hText);
+ SetDlgItemText(hdlg, IDC_XTEXT, opt.xText);
+ SetDlgItemText(hdlg, IDC_BTITLE2, opt.sText);
+}
+
+// free the display text settings from memory
+static void FreeTextVar(void)
+{
+ wfree(&opt.cText);
+ wfree(&opt.bText);
+ wfree(&opt.bTitle);
+ wfree(&opt.eText);
+ wfree(&opt.nText);
+ wfree(&opt.hText);
+ wfree(&opt.xText);
+ wfree(&opt.sText);
+}
+
+// text option dialog
+
+static const char *varname[8] = { "C", "b", "B", "N", "X", "E", "H", "S" };
+static const int cname[8] = { IDC_CTEXT, IDC_BTITLE, IDC_BTEXT, IDC_NTEXT, IDC_XTEXT, IDC_ETEXT, IDC_HTEXT, IDC_BTITLE2 };
+static wchar_t* const *var[8] = { &opt.cText, &opt.bTitle, &opt.bText, &opt.nText, &opt.xText, &opt.eText, &opt.hText, &opt.sText };
+
+static INT_PTR CALLBACK DlgProcText(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ RECT rc, pos;
+ HWND button;
+ HMENU hMenu, hMenu1;
+ wchar_t str[4096];
+ switch (msg) {
+ case WM_INITDIALOG:
+ opt_startup = TRUE;
+ // set windows position, make it top-most
+ GetWindowRect(hdlg, &rc);
+ SetWindowPos(hdlg, HWND_TOPMOST, rc.left, rc.top, 0, 0, SWP_NOSIZE);
+ TranslateDialogDefault(hdlg);
+ // generate the display text for variable list
+ wcsncpy(str, VAR_LIST_OPT, _countof(str) - 1);
+ SetDlgItemText(hdlg, IDC_VARLIST, str);
+
+ // make the more variable and other buttons flat
+ SendDlgItemMessage(hdlg, IDC_MORE, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM1, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM2, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM3, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM4, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM5, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM6, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM7, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_TM8, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_RESET, BUTTONSETASFLATBTN, TRUE, 0);
+ // load the settings
+ LoadTextSettings(hdlg);
+ opt_startup = FALSE;
+ return TRUE;
+
+ case WM_COMMAND:
+ if (opt_startup) return TRUE;
+ switch (LOWORD(wParam)) {
+ case IDC_CTEXT:
+ case IDC_BTITLE:
+ case IDC_BTEXT:
+ case IDC_NTEXT:
+ case IDC_XTEXT:
+ case IDC_ETEXT:
+ case IDC_HTEXT:
+ case IDC_BTITLE2:
+ if (HIWORD(wParam) == EN_CHANGE)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_MORE:
+ // display custom variables list
+ MoreVarList();
+ break;
+
+ case IDC_TM1:
+ case IDC_TM2:
+ case IDC_TM3:
+ case IDC_TM4:
+ case IDC_TM5:
+ case IDC_TM6:
+ case IDC_TM7:
+ case IDC_TM8:
+ // display the menu
+ button = GetDlgItem(hdlg, LOWORD(wParam));
+ GetWindowRect(button, &pos);
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_TMMENU));
+ hMenu1 = GetSubMenu(hMenu, 0);
+ TranslateMenu(hMenu1);
+ switch (TrackPopupMenu(hMenu1, TPM_LEFTBUTTON | TPM_RETURNCMD, pos.left, pos.bottom, 0, hdlg, nullptr)) {
+ case ID_MPREVIEW:
+ {
+ // show the preview in a message box, using the weather data from the default station
+ WEATHERINFO winfo = LoadWeatherInfo(opt.DefStn);
+ GetDisplay(&winfo, *var[LOWORD(wParam) - IDC_TM1], str);
+ MessageBox(nullptr, str, TranslateT("Weather Protocol Text Preview"), MB_OK | MB_TOPMOST);
+ break;
+ }
+ case ID_MRESET:
+ unsigned varo = LOWORD(wParam) - IDC_TM1;
+ // remove the old setting from db and free memory
+ wchar_t* vartmp = *var[varo];
+ wfree(&vartmp);
+ SetTextDefault(varname[varo]);
+ SetDlgItemText(hdlg, cname[varo], *var[varo]);
+ break;
+ }
+ DestroyMenu(hMenu);
+ break;
+
+ case IDC_RESET:
+ // left click action selection menu
+ button = GetDlgItem(hdlg, IDC_RESET);
+ GetWindowRect(button, &pos);
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_TMENU));
+ hMenu1 = GetSubMenu(hMenu, 0);
+ TranslateMenu(hMenu1);
+ switch (TrackPopupMenu(hMenu1, TPM_LEFTBUTTON | TPM_RETURNCMD, pos.left, pos.bottom, 0, hdlg, nullptr)) {
+ case ID_T1:
+ // reset to the strings in memory, discard all changes
+ LoadTextSettings(hdlg);
+ break;
+
+ case ID_T2:
+ // reset to the default setting
+ FreeTextVar();
+ SetTextDefault("CbBENHX");
+ LoadTextSettings(hdlg);
+ break;
+ }
+ DestroyMenu(hMenu);
+ break;
+ }
+ return TRUE;
+ case WM_NOTIFY:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ // save the option
+ wchar_t textstr[MAX_TEXT_SIZE];
+ // free memory for old settings
+ FreeTextVar();
+ // save new settings to memory
+ GetDlgItemText(hdlg, IDC_CTEXT, textstr, _countof(textstr));
+ wSetData(&opt.cText, textstr);
+ GetDlgItemText(hdlg, IDC_BTEXT, textstr, _countof(textstr));
+ wSetData(&opt.bText, textstr);
+ GetDlgItemText(hdlg, IDC_BTITLE, textstr, _countof(textstr));
+ wSetData(&opt.bTitle, textstr);
+ GetDlgItemText(hdlg, IDC_ETEXT, textstr, _countof(textstr));
+ wSetData(&opt.eText, textstr);
+ GetDlgItemText(hdlg, IDC_NTEXT, textstr, _countof(textstr));
+ wSetData(&opt.nText, textstr);
+ GetDlgItemText(hdlg, IDC_HTEXT, textstr, _countof(textstr));
+ wSetData(&opt.hText, textstr);
+ GetDlgItemText(hdlg, IDC_XTEXT, textstr, _countof(textstr));
+ wSetData(&opt.xText, textstr);
+ GetDlgItemText(hdlg, IDC_BTITLE2, textstr, _countof(textstr));
+ wSetData(&opt.sText, textstr);
+ SaveOptions();
+ UpdateAllInfo(0, 0);
+ break;
+ }
+ break;
+ }
+ return FALSE;
+}
+
+
+//============ OPTION INITIALIZATION ============
+
+// register the weather option pages
+int OptInit(WPARAM wParam, LPARAM)
+{
+ // plugin options
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 95600;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPTIONS);
+ odp.pfnDlgProc = OptionsProc;
+ odp.szGroup.a = LPGEN("Network");
+ odp.szTitle.a = WEATHERPROTOTEXT;
+ odp.szTab.a = LPGEN("General");
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addOptions(wParam, &odp);
+
+ // text options
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_TEXTOPT);
+ odp.pfnDlgProc = DlgProcText;
+ odp.szTab.a = LPGEN("Display");
+ g_plugin.addOptions(wParam, &odp);
+
+ // if popup service exists, load the weather popup options
+ if ((ServiceExists(MS_POPUP_ADDPOPUPW))) {
+ odp.position = 100000000;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_POPUP);
+ odp.szGroup.a = LPGEN("Popups");
+ odp.szTab.a = nullptr;
+ odp.pfnDlgProc = DlgPopupOpts;
+ g_plugin.addOptions(wParam, &odp);
+ }
+
+ return 0;
+}
diff --git a/protocols/Weather/src/weather_popup.cpp b/protocols/Weather/src/weather_popup.cpp
new file mode 100644
index 0000000000..6e72d5595e
--- /dev/null
+++ b/protocols/Weather/src/weather_popup.cpp
@@ -0,0 +1,428 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2009 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* This file contain the source related to weather popups, including popup
+ options, popup display, and the code for popup process.
+*/
+
+#include "stdafx.h"
+
+// variables for weather_popup.c
+static MCONTACT hPopupContact;
+
+//============ SHOW WEATHER POPUPS ============
+
+//============ WEATHER ERROR POPUPS ============
+
+// display weather error or notices (not threaded)
+// wParam = error text
+// lParam = display type
+// Type can either be SM_WARNING, SM_NOTIFY, or SM_WEATHERALERT
+int WeatherError(WPARAM wParam, LPARAM lParam)
+{
+ if (!opt.UsePopup)
+ return 0;
+
+ wchar_t* tszMsg = (wchar_t*)wParam;
+
+ if ((DWORD)lParam == SM_WARNING)
+ PUShowMessageW(tszMsg, SM_WARNING);
+ else if ((DWORD)lParam == SM_NOTIFY)
+ PUShowMessageW(tszMsg, SM_NOTIFY);
+ else if ((DWORD)lParam == SM_WEATHERALERT) {
+ POPUPDATAW ppd = { 0 };
+ wchar_t str1[512], str2[512];
+
+ // get the 2 strings
+ wcsncpy(str1, tszMsg, _countof(str1) - 1);
+ wcsncpy(str2, tszMsg, _countof(str2) - 1);
+ wchar_t *chop = wcschr(str1, 255);
+ if (chop != nullptr)
+ *chop = '\0';
+ else
+ str1[0] = 0;
+ chop = wcschr(str2, 255);
+ if (chop != nullptr)
+ wcsncpy(str2, chop + 1, _countof(str2) - 1);
+ else
+ str2[0] = 0;
+
+ // setup the popup
+ ppd.lchIcon = (HICON)LoadImage(nullptr, MAKEINTRESOURCE(OIC_BANG), IMAGE_ICON,
+ GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_SHARED);
+ mir_wstrcpy(ppd.lpwzContactName, str1);
+ mir_wstrcpy(ppd.lpwzText, str2);
+ ppd.colorBack = (opt.UseWinColors) ? GetSysColor(COLOR_BTNFACE) : opt.BGColour;
+ ppd.colorText = (opt.UseWinColors) ? GetSysColor(COLOR_WINDOWTEXT) : opt.TextColour;
+ ppd.iSeconds = opt.pDelay;
+ PUAddPopupW(&ppd);
+ }
+ return 0;
+}
+
+// wrapper function for displaying weather warning popup by triggering an event
+// (threaded)
+// lpzText = error text
+// kind = display type (see m_popup.h)
+int WPShowMessage(wchar_t* lpzText, WORD kind)
+{
+ NotifyEventHooks(hHookWeatherError, (WPARAM)lpzText, (LPARAM)kind);
+ return 0;
+}
+
+//============ WEATHER POPUP PROCESSES ============
+
+// popup dialog pocess
+// for selecting actions when click on the popup window
+// use for displaying contact menu
+static LRESULT CALLBACK PopupDlgProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ DWORD ID = 0;
+ MCONTACT hContact;
+ hContact = PUGetContact(hWnd);
+
+ switch (message) {
+ case WM_COMMAND:
+ ID = opt.LeftClickAction;
+ if (ID != IDM_M7) PUDeletePopup(hWnd);
+ SendMessage(hPopupWindow, ID, hContact, 0);
+ return TRUE;
+
+ case WM_CONTEXTMENU:
+ ID = opt.RightClickAction;
+ if (ID != IDM_M7) PUDeletePopup(hWnd);
+ SendMessage(hPopupWindow, ID, hContact, 0);
+ return TRUE;
+
+ case UM_FREEPLUGINDATA:
+ IcoLib_ReleaseIcon((HICON)PUGetPluginData(hWnd));
+ return FALSE;
+ }
+
+ return DefWindowProc(hWnd, message, wParam, lParam);
+}
+
+// display weather popups
+// wParam = the contact to display popup
+// lParam = whether the weather data is changed or not
+int WeatherPopup(WPARAM hContact, LPARAM lParam)
+{
+ // determine if the popup should display or not
+ if (opt.UsePopup && opt.UpdatePopup && (!opt.PopupOnChange || (BOOL)lParam) && !g_plugin.getByte(hContact, "DPopUp")) {
+ WEATHERINFO winfo = LoadWeatherInfo(hContact);
+
+ // setup the popup
+ POPUPDATAW ppd = { 0 };
+ ppd.lchContact = hContact;
+ ppd.PluginData = ppd.lchIcon = Skin_LoadProtoIcon(MODULENAME, winfo.status);
+ GetDisplay(&winfo, opt.pTitle, ppd.lpwzContactName);
+ GetDisplay(&winfo, opt.pText, ppd.lpwzText);
+ ppd.PluginWindowProc = PopupDlgProc;
+ ppd.colorBack = (opt.UseWinColors) ? GetSysColor(COLOR_BTNFACE) : opt.BGColour;
+ ppd.colorText = (opt.UseWinColors) ? GetSysColor(COLOR_WINDOWTEXT) : opt.TextColour;
+ ppd.iSeconds = opt.pDelay;
+ PUAddPopupW(&ppd);
+ }
+ return 0;
+}
+
+
+// process for the popup window
+// containing the code for popup actions
+LRESULT CALLBACK PopupWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
+{
+ POINT pt;
+ HMENU hMenu;
+ switch (uMsg) {
+ case IDM_M2: // brief info
+ BriefInfo(wParam, 0);
+ break;
+
+ case IDM_M3: // read complete forecast
+ LoadForecast(wParam, 0);
+ break;
+
+ case IDM_M4: // display weather map
+ WeatherMap(wParam, 0);
+ break;
+
+ case IDM_M5: // open history window
+ CallService(MS_HISTORY_SHOWCONTACTHISTORY, wParam, 0);
+ break;
+
+ case IDM_M6: // open external log
+ ViewLog(wParam, 0);
+ break;
+
+ case IDM_M7: // display contact menu
+ hMenu = Menu_BuildContactMenu(wParam);
+ GetCursorPos(&pt);
+ hPopupContact = wParam;
+ TrackPopupMenu(hMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, nullptr);
+ DestroyMenu(hMenu);
+ break;
+
+ case IDM_M8: // display contact detail
+ CallService(MS_USERINFO_SHOWDIALOG, wParam, 0);
+
+ case WM_COMMAND: //Needed by the contact's context menu
+ if (Clist_MenuProcessCommand(LOWORD(wParam), MPCF_CONTACTMENU, hPopupContact))
+ break;
+ return FALSE;
+
+ case WM_MEASUREITEM: //Needed by the contact's context menu
+ return Menu_MeasureItem(lParam);
+
+ case WM_DRAWITEM: //Needed by the contact's context menu
+ return Menu_DrawItem(lParam);
+ }
+
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);//FALSE;
+}
+
+//============ POPUP OPTIONS ============
+
+// used to select the menu item for popup action menu
+static void SelectMenuItem(HMENU hMenu, int Check)
+{
+ for (int i = 0; i <= GetMenuItemCount(hMenu) - 1; i++)
+ CheckMenuItem(hMenu, i, MF_BYPOSITION | ((int)GetMenuItemID(hMenu, i) == Check) * 8);
+}
+
+// temporary read the current option to memory
+// but does not write to the database
+void ReadPopupOpt(HWND hdlg)
+{
+ wchar_t text[MAX_TEXT_SIZE];
+ wchar_t str[512];
+
+ // popup colour
+ opt.TextColour = SendDlgItemMessage(hdlg, IDC_TEXTCOLOUR, CPM_GETCOLOUR, 0, 0);
+ opt.BGColour = SendDlgItemMessage(hdlg, IDC_BGCOLOUR, CPM_GETCOLOUR, 0, 0);
+
+ // get delay time
+ GetDlgItemText(hdlg, IDC_DELAY, str, _countof(str));
+ int num = _wtoi(str);
+ opt.pDelay = num;
+
+ // other options
+ opt.UseWinColors = (BYTE)IsDlgButtonChecked(hdlg, IDC_USEWINCOLORS);
+ opt.UsePopup = (BYTE)IsDlgButtonChecked(hdlg, IDC_E);
+ opt.UpdatePopup = (BYTE)IsDlgButtonChecked(hdlg, IDC_POP1);
+ opt.AlertPopup = (BYTE)IsDlgButtonChecked(hdlg, IDC_POP2);
+ opt.PopupOnChange = (BYTE)IsDlgButtonChecked(hdlg, IDC_CH);
+ opt.ShowWarnings = (BYTE)IsDlgButtonChecked(hdlg, IDC_W);
+
+ // popup texts
+ wfree(&opt.pText);
+ wfree(&opt.pTitle);
+ GetDlgItemText(hdlg, IDC_PText, text, _countof(text));
+ wSetData(&opt.pText, text);
+ GetDlgItemText(hdlg, IDC_PTitle, text, _countof(text));
+ wSetData(&opt.pTitle, text);
+}
+
+// copied and modified from NewStatusNotify
+INT_PTR CALLBACK DlgPopupOpts(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ int ID;
+ wchar_t str[512];
+ HMENU hMenu, hMenu1;
+ RECT pos;
+ HWND button;
+ MCONTACT hContact;
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hdlg);
+ SaveOptions();
+
+ // click actions
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_PMENU));
+ hMenu1 = GetSubMenu(hMenu, 0);
+ GetMenuString(hMenu1, opt.LeftClickAction, str, _countof(str), MF_BYCOMMAND);
+ SetDlgItemText(hdlg, IDC_LeftClick, TranslateW(str));
+ GetMenuString(hMenu1, opt.RightClickAction, str, _countof(str), MF_BYCOMMAND);
+ SetDlgItemText(hdlg, IDC_RightClick, TranslateW(str));
+ DestroyMenu(hMenu);
+
+ // other options
+ CheckDlgButton(hdlg, IDC_E, opt.UsePopup ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_POP2, opt.AlertPopup ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_POP1, opt.UpdatePopup ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_CH, opt.PopupOnChange ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hdlg, IDC_W, opt.ShowWarnings ? BST_CHECKED : BST_UNCHECKED);
+ SetDlgItemText(hdlg, IDC_PText, opt.pText);
+ SetDlgItemText(hdlg, IDC_PTitle, opt.pTitle);
+ // setting popup delay option
+ _ltow(opt.pDelay, str, 10);
+ SetDlgItemText(hdlg, IDC_DELAY, str);
+ if (opt.pDelay == -1)
+ CheckRadioButton(hdlg, IDC_PD1, IDC_PD3, IDC_PD2);
+ else if (opt.pDelay == 0)
+ CheckRadioButton(hdlg, IDC_PD1, IDC_PD3, IDC_PD1);
+ else
+ CheckRadioButton(hdlg, IDC_PD1, IDC_PD3, IDC_PD3);
+ //Colours. First step is configuring the colours.
+ SendDlgItemMessage(hdlg, IDC_BGCOLOUR, CPM_SETCOLOUR, 0, opt.BGColour);
+ SendDlgItemMessage(hdlg, IDC_TEXTCOLOUR, CPM_SETCOLOUR, 0, opt.TextColour);
+ //Second step is disabling them if we want to use default Windows ones.
+ CheckDlgButton(hdlg, IDC_USEWINCOLORS, opt.UseWinColors ? BST_CHECKED : BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hdlg, IDC_BGCOLOUR), !opt.UseWinColors);
+ EnableWindow(GetDlgItem(hdlg, IDC_TEXTCOLOUR), !opt.UseWinColors);
+
+ // buttons
+ SendDlgItemMessage(hdlg, IDC_PREVIEW, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_PDEF, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_LeftClick, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_RightClick, BUTTONSETASFLATBTN, TRUE, 0);
+ SendDlgItemMessage(hdlg, IDC_VAR3, BUTTONSETASFLATBTN, TRUE, 0);
+ return TRUE;
+
+ case WM_COMMAND:
+ // enable the "apply" button
+ if (HIWORD(wParam) == BN_CLICKED && GetFocus() == (HWND)lParam)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ if (!((LOWORD(wParam) == IDC_UPDATE || LOWORD(wParam) == IDC_DEGREE) &&
+ (HIWORD(wParam) != EN_CHANGE || (HWND)lParam != GetFocus())))
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ //These are simple clicks: we don't save, but we tell the Options Page to enable the "Apply" button.
+ switch (LOWORD(wParam)) {
+ case IDC_BGCOLOUR: //Fall through
+ case IDC_TEXTCOLOUR:
+ // select new colors
+ if (HIWORD(wParam) == CPN_COLOURCHANGED)
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_USEWINCOLORS:
+ // use window color - enable/disable color selection controls
+ EnableWindow(GetDlgItem(hdlg, IDC_BGCOLOUR), !(opt.UseWinColors));
+ EnableWindow(GetDlgItem(hdlg, IDC_TEXTCOLOUR), !(opt.UseWinColors));
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_E:
+ case IDC_CH:
+ SendMessage(GetParent(hdlg), PSM_CHANGED, 0, 0);
+ break;
+
+ case IDC_RightClick:
+ // right click action selection menu
+ button = GetDlgItem(hdlg, IDC_RightClick);
+ GetWindowRect(button, &pos);
+
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_PMENU));
+ hMenu1 = GetSubMenu(hMenu, 0);
+ TranslateMenu(hMenu1);
+ SelectMenuItem(hMenu1, opt.RightClickAction);
+ ID = TrackPopupMenu(hMenu1, TPM_LEFTBUTTON | TPM_RETURNCMD, pos.left, pos.bottom, 0, hdlg, nullptr);
+ if (ID)
+ opt.RightClickAction = ID;
+ DestroyMenu(hMenu);
+
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_PMENU));
+ hMenu1 = GetSubMenu(hMenu, 0);
+ GetMenuString(hMenu1, opt.RightClickAction, str, _countof(str), MF_BYCOMMAND);
+ SetDlgItemText(hdlg, IDC_RightClick, TranslateW(str));
+ DestroyMenu(hMenu);
+ break;
+
+ case IDC_LeftClick:
+ // left click action selection menu
+ button = GetDlgItem(hdlg, IDC_LeftClick);
+ GetWindowRect(button, &pos);
+
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_PMENU));
+ hMenu1 = GetSubMenu(hMenu, 0);
+ TranslateMenu(hMenu1);
+ SelectMenuItem(hMenu1, opt.LeftClickAction);
+ ID = TrackPopupMenu(hMenu1, TPM_LEFTBUTTON | TPM_RETURNCMD, pos.left, pos.bottom, 0, hdlg, nullptr);
+ if (ID) opt.LeftClickAction = ID;
+ DestroyMenu(hMenu);
+
+ hMenu = LoadMenu(g_plugin.getInst(), MAKEINTRESOURCE(IDR_PMENU));
+ hMenu1 = GetSubMenu(hMenu, 0);
+ GetMenuString(hMenu1, opt.LeftClickAction, str, _countof(str), MF_BYCOMMAND);
+ SetDlgItemText(hdlg, IDC_LeftClick, TranslateW(str));
+ DestroyMenu(hMenu);
+ break;
+
+ case IDC_PD1:
+ // Popup delay setting from Popup plugin
+ SetDlgItemText(hdlg, IDC_DELAY, L"0");
+ CheckRadioButton(hdlg, IDC_PD1, IDC_PD3, IDC_PD1);
+ break;
+
+ case IDC_PD2:
+ // Popup delay = permanent
+ SetDlgItemText(hdlg, IDC_DELAY, L"-1");
+ CheckRadioButton(hdlg, IDC_PD1, IDC_PD3, IDC_PD2);
+ break;
+
+ case IDC_DELAY:
+ // if text is edited
+ CheckRadioButton(hdlg, IDC_PD1, IDC_PD3, IDC_PD3);
+ break;
+
+ case IDC_PDEF:
+ // set the default value for popup texts
+ SetTextDefault("Pp");
+ SetDlgItemText(hdlg, IDC_PText, opt.pText);
+ SetDlgItemText(hdlg, IDC_PTitle, opt.pTitle);
+ wfree(&opt.pText);
+ wfree(&opt.pTitle);
+ break;
+
+ case IDC_VAR3:
+ // display variable list
+ wcsncpy(str, L" \n", _countof(str) - 1); // to make the message box wider
+ mir_wstrncat(str, VAR_LIST_POPUP, _countof(str) - mir_wstrlen(str));
+ mir_wstrncat(str, L"\n", _countof(str) - mir_wstrlen(str));
+ mir_wstrncat(str, CUSTOM_VARS, _countof(str) - mir_wstrlen(str));
+ MessageBox(nullptr, str, TranslateT("Variable List"), MB_OK | MB_ICONASTERISK | MB_TOPMOST);
+ break;
+
+ case IDC_PREVIEW:
+ // popup preview
+ hContact = opt.DefStn;
+ ReadPopupOpt(hdlg); // read new options to memory
+ WeatherPopup((WPARAM)opt.DefStn, (BOOL)TRUE); // display popup using new opt
+ DestroyOptions();
+ LoadOptions(); // restore old option in memory
+ opt.DefStn = hContact;
+ break;
+ }
+ break;
+
+ case WM_NOTIFY: //Here we have pressed either the OK or the APPLY button.
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_APPLY:
+ ReadPopupOpt(hdlg);
+
+ // save the options, and update main menu
+ SaveOptions();
+ UpdatePopupMenu(opt.UsePopup);
+ return TRUE;
+ }
+ break;
+ }
+ return FALSE;
+}
diff --git a/protocols/Weather/src/weather_svcs.cpp b/protocols/Weather/src/weather_svcs.cpp
new file mode 100644
index 0000000000..7c3d2611aa
--- /dev/null
+++ b/protocols/Weather/src/weather_svcs.cpp
@@ -0,0 +1,375 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file contain the source related to weather protocol services
+as required for a Miranda protocol. Also, it contains functions for
+building/changing the weather menu items.
+*/
+
+#include "stdafx.h"
+
+static HGENMENU hEnableDisablePopupMenu;
+static HGENMENU hEnableDisableMenu;
+
+//============ MIRANDA PROTOCOL SERVICES ============
+
+// protocol service function for setting weather protocol status
+INT_PTR WeatherSetStatus(WPARAM new_status, LPARAM)
+{
+ new_status = new_status != ID_STATUS_OFFLINE ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE;
+
+ // if we don't want to show status for default station
+ if (!opt.NoProtoCondition && status != new_status) {
+ old_status = status;
+ status = new_status != ID_STATUS_OFFLINE ? ID_STATUS_ONLINE : ID_STATUS_OFFLINE;
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, status);
+
+ UpdateMenu(new_status != ID_STATUS_OFFLINE);
+ if (new_status != ID_STATUS_OFFLINE)
+ UpdateAll(FALSE, FALSE);
+ }
+
+ return 0;
+}
+
+// get capabilities protocol service function
+INT_PTR WeatherGetCaps(WPARAM wParam, LPARAM)
+{
+ INT_PTR ret = 0;
+
+ switch (wParam) {
+ case PFLAGNUM_1:
+ // support search and visible list
+ ret = PF1_BASICSEARCH | PF1_ADDSEARCHRES | PF1_EXTSEARCH | PF1_VISLIST | PF1_MODEMSGRECV;
+ break;
+
+ case PFLAGNUM_2:
+ ret = PF2_ONLINE | PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND |
+ PF2_HEAVYDND | PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE;
+ break;
+
+ case PFLAGNUM_4:
+ ret = PF4_AVATARS | PF4_NOCUSTOMAUTH | PF4_NOAUTHDENYREASON | PF4_FORCEAUTH;
+ break;
+
+ case PFLAGNUM_5: /* this is PFLAGNUM_5 change when alpha SDK is released */
+ ret = PF2_INVISIBLE | PF2_SHORTAWAY | PF2_LONGAWAY | PF2_LIGHTDND | PF2_HEAVYDND |
+ PF2_FREECHAT | PF2_OUTTOLUNCH | PF2_ONTHEPHONE;
+ break;
+
+ case PFLAG_UNIQUEIDTEXT:
+ ret = (INT_PTR)Translate("Station ID");
+ break;
+ }
+ return ret;
+}
+
+// protocol service function to get weather protocol name
+INT_PTR WeatherGetName(WPARAM wParam, LPARAM lParam)
+{
+ strncpy((char*)lParam, WEATHERPROTOTEXT, wParam - 1);
+ *((char*)lParam + wParam - 1) = 0;
+ return 0;
+}
+
+// protocol service function to get the current status of the protocol
+INT_PTR WeatherGetStatus(WPARAM, LPARAM)
+{
+ return status;
+}
+
+// protocol service function to get the icon of the protocol
+INT_PTR WeatherLoadIcon(WPARAM wParam, LPARAM)
+{
+ return (LOWORD(wParam) == PLI_PROTOCOL) ? (INT_PTR)CopyIcon(LoadIconEx("main", FALSE)) : 0;
+}
+
+static void __cdecl AckThreadProc(HANDLE param)
+{
+ Sleep(100);
+ ProtoBroadcastAck(MODULENAME, (DWORD_PTR)param, ACKTYPE_GETINFO, ACKRESULT_SUCCESS, (HANDLE)1);
+}
+
+// nothing to do here because weather proto do not need to retrieve contact info form network
+// so just return a 0
+INT_PTR WeatherGetInfo(WPARAM, LPARAM lParam)
+{
+ CCSDATA *ccs = (CCSDATA *)lParam;
+ mir_forkthread(AckThreadProc, (void*)ccs->hContact);
+ return 0;
+}
+
+// avatars
+static const wchar_t *statusStr[] = { L"Light", L"Fog", L"SShower", L"Snow", L"RShower", L"Rain", L"PCloudy", L"Cloudy", L"Sunny", L"NA" };
+static const WORD statusValue[] = { LIGHT, FOG, SSHOWER, SNOW, RSHOWER, RAIN, PCLOUDY, CLOUDY, SUNNY, NA };
+
+INT_PTR WeatherGetAvatarInfo(WPARAM, LPARAM lParam)
+{
+ wchar_t szSearchPath[MAX_PATH], *chop;
+ unsigned i;
+ PROTO_AVATAR_INFORMATION *pai = (PROTO_AVATAR_INFORMATION*)lParam;
+
+ GetModuleFileName(GetModuleHandle(nullptr), szSearchPath, _countof(szSearchPath));
+ chop = wcsrchr(szSearchPath, '\\');
+
+ if (chop) *chop = '\0';
+ else szSearchPath[0] = 0;
+
+ int iStatus = g_plugin.getWord(pai->hContact, "StatusIcon");
+ for (i = 0; i < 10; i++)
+ if (statusValue[i] == iStatus)
+ break;
+
+ if (i >= 10)
+ return GAIR_NOAVATAR;
+
+ pai->format = PA_FORMAT_PNG;
+ mir_snwprintf(pai->filename, L"%s\\Plugins\\Weather\\%s.png", szSearchPath, statusStr[i]);
+ if (_waccess(pai->filename, 4) == 0)
+ return GAIR_SUCCESS;
+
+ pai->format = PA_FORMAT_GIF;
+ mir_snwprintf(pai->filename, L"%s\\Plugins\\Weather\\%s.gif", szSearchPath, statusStr[i]);
+ if (_waccess(pai->filename, 4) == 0)
+ return GAIR_SUCCESS;
+
+ pai->format = PA_FORMAT_UNKNOWN;
+ pai->filename[0] = 0;
+ return GAIR_NOAVATAR;
+}
+
+void AvatarDownloaded(MCONTACT hContact)
+{
+ PROTO_AVATAR_INFORMATION ai = { 0 };
+ ai.hContact = hContact;
+
+ if (WeatherGetAvatarInfo(GAIF_FORCE, (LPARAM)&ai) == GAIR_SUCCESS)
+ ProtoBroadcastAck(MODULENAME, hContact, ACKTYPE_AVATAR, ACKRESULT_SUCCESS, &ai);
+ else
+ ProtoBroadcastAck(MODULENAME, hContact, ACKTYPE_AVATAR, ACKRESULT_STATUS, nullptr);
+}
+
+static void __cdecl WeatherGetAwayMsgThread(void *arg)
+{
+ Sleep(100);
+
+ MCONTACT hContact = (DWORD_PTR)arg;
+ DBVARIANT dbv;
+ if (!db_get_ws(hContact, "CList", "StatusMsg", &dbv)) {
+ ProtoBroadcastAck(MODULENAME, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1, (LPARAM)dbv.pwszVal);
+ db_free(&dbv);
+ }
+ else ProtoBroadcastAck(MODULENAME, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, (HANDLE)1);
+}
+
+static INT_PTR WeatherGetAwayMsg(WPARAM, LPARAM lParam)
+{
+ CCSDATA* ccs = (CCSDATA*)lParam;
+ if (ccs == nullptr)
+ return 0;
+
+ mir_forkthread(WeatherGetAwayMsgThread, (void*)ccs->hContact);
+ return 1;
+}
+
+//============ PROTOCOL INITIALIZATION ============
+// protocol services
+void InitServices(void)
+{
+ CreateProtoServiceFunction(MODULENAME, PS_GETCAPS, WeatherGetCaps);
+ CreateProtoServiceFunction(MODULENAME, PS_GETNAME, WeatherGetName);
+ CreateProtoServiceFunction(MODULENAME, PS_LOADICON, WeatherLoadIcon);
+ CreateProtoServiceFunction(MODULENAME, PS_SETSTATUS, WeatherSetStatus);
+ CreateProtoServiceFunction(MODULENAME, PS_GETSTATUS, WeatherGetStatus);
+ CreateProtoServiceFunction(MODULENAME, PS_BASICSEARCH, WeatherBasicSearch);
+ CreateProtoServiceFunction(MODULENAME, PS_SEARCHBYEMAIL, WeatherBasicSearch);
+ CreateProtoServiceFunction(MODULENAME, PS_ADDTOLIST, WeatherAddToList);
+ CreateProtoServiceFunction(MODULENAME, PSS_GETINFO, WeatherGetInfo);
+ CreateProtoServiceFunction(MODULENAME, PS_GETAVATARINFO, WeatherGetAvatarInfo);
+ CreateProtoServiceFunction(MODULENAME, PSS_GETAWAYMSG, WeatherGetAwayMsg);
+ CreateProtoServiceFunction(MODULENAME, PS_CREATEADVSEARCHUI, WeatherCreateAdvancedSearchUI);
+ CreateProtoServiceFunction(MODULENAME, PS_SEARCHBYADVANCED, WeatherAdvancedSearch);
+
+ CreateProtoServiceFunction(MODULENAME, MS_WEATHER_GETDISPLAY, GetDisplaySvcFunc);
+}
+
+//============ MENU INITIALIZATION ============
+
+void UpdateMenu(BOOL State)
+{
+ // update option setting
+ opt.CAutoUpdate = State;
+ g_plugin.setByte("AutoUpdate", (BYTE)State);
+
+ if (State) { // to enable auto-update
+ Menu_ModifyItem(hEnableDisableMenu, LPGENW("Auto Update Enabled"), GetIconHandle("main"));
+ opt.AutoUpdate = 1;
+ }
+ else { // to disable auto-update
+ Menu_ModifyItem(hEnableDisableMenu, LPGENW("Auto Update Disabled"), GetIconHandle("disabled"));
+ opt.AutoUpdate = 0;
+ }
+
+ CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)hTBButton, !State ? TTBST_PUSHED : 0);
+}
+
+void UpdatePopupMenu(BOOL State)
+{
+ // update option setting
+ opt.UsePopup = State;
+ g_plugin.setByte("UsePopup", (BYTE)opt.UsePopup);
+
+ if (State) // to enable popup
+ Menu_ModifyItem(hEnableDisablePopupMenu, LPGENW("Disable &weather notification"), GetIconHandle("popup"));
+ else // to disable popup
+ Menu_ModifyItem(hEnableDisablePopupMenu, LPGENW("Enable &weather notification"), GetIconHandle("nopopup"));
+}
+
+// update the weather auto-update menu item when click on it
+INT_PTR EnableDisableCmd(WPARAM wParam, LPARAM lParam)
+{
+ UpdateMenu(wParam == TRUE ? (BOOL)lParam : !opt.CAutoUpdate);
+ return 0;
+}
+
+// update the weather popup menu item when click on it
+INT_PTR MenuitemNotifyCmd(WPARAM, LPARAM)
+{
+ UpdatePopupMenu(!opt.UsePopup);
+ return 0;
+}
+
+// adding weather contact menus
+// copied and modified form "modified MSN Protocol"
+void AddMenuItems(void)
+{
+ CMenuItem mi(&g_plugin);
+
+ // contact menu
+ SET_UID(mi, 0x266ef52b, 0x869a, 0x4cac, 0xa9, 0xf8, 0xea, 0x5b, 0xb8, 0xab, 0xe0, 0x24);
+ CreateServiceFunction(MS_WEATHER_UPDATE, UpdateSingleStation);
+ mi.position = -0x7FFFFFFA;
+ mi.hIcolibItem = GetIconHandle("update");
+ mi.name.a = LPGEN("Update Weather");
+ mi.pszService = MS_WEATHER_UPDATE;
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x45361b4, 0x8de, 0x44b4, 0x8f, 0x11, 0x9b, 0xe9, 0x6e, 0xa8, 0x83, 0x54);
+ CreateServiceFunction(MS_WEATHER_REFRESH, UpdateSingleRemove);
+ mi.position = -0x7FFFFFF9;
+ mi.hIcolibItem = GetIconHandle("update2");
+ mi.name.a = LPGEN("Remove Old Data then Update");
+ mi.pszService = MS_WEATHER_REFRESH;
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x4232975e, 0xb181, 0x46a5, 0xb7, 0x6e, 0xd2, 0x5f, 0xef, 0xb8, 0xc4, 0x4d);
+ CreateServiceFunction(MS_WEATHER_BRIEF, BriefInfoSvc);
+ mi.position = -0x7FFFFFF8;
+ mi.hIcolibItem = GetIconHandle("brief");
+ mi.name.a = LPGEN("Brief Information");
+ mi.pszService = MS_WEATHER_BRIEF;
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x3d6ed729, 0xd49a, 0x4ae9, 0x8e, 0x2, 0x9f, 0xe0, 0xf0, 0x2c, 0xcc, 0xb1);
+ CreateServiceFunction(MS_WEATHER_COMPLETE, LoadForecast);
+ mi.position = -0x7FFFFFF7;
+ mi.hIcolibItem = GetIconHandle("read");
+ mi.name.a = LPGEN("Read Complete Forecast");
+ mi.pszService = MS_WEATHER_COMPLETE;
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0xc4b6c5e0, 0x13c3, 0x4e02, 0x8a, 0xeb, 0xeb, 0x8a, 0xe2, 0x66, 0x40, 0xd4);
+ CreateServiceFunction(MS_WEATHER_MAP, WeatherMap);
+ mi.position = -0x7FFFFFF6;
+ mi.hIcolibItem = GetIconHandle("map");
+ mi.name.a = LPGEN("Weather Map");
+ mi.pszService = MS_WEATHER_MAP;
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0xee3ad7f4, 0x3377, 0x4e4c, 0x8f, 0x3c, 0x3b, 0xf5, 0xd4, 0x86, 0x28, 0x25);
+ CreateServiceFunction(MS_WEATHER_LOG, ViewLog);
+ mi.position = -0x7FFFFFF5;
+ mi.hIcolibItem = GetIconHandle("log");
+ mi.name.a = LPGEN("View Log");
+ mi.pszService = MS_WEATHER_LOG;
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ SET_UID(mi, 0x1b01cd6a, 0xe5ee, 0x42b4, 0xa1, 0x6d, 0x43, 0xb9, 0x4, 0x58, 0x43, 0x2e);
+ CreateServiceFunction(MS_WEATHER_EDIT, EditSettings);
+ mi.position = -0x7FFFFFF4;
+ mi.hIcolibItem = GetIconHandle("edit");
+ mi.name.a = LPGEN("Edit Settings");
+ mi.pszService = MS_WEATHER_EDIT;
+ Menu_AddContactMenuItem(&mi, MODULENAME);
+
+ // adding main menu items
+ mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("Weather"), 500099000);
+ Menu_ConfigureItem(mi.root, MCI_OPT_UID, "82809D2F-2CF0-4E15-9350-D257A7748552");
+
+ SET_UID(mi, 0x5ad16188, 0xe0a0, 0x4c31, 0x85, 0xc3, 0xe4, 0x85, 0x79, 0x7e, 0x4b, 0x9c);
+ CreateServiceFunction(MS_WEATHER_ENABLED, EnableDisableCmd);
+ mi.name.a = LPGEN("Enable/Disable Weather Update");
+ mi.hIcolibItem = GetIconHandle("main");
+ mi.position = 10100001;
+ mi.pszService = MS_WEATHER_ENABLED;
+ hEnableDisableMenu = Menu_AddMainMenuItem(&mi);
+ UpdateMenu(opt.AutoUpdate);
+
+ SET_UID(mi, 0x2b1c2054, 0x2991, 0x4025, 0x87, 0x73, 0xb6, 0xf7, 0x85, 0xac, 0xc7, 0x37);
+ CreateServiceFunction(MS_WEATHER_UPDATEALL, UpdateAllInfo);
+ mi.position = 20100001;
+ mi.hIcolibItem = GetIconHandle("update");
+ mi.name.a = LPGEN("Update All Weather");
+ mi.pszService = MS_WEATHER_UPDATEALL;
+ Menu_AddMainMenuItem(&mi);
+
+ SET_UID(mi, 0x8234c00e, 0x788e, 0x424f, 0xbc, 0xc4, 0x2, 0xfd, 0x67, 0x58, 0x2d, 0x19);
+ CreateServiceFunction(MS_WEATHER_REFRESHALL, UpdateAllRemove);
+ mi.position = 20100002;
+ mi.hIcolibItem = GetIconHandle("update2");
+ mi.name.a = LPGEN("Remove Old Data then Update All");
+ mi.pszService = MS_WEATHER_REFRESHALL;
+ Menu_AddMainMenuItem(&mi);
+
+ // only run if popup service exists
+ if (ServiceExists(MS_POPUP_ADDPOPUPW)) {
+ SET_UID(mi, 0xdc5411cb, 0xb7c7, 0x443b, 0x88, 0x5a, 0x90, 0x24, 0x43, 0xde, 0x54, 0x3e);
+ CreateServiceFunction(MODULENAME "/PopupMenu", MenuitemNotifyCmd);
+ mi.name.a = LPGEN("Weather Notification");
+ mi.hIcolibItem = GetIconHandle("popup");
+ mi.position = 0;
+ mi.root = g_plugin.addRootMenu(MO_MAIN, LPGENW("Popups"), 0);
+ mi.pszService = MODULENAME "/PopupMenu";
+ hEnableDisablePopupMenu = Menu_AddMainMenuItem(&mi);
+ UpdatePopupMenu(opt.UsePopup);
+ }
+
+ if (ServiceExists(MS_CLIST_FRAMES_ADDFRAME)) {
+ SET_UID(mi, 0xe193fe9b, 0xf6ad, 0x41ac, 0x95, 0x29, 0x45, 0x4, 0x44, 0xb1, 0xeb, 0x5d);
+ mi.pszService = "Weather/mwin_menu";
+ CreateServiceFunction(mi.pszService, Mwin_MenuClicked);
+ mi.position = -0x7FFFFFF0;
+ mi.hIcolibItem = nullptr;
+ mi.root = nullptr;
+ mi.name.a = LPGEN("Display in a frame");
+ hMwinMenu = Menu_AddContactMenuItem(&mi, MODULENAME);
+ }
+}
diff --git a/protocols/Weather/src/weather_update.cpp b/protocols/Weather/src/weather_update.cpp
new file mode 100644
index 0000000000..12736b35be
--- /dev/null
+++ b/protocols/Weather/src/weather_update.cpp
@@ -0,0 +1,607 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+This file contain the source related to updating new weather
+information, both automatic (by timer) and manually (by selecting
+menu items).
+*/
+
+#include "stdafx.h"
+
+UPDATELIST *UpdateListHead, *UpdateListTail;
+
+//============ RETRIEVE NEW WEATHER ============
+//
+// retrieve weather info and display / log them
+// hContact = current contact
+int UpdateWeather(MCONTACT hContact)
+{
+ wchar_t str[256], str2[MAX_TEXT_SIZE];
+ DBVARIANT dbv;
+ BOOL Ch = FALSE;
+
+ if (hContact == NULL) // some error prevention
+ return 1;
+
+ dbv.pszVal = "";
+
+ // log to netlib log for debug purpose
+ Netlib_LogfW(hNetlibUser, L"************************************************************************");
+ int dbres = g_plugin.getWString(hContact, "Nick", &dbv);
+
+ Netlib_LogfW(hNetlibUser, L"<-- Start update for station -->");
+
+ // download the info and parse it
+ // result are stored in database
+ int code = GetWeatherData(hContact);
+ if (code != 0) {
+ // error occurs if the return value is not equals to 0
+ if (opt.ShowWarnings) {
+ // show warnings by popup
+ mir_snwprintf(str, _countof(str) - 105,
+ TranslateT("Unable to retrieve weather information for %s"), dbv.pwszVal);
+ mir_wstrncat(str, L"\n", _countof(str) - mir_wstrlen(str));
+ wchar_t *tszError = GetError(code);
+ mir_wstrncat(str, tszError, _countof(str) - mir_wstrlen(str));
+ WPShowMessage(str, SM_WARNING);
+ mir_free(tszError);
+ }
+ // log to netlib
+ Netlib_LogfW(hNetlibUser, L"Error! Update cannot continue... Start to free memory");
+ Netlib_LogfW(hNetlibUser, L"<-- Error occurs while updating station: %s -->", dbv.pwszVal);
+ if (!dbres) db_free(&dbv);
+ return 1;
+ }
+ if (!dbres) db_free(&dbv);
+
+ // initialize, load new weather Data
+ WEATHERINFO winfo = LoadWeatherInfo(hContact);
+
+ // translate weather condition
+ mir_wstrcpy(winfo.cond, TranslateW(winfo.cond));
+
+ // compare the old condition and determine if the weather had changed
+ if (opt.UpdateOnlyConditionChanged) { // consider condition change
+ if (!g_plugin.getWString(hContact, "LastCondition", &dbv)) {
+ if (mir_wstrcmpi(winfo.cond, dbv.pwszVal)) Ch = TRUE; // the weather condition is changed
+ db_free(&dbv);
+ }
+ else Ch = TRUE;
+ if (!g_plugin.getWString(hContact, "LastTemperature", &dbv)) {
+ if (mir_wstrcmpi(winfo.temp, dbv.pwszVal)) Ch = TRUE; // the temperature is changed
+ db_free(&dbv);
+ }
+ else Ch = TRUE;
+ }
+ else { // consider update time change
+ if (!g_plugin.getWString(hContact, "LastUpdate", &dbv)) {
+ if (mir_wstrcmpi(winfo.update, dbv.pwszVal)) Ch = TRUE; // the update time is changed
+ db_free(&dbv);
+ }
+ else Ch = TRUE;
+ }
+
+ // have weather alert issued?
+ dbres = db_get_ws(hContact, WEATHERCONDITION, "Alert", &dbv);
+ if (!dbres && dbv.pwszVal[0] != 0) {
+ if (opt.AlertPopup && !g_plugin.getByte(hContact, "DPopUp") && Ch) {
+ // display alert popup
+ mir_snwprintf(str, L"Alert for %s%c%s", winfo.city, 255, dbv.pwszVal);
+ WPShowMessage(str, SM_WEATHERALERT);
+ }
+ // alert issued, set display to italic
+ if (opt.MakeItalic)
+ g_plugin.setWord(hContact, "ApparentMode", ID_STATUS_OFFLINE);
+ Skin_PlaySound("weatheralert");
+ }
+ // alert dropped, set the display back to normal
+ else g_plugin.delSetting(hContact, "ApparentMode");
+ if (!dbres) db_free(&dbv);
+
+ // backup current condition for checking if the weather is changed or not
+ g_plugin.setWString(hContact, "LastLog", winfo.update);
+ g_plugin.setWString(hContact, "LastCondition", winfo.cond);
+ g_plugin.setWString(hContact, "LastTemperature", winfo.temp);
+ g_plugin.setWString(hContact, "LastUpdate", winfo.update);
+
+ // display condition on contact list
+ if (opt.DisCondIcon && winfo.status != ID_STATUS_OFFLINE)
+ g_plugin.setWord(hContact, "Status", ID_STATUS_ONLINE);
+ else
+ g_plugin.setWord(hContact, "Status", winfo.status);
+ AvatarDownloaded(hContact);
+
+ GetDisplay(&winfo, opt.cText, str2);
+ db_set_ws(hContact, "CList", "MyHandle", str2);
+
+ GetDisplay(&winfo, opt.sText, str2);
+ if (str2[0])
+ db_set_ws(hContact, "CList", "StatusMsg", str2);
+ else
+ db_unset(hContact, "CList", "StatusMsg");
+
+ ProtoBroadcastAck(MODULENAME, hContact, ACKTYPE_AWAYMSG, ACKRESULT_SUCCESS, nullptr, (LPARAM)(str2[0] ? str2 : nullptr));
+
+ // save descriptions in MyNotes
+ GetDisplay(&winfo, opt.nText, str2);
+ db_set_ws(hContact, "UserInfo", "MyNotes", str2);
+ GetDisplay(&winfo, opt.xText, str2);
+ db_set_ws(hContact, WEATHERCONDITION, "WeatherInfo", str2);
+
+ // set the update tag
+ g_plugin.setByte(hContact, "IsUpdated", TRUE);
+
+ // save info for default weather condition
+ if (!mir_wstrcmp(winfo.id, opt.Default) && !opt.NoProtoCondition) {
+ // save current condition for default station to be displayed after the update
+ old_status = status;
+ status = winfo.status;
+ // a workaround for a default station that currently have an n/a icon assigned
+ if (status == ID_STATUS_OFFLINE) status = NOSTATUSDATA;
+ ProtoBroadcastAck(MODULENAME, NULL, ACKTYPE_STATUS, ACKRESULT_SUCCESS, (HANDLE)old_status, status);
+ }
+
+ // logging
+ if (Ch) {
+ // play the sound event
+ Skin_PlaySound("weatherupdated");
+
+ if (g_plugin.getByte(hContact, "File")) {
+ // external log
+ if (!g_plugin.getWString(hContact, "Log", &dbv)) {
+ // for the option for overwriting the file, delete old file first
+ if (g_plugin.getByte(hContact, "Overwrite"))
+ DeleteFile(dbv.pwszVal);
+
+ // open the file and set point to the end of file
+ FILE *file = _wfopen(dbv.pwszVal, L"a");
+ db_free(&dbv);
+ if (file != nullptr) {
+ // write data to the file and close
+ GetDisplay(&winfo, opt.eText, str2);
+ fputws(str2, file);
+ fclose(file);
+ }
+ }
+ }
+
+ if (g_plugin.getByte(hContact, "History")) {
+ // internal log using history
+ GetDisplay(&winfo, opt.hText, str2);
+
+ T2Utf szMessage(str2);
+
+ DBEVENTINFO dbei = {};
+ dbei.szModule = MODULENAME;
+ dbei.timestamp = (DWORD)time(0);
+ dbei.flags = DBEF_READ | DBEF_UTF;
+ dbei.eventType = EVENTTYPE_MESSAGE;
+ dbei.pBlob = szMessage;
+ dbei.cbBlob = (DWORD)mir_strlen(szMessage) + 1;
+ db_event_add(hContact, &dbei);
+ }
+
+ // show the popup
+ NotifyEventHooks(hHookWeatherUpdated, hContact, (LPARAM)Ch);
+ }
+
+ Netlib_LogfW(hNetlibUser, L"Update Completed - Start to free memory");
+ Netlib_LogfW(hNetlibUser, L"<-- Update successful for station -->");
+
+ // Update frame data
+ UpdateMwinData(hContact);
+
+ // update brief info if its opened
+ HWND hMoreDataDlg = WindowList_Find(hDataWindowList, hContact);
+ if (hMoreDataDlg != nullptr)
+ PostMessage(hMoreDataDlg, WM_UPDATEDATA, 0, 0);
+ return 0;
+}
+
+//============ UPDATE LIST ============
+//
+// a linked list queue for updating weather station
+// this function add a weather contact to the end of queue for update
+// hContact = current contact
+void UpdateListAdd(MCONTACT hContact)
+{
+ UPDATELIST *newItem = (UPDATELIST*)mir_alloc(sizeof(UPDATELIST));
+ newItem->hContact = hContact;
+ newItem->next = nullptr;
+
+ WaitForSingleObject(hUpdateMutex, INFINITE);
+
+ if (UpdateListTail == nullptr) UpdateListHead = newItem;
+ else UpdateListTail->next = newItem;
+ UpdateListTail = newItem;
+
+ ReleaseMutex(hUpdateMutex);
+}
+
+// get the first item from the update queue and remove it from the queue
+// return value = the contact for next update
+MCONTACT UpdateGetFirst()
+{
+ MCONTACT hContact = NULL;
+
+ WaitForSingleObject(hUpdateMutex, INFINITE);
+
+ if (UpdateListHead != nullptr) {
+ UPDATELIST* Item = UpdateListHead;
+
+ hContact = Item->hContact;
+ UpdateListHead = Item->next;
+ mir_free(Item);
+
+ if (UpdateListHead == nullptr)
+ UpdateListTail = nullptr;
+ }
+
+ ReleaseMutex(hUpdateMutex);
+
+ return hContact;
+}
+
+void DestroyUpdateList(void)
+{
+ WaitForSingleObject(hUpdateMutex, INFINITE);
+
+ // free the list one by one
+ UPDATELIST *temp = UpdateListHead;
+ while (temp != nullptr) {
+ UpdateListHead = temp->next;
+ mir_free(temp);
+ temp = UpdateListHead;
+ }
+ // make sure the entire list is clear
+ UpdateListTail = nullptr;
+
+ ReleaseMutex(hUpdateMutex);
+}
+
+// update all weather thread
+// this thread update each weather station from the queue
+static void UpdateThreadProc(void *)
+{
+ WaitForSingleObject(hUpdateMutex, INFINITE);
+ if (ThreadRunning) {
+ ReleaseMutex(hUpdateMutex);
+ return;
+ }
+ ThreadRunning = TRUE; // prevent 2 instance of this thread running
+ ReleaseMutex(hUpdateMutex);
+
+ // update weather by getting the first station from the queue until the queue is empty
+ while (UpdateListHead != nullptr && !Miranda_IsTerminated())
+ UpdateWeather(UpdateGetFirst());
+
+ // exit the update thread
+ ThreadRunning = FALSE;
+}
+
+//============ UPDATE WEATHER ============
+//
+// update all weather station
+// AutoUpdate = true if it is from automatic update using timer
+// false if it is from update by clicking the main menu
+void UpdateAll(BOOL AutoUpdate, BOOL RemoveData)
+{
+ // add all weather contact to the update queue list
+ for (auto &hContact : Contacts(MODULENAME))
+ if (!g_plugin.getByte(hContact, "AutoUpdate") || !AutoUpdate) {
+ if (RemoveData)
+ DBDataManage(hContact, WDBM_REMOVE, 0, 0);
+ UpdateListAdd(hContact);
+ }
+
+ // if it is not updating, then start the update thread process
+ // if it is updating, the stations just added to the queue will get updated by the already-running process
+ if (!ThreadRunning)
+ mir_forkthread(UpdateThreadProc);
+}
+
+// update a single station
+// wParam = handle for the weather station that is going to be updated
+INT_PTR UpdateSingleStation(WPARAM wParam, LPARAM)
+{
+ if (IsMyContact(wParam)) {
+ // add the station to the end of the update queue
+ UpdateListAdd(wParam);
+
+ // if it is not updating, then start the update thread process
+ // if it is updating, the stations just added to the queue will get
+ // updated by the already-running process
+ if (!ThreadRunning)
+ mir_forkthread(UpdateThreadProc);
+ }
+
+ return 0;
+}
+
+// update a single station with removing the old data
+// wParam = handle for the weather station that is going to be updated
+INT_PTR UpdateSingleRemove(WPARAM wParam, LPARAM)
+{
+ if (IsMyContact(wParam)) {
+ // add the station to the end of the update queue, and also remove old data
+ DBDataManage(wParam, WDBM_REMOVE, 0, 0);
+ UpdateListAdd(wParam);
+
+ // if it is not updating, then start the update thread process
+ // if it is updating, the stations just added to the queue will get updated by the already-running process
+ if (!ThreadRunning)
+ mir_forkthread(UpdateThreadProc);
+ }
+
+ return 0;
+}
+
+// the "Update All" menu item in main menu
+INT_PTR UpdateAllInfo(WPARAM, LPARAM)
+{
+ if (!ThreadRunning)
+ UpdateAll(FALSE, FALSE);
+ return 0;
+}
+
+// the "Update All" menu item in main menu and remove the old data
+INT_PTR UpdateAllRemove(WPARAM, LPARAM)
+{
+ if (!ThreadRunning)
+ UpdateAll(FALSE, TRUE);
+ return 0;
+}
+
+//============ GETTING WEATHER DATA ============
+//
+// getting weather data and save them into the database
+// hContact = the contact to get the data
+int GetWeatherData(MCONTACT hContact)
+{
+ // get each part of the id's
+ wchar_t id[256];
+ GetStationID(hContact, id, _countof(id));
+
+ // test ID format
+ wchar_t* szInfo = wcschr(id, '/');
+ if (szInfo == nullptr)
+ return INVALID_ID_FORMAT;
+
+ GetID(id);
+
+ wchar_t Svc[256];
+ GetStationID(hContact, Svc, _countof(Svc));
+ GetSvc(Svc);
+
+ // check for invalid station
+ if (id[0] == 0) return INVALID_ID;
+ if (Svc[0] == 0) return INVALID_SVC;
+
+ // get the update strings (loaded to memory from ini files)
+ WIDATA *Data = GetWIData(Svc);
+ if (Data == nullptr)
+ return SVC_NOT_FOUND; // the ini for the station cannot be found
+
+ WORD cond = NA;
+ char loc[256];
+ for (int i = 0; i < 4; ++i) {
+ // generate update URL
+ switch (i) {
+ case 0:
+ mir_snprintf(loc, Data->UpdateURL, _T2A(id));
+ break;
+
+ case 1:
+ mir_snprintf(loc, Data->UpdateURL2, _T2A(id));
+ break;
+
+ case 2:
+ mir_snprintf(loc, Data->UpdateURL3, _T2A(id));
+ break;
+
+ case 3:
+ mir_snprintf(loc, Data->UpdateURL4, _T2A(id));
+ break;
+
+ default:
+ continue;
+ }
+
+ if (loc[0] == 0)
+ continue;
+
+ // download the html file from the internet
+ wchar_t* szData = nullptr;
+ int retval = InternetDownloadFile(loc, Data->Cookie, Data->UserAgent, &szData);
+ if (retval != 0) {
+ mir_free(szData);
+ return retval;
+ }
+ if (wcsstr(szData, L"Document Not Found") != nullptr) {
+ mir_free(szData);
+ return DOC_NOT_FOUND;
+ }
+
+ szInfo = szData;
+ WIDATAITEMLIST *Item = Data->UpdateData;
+
+ // begin parsing item by item
+ while (Item != nullptr) {
+ if (Item->Item.Url[0] != 0 && Item->Item.Url[0] != (i + '1')) {
+ Item = Item->Next;
+ continue;
+ }
+
+ wchar_t DataValue[MAX_DATA_LEN];
+ switch (Item->Item.Type) {
+ case WID_NORMAL:
+ // if it is a normal item with start= and end=, then parse through the downloaded string
+ // to get a data value.
+ GetDataValue(&Item->Item, DataValue, &szInfo);
+ if (mir_wstrcmp(Item->Item.Name, L"Condition") && mir_wstrcmpi(Item->Item.Unit, L"Cond"))
+ wcsncpy(DataValue, TranslateW(DataValue), MAX_DATA_LEN - 1);
+ break;
+
+ case WID_SET:
+ {
+ // for the "Set Data=" operation
+ DBVARIANT dbv;
+ wchar_t *chop, *str, str2[MAX_DATA_LEN];
+ BOOL hasvar = FALSE;
+ size_t stl;
+
+ // get the set data operation string
+ str = Item->Item.End;
+ DataValue[0] = 0;
+ // go through each part of the operation string seperated by the & operator
+ do {
+ // the end of the string, last item
+ chop = wcsstr(str, L" & ");
+ if (chop == nullptr)
+ chop = wcschr(str, '\0');
+
+ stl = min(sizeof(str2) - 1, (unsigned)(chop - str - 2));
+ wcsncpy(str2, str + 1, stl);
+ str2[stl] = 0;
+
+ switch (str[0]) {
+ case '[': // variable, add the value to the result string
+ hasvar = TRUE;
+ if (!DBGetData(hContact, _T2A(str2), &dbv)) {
+ mir_wstrncat(DataValue, dbv.pwszVal, _countof(DataValue) - mir_wstrlen(DataValue));
+ DataValue[_countof(DataValue) - 1] = 0;
+ db_free(&dbv);
+ }
+ break;
+
+ case'\"': // constant, add it to the result string
+ mir_wstrncat(DataValue, TranslateW(str2), _countof(DataValue) - mir_wstrlen(DataValue));
+ DataValue[_countof(DataValue) - 1] = 0;
+ break;
+ }
+
+ // remove the front part of the string that is done and continue parsing
+ str = chop + 3;
+ } while (chop[0] && str[0]);
+
+ if (!hasvar) ConvertDataValue(&Item->Item, DataValue);
+ break;
+ }
+ case WID_BREAK:
+ {
+ // for the "Break Data=" operation
+ DBVARIANT dbv;
+ if (!DBGetData(hContact, _T2A(Item->Item.Start), &dbv)) {
+ wcsncpy(DataValue, dbv.pwszVal, _countof(DataValue));
+ DataValue[_countof(DataValue) - 1] = 0;
+ db_free(&dbv);
+ }
+ else {
+ DataValue[0] = 0;
+ break; // do not continue if the source is invalid
+ }
+
+ // generate the strings
+ wchar_t* end = wcsstr(DataValue, Item->Item.Break);
+ if (end == nullptr) {
+ DataValue[0] = 0;
+ break; // exit if break string is not found
+ }
+ *end = '\0';
+ end += mir_wstrlen(Item->Item.Break);
+ while (end[0] == ' ')
+ end++; // remove extra space
+
+ ConvertDataValue(&Item->Item, DataValue);
+
+ // write the 2 strings created from the break operation
+ if (Item->Item.End[0])
+ db_set_ws(hContact, WEATHERCONDITION, _T2A(Item->Item.End), end);
+ break;
+ }
+ }
+
+ // don't store data if it is not available
+ if ((DataValue[0] != 0 && mir_wstrcmp(DataValue, NODATA) &&
+ mir_wstrcmp(DataValue, TranslateW(NODATA)) && mir_wstrcmp(Item->Item.Name, L"Ignore")) ||
+ (!mir_wstrcmp(Item->Item.Name, L"Alert") && i == 0)) {
+ // temporary workaround for mToolTip to show feel-like temperature
+ if (!mir_wstrcmp(Item->Item.Name, L"Feel"))
+ db_set_ws(hContact, WEATHERCONDITION, "Heat Index", DataValue);
+ GetStationID(hContact, Svc, _countof(Svc));
+ if (!mir_wstrcmp(Svc, opt.Default))
+ db_set_ws(0, DEFCURRENTWEATHER, _T2A(Item->Item.Name), DataValue);
+ if (!mir_wstrcmp(Item->Item.Name, L"Condition")) {
+ wchar_t buf[128], *cbuf;
+ mir_snwprintf(buf, L"#%s Weather", DataValue);
+ cbuf = TranslateW(buf);
+ if (cbuf[0] == '#')
+ cbuf = TranslateW(DataValue);
+ db_set_ws(hContact, WEATHERCONDITION, _T2A(Item->Item.Name), cbuf);
+ CharLowerBuff(DataValue, (DWORD)mir_wstrlen(DataValue));
+ cond = GetIcon(DataValue, Data);
+ }
+ else if (mir_wstrcmpi(Item->Item.Unit, L"Cond") == 0) {
+ wchar_t buf[128], *cbuf;
+ mir_snwprintf(buf, L"#%s Weather", DataValue);
+ cbuf = TranslateW(buf);
+ if (cbuf[0] == '#')
+ cbuf = TranslateW(DataValue);
+ db_set_ws(hContact, WEATHERCONDITION, _T2A(Item->Item.Name), cbuf);
+ }
+ else db_set_ws(hContact, WEATHERCONDITION, _T2A(Item->Item.Name), DataValue);
+ }
+ Item = Item->Next;
+ }
+ mir_free(szData);
+ }
+
+ // assign condition icon
+ g_plugin.setWord(hContact, "StatusIcon", cond);
+ g_plugin.setWString(hContact, "MirVer", Data->DisplayName);
+ return 0;
+}
+
+//============ UPDATE TIMERS ============
+//
+// main auto-update timer
+void CALLBACK timerProc(HWND, UINT, UINT_PTR, DWORD)
+{
+ // only run if it is not current updating and the auto update option is enabled
+ if (!ThreadRunning && opt.CAutoUpdate && !Miranda_IsTerminated() && (opt.NoProtoCondition || status == ID_STATUS_ONLINE))
+ UpdateAll(TRUE, FALSE);
+}
+
+
+// temporary timer for first run
+// when this is run, it kill the old startup timer and create the permenant one above
+void CALLBACK timerProc2(HWND, UINT, UINT_PTR, DWORD)
+{
+ KillTimer(nullptr, timerId);
+ ThreadRunning = FALSE;
+
+ if (Miranda_IsTerminated())
+ return;
+
+ if (opt.StartupUpdate && opt.NoProtoCondition)
+ UpdateAll(FALSE, FALSE);
+ timerId = SetTimer(nullptr, 0, ((int)opt.UpdateTime) * 60000, timerProc);
+}
diff --git a/protocols/Weather/src/weather_userinfo.cpp b/protocols/Weather/src/weather_userinfo.cpp
new file mode 100644
index 0000000000..3ce9460c1b
--- /dev/null
+++ b/protocols/Weather/src/weather_userinfo.cpp
@@ -0,0 +1,358 @@
+/*
+Weather Protocol plugin for Miranda IM
+Copyright (c) 2012 Miranda NG team
+Copyright (c) 2005-2011 Boris Krasnovskiy All Rights Reserved
+Copyright (c) 2002-2005 Calvin Che
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation; version 2
+of the License.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+
+/*
+This file contain the source that is related to display contact
+information, including the one shows in user detail and the brief
+information
+*/
+
+#include "stdafx.h"
+
+//============ BRIEF INFORMATION ============
+//
+static int BriefDlgResizer(HWND, LPARAM, UTILRESIZECONTROL *urc)
+{
+ switch (urc->wId) {
+ case IDC_HEADERBAR:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH;
+
+ case IDC_MTEXT:
+ case IDC_DATALIST:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP | RD_ANCHORX_WIDTH | RD_ANCHORY_HEIGHT;
+
+ case IDC_MUPDATE:
+ return RD_ANCHORX_LEFT | RD_ANCHORY_BOTTOM;
+
+ case IDC_MTOGGLE:
+ case IDC_MWEBPAGE:
+ case IDCANCEL:
+ return RD_ANCHORX_RIGHT | RD_ANCHORY_BOTTOM;
+ }
+ return RD_ANCHORX_LEFT | RD_ANCHORY_TOP;
+}
+
+// set the title of the dialog and on the which rectangle
+// also load brief info into message box
+static void LoadBriefInfoText(HWND hwndDlg, MCONTACT hContact)
+{
+ WEATHERINFO winfo;
+ wchar_t str[4096];
+
+ // load weather information from the contact into the WEATHERINFO struct
+ winfo = LoadWeatherInfo(hContact);
+ // check if data exist. If not, display error message box
+ if (!g_plugin.getByte(hContact, "IsUpdated"))
+ wcsncpy(str, WEATHER_NO_INFO, _countof(str) - 1);
+ else
+ // set the display text and show the message box
+ GetDisplay(&winfo, opt.bText, str);
+ SetDlgItemText(hwndDlg, IDC_MTEXT, str);
+
+ GetDisplay(&winfo, L"%c, %t", str);
+ SetWindowText(hwndDlg, winfo.city);
+ SetDlgItemText(hwndDlg, IDC_HEADERBAR, str);
+}
+
+// dialog process for more data in the user info window
+// lParam = contact handle
+static INT_PTR CALLBACK DlgProcMoreData(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ static const unsigned tabstops = 48;
+ MCONTACT hContact = (MCONTACT)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ // save the contact handle for later use
+ hContact = lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hContact);
+
+ SendDlgItemMessage(hwndDlg, IDC_MTEXT, EM_AUTOURLDETECT, (WPARAM)TRUE, 0);
+ SendDlgItemMessage(hwndDlg, IDC_MTEXT, EM_SETEVENTMASK, 0, ENM_LINK);
+ SendDlgItemMessage(hwndDlg, IDC_MTEXT, EM_SETMARGINS, EC_LEFTMARGIN, 5);
+ SendDlgItemMessage(hwndDlg, IDC_MTEXT, EM_SETTABSTOPS, 1, (LPARAM)&tabstops);
+
+ // get the list to display
+ {
+ LV_COLUMN lvc = { 0 };
+ HWND hList = GetDlgItem(hwndDlg, IDC_DATALIST);
+ RECT aRect = { 0 };
+ GetClientRect(hList, &aRect);
+
+ // managing styles
+ lvc.mask = LVCF_WIDTH | LVCF_TEXT;
+ ListView_SetExtendedListViewStyleEx(hList,
+ LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP,
+ LVS_EX_FULLROWSELECT | LVS_EX_INFOTIP);
+
+ // inserting columns
+ lvc.cx = LIST_COLUMN;
+ lvc.pszText = TranslateT("Variable");
+ ListView_InsertColumn(hList, 0, &lvc);
+
+ lvc.cx = aRect.right - LIST_COLUMN - GetSystemMetrics(SM_CXVSCROLL) - 3;
+ lvc.pszText = TranslateT("Information");
+ ListView_InsertColumn(hList, 1, &lvc);
+
+ // inserting data
+ SendMessage(hwndDlg, WM_UPDATEDATA, 0, 0);
+ }
+ TranslateDialogDefault(hwndDlg);
+
+ // prevent dups of the window
+ WindowList_Add(hDataWindowList, hwndDlg, hContact);
+
+ // restore window position
+ Utils_RestoreWindowPositionNoMove(hwndDlg, NULL, MODULENAME, "BriefInfo_");
+ return TRUE;
+
+ case WM_UPDATEDATA:
+ ListView_DeleteAllItems(GetDlgItem(hwndDlg, IDC_DATALIST));
+ LoadBriefInfoText(hwndDlg, hContact);
+ DBDataManage(hContact, WDBM_DETAILDISPLAY, (WPARAM)hwndDlg, 0);
+
+ // set icons
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Window_SetProtoIcon_IcoLib(hwndDlg, MODULENAME, g_plugin.getWord(hContact, "StatusIcon", 0));
+
+ RedrawWindow(GetDlgItem(hwndDlg, IDC_HEADERBAR), nullptr, nullptr, RDW_INVALIDATE | RDW_UPDATENOW);
+ break;
+
+ case WM_SIZE:
+ {
+ RECT rc;
+ HWND hList = GetDlgItem(hwndDlg, IDC_DATALIST);
+ GetWindowRect(hList, &rc);
+ ListView_SetColumnWidth(hList, 1, ListView_GetColumnWidth(hList, 1) + (int)LOWORD(lParam) - (rc.right - rc.left));
+
+ Utils_ResizeDialog(hwndDlg, g_plugin.getInst(), MAKEINTRESOURCEA(IDD_BRIEF), BriefDlgResizer);
+ }
+ break;
+
+ case WM_GETMINMAXINFO:
+ {
+ LPMINMAXINFO mmi = (LPMINMAXINFO)lParam;
+
+ // The minimum width in points
+ mmi->ptMinTrackSize.x = 350;
+ // The minimum height in points
+ mmi->ptMinTrackSize.y = 300;
+ }
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDCANCEL:
+ // close the info window
+ DestroyWindow(hwndDlg);
+ break;
+
+ case IDC_MUPDATE:
+ {
+ HWND hList = GetDlgItem(hwndDlg, IDC_DATALIST);
+
+ // update current data
+ // set the text to "updating"
+ SetDlgItemText(hwndDlg, IDC_MTEXT, TranslateT("Retrieving new data, please wait..."));
+ ListView_DeleteAllItems(hList);
+
+ LV_ITEM lvi = { 0 };
+ lvi.mask = LVIF_TEXT | LVIF_PARAM;
+ lvi.lParam = 1;
+ lvi.pszText = L"";
+ lvi.iItem = ListView_InsertItem(hList, &lvi);
+ lvi.pszText = TranslateT("Retrieving new data, please wait...");
+ ListView_SetItemText(hList, lvi.iItem, 1, lvi.pszText);
+ UpdateSingleStation(hContact, 0);
+ break;
+ }
+
+ case IDC_MWEBPAGE:
+ LoadForecast(hContact, 0); // read complete forecast
+ break;
+
+ case IDC_MTOGGLE:
+ if (IsWindowVisible(GetDlgItem(hwndDlg, IDC_DATALIST)))
+ SetDlgItemText(hwndDlg, IDC_MTOGGLE, TranslateT("More Info"));
+ else
+ SetDlgItemText(hwndDlg, IDC_MTOGGLE, TranslateT("Brief Info"));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_DATALIST), (int)!IsWindowVisible(
+ GetDlgItem(hwndDlg, IDC_DATALIST)));
+ ShowWindow(GetDlgItem(hwndDlg, IDC_MTEXT), (int)!IsWindowVisible(GetDlgItem(hwndDlg, IDC_MTEXT)));
+ break;
+ }
+ break;
+
+ case WM_NOTIFY:
+ {
+ LPNMHDR pNmhdr = (LPNMHDR)lParam;
+ if (pNmhdr->idFrom == IDC_MTEXT && pNmhdr->code == EN_LINK) {
+ ENLINK *enlink = (ENLINK *)lParam;
+ switch (enlink->msg) {
+ case WM_LBUTTONUP:
+ TEXTRANGE tr;
+ tr.chrg = enlink->chrg;
+ tr.lpstrText = (wchar_t*)mir_alloc(sizeof(wchar_t)*(tr.chrg.cpMax - tr.chrg.cpMin + 8));
+ SendMessage(pNmhdr->hwndFrom, EM_GETTEXTRANGE, 0, (LPARAM)&tr);
+ Utils_OpenUrlW(tr.lpstrText);
+ mir_free(tr.lpstrText);
+ break;
+ }
+ }
+ }
+ break;
+
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+
+ case WM_DESTROY:
+ Window_FreeIcon_IcoLib(hwndDlg);
+ Utils_SaveWindowPosition(hwndDlg, NULL, MODULENAME, "BriefInfo_");
+ WindowList_Remove(hDataWindowList, hwndDlg);
+ break;
+ }
+
+ return FALSE;
+}
+
+// dialog process for the weather tab under user info
+// lParam = current contact
+static INT_PTR CALLBACK DlgProcUIPage(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam)
+{
+ WEATHERINFO w;
+ wchar_t str[MAX_TEXT_SIZE];
+
+ MCONTACT hContact = (MCONTACT)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ SendDlgItemMessage(hwndDlg, IDC_MOREDETAIL, BUTTONSETASFLATBTN, TRUE, 0);
+ // save the contact handle for later use
+ hContact = lParam;
+ SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (LONG_PTR)hContact);
+ // load weather info for the contact
+ w = LoadWeatherInfo(lParam);
+ SetDlgItemText(hwndDlg, IDC_INFO1, GetDisplay(&w, TranslateT("Current condition for %n"), str));
+
+ SendDlgItemMessage(hwndDlg, IDC_INFOICON, STM_SETICON, (WPARAM)Skin_LoadProtoIcon(MODULENAME, g_plugin.getWord(hContact, "StatusIcon")), 0);
+ {
+ // bold and enlarge the current condition
+ LOGFONT lf;
+ HFONT hNormalFont = (HFONT)SendDlgItemMessage(hwndDlg, IDC_INFO2, WM_GETFONT, 0, 0);
+ GetObject(hNormalFont, sizeof(lf), &lf);
+ lf.lfWeight = FW_BOLD;
+ lf.lfWidth = 7;
+ lf.lfHeight = 15;
+ SendDlgItemMessage(hwndDlg, IDC_INFO2, WM_SETFONT, (WPARAM)CreateFontIndirect(&lf), 0);
+ }
+ // set the text for displaying other current weather conditions data
+ GetDisplay(&w, L"%c %t", str);
+ SetDlgItemText(hwndDlg, IDC_INFO2, str);
+ SetDlgItemText(hwndDlg, IDC_INFO3, w.feel);
+ SetDlgItemText(hwndDlg, IDC_INFO4, w.pressure);
+ GetDisplay(&w, L"%i %w", str);
+ SetDlgItemText(hwndDlg, IDC_INFO5, str);
+ SetDlgItemText(hwndDlg, IDC_INFO6, w.dewpoint);
+ SetDlgItemText(hwndDlg, IDC_INFO7, w.sunrise);
+ SetDlgItemText(hwndDlg, IDC_INFO8, w.sunset);
+ SetDlgItemText(hwndDlg, IDC_INFO9, w.high);
+ SetDlgItemText(hwndDlg, IDC_INFO10, w.low);
+ GetDisplay(&w, TranslateT("Last update on: %u"), str);
+ SetDlgItemText(hwndDlg, IDC_INFO11, str);
+ SetDlgItemText(hwndDlg, IDC_INFO12, w.humid);
+ SetDlgItemText(hwndDlg, IDC_INFO13, w.vis);
+ break;
+
+ case WM_DESTROY:
+ IcoLib_ReleaseIcon((HICON)SendDlgItemMessage(hwndDlg, IDC_INFOICON, STM_SETICON, 0, 0));
+ DeleteObject((HFONT)SendDlgItemMessage(hwndDlg, IDC_INFO2, WM_GETFONT, 0, 0));
+ break;
+
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_MOREDETAIL:
+ HWND hMoreDataDlg = WindowList_Find(hDataWindowList, hContact);
+ if (hMoreDataDlg == nullptr)
+ hMoreDataDlg = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_BRIEF), nullptr, DlgProcMoreData, hContact);
+ else {
+ SetForegroundWindow(hMoreDataDlg);
+ SetFocus(hMoreDataDlg);
+ }
+ ShowWindow(GetDlgItem(hMoreDataDlg, IDC_MTEXT), 0);
+ ShowWindow(GetDlgItem(hMoreDataDlg, IDC_DATALIST), 1);
+ }
+ break;
+ }
+ return 0;
+}
+
+//============ CONTACT INFORMATION ============
+//
+// initialize user info
+// lParam = current contact
+int UserInfoInit(WPARAM wParam, LPARAM hContact)
+{
+ OPTIONSDIALOGPAGE odp = {};
+ odp.position = 100000000;
+ odp.szTitle.a = MODULENAME;
+
+ if (hContact == 0) {
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_INFO);
+ odp.pfnDlgProc = DlgProcINIPage;
+ g_plugin.addUserInfo(wParam, &odp);
+ }
+ else if (IsMyContact(hContact)) { // check if it is a weather contact
+ // register the contact info page
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_USERINFO);
+ odp.pfnDlgProc = DlgProcUIPage;
+ odp.flags = ODPF_BOLDGROUPS;
+ g_plugin.addUserInfo(wParam, &odp);
+ }
+ return 0;
+}
+
+
+// show brief information dialog
+// wParam = current contact
+int BriefInfo(WPARAM wParam, LPARAM)
+{
+ // make sure that the contact is actually a weather one
+ if (IsMyContact(wParam)) {
+ HWND hMoreDataDlg = WindowList_Find(hDataWindowList, wParam);
+ if (hMoreDataDlg != nullptr) {
+ SetForegroundWindow(hMoreDataDlg);
+ SetFocus(hMoreDataDlg);
+ }
+ else hMoreDataDlg = CreateDialogParam(g_plugin.getInst(), MAKEINTRESOURCE(IDD_BRIEF), nullptr, DlgProcMoreData, (LPARAM)wParam);
+
+ ShowWindow(GetDlgItem(hMoreDataDlg, IDC_DATALIST), 0);
+ ShowWindow(GetDlgItem(hMoreDataDlg, IDC_MTEXT), 1);
+ SetDlgItemText(hMoreDataDlg, IDC_MTOGGLE, TranslateT("More Info"));
+ return 1;
+ }
+ return 0;
+}
+
+INT_PTR BriefInfoSvc(WPARAM wParam, LPARAM lParam)
+{
+ return BriefInfo(wParam, lParam);
+}