/* =============================================================================== 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, GSMDF_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("") : 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,GSMDF_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(""), 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(""), 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) }