diff options
Diffstat (limited to 'newstatusnotify/main.c')
-rw-r--r-- | newstatusnotify/main.c | 2378 |
1 files changed, 2378 insertions, 0 deletions
diff --git a/newstatusnotify/main.c b/newstatusnotify/main.c new file mode 100644 index 0000000..3885d73 --- /dev/null +++ b/newstatusnotify/main.c @@ -0,0 +1,2378 @@ +/*
+===============================================================================
+ NewStatusNotify plugin
+
+This plugin notifies you when a contact changes his/hers status with a PopUp.
+You can customize its look (style of popup, font, text...) and behaviour (the
+position in the screen, the time to live, the action on a mouse click).
+
+File name: "main.c"
+
+Written by: Hrk (Luca Santarelli) (2002-2004), Vasilich (2005-2008)
+
+Miranda IM can be found here:
+http://miranda-im.org/
+
+===============================================================================
+*/
+
+#define _MERGE_RDATA_
+#include "newstatusnotify.h"
+#include "res\version.h"
+
+//extern int CallServiceSync(const char *name, WPARAM wParam, LPARAM lParam);
+
+/*
+HANDLE hContact = (HANDLE)wParam;
+WORD oldStatus = LOWORD(lParam);
+WORD newStatus = HIWORD(lParam);
+oldStatus is the status the contact was before the change.
+newStatus is the status the contact is now.
+Cast them to (int) if you need them that way.
+*/
+#define ME_STATUSCHANGE_CONTACTSTATUSCHANGED "Miranda/StatusChange/ContactStatusChanged"
+
+//malloc functions
+//#define malloc(size) GlobalAlloc(GMEM_ZEROINIT | GMEM_FIXED, size)
+//#define free(something) GlobalFree(something)
+
+const char* ModuleName = "NewStatusNotify";
+//const char *ModuleIndSounds = "NewStatusNotifyIS";
+
+#define POPUPCOLOR_BG_40071 RGB(0x80, 0x80, 0x80)
+#define POPUPCOLOR_TX_40071 RGB(0xFF, 0xFF, 0xFF)
+#define POPUPCOLOR_BG_40072 RGB(0x95, 0x95, 0xFF)
+#define POPUPCOLOR_TX_40072 RGB(0x00, 0x00, 0xFF)
+#define POPUPCOLOR_BG_40073 RGB(0x00, 0x40, 0x80)
+#define POPUPCOLOR_TX_40073 RGB(0xFF, 0xFF, 0xFF)
+#define POPUPCOLOR_BG_40074 RGB(0x00, 0x00, 0x00)
+#define POPUPCOLOR_TX_40074 RGB(0xFF, 0x00, 0x00)
+#define POPUPCOLOR_BG_40075 RGB(0x00, 0x00, 0x80)
+#define POPUPCOLOR_TX_40075 RGB(0xFF, 0xFF, 0xFF)
+#define POPUPCOLOR_BG_40076 RGB(0xFF, 0x80, 0x80)
+#define POPUPCOLOR_TX_40076 RGB(0x00, 0x00, 0x00)
+#define POPUPCOLOR_BG_40077 RGB(0xFF, 0xFF, 0x00)
+#define POPUPCOLOR_TX_40077 RGB(0x00, 0x00, 0x00)
+#define POPUPCOLOR_BG_40078 RGB(0xC0, 0xC0, 0xC0)
+#define POPUPCOLOR_TX_40078 RGB(0x60, 0x60, 0x60)
+#define POPUPCOLOR_BG_40079 RGB(0x00, 0x80, 0x00)
+#define POPUPCOLOR_TX_40079 RGB(0xFF, 0xFF, 0xFF)
+#define POPUPCOLOR_BG_40080 RGB(0x00, 0xFF, 0x00)
+#define POPUPCOLOR_TX_40080 RGB(0x00, 0x00, 0x00)
+
+//Actions
+#define ACTION_OPENMSG 0x01
+#define ACTION_DISMISS 0x02
+#define ACTION_USERMENU 0x03
+
+#define BEEP_DURATION_DEFAULT 155
+#define BEEP_HZ_DEFAULT 155
+
+#define UM_SETDLGITEMINT 5674
+
+#define PROTO_POPUPS_ON 1
+#define PROTO_POPUPS_OFF 0
+
+#define UM_SETCONTACT (WM_USER + 452)
+#define UM_PROCESS (WM_USER + 453)
+
+#define TIMEOUT_POPUP 1
+#define TIMEOUT_CUSTOM 2
+#define TIMEOUT_PERMANENT 3
+#define TIMEOUT_MINVALUE 1
+#define TIMEOUT_MAXVALUE 60
+
+//Defaults
+#define DEFAULT_PROTO_POPUPS PROTO_POPUPS_ON
+#define DEFAULT_USEWINCOLORS 0
+#define DEFAULT_USEDULLTEXT 0
+#define DEFAULT_TEMPDISABLE 0
+#define DEFAULT_AUTODISABLE 0
+#define DEFAULT_FROMOFFLINE 1
+#define DEFAULT_ENABLESOUNDS 1
+#define DEFAULT_USESPEAKER 0
+#define DEFAULT_AVOIDCLIPPING 1
+#define DEFAULT_HIDDENCONTACTSTOO 0
+#define DEFAULT_SHOWONCONNECTION 0
+#define DEFAULT_SHOWDISABLEMENU 1
+#define DEFAULT_NOTIFYWITHPOPUP 1
+#define DEFAULT_NOTIFYWITHSOUND 1
+#define DEFAULT_READAWAYMSG 0
+#define DEFAULT_SHOWPREVIOUSSTATUS 0
+#define DEFAULT_LOG 0
+#define DEFAULT_LOGFILE _T("NewStatusNotify.log")
+#define DEFAULT_BLINKICON FALSE
+#define DEFAULT_BLINKICON_STATUS FALSE
+#define DEFAULT_TIMEOUT TIMEOUT_POPUP
+#define DEFAULT_TIMEOUT_VALUE 7
+#define DEFAULT_USE_IND_SOUNDS 1
+#define DEFAULT_SHOWGROUP 0
+
+#define STRING_SHOWPREVIOUSSTATUS _T("(was %s)")
+/*This is needed to send a message with safe multithreading.
+We'll create a private hook and we'll call it via NotifyEventHooks, which brings execution
+back to the main thread.
+What we'll need is:
+- hHookSendMessage, the handle to the created hook.
+- hSendMessage, the hook we'll use (i.e.: the hook which will be notified)
+- ME_NSN_CONTACTSENDMESSAGE, the service name
+- ContactSendMessage(WPARAM, LPARAM) which will do the job
+- (finally) move the send code to ContactSendMessage and put a NotifyEventHooks inside WM_COMMAND.
+*/
+
+#define ME_NSN_CONTACTSENDMESSAGE "NewStatusNotify/ContactSendMessage"
+
+#define WM_AWAYMSG (WM_USER + 0x0192)
+//===== PopUp =====
+LRESULT CALLBACK PopupDlgProc(HWND, UINT, WPARAM, LPARAM);
+//===== DB functions =====
+BYTE GetGender(HANDLE);
+//===== MessageBoxes =====
+void Log(TCHAR*); //This one is used to notify the user
+
+static int ModulesLoaded(WPARAM wParam,LPARAM lParam);
+//===== Options =====
+void LoadOptions();
+void installMainMenuItem();
+static int OptionsInitialize(WPARAM,LPARAM);
+static BOOL CALLBACK DlgProcGeneralOpts(HWND, UINT, WPARAM, LPARAM);
+static BOOL CALLBACK DlgProcPopUpOpts(HWND, UINT, WPARAM, LPARAM);
+extern BOOL CALLBACK DlgProcSoundOpts2(HWND, UINT, WPARAM, LPARAM);
+extern BOOL CALLBACK DlgProcSoundUIPage(HWND, UINT, WPARAM, LPARAM);
+static BOOL CALLBACK DlgProcAutoDisableOpts(HWND, UINT, WPARAM, LPARAM);
+static int OpenUserDetails(WPARAM, LPARAM);
+extern int UserInfoInitialise(WPARAM, LPARAM);
+//===== Plugin specific =====
+static int ContactSettingChanged(WPARAM,LPARAM);
+static int ContactStatusChanged(WPARAM, LPARAM);
+static int EnableDisableMenuCommand(WPARAM,LPARAM);
+static int ProtoAck(WPARAM,LPARAM);
+static int ContactSendMessage(WPARAM, LPARAM);
+static BOOL StatusHasAwayMessage(char *szProto, int status);
+void PlaySpeakerBeep(void);
+int SortPUWinList(void* it1, void * it2);
+
+//===== Initializations =====
+void InitStatusList();
+void InitPopUpFont();
+
+//HANDLE hhkProtoAck = NULL;
+HANDLE hPopupList = NULL;
+void CALLBACK ConnectionTimerProc(HWND,UINT,UINT,DWORD);
+void ReceivedAwayMessage(HWND, LPARAM , PLUGINDATA *);
+void ReceivedStatusMessage (HANDLE, BOOL);
+BOOL QueryAwayMessage(HWND , PLUGINDATA *);
+
+//===== Global variables =====
+//===== General Plugin =====
+HINSTANCE hInst;
+PLUGINLINK *pluginLink;
+//CLISTMENUITEM mi;
+int hEnableDisableMenu;
+BOOL IsOptionsOpened=FALSE;
+//===== Event Handles =====
+HANDLE hOptionsInitialize, hModulesLoaded, hOpenUserDetails;
+HANDLE hUserInfoInitialise;
+HANDLE hContactSettingChanged, hHookContactStatusChanged, hContactStatusChanged;
+//HANDLE hEventStatusModeChange;
+HANDLE hProtoAck;
+HANDLE hHookSendMessage, hSendMessage; //Internal "private" event.
+//HANDLE hHookMenuProcessCommand, hMenuProcessCommand; //Internal "private" event.
+HANDLE hServiceMenu;
+extern HWND hCListClone;
+HWND SecretWnd;
+//HWND hRightClickWnd; //This is needed to bring menu commands back to the main Miranda thread.
+//static LRESULT CALLBACK RightClickWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+//===== Other stuff... =====
+BYTE count = 0;
+int uniqueEventId = 0;
+#define ID_STATUS_ALLSTATUSES 40070
+#define STATUSES 11
+aStatus StatusesList[STATUSES];
+MYOPTIONS MyOptions = { 0 };
+struct MM_INTERFACE mmi = { 0 };
+struct LIST_INTERFACE li = { 0 };
+SortedList *PopupList = NULL;
+
+//===== Plugin informations struct =====
+PLUGININFO pluginInfo={
+ sizeof(PLUGININFO),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESC,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ DEFMOD_RNDUSERONLINE //Replaces UserOnline functionality
+};
+
+//===== New Plugin informations struct =====
+PLUGININFOEX pluginInfoEx={
+ sizeof(PLUGININFOEX),
+ __PLUGIN_NAME,
+ PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM),
+ __DESC,
+ __AUTHOR,
+ __AUTHOREMAIL,
+ __COPYRIGHT,
+ __AUTHORWEB,
+ UNICODE_AWARE,
+ DEFMOD_RNDUSERONLINE, //Replaces UserOnline functionality
+ MIID_NSN
+};
+
+//===== Basic plugin (template) functions =====
+//=== DllMain =====
+BOOL WINAPI DllMain(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpvReserved) {
+ hInst=hinstDLL;
+ DisableThreadLibraryCalls(hInst);
+ return TRUE;
+}
+
+//===== MirandaPluginInfo =====
+//extern "C"
+__declspec(dllexport) PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) {
+ return &pluginInfo;
+}
+
+//extern "C"
+__declspec(dllexport) PLUGININFOEX* MirandaPluginInfoEx(DWORD mirandaVersion) {
+ return &pluginInfoEx;
+}
+
+static const MUUID interfaces[] = { MIID_USERONLINE, MIID_LAST };
+
+__declspec(dllexport) const MUUID *MirandaPluginInterfaces(void) {
+ return interfaces;
+}
+
+//===== Load =====
+//extern "C"
+int __declspec(dllexport) Load(PLUGINLINK *link) {
+ int i = 0;
+ pluginLink=link;
+ mir_getMMI(&mmi);
+ mir_getLI(&li);
+
+ //Hooks
+ //"Service" Hook, used when the DB settings change: we'll monitor the "status" setting.
+ hContactSettingChanged = HookEvent(ME_DB_CONTACT_SETTINGCHANGED,ContactSettingChanged);
+ //We create this Hook which will notify everyone when a contact changes his status.
+ hHookContactStatusChanged = CreateHookableEvent(ME_STATUSCHANGE_CONTACTSTATUSCHANGED);
+ hModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded);
+ //We add the option page and the user info page (it's needed because options are loaded after plugins)
+ hOptionsInitialize = HookEvent(ME_OPT_INITIALISE,OptionsInitialize);
+ //Protocol status: we use this for "PopUps on Connection".
+ hProtoAck = HookEvent(ME_PROTO_ACK,ProtoAck);
+ //This is needed for "NoSound"-like routines.
+// hEventStatusModeChange = HookEvent(ME_CLIST_STATUSMODECHANGE,StatusModeChanged);
+ //open user details dialog on proper page when clicking on contact in our SOUNDS2 options page
+ hOpenUserDetails=HookEvent(ME_CLIST_DOUBLECLICKED, OpenUserDetails);
+ //Internal hooks used to bring the send dialog to the main thread.
+ hHookSendMessage = CreateHookableEvent(ME_NSN_CONTACTSENDMESSAGE);
+ hSendMessage = HookEvent(ME_NSN_CONTACTSENDMESSAGE, ContactSendMessage);
+// hHookMenuProcessCommand = CreateHookableEvent(ME_NSN_CONTACTMENUPROCESSCOMMAND);
+// hMenuProcessCommand = HookEvent(ME_NSN_CONTACTMENUPROCESSCOMMAND, ContactMenuProcessCommand);
+ //Menu item
+ if (DBGetContactSettingByte(NULL,ModuleName,"ShowDisableMenu",DEFAULT_SHOWDISABLEMENU)==TRUE)
+ installMainMenuItem();
+/* {
+ CLISTMENUITEM mi = { 0 };
+ CreateServiceFunction("NewStatusNotify/EnableDisableMenuCommand",EnableDisableMenuCommand);
+ mi.cbSize = sizeof(mi);
+ //mi.position=-0x7FFFFFFF;
+ mi.flags=0;
+ if (DBGetContactSettingByte(NULL,ModuleName,"TempDisable",DEFAULT_TEMPDISABLE)==TRUE)
+ {
+ //If Miranda is 0.1.2.1-, we can't change both text and icon.
+ //If it's 0.1.2.2+, we can and we do.
+ mi.pszName = Translate("Enable status ¬ification");
+ //if (CallService(MS_SYSTEM_GETVERSION,(WPARAM)0,(LPARAM)0) >= 0x00010202)
+ mi.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_NOPOPUP));
+ }
+ else
+ {
+ mi.pszName = Translate("Disable status ¬ification");
+ //if (CallService(MS_SYSTEM_GETVERSION,(WPARAM)0,(LPARAM)0) >= 0x00010202)
+ mi.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_POPUP));
+ }
+ mi.pszService="NewStatusNotify/EnableDisableMenuCommand";
+ mi.pszPopupName = NULL; //Translate("PopUps");
+ hEnableDisableMenu = CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+ }
+*/
+ InitStatusList();
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++)
+ SkinAddNewSoundEx( StatusesList[Index(i)].lpzSkinSoundName, "Status Notify", StatusesList[Index(i)].lpzSkinSoundDesc);
+
+ SkinAddNewSoundEx("UserFromOffline", "Status Notify", "User: from offline (has priority!)");
+ count = 0;
+
+ LoadOptions();
+ CallService(MS_DB_SETSETTINGRESIDENT, (WPARAM)TRUE,(LPARAM)"MetaContacts/LastOnline");
+ CallService(MS_DB_SETSETTINGRESIDENT, (WPARAM)TRUE,(LPARAM)"NewStatusNotify/"LASTPOPUPTEXT);
+
+ //if (PopupList == NULL)
+ PopupList = li.List_Create(0,10);
+ PopupList->sortFunc = SortPUWinList;
+ return 0;
+}
+
+//===== Unload =====
+//extern "C"
+int __declspec(dllexport) Unload(void) {
+ li.List_Destroy(PopupList);
+ //Hooks
+ UnhookEvent(hContactSettingChanged);
+ UnhookEvent(hOptionsInitialize);
+ UnhookEvent(hModulesLoaded);
+ UnhookEvent(hUserInfoInitialise);
+ //UnhookEvent(hEventStatusModeChange);
+ UnhookEvent(hProtoAck);
+ UnhookEvent(hSendMessage);
+ UnhookEvent(hOpenUserDetails);
+
+ DestroyHookableEvent(hHookContactStatusChanged);
+ DestroyHookableEvent(hHookSendMessage);
+
+ if (ServiceExists("NewStatusNotify/EnableDisableMenuCommand"))
+ DestroyServiceFunction(hServiceMenu);
+
+// UnhookEvent(hMenuProcessCommand);
+ return 0;
+}
+/*
+#ifdef _TINYCONFIG
+BOOL WINAPI _DllMainCRTStartup(HINSTANCE hinstDLL,DWORD fdwReason,LPVOID lpReserved)
+{
+ return DllMain(hinstDLL,fdwReason,lpReserved);
+}
+#endif
+*/
+
+//===== Options pages =====
+void LoadOptions() {
+ //ZeroMemory(&MyOptions, sizeof(MyOptions));
+ //PopUp Options
+ MyOptions.Colors =DBGetContactSettingByte(NULL,ModuleName, "Colors", DEFAULT_COLORS);
+ MyOptions.UseDullText = DBGetContactSettingByte(NULL, ModuleName, "UseDullText", DEFAULT_USEDULLTEXT);
+ MyOptions.ReadAwayMsg = DBGetContactSettingByte(NULL, ModuleName, "ReadAwayMsg", DEFAULT_READAWAYMSG);
+ MyOptions.ShowGroup = DBGetContactSettingByte(NULL, ModuleName, "ShowGroup", DEFAULT_SHOWGROUP);
+ MyOptions.ShowPreviousStatus = DBGetContactSettingByte(NULL, ModuleName, "ShowPreviousStatus", DEFAULT_SHOWPREVIOUSSTATUS);
+ MyOptions.FromOfflinePopup = DBGetContactSettingByte(NULL, ModuleName, "FromOfflinePopup", 0);
+ //General Options
+ MyOptions.FromOffline = DBGetContactSettingByte(NULL, ModuleName, "FromOffline", DEFAULT_FROMOFFLINE);
+ MyOptions.TempDisable = DBGetContactSettingByte(NULL, ModuleName, "TempDisable", DEFAULT_TEMPDISABLE);
+ MyOptions.AutoDisable = DBGetContactSettingByte(NULL, ModuleName, "AutoDisable", DEFAULT_AUTODISABLE);
+ MyOptions.EnableSoundForMyCurrentStatus = DEFAULT_NOTIFYWITHSOUND;
+ MyOptions.EnablePopupForMyCurrentStatus = DEFAULT_NOTIFYWITHPOPUP;
+ MyOptions.UseSpeaker = DBGetContactSettingByte(NULL, ModuleName, "UseSpeaker", DEFAULT_USESPEAKER);
+ MyOptions.AvoidClipping = DBGetContactSettingByte(NULL, ModuleName, "AvoidClipping", DEFAULT_AVOIDCLIPPING);
+ MyOptions.HiddenContactsToo = DBGetContactSettingByte(NULL, ModuleName, "HiddenContactsToo", DEFAULT_HIDDENCONTACTSTOO);
+ MyOptions.Log = DBGetContactSettingByte(NULL, ModuleName, "Log", DEFAULT_LOG);
+ MyOptions.BlinkIcon = DBGetContactSettingByte(NULL, ModuleName, "BlinkIcon", DEFAULT_BLINKICON);
+ MyOptions.BlinkIcon_Status = DBGetContactSettingByte(NULL, ModuleName, "BlinkIcon_Status", DEFAULT_BLINKICON_STATUS);
+ MyOptions.byTimeout = DBGetContactSettingByte(NULL, ModuleName, "byTimeout", DEFAULT_TIMEOUT);
+ MyOptions.iTimeout = DBGetContactSettingDword(NULL, ModuleName, "iTimeout", DEFAULT_TIMEOUT_VALUE);
+ MyOptions.UseIndSnd = DBGetContactSettingByte(NULL, ModuleName, "UseIndSounds", DEFAULT_USE_IND_SOUNDS);
+ return;
+}
+
+static int OptionsInitialize(WPARAM wParam,LPARAM lParam) {
+ OPTIONSDIALOGPAGE odp = { 0 };
+
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_NSN_OPT);
+ odp.pszTitle = "Notify";
+ odp.pszGroup = "Status";
+ odp.groupPosition = 910000000;
+ odp.flags=ODPF_BOLDGROUPS;
+ odp.pfnDlgProc = DlgProcGeneralOpts;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+
+ if (ServiceExists(MS_POPUP_ADDPOPUP)) {
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_NSN_OPT_POPUP);
+ odp.pszTitle = "Status Notify";
+ odp.pszGroup = "PopUps";
+ odp.groupPosition = 910000000;
+ odp.flags=ODPF_BOLDGROUPS;
+ odp.pfnDlgProc = DlgProcPopUpOpts;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ }
+ ZeroMemory(&odp,sizeof(odp));
+ odp.cbSize = sizeof(odp);
+ odp.position = 100000000;
+ odp.hInstance = hInst;
+ odp.pszTemplate = MAKEINTRESOURCEA(IDD_NSN_OPT_EVENTS_ENABLE);
+ odp.pszTitle = "Status Notify";
+ odp.pszGroup = "Events";
+ odp.groupPosition = 910000000;
+ odp.flags=ODPF_BOLDGROUPS;
+ odp.pfnDlgProc = DlgProcSoundOpts2;
+ CallService(MS_OPT_ADDPAGE,wParam,(LPARAM)&odp);
+ return 0;
+}
+
+//===== MessageBoxes =====
+void Log(TCHAR* str) {
+ MessageBox(NULL, str, _T("NewStatusNotify"), MB_OK | MB_ICONINFORMATION);
+}
+
+void HigherLower(int maxValue, int minValue) {
+ TCHAR str[64] = { 0 };
+ wsprintf(str, TranslateT("You cannot specify a value lower than %d and higher than %d."), minValue, maxValue);
+ Log(str);
+}
+
+//====== list function===
+int SortPUWinList(void* it1, void * it2) {
+ PUWinItem* item1=(PUWinItem*)it1;
+ PUWinItem* item2=(PUWinItem*)it2;
+ return (int)(item1->hContact) - (int)(item2->hContact);
+}
+
+//===== DB Functions ====
+//This function has the purpose of finding out the gender of a given contact.
+//Currently only ICQ supports gender, as far as I know, but this could change in
+//the future (i.e.: if I make a LAN protocol plugin).
+//It works quite easy: it gets the base protocol for the contact and compares it
+//with the known protocols. This can't be done automatically because each protocol
+//could store gender informations in a different way, so it'a all manual.
+//The first comparison which goes fine, checks for the gender and returns.
+#define GENDER_UNSPECIFIED 0
+#define GENDER_MALE 1
+#define GENDER_FEMALE 2
+
+BYTE GetGender(HANDLE hContact) {
+ char* lpzProtocol =(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if (lpzProtocol) {
+ switch (DBGetContactSettingByte((HANDLE)hContact,lpzProtocol,"Gender",0)) {
+ case 'M':
+ case 'm':
+ return GENDER_MALE;
+ case 'F':
+ case 'f':
+ return GENDER_FEMALE;
+ default:
+ return GENDER_UNSPECIFIED;
+ }
+ }
+ else
+ return GENDER_UNSPECIFIED;
+}
+// New Status Notify PopUp Window Message Proc
+LRESULT CALLBACK PopupDlgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) {
+ HANDLE hContact = NULL;
+ PLUGINDATA * pdp = NULL;
+ char *szProto={0};
+ hContact = PUGetContact(hwnd);
+
+ switch(message) {
+ case WM_MEASUREITEM: //Needed by the contact's context menu
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+ case WM_DRAWITEM: //Needed by the contact's context menu
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+ case WM_COMMAND: { //This one returns TRUE if it processed the menu command, and FALSE if it did not process it.
+ if(CallServiceSync(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU),(LPARAM)hContact))
+ break;
+ //If we get here, it was not a menu process command, but a click on one of our controls.
+ if (hContact > 0) {
+ //Get the plugin data (we need the PopUp service to do it)
+ pdp = (PLUGINDATA*)PUGetPluginData(hwnd);
+ if (!pdp || ((int)pdp == -1))
+ return 0; // (== -1, == 0);
+ //If the contact (MS_POPUP_GETCONTACT) is < 0 (wrong) or = 0 (it's us) we do exit.
+ //Now we can act.
+ if ((DBGetContactSettingByte(NULL, ModuleName, "LeftClickAction", ACTION_OPENMSG) == ACTION_OPENMSG)
+ /*&& (pdp->newStatus != ID_STATUS_OFFLINE) */)
+ NotifyEventHooks(hHookSendMessage, (WPARAM)hContact, 0);
+ }
+ //After a click, we destroy it nevertheless: message window or not.
+ PUDeletePopUp(hwnd);
+ break;
+ }
+ case WM_CONTEXTMENU: {
+ if (DBGetContactSettingByte(NULL, ModuleName, "RightClickAction", ACTION_DISMISS) == ACTION_DISMISS)
+ PUDeletePopUp(hwnd);
+ else {
+ //What do I do? This:
+ if (hContact < 0) break;
+ if (hContact != NULL) {
+ POINT pt = { 0 };
+ HMENU hMenu=(HMENU)CallService(MS_CLIST_MENUBUILDCONTACT,(WPARAM)hContact,0);
+ GetCursorPos(&pt);
+ TrackPopupMenu(hMenu,0,pt.x,pt.y,0,hwnd,NULL);
+ DestroyMenu(hMenu);
+ }
+ }
+ break;
+ }
+ case UM_FREEPLUGINDATA: {
+ PUWinItem PULItem = {0};
+ PUWinItem* pPULItem = {0};
+ //PLUGINDATA * pdp = {0};
+ pdp = (PLUGINDATA*)PUGetPluginData(hwnd);
+ if (pdp != 0) {
+// if (pdp->hAwayMsgHook != NULL) UnhookEvent(pdp->hAwayMsgHook);
+ mir_free(pdp);
+ }
+ PULItem.hContact = hContact;
+ pPULItem = li.List_Find(PopupList, &PULItem);
+ if ( (pPULItem != NULL) && (pPULItem->hWnd == hwnd ) ) // if we have item in list for this contact and this window
+ li.List_RemovePtr(PopupList, pPULItem);
+ return 0;
+ //break;
+ }
+ case UM_INITPOPUP: {
+ pdp = (PLUGINDATA*)PUGetPluginData(hwnd);
+
+ if (!PUIsSecondLineShown()) //There is no second line, it's not needed to check for away message.
+ return 0;
+
+ szProto= (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)(HANDLE)PUGetContact(hwnd),(LPARAM)0);
+
+ if (MyOptions.ReadAwayMsg && StatusHasAwayMessage(szProto, pdp->newStatus)) {
+// BOOL bNeedCreateItem;
+ PUWinItem PULItem = {0};
+ PUWinItem* pPULItem = {0};
+ PULItem.hContact = hContact;
+ pPULItem = li.List_Find(PopupList, &PULItem);
+ if ((pPULItem != NULL) /*&& (pPULItem->hWnd != NULL)*/) // if we have any list item for this contact AND popup already exists
+// li.List_RemovePtr(PopupList, pPULItem); // remove item from popupList to have only ONE entry for that contact in list
+ {
+ pPULItem->hWnd = hwnd;
+ time(&(pPULItem->TimeStamp));
+ }
+
+ // if we (had any list item for this contact AND popup already exists) OR do not have any item for this contact
+ /*bNeedCreateItem = ((pPULItem != NULL) && (pPULItem->hWnd != NULL)) || (pPULItem == NULL);
+ if (bNeedCreateItem)
+ {
+ pPULItem = (PUWinItem*)mir_alloc(sizeof(PULItem)); // create new list item
+ pPULItem->hContact = hContact;
+ lstrcpyn(pPULItem->StatMsg, _T(""), MAX_SECONDLINE);
+ }
+ pPULItem->hWnd = hwnd;
+
+ time(&(pPULItem->TimeStamp));
+ pPULItem->Status = pdp->newStatus;
+ if (bNeedCreateItem)
+ li.List_Insert(PopupList, (void*)pPULItem, 0); // insert item to list
+ */
+ if (szProto && (strstr(szProto, "ICQ") || strstr(szProto, "AIM")) )
+ { //A bit useless (to receive a status change, szProto wasn't NULL in the first place)
+ WORD myStatus = (WORD)CallProtoService(szProto,PS_GETSTATUS, (WPARAM)0, (LPARAM)0);
+ if (myStatus != ID_STATUS_INVISIBLE) // do not retrieve status messages when we are in ICQ invisible mode - otherwise it will show us to other side!
+ QueryAwayMessage(hwnd, pdp);
+ /*else //not ICQ, query the away message whatever status we're in.
+ QueryAwayMessage(hwnd, pdp);*/
+ }
+ }
+ return 0;
+ }
+ case WM_AWAYMSG: { //We're here because ME_PROTO_ACK has been hooked to this window (too!).
+ pdp = (PLUGINDATA*)PUGetPluginData(hwnd);
+ ReceivedAwayMessage(hwnd, lParam, pdp);
+ //Since we're in a Hook routine, we need to return 0 or the hook chain will stop.
+ return FALSE;
+ }
+ default:
+ break;
+ }
+ return DefWindowProc(hwnd, message, wParam, lParam);
+};
+
+//===== Function called when the settings for a contact are changed.
+// We'll basically see if the changed setting regards contact's Status: either
+// exit from the function to let other modules do their job, or do *our* job.
+// A significant part of this code has been copied from Miranda's original code
+// by Cyreve, but I have changed something.
+static int ContactSettingChanged(WPARAM wParam,LPARAM lParam) {
+ DBCONTACTWRITESETTING *cws=(DBCONTACTWRITESETTING*)lParam;
+ WORD newStatus = 0,oldStatus = 0;
+ DWORD dwStatuses = 0;
+
+ //We need to exit from this function as fast as possible, so we'll first check
+ //if the setting regards us (exit) or if it's not related to status (exit).
+ //If we're there, it means it's a status change we *could* notify, so let's see
+ //if we are ignoring that event. This means if *Miranda* ignores it (ignores every
+ //notification) and then if *We* ignore the event.
+ //Lastly, we check if we're offline: this will happen only once per connection, while
+ //the checks above will happen more frequently.
+
+ //The setting must belong to a contact different from myself (NULL) and needs
+ //to be related to the status. Otherwise, exit as fast as possible.
+ if ((HANDLE)wParam != NULL && !lstrcmpA(cws->szSetting,"Status")) {
+ //Ok, if we're here we have a contact who is changing his status.
+ //The first thing we need to do is save the new Status in place of the old
+ //one, then we'll proceed.
+ //If we don't do this, we may exit from the function without updating the
+ //UserOnline settings.
+ DBVARIANT dbvProto = { 0 };
+ newStatus = cws->value.wVal;
+ //--------------------------------need to check if it is really a protocol
+ if (DBGetContactSettingString((HANDLE)wParam, "Protocol", "p", &dbvProto) == DB_READ_SUCCESS) {
+ BOOL temp = strcmp(cws->szModule, dbvProto.pszVal) != 0;
+ DBFreeVariant(&dbvProto);
+ if ( temp ) {
+ //MessageBox(0,_T("Not a proto. Return!"), _T("NSN"), 0);
+ return 0;
+ }
+ }
+ oldStatus = DBGetContactSettingRangedWord((HANDLE)wParam,"UserOnline","OldStatus",ID_STATUS_OFFLINE, ID_STATUS_MIN, ID_STATUS_MAX);
+ //If they are the same, you don't need to write to the DB or do anything else.
+ if (oldStatus == newStatus)
+ return 0;
+ //If we get here, the two stauses differ, so we can proceed.
+ DBWriteContactSettingWord((HANDLE)wParam,"UserOnline","OldStatus",(WORD)newStatus);
+
+ //If *Miranda* ignores the UserOnline event, exit!
+ if(CallService(MS_IGNORE_ISIGNORED,wParam,IGNOREEVENT_USERONLINE))
+ return 0;
+
+ //If we get here, we have to notify the Hooks.
+ dwStatuses = MAKELPARAM(oldStatus, newStatus);
+ NotifyEventHooks(hHookContactStatusChanged, (WPARAM)wParam, (LPARAM)dwStatuses);
+
+ return 0;
+ }
+ if ( ((HANDLE)wParam != NULL) //not our own status message
+ && MyOptions.ReadAwayMsg //and we should show status message in popup
+ && !lstrcmpA(cws->szSetting,"StatusMsg")) //and changed DB value is StatusMessage
+ ReceivedStatusMessage((HANDLE)wParam, TRUE);
+
+ return 0;
+}
+
+static int ContactStatusChanged(WPARAM wParam, LPARAM lParam) {
+ WORD oldStatus = LOWORD(lParam);
+ WORD newStatus = HIWORD(lParam);
+ char str[8] = { 0 };
+ WORD myStatus;
+ char lpszSubProto[MAX_PROTONAMELEN] = { 0 };
+ char *lpzProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)wParam,0);
+
+ //------------------------------ if NewStatusNotify has been disabled, exit!
+ if (MyOptions.TempDisable)
+ return 0;
+
+ myStatus = (WORD)CallProtoService(lpzProto,PS_GETSTATUS, (WPARAM)0, (LPARAM)0);
+
+ //------------------------------- if it is metacontact - get subcontact and test if its proto is offline or we don't want to be notified at connect
+ if ( (ServiceExists(MS_MC_GETPROTOCOLNAME)) &&
+ (strcmp(lpzProto,(char*)CallService(MS_MC_GETPROTOCOLNAME,0,0)) == 0) ) //this contact is Meta
+ {
+ //get Most Online contact - it is DefContact if DefContact is Online,
+ // otherwise first online contact in the Subcontacts list
+ char cMCBaseProto[MAX_PROTONAMELEN] = {0};
+ HANDLE hSubContact = (HANDLE)CallService(MS_MC_GETMOSTONLINECONTACT,wParam,0);
+ // get Proto name from Subcontact
+ if (hSubContact)
+ strncpy(cMCBaseProto, (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hSubContact,0), MAX_PROTONAMELEN);
+ if (cMCBaseProto == NULL)
+ mir_snprintf(lpszSubProto, MAX_PROTONAMELEN,
+ "No subcontact returned for given metacontact %d. Probably, this metacontact is corrupted", wParam);
+ else
+ {
+ strncpy(lpszSubProto, cMCBaseProto, sizeof(lpszSubProto)-1 );
+ if (newStatus > ID_STATUS_OFFLINE)
+ // write last online protocol for Metacontact. from MIM 0.6+ this setting is resident
+ DBWriteContactSettingString((HANDLE)wParam, lpzProto, "LastOnline", lpszSubProto);
+ else
+ {
+ // read last online proto for metaconatct if exists,
+ // to avoid notifying when meta went offline but default contact's proto still online
+ DBVARIANT dbvProto = { 0 };
+ if (DBGetContactSettingString((HANDLE)wParam, lpzProto, "LastOnline", &dbvProto) == DB_READ_SUCCESS)
+ {
+ //strcpy(lpzSubProto, dbvProto.pszVal);
+ //lpszSubProto = (char*)_malloca( strlen(dbvProto.pszVal)+1 );
+ strncpy( lpszSubProto, dbvProto.pszVal, sizeof(lpszSubProto)-1 );
+ DBFreeVariant(&dbvProto);
+ }
+ }
+ /*
+ { //debug out for metas
+ char outtxt[200];
+ WORD mySubStatus;
+ // get Proto Status from Subcontact
+ mySubStatus = (WORD)CallProtoService(lpzSubProto,PS_GETSTATUS, (WPARAM)0, (LPARAM)0);
+ mir_snprintf(outtxt,sizeof(outtxt), "Main Proto: %s\nMain Status: %d\nSubProto: %s\nSubStatus: %d",lpzProto, myStatus, lpzSubProto, mySubStatus);
+ PUShowMessage(outtxt, SM_NOTIFY);
+ } */
+
+ // check if Subcontact's proto is offline and we shouldn't notify about status change on connect
+ if ( (DBGetContactSettingByte(NULL, ModuleName, lpszSubProto, DEFAULT_PROTO_POPUPS) == PROTO_POPUPS_OFF &&
+ DBGetContactSettingByte(NULL, ModuleName, "ShowOnConnection", DEFAULT_SHOWONCONNECTION) == FALSE) )
+ return(0);
+ }
+ }
+ //------------------------------- end of meta --
+ else
+ {
+ //------------------------------ if we're offline (=> we've just switched to offline), exit as fast as possible.
+ if (myStatus == ID_STATUS_OFFLINE)
+ return 0;
+ //------------------------------ if the user doesn't want sounds on connect, exit. (If this was a popup after a connection 8))
+ if (DBGetContactSettingByte(NULL, ModuleName, lpzProto, DEFAULT_PROTO_POPUPS) == PROTO_POPUPS_OFF &&
+ DBGetContactSettingByte(NULL, ModuleName, "ShowOnConnection", DEFAULT_SHOWONCONNECTION) == FALSE)
+ //ShowOnConnection must be FALSE to get here.
+ //lpzProto must be FALSE (0) (PROTO_POPUPS_OFF) to get here (value for that proto in NSN settings is 0(=disable notifications)).
+ //It means that we don't want to be notified at startup/connection and we're at startup/connection.
+ return 0;
+ }
+ //If *we* ignore the event, exit as fast as possible, but if the previous status was Offline, there's the chance
+ //we want to be notified.
+ if ( !(MyOptions.FromOffline && (oldStatus == ID_STATUS_OFFLINE)) )
+ { // Either it wasn't a change from Offline or we didn't enable that.
+ wsprintfA(str, "%d", newStatus);
+ if (DBGetContactSettingByte(NULL, ModuleName, str,0) == 1)
+ return 0; // "Notify when a contact changes to one of..." is unchecked
+ }
+ //If the contact is hidden (i.e.: not in Contact List), exit.
+ //This is here because it's a read from the DB and it has lesser chances to be called than the others.
+ //I mean: it's easier that the user disabled something, than a hidden contact is changing status.
+ if ((!MyOptions.HiddenContactsToo) && (DBGetContactSettingByte((HANDLE)wParam, "CList", "Hidden", 0) == 1))
+ return 0;
+ // we don't want to be notified if new chatroom comes online
+ if (DBGetContactSettingByte((HANDLE)wParam, lpzProto, "ChatRoom", 0) == 1)
+ return 0;
+ //Ok, we want to be notified. :-)
+
+ { // check if that proto from which we received statuschange notification, isn't in autodisable list
+ char statusIDs[12] = { 0 }, statusIDp[12] = { 0 }; //2 (my) + 5 (40078) + 1 (\0) = 8; 12 - 8 = 4 free bytes, you never can tell.
+
+ if (MyOptions.AutoDisable == TRUE)
+ {
+ wsprintfA(statusIDs,"s%d", myStatus);
+ wsprintfA(statusIDp,"p%d", myStatus);
+ //Now statusIDs contains "s40078", "s40072" or whatever.
+ //DBGetContactSettingByte(NULL, ModuleName, statusIDs,xx) will give me one of these two values:
+ // 1 (TRUE) meaning that we want sounds and popups
+ // 0 (FALSE) meaning that we don't want them.
+ MyOptions.EnableSoundForMyCurrentStatus = DBGetContactSettingByte(NULL, ModuleName, statusIDs, DEFAULT_NOTIFYWITHSOUND) ? TRUE : FALSE;
+ MyOptions.EnablePopupForMyCurrentStatus = DBGetContactSettingByte(NULL, ModuleName, statusIDp, DEFAULT_NOTIFYWITHPOPUP) ? TRUE : FALSE;
+ }
+ }
+
+ //-------------------------------------------------===== Status PopUp =====---------------------------------------------------------
+ if ( MyOptions.EnablePopupForMyCurrentStatus && DBGetContactSettingByte((HANDLE)wParam, ModuleName, "EnablePopups",1) ) //DBGetContactSettingByte(NULL, "CList", "DisableTrayFlash", FALSE) == FALSE)
+ {
+ // Create the window, add its corresponding structure to our list of visible NSN windows, show the list.
+ BOOL NotifyMe = FALSE;
+ BOOL bIsICQ = FALSE;
+ POPUPDATAT ppd = { 0 };
+ PLUGINDATA * pdp = (PLUGINDATA*)mir_alloc(sizeof(PLUGINDATA));
+ TCHAR lptzStatusText[MAX_SECONDLINE] = { 0 };
+ BOOL bReadAwayMsg = TRUE;
+ //implementation of contact status event filtering
+ if (MyOptions.FromOfflinePopup && (oldStatus == ID_STATUS_OFFLINE) )
+ NotifyMe = TRUE;
+
+ if (NotifyMe == FALSE)
+ { // Either it wasn't a change from Offline or we didn't enable that.
+ wsprintfA(str, "%dp", newStatus);
+ if (DBGetContactSettingByte(NULL, ModuleName, str,0) == 1)
+ NotifyMe=TRUE; // "Notify when a contact changes to one of..." is checked
+ }
+
+ if (NotifyMe == TRUE)
+ {
+ ppd.lchContact = (HANDLE)wParam; //You need to give me a valid hContact, because I don't check its existance.
+ ppd.lchIcon = LoadSkinnedProtoIcon(lpzProto, newStatus);
+ lstrcpyn(ppd.lptzContactName, (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)wParam, GCMDF_TCHAR), SIZEOF(ppd.lptzContactName)-1);
+
+ if (MyOptions.ShowGroup)
+ {//add group name to popup title
+ DBVARIANT dbv = { 0 };
+ if ((DBGetContactSettingTString((HANDLE)wParam, "CList", "Group", &dbv)) == DB_READ_SUCCESS)
+ {
+ mir_sntprintf(ppd.lptzContactName, SIZEOF(ppd.lptzContactName)-1, _T("%s (%s)"), ppd.lptzContactName, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ }
+ if (lpzProto)
+ { //A bit useless (to receive a status change, szProto wasn't NULL in the first place)
+ if (strstr(lpzProto, "ICQ"))
+ {
+ bIsICQ = TRUE;
+ if (myStatus == ID_STATUS_INVISIBLE)
+ // do not retrieve status messages when we are in ICQ invisible mode - otherwise it will show us to other side!
+ bReadAwayMsg = FALSE;
+ }
+ }
+
+ if (lpszSubProto[0] != '\0')
+// if ( assign lpszSubProto)
+ {
+ if (StatusHasAwayMessage(lpszSubProto, newStatus) == FALSE)
+ bReadAwayMsg = FALSE;
+ }
+
+ bReadAwayMsg = bReadAwayMsg && MyOptions.ReadAwayMsg && StatusHasAwayMessage(lpzProto, newStatus);
+ //--------------
+ {
+ if (MyOptions.UseDullText == 1)
+ lstrcpyn(lptzStatusText,StatusesList[Index(newStatus)].lpzStandardText, MAX_STATUSTEXT);
+ else if (MyOptions.UseDullText == 2)
+ {
+ //Get gender and load status text.
+ switch (GetGender((HANDLE)wParam))
+ {
+ case GENDER_MALE:
+ lstrcpyn(lptzStatusText,StatusesList[Index(newStatus)].lpzMStatusText, MAX_STATUSTEXT);
+ break;
+ case GENDER_FEMALE:
+ lstrcpyn(lptzStatusText,StatusesList[Index(newStatus)].lpzFStatusText, MAX_STATUSTEXT);
+ break;
+ case GENDER_UNSPECIFIED:
+ lstrcpyn(lptzStatusText,StatusesList[Index(newStatus)].lpzUStatusText, MAX_STATUSTEXT);
+ break;
+ }
+ }
+ //Now we add the old status.
+ if (MyOptions.ShowPreviousStatus && ((oldStatus>=ID_STATUS_OFFLINE)||(oldStatus<=ID_STATUS_OUTTOLUNCH) ))
+ {
+ TCHAR szAux[MAX_STATUSTEXT] = { 0 };
+ wsprintf(szAux, TranslateTS(STRING_SHOWPREVIOUSSTATUS), StatusesList[Index(oldStatus)].lpzStandardText);
+ lstrcat(lstrcat(lptzStatusText, _T(" ")), szAux);
+ }
+ }
+
+ if (DBGetContactSettingByte(NULL, ModuleName, "ShowMirVer",0))
+ {//add MirVer Text if key is set
+ DBVARIANT dbv = { 0 };
+ if ((DBGetContactSettingTString((HANDLE)wParam, lpzProto, "MirVer", &dbv)) == DB_READ_SUCCESS)
+ {
+ lstrcat(ppd.lptzText, dbv.ptszVal);
+ lstrcat(ppd.lptzText, _T("\n"));
+ DBFreeVariant(&dbv);
+ }
+ else
+ lstrcpy(ppd.lptzText, _T(""));
+ }
+ //--------------
+ if (bReadAwayMsg)
+ {
+ PUWinItem PULItem = {0};
+ PUWinItem* pPULItem = {0};
+ DBVARIANT dbv = { 0 };
+ TCHAR sztDBStatMes[MAX_SECONDLINE] = {0};
+ time_t now;
+ time(&now);
+ PULItem.hContact = (HANDLE)wParam;
+ pPULItem = li.List_Find(PopupList, &PULItem);
+ if ((pPULItem != NULL) /*&& ( ((pPULItem->TimeStamp + 1) < now) || (pPULItem->hWnd != NULL) )*/ )
+ {
+ //li.List_RemovePtr(PopupList, pPULItem); // remove record from popupList if we have any for this contact
+ if (pPULItem->hWnd != NULL)
+ {
+ pPULItem->hWnd = NULL;
+ PUDeletePopUp(pPULItem->hWnd);
+ }
+ }
+
+ DBWriteContactSettingTString((HANDLE)wParam, ModuleName, LASTPOPUPTEXT, lptzStatusText);
+ if ((DBGetContactSettingTString((HANDLE)wParam, "Clist", "StatusMsg", &dbv)) == DB_READ_SUCCESS)
+ {
+ lstrcpyn(sztDBStatMes, dbv.ptszVal, MAX_SECONDLINE);
+ DBFreeVariant(&dbv);
+ }
+ else
+ lstrcpyn(sztDBStatMes, _T(""), MAX_SECONDLINE);
+
+ mir_sntprintf(lptzStatusText, MAX_SECONDLINE, _T("%s\n%s"), lptzStatusText,
+ (pPULItem != NULL) ? pPULItem->StatMsg : ( bIsICQ ? TranslateT("<retrieving status message>") : sztDBStatMes ) );
+ }
+ //--------------
+ lstrcat(ppd.lptzText, lptzStatusText);
+
+ switch (MyOptions.Colors)
+ {
+ case byCOLOR_OWN:
+ ppd.colorBack = StatusesList[Index(newStatus)].colorBack;
+ ppd.colorText = StatusesList[Index(newStatus)].colorText;
+ break;
+ case byCOLOR_WINDOWS:
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ break;
+ case byCOLOR_POPUP:
+ ppd.colorBack = ppd.colorText = 0;
+ break;
+ }
+ ppd.PluginWindowProc = (WNDPROC)PopupDlgProc;
+ //Now the "additional" data.
+ pdp->oldStatus = oldStatus;
+ pdp->newStatus = newStatus;
+ //Now that the plugin data has been filled, we add it to the PopUpData.
+ ppd.PluginData = pdp;
+ switch (MyOptions.byTimeout)
+ {
+ case TIMEOUT_CUSTOM:
+ ppd.iSeconds = MyOptions.iTimeout;
+ break;
+ case TIMEOUT_PERMANENT:
+ ppd.iSeconds = -1;
+ break;
+ case TIMEOUT_POPUP:
+ default:
+ ppd.iSeconds = 0;
+ break;
+ }
+ PUAddPopUpT(&ppd);
+ }
+ }
+ //-------------------------------------------------===== Blinking Icon section =====---------------------------------------------------
+ if ((MyOptions.BlinkIcon) && (newStatus > ID_STATUS_OFFLINE)) //emulate behavior of core - don't show tray icon when contact went offline
+ {
+ CLISTEVENT cle = { 0 };
+ TCHAR szTooltip[256];
+
+ cle.cbSize = sizeof(cle);
+ cle.flags = CLEF_ONLYAFEW|CLEF_TCHAR;
+ cle.hContact = (HANDLE)wParam;
+ cle.hDbEvent = (HANDLE)wParam;
+ if (MyOptions.BlinkIcon_Status)
+ cle.hIcon = LoadSkinnedProtoIcon(lpzProto, newStatus);
+ else
+ cle.hIcon = LoadSkinnedIcon(SKINICON_OTHER_USERONLINE);
+ cle.pszService = "UserOnline/Description";
+ wsprintf(szTooltip, TranslateT("%s is now %s"), (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, wParam, GCDNF_TCHAR),
+ StatusesList[Index(newStatus)].lpzStandardText);
+ cle.ptszTooltip = szTooltip;
+ CallService(MS_CLIST_ADDEVENT, 0, (LPARAM)&cle);
+ }
+ //-------------------------------------------------===== Log to file section =====------------------------------------------------------
+ if (MyOptions.Log) {
+ HANDLE hFile = NULL;
+ DBVARIANT dbv = { 0 };
+ SECURITY_ATTRIBUTES sa = { 0 };
+ BOOL bResult = FALSE;
+ TCHAR szName[1024] = { 0 }, szStatus[MAX_STATUSTEXT] = { 0 }, szOldStatus[MAX_STATUSTEXT] = { 0 };
+ TCHAR szDate[MAX_STATUSTEXT] = { 0 }, szTime[MAX_STATUSTEXT] = { 0 };
+ TCHAR szFile[MAX_PATH] = { 0 }, szFileRel[MAX_PATH] = { 0 };
+ REPLACEVARSDATA dat = {0};
+ TCHAR *pszText = NULL, *res;
+ DWORD dwBytes = 0, dwWritten = 0;
+
+ if (DBGetContactSettingTString(NULL, ModuleName, "Logfile", &dbv) == 0) {
+ lstrcpyn(szFileRel, dbv.ptszVal, MAX_PATH);
+ DBFreeVariant(&dbv);
+ }
+ else
+ lstrcpyn(szFileRel, DEFAULT_LOGFILE, MAX_PATH);
+
+ dat.cbSize = sizeof(dat);
+ dat.dwFlags = RVF_TCHAR;
+
+ res = (TCHAR*)CallService(MS_UTILS_REPLACEVARS, (WPARAM)szFileRel, (LPARAM)&dat);
+ if (res) {
+ _tcscpy(szFileRel, res);
+ mir_free(res);
+ }
+
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)szFileRel, (LPARAM)szFile);
+
+ sa.nLength = sizeof(SECURITY_ATTRIBUTES);
+ sa.lpSecurityDescriptor = NULL;
+ sa.bInheritHandle = TRUE;
+
+ hFile = CreateFile(szFile,
+ GENERIC_WRITE, FILE_SHARE_READ, &sa,
+ OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (hFile != INVALID_HANDLE_VALUE) { // The file was successfully opened
+ DWORD FilePos = SetFilePointer(hFile, 0, NULL, FILE_END);
+ if (FilePos != INVALID_SET_FILE_POINTER) { // and positioned.
+#ifdef _UNICODE
+ if (FilePos == 0)
+ fputwc((const wchar_t)"\xFF\xFE", hFile); // newly created file - write UTF-16 LE BOM
+#endif
+ lstrcpyn(szName, (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)wParam, GCDNF_TCHAR), SIZEOF(szName)-1);
+ lstrcpyn(szStatus,StatusesList[Index(newStatus)].lpzStandardText, MAX_STATUSTEXT);
+ lstrcpyn(szOldStatus,StatusesList[Index(oldStatus)].lpzStandardText, MAX_STATUSTEXT);
+ GetTimeFormat(LOCALE_USER_DEFAULT, 0, NULL,_T("HH':'mm"),szTime, SIZEOF(szTime));
+ GetDateFormat(LOCALE_USER_DEFAULT, 0, NULL,_T("dd'.'MM'.'yyyy"),szDate, SIZEOF(szDate));
+
+ dwBytes = lstrlen(TranslateT("%s, %s. %s changed to: %s (was: %s).\r\n")) + lstrlen(szDate) + lstrlen(szTime) +
+ lstrlen(szName) + lstrlen(szStatus) + lstrlen(szOldStatus) + 1; //'\0'
+ pszText = (TCHAR*)mir_alloc(dwBytes * sizeof(TCHAR));
+ wsprintf(pszText, TranslateT("%s, %s. %s changed to: %s (was: %s).\r\n"), szDate, szTime, szName, szStatus, szOldStatus);
+ bResult = WriteFile(hFile, pszText, lstrlen(pszText)* sizeof(TCHAR), &dwWritten, NULL);
+ SetEndOfFile(hFile);
+ mir_free(pszText);
+ }
+ CloseHandle(hFile); //This closes the handle both if the write routine worked or not.
+ }
+ }
+ //-------------------------------------------------===== Sound section =====---------------------------------------------------------
+ if (DBGetContactSettingByte(NULL, "Skin", "UseSound", TRUE)
+ && MyOptions.EnableSoundForMyCurrentStatus
+ && DBGetContactSettingByte((HANDLE)wParam,ModuleName,"EnableSounds",1) ) {
+ // We want to hear something, ok. Every status has a sound associated,
+ // and we get it via the StatusesList[Index(newStatus)].lpzSkinSoundName.
+ // Before we play, however, we check if we have to use the sound card or the speaker:
+ // we'll add here individual sounds too, with a check for a group and contact specific sound.
+ // Since speaker and individual sounds don't get played automatically by the skin module,
+ // we have to check manually if they're enabled or disabled.
+
+ // Check for individualized sounds.
+ if (MyOptions.UseIndSnd) { // -----------------------------Individual sounds are enabled-----------------------
+ TCHAR stzSound[MAX_PATH] = { 0 };
+ DBVARIANT dbvDefIndStatus = { 0 }, dbvDefIndFO = { 0 };
+ if (oldStatus == ID_STATUS_OFFLINE) {
+ if (DBGetContactSettingTString((HANDLE)wParam, ModuleName, "UserFromOffline", &dbvDefIndFO)==DB_READ_SUCCESS) {
+ // individual FromOffline sound defined and contact came from offline
+ lstrcpyn(stzSound, dbvDefIndFO.ptszVal, SIZEOF(stzSound)-1);
+ DBFreeVariant(&dbvDefIndFO);
+ }
+ }
+ else // check other individual sounds
+ if (DBGetContactSettingTString((HANDLE)wParam, ModuleName, StatusesList[Index(newStatus)].lpzSkinSoundName, &dbvDefIndStatus) == DB_READ_SUCCESS) {
+ lstrcpyn(stzSound, dbvDefIndStatus.ptszVal, SIZEOF(stzSound)-1);
+ DBFreeVariant(&dbvDefIndStatus);
+ }
+ if (stzSound[0] != 0) { // check if any of above checks is succeed and we need to play sound
+ if (MyOptions.UseSpeaker)
+ PlaySpeakerBeep();
+ else { // play through soundcard
+ // Now make path to IndSound absolute, as it isn't registered
+ TCHAR stzTranslatedSound[MAX_PATH] = { 0 };
+ CallService(MS_UTILS_PATHTOABSOLUTET, (WPARAM)stzSound, (LPARAM)stzTranslatedSound);
+ #ifdef DEBUG
+ if (!szTranslatedSound) PUShowMessage("ToAbsolutePath failed.", SM_WARNING);
+ #endif
+ //finally play Individual sound and return
+#ifdef MS_SKIN_PLAYSOUNDEX
+ if (ServiceExists(MS_SKIN_PLAYSOUNDEX))
+ {
+ SkinPlaySoundEx(szTranslatedSound);
+ }
+ else
+#endif
+#ifdef RegIndivSounds
+ SkinPlaySound(szTranslatedSound);
+#else
+ if (MyOptions.AvoidClipping)
+ PlaySound(stzTranslatedSound, NULL, SND_ASYNC | SND_FILENAME | SND_NOSTOP);
+ else
+ PlaySound(stzTranslatedSound, NULL, SND_ASYNC | SND_FILENAME);
+#endif
+ }
+ return 0; //sound is played - exit
+ }
+ }
+ //----------------------------------------------------check global sounds --------------------------------
+ // Here there's the automatic check by Skin module, and this is right:
+ // if a user hasn't a specific sound, he is a "default" user, withstanding the rules of Skin module.
+ {
+ char szSoundFile[MAX_PATH] = { 0 };
+ DBVARIANT dbvSound = { 0 };
+
+ if ( ( DBGetContactSettingByte(NULL, "SkinSoundsOff", "UserFromOffline",FALSE) == FALSE)
+ && (DBGetContactSettingString(NULL,"SkinSounds", "UserFromOffline",&dbvSound) == DB_READ_SUCCESS)
+ && (oldStatus == ID_STATUS_OFFLINE )) //The "UserFromOffline" sound isn't disabled, is defined and old status is offline
+ lstrcpyA(szSoundFile, "UserFromOffline");
+ else if ( (DBGetContactSettingByte(NULL, "SkinSoundsOff", StatusesList[Index(newStatus)].lpzSkinSoundName,FALSE) == FALSE)
+ && (DBGetContactSettingString(NULL,"SkinSounds", StatusesList[Index(newStatus)].lpzSkinSoundName,&dbvSound) == DB_READ_SUCCESS) )
+ lstrcpynA(szSoundFile, StatusesList[Index(newStatus)].lpzSkinSoundName, SIZEOF(szSoundFile)-1);
+ DBFreeVariant(&dbvSound);
+ if (szSoundFile[0]!=0) { // check if any of above checks is succeed and we need to play sound
+ if (MyOptions.UseSpeaker) PlaySpeakerBeep();
+ else SkinPlaySound(szSoundFile);
+ }
+ } //end of global sounds
+ } // End of sound playing
+ return 0;
+} //--------------------------------------------------============End of ContactStatusChanged=======------------------------------
+
+//This is for the PopUp Options: style, position, color, font...
+static BOOL CALLBACK DlgProcPopUpOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ WORD i = 0;
+ char str[8] = {0};
+
+ switch (msg) {
+
+/************************************************************************
+ case WM_CONTEXTMENU:
+ {
+ POINT pt = { 0 };
+ HMENU hMenu=(HMENU)CallService(MS_CLIST_MENUBUILDCONTACT,214849,0);
+ GetCursorPos(&pt);
+ TrackPopupMenu(hMenu,0,pt.x,pt.y,0,hwndDlg,NULL);
+ DestroyMenu(hMenu);
+ break;
+ }
+*///-------------------------------------------------------------------------
+
+
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ //Colours.
+ if (MyOptions.Colors == byCOLOR_OWN) {
+ CheckDlgButton(hwndDlg, IDC_USEOWNCOLORS, BST_CHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEPOPUPCOLORS, BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEWINCOLORS, BST_UNCHECKED);
+ //EnableWindow(GetDlgItem(hwndDlg, IDC_USEPOPUPCOLORS), FALSE);
+ //EnableWindow(GetDlgItem(hwndDlg, IDC_USEWINCOLORS), TRUE);
+ }
+ if (MyOptions.Colors == byCOLOR_WINDOWS) {
+ CheckDlgButton(hwndDlg, IDC_USEOWNCOLORS, BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEPOPUPCOLORS, BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEWINCOLORS, BST_CHECKED);
+ //EnableWindow(GetDlgItem(hwndDlg, IDC_USEPOPUPCOLORS), FALSE);
+ //EnableWindow(GetDlgItem(hwndDlg, IDC_USEWINCOLORS), TRUE);
+ }
+ if (MyOptions.Colors == byCOLOR_POPUP) {
+ CheckDlgButton(hwndDlg, IDC_USEOWNCOLORS, BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEPOPUPCOLORS, BST_CHECKED);
+ CheckDlgButton(hwndDlg, IDC_USEWINCOLORS, BST_UNCHECKED);
+ //EnableWindow(GetDlgItem(hwndDlg, IDC_USEWINCOLORS), FALSE);
+ //EnableWindow(GetDlgItem(hwndDlg, IDC_USEPOPUPCOLORS), TRUE);
+ }
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ SendDlgItemMessage(hwndDlg,(i+2000),CPM_SETCOLOUR,0,StatusesList[Index(i)].colorBack);
+ SendDlgItemMessage(hwndDlg,(i+1000),CPM_SETCOLOUR,0,StatusesList[Index(i)].colorText);
+ EnableWindow(GetDlgItem(hwndDlg, i+2000), (MyOptions.Colors == byCOLOR_OWN));
+ EnableWindow(GetDlgItem(hwndDlg, (i+1000)), (MyOptions.Colors == byCOLOR_OWN));
+ }
+ //Ugly dull text...
+ CheckDlgButton(hwndDlg, IDC_CHK_USEDULLTEXT, MyOptions.UseDullText == 1 ? BST_CHECKED : BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_CHK_SHOWALTDESCS, MyOptions.UseDullText == 2 ? BST_CHECKED : BST_UNCHECKED);
+ //Away messages
+ CheckDlgButton(hwndDlg, IDC_READAWAYMSG, MyOptions.ReadAwayMsg?BST_CHECKED:BST_UNCHECKED);
+ //Previous status
+ CheckDlgButton(hwndDlg, IDC_SHOWPREVIOUSSTATUS, MyOptions.ShowPreviousStatus?BST_CHECKED:BST_UNCHECKED);
+ if (MyOptions.UseDullText == 0)
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWPREVIOUSSTATUS), FALSE);
+ //Show group name in title
+ CheckDlgButton(hwndDlg, IDC_SHOWGROUP, MyOptions.ShowGroup?BST_CHECKED:BST_UNCHECKED);
+ //Timeout
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_POPUP, (MyOptions.byTimeout == TIMEOUT_POPUP)?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_CUSTOM, (MyOptions.byTimeout == TIMEOUT_CUSTOM)?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_TIMEOUT_PERMANENT, (MyOptions.byTimeout == TIMEOUT_PERMANENT)?BST_CHECKED:BST_UNCHECKED);
+ SetDlgItemInt(hwndDlg, IDC_TIMEOUT_VALUE, MyOptions.iTimeout, FALSE);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TIMEOUT_VALUE), MyOptions.byTimeout == TIMEOUT_CUSTOM);
+ //Mouse actions
+ switch(DBGetContactSettingByte(NULL, ModuleName, "LeftClickAction",ACTION_OPENMSG))
+ {
+ case ACTION_OPENMSG:
+ CheckDlgButton(hwndDlg, IDC_LC_OPENMSG, BST_CHECKED);
+ break;
+ case ACTION_DISMISS:
+ CheckDlgButton(hwndDlg, IDC_LC_DISMISS, BST_CHECKED);
+ break;
+ }
+ switch(DBGetContactSettingByte(NULL, ModuleName, "RightClickAction",ACTION_DISMISS))
+ {
+ case ACTION_USERMENU:
+ CheckDlgButton(hwndDlg, IDC_RC_USERMENU, BST_CHECKED);
+ break;
+ case ACTION_DISMISS:
+ CheckDlgButton(hwndDlg, IDC_RC_DISMISS, BST_CHECKED);
+ break;
+ }
+ //Statuses notified
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++)
+ {
+ wsprintfA(str, "%dp", i);
+ CheckDlgButton(hwndDlg, i, (DBGetContactSettingByte(NULL, ModuleName, str, 1))?BST_CHECKED:BST_UNCHECKED);
+ }
+ //Always notify when changing from offline?
+ CheckDlgButton(hwndDlg, IDC_CHK_PFROMOFFLINE, (MyOptions.FromOfflinePopup)?BST_CHECKED:BST_UNCHECKED);
+ return TRUE;
+ //End WM_INITDIALOG
+ case WM_COMMAND:
+ {
+//-----------------------
+// if(CallService(MS_CLIST_MENUPROCESSCOMMAND, MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU),214849)) break;
+//------------------------
+ WORD idCtrl = LOWORD(wParam), wNotifyCode = HIWORD(wParam);
+ if (wNotifyCode == EN_KILLFOCUS && idCtrl == IDC_TIMEOUT_VALUE) { //Check if it's in the range.
+ int newValue, oldValue = GetDlgItemInt(hwndDlg, idCtrl, NULL, FALSE);
+ int maxValue, minValue;
+ //newValue gets set in WM_COMMAND to the right value. We only update the edit field.
+ newValue = MyOptions.iTimeout;
+ maxValue = TIMEOUT_MAXVALUE;
+ minValue = TIMEOUT_MINVALUE;
+ if (oldValue > maxValue || oldValue < minValue) {
+ HigherLower(maxValue, minValue);
+ SendMessage(hwndDlg,UM_SETDLGITEMINT,idCtrl,newValue);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ return TRUE;
+ }
+ if(idCtrl == IDC_TIMEOUT_VALUE && (wNotifyCode != EN_CHANGE || (HWND)lParam != GetFocus())) {
+ return TRUE;
+ }
+ if (wNotifyCode == CPN_COLOURCHANGED) {
+ //It's a colour picker change. idCtrl is the control id.
+ COLORREF colour = SendDlgItemMessage(hwndDlg,idCtrl,CPM_GETCOLOUR,0,0);
+ int ctlID = idCtrl;
+ if ((ctlID > 41070) && (ctlID < 42070) ) //It's 41071 or above => Text colour.
+ StatusesList[Index(ctlID-1000)].colorText = colour;
+ else //Background colour.
+ StatusesList[Index(ctlID-2000)].colorBack = colour;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ return TRUE;
+ }
+ switch(idCtrl)
+ {
+ case IDC_USEOWNCOLORS:
+ {
+ BOOL bEnableOthers = FALSE;
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ MyOptions.Colors = byCOLOR_OWN;
+ bEnableOthers = TRUE;
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++)
+ {
+ EnableWindow(GetDlgItem(hwndDlg,( i+2000)), bEnableOthers); //Background
+ EnableWindow(GetDlgItem(hwndDlg, (i+1000)), bEnableOthers); //Text
+ }
+// EnableWindow(GetDlgItem(hwndDlg, IDC_USEPOPUPCOLORS), bEnableOthers);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case IDC_USEWINCOLORS:
+ {
+ BOOL bEnableOthers = FALSE;
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ //Use Windows colours.
+ MyOptions.Colors = byCOLOR_WINDOWS;
+ bEnableOthers = FALSE;
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++)
+ {
+ EnableWindow(GetDlgItem(hwndDlg,( i+2000)), bEnableOthers); //Background
+ EnableWindow(GetDlgItem(hwndDlg, (i+1000)), bEnableOthers); //Text
+ }
+ //EnableWindow(GetDlgItem(hwndDlg, IDC_USEPOPUPCOLORS), bEnableOthers);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case IDC_USEPOPUPCOLORS:
+ {
+ BOOL bEnableOthers = FALSE;
+ if (wNotifyCode != BN_CLICKED)
+ break;
+ //Use Windows colours.
+ MyOptions.Colors = byCOLOR_POPUP;
+ bEnableOthers = FALSE;
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, (i+2000)), bEnableOthers); //Background
+ EnableWindow(GetDlgItem(hwndDlg, (i+1000)), bEnableOthers); //Text
+ }
+// EnableWindow(GetDlgItem(hwndDlg, IDC_USEWINCOLORS), bEnableOthers);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ }
+ case IDC_PREVIEW:
+ {//Declarations and initializations
+ POPUPDATAT ppd = { 0 };
+ PLUGINDATA * pd = NULL;
+ //int i = 0;
+ for (i = ID_STATUS_MIN; i <= (ID_STATUS_MAX+1); i++)
+ {wsprintfA(str, "%dp", i);
+ lstrcpy(ppd.lptzText, _T(""));
+ if ( !IsDlgButtonChecked(hwndDlg, i)) continue; // skip unchecked statuses
+ //ZeroMemory(&ppd, sizeof(POPUPDATAEX)); //Clear the object.
+ pd = (PLUGINDATA*)mir_alloc(sizeof(PLUGINDATA));
+ /* This is my #1 cause of bugs: I always forget to add this malloc!!
+ Without this malloc, every popup would share the same PLUGINDATA structure,
+ so the second popup to be destroyed causes a c0000005! */
+// pd->oldStatus =(WORD)(ID_STATUS_OFFLINE + ((i-1)%10));
+ pd->oldStatus = (WORD)(i <= ID_STATUS_MAX ? i : ID_STATUS_OFFLINE);
+// pd->newStatus = (WORD)(i <= ID_STATUS_MAX ? (pd->oldStatus + 1) : ID_STATUS_ONLINE);
+ pd->newStatus = (WORD)(i < ID_STATUS_MAX ? (i + 1) : (i == ID_STATUS_MAX ? ID_STATUS_OFFLINE : ID_STATUS_ONLINE));
+ ppd.lchIcon = LoadSkinnedIcon(StatusesList[Index(pd->newStatus)].Icon);
+ lstrcpyn(ppd.lptzContactName, (TCHAR*)CallService(MS_CLIST_GETCONTACTDISPLAYNAME, (WPARAM)NULL,GCMDF_TCHAR), SIZEOF(ppd.lptzContactName)-1);
+ if (MyOptions.UseDullText == 1)
+ lstrcpy(ppd.lptzText, StatusesList[Index(pd->newStatus)].lpzStandardText);
+ else if (MyOptions.UseDullText == 2)
+ lstrcpy(ppd.lptzText, StatusesList[Index(pd->newStatus)].lpzUStatusText);
+ if (MyOptions.ShowPreviousStatus)
+ {
+ TCHAR szAux[MAX_STATUSTEXT];
+ wsprintf(szAux, TranslateTS(STRING_SHOWPREVIOUSSTATUS), StatusesList[Index(pd->oldStatus)].lpzStandardText);
+ lstrcat(lstrcat(ppd.lptzText, _T(" ")), szAux);
+ }
+ if (MyOptions.ReadAwayMsg)
+ {
+ if (MyOptions.UseDullText > 0)
+ lstrcat(ppd.lptzText, _T("\n"));
+ if (i<=ID_STATUS_MAX)
+ {
+ char *DefAwayMsg = (char*)CallService(MS_AWAYMSG_GETSTATUSMSG,pd->newStatus,0);
+ if (DefAwayMsg != NULL)
+ {
+#ifdef UNICODE
+ WCHAR wsUStatMsg[MAX_SECONDLINE] = { 0 };
+ MultiByteToWideChar((unsigned int)CallService(MS_LANGPACK_GETCODEPAGE,0,0),MB_PRECOMPOSED, DefAwayMsg, -1, wsUStatMsg, sizeof(wsUStatMsg)/sizeof(WCHAR));
+ lstrcat(ppd.lptzText, wsUStatMsg);
+#else
+ lstrcat(ppd.lptzText, DefAwayMsg);
+#endif
+ mir_free(DefAwayMsg);
+ }
+ }
+ else
+ lstrcat(ppd.lptzText, TranslateT("This is \"from offline\" status message"));
+
+ }
+ switch (MyOptions.Colors)
+ {
+ case byCOLOR_WINDOWS:
+ ppd.colorBack = GetSysColor(COLOR_BTNFACE);
+ ppd.colorText = GetSysColor(COLOR_WINDOWTEXT);
+ break;
+ case byCOLOR_OWN:
+ ppd.colorBack = StatusesList[Index(pd->newStatus)].colorBack;
+ ppd.colorText = StatusesList[Index(pd->newStatus)].colorText;
+ break;
+ case byCOLOR_POPUP:
+ ppd.colorBack = ppd.colorText = 0;
+ break;
+ }
+ ppd.PluginWindowProc = (WNDPROC)PopupDlgProc;
+ ppd.PluginData = pd;
+ switch (MyOptions.byTimeout)
+ {
+ case TIMEOUT_CUSTOM:
+ ppd.iSeconds = MyOptions.iTimeout; break;
+ case TIMEOUT_PERMANENT:
+ ppd.iSeconds = -1; break;
+ case TIMEOUT_POPUP:
+ default:
+ ppd.iSeconds = 0; break;
+ }
+ PUAddPopUpT(&ppd);
+ }
+ break;
+ }
+ case IDC_CHK_USEDULLTEXT:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.UseDullText = MyOptions.UseDullText == 1 ? 0 : 1 ;
+ if (MyOptions.UseDullText == 1)
+ {
+ CheckDlgButton(hwndDlg, IDC_CHK_SHOWALTDESCS, BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWPREVIOUSSTATUS), TRUE);
+ }
+ else
+ if (MyOptions.UseDullText == 0)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWPREVIOUSSTATUS), FALSE);
+ MyOptions.ShowPreviousStatus = FALSE;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_CHK_SHOWALTDESCS:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.UseDullText = MyOptions.UseDullText == 2 ? 0 : 2 ;
+ if (MyOptions.UseDullText == 2)
+ {
+ CheckDlgButton(hwndDlg, IDC_CHK_USEDULLTEXT, BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWPREVIOUSSTATUS), TRUE);
+ }
+ else
+ if (MyOptions.UseDullText == 0)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_SHOWPREVIOUSSTATUS), FALSE);
+ MyOptions.ShowPreviousStatus = FALSE;
+ }
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_READAWAYMSG:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.ReadAwayMsg = !MyOptions.ReadAwayMsg;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_SHOWGROUP:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.ShowGroup = !MyOptions.ShowGroup;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_SHOWPREVIOUSSTATUS:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.ShowPreviousStatus = !MyOptions.ShowPreviousStatus;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_POPUP:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.byTimeout = TIMEOUT_POPUP;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TIMEOUT_VALUE), FALSE); //It has to be disabled.
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_CUSTOM:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.byTimeout = TIMEOUT_CUSTOM;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TIMEOUT_VALUE), TRUE); //It has to be enabled.
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_PERMANENT:
+ if (wNotifyCode != BN_CLICKED) break;
+ MyOptions.byTimeout = TIMEOUT_PERMANENT;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_TIMEOUT_VALUE), FALSE);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TIMEOUT_VALUE:
+ {
+ if (wNotifyCode == EN_CHANGE)
+ {
+ int iTemp = GetDlgItemInt(hwndDlg, IDC_TIMEOUT_VALUE, NULL, FALSE);
+ if (iTemp > TIMEOUT_MAXVALUE)
+ MyOptions.iTimeout = TIMEOUT_MAXVALUE;
+ else if (iTemp < TIMEOUT_MINVALUE)
+ MyOptions.iTimeout = TIMEOUT_MINVALUE;
+ else
+ MyOptions.iTimeout = iTemp;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ }
+ break;
+ }
+ case IDC_LC_DISMISS: case IDC_LC_OPENMSG:
+ case IDC_RC_DISMISS: case IDC_RC_USERMENU:
+ case IDC_CHK_PONLINE: case IDC_CHK_POFFLINE: case IDC_CHK_PINVISIBLE:
+ case IDC_CHK_PFREECHAT: case IDC_CHK_PAWAY: case IDC_CHK_PNA:
+ case IDC_CHK_POCCUPIED: case IDC_CHK_PDND: case IDC_CHK_POUTTOLUNCH:
+ case IDC_CHK_PONTHEPHONE:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_CHK_PFROMOFFLINE:
+ MyOptions.FromOfflinePopup=!MyOptions.FromOfflinePopup;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ } //End switch(WM_COMMAND)
+ break;
+ //End WM_COMMAND
+ }
+ case WM_NOTIFY: { //Here we have pressed either the OK or the APPLY button.
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_RESET:
+ //Restore the options stored in memory.
+ LoadOptions();
+ //We need to reload the status colours too.
+ InitStatusList();
+ return TRUE;
+ case PSN_APPLY: {
+ //Text colour
+ char szSetting[8] = { 0 };
+ DWORD ctlColour = 0;
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ ctlColour = SendDlgItemMessage(hwndDlg,(i+2000),CPM_GETCOLOUR,0,0);
+ StatusesList[Index(i)].colorBack = ctlColour;
+ wsprintfA(szSetting, "%ibg", i);
+ DBWriteContactSettingDword(NULL, ModuleName, szSetting,ctlColour);
+ ctlColour = SendDlgItemMessage(hwndDlg,(i+1000),CPM_GETCOLOUR,0,0);
+ StatusesList[Index(i)].colorText = ctlColour;
+ wsprintfA(szSetting, "%itx", i);
+ DBWriteContactSettingDword(NULL, ModuleName, szSetting, ctlColour);
+ }
+ //Colors
+ DBWriteContactSettingByte(NULL, ModuleName, "Colors", MyOptions.Colors);
+ //Use Dull Text
+ DBWriteContactSettingByte(NULL, ModuleName, "UseDullText", (BYTE)MyOptions.UseDullText);
+ //Away messages
+ DBWriteContactSettingByte(NULL, ModuleName, "ReadAwayMsg", (BYTE)MyOptions.ReadAwayMsg);
+ //Show previous status
+ DBWriteContactSettingByte(NULL, ModuleName, "ShowPreviousStatus", (BYTE)MyOptions.ShowPreviousStatus);
+ //Show group name in title
+ DBWriteContactSettingByte(NULL, ModuleName, "ShowGroup", (BYTE)MyOptions.ShowGroup);
+ //Timeout.
+ DBWriteContactSettingByte(NULL, ModuleName, "byTimeout", MyOptions.byTimeout);
+ DBWriteContactSettingDword(NULL, ModuleName, "iTimeout", MyOptions.iTimeout);
+ //Left mouse click
+ DBWriteContactSettingByte(NULL, ModuleName, "LeftClickAction", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_LC_DISMISS)?ACTION_DISMISS:ACTION_OPENMSG));
+ //Right mouse click
+ DBWriteContactSettingByte(NULL, ModuleName, "RightClickAction", (BYTE)(IsDlgButtonChecked(hwndDlg, IDC_RC_DISMISS)?ACTION_DISMISS:ACTION_USERMENU));
+ //Notified statuses
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ wsprintfA(str, "%dp", i);
+ DBWriteContactSettingByte(NULL, ModuleName, str, (BYTE)(BOOL)(IsDlgButtonChecked(hwndDlg, i)));
+ }
+ //Always notify when changing from offline
+ DBWriteContactSettingByte(NULL, ModuleName, "FromOfflinePopup", (BYTE)MyOptions.FromOfflinePopup);
+ return TRUE;
+ } //case PSN_APPLY
+ } // switch code
+ break;
+ } //switch idFrom
+ break; //End WM_NOTIFY
+ }
+ case UM_SETDLGITEMINT:
+ SetDlgItemInt(hwndDlg, (int)wParam, (int)lParam, FALSE);
+ break;
+ } //switch message
+ return FALSE;
+}
+
+static BOOL CALLBACK DlgProcAutoDisableOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ int i = 0;
+ char str[8] = { 0 };
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ //Statuses notified
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ wsprintfA(str, "p%d", i);
+ CheckDlgButton(hwndDlg, i,
+ (DBGetContactSettingByte(NULL, ModuleName, str, DEFAULT_NOTIFYWITHPOPUP))?BST_UNCHECKED:BST_CHECKED);
+ }
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ wsprintfA(str, "s%d", i);
+ CheckDlgButton(hwndDlg, (i+2000),
+ (DBGetContactSettingByte(NULL, ModuleName, str, DEFAULT_NOTIFYWITHSOUND))?BST_UNCHECKED:BST_CHECKED);
+ }
+ return TRUE;
+ case WM_CLOSE:
+ DestroyWindow(hwndDlg);
+ break;
+ case WM_COMMAND:
+ switch (LOWORD(wParam)) {
+ case IDC_OK:
+ //Notified statuses
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ wsprintfA(str, "p%d", i);
+ DBWriteContactSettingByte(NULL, ModuleName, str, (BYTE)(BOOL)(!IsDlgButtonChecked(hwndDlg, i)));
+ }
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ wsprintfA(str, "s%d", i);
+ DBWriteContactSettingByte(NULL, ModuleName, str, (BYTE)(BOOL)(!IsDlgButtonChecked(hwndDlg, (i+2000))));
+ }
+ //Fall through
+ case IDC_CANCEL:
+ DestroyWindow(hwndDlg);
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ return FALSE;
+}
+
+//===== General options: use sounds? Use internal speaker? When to notify? Temporarily disable?
+static BOOL CALLBACK DlgProcGeneralOpts(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) {
+ int i = 0;
+ char str[8] = { 0 };
+ DBVARIANT dbv = { 0 };
+ WORD idCtrl = LOWORD(wParam), wNotifyCode = HIWORD(wParam);
+
+ switch (msg) {
+ case WM_INITDIALOG:
+ TranslateDialogDefault(hwndDlg);
+ //Automatically disable
+ CheckDlgButton(hwndDlg, IDC_AUTODISABLE,MyOptions.AutoDisable?BST_CHECKED:BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGUREAUTODISABLE), IsDlgButtonChecked(hwndDlg, IDC_AUTODISABLE));
+ //Temporary disable
+ CheckDlgButton(hwndDlg, IDC_TEMPDISABLE,MyOptions.TempDisable?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_SHOWONCONNECTION,DBGetContactSettingByte(NULL, ModuleName, "ShowOnConnection", DEFAULT_SHOWONCONNECTION)?BST_CHECKED:BST_UNCHECKED);
+ //Blink icon.
+ CheckDlgButton(hwndDlg, IDC_BLINKICON, MyOptions.BlinkIcon?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_BLINKICON_STATUS, MyOptions.BlinkIcon_Status?BST_CHECKED:BST_UNCHECKED);
+ //Menu item
+ CheckDlgButton(hwndDlg, IDC_SHOWDISABLEMENU, DBGetContactSettingByte(NULL, ModuleName, "ShowDisableMenu", DEFAULT_SHOWDISABLEMENU)?BST_CHECKED:BST_UNCHECKED);
+ //HiddenContactsToo
+ CheckDlgButton(hwndDlg, IDC_HIDDENCONTACTSTOO, MyOptions.HiddenContactsToo?BST_UNCHECKED:BST_CHECKED);
+ //Sounds
+ CheckDlgButton(hwndDlg, IDC_SPEAKER,MyOptions.UseSpeaker?BST_CHECKED:BST_UNCHECKED);
+ CheckDlgButton(hwndDlg, IDC_AVOIDCLIPPING,MyOptions.AvoidClipping?BST_CHECKED:BST_UNCHECKED);
+ //Individual sounds
+ CheckDlgButton(hwndDlg, IDC_USEINDIVSOUNDS,MyOptions.UseIndSnd?BST_CHECKED:BST_UNCHECKED);
+ //Statuses notified
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++)
+ {
+ wsprintfA(str, "%d", i);
+ CheckDlgButton(hwndDlg, i, (DBGetContactSettingByte(NULL, ModuleName, str, 0))?BST_UNCHECKED:BST_CHECKED);
+ }
+ //Always notify when changing from offline?
+ CheckDlgButton(hwndDlg, IDC_CHK_FROMOFFLINE, (MyOptions.FromOffline)?BST_CHECKED:BST_UNCHECKED);
+ // Log options
+ if (DBGetContactSettingTString(NULL, ModuleName, "Logfile", &dbv) == 0)
+ {
+ SetDlgItemText(hwndDlg, IDC_LOGFILE, dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+ else
+ SetDlgItemText(hwndDlg, IDC_LOGFILE, DEFAULT_LOGFILE);
+ CheckDlgButton(hwndDlg, IDC_LOG, MyOptions.Log?BST_CHECKED:BST_UNCHECKED);
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOGFILE), IsDlgButtonChecked(hwndDlg, IDC_LOG));
+
+ return TRUE;
+ //End WM_INITDIALOG
+ case WM_COMMAND:
+ //These are simple clicks: we don't save, but we tell the Options Page to enable the "Apply" button.
+ switch(LOWORD(wParam)) {
+ case IDC_CONFIGUREAUTODISABLE:
+ CreateDialogA(hInst, MAKEINTRESOURCEA(IDD_AUTODISABLE), hwndDlg, DlgProcAutoDisableOpts);
+ break;
+ case IDC_SPEAKER:
+ MyOptions.UseSpeaker=!MyOptions.UseSpeaker;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_USEINDIVSOUNDS:
+ MyOptions.UseIndSnd=!MyOptions.UseIndSnd;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ case IDC_AVOIDCLIPPING:
+ MyOptions.AvoidClipping=!MyOptions.AvoidClipping;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_AUTODISABLE:
+ MyOptions.AutoDisable = !MyOptions.AutoDisable;
+ EnableWindow(GetDlgItem(hwndDlg, IDC_CONFIGUREAUTODISABLE), IsDlgButtonChecked(hwndDlg, IDC_AUTODISABLE));
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_TEMPDISABLE:
+ EnableDisableMenuCommand(0,0);
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_HIDDENCONTACTSTOO:
+ MyOptions.HiddenContactsToo=!MyOptions.HiddenContactsToo;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_BLINKICON:
+ MyOptions.BlinkIcon = !MyOptions.BlinkIcon;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_BLINKICON_STATUS:
+ MyOptions.BlinkIcon_Status = !MyOptions.BlinkIcon_Status;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_SHOWDISABLEMENU:
+ case IDC_SHOWONCONNECTION:
+ case IDC_CHK_ONLINE: case IDC_CHK_OFFLINE: case IDC_CHK_INVISIBLE:
+ case IDC_CHK_FREECHAT: case IDC_CHK_AWAY: case IDC_CHK_NA:
+ case IDC_CHK_OCCUPIED: case IDC_CHK_DND: case IDC_CHK_OUTTOLUNCH:
+ case IDC_CHK_ONTHEPHONE:
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_CHK_FROMOFFLINE:
+ MyOptions.FromOffline=!MyOptions.FromOffline;
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0);
+ break;
+ case IDC_LOG:
+ if (idCtrl == IDC_LOG && wNotifyCode == BN_CLICKED)
+ {
+ EnableWindow(GetDlgItem(hwndDlg, IDC_LOGFILE), IsDlgButtonChecked(hwndDlg, IDC_LOG));
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0,0);
+ break;
+ }
+ if (idCtrl == IDC_LOGFILE && wNotifyCode == EN_CHANGE && (HWND)lParam == GetFocus())
+ {
+ SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0,0);
+ break;
+ }
+ default:
+ break;
+ }//End switch(WM_COMMAND)
+ break;
+ //End WM_COMMAND
+ case WM_NOTIFY: //Here we have pressed either the OK or the APPLY button.
+ switch(((LPNMHDR)lParam)->idFrom) {
+ case 0:
+ switch (((LPNMHDR)lParam)->code) {
+ case PSN_RESET:
+ LoadOptions();
+ return TRUE;
+ case PSN_APPLY: {
+ TCHAR szFile[MAX_PATH] = { 0 };
+ int iResult = GetDlgItemText(hwndDlg, IDC_LOGFILE, szFile, MAX_PATH);
+ DBWriteContactSettingByte(NULL, ModuleName, "AutoDisable", (BYTE)MyOptions.AutoDisable);
+ //Temporarily disable NewStatusNotify
+ DBWriteContactSettingByte(NULL, ModuleName, "TempDisable", (BYTE)MyOptions.TempDisable);
+ DBWriteContactSettingByte(NULL, ModuleName, "ShowDisableMenu", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_SHOWDISABLEMENU));
+
+ if (IsDlgButtonChecked(hwndDlg, IDC_SHOWDISABLEMENU)) { //We want the menu item.
+ if (!hEnableDisableMenu) { //It's not present, so make it.
+ installMainMenuItem();
+ }
+ // else -> It is present, don't do anything.
+ }
+ else { //We don't want the menu item.
+ if (hEnableDisableMenu) { //It is present, we remove it.
+ if (ServiceExists(MS_CLIST_REMOVEMAINMENUITEM)) {
+ CallService(MS_CLIST_REMOVEMAINMENUITEM, (WPARAM)hEnableDisableMenu, (LPARAM)0);
+ hEnableDisableMenu = (int)(HANDLE)NULL;
+ }
+ //else -> the service does not exist, we leave the item there.
+ }
+ //else -> it is not present, we don't need to do anything.
+ }
+
+ DBWriteContactSettingByte(NULL, ModuleName, "ShowOnConnection", (BYTE)IsDlgButtonChecked(hwndDlg, IDC_SHOWONCONNECTION));
+ //individual sounds
+ DBWriteContactSettingByte(NULL, ModuleName, "UseIndSounds", (BYTE)MyOptions.UseIndSnd);
+ //Speaker
+ DBWriteContactSettingByte(NULL, ModuleName, "UseSpeaker", (BYTE)MyOptions.UseSpeaker);
+ //Avoid clipping
+ DBWriteContactSettingByte(NULL, ModuleName, "AvoidClipping", (BYTE)MyOptions.AvoidClipping);
+ //HiddenContactsToo
+ DBWriteContactSettingByte(NULL, ModuleName, "HiddenContactsToo", (BYTE)MyOptions.HiddenContactsToo);
+ //Blink icon
+ DBWriteContactSettingByte(NULL, ModuleName, "BlinkIcon", (BYTE)MyOptions.BlinkIcon);
+ DBWriteContactSettingByte(NULL, ModuleName, "BlinkIcon_Status", (BYTE)MyOptions.BlinkIcon_Status);
+ //Notified statuses
+ for (i = ID_STATUS_MIN; i <= ID_STATUS_MAX; i++) {
+ wsprintfA(str, "%d", i);
+ DBWriteContactSettingByte(NULL, ModuleName, str, (BYTE)(BOOL)(!IsDlgButtonChecked(hwndDlg, i)));
+ }
+ //Always notify when changing from offline
+ DBWriteContactSettingByte(NULL, ModuleName, "FromOffline", (BYTE)MyOptions.FromOffline);
+
+ // log filename -------------------------
+ if (iResult == 0)
+ { //No file name has been given. Use default one.
+ SetDlgItemText(hwndDlg, IDC_LOGFILE, DEFAULT_LOGFILE);
+ lstrcpy(szFile, DEFAULT_LOGFILE);
+ }
+ MyOptions.Log = IsDlgButtonChecked(hwndDlg, IDC_LOG)?1:0;
+ DBWriteContactSettingByte(NULL, ModuleName, "Log", MyOptions.Log);
+ DBWriteContactSettingTString(NULL, ModuleName, "Logfile", szFile);
+ //---------------------------
+
+ return TRUE;
+ } //case PSN_APPLY
+ } // switch code
+ break;
+ } //switch idFrom
+ break; //End WM_NOTIFY
+ } //switch message
+ return FALSE;
+}
+
+//===== Initializations =====
+void InitStatusList() {
+ int index = 0;
+ //Online
+ index = Index(ID_STATUS_ONLINE);
+ StatusesList[index].ID = ID_STATUS_ONLINE;
+ StatusesList[index].Icon = SKINICON_STATUS_ONLINE;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) is back online!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) is back online!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) is back online!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("Online"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserOnline", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Online", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "global.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_ONLINE;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40072bg", POPUPCOLOR_BG_40072);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40072tx", POPUPCOLOR_TX_40072);
+
+ //Offline
+ index = Index(ID_STATUS_OFFLINE);
+ StatusesList[index].ID = ID_STATUS_OFFLINE;
+ StatusesList[index].Icon = SKINICON_STATUS_OFFLINE;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) went offline! :("), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) went offline! :("), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) went offline! :("), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("Offline"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserOffline", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Offline", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "offline.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = 0;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40071bg", POPUPCOLOR_BG_40071);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40071tx", POPUPCOLOR_TX_40071);
+
+ //Invisible
+ index = Index(ID_STATUS_INVISIBLE);
+ StatusesList[index].ID = ID_STATUS_INVISIBLE;
+ StatusesList[index].Icon = SKINICON_STATUS_INVISIBLE;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) hides in shadows..."), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) hides in shadows..."), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) hides in shadows..."), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("Invisible"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserInvisible", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Invisible", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "invisible.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_INVISIBLE;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40078bg", POPUPCOLOR_BG_40078);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40078tx", POPUPCOLOR_TX_40078);
+
+ //Free for chat
+ index = Index(ID_STATUS_FREECHAT);
+ StatusesList[index].ID = ID_STATUS_FREECHAT;
+ StatusesList[index].Icon = SKINICON_STATUS_FREE4CHAT;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) feels talkative!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) feels talkative!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) feels talkative!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("Free for chat"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserFreeForChat", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Free For Chat", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "free4chat.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_FREECHAT;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40077bg", POPUPCOLOR_BG_40077);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40077tx", POPUPCOLOR_TX_40077);
+
+ //Away
+ index = Index(ID_STATUS_AWAY);
+ StatusesList[index].ID = ID_STATUS_AWAY;
+ StatusesList[index].Icon = SKINICON_STATUS_AWAY;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) went Away"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) went Away"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) went Away"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("Away"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserAway", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Away", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "away.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_SHORTAWAY;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40073bg", POPUPCOLOR_BG_40073);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40073tx", POPUPCOLOR_TX_40073);
+
+ //NA
+ index = Index(ID_STATUS_NA);
+ StatusesList[index].ID = ID_STATUS_NA;
+ StatusesList[index].Icon = SKINICON_STATUS_NA;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) isn't there anymore!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) isn't there anymore!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) isn't there anymore!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("NA"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserNA", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Not Available", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "na.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_LONGAWAY;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40075bg", POPUPCOLOR_BG_40075);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40075tx", POPUPCOLOR_TX_40075);
+
+ //Occupied
+ index = Index(ID_STATUS_OCCUPIED);
+ StatusesList[index].ID = ID_STATUS_OCCUPIED;
+ StatusesList[index].Icon = SKINICON_STATUS_OCCUPIED;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) has something else to do."), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) has something else to do."), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) has something else to do."), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("Occupied"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserOccupied", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Occupied", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "occupied.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_LIGHTDND;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40076bg", POPUPCOLOR_BG_40076);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40076tx", POPUPCOLOR_TX_40076);
+
+ //DND
+ index = Index(ID_STATUS_DND);
+ StatusesList[index].ID = ID_STATUS_DND;
+ StatusesList[index].Icon = SKINICON_STATUS_DND;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) doesn't want to be disturbed!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) doesn't want to be disturbed!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) doesn't want to be disturbed!"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("DND"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserDND", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Do Not Disturb", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "dnd.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_HEAVYDND;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40074bg", POPUPCOLOR_BG_40074);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40074tx", POPUPCOLOR_TX_40074);
+
+ //OutToLunch
+ index = Index(ID_STATUS_OUTTOLUNCH);
+ StatusesList[index].ID = ID_STATUS_OUTTOLUNCH;
+ StatusesList[index].Icon = SKINICON_STATUS_OUTTOLUNCH;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) is eating something"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) is eating something"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) is eating something"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("Out to lunch"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserOutToLunch", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: Out To Lunch", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "lunch.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_OUTTOLUNCH;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40080bg", POPUPCOLOR_BG_40080);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40080tx", POPUPCOLOR_TX_40080);
+
+ //OnThePhone
+ index = Index(ID_STATUS_ONTHEPHONE);
+ StatusesList[index].ID = ID_STATUS_ONTHEPHONE;
+ StatusesList[index].Icon = SKINICON_STATUS_ONTHEPHONE;
+ lstrcpyn(StatusesList[index].lpzMStatusText, TranslateT("(M) had to answer the phone"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzFStatusText, TranslateT("(F) had to answer the phone"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzUStatusText, TranslateT("(U) had to answer the phone"), MAX_STATUSTEXT);
+ lstrcpyn(StatusesList[index].lpzStandardText, TranslateT("On the phone"), MAX_STANDARDTEXT);
+ lstrcpynA(StatusesList[index].lpzSkinSoundName, "UserOnThePhone", MAX_SKINSOUNDNAME);
+ lstrcpynA(StatusesList[index].lpzSkinSoundDesc, "User: On The Phone", MAX_SKINSOUNDDESC);
+ lstrcpynA(StatusesList[index].lpzSkinSoundFile, "phone.wav", MAX_PATH);
+// StatusesList[index].dwProtoFlag = PF2_ONTHEPHONE;
+ StatusesList[index].colorBack = DBGetContactSettingDword(NULL, ModuleName, "40079bg", POPUPCOLOR_BG_40079);
+ StatusesList[index].colorText = DBGetContactSettingDword(NULL, ModuleName, "40079tx", POPUPCOLOR_TX_40079);
+}
+
+//Function which makes the initializations
+static int ModulesLoaded(WPARAM wParam,LPARAM lParam) {
+ //===== Initializations =====
+ //UserInfo detail page
+ hUserInfoInitialise = HookEvent(ME_USERINFO_INITIALISE, UserInfoInitialise);
+
+ //Contact status change: let's try if here.
+ hContactStatusChanged = HookEvent(ME_STATUSCHANGE_CONTACTSTATUSCHANGED,ContactStatusChanged);
+
+ //I need this window for the "PopUps on connect" routines.
+ SecretWnd = CreateWindowEx(WS_EX_TOOLWINDOW,_T("static"),_T("ConnectionTimerWindow"),0,
+ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,HWND_DESKTOP,
+ NULL,hInst,NULL);
+ //I need this window for the Right Click menu actions
+/* hRightClickWnd = CreateWindowEx(WS_EX_TOOLWINDOW,_T("static"),_T("NewStatusNotify_RightClickWindow"),0,
+ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,HWND_DESKTOP,
+ NULL,hInst,NULL);
+*/
+// SetWindowLong(hRightClickWnd, GWL_WNDPROC, (LONG)(WNDPROC)RightClickWndProc);
+
+ //PopUps on connect.
+ {
+ int protoCount = 0, i = 0;
+ PROTOCOLDESCRIPTOR **protos = NULL;
+ CallService(MS_PROTO_ENUMPROTOCOLS, (WPARAM)&protoCount, (LPARAM)&protos);
+ for (i=0; i < protoCount; i++)
+ {
+ if (protos[i]->type == PROTOTYPE_PROTOCOL)
+ DBWriteContactSettingByte(NULL, ModuleName, protos[i]->szName, PROTO_POPUPS_OFF);
+ }
+ }
+ { //----------------updater support---------------
+ Update update = {0};
+ char szVersion[16];
+ update.cbSize = sizeof(Update);
+
+ update.szComponentName = pluginInfo.shortName;
+ update.pbVersion = (BYTE *)CreateVersionStringPlugin(&pluginInfo, szVersion);
+ update.cpbVersion = strlen((char *)update.pbVersion);
+ // these are the three lines that matter - the archive, the page containing the version string, and the text (or data)
+ // before the version that we use to locate it on the page
+ // (note that if the update URL and the version URL point to standard file listing entries, the backend xml
+ // data will be used to check for updates rather than the actual web page - this is not true for beta urls)
+/* update.szUpdateURL = "http://vasilich.org/mim/nsn/nsnw.zip";
+ update.szVersionURL = "http://vasilich.org/blog/";
+ update.pbVersionPrefix = (BYTE *)"NSNw ";
+ update.cpbVersionPrefix = strlen((char *)update.pbVersionPrefix);
+*/
+ update.szBetaVersionURL = "http://vasilich.org/mim/nsn/nsn.txt";
+ update.pbBetaVersionPrefix = (BYTE *)"NSN ";
+#ifdef UNICODE
+ update.szBetaUpdateURL = "http://vasilich.org/mim/nsn/nsnw.zip";
+#else
+ update.szBetaUpdateURL = "http://vasilich.org/mim/nsn/nsn.zip";
+#endif
+ update.cpbBetaVersionPrefix = strlen((char *)update.pbBetaVersionPrefix);
+ // do the same for the beta versions of the above struct members if you wish to allow beta updates from another URL
+ if(ServiceExists(MS_UPDATE_REGISTER)) CallService(MS_UPDATE_REGISTER, 0, (LPARAM)&update);
+ }
+ // popup window list
+ return 0;
+}
+
+static int EnableDisableMenuCommand(WPARAM wParam,LPARAM lParam) {
+ //We're here to change something, so let's read the actual value.
+ //TempDisable == FALSE means that right now the popups are enabled
+ //and if we're here, we want to disable them.
+ //The icon works this way:
+ //if the notifications are disabled, the icon hasn't the red circle;
+ //if the notifications are enabled, the icon has the red circle.
+
+ //This is for compatibility purposes.
+ CLISTMENUITEM mi = { 0 };
+
+ mi.cbSize = sizeof(mi);
+
+ if (MyOptions.TempDisable == FALSE)
+ {
+ DBWriteContactSettingByte(NULL,ModuleName,"TempDisable",TRUE);
+ mi.ptszName = _T("Enable status ¬ification");
+ mi.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_MAINMENU_OFF));//IDI_NOPOPUP));
+ MyOptions.TempDisable = TRUE;
+ }
+ else
+ {
+ DBWriteContactSettingByte(NULL,ModuleName,"TempDisable",FALSE);
+ mi.ptszName = _T("Disable status ¬ification");
+ mi.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_MAINMENU_ON));//IDI_POPUP));
+ MyOptions.TempDisable = FALSE;
+ }
+ mi.flags = CMIM_ICON | CMIM_NAME | CMIF_TCHAR;
+ CallService(MS_CLIST_MODIFYMENUITEM,(WPARAM)hEnableDisableMenu,(LPARAM)&mi);
+ return 0;
+}
+
+static int ProtoAck(WPARAM wParam,LPARAM lParam)
+{
+ ACKDATA *ack=(ACKDATA*)lParam;
+
+ if(ack->type == ACKTYPE_STATUS)
+ { //We get here on a status change, or a status notification (meaning: old status and new status are just like the same)
+ WORD newStatus = (WORD)ack->lParam;
+ WORD oldStatus = (WORD)ack->hProcess;
+ char * szProtocol = (char*)ack->szModule;
+ //Now we have the statuses and (a pointer to the string representing) the protocol.
+ if (oldStatus == newStatus)
+ return 0; //Useless message.
+ if (newStatus == ID_STATUS_OFFLINE)
+ {
+ //The protocol switched to offline. Disable the popups for this protocol
+ DBWriteContactSettingByte(NULL, ModuleName, szProtocol, PROTO_POPUPS_OFF);
+ }
+ else if (oldStatus < ID_STATUS_ONLINE && newStatus >= ID_STATUS_ONLINE)
+ {
+ //The protocol changed from a disconnected status to a connected status.
+ //Enable the popups for this protocol.
+ int idTimer = 0;
+ idTimer = AddAtomA(szProtocol);
+ if (idTimer)
+ {
+ UINT ConnectTimer;
+ char TimerProtoName[256] = {0};
+ mir_snprintf(TimerProtoName,sizeof(TimerProtoName),"ConnectionTimeout%s",szProtocol);
+ ConnectTimer = DBGetContactSettingDword(0,ModuleName,TimerProtoName,DBGetContactSettingDword(0,ModuleName,"ConnectionTimeout",10000));
+ SetTimer(SecretWnd, idTimer,ConnectTimer, ConnectionTimerProc);
+ }
+ else
+ { //AddAtom failed.
+ DWORD dwError = GetLastError();
+ TCHAR szError[1024] = { 0 };
+ wsprintf(szError, _T("AddAtom() failed.\r\n%s\r\n%d\nError: %d."), __TFILE__, __LINE__, dwError);
+ Log(szError);
+ }
+ }
+ else
+ {
+ //The protocol changed in a way we don't care.
+ }
+ return 0;
+ }
+ else if (ack->type == ACKTYPE_AWAYMSG) //It's an away message
+ {
+ if ((ack->hContact != 0) && MyOptions.ReadAwayMsg) //not our own status message and we should show status message in popup
+ switch (ack->result)
+ {
+ case ACKRESULT_SUCCESS: //The away message got read.
+ {
+ ReceivedStatusMessage(ack->hContact, TRUE);
+ break;
+ }
+ case ACKRESULT_FAILED: //The away message could not be read.
+ {
+ ReceivedStatusMessage(ack->hContact, FALSE);
+ break;
+ }
+ default:
+ break;
+ }
+ return 0;
+ }
+ else //It's something we don't need.
+ return 0;
+}
+
+VOID CALLBACK ConnectionTimerProc(HWND hwnd,UINT uMsg,UINT idEvent,DWORD dwTime)
+{
+ if (uMsg != WM_TIMER) return;
+ else
+ {
+ char szProtocol[MAX_PATH] = { 0 };
+ DWORD dwResult = 0;
+ //We've received a timer message: enable the popups for a specified protocol.
+ KillTimer(hwnd, idEvent);
+ dwResult = (DWORD)GetAtomNameA((ATOM)idEvent, szProtocol, sizeof(szProtocol));
+ if (dwResult) DBWriteContactSettingByte(NULL, ModuleName, szProtocol, PROTO_POPUPS_ON);
+ }
+}
+
+static int ContactSendMessage(WPARAM wParam, LPARAM lParam)
+{
+ HANDLE hContact = (HANDLE)wParam;
+
+ if (ServiceExists("SRMsg/LaunchMessageWindow")) //Conversation is enabled.
+ CallService("SRMsg/LaunchMessageWindow",(WPARAM)hContact,0);
+ else //SplitMsgDialog or the default one are enabled.
+ CallService(MS_MSG_SENDMESSAGE,(WPARAM)hContact,0);
+
+ return 0;
+}
+
+#ifndef WM_AWAYMSG
+#define WM_AWAYMSG (WM_USER + 0x0054)
+#endif
+
+BOOL QueryAwayMessage(HWND hWnd, PLUGINDATA * pdp)
+{
+ char *szProto;
+ //HANDLE hContact = (HANDLE)CallService(MS_POPUP_GETCONTACT,(WPARAM)hWnd,0);
+ HANDLE hContact = PUGetContact(hWnd);
+
+/* if (pdp->hAwayMsgHook)
+ { //We're already asking for it.
+//test UnhookEvent(pdp->hAwayMsgHook);
+ return FALSE;
+ }
+*/
+ szProto=(char*)CallService(MS_PROTO_GETCONTACTBASEPROTO,(WPARAM)hContact,0);
+ if(szProto)
+ {
+ if(CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_1,0)&PF1_MODEMSGRECV)
+ {
+ if(CallProtoService(szProto,PS_GETCAPS,PFLAGNUM_3,0)&/*StatusesList[Index((pdp->newStatus))].dwProtoFlag*/Proto_Status2Flag(pdp->newStatus))
+ {
+ //The following HookEventMessage will hook the ME_PROTO_ACK event and send a WM_AWAYMSG to hWnd when the hooks get notified.
+ //pdp->hAwayMsgHook = HookEventMessage(ME_PROTO_ACK,hWnd,WM_AWAYMSG);
+ //The following instruction asks Miranda to retrieve the away message and associates a hProcess (handle) to this request. This handle will appear in the ME_PROTO_ACK event.
+ pdp->hAwayMsgProcess = (HANDLE)CallContactService(hContact, PSS_GETAWAYMSG, 0, 0);
+ return TRUE;
+ //return FALSE;
+ }
+ }
+ }
+ return FALSE;
+}
+void ReceivedStatusMessage(HANDLE hContact, BOOL AckSuccess)
+{
+ TCHAR tStatMes[MAX_SECONDLINE] = {0};
+ TCHAR tNewMsg[MAX_SECONDLINE] = {0};
+ PUWinItem PULItem = {0};
+ PUWinItem* pPULItem = {0};
+ time_t now = 0;
+ if (AckSuccess) //The away message got read.
+ {
+ DBVARIANT dbv = {0};
+ if (DBGetContactSettingTString(hContact, "CList", "StatusMsg", &dbv ) == DB_READ_SUCCESS)
+ { //got Status message
+ lstrcpyn(tStatMes, dbv.ptszVal, MAX_SECONDLINE);
+ DBFreeVariant(&dbv);
+ }
+ }
+ else //The away message could not be read.
+ {
+ lstrcpyn(tStatMes, TranslateT("<error>"), MAX_SECONDLINE); // send "error reading" to popup
+ }
+ time(&now);
+ PULItem.hContact = hContact;
+ pPULItem = li.List_Find(PopupList, &PULItem);
+ if (pPULItem == NULL) // item not found: -------------------------------------------
+ {
+ pPULItem = (PUWinItem*)mir_alloc(sizeof(PULItem));
+ pPULItem->hContact = hContact;
+ pPULItem->hWnd = NULL;
+ time(&(pPULItem->TimeStamp));
+ lstrcpyn(pPULItem->StatMsg, tStatMes, MAX_SECONDLINE);
+ pPULItem->Status = 0;
+ li.List_Insert(PopupList, (void*)pPULItem, 0); // - insert item to list
+ }
+ else
+ if (pPULItem->hWnd == NULL) // item found, but without popup: ------------------------------
+ {
+ lstrcpyn(pPULItem->StatMsg, tStatMes, MAX_SECONDLINE); // - put new StatusMessage in this item
+ time(&(pPULItem->TimeStamp));
+ }
+ else // item found with popup: --------------------------------------
+// if ((pPULItem->TimeStamp+1) < now) // and popup timestamp more than 1 second old
+ {
+ DBVARIANT dbv = { 0 };
+ if ((DBGetContactSettingTString(hContact, ModuleName, LASTPOPUPTEXT, &dbv) == DB_READ_SUCCESS) )
+ {
+ mir_sntprintf(tNewMsg, MAX_SECONDLINE, _T("%s\n%s"), dbv.ptszVal, tStatMes);
+ PUChangeTextT(pPULItem->hWnd, tNewMsg); // - send TextChange to it
+ DBFreeVariant(&dbv);
+ }
+ }
+}
+
+void ReceivedAwayMessage(HWND hWnd, LPARAM lParam, PLUGINDATA * pdp) {
+
+ //HANDLE hContact = (HANDLE)CallService(MS_POPUP_GETCONTACT, (WPARAM)hWnd, 0);
+// PLUGINDATA *pdp = NULL;
+ HANDLE hPUContact = PUGetContact(hWnd);
+ ACKDATA *ack=(ACKDATA*)lParam;
+
+ HANDLE hContact = ack->hContact;
+ if (!hWnd) return; // We haven't requested this ACK... well, or our window has gone...
+ pdp = (PLUGINDATA *)PUGetPluginData(hWnd);
+ if (pdp == (PLUGINDATA *)(-1)) return; // something gone wrong in popup
+
+ if (ack->type != ACKTYPE_AWAYMSG) return;
+ if (ack->hProcess == pdp->hAwayMsgProcess) { //It's my hProcess, so it's ok to continue.
+ TCHAR szNewText[MAX_SECONDLINE] = { 0 };
+ TCHAR szText [MAX_SECONDLINE] = { 0 };
+ char * szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact,(LPARAM)0);
+ //The first thing we go is removing the hook from the chain to avoid useless calls.
+// UnhookEvent(pdp->hAwayMsgHook);
+// pdp->hAwayMsgHook = NULL;
+ //Now we proceed.
+ if (ack->result == ACKRESULT_SUCCESS)
+ { //We've the new text.
+ DBVARIANT dbv = { 0 };
+ if ((DBGetContactSettingTString(hContact, ModuleName, LASTPOPUPTEXT, &dbv)) == DB_READ_SUCCESS)
+ {
+ mir_sntprintf(szText, SIZEOF(szText), _T("%s\n"), dbv.ptszVal);
+ DBFreeVariant(&dbv);
+ }
+
+#ifdef UNICODE
+ {
+ //as we have no unicode in status messages, then we are trying to guess which CP has message
+ unsigned int CodePage;
+ //char * szProto = (char*)CallService(MS_PROTO_GETCONTACTBASEPROTO, (WPARAM)hContact,(LPARAM)0);
+ //first check if there is setting for TabSRMM for defining userCP for that contact
+ CodePage=(unsigned int)DBGetContactSettingDword(hContact, "Tab_SRMsg", "ANSIcodepage", 5);
+ if ( (!strncmp(szProto, "ICQ", 3)) && (CodePage==5) ) //check for codepage setting if proto is ICQ
+ {
+ CodePage=DBGetContactSettingWord(hContact, szProto, "AnsiCodePage", 5); //for contact
+ if (CodePage==CP_ACP)
+ CodePage=(unsigned int)DBGetContactSettingDword(NULL, szProto, "AnsiCodePage", 5); //and global setting
+ }
+ if (CodePage==5)
+ CodePage=(unsigned int)CallService(MS_LANGPACK_GETCODEPAGE,0,0); //at the end, if all previous methods failed, check LangPack CP
+ MultiByteToWideChar(CodePage, MB_PRECOMPOSED,(char*)ack->lParam, -1, szNewText, MAX_SECONDLINE/sizeof(wchar_t));
+ }
+#else
+ lstrcpyn(szNewText, (char*)ack->lParam, MAX_SECONDLINE);
+#endif
+ }
+ else { //We could not get the new text.
+ lstrcpyn(szNewText, TranslateT("<error>"), MAX_SECONDLINE);
+ }
+ //Now that we have the new text, let's procees and change the second line text.
+ lstrcat(szText,szNewText);
+ SendMessage(hWnd, WM_SETREDRAW, FALSE, 0);
+ PUChangeTextT(hWnd, (TCHAR*)szText);
+ SendMessage(hWnd, WM_SETREDRAW, TRUE, 0);
+ return;
+ }
+ else
+ return;
+}
+
+static BOOL StatusHasAwayMessage(char * szProto, int status)
+{
+ if (szProto != NULL)
+ {
+ unsigned long iSUpportsSM = (unsigned long)CallProtoService(szProto, PS_GETCAPS, (WPARAM)PFLAGNUM_3,(LPARAM)0);
+ switch (status) {
+ case ID_STATUS_AWAY:
+ return (iSUpportsSM & PF2_SHORTAWAY)?TRUE:FALSE;
+ break;
+ case ID_STATUS_NA:
+ return (iSUpportsSM & PF2_LONGAWAY)?TRUE:FALSE;
+ break;
+ case ID_STATUS_OCCUPIED:
+ return (iSUpportsSM & PF2_LIGHTDND)?TRUE:FALSE;
+ break;
+ case ID_STATUS_DND:
+ return (iSUpportsSM & PF2_HEAVYDND)?TRUE:FALSE;
+ break;
+ case ID_STATUS_FREECHAT:
+ return (iSUpportsSM & PF2_FREECHAT)?TRUE:FALSE;
+ break;
+ case ID_STATUS_ONTHEPHONE:
+ return (iSUpportsSM & PF2_ONTHEPHONE)?TRUE:FALSE;
+ break;
+ case ID_STATUS_OUTTOLUNCH:
+ return (iSUpportsSM & PF2_OUTTOLUNCH)?TRUE:FALSE;
+ break;
+ case ID_STATUS_ONLINE:
+ return (iSUpportsSM & PF2_ONLINE)?TRUE:FALSE;
+ break;
+ default:
+ return FALSE;
+ }
+ }
+ else
+ switch (status)
+ {
+ case ID_STATUS_AWAY:
+ case ID_STATUS_NA:
+ case ID_STATUS_OCCUPIED:
+ case ID_STATUS_DND:
+ case ID_STATUS_FREECHAT:
+ case ID_STATUS_ONTHEPHONE:
+ case ID_STATUS_OUTTOLUNCH:
+ return TRUE;
+ default:
+ return FALSE;
+ }
+}
+
+/*static LRESULT CALLBACK RightClickWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
+ static HANDLE hContact;
+ switch (uMsg) {
+ case UM_PROCESS:
+ CallService(MS_CLIST_MENUPROCESSCOMMAND, wParam, lParam);
+ return 0;
+ case UM_SETCONTACT:
+ hContact = (HANDLE)wParam;
+ return FALSE;
+ case WM_COMMAND:
+ if(CallService(MS_CLIST_MENUPROCESSCOMMAND,
+ MAKEWPARAM(LOWORD(wParam),MPCF_CONTACTMENU),(LPARAM)hContact))
+ break;
+ return FALSE;
+ case WM_MEASUREITEM: //Needed by the contact's context menu
+ return CallService(MS_CLIST_MENUMEASUREITEM,wParam,lParam);
+ case WM_DRAWITEM: //Needed by the contact's context menu
+ return CallService(MS_CLIST_MENUDRAWITEM,wParam,lParam);
+ default:
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);//FALSE;
+ }
+ return DefWindowProc(hWnd, uMsg, wParam, lParam);//FALSE;
+}
+*/
+void installMainMenuItem() {
+ CLISTMENUITEM mi = { 0 };
+
+ hServiceMenu = (HANDLE)CreateServiceFunction("NewStatusNotify/EnableDisableMenuCommand",EnableDisableMenuCommand);
+ mi.cbSize=sizeof(mi);
+ //mi.position=-0x7FFFFFFF;
+ mi.flags = CMIF_TCHAR;
+ if (DBGetContactSettingByte(NULL,ModuleName,"TempDisable",DEFAULT_TEMPDISABLE)==TRUE) //(MyOptions.TempDisable)
+ {
+ mi.ptszName = _T("Enable status ¬ification");
+ mi.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_MAINMENU_OFF)); //IDI_NOPOPUP));
+ }
+ else
+ {
+ mi.ptszName = _T("Disable status ¬ification");
+ mi.hIcon = LoadIcon(hInst,MAKEINTRESOURCE(IDI_MAINMENU_ON)); //IDI_POPUP));
+ }
+ mi.pszService="NewStatusNotify/EnableDisableMenuCommand";
+ //mi.ptszPopupName = NULL; //Translate("PopUps");
+ hEnableDisableMenu = CallService(MS_CLIST_ADDMAINMENUITEM,0,(LPARAM)&mi);
+}
+
+void PlaySpeakerBeep(void)
+{
+ OSVERSIONINFO osvi = { 0 };
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ GetVersionEx(&osvi);
+ if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) //We're on an NT platform
+ Beep(BEEP_HZ_DEFAULT,BEEP_DURATION_DEFAULT); //BEEP_HZ_DEFAULT Hz, BEEP_DURATION_DEFAULT milliseconds
+ else //We aren't on a NT platform.
+ MessageBeep(0xFFFFFFFF); //Standard beep through speaker
+}
+
+static int OpenUserDetails(WPARAM wParam, LPARAM lParam)
+{
+ POINT pMouseCoord = {0};
+ if ((GetCursorPos(&pMouseCoord)) && (WindowFromPoint (pMouseCoord)==hCListClone) )
+ {//ugly hack to check if dblclick was on our CList Clone. hClistClone is assigned in appropriate Options Proc
+ //Now that we've changed the "LastTab" info, we can show the dialog.
+ DBWriteContactSettingTString(NULL,"UserInfo","LastTab", TranslateT(NAME_OF_UIPAGE));
+ {//support of stupid settings form of UserInfoEx plugin
+ TCHAR tBrackets[MAX_PATH] = _T("{");
+ _tcscat(tBrackets, TranslateT(NAME_OF_UIPAGE));
+ _tcscat(tBrackets, _T("}"));
+ DBWriteContactSettingTString(NULL,"UserInfoEx","LastItem", tBrackets);
+ }
+ CallService(MS_USERINFO_SHOWDIALOG, (WPARAM)(HANDLE)wParam, 0);
+ return 1; //return nonzero means we don't need to show dialog window
+ }
+ else
+ return 0; // default handler (open dialog window)
+}
|