diff options
Diffstat (limited to 'src')
28 files changed, 3593 insertions, 0 deletions
diff --git a/src/core/stdpopup/res/popup.ico b/src/core/stdpopup/res/popup.ico Binary files differnew file mode 100644 index 0000000000..5b3fe4e65b --- /dev/null +++ b/src/core/stdpopup/res/popup.ico diff --git a/src/core/stdpopup/res/popup_history.ico b/src/core/stdpopup/res/popup_history.ico Binary files differnew file mode 100644 index 0000000000..6c1905b295 --- /dev/null +++ b/src/core/stdpopup/res/popup_history.ico diff --git a/src/core/stdpopup/res/popup_no.ico b/src/core/stdpopup/res/popup_no.ico Binary files differnew file mode 100644 index 0000000000..cac10ab068 --- /dev/null +++ b/src/core/stdpopup/res/popup_no.ico diff --git a/src/core/stdpopup/res/resource.rc b/src/core/stdpopup/res/resource.rc new file mode 100644 index 0000000000..da3edb45ab --- /dev/null +++ b/src/core/stdpopup/res/resource.rc @@ -0,0 +1,195 @@ +// Microsoft Visual C++ generated resource script. +// +#include "..\src\resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include <winres.h> + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "..\\src\\resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Dialog +// + +IDD_LST_HISTORY DIALOGEX 0, 0, 467, 303 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME +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 + EDITTEXT IDC_HISTORY_FILTER,4,285,176,14,ES_AUTOHSCROLL + CONTROL "Case sensitive",IDC_HISTORY_FILTER_CASESENSITIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,185,287,222,10 +END + +IDD_OPT1 DIALOGEX 0, 0, 297, 218 +STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 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 + COMBOBOX IDC_CMB_ANIMATE,25,160,120,68,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP + 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,173,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 +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 0, 0, 0x1 +BEGIN +END + +IDD_CLASSES DIALOGEX 0, 0, 294, 223 +STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_CHILD +EXSTYLE WS_EX_CONTROLPARENT +FONT 8, "MS Shell Dlg", 400, 0, 0x1 +BEGIN + LISTBOX IDC_LST_CLASSES,7,18,102,184,LBS_SORT | LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP + RTEXT "Text color:",IDC_STATIC,136,66,80,8 + RTEXT "Background color:",IDC_STATIC,136,88,80,8 + CONTROL "Set timeout",IDC_CHK_TIMEOUT,"Button",BS_AUTOCHECKBOX | WS_DISABLED | WS_TABSTOP,144,122,99,10 + RTEXT "Timeout value (0 = default):",IDC_STATIC,114,143,121,8 + EDITTEXT IDC_ED_TIMEOUT,243,140,43,14,ES_RIGHT | ES_AUTOHSCROLL | ES_NUMBER | WS_DISABLED + CONTROL "Custom1",IDC_COL_TEXT,"ColourPicker",WS_DISABLED | WS_TABSTOP,243,61,44,17 + CONTROL "Custom1",IDC_COL_BG,"ColourPicker",WS_DISABLED | WS_TABSTOP,243,82,44,17 + PUSHBUTTON "Preview",IDC_BTN_PREVIEW,141,178,130,17,WS_DISABLED +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 + + IDD_CLASSES, DIALOG + BEGIN + LEFTMARGIN, 7 + RIGHTMARGIN, 287 + TOPMARGIN, 7 + BOTTOMMARGIN, 216 + END +END +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_POPUP ICON "popup.ico" +IDI_NOPOPUP ICON "popup_no.ico" +IDI_HISTORY ICON "popup_history.ico" +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/src/core/stdpopup/res/version.rc b/src/core/stdpopup/res/version.rc new file mode 100644 index 0000000000..5a5ddd63ed --- /dev/null +++ b/src/core/stdpopup/res/version.rc @@ -0,0 +1,9 @@ +// Microsoft Visual C++ generated resource script. +// +#ifdef APSTUDIO_INVOKED +#error this file is not editable by Microsoft Visual C++ +#endif //APSTUDIO_INVOKED + +#include "..\src\version.h" + +#include "..\..\build\Version.rc" diff --git a/src/core/stdpopup/src/icons.cpp b/src/core/stdpopup/src/icons.cpp new file mode 100644 index 0000000000..4b0a61c819 --- /dev/null +++ b/src/core/stdpopup/src/icons.cpp @@ -0,0 +1,22 @@ +#include "stdafx.h" + +static IconItem iconList1[] = +{ + // toolbar + { LPGEN("Popups are enabled"), ICO_TB_POPUP_ON, IDI_POPUP }, + { LPGEN("Popups are disabled"), ICO_TB_POPUP_OFF, IDI_NOPOPUP } +}; + +static IconItem iconList2[] = +{ + // common + { LPGEN("Popups are enabled"), ICO_POPUP_ON, IDI_POPUP }, + { LPGEN("Popups are disabled"), ICO_POPUP_OFF, IDI_NOPOPUP }, + { LPGEN("Popup History"), ICO_HISTORY, IDI_HISTORY }, +}; + +void InitIcons() +{ + g_plugin.registerIcon(SECT_TOLBAR, iconList1); + g_plugin.registerIcon(SECT_POPUP, iconList2); +} diff --git a/src/core/stdpopup/src/icons.h b/src/core/stdpopup/src/icons.h new file mode 100644 index 0000000000..6b35713b67 --- /dev/null +++ b/src/core/stdpopup/src/icons.h @@ -0,0 +1,16 @@ +#ifndef __icons_h__ +#define __icons_h__ + +//ICONS +#define SECT_TOLBAR "Toolbar" +#define SECT_POPUP MODULENAME + +#define ICO_TB_POPUP_ON "TBButton_popup_ToogleUp" +#define ICO_TB_POPUP_OFF "TBButton_popup_ToogleDOWN" +#define ICO_POPUP_ON MODULENAME"_enabled" +#define ICO_POPUP_OFF MODULENAME"_disabled" +#define ICO_HISTORY MODULENAME"_history" + +void InitIcons(); + +#endif // __icons_h__ diff --git a/src/core/stdpopup/src/message_pump.cpp b/src/core/stdpopup/src/message_pump.cpp new file mode 100644 index 0000000000..977f139219 --- /dev/null +++ b/src/core/stdpopup/src/message_pump.cpp @@ -0,0 +1,110 @@ +#include "stdafx.h" + +unsigned message_pump_thread_id = 0; +int num_popups = 0; + +HANDLE hMPEvent; + +#define MUM_FINDWINDOW (WM_USER + 0x050) + +#define MAX_POPUPS 100 + +unsigned __stdcall MessagePumpThread(void* param) +{ + if (param) + SetEvent((HANDLE)param); + + MSG hwndMsg = {}; + while (GetMessage(&hwndMsg, nullptr, 0, 0) > 0 && !bShutdown) { + if (hwndMsg.hwnd != nullptr && IsDialogMessage(hwndMsg.hwnd, &hwndMsg)) /* Wine fix. */ + continue; + 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 && IsFullScreen()) || IsWorkstationLocked()) + enabled = false; + + PopupData *pd = (PopupData*)hwndMsg.lParam; + if (enabled && num_popups < MAX_POPUPS) { + HWND hwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TOPMOST, POP_WIN_CLASS, L"Popup", WS_POPUP, 0, 0, 0, 0, nullptr, nullptr, g_plugin.getInst(), (LPVOID)hwndMsg.lParam); + num_popups++; + if (hwndMsg.wParam) // set notifyer handle + SendMessage(hwnd, PUM_SETNOTIFYH, hwndMsg.wParam, 0); + } + else if (pd) { + mir_free(pd->pwzTitle); + mir_free(pd->pwzText); + mir_free(pd); + } + } + break; + + case MUM_DELETEPOPUP: + { + HWND hwnd = (HWND)hwndMsg.lParam; + if (IsWindow(hwnd)) { + DestroyWindow(hwnd); + num_popups--; + } + } + break; + + case MUM_NMUPDATE: + BroadcastMessage(PUM_UPDATENOTIFY, hwndMsg.wParam, 0); + break; + + case MUM_NMREMOVE: + BroadcastMessage(PUM_KILLNOTIFY, hwndMsg.wParam, 0); + break; + + case MUM_NMAVATAR: + RepositionWindows(); + break; + + default: + TranslateMessage(&hwndMsg); + DispatchMessage(&hwndMsg); + break; + } + } + + DeinitWindowStack(); + num_popups = 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 InitMessagePump() +{ + WNDCLASS popup_win_class = { 0 }; + popup_win_class.lpfnWndProc = PopupWindowProc; + popup_win_class.hInstance = g_plugin.getInst(); + popup_win_class.lpszClassName = POP_WIN_CLASS; + popup_win_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + RegisterClass(&popup_win_class); + + InitServices(); + + hMPEvent = CreateEvent(nullptr, TRUE, 0, nullptr); + CloseHandle(mir_forkthreadex(MessagePumpThread, hMPEvent, &message_pump_thread_id)); + WaitForSingleObject(hMPEvent, INFINITE); + CloseHandle(hMPEvent); +} + +void DeinitMessagePump() +{ + PostMPMessage(WM_QUIT, 0, 0); +} diff --git a/src/core/stdpopup/src/message_pump.h b/src/core/stdpopup/src/message_pump.h new file mode 100644 index 0000000000..e58d039850 --- /dev/null +++ b/src/core/stdpopup/src/message_pump.h @@ -0,0 +1,47 @@ +#ifndef _MESSAGE_PUMP_INC +#define _MESSAGE_PUMP_INC + +extern unsigned 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) +#define MUM_NMAVATAR (WM_USER + 0x015) + +struct PopupData +{ + int cbSize; + int flags; // OR of PDF_* flags below + MCONTACT hContact; + HICON hIcon; + union { + char *pszTitle; + wchar_t *ptzTitle; + wchar_t *pwzTitle; + }; + union { + char *pszText; + wchar_t *ptzText; + wchar_t *pwzText; + }; + COLORREF colorBack; // if colorBack and colorText are equal, defaults will be used + COLORREF colorText; + WNDPROC windowProc; // optional custom window procedure + int timeout; // -1 == infinite, 0 == default, otherwise timeout in seconds + void *opaque; + + void SetIcon(HICON); +}; + +// 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 InitMessagePump(); +void DeinitMessagePump(); + +#endif diff --git a/src/core/stdpopup/src/notify.h b/src/core/stdpopup/src/notify.h new file mode 100644 index 0000000000..014670a963 --- /dev/null +++ b/src/core/stdpopup/src/notify.h @@ -0,0 +1,6 @@ +#ifndef _NOTIFY_IMP_INC +#define _NOTIFY_IMP_INC + +void InitNotify(); + +#endif diff --git a/src/core/stdpopup/src/notify_imp.cpp b/src/core/stdpopup/src/notify_imp.cpp new file mode 100644 index 0000000000..aaa7ae45d5 --- /dev/null +++ b/src/core/stdpopup/src/notify_imp.cpp @@ -0,0 +1,126 @@ +#include "stdafx.h" + +//struct + +int Popup2Show(WPARAM, LPARAM lParam) +{ + + HANDLE hNotify = (HANDLE)lParam; + + PopupData *pd_out = (PopupData *)mir_alloc(sizeof(PopupData)); + memset(pd_out, 0, sizeof(PopupData)); + + PostMPMessage(MUM_CREATEPOPUP, (WPARAM)hNotify, (LPARAM)pd_out); + PostMPMessage(MUM_NMUPDATE, (WPARAM)hNotify, 0); + return 0; +} + +INT_PTR svcPopup2Show(WPARAM wParam, LPARAM lParam) +{ + return Popup2Show(wParam, lParam); +} + +int Popup2Update(WPARAM, LPARAM lParam) +{ + HANDLE hNotify = (HANDLE)lParam; + PostMPMessage(MUM_NMUPDATE, (WPARAM)hNotify, 0); + return 0; +} + +int AvatarChanged(WPARAM, LPARAM) +{ + PostMPMessage(MUM_NMAVATAR, 0, 0); + return 0; +} + +INT_PTR svcPopup2Update(WPARAM wParam, LPARAM lParam) +{ + return Popup2Update(wParam, lParam); +} + +int Popup2Remove(WPARAM, LPARAM lParam) +{ + HANDLE hNotify = (HANDLE)lParam; + PostMPMessage(MUM_NMREMOVE, (WPARAM)hNotify, 0); + return 0; +} + +INT_PTR svcPopup2Remove(WPARAM wParam, LPARAM lParam) +{ + return Popup2Remove(wParam, lParam); +} + +INT_PTR svcPopup2DefaultActions(WPARAM wParam, 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; +} + +INT_PTR CALLBACK DlgProcPopups(HWND hwnd, UINT msg, WPARAM, LPARAM lParam) +{ + /* To change options use MNotifySet*(hNotify, ....) Apply/Cancel is implemented in notify.dll */ + HANDLE hNotify = (HANDLE)GetWindowLongPtr(hwnd, GWLP_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; + SetWindowLongPtr(hwnd, GWLP_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) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT_NOTIFY); + odp.szTitle.a = LPGEN("YAPP Popups"); + odp.flags = ODPF_BOLDGROUPS; + odp.pfnDlgProc = DlgProcPopups; + CallService(MS_NOTIFY_OPT_ADDPAGE, wParam, (LPARAM)&odp); + return 0; +} + +int NotifyModulesLoaded(WPARAM, LPARAM) +{ + HookEvent(ME_NOTIFY_OPT_INITIALISE, NotifyOptionsInitialize); + HookEvent(ME_AV_AVATARCHANGED, AvatarChanged); + return 0; +} + +void InitNotify() +{ + HookEvent(ME_NOTIFY_SHOW, Popup2Show); + HookEvent(ME_NOTIFY_UPDATE, Popup2Update); + HookEvent(ME_NOTIFY_REMOVE, Popup2Remove); + + CreateServiceFunction("Popup2/DefaultActions", svcPopup2DefaultActions); + + CreateServiceFunction(MS_POPUP2_SHOW, svcPopup2Show); + CreateServiceFunction(MS_POPUP2_UPDATE, svcPopup2Update); + CreateServiceFunction(MS_POPUP2_REMOVE, svcPopup2Remove); + + HookEvent(ME_SYSTEM_MODULESLOADED, NotifyModulesLoaded); +} diff --git a/src/core/stdpopup/src/options.cpp b/src/core/stdpopup/src/options.cpp new file mode 100644 index 0000000000..c30ed0635e --- /dev/null +++ b/src/core/stdpopup/src/options.cpp @@ -0,0 +1,499 @@ +#include "stdafx.h" + +Options options; + +HICON hPopupIcon = nullptr; + +void LoadModuleDependentOptions() { + if (ServiceExists(MS_AV_DRAWAVATAR)) + options.av_layout = (PopupAvLayout)g_plugin.getByte("AVLayout", PAV_RIGHT); + else + options.av_layout = PAV_NONE; + + options.time_layout = (PopupTimeLayout)g_plugin.getByte("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 = g_plugin.getDword("DefaultTimeout", 7); + options.win_width = g_plugin.getDword("WinWidth", 220); + options.win_max_height = g_plugin.getDword("WinMaxHeight", 400); + options.location = (PopupLocation)g_plugin.getByte("Location", (BYTE)PL_BOTTOMRIGHT); + options.opacity = g_plugin.getByte("Opacity", 75); + options.border = g_plugin.getByte("Border", 1) == 1; + options.round = g_plugin.getByte("RoundCorners", 1) == 1; + options.av_round = g_plugin.getByte("AvatarRoundCorners", 1) == 1; + options.animate = g_plugin.getByte("Animate", 0); + options.trans_bg = g_plugin.getByte("TransparentBg", 0) == 1; + options.use_mim_monitor = g_plugin.getByte("UseMimMonitor", 1) == 1; + options.right_icon = g_plugin.getByte("RightIcon", 0) == 1; + options.av_layout = PAV_NONE; // corrected in LoadModuleDependentOptions function above + options.av_size = g_plugin.getDword("AVSize", 40); //tweety + options.text_indent = g_plugin.getDword("TextIndent", 22); + options.global_hover = g_plugin.getByte("GlobalHover", 1) == 1; + options.time_layout = (PopupTimeLayout)g_plugin.getByte("TimeLayout", PT_RIGHT); + + char buff[128]; + for (int i = 0; i < 10; i++) { + mir_snprintf(buff, "DisableStatus%d", i - 1); // -1 because i forgot offline status earlier! + options.disable_status[i] = (g_plugin.getByte(buff, 0) == 1); + } + + options.disable_full_screen = g_plugin.getByte("DisableFullScreen", 1) == 1; + options.drop_shadow = g_plugin.getByte("DropShadow", 0) == 1; + options.sb_width = g_plugin.getDword("SidebarWidth", 22); + options.padding = g_plugin.getDword("Padding", 4); + options.av_padding = g_plugin.getDword("AvatarPadding", 4); +} + +void SaveOptions() +{ + g_plugin.setDword("DefaultTimeout", options.default_timeout); + g_plugin.setDword("WinWidth", options.win_width); + g_plugin.setDword("WinMaxHeight", options.win_max_height); + g_plugin.setByte("Location", (BYTE)options.location); + g_plugin.setByte("Opacity", (BYTE)options.opacity); + g_plugin.setByte("Border", (options.border ? 1 : 0)); + g_plugin.setByte("RoundCorners", (options.round ? 1 : 0)); + g_plugin.setByte("AvatarRoundCorners", (options.av_round ? 1 : 0)); + g_plugin.setByte("Animate", options.animate); + g_plugin.setByte("TransparentBg", (options.trans_bg ? 1 : 0)); + g_plugin.setByte("UseMimMonitor", (options.use_mim_monitor ? 1 : 0)); + g_plugin.setByte("RightIcon", (options.right_icon ? 1 : 0)); + if (ServiceExists(MS_AV_DRAWAVATAR)) + g_plugin.setByte("AVLayout", (BYTE)options.av_layout); + g_plugin.setDword("AVSize", options.av_size); + g_plugin.setDword("TextIndent", options.text_indent); + g_plugin.setByte("GlobalHover", (options.global_hover ? 1 : 0)); + g_plugin.setByte("TimeLayout", (BYTE)options.time_layout); + + char buff[128]; + for (int i = 0; i < 9; i++) { + mir_snprintf(buff, "DisableStatus%d", i - 1); + g_plugin.setByte(buff, options.disable_status[i] ? 1 : 0); + } + g_plugin.setByte("DisableFullScreen", (options.disable_full_screen ? 1 : 0)); + g_plugin.setByte("DropShadow", (options.drop_shadow ? 1 : 0)); + g_plugin.setDword("SidebarWidth", options.sb_width); + g_plugin.setDword("Padding", options.padding); + g_plugin.setDword("AvatarPadding", options.av_padding); +} + +void ShowExamplePopups() +{ + PopupData pd = {sizeof(PopupData)}; + pd.hIcon = hPopupIcon; + pd.flags = PDF_TCHAR; + + pd.ptzTitle = TranslateT("Example"); + pd.ptzText = TranslateT("The quick brown fox jumped over the lazy dog."); + ShowPopup(pd); + + pd.ptzTitle = TranslateT("Example With a Long Title"); + pd.ptzText = TranslateT("The quick brown fox jumped over the lazy dog."); + ShowPopup(pd); + + pd.ptzTitle = TranslateT("Example"); + pd.ptzText = TranslateT("Thequickbrownfoxjumpedoverthelazydog."); + ShowPopup(pd); + + for (auto &hContact : Contacts()) { + if (options.av_layout != PAV_NONE && ServiceExists(MS_AV_DRAWAVATAR)) { + AVATARCACHEENTRY *ace = (AVATARCACHEENTRY *)CallService(MS_AV_GETAVATARBITMAP, hContact, 0); + if (ace && (ace->dwFlags & AVS_BITMAP_VALID)) { + pd.hContact = hContact; + pd.ptzText = TranslateT("An avatar."); + ShowPopup(pd); + break; + } + } + } +} + +static INT_PTR 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 ? BST_CHECKED : BST_UNCHECKED); + + { + // 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; + + int i = 0; + for (; i < 10; i++) { + lvI.pszText = Clist_GetStatusModeDescription(ID_STATUS_OFFLINE + i, 0); + 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, BST_CHECKED); + HWND hw = GetDlgItem(hwndDlg, IDC_ED_TIMEOUT); + EnableWindow(hw, FALSE); + SetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, 7, FALSE); + } + else { + CheckDlgButton(hwndDlg, IDC_RAD_TIMEOUT, BST_CHECKED); + SetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, options.default_timeout, FALSE); + } + + if (options.right_icon) + CheckDlgButton(hwndDlg, IDC_RAD_RIGHTICON, BST_CHECKED); + else + CheckDlgButton(hwndDlg, IDC_RAD_LEFTICON, BST_CHECKED); + + if (ServiceExists(MS_AV_DRAWAVATAR)) { + switch(options.av_layout) { + case PAV_NONE: CheckDlgButton(hwndDlg, IDC_RAD_NOAV, BST_CHECKED); break; + case PAV_RIGHT: CheckDlgButton(hwndDlg, IDC_RAD_RIGHTAV, BST_CHECKED); break; + case PAV_LEFT: CheckDlgButton(hwndDlg, IDC_RAD_LEFTAV, BST_CHECKED); break; + } + } + else { + CheckDlgButton(hwndDlg, IDC_RAD_NOAV, BST_CHECKED); + 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, BST_CHECKED); break; + case PL_BOTTOMLEFT: CheckDlgButton(hwndDlg, IDC_RAD_BOTTOMLEFT, BST_CHECKED); break; + case PL_TOPRIGHT: CheckDlgButton(hwndDlg, IDC_RAD_TOPRIGHT, BST_CHECKED); break; + case PL_TOPLEFT: CheckDlgButton(hwndDlg, IDC_RAD_TOPLEFT, BST_CHECKED); break; + } + + SetDlgItemInt(hwndDlg, IDC_ED_TRANS, options.opacity, FALSE); + CheckDlgButton(hwndDlg, IDC_CHK_BORDER, options.border ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_ROUNDCORNERS, options.round ? BST_CHECKED : BST_UNCHECKED); + CheckDlgButton(hwndDlg, IDC_CHK_ROUNDCORNERSAV, options.av_round ? BST_CHECKED : BST_UNCHECKED); + + SendDlgItemMessage(hwndDlg, IDC_CMB_ANIMATE, CB_ADDSTRING, 0, (LPARAM)TranslateT("No animate")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ANIMATE, CB_ADDSTRING, 0, (LPARAM)TranslateT("Horizontal animate")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ANIMATE, CB_ADDSTRING, 0, (LPARAM)TranslateT("Vertical animate")); + SendDlgItemMessage(hwndDlg, IDC_CMB_ANIMATE, CB_SETCURSEL, options.animate, 0); + + CheckDlgButton(hwndDlg, IDC_CHK_TRANSBG, options.trans_bg ? BST_CHECKED : BST_UNCHECKED); + 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 { + HWND hw = GetDlgItem(hwndDlg, IDC_ED_TIMEOUT); + switch( LOWORD(wParam)) { + case IDC_RAD_NOTIMEOUT: + EnableWindow(hw, IsDlgButtonChecked(hwndDlg, IDC_RAD_TIMEOUT)); + break; + case IDC_RAD_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 = SendDlgItemMessage(hwndDlg, IDC_CMB_ANIMATE, CB_GETCURSEL, 0, 0); + 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; +} + +LIST<POPUPCLASS> arNewClasses(3); + +static INT_PTR CALLBACK DlgProcOptsClasses(HWND hwndDlg, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch ( msg ) { + case WM_INITDIALOG: + TranslateDialogDefault( hwndDlg ); + + arNewClasses = arClasses; + { + int index; + for (int i = 0; i < arNewClasses.getCount(); i++) { + POPUPCLASS *pc = arNewClasses[i]; + if (pc->flags & PCF_UNICODE) + index = SendDlgItemMessageW(hwndDlg, IDC_LST_CLASSES, LB_ADDSTRING, 0, (LPARAM)pc->pszDescription.w); + else + index = SendDlgItemMessageA(hwndDlg, IDC_LST_CLASSES, LB_ADDSTRING, 0, (LPARAM)pc->pszDescription.a); + + SendDlgItemMessage(hwndDlg, IDC_LST_CLASSES, LB_SETITEMDATA, index, i); + } + } + return FALSE; + + case WM_COMMAND: + if ( LOWORD(wParam) == IDC_LST_CLASSES && HIWORD(wParam) == LBN_SELCHANGE) { + int index = SendDlgItemMessage(hwndDlg, IDC_LST_CLASSES, LB_GETCURSEL, 0, 0); + EnableWindow(GetDlgItem(hwndDlg, IDC_BTN_PREVIEW), index != -1); + EnableWindow(GetDlgItem(hwndDlg, IDC_COL_TEXT), index != -1); + EnableWindow(GetDlgItem(hwndDlg, IDC_COL_BG), index != -1); + EnableWindow(GetDlgItem(hwndDlg, IDC_CHK_TIMEOUT), index != -1); + if (index != -1) { + int i = SendDlgItemMessage(hwndDlg, IDC_LST_CLASSES, LB_GETITEMDATA, index, 0); + SendDlgItemMessage(hwndDlg, IDC_COL_TEXT, CPM_SETCOLOUR, 0, (LPARAM)arNewClasses[i]->colorText); + SendDlgItemMessage(hwndDlg, IDC_COL_BG, CPM_SETCOLOUR, 0, (LPARAM)arNewClasses[i]->colorBack); + CheckDlgButton(hwndDlg, IDC_CHK_TIMEOUT, arNewClasses[i]->iSeconds != -1 ? BST_CHECKED : BST_UNCHECKED); + SetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, arNewClasses[i]->iSeconds, TRUE); + } + EnableWindow(GetDlgItem(hwndDlg, IDC_ED_TIMEOUT), index != -1 && IsDlgButtonChecked(hwndDlg, IDC_CHK_TIMEOUT)); + return TRUE; + } + if ( HIWORD(wParam) == EN_CHANGE && (HWND)lParam == GetFocus()) { + int index = SendDlgItemMessage(hwndDlg, IDC_LST_CLASSES, LB_GETCURSEL, 0, 0); + if (index != -1) { + int i = SendDlgItemMessage(hwndDlg, IDC_LST_CLASSES, LB_GETITEMDATA, index, 0); + BOOL tr; + int t = GetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, &tr, FALSE); + arNewClasses[i]->iSeconds = t; + + SendMessage(GetParent(hwndDlg), PSM_CHANGED, 0, 0); + } + } + else { + int index = SendDlgItemMessage(hwndDlg, IDC_LST_CLASSES, LB_GETCURSEL, 0, 0); + if (index != -1) { + int i = SendDlgItemMessage(hwndDlg, IDC_LST_CLASSES, LB_GETITEMDATA, index, 0); + switch(LOWORD(wParam)) { + case IDC_CHK_TIMEOUT: + { + BOOL isChecked = IsDlgButtonChecked(hwndDlg, IDC_CHK_TIMEOUT); + EnableWindow(GetDlgItem(hwndDlg, IDC_ED_TIMEOUT), isChecked); + if (isChecked) arNewClasses[i]->iSeconds = 0; + else arNewClasses[i]->iSeconds = -1; + SetDlgItemInt(hwndDlg, IDC_ED_TIMEOUT, arNewClasses[i]->iSeconds, TRUE); + } + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0); + break; + + case IDC_COL_TEXT: + arNewClasses[i]->colorText = SendDlgItemMessage(hwndDlg, IDC_COL_TEXT, CPM_GETCOLOUR, 0, 0); + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0); + break; + + case IDC_COL_BG: + arNewClasses[i]->colorBack = SendDlgItemMessage(hwndDlg, IDC_COL_BG, CPM_GETCOLOUR, 0, 0); + SendMessage( GetParent( hwndDlg ), PSM_CHANGED, 0, 0); + break; + + case IDC_BTN_PREVIEW: + if (arNewClasses[i]->flags & PCF_UNICODE) { + POPUPCLASS pc = *arNewClasses[i]; + pc.PluginWindowProc = nullptr; + POPUPDATACLASS d = {sizeof(d), pc.pszName}; + d.pwszTitle = L"Preview"; + d.pwszText = L"The quick brown fox jumps over the lazy dog."; + CallService(MS_POPUP_ADDPOPUPCLASS, (WPARAM)&pc, (LPARAM)&d); + } + else { + POPUPCLASS pc = *arNewClasses[i]; + pc.PluginWindowProc = nullptr; + POPUPDATACLASS d = {sizeof(d), pc.pszName}; + d.pszTitle = "Preview"; + d.pszText = "The quick brown fox jumps over the lazy dog."; + CallService(MS_POPUP_ADDPOPUPCLASS, (WPARAM)&pc, (LPARAM)&d); + } + break; + } + } + } + break; + + case WM_NOTIFY: + if (((LPNMHDR)lParam)->code == PSN_APPLY) { + arClasses = arNewClasses; + char setting[256]; + for (auto &pc : arClasses) { + mir_snprintf(setting, "%s/Timeout", pc->pszName); + g_plugin.setWord(setting, pc->iSeconds); + mir_snprintf(setting, "%s/TextCol", pc->pszName); + g_plugin.setDword(setting, (DWORD)pc->colorText); + mir_snprintf(setting, "%s/BgCol", pc->pszName); + g_plugin.setDword(setting, (DWORD)pc->colorBack); + } + return TRUE; + } + break; + + case WM_DESTROY: + arNewClasses.destroy(); + break; + } + return 0; +} + +int OptInit(WPARAM wParam, LPARAM) +{ + OPTIONSDIALOGPAGE odp = {}; + odp.flags = ODPF_BOLDGROUPS; + odp.position = -790000000; + odp.szTitle.a = LPGEN("Popups"); + + odp.pszTemplate = MAKEINTRESOURCEA(IDD_OPT1); + odp.szTab.a = LPGEN("Settings"); + odp.pfnDlgProc = DlgProcOpts1; + g_plugin.addOptions(wParam, &odp); + + odp.pszTemplate = MAKEINTRESOURCEA(IDD_CLASSES); + odp.szTab.a = LPGEN("Classes"); + odp.pfnDlgProc = DlgProcOptsClasses; + g_plugin.addOptions(wParam, &odp); + return 0; +} + +void InitOptions() +{ + HookEvent(ME_OPT_INITIALISE, OptInit); + + // an icon for preview popups + hPopupIcon = Skin_LoadIcon(SKINICON_EVENT_MESSAGE); + LoadOptions(); +} diff --git a/src/core/stdpopup/src/options.h b/src/core/stdpopup/src/options.h new file mode 100644 index 0000000000..9269b65779 --- /dev/null +++ b/src/core/stdpopup/src/options.h @@ -0,0 +1,35 @@ +#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; + int 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(); + +#endif diff --git a/src/core/stdpopup/src/popwin.cpp b/src/core/stdpopup/src/popwin.cpp new file mode 100644 index 0000000000..ec16f78e7e --- /dev/null +++ b/src/core/stdpopup/src/popwin.cpp @@ -0,0 +1,749 @@ +#include "stdafx.h" + +#define ID_CLOSETIMER 0x0101 +#define ID_MOVETIMER 0x0102 + +DWORD pop_start_x, pop_start_y; +int global_mouse_in = 0; + +void trimW(wchar_t *str) { + int len = (int)mir_wstrlen(str), pos; + // trim whitespace (e.g. from OTR detection) + for (pos = len - 1; pos >= 0; pos--) { + if (str[pos] == L' ' || str[pos] == L'\t' || str[pos] == L'\r' || str[pos] == L'\n') str[pos] = 0; + else break; + } + + // remove tabs + for (pos = len - 1; pos >= 0; pos--) + if (str[pos] == L'\t') str[pos] = L' '; +} + +struct HWNDStackNode { + HWND hwnd; + struct HWNDStackNode *next; +}; + +HWNDStackNode *hwnd_stack_top = nullptr; +int stack_size = 0; + +void RepositionWindows() { + HWNDStackNode *current; + int x = pop_start_x, y; + int height; + + if (options.animate == ANIMATE_HORZ) + { + if (options.location == PL_BOTTOMRIGHT || options.location == PL_TOPRIGHT) + x -= options.win_width + 1; + if (options.location == PL_BOTTOMLEFT || options.location == PL_TOPLEFT) + x += options.win_width + 1; + } + + // Особый случай: выдвижение окна из-за верхнего края экрана. + if ((options.animate == ANIMATE_VERT) && (options.location == PL_TOPLEFT || options.location == PL_TOPRIGHT)) + y = 0; + else + y = pop_start_y; + + current = hwnd_stack_top; + while (current) + { + SendMessage(current->hwnd, PUM_GETHEIGHT, (WPARAM)&height, 0); + + // Если окна размещать у нижнего края, то координата текущего окна меньше на его высоту. + if (options.location == PL_BOTTOMLEFT || options.location == PL_BOTTOMRIGHT) + y -= height + 1; + + // Перемещаем окно. + SendMessage(current->hwnd, PUM_MOVE, (WPARAM)x, (LPARAM)y); + // Координата для следующего окна. + // Если окна размещать у верхнего края, то координата следующего окна больше на высоту текущего окна. + if (options.location == PL_TOPLEFT || options.location == PL_TOPRIGHT) + y += height + 1; + + // Переходим к следующему окну. + current = current->next; + } +} + +void AddWindowToStack(HWND hwnd) { + + HWNDStackNode *new_node = (HWNDStackNode *)mir_alloc(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); + + RECT wa_rect; + SystemParametersInfo(SPI_GETWORKAREA, 0, &wa_rect, 0); + if (options.use_mim_monitor) { + RECT clr; + GetWindowRect(g_clistApi.hwndContactList, &clr); + HMONITOR hMonitor = MonitorFromRect(&clr, MONITOR_DEFAULTTONEAREST); + if (hMonitor) { + MONITORINFO mi; + mi.cbSize = sizeof(mi); + if (GetMonitorInfo(hMonitor, &mi)) + wa_rect = mi.rcWork; + } + } + + switch (options.animate) { + case ANIMATE_NO: + 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; + else + pop_start_y = wa_rect.top + 1; + break; + case ANIMATE_HORZ: + if (options.location == PL_BOTTOMRIGHT || options.location == PL_TOPRIGHT) + pop_start_x = wa_rect.right; + else + pop_start_x = wa_rect.left - options.win_width; + + if (options.location == PL_BOTTOMRIGHT || options.location == PL_BOTTOMLEFT) + pop_start_y = wa_rect.bottom; + else + pop_start_y = wa_rect.top + 1; + break; + case ANIMATE_VERT: + 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; + else + pop_start_y = wa_rect.top - height + 1; + break; + } + + SetWindowPos(hwnd, nullptr, pop_start_x, pop_start_y, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE); + + stack_size++; + + RepositionWindows(); +} + +void RemoveWindowFromStack(HWND hwnd) +{ + HWNDStackNode *current = hwnd_stack_top, *prev = nullptr; + while(current) { + if (current->hwnd == hwnd) { + if (prev) + prev->next = current->next; + else + hwnd_stack_top = current->next; + + mir_free(current); + stack_size--; + break; + } + + prev = current; + current = current->next; + } + + // Если после удаления в стеке остались окна, то нужно провести сжатие: + // сдвинуть все окна к верхнему/нижнему краю экрана. + if (hwnd_stack_top) + RepositionWindows(); +} + +void BroadcastMessage(UINT msg, WPARAM wParam, LPARAM lParam) +{ + HWNDStackNode *current = hwnd_stack_top; + while(current) { + SendMessage(current->hwnd, msg, wParam, lParam); + current = current->next; + } +} + +void DeinitWindowStack() +{ + HWNDStackNode *current = hwnd_stack_top; + hwnd_stack_top = nullptr; + while(current) { + HWNDStackNode *pNext = current->next; + DestroyWindow(current->hwnd); + current = pNext; + } +} + +struct PopupWindowData +{ + PopupData *pd; + int new_x, new_y; + bool is_round, av_is_round, mouse_in, close_on_leave; + bool custom_col; + HBRUSH bkBrush, barBrush, underlineBrush; + HPEN bPen; + wchar_t 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; +}; + +LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + PopupWindowData *pwd = (PopupWindowData *)GetWindowLongPtr(hwnd, GWLP_USERDATA); + PopupData *pd = nullptr; + if (pwd) pd = pwd->pd; + + switch(uMsg) { + case WM_CREATE: + { + CREATESTRUCT *cs = (CREATESTRUCT *)lParam; + pwd = (PopupWindowData *)mir_alloc(sizeof(PopupWindowData)); + pd = (PopupData *)cs->lpCreateParams; + pwd->pd = pd; + pwd->hNotify = nullptr; + + trimW(pwd->pd->pwzTitle); + trimW(pwd->pd->pwzText); + + 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); + 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 + pwd->underlineBrush = CreateSolidBrush(pd->colorBack); // make sidebar a dark version of the bg + } + else { + pwd->bkBrush = CreateSolidBrush(colBg); + pwd->barBrush = CreateSolidBrush(colSidebar); + pwd->underlineBrush = CreateSolidBrush(colTitleUnderline); + } + + 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, nullptr, pwd->tbuff, 128); + + SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)pwd); + + // make a really long timeout - say 7 days? ;) + if (pd->timeout == -1 || (pd->timeout == 0 && options.default_timeout == -1)) + SetTimer(hwnd, ID_CLOSETIMER, 7 * 24 * 60 * 60 * 1000, nullptr); + else if (pd->timeout == 0) + SetTimer(hwnd, ID_CLOSETIMER, options.default_timeout * 1000, nullptr); + else + SetTimer(hwnd, ID_CLOSETIMER, pd->timeout * 1000, nullptr); + + AddWindowToStack(hwnd); // this updates our size + } + + // transparency +#ifdef WS_EX_LAYERED + SetWindowLongPtr(hwnd, GWL_EXSTYLE, GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_LAYERED); +#endif + +#ifdef CS_DROPSHADOW + if (options.drop_shadow) { + SetClassLong(hwnd, GCL_STYLE, CS_DROPSHADOW); + } +#endif + +#ifdef LWA_ALPHA + SetLayeredWindowAttributes(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; + SetLayeredWindowAttributes(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 = { 0 }; + tme.cbSize = 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, nullptr); // reset timer if mouse in window - allow another 800 ms + else { + PostMessage(hwnd, UM_DESTROYPOPUP, 0, 0); + } + return TRUE; + } + + 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, nullptr, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE | SWP_NOACTIVATE); + + if (!IsWindowVisible(hwnd)) { + ShowWindow(hwnd, SW_SHOWNOACTIVATE); + UpdateWindow(hwnd); + } + return TRUE; + } + break; + + case WM_ERASEBKGND: + { + HDC hdc = (HDC) wParam; + RECT r, r_bar; + GetClientRect(hwnd, &r); + + // bg + FillRect(hdc, &r, pwd->bkBrush); + // 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=(h<v)?h:v; + } + RoundRect(hdc, 0, 0, (r.right - r.left), (r.bottom - r.top), h, h); + + SelectObject(hdc, hOldBrush); + SelectObject(hdc, hOldPen); + } + } + return TRUE; + + case WM_PAINT: + { + RECT r; + PAINTSTRUCT ps; + BeginPaint(hwnd, &ps); + HDC hdc = ps.hdc; + GetClientRect(hwnd, &r); + + SetBkMode(hdc, TRANSPARENT); + + // avatar & time if with avatar + if (options.av_layout != PAV_NONE && (pwd->have_av || options.time_layout == PT_WITHAV)) { + RECT avr = { 0 }; + 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, -1, &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 = {}; + adr.hContact = pd->hContact; + adr.hTargetDC = ps.hdc; + adr.rcDraw = avr; + adr.dwFlags = (pwd->av_is_round ? AVDRQ_ROUNDEDCORNER : 0); + adr.radius = 5; //(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->hIcon) { + 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->hIcon, 16, 16, 0, nullptr, 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, -1, &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, -1, &ttr, DT_VCENTER | DT_LEFT | DT_SINGLELINE | DT_NOPREFIX); + 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 = r.left + options.padding + options.text_indent; 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); + wchar_t *title = mir_wstrdup(pd->pwzTitle); + DrawText(ps.hdc, title, -1, &tr, DT_VCENTER | DT_LEFT | DT_END_ELLIPSIS | DT_SINGLELINE | DT_NOPREFIX); + mir_free(title); + + // title underline + RECT tur; + tur.left = r.left + options.sb_width + options.padding; + tur.right = r.right - options.padding; + tur.top = tr.bottom + options.padding/2; + tur.bottom = tur.top + 1; + FillRect(ps.hdc, &tur, pwd->underlineBrush); + + // second line(s) + if (pd->pwzText[0]) { + 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); + + wchar_t *text = mir_wstrdup(pd->pwzText); + 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, text, -1, &tr, DT_NOPREFIX | DT_WORDBREAK | DT_EXTERNALLEADING | DT_TOP | DT_LEFT | DT_WORD_ELLIPSIS); + mir_free(text); + } + + 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); + DeleteObject(pwd->underlineBrush); + KillTimer(hwnd, ID_MOVETIMER); + KillTimer(hwnd, ID_CLOSETIMER); + + RemoveWindowFromStack(hwnd); + + SendMessage(hwnd, UM_FREEPLUGINDATA, 0, 0); + + if (pd) { + pd->SetIcon(nullptr); + mir_free(pd->pwzTitle); + mir_free(pd->pwzText); + mir_free(pd); + } + mir_free(pwd); pwd = nullptr; pd = nullptr; + SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); + break; + + case PUM_UPDATERGN: + // round corners + if (pwd->is_round) { + HRGN hRgn1; + RECT r; + + int w = 11; + GetWindowRect(hwnd,&r); + int h = (r.right-r.left) > (w*2)?w:(r.right-r.left); + int v = (r.bottom-r.top) > (w*2)?w:(r.bottom-r.top); + h=(h<v)?h:v; + hRgn1=CreateRoundRectRgn(0,0,(r.right-r.left) + 1,(r.bottom-r.top) + 1,h,h); + SetWindowRgn(hwnd,hRgn1,FALSE); + } + return TRUE; + + case PUM_MOVE: + if (options.animate) { + KillTimer(hwnd, ID_MOVETIMER); + pwd->new_x = (int)wParam; + pwd->new_y = (int)lParam; + SetTimer(hwnd, ID_MOVETIMER, 10, nullptr); + } + else { + SetWindowPos(hwnd, nullptr, (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: + replaceStrW(pd->ptzText, (wchar_t*)lParam); + InvalidateRect(hwnd, nullptr, TRUE); + RepositionWindows(); + return TRUE; + + case PUM_GETCONTACT: + { + MCONTACT *phContact = (MCONTACT*)wParam; + *phContact = pd->hContact; + if (lParam) + SetEvent((HANDLE)lParam); + } + return TRUE; + + case PUM_GETHEIGHT: + { + int *pHeight = (int*)wParam; + HDC hdc = GetDC(hwnd); + SIZE size; + HFONT hOldFont = (HFONT)GetCurrentObject(hdc, OBJ_FONT); + + // time_height + width + if (options.time_layout != PT_NONE) { + SIZE size_t; + if (hFontTime) SelectObject(hdc, (HGDIOBJ)hFontTime); + GetTextExtentPoint32(hdc, pwd->tbuff, (int)mir_wstrlen(pwd->tbuff), &size_t); + pwd->time_height = size_t.cy; + pwd->time_width = size_t.cx; + } + + // titlebar height + if (hFontFirstLine) SelectObject(hdc, (HGDIOBJ)hFontFirstLine); + wchar_t *title = mir_wstrdup(pd->pwzTitle); + GetTextExtentPoint32(hdc, title, (int)mir_wstrlen(title), &size); + mir_free(title); + 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->hContact, 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 = options.av_size * ace->bmWidth / ace->bmHeight; + } else { + pwd->real_av_height = options.av_size * ace->bmHeight / ace->bmWidth; + pwd->real_av_width = options.av_size; + } + pwd->have_av = true; + pwd->av_height = pwd->real_av_height; + } + } + + // text height + if (pd->pwzText[0]) { + 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); + wchar_t *text = mir_wstrdup(pd->pwzText); + DrawText(hdc, text, -1, &r, DT_CALCRECT | DT_NOPREFIX | DT_WORDBREAK | DT_EXTERNALLEADING | DT_TOP | DT_LEFT | DT_WORD_ELLIPSIS); + pwd->text_height = r.bottom; + mir_free(text); + } + + SelectObject(hdc, hOldFont); + 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, nullptr, 0, 0, options.win_width, *pHeight, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + SendMessage(hwnd, PUM_UPDATERGN, 0, 0); + InvalidateRect(hwnd, nullptr, TRUE); + } + } + return TRUE; + + case PUM_GETOPAQUE: + { + void **pData = (void **)wParam; + if (pd) *pData = pd->opaque; + if (lParam) SetEvent((HANDLE)lParam); + } + return TRUE; + + case PUM_CHANGE: + { + KillTimer(hwnd, ID_CLOSETIMER); + if (pd) { + mir_free(pd->pwzTitle); + mir_free(pd->pwzText); + mir_free(pd); + } + pwd->pd = pd = (PopupData *)lParam; + + // make a really long timeout - say 7 days? ;) + if (pd->timeout == -1) + SetTimer(hwnd, ID_CLOSETIMER, 7 * 24 * 60 * 60 * 1000, nullptr); + else if (pd->timeout == 0) + SetTimer(hwnd, ID_CLOSETIMER, 7 * 1000, nullptr); + else + SetTimer(hwnd, ID_CLOSETIMER, pd->timeout * 1000, nullptr); + + InvalidateRect(hwnd, nullptr, 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->timeout = MNotifyGetDWord(pwd->hNotify, NFOPT_TIMEOUT, options.default_timeout); + pd->hContact = (MCONTACT)MNotifyGetDWord(pwd->hNotify, NFOPT_CONTACT, 0); + pd->hIcon = (HICON)MNotifyGetDWord(pwd->hNotify, NFOPT_ICON, 0); + + const wchar_t *swzName = MNotifyGetWString(pwd->hNotify, NFOPT_TITLEW, nullptr); + mir_free(pd->pwzTitle); + pd->pwzTitle = mir_wstrdup(swzName); + + const wchar_t *swzText = MNotifyGetWString(pwd->hNotify, NFOPT_TEXTW, nullptr); + mir_free(pd->pwzText); + pd->pwzText = mir_wstrdup(swzText); + + InvalidateRect(hwnd, nullptr, 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->windowProc) + return CallWindowProc(pd->windowProc, hwnd, uMsg, wParam, lParam); + + // provide a way to close popups, if no PluginWindowProc is provided + if (uMsg == WM_CONTEXTMENU) { + SendMessage(hwnd, UM_DESTROYPOPUP, 0, 0); + return TRUE; + } + + return DefWindowProc(hwnd, uMsg, wParam, lParam); +} + +void PopupData::SetIcon(HICON hNewIcon) +{ + if (flags & PDF_ICOLIB) { + IcoLib_ReleaseIcon(hIcon); + flags &= ~PDF_ICOLIB; + } + + hIcon = hNewIcon; + + if (IcoLib_IsManaged(hIcon)) { + IcoLib_AddRef(hIcon); + flags |= PDF_ICOLIB; + } +} diff --git a/src/core/stdpopup/src/popwin.h b/src/core/stdpopup/src/popwin.h new file mode 100644 index 0000000000..e31b4aa6cf --- /dev/null +++ b/src/core/stdpopup/src/popwin.h @@ -0,0 +1,31 @@ +#ifndef _POPWIN_INC +#define _POPWIN_INC + +#define POP_WIN_CLASS _A2W(MODULENAME) L"YAPPWindowClass" + +#define PUM_SETTEXT (WM_USER + 0x020) +#define PUM_GETCONTACT (WM_USER + 0x021) +#define PUM_GETOPAQUE (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) + +#define ANIMATE_NO 0 +#define ANIMATE_HORZ 1 +#define ANIMATE_VERT 2 + +void DeinitWindowStack(); + +void BroadcastMessage(UINT msg, WPARAM wParam, LPARAM lParam); + +void RepositionWindows(); + +LRESULT CALLBACK PopupWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); + + +#endif diff --git a/src/core/stdpopup/src/resource.h b/src/core/stdpopup/src/resource.h new file mode 100644 index 0000000000..0ac6564fec --- /dev/null +++ b/src/core/stdpopup/src/resource.h @@ -0,0 +1,69 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by D:\Development\Miranda NG\plugins\YAPP\res\yapp.rc +// +#define IDD_OPT1 101 +#define IDD_OPT_NOTIFY 103 +#define IDD_LST_HISTORY 104 +#define IDI_POPUP 105 +#define IDI_NOPOPUP 106 +#define IDI_HISTORY 107 +#define IDD_CLASSES 108 +#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_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_CMB_ANIMATE 1021 +#define IDC_CHK_TRANSBG 1022 +#define IDC_RAD_RIGHTICON 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_LEFTAV 1029 +#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_TIME 1038 +#define IDC_COL_TEXT 1039 +#define IDC_COL_BG 1040 +#define IDC_CLOSE 1041 +#define IDC_LST_HISTORY 1043 +#define IDC_HISTORY_FILTER 1044 +#define IDC_HISTORY_FILTER_CASESENSITIVE 1046 +#define IDC_LST_CLASSES 1047 +#define IDC_CHK_TIMEOUT 1048 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 108 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1051 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/src/core/stdpopup/src/services.cpp b/src/core/stdpopup/src/services.cpp new file mode 100644 index 0000000000..31cb9c49ef --- /dev/null +++ b/src/core/stdpopup/src/services.cpp @@ -0,0 +1,448 @@ +#include "stdafx.h" + +extern HANDLE hTTButton; +extern HGENMENU hMenuRoot, hMenuItem; +static HANDLE hEventNotify; + +void StripBBCodesInPlace(wchar_t *text) +{ + if (text == nullptr || g_plugin.getByte("StripBBCodes", 1) == 0) + return; + + int read = 0, write = 0; + int len = (int)mir_wstrlen(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 (len - read >= 5 && (_wcsnicmp(text + read, L"[size", 5) == 0)) { + while(read < len && text[read] != L']') read++; + read++;// skip the ']' + } else if (len - read >= 7 && (_wcsnicmp(text + read, L"[/size]", 7) == 0)) + read += 7; + else { + if (text[read] != text[write]) text[write] = text[read]; + read++; write++; + } + } +} + +static INT_PTR CreatePopup(WPARAM wParam, LPARAM) +{ + if (bShutdown) + return -1; + + POPUPDATA *pd_in = (POPUPDATA *)wParam; + if ( NotifyEventHooks(hEventNotify, (WPARAM)pd_in->lchContact, (LPARAM)pd_in->PluginWindowProc)) + return 0; + + PopupData *pd_out = (PopupData *)mir_calloc(sizeof(PopupData)); + pd_out->cbSize = sizeof(PopupData); + pd_out->flags = PDF_UNICODE; + pd_out->pwzTitle = mir_a2u(pd_in->lpzContactName); + pd_out->pwzText = mir_a2u(pd_in->lpzText); + StripBBCodesInPlace(pd_out->pwzTitle); + StripBBCodesInPlace(pd_out->pwzText); + + pd_out->hContact = pd_in->lchContact; + pd_out->SetIcon(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->windowProc = pd_in->PluginWindowProc; + pd_out->opaque = pd_in->PluginData; + pd_out->timeout = pd_in->iSeconds; + + lstPopupHistory.Add(pd_out->pwzTitle, pd_out->pwzText, time(0)); + if (!db_get_b(0, "Popup", "ModuleIsEnabled", 1)) { + mir_free(pd_out->pwzTitle); + mir_free(pd_out->pwzText); + mir_free(pd_out); + return -1; + } + + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out); + return 0; +} + +static INT_PTR CreatePopupW(WPARAM wParam, LPARAM) +{ + if (bShutdown) + return -1; + + POPUPDATAW *pd_in = (POPUPDATAW *)wParam; + if ( NotifyEventHooks(hEventNotify, (WPARAM)pd_in->lchContact, (LPARAM)pd_in->PluginWindowProc)) + return 0; + + PopupData *pd_out = (PopupData *)mir_calloc(sizeof(PopupData)); + pd_out->cbSize = sizeof(PopupData); + pd_out->flags = PDF_UNICODE; + pd_out->pwzTitle = mir_wstrdup(pd_in->lpwzContactName); + pd_out->pwzText = mir_wstrdup(pd_in->lpwzText); + StripBBCodesInPlace(pd_out->pwzTitle); + StripBBCodesInPlace(pd_out->pwzText); + + pd_out->hContact = pd_in->lchContact; + pd_out->SetIcon(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->windowProc = pd_in->PluginWindowProc; + pd_out->opaque = pd_in->PluginData; + pd_out->timeout = pd_in->iSeconds; + + lstPopupHistory.Add(pd_out->pwzTitle, pd_out->pwzText, time(0)); + if (!db_get_b(0, "Popup", "ModuleIsEnabled", 1)) { + mir_free(pd_out->pwzTitle); + mir_free(pd_out->pwzText); + mir_free(pd_out); + return -1; + } + + PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out); + return 0; +} + +static INT_PTR ChangeTextW(WPARAM wParam, LPARAM lParam) +{ + HWND hwndPop = (HWND)wParam; + wchar_t *newText = NEWWSTR_ALLOCA((wchar_t *)lParam); + StripBBCodesInPlace(newText); + + if (IsWindow(hwndPop)) + SendMessage(hwndPop, PUM_SETTEXT, 0, (LPARAM)newText); + return 0; +} + +void ShowPopup(PopupData &pd_in) +{ + PopupData *pd_out = (PopupData *)mir_alloc(sizeof(PopupData)); + *pd_out = pd_in; + if (pd_in.flags & PDF_UNICODE) { + pd_out->pwzTitle = mir_wstrdup(pd_in.pwzTitle); + pd_out->pwzText = mir_wstrdup(pd_in.pwzText); + } + else { + pd_out->flags |= PDF_UNICODE; + pd_out->pwzTitle = mir_a2u(pd_in.pszTitle); + pd_out->pwzText = mir_a2u(pd_in.pszText); + } + StripBBCodesInPlace(pd_out->pwzTitle); + StripBBCodesInPlace(pd_out->pwzText); + + lstPopupHistory.Add(pd_out->pwzTitle, pd_out->pwzText, time(0)); + + if (!db_get_b(0, "Popup", "ModuleIsEnabled", 1)) { + mir_free(pd_out->pwzTitle); + mir_free(pd_out->pwzText); + mir_free(pd_out); + } + else PostMPMessage(MUM_CREATEPOPUP, 0, (LPARAM)pd_out); +} + +static INT_PTR GetContact(WPARAM wParam, LPARAM) +{ + HWND hwndPop = (HWND)wParam; + MCONTACT hContact; + if (GetCurrentThreadId() == message_pump_thread_id) + SendMessage(hwndPop, PUM_GETCONTACT, (WPARAM)&hContact, 0); + else { + HANDLE hEvent = CreateEvent(nullptr, 0, 0, nullptr); + PostMessage(hwndPop, PUM_GETCONTACT, (WPARAM)&hContact, (LPARAM)hEvent); + MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, 0, 0); + CloseHandle(hEvent); + } + + return (INT_PTR)hContact; +} + +static INT_PTR GetOpaque(WPARAM wParam, LPARAM) +{ + HWND hwndPop = (HWND)wParam; + void *data = nullptr; + if (GetCurrentThreadId() == message_pump_thread_id) + SendMessage(hwndPop, PUM_GETOPAQUE, (WPARAM)&data, 0); + else { + HANDLE hEvent = CreateEvent(nullptr, 0, 0, nullptr); + PostMessage(hwndPop, PUM_GETOPAQUE, (WPARAM)&data, (LPARAM)hEvent); + MsgWaitForMultipleObjectsEx(1, &hEvent, INFINITE, 0, 0); + CloseHandle(hEvent); + } + + return (INT_PTR)data; +} + +void UpdateMenu() +{ + bool isEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) == 1; + if (isEnabled) { + Menu_ModifyItem(hMenuItem, LPGENW("Disable Popups"), IcoLib_GetIcon(ICO_POPUP_ON)); + Menu_ModifyItem(hMenuRoot, nullptr, IcoLib_GetIcon(ICO_POPUP_ON)); + } + else { + Menu_ModifyItem(hMenuItem, LPGENW("Enable Popups"), IcoLib_GetIcon(ICO_POPUP_OFF)); + Menu_ModifyItem(hMenuRoot, nullptr, IcoLib_GetIcon(ICO_POPUP_OFF)); + } + + if (hTTButton) + CallService(MS_TTB_SETBUTTONSTATE, (WPARAM)hTTButton, isEnabled ? TTBST_PUSHED : 0); +} + +INT_PTR PopupQuery(WPARAM wParam, LPARAM) +{ + switch(wParam) { + case PUQS_ENABLEPOPUPS: + { + bool enabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0; + if (!enabled) db_set_b(0, "Popup", "ModuleIsEnabled", 1); + UpdateMenu(); + return !enabled; + } + break; + case PUQS_DISABLEPOPUPS: + { + bool enabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) != 0; + if (enabled) db_set_b(0, "Popup", "ModuleIsEnabled", 0); + UpdateMenu(); + return enabled; + } + break; + + case PUQS_GETSTATUS: + return db_get_b(0, "Popup", "ModuleIsEnabled", 1); + default: + UpdateMenu(); + return 1; + } +} + +static INT_PTR TogglePopups(WPARAM, LPARAM) +{ + BYTE val = db_get_b(0, "Popup", "ModuleIsEnabled", 1); + db_set_b(0, "Popup", "ModuleIsEnabled", !val); + UpdateMenu(); + return 0; +} + +static INT_PTR PopupChangeW(WPARAM wParam, LPARAM lParam) +{ + if (bShutdown) + return -1; + + HWND hwndPop = (HWND)wParam; + POPUPDATAW *pd_in = (POPUPDATAW *)lParam; + + if (IsWindow(hwndPop)) { + PopupData pd_out; + pd_out.cbSize = sizeof(PopupData); + pd_out.flags = PDF_UNICODE; + + pd_out.pwzTitle = mir_wstrdup(pd_in->lpwzContactName); + pd_out.pwzText = mir_wstrdup(pd_in->lpwzText); + StripBBCodesInPlace(pd_out.pwzTitle); + StripBBCodesInPlace(pd_out.pwzText); + + pd_out.hContact = pd_in->lchContact; + pd_out.SetIcon(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.windowProc = pd_in->PluginWindowProc; + pd_out.opaque = pd_in->PluginData; + pd_out.timeout = pd_in->iSeconds; + + lstPopupHistory.Add(pd_out.pwzTitle, pd_out.pwzText, time(0)); + + SendMessage(hwndPop, PUM_CHANGE, 0, (LPARAM)&pd_out); + } + return 0; +} + +static INT_PTR ShowMessage(WPARAM wParam, LPARAM lParam) +{ + if (bShutdown) + return -1; + + if (db_get_b(0, "Popup", "ModuleIsEnabled", 1)) { + POPUPDATAW pd = {0}; + mir_wstrcpy(pd.lpwzContactName, lParam == SM_WARNING ? L"Warning" : L"Notification"); + pd.lchIcon = LoadIcon(nullptr, lParam == SM_WARNING ? IDI_WARNING : IDI_INFORMATION); + wcsncpy(pd.lpwzText, _A2T((char *)wParam), MAX_SECONDLINE); pd.lpwzText[MAX_SECONDLINE-1] = 0; + CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0); + } + return 0; +} + +static INT_PTR ShowMessageW(WPARAM wParam, LPARAM lParam) +{ + if (bShutdown) + return -1; + + if (db_get_b(0, "Popup", "ModuleIsEnabled", 1)) { + POPUPDATAW pd = {0}; + mir_wstrcpy(pd.lpwzContactName, lParam == SM_WARNING ? L"Warning" : L"Notification"); + pd.lchIcon = LoadIcon(nullptr, lParam == SM_WARNING ? IDI_WARNING : IDI_INFORMATION); + wcsncpy(pd.lpwzText, (wchar_t *)wParam, MAX_SECONDLINE); + CallService(MS_POPUP_ADDPOPUPW, (WPARAM)&pd, 0); + } + return 0; +} + +//=====Popup/ShowHistory + +INT_PTR Popup_ShowHistory(WPARAM, LPARAM) +{ + if (!hHistoryWindow) + hHistoryWindow = CreateDialog(g_plugin.getInst(), MAKEINTRESOURCE(IDD_LST_HISTORY), nullptr, DlgProcHistLst); + + ShowWindow(hHistoryWindow, SW_SHOW); + return 0; +} + +LIST<POPUPCLASS> arClasses(3, PtrKeySortT); + +static INT_PTR RegisterPopupClass(WPARAM, LPARAM lParam) +{ + POPUPCLASS *pc = (POPUPCLASS*)mir_alloc( sizeof(POPUPCLASS)); + memcpy(pc, (PVOID)lParam, sizeof(POPUPCLASS)); + + pc->pszName = mir_strdup(pc->pszName); + if (pc->flags & PCF_UNICODE) + pc->pszDescription.w = mir_wstrdup(pc->pszDescription.w); + else + pc->pszDescription.a = mir_strdup(pc->pszDescription.a); + + char setting[256]; + mir_snprintf(setting, "%s/Timeout", pc->pszName); + pc->iSeconds = g_plugin.getWord(setting, pc->iSeconds); + if (pc->iSeconds == (WORD)-1) pc->iSeconds = -1; + mir_snprintf(setting, "%s/TextCol", pc->pszName); + pc->colorText = (COLORREF)g_plugin.getDword(setting, (DWORD)pc->colorText); + mir_snprintf(setting, "%s/BgCol", pc->pszName); + pc->colorBack = (COLORREF)g_plugin.getDword(setting, (DWORD)pc->colorBack); + + arClasses.insert(pc); + return (INT_PTR)pc; +} + +static void FreePopupClass(POPUPCLASS *pc) +{ + mir_free(pc->pszName); + mir_free(pc->pszDescription.w); + mir_free(pc); +} + +static INT_PTR UnregisterPopupClass(WPARAM, LPARAM lParam) +{ + POPUPCLASS *pc = (POPUPCLASS*)lParam; + if (pc == nullptr) + return 1; + if (arClasses.find(pc) == nullptr) + return 1; + + arClasses.remove(pc); + FreePopupClass(pc); + return 0; +} + +static INT_PTR CreateClassPopup(WPARAM wParam, LPARAM lParam) +{ + POPUPDATACLASS *pdc = (POPUPDATACLASS *)lParam; + if (pdc->cbSize < sizeof(POPUPDATACLASS)) return 1; + + POPUPCLASS *pc = nullptr; + if (wParam) + pc = (POPUPCLASS *)wParam; + else { + for (auto &it : arClasses) { + if (mir_strcmp(it->pszName, pdc->pszClassName) == 0) { + pc = it; + break; + } + } + } + if (pc) { + if (NotifyEventHooks(hEventNotify, (WPARAM)pdc->hContact, (LPARAM)pc->PluginWindowProc)) + return 0; + + PopupData pd = { sizeof(PopupData) }; + if (pc->flags & PCF_UNICODE) pd.flags |= PDF_UNICODE; + pd.colorBack = pc->colorBack; + pd.colorText = pc->colorText; + pd.SetIcon(pc->hIcon); + pd.timeout = pc->iSeconds; + pd.windowProc = pc->PluginWindowProc; + + pd.hContact = pdc->hContact; + pd.opaque = pdc->PluginData; + pd.pszTitle = (char *)pdc->pszTitle; + pd.pszText = (char *)pdc->pszText; + + ShowPopup(pd); + } + return 0; +} + +INT_PTR Popup_DeletePopup(WPARAM, LPARAM lParam) +{ + return (INT_PTR)SendMessage((HWND)lParam, UM_DESTROYPOPUP, 0, 0); +} + +////////////////////////////////////////////////////////////////////////////// + +void InitServices() +{ + hEventNotify = CreateHookableEvent(ME_POPUP_FILTER); + + CreateServiceFunction(MS_POPUP_REGISTERCLASS, RegisterPopupClass); + CreateServiceFunction(MS_POPUP_UNREGISTERCLASS, UnregisterPopupClass); + + CreateServiceFunction(MS_POPUP_ADDPOPUPCLASS, CreateClassPopup); + CreateServiceFunction(MS_POPUP_ADDPOPUP, CreatePopup); + CreateServiceFunction(MS_POPUP_ADDPOPUPW, CreatePopupW); + CreateServiceFunction(MS_POPUP_CHANGETEXTW, ChangeTextW); + CreateServiceFunction(MS_POPUP_CHANGEW, PopupChangeW); + CreateServiceFunction(MS_POPUP_GETCONTACT, GetContact); + CreateServiceFunction(MS_POPUP_GETPLUGINDATA, GetOpaque); + CreateServiceFunction(MS_POPUP_QUERY, PopupQuery); + + CreateServiceFunction(MS_POPUP_SHOWMESSAGE, ShowMessage); + CreateServiceFunction(MS_POPUP_SHOWMESSAGEW, ShowMessageW); + + CreateServiceFunction(MS_POPUP_SHOWHISTORY, Popup_ShowHistory); + CreateServiceFunction("Popup/EnableDisableMenuCommand", TogglePopups); + + CreateServiceFunction(MS_POPUP_DESTROYPOPUP, Popup_DeletePopup); +} + +void DeinitServices() +{ + DestroyHookableEvent(hEventNotify); + + for (auto &it : arClasses) + FreePopupClass(it); +} diff --git a/src/core/stdpopup/src/services.h b/src/core/stdpopup/src/services.h new file mode 100644 index 0000000000..10292562b7 --- /dev/null +++ b/src/core/stdpopup/src/services.h @@ -0,0 +1,9 @@ +#ifndef _SERVICES_INC +#define _SERVICES_INC + +void InitServices(); +void DeinitServices(); + +extern LIST<POPUPCLASS> arClasses; + +#endif diff --git a/src/core/stdpopup/src/stdafx.cxx b/src/core/stdpopup/src/stdafx.cxx new file mode 100644 index 0000000000..1b563fc866 --- /dev/null +++ b/src/core/stdpopup/src/stdafx.cxx @@ -0,0 +1,18 @@ +/* +Copyright (C) 2012-19 Miranda NG team (https://miranda-ng.org) + +This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License +as published by the Free Software Foundation version 2 +of the License. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + +#include "stdafx.h"
\ No newline at end of file diff --git a/src/core/stdpopup/src/stdafx.h b/src/core/stdpopup/src/stdafx.h new file mode 100644 index 0000000000..0255cba2d7 --- /dev/null +++ b/src/core/stdpopup/src/stdafx.h @@ -0,0 +1,79 @@ +// stdafx.h : include file for standard system include files, +// or project specific include files that are used frequently, but +// are changed infrequently +// + +#pragma once + +#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers + +#include <windows.h> +#include <windowsx.h> +#include <commctrl.h> +#include <time.h> +#include <malloc.h> + +#include <newpluginapi.h> +#include <m_utils.h> +#include <m_langpack.h> +#include <m_options.h> +#include <m_skin.h> +#include <m_clistint.h> +#include <m_fontservice.h> +#include <m_avatars.h> +#include <m_popup.h> +#include <m_icolib.h> +#include <m_toptoolbar.h> +#include <win2k.h> + +#include <m_notify.h> +#include <m_yapp.h> +#include <m_ieview.h> //need this for special renderers + +#include "version.h" +#include "message_pump.h" +#include "options.h" +#include "popwin.h" +#include "notify.h" +#include "services.h" +#include "resource.h" +#include "yapp_history.h" +#include "icons.h" + +#define MODULENAME "YAPP" + +struct CMPlugin : public PLUGIN<CMPlugin> +{ + CMPlugin(); + + int Load() override; + int Unload() override; +}; + +extern bool bShutdown; + +extern HFONT hFontFirstLine, hFontSecondLine, hFontTime; +extern COLORREF colFirstLine, colSecondLine, colBg, colTime, colBorder, colSidebar, colTitleUnderline; + +extern MNOTIFYLINK *notifyLink; + +// work around a bug in neweventnotify, possibly httpserver +// ignore the address passed to the 'get plugin data' service +extern bool ignore_gpd_passed_addy; + +// win32 defines for mingw version of windows headers :( +#ifndef LVM_SORTITEMSEX +#define LVM_SORTITEMSEX (LVM_FIRST + 81) + +typedef int (CALLBACK *PFNLVCOMPARE)(LPARAM, LPARAM, LPARAM); + +#define ListView_SortItemsEx(hwndLV, _pfnCompare, _lPrm) \ + (BOOL)SendMessage((hwndLV), LVM_SORTITEMSEX, (WPARAM)(LPARAM)(_lPrm), (LPARAM)(PFNLVCOMPARE)(_pfnCompare)) +#endif + +#define PDF_UNICODE 0x0001 +#define PDF_ICOLIB 0x0002 + +#define PDF_TCHAR PDF_UNICODE + +void ShowPopup(PopupData &pd_in); diff --git a/src/core/stdpopup/src/version.h b/src/core/stdpopup/src/version.h new file mode 100644 index 0000000000..60b3d8b5ad --- /dev/null +++ b/src/core/stdpopup/src/version.h @@ -0,0 +1,11 @@ +#include <m_version.h> + +#define __FILEVERSION_STRING MIRANDA_VERSION_FILEVERSION +#define __VERSION_STRING MIRANDA_VERSION_STRING + +#define __PLUGIN_NAME "Standard popup module" +#define __FILENAME "stdpopup.dll" +#define __DESCRIPTION "Core module for popups." +#define __AUTHOR "Miranda NG team" +#define __AUTHORWEB "https://miranda-ng.org/p/StdPopup/" +#define __COPYRIGHT "© 2012-19 Miranda NG team" diff --git a/src/core/stdpopup/src/yapp.cpp b/src/core/stdpopup/src/yapp.cpp new file mode 100644 index 0000000000..047c33502f --- /dev/null +++ b/src/core/stdpopup/src/yapp.cpp @@ -0,0 +1,229 @@ +// popups2.cpp : Defines the entry point for the DLL application. +// + +#include "stdafx.h" + +CMPlugin g_plugin; +bool bShutdown = false; + +MNOTIFYLINK *notifyLink = nullptr; + +// 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 = {}, font_id_secondline = {}, font_id_time = {}; +ColourIDW colour_id_bg = {}, colour_id_border = {}, colour_id_sidebar = {}, colour_id_titleunderline = {}; + +COLORREF colBg = GetSysColor(COLOR_3DSHADOW); +HFONT hFontFirstLine = nullptr, hFontSecondLine = nullptr, hFontTime = nullptr; +COLORREF colFirstLine = RGB(255, 0, 0), colSecondLine = 0, colTime = RGB(0, 0, 255), colBorder = RGB(0, 0, 0), + colSidebar = RGB(128, 128, 128), colTitleUnderline = GetSysColor(COLOR_3DSHADOW); + +// toptoolbar button +HANDLE hTTButton; + +// menu items +HGENMENU hMenuRoot, hMenuItem, hMenuItemHistory; + +///////////////////////////////////////////////////////////////////////////////////////// + +PLUGININFOEX pluginInfoEx = +{ + sizeof(PLUGININFOEX), + __PLUGIN_NAME, + MIRANDA_VERSION_DWORD, + __DESCRIPTION, + __AUTHOR, + __COPYRIGHT, + __AUTHORWEB, + UNICODE_AWARE, + // {EFD15F16-7AE4-40D7-A8E3-A411ED747BD5} + {0xefd15f16, 0x7ae4, 0x40d7, {0xa8, 0xe3, 0xa4, 0x11, 0xed, 0x74, 0x7b, 0xd5}} +}; + +CMPlugin::CMPlugin() : + PLUGIN<CMPlugin>(MODULENAME, pluginInfoEx) +{} + +///////////////////////////////////////////////////////////////////////////////////////// + +extern "C" __declspec(dllexport) const MUUID MirandaInterfaces[] = { MIID_POPUP, MIID_LAST }; + +///////////////////////////////////////////////////////////////////////////////////////// + +static int ReloadFont(WPARAM, LPARAM) +{ + LOGFONT log_font; + if (hFontFirstLine) DeleteObject(hFontFirstLine); + colFirstLine = Font_GetW(font_id_firstline, &log_font); + hFontFirstLine = CreateFontIndirect(&log_font); + if (hFontSecondLine) DeleteObject(hFontSecondLine); + colSecondLine = Font_GetW(font_id_secondline, &log_font); + hFontSecondLine = CreateFontIndirect(&log_font); + if (hFontTime) DeleteObject(hFontTime); + colTime = Font_GetW(font_id_time, &log_font); + hFontTime = CreateFontIndirect(&log_font); + + colBg = Colour_GetW(colour_id_bg); + colBorder = Colour_GetW(colour_id_border); + colSidebar = Colour_GetW(colour_id_sidebar); + colTitleUnderline = Colour_GetW(colour_id_titleunderline); + return 0; +} + +static int TTBLoaded(WPARAM, LPARAM) +{ + TTBButton ttb = {}; + ttb.pszService = "Popup/EnableDisableMenuCommand"; + ttb.lParamUp = 1; + ttb.dwFlags = TTBBF_VISIBLE | TTBBF_SHOWTOOLTIP | TTBBF_ASPUSHBUTTON; + if (db_get_b(0, "Popup", "ModuleIsEnabled", 1)) + ttb.dwFlags |= TTBBF_PUSHED; + ttb.name = LPGEN("Toggle Popups"); + ttb.hIconHandleUp = IcoLib_GetIconHandle(ICO_TB_POPUP_OFF); + ttb.hIconHandleDn = IcoLib_GetIconHandle(ICO_TB_POPUP_ON); + ttb.pszTooltipUp = LPGEN("Enable Popups"); + ttb.pszTooltipDn = LPGEN("Disable Popups"); + hTTButton = g_plugin.addTTB(&ttb); + return 0; +} + +static void InitFonts() +{ + font_id_firstline.flags = FIDF_ALLOWEFFECTS; + mir_wstrcpy(font_id_firstline.group, LPGENW("Popups")); + mir_wstrcpy(font_id_firstline.name, LPGENW("First line")); + mir_strcpy(font_id_firstline.dbSettingsGroup, MODULENAME); + mir_strcpy(font_id_firstline.setting, "FontFirst"); + mir_wstrcpy(font_id_firstline.backgroundGroup, L"Popups"); + mir_wstrcpy(font_id_firstline.backgroundName, L"Background"); + font_id_firstline.order = 0; + g_plugin.addFont(&font_id_firstline); + + font_id_secondline.flags = FIDF_ALLOWEFFECTS; + mir_wstrcpy(font_id_secondline.group, LPGENW("Popups")); + mir_wstrcpy(font_id_secondline.name, LPGENW("Second line")); + mir_strcpy(font_id_secondline.dbSettingsGroup, MODULENAME); + mir_strcpy(font_id_secondline.setting, "FontSecond"); + mir_wstrcpy(font_id_secondline.backgroundGroup, L"Popups"); + mir_wstrcpy(font_id_secondline.backgroundName, L"Background"); + font_id_secondline.order = 1; + g_plugin.addFont(&font_id_secondline); + + font_id_time.flags = FIDF_ALLOWEFFECTS; + mir_wstrcpy(font_id_time.group, LPGENW("Popups")); + mir_wstrcpy(font_id_time.name, LPGENW("Time")); + mir_strcpy(font_id_time.dbSettingsGroup, MODULENAME); + mir_strcpy(font_id_time.setting, "FontTime"); + mir_wstrcpy(font_id_time.backgroundGroup, L"Popups"); + mir_wstrcpy(font_id_time.backgroundName, L"Background"); + font_id_time.order = 2; + g_plugin.addFont(&font_id_time); + + mir_wstrcpy(colour_id_bg.group, LPGENW("Popups")); + mir_wstrcpy(colour_id_bg.name, LPGENW("Background")); + mir_strcpy(colour_id_bg.dbSettingsGroup, MODULENAME); + mir_strcpy(colour_id_bg.setting, "ColourBg"); + colour_id_bg.defcolour = GetSysColor(COLOR_3DSHADOW); + colour_id_bg.order = 0; + g_plugin.addColor(&colour_id_bg); + + mir_wstrcpy(colour_id_border.group, LPGENW("Popups")); + mir_wstrcpy(colour_id_border.name, LPGENW("Border")); + mir_strcpy(colour_id_border.dbSettingsGroup, MODULENAME); + mir_strcpy(colour_id_border.setting, "ColourBorder"); + colour_id_border.defcolour = RGB(0, 0, 0); + colour_id_border.order = 1; + g_plugin.addColor(&colour_id_border); + + mir_wstrcpy(colour_id_sidebar.group, LPGENW("Popups")); + mir_wstrcpy(colour_id_sidebar.name, LPGENW("Sidebar")); + mir_strcpy(colour_id_sidebar.dbSettingsGroup, MODULENAME); + mir_strcpy(colour_id_sidebar.setting, "ColourSidebar"); + colour_id_sidebar.defcolour = RGB(128, 128, 128); + colour_id_sidebar.order = 2; + g_plugin.addColor(&colour_id_sidebar); + + mir_wstrcpy(colour_id_titleunderline.group, LPGENW("Popups")); + mir_wstrcpy(colour_id_titleunderline.name, LPGENW("Title underline")); + mir_strcpy(colour_id_titleunderline.dbSettingsGroup, MODULENAME); + mir_strcpy(colour_id_titleunderline.setting, "ColourTitleUnderline"); + colour_id_titleunderline.defcolour = GetSysColor(COLOR_3DSHADOW); + colour_id_titleunderline.order = 3; + g_plugin.addColor(&colour_id_titleunderline); + + ReloadFont(0, 0); +} + +static void InitMenuItems(void) +{ + bool isEnabled = db_get_b(0, "Popup", "ModuleIsEnabled", 1) == 1; + + hMenuRoot = g_plugin.addRootMenu(MO_MAIN, LPGENW("Popups"), 500010000, IcoLib_GetIcon(isEnabled ? ICO_POPUP_ON : ICO_POPUP_OFF, 0)); + Menu_ConfigureItem(hMenuRoot, MCI_OPT_UID, "043A641A-2767-4C57-AA57-9233D6F9DC54"); + + CMenuItem mi(&g_plugin); + mi.flags = CMIF_UNICODE; + mi.root = hMenuRoot; + + SET_UID(mi, 0x92c386ae, 0x6e81, 0x452d, 0xb5, 0x71, 0x87, 0x46, 0xe9, 0x2, 0x66, 0xe9); + mi.hIcolibItem = IcoLib_GetIcon(ICO_HISTORY, 0); + mi.pszService= MS_POPUP_SHOWHISTORY; + mi.name.w = LPGENW("Popup History"); + hMenuItemHistory = Menu_AddMainMenuItem(&mi); + + SET_UID(mi, 0x4353d44e, 0x177, 0x4843, 0x88, 0x30, 0x25, 0x5d, 0x91, 0xad, 0xdf, 0x3f); + mi.hIcolibItem = IcoLib_GetIcon(isEnabled ? ICO_POPUP_ON : ICO_POPUP_OFF, 0); + mi.pszService = "Popup/EnableDisableMenuCommand"; + mi.name.w = (isEnabled ? LPGENW("Disable Popups") : LPGENW("Enable Popups")); + hMenuItem = Menu_AddMainMenuItem(&mi); +} + +static int ModulesLoaded(WPARAM, LPARAM) +{ + MNotifyGetLink(); + + if (ServiceExists(MS_HPP_EG_WINDOW)) + lstPopupHistory.SetRenderer(RENDER_HISTORYPP); + + HookEvent(ME_FONT_RELOAD, ReloadFont); + HookEvent(ME_TTB_MODULELOADED, TTBLoaded); + + LoadModuleDependentOptions(); + + if (GetModuleHandle(L"neweventnotify")) + ignore_gpd_passed_addy = true; + + return 0; +} + +static int PreShutdown(WPARAM, LPARAM) +{ + bShutdown = true; + DeinitMessagePump(); + return 0; +} + +int CMPlugin::Load() +{ + InitMessagePump(); + InitOptions(); + InitNotify(); + InitFonts(); + InitIcons(); + InitMenuItems(); + + HookEvent(ME_SYSTEM_PRESHUTDOWN, PreShutdown); + HookEvent(ME_SYSTEM_MODULESLOADED, ModulesLoaded); + return 0; +} + +///////////////////////////////////////////////////////////////////////////////////////// + +int CMPlugin::Unload() +{ + DeleteObject(hFontFirstLine); + DeleteObject(hFontSecondLine); + DeleteObject(hFontTime); + return 0; +} diff --git a/src/core/stdpopup/src/yapp_history.cpp b/src/core/stdpopup/src/yapp_history.cpp new file mode 100644 index 0000000000..fb881cf1c1 --- /dev/null +++ b/src/core/stdpopup/src/yapp_history.cpp @@ -0,0 +1,84 @@ +#include "stdafx.h" + +PopupHistoryList::PopupHistoryList(int renderer) +{ + this->renderer = renderer; + 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() +{ + for (int i = 0; i < count; i++) + { + DeleteData(i); + } + count = 0; +} + +void PopupHistoryList::RemoveItem(int index) +{ + DeleteData(index); //free the mem for that particular item + for (int 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]; + mir_free(item->titleT); + mir_free(item->messageT); + item->timestamp = 0; //invalidate item + item->title = nullptr; + item->message = nullptr; + 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, GetRenderer()); +} + +void PopupHistoryList::Add(char *title, char *message, time_t timestamp) +{ + PopupHistoryData item = {0}; //create a history item + item.timestamp = timestamp; + item.title = mir_strdup(title); + item.message = mir_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.titleT = mir_wstrdup(title); + item.messageT = mir_wstrdup(message); + AddItem(item); //add it +} + +PopupHistoryData *PopupHistoryList::Get(int index) +{ + if ((index < 0) || (index >= count)) //a bit of sanity check + { + return nullptr; + } + + return &historyData[index]; +} diff --git a/src/core/stdpopup/src/yapp_history.h b/src/core/stdpopup/src/yapp_history.h new file mode 100644 index 0000000000..ff13d28b34 --- /dev/null +++ b/src/core/stdpopup/src/yapp_history.h @@ -0,0 +1,93 @@ +#ifndef __yapp_history_h__ +#define __yapp_history_h__ + +/****HISTORY ++ STUFF ***/ +//there's no include file for h++ yet +#ifndef MS_HPP_EG_WINDOW +#define MS_HPP_EG_WINDOW "History++/ExtGrid/NewWindow" +#endif + +#ifndef MS_HPP_EG_EVENT +#define MS_HPP_EG_EVENT "History++/ExtGrid/Event" +#endif + +#ifndef MS_HPP_EG_NAVIGATE +#define MS_HPP_EG_NAVIGATE "History++/ExtGrid/Navigate" +#endif + +#ifndef MS_HPP_EG_OPTIONSCHANGED +#define MS_HPP_EG_OPTIONSCHANGED "History++/ExtGrid/OptionsChanged" +#endif +/************************/ + + + +#define HISTORY_SIZE 200 //number of popup history items + +#define PHDF_UNICODE 1 + +#define POPUPMENU_TITLE 100 +#define POPUPMENU_MESSAGE 101 +#define POPUPMENU_TIMESTAMP 102 + +#define RENDER_DEFAULT 0x00000 +#define RENDER_HISTORYPP 0x00001 +#define RENDER_IEVIEW 0x00002 + +struct PopupHistoryData{ + DWORD flags; //PHDF_* flags + union{ + char *message; + wchar_t *messageW; + wchar_t *messageT; + }; + union{ + char *title; + wchar_t *titleW; + wchar_t *titleT; + }; + time_t timestamp; +}; + +class PopupHistoryList{ + private: + PopupHistoryData *historyData; //historyData[0] - oldest, historyData[size - 1] - newest + int count; + int size; + int renderer; + + void DeleteData(int index); + void AddItem(PopupHistoryData item); //adds a PopupHistoryData item + void RemoveItem(int index); + + public: + PopupHistoryList(int renderer = RENDER_DEFAULT); + ~PopupHistoryList(); + + int GetRenderer() { return renderer; } + void SetRenderer(int newRenderer) { renderer = newRenderer; } + + 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() { return count; } + int Size() { return 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, int renderer); + +INT_PTR CALLBACK DlgProcHistLst(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + +#endif //__popup_history_h__ diff --git a/src/core/stdpopup/src/yapp_history_dlg.cpp b/src/core/stdpopup/src/yapp_history_dlg.cpp new file mode 100644 index 0000000000..8e546af363 --- /dev/null +++ b/src/core/stdpopup/src/yapp_history_dlg.cpp @@ -0,0 +1,675 @@ +#include "stdafx.h" + +//************ Some helper resize stuff ****************** + +#define MIN_HISTORY_WIDTH 540 +#define MIN_HISTORY_HEIGHT 300 + +#define GAP_SIZE 2 + +#define ANCHOR_LEFT 0x000001 +#define ANCHOR_RIGHT 0x000002 +#define ANCHOR_TOP 0x000004 +#define ANCHOR_BOTTOM 0x000008 +#define ANCHOR_ALL ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_TOP | ANCHOR_BOTTOM + +HWND hHistoryWindow = nullptr; //the history window +PopupHistoryList lstPopupHistory; //defined in main.cpp + +const wchar_t *szHistoryColumns[] = {L"Title", L"Message", L"Timestamp"}; //need to make sure that the string and size vectors have the same number of elements +const int cxHistoryColumns[] = {100, 450, 115}; +const int cHistoryColumns = sizeof(szHistoryColumns) / sizeof(szHistoryColumns[0]); + +struct PopupHistoryWindowData{ + HWND hIEView; +}; + +void ScreenToClient(HWND hWnd, LPRECT rect) +{ + POINT pt; + int cx = rect->right - rect->left; + int cy = rect->bottom - rect->top; + pt.x = rect->left; + pt.y = rect->top; + ScreenToClient(hWnd, &pt); + rect->left = pt.x; + rect->top = pt.y; + rect->right = pt.x + cx; + rect->bottom = pt.y + cy; +} + +RECT AnchorCalcPos(HWND window, const RECT *rParent, const WINDOWPOS *parentPos, int anchors) +{ + RECT rChild; + RECT rTmp; + + GetWindowRect(window, &rChild); + ScreenToClient(parentPos->hwnd, &rChild); + + int cx = rParent->right - rParent->left; + int cy = rParent->bottom - rParent->top; + if ((cx == parentPos->cx) && (cy == parentPos->cy)) + { + return rChild; + } + if (parentPos->flags & SWP_NOSIZE) + { + return rChild; + } + + rTmp.left = parentPos->x - rParent->left; + rTmp.right = (parentPos->x + parentPos->cx) - rParent->right; + rTmp.bottom = (parentPos->y + parentPos->cy) - rParent->bottom; + rTmp.top = parentPos->y - rParent->top; + + cx = (rTmp.left) ? -rTmp.left : rTmp.right; + cy = (rTmp.top) ? -rTmp.top : rTmp.bottom; + + rChild.right += cx; + rChild.bottom += cy; + //expanded the window accordingly, now we need to enforce the anchors + if ((anchors & ANCHOR_LEFT) && (!(anchors & ANCHOR_RIGHT))) + { + rChild.right -= cx; + } + if ((anchors & ANCHOR_TOP) && (!(anchors & ANCHOR_BOTTOM))) + { + rChild.bottom -= cy; + } + if ((anchors & ANCHOR_RIGHT) && (!(anchors & ANCHOR_LEFT))) + { + rChild.left += cx; + } + if ((anchors & ANCHOR_BOTTOM) && (!(anchors & ANCHOR_TOP))) + { + rChild.top += cy; + } + return rChild; +} + +void AnchorMoveWindow(HWND window, const WINDOWPOS *parentPos, int anchors) +{ + RECT rParent; + RECT rChild; + + if (parentPos->flags & SWP_NOSIZE) + { + return; + } + GetWindowRect(parentPos->hwnd, &rParent); + rChild = AnchorCalcPos(window, &rParent, parentPos, anchors); + MoveWindow(window, rChild.left, rChild.top, rChild.right - rChild.left, rChild.bottom - rChild.top, FALSE); +} + +void AddAnchorWindowToDeferList(HDWP &hdWnds, HWND window, RECT *rParent, WINDOWPOS *wndPos, int anchors) +{ + if (nullptr == window) /* Wine fix. */ + return; + RECT rChild = AnchorCalcPos(window, rParent, wndPos, anchors); + hdWnds = DeferWindowPos(hdWnds, window, HWND_NOTOPMOST, rChild.left, rChild.top, rChild.right - rChild.left, rChild.bottom - rChild.top, SWP_NOZORDER); +} +//************************************************************ + +//Stucture passed to list sort function +struct SortParams{ + HWND hList; + int column; +}; + +static int lastColumn = -1; //last sort column + +int MatchesFilterCS(const wchar_t *filter, const PopupHistoryData *popupItem) //case sensitive +{ + if (mir_wstrlen(filter) <= 0) { return 1; } //if no filter is set then the popup item matches the filter + int match = 0; + + match = (wcsstr(popupItem->messageT, filter)) ? 1 : match; //check message part + + if (!match) //check title part of no match has been found + { + match = (wcsstr(popupItem->titleT, filter)) ? 1 : match; + } + + if (!match) //if no match has been found yet try to match the timestamp + { + wchar_t buffer[1024]; + struct tm *myTime = localtime(&popupItem->timestamp); + wcsftime(buffer, 1024, L"%c", myTime); + match = (wcsstr(buffer, filter)) ? 1 : match; + } + + return match; +} + +__inline void ConvertCase(wchar_t *dest, const wchar_t *source, size_t size) +{ + wcsncpy(dest, source, size); + wcslwr(dest); +} + +int MatchesFilterCI(const wchar_t *filterS, const PopupHistoryData *popupItem) +{ + if (mir_wstrlen(filterS) <= 0) { return 1; } //if no filter is set then the popup item matches the filter + int match = 0; + const int BUFFER_SIZE = 1024; + wchar_t buffer[BUFFER_SIZE]; + wchar_t filterI[BUFFER_SIZE]; + + ConvertCase(filterI, filterS, BUFFER_SIZE); + + ConvertCase(buffer, popupItem->messageT, BUFFER_SIZE); //check message part + match = (wcsstr(buffer, filterI)) ? 1 : match; + + if (!match) // check title part of no match has been found + { + ConvertCase(buffer, popupItem->titleT, BUFFER_SIZE); + match = (wcsstr(buffer, filterI)) ? 1 : match; + } + + if (!match) //if no match has been found yet try to match the timestamp + { + struct tm *myTime = localtime(&popupItem->timestamp); + wcsftime(buffer, 1024, L"%c", myTime); + match = (wcsstr(buffer, filterI)) ? 1 : match; + } + + return match; +} + +int CALLBACK PopupsCompare(LPARAM lParam1, LPARAM lParam2, LPARAM myParam) +{ + SortParams params = *(SortParams *) myParam; + const int MAX_SIZE = 512; + wchar_t text1[MAX_SIZE]; + wchar_t text2[MAX_SIZE]; + int res; + + ListView_GetItemText(params.hList, (int) lParam1, params.column, text1, _countof(text1)); + ListView_GetItemText(params.hList, (int) lParam2, params.column, text2, _countof(text2)); + + res = mir_wstrcmpi(text1, text2); + + res = (params.column == lastColumn) ? -res : res; //do reverse search on second click on same column + + return res; +} + + + +int CalcCustomControlPos(IEVIEWWINDOW *ieWnd, HWND hMainWindow) +{ + RECT rect; + GetWindowRect(GetDlgItem(hMainWindow, IDC_LST_HISTORY), &rect); + ScreenToClient(hMainWindow, &rect); + //GetClientRect(hMainWindow, &rect); + + ieWnd->x = rect.left + GAP_SIZE; + ieWnd->y = rect.top + GAP_SIZE; + ieWnd->cx = rect.right - rect.left - (2 * GAP_SIZE); + ieWnd->cy = rect.bottom - rect.top - (2 * GAP_SIZE); + return 0; +} + +void MoveCustomControl(HWND hWnd, int renderer) +{ + switch (renderer) + { + case RENDER_HISTORYPP: + case RENDER_IEVIEW: + { + PopupHistoryWindowData *data = (PopupHistoryWindowData *) GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (data) + { + IEVIEWWINDOW ieWnd = {0}; + ieWnd.cbSize = sizeof(ieWnd); + ieWnd.parent = hWnd; + ieWnd.hwnd = data->hIEView; + ieWnd.iType = IEW_SETPOS; + CalcCustomControlPos(&ieWnd, hWnd); + + CallService((renderer == RENDER_HISTORYPP) ? MS_HPP_EG_WINDOW : MS_IEVIEW_WINDOW, 0, (LPARAM) &ieWnd); + } + + break; + } + } +} + +void LoadRenderer(HWND hWnd, int renderer) +{ + switch (renderer) + { + case RENDER_HISTORYPP: + case RENDER_IEVIEW: + { + IEVIEWWINDOW ieWnd = {0}; + + ieWnd.cbSize = sizeof(ieWnd); + ieWnd.iType = IEW_CREATE; + ieWnd.dwMode = IEWM_HISTORY; + ieWnd.dwFlags = 0; + ieWnd.parent = hWnd; + CalcCustomControlPos(&ieWnd, hWnd); + + CallService((renderer == RENDER_HISTORYPP) ? MS_HPP_EG_WINDOW : MS_IEVIEW_WINDOW, 0, (LPARAM) &ieWnd); //create the IeView or H++ control. + + PopupHistoryWindowData *data = (PopupHistoryWindowData *) mir_alloc(sizeof(PopupHistoryWindowData)); //create custom control data + data->hIEView = ieWnd.hwnd; + ShowWindow(data->hIEView, SW_SHOW); + SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR) data); //set it as the window's user data + ShowWindow(GetDlgItem(hWnd, IDC_LST_HISTORY), SW_HIDE); + //SetWindowPos(GetDlgItem(hWnd, IDC_LST_HISTORY), HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE); + + break; + } + } +} + +void UnloadRenderer(HWND hWnd, int renderer) +{ + switch (renderer) + { + case RENDER_HISTORYPP: + case RENDER_IEVIEW: + { + PopupHistoryWindowData *data = (PopupHistoryWindowData *) GetWindowLongPtr(hWnd, GWLP_USERDATA); + + if (data) + { + IEVIEWWINDOW ieWnd = {0}; + ieWnd.cbSize = sizeof(ieWnd); + ieWnd.parent = hWnd; + ieWnd.hwnd = data->hIEView; + ieWnd.iType = IEW_DESTROY; + CallService((renderer == RENDER_HISTORYPP) ? MS_HPP_EG_WINDOW : MS_IEVIEW_WINDOW, 0, (LPARAM) &ieWnd); + + mir_free(data); + } + + break; + } + } +} + +void DeleteOldEvents(HWND hWnd, int renderer) +{ + switch (renderer) + { + case RENDER_HISTORYPP: + case RENDER_IEVIEW: + { + PopupHistoryWindowData *data = (PopupHistoryWindowData *) GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (data) + { + IEVIEWEVENT ieEvent = {0}; + ieEvent.cbSize = sizeof(IEVIEWEVENT); + ieEvent.hwnd = data->hIEView; + ieEvent.iType = IEE_CLEAR_LOG; + + CallService((renderer == RENDER_HISTORYPP) ? MS_HPP_EG_EVENT : MS_IEVIEW_EVENT, 0, (LPARAM) &ieEvent); + } + + break; + } + + case RENDER_DEFAULT: + default: + { + ListView_DeleteAllItems(GetDlgItem(hWnd, IDC_LST_HISTORY)); + + break; + } + + } +} + +typedef int (*SIG_MATCHESFILTER)(const wchar_t *filter, const PopupHistoryData *popupItem); +typedef void (*SIG_ADDEVENTS)(HWND hWnd, int renderer, wchar_t *filter, SIG_MATCHESFILTER MatchesFilter); + +IEVIEWEVENTDATA *CreateAndFillEventData(PopupHistoryData *popupItem) +{ + IEVIEWEVENTDATA *eventData = (IEVIEWEVENTDATA *) mir_calloc(sizeof(IEVIEWEVENTDATA)); + if (eventData) + { + eventData->cbSize = sizeof(IEVIEWEVENTDATA); + eventData->iType = IEED_EVENT_MESSAGE; + + eventData->dwFlags = IEEDF_UNICODE_NICK | IEEDF_UNICODE_TEXT | IEEDF_UNICODE_TEXT2; + eventData->pszNickW = popupItem->titleT; + eventData->pszTextW = popupItem->messageT; + + eventData->time = (DWORD) popupItem->timestamp; + eventData->next = nullptr; + } + + return eventData; +} + +void AddEventsCustomControl(HWND hWnd, int renderer, wchar_t *filter, SIG_MATCHESFILTER MatchesFilter) +{ + PopupHistoryWindowData *pwData = (PopupHistoryWindowData *) GetWindowLongPtr(hWnd, GWLP_USERDATA); + if (pwData) + { + IEVIEWEVENT ieEvent = {0}; + ieEvent.cbSize = sizeof(IEVIEWEVENT); + ieEvent.hwnd = pwData->hIEView; + ieEvent.codepage = CP_ACP; + ieEvent.iType = IEE_LOG_MEM_EVENTS; + ieEvent.hContact = NULL; + + IEVIEWEVENTDATA *eventData = nullptr; + IEVIEWEVENTDATA *cED = nullptr; + IEVIEWEVENTDATA *prevED = nullptr; + + int i; + int count = 0; + int size = lstPopupHistory.Count(); + PopupHistoryData *popupItem; + + for (i = 0; i < size; i++) + { + popupItem = lstPopupHistory.Get(i); + if (MatchesFilter(filter, popupItem)) + { + cED = CreateAndFillEventData(popupItem); + if (cED) + { + count++; + if (!eventData) + { + eventData = cED; + } + else{ + prevED->next = cED; + } + + prevED = cED; + } + } + } + ieEvent.count = count; + ieEvent.eventData = eventData; + + CallService((renderer == RENDER_HISTORYPP) ? MS_HPP_EG_EVENT : MS_IEVIEW_EVENT, 0, (LPARAM) &ieEvent); + + while (eventData) + { + cED = eventData; + eventData = eventData->next; + mir_free(cED); + } + } +} + +void AddEventsDefault(HWND hWnd, int, wchar_t *filter, SIG_MATCHESFILTER MatchesFilter) +{ + HWND hHistoryList = GetDlgItem(hWnd, IDC_LST_HISTORY); + wchar_t buffer[1024]; + struct tm *myTime; + + LVITEM item = {0}; + item.mask = LVIF_TEXT; + + int i, lIndex; + lIndex = 0; + PopupHistoryData *popupItem; + for (i = 0; i < lstPopupHistory.Count(); i++) + { + item.iItem = lIndex; + popupItem = lstPopupHistory.Get(i); + if (MatchesFilter(filter, popupItem)) + { + item.pszText = popupItem->titleT; + ListView_InsertItem(hHistoryList, &item); + ListView_SetItemText(hHistoryList, lIndex, 1, popupItem->messageT); + myTime = localtime(&popupItem->timestamp); + wcsftime(buffer, 1024, L"%c", myTime); + ListView_SetItemText(hHistoryList, lIndex++, 2, buffer); + } + } +} + +void RefreshPopupHistory(HWND hWnd, int renderer) +{ + if (!hWnd) { return; } + const int MAX_FILTER_SIZE = 1024; + SIG_MATCHESFILTER MatchesFilter = (IsDlgButtonChecked(hWnd, IDC_HISTORY_FILTER_CASESENSITIVE)) ? MatchesFilterCS : MatchesFilterCI; //case sensitive compare or not ? + + SIG_ADDEVENTS AddEvents = (renderer == RENDER_DEFAULT) ? AddEventsDefault : AddEventsCustomControl; + + wchar_t filter[MAX_FILTER_SIZE]; + DeleteOldEvents(hWnd, renderer); //delete events + + GetDlgItemText(hWnd, IDC_HISTORY_FILTER, filter, _countof(filter)); //get filter text + + AddEvents(hWnd, renderer, filter, MatchesFilter); + + if (renderer == RENDER_DEFAULT) + { + HWND hHistoryList = GetDlgItem(hWnd, IDC_LST_HISTORY); + SortParams params = {}; + 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)) + { + wchar_t buffer[2048]; + buffer[0] = '\0'; + wchar_t *clipboard; + int i; + int found = 0; + int count = ListView_GetItemCount(hList); + int textType; + + textType = CF_UNICODETEXT; + + + for (i = 0; i < count; i++) + { + if (ListView_GetItemState(hList, i, LVIS_SELECTED)) + { + ListView_GetItemText(hList, i, selection - 100, buffer, _countof(buffer)); + found = 1; + break; + } + } + if (found) + { + EmptyClipboard(); + int len = (int)mir_wstrlen(buffer); + + HANDLE hData = GlobalAlloc(GMEM_MOVEABLE, (len + 2) * sizeof(wchar_t)); + clipboard = (wchar_t *) GlobalLock(hData); + wcsncpy(clipboard, buffer, len); + clipboard[len] = '\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 +static LRESULT CALLBACK PopupsListSubclassProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + switch (msg) { + case WM_CONTEXTMENU: + { + POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(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, pt.x, pt.y, 0, hWnd, nullptr); + 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 mir_callNextSubclass(hWnd, PopupsListSubclassProc, msg, wParam, lParam); +} + +//load the columns +void LoadHistoryColumns(HWND hHistoryList) +{ + LVCOLUMN col; + col.mask = LVCF_TEXT | LVCF_WIDTH; + int i; + + for (i = 0; i < cHistoryColumns; i++) + { + col.pszText = TranslateW(szHistoryColumns[i]); + col.cx = cxHistoryColumns[i]; + ListView_InsertColumn(hHistoryList, i, &col); + } +} + +//this is the history list window handler +INT_PTR CALLBACK DlgProcHistLst(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + static int bInitializing; + + switch (msg) { + case WM_INITDIALOG: + bInitializing = 1; + Window_SetIcon_IcoLib(hWnd, IcoLib_GetIconHandle(ICO_HISTORY)); + { + int renderer = lstPopupHistory.GetRenderer(); + LoadRenderer(hWnd, renderer); + + TranslateDialogDefault(hWnd); + HWND hHistoryList = GetDlgItem(hWnd, IDC_LST_HISTORY); + + ListView_SetExtendedListViewStyleEx(hHistoryList, LVS_EX_FULLROWSELECT, LVS_EX_FULLROWSELECT); + + mir_subclassWindow(hHistoryList, PopupsListSubclassProc); + + LoadHistoryColumns(hHistoryList); + + RefreshPopupHistory(hWnd, renderer); + } + bInitializing = 0; + return TRUE; + + case WM_DESTROY: + UnloadRenderer(hWnd, lstPopupHistory.GetRenderer()); + hHistoryWindow = nullptr; + break; + + case WM_CLOSE: + DestroyWindow(hWnd); + break; + + case WM_WINDOWPOSCHANGING: + { + WINDOWPOS *wndPos = (WINDOWPOS *) lParam; + + if (wndPos->cx < MIN_HISTORY_WIDTH) + wndPos->cx = MIN_HISTORY_WIDTH; + if (wndPos->cy < MIN_HISTORY_HEIGHT) + wndPos->cy = MIN_HISTORY_HEIGHT; + + RECT rParent; + GetWindowRect(hWnd, &rParent); + HDWP hdWnds = BeginDeferWindowPos(4); + + AddAnchorWindowToDeferList(hdWnds, GetDlgItem(hWnd, IDC_CLOSE), &rParent, wndPos, ANCHOR_RIGHT | ANCHOR_BOTTOM); + AddAnchorWindowToDeferList(hdWnds, GetDlgItem(hWnd, IDC_HISTORY_FILTER), &rParent, wndPos, ANCHOR_LEFT | ANCHOR_BOTTOM); + AddAnchorWindowToDeferList(hdWnds, GetDlgItem(hWnd, IDC_HISTORY_FILTER_CASESENSITIVE), &rParent, wndPos, ANCHOR_LEFT | ANCHOR_RIGHT | ANCHOR_BOTTOM); + AddAnchorWindowToDeferList(hdWnds, GetDlgItem(hWnd, IDC_LST_HISTORY), &rParent, wndPos, ANCHOR_ALL); + + EndDeferWindowPos(hdWnds); + MoveCustomControl(hWnd, lstPopupHistory.GetRenderer()); //move the custom control too, if any + } + break; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDC_CLOSE: + SendMessage(hWnd, WM_CLOSE, 0, 0); + break; + + case IDC_HISTORY_FILTER_CASESENSITIVE: + if (HIWORD(wParam) == BN_CLICKED) + RefreshPopupHistory(hWnd, lstPopupHistory.GetRenderer()); + + case IDC_HISTORY_FILTER: + if (HIWORD(wParam) == EN_CHANGE) + if (!bInitializing) + RefreshPopupHistory(hWnd, lstPopupHistory.GetRenderer()); + 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 = {}; + 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; + } + + return 0; +} diff --git a/src/core/stdpopup/stdpopup.vcxproj b/src/core/stdpopup/stdpopup.vcxproj new file mode 100644 index 0000000000..bf4b3a03c2 --- /dev/null +++ b/src/core/stdpopup/stdpopup.vcxproj @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <ItemGroup Label="ProjectConfigurations"> + <ProjectConfiguration Include="Debug|Win32"> + <Configuration>Debug</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Debug|x64"> + <Configuration>Debug</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|Win32"> + <Configuration>Release</Configuration> + <Platform>Win32</Platform> + </ProjectConfiguration> + <ProjectConfiguration Include="Release|x64"> + <Configuration>Release</Configuration> + <Platform>x64</Platform> + </ProjectConfiguration> + </ItemGroup> + <PropertyGroup Label="Globals"> + <ProjectName>StdPopup</ProjectName> + <ProjectGuid>{B6FC188B-8E54-4197-9444-8BADE9AA75E2}</ProjectGuid> + </PropertyGroup> + <ImportGroup Label="PropertySheets"> + <Import Project="$(ProjectDir)..\..\..\build\vc.common\core.props" /> + </ImportGroup> +</Project>
\ No newline at end of file diff --git a/src/core/stdpopup/stdpopup.vcxproj.filters b/src/core/stdpopup/stdpopup.vcxproj.filters new file mode 100644 index 0000000000..fcae13a9d8 --- /dev/null +++ b/src/core/stdpopup/stdpopup.vcxproj.filters @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(ProjectDir)..\..\build\vc.common\common.filters" /> +</Project>
\ No newline at end of file diff --git a/src/mir_app/src/newplugins.cpp b/src/mir_app/src/newplugins.cpp index 4ccbf43a4c..c77b80b7b9 100644 --- a/src/mir_app/src/newplugins.cpp +++ b/src/mir_app/src/newplugins.cpp @@ -139,6 +139,7 @@ static MuuidReplacement pluginDefault[] = { MIID_AUTOAWAY, L"stdautoaway", nullptr }, // 6
{ MIID_USERONLINE, L"stduseronline", nullptr }, // 7
{ MIID_SRAWAY, L"stdaway", nullptr }, // 8
+ { MIID_POPUP, L"stdpopup", nullptr }, // 9
};
int getDefaultPluginIdx(const MUUID &muuid)
|