From 0c3d3e587e699d06352f269d887d749a8542559a Mon Sep 17 00:00:00 2001 From: sje Date: Wed, 1 Nov 2006 14:58:39 +0000 Subject: git-svn-id: https://server.scottellis.com.au/svn/mim_plugs@26 4f64403b-2f21-0410-a795-97e2b3489a10 --- yapp/YAPP.mdsp | 109 +++++++ yapp/common.h | 69 ++++ yapp/docs/licence_YAPP.txt | 6 + yapp/docs/m_notify.h | 177 +++++++++++ yapp/docs/m_popup.h | 339 ++++++++++++++++++++ yapp/docs/m_popup2.h | 29 ++ yapp/message_pump.cpp | 160 ++++++++++ yapp/message_pump.h | 22 ++ yapp/notify.h | 7 + yapp/notify_imp.cpp | 125 ++++++++ yapp/options.cpp | 394 +++++++++++++++++++++++ yapp/options.h | 36 +++ yapp/popup_history.cpp | 393 +++++++++++++++++++++++ yapp/popup_history.h | 59 ++++ yapp/popups2.cpp | 238 ++++++++++++++ yapp/popups2.h | 12 + yapp/popups2.rc | 170 ++++++++++ yapp/popups2.sln | 19 ++ yapp/popups2.vcproj | 377 ++++++++++++++++++++++ yapp/popwin.cpp | 760 +++++++++++++++++++++++++++++++++++++++++++++ yapp/popwin.h | 27 ++ yapp/resource.h | 68 ++++ yapp/resource.rc | 6 + yapp/services.cpp | 363 ++++++++++++++++++++++ yapp/services.h | 25 ++ yapp/version.h | 23 ++ yapp/version.rc | 34 ++ 27 files changed, 4047 insertions(+) create mode 100644 yapp/YAPP.mdsp create mode 100644 yapp/common.h create mode 100644 yapp/docs/licence_YAPP.txt create mode 100644 yapp/docs/m_notify.h create mode 100644 yapp/docs/m_popup.h create mode 100644 yapp/docs/m_popup2.h create mode 100644 yapp/message_pump.cpp create mode 100644 yapp/message_pump.h create mode 100644 yapp/notify.h create mode 100644 yapp/notify_imp.cpp create mode 100644 yapp/options.cpp create mode 100644 yapp/options.h create mode 100644 yapp/popup_history.cpp create mode 100644 yapp/popup_history.h create mode 100644 yapp/popups2.cpp create mode 100644 yapp/popups2.h create mode 100644 yapp/popups2.rc create mode 100644 yapp/popups2.sln create mode 100644 yapp/popups2.vcproj create mode 100644 yapp/popwin.cpp create mode 100644 yapp/popwin.h create mode 100644 yapp/resource.h create mode 100644 yapp/resource.rc create mode 100644 yapp/services.cpp create mode 100644 yapp/services.h create mode 100644 yapp/version.h create mode 100644 yapp/version.rc diff --git a/yapp/YAPP.mdsp b/yapp/YAPP.mdsp new file mode 100644 index 0000000..4254ce9 --- /dev/null +++ b/yapp/YAPP.mdsp @@ -0,0 +1,109 @@ +[Project] +name=YAPP +type=2 +defaultConfig=0 + + +[Debug] +// compiler +workingDirectory= +arguments= +intermediateFilesDirectory=Debug +outputFilesDirectory=Debug +compilerPreprocessor=_UNICODE,UNICODE,POPUPS2_EXPORTS +extraCompilerOptions= +compilerIncludeDirectory=..\..\include +noWarning=0 +defaultWarning=0 +allWarning=1 +extraWarning=0 +isoWarning=0 +warningsAsErrors=0 +debugType=1 +debugLevel=2 +exceptionEnabled=1 +runtimeTypeEnabled=1 +optimizeLevel=0 + +// linker +libraryPath= +outputFilename=Debug\yapp.dll +libraries=unicows,gdi32,comctl32 +extraLinkerOptions= +ignoreStartupFile=0 +ignoreDefaultLibs=0 +stripExecutableFile=0 + +// archive +extraArchiveOptions= + +//resource +resourcePreprocessor= +resourceIncludeDirectory= +extraResourceOptions= + +[Release] +// compiler +workingDirectory= +arguments= +intermediateFilesDirectory=Release +outputFilesDirectory=Release +compilerPreprocessor=_UNICODE,UNICODE,POPUPS2_EXPORTS +extraCompilerOptions= +compilerIncludeDirectory=..\..\include +noWarning=0 +defaultWarning=0 +allWarning=1 +extraWarning=0 +isoWarning=0 +warningAsErrors=0 +debugType=0 +debugLevel=1 +exceptionEnabled=1 +runtimeTypeEnabled=1 +optimizeLevel=2 + +// linker +libraryPath= +outputFilename=Release\yapp.dll +libraries=unicows,gdi32,comctl32 +extraLinkerOptions= +ignoreStartupFile=0 +ignoreDefaultLibs=0 +stripExecutableFile=1 + +// archive +extraArchiveOptions= + +//resource +resourcePreprocessor= +resourceIncludeDirectory= +extraResourceOptions= + +[Source] +1=message_pump.cpp +2=notify_imp.cpp +3=options.cpp +4=popups2.cpp +5=popwin.cpp +6=services.cpp +7=popup_history.cpp +[Header] +1=message_pump.h +2=notify.h +3=options.h +4=popups2.h +5=popwin.h +6=resource.h +7=services.h +8=version.h +9=popup_history.h +10=common.h +[Resource] +1=resource.rc +[Other] +1=popups2.rc +2=version.rc +[History] +popwin.cpp,15185 +version.h,175 diff --git a/yapp/common.h b/yapp/common.h new file mode 100644 index 0000000..967aef3 --- /dev/null +++ b/yapp/common.h @@ -0,0 +1,69 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +// Modify the following defines if you have to target a platform prior to the ones specified below. +// Refer to MSDN for the latest info on corresponding values for different platforms. +#ifndef WINVER // Allow use of features specific to Windows XP or later. +#define WINVER 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINNT // Allow use of features specific to Windows XP or later. +#define _WIN32_WINNT 0x0501 // Change this to the appropriate value to target other versions of Windows. +#endif + +#ifndef _WIN32_WINDOWS // Allow use of features specific to Windows 98 or later. +#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. +#endif + +#ifndef _WIN32_IE // Allow use of features specific to IE 6.0 or later. +#define _WIN32_IE 0x0600 // Change this to the appropriate value to target other versions of IE. +#endif + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers +// Windows Header Files: +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +#define MODULE "YAPP" + +extern HMODULE hInst; +extern PLUGINLINK *pluginLink; +extern HANDLE mainThread; + +extern HFONT hFontFirstLine, hFontSecondLine, hFontTime; +extern COLORREF colFirstLine, colSecondLine, colBg, colTime, colBorder, colSidebar; + +extern MNOTIFYLINK *notifyLink; + +extern int code_page; + +// work around a bug in neweventnotify, possibly httpserver +// ignore the address passed to the 'get plugin data' service +extern bool ignore_gpd_passed_addy; diff --git a/yapp/docs/licence_YAPP.txt b/yapp/docs/licence_YAPP.txt new file mode 100644 index 0000000..a8cbd75 --- /dev/null +++ b/yapp/docs/licence_YAPP.txt @@ -0,0 +1,6 @@ +The YAPP plugin for Miranda-IM is Copyright (c) 2006 Scott Ellis (mail@scottellis.com.au) + +http://www.scottellis.com.au + +It is released under the General Public Licence, available here: +http://www.gnu.org/copyleft/gpl.html \ No newline at end of file diff --git a/yapp/docs/m_notify.h b/yapp/docs/m_notify.h new file mode 100644 index 0000000..f9bce96 --- /dev/null +++ b/yapp/docs/m_notify.h @@ -0,0 +1,177 @@ +#ifndef __m_notify_h__ +#define __m_notify_h__ + +/*** Miranda Notify Dispatcher ************************************************\ +Notify Dispatcher provides common interface to different notification plugins +like osd, popup, ticker etc. +\******************************************************************************/ + +/* Options UI event and service. The same as for miranda options */ +#define ME_NOTIFY_OPT_INITIALISE "Notify/Opt/Initialise" +#define MS_NOTIFY_OPT_ADDPAGE "Notify/Opt/AddPage" + +typedef struct tagMNOTIFYACTIONINFO { + HICON icon; + char name[MAXMODULELABELLENGTH]; + char service[MAXMODULELABELLENGTH]; + DWORD cookie; +} MNOTIFYACTIONINFO; + +// Just like miranda pluginLink... This should work faster then services, +// we need some reactivity in notifications. +typedef struct tagMNNOTIFYLINK +{ + /* Create a new notification type */ + HANDLE (* Register)(const char *name, HICON icon); + + // Create a new notification object + HANDLE (* Create)(HANDLE type); + + // Check is handle is a valid notification object + int (* IsValid)(HANDLE notify); + + // Set/get information about object, or type defaults + int (* Set)(HANDLE notifyORtype, const char *name, DBVARIANT val); + int (* Get)(HANDLE notifyORtype, const char *name, DBVARIANT *val); + + // Set/get actions + int (* AddAction)(HANDLE notifyORtype, HICON icon, const char *name, const char *service, DWORD cookie); + int (* GetActions)(HANDLE notifyORtype, MNOTIFYACTIONINFO *actions); + + // Increment/decrement refer count of notification object. Unreferred objects are destroyed + int (* AddRef)(HANDLE notify); + int (* Release)(HANDLE notify); + + // Notify user + void (* Show)(HANDLE notify); + void (* Update)(HANDLE notify); + void (* Remove)(HANDLE notify); +} MNOTIFYLINK; + +// Get the MNOTIFYLINK struct +// result = (LRESULT)(MNOTIFYLINK*)notifyLink +#define MS_NOTIFY_GETLINK "Notify/GetLink" + +// Hook this to process corresponding actions +#define ME_NOTIFY_SHOW "Notify/Show" +#define ME_NOTIFY_UPDATE "Notify/Update" +#define ME_NOTIFY_REMOVE "Notify/Remove" + +#if !defined(MNOTIFY_NOEXTERN) + extern + #ifdef __cpluplus + "C" + #endif + MNOTIFYLINK *notifyLink; +#endif + +#if !defined(MNOTIFY_NOHELPERS) && !defined(MNOTIFY_NOEXTERN) + #define MNotifyRegister(a,b) (notifyLink?notifyLink->Register((a),(b)):0) + #define MNotifyCreate(a) (notifyLink?notifyLink->Create((a)):0) + #define MNotifyIsValid(a) (notifyLink?notifyLink->IsValid((a)):0) + #define MNotifySet(a,b,c) (notifyLink?notifyLink->Set((a),(b),(c)):0) + #define MNotifyGet(a,b,c) (notifyLink?notifyLink->Get((a),(b),(c)):0) + #define MNotifyAddAction(a,b,c) (notifyLink?notifyLink->AddAction((a),(b),(c)):0) + #define MNotifyGetActions(a,b) (notifyLink?notifyLink->GetActions((a),(b)):0) + #define MNotifyGet(a,b,c) (notifyLink?notifyLink->Get((a),(b),(c)):0) + #define MNotifyAddRef(a) (notifyLink?notifyLink->AddRef((a)):0) + #define MNotifyRelease(a) (notifyLink?notifyLink->Release((a)):0) + #define MNotifyShow(a) (notifyLink?notifyLink->Show(a):0) + #define MNotifyUpdate(a) (notifyLink?notifyLink->Update(a):0) + #define MNotifyRemove(a) (notifyLink?notifyLink->Remove(a):0) + + static void __inline MNotifyGetLink() + { + notifyLink = ServiceExists(MS_NOTIFY_GETLINK) ? (MNOTIFYLINK *)CallService(MS_NOTIFY_GETLINK,0,0) : 0; + } + + // get helpers + static __inline BYTE MNotifyGetByte(HANDLE notifyORtype, const char *name, BYTE defValue) + { + DBVARIANT dbv; + MNotifyGet(notifyORtype, name, &dbv); + if (dbv.type != DBVT_BYTE) return defValue; + return dbv.bVal; + } + static __inline WORD MNotifyGetWord(HANDLE notifyORtype, const char *name, WORD defValue) + { + DBVARIANT dbv; + MNotifyGet(notifyORtype, name, &dbv); + if (dbv.type != DBVT_WORD) return defValue; + return dbv.wVal; + } + static __inline DWORD MNotifyGetDWord(HANDLE notifyORtype, const char *name, DWORD defValue) + { + DBVARIANT dbv; + MNotifyGet(notifyORtype, name, &dbv); + if (dbv.type != DBVT_DWORD) return defValue; + return dbv.dVal; + } + static __inline const char *MNotifyGetString(HANDLE notifyORtype, const char *name, const char *defValue) + { + DBVARIANT dbv; + MNotifyGet(notifyORtype, name, &dbv); + if (dbv.type != DBVT_ASCIIZ) return defValue; + return dbv.pszVal; + } + static __inline const WCHAR *MNotifyGetWString(HANDLE notifyORtype, const char *name, const WCHAR *defValue) + { + DBVARIANT dbv; + MNotifyGet(notifyORtype, name, &dbv); + if (dbv.type != DBVT_WCHAR) return defValue; + return dbv.pwszVal; + } + + // set helpers + static __inline void MNotifySetByte(HANDLE notifyORtype, const char *name, BYTE value) + { + DBVARIANT dbv; + dbv.type = DBVT_BYTE; + dbv.bVal = value; + MNotifySet(notifyORtype, name, dbv); + } + static __inline void MNotifySetWord(HANDLE notifyORtype, const char *name, WORD value) + { + DBVARIANT dbv; + dbv.type = DBVT_WORD; + dbv.wVal = value; + MNotifySet(notifyORtype, name, dbv); + } + static __inline void MNotifySetDWord(HANDLE notifyORtype, const char *name, DWORD value) + { + DBVARIANT dbv; + dbv.type = DBVT_DWORD; + dbv.dVal = value; + MNotifySet(notifyORtype, name, dbv); + } + static __inline void MNotifySetString(HANDLE notifyORtype, const char *name, const char *value) + { + DBVARIANT dbv; + dbv.type = DBVT_ASCIIZ; + dbv.pszVal = (char *)value; + MNotifySet(notifyORtype, name, dbv); + } + static __inline void MNotifySetWString(HANDLE notifyORtype, const char *name, const WCHAR *value) + { + DBVARIANT dbv; + dbv.type = DBVT_WCHAR; + dbv.pwszVal = (WCHAR *)value; + MNotifySet(notifyORtype, name, dbv); + } +#endif + +// Common options for Get/Set actions +#define NFOPT_TYPENAME "General/TypeName" +#define NFOPT_ICON "General/Icon" +#define NFOPT_CONTACT "General/Contact" +#define NFOPT_EVENT "General/Event" +#define NFOPT_TEXT "General/Text" +#define NFOPT_TEXTW "General/TextW" +#define NFOPT_TITLE "General/Title" +#define NFOPT_TITLEW "General/TitleW" +#define NFOPT_BACKCOLOR "General/BackColor" +#define NFOPT_TEXTCOLOR "General/TextColor" +#define NFOPT_TIMEOUT "General/Timeout" +//#define NFOPT_ONDESTROY "General/OnDestroy" + +#endif // __m_notify_h__ diff --git a/yapp/docs/m_popup.h b/yapp/docs/m_popup.h new file mode 100644 index 0000000..f0a92a4 --- /dev/null +++ b/yapp/docs/m_popup.h @@ -0,0 +1,339 @@ +/* +=============================================================================== + PopUp plugin +Plugin Name: PopUp +Plugin authors: Luca Santarelli aka hrk (hrk@users.sourceforge.net) + Victor Pavlychko aka zazoo (nullbie@gmail.com) +=============================================================================== +The purpose of this plugin is to give developers a common "platform/interface" +to show PopUps. It is born from the source code of NewStatusNotify, another +plugin I've made. + +Remember that users *must* have this plugin enabled, or they won't get any +popup. Write this in the requirements, do whatever you wish ;-)... but tell +them! +=============================================================================== +*/ + +#ifndef __m_popup_h__ +#define __m_popup_h__ + +/* +NOTE! Since Popup 1.0.1.2 there is a main meun group called "PopUps" where I +have put a "Enable/Disable" item. You can add your own "enable/disable" items +by adding these lines before you call MS_CLIST_ADDMAINMENUITEM: +mi.pszPopUpName = Translate("PopUps"); +mi.position = 0; //You don't need it and it's better if you put it to zero. +*/ + +#define MAX_CONTACTNAME 2048 +#define MAX_SECONDLINE 2048 + +// This is the basic data you'll need to fill and pass to the service function. +typedef struct +{ + HANDLE lchContact; // Handle to the contact, can be NULL (main contact). + HICON lchIcon; // Handle to a icon to be shown. Cannot be NULL. + union + { + char lptzContactName[MAX_CONTACTNAME]; // This is the contact name or the first line in the plugin. Cannot be NULL. + char lpzContactName[MAX_CONTACTNAME]; + }; + union + { + char lptzText[MAX_SECONDLINE]; // This is the second line text. Users can choose to hide it. Cannot be NULL. + char lpzText[MAX_SECONDLINE]; + }; + COLORREF colorBack; // COLORREF to be used for the background. Can be NULL, default will be used. + COLORREF colorText; // COLORREF to be used for the text. Can be NULL, default will be used. + WNDPROC PluginWindowProc; // Read below. Can be NULL; default will be used. + void * PluginData; // Read below. Can be NULL. +} POPUPDATA, * LPPOPUPDATA; + +// Extended popup data +typedef struct +{ + HANDLE lchContact; + HICON lchIcon; + union + { + char lptzContactName[MAX_CONTACTNAME]; + char lpzContactName[MAX_CONTACTNAME]; + }; + union + { + char lptzText[MAX_SECONDLINE]; + char lpzText[MAX_SECONDLINE]; + }; + COLORREF colorBack; + COLORREF colorText; + WNDPROC PluginWindowProc; + void * PluginData; + int iSeconds; // Custom delay time in seconds. -1 means "forever", 0 means "default time". + char cZero[16]; // Some unused bytes which may come useful in the future. +} POPUPDATAEX, *LPPOPUPDATAEX; + +// Unicode version of POPUPDATAEX +typedef struct +{ + HANDLE lchContact; + HICON lchIcon; + union + { + WCHAR lptzContactName[MAX_CONTACTNAME]; + WCHAR lpwzContactName[MAX_CONTACTNAME]; + }; + union + { + WCHAR lptzText[MAX_CONTACTNAME]; + WCHAR lpwzText[MAX_CONTACTNAME]; + }; + COLORREF colorBack; + COLORREF colorText; + WNDPROC PluginWindowProc; + void * PluginData; + int iSeconds; + char cZero[16]; +} POPUPDATAW, *LPPOPUPDATAW; + +#if defined(_UNICODE) || defined(UNICODE) + typedef POPUPDATAW POPUPDATAT; + typedef LPPOPUPDATAW LPPOPUPDATAT; +#else + typedef POPUPDATAEX POPUPDATAT; + typedef LPPOPUPDATAEX LPPOPUPDATAT; +#endif + +/* PopUp/AddPopup +Creates, adds and shows a popup, given a (valid) POPUPDATA structure pointer. + +wParam = (WPARAM)(*POPUPDATA)PopUpDataAddress +lParam = 0 + +Returns: > 0 on success, 0 if creation went bad, -1 if the PopUpData contained unacceptable values. +NOTE: it returns -1 if the PopUpData was not valid, if there were already too many popups, if the module was disabled. +Otherwise, it can return anything else... + +Popup Plus 2.0.4.0+ +You may pass additional creation flags via lParam: + APF_RETURN_HWND ....... function returns handle to newly created popup window (however this calls are a bit slower) + APF_CUSTOM_POPUP ...... new popup is created in hidden state and doesn't obey to popup queue rules. + you may control it via UM_* messages and custom window procedure +*/ +#define APF_RETURN_HWND 0x1 +#define APF_CUSTOM_POPUP 0x2 + +#define MS_POPUP_ADDPOPUP "PopUp/AddPopUp" +static int __inline PUAddPopUp(POPUPDATA* ppdp) { + return CallService(MS_POPUP_ADDPOPUP, (WPARAM)ppdp,0); +} + +#define MS_POPUP_ADDPOPUPEX "PopUp/AddPopUpEx" +static int __inline PUAddPopUpEx(POPUPDATAEX* ppdp) { + return CallService(MS_POPUP_ADDPOPUPEX, (WPARAM)ppdp,0); +} + +#define MS_POPUP_ADDPOPUPW "PopUp/AddPopUpW" +static int __inline PUAddPopUpW(POPUPDATAW* ppdp) { + return CallService(MS_POPUP_ADDPOPUPW, (WPARAM)ppdp,0); +} + +#if defined(_UNICODE) || defined(UNICODE) + #define MS_POPUP_ADDPOPUPT MS_POPUP_ADDPOPUPW + #define PUAddPopUpT PUAddPopUpW +#else + #define MS_POPUP_ADDPOPUPT MS_POPUP_ADDPOPUPEX + #define PUAddPopUpT PUAddPopUpEx +#endif + + +/* PopUp/GetContact +Returns the handle to the contact associated to the specified PopUpWindow. + +wParam = (WPARAM)(HWND)hPopUpWindow +lParam = 0; + +Returns: the HANDLE of the contact. Can return NULL, meaning it's the main contact. -1 means failure. +*/ +#define MS_POPUP_GETCONTACT "PopUp/GetContact" +static HANDLE __inline PUGetContact(HWND hPopUpWindow) { + return (HANDLE)CallService(MS_POPUP_GETCONTACT, (WPARAM)hPopUpWindow,0); +} + +/* PopUp/GetPluginData +Returns custom plugin date associated with popup + +wParam = (WPARAM)(HWND)hPopUpWindow +lParam = (LPARAM)(PLUGINDATA*)PluginDataAddress; + +Returns: the address of the PLUGINDATA structure. Can return NULL, meaning nothing was given. -1 means failure. + +IMPORTANT NOTE: it doesn't seem to work if you do: +CallService(..., (LPARAM)aPointerToAStruct); +and then use that struct. +Do this, instead: +aPointerToStruct = CallService(..., (LPARAM)aPointerToAStruct); +and it will work. Just look at the example I've written above (PopUpDlgProc). + +*/ +#define MS_POPUP_GETPLUGINDATA "PopUp/GetPluginData" +static void __inline * PUGetPluginData(HWND hPopUpWindow) { + long * uselessPointer = NULL; + return (void*)CallService(MS_POPUP_GETPLUGINDATA,(WPARAM)hPopUpWindow,(LPARAM)uselessPointer); +} + +/* PopUp/IsSecondLineShown +Checks if second line is enable + +wParam = 0 +lParam = 0 + +Returns: 0 if the user has chosen not to have the second line, 1 if he choose to have the second line. +*/ +#define MS_POPUP_ISSECONDLINESHOWN "PopUp/IsSecondLineShown" +static BOOL __inline PUIsSecondLineShown() { + return (BOOL)CallService(MS_POPUP_ISSECONDLINESHOWN,0,0); +} + +/* PopUp/Query + +Requests an action or an answer from PopUp module. + +wParam = (WPARAM)wpQuery + +returns 0 on success, -1 on error, 1 on stupid calls ;-) +*/ + +#define PUQS_ENABLEPOPUPS 1 // returns 0 if state was changed, 1 if state wasn't changed +#define PUQS_DISABLEPOPUPS 2 // " " +#define PUQS_GETSTATUS 3 //Returns 1 (TRUE) if popups are enabled, 0 (FALSE) if popups are disabled. + +#define MS_POPUP_QUERY "PopUp/Query" + +/* UM_FREEPLUGINDATA +Process this message if you have allocated your own memory. (i.e.: POPUPDATA.PluginData != NULL) + +wParam = 0 +lParam = 0 +*/ +#define UM_FREEPLUGINDATA (WM_USER + 0x0200) + +/* UM_DESTROYPOPUP +Send this message when you want to destroy the popup, or use the function below. + +wParam = 0 +lParam = 0 +*/ +#define UM_DESTROYPOPUP (WM_USER + 0x0201) +static int __inline PUDeletePopUp(HWND hWndPopUp) { + return (int)SendMessage(hWndPopUp, UM_DESTROYPOPUP,0,0); +} + +/* UM_INITPOPUP +This message is sent to the PopUp when its creation has been finished, so POPUPDATA (and thus your PluginData) is reachable. +Catch it if you needed to catch WM_CREATE or WM_INITDIALOG, which you'll never ever get in your entire popup-life. +Return value: if you process this message, return 0. If you don't process it, return 0. Do whatever you like ;-) + +wParam = (WPARAM)(HWND)hPopUpWindow (this is useless, you get message inside your popup window) +lParam = 0 +*/ +#define UM_INITPOPUP (WM_USER + 0x0202) + +/* PopUp/Changetext +Changes the text displayed in the second line of the popup. + +wParam = (WPARAM)(HWND)hPopUpWindow +lParam = (LPARAM)(char*)lpzNewText + +returns: > 0 for success, -1 for failure, 0 if the failure is due to second line not being shown. (but you could call +PUIsSecondLineShown() before changing the text...) +*/ +#define MS_POPUP_CHANGETEXT "PopUp/Changetext" +static int __inline PUChangeText(HWND hWndPopUp, LPCTSTR lpzNewText) { + return (int)CallService(MS_POPUP_CHANGETEXT, (WPARAM)hWndPopUp, (LPARAM)lpzNewText); +} + +#define MS_POPUP_CHANGETEXTW "PopUp/ChangetextW" +static int __inline PUChangeTextW(HWND hWndPopUp, LPCWSTR lpwzNewText) { + return (int)CallService(MS_POPUP_CHANGETEXTW, (WPARAM)hWndPopUp, (LPARAM)lpwzNewText); +} + +#if defined(_UNICODE) || defined(UNICODE) + #define MS_POPUP_CHANGETEXTT MS_POPUP_CHANGETEXTW + #define PUChangeTextT PUChangeTextW +#else + #define MS_POPUP_CHANGETEXTT MS_POPUP_CHANGETEXT + #define PUChangeTextT PUChangeText +#endif + +/* PopUp/Change +Changes the entire popup + +wParam = (WPARAM)(HWND)hPopUpWindow +lParam = (LPARAM)(POPUPDATAEX*)newData +*/ +#define MS_POPUP_CHANGE "PopUp/Change" +static int __inline PUChange(HWND hWndPopUp, POPUPDATAEX *newData) { + return (int)CallService(MS_POPUP_CHANGE, (WPARAM)hWndPopUp, (LPARAM)newData); +} + +#define MS_POPUP_CHANGEW "PopUp/ChangeW" +static int __inline PUChangeW(HWND hWndPopUp, POPUPDATAW *newData) { + return (int)CallService(MS_POPUP_CHANGE, (WPARAM)hWndPopUp, (LPARAM)newData); +} + +#if defined(_UNICODE) || defined(UNICODE) + #define MS_POPUP_CHANGET MS_POPUP_CHANGEW + #define PUChangeT PUChangeW +#else + #define MS_POPUP_CHANGET MS_POPUP_CHANGE + #define PUChangeT PUChange +#endif + +/* UM_CHANGEPOPUP +This message is triggered by Change/ChangeText services. You also may post it directly :) + +wParam = Modification type +lParam = value of type defined by wParam +*/ + +#define CPT_TEXT 1 // lParam = (char *)text +#define CPT_TEXTW 2 // lParam = (WCHAR *)text +#define CPT_TITLE 3 // lParam = (char *)title +#define CPT_TITLEW 4 // lParam = (WCHAR *)title +#define CPT_DATA 5 // lParam = (POPUPDATA *)data +#define CPT_DATAEX 6 // lParam = (POPUPDATAEX *)data +#define CPT_DATAW 7 // lParam = (POPUPDATAW *)data + +#define UM_CHANGEPOPUP (WM_USER + 0x0203) + +#if defined(_UNICODE) || defined(UNICODE) + #define CPT_TEXTT CPT_TEXTW + #define CPT_TITLET CPT_TITLEW + #define CPT_DATAT CPT_DATAW +#else + #define CPT_TEXTT CPT_TEXT + #define CPT_TITLET CPT_TITLE + #define CPT_DATAT CPT_DATA +#endif + +/* PopUp/ShowMessage +This is mainly for developers. +Shows a warning message in a PopUp. It's useful if you need a "MessageBox" like function, but you don't want a modal +window (which will interfere with a DialogProcedure. MessageBox steals focus and control, this one not. + +wParam = (char *)lpzMessage +lParam = 0; + +Returns: 0 if the popup was shown, -1 in case of failure. +*/ +#define SM_WARNING 0x01 //Triangle icon. +#define SM_NOTIFY 0x02 //Exclamation mark icon. +#define MS_POPUP_SHOWMESSAGE "PopUp/ShowMessage" + +static int __inline PUShowMessage(char *lpzText, BYTE kind) { + return (int)CallService(MS_POPUP_SHOWMESSAGE, (WPARAM)lpzText,(LPARAM)kind); +} + +#endif // __m_popup_h__ + diff --git a/yapp/docs/m_popup2.h b/yapp/docs/m_popup2.h new file mode 100644 index 0000000..b23aecf --- /dev/null +++ b/yapp/docs/m_popup2.h @@ -0,0 +1,29 @@ +#ifndef __m_popup2_h__ +#define __m_popup2_h__ + +#define NFOPT_POPUP2_BACKCOLOR "Popup2/BackColor" +#define NFOPT_POPUP2_TEXTCOLOR "Popup2/TextColor" +#define NFOPT_POPUP2_TIMEOUT "Popup2/Timeout" +#define NFOPT_POPUP2_LCLICKSVC "Popup2/LClickSvc" +#define NFOPT_POPUP2_LCLICKCOOKIE "Popup2/LClickCookie" +#define NFOPT_POPUP2_RCLICKSVC "Popup2/RClickSvc" +#define NFOPT_POPUP2_RCLICKCOOKIE "Popup2/RClickCookie" +#define NFOPT_POPUP2_STATUSMODE "Popup2/StatusMode" +#define NFOPT_POPUP2_PLUGINDATA "Popup2/PluginData" +#define NFOPT_POPUP2_WNDPROC "Popup2/WndProc" + +#define NFOPT_POPUP2_BACKCOLOR_S "Popup2/BackColor/Save" +#define NFOPT_POPUP2_TEXTCOLOR_S "Popup2/TextColor/Save" +#define NFOPT_POPUP2_TIMEOUT_S "Popup2/Timeout/Save" + +#define MS_POPUP2_SHOW "Popup2/Show" +#define MS_POPUP2_UPDATE "Popup2/Update" +#define MS_POPUP2_REMOVE "Popup2/Remove" + +#ifndef POPUP2_NOHELPERS + #define MPopup2Show(a) (CallService(MS_POPUP2_SHOW, 0, (LPARAM)(a))) + #define MPopup2Update(a) (CallService(MS_POPUP2_UPDATE, 0, (LPARAM)(a))) + #define MPopup2Remove(a) (CallService(MS_POPUP2_REMOVE, 0, (LPARAM)(a))) +#endif + +#endif // __m_popup2_h__ diff --git a/yapp/message_pump.cpp b/yapp/message_pump.cpp new file mode 100644 index 0000000..f5cedd8 --- /dev/null +++ b/yapp/message_pump.cpp @@ -0,0 +1,160 @@ +#include "common.h" +#include "message_pump.h" +#include "popwin.h" +#include "services.h" +#include "options.h" + +DWORD message_pump_thread_id = 0; +int num_popups = 0; + +HANDLE hMPEvent; + +#define MUM_FINDWINDOW (WM_USER + 0x050) + +#define MAX_POPUPS 100 + +// from popups, popup2 implementation +bool is_full_screen() { + HWND hWnd = GetForegroundWindow(); + + if(!hWnd || !IsWindowVisible(hWnd) || IsIconic(hWnd) || !IsZoomed(hWnd)) return false; + + RECT rc; + if(!GetWindowRect(hWnd,&rc)) return false; + + if (rc.right - rc.left < GetSystemMetrics(SM_CXSCREEN) || rc.bottom - rc.top < GetSystemMetrics(SM_CYSCREEN)) return false; + + // at this point, it will return true for full-screen and, unfortunately, maximized windows - so we need to check for maximized + + LONG style = GetWindowLong(hWnd, GWL_STYLE); + if(style & WS_MAXIMIZEBOX) return false; + + return true; +} + +bool is_workstation_locked() +{ + bool rc = false; + HDESK hDesk = OpenDesktop(_T("default"), 0, FALSE, DESKTOP_SWITCHDESKTOP); + if(hDesk != 0) { + HDESK hDeskInput = OpenInputDesktop(0, FALSE, DESKTOP_SWITCHDESKTOP); + if(hDeskInput == 0) { + rc = true; + } else + CloseDesktop(hDeskInput); + + CloseDesktop(hDesk); + } + + return rc; +} + + +DWORD CALLBACK MessagePumpThread(LPVOID param) { + CallService(MS_SYSTEM_THREAD_PUSH, 0, 0); + + InitWindowStack(); + + if(param) SetEvent((HANDLE)param); + + MSG hwndMsg = {0}; + while(GetMessage(&hwndMsg, 0, 0, 0) > 0 && !Miranda_Terminated()) { + if(!IsDialogMessage(hwndMsg.hwnd, &hwndMsg)) { + switch(hwndMsg.message) { + case MUM_CREATEPOPUP: + { + bool enabled = true; + int status = CallService(MS_CLIST_GETSTATUSMODE, 0, 0); + if(status >= ID_STATUS_OFFLINE && status <= ID_STATUS_OUTTOLUNCH && options.disable_status[status - ID_STATUS_OFFLINE]) + enabled = false; + if(options.disable_full_screen && is_full_screen() || is_workstation_locked()) + enabled = false; + if(enabled && num_popups < MAX_POPUPS) { + //HWND hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, POP_WIN_CLASS, _T("Popup"), WS_POPUP, 0, 0, 0, 0, GetDesktopWindow(), 0, hInst, (LPVOID)hwndMsg.lParam); + HWND hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, POP_WIN_CLASS, _T("Popup"), WS_POPUP, 0, 0, 0, 0, 0, 0, hInst, (LPVOID)hwndMsg.lParam); + num_popups++; + if(hwndMsg.wParam) // set notifyer handle + SendMessage(hwnd, PUM_SETNOTIFYH, hwndMsg.wParam, 0); + } else { + free((void *)hwndMsg.lParam); + } + } + break; + + case MUM_DELETEPOPUP: + { + HWND hwnd = (HWND)hwndMsg.lParam; + if(IsWindow(hwnd)) { + DestroyWindow(hwnd); + num_popups--; + } + } + break; + case MUM_NMUPDATE: + { + HANDLE hNotify = (HANDLE)hwndMsg.wParam; + BroadcastMessage(PUM_UPDATENOTIFY, (WPARAM)hNotify, 0); + } + break; + case MUM_NMREMOVE: + { + HANDLE hNotify = (HANDLE)hwndMsg.wParam; + BroadcastMessage(PUM_KILLNOTIFY, (WPARAM)hNotify, 0); + } + break; + default: + { + TranslateMessage(&hwndMsg); + DispatchMessage(&hwndMsg); + // do this here in case the window has gone + if(hwndMsg.message == PUM_CHANGE || hwndMsg.message == PUM_SETTEXT) + free((void *)hwndMsg.lParam); + } + break; + } + } + } + + DeinitWindowStack(); + num_popups = 0; + + //if(param) SetEvent((HANDLE)param); + + CallService(MS_SYSTEM_THREAD_POP, 0, 0); + + return 0; +} + +void PostMPMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + PostThreadMessage(message_pump_thread_id, msg, wParam, lParam); +} + +// given a popup data pointer, and a handle to an event, this function +// will post a message to the message queue which will set the hwnd value +// and then set the event...so create an event, call this function and then wait on the event +// when the event is signalled, the hwnd will be valid +void FindWindow(POPUPDATAW *pd, HANDLE hEvent, HWND *hwnd); + +void InitMessagePump() { + WNDCLASS popup_win_class = {0}; + popup_win_class.style = 0; + popup_win_class.lpfnWndProc = PopupWindowProc; + popup_win_class.hInstance = hInst; + popup_win_class.lpszClassName = POP_WIN_CLASS; + popup_win_class.hCursor = LoadCursor(NULL, IDC_ARROW); + RegisterClass(&popup_win_class); + + InitServices(); + + hMPEvent = CreateEvent(0, TRUE, 0, 0); + CloseHandle(CreateThread(0, 0, MessagePumpThread, hMPEvent, 0, &message_pump_thread_id)); + WaitForSingleObject(hMPEvent, INFINITE); + CloseHandle(hMPEvent); +} + +void DeinitMessagePump() { + + PostMPMessage(WM_QUIT, 0, 0); + + DeinitServices(); +} diff --git a/yapp/message_pump.h b/yapp/message_pump.h new file mode 100644 index 0000000..e57ed55 --- /dev/null +++ b/yapp/message_pump.h @@ -0,0 +1,22 @@ +#ifndef _MESSAGE_PUMP_INC +#define _MESSAGE_PUMP_INC + +extern DWORD message_pump_thread_id; +void PostMPMessage(UINT msg, WPARAM, LPARAM); + +#define MUM_CREATEPOPUP (WM_USER + 0x011) +#define MUM_DELETEPOPUP (WM_USER + 0x012) + +#define MUM_NMUPDATE (WM_USER + 0x013) +#define MUM_NMREMOVE (WM_USER + 0x014) + +// given a popup data pointer, and a handle to an event, this function +// will post a message to the message queue which will set the hwnd value +// and then set the event...so create an event, call this function and then wait on the event +// when the event is signalled, the hwnd will be valid +void FindWindow(POPUPDATAW *pd, HANDLE hEvent, HWND *hwnd); + +void InitMessagePump(); +void DeinitMessagePump(); + +#endif diff --git a/yapp/notify.h b/yapp/notify.h new file mode 100644 index 0000000..7f39eb6 --- /dev/null +++ b/yapp/notify.h @@ -0,0 +1,7 @@ +#ifndef _NOTIFY_IMP_INC +#define _NOTIFY_IMP_INC + +void InitNotify(); +void DeinitNotify(); + +#endif diff --git a/yapp/notify_imp.cpp b/yapp/notify_imp.cpp new file mode 100644 index 0000000..acb9d36 --- /dev/null +++ b/yapp/notify_imp.cpp @@ -0,0 +1,125 @@ +#include "common.h" +#include "resource.h" +#include "notify.h" +#include "message_pump.h" +#include "docs/m_popup2.h" + +HANDLE hhkShow=0, hhkUpdate=0, hhkRemove=0; + +//struct + +int Popup2Show(WPARAM wParam, LPARAM lParam) { + + HANDLE hNotify = (HANDLE)lParam; + + POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW)); + memset(pd_out, 0, sizeof(POPUPDATAW)); + + PostMPMessage(MUM_CREATEPOPUP, (WPARAM)hNotify, (LPARAM)pd_out); + PostMPMessage(MUM_NMUPDATE, (WPARAM)hNotify, (LPARAM)0); + return 0; +} + +int Popup2Update(WPARAM wParam, LPARAM lParam) { + HANDLE hNotify = (HANDLE)lParam; + PostMPMessage(MUM_NMUPDATE, (WPARAM)hNotify, (LPARAM)0); + return 0; +} + +int Popup2Remove(WPARAM wParam, LPARAM lParam) { + HANDLE hNotify = (HANDLE)lParam; + PostMPMessage(MUM_NMREMOVE, (WPARAM)hNotify, (LPARAM)0); + return 0; +} + +int svcPopup2DefaultActions(WPARAM wParam, LPARAM lParam) +{ + //PopupWindow *wnd = (PopupWindow *)MNotifyGetDWord((HANDLE)lParam, "Popup2/data", (DWORD)NULL); + //if (!wnd) return 0; + switch (wParam) + { + case 0: + { // send message + //if (wnd->lchContact) CallServiceSync(MS_MSG_SENDMESSAGE, (WPARAM)wnd->lchContact, 0); + //SendMessage(wnd->lchMain, UM_DESTROYPOPUP, 0, 0); + break; + } + case 1: + { // dismiss popup + //SendMessage(wnd->lchMain, UM_DESTROYPOPUP, 0, 0); + break; + } + } + return 0; +} + +BOOL CALLBACK DlgProcPopUps(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ +/* To change options use MNotifySet*(hNotify, ....) Apply/Cancel is implemented in notify.dll */ + HANDLE hNotify = (HANDLE)GetWindowLong(hwnd, GWL_USERDATA); + switch (msg) + { + case WM_USER+100: + { + // This will be extendet to handle array of handles for multiselect lParam + // will be HANDLE * and wParam wil; store amount of handles passed + hNotify = (HANDLE)lParam; + SetWindowLong(hwnd, GWL_USERDATA, lParam); + return TRUE; + } + + case WM_COMMAND: + // This in different from Miranda options! + SendMessage(GetParent(GetParent(hwnd)), PSM_CHANGED, 0, 0); + break; + } + return FALSE; +} + +int NotifyOptionsInitialize(WPARAM wParam,LPARAM lParam) +{ + OPTIONSDIALOGPAGE odp = {0}; + odp.cbSize = sizeof(odp); + odp.hInstance = hInst; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NOTIFY); + odp.pszTitle = Translate("YAPP Popups"); + odp.flags=ODPF_BOLDGROUPS; + odp.pfnDlgProc = DlgProcPopUps; + CallService(MS_NOTIFY_OPT_ADDPAGE, wParam, (LPARAM)&odp); + return 0; +} + +HANDLE hEventNotifyOptInit, hEventNotifyModulesLoaded; + +int NotifyModulesLoaded(WPARAM wParam,LPARAM lParam) +{ + hEventNotifyOptInit = HookEvent(ME_NOTIFY_OPT_INITIALISE, NotifyOptionsInitialize); + return 0; +} + +HANDLE hServicesNotify[4]; +void InitNotify() { + hhkShow = HookEvent(ME_NOTIFY_SHOW, Popup2Show); + hhkUpdate = HookEvent(ME_NOTIFY_UPDATE, Popup2Update); + hhkRemove = HookEvent(ME_NOTIFY_REMOVE, Popup2Remove); + + hServicesNotify[0] = CreateServiceFunction("Popup2/DefaultActions", svcPopup2DefaultActions); + + hServicesNotify[1] = CreateServiceFunction(MS_POPUP2_SHOW, Popup2Show); + hServicesNotify[2] = CreateServiceFunction(MS_POPUP2_UPDATE, Popup2Update); + hServicesNotify[3] = CreateServiceFunction(MS_POPUP2_REMOVE, Popup2Remove); + + hEventNotifyModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, NotifyModulesLoaded); +} + +void DeinitNotify() { + UnhookEvent(hhkShow); + UnhookEvent(hhkUpdate); + UnhookEvent(hhkRemove); + + UnhookEvent(hEventNotifyOptInit); + UnhookEvent(hEventNotifyModulesLoaded); + + for(int i = 0; i < 4; i++) + DestroyServiceFunction(hServicesNotify[i]); +} diff --git a/yapp/options.cpp b/yapp/options.cpp new file mode 100644 index 0000000..5f3e0d5 --- /dev/null +++ b/yapp/options.cpp @@ -0,0 +1,394 @@ +#include "common.h" +#include "options.h" +#include "resource.h" +#include "popwin.h" +#include + +Options options; + +HICON hPopupIcon = 0; + +void LoadModuleDependentOptions() { + if(ServiceExists(MS_AV_DRAWAVATAR)) + options.av_layout = (PopupAvLayout)DBGetContactSettingByte(0, MODULE, "AVLayout", PAV_RIGHT); + else + options.av_layout = PAV_NONE; + + options.time_layout = (PopupTimeLayout)DBGetContactSettingByte(0, MODULE, "TimeLayout", (ServiceExists(MS_AV_DRAWAVATAR) ? PT_WITHAV : PT_RIGHT)); + if(options.time_layout == PT_WITHAV && !ServiceExists(MS_AV_DRAWAVATAR)) + options.time_layout = PT_RIGHT; +} + +void LoadOptions() { + options.default_timeout = DBGetContactSettingDword(0, MODULE, "DefaultTimeout", 7); + options.win_width = DBGetContactSettingDword(0, MODULE, "WinWidth", 220); + options.win_max_height = DBGetContactSettingDword(0, MODULE, "WinMaxHeight", 400); + options.location = (PopupLocation)DBGetContactSettingByte(0, MODULE, "Location", (BYTE)PL_BOTTOMRIGHT); + options.opacity = DBGetContactSettingByte(0, MODULE, "Opacity", 75); + options.border = (DBGetContactSettingByte(0, MODULE, "Border", 1) == 1); + options.round = (DBGetContactSettingByte(0, MODULE, "RoundCorners", 1) == 1); + options.av_round = (DBGetContactSettingByte(0, MODULE, "AvatarRoundCorners", 1) == 1); + options.animate = (DBGetContactSettingByte(0, MODULE, "Animate", 1) == 1); + options.trans_bg = (DBGetContactSettingByte(0, MODULE, "TransparentBg", 0) == 1); + options.use_mim_monitor = (DBGetContactSettingByte(0, MODULE, "UseMimMonitor", 1) == 1); + options.right_icon = (DBGetContactSettingByte(0, MODULE, "RightIcon", 0) == 1); + options.av_layout = PAV_NONE; // corrected in LoadModuleDependentOptions function above + options.av_size = DBGetContactSettingDword(0, MODULE, "AVSize", 40); //tweety + options.text_indent = DBGetContactSettingDword(0, MODULE, "TextIndent", 22); + options.global_hover = (DBGetContactSettingByte(0, MODULE, "GlobalHover", 1) == 1); + options.time_layout = PT_RIGHT; // corrected in LoadModuleDependentOptions function above + + char buff[128]; + for(int i = 0; i < 10; i++) { + sprintf(buff, "DisableStatus%d", i - 1); // -1 because i forgot offline status earlier! + options.disable_status[i] = (DBGetContactSettingByte(0, MODULE, buff, 0) == 1); + } + + options.disable_full_screen = (DBGetContactSettingByte(0, MODULE, "DisableFullScreen", 1) == 1); + options.drop_shadow = (DBGetContactSettingByte(0, MODULE, "DropShadow", 0) == 1); + options.sb_width = DBGetContactSettingDword(0, MODULE, "SidebarWidth", 22); + options.padding = DBGetContactSettingDword(0, MODULE, "Padding", 4); + options.av_padding = DBGetContactSettingDword(0, MODULE, "AvatarPadding", 4); +} + +void SaveOptions() { + DBWriteContactSettingDword(0, MODULE, "DefaultTimeout", options.default_timeout); + DBWriteContactSettingDword(0, MODULE, "WinWidth", options.win_width); + DBWriteContactSettingDword(0, MODULE, "WinMaxHeight", options.win_max_height); + DBWriteContactSettingByte(0, MODULE, "Location", (BYTE)options.location); + DBWriteContactSettingByte(0, MODULE, "Opacity", (BYTE)options.opacity); + DBWriteContactSettingByte(0, MODULE, "Border", (options.border ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "RoundCorners", (options.round ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "AvatarRoundCorners", (options.av_round ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "Animate", (options.animate ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "TransparentBg", (options.trans_bg ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "UseMimMonitor", (options.use_mim_monitor ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "RightIcon", (options.right_icon ? 1 : 0)); + if(ServiceExists(MS_AV_DRAWAVATAR)) + DBWriteContactSettingByte(0, MODULE, "AVLayout", (BYTE)options.av_layout); + DBWriteContactSettingDword(0, MODULE, "AVSize", options.av_size); + DBWriteContactSettingDword(0, MODULE, "TextIndent", options.text_indent); + DBWriteContactSettingByte(0, MODULE, "GlobalHover", (options.global_hover ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "TimeLayout", (BYTE)options.time_layout); + + char buff[128]; + for(int i = 0; i < 9; i++) { + sprintf(buff, "DisableStatus%d", i - 1); + DBWriteContactSettingByte(0, MODULE, buff, options.disable_status[i] ? 1 : 0); + } + DBWriteContactSettingByte(0, MODULE, "DisableFullScreen", (options.disable_full_screen ? 1 : 0)); + DBWriteContactSettingByte(0, MODULE, "DropShadow", (options.drop_shadow ? 1 : 0)); + DBWriteContactSettingDword(0, MODULE, "SidebarWidth", options.sb_width); + DBWriteContactSettingDword(0, MODULE, "Padding", options.padding); + DBWriteContactSettingDword(0, MODULE, "AvatarPadding", options.av_padding); +} + +void ShowExamplePopups() { + POPUPDATAW pd = {0}; + pd.lchIcon = hPopupIcon; + + wcscpy(pd.lpwzContactName, TranslateT("Example")); + wcscpy(pd.lpwzText, TranslateT("The quick brown fox jumped over the lazy dog.")); + CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0); + wcscpy(pd.lpwzContactName, TranslateT("Example With a Long Title")); + wcscpy(pd.lpwzText, TranslateT("The quick brown fox jumped over the lazy dog.")); + CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0); + wcscpy(pd.lpwzContactName, TranslateT("Example")); + wcscpy(pd.lpwzText, TranslateT("Thequickbrownfoxjumpedoverthelazydog.")); + CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0); + + HANDLE hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDFIRST, 0, 0); + while(hContact) { + if(options.av_layout != PAV_NONE && ServiceExists(MS_AV_DRAWAVATAR)) { + AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)hContact, 0); + if(ace && ace->dwFlags & AVS_BITMAP_VALID) { + pd.lchContact = hContact; + wcscpy(pd.lpwzText, TranslateT("An avatar.")); + CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0); + break; + } + } + + hContact = (HANDLE)CallService(MS_DB_CONTACT_FINDNEXT, (WPARAM)hContact, 0); + } +} + +static BOOL CALLBACK DlgProcOpts1(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) { + + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + + SendDlgItemMessage(hwndDlg, IDC_CMB_PLACEMENT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Bottom right")); + SendDlgItemMessage(hwndDlg, IDC_CMB_PLACEMENT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Bottom left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_PLACEMENT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Top right")); + SendDlgItemMessage(hwndDlg, IDC_CMB_PLACEMENT, CB_ADDSTRING, 0, (LPARAM)TranslateT("Top left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_PLACEMENT, CB_SETCURSEL, (int)options.location, 0); + + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_ADDSTRING, 0, (LPARAM)TranslateT("Icon on left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_ADDSTRING, 0, (LPARAM)TranslateT("Icon on right")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_SETCURSEL, (options.right_icon ? 1 : 0), 0); + + SendDlgItemMessage(hwndDlg, IDC_CMB_TIME, CB_ADDSTRING, 0, (LPARAM)TranslateT("No time")); + SendDlgItemMessage(hwndDlg, IDC_CMB_TIME, CB_ADDSTRING, 0, (LPARAM)TranslateT("Time on left")); + SendDlgItemMessage(hwndDlg, IDC_CMB_TIME, CB_ADDSTRING, 0, (LPARAM)TranslateT("Time on right")); + if(ServiceExists(MS_AV_DRAWAVATAR)) + SendDlgItemMessage(hwndDlg, IDC_CMB_TIME, CB_ADDSTRING, 0, (LPARAM)TranslateT("Time above avatar")); + SendDlgItemMessage(hwndDlg, IDC_CMB_TIME, CB_SETCURSEL, (int)options.time_layout, 0); + + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_ADDSTRING, 0, (LPARAM)TranslateT("No avatar")); + if(ServiceExists(MS_AV_DRAWAVATAR)) { + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Left avatar")); + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_ADDSTRING, 0, (LPARAM)TranslateT("Right avatar")); + } else { + HWND hw = GetDlgItem(hwndDlg, IDC_CMB_AV); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_SPIN_AVSIZE); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_ED_AVSIZE); + EnableWindow(hw, FALSE); + } + SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_SETCURSEL, (int)options.av_layout, 0); + + CheckDlgButton(hwndDlg, IDC_CHK_GLOBALHOVER, options.global_hover ? TRUE : FALSE); + + { + // initialise and fill listbox + HWND hwndList = GetDlgItem(hwndDlg, IDC_LST_STATUS); + ListView_DeleteAllItems(hwndList); + + SendMessage(hwndList,LVM_SETEXTENDEDLISTVIEWSTYLE, 0,LVS_EX_FULLROWSELECT | LVS_EX_CHECKBOXES); + + LVCOLUMN lvc = {0}; + // Initialize the LVCOLUMN structure. + // The mask specifies that the format, width, text, and + // subitem members of the structure are valid. + lvc.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM; + lvc.fmt = LVCFMT_LEFT; + + lvc.iSubItem = 0; + lvc.pszText = TranslateT("Status"); + lvc.cx = 200; // width of column in pixels + ListView_InsertColumn(hwndList, 0, &lvc); + + + LVITEM lvI = {0}; + + // Some code to create the list-view control. + // Initialize LVITEM members that are common to all + // items. + lvI.mask = LVIF_TEXT; + + char *strptr; + wchar_t buff[256]; + int i = 0; + for(; i < 10; i++) { + strptr = (char *)CallService(MS_CLIST_GETSTATUSMODEDESCRIPTION, (WPARAM)(ID_STATUS_OFFLINE + i), (LPARAM)0); + int cp = CP_ACP; + if(ServiceExists(MS_LANGPACK_GETCODEPAGE)) + cp = (int)CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); + MultiByteToWideChar(cp, 0, strptr, -1, buff, 256); + lvI.pszText = buff; + lvI.iItem = i; + ListView_InsertItem(hwndList, &lvI); + ListView_SetCheckState(hwndList, i, options.disable_status[i]); + } + lvI.pszText = TranslateT("Full-screen app running"); + lvI.iItem = i; + ListView_InsertItem(hwndList, &lvI); + ListView_SetCheckState(hwndList, i, options.disable_full_screen); + } + + SendDlgItemMessage(hwndDlg, IDC_SPIN_TIMEOUT, UDM_SETRANGE, 0, (LPARAM)MAKELONG(360, 1)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_WIDTH, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_MAXHEIGHT, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_TRANS, UDM_SETRANGE, 0, (LPARAM)MAKELONG(100, 1)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_AVSIZE, UDM_SETRANGE, 0, (LPARAM)MAKELONG(100, 16)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_INDENT, UDM_SETRANGE, 0, (LPARAM)MAKELONG(400, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_SBWIDTH, UDM_SETRANGE, 0, (LPARAM)MAKELONG(2048, 0)); + SendDlgItemMessage(hwndDlg, IDC_SPIN_PADDING, UDM_SETRANGE, 0, (LPARAM)MAKELONG(400, 0)); + + if(options.default_timeout == -1) { + CheckDlgButton(hwndDlg, IDC_RAD_NOTIMEOUT, TRUE); + HWND hw = GetDlgItem(hwndDlg, IDC_ED_TIMEOUT); + EnableWindow(hw, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, 7, FALSE); + } else { + CheckDlgButton(hwndDlg, IDC_RAD_TIMEOUT, TRUE); + SetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, options.default_timeout, FALSE); + } + if(options.right_icon) CheckDlgButton(hwndDlg, IDC_RAD_RIGHTICON, TRUE); + else CheckDlgButton(hwndDlg, IDC_RAD_LEFTICON, TRUE); + + if(ServiceExists(MS_AV_DRAWAVATAR)) { + switch(options.av_layout) { + case PAV_NONE: CheckDlgButton(hwndDlg, IDC_RAD_NOAV, TRUE); break; + case PAV_RIGHT: CheckDlgButton(hwndDlg, IDC_RAD_RIGHTAV, TRUE); break; + case PAV_LEFT: CheckDlgButton(hwndDlg, IDC_RAD_LEFTAV, TRUE); break; + } + } else { + CheckDlgButton(hwndDlg, IDC_RAD_NOAV, TRUE); + HWND hw = GetDlgItem(hwndDlg, IDC_RAD_RIGHTAV); + EnableWindow(hw, FALSE); + hw = GetDlgItem(hwndDlg, IDC_RAD_LEFTAV); + EnableWindow(hw, FALSE); + } + + SetDlgItemInt(hwndDlg, IDC_ED_WIDTH, options.win_width, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_MAXHEIGHT, options.win_max_height, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_AVSIZE, options.av_size, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_INDENT, options.text_indent, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_SBWIDTH, options.sb_width, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_PADDING, options.padding, FALSE); + + switch(options.location) { + case PL_BOTTOMRIGHT: CheckDlgButton(hwndDlg, IDC_RAD_BOTTOMRIGHT, TRUE); break; + case PL_BOTTOMLEFT: CheckDlgButton(hwndDlg, IDC_RAD_BOTTOMLEFT, TRUE); break; + case PL_TOPRIGHT: CheckDlgButton(hwndDlg, IDC_RAD_TOPRIGHT, TRUE); break; + case PL_TOPLEFT: CheckDlgButton(hwndDlg, IDC_RAD_TOPLEFT, TRUE); break; + } + + SetDlgItemInt(hwndDlg, IDC_ED_TRANS, options.opacity, FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_BORDER, options.border); + CheckDlgButton(hwndDlg, IDC_CHK_ROUNDCORNERS, options.round); + CheckDlgButton(hwndDlg, IDC_CHK_ROUNDCORNERSAV, options.av_round); + + CheckDlgButton(hwndDlg, IDC_CHK_ANIMATE, options.animate); + CheckDlgButton(hwndDlg, IDC_CHK_TRANSBG, options.trans_bg); + + return FALSE; + case WM_COMMAND: + if ( HIWORD( wParam ) == CBN_SELCHANGE) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } else if ( HIWORD( wParam ) == EN_CHANGE && ( HWND )lParam == GetFocus()) { + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } else if ( HIWORD( wParam ) == BN_CLICKED ) { + if(LOWORD(wParam) == IDC_BTN_PREVIEW) { + ShowExamplePopups(); + } else { + switch( LOWORD( wParam )) { + case IDC_RAD_NOTIMEOUT: + { + HWND hw = GetDlgItem(hwndDlg, IDC_ED_TIMEOUT); + EnableWindow(hw, IsDlgButtonChecked(hwndDlg, IDC_RAD_TIMEOUT)); + } + break; + case IDC_RAD_TIMEOUT: + { + HWND hw = GetDlgItem(hwndDlg, IDC_ED_TIMEOUT); + EnableWindow(hw, IsDlgButtonChecked(hwndDlg, IDC_RAD_TIMEOUT)); + } + break; + } + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + break; + case WM_NOTIFY: + if(IsWindowVisible(hwndDlg) && ((LPNMHDR) lParam)->hwndFrom == GetDlgItem(hwndDlg, IDC_LST_STATUS)) { + switch (((LPNMHDR) lParam)->code) { + + case LVN_ITEMCHANGED: + { + NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam; + if((nmlv->uNewState ^ nmlv->uOldState) & LVIS_STATEIMAGEMASK) { + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0 ); + } + } + break; + } + } else + if (((LPNMHDR)lParam)->code == (unsigned)PSN_APPLY ) { + BOOL trans; + int new_val; + if(IsDlgButtonChecked(hwndDlg, IDC_RAD_NOTIMEOUT)) + options.default_timeout = -1; + else { + new_val = GetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, &trans, FALSE); + if(trans) options.default_timeout = new_val; + } + if(options.default_timeout == 0) { + SetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, options.default_timeout, FALSE); + MessageBox(hwndDlg, TranslateT("You cannot set a default timeout of 0.\nValue has been reset."), TranslateT("Error"), MB_OK | MB_ICONWARNING); + options.default_timeout = 7; // prevent instant timeout + } + + new_val = GetDlgItemInt(hwndDlg, IDC_ED_WIDTH, &trans, FALSE); + if(trans) options.win_width = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_MAXHEIGHT, &trans, FALSE); + if(trans) options.win_max_height = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_AVSIZE, &trans, FALSE); + if(trans) options.av_size = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_INDENT, &trans, FALSE); + if(trans) options.text_indent = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_SBWIDTH, &trans, FALSE); + if(trans) options.sb_width = new_val; + new_val = GetDlgItemInt(hwndDlg, IDC_ED_PADDING, &trans, FALSE); + if(trans) options.padding = new_val; + + options.location = (PopupLocation)SendDlgItemMessage(hwndDlg, IDC_CMB_PLACEMENT, CB_GETCURSEL, 0, 0); + options.right_icon = (SendDlgItemMessage(hwndDlg, IDC_CMB_ICON, CB_GETCURSEL, 0, 0) == 1); + options.av_layout = (PopupAvLayout)SendDlgItemMessage(hwndDlg, IDC_CMB_AV, CB_GETCURSEL, 0, 0); + options.time_layout = (PopupTimeLayout)SendDlgItemMessage(hwndDlg, IDC_CMB_TIME, CB_GETCURSEL, 0, 0); + + new_val = GetDlgItemInt(hwndDlg, IDC_ED_TRANS, &trans, FALSE); + if(trans) options.opacity = new_val; + options.border = IsDlgButtonChecked(hwndDlg, IDC_CHK_BORDER) && IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CHK_BORDER)) ? true : false; + options.round = IsDlgButtonChecked(hwndDlg, IDC_CHK_ROUNDCORNERS) && IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CHK_ROUNDCORNERS)) ? true : false; + options.av_round = IsDlgButtonChecked(hwndDlg, IDC_CHK_ROUNDCORNERSAV) && IsWindowEnabled(GetDlgItem(hwndDlg, IDC_CHK_ROUNDCORNERSAV)) ? true : false; + options.animate = IsDlgButtonChecked(hwndDlg, IDC_CHK_ANIMATE) ? true : false; + options.trans_bg = IsDlgButtonChecked(hwndDlg, IDC_CHK_TRANSBG) ? true : false; + options.global_hover = IsDlgButtonChecked(hwndDlg, IDC_CHK_GLOBALHOVER) ? true : false; + + int i = 0; + for(; i < 10; i++) + options.disable_status[i] = (ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_LST_STATUS), i) == 1); + options.disable_full_screen = (ListView_GetCheckState(GetDlgItem(hwndDlg, IDC_LST_STATUS), i) == 1); + + SaveOptions(); + return TRUE; + } + break; + } + + return 0; +} + +int OptInit(WPARAM wParam, LPARAM lParam) { +#define OPTIONPAGE_OLD_SIZE2 60 + + OPTIONSDIALOGPAGE odp = { 0 }; + //odp.cbSize = sizeof(odp); + odp.cbSize = OPTIONPAGE_OLD_SIZE2; + odp.flags = ODPF_BOLDGROUPS; + //odp.flags |= ODPF_UNICODE; + odp.position = -790000000; + odp.hInstance = hInst; + + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT1); + odp.pszTitle = Translate("PopUps"); + //odp.pszTitle = Translate("General"); + //odp.pszGroup = Translate("PopUps"); + //odp.ptszTitle = TranslateT("Updater"); + //odp.ptszGroup = TranslateT("Plugins"); + odp.nIDBottomSimpleControl = 0; + odp.pfnDlgProc = DlgProcOpts1; + CallService( MS_OPT_ADDPAGE, wParam,( LPARAM )&odp ); + + return 0; +} + +HANDLE hEventOptInit; +void InitOptions() { + hEventOptInit = HookEvent(ME_OPT_INITIALISE, OptInit); + + // an icon for preview popups + hPopupIcon = LoadSkinnedIcon(SKINICON_EVENT_MESSAGE); + + LoadOptions(); +} + +void DeinitOptions() { + UnhookEvent(hEventOptInit); + DestroyIcon(hPopupIcon); +} diff --git a/yapp/options.h b/yapp/options.h new file mode 100644 index 0000000..55b94d1 --- /dev/null +++ b/yapp/options.h @@ -0,0 +1,36 @@ +#ifndef _OPTIONS_INC +#define _OPTIONS_INC + +typedef enum {PL_BOTTOMRIGHT=0, PL_BOTTOMLEFT=1, PL_TOPRIGHT=2, PL_TOPLEFT=3} PopupLocation; +typedef enum {PAV_NONE=0, PAV_LEFT=1, PAV_RIGHT=2} PopupAvLayout; +typedef enum {PT_NONE=0, PT_LEFT=1, PT_RIGHT=2, PT_WITHAV=3} PopupTimeLayout; +typedef struct { + int win_width, win_max_height, av_size; //tweety + int default_timeout; + PopupLocation location; + int opacity; + bool border; + bool round, av_round; + bool animate; + bool trans_bg; + bool use_mim_monitor; + bool right_icon; + PopupAvLayout av_layout; + bool disable_status[10]; + int text_indent; + bool global_hover; + PopupTimeLayout time_layout; + bool disable_full_screen; + bool drop_shadow; + int sb_width; + int padding, av_padding; +} Options; + +extern Options options; + +void InitOptions(); +void LoadOptions(); +void LoadModuleDependentOptions(); +void DeinitOptions(); + +#endif diff --git a/yapp/popup_history.cpp b/yapp/popup_history.cpp new file mode 100644 index 0000000..6e9b59e --- /dev/null +++ b/yapp/popup_history.cpp @@ -0,0 +1,393 @@ +#define STRICT +#define WIN32_LEAN_AND_MEAN + +#include "common.h" +#include "resource.h" +#include "popup_history.h" +#include + +#define POPUPMENU_TITLE 100 +#define POPUPMENU_MESSAGE 101 +#define POPUPMENU_TIMESTAMP 102 + +HWND hHistoryWindow = 0; //the history window +PopupHistoryList lstPopupHistory; //defined in main.cpp + +WNDPROC oldPopupsListProc = NULL; + +PopupHistoryList::PopupHistoryList() +{ + size = HISTORY_SIZE; //fixed size (at least for now) + historyData = (PopupHistoryData *) malloc(size * sizeof(PopupHistoryData)); //alloc space for data + count = 0; +} + +PopupHistoryList::~PopupHistoryList() +{ + Clear(); //clear the data strings + free(historyData); //deallocate the data list +} + +void PopupHistoryList::Clear() +{ + int i; + for (i = 0; i < count; i++) + { + DeleteData(i); + } + count = 0; +} + +int PopupHistoryList::Count() +{ + return count; +} + +int PopupHistoryList::Size() +{ + return size; +} + +void PopupHistoryList::RemoveItem(int index) +{ + int i; + DeleteData(index); //free the mem for that particular item + for (i = index + 1; i < count; i++) + { + historyData[i - 1] = historyData[i]; //shift all items to the left + } +} + +void PopupHistoryList::DeleteData(int index) +{ + PopupHistoryData *item = &historyData[index]; + if (item->flags && PHDF_UNICODE) //strings are dupped(), we need to free them + { + free(item->titleW); + free(item->messageW); + } + else{ + free(item->title); + free(item->message); + } + item->timestamp = 0; //invalidate item + item->title = NULL; + item->message = NULL; + item->flags = 0; +} + +void PopupHistoryList::AddItem(PopupHistoryData item) +{ + if (count >= size) + { + RemoveItem(0); //remove first element - the oldest + count--; //it will be inc'ed later + } + historyData[count++] = item; //item has it's relevant strings dupped() + RefreshPopupHistory(hHistoryWindow); +} + +void PopupHistoryList::Add(char *title, char *message, time_t timestamp) +{ + PopupHistoryData item = {0}; //create a history item + item.timestamp = timestamp; + item.title = strdup(title); + item.message = strdup(message); + AddItem(item); //add it (flags = 0) +} + +void PopupHistoryList::Add(wchar_t *title, wchar_t *message, time_t timestamp) +{ + PopupHistoryData item = {0}; //create an unicode history item + item.flags = PHDF_UNICODE; //mark it as unicode + item.timestamp = timestamp; + item.titleW = _wcsdup(title); + item.messageW = _wcsdup(message); + AddItem(item); //add it +} + +PopupHistoryData *PopupHistoryList::Get(int index) +{ + if ((index < 0) || (index >= count)) //a bit of sanity check + { + return NULL; + } + + return &historyData[index]; +} + +//Stucture passed to list sort function +struct SortParams{ + HWND hList; + int column; +}; + +static int lastColumn = -1; //last sort column + +int CALLBACK PopupsCompare(LPARAM lParam1, LPARAM lParam2, LPARAM myParam) +{ + SortParams params = *(SortParams *) myParam; + const int MAX_SIZE = 512; + TCHAR text1[MAX_SIZE]; + TCHAR text2[MAX_SIZE]; + int res; + + ListView_GetItemText(params.hList, (int) lParam1, params.column, text1, MAX_SIZE); + ListView_GetItemText(params.hList, (int) lParam2, params.column, text2, MAX_SIZE); + + res = _tcsicmp(text1, text2); + + res = (params.column == lastColumn) ? -res : res; //do reverse search on second click on same column + + return res; +} + +void RefreshPopupHistory(HWND hWnd) +{ + if (!hWnd) + { + return; + } + HWND hHistoryList = GetDlgItem(hWnd, IDC_LST_HISTORY); + ListView_DeleteAllItems(hHistoryList); + + int i; + LVITEM item = {0}; + item.mask = LVIF_TEXT; + wchar_t buffer[1024]; + struct tm *myTime; + for (i = 0; i < lstPopupHistory.Count(); i++) + { + item.iItem = i; + PopupHistoryData *popupItem = lstPopupHistory.Get(i); + item.pszText = popupItem->titleW; + ListView_InsertItem(hHistoryList, &item); + ListView_SetItemText(hHistoryList, i, 1, popupItem->messageW); + myTime = localtime(&popupItem->timestamp); + wcsftime(buffer, 1024, L"%c", myTime); + ListView_SetItemText(hHistoryList, i, 2, buffer); + } + + SortParams params = {0}; + params.hList = hHistoryList; + params.column = lastColumn; + + ListView_SortItemsEx(hHistoryList, PopupsCompare, ¶ms); +} + +void CopyPopupDataToClipboard(HWND hList, int selection) +{ + if (!selection) + { + return; + } + + if (!GetOpenClipboardWindow()) + { + if (OpenClipboard(hList)) + { + TCHAR buffer[2048]; + buffer[0] = _T('\0'); + TCHAR *clipboard; + int i; + int found = 0; + int count = ListView_GetItemCount(hList); + int textType; +#ifdef _UNICODE + textType = CF_UNICODETEXT; +#else + textType = CF_TEXT; +#endif + + for (i = 0; i < count; i++) + { + if (ListView_GetItemState(hList, i, LVIS_SELECTED)) + { + ListView_GetItemText(hList, i, selection - 100, buffer, 2048); + found = 1; + break; + } + } + if (found) + { + EmptyClipboard(); + int len = _tcslen(buffer); + + HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, (len + 2) * sizeof(TCHAR)); + clipboard = (TCHAR *) GlobalLock(hData); + _tcsncpy(clipboard, buffer, len); + clipboard[len] = _T('\0'); + GlobalUnlock(hData); + if (!SetClipboardData(textType, hData)) + { + PUShowMessage("Could not set clipboard data", SM_WARNING); + } + } + CloseClipboard(); + } + else{ + PUShowMessage("Could not open clipboard", SM_WARNING); + } + } + else{ + PUShowMessage("The clipboard is not available", SM_WARNING); + } +} + +//subclass proc for the list view +BOOL CALLBACK PopupsListSubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_CONTEXTMENU: + { + int x = LOWORD(lParam); + int y = HIWORD(lParam); + int selection; + + HMENU hMenu = CreatePopupMenu(); + AppendMenu(hMenu, MF_STRING, POPUPMENU_TITLE, TranslateT("Copy title to clipboard")); + AppendMenu(hMenu, MF_STRING, POPUPMENU_MESSAGE, TranslateT("Copy message to clipboard")); + AppendMenu(hMenu, MF_STRING, POPUPMENU_TIMESTAMP, TranslateT("Copy timestamp to clipboard")); + selection = TrackPopupMenu(hMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON | TPM_RETURNCMD, x, y, 0, hWnd, NULL); + DestroyMenu(hMenu); + if (selection) + { + CopyPopupDataToClipboard(hWnd, selection); + } + + break; + } + + case WM_KEYUP: + { + switch (wParam) + { + case 'C': + { + if (GetKeyState(VK_CONTROL)) + { + CopyPopupDataToClipboard(hWnd, POPUPMENU_MESSAGE); + } + + break; + } + + case VK_ESCAPE: + { + SendMessage(GetParent(hWnd), WM_CLOSE, 0, 0); + + break; + } + + } + + break; + } + + case WM_SYSKEYDOWN: + { + if (wParam == 'X') + { + SendMessage(GetParent(hWnd), WM_CLOSE, 0, 0); + } + + break; + } + } + + return CallWindowProc(oldPopupsListProc, hWnd, msg, wParam, lParam); +} + +//this is the history list window handler +BOOL CALLBACK DlgProcHistLst(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) + { + case WM_INITDIALOG: + { + TranslateDialogDefault(hWnd); + HWND hHistoryList = GetDlgItem(hWnd, IDC_LST_HISTORY); + + ListView_SetExtendedListViewStyleEx (hHistoryList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); + + oldPopupsListProc = (WNDPROC) SetWindowLong(hHistoryList, GWL_WNDPROC, (LONG) PopupsListSubclassProc); + + LVCOLUMN col; + col.mask = LVCF_TEXT | LVCF_WIDTH; + col.pszText = L"Title"; + col.cx = 100; + ListView_InsertColumn(hHistoryList, 0, &col); + col.pszText = L"Message"; + col.cx = 450; + ListView_InsertColumn(hHistoryList, 1, &col); + col.pszText = L"Timestamp"; + col.cx = 115; + ListView_InsertColumn(hHistoryList, 2, &col); + RefreshPopupHistory(hWnd); + + return TRUE; + } + + case WM_DESTROY: + { + hHistoryWindow = NULL; + + break; + } + + case WM_CLOSE: + { + DestroyWindow(hWnd); + + break; + } + + case WM_COMMAND: + { + switch (LOWORD(wParam)) + { + case IDC_CLOSE: + { + SendMessage(hWnd, WM_CLOSE, 0, 0); + + break; + } + } + + break; + } + + case WM_NOTIFY: + { + switch(((LPNMHDR)lParam)->idFrom) + { + case IDC_LST_HISTORY: + { + switch (((LPNMHDR)lParam)->code) + { + case LVN_COLUMNCLICK: + { + LPNMLISTVIEW lv = (LPNMLISTVIEW) lParam; + int column = lv->iSubItem; + SortParams params = {0}; + params.hList = GetDlgItem(hWnd, IDC_LST_HISTORY); + params.column = column; + + ListView_SortItemsEx(params.hList, PopupsCompare, (LPARAM) ¶ms); + lastColumn = (params.column == lastColumn) ? -1 : params.column; + + break; + } + } + + break; + } + } + + break; + } + } + + return 0; +} diff --git a/yapp/popup_history.h b/yapp/popup_history.h new file mode 100644 index 0000000..8bb6c7b --- /dev/null +++ b/yapp/popup_history.h @@ -0,0 +1,59 @@ +#ifndef __popup_history_h__ +#define __popup_history_h__ + +#define HISTORY_SIZE 200 //number of popup history items + +#define PHDF_UNICODE 1 + +struct PopupHistoryData{ + DWORD flags; //PHDF_* flags + union{ + char *message; + wchar_t *messageW; + }; + union{ + char *title; + wchar_t *titleW; + }; + time_t timestamp; +}; + +class PopupHistoryList{ + protected: + private: + PopupHistoryData *historyData; //historyData[0] - oldest, historyData[size - 1] - newest + int count; + int size; + + void DeleteData(int index); + void AddItem(PopupHistoryData item); //adds a PopupHistoryData item + void RemoveItem(int index); + + public: + PopupHistoryList(); + ~PopupHistoryList(); + + void Add(char *title, char *message, time_t timestamp); + void Add(wchar_t *title, wchar_t *message, time_t timestamp); + + PopupHistoryData *Get(int index); + + void Clear(); + int Count(); + int Size(); +}; + +/*Shows a history with the last popups. +Useful if you've missed a popup when it appeared. +wParam - 0 +lParam - 0 +*/ +#define MS_POPUP_SHOWHISTORY "PopUp/ShowHistory" + +extern PopupHistoryList lstPopupHistory; //defined in main.cpp +extern HWND hHistoryWindow; //the history window +void RefreshPopupHistory(HWND hWnd); + +BOOL CALLBACK DlgProcHistLst(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +#endif //__popup_history_h__ diff --git a/yapp/popups2.cpp b/yapp/popups2.cpp new file mode 100644 index 0000000..3439832 --- /dev/null +++ b/yapp/popups2.cpp @@ -0,0 +1,238 @@ +// popups2.cpp : Defines the entry point for the DLL application. +// + +#include "common.h" +#include "popups2.h" +#include "version.h" +#include "message_pump.h" +#include "options.h" +#include "popwin.h" +#include "notify.h" + +HMODULE hInst = 0; +HANDLE mainThread = 0; + +MNOTIFYLINK *notifyLink = 0; + +int code_page = CP_ACP; + +// used to work around a bug in neweventnotify and others with the address passed in the GetPluginData function +bool ignore_gpd_passed_addy = false; + +FontIDW font_id_firstline = {0}, font_id_secondline = {0}, font_id_time = {0}; +ColourIDW colour_id_bg = {0}, colour_id_border = {0}, colour_id_sidebar = {0}; +COLORREF colBg = GetSysColor(COLOR_3DSHADOW); +HFONT hFontFirstLine = 0, hFontSecondLine = 0, hFontTime = 0; +COLORREF colFirstLine = RGB(255, 0, 0), colSecondLine = 0, colTime = RGB(0, 0, 255), colBorder = RGB(0, 0, 0), colSidebar = RGB(128, 128, 128); + + +// hooked here so it's in the main thread +HANDLE hAvChangeEvent = 0; + +PLUGININFO pluginInfo={ + sizeof(PLUGININFO), + __PLUGIN_NAME, + PLUGIN_MAKE_VERSION(__MAJOR_VERSION, __MINOR_VERSION, __RELEASE_NUM, __BUILD_NUM), + __DESC, + __AUTHOR, + __AUTHOREMAIL, + __COPYRIGHT, + __AUTHORWEB, + 0, //not transient + 0 //doesn't replace anything built-in +}; + +PLUGINLINK *pluginLink = 0; + +extern "C" BOOL APIENTRY DllMain( HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) +{ + hInst = hModule; + //DisableThreadLibraryCalls(hInst); + return TRUE; +} + +extern "C" POPUPS2_API PLUGININFO* MirandaPluginInfo(DWORD mirandaVersion) +{ + return &pluginInfo; +} + +int ReloadFont(WPARAM wParam, LPARAM lParam) { + LOGFONT log_font; + + if(hFontFirstLine) DeleteObject(hFontFirstLine); + colFirstLine = CallService(MS_FONT_GETW, (WPARAM)&font_id_firstline, (LPARAM)&log_font); + hFontFirstLine = CreateFontIndirect(&log_font); + if(hFontSecondLine) DeleteObject(hFontSecondLine); + colSecondLine = CallService(MS_FONT_GETW, (WPARAM)&font_id_secondline, (LPARAM)&log_font); + hFontSecondLine = CreateFontIndirect(&log_font); + if(hFontTime) DeleteObject(hFontTime); + colTime = CallService(MS_FONT_GETW, (WPARAM)&font_id_time, (LPARAM)&log_font); + hFontTime = CreateFontIndirect(&log_font); + + colBg = CallService(MS_COLOUR_GETW, (WPARAM)&colour_id_bg, 0); + colBorder = CallService(MS_COLOUR_GETW, (WPARAM)&colour_id_border, 0); + colSidebar = CallService(MS_COLOUR_GETW, (WPARAM)&colour_id_sidebar, 0); + + return 0; +} + +HANDLE hEventReloadFont = 0; + +int ModulesLoaded(WPARAM wParam, LPARAM lParam) { + MNotifyGetLink(); + + if(ServiceExists(MS_UPDATE_REGISTER)) { + // register with updater + Update update = {0}; + char szVersion[16]; + + update.cbSize = sizeof(Update); + + update.szComponentName = pluginInfo.shortName; + update.pbVersion = (BYTE *)CreateVersionString(pluginInfo.version, szVersion); + update.cpbVersion = strlen((char *)update.pbVersion); + + update.szUpdateURL = UPDATER_AUTOREGISTER; + + // 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.szBetaUpdateURL = "http://www.scottellis.com.au/miranda_plugins/yapp.zip"; + update.szBetaVersionURL = "http://www.scottellis.com.au/miranda_plugins/ver_yapp.html"; + update.pbBetaVersionPrefix = (BYTE *)"YAPP version "; + + update.cpbBetaVersionPrefix = strlen((char *)update.pbBetaVersionPrefix); + + CallService(MS_UPDATE_REGISTER, 0, (WPARAM)&update); + } + + if(ServiceExists(MS_FONT_REGISTERW)) { + font_id_firstline.cbSize = sizeof(FontIDW); + font_id_firstline.flags = FIDF_ALLOWEFFECTS; + wcscpy(font_id_firstline.group, TranslateT("Popups")); + wcscpy(font_id_firstline.name, TranslateT("First Line")); + strcpy(font_id_firstline.dbSettingsGroup, MODULE); + strcpy(font_id_firstline.prefix, "FontFirst"); + font_id_firstline.order = 0; + + font_id_secondline.cbSize = sizeof(FontIDW); + font_id_secondline.flags = FIDF_ALLOWEFFECTS; + wcscpy(font_id_secondline.group, TranslateT("Popups")); + wcscpy(font_id_secondline.name, TranslateT("Second Line")); + strcpy(font_id_secondline.dbSettingsGroup, MODULE); + strcpy(font_id_secondline.prefix, "FontSecond"); + font_id_secondline.order = 1; + + font_id_time.cbSize = sizeof(FontIDW); + font_id_time.flags = FIDF_ALLOWEFFECTS; + wcscpy(font_id_time.group, TranslateT("Popups")); + wcscpy(font_id_time.name, TranslateT("Time")); + strcpy(font_id_time.dbSettingsGroup, MODULE); + strcpy(font_id_time.prefix, "FontTime"); + font_id_time.order = 2; + + CallService(MS_FONT_REGISTERW, (WPARAM)&font_id_firstline, 0); + CallService(MS_FONT_REGISTERW, (WPARAM)&font_id_secondline, 0); + CallService(MS_FONT_REGISTERW, (WPARAM)&font_id_time, 0); + + colour_id_bg.cbSize = sizeof(ColourIDW); + wcscpy(colour_id_bg.group, TranslateT("Popups")); + wcscpy(colour_id_bg.name, TranslateT("Background")); + strcpy(colour_id_bg.dbSettingsGroup, MODULE); + strcpy(colour_id_bg.setting, "ColourBg"); + colour_id_bg.defcolour = GetSysColor(COLOR_3DSHADOW); + colour_id_bg.order = 0; + + colour_id_border.cbSize = sizeof(ColourIDW); + wcscpy(colour_id_border.group, TranslateT("Popups")); + wcscpy(colour_id_border.name, TranslateT("Border")); + strcpy(colour_id_border.dbSettingsGroup, MODULE); + strcpy(colour_id_border.setting, "ColourBorder"); + colour_id_border.defcolour = RGB(0, 0, 0); + colour_id_border.order = 1; + + colour_id_sidebar.cbSize = sizeof(ColourIDW); + wcscpy(colour_id_sidebar.group, TranslateT("Popups")); + wcscpy(colour_id_sidebar.name, TranslateT("Sidebar")); + strcpy(colour_id_sidebar.dbSettingsGroup, MODULE); + strcpy(colour_id_sidebar.setting, "ColourSidebar"); + colour_id_sidebar.defcolour = RGB(128, 128, 128); + colour_id_sidebar.order = 2; + + CallService(MS_COLOUR_REGISTERW, (WPARAM)&colour_id_bg, 0); + CallService(MS_COLOUR_REGISTERW, (WPARAM)&colour_id_border, 0); + CallService(MS_COLOUR_REGISTERW, (WPARAM)&colour_id_sidebar, 0); + + ReloadFont(0, 0); + + hEventReloadFont = HookEvent(ME_FONT_RELOAD, ReloadFont); + } else { + LOGFONT lf = {0}; + lf.lfCharSet = DEFAULT_CHARSET; + lf.lfHeight = -14; + lf.lfWeight = FW_BOLD; + hFontFirstLine = CreateFontIndirect(&lf); + + lf.lfHeight = -12; + lf.lfWeight = 0; + hFontSecondLine = CreateFontIndirect(&lf); + + lf.lfHeight = -8; + lf.lfWeight = 0; + hFontTime = CreateFontIndirect(&lf); + } + + hAvChangeEvent = HookEvent(ME_AV_AVATARCHANGED, AvatarChanged); + + LoadModuleDependentOptions(); + + if(GetModuleHandle(_T("neweventnotify"))) + { + ignore_gpd_passed_addy = true; + } + + return 0; +} + +int PreShutdown(WPARAM wParam, LPARAM lParam) { + if(hAvChangeEvent) UnhookEvent(hAvChangeEvent); + DeinitMessagePump(); + return 0; +} + +HANDLE hEventPreShutdown, hEventModulesLoaded; + +extern "C" int POPUPS2_API Load(PLUGINLINK *link) { + pluginLink = link; + DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), &mainThread, THREAD_SET_CONTEXT, FALSE, 0); + + INITCOMMONCONTROLSEX icex; + + // Ensure that the common control DLL is loaded (for listview) + icex.dwSize = sizeof(INITCOMMONCONTROLSEX); + icex.dwICC = ICC_LISTVIEW_CLASSES; + InitCommonControlsEx(&icex); + + InitMessagePump(); + InitOptions(); + InitNotify(); + + hEventPreShutdown = HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown); + hEventModulesLoaded = HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + + return 0; +} + +extern "C" int POPUPS2_API Unload() { + if(hEventReloadFont) UnhookEvent(hEventReloadFont); + UnhookEvent(hEventPreShutdown); + UnhookEvent(hEventModulesLoaded); + DeinitNotify(); + if(ServiceExists(MS_FONT_REGISTERW)) { + DeleteObject(hFontFirstLine); + DeleteObject(hFontSecondLine); + DeleteObject(hFontTime); + } // otherwise, no need to delete the handle + return 0; +} diff --git a/yapp/popups2.h b/yapp/popups2.h new file mode 100644 index 0000000..59e4f89 --- /dev/null +++ b/yapp/popups2.h @@ -0,0 +1,12 @@ +// The following ifdef block is the standard way of creating macros which make exporting +// from a DLL simpler. All files within this DLL are compiled with the POPUPS2_EXPORTS +// symbol defined on the command line. this symbol should not be defined on any project +// that uses this DLL. This way any other project whose source files include this file see +// POPUPS2_API functions as being imported from a DLL, whereas this DLL sees symbols +// defined with this macro as being exported. +#ifdef POPUPS2_EXPORTS +#define POPUPS2_API __declspec(dllexport) +#else +#define POPUPS2_API __declspec(dllimport) +#endif + diff --git a/yapp/popups2.rc b/yapp/popups2.rc new file mode 100644 index 0000000..9ffafcd --- /dev/null +++ b/yapp/popups2.rc @@ -0,0 +1,170 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "afxres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (U.S.) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) +#endif //_WIN32 + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""afxres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +#endif // English (U.S.) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// English (Australia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENA) +#ifdef _WIN32 +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_AUS +#pragma code_page(1252) +#endif //_WIN32 + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LST_HISTORY DIALOGEX 0, 0, 467, 303 +STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Popup history" +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + PUSHBUTTON "Close",IDC_CLOSE,413,285,50,14 + CONTROL "",IDC_LST_HISTORY,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_ALIGNLEFT | WS_BORDER | WS_TABSTOP,4,3,459,278 +END + +IDD_OPT1 DIALOGEX 0, 0, 297, 218 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + GROUPBOX "Default Timeout",IDC_STATIC,4,7,153,36 + CONTROL "Never timeout",IDC_RAD_NOTIMEOUT,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT | WS_GROUP,10,16,77,10,WS_EX_RIGHT + CONTROL "Set timeout:",IDC_RAD_TIMEOUT,"Button",BS_AUTORADIOBUTTON | BS_LEFTTEXT,10,28,77,10,WS_EX_RIGHT + EDITTEXT IDC_ED_TIMEOUT,93,26,40,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_TIMEOUT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,131,26,12,14 + EDITTEXT IDC_ED_WIDTH,238,84,39,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_WIDTH,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,270,84,12,14 + EDITTEXT IDC_ED_MAXHEIGHT,238,102,39,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_MAXHEIGHT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,270,102,12,14 + RTEXT "Width:",IDC_STATIC,174,87,60,8,0,WS_EX_RIGHT + RTEXT "Maximum height:",IDC_STATIC,174,104,60,8,0,WS_EX_RIGHT + GROUPBOX "Options",IDC_STATIC,4,116,153,94 + EDITTEXT IDC_ED_TRANS,97,183,33,12,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_TRANS,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS | UDS_NOTHOUSANDS,126,183,13,12 + RTEXT "Opacity(%):",IDC_STATIC,11,186,84,8,0,WS_EX_RIGHT + CONTROL "Border",IDC_CHK_BORDER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,127,121,10 + CONTROL "Round corners (window)",IDC_CHK_ROUNDCORNERS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,138,121,10 + PUSHBUTTON "Preview",IDC_BTN_PREVIEW,161,194,130,17 + CONTROL "Animate",IDC_CHK_ANIMATE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,160,121,10 + CONTROL "Transparent background",IDC_CHK_TRANSBG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,199,121,10 + GROUPBOX "Layout",IDC_STATIC,161,7,129,184 + RTEXT "Avatar size:",IDC_STATIC,174,122,60,8,0,WS_EX_RIGHT + EDITTEXT IDC_ED_AVSIZE,238,120,39,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_AVSIZE,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,270,120,12,14 + GROUPBOX "Disable when",IDC_STATIC,4,44,153,70 + CONTROL "",IDC_LST_STATUS,"SysListView32",LVS_REPORT | LVS_SINGLESEL | LVS_NOLABELWRAP | LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,7,54,148,56 + COMBOBOX IDC_CMB_PLACEMENT,165,19,120,68,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CMB_ICON,165,36,120,69,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + COMBOBOX IDC_CMB_AV,165,53,120,76,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + CONTROL "Global hover",IDC_CHK_GLOBALHOVER,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,171,121,10 + COMBOBOX IDC_CMB_TIME,165,69,120,76,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + EDITTEXT IDC_ED_SBWIDTH,238,138,39,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_SBWIDTH,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,270,138,11,14 + RTEXT "Sidebar width:",IDC_STATIC,174,141,60,8 + EDITTEXT IDC_ED_INDENT,238,156,39,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_INDENT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,269,156,12,14 + RTEXT "Text indent:",IDC_STATIC,174,160,60,8 + EDITTEXT IDC_ED_PADDING,238,173,39,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER,WS_EX_RIGHT + CONTROL "",IDC_SPIN_PADDING,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,270,173,12,14 + RTEXT "Padding:",IDC_STATIC,174,176,60,8 + CONTROL "Round corners (avatar)",IDC_CHK_ROUNDCORNERSAV,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,25,149,121,10 +END + +IDD_OPT_NOTIFY DIALOGEX 0, 0, 187, 91 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN +END + + +///////////////////////////////////////////////////////////////////////////// +// +// DESIGNINFO +// + +#ifdef APSTUDIO_INVOKED +GUIDELINES DESIGNINFO +BEGIN + IDD_OPT1, DIALOG + BEGIN + LEFTMARGIN, 4 + RIGHTMARGIN, 290 + TOPMARGIN, 7 + BOTTOMMARGIN, 211 + END + + IDD_OPT_NOTIFY, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 180 + TOPMARGIN, 7 + BOTTOMMARGIN, 84 + END +END +#endif // APSTUDIO_INVOKED + +#endif // English (Australia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/yapp/popups2.sln b/yapp/popups2.sln new file mode 100644 index 0000000..cefe976 --- /dev/null +++ b/yapp/popups2.sln @@ -0,0 +1,19 @@ +Microsoft Visual Studio Solution File, Format Version 9.00 +# Visual Studio 2005 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "popups2", "popups2.vcproj", "{B6FC188B-8E54-4197-9444-8BADE9AA75E2}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B6FC188B-8E54-4197-9444-8BADE9AA75E2}.Debug|Win32.ActiveCfg = Debug|Win32 + {B6FC188B-8E54-4197-9444-8BADE9AA75E2}.Debug|Win32.Build.0 = Debug|Win32 + {B6FC188B-8E54-4197-9444-8BADE9AA75E2}.Release|Win32.ActiveCfg = Release|Win32 + {B6FC188B-8E54-4197-9444-8BADE9AA75E2}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/yapp/popups2.vcproj b/yapp/popups2.vcproj new file mode 100644 index 0000000..400b8cd --- /dev/null +++ b/yapp/popups2.vcproj @@ -0,0 +1,377 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/yapp/popwin.cpp b/yapp/popwin.cpp new file mode 100644 index 0000000..9915961 --- /dev/null +++ b/yapp/popwin.cpp @@ -0,0 +1,760 @@ +#include "common.h" +#include "popwin.h" +#include "message_pump.h" +#include "options.h" + +HMODULE hUserDll; +BOOL (WINAPI *MySetLayeredWindowAttributes)(HWND,COLORREF,BYTE,DWORD) = 0; +BOOL (WINAPI *MyAnimateWindow)(HWND hWnd,DWORD dwTime,DWORD dwFlags) = 0; + +#define ID_CLOSETIMER 0x0101 +#define ID_MOVETIMER 0x0102 + +DWORD pop_start_x, pop_start_y; +int global_mouse_in = 0; + +void SetStartValues() { + RECT wa_rect; + if(options.use_mim_monitor) { + RECT clr; + HMONITOR hMonitor; + GetWindowRect((HWND)CallService(MS_CLUI_GETHWND, 0, 0), &clr); + hMonitor = MonitorFromRect(&clr, MONITOR_DEFAULTTONEAREST); + + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + + wa_rect = mi.rcWork; + + } else + SystemParametersInfo(SPI_GETWORKAREA, 0, &wa_rect, 0); + + if(options.location == PL_BOTTOMRIGHT || options.location == PL_TOPRIGHT) + pop_start_x = wa_rect.right - options.win_width - 1; + else + pop_start_x = wa_rect.left + 1; + + if(options.location == PL_BOTTOMRIGHT || options.location == PL_BOTTOMLEFT) + pop_start_y = wa_rect.bottom - 1; + else + pop_start_y = wa_rect.top + 1; +} + +struct HWNDStackNode { + HWND hwnd; + struct HWNDStackNode *next; +}; + +HWNDStackNode *hwnd_stack_top = 0; +int stack_size = 0; + +void RepositionWindows() { + HWNDStackNode *current; + int x = pop_start_x, y = pop_start_y; + int height;//, total_height = 0; + + /* + current = hwnd_stack_top; + while(current) { + SendMessage(current->hwnd, PUM_GETHEIGHT, (WPARAM)&height, 0); + total_height += height; + current = current->next; + } + */ + + current = hwnd_stack_top; + while(current) { + SendMessage(current->hwnd, PUM_GETHEIGHT, (WPARAM)&height, 0); + if(options.location == PL_BOTTOMRIGHT || options.location == PL_BOTTOMLEFT) y -= height + 1; + SendMessage(current->hwnd, PUM_MOVE, (WPARAM)x, (LPARAM)y); + if(options.location == PL_TOPRIGHT || options.location == PL_TOPLEFT) y += height + 1; + + current = current->next; + } +} + +void AddWindowToStack(HWND hwnd) { + SetStartValues(); + + HWNDStackNode *new_node = (HWNDStackNode *)malloc(sizeof(HWNDStackNode)); + new_node->hwnd = hwnd; + new_node->next = hwnd_stack_top; + hwnd_stack_top = new_node; + + int height; + SendMessage(hwnd, PUM_GETHEIGHT, (WPARAM)&height, 0); + + int x = pop_start_x, y = pop_start_y; + if(options.location == PL_BOTTOMRIGHT || options.location == PL_TOPRIGHT) + x += options.win_width; + else + x -= options.win_width; + + if(options.location == PL_BOTTOMRIGHT || options.location == PL_BOTTOMLEFT) y -= height; + SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + if(options.location == PL_TOPRIGHT || options.location == PL_TOPLEFT) y += height; + + stack_size++; + + RepositionWindows(); +} + +void RemoveWindowFromStack(HWND hwnd) { + HWNDStackNode *current = hwnd_stack_top, *prev = 0; + while(current) { + if(current->hwnd == hwnd) { + if(prev) { + prev->next = current->next; + } else { + hwnd_stack_top = current->next; + } + free(current); + stack_size--; + break; + } + + prev = current; + current = current->next; + } + + if(hwnd_stack_top) RepositionWindows(); +} + +void ClearStack() { + while(hwnd_stack_top) { + DestroyWindow(hwnd_stack_top->hwnd); + } +} + +void BroadcastMessage(UINT msg, WPARAM wParam, LPARAM lParam) { + HWNDStackNode *current = hwnd_stack_top; + while(current) { + SendMessage(current->hwnd, msg, wParam, lParam); + current = current->next; + } +} + +struct PopupWindowData { + POPUPDATAW *pd; + int new_x, new_y; + bool is_round, av_is_round, mouse_in, close_on_leave; + bool custom_col; + HBRUSH bkBrush, barBrush; + HPEN bPen; + TCHAR tbuff[128]; + int tb_height, av_height, text_height, time_height, time_width; + int real_av_width, real_av_height; + bool have_av; + HANDLE hNotify; +}; + +void trim(TCHAR *str) { + int len = _tcslen(str), pos; + // trim whitespace (e.g. from OTR detection) + for(pos = len - 1; pos >= 0; pos--) { + if(str[pos] == _T(' ') || str[pos] == _T('\t') || str[pos] == _T('\r') || str[pos] == _T('\n')) str[pos] = 0; + else break; + } + + // remove tabs + for(pos = len - 1; pos >= 0; pos--) + if(str[pos] == _T('\t')) str[pos] = _T(' '); +} + +LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { + PopupWindowData *pwd = (PopupWindowData *)GetWindowLong(hwnd, GWL_USERDATA); + POPUPDATAW *pd = 0; + if(pwd) pd = pwd->pd; + + switch(uMsg) { + case WM_CREATE: + { + CREATESTRUCT *cs = (CREATESTRUCT *)lParam; + pwd = (PopupWindowData *)malloc(sizeof(PopupWindowData)); + pd = (POPUPDATAW *)cs->lpCreateParams; + pwd->pd = pd; + pwd->hNotify = 0; + + trim(pwd->pd->lpwzContactName); + trim(pwd->pd->lpwzText); + + pwd->is_round = options.round; + pwd->av_is_round = options.av_round; + pwd->mouse_in = pwd->close_on_leave = false; + pwd->custom_col = (pd->colorBack != pd->colorText); + + pwd->tb_height = pwd->av_height = pwd->text_height = pwd->time_height = pwd->time_width = 0; + pwd->have_av = false; + + if(pwd->custom_col) { + pwd->bkBrush = CreateSolidBrush(pd->colorBack); + + //pwd->barBrush = CreateSolidBrush(pd->colorBack / 2); // make sidebar a dark version of the bg + //DWORD darkBg = (((pd->colorBack & 0xff0000) >> 1) & 0xff0000) + (((pd->colorBack & 0xff00) >> 1) & 0xff00) + (((pd->colorBack & 0xff) >> 1) & 0xff); + //DWORD darkBg = (pdColorBack >> 1) & 0x7f7f7f; // equivalent to above :) + + DWORD darkBg = pd->colorBack - ((pd->colorBack >> 2) & 0x3f3f3f); // 3/4 of current individual RGB components + pwd->barBrush = CreateSolidBrush(darkBg); // make sidebar a dark version of the bg + } else { + pwd->bkBrush = CreateSolidBrush(colBg); + pwd->barBrush = CreateSolidBrush(colSidebar); + } + + if(options.border) pwd->bPen = (HPEN)CreatePen(PS_SOLID, 1, colBorder); + else pwd->bPen = CreatePen(PS_SOLID, 1, pwd->custom_col ? pd->colorBack : colBg); + + SYSTEMTIME st; + GetLocalTime(&st); + GetTimeFormat(LOCALE_USER_DEFAULT, TIME_NOSECONDS, &st, 0, pwd->tbuff, 128); + + SetWindowLong(hwnd, GWL_USERDATA, (LONG)pwd); + + if(pd->iSeconds == -1 || (pd->iSeconds == 0 && options.default_timeout == -1)) { + // make a really long timeout - say 7 days? ;) + SetTimer(hwnd, ID_CLOSETIMER, 7 * 24 * 60 * 60 * 1000, 0); + } else { + if(pd->iSeconds == 0) { + SetTimer(hwnd, ID_CLOSETIMER, options.default_timeout * 1000, 0); + } else { + SetTimer(hwnd, ID_CLOSETIMER, pd->iSeconds * 1000, 0); + } + } + + AddWindowToStack(hwnd); // this updates our size + } + + // transparency +#ifdef WS_EX_LAYERED + SetWindowLong(hwnd, GWL_EXSTYLE, GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); +#endif + +#ifdef CS_DROPSHADOW + if (options.drop_shadow) { + SetClassLong(hwnd, GCL_STYLE, CS_DROPSHADOW); + } +#endif + +#ifdef LWA_ALPHA + if(MySetLayeredWindowAttributes) { + MySetLayeredWindowAttributes(hwnd, RGB(0,0,0), (int)(options.opacity / 100.0 * 255), LWA_ALPHA); + if(options.trans_bg) { + COLORREF bg; + if(pd->colorBack == pd->colorText) + bg = colBg; + else + bg = pd->colorBack; + MySetLayeredWindowAttributes(hwnd, bg, 0, LWA_COLORKEY); + } + } +#endif + PostMessage(hwnd, UM_INITPOPUP, (WPARAM)hwnd, 0); + return 0; + case WM_MOUSEMOVE: + if(pwd && !pwd->mouse_in) { + pwd->mouse_in = true; + global_mouse_in++; + TRACKMOUSEEVENT tme = { sizeof(tme) }; + tme.dwFlags = TME_LEAVE; + tme.hwndTrack = hwnd; + TrackMouseEvent(&tme); + } + break; + case WM_MOUSELEAVE: + if(pwd && pwd->mouse_in) { + pwd->mouse_in = false; + global_mouse_in--; + } + return 0; + case WM_LBUTTONUP: + // fake STN_CLICKED notification + SendMessage(hwnd, WM_COMMAND, MAKEWPARAM(0, STN_CLICKED), 0); + break; + case WM_TIMER: + if(wParam == ID_CLOSETIMER) { + KillTimer(hwnd, ID_CLOSETIMER); + if(pwd->mouse_in || (options.global_hover && global_mouse_in)) + SetTimer(hwnd, ID_CLOSETIMER, 800, 0); // reset timer if mouse in window - allow another 800 ms + else { + PostMessage(hwnd, UM_DESTROYPOPUP, 0, 0); + } + return TRUE; + } else if(wParam == ID_MOVETIMER) { + RECT r; + GetWindowRect(hwnd, &r); + + if(r.left == pwd->new_x && r.top == pwd->new_y) { + KillTimer(hwnd, ID_MOVETIMER); + return TRUE; + } + int adj_x = (pwd->new_x - r.left) / 4, adj_y = (pwd->new_y - r.top) / 4; + if(adj_x == 0) adj_x = (pwd->new_x - r.left); + if(adj_y == 0) adj_y = (pwd->new_y - r.top); + + int x = r.left + adj_x, y = r.top + adj_y; + //SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE | SWP_NOREDRAW | SWP_NOCOPYBITS); + SetWindowPos(hwnd, 0, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + + + /* + // clip to monitor bounds (paints badly!) + //HDC hdc = GetDC(hwnd); + HMONITOR hMonitor = MonitorFromRect(&r, MONITOR_DEFAULTTONEAREST); + MONITORINFO mi; + mi.cbSize = sizeof(mi); + GetMonitorInfo(hMonitor, &mi); + + POINT p[2]; + p[0].x = mi.rcWork.left; p[0].y = mi.rcWork.top; p[1].x = mi.rcWork.right; p[1].y = mi.rcWork.bottom; + //LPtoDP(hdc, p, 2); + ScreenToClient(hwnd, &p[0]); ScreenToClient(hwnd, &p[1]); + + HRGN hMonRgn = CreateRectRgn(p[0].x, p[0].y, p[1].x, p[1].y); + //ReleaseDC(hwnd, hdc); + + RECT cr; GetClientRect(hwnd, &cr); + HRGN hWndRgn = CreateRectRgn(cr.left, cr.top, cr.right + 3, cr.bottom + 3); + CombineRgn(hMonRgn, hMonRgn, hWndRgn, RGN_AND); + + // round corners + if(options.round) { + HRGN hRgn1; + int v,h, w=10; + h=(r.right-r.left)>(w*2)?w:(r.right-r.left); + v=(r.bottom-r.top)>(w*2)?w:(r.bottom-r.top); + h=(hbkBrush); + // sidebar + r_bar = r; + r_bar.right = r.left + options.sb_width; + FillRect(hdc, &r_bar, pwd->barBrush); + // border + if(options.border) { + + HBRUSH hOldBrush = (HBRUSH)SelectObject(hdc, GetStockObject(NULL_BRUSH)); + HPEN hOldPen = (HPEN)SelectObject(hdc, pwd->bPen); + + int h = 0; + if(options.round) { + int v; + int w=14; + h=(r.right-r.left)>(w*2)?w:(r.right-r.left); + v=(r.bottom-r.top)>(w*2)?w:(r.bottom-r.top); + h=(hcustom_col) SetBkColor(ps.hdc, pd->colorBack); + //else SetBkColor(ps.hdc, colBg); + SetBkMode(hdc, TRANSPARENT); + + // avatar & time if with avatar + if(options.av_layout != PAV_NONE && (pwd->have_av || options.time_layout == PT_WITHAV)) { + RECT avr; + avr.top = options.av_padding; + + if(options.av_layout == PAV_LEFT) { + avr.left = r.left + options.av_padding; + if(pwd->have_av && options.time_layout == PT_WITHAV) avr.right = avr.left + max(pwd->real_av_width, pwd->time_width); + else if(pwd->have_av) avr.right = avr.left + pwd->real_av_width; + else avr.right = avr.left + pwd->time_width; + r.left = avr.right; + } else if(options.av_layout == PAV_RIGHT) { + avr.right = r.right - options.av_padding; + if(pwd->have_av && options.time_layout == PT_WITHAV) avr.left = avr.right - max(pwd->real_av_width, pwd->time_width); + else if(pwd->have_av) avr.left = avr.right - pwd->real_av_width; + else avr.left = avr.right - pwd->time_width; + r.right = avr.left; + } + + if(options.time_layout == PT_WITHAV) { + avr.top = options.padding; + avr.bottom = avr.top + pwd->time_height; + if(pwd->custom_col) SetTextColor(ps.hdc, pd->colorText); + else SetTextColor(ps.hdc, colTime); + if(hFontTime) SelectObject(hdc, (HGDIOBJ)hFontTime); + DrawText(ps.hdc, pwd->tbuff, wcslen(pwd->tbuff), &avr, DT_VCENTER | DT_CENTER | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX); + avr.top = avr.bottom + options.av_padding; + } + + if(pwd->have_av) { + // correct for wider time + if(options.time_layout == PT_WITHAV && pwd->time_width > options.av_size) { + avr.left = avr.left + (pwd->time_width - pwd->real_av_width) / 2; + avr.right = avr.left + pwd->real_av_width; + } + avr.bottom = avr.top + pwd->real_av_height; + + AVATARDRAWREQUEST adr = {0}; + adr.cbSize = sizeof(adr); + adr.hContact = pd->lchContact; + adr.hTargetDC = ps.hdc; + adr.rcDraw = avr; + adr.dwFlags = (pwd->is_round ? AVDRQ_ROUNDEDCORNER : 0); + adr.radius = (pwd->av_is_round ? 5 : 0); + + CallService(MS_AV_DRAWAVATAR, 0, (LPARAM)&adr); + } + } + + // title icon + int iconx, textxmin = r.left + options.padding, textxmax = r.right - options.padding; + if(pd->lchIcon) { + if(options.right_icon) { + iconx = r.right - (16 + options.padding); + textxmax -= 16 + options.padding; + } else { + iconx = r.left + options.padding; + textxmin += 16 + options.padding; + } + DrawIconEx(ps.hdc, iconx, options.padding + (pwd->tb_height - 16) / 2, pd->lchIcon, 16, 16, 0, NULL, DI_NORMAL); + } + + // title time + if(options.time_layout == PT_LEFT || options.time_layout == PT_RIGHT) { + RECT ttr; + ttr.top = r.top + options.padding; ttr.bottom = ttr.top + pwd->tb_height; + if(pwd->custom_col) SetTextColor(ps.hdc, pd->colorText); + else SetTextColor(ps.hdc, colTime); + if(hFontTime) SelectObject(hdc, (HGDIOBJ)hFontTime); + switch(options.time_layout) { + case PT_LEFT: + ttr.left = textxmin; ttr.right = ttr.left + pwd->time_width; + textxmin += pwd->time_width + options.padding; + DrawText(ps.hdc, pwd->tbuff, wcslen(pwd->tbuff), &ttr, DT_VCENTER | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX); + break; + case PT_RIGHT: + ttr.right = textxmax; ttr.left = ttr.right - pwd->time_width; + textxmax -= pwd->time_width + options.padding; + DrawText(ps.hdc, pwd->tbuff, wcslen(pwd->tbuff), &ttr, DT_VCENTER | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX); + break; + default: + break; + } + } + + if(textxmin < options.sb_width) textxmin = options.sb_width + options.padding / 2; + + // title text + if(hFontFirstLine) SelectObject(ps.hdc, (HGDIOBJ)hFontFirstLine); + RECT tr; + tr.left = textxmin; tr.right = textxmax; tr.top = r.top + options.padding; tr.bottom = tr.top + pwd->tb_height; + + if(pwd->custom_col) SetTextColor(ps.hdc, pd->colorText); + else SetTextColor(ps.hdc, colFirstLine); + DrawText(ps.hdc, pd->lpwzContactName, wcslen(pd->lpwzContactName), &tr, DT_VCENTER | DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX); + + // second line(s) + int len_second = wcslen(pd->lpwzText); + if(len_second) { + if(hFontSecondLine) SelectObject(ps.hdc, (HGDIOBJ)hFontSecondLine); + if(!pwd->custom_col) + SetTextColor(ps.hdc, colSecondLine); + + // expand text if no avatar and the time isn't too large + if(options.av_layout != PAV_NONE && options.time_layout == PT_WITHAV && pwd->time_height <= pwd->tb_height && !pwd->have_av) + GetClientRect(hwnd, &r); + + tr.left = r.left + options.padding + options.text_indent; tr.right = r.right - options.padding; tr.top = tr.bottom + options.padding; tr.bottom = r.bottom - options.padding; + DrawText(ps.hdc, pd->lpwzText, len_second, &tr, DT_NOPREFIX | DT_WORDBREAK | DT_EXTERNALLEADING | DT_TOP | DT_LEFT | DT_WORD_ELLIPSIS); + } + + EndPaint(hwnd, &ps); + //} + } + return 0; + case WM_DESTROY: + if(pwd->mouse_in) global_mouse_in--; + + ShowWindow(hwnd, SW_HIDE); + + DeleteObject(pwd->bkBrush); + DeleteObject(pwd->bPen); + DeleteObject(pwd->barBrush); + KillTimer(hwnd, ID_MOVETIMER); + KillTimer(hwnd, ID_CLOSETIMER); + + RemoveWindowFromStack(hwnd); + + SendMessage(hwnd, UM_FREEPLUGINDATA, 0, 0); + + free(pd); pd = 0; + free(pwd); pwd = 0; + SetWindowLong(hwnd, GWL_USERDATA, 0); + + break; + case PUM_UPDATERGN: + // round corners + if(pwd->is_round) { + HRGN hRgn1; + RECT r; + + int v,h; + int w=11; + GetWindowRect(hwnd,&r); + h=(r.right-r.left)>(w*2)?w:(r.right-r.left); + v=(r.bottom-r.top)>(w*2)?w:(r.bottom-r.top); + h=(hnew_x = (int)wParam; + pwd->new_y = (int)lParam; + SetTimer(hwnd, ID_MOVETIMER, 10, 0); + } else { + SetWindowPos(hwnd, 0, (int)wParam, (int)lParam, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + if(!IsWindowVisible(hwnd)) { + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + UpdateWindow(hwnd); + } + } + } + return TRUE; + case PUM_SETTEXT: + { + wcscpy(pd->lpwzText, (wchar_t *)lParam); + // free((void *)lParam); // freed in message pump in case the window has gone + InvalidateRect(hwnd, 0, TRUE); + RepositionWindows(); + } + return TRUE; + + case PUM_GETCONTACT: + { + HANDLE *phContact = (HANDLE *)wParam; + *phContact = pd->lchContact; + if(lParam) SetEvent((HANDLE)lParam); + } + return TRUE; + case PUM_GETHEIGHT: + { + int *pHeight = (int *)wParam; + HDC hdc = GetDC(hwnd); + SIZE size; + + // time_height + width + if(options.time_layout != PT_NONE) { + SIZE size_t; + if(hFontTime) SelectObject(hdc, (HGDIOBJ)hFontTime); + GetTextExtentPoint32(hdc, pwd->tbuff, wcslen(pwd->tbuff), &size_t); + pwd->time_height = size_t.cy; + pwd->time_width = size_t.cx; + } + + // titlebar height + if(hFontFirstLine) SelectObject(hdc, (HGDIOBJ)hFontFirstLine); + GetTextExtentPoint32(hdc, pd->lpwzContactName, wcslen(pd->lpwzContactName), &size); + pwd->tb_height = size.cy; + if(options.time_layout == PT_LEFT || options.time_layout == PT_RIGHT) { + if(pwd->tb_height < pwd->time_height) pwd->tb_height = pwd->time_height; + } + if(pwd->tb_height < 16) pwd->tb_height = 16; + + // avatar height + if(options.av_layout != PAV_NONE && ServiceExists(MS_AV_DRAWAVATAR)) { + AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, (WPARAM)pd->lchContact, 0); + if(ace && (ace->dwFlags & AVS_BITMAP_VALID) && !(ace->dwFlags & AVS_HIDEONCLIST)) { + if(ace->bmHeight >= ace->bmWidth) { + pwd->real_av_height = options.av_size; + pwd->real_av_width = (int)(options.av_size * (ace->bmWidth / (double)ace->bmHeight)); + } else { + pwd->real_av_height = (int)(options.av_size * (ace->bmHeight / (double)ace->bmWidth)); + pwd->real_av_width = options.av_size; + } + pwd->have_av = true; + pwd->av_height = pwd->real_av_height; + } + } + + // text height + int len_second = wcslen(pd->lpwzText); + if(len_second) { + RECT r; + r.left = r.top = 0; + r.right = options.win_width - 2 * options.padding - options.text_indent; + if(pwd->have_av && options.time_layout == PT_WITHAV) + r.right -= (max(options.av_size, pwd->time_width) + options.padding); + else if(pwd->have_av) + r.right -= (options.av_size + options.padding); + else if(options.av_layout != PAV_NONE && options.time_layout == PT_WITHAV && pwd->time_height >= pwd->tb_height) + r.right -= pwd->time_width + options.padding; + + if(hFontSecondLine) SelectObject(hdc, (HGDIOBJ)hFontSecondLine); + DrawText(hdc, pd->lpwzText, len_second, &r, DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK | DT_EXTERNALLEADING | DT_TOP | DT_LEFT | DT_WORD_ELLIPSIS); + pwd->text_height = r.bottom; + } + + ReleaseDC(hwnd, hdc); + + if(options.time_layout == PT_WITHAV && options.av_layout != PAV_NONE) + *pHeight = max(pwd->tb_height + pwd->text_height + 3 * options.padding, pwd->av_height + pwd->time_height + options.padding + 2 * options.av_padding); + else + *pHeight = max(pwd->tb_height + pwd->text_height + 3 * options.padding, pwd->av_height + 2 * options.av_padding); + + if(*pHeight > options.win_max_height) *pHeight = options.win_max_height; + + RECT r; + GetWindowRect(hwnd, &r); + if(r.right - r.left != options.win_width || r.bottom - r.top != *pHeight) { + SetWindowPos(hwnd, 0, 0, 0, options.win_width, *pHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + SendMessage(hwnd, PUM_UPDATERGN, 0, 0); + InvalidateRect(hwnd, 0, TRUE); + } + } + return TRUE; + case PUM_GETPLUGINDATA: + { + void **pData = (void **)wParam; + if(pd) *pData = pd->PluginData; + if(lParam) SetEvent((HANDLE)lParam); + } + return TRUE; + case PUM_CHANGE: + { + KillTimer(hwnd, ID_CLOSETIMER); + free(pd); + pwd->pd = pd = (POPUPDATAW *)malloc(sizeof(POPUPDATAW)); + memcpy(pd, (void *)lParam, sizeof(POPUPDATAW)); // the passed in value is freed in the message pump, in case the window has gone + + if(pd->iSeconds != -1) { + if(pd->iSeconds == 0) { + SetTimer(hwnd, ID_CLOSETIMER, 7 * 1000, 0); + } else { + SetTimer(hwnd, ID_CLOSETIMER, pd->iSeconds * 1000, 0); + } + } else { + // make a really long timeout - say 7 days? ;) + SetTimer(hwnd, ID_CLOSETIMER, 7 * 24 * 60 * 60 * 1000, 0); + } + + InvalidateRect(hwnd, 0, TRUE); + RepositionWindows(); + } + return TRUE; + + case PUM_SETNOTIFYH: + pwd->hNotify = (HANDLE)wParam; + return TRUE; + + case PUM_UPDATENOTIFY: + if(pwd->hNotify == (HANDLE)wParam) { + pd->colorBack = MNotifyGetDWord(pwd->hNotify, NFOPT_BACKCOLOR, colBg); + pd->colorText = MNotifyGetDWord(pwd->hNotify, NFOPT_TEXTCOLOR, colSecondLine); + pd->iSeconds = MNotifyGetDWord(pwd->hNotify, NFOPT_TIMEOUT, options.default_timeout); + pd->lchContact = (HANDLE)MNotifyGetDWord(pwd->hNotify, NFOPT_CONTACT, 0); + pd->lchIcon = (HICON)MNotifyGetDWord(pwd->hNotify, NFOPT_ICON, 0); + const wchar_t *swzName = MNotifyGetWString(pwd->hNotify, NFOPT_TITLEW, 0); + if(swzName) { + wcsncpy(pd->lpwzContactName, swzName, MAX_CONTACTNAME); + } else { + const char *szName = MNotifyGetString(pwd->hNotify, NFOPT_TITLE, 0); + if(szName) MultiByteToWideChar(code_page, 0, szName, -1, pd->lpwzContactName, MAX_CONTACTNAME); + else pd->lpwzContactName[0] = 0; + } + pd->lpwzContactName[MAX_CONTACTNAME - 1] = 0; + + const wchar_t *swzText = MNotifyGetWString(pwd->hNotify, NFOPT_TEXTW, 0); + if(swzText) { + wcsncpy(pd->lpwzText, swzText, MAX_SECONDLINE); + } else { + const char *szText = MNotifyGetString(pwd->hNotify, NFOPT_TEXT, 0); + if(szText) MultiByteToWideChar(code_page, 0, szText, -1, pd->lpwzText, MAX_SECONDLINE); + else pd->lpwzText[0] = 0; + } + pd->lpwzText[MAX_SECONDLINE - 1] = 0; + InvalidateRect(hwnd, 0, TRUE); + RepositionWindows(); + } + + return TRUE; + case PUM_KILLNOTIFY: + if(pwd->hNotify != (HANDLE)wParam) + return TRUE; + // drop through + + case UM_DESTROYPOPUP: + PostMPMessage(MUM_DELETEPOPUP, 0, (LPARAM)hwnd); + return TRUE; + } + + + if(pd && pd->PluginWindowProc) + return CallWindowProc(pd->PluginWindowProc, hwnd, uMsg, wParam, lParam); + else { + // provide a way to close popups, if no PluginWindowProc is provided + if(uMsg == WM_CONTEXTMENU) { + SendMessage(hwnd, UM_DESTROYPOPUP, 0, 0); + return TRUE; + } else + return DefWindowProc(hwnd, uMsg, wParam, lParam); + } +} + +int AvatarChanged(WPARAM wParam, LPARAM lParam) { + RepositionWindows(); + return 0; +} + +void InitWindowStack() { + hUserDll = LoadLibrary(_T("user32.dll")); + if (hUserDll) { + MySetLayeredWindowAttributes = (BOOL (WINAPI *)(HWND,COLORREF,BYTE,DWORD))GetProcAddress(hUserDll, "SetLayeredWindowAttributes"); + MyAnimateWindow=(BOOL (WINAPI*)(HWND,DWORD,DWORD))GetProcAddress(hUserDll,"AnimateWindow"); + } +} + +void DeinitWindowStack() { + ClearStack(); + if(hUserDll) FreeLibrary(hUserDll); +} + diff --git a/yapp/popwin.h b/yapp/popwin.h new file mode 100644 index 0000000..951a6ba --- /dev/null +++ b/yapp/popwin.h @@ -0,0 +1,27 @@ +#ifndef _POPWIN_INC +#define _POPWIN_INC + +#define POP_WIN_CLASS _T(MODULE) _T("WinClass") + +#define PUM_SETTEXT (WM_USER + 0x020) +#define PUM_GETCONTACT (WM_USER + 0x021) +#define PUM_GETPLUGINDATA (WM_USER + 0x022) +#define PUM_CHANGE (WM_USER + 0x023) +#define PUM_MOVE (WM_USER + 0x024) +#define PUM_GETHEIGHT (WM_USER + 0x025) +#define PUM_UPDATERGN (WM_USER + 0x026) + +#define PUM_SETNOTIFYH (WM_USER + 0x030) +#define PUM_KILLNOTIFY (WM_USER + 0x031) +#define PUM_UPDATENOTIFY (WM_USER + 0x032) + +void InitWindowStack(); +void DeinitWindowStack(); + +void BroadcastMessage(UINT msg, WPARAM wParam, LPARAM lParam); +int AvatarChanged(WPARAM wParam, LPARAM lParam); // exposed so hook/unhook is in main thread + +LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + +#endif diff --git a/yapp/resource.h b/yapp/resource.h new file mode 100644 index 0000000..2ba26c1 --- /dev/null +++ b/yapp/resource.h @@ -0,0 +1,68 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by popups2.rc +// +#define IDD_OPT1 101 +#define IDD_DIALOG1 102 +#define IDD_OPT_NOTIFY 103 +#define IDD_LST_HISTORY 104 +#define IDC_RAD_NOTIMEOUT 1001 +#define IDC_RAD_TIMEOUT 1002 +#define IDC_ED_TIMEOUT 1003 +#define IDC_SPIN_TIMEOUT 1004 +#define IDC_ED_WIDTH 1005 +#define IDC_ED_MAXHEIGHT 1006 +#define IDC_SPIN_WIDTH 1007 +#define IDC_SPIN3 1008 +#define IDC_SPIN_MAXHEIGHT 1009 +#define IDC_RAD_BOTTOMRIGHT 1010 +#define IDC_ED_INDENT 1011 +#define IDC_RAD_BOTTOMLEFT 1012 +#define IDC_ED_SBWIDTH 1012 +#define IDC_SPIN_INDENT 1013 +#define IDC_RAD_TOPRIGHT 1014 +#define IDC_SPIN_SBWIDTH 1014 +#define IDC_RAD_TOPLEFT 1015 +#define IDC_ED_PADDING 1015 +#define IDC_ED_TRANS 1016 +#define IDC_SPIN_TRANS 1017 +#define IDC_CHK_BORDER 1018 +#define IDC_CHK_ROUNDCORNERS 1019 +#define IDC_BTN_PREVIEW 1020 +#define IDC_CHK_ANIMATE 1021 +#define IDC_CHK_TRANSBG 1022 +#define IDC_RAD_RIGHTICON 1023 +#define IDC_SPIN_INDENT2 1023 +#define IDC_SPIN_PADDING 1023 +#define IDC_CHK_GLOBALHOVER 1024 +#define IDC_RAD_LEFTICON 1025 +#define IDC_CHK_ROUNDCORNERSAV 1025 +#define IDC_RAD_NOAV 1026 +#define IDC_RAD_RIGHTAV 1027 +#define IDC_RAD_NOAV3 1028 +#define IDC_RAD_LEFTAV 1029 +#define IDC_ED_MAXHEIGHT2 1030 +#define IDC_ED_AVSIZE 1031 +#define IDC_SPIN_AVSIZE 1032 +#define IDC_LST_STATUS 1033 +#define IDC_CMB_PLACEMENT 1034 +#define IDC_CMB_ICON 1035 +#define IDC_CMB_AV 1036 +#define IDC_CMB_AV2 1037 +#define IDC_CMB_TIME 1038 +#define IDC_CUSTOM1 1039 +#define IDC_BORDERCOLOUR 1040 +#define IDC_CLOSE 1041 +#define IDC_LIST3 1042 +#define IDC_LST_HISTORY 1043 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 105 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1044 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/yapp/resource.rc b/yapp/resource.rc new file mode 100644 index 0000000..f39a424 --- /dev/null +++ b/yapp/resource.rc @@ -0,0 +1,6 @@ + +// this makes our dependencies work better +#include "version.h" + +#include "popups2.rc" +#include "version.rc" diff --git a/yapp/services.cpp b/yapp/services.cpp new file mode 100644 index 0000000..28f2b5b --- /dev/null +++ b/yapp/services.cpp @@ -0,0 +1,363 @@ +#include "common.h" +#include "services.h" +#include "popwin.h" +#include "message_pump.h" +#include "resource.h" +#include "popup_history.h" +#include + +#define NUM_SERVICES 13 +HANDLE hService[NUM_SERVICES]; +HANDLE hMenuShowHistory, hMenuToggleOnOff; + +void StripBBCodesInPlace(wchar_t *text) { + if(!DBGetContactSettingByte(0, MODULE, "StripBBCodes", 1)) + return; + + int read = 0, write = 0; + int len = wcslen(text); + + while(read <= len) { // copy terminating null too + while(read <= len && text[read] != L'[') { + if(text[read] != text[write]) text[write] = text[read]; + read++; write++; + } + if(read == len) break; + + if(len - read >= 3 && (wcsnicmp(text + read, L"[b]", 3) == 0 || wcsnicmp(text + read, L"[i]", 3) == 0)) + read += 3; + else if(len - read >= 4 && (wcsnicmp(text + read, L"[/b]", 4) == 0 || wcsnicmp(text + read, L"[/i]", 4) == 0)) + read += 4; + else if(len - read >= 6 && (wcsnicmp(text + read, L"[color", 6) == 0)) { + while(read < len && text[read] != L']') read++; + read++;// skip the ']' + } else if(len - read >= 8 && (wcsnicmp(text + read, L"[/color]", 8) == 0)) + read += 8; + else { + if(text[read] != text[write]) text[write] = text[read]; + read++; write++; + } + } +} + +int CreatePopupA(WPARAM wParam, LPARAM lParam) { + if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) return 0; + + POPUPDATA *pd_in = (POPUPDATA *)wParam; + POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW)); + + MultiByteToWideChar(code_page, 0, pd_in->lpzContactName, -1, pd_out->lpwzContactName, MAX_CONTACTNAME); + MultiByteToWideChar(code_page, 0, pd_in->lpzText, -1, pd_out->lpwzText, MAX_SECONDLINE); + pd_out->lpwzContactName[MAX_CONTACTNAME - 1] = 0; + pd_out->lpwzText[MAX_SECONDLINE - 1] = 0; + StripBBCodesInPlace(pd_out->lpwzContactName); + StripBBCodesInPlace(pd_out->lpwzText); + + pd_out->lchContact = pd_in->lchContact; + pd_out->lchIcon = pd_in->lchIcon; + if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg' + pd_out->colorBack = pd_out->colorText = 0; + else { + pd_out->colorBack = pd_in->colorBack & 0xFFFFFF; + pd_out->colorText = pd_in->colorText & 0xFFFFFF; + } + pd_out->PluginWindowProc = pd_in->PluginWindowProc; + pd_out->PluginData = pd_in->PluginData; + pd_out->iSeconds = 0; + + lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0)); + + if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) { + free(pd_out); + return 0; + } + + //MessageBox(0, pd_out->lpwzContactName, _T("CreatePopupA"), MB_OK); + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out); + return 0; +} + +int CreatePopupExA(WPARAM wParam, LPARAM lParam) { + + POPUPDATAEX *pd_in = (POPUPDATAEX *)wParam; + POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW)); + + MultiByteToWideChar(code_page, 0, pd_in->lpzContactName, -1, pd_out->lpwzContactName, MAX_CONTACTNAME); + MultiByteToWideChar(code_page, 0, pd_in->lpzText, -1, pd_out->lpwzText, MAX_SECONDLINE); + pd_out->lpwzContactName[MAX_CONTACTNAME - 1] = 0; + pd_out->lpwzText[MAX_SECONDLINE - 1] = 0; + StripBBCodesInPlace(pd_out->lpwzContactName); + StripBBCodesInPlace(pd_out->lpwzText); + + pd_out->lchContact = pd_in->lchContact; + pd_out->lchIcon = pd_in->lchIcon; + if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg' + pd_out->colorBack = pd_out->colorText = 0; + else { + pd_out->colorBack = pd_in->colorBack & 0xFFFFFF; + pd_out->colorText = pd_in->colorText & 0xFFFFFF; + } + pd_out->PluginWindowProc = pd_in->PluginWindowProc; + pd_out->PluginData = pd_in->PluginData; + pd_out->iSeconds = pd_in->iSeconds; + + lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0)); + if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) { + free(pd_out); + return 0; + } + + //MessageBox(0, pd_out->lpwzContactName, _T("CreatePopupExA"), MB_OK); + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out); + return 0; +} + +int CreatePopupW(WPARAM wParam, LPARAM lParam) { + + POPUPDATAW *pd_in = (POPUPDATAW *)wParam; + POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW)); + memcpy(pd_out, pd_in, sizeof(POPUPDATAW)); + pd_out->lpwzContactName[MAX_CONTACTNAME - 1] = 0; + pd_out->lpwzText[MAX_SECONDLINE - 1] = 0; + StripBBCodesInPlace(pd_out->lpwzContactName); + StripBBCodesInPlace(pd_out->lpwzText); + + if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg' + pd_out->colorBack = pd_out->colorText = 0; + else { + pd_out->colorBack &= 0xFFFFFF; + pd_out->colorText &= 0xFFFFFF; + } + + lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0)); + if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) { + free(pd_out); + return 0; + } + + //MessageBox(0, pd_out->lpwzContactName, _T("CreatePopupW"), MB_OK); + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out); + return 0; +} + +int ChangeTextW(WPARAM wParam, LPARAM lParam) { + HWND hwndPop = (HWND)wParam; + wchar_t *newText = wcsdup((wchar_t *)lParam); + StripBBCodesInPlace(newText); + + if(IsWindow(hwndPop)) + PostMessage(hwndPop, PUM_SETTEXT, 0, (LPARAM)newText); + return 0; +} + +int ChangeTextA(WPARAM wParam, LPARAM lParam) { + HWND hwndPop = (HWND)wParam; + char *newText = (char *)lParam; + wchar_t buff[MAX_SECONDLINE]; + MultiByteToWideChar(code_page, 0, newText, -1, buff, MAX_SECONDLINE); + buff[MAX_SECONDLINE - 1] = 0; + StripBBCodesInPlace(buff); + + if(IsWindow(hwndPop)) + PostMessage(hwndPop, PUM_SETTEXT, 0, (LPARAM)wcsdup(buff)); + return 0; +} + +int GetContact(WPARAM wParam, LPARAM lParam) { + HWND hwndPop = (HWND)wParam; + HANDLE hContact; + if(GetCurrentThreadId() == message_pump_thread_id) { + SendMessage(hwndPop, PUM_GETCONTACT, (WPARAM)&hContact, 0); + } else { + HANDLE hEvent = CreateEvent(0, 0, 0, 0); + PostMessage(hwndPop, PUM_GETCONTACT, (WPARAM)&hContact, (LPARAM)hEvent); + //WaitForSingleObject(hEvent, INFINITE); + MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, 0, 0); + CloseHandle(hEvent); + } + + return (int)hContact; +} + +int GetPluginData(WPARAM wParam, LPARAM lParam) { + HWND hwndPop = (HWND)wParam; + void *data = 0; + if(GetCurrentThreadId() == message_pump_thread_id) { + SendMessage(hwndPop, PUM_GETPLUGINDATA, (WPARAM)&data, 0); + } else { + HANDLE hEvent = CreateEvent(0, 0, 0, 0); + PostMessage(hwndPop, PUM_GETPLUGINDATA, (WPARAM)&data, (LPARAM)hEvent); + //WaitForSingleObject(hEvent, INFINITE); + MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, 0, 0); + CloseHandle(hEvent); + } + + if(lParam && !ignore_gpd_passed_addy) { + void **pData = (void **)lParam; + *pData = data; + } + + return (int)data; +} + +int IsSecondLineShown(WPARAM wParam, LPARAM lParam) { + return TRUE; +} + +int PopupQuery(WPARAM wParam, LPARAM lParam) { + switch(wParam) { + case PUQS_ENABLEPOPUPS: + { + bool enabled = (DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 1); + if(!enabled) DBWriteContactSettingByte(0, MODULE, "Enabled", 1); + return enabled ? 0: 1; + } + break; + case PUQS_DISABLEPOPUPS: + { + bool enabled = (DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 1); + if(enabled) DBWriteContactSettingByte(0, MODULE, "Enabled", 0); + DBWriteContactSettingByte(0, MODULE, "Enabled", 0); + return enabled ? 1 : 0; + } + break; + case PUQS_GETSTATUS: + return DBGetContactSettingByte(0, MODULE, "Enabled", 1); + default: + return 1; + } + return 0; +} + +int TogglePopups(WPARAM wParam, LPARAM lParam) { + CLISTMENUITEM menu; + menu.cbSize = sizeof(CLISTMENUITEM); + // just write to db - the rest is handled in the Meta_SettingChanged function + if(DBGetContactSettingByte(0, MODULE, "Enabled", 1)) { + DBWriteContactSettingByte(0, MODULE, "Enabled", 0); + // modify main menu item + menu.flags = CMIM_NAME;// | CMIM_ICON; + menu.hIcon=0; //LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MCMENUOFF)); + menu.pszName = (char *)Translate("Enable Popups"); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuToggleOnOff, (LPARAM)&menu); + } else { + DBWriteContactSettingByte(0, MODULE, "Enabled", 1); + // modify main menu item + menu.flags = CMIM_NAME;// | CMIM_ICON; + menu.hIcon=0;//LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MCMENU)); + menu.pszName = (char *)Translate("Disable Popups"); + CallService(MS_CLIST_MODIFYMENUITEM, (WPARAM)hMenuToggleOnOff, (LPARAM)&menu); + } + return 0; +} + +int PopupChange(WPARAM wParam, LPARAM lParam) { + HWND hwndPop = (HWND)wParam; + POPUPDATAEX *pd_in = (POPUPDATAEX *)lParam; + + if(IsWindow(hwndPop)) { + POPUPDATAW *pd_out = (POPUPDATAW *)malloc(sizeof(POPUPDATAW)); + + MultiByteToWideChar(code_page, 0, pd_in->lpzContactName, -1, pd_out->lpwzContactName, MAX_CONTACTNAME); + MultiByteToWideChar(code_page, 0, pd_in->lpzText, -1, pd_out->lpwzText, MAX_SECONDLINE); + + pd_out->lchContact = pd_in->lchContact; + pd_out->lchIcon = pd_in->lchIcon; + if(pd_in->colorBack == 0xffffffff) // that's the old #define for 'skinned bg' + pd_out->colorBack = pd_out->colorText = 0; + else { + pd_out->colorBack = pd_in->colorBack & 0xFFFFFF; + pd_out->colorText = pd_in->colorText & 0xFFFFFF; + } + pd_out->colorBack = pd_in->colorBack; + pd_out->colorText = pd_in->colorText; + pd_out->PluginWindowProc = pd_in->PluginWindowProc; + pd_out->PluginData = pd_in->PluginData; + pd_out->iSeconds = pd_in->iSeconds; + + lstPopupHistory.Add(pd_out->lpwzContactName, pd_out->lpwzText, time(0)); + + PostMessage(hwndPop, PUM_CHANGE, 0, (LPARAM)pd_out); + } + return 0; +} + +int ShowMessage(WPARAM wParam, LPARAM lParam) { + if(!DBGetContactSettingByte(0, MODULE, "Enabled", 1)) return 0; + + POPUPDATAW pd = {0}; + wcscpy(pd.lpwzContactName, lParam == SM_WARNING ? _T("Warning") : _T("Notification")); + pd.lchIcon = LoadIcon(0, lParam == SM_WARNING ? IDI_WARNING : IDI_INFORMATION); + wchar_t buff[MAX_SECONDLINE]; + MultiByteToWideChar(code_page, 0, (char *)wParam, -1, buff, MAX_SECONDLINE); + wcscpy(pd.lpwzText, buff); + + CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0); + + return 0; +} + +//=====PopUp/ShowHistory +//extern BOOL CALLBACK DlgProcHistLstOpts(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +int PopUp_ShowHistory(WPARAM wParam, LPARAM lParam) +{ + if (!hHistoryWindow) { + hHistoryWindow = CreateDialog(hInst, MAKEINTRESOURCE(IDD_LST_HISTORY), NULL, DlgProcHistLst); + } + ShowWindow(hHistoryWindow, SW_SHOW); + return 0; +} + +int PrebuildMenu(WPARAM wParam, LPARAM lParam) { + return 0; +} + +HANDLE hEventBuildMenu; + +void InitServices() { + int i = 0; + hService[i++] = CreateServiceFunction(MS_POPUP_ADDPOPUP, CreatePopupA); + hService[i++] = CreateServiceFunction(MS_POPUP_ADDPOPUPEX, CreatePopupExA); + hService[i++] = CreateServiceFunction(MS_POPUP_ADDPOPUPW, CreatePopupW); + hService[i++] = CreateServiceFunction(MS_POPUP_CHANGETEXTW, ChangeTextW); + hService[i++] = CreateServiceFunction(MS_POPUP_CHANGETEXT, ChangeTextA); + hService[i++] = CreateServiceFunction(MS_POPUP_CHANGE, PopupChange); + hService[i++] = CreateServiceFunction(MS_POPUP_GETCONTACT, GetContact); + hService[i++] = CreateServiceFunction(MS_POPUP_GETPLUGINDATA, GetPluginData); + hService[i++] = CreateServiceFunction(MS_POPUP_ISSECONDLINESHOWN, IsSecondLineShown); + hService[i++] = CreateServiceFunction(MS_POPUP_QUERY, PopupQuery); + + hService[i++] = CreateServiceFunction(MS_POPUP_SHOWMESSAGE, ShowMessage); + + hService[i++] = CreateServiceFunction(MS_POPUP_SHOWHISTORY, PopUp_ShowHistory); + hService[i++] = CreateServiceFunction("PopUp/ToggleEnabled", TogglePopups); + + CLISTMENUITEM menu = {0}; + + menu.cbSize=sizeof(menu); + menu.flags = CMIM_ALL; + + menu.hIcon=0; + menu.position = 500010000; + menu.pszPopupName = Translate("PopUps"); + + menu.pszService= MS_POPUP_SHOWHISTORY; + menu.pszName = Translate("Popup History"); + hMenuShowHistory = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&menu); + + menu.pszService= "PopUp/ToggleEnabled"; + menu.pszName = (DBGetContactSettingByte(0, MODULE, "Enabled", 1) == 1 ? Translate("Disable Popups") : Translate("Enable Popups")); + hMenuToggleOnOff = (HANDLE)CallService(MS_CLIST_ADDMAINMENUITEM, 0, (LPARAM)&menu); + + hEventBuildMenu = HookEvent(ME_CLIST_PREBUILDCONTACTMENU, PrebuildMenu); + + if(ServiceExists(MS_LANGPACK_GETCODEPAGE)) + code_page = CallService(MS_LANGPACK_GETCODEPAGE, 0, 0); +} + +void DeinitServices() { + UnhookEvent(hEventBuildMenu); + + for(int i = 0; i < NUM_SERVICES; i++) + if(hService[i]) DestroyServiceFunction(hService[i]); +} diff --git a/yapp/services.h b/yapp/services.h new file mode 100644 index 0000000..35f6285 --- /dev/null +++ b/yapp/services.h @@ -0,0 +1,25 @@ +#ifndef _SERVICES_INC +#define _SERVICES_INC + +void InitServices(); +void DeinitServices(); + +/* +int CreatePopupA(WPARAM wParam, LPARAM lParam); +int CreatePopupExA(WPARAM wParam, LPARAM lParam); +int CreatePopupW(WPARAM wParam, LPARAM lParam); +int ChangeTextW(WPARAM wParam, LPARAM lParam); +int ChangeTextA(WPARAM wParam, LPARAM lParam); +int GetContact(WPARAM wParam, LPARAM lParam); +int GetPluginData(WPARAM wParam, LPARAM lParam); +int IsSecondLineShown(WPARAM wParam, LPARAM lParam); +int PopupQuery(WPARAM wParam, LPARAM lParam); +int PopupChange(WPARAM wParam, LPARAM lParam); +int ShowMessage(WPARAM wParam, LPARAM lParam); + +int PopUp_ShowHistory(WPARAM wParam, LPARAM lParam); + +int TogglePopups(WPARAM wParam, LPARAM lParam); +*/ + +#endif diff --git a/yapp/version.h b/yapp/version.h new file mode 100644 index 0000000..c02ba06 --- /dev/null +++ b/yapp/version.h @@ -0,0 +1,23 @@ +#ifndef __VERSION_H_INCLUDED +#define __VERSION_H_INCLUDED + +#define __MAJOR_VERSION 0 +#define __MINOR_VERSION 1 +#define __RELEASE_NUM 4 +#define __BUILD_NUM 3 + +#define __FILEVERSION_STRING __MAJOR_VERSION,__MINOR_VERSION,__RELEASE_NUM,__BUILD_NUM +#define __FILEVERSION_STRING_DOTS __MAJOR_VERSION.__MINOR_VERSION.__RELEASE_NUM.__BUILD_NUM +#define __STRINGIFY(x) #x +#define __VERSION_STRING __STRINGIFY(__FILEVERSION_STRING_DOTS) + +#define __DESC "Yet Another Popup Plugin - provides popup notification window services (unicode and ansi) to other plugins" +#define __AUTHOR "Scott Ellis" +#define __AUTHOREMAIL "mail@scottellis.com.au" +#define __COPYRIGHT "© 2005,2006 Scott Ellis" +#define __AUTHORWEB "http://www.scottellis.com.au" + +#define __PLUGIN_NAME "YAPP" +#define __FILENAME "yapp.dll" + +#endif //__VERSION_H_INCLUDED diff --git a/yapp/version.rc b/yapp/version.rc new file mode 100644 index 0000000..842659d --- /dev/null +++ b/yapp/version.rc @@ -0,0 +1,34 @@ + +#include +#include "version.h" + +VS_VERSION_INFO VERSIONINFO + FILEVERSION __FILEVERSION_STRING + PRODUCTVERSION __FILEVERSION_STRING + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "000004b0" + BEGIN + VALUE "Author", __AUTHOR + VALUE "FileDescription", __DESC + VALUE "FileVersion", __VERSION_STRING + VALUE "InternalName", __PLUGIN_NAME + VALUE "LegalCopyright", __COPYRIGHT + VALUE "OriginalFilename", __FILENAME + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x0, 1200 + END +END -- cgit v1.2.3